[flang-commits] [flang] [flang] Propagate INTENT(IN) dummy arguments as readonly (PR #207732)

via flang-commits flang-commits at lists.llvm.org
Mon Jul 6 07:36:14 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-driver

Author: Sergey Shcherbinin (SergeyShch01)

<details>
<summary>Changes</summary>

Mark eligible INTENT(IN) dummy data objects with a fir.read_only argument attribute during lowering, and teach FunctionAttr to translate the marker to llvm.readonly for reference arguments when optimizing for speed.

LLVM FunctionAttrs can infer readonly later in the pipeline, but this happens too late for IPSCCP function specialization. Within FunctionSpecializer, promoteConstantStackValues requires onlyReadsMemory() for pointer arguments before promoting constant by-reference scalar values. Providing readonly satisfies this check and allows these arguments to be promoted, enabling function specialization, including in non-LTO builds.

Keep readonly handling independent from noalias and nocapture. In particular, TARGET arguments may be readonly but must not receive noalias or nocapture. Also apply readonly to BIND(C) definitions while leaving external C declarations unannotated. VALUE, POINTER, ALLOCATABLE, ASYNCHRONOUS, and VOLATILE dummy arguments remain excluded.

In our measurements, the enabled function specialization gives a 1.5x speedup on 548.exchange2 on NVIDIA Neoverse V2 in a non-LTO configuration.

Update existing lowering tests to account for the new fir.read_only argument attribute, and add three focused tests:

* dummy-argument-readonly.f90 checks when CallInterface emits the fir.read_only marker, including positive and excluded cases.

* function-attrs-readonly.fir checks the translation from fir.read_only to llvm.readonly, including TARGET and BIND(C) handling.

* function-attr-readonly.f90 checks the optimization-level pipeline gate and verifies that readonly reaches the final LLVM IR.

---

Patch is 75.61 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207732.diff


34 Files Affected:

- (modified) flang/include/flang/Optimizer/Dialect/FIROpsSupport.h (+12) 
- (modified) flang/include/flang/Optimizer/Transforms/Passes.td (+3) 
- (modified) flang/lib/Lower/CallInterface.cpp (+38-1) 
- (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+2-1) 
- (modified) flang/lib/Optimizer/Transforms/FunctionAttr.cpp (+25-10) 
- (added) flang/test/Driver/function-attr-readonly.f90 (+37) 
- (modified) flang/test/Lower/HLFIR/elemental-array-ops.f90 (+4-4) 
- (modified) flang/test/Lower/HLFIR/elemental-polymorphic-merge.f90 (+2-2) 
- (modified) flang/test/Lower/HLFIR/elemental-result-length.f90 (+1-1) 
- (modified) flang/test/Lower/HLFIR/structure-constructor.f90 (+1-1) 
- (modified) flang/test/Lower/Intrinsics/c_devptr_eq_ne.f90 (+2-2) 
- (modified) flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90 (+2-2) 
- (modified) flang/test/Lower/Intrinsics/dconjg.f90 (+1-1) 
- (modified) flang/test/Lower/Intrinsics/dimag.f90 (+1-1) 
- (modified) flang/test/Lower/Intrinsics/dreal.f90 (+1-1) 
- (modified) flang/test/Lower/Intrinsics/reduce.f90 (+1-1) 
- (modified) flang/test/Lower/OpenACC/acc-declare.f90 (+2-2) 
- (modified) flang/test/Lower/OpenMP/optional-argument-map-2.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/optional-argument-map.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/wsloop-unstructured.f90 (+4-4) 
- (modified) flang/test/Lower/achar.f90 (+1-1) 
- (modified) flang/test/Lower/arguments.f90 (+2-2) 
- (modified) flang/test/Lower/array-elemental-calls-char-dynamic.f90 (+2-2) 
- (modified) flang/test/Lower/dispatch.f90 (+2-2) 
- (added) flang/test/Lower/dummy-argument-readonly.f90 (+144) 
- (modified) flang/test/Lower/parent-component.f90 (+2-2) 
- (modified) flang/test/Lower/polymorphic-temp.f90 (+12-12) 
- (modified) flang/test/Lower/polymorphic.f90 (+11-12) 
- (modified) flang/test/Lower/repack-arrays.f90 (+1-1) 
- (modified) flang/test/Lower/select-type-2.f90 (+1-1) 
- (modified) flang/test/Lower/select-type.f90 (+6-6) 
- (modified) flang/test/Lower/unsigned-ops.f90 (+2-3) 
- (modified) flang/test/Lower/volatile1.f90 (+1-1) 
- (added) flang/test/Transforms/function-attrs-readonly.fir (+73) 


``````````diff
diff --git a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
index 9e168ffe90d0d..0f921f0f50448 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
@@ -98,6 +98,18 @@ static constexpr llvm::StringRef getVolatileAttrName() {
   return "fir.volatile";
 }
 
+/// Attribute to mark a dummy data object argument as read-only, i.e. the callee
+/// does not write through this argument (INTENT(IN), passed by reference).
+/// TARGET and derived-type arguments are allowed: LLVM `readonly` only
+/// constrains writes through the argument and pointers based on it. Writes
+/// through pointers loaded from the object, such as a POINTER component's
+/// target, do not violate it. Lowered to the LLVM `readonly` argument
+/// attribute by the FunctionAttr pass, which keeps `noalias` and `nocapture`
+/// disabled for TARGET arguments.
+static constexpr llvm::StringRef getReadOnlyAttrName() {
+  return "fir.read_only";
+}
+
 /// Attribute to mark that a function argument is a character dummy procedure.
 /// Character dummy procedure have special ABI constraints.
 static constexpr llvm::StringRef getCharacterProcedureDummyAttrName() {
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 5617e282ed5ce..33f3cc23f90fe 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -477,6 +477,9 @@ def FunctionAttr : Pass<"function-attr", "mlir::func::FuncOp"> {
        Option<"setNoAlias", "set-noalias", "bool", /*default=*/"false",
               "Set LLVM noalias attribute on function arguments, "
               "if possible">,
+       Option<"setReadOnly", "set-readonly", "bool", /*default=*/"false",
+              "Set LLVM readonly attribute on INTENT(IN) by-reference "
+              "function arguments, if possible">,
   ];
 }
 
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 3fcf314faefb6..c29e12fa31794 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -187,6 +187,30 @@ asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject &&dummy) {
                                                        std::move(shape)));
 }
 
