[Flang] Compute elemental character MIN/MAX result length in HLFIR (#189464)
This patch fixes lowering of elemental character MIN/MAX in HLFIR. Previously, these cases could hit a lowering-time TODO `ElementalIntrinsicCallBuilder::computeDynamicCharacterResultLength` and abort. This change computes the character result length as the maximum length of the present actual arguments, allowing valid elemental character MIN/MAX calls to lower successfully. Added regression coverage for elemental character MIN/MAX, including differing-length arguments. Co-authored-by: Sairudra More <moresair@pe31.hpc.amslabs.hpecorp.net>
This commit is contained in:
parent
c245d764b8
commit
234b8469e9
@ -2758,10 +2758,53 @@ public:
|
||||
intrinsic->name == "merge")
|
||||
return loweredActuals[0].value().genCharLength(
|
||||
callContext.loc, callContext.getBuilder());
|
||||
// Character MIN/MAX is the min/max of the arguments length that are
|
||||
// present.
|
||||
TODO(callContext.loc,
|
||||
"compute elemental character min/max function result length in HLFIR");
|
||||
// Character MIN/MAX result length is the length of the longest
|
||||
// argument that is present.
|
||||
assert(intrinsic &&
|
||||
(intrinsic->name == "min" || intrinsic->name == "max") &&
|
||||
"unexpected elemental intrinsic with character result");
|
||||
fir::FirOpBuilder &builder = callContext.getBuilder();
|
||||
mlir::Location loc = callContext.loc;
|
||||
mlir::Type idxTy = builder.getIndexType();
|
||||
mlir::Value resultLength;
|
||||
for (auto &preparedActual : loweredActuals) {
|
||||
if (!preparedActual)
|
||||
continue;
|
||||
mlir::Value argLen;
|
||||
if (preparedActual->handleDynamicOptional()) {
|
||||
// genCharLength must not be called on an absent optional: the
|
||||
// descriptor may be unreadable (e.g. assumed-length character).
|
||||
// Guard it with a fir.if so the read only happens when present.
|
||||
mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
|
||||
mlir::Value isPresent = preparedActual->getIsPresent();
|
||||
auto &capture = *preparedActual;
|
||||
argLen =
|
||||
builder
|
||||
.genIfOp(loc, {idxTy}, isPresent,
|
||||
/*withElseRegion=*/true)
|
||||
.genThen([&]() {
|
||||
mlir::Value len = capture.genCharLength(loc, builder);
|
||||
len = builder.createConvert(loc, idxTy, len);
|
||||
fir::ResultOp::create(builder, loc, len);
|
||||
})
|
||||
.genElse([&]() { fir::ResultOp::create(builder, loc, zero); })
|
||||
.getResults()[0];
|
||||
} else {
|
||||
argLen = preparedActual->genCharLength(loc, builder);
|
||||
argLen = builder.createConvert(loc, idxTy, argLen);
|
||||
}
|
||||
if (!resultLength) {
|
||||
resultLength = argLen;
|
||||
} else {
|
||||
mlir::Value cmp = mlir::arith::CmpIOp::create(
|
||||
builder, loc, mlir::arith::CmpIPredicate::sgt, argLen,
|
||||
resultLength);
|
||||
resultLength = mlir::arith::SelectOp::create(builder, loc, cmp, argLen,
|
||||
resultLength);
|
||||
}
|
||||
}
|
||||
assert(resultLength && "MIN/MAX must have at least two arguments");
|
||||
return resultLength;
|
||||
}
|
||||
|
||||
mlir::Value getPolymorphicResultMold(
|
||||
|
||||
129
flang/test/Lower/HLFIR/elemental-character-min-max.f90
Normal file
129
flang/test/Lower/HLFIR/elemental-character-min-max.f90
Normal file
@ -0,0 +1,129 @@
|
||||
! Test lowering of elemental character MIN/MAX to HLFIR
|
||||
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
|
||||
|
||||
! Test elemental character MIN with two array arguments of the same length.
|
||||
subroutine test_elemental_char_min(a, b, res)
|
||||
character(5) :: a(10), b(10), res(10)
|
||||
res = min(a, b)
|
||||
end subroutine
|
||||
! CHECK-LABEL: func.func @_QPtest_elemental_char_min(
|
||||
! CHECK: %[[C5_A:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[A:.*]]:2 = hlfir.declare {{.*}}Ea"
|
||||
! CHECK: %[[C5_B:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[B:.*]]:2 = hlfir.declare {{.*}}Eb"
|
||||
! CHECK: %[[RES:.*]]:2 = hlfir.declare {{.*}}Eres"
|
||||
! CHECK: %[[CMP:.*]] = arith.cmpi sgt, %[[C5_B]], %[[C5_A]] : index
|
||||
! CHECK: %[[RESULT_LEN:.*]] = arith.select %[[CMP]], %[[C5_B]], %[[C5_A]] : index
|
||||
! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} typeparams %[[RESULT_LEN]] unordered : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,?>> {
|
||||
! CHECK: ^bb0(%[[IDX:.*]]: index):
|
||||
! CHECK: %{{.*}} = hlfir.char_extremum min, %{{.*}}, %{{.*}} :
|
||||
! CHECK: hlfir.yield_element
|
||||
! CHECK: }
|
||||
|
||||
! Test elemental character MAX with two array arguments.
|
||||
subroutine test_elemental_char_max(a, b, res)
|
||||
character(5) :: a(10), b(10), res(10)
|
||||
res = max(a, b)
|
||||
end subroutine
|
||||
! CHECK-LABEL: func.func @_QPtest_elemental_char_max(
|
||||
! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} typeparams %{{.*}} unordered : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,?>> {
|
||||
! CHECK: ^bb0(%[[IDX:.*]]: index):
|
||||
! CHECK: %{{.*}} = hlfir.char_extremum max, %{{.*}}, %{{.*}} :
|
||||
! CHECK: hlfir.yield_element
|
||||
! CHECK: }
|
||||
|
||||
! Test elemental character MIN with different argument lengths.
|
||||
! The result length must be the maximum of the argument lengths.
|
||||
subroutine test_elemental_char_min_diff_len(a, b, res)
|
||||
character(5) :: a(10)
|
||||
character(7) :: b(10), res(10)
|
||||
res = min(a, b)
|
||||
end subroutine
|
||||
! CHECK-LABEL: func.func @_QPtest_elemental_char_min_diff_len(
|
||||
! CHECK: %[[C5:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[A:.*]]:2 = hlfir.declare {{.*}}Ea"
|
||||
! CHECK: %[[C7:.*]] = arith.constant 7 : index
|
||||
! CHECK: %[[B:.*]]:2 = hlfir.declare {{.*}}Eb"
|
||||
! CHECK: %[[CMP:.*]] = arith.cmpi sgt, %[[C7]], %[[C5]] : index
|
||||
! CHECK: %[[RESULT_LEN:.*]] = arith.select %[[CMP]], %[[C7]], %[[C5]] : index
|
||||
! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} typeparams %[[RESULT_LEN]] unordered : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,?>> {
|
||||
|
||||
! Test elemental character MIN with three array arguments.
|
||||
! The result length must be the maximum across all argument lengths.
|
||||
subroutine test_elemental_char_min_three(a, b, c, res)
|
||||
character(5) :: a(10), b(10), c(10), res(10)
|
||||
res = min(a, b, c)
|
||||
end subroutine
|
||||
! CHECK-LABEL: func.func @_QPtest_elemental_char_min_three(
|
||||
! CHECK: %[[C5_A:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[A:.*]]:2 = hlfir.declare {{.*}}Ea"
|
||||
! CHECK: %[[C5_B:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[B:.*]]:2 = hlfir.declare {{.*}}Eb"
|
||||
! CHECK: %[[C5_C:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[C:.*]]:2 = hlfir.declare {{.*}}Ec"
|
||||
! CHECK: %[[CMP1:.*]] = arith.cmpi sgt, %[[C5_B]], %[[C5_A]] : index
|
||||
! CHECK: %[[MAX1:.*]] = arith.select %[[CMP1]], %[[C5_B]], %[[C5_A]] : index
|
||||
! CHECK: %[[CMP2:.*]] = arith.cmpi sgt, %[[C5_C]], %[[MAX1]] : index
|
||||
! CHECK: %[[RESULT_LEN:.*]] = arith.select %[[CMP2]], %[[C5_C]], %[[MAX1]] : index
|
||||
! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} typeparams %[[RESULT_LEN]] unordered : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,?>> {
|
||||
! CHECK: ^bb0(%[[IDX:.*]]: index):
|
||||
! CHECK: %{{.*}} = hlfir.char_extremum min, %{{.*}}, %{{.*}}, %{{.*}} :
|
||||
! CHECK: hlfir.yield_element
|
||||
! CHECK: }
|
||||
|
||||
! Test elemental character MIN with an optional dummy argument of fixed length.
|
||||
! The result length must guard the optional argument's length with fir.if so
|
||||
! genCharLength is not called on an absent optional (unsafe for assumed length).
|
||||
subroutine test_elemental_char_min_optional(a, b, c, res)
|
||||
character(5) :: a(10), b(10), res(10)
|
||||
character(5), optional :: c(10)
|
||||
res = min(a, b, c)
|
||||
end subroutine
|
||||
! CHECK-LABEL: func.func @_QPtest_elemental_char_min_optional(
|
||||
! CHECK: %[[C5_A:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[A:.*]]:2 = hlfir.declare {{.*}}Ea"
|
||||
! CHECK: %[[C5_B:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[B:.*]]:2 = hlfir.declare {{.*}}Eb"
|
||||
! CHECK: %[[C5_C:.*]] = arith.constant 5 : index
|
||||
! CHECK: %[[C:.*]]:2 = hlfir.declare {{.*}}Ec"
|
||||
! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[C]]#0
|
||||
! CHECK: %[[CMP1:.*]] = arith.cmpi sgt, %[[C5_B]], %[[C5_A]] : index
|
||||
! CHECK: %[[MAX1:.*]] = arith.select %[[CMP1]], %[[C5_B]], %[[C5_A]] : index
|
||||
! CHECK: %[[C0:.*]] = arith.constant 0 : index
|
||||
! CHECK: %[[OPT_LEN:.*]] = fir.if %[[IS_PRESENT]] -> (index) {
|
||||
! CHECK: fir.result %[[C5_C]] : index
|
||||
! CHECK: } else {
|
||||
! CHECK: fir.result %[[C0]] : index
|
||||
! CHECK: }
|
||||
! CHECK: %[[CMP2:.*]] = arith.cmpi sgt, %[[OPT_LEN]], %[[MAX1]] : index
|
||||
! CHECK: %[[RESULT_LEN:.*]] = arith.select %[[CMP2]], %[[OPT_LEN]], %[[MAX1]] : index
|
||||
! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} typeparams %[[RESULT_LEN]]
|
||||
|
||||
! Test elemental character MIN with an assumed-length optional dummy argument.
|
||||
! genCharLength reads from the descriptor, so it must be guarded with fir.if
|
||||
! to avoid reading an invalid descriptor when the argument is absent.
|
||||
subroutine test_elemental_char_min_assumed_optional(a, b, c, res)
|
||||
character(*), intent(in) :: a(:), b(:)
|
||||
character(*), intent(in), optional :: c(:)
|
||||
character(*), intent(out) :: res(:)
|
||||
res = min(a, b, c)
|
||||
end subroutine
|
||||
! CHECK-LABEL: func.func @_QPtest_elemental_char_min_assumed_optional(
|
||||
! CHECK: %[[A:.*]]:2 = hlfir.declare {{.*}}Ea"
|
||||
! CHECK: %[[B:.*]]:2 = hlfir.declare {{.*}}Eb"
|
||||
! CHECK: %[[C:.*]]:2 = hlfir.declare {{.*}}Ec"
|
||||
! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[C]]#0
|
||||
! CHECK: %[[LEN_A:.*]] = fir.box_elesize %[[A]]#1
|
||||
! CHECK: %[[LEN_B:.*]] = fir.box_elesize %[[B]]#1
|
||||
! CHECK: %[[CMP1:.*]] = arith.cmpi sgt, %[[LEN_B]], %[[LEN_A]] : index
|
||||
! CHECK: %[[MAX1:.*]] = arith.select %[[CMP1]], %[[LEN_B]], %[[LEN_A]] : index
|
||||
! CHECK: %[[C0:.*]] = arith.constant 0 : index
|
||||
! CHECK: %[[OPT_LEN:.*]] = fir.if %[[IS_PRESENT]] -> (index) {
|
||||
! CHECK: %[[LEN_C:.*]] = fir.box_elesize %[[C]]#1
|
||||
! CHECK: fir.result %[[LEN_C]] : index
|
||||
! CHECK: } else {
|
||||
! CHECK: fir.result %[[C0]] : index
|
||||
! CHECK: }
|
||||
! CHECK: %[[CMP2:.*]] = arith.cmpi sgt, %[[OPT_LEN]], %[[MAX1]] : index
|
||||
! CHECK: %[[RESULT_LEN:.*]] = arith.select %[[CMP2]], %[[OPT_LEN]], %[[MAX1]] : index
|
||||
! CHECK: %[[ELEMENTAL:.*]] = hlfir.elemental %{{.*}} typeparams %[[RESULT_LEN]] unordered
|
||||
Loading…
x
Reference in New Issue
Block a user