This is an archive of the discontinued LLVM Phabricator instance.

[flang][hlfir] initial work towards hlfir.char_extremum op
DraftPublic

Authored by cabreraam on Feb 4 2023, 2:11 PM.
This is a draft revision that has not yet been submitted for review.

Details

Summary

My initial work towards an hlfir.char_extremum op.

@jeanPerier, I'm using these 3 commits:

as a foundation for this work.
I still need to add tests like in your flang/test/HLFIR/concat.fir, but once I do that, I'll start working on:

  • translating expr --> hlfir
  • char_extremum codegen

What do you think so far? An okay direction so far? Anything I should be aware of going forward?


2023-05-02
Added some preliminary support for code-gen.
Feedback appreciated!

Relevant files:
+ BufferizeHLFIR.cpp
+ Character.cpp
+ Character.h
+ char_extremum-bufferization.fir

Diff Detail

Event Timeline

cabreraam created this revision.Feb 4 2023, 2:11 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald Transcript
cabreraam edited the summary of this revision. (Show Details)Feb 4 2023, 2:16 PM
cabreraam added a reviewer: jeanPerier.
cabreraam edited subscribers, added: jeanPerier; removed: mehdi_amini, sunshaoce.
cabreraam edited the summary of this revision. (Show Details)Feb 4 2023, 2:17 PM
cabreraam added inline comments.Feb 4 2023, 2:20 PM
flang/include/flang/Optimizer/HLFIR/HLFIROps.td
325

this should not return index. this should return the minimum/maximum string. I'll fix that later

332

maybe this should be AnyScalarCharacterEntity again

Also need to run clang-format. Will eventually do that.

cabreraam updated this revision to Diff 508468.Mar 26 2023, 8:44 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
cabreraam updated this revision to Diff 508470.Mar 26 2023, 8:48 PM
  • [flang][hlfir] remove some fluff
This comment was removed by cabreraam.
cabreraam added inline comments.Mar 26 2023, 8:56 PM
flang/lib/Lower/ConvertCall.cpp
1509

This is where the logic for variadic arguments should go.

@jeanPerier what's a good way to initialize a mlir::ValueRange without assuming the size of operands?

This looks like a great draft to me.

flang/lib/Lower/ConvertCall.cpp
1502

You likely need to only fall here for character min/max (missing condition on the result or operand type).

1509

Doesn't passing operands directly works? I believe an mlir::ValueRange can be implicitely constructed from an llvm::SmallVector<mlir::Value>.

