diff --git a/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt b/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt --- a/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt +++ b/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt @@ -5,6 +5,8 @@ mlir_tablegen(HLFIRDialect.cpp.inc -gen-dialect-defs -dialect=hlfir) mlir_tablegen(HLFIRAttributes.h.inc -gen-attrdef-decls -attrdefs-dialect=hlfir) mlir_tablegen(HLFIRAttributes.cpp.inc -gen-attrdef-defs -attrdefs-dialect=hlfir) +mlir_tablegen(HLFIREnums.h.inc -gen-enum-decls) +mlir_tablegen(HLFIREnums.cpp.inc -gen-enum-defs) set(LLVM_TARGET_DEFINITIONS HLFIROps.td) mlir_tablegen(HLFIROps.h.inc -gen-op-decls) diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h --- a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h +++ b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h @@ -26,6 +26,8 @@ #include "flang/Optimizer/HLFIR/HLFIRDialect.h.inc" +#include "flang/Optimizer/HLFIR/HLFIREnums.h.inc" + #define GET_TYPEDEF_CLASSES #include "flang/Optimizer/HLFIR/HLFIRTypes.h.inc" diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td --- a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td @@ -15,6 +15,7 @@ #define FORTRAN_DIALECT_HLFIR_OP_BASE include "mlir/IR/AttrTypeBase.td" +include "mlir/IR/EnumAttr.td" include "mlir/IR/OpBase.td" include "flang/Optimizer/Dialect/FIRTypes.td" @@ -133,4 +134,12 @@ def AnyFortranLogicalOrI1ArrayObject : Type; +def hlfir_CharExtremumPredicateAttr : I32EnumAttr< + "CharExtremumPredicate", "", + [ + I32EnumAttrCase<"min", 0>, + I32EnumAttrCase<"max", 1> + ]> { + let cppNamespace = "hlfir"; +} #endif // FORTRAN_DIALECT_HLFIR_OP_BASE diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td --- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td @@ -317,6 +317,29 @@ let hasVerifier = 1; } +def hlfir_CharExtremumOp : hlfir_Op<"char_extremum", []> { + let summary = "Find max/min from given character strings"; + let description = [{ + Find the minimum or maximum of two or more character strings of a same character kind and return the string with the minimum or maximum number of characters. Example: + + %0 = hlfir.char_extremum min, %arg0, %arg1 : (!fir.ref>, !fir.ref>) -> !hlfir.expr> + }]; + + let arguments = (ins hlfir_CharExtremumPredicateAttr:$predicate, + Variadic:$strings + ); + + let results = (outs AnyScalarCharacterExpr); + + let assemblyFormat = [{ + $predicate `,` $strings attr-dict `:` functional-type(operands, results) + }]; + + let builders = [OpBuilder<(ins "hlfir::CharExtremumPredicate":$predicate, "mlir::ValueRange":$strings)>]; + + let hasVerifier = 1; +} + def hlfir_SetLengthOp : hlfir_Op<"set_length", []> { let summary = "change the length of a character entity"; let description = [{ diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp --- a/flang/lib/Lower/ConvertCall.cpp +++ b/flang/lib/Lower/ConvertCall.cpp @@ -1283,13 +1283,19 @@ hlfir::Entity actual = arg->getOriginalActual(); mlir::Value valArg; - fir::ArgLoweringRule argRules = - fir::lowerIntrinsicArgumentAs(*argLowering, i); - if (!argRules.handleDynamicOptional && - argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired) - valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual); - else - valArg = actual.getBase(); + // if intrinsic handler has no lowering rules + if (!argLowering) { + valArg = hlfir::loadTrivialScalar(loc, builder, actual); + } + else { + fir::ArgLoweringRule argRules = + fir::lowerIntrinsicArgumentAs(*argLowering, i); + if (!argRules.handleDynamicOptional && + argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired) + valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual); + else + valArg = actual.getBase(); + } operands.emplace_back(valArg); } @@ -1354,6 +1360,28 @@ return {hlfir::EntityWithAttributes{transposeOp.getResult()}}; } + if (intrinsicName == "min" || intrinsicName == "max") { + bool argsAreCharacterType = true; + + for (std::optional arg : loweredActuals) + if (!arg->getOriginalActual().isCharacter()) { + argsAreCharacterType = false; + break; + } + + if (argsAreCharacterType) { + llvm::SmallVector operands = getOperandVector(loweredActuals); + assert(operands.size() >= 2); + + hlfir::CharExtremumPredicate pred = (intrinsicName == "min") ? + hlfir::CharExtremumPredicate::min : hlfir::CharExtremumPredicate::max; + hlfir::CharExtremumOp charExtremumOp = + builder.create(loc, pred, mlir::ValueRange{operands}); + //builder.create(loc, pred, mlir::ValueRange{operands[0], operands[1]}); + return {hlfir::EntityWithAttributes{charExtremumOp.getResult()}}; + } + } + // TODO add hlfir operations for other transformational intrinsics here // fallback to calling the intrinsic via fir.call diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -485,6 +485,48 @@ build(builder, result, resultType, strings, len); } +//===----------------------------------------------------------------------===// +// CharExtremumOp +//===----------------------------------------------------------------------===// + +mlir::LogicalResult hlfir::CharExtremumOp::verify() { + // there could probably be some refactoring between this verify() and ConcatOp's verify since they're the same... + if (getStrings().size() < 2) + return emitOpError("must be provided at least two string operands"); + unsigned kind = getCharacterKind(getResult().getType()); + for (auto string : getStrings()) + if (kind != getCharacterKind(string.getType())) + return emitOpError("strings must have the same KIND as the result type"); + return mlir::success(); +} + +void hlfir::CharExtremumOp::build(mlir::OpBuilder &builder, + mlir::OperationState &result, + hlfir::CharExtremumPredicate predicate, + mlir::ValueRange strings) { + + fir::CharacterType::LenType resultTypeLen = 0; + assert(!strings.empty() && "must contain operands"); + unsigned kind = getCharacterKind(strings[0].getType()); + for (auto string : strings) + if (auto cstLen = getCharacterLengthIfStatic(string.getType())) { + if (resultTypeLen == 0) + resultTypeLen = *cstLen; + else if (resultTypeLen > *cstLen) + resultTypeLen = *cstLen; + + } else { + resultTypeLen = fir::CharacterType::unknownLen(); + break; + } + auto resultType = hlfir::ExprType::get( + builder.getContext(), hlfir::ExprType::Shape{}, + fir::CharacterType::get(builder.getContext(), kind, resultTypeLen), + false); + + build(builder, result, resultType, predicate, strings); +} + //===----------------------------------------------------------------------===// // SetLengthOp //===----------------------------------------------------------------------===// @@ -887,4 +929,5 @@ } #define GET_OP_CLASSES +#include "flang/Optimizer/HLFIR/HLFIREnums.cpp.inc" #include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc" diff --git a/flang/test/HLFIR/char_extremum.fir b/flang/test/HLFIR/char_extremum.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/char_extremum.fir @@ -0,0 +1,130 @@ +// Test hlfir.char_extremum operation parse, verify (no errors), and unparse. + +// RUN: fir-opt %s | fir-opt | FileCheck %s + + +// variable check +func.func @char_extremum_min_var(%arg0: !fir.ref>, %arg1: !fir.ref>) { + %0 = hlfir.char_extremum min, %arg0, %arg1 : (!fir.ref>, !fir.ref>) -> !hlfir.expr> + return +} + +// CHECK-LABEL: func.func @char_extremum_min_var( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.ref>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]] : (!fir.ref>, !fir.ref>) -> !hlfir.expr> + +func.func @char_extremum_max_var(%arg0: !fir.ref>, %arg1: !fir.ref>) { +%0 = hlfir.char_extremum min, %arg0, %arg1 : (!fir.ref>, !fir.ref>) -> !hlfir.expr> +return +} + +// CHECK-LABEL: func.func @char_extremum_max_var( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.ref>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]] : (!fir.ref>, !fir.ref>) -> !hlfir.expr> + +func.func @char_extremum_min_var_variadic(%arg0: !fir.ref>, %arg1: !fir.ref>, %arg2: !fir.ref>) { + %0 = hlfir.char_extremum min, %arg0, %arg1, %arg2 : (!fir.ref>, !fir.ref>, !fir.ref>) -> !hlfir.expr> + return +} + +// CHECK-LABEL: func.func @char_extremum_min_var_variadic( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_2:.*]]: !fir.ref>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]], %[[VAL_2]] : (!fir.ref>, !fir.ref>, !fir.ref>) -> !hlfir.expr> + +func.func @char_extremum_max_var_variadic(%arg0: !fir.ref>, %arg1: !fir.ref>, %arg2: !fir.ref>) { + %0 = hlfir.char_extremum max, %arg0, %arg1, %arg2 : (!fir.ref>, !fir.ref>, !fir.ref>) -> !hlfir.expr> + return +} + +// CHECK-LABEL: func.func @char_extremum_max_var_variadic( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_2:.*]]: !fir.ref>) { +// CHECK: %{{.*}} = hlfir.char_extremum max, %[[VAL_0]], %[[VAL_1]], %[[VAL_2]] : (!fir.ref>, !fir.ref>, !fir.ref>) -> !hlfir.expr> + + +// boxchar check +func.func @char_extremum_min_boxchar(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>) { + %0 = hlfir.char_extremum min, %arg0, %arg1 : (!fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @char_extremum_min_boxchar( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.boxchar<1>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]] : (!fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + +func.func @char_extremum_max_boxchar(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>) { + %0 = hlfir.char_extremum max, %arg0, %arg1 : (!fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @char_extremum_max_boxchar( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.boxchar<1>) { +// CHECK: %{{.*}} = hlfir.char_extremum max, %[[VAL_0]], %[[VAL_1]] : (!fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + +func.func @char_extremum_min_boxchar_variadic(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>, %arg2: !fir.boxchar<1>, %arg3: !fir.boxchar<1>) { + %0 = hlfir.char_extremum min, %arg0, %arg1, %arg2, %arg3 : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @char_extremum_min_boxchar_variadic( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_2:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_3:[a-zA-Z0-9_]*]]: !fir.boxchar<1>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]], %[[VAL_2]], %[[VAL_3]] : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + +func.func @char_extremum_max_boxchar_variadic(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>, %arg2: !fir.boxchar<1>, %arg3: !fir.boxchar<1>) { + %0 = hlfir.char_extremum max, %arg0, %arg1, %arg2, %arg3 : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @char_extremum_max_boxchar_variadic( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_2:[a-zA-Z0-9_]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_3:[a-zA-Z0-9_]*]]: !fir.boxchar<1>) { +// CHECK: %{{.*}} = hlfir.char_extremum max, %[[VAL_0]], %[[VAL_1]], %[[VAL_2]], %[[VAL_3]] : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr> + + +//expr check +func.func @char_extremum_min_expr(%arg0: !hlfir.expr>, %arg1: !hlfir.expr>) { + %0 = hlfir.char_extremum min, %arg0, %arg1 : (!hlfir.expr>, !hlfir.expr>) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @char_extremum_min_expr( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !hlfir.expr>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]] : (!hlfir.expr>, !hlfir.expr>) -> !hlfir.expr> + +func.func @char_extremum_max_expr(%arg0: !hlfir.expr>, %arg1: !hlfir.expr>) { + %0 = hlfir.char_extremum max, %arg0, %arg1 : (!hlfir.expr>, !hlfir.expr>) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @char_extremum_max_expr( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !hlfir.expr>) { +// CHECK: %{{.*}} = hlfir.char_extremum max, %[[VAL_0]], %[[VAL_1]] : (!hlfir.expr>, !hlfir.expr>) -> !hlfir.expr> + +func.func @char_extremum_min_expr_variadic(%arg0: !hlfir.expr>, %arg1: !hlfir.expr>, %arg2: !hlfir.expr>) { + %0 = hlfir.char_extremum min, %arg0, %arg1, %arg2 : (!hlfir.expr>, !hlfir.expr>, !hlfir.expr>) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @char_extremum_min_expr_variadic( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_2:[a-zA-Z0-9_]*]]: !hlfir.expr>) { +// CHECK: %{{.*}} = hlfir.char_extremum min, %[[VAL_0]], %[[VAL_1]], %[[VAL_2]] : (!hlfir.expr>, !hlfir.expr>, !hlfir.expr>) -> !hlfir.expr> + +func.func @char_extremum_max_expr_variadic(%arg0: !hlfir.expr>, %arg1: !hlfir.expr>, %arg2: !hlfir.expr>) { + %0 = hlfir.char_extremum max, %arg0, %arg1, %arg2 : (!hlfir.expr>, !hlfir.expr>, !hlfir.expr>) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @char_extremum_max_expr_variadic( +// CHECK-SAME: %[[VAL_0:[a-zA-Z0-9_]*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_1:[a-zA-Z0-9_]*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_2:[a-zA-Z0-9_]*]]: !hlfir.expr>) { +// CHECK: %{{.*}} = hlfir.char_extremum max, %[[VAL_0]], %[[VAL_1]], %[[VAL_2]] : (!hlfir.expr>, !hlfir.expr>, !hlfir.expr>) -> !hlfir.expr> + diff --git a/flang/test/Lower/HLFIR/max.f03 b/flang/test/Lower/HLFIR/max.f03 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/HLFIR/max.f03 @@ -0,0 +1,16 @@ + +subroutine max_char(c1, c2, c3) + character(*) :: c1, c2, c3 + c1 = max(c2,c3) +end subroutine + +subroutine concat_2(c1, c2, c3) + character(*) :: c1(100) + character :: c2(100)*10, c3(100)*20 + c1(1) = max(c2(1),c3(1)) +end subroutine + +subroutine concat3(c1, c2, c3, c4) + character(*) :: c1, c2, c3, c4 + c1 = max(c2, c3, c4) +end subroutine \ No newline at end of file