[flang-commits] [flang] pass intent-in param directly (PR #197636)
Yingying Wang via flang-commits
flang-commits at lists.llvm.org
Thu May 14 01:31:13 PDT 2026
https://github.com/buggfg created https://github.com/llvm/llvm-project/pull/197636
**Description**
When the dummy argument is declared `INTENT(IN)`, the procedure is not permitted by the Fortran standard (F2018, 8.5.10) to define the dummy argument. In such cases, creating a temporary and performing copy-out may be unnecessary and can introduce avoidable overhead, particularly for large string constants or array parameters.
**Validation & performance**
- No compile or semantic errors observed on SPEC2006 and SPEC2017 with the new feature enabled for validation.
- With this patch:
- SPEC2006 465.tonto on RISC-V (XuanTieC920): **5% speedup**
- SPEC2006 465.tonto on RISC-V (KMHv3 RTL): **3% speedup**
>From dbdc98a417138de9a84e36218a769ac3ff4be004 Mon Sep 17 00:00:00 2001
From: buggfg <3171290993 at qq.com>
Date: Thu, 14 May 2026 16:22:14 +0800
Subject: [PATCH] pass intent-in param directly
Co-authored-by: refuseno <103424664+refuseno at users.noreply.github.com>
Co-authored-by: ict-ql <168183727+ict-ql at users.noreply.github.com>
Co-authored-by: Lin Wang <wanglulin at ict.ac.cn>
---
flang/lib/Lower/ConvertCall.cpp | 6 +-
.../calls-constant-expr-arg-intent-in.f90 | 103 ++++++++++++++++++
2 files changed, 107 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Lower/HLFIR/calls-constant-expr-arg-intent-in.f90
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index ea65a49355330..2b0a4eeb7a50f 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1500,6 +1500,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
// is contiguous according to the dummy type.
if (mustSetDynamicTypeToDummyType)
entity = genSetDynamicTypeToDummyType(entity);
+
+ const bool isParameter = isParameterObjectOrSubObject(entity);
if (arg.hasValueAttribute() ||
// Constant expressions might be lowered as variables with
// 'parameter' attribute. Even though the constant expressions
@@ -1507,7 +1509,7 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
// possible, we have to create a temporary copies when we pass
// them down the call stack because of potential compiler
// generated writes in copy-out.
- isParameterObjectOrSubObject(entity)) {
+ (isParameter && arg.mayBeModifiedByCall())) {
// Make a copy in a temporary.
auto copy = hlfir::AsExprOp::create(builder, loc, entity);
mlir::Type storageType = entity.getType();
@@ -1517,7 +1519,7 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
entity = hlfir::Entity{associate.getBase()};
// Register the temporary destruction after the call.
preparedDummy.pushExprAssociateCleanUp(associate);
- } else if (mustDoCopyIn || mustDoCopyOut) {
+ } else if ((mustDoCopyIn || mustDoCopyOut) && !isParameter) {
// Copy-in non contiguous variables.
//
// TODO: copy-in and copy-out are now determined separately, in order
diff --git a/flang/test/Lower/HLFIR/calls-constant-expr-arg-intent-in.f90 b/flang/test/Lower/HLFIR/calls-constant-expr-arg-intent-in.f90
new file mode 100644
index 0000000000000..f0bf7b0a226c0
--- /dev/null
+++ b/flang/test/Lower/HLFIR/calls-constant-expr-arg-intent-in.f90
@@ -0,0 +1,103 @@
+! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
+
+! Test that no temporary copy is made for parameter/constant actual arguments
+! when passed to INTENT(IN) dummy arguments. Since the callee guarantees not
+! to modify the argument (F2018 8.5.10), there is no risk of copy-out writing
+! into read-only memory, so the global address can be passed directly.
+
+! Case 1: named PARAMETER array passed to INTENT(IN) assumed-shape dummy
+subroutine test_param_array_intent_in()
+ interface
+ subroutine bar(x)
+ integer, intent(in) :: x(:)
+ end subroutine
+ end interface
+ integer, parameter :: p(5) = [1, 2, 3, 4, 5]
+ call bar(p)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_param_array_intent_in() {
+! CHECK: %0 = fir.dummy_scope : !fir.dscope
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QFtest_param_array_intent_inECp) : !fir.ref<!fir.array<5xi32>>
+! CHECK: %[[INDEX:.*]] = arith.constant 5 : index
+! CHECK: %[[SHAPE:.*]] = fir.shape %[[INDEX]] : (index) -> !fir.shape<1>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ADDR]](%[[SHAPE]]) {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QFtest_param_array_intent_inECp"} : (!fir.ref<!fir.array<5xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<5xi32>>, !fir.ref<!fir.array<5xi32>>)
+! CHECK: %[[ADDR2:.*]] = fir.address_of(@_QQro.5xi4.0) : !fir.ref<!fir.array<5xi32>>
+! CHECK: %[[INDEX2:.*]] = arith.constant 5 : index
+! CHECK: %[[SHAPE2:.*]] = fir.shape %[[INDEX2]] : (index) -> !fir.shape<1>
+! CHECK: %[[DECL2:.*]]:2 = hlfir.declare %[[ADDR2]](%[[SHAPE2]]) {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QQro.5xi4.0"} : (!fir.ref<!fir.array<5xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<5xi32>>, !fir.ref<!fir.array<5xi32>>)
+! CHECK: %[[EMBOX:.*]] = fir.embox %[[DECL2]]#0(%[[SHAPE2]]) : (!fir.ref<!fir.array<5xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<5xi32>>
+! CHECK: %[[CONVERT:.*]] = fir.convert %[[EMBOX]] : (!fir.box<!fir.array<5xi32>>) -> !fir.box<!fir.array<?xi32>>
+! CHECK: fir.call @_QPbar(%[[CONVERT]]) fastmath<contract> : (!fir.box<!fir.array<?xi32>>) -> ()
+! CHECK: return
+! CHECK: }
+
+! Case 2: string literal passed to INTENT(IN) character dummy
+subroutine test_string_literal_intent_in()
+ interface
+ subroutine baz(msg)
+ character(*), intent(in) :: msg
+ end subroutine
+ end interface
+ call baz("hello world")
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_string_literal_intent_in() {
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QQ{{.*}}) : !fir.ref<!fir.char<1,11>>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ADDR]] {{.*}}{fortran_attrs = #fir.var_attrs<parameter>
+! CHECK-NOT: hlfir.as_expr
+! CHECK-NOT: hlfir.associate
+! CHECK: %[[BOXCHAR:.*]] = fir.emboxchar %[[DECL]]#0, %{{.*}} : (!fir.ref<!fir.char<1,11>>, index) -> !fir.boxchar<1>
+! CHECK: fir.call @_QPbaz(%[[BOXCHAR]])
+! CHECK-NOT: hlfir.end_associate
+! CHECK: return
+! CHECK: }
+
+! Case 3: scalar PARAMETER passed to INTENT(IN) scalar dummy
+subroutine test_param_scalar_intent_in()
+ interface
+ subroutine scalar_in(n)
+ integer, intent(in) :: n
+ end subroutine
+ end interface
+ integer, parameter :: k = 42
+ call scalar_in(k)
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_param_scalar_intent_in() {
+! CHECK: %0 = fir.dummy_scope : !fir.dscope
+! CHECK: %[[ADDR:.*]]= fir.address_of(@_QFtest_param_scalar_intent_inECk) : !fir.ref<i32>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %1 {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QFtest_param_scalar_intent_inECk"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[CONST:.*]] = arith.constant 42 : i32
+! CHECK: %[[ASSO:.*]]:3 = hlfir.associate %[[CONST]] {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
+! CHECK: fir.call @_QPscalar_in(%[[ASSO]]#0) fastmath<contract> : (!fir.ref<i32>) -> ()
+! CHECK: hlfir.end_associate %[[ASSO]]#1, %[[ASSO]]#2 : !fir.ref<i32>, i1
+! CHECK: return
+! CHECK: }
+
+! Case 4: constant array expression passed to dummy WITHOUT intent,copy still be made
+subroutine test_param_array_no_intent()
+ interface
+ subroutine qux(x)
+ integer :: x(:)
+ end subroutine
+ end interface
+ integer, parameter :: p(3) = [10, 20, 30]
+ call qux(p)
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_param_array_no_intent() {
+! CHECK: %0 = fir.dummy_scope : !fir.dscope
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QFtest_param_array_no_intentECp) : !fir.ref<!fir.array<3xi32>>
+! CHECK: %[[CONST:.*]] = arith.constant 3 : index
+! CHECK: %[[SHAPE:.*]] = fir.shape %[[CONST]] : (index) -> !fir.shape<1>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %1(%2) {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QFtest_param_array_no_intentECp"} : (!fir.ref<!fir.array<3xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3xi32>>, !fir.ref<!fir.array<3xi32>>)
+! CHECK: %[[ADDR2:.*]] = fir.address_of(@_QQro.3xi4.1) : !fir.ref<!fir.array<3xi32>>
+! CHECK: %[[CONST2:.*]] = arith.constant 3 : index
+! CHECK: %[[SHAPE2:.*]] = fir.shape %[[CONST2]] : (index) -> !fir.shape<1>
+! CHECK: %[[DECL2:.*]]:2 = hlfir.declare %[[ADDR2]](%[[SHAPE2]]) {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QQro.3xi4.1"} : (!fir.ref<!fir.array<3xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3xi32>>, !fir.ref<!fir.array<3xi32>>)
+! CHECK: %[[EXPR:.*]] = hlfir.as_expr %[[DECL2]]#0 : (!fir.ref<!fir.array<3xi32>>) -> !hlfir.expr<3xi32>
+! CHECK: %[[ASSO:.*]]:3 = hlfir.associate %[[EXPR]](%[[SHAPE2]]) {adapt.valuebyref} : (!hlfir.expr<3xi32>, !fir.shape<1>) -> (!fir.ref<!fir.array<3xi32>>, !fir.ref<!fir.array<3xi32>>, i1)
+! CHECK: %[[EMBOX:.*]] = fir.embox %[[ASSO]]#0(%[[SHAPE2]]) : (!fir.ref<!fir.array<3xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<3xi32>>
+! CHECK: %[[CONVERT:.*]] = fir.convert %[[EMBOX]] : (!fir.box<!fir.array<3xi32>>) -> !fir.box<!fir.array<?xi32>>
+! CHECK: fir.call @_QPqux(%[[CONVERT]]) fastmath<contract> : (!fir.box<!fir.array<?xi32>>) -> ()
+! CHECK: hlfir.end_associate %[[ASSO]]#1, %[[ASSO]]#2 : !fir.ref<!fir.array<3xi32>>, i1
+! CHECK: return
+! CHECK: }
\ No newline at end of file
More information about the flang-commits
mailing list