flang/test/Lower/HLFIR/max.f03
16 ↗(On Diff #508470)

nit: it seems the file is missing a new line here.

cabreraam added inline comments.Mar 27 2023, 12:57 PM
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
566

Need to address this! This is fine if predicate == hlfir::CharExtremumPredicate::min, but we want the opposite if the predicate is min.

cabreraam updated this revision to Diff 508814.Mar 27 2023, 3:20 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
cabreraam marked 2 inline comments as done.Mar 27 2023, 3:24 PM

I implemented your suggestions! I should be finished editing the test with all of the FileCheck stuff by tomorrow.

This is what the output of bbc max.f03 looks like:

mlir-asm-printer: Verifying operation: func.func
func.func @_QPconcat3(%arg0: !fir.boxchar<1> {fir.bindc_name = "c1"}, %arg1: !fir.boxchar<1> {fir.bindc_name = "c2"}, %arg2: !fir.boxchar<1> {fir.bindc_name = "c3"}, %arg3: !fir.boxchar<1> {fir.bindc_name = "c4"}) {
  %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
  %1:2 = hlfir.declare %0#0 typeparams %0#1 {uniq_name = "_QFconcat3Ec1"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
  %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
  %3:2 = hlfir.declare %2#0 typeparams %2#1 {uniq_name = "_QFconcat3Ec2"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
  %4:2 = fir.unboxchar %arg2 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
  %5:2 = hlfir.declare %4#0 typeparams %4#1 {uniq_name = "_QFconcat3Ec3"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
  %6:2 = fir.unboxchar %arg3 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
  %7:2 = hlfir.declare %6#0 typeparams %6#1 {uniq_name = "_QFconcat3Ec4"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
  %8 = hlfir.char_extremum max, %3#0, %5#0, %7#0 : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr<!fir.char<1,?>>
  hlfir.assign %8 to %1#0 : !hlfir.expr<!fir.char<1,?>>, !fir.boxchar<1>
  hlfir.destroy %8 : !hlfir.expr<!fir.char<1,?>>
  return
}
ImplicitTypeIDRegistry::lookupOrInsert(Fortran::lower::VerifierPass)
ImplicitTypeIDRegistry::lookupOrInsert(mlir::detail::OpToOpPassAdaptor)
ImplicitTypeIDRegistry::lookupOrInsert(mlir::detail::PreservedAnalyses::AllAnalysesType)
mlir-asm-printer: Verifying operation: builtin.module
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
  func.func @_QPmax_char(%arg0: !fir.boxchar<1> {fir.bindc_name = "c1"}, %arg1: !fir.boxchar<1> {fir.bindc_name = "c2"}, %arg2: !fir.boxchar<1> {fir.bindc_name = "c3"}) {
    %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %1:2 = hlfir.declare %0#0 typeparams %0#1 {uniq_name = "_QFmax_charEc1"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %3:2 = hlfir.declare %2#0 typeparams %2#1 {uniq_name = "_QFmax_charEc2"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %4:2 = fir.unboxchar %arg2 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %5:2 = hlfir.declare %4#0 typeparams %4#1 {uniq_name = "_QFmax_charEc3"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %6 = hlfir.char_extremum max, %3#0, %5#0 : (!fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr<!fir.char<1,?>>
    hlfir.assign %6 to %1#0 : !hlfir.expr<!fir.char<1,?>>, !fir.boxchar<1>
    hlfir.destroy %6 : !hlfir.expr<!fir.char<1,?>>
    return
  }
  func.func @_QPconcat_2(%arg0: !fir.boxchar<1> {fir.bindc_name = "c1"}, %arg1: !fir.boxchar<1> {fir.bindc_name = "c2"}, %arg2: !fir.boxchar<1> {fir.bindc_name = "c3"}) {
    %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %1 = fir.convert %0#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.array<100x!fir.char<1,?>>>
    %c100 = arith.constant 100 : index
    %2 = fir.shape %c100 : (index) -> !fir.shape<1>
    %3:2 = hlfir.declare %1(%2) typeparams %0#1 {uniq_name = "_QFconcat_2Ec1"} : (!fir.ref<!fir.array<100x!fir.char<1,?>>>, !fir.shape<1>, index) -> (!fir.box<!fir.array<100x!fir.char<1,?>>>, !fir.ref<!fir.array<100x!fir.char<1,?>>>)
    %4:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %c10 = arith.constant 10 : index
    %5 = fir.convert %4#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.array<100x!fir.char<1,10>>>
    %c100_0 = arith.constant 100 : index
    %6 = fir.shape %c100_0 : (index) -> !fir.shape<1>
    %7:2 = hlfir.declare %5(%6) typeparams %c10 {uniq_name = "_QFconcat_2Ec2"} : (!fir.ref<!fir.array<100x!fir.char<1,10>>>, !fir.shape<1>, index) -> (!fir.ref<!fir.array<100x!fir.char<1,10>>>, !fir.ref<!fir.array<100x!fir.char<1,10>>>)
    %8:2 = fir.unboxchar %arg2 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %c20 = arith.constant 20 : index
    %9 = fir.convert %8#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.array<100x!fir.char<1,20>>>
    %c100_1 = arith.constant 100 : index
    %10 = fir.shape %c100_1 : (index) -> !fir.shape<1>
    %11:2 = hlfir.declare %9(%10) typeparams %c20 {uniq_name = "_QFconcat_2Ec3"} : (!fir.ref<!fir.array<100x!fir.char<1,20>>>, !fir.shape<1>, index) -> (!fir.ref<!fir.array<100x!fir.char<1,20>>>, !fir.ref<!fir.array<100x!fir.char<1,20>>>)
    %c1 = arith.constant 1 : index
    %12 = hlfir.designate %7#0 (%c1)  typeparams %c10 : (!fir.ref<!fir.array<100x!fir.char<1,10>>>, index, index) -> !fir.ref<!fir.char<1,10>>
    %c1_2 = arith.constant 1 : index
    %13 = hlfir.designate %11#0 (%c1_2)  typeparams %c20 : (!fir.ref<!fir.array<100x!fir.char<1,20>>>, index, index) -> !fir.ref<!fir.char<1,20>>
    %14 = hlfir.char_extremum max, %12, %13 : (!fir.ref<!fir.char<1,10>>, !fir.ref<!fir.char<1,20>>) -> !hlfir.expr<!fir.char<1,10>>
    %c1_3 = arith.constant 1 : index
    %15 = hlfir.designate %3#0 (%c1_3)  typeparams %0#1 : (!fir.box<!fir.array<100x!fir.char<1,?>>>, index, index) -> !fir.boxchar<1>
    hlfir.assign %14 to %15 : !hlfir.expr<!fir.char<1,10>>, !fir.boxchar<1>
    hlfir.destroy %14 : !hlfir.expr<!fir.char<1,10>>
    return
  }
  func.func @_QPconcat3(%arg0: !fir.boxchar<1> {fir.bindc_name = "c1"}, %arg1: !fir.boxchar<1> {fir.bindc_name = "c2"}, %arg2: !fir.boxchar<1> {fir.bindc_name = "c3"}, %arg3: !fir.boxchar<1> {fir.bindc_name = "c4"}) {
    %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %1:2 = hlfir.declare %0#0 typeparams %0#1 {uniq_name = "_QFconcat3Ec1"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %3:2 = hlfir.declare %2#0 typeparams %2#1 {uniq_name = "_QFconcat3Ec2"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %4:2 = fir.unboxchar %arg2 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %5:2 = hlfir.declare %4#0 typeparams %4#1 {uniq_name = "_QFconcat3Ec3"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %6:2 = fir.unboxchar %arg3 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
    %7:2 = hlfir.declare %6#0 typeparams %6#1 {uniq_name = "_QFconcat3Ec4"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
    %8 = hlfir.char_extremum max, %3#0, %5#0, %7#0 : (!fir.boxchar<1>, !fir.boxchar<1>, !fir.boxchar<1>) -> !hlfir.expr<!fir.char<1,?>>
    hlfir.assign %8 to %1#0 : !hlfir.expr<!fir.char<1,?>>, !fir.boxchar<1>
    hlfir.destroy %8 : !hlfir.expr<!fir.char<1,?>>
    return
  }
}
flang/lib/Lower/ConvertCall.cpp
1509

