Skip to content

Commit bdf2709

Browse files
authored
[flang] Add hlfir.char_trim operation (llvm#156064)
Fortran character trim is currently lowered directly into a runtime call, which makes it more complex to simplify expressions using it. With this patch trim is first lowered into an hlfir.char_trim operation, that is only later transformed into a runtime call. This makes it easier to remove unnecessary calls to trim, as proposed in llvm#154593.
1 parent 698f39b commit bdf2709

File tree

7 files changed

+223
-69
lines changed

7 files changed

+223
-69
lines changed

flang/include/flang/Optimizer/HLFIR/HLFIROps.td

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,23 @@ def hlfir_CmpCharOp : hlfir_Op<"cmpchar",
375375
let hasVerifier = 1;
376376
}
377377

378+
def hlfir_CharTrimOp
379+
: hlfir_Op<
380+
"char_trim", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
381+
let summary = "trim character";
382+
let description = [{ Trim a character string. }];
383+
384+
let arguments = (ins AnyScalarCharacterEntity:$chr);
385+
386+
let results = (outs AnyScalarCharacterExpr);
387+
388+
let assemblyFormat = [{
389+
$chr attr-dict `:` functional-type(operands, results)
390+
}];
391+
392+
let builders = [OpBuilder<(ins "mlir::Value":$chr)>];
393+
}
394+
378395
def hlfir_AllOp : hlfir_Op<"all", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
379396
let summary = "ALL transformational intrinsic";
380397
let description = [{

flang/lib/Lower/HlfirIntrinsics.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ class HlfirCharExtremumLowering : public HlfirTransformationalIntrinsic {
159159
hlfir::CharExtremumPredicate pred;
160160
};
161161

162+
class HlfirCharTrimLowering : public HlfirTransformationalIntrinsic {
163+
public:
164+
HlfirCharTrimLowering(fir::FirOpBuilder &builder, mlir::Location loc)
165+
: HlfirTransformationalIntrinsic(builder, loc) {}
166+
167+
protected:
168+
mlir::Value
169+
lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,
170+
const fir::IntrinsicArgumentLoweringRules *argLowering,
171+
mlir::Type stmtResultType) override;
172+
};
173+
162174
class HlfirCShiftLowering : public HlfirTransformationalIntrinsic {
163175
public:
164176
using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;
@@ -421,6 +433,15 @@ mlir::Value HlfirCharExtremumLowering::lowerImpl(
421433
return createOp<hlfir::CharExtremumOp>(pred, mlir::ValueRange{operands});
422434
}
423435

436+
mlir::Value HlfirCharTrimLowering::lowerImpl(
437+
const Fortran::lower::PreparedActualArguments &loweredActuals,
438+
const fir::IntrinsicArgumentLoweringRules *argLowering,
439+
mlir::Type stmtResultType) {
440+
auto operands = getOperandVector(loweredActuals, argLowering);
441+
assert(operands.size() == 1);
442+
return createOp<hlfir::CharTrimOp>(operands[0]);
443+
}
444+
424445
mlir::Value HlfirCShiftLowering::lowerImpl(
425446
const Fortran::lower::PreparedActualArguments &loweredActuals,
426447
const fir::IntrinsicArgumentLoweringRules *argLowering,
@@ -555,6 +576,9 @@ std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
555576
return HlfirCharExtremumLowering{builder, loc,
556577
hlfir::CharExtremumPredicate::max}
557578
.lower(loweredActuals, argLowering, stmtResultType);
579+
if (name == "trim")
580+
return HlfirCharTrimLowering{builder, loc}.lower(
581+
loweredActuals, argLowering, stmtResultType);
558582
}
559583
return std::nullopt;
560584
}

flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,28 @@ void hlfir::CmpCharOp::getEffects(
855855
getIntrinsicEffects(getOperation(), effects);
856856
}
857857

858+
//===----------------------------------------------------------------------===//
859+
// CharTrimOp
860+
//===----------------------------------------------------------------------===//
861+
862+
void hlfir::CharTrimOp::build(mlir::OpBuilder &builder,
863+
mlir::OperationState &result, mlir::Value chr) {
864+
unsigned kind = getCharacterKind(chr.getType());
865+
auto resultType = hlfir::ExprType::get(
866+
builder.getContext(), hlfir::ExprType::Shape{},
867+
fir::CharacterType::get(builder.getContext(), kind,
868+
fir::CharacterType::unknownLen()),
869+
/*polymorphic=*/false);
870+
build(builder, result, resultType, chr);
871+
}
872+
873+
void hlfir::CharTrimOp::getEffects(
874+
llvm::SmallVectorImpl<
875+
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
876+
&effects) {
877+
getIntrinsicEffects(getOperation(), effects);
878+
}
879+
858880
//===----------------------------------------------------------------------===//
859881
// NumericalReductionOp
860882
//===----------------------------------------------------------------------===//

flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -585,21 +585,49 @@ class CmpCharOpConversion : public HlfirIntrinsicConversion<hlfir::CmpCharOp> {
585585
}
586586
};
587587