+/// An INTENT(IN) data object passed by reference is not modified by the callee,
+/// so the LLVM `readonly` argument attribute can be set. Notes on the
+/// exclusions:
+///   - VALUE, POINTER, and ALLOCATABLE dummies require ABI- or
+///     descriptor-specific handling before `readonly` can be applied and are
+///     left out of scope;
+///   - ASYNCHRONOUS memory may change underneath the callee;
+///   - VOLATILE+INTENT(IN) is prohibited by the standard (C870); kept here
+///     defensively.
+/// TARGET and derived types are intentionally allowed: any write that
+/// INTENT(IN) still permits (e.g. writing the target of a POINTER component)
+/// goes through a pointer loaded from the object, not through the argument
+/// pointer, and hence does not violate LLVM `readonly` (which only constrains
+/// the argument and pointers based on it).
+static bool dummyArgIsReadOnly(
+    const Fortran::evaluate::characteristics::DummyDataObject &obj) {
+  using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
+  return obj.intent == Fortran::common::Intent::In &&
+         !obj.attrs.test(Attrs::Value) && !obj.attrs.test(Attrs::Pointer) &&
+         !obj.attrs.test(Attrs::Allocatable) &&
+         !obj.attrs.test(Attrs::Asynchronous) &&
+         !obj.attrs.test(Attrs::Volatile);
+}
+
 static Fortran::evaluate::characteristics::DummyArgument
 asImplicitArg(Fortran::evaluate::characteristics::DummyArgument &&dummy) {
   return Fortran::common::visit(
@@ -1084,8 +1108,15 @@ class Fortran::lower::CallInterfaceImpl {
     } else {
       // non-PDT derived type allowed in implicit interface.
       mlir::Type refType = getRefType(dynamicType, obj);
+      llvm::SmallVector<mlir::NamedAttribute> attrs = dummyNameAttr(entity);
+      // Propagate the INTENT(IN) contract for by-reference dummies handled by
+      // the implicit-interface path.
+      if (dummyArgIsReadOnly(obj))
+        attrs.emplace_back(
+            mlir::StringAttr::get(&mlirContext, fir::getReadOnlyAttrName()),
+            mlir::UnitAttr::get(&mlirContext));
       addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress,
-                    dummyNameAttr(entity));
+                    attrs);
       addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
     }
   }