Ope, you were right. That worked!

cabreraam marked an inline comment as done and an inline comment as not done.Mar 27 2023, 8:59 PM
jeanPerier added inline comments.Mar 28 2023, 1:13 AM
flang/lib/Lower/ConvertCall.cpp
1487–1493

That is a bit overkill (to loop through the arguments), looking at the first is sufficient. But better yet, you should just be able to do if ((intrinsicName == "min" || intrinsicName == "max") && hlfir::getFortranElementType(callContext.resultType.value()).isa<fir::CharacterType>())

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

Actually, the condition is always the reverse: the result length is always the length of the longest operand (for both min and max). You may just want to use std::max for more clarity:

if (auto cstLen = getCharacterLengthIfStatic(string.getType())) {
  resultTypeLen = std::max(resultTypeLen, *cstLen);
} ...
cabreraam updated this revision to Diff 509165.Mar 28 2023, 4:17 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
cabreraam updated this revision to Diff 509166.Mar 28 2023, 4:19 PM
  • clang-format
cabreraam marked 2 inline comments as done.Mar 28 2023, 4:20 PM

Addressed your most recent feedback and added another test!

Looks good

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

Nit: character length are always positive (character(n) is sanitized to character(max(0,n))) in lowering, so you do not need the if (resultTypeLen == 0) case, it is the same as std::max(resultTypeLen, *cstLen); given *cstLen >=0.
An even if we failed to sanitized the incoming operand lengths, it is safer to return a value that is guaranteed to be positive here.

