[flang-commits] [flang] [flang] Don't do actual concatenation when computing LEN() of concatenated strings (PR #180676)
Eugene Epshteyn via flang-commits
flang-commits at lists.llvm.org
Mon Feb 9 21:05:59 PST 2026
https://github.com/eugeneepshteyn created https://github.com/llvm/llvm-project/pull/180676
For cases like the following don't actually compute concatenation of
`str // "abc"`, because we only need final length:
```
character(len=*), intent(in) :: str
character(len=len(str // "abc")) :: res
```
For such cases, don't emit hlfir.concat.
>From 77d9b3bcadd381918d34953e4f2ac450954435e1 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 9 Feb 2026 23:52:57 -0500
Subject: [PATCH 1/2] [flang] Don't do actual concatenation when computing
LEN() of concatenated strings
For cases like the following don't actually compute concatenation of
`str // "abc"`, because we only need final length:
```
character(len=*), intent(in) :: str
character(len=len(str // "abc")) :: res
```
For such cases, don't emit hlfir.concat.
---
flang/lib/Lower/HlfirIntrinsics.cpp | 24 +++++++++
.../Lower/HLFIR/assumed-rank-inquiries.f90 | 2 +-
.../elemental-result-length-len-folding.f90 | 54 +++++++++++++++++++
3 files changed, 79 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Lower/HLFIR/elemental-result-length-len-folding.f90
diff --git a/flang/lib/Lower/HlfirIntrinsics.cpp b/flang/lib/Lower/HlfirIntrinsics.cpp
index 27c8bb87f7542..8fa40ff788d09 100644
--- a/flang/lib/Lower/HlfirIntrinsics.cpp
+++ b/flang/lib/Lower/HlfirIntrinsics.cpp
@@ -220,6 +220,14 @@ class HlfirIndexLowering : public HlfirTransformationalIntrinsic {
mlir::Type stmtResultType) override;
};
+struct HlfirLenLowering : public HlfirTransformationalIntrinsic {
+ using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;
+ mlir::Value
+ lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,
+ const fir::IntrinsicArgumentLoweringRules *argLowering,
+ mlir::Type stmtResultType) override;
+};
+
} // namespace
mlir::Value HlfirTransformationalIntrinsic::loadBoxAddress(
@@ -558,6 +566,19 @@ mlir::Value HlfirIndexLowering::lowerImpl(
return result;
}
+mlir::Value HlfirLenLowering::lowerImpl(
+ const Fortran::lower::PreparedActualArguments &loweredActuals,
+ const fir::IntrinsicArgumentLoweringRules *argLowering,
+ mlir::Type stmtResultType) {
+ // LEN (STRING [, KIND])
+ assert((loweredActuals.size() == 1 || loweredActuals.size() == 2) &&
+ loweredActuals[0].has_value());
+ Fortran::lower::PreparedActualArgument &strArg =
+ const_cast<Fortran::lower::PreparedActualArgument &>(*loweredActuals[0]);
+ return builder.createConvert(
+ loc, stmtResultType, strArg.genCharLength(loc, builder));
+}
+
std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
fir::FirOpBuilder &builder, mlir::Location loc, const std::string &name,
const Fortran::lower::PreparedActualArguments &loweredActuals,
@@ -615,6 +636,9 @@ std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
if (name == "index")
return HlfirIndexLowering{builder, loc}.lower(loweredActuals, argLowering,
stmtResultType);
+ if (name == "len")
+ return HlfirLenLowering{builder, loc}.lower(loweredActuals, argLowering,
+ stmtResultType);
if (mlir::isa<fir::CharacterType>(stmtResultType)) {
if (name == "min")
diff --git a/flang/test/Lower/HLFIR/assumed-rank-inquiries.f90 b/flang/test/Lower/HLFIR/assumed-rank-inquiries.f90
index fcca1733dc639..5b49acc4c3d46 100644
--- a/flang/test/Lower/HLFIR/assumed-rank-inquiries.f90
+++ b/flang/test/Lower/HLFIR/assumed-rank-inquiries.f90
@@ -166,7 +166,7 @@ subroutine c_loc_2(x)
! CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<*:!fir.char<1,?>>> {fir.bindc_name = "x"}) {
! CHECK: %[[VAL_1:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %[[VAL_1]] arg {{[0-9]+}} {uniq_name = "_QFtest_len_1Ex"} : (!fir.box<!fir.array<*:!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<*:!fir.char<1,?>>>, !fir.box<!fir.array<*:!fir.char<1,?>>>)
-! CHECK: %[[VAL_3:.*]] = fir.box_elesize %[[VAL_2]]#0 : (!fir.box<!fir.array<*:!fir.char<1,?>>>) -> index
+! CHECK: %[[VAL_3:.*]] = fir.box_elesize %[[VAL_2]]#1 : (!fir.box<!fir.array<*:!fir.char<1,?>>>) -> index
! CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]] : (index) -> i32
! CHECK: %[[VAL_5:.*]]:3 = hlfir.associate %[[VAL_4]] {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
! CHECK: fir.call @_QPtakes_integer(%[[VAL_5]]#0) fastmath<contract> : (!fir.ref<i32>) -> ()
diff --git a/flang/test/Lower/HLFIR/elemental-result-length-len-folding.f90 b/flang/test/Lower/HLFIR/elemental-result-length-len-folding.f90
new file mode 100644
index 0000000000000..c59a31b3b21ad
--- /dev/null
+++ b/flang/test/Lower/HLFIR/elemental-result-length-len-folding.f90
@@ -0,0 +1,54 @@
+! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
+
+! Test that LEN intrinsic in elemental function result specification expression
+! is lowered efficiently in HLFIR, especially when it involves expressions
+! like concatenation that can be folded or handled without materialization.
+
+elemental function add_suffix(str) result(res)
+ character(len=*), intent(in) :: str
+ character(len=len(str // "abc")) :: res
+ res = str // "xyz"
+end function
+
+! CHECK-LABEL: func.func @_QPadd_suffix(
+! CHECK: hlfir.declare {{.*}} "_QFadd_suffixEstr"
+! CHECK-NOT: hlfir.concat
+! CHECK: hlfir.declare {{.*}} "_QFadd_suffixEres"
+! CHECK: hlfir.concat
+! CHECK: hlfir.assign
+
+subroutine test_call(s, r)
+ character(*), intent(in) :: s(:)
+ character(*), intent(out) :: r(:)
+ interface
+ elemental function add_suffix(str) result(res)
+ character(len=*), intent(in) :: str
+ character(len=len(str // "abc")) :: res
+ end function
+ end interface
+ r = add_suffix(s)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_call(
+! CHECK: hlfir.declare {{.*}} "_QFtest_callEs"
+! CHECK-NOT: hlfir.concat
+! CHECK: hlfir.elemental {{.*}} {
+! CHECK: hlfir.designate
+! CHECK: fir.alloca
+! CHECK: hlfir.declare
+! CHECK: fir.call @_QPadd_suffix
+! CHECK: }
+
+subroutine test_trim(s, n)
+ character(*) :: s
+ integer :: n
+ n = len(trim(s))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_trim(
+! CHECK: hlfir.declare {{.*}} "_QFtest_trimEs"
+! CHECK: %[[TRIM:.*]] = hlfir.char_trim
+! CHECK-NEXT: %[[LEN:.*]] = hlfir.get_length %[[TRIM]]
+! CHECK-NEXT: %[[LEN_I32:.*]] = fir.convert %[[LEN]] : (index) -> i32
+! CHECK-NEXT: hlfir.assign %[[LEN_I32]] to {{.*}}
+! CHECK-NEXT: hlfir.destroy %[[TRIM]]
>From dbfc18cc8d57917b152b614569a73c8be07ab1ea Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 10 Feb 2026 00:03:57 -0500
Subject: [PATCH 2/2] clang-format
---
flang/lib/Lower/HlfirIntrinsics.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/HlfirIntrinsics.cpp b/flang/lib/Lower/HlfirIntrinsics.cpp
index 8fa40ff788d09..f63628d7824ff 100644
--- a/flang/lib/Lower/HlfirIntrinsics.cpp
+++ b/flang/lib/Lower/HlfirIntrinsics.cpp
@@ -572,11 +572,11 @@ mlir::Value HlfirLenLowering::lowerImpl(
mlir::Type stmtResultType) {
// LEN (STRING [, KIND])
assert((loweredActuals.size() == 1 || loweredActuals.size() == 2) &&
- loweredActuals[0].has_value());
+ loweredActuals[0].has_value());
Fortran::lower::PreparedActualArgument &strArg =
const_cast<Fortran::lower::PreparedActualArgument &>(*loweredActuals[0]);
- return builder.createConvert(
- loc, stmtResultType, strArg.genCharLength(loc, builder));
+ return builder.createConvert(loc, stmtResultType,
+ strArg.genCharLength(loc, builder));
}
std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
More information about the flang-commits
mailing list