@@ -1144,6 +1175,12 @@ class Fortran::lower::CallInterfaceImpl {
           mlir::StringAttr::get(&mlirContext, cuf::getDataAttrName()),
           cuf::getDataAttribute(&mlirContext, obj.cudaDataAttr));
 
+    // INTENT(IN) by-reference dummies can be marked readonly (see
+    // dummyArgIsReadOnly). On non-reference (e.g. box) arguments the marker is
+    // a no-op: the FunctionAttr pass only translates it for fir::ReferenceType.
+    if (dummyArgIsReadOnly(obj))
+      addMLIRAttr(fir::getReadOnlyAttrName());
+
     // TODO: intents that require special care (e.g finalization)
 
     if (obj.type.corank() > 0)
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index a4e9d0c227817..c023a67f874e8 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -446,13 +446,14 @@ void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm,
   // function specialization is fixed.
   bool setNoAlias = forceNoAlias;
   bool setNoCapture = config.OptLevel.isOptimizingForSpeed();
+  bool setReadOnly = config.OptLevel.isOptimizingForSpeed();
 
   pm.addPass(fir::createFunctionAttr(
       {framePointerKind, config.InstrumentFunctionEntry,
        config.InstrumentFunctionExit, config.NoInfsFPMath, config.NoNaNsFPMath,
        config.ApproxFuncFPMath, config.NoSignedZerosFPMath, config.UnsafeFPMath,
        config.Reciprocals, config.PreferVectorWidth, config.UseSampleProfile,
-       /*tuneCPU=*/"", setNoCapture, setNoAlias}));
+       /*tuneCPU=*/"", setNoCapture, setNoAlias, setReadOnly}));
 
   if (config.EnableOpenMP) {
     pm.addNestedPass<mlir::func::FuncOp>(
diff --git a/flang/lib/Optimizer/Transforms/FunctionAttr.cpp b/flang/lib/Optimizer/Transforms/FunctionAttr.cpp
index b49803e989265..45b32d13ad62e 100644
--- a/flang/lib/Optimizer/Transforms/FunctionAttr.cpp
+++ b/flang/lib/Optimizer/Transforms/FunctionAttr.cpp
@@ -54,31 +54,46 @@ void FunctionAttrPass::runOnOperation() {
   auto deconstructed = fir::NameUniquer::deconstruct(name);
   bool isFromModule = !deconstructed.second.modules.empty();
 
-  if ((isFromModule || !func.isDeclaration()) &&
-      !fir::hasBindcAttr(func.getOperation())) {
+  if (isFromModule || !func.isDeclaration()) {
+    const bool isBindC = fir::hasBindcAttr(func.getOperation());
+    const bool isDefinition = !func.isDeclaration();
     llvm::StringRef nocapture = mlir::LLVM::LLVMDialect::getNoCaptureAttrName();
     llvm::StringRef noalias = mlir::LLVM::LLVMDialect::getNoAliasAttrName();
+    llvm::StringRef readonly = mlir::LLVM::LLVMDialect::getReadonlyAttrName();
     mlir::UnitAttr unitAttr = mlir::UnitAttr::get(func.getContext());
 
     for (auto [index, argType] : llvm::enumerate(func.getArgumentTypes())) {
       bool isNoCapture = false;
       bool isNoAlias = false;
-      if (mlir::isa<fir::ReferenceType>(argType) &&
-          !func.getArgAttr(index, fir::getTargetAttrName()) &&
-          !func.getArgAttr(index, fir::getAsynchronousAttrName()) &&
-          !func.getArgAttr(index, fir::getVolatileAttrName())) {
-        isNoCapture = true;
-        isNoAlias = !fir::isPointerType(argType);
+      bool isReadOnly = false;
+      if (mlir::isa<fir::ReferenceType>(argType)) {
+        // The read-only marker is attached to by-reference dummies the callee
+        // is guaranteed not to write through (see CallInterface). Unlike
+        // noalias/nocapture, it is valid even for TARGET arguments, so it is
+        // computed independently of the target/asynchronous/volatile gating.
+        isReadOnly = static_cast<bool>(
+            func.getArgAttr(index, fir::getReadOnlyAttrName()));
+        if (!func.getArgAttr(index, fir::getTargetAttrName()) &&
+            !func.getArgAttr(index, fir::getAsynchronousAttrName()) &&
+            !func.getArgAttr(index, fir::getVolatileAttrName())) {
+          isNoCapture = true;
+          isNoAlias = !fir::isPointerType(argType);
+        }
       } else if (mlir::isa<fir::BaseBoxType>(argType)) {
         // !fir.box arguments will be passed as descriptor pointers
         // at LLVM IR dialect level - they cannot be captured,
         // and cannot alias with anything within the function.
         isNoCapture = isNoAlias = true;
       }
-      if (isNoCapture && setNoCapture)
+      // nocapture/noalias are not applied to BIND(C) interfaces (C ABI).
+      if (isNoCapture && setNoCapture && !isBindC)
         func.setArgAttr(index, nocapture, unitAttr);
-      if (isNoAlias && setNoAlias)
+      if (isNoAlias && setNoAlias && !isBindC)
         func.setArgAttr(index, noalias, unitAttr);
+      // readonly is also valid for BIND(C) definitions (the Fortran body must
+      // honor INTENT(IN)), but not for declarations of external C functions.
+      if (isReadOnly && setReadOnly && (!isBindC || isDefinition))
+        func.setArgAttr(index, readonly, unitAttr);
     }
   }
 
diff --git a/flang/test/Driver/function-attr-readonly.f90 b/flang/test/Driver/function-attr-readonly.f90
new file mode 100644
index 0000000000000..fa1e079bf5069
--- /dev/null
+++ b/flang/test/Driver/function-attr-readonly.f90
@@ -0,0 +1,37 @@
+! Verify that the default pipeline translates the lowering marker only at
+! optimization levels that optimize for speed, and that llvm.readonly reaches
+! the final LLVM IR.
+! force-no-alias currently defaults to true, so llvm.noalias is expected at
+! both levels; llvm.nocapture and llvm.readonly are the attributes gated here.
+!
+! RUN: %flang_fc1 -emit-llvm -O0 \
+! RUN:   -mmlir --mlir-disable-threading \
+! RUN:   -mmlir --mlir-print-ir-after=function-attr \
+! RUN:   -mmlir --mlir-print-ir-module-scope %s -o /dev/null 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=O0
+! RUN: %flang_fc1 -emit-llvm -O1 \
+! RUN:   -mmlir --mlir-disable-threading \
+! RUN:   -mmlir --mlir-print-ir-after=function-attr \
+! RUN:   -mmlir --mlir-print-ir-module-scope %s -o /dev/null 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=O1
+! RUN: %flang_fc1 -emit-llvm -O1 %s -o - | FileCheck %s --check-prefix=LLVM
+
+module readonly_pipeline_mod
+contains
+  subroutine readonly_pipeline(x, y)
+    integer, intent(in) :: x
+    integer, intent(inout) :: y
+    y = x
+  end subroutine
+end module
+
+! O0-LABEL: func.func @_QMreadonly_pipeline_modPreadonly_pipeline(
+! O0-SAME:    %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x", fir.read_only, llvm.noalias},
+! O0-SAME:    %{{.*}}: !fir.ref<i32> {fir.bindc_name = "y", llvm.noalias}) {
+
+! O1-LABEL: func.func @_QMreadonly_pipeline_modPreadonly_pipeline(
+! O1-SAME:    %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x", fir.read_only, llvm.noalias, llvm.nocapture, llvm.readonly},
+! O1-SAME:    %{{.*}}: !fir.ref<i32> {fir.bindc_name = "y", llvm.noalias, llvm.nocapture}) {
+
+! LLVM-LABEL: define void @_QMreadonly_pipeline_modPreadonly_pipeline(
+! LLVM-SAME:    ptr {{.*}}readonly{{.*}} %0,
diff --git a/flang/test/Lower/HLFIR/elemental-array-ops.f90 b/flang/test/Lower/HLFIR/elemental-array-ops.f90
index 9007e26b05a64..8abe6acfb6cea 100644
--- a/flang/test/Lower/HLFIR/elemental-array-ops.f90
+++ b/flang/test/Lower/HLFIR/elemental-array-ops.f90
@@ -155,8 +155,8 @@ end function callee
   l = x==callee(y)
 end subroutine char_return
 ! CHECK-LABEL:   func.func @_QPchar_return(
-! CHECK-SAME:                              %[[VAL_0:.*]]: !fir.box<!fir.array<?x!fir.char<1,3>>> {fir.bindc_name = "x"},
-! CHECK-SAME:                              %[[VAL_1:.*]]: !fir.box<!fir.array<?x!fir.char<1,3>>> {fir.bindc_name = "y"}) {
+! CHECK-SAME:                              %[[VAL_0:.*]]: !fir.box<!fir.array<?x!fir.char<1,3>>> {fir.bindc_name = "x", fir.read_only},
+! CHECK-SAME:                              %[[VAL_1:.*]]: !fir.box<!fir.array<?x!fir.char<1,3>>> {fir.bindc_name = "y", fir.read_only}) {
 ! CHECK:           %[[VAL_2:.*]] = fir.alloca !fir.char<1,3> {bindc_name = ".result"}
 ! CHECK:           %[[VAL_3:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.logical<4>>>> {bindc_name = "l", uniq_name = "_QFchar_returnEl"}
 ! CHECK:           %[[VAL_4:.*]] = fir.zero_bits !fir.heap<!fir.array<?x!fir.logical<4>>>
@@ -209,7 +209,7 @@ subroutine polymorphic_parenthesis(x, y)
 end subroutine polymorphic_parenthesis
 ! CHECK-LABEL:   func.func @_QPpolymorphic_parenthesis(
 ! CHECK-SAME:        %[[VAL_0:.*]]: !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>>> {fir.bindc_name = "x"},
-! CHECK-SAME:        %[[VAL_1:.*]]: !fir.class<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>> {fir.bindc_name = "y"}) {
+! CHECK-SAME:        %[[VAL_1:.*]]: !fir.class<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>> {fir.bindc_name = "y", fir.read_only}) {
 ! CHECK:           %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFpolymorphic_parenthesisEx"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>>>, !fir.dscope) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>>>)
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFpolymorphic_parenthesisEy"} : (!fir.class<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>, !fir.dscope) -> (!fir.class<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>, !fir.class<!fir.array<?x!fir.type<_QFpolymorphic_parenthesisTt>>>)
 ! CHECK:           %[[VAL_4:.*]] = arith.constant 0 : index
@@ -233,7 +233,7 @@ subroutine unlimited_polymorphic_parenthesis(x, y)
 end subroutine unlimited_polymorphic_parenthesis
 ! CHECK-LABEL:   func.func @_QPunlimited_polymorphic_parenthesis(
 ! CHECK-SAME:        %[[VAL_0:.*]]: !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>> {fir.bindc_name = "x"},
-! CHECK-SAME:        %[[VAL_1:.*]]: !fir.class<!fir.array<?xnone>> {fir.bindc_name = "y"}) {
+! CHECK-SAME:        %[[VAL_1:.*]]: !fir.class<!fir.array<?xnone>> {fir.bindc_name = "y", fir.read_only}) {
 ! CHECK:           %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFunlimited_polymorphic_parenthesisEx"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>, !fir.dscope) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>)
 ! CHECK:           %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFunlimited_polymorphic_parenthesisEy"} : (!fir.class<!fir.array<?xnone>>, !fir.dscope) -> (!fir.class<!fir.array<?xnone>>, !fir.class<!fir.array<?xnone>>)
 ! CHECK:           %[[VAL_4:.*]] = arith.constant 0 : index
diff --git a/flang/test/Lower/HLFIR/elemental-polymorphic-merge.f90 b/flang/test/Lower/HLFIR/elemental-polymorphic-merge.f90
index 7453ecaafd373..cd8d329afd57c 100644
--- a/flang/test/Lower/HLFIR/elemental-polymorphic-merge.f90
+++ b/flang/test/Lower/HLFIR/elemental-polymorphic-merge.f90
@@ -10,8 +10,8 @@ subroutine test_polymorphic_merge(x, y, r, m)
   r = merge(x, y, m)
 end subroutine test_polymorphic_merge
 ! CHECK-LABEL:   func.func @_QPtest_polymorphic_merge(
-! CHECK-SAME:        %[[VAL_0:.*]]: !fir.class<!fir.type<_QFtest_polymorphic_mergeTt>> {fir.bindc_name = "x"},
-! CHECK-SAME:        %[[VAL_1:.*]]: !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphic_mergeTt>>> {fir.bindc_name = "y"},
+! CHECK-SAME:        %[[VAL_0:.*]]: !fir.class<!fir.type<_QFtest_polymorphic_mergeTt>> {fir.bindc_name = "x", fir.read_only},
+! CHECK-SAME:        %[[VAL_1:.*]]: !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphic_mergeTt>>> {fir.bindc_name = "y", fir.read_only},
 ! CHECK-SAME:        %[[VAL_2:.*]]: !fir.ref<!fir.class<!fir.heap<!fir.array<?x!fir.type<_QFtest_polymorphic_mergeTt>>>>> {fir.bindc_name = "r"},
 ! CHECK-SAME:        %[[VAL_3:.*]]: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "m"}) {
 ! CHECK:           %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_3]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {uniq_name = "_QFtest_polymorphic_mergeEm"} : (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.box<!fir.array<?x!fir.logical<4>>>)
diff --git a/flang/test/Lower/HLFIR/elemental-result-length.f90 b/flang/test/Lower/HLFIR/elemental-result-length.f90
index 95298ef2fc41f..444d1c6d4cb7f 100644
--- a/flang/test/Lower/HLFIR/elemental-result-length.f90
+++ b/flang/test/Lower/HLFIR/elemental-result-length.f90
@@ -43,7 +43,7 @@ subroutine sub4(a,b,c)
 end subroutine
 
 ! CHECK-LABEL: func.func @_QMm1Psub4(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "a"}, %[[ARG1:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "b"}, %[[ARG2:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "c"}) {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "a", fir.read_only}, %[[ARG1:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "b", fir.read_only}, %[[ARG2:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "c"}) {
 ! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{.*}} {{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Fsub4Ea"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
 ! CHECK: %[[B:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{.*}} {{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Fsub4Eb"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
 ! CHECK: %[[C:.*]]:2 = hlfir.declare %[[ARG2]] dummy_scope %{{.*}} {{.*}} {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QMm1Fsub4Ec"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
diff --git a/flang/test/Lower/HLFIR/structure-constructor.f90 b/flang/test/Lower/HLFIR/structure-constructor.f90
index f2d2c094198b3..f937dc0b23d0e 100644
--- a/flang/test/Lower/HLFIR/structure-constructor.f90
+++ b/flang/test/Lower/HLFIR/structure-constructor.f90
@@ -314,7 +314,7 @@ subroutine test7(n)
   x = t7(n)
 end subroutine test7
 ! CHECK-LABEL:   func.func @_QPtest7(
-! CHECK-SAME:                        %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "n"}) {
+! CHECK-SAME:                        %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "n", fir.read_only}) {
 ! CHECK:           %[[VAL_1:.*]] = fir.alloca !fir.type<_QMtypesTt7{c1:i32,c2:!fir.box<!fir.heap<!fir.array<?xf32>>>}>
 ! CHECK:           %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest7En"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
 ! CHECK:           %[[VAL_3:.*]] = fir.alloca !fir.type<_QMtypesTt7{c1:i32,c2:!fir.box<!fir.heap<!fir.array<?xf32>>>}> {bindc_name = "x", uniq_name = "_QFtest7Ex"}
diff --git a/flang/test/Lower/Intrinsics/c_devptr_eq_ne.f90 b/flang/test/Lower/Intrinsics/c_devptr_eq_ne.f90
index 7188bd177a1a3..1477ce638334d 100644
--- a/flang/test/Lower/Intrinsics/c_devptr_eq_ne.f90
+++ b/flang/test/Lower/Intrinsics/c_devptr_eq_ne.f90
@@ -10,7 +10,7 @@ function test_c_devptr_eq(d1, d2)
 end
 
 ! CHECK-LABEL: func.func @_QPtest_c_devptr_eq(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_dev...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/207732


More information about the flang-commits mailing list