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 @@ -20,6 +20,8 @@ namespace hlfir { /// Is this a type that can be used for an HLFIR variable ? bool isFortranVariableType(mlir::Type); +bool isFortranScalarCharacterType(mlir::Type); +bool isFortranScalarCharacterExprType(mlir::Type); } // namespace hlfir #include "flang/Optimizer/HLFIR/HLFIRDialect.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 @@ -63,7 +63,8 @@ let extraClassDeclaration = [{ using Shape = llvm::SmallVector; mlir::Type getEleTy() const {return getElementType();} - bool isArray() const { return !getShape().empty(); } + bool isScalar() const { return getShape().empty(); } + bool isArray() const { return !isScalar(); } bool isPolymorphic() const { return getPolymorphic(); } }]; @@ -85,5 +86,14 @@ def AnyFortranEntity : TypeConstraint, "any Fortran value or variable type">; +def IsFortranScalarCharacterPred + : CPred<"::hlfir::isFortranScalarCharacterType($_self)">; +def AnyScalarCharacterEntity : Type; + +def IsFortranScalarCharacterExprPred + : CPred<"::hlfir::isFortranScalarCharacterExprType($_self)">; +def AnyScalarCharacterExpr : Type; #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 @@ -208,4 +208,23 @@ let hasVerifier = 1; } +def hlfir_ConcatOp : hlfir_Op<"concat", []> { + let summary = "concatenate characters"; + let description = [{ + Concatenate two or more character strings of a same character kind. + }]; + + let arguments = (ins Variadic:$strings, + AnyIntegerType:$length); + + let results = (outs AnyScalarCharacterExpr); + + let assemblyFormat = [{ + $strings `len` $length + attr-dict `:` functional-type(operands, results) + }]; + + let hasVerifier = 1; +} + #endif // FORTRAN_DIALECT_HLFIR_OPS diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp --- a/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp @@ -85,3 +85,17 @@ .Case([](auto) { return true; }) .Default([](mlir::Type) { return false; }); } + +bool hlfir::isFortranScalarCharacterType(mlir::Type type) { + return isFortranScalarCharacterExprType(type) || + type.isa() || + fir::unwrapPassByRefType(fir::unwrapRefType(type)) + .isa(); +} + +bool hlfir::isFortranScalarCharacterExprType(mlir::Type type) { + if (auto exprType = type.dyn_cast()) + return exprType.isScalar() && + exprType.getElementType().isa(); + return false; +} 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 @@ -337,5 +337,22 @@ return mlir::success(); } +//===----------------------------------------------------------------------===// +// ConcatOp +//===----------------------------------------------------------------------===// + +mlir::LogicalResult hlfir::ConcatOp::verify() { + if (getStrings().size() < 2) + return emitOpError("must be provided at least two string operands"); + auto exprTy = getResult().getType().cast(); + unsigned kind = exprTy.getElementType().cast().getFKind(); + for (auto string : getStrings()) + if (kind != getFortranElementType(string.getType()) + .cast() + .getFKind()) + return emitOpError("strings must have the same KIND as the result type"); + return mlir::success(); +} + #define GET_OP_CLASSES #include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc" diff --git a/flang/test/HLFIR/concat.fir b/flang/test/HLFIR/concat.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/concat.fir @@ -0,0 +1,62 @@ +// Test hlfir.concat operation parse, verify (no errors), and unparse. + +// RUN: fir-opt %s | fir-opt | FileCheck %s + +func.func @concat_var(%arg0: !fir.ref>, %arg1: !fir.ref>) { + %c30 = arith.constant 30 : index + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.ref>, !fir.ref>, index) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @concat_var( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref>) { +// CHECK: %[[VAL_2:.*]] = arith.constant 30 : index +// CHECK: %[[VAL_3:.*]] = hlfir.concat %[[VAL_0]], %[[VAL_1]] len %[[VAL_2]] : (!fir.ref>, !fir.ref>, index) -> !hlfir.expr> + + +func.func @concat_boxchar(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>) { + %c30 = arith.constant 30 : index + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.boxchar<1>, !fir.boxchar<1>, index) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @concat_boxchar( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.boxchar<1>) { +// CHECK: %[[VAL_2:.*]] = arith.constant 30 : index +// CHECK: %[[VAL_3:.*]] = hlfir.concat %[[VAL_0]], %[[VAL_1]] len %[[VAL_2]] : (!fir.boxchar<1>, !fir.boxchar<1>, index) -> !hlfir.expr> + + +func.func @concat_boxchar_kind2(%arg0: !fir.boxchar<2>, %arg1: !fir.boxchar<2>) { + %c30 = arith.constant 30 : index + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.boxchar<2>, !fir.boxchar<2>, index) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @concat_boxchar_kind2( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<2>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.boxchar<2>) { +// CHECK: %[[VAL_2:.*]] = arith.constant 30 : index +// CHECK: %[[VAL_3:.*]] = hlfir.concat %[[VAL_0]], %[[VAL_1]] len %[[VAL_2]] : (!fir.boxchar<2>, !fir.boxchar<2>, index) -> !hlfir.expr> + + +func.func @concat_expr(%arg0: !hlfir.expr>, %arg1: !hlfir.expr>) { + %c30 = arith.constant 30 : index + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!hlfir.expr>, !hlfir.expr>, index) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @concat_expr( +// CHECK-SAME: %[[VAL_0:.*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_1:.*]]: !hlfir.expr>) { +// CHECK: %[[VAL_2:.*]] = arith.constant 30 : index +// CHECK: %[[VAL_3:.*]] = hlfir.concat %[[VAL_0]], %[[VAL_1]] len %[[VAL_2]] : (!hlfir.expr>, !hlfir.expr>, index) -> !hlfir.expr> + + +func.func @concat_several_args(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>) { + %c30 = arith.constant 30 : index + %0 = hlfir.concat %arg0, %arg1, %arg1 len %c30 : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>, index) -> (!hlfir.expr>) + return +} +// CHECK-LABEL: func.func @concat_several_args( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.boxchar<1>) { +// CHECK: %[[VAL_2:.*]] = arith.constant 30 : index +// CHECK: %[[VAL_3:.*]] = hlfir.concat %[[VAL_0]], %[[VAL_1]], %[[VAL_1]] len %[[VAL_2]] : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>, index) -> !hlfir.expr> diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir --- a/flang/test/HLFIR/invalid.fir +++ b/flang/test/HLFIR/invalid.fir @@ -255,3 +255,43 @@ %0 = hlfir.designate %arg0(%c1) typeparams %c1 : (!fir.box>, index, index) -> !fir.ref return } + +// ----- +func.func @bad_concat(%arg0: !fir.ref>, %arg1: !fir.ref>) { + %c30 = arith.constant 30 : index + // expected-error@+1 {{'hlfir.concat' op result #0 must be any character scalar expression type, but got '!fir.ref>'}} + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.ref>, !fir.ref>, index) -> (!fir.ref>) + return +} + +// ----- +func.func @bad_concat_2(%arg0: !fir.ref>>, %arg1: !fir.ref>>) { + %c30 = arith.constant 30 : index + // expected-error@+1 {{'hlfir.concat' op operand #0 must be any character scalar type, but got '!fir.ref>>'}} + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.ref>>, !fir.ref>>, index) -> (!hlfir.expr<100x!fir.char<1,30>>) + return +} + +// ----- +func.func @bad_concat_3(%arg0: !fir.ref>, %arg1: !fir.ref) { + %c30 = arith.constant 30 : index + // expected-error@+1 {{'hlfir.concat' op operand #1 must be any character scalar type, but got '!fir.ref'}} + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.ref>, !fir.ref, index) -> (!hlfir.expr>) + return +} + +// ----- +func.func @bad_concat_4(%arg0: !fir.ref>, %arg1: !fir.ref>) { + %c30 = arith.constant 30 : index + // expected-error@+1 {{'hlfir.concat' op strings must have the same KIND as the result type}} + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.ref>, !fir.ref>, index) -> (!hlfir.expr>) + return +} + +// ----- +func.func @bad_concat_4(%arg0: !fir.ref>) { + %c30 = arith.constant 30 : index + // expected-error@+1 {{'hlfir.concat' op must be provided at least two string operands}} + %0 = hlfir.concat %arg0 len %c30 : (!fir.ref>, index) -> (!hlfir.expr>) + return +}