[flang-commits] [flang] [Flang] Compute elemental character MIN/MAX result length in HLFIR (PR #189464)
via flang-commits
flang-commits at lists.llvm.org
Mon Mar 30 12:54:59 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Sairudra More (Saieiei)
<details>
<summary>Changes</summary>
This patch fixes lowering of elemental character MIN/MAX in HLFIR.
Previously, applying `MIN` or `MAX` elementally to character arrays caused a compile-time abort:
```
not yet implemented: compute elemental character min/max function result length in HLFIR
```
The crash originated from a TODO in `ElementalIntrinsicCallBuilder::computeDynamicCharacterResultLength` in `ConvertCall.cpp`. While `adjustl`, `adjustr`, and `merge` were handled (result length = first argument's length), no implementation existed for `min`/`max`.
**Reproducer:**
```fortran
program test
character(5) :: a(3) = [character(5) :: 'zzzzz', 'aaaaa', 'mmmmm']
character(5) :: b(3)
b = min(a, 'hello')
print '(3A6)', b
end program
```
**Fix:**
Per the Fortran 2023 standard (16.9.135 MAX, 16.9.141 MIN), the result character length is the length of the longest actual argument. The fix computes `max(len(a1), len(a2), ...)` across all present arguments using `arith::CmpIOp` (sgt) + `arith::SelectOp`, with proper handling for dynamically optional arguments (length treated as 0 when absent).
**Testing:**
- Added regression test covering elemental character MIN/MAX with same-length args, different-length args, MAX variant, and three arguments.
- `make check-flang`: all 4277 tests pass.
- `make check-flang-rt`: all 279 tests pass.
---
Full diff: https://github.com/llvm/llvm-project/pull/189464.diff
2 Files Affected:
- (modified) flang/lib/Lower/ConvertCall.cpp (+31-4)
- (added) flang/test/Lower/HLFIR/elemental-character-min-max.f90 (+72)
``````````diff
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index ae9d1733d053d..5b69e49c67670 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -2758,10 +2758,37 @@ class ElementalIntrinsicCallBuilder
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::Value resultLength;
+ for (auto &preparedActual : loweredActuals) {
+ if (!preparedActual)
+ continue;
+ mlir::Value argLen = preparedActual->genCharLength(loc, builder);
+ argLen = builder.createConvert(loc, builder.getIndexType(), argLen);
+ if (preparedActual->handleDynamicOptional()) {
+ mlir::Value zero =
+ builder.createIntegerConstant(loc, builder.getIndexType(), 0);
+ argLen = mlir::arith::SelectOp::create(
+ builder, loc, preparedActual->getIsPresent(), argLen, zero);
+ }
+ 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(
diff --git a/flang/test/Lower/HLFIR/elemental-character-min-max.f90 b/flang/test/Lower/HLFIR/elemental-character-min-max.f90
new file mode 100644
index 0000000000000..5df54a9262c5c
--- /dev/null
+++ b/flang/test/Lower/HLFIR/elemental-character-min-max.f90
@@ -0,0 +1,72 @@
+! 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: }
``````````
</details>
https://github.com/llvm/llvm-project/pull/189464
More information about the flang-commits
mailing list