cabreraam updated this revision to Diff 509396.Mar 29 2023, 9:22 AM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
  • clang-format
  • [flang][hlfir] addressing some of Jean's nits
cabreraam updated this revision to Diff 518804.May 2 2023, 11:04 AM

Updating D143326: [flang][hlfir] initial work towards hlfir.char_extremum op

cabreraam edited the summary of this revision. (Show Details)May 2 2023, 11:05 AM
cabreraam added reviewers: tblah, jacob-crawley.
cabreraam edited the summary of this revision. (Show Details)May 2 2023, 11:08 AM
cabreraam edited the summary of this revision. (Show Details)
cabreraam marked an inline comment as done.May 2 2023, 1:17 PM
cabreraam updated this revision to Diff 520894.May 9 2023, 8:37 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
  • [flang][hlfir] rebase for hlfir.char_extremum
  • [flang][hlfir] addressing some of Jean's nits
  • [flang][hlfir] prelim work towards adding codegen for hlfir.char_extremum
  • [flang][hlfir] added codegen for variadic args and

Thanks for the updated patch!

flang/lib/Optimizer/Builder/Character.cpp
823

Using llvm::ArrayRef<fir::CharBox> might be better. It forces the caller to pass characters (and std::vector is not a type that is used in lowering that uses llvm data structures).

827–829

You are losing knowledge about the result length that you had in the hlfir operation that new about the result type length if it was something known at compile time. Static alloc are easier to optimize, hoist out of loops....

You should likely add a result mlir::Type argument to createCharExtremum2, and get it from the hlfir operation when calling this helper.

849–850

MIN/MAX are comparing the lexicographical order of the characters, not their length. So the logic here needs to be a lot more complex. While it can be done inlined, it will generate a lot of code.

