[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