588+
class CharTrimOpConversion
589+
: public HlfirIntrinsicConversion<hlfir::CharTrimOp> {
590+
using HlfirIntrinsicConversion<hlfir::CharTrimOp>::HlfirIntrinsicConversion;
591+
592+
llvm::LogicalResult
593+
matchAndRewrite(hlfir::CharTrimOp trim,
594+
mlir::PatternRewriter &rewriter) const override {
595+
fir::FirOpBuilder builder{rewriter, trim.getOperation()};
596+
const mlir::Location &loc = trim->getLoc();
597+
598+
llvm::SmallVector<IntrinsicArgument, 1> inArgs;
599+
mlir::Value chr = trim.getChr();
600+
inArgs.push_back({chr, chr.getType()});
601+
602+
auto *argLowering = fir::getIntrinsicArgumentLowering("trim");
603+
llvm::SmallVector<fir::ExtendedValue, 1> args =
604+
lowerArguments(trim, inArgs, rewriter, argLowering);
605+
606+
mlir::Type resultType = hlfir::getFortranElementType(trim.getType());
607+
608+
auto [resultExv, mustBeFreed] =
609+
fir::genIntrinsicCall(builder, loc, "trim", resultType, args);
610+
611+
processReturnValue(trim, resultExv, mustBeFreed, builder, rewriter);
612+
return mlir::success();
613+
}
614+
};
615+
588616
class LowerHLFIRIntrinsics
589617
: public hlfir::impl::LowerHLFIRIntrinsicsBase<LowerHLFIRIntrinsics> {
590618
public:
591619
void runOnOperation() override {
592620
mlir::ModuleOp module = this->getOperation();
593621
mlir::MLIRContext *context = &getContext();
594622
mlir::RewritePatternSet patterns(context);
595-
patterns.insert<MatmulOpConversion, MatmulTransposeOpConversion,
596-
AllOpConversion, AnyOpConversion, SumOpConversion,
597-
ProductOpConversion, TransposeOpConversion,
598-
CountOpConversion, DotProductOpConversion,
599-
MaxvalOpConversion, MinvalOpConversion, MinlocOpConversion,
600-
MaxlocOpConversion, ArrayShiftOpConversion<hlfir::CShiftOp>,
601-
ArrayShiftOpConversion<hlfir::EOShiftOp>,
602-
ReshapeOpConversion, CmpCharOpConversion>(context);
623+
patterns.insert<
624+
MatmulOpConversion, MatmulTransposeOpConversion, AllOpConversion,
625+
AnyOpConversion, SumOpConversion, ProductOpConversion,
626+
TransposeOpConversion, CountOpConversion, DotProductOpConversion,
627+
MaxvalOpConversion, MinvalOpConversion, MinlocOpConversion,
628+
MaxlocOpConversion, ArrayShiftOpConversion<hlfir::CShiftOp>,
629+
ArrayShiftOpConversion<hlfir::EOShiftOp>, ReshapeOpConversion,
630+
CmpCharOpConversion, CharTrimOpConversion>(context);
603631

604632
// While conceptually this pass is performing dialect conversion, we use
605633
// pattern rewrites here instead of dialect conversion because this pass

flang/test/HLFIR/trim.fir

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: fir-opt --lower-hlfir-intrinsics %s | FileCheck %s
2+
3+
// CHECK-LABEL: func.func @_QPtrim_test(
4+
// CHECK-SAME: %[[ARG0:.*]]: !fir.boxchar<1> {fir.bindc_name = "c"}) {
5+
// CHECK: %[[VAL_0:.*]] = arith.constant true
6+
// CHECK: %[[VAL_2:.*]] = arith.constant 0 : index
7+
// CHECK: %[[VAL_3:.*]] = arith.constant 8 : index
8+
// CHECK: %[[VAL_4:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,?>>>
9+
// CHECK: %[[VAL_5:.*]] = fir.dummy_scope : !fir.dscope
10+
// CHECK: %[[VAL_6:.*]]:2 = fir.unboxchar %[[ARG0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
11+
// CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_6]]#0 typeparams %[[VAL_6]]#1 dummy_scope %[[VAL_5]] {uniq_name = "_QFtrim_testEc"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
12+
// CHECK: %[[VAL_8:.*]] = fir.alloca !fir.char<1,8> {bindc_name = "tc", uniq_name = "_QFtrim_testEtc"}
13+
// CHECK: %[[VAL_9:.*]]:2 = hlfir.declare %[[VAL_8]] typeparams %[[VAL_3]] {uniq_name = "_QFtrim_testEtc"} : (!fir.ref<!fir.char<1,8>>, index) -> (!fir.ref<!fir.char<1,8>>, !fir.ref<!fir.char<1,8>>)
14+
// CHECK: %[[VAL_10:.*]] = fir.embox %[[VAL_7]]#1 typeparams %[[VAL_6]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
15+
// CHECK: %[[VAL_11:.*]] = fir.zero_bits !fir.heap<!fir.char<1,?>>
16+
// CHECK: %[[VAL_12:.*]] = fir.embox %[[VAL_11]] typeparams %[[VAL_2]] : (!fir.heap<!fir.char<1,?>>, index) -> !fir.box<!fir.heap<!fir.char<1,?>>>
17+
// CHECK: fir.store %[[VAL_12]] to %[[VAL_4]] : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
18+
// CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_4]] : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>) -> !fir.ref<!fir.box<none>>
19+
// CHECK: %[[VAL_15:.*]] = fir.convert %[[VAL_10]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
20+
// CHECK: fir.call @_FortranATrim(%[[VAL_14]], %[[VAL_15]], %{{.*}}, %{{.*}}) : (!fir.ref<!fir.box<none>>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
21+
// CHECK: %[[VAL_17:.*]] = fir.load %[[VAL_4]] : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
22+
// CHECK: %[[VAL_18:.*]] = fir.box_elesize %[[VAL_17]] : (!fir.box<!fir.heap<!fir.char<1,?>>>) -> index
23+
// CHECK: %[[VAL_19:.*]] = fir.box_addr %[[VAL_17]] : (!fir.box<!fir.heap<!fir.char<1,?>>>) -> !fir.heap<!fir.char<1,?>>
24+
// CHECK: %[[VAL_20:.*]]:2 = hlfir.declare %[[VAL_19]] typeparams %[[VAL_18]] {uniq_name = ".tmp.intrinsic_result"} : (!fir.heap<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.heap<!fir.char<1,?>>)
25+
// CHECK: %[[VAL_21:.*]] = hlfir.as_expr %[[VAL_20]]#0 move %[[VAL_0]] : (!fir.boxchar<1>, i1) -> !hlfir.expr<!fir.char<1,?>>
26+
// CHECK: hlfir.assign %[[VAL_21]] to %[[VAL_9]]#0 : !hlfir.expr<!fir.char<1,?>>, !fir.ref<!fir.char<1,8>>
27+
// CHECK: hlfir.destroy %[[VAL_21]] : !hlfir.expr<!fir.char<1,?>>
28+
// CHECK: return
29+
// CHECK: }
30+
31+
func.func @_QPtrim_test(%arg0: !fir.boxchar<1> {fir.bindc_name = "c"}) {
32+
%0 = fir.dummy_scope : !fir.dscope
33+
%1:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
34+
%2:2 = hlfir.declare %1#0 typeparams %1#1 dummy_scope %0 {uniq_name = "_QFtrim_testEc"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
35+
%c8 = arith.constant 8 : index
36+
%3 = fir.alloca !fir.char<1,8> {bindc_name = "tc", uniq_name = "_QFtrim_testEtc"}
37+
%4:2 = hlfir.declare %3 typeparams %c8 {uniq_name = "_QFtrim_testEtc"} : (!fir.ref<!fir.char<1,8>>, index) -> (!fir.ref<!fir.char<1,8>>, !fir.ref<!fir.char<1,8>>)
38+
%5 = hlfir.char_trim %2#0 : (!fir.boxchar<1>) -> !hlfir.expr<!fir.char<1,?>>
39+
hlfir.assign %5 to %4#0 : !hlfir.expr<!fir.char<1,?>>, !fir.ref<!fir.char<1,8>>
40+
hlfir.destroy %5 : !hlfir.expr<!fir.char<1,?>>
41+
return
42+
}

flang/test/Lower/HLFIR/trim.f90

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
2+
3+
! CHECK-LABEL: func.func @_QPtrim_test(
4+
! CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1> {fir.bindc_name = "c"}) {
5+
! CHECK: %[[VAL_1:.*]] = fir.dummy_scope : !fir.dscope
6+
! CHECK-NEXT: %[[VAL_2:.*]]:2 = fir.unboxchar %[[VAL_0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
7+
! CHECK-NEXT: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]]#0 typeparams %[[VAL_2]]#1 dummy_scope %[[VAL_1]] {uniq_name = "_QFtrim_testEc"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
8+
! CHECK-NEXT: %[[VAL_4:.*]] = arith.constant 8 : index
9+
! CHECK-NEXT: %[[VAL_5:.*]] = fir.alloca !fir.char<1,8> {bindc_name = "tc", uniq_name = "_QFtrim_testEtc"}
10+
! CHECK-NEXT: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]] typeparams %[[VAL_4]] {uniq_name = "_QFtrim_testEtc"} : (!fir.ref<!fir.char<1,8>>, index) -> (!fir.ref<!fir.char<1,8>>, !fir.ref<!fir.char<1,8>>)
11+
! CHECK-NEXT: %[[VAL_7:.*]] = hlfir.char_trim %[[VAL_3]]#0 : (!fir.boxchar<1>) -> !hlfir.expr<!fir.char<1,?>>
12+
! CHECK-NEXT: hlfir.assign %[[VAL_7]] to %[[VAL_6]]#0 : !hlfir.expr<!fir.char<1,?>>, !fir.ref<!fir.char<1,8>>
13+
! CHECK-NEXT: hlfir.destroy %[[VAL_7]] : !hlfir.expr<!fir.char<1,?>>
14+
! CHECK-NEXT: return
15+
! CHECK-NEXT: }
16+
subroutine trim_test(c)
17+
character(*) :: c
18+
character(8) :: tc
19+
20+
tc = trim(c)
21+
end subroutine
22+
23+
! Test trim with fixed length character.
24+
! The length of the returned character type must be unknown.
25+
! CHECK-LABEL: func.func @_QPtrim_test2(
26+
! CHECK: hlfir.char_trim %{{.*}}#0 : (!fir.ref<!fir.char<1,8>>) -> !hlfir.expr<!fir.char<1,?>>
27+
subroutine trim_test2(c)
28+
character(8) :: c
29+
character(8) :: tc
30+
31+
tc = trim(c)
32+
end subroutine

0 commit comments

Comments
 (0)