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 @@ -236,6 +236,26 @@ let hasVerifier = 1; } +def hlfir_SetLengthOp : hlfir_Op<"set_length", []> { + let summary = "change the length of a character entity"; + let description = [{ + Change the length of character entity. This trims or pads the + character argument according to the new length. + }]; + + let arguments = (ins AnyScalarCharacterEntity:$string, + AnyIntegerType:$length); + + let results = (outs AnyScalarCharacterExpr); + + let assemblyFormat = [{ + $string `len` $length + attr-dict `:` functional-type(operands, results) + }]; + + let builders = [OpBuilder<(ins "mlir::Value":$string,"mlir::Value":$len)>]; +} + def hlfir_AssociateOp : hlfir_Op<"associate", [AttrSizedOperandSegments, DeclareOpInterfaceMethods]> { let summary = "Create a variable from an expression value"; diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -431,6 +431,9 @@ if (auto concat = expr.getDefiningOp()) { result.push_back(concat.getLength()); return; + } else if (auto concat = expr.getDefiningOp()) { + result.push_back(concat.getLength()); + return; } else if (auto asExpr = expr.getDefiningOp()) { hlfir::genLengthParameters(loc, builder, hlfir::Entity{asExpr.getVar()}, result); 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 @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "flang/Optimizer/HLFIR/HLFIROps.h" +#include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/DialectImplementation.h" @@ -400,6 +401,24 @@ build(builder, result, resultType, strings, len); } +//===----------------------------------------------------------------------===// +// SetLengthOp +//===----------------------------------------------------------------------===// + +void hlfir::SetLengthOp::build(mlir::OpBuilder &builder, + mlir::OperationState &result, mlir::Value string, + mlir::Value len) { + fir::CharacterType::LenType resultTypeLen = fir::CharacterType::unknownLen(); + if (auto cstLen = fir::getIntIfConstant(len)) + resultTypeLen = *cstLen; + unsigned kind = getCharacterKind(string.getType()); + auto resultType = hlfir::ExprType::get( + builder.getContext(), hlfir::ExprType::Shape{}, + fir::CharacterType::get(builder.getContext(), kind, resultTypeLen), + false); + build(builder, result, resultType, string, len); +} + //===----------------------------------------------------------------------===// // AssociateOp //===----------------------------------------------------------------------===// diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp --- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp @@ -228,6 +228,36 @@ } }; +struct SetLengthOpConversion + : public mlir::OpConversionPattern { + using mlir::OpConversionPattern::OpConversionPattern; + explicit SetLengthOpConversion(mlir::MLIRContext *ctx) + : mlir::OpConversionPattern{ctx} {} + mlir::LogicalResult + matchAndRewrite(hlfir::SetLengthOp setLength, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Location loc = setLength->getLoc(); + auto module = setLength->getParentOfType(); + fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module)); + // Create a temp with the new length. + hlfir::Entity string{getBufferizedExprStorage(adaptor.getString())}; + auto charType = hlfir::getFortranElementType(setLength.getType()); + llvm::StringRef tmpName{".tmp"}; + llvm::SmallVector lenParams{adaptor.getLength()}; + auto alloca = builder.createTemporary(loc, charType, tmpName, + /*shape=*/std::nullopt, lenParams); + auto declareOp = builder.create( + loc, alloca, tmpName, /*shape=*/mlir::Value{}, lenParams, + fir::FortranVariableFlagsAttr{}); + // Assign string value to the created temp. + builder.create(loc, string, declareOp.getBase()); + mlir::Value bufferizedExpr = + packageBufferizedExpr(loc, builder, alloca, false); + rewriter.replaceOp(setLength, bufferizedExpr); + return mlir::success(); + } +}; + struct AssociateOpConversion : public mlir::OpConversionPattern { using mlir::OpConversionPattern::OpConversionPattern; @@ -401,10 +431,11 @@ patterns.insert(context); + NoReassocOpConversion, SetLengthOpConversion>(context); mlir::ConversionTarget target(*context); target.addIllegalOp(); + hlfir::EndAssociateOp, hlfir::SetLengthOp, + hlfir::YieldElementOp>(); target.markUnknownOpDynamicallyLegal([](mlir::Operation *op) { return llvm::all_of( op->getResultTypes(), diff --git a/flang/test/HLFIR/set_length-codegen.fir b/flang/test/HLFIR/set_length-codegen.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/set_length-codegen.fir @@ -0,0 +1,33 @@ +// Test hlfir.set_length operation lowering to operations operating on memory. +// RUN: fir-opt %s -bufferize-hlfir | FileCheck %s + +func.func @test_cst_len(%str : !fir.boxchar<1>) { + %c10 = arith.constant 10 : index + %0 = hlfir.set_length %str len %c10 : (!fir.boxchar<1>, index) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @test_cst_len( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>) { +// CHECK: %[[VAL_1:.*]] = fir.alloca !fir.char<1,10> {bindc_name = ".tmp"} +// CHECK: %[[VAL_2:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_1]] typeparams %[[VAL_2]] {uniq_name = ".tmp"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) +// CHECK: hlfir.assign %[[VAL_0]] to %[[VAL_3]]#0 : !fir.boxchar<1>, !fir.ref> +// CHECK: %[[VAL_4:.*]] = arith.constant false +// CHECK: %[[VAL_5:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_6:.*]] = fir.insert_value %[[VAL_5]], %[[VAL_4]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_7:.*]] = fir.insert_value %[[VAL_6]], %[[VAL_1]], [0 : index] : (tuple>, i1>, !fir.ref>) -> tuple>, i1> + +func.func @test_dyn_len(%str : !fir.ref>, %len : index) { + %0 = hlfir.set_length %str len %len : (!fir.ref>, index) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @test_dyn_len( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:.*]]: index) { +// CHECK: %[[VAL_2:.*]] = fir.alloca !fir.char<1,?>(%[[VAL_1]] : index) {bindc_name = ".tmp"} +// CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[VAL_1]] {uniq_name = ".tmp"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: hlfir.assign %[[VAL_0]] to %[[VAL_3]]#0 : !fir.ref>, !fir.boxchar<1> +// CHECK: %[[VAL_4:.*]] = arith.constant false +// CHECK: %[[VAL_5:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_6:.*]] = fir.insert_value %[[VAL_5]], %[[VAL_4]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_7:.*]] = fir.insert_value %[[VAL_6]], %[[VAL_2]], [0 : index] : (tuple>, i1>, !fir.ref>) -> tuple>, i1> diff --git a/flang/test/HLFIR/set_length.fir b/flang/test/HLFIR/set_length.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/set_length.fir @@ -0,0 +1,30 @@ +// Test hlfir.set_length operation parse, verify (no errors), and unparse. +// RUN: fir-opt %s | fir-opt | FileCheck %s + +func.func @test_cst_len(%str : !fir.boxchar<1>) { + %c10 = arith.constant 10 : index + %0 = hlfir.set_length %str len %c10 : (!fir.boxchar<1>, index) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @test_cst_len( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>) { +// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_2:.*]] = hlfir.set_length %[[VAL_0]] len %[[VAL_1]] : (!fir.boxchar<1>, index) -> !hlfir.expr> + +func.func @test_dyn_len(%str : !fir.ref>, %len : index) { + %0 = hlfir.set_length %str len %len : (!fir.ref>, index) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @test_dyn_len( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:.*]]: index) { +// CHECK: %[[VAL_2:.*]] = hlfir.set_length %[[VAL_0]] len %[[VAL_1]] : (!fir.ref>, index) -> !hlfir.expr> + +func.func @test_from_expr(%str : !hlfir.expr>, %len : index) { + %0 = hlfir.set_length %str len %len : (!hlfir.expr>, index) -> !hlfir.expr> + return +} +// CHECK-LABEL: func.func @test_from_expr( +// CHECK-SAME: %[[VAL_0:.*]]: !hlfir.expr>, +// CHECK-SAME: %[[VAL_1:.*]]: index) { +// CHECK: %[[VAL_2:.*]] = hlfir.set_length %[[VAL_0]] len %[[VAL_1]] : (!hlfir.expr>, index) -> !hlfir.expr>