[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