[flang-commits] [flang] [flang] Route elemental CHARACTER MIN/MAX OPTIONAL cases through custom lowering (PR #191244)
Sairudra More via flang-commits
flang-commits at lists.llvm.org
Tue Apr 14 06:22:05 PDT 2026
https://github.com/Saieiei updated https://github.com/llvm/llvm-project/pull/191244
>From e04fb6c8e92503b48b21b5e6b0fead1e0e76c64f Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Fri, 10 Apr 2026 23:40:01 -0500
Subject: [PATCH] [flang] Route elemental CHARACTER MIN/MAX OPTIONAL cases
through custom lowering
Route elemental CHARACTER MIN/MAX with dynamically optional arguments through
genCustomIntrinsicRefCore so they hit the existing TODO instead of lowering
incorrectly.
Also unwrap sequence types before checking fir::isa_char in
prepareMinOrMaxArguments, so the same TODO is triggered for elemental calls.
Add a negative test for the diagnostic.
---
flang/lib/Lower/ConvertCall.cpp | 8 +++
flang/lib/Lower/CustomIntrinsicCall.cpp | 2 +-
...mental-character-min-max-optional-todo.f90 | 12 ++++
.../HLFIR/elemental-character-min-max.f90 | 57 -------------------
4 files changed, 21 insertions(+), 58 deletions(-)
create mode 100644 flang/test/Lower/HLFIR/elemental-character-min-max-optional-todo.f90
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index ea65a49355330..54d08bb420755 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -2342,6 +2342,14 @@ static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore(
const Fortran::evaluate::SpecificIntrinsic *intrinsic,
const fir::IntrinsicHandlerEntry &intrinsicEntry,
CallContext &callContext) {
+ // Delegate intrinsics with custom optional handling to
+ // genCustomIntrinsicRefCore before attempting any HLFIR op lowering. This
+ // ensures consistent dispatch symmetry with genIntrinsicRefCore and
+ // genIntrinsicRef, both of which check for custom optional handling before
+ // reaching the HLFIR intrinsic path.
+ if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
+ callContext.procRef, *intrinsic, callContext.converter))
+ return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);
// Try lowering transformational intrinsic ops to HLFIR ops if enabled
// (transformational always have a result type)
if (useHlfirIntrinsicOps && callContext.resultType) {
diff --git a/flang/lib/Lower/CustomIntrinsicCall.cpp b/flang/lib/Lower/CustomIntrinsicCall.cpp
index c6d5ca75e1f69..cb1a095774b0c 100644
--- a/flang/lib/Lower/CustomIntrinsicCall.cpp
+++ b/flang/lib/Lower/CustomIntrinsicCall.cpp
@@ -118,7 +118,7 @@ static void prepareMinOrMaxArguments(
assert(retTy && "MIN and MAX must have a return type");
mlir::Type resultType = *retTy;
mlir::Location loc = converter.getCurrentLocation();
- if (fir::isa_char(resultType))
+ if (fir::isa_char(fir::unwrapSequenceType(resultType)))
TODO(loc, "CHARACTER MIN and MAX with dynamically optional arguments");
for (auto arg : llvm::enumerate(procRef.arguments())) {
const auto *expr =
diff --git a/flang/test/Lower/HLFIR/elemental-character-min-max-optional-todo.f90 b/flang/test/Lower/HLFIR/elemental-character-min-max-optional-todo.f90
new file mode 100644
index 0000000000000..62eec30a01144
--- /dev/null
+++ b/flang/test/Lower/HLFIR/elemental-character-min-max-optional-todo.f90
@@ -0,0 +1,12 @@
+! Test that elemental character MIN/MAX with dynamically optional arguments
+! correctly hits the TODO (not yet implemented) diagnostic.
+! RUN: %not_todo_cmd bbc -emit-hlfir -o - %s 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: CHARACTER MIN and MAX with dynamically optional arguments
+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
diff --git a/flang/test/Lower/HLFIR/elemental-character-min-max.f90 b/flang/test/Lower/HLFIR/elemental-character-min-max.f90
index fc149f0759188..5df54a9262c5c 100644
--- a/flang/test/Lower/HLFIR/elemental-character-min-max.f90
+++ b/flang/test/Lower/HLFIR/elemental-character-min-max.f90
@@ -70,60 +70,3 @@ subroutine test_elemental_char_min_three(a, b, c, res)
! 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
More information about the flang-commits
mailing list