I would suggest using
fir::runtime::genCharCompare(builder, loc, pred, lhs, rhs) (https://github.com/llvm/llvm-project/blob/6c8d8d10455a3d38a92102ae3a94e1c06d3eb363/flang/include/flang/Optimizer/Builder/Runtime/Character.h#L46) instead.

863

The result length is always the one from the longest argument, both for MIN and MAX. In case the result length is known in the result type (my comment above), you should also directly compute it from the type, to avoid the select on the length.

I would suggest doing something to split the (compile time) loop looking for the biggestLen, and the one looking for the extremum. The first loop would only be done if the result type length is unknown.

865–877

You need to call the assignment logic here (fir::factory::CharacterExprHelper::createAssign) to deal with the potential padding (the "biggest"/"smallest" in lexicographical order may have smaller length than the result).

flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
658

Nit: use llvm::SmallVector<fir::CharBox> here instead (fir::ExtentdedValue are bigger, so if you know you have CharBox, better to store that).

675

You could pass addrType here I think and let createCharExtremum2 use the info inside for static length result, and ensure the returned value has the correct address type.

cabreraam updated this revision to Diff 523613.May 18 2023, 5:37 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
  • [flang][hlfir] rebase for hlfir.char_extremum
  • [flang][hlfir] addressing some of Jean's nits
  • [flang][hlfir] prelim work towards adding codegen for hlfir.char_extremum
  • [flang][hlfir] added codegen for variadic args and
  • [flang][hlfir] current work towards hlfir.char_extremum
cabreraam added inline comments.May 19 2023, 4:26 AM
flang/lib/Optimizer/Builder/Character.cpp
919–937

@jeanPerier @tblah @jacob-crawley

I am struggling mightily here. Ultimately I still have the same issue with mismatched types.

The issue is, I want to be able to hold onto the character type that has the biggest/smallest len based on the genCharCompare result.

However, when you use things like mlir::arith::Select or even using an if statement where you specify the return type, the return type will vary based on the character buffer that gets selected (assuming that both of the lengths are known at compile time). I cannot figure out how to solve this.

I feel like a solution could include something like:

  • based on the output of genCharCompare I should be able to select between the two character buffers
  • I want to cast both to the result type that's the biggest (if i'm looking for max) or the smallest (if I'm looking for min).
  • Then, I can just use mlir::arith::SelectOp to select the character buffer I want because the types will be the same!
  • Then I can just discard whichever one I don't need and then keep going through the rest of the character strings to perform the same comparisons

Thoughts? Any help is appreciated!

cabreraam added inline comments.May 19 2023, 4:46 AM
flang/lib/Optimizer/Builder/Character.cpp
919–937

I could cast to the appropriate type in the if/then/else logic, but I don’t know how to get that SSA value to persist out of the loop without knowing the type before hand.

jeanPerier added inline comments.May 19 2023, 6:14 AM
flang/lib/Optimizer/Builder/Character.cpp
919–937

The issue is, I want to be able to hold onto the character type that has the biggest/smallest len based on the genCharCompare result.

I do not think you need this. genCharCompare is doing a lexicographical compare, you need this to determine which operand should be assigned to the temp, but not to find the biggest length.

To find the biggest length, you just need to compare and select the operand's length to find the biggest length (regardless of whether this is character min/max).

Then, once you have that, you can allocate the result buffer and do the lexicographical compare to select the operand. You will need to thread both the address and length of the extremum until the assignment. You can just cast the addresses to fir.ref<fir.char<?xkind>> to get uniform type in the select. This could be optimized if all the operands have the same compile time constant length in a second time.
See below on on to create such type.

I want to cast both to the result type that's the biggest (if i'm looking for max) or the smallest (if I'm looking for min).

The result type is independent of whether this is a character min/max. It always has the length of the longest operand. The MIN/MAX aspect only imapct the value selected to fill the result, not the result length.

Creating the fir.ref<fir.char<?>> type:

auto kind = recoverCharacterType(resultType).getFKind(); // resultType could also be the first operand type.
auto typeLen = fir::CharacterType::unknownLen();
auto charTy = fir::CharacterType::get(builder.getContext(), kind, typeLen);
auto charRefType = fir::ReferenceType::get(charTy);

Then you can cast all the buffer with this type before selecting them, and you can select the length next to that.

cabreraam updated this revision to Diff 523968.May 19 2023, 5:04 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
  • [flang][hlfir] addressing some of Jean's nits
  • [flang][hlfir] prelim work towards adding codegen for hlfir.char_extremum
  • [flang][hlfir] added codegen for variadic args and
  • [flang][hlfir] current work towards hlfir.char_extremum
  • [flang][hlfir] latest attempt at codegen for
cabreraam marked 8 inline comments as done.May 19 2023, 5:15 PM
cabreraam added inline comments.
flang/lib/Optimizer/Builder/Character.cpp
919–937

This was immensely helpful!

What I ended up doing was casting both candidate buffers to the biggest size because I'll know that after the the arith::CmpI compare. Then, I selected the the correct buffer based on the genCharCompare. I took a look at the resulting code-gen -- which is in char_extremum-bufferization.fir -- for the case when the lengths are known at compiler time -- the max2 and min2 tests -- and the character type's length does get returned when creating the resulting CharBovValue.

Let me know what you think!

cabreraam updated this revision to Diff 524136.May 21 2023, 3:54 PM
cabreraam marked an inline comment as done.
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
  • [flang][hlfir] addressing some of Jean's nits
  • [flang][hlfir] prelim work towards adding codegen for hlfir.char_extremum
  • [flang][hlfir] added codegen for variadic args and
  • [flang][hlfir] current work towards hlfir.char_extremum
  • [flang][hlfir] latest attempt at codegen for
  • [flang] reverting this back to original state

Thanks for the update, it is staring to get shape!

flang/lib/Optimizer/Builder/Character.cpp
926–930

Casting to the biggest length could actually lead to bugs: FIR will happily assume the resulting buffer from the select has the length indicated in its type, while if a "smaller" buffer was selected, this is not true and would lead to out of bounds read while later doing the assignment.

Starting by casting to unknown length is good enough. If both have the same constant length, the select result can have that type. This case is easy to cover by only doing the buffer cast to unknown length if recoverCharacterType(resultBuf.getType()) != recoverCharacterType(currBuf.getType()).

954

This may still read past the "from" buffer if the extremum has smaller length than the result. Please use fir::factory::CharacterExprHelper::createAssign as previously suggested to deal with the padding as needed.

cabreraam updated this revision to Diff 525813.May 25 2023, 2:29 PM
  • [flang][hlfir] correction to hlfir.char_extremum doc string
  • [flang][hlfir] first attempt at parsing test for hlfir.char_extremum
  • [flang][hlfir] convertcall.cpp work towards hlfir.char_extremum
  • [flang][hlfir] lowering hlfir.char_extremum
  • [flang][hlfir] start of test for max.f03
  • [flang][hlfir] remove some fluff
  • [flang][hlfir] make changes based on jean's suggestions
  • [flang][hlfir] implemented Jeans suggestions and added a test to check lowering Fortran to HLFIR char extremum
  • [flang][hlfir] addressing some of Jean's nits
  • [flang][hlfir] prelim work towards adding codegen for hlfir.char_extremum
  • [flang][hlfir] added codegen for variadic args and
  • [flang][hlfir] current work towards hlfir.char_extremum
  • [flang][hlfir] latest attempt at codegen for
  • [flang][hlfir] cast currBuf and resultBuf to unknown then recover len after lexical compare if known
cabreraam marked 2 inline comments as done.May 25 2023, 2:38 PM

If you think this looks good, let me know! I'll create separate patches for op definition/code-gen and then one for lowering.

flang/lib/Optimizer/Builder/Character.cpp
833–835

Casting to the biggest length could actually lead to bugs: FIR will happily assume the resulting buffer from the select has the length indicated in its type, while if a "smaller" buffer was selected, this is not true and would lead to out of bounds read while later doing the assignment.

Starting by casting to unknown length is good enough. If both have the same constant length, the select result can have that type. This case is easy to cover by only doing the buffer cast to unknown length if recoverCharacterType(resultBuf.getType()) != recoverCharacterType(currBuf.getType()).

I now convert to a character type with unknownLen and then recover the length in lines 844-849 if the length is known at compile time. See tests max2 and min2 in char_extremum-bufferization.fir as proof that this conversion happens.

856

This may still read past the "from" buffer if the extremum has smaller length than the result. Please use fir::factory::CharacterExprHelper::createAssign as previously suggested to deal with the padding as needed.

Done!

954

This may still read past the "from" buffer if the extremum has smaller length than the result. Please use fir::factory::CharacterExprHelper::createAssign as previously suggested to deal with the padding as needed.

Thanks for the update

flang/lib/Optimizer/Builder/Character.cpp
844–849

This likely will never be used since resultLen is produced by a fir.select (unless there is only one argument, which is not legal) and no folding occurred yet (maybe mlir::Fold could succeed here, I am not sure, but I think it is to leave that up to the next canonicalization pass).

flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
612

I am not sure a vector is needed here, it is stored and used inside the same loop,

613

Same here.

633

Nit: we lack a proper convention to name temps, but they all start with "." to underline this is not a user variable name. ".tmp.char_extremum" would also give more info about the purpose of this temp.

cabreraam marked an inline comment as done.May 30 2023, 10:57 AM
cabreraam added inline comments.
flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
613

The uses for this are outside of the scope of the for loop; I use them when I pass opCBVs to the createCharExtremum helper.

cabreraam marked an inline comment as done.May 30 2023, 11:03 AM