[flang-commits] [flang] [flang] Propagate INTENT(IN) dummy arguments as readonly (PR #207732)
Sergey Shcherbinin via flang-commits
flang-commits at lists.llvm.org
Mon Jul 6 06:37:14 PDT 2026
https://github.com/SergeyShch01 updated https://github.com/llvm/llvm-project/pull/207732
>From 81fe5ccdf7752160564c8af6b53b4e440e1b0078 Mon Sep 17 00:00:00 2001
From: Sergey Shcherbinin <sscherbinin at nvidia.com>
Date: Sat, 4 Jul 2026 17:59:50 +0400
Subject: [PATCH] [flang] Propagate INTENT(IN) dummy arguments as readonly
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.
---
.../flang/Optimizer/Dialect/FIROpsSupport.h | 12 ++
.../flang/Optimizer/Transforms/Passes.td | 3 +
flang/lib/Lower/CallInterface.cpp | 39 ++++-
flang/lib/Optimizer/Passes/Pipelines.cpp | 3 +-
.../lib/Optimizer/Transforms/FunctionAttr.cpp | 35 +++--
flang/test/Driver/function-attr-readonly.f90 | 37 +++++
.../test/Lower/HLFIR/elemental-array-ops.f90 | 8 +-
.../HLFIR/elemental-polymorphic-merge.f90 | 4 +-
.../Lower/HLFIR/elemental-result-length.f90 | 2 +-
.../Lower/HLFIR/structure-constructor.f90 | 2 +-
.../test/Lower/Intrinsics/c_devptr_eq_ne.f90 | 4 +-
flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90 | 4 +-
flang/test/Lower/Intrinsics/dconjg.f90 | 2 +-
flang/test/Lower/Intrinsics/dimag.f90 | 2 +-
flang/test/Lower/Intrinsics/dreal.f90 | 2 +-
flang/test/Lower/Intrinsics/reduce.f90 | 2 +-
flang/test/Lower/OpenACC/acc-declare.f90 | 4 +-
.../Lower/OpenMP/optional-argument-map-2.f90 | 2 +-
.../Lower/OpenMP/optional-argument-map.f90 | 2 +-
.../test/Lower/OpenMP/wsloop-unstructured.f90 | 8 +-
flang/test/Lower/achar.f90 | 2 +-
flang/test/Lower/arguments.f90 | 4 +-
.../array-elemental-calls-char-dynamic.f90 | 4 +-
flang/test/Lower/dispatch.f90 | 4 +-
flang/test/Lower/dummy-argument-readonly.f90 | 144 ++++++++++++++++++
flang/test/Lower/parent-component.f90 | 4 +-
flang/test/Lower/polymorphic-temp.f90 | 24 +--
flang/test/Lower/polymorphic.f90 | 23 ++-
flang/test/Lower/repack-arrays.f90 | 2 +-
flang/test/Lower/select-type-2.f90 | 2 +-
flang/test/Lower/select-type.f90 | 12 +-
flang/test/Lower/unsigned-ops.f90 | 5 +-
flang/test/Lower/volatile1.f90 | 2 +-
.../Transforms/function-attrs-readonly.fir | 73 +++++++++
34 files changed, 401 insertions(+), 81 deletions(-)
create mode 100644 flang/test/Driver/function-attr-readonly.f90
create mode 100644 flang/test/Lower/dummy-argument-readonly.f90
create mode 100644 flang/test/Transforms/function-attrs-readonly.fir
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_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d1"}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d2"}) -> !fir.logical<4> {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d1", fir.read_only}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d2", fir.read_only}) -> !fir.logical<4> {
! CHECK: %[[DECL_ARG0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_devptr_eqEd1"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>)
! CHECK: %[[DECL_ARG1:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_devptr_eqEd2"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>)
! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.logical<4> {bindc_name = "test_c_devptr_eq", uniq_name = "_QFtest_c_devptr_eqEtest_c_devptr_eq"}
@@ -38,7 +38,7 @@ function test_c_devptr_ne(d1, d2)
end
! CHECK-LABEL: func.func @_QPtest_c_devptr_ne(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d1"}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d2"}) -> !fir.logical<4> {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d1", fir.read_only}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "d2", fir.read_only}) -> !fir.logical<4> {
! CHECK: %[[DECL_ARG0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_devptr_neEd1"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>)
! CHECK: %[[DECL_ARG1:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_devptr_neEd2"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>)
! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.logical<4> {bindc_name = "test_c_devptr_ne", uniq_name = "_QFtest_c_devptr_neEtest_c_devptr_ne"}
diff --git a/flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90 b/flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90
index 16c2452dcbdbf..593a55e824dea 100644
--- a/flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90
+++ b/flang/test/Lower/Intrinsics/c_ptr_eq_ne.f90
@@ -9,7 +9,7 @@ function test_c_ptr_eq(ptr1, ptr2)
end
! CHECK-LABEL: func.func @_QPtest_c_ptr_eq(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr1"}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr2"}) -> !fir.logical<4> {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr1", fir.read_only}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr2", fir.read_only}) -> !fir.logical<4> {
! CHECK: %[[DECL_ARG0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_ptr_eqEptr1"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>)
! CHECK: %[[DECL_ARG1:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_ptr_eqEptr2"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>)
! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.logical<4> {bindc_name = "test_c_ptr_eq", uniq_name = "_QFtest_c_ptr_eqEtest_c_ptr_eq"}
@@ -34,7 +34,7 @@ function test_c_ptr_ne(ptr1, ptr2)
end
! CHECK-LABEL: func.func @_QPtest_c_ptr_ne(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr1"}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr2"}) -> !fir.logical<4> {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr1", fir.read_only}, %[[ARG1:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "ptr2", fir.read_only}) -> !fir.logical<4> {
! CHECK: %[[DECL_ARG0:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_ptr_neEptr1"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>)
! CHECK: %[[DECL_ARG1:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{[0-9]+}} arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_c_ptr_neEptr2"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>)
! CHECK: %[[ALLOCA:.*]] = fir.alloca !fir.logical<4> {bindc_name = "test_c_ptr_ne", uniq_name = "_QFtest_c_ptr_neEtest_c_ptr_ne"}
diff --git a/flang/test/Lower/Intrinsics/dconjg.f90 b/flang/test/Lower/Intrinsics/dconjg.f90
index 0a219565f5bb5..b223259a39feb 100644
--- a/flang/test/Lower/Intrinsics/dconjg.f90
+++ b/flang/test/Lower/Intrinsics/dconjg.f90
@@ -6,7 +6,7 @@ subroutine test_dconjg(r, c)
! CHECK-LABEL: func @_QPtest_dconjg(
! CHECK-SAME: %[[ARG_0:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "r"},
-! CHECK-SAME: %[[ARG_1:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "c"}) {
+! CHECK-SAME: %[[ARG_1:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "c", fir.read_only}) {
! CHECK: %[[DS:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[C_DECL:.*]]:2 = hlfir.declare %[[ARG_1]] dummy_scope %[[DS]] {{.*}}
! CHECK: %[[R_DECL:.*]]:2 = hlfir.declare %[[ARG_0]] dummy_scope %[[DS]] {{.*}}
diff --git a/flang/test/Lower/Intrinsics/dimag.f90 b/flang/test/Lower/Intrinsics/dimag.f90
index a90fb31e5864b..7826d441b2e9c 100644
--- a/flang/test/Lower/Intrinsics/dimag.f90
+++ b/flang/test/Lower/Intrinsics/dimag.f90
@@ -6,7 +6,7 @@ subroutine test_dimag(r, c)
! CHECK-LABEL: func @_QPtest_dimag(
! CHECK-SAME: %[[ARG_0:.*]]: !fir.ref<f64> {fir.bindc_name = "r"},
-! CHECK-SAME: %[[ARG_1:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "c"}) {
+! CHECK-SAME: %[[ARG_1:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "c", fir.read_only}) {
! CHECK: %[[DS:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[C_DECL:.*]]:2 = hlfir.declare %[[ARG_1]] dummy_scope %[[DS]] {{.*}}
! CHECK: %[[R_DECL:.*]]:2 = hlfir.declare %[[ARG_0]] dummy_scope %[[DS]] {{.*}}
diff --git a/flang/test/Lower/Intrinsics/dreal.f90 b/flang/test/Lower/Intrinsics/dreal.f90
index 13d8cabba9e64..3f72aa7d3d630 100644
--- a/flang/test/Lower/Intrinsics/dreal.f90
+++ b/flang/test/Lower/Intrinsics/dreal.f90
@@ -6,7 +6,7 @@ subroutine test_dreal(r, c)
! CHECK-LABEL: func.func @_QPtest_dreal(
! CHECK-SAME: %[[ARG_0:.*]]: !fir.ref<f64> {fir.bindc_name = "r"},
-! CHECK-SAME: %[[ARG_1:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "c"}) {
+! CHECK-SAME: %[[ARG_1:.*]]: !fir.ref<complex<f64>> {fir.bindc_name = "c", fir.read_only}) {
! CHECK: %[[DS:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[C:.*]]:2 = hlfir.declare %[[ARG_1]] dummy_scope %[[DS]]
! CHECK: %[[R:.*]]:2 = hlfir.declare %[[ARG_0]] dummy_scope %[[DS]]
diff --git a/flang/test/Lower/Intrinsics/reduce.f90 b/flang/test/Lower/Intrinsics/reduce.f90
index 27c4277ffebeb..c69e3e92025d3 100644
--- a/flang/test/Lower/Intrinsics/reduce.f90
+++ b/flang/test/Lower/Intrinsics/reduce.f90
@@ -64,7 +64,7 @@ subroutine integer1(a, id, d1, d2)
end subroutine
! CHECK-LABEL: func.func @_QMreduce_modPinteger1(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?xi8>> {fir.bindc_name = "a"}, %[[ARG1:.*]]: !fir.ref<i8> {fir.bindc_name = "id"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?xi8>> {fir.bindc_name = "a", fir.read_only}, %[[ARG1:.*]]: !fir.ref<i8> {fir.bindc_name = "id"}
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMreduce_modFinteger1Ea"} : (!fir.box<!fir.array<?xi8>>, !fir.dscope) -> (!fir.box<!fir.array<?xi8>>, !fir.box<!fir.array<?xi8>>)
! CHECK: %[[ID:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{.*}} {uniq_name = "_QMreduce_modFinteger1Eid"} : (!fir.ref<i8>, !fir.dscope) -> (!fir.ref<i8>, !fir.ref<i8>)
! CHECK: %[[ALLOC_RES:.*]] = fir.alloca i8 {bindc_name = "res", uniq_name = "_QMreduce_modFinteger1Eres"}
diff --git a/flang/test/Lower/OpenACC/acc-declare.f90 b/flang/test/Lower/OpenACC/acc-declare.f90
index 3339fa31c3d27..ab1e76e20e380 100644
--- a/flang/test/Lower/OpenACC/acc-declare.f90
+++ b/flang/test/Lower/OpenACC/acc-declare.f90
@@ -74,8 +74,8 @@ subroutine acc_declare_present_host_and_comp(host, coeffs)
end subroutine
! CHECK-LABEL: func.func @_QMacc_declarePacc_declare_present_host_and_comp(
-! CHECK-SAME: %[[ARGHOST:.*]]: !fir.ref<!fir.type<_QMacc_declare{{.*}}Tdt_box{{.*}}>> {fir.bindc_name = "host"},
-! CHECK-SAME: %[[ARGCOEFF:.*]]: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "coeffs"})
+! CHECK-SAME: %[[ARGHOST:.*]]: !fir.ref<!fir.type<_QMacc_declare{{.*}}Tdt_box{{.*}}>> {fir.bindc_name = "host", fir.read_only},
+! CHECK-SAME: %[[ARGCOEFF:.*]]: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "coeffs", fir.read_only})
! CHECK-DAG: acc.present{{.*}}name = "host"
! CHECK-DAG: acc.present{{.*}}name = "host%buf"
! CHECK-DAG: acc.present{{.*}}name = "coeffs"
diff --git a/flang/test/Lower/OpenMP/optional-argument-map-2.f90 b/flang/test/Lower/OpenMP/optional-argument-map-2.f90
index 58a3654bf7f94..2d7e0a9a96bd8 100644
--- a/flang/test/Lower/OpenMP/optional-argument-map-2.f90
+++ b/flang/test/Lower/OpenMP/optional-argument-map-2.f90
@@ -54,7 +54,7 @@ end module mod
! CHECK: }
! CHECK-LABEL: func.func @_QMmodProutine_boxchar(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.boxchar<1> {fir.bindc_name = "a", fir.optional}) {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.boxchar<1> {fir.bindc_name = "a", fir.optional, fir.read_only}) {
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.boxchar<1>
! CHECK: %[[VAL_1:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[VAL_2:.*]]:2 = fir.unboxchar %[[ARG0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
diff --git a/flang/test/Lower/OpenMP/optional-argument-map.f90 b/flang/test/Lower/OpenMP/optional-argument-map.f90
index 863742ce67927..84c2cc76495ae 100644
--- a/flang/test/Lower/OpenMP/optional-argument-map.f90
+++ b/flang/test/Lower/OpenMP/optional-argument-map.f90
@@ -15,7 +15,7 @@ end subroutine test
end module foo
! CHECK-LABEL: func.func @_QMfooPtest(
-! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "i"},
+! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only},
! CHECK-SAME: %[[VAL_1:.*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "a", fir.optional}) {
! CHECK: %[[VAL_2:.*]] = fir.alloca !fir.box<!fir.array<?xf32>>
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_inout, optional>, uniq_name = "_QMfooFtestEa"} : (!fir.box<!fir.array<?xf32>>, !fir.dscope) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>)
diff --git a/flang/test/Lower/OpenMP/wsloop-unstructured.f90 b/flang/test/Lower/OpenMP/wsloop-unstructured.f90
index 6174718c08758..8ba56bac11e1d 100644
--- a/flang/test/Lower/OpenMP/wsloop-unstructured.f90
+++ b/flang/test/Lower/OpenMP/wsloop-unstructured.f90
@@ -24,10 +24,10 @@ end subroutine sub
! that all blocks are terminated
! CHECK-LABEL: func.func @_QPsub(
-! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "imax"},
-! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<i32> {fir.bindc_name = "jmax"},
-! CHECK-SAME: %[[VAL_2:.*]]: !fir.ref<!fir.array<?x?xf32>> {fir.bindc_name = "x"},
-! CHECK-SAME: %[[VAL_3:.*]]: !fir.ref<!fir.array<?x?xf32>> {fir.bindc_name = "y"}) {
+! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "imax", fir.read_only},
+! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<i32> {fir.bindc_name = "jmax", fir.read_only},
+! CHECK-SAME: %[[VAL_2:.*]]: !fir.ref<!fir.array<?x?xf32>> {fir.bindc_name = "x", fir.read_only},
+! CHECK-SAME: %[[VAL_3:.*]]: !fir.ref<!fir.array<?x?xf32>> {fir.bindc_name = "y", fir.read_only}) {
! [...]
! CHECK: omp.wsloop private({{.*}}) {
! CHECK-NEXT: omp.loop_nest (%[[VAL_53:.*]], %[[VAL_54:.*]]) : i32 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}}) {
diff --git a/flang/test/Lower/achar.f90 b/flang/test/Lower/achar.f90
index 63bea0076c597..ae35db348a33d 100644
--- a/flang/test/Lower/achar.f90
+++ b/flang/test/Lower/achar.f90
@@ -12,7 +12,7 @@ subroutine achar_test1(a)
end subroutine achar_test1
! CHECK-LABEL: func.func @_QPachar_test1(
-! CHECK-SAME: %[[ARG:.*]]: !fir.ref<i32> {fir.bindc_name = "a"}) {
+! CHECK-SAME: %[[ARG:.*]]: !fir.ref<i32> {fir.bindc_name = "a", fir.read_only}) {
! CHECK: %[[TMP:.*]] = fir.alloca !fir.char<1>
! CHECK: %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG]] dummy_scope %[[DSCOPE]] arg 1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFachar_test1Ea"}
diff --git a/flang/test/Lower/arguments.f90 b/flang/test/Lower/arguments.f90
index f54fa6868d554..9667494f0f011 100644
--- a/flang/test/Lower/arguments.f90
+++ b/flang/test/Lower/arguments.f90
@@ -7,7 +7,7 @@ subroutine sub1(a, b)
! Check that arguments are correctly set and no local allocation is happening.
! CHECK-LABEL: func @_QPsub1(
-! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "a"}, %{{.*}}: !fir.ref<!fir.logical<4>> {fir.bindc_name = "b"})
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "a", fir.read_only}, %{{.*}}: !fir.ref<!fir.logical<4>> {fir.bindc_name = "b"})
! CHECK-NOT: fir.alloc
! CHECK: return
@@ -31,7 +31,7 @@ integer function fct1(a, b)
end
! CHECK-LABEL: func @_QPfct1(
-! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "a"}, %{{.*}}: !fir.ref<!fir.logical<4>> {fir.bindc_name = "b"}) -> i32
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "a", fir.read_only}, %{{.*}}: !fir.ref<!fir.logical<4>> {fir.bindc_name = "b"}) -> i32
real function fct2(i)
integer :: i(2, 5)
diff --git a/flang/test/Lower/array-elemental-calls-char-dynamic.f90 b/flang/test/Lower/array-elemental-calls-char-dynamic.f90
index 1c3b299c3d4c6..fca2d5c518787 100644
--- a/flang/test/Lower/array-elemental-calls-char-dynamic.f90
+++ b/flang/test/Lower/array-elemental-calls-char-dynamic.f90
@@ -186,8 +186,8 @@ elemental function f_poly(p1, p2)
end subroutine
! CHECK-LABEL: func.func @_QPtest_polymorphic(
! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "res"},
-! CHECK-SAME: %[[ARG1:.*]]: !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>> {fir.bindc_name = "p1"},
-! CHECK-SAME: %[[ARG2:.*]]: !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>> {fir.bindc_name = "p2"}) {
+! CHECK-SAME: %[[ARG1:.*]]: !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>> {fir.bindc_name = "p1", fir.read_only},
+! CHECK-SAME: %[[ARG2:.*]]: !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>> {fir.bindc_name = "p2", fir.read_only}) {
! CHECK: %[[VAL_0:.*]] = fir.dummy_scope : !fir.dscope
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %[[VAL_0]] arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_polymorphicEp1"} : (!fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>>, !fir.dscope) -> (!fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>>, !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>>)
! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[ARG2]] dummy_scope %[[VAL_0]] arg {{[0-9]+}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest_polymorphicEp2"} : (!fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>>, !fir.dscope) -> (!fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>>, !fir.class<!fir.array<?x!fir.type<_QFtest_polymorphicTt>>>)
diff --git a/flang/test/Lower/dispatch.f90 b/flang/test/Lower/dispatch.f90
index 7692bd31d4533..a66287d2b4fd6 100644
--- a/flang/test/Lower/dispatch.f90
+++ b/flang/test/Lower/dispatch.f90
@@ -107,7 +107,7 @@ subroutine p1_proc4_arg1(i, this)
integer, intent(in) :: i
class(p1) :: this
end subroutine
- ! CHECK-LABEL: func.func @_QMcall_dispatchPp1_proc4_arg1(%{{.*}}: !fir.ref<i32> {fir.bindc_name = "i"}, %{{.*}}: !fir.class<!fir.type<_QMcall_dispatchTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
+ ! CHECK-LABEL: func.func @_QMcall_dispatchPp1_proc4_arg1(%{{.*}}: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}, %{{.*}}: !fir.class<!fir.type<_QMcall_dispatchTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
subroutine tbp_nopass()
end subroutine
@@ -127,7 +127,7 @@ subroutine tbp_pass_arg1(i, this)
integer, intent(in) :: i
class(p1) :: this
end subroutine
- ! CHECK-LABEL: func.func @_QMcall_dispatchPtbp_pass_arg1(%{{.*}}: !fir.ref<i32> {fir.bindc_name = "i"}, %{{.*}}: !fir.class<!fir.type<_QMcall_dispatchTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
+ ! CHECK-LABEL: func.func @_QMcall_dispatchPtbp_pass_arg1(%{{.*}}: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}, %{{.*}}: !fir.class<!fir.type<_QMcall_dispatchTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
subroutine check_dispatch(p)
class(p1) :: p
diff --git a/flang/test/Lower/dummy-argument-readonly.f90 b/flang/test/Lower/dummy-argument-readonly.f90
new file mode 100644
index 0000000000000..8728eed30e743
--- /dev/null
+++ b/flang/test/Lower/dummy-argument-readonly.f90
@@ -0,0 +1,144 @@
+! Test the fir.read_only marker produced by CallInterface for INTENT(IN) dummy
+! data objects. The marker is emitted during lowering independently of the
+! optimization level.
+!
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
+
+subroutine scalar_intent_in(x)
+ integer, intent(in) :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPscalar_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x", fir.read_only}) {
+
+subroutine optional_scalar_intent_in(x)
+ integer, intent(in), optional :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPoptional_scalar_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x", fir.optional, fir.read_only}) {
+
+subroutine explicit_shape_intent_in(x)
+ integer, intent(in) :: x(10)
+end subroutine
+! CHECK-LABEL: func.func @_QPexplicit_shape_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "x", fir.read_only}) {
+
+subroutine assumed_size_intent_in(x)
+ integer, intent(in) :: x(*)
+end subroutine
+! CHECK-LABEL: func.func @_QPassumed_size_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.array<?xi32>> {fir.bindc_name = "x", fir.read_only}) {
+
+subroutine target_scalar_intent_in(x)
+ integer, intent(in), target :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPtarget_scalar_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x", fir.read_only, fir.target}) {
+
+subroutine target_explicit_shape_intent_in(x)
+ integer, intent(in), target :: x(10)
+end subroutine
+! CHECK-LABEL: func.func @_QPtarget_explicit_shape_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "x", fir.read_only, fir.target}) {
+
+! CHARACTER uses the dedicated boxchar path in handleImplicitDummy. The
+! current optimization does not annotate the data pointer inside boxchar.
+subroutine character_intent_in(x)
+ character(len=*), intent(in) :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPcharacter_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.boxchar<1> {fir.bindc_name = "x"}) {
+
+module readonly_derived_types
+ type :: plain_type
+ integer :: value
+ end type
+ type :: allocatable_component_type
+ integer, allocatable :: values(:)
+ end type
+ type :: pointer_component_type
+ integer, pointer :: value
+ end type
+contains
+ subroutine derived_plain_intent_in(x)
+ type(plain_type), intent(in) :: x
+ end subroutine
+! CHECK-LABEL: func.func @_QMreadonly_derived_typesPderived_plain_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.type<{{.*}}>> {fir.bindc_name = "x", fir.read_only}) {
+
+ subroutine derived_allocatable_intent_in(x)
+ type(allocatable_component_type), intent(in) :: x
+ end subroutine
+! CHECK-LABEL: func.func @_QMreadonly_derived_typesPderived_allocatable_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.type<{{.*}}>> {fir.bindc_name = "x", fir.read_only}) {
+
+ subroutine derived_pointer_intent_in(x)
+ type(pointer_component_type), intent(in) :: x
+ ! Defining the target through the loaded pointer component is permitted and
+ ! does not violate the shallow LLVM readonly contract on x.
+ if (associated(x%value)) x%value = 42
+ end subroutine
+! CHECK-LABEL: func.func @_QMreadonly_derived_typesPderived_pointer_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.type<{{.*}}>> {fir.bindc_name = "x", fir.read_only}) {
+end module
+
+subroutine bindc_definition_intent_in(x) bind(c)
+ use iso_c_binding, only : c_int
+ integer(c_int), intent(in) :: x
+end subroutine
+! CHECK-LABEL: func.func @bindc_definition_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x", fir.read_only}) attributes
+
+! Assumed-shape arguments are boxes. CallInterface records the source contract,
+! but FunctionAttr currently translates the marker only on ReferenceType.
+subroutine assumed_shape_intent_in(x)
+ integer, intent(in) :: x(:)
+end subroutine
+! CHECK-LABEL: func.func @_QPassumed_shape_intent_in(
+! CHECK-SAME: %{{.*}}: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "x", fir.read_only}) {
+
+subroutine intent_inout(x)
+ integer, intent(inout) :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_inout(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x"}) {
+
+subroutine intent_out(x)
+ integer, intent(out) :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_out(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x"}) {
+
+subroutine intent_unspecified(x)
+ integer :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_unspecified(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.bindc_name = "x"}) {
+
+subroutine intent_in_value(x)
+ integer, intent(in), value :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_in_value(
+! CHECK-SAME: %{{.*}}: i32 {fir.bindc_name = "x"}) {
+
+subroutine intent_in_pointer(x)
+ integer, intent(in), pointer :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_in_pointer(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.box<!fir.ptr<i32>>> {fir.bindc_name = "x"}) {
+
+subroutine intent_in_allocatable(x)
+ integer, intent(in), allocatable :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_in_allocatable(
+! CHECK-SAME: %{{.*}}: !fir.ref<!fir.box<!fir.heap<i32>>> {fir.bindc_name = "x"}) {
+
+subroutine intent_in_asynchronous(x)
+ integer, intent(in), asynchronous :: x
+end subroutine
+! CHECK-LABEL: func.func @_QPintent_in_asynchronous(
+! CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.asynchronous, fir.bindc_name = "x"}) {
+
+! VOLATILE with INTENT(IN) is prohibited by C870 (Fortran 2023) and is already
+! covered by test/Semantics/misc-declarations.f90, so it cannot be exercised by
+! a valid lowering test. The Volatile exclusion in dummyArgIsReadOnly remains a
+! defensive check.
diff --git a/flang/test/Lower/parent-component.f90 b/flang/test/Lower/parent-component.f90
index 66d25a0c12c86..3fb2d761eb2d0 100644
--- a/flang/test/Lower/parent-component.f90
+++ b/flang/test/Lower/parent-component.f90
@@ -29,13 +29,13 @@ subroutine print_scalar(a)
type(p), intent(in) :: a
print*, a
end subroutine
- ! CHECK-LABEL: func.func private @_QFPprint_scalar(%{{.*}}: !fir.ref<!fir.type<_QFTp{a:i32}>> {fir.bindc_name = "a"})
+ ! CHECK-LABEL: func.func private @_QFPprint_scalar(%{{.*}}: !fir.ref<!fir.type<_QFTp{a:i32}>> {fir.bindc_name = "a", fir.read_only})
subroutine print_p(a)
type(p), intent(in) :: a(2)
print*, a
end subroutine
- ! CHECK-LABEL: func.func private @_QFPprint_p(%{{.*}}: !fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>> {fir.bindc_name = "a"})
+ ! CHECK-LABEL: func.func private @_QFPprint_p(%{{.*}}: !fir.ref<!fir.array<2x!fir.type<_QFTp{a:i32}>>> {fir.bindc_name = "a", fir.read_only})
subroutine init_with_slice()
type(c) :: y(2) = [ c(11, 21), c(12, 22) ]
diff --git a/flang/test/Lower/polymorphic-temp.f90 b/flang/test/Lower/polymorphic-temp.f90
index 394c8e2069f2e..c8e499e3ed01a 100644
--- a/flang/test/Lower/polymorphic-temp.f90
+++ b/flang/test/Lower/polymorphic-temp.f90
@@ -63,7 +63,7 @@ subroutine test_temp_from_intrinsic_reshape(i)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_intrinsic_reshape(
-! CHECK-SAME: %[[I:.*]]: !fir.class<!fir.array<20x20xnone>> {fir.bindc_name = "i"}) {
+! CHECK-SAME: %[[I:.*]]: !fir.class<!fir.array<20x20xnone>> {fir.bindc_name = "i", fir.read_only}) {
! CHECK: %[[A:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x?xnone>>> {bindc_name = "a", uniq_name = "_QMpoly_tmpFtest_temp_from_intrinsic_reshapeEa"}
! CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[A]]
! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %[[I]]
@@ -85,7 +85,7 @@ subroutine test_temp_from_intrinsic_pack(i, mask)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_intrinsic_pack(
-! CHECK-SAME: %[[I:.*]]: !fir.class<!fir.array<20x20x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "i"}, %[[MASK:.*]]: !fir.ref<!fir.array<20x20x!fir.logical<4>>> {fir.bindc_name = "mask"}) {
+! CHECK-SAME: %[[I:.*]]: !fir.class<!fir.array<20x20x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "i", fir.read_only}, %[[MASK:.*]]: !fir.ref<!fir.array<20x20x!fir.logical<4>>> {fir.bindc_name = "mask", fir.read_only}) {
! CHECK: %[[TMP_RES:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>>>
! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %[[I]]
! CHECK: %[[MASK_DECL:.*]]:2 = hlfir.declare %[[MASK]]
@@ -109,7 +109,7 @@ subroutine test_temp_from_unpack(v, m, f)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_unpack(
-! CHECK-SAME: %[[V:.*]]: !fir.class<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "v"}, %[[M:.*]]: !fir.box<!fir.array<?x?x!fir.logical<4>>> {fir.bindc_name = "m"}, %[[F:.*]]: !fir.class<!fir.array<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "f"}) {
+! CHECK-SAME: %[[V:.*]]: !fir.class<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "v", fir.read_only}, %[[M:.*]]: !fir.box<!fir.array<?x?x!fir.logical<4>>> {fir.bindc_name = "m", fir.read_only}, %[[F:.*]]: !fir.class<!fir.array<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "f", fir.read_only}) {
! CHECK: %[[TMP_RES:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>>>>
! CHECK: %[[F_DECL:.*]]:2 = hlfir.declare %[[F]]
! CHECK: %[[M_DECL:.*]]:2 = hlfir.declare %[[M]]
@@ -132,7 +132,7 @@ subroutine test_temp_from_intrinsic_cshift(a, shift)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_intrinsic_cshift(
-! CHECK-SAME: %[[ARRAY:.*]]: !fir.class<!fir.array<20x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "a"}, %[[SHIFT:.*]]: !fir.ref<i32> {fir.bindc_name = "shift"}) {
+! CHECK-SAME: %[[ARRAY:.*]]: !fir.class<!fir.array<20x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "a", fir.read_only}, %[[SHIFT:.*]]: !fir.ref<i32> {fir.bindc_name = "shift"}) {
! CHECK: %[[ARRAY_DECL:.*]]:2 = hlfir.declare %[[ARRAY]]
! CHECK: %[[SHIFT_DECL:.*]]:2 = hlfir.declare %[[SHIFT]]
! CHECK: %[[CSHIFT:.*]] = hlfir.cshift %[[ARRAY_DECL]]#0 %[[SHIFT_DECL]]#0 : (!fir.class<!fir.array<20x!fir.type<_QMpoly_tmpTp1{a:i32}>>>, !fir.ref<i32>) -> !hlfir.expr<20x!fir.type<_QMpoly_tmpTp1{a:i32}>?>
@@ -146,7 +146,7 @@ subroutine test_temp_from_intrinsic_eoshift(a, shift, b)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_intrinsic_eoshift(
-! CHECK-SAME: %[[ARRAY:.*]]: !fir.class<!fir.array<20x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "a"}, %[[SHIFT:.*]]: !fir.ref<i32> {fir.bindc_name = "shift"}, %[[BOUNDARY:.*]]: !fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>> {fir.bindc_name = "b"}) {
+! CHECK-SAME: %[[ARRAY:.*]]: !fir.class<!fir.array<20x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "a", fir.read_only}, %[[SHIFT:.*]]: !fir.ref<i32> {fir.bindc_name = "shift"}, %[[BOUNDARY:.*]]: !fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>> {fir.bindc_name = "b", fir.read_only}) {
! CHECK: %[[ARRAY_DECL:.*]]:2 = hlfir.declare %[[ARRAY]]
! CHECK: %[[BOUNDARY_DECL:.*]]:2 = hlfir.declare %[[BOUNDARY]]
! CHECK: %[[SHIFT_DECL:.*]]:2 = hlfir.declare %[[SHIFT]]
@@ -159,7 +159,7 @@ subroutine test_temp_from_intrinsic_transfer(source, mold)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_intrinsic_transfer(
-! CHECK-SAME: %[[SOURCE:.*]]: !fir.class<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "source"}, %[[MOLD:.*]]: !fir.class<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "mold"}) {
+! CHECK-SAME: %[[SOURCE:.*]]: !fir.class<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "source", fir.read_only}, %[[MOLD:.*]]: !fir.class<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "mold", fir.read_only}) {
! CHECK: %[[TMP_RES:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x!fir.type<_QMpoly_tmpTp1{a:i32}>>>>
! CHECK: %[[MOLD_DECL:.*]]:2 = hlfir.declare %[[MOLD]]
! CHECK: %[[SOURCE_DECL:.*]]:2 = hlfir.declare %[[SOURCE]]
@@ -174,7 +174,7 @@ subroutine test_temp_from_intrinsic_transpose(matrix)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_temp_from_intrinsic_transpose(
-! CHECK-SAME: %[[MATRIX:.*]]: !fir.class<!fir.array<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "matrix"}) {
+! CHECK-SAME: %[[MATRIX:.*]]: !fir.class<!fir.array<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>>> {fir.bindc_name = "matrix", fir.read_only}) {
! CHECK: %[[MATRIX_DECL:.*]]:2 = hlfir.declare %[[MATRIX]]
! CHECK: %[[TRANS:.*]] = hlfir.transpose %[[MATRIX_DECL]]#0 : (!fir.class<!fir.array<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>>>) -> !hlfir.expr<?x?x!fir.type<_QMpoly_tmpTp1{a:i32}>?>
@@ -189,7 +189,7 @@ subroutine test_merge_intrinsic(a, b)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_merge_intrinsic(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>> {fir.bindc_name = "a"}, %[[ARG1:.*]]: !fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>> {fir.bindc_name = "b"}) {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>> {fir.bindc_name = "a", fir.read_only}, %[[ARG1:.*]]: !fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>> {fir.bindc_name = "b", fir.read_only}) {
! CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[ARG0]]
! CHECK: %[[B_DECL:.*]]:2 = hlfir.declare %[[ARG1]]
! CHECK: %[[DES_A:.*]] = hlfir.designate %[[A_DECL]]#0{"a"} : (!fir.class<!fir.type<_QMpoly_tmpTp1{a:i32}>>) -> !fir.ref<i32>
@@ -208,7 +208,7 @@ subroutine test_merge_intrinsic2(a, b, i)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_merge_intrinsic2(
-! CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.class<!fir.heap<!fir.type<_QMpoly_tmpTp1{a:i32}>>>> {fir.bindc_name = "a"}, %[[B:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.type<_QMpoly_tmpTp1{a:i32}>>>> {fir.bindc_name = "b"}, %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i"}) {
+! CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.class<!fir.heap<!fir.type<_QMpoly_tmpTp1{a:i32}>>>> {fir.bindc_name = "a"}, %[[B:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.type<_QMpoly_tmpTp1{a:i32}>>>> {fir.bindc_name = "b"}, %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}) {
! CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[A]]
! CHECK: %[[B_DECL:.*]]:2 = hlfir.declare %[[B]]
! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %[[I]]
@@ -230,7 +230,7 @@ subroutine test_merge_intrinsic3(a, b, i)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_merge_intrinsic3(
-! CHECK-SAME: %[[A:.*]]: !fir.class<none> {fir.bindc_name = "a"}, %[[B:.*]]: !fir.class<none> {fir.bindc_name = "b"}, %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i"}) {
+! CHECK-SAME: %[[A:.*]]: !fir.class<none> {fir.bindc_name = "a", fir.read_only}, %[[B:.*]]: !fir.class<none> {fir.bindc_name = "b", fir.read_only}, %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}) {
! CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[A]]
! CHECK: %[[B_DECL:.*]]:2 = hlfir.declare %[[B]]
! CHECK: %[[I_DECL:.*]]:2 = hlfir.declare %[[I]]
@@ -247,7 +247,7 @@ subroutine test_merge_intrinsic4(i)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_merge_intrinsic4(
-! CHECK-SAME: %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i"}) {
+! CHECK-SAME: %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}) {
! CHECK: %[[V_0:[0-9]+]] = fir.alloca !fir.class<!fir.heap<none>> {bindc_name = "a", uniq_name = "_QMpoly_tmpFtest_merge_intrinsic4Ea"}
! CHECK: %[[V_1:[0-9]+]] = fir.zero_bits !fir.heap<none>
! CHECK: %[[V_2:[0-9]+]] = fir.embox %[[V_1]] : (!fir.heap<none>) -> !fir.class<!fir.heap<none>>
@@ -274,7 +274,7 @@ subroutine test_merge_intrinsic5(i)
end subroutine
! CHECK-LABEL: func.func @_QMpoly_tmpPtest_merge_intrinsic5(
-! CHECK-SAME: %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i"}) {
+! CHECK-SAME: %[[I:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}) {
! CHECK: %[[V_0:[0-9]+]] = fir.alloca !fir.class<!fir.ptr<none>> {bindc_name = "a", uniq_name = "_QMpoly_tmpFtest_merge_intrinsic5Ea"}
! CHECK: %[[V_1:[0-9]+]] = fir.zero_bits !fir.ptr<none>
! CHECK: %[[V_2:[0-9]+]] = fir.embox %[[V_1]] : (!fir.ptr<none>) -> !fir.class<!fir.ptr<none>>
diff --git a/flang/test/Lower/polymorphic.f90 b/flang/test/Lower/polymorphic.f90
index cd9a9a213e354..83b57824e1ead 100644
--- a/flang/test/Lower/polymorphic.f90
+++ b/flang/test/Lower/polymorphic.f90
@@ -66,7 +66,7 @@ elemental subroutine assign_p1_int(lhs, rhs)
lhs%b = rhs
End Subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPassign_p1_int(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "lhs"}, %[[ARG1:.*]]: !fir.ref<i32> {fir.bindc_name = "rhs"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "lhs"}, %[[ARG1:.*]]: !fir.ref<i32> {fir.bindc_name = "rhs", fir.read_only}
! CHECK-SAME: attributes {fir.proc_attrs = #fir.proc_attrs<elemental, pure>}
! CHECK: %[[LHS:.*]]:2 = hlfir.declare %[[ARG0]]{{.*}}{fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QMpolymorphic_testFassign_p1_intElhs"}
! CHECK: %[[RHS:.*]]:2 = hlfir.declare %[[ARG1]]{{.*}}{fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMpolymorphic_testFassign_p1_intErhs"}
@@ -80,7 +80,7 @@ elemental integer function elemental_fct(this)
elemental_fct = this%a
end function
! CHECK-LABEL: func.func @_QMpolymorphic_testPelemental_fct(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "this", fir.read_only})
! CHECK-SAME: -> i32 attributes {fir.proc_attrs = #fir.proc_attrs<elemental, pure>}
! CHECK: %[[THIS:.*]]:2 = hlfir.declare %[[ARG0]]{{.*}}{fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMpolymorphic_testFelemental_fctEthis"}
! CHECK: %[[A:.*]] = hlfir.designate %[[THIS]]#0{"a"}
@@ -102,7 +102,7 @@ elemental subroutine elemental_sub_pass(c, this)
this%a = this%a * this%b + c
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPelemental_sub_pass(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "c"}, %[[ARG1:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "c", fir.read_only}, %[[ARG1:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "this"})
! CHECK-SAME: attributes {fir.proc_attrs = #fir.proc_attrs<elemental, pure>}
logical elemental function lt(i, poly)
@@ -111,7 +111,7 @@ logical elemental function lt(i, poly)
lt = i < poly%a
End Function
! CHECK-LABEL: func.func @_QMpolymorphic_testPlt(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "i"}, %[[ARG1:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "poly"})
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.read_only}, %[[ARG1:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "poly", fir.read_only})
! CHECK-SAME: -> !fir.logical<4> attributes {fir.proc_attrs = #fir.proc_attrs<elemental, pure>}
! CHECK: %[[I:.*]]:2 = hlfir.declare %[[ARG0]]{{.*}}{fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMpolymorphic_testFltEi"}
! CHECK: %[[POLY:.*]]:2 = hlfir.declare %[[ARG1]]{{.*}}{fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMpolymorphic_testFltEpoly"}
@@ -310,7 +310,7 @@ subroutine takes_p1(p)
class(p1), intent(in) :: p
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPtakes_p1(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "p"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "p", fir.read_only}
! TODO: implement polymorphic temporary in lowering
! subroutine no_reassoc_poly_value(a, i)
@@ -363,7 +363,7 @@ subroutine up_input(a)
class(*), intent(in) :: a
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPup_input(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a", fir.read_only}
subroutine pass_trivial_to_up()
call up_input('hello')
@@ -389,7 +389,7 @@ subroutine up_arr_input(a)
class(*), intent(in) :: a(2)
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPup_arr_input(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.array<2xnone>> {fir.bindc_name = "a"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.array<2xnone>> {fir.bindc_name = "a", fir.read_only}
subroutine pass_trivial_arr_to_up()
character :: c(2)
@@ -550,7 +550,7 @@ subroutine write_p1(dtv, unit, iotype, v_list, iostat, iomsg)
! dummy subroutine for testing purpose
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPwrite_p1(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "dtv"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}>> {fir.bindc_name = "dtv", fir.read_only}
subroutine read_p1(dtv, unit, iotype, v_list, iostat, iomsg)
class(p1), intent(inout) :: dtv
@@ -680,13 +680,13 @@ subroutine opt_int(i)
call opt_up(i)
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPopt_int(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.optional}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "i", fir.optional, fir.read_only}
subroutine opt_up(up)
class(*), optional, intent(in) :: up
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPopt_up(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "up", fir.optional}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "up", fir.optional, fir.read_only}
function rhs()
class(p1), pointer :: rhs
@@ -865,7 +865,7 @@ subroutine pass_up(up)
class(*), intent(in) :: up
end subroutine
! CHECK-LABEL: func.func @_QMpolymorphic_testPpass_up(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "up"}
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "up", fir.read_only}
! TODO: unlimited polymorphic temporary in lowering
! subroutine parenthesized_up(a)
@@ -884,4 +884,3 @@ program test
l = i < o%inner
end program
-
diff --git a/flang/test/Lower/repack-arrays.f90 b/flang/test/Lower/repack-arrays.f90
index ff89df82793a3..1a87692100621 100644
--- a/flang/test/Lower/repack-arrays.f90
+++ b/flang/test/Lower/repack-arrays.f90
@@ -82,7 +82,7 @@ subroutine test4(x)
end subroutine test4
! ALL-LABEL: func.func @_QPtest5(
-! ALL-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x"}) {
+! ALL-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x", fir.read_only}) {
subroutine test5(x)
real, intent(in) :: x(:)
! ALL: %[[VAL_2:.*]] = fir.pack_array %[[VAL_0]]
diff --git a/flang/test/Lower/select-type-2.f90 b/flang/test/Lower/select-type-2.f90
index 55970031beb94..336345fde5f11 100644
--- a/flang/test/Lower/select-type-2.f90
+++ b/flang/test/Lower/select-type-2.f90
@@ -29,7 +29,7 @@ subroutine select_type1(a)
end subroutine
! CHECK-LABEL: func.func @_QMselect_type_2Pselect_type1(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>> {fir.bindc_name = "a", fir.read_only}) {
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope {{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMselect_type_2Fselect_type1Ea"}
! CHECK: %[[TDESC_P3_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_2Tp3
! CHECK: %[[TDESC_P3_CONV:.*]] = fir.convert %[[TDESC_P3_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
diff --git a/flang/test/Lower/select-type.f90 b/flang/test/Lower/select-type.f90
index 1c6bb5ce0d4f6..f614ec7a8f344 100644
--- a/flang/test/Lower/select-type.f90
+++ b/flang/test/Lower/select-type.f90
@@ -55,7 +55,7 @@ subroutine select_type1(a)
end subroutine
! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type1(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"})
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a", fir.read_only})
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]]
! CHECK: fir.select_type %[[A]]#1 : !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
! CHECK-SAME: [#fir.type_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[TYPE_IS_BLK:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[CLASS_IS_P1_BLK:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp2{{.*}}>>, ^[[CLASS_IS_P2_BLK:.*]], unit, ^[[DEFAULT_BLOCK:.*]]]
@@ -68,7 +68,7 @@ subroutine select_type1(a)
! CHECK: ^[[DEFAULT_BLOCK]]
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type1(
-! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
+! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a", fir.read_only}) {
! CFG: %[[A:.*]]:2 = hlfir.declare %[[ARG0]]
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[A]]#1 : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
@@ -215,7 +215,7 @@ subroutine select_type4(a)
end subroutine
! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type4(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"})
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a", fir.read_only})
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope {{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMselect_type_lower_testFselect_type4Ea"}
! CHECK: fir.select_type %[[A]]#1 : !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
! CHECK-SAME: [#fir.type_is<!fir.type<_QMselect_type_lower_testTp3K8{{.*}}>>, ^[[P3_8:.*]], #fir.type_is<!fir.type<_QMselect_type_lower_testTp3K4{{.*}}>>, ^[[P3_4:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[P1:.*]], unit, ^[[EXIT:.*]]]
@@ -225,7 +225,7 @@ subroutine select_type4(a)
! CHECK: ^[[EXIT]]
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type4(
-! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
+! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a", fir.read_only}) {
! CFG: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope {{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMselect_type_lower_testFselect_type4Ea"}
! CFG: %[[TDESC_P3_8_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp3K8
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[A]]#1 : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<{{.*}}>
@@ -278,7 +278,7 @@ subroutine select_type5(a)
end subroutine
! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type5(
-! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"})
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a", fir.read_only})
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg 1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMselect_type_lower_testFselect_type5Ea"} : (!fir.class<none>, !fir.dscope) -> (!fir.class<none>, !fir.class<none>)
! CHECK: fir.select_type %[[A]]#1 : !fir.class<none>
! CHECK-SAME: [#fir.type_is<i8>, ^[[I8_BLK:.*]], #fir.type_is<i32>, ^[[I32_BLK:.*]], #fir.type_is<f32>, ^[[F32_BLK:.*]], #fir.type_is<!fir.logical<4>>, ^[[LOG_BLK:.*]], #fir.type_is<!fir.char<1,?>>, ^[[CHAR_BLK:.*]], unit, ^[[DEFAULT:.*]]]
@@ -290,7 +290,7 @@ subroutine select_type5(a)
! CHECK: ^[[DEFAULT]]
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type5(
-! CFG-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"}) {
+! CFG-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a", fir.read_only}) {
! CFG: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{[0-9]+}} arg 1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMselect_type_lower_testFselect_type5Ea"} : (!fir.class<none>, !fir.dscope) -> (!fir.class<none>, !fir.class<none>)
! CFG: %[[INT8_TC:.*]] = arith.constant 7 : i8
! CFG: %[[TYPE_CODE:.*]] = fir.box_typecode %[[A]]#1 : (!fir.class<none>) -> i8
diff --git a/flang/test/Lower/unsigned-ops.f90 b/flang/test/Lower/unsigned-ops.f90
index 644f0c4ea11c9..ff6704e37d77b 100644
--- a/flang/test/Lower/unsigned-ops.f90
+++ b/flang/test/Lower/unsigned-ops.f90
@@ -5,7 +5,7 @@ unsigned function f01(u, v)
f01 = u + v - 1u
end
-!CHECK: func.func @_QPf01(%[[ARG0:.*]]: !fir.ref<ui32> {fir.bindc_name = "u"}, %[[ARG1:.*]]: !fir.ref<ui32> {fir.bindc_name = "v"}) -> ui32 {
+!CHECK: func.func @_QPf01(%[[ARG0:.*]]: !fir.ref<ui32> {fir.bindc_name = "u", fir.read_only}, %[[ARG1:.*]]: !fir.ref<ui32> {fir.bindc_name = "v", fir.read_only}) -> ui32 {
!CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
!CHECK: %[[VAL_0:.*]] = fir.dummy_scope : !fir.dscope
!CHECK: %[[VAL_1:.*]] = fir.alloca ui32 {bindc_name = "f01", uniq_name = "_QFf01Ef01"}
@@ -30,7 +30,7 @@ unsigned function f02(u, v)
f02 = u ** v - 1u
end
-!CHECK: func.func @_QPf02(%[[ARG0:.*]]: !fir.ref<ui32> {fir.bindc_name = "u"}, %[[ARG1:.*]]: !fir.ref<ui32> {fir.bindc_name = "v"}) -> ui32 {
+!CHECK: func.func @_QPf02(%[[ARG0:.*]]: !fir.ref<ui32> {fir.bindc_name = "u", fir.read_only}, %[[ARG1:.*]]: !fir.ref<ui32> {fir.bindc_name = "v", fir.read_only}) -> ui32 {
!CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
!CHECK: %[[VAL_0:.*]] = fir.dummy_scope : !fir.dscope
!CHECK: %[[VAL_1:.*]] = fir.alloca ui32 {bindc_name = "f02", uniq_name = "_QFf02Ef02"}
@@ -49,4 +49,3 @@ unsigned function f02(u, v)
!CHECK: fir.store %[[VAL_13]] to %[[VAL_2]] : !fir.ref<ui32>
!CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_2]] : !fir.ref<ui32>
!CHECK: return %[[VAL_14]] : ui32
-
diff --git a/flang/test/Lower/volatile1.f90 b/flang/test/Lower/volatile1.f90
index ec1c85eb8e981..8d7c71fd0e05c 100644
--- a/flang/test/Lower/volatile1.f90
+++ b/flang/test/Lower/volatile1.f90
@@ -76,7 +76,7 @@ subroutine declared_volatile_in_this_scope(v,n)
! CHECK-LABEL: func.func private @_QFPdeclared_volatile_in_this_scope(
! CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<!fir.array<?xi32>> {fir.bindc_name = "v"},
-! CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "n"}) attributes {{.+}} {
+! CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "n", fir.read_only}) attributes {{.+}} {
! CHECK: %[[VAL_2:.*]] = arith.constant 1 : i32
! CHECK: %[[VAL_3:.*]] = arith.constant 0 : index
! CHECK: %[[VAL_4:.*]] = fir.dummy_scope : !fir.dscope
diff --git a/flang/test/Transforms/function-attrs-readonly.fir b/flang/test/Transforms/function-attrs-readonly.fir
new file mode 100644
index 0000000000000..3eb3ef8f527d1
--- /dev/null
+++ b/flang/test/Transforms/function-attrs-readonly.fir
@@ -0,0 +1,73 @@
+// RUN: fir-opt --function-attr="set-readonly=true" %s | FileCheck %s --check-prefix=CHECK
+// RUN: fir-opt --function-attr="set-readonly=true set-nocapture=true set-noalias=true" %s | FileCheck %s --check-prefix=ALL
+// RUN: fir-opt --function-attr="set-readonly=false" %s | FileCheck %s --check-prefix=OFF
+
+// Test translation of the FIR marker placed on INTENT(IN) by-reference dummy
+// arguments to the LLVM dialect argument attribute.
+
+// CHECK-LABEL: func.func private @test_ref(
+// CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only, llvm.readonly}) {
+// ALL-LABEL: func.func private @test_ref(
+// ALL-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only, llvm.noalias, llvm.nocapture, llvm.readonly}) {
+// OFF-LABEL: func.func private @test_ref(
+// OFF-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only}) {
+func.func private @test_ref(%arg0: !fir.ref<i32> {fir.read_only}) {
+ return
+}
+
+// An unmarked reference is not read-only.
+// CHECK-LABEL: func.func private @test_unmarked_ref(
+// CHECK-SAME: %{{.*}}: !fir.ref<i32>) {
+func.func private @test_unmarked_ref(%arg0: !fir.ref<i32>) {
+ return
+}
+
+// TARGET prevents noalias and nocapture, but it does not prevent readonly.
+// Match the complete argument attribute dictionary under the ALL run so an
+// accidental llvm.noalias or llvm.nocapture addition makes the test fail.
+// CHECK-LABEL: func.func private @test_target(
+// CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only, fir.target, llvm.readonly}) {
+// ALL-LABEL: func.func private @test_target(
+// ALL-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only, fir.target, llvm.readonly}) {
+func.func private @test_target(%arg0: !fir.ref<i32> {fir.read_only, fir.target}) {
+ return
+}
+
+// A derived-type reference is handled like any other reference. Pointer or
+// allocatable components do not affect the shallow LLVM readonly contract.
+// CHECK-LABEL: func.func private @test_derived(
+// CHECK-SAME: %{{.*}}: !fir.ref<!fir.type<_QFtest_derivedTt{p:!fir.box<!fir.ptr<i32>>}>> {fir.read_only, llvm.readonly}) {
+func.func private @test_derived(%arg0: !fir.ref<!fir.type<_QFtest_derivedTt{p:!fir.box<!fir.ptr<i32>>}>> {fir.read_only}) {
+ return
+}
+
+// A Fortran BIND(C) definition must obey INTENT(IN), so readonly is valid.
+// Unlike ordinary definitions, it must not receive noalias or nocapture.
+// CHECK-LABEL: func.func @test_bindc_definition(
+// CHECK-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only, llvm.readonly}) attributes
+// ALL-LABEL: func.func @test_bindc_definition(
+// ALL-SAME: %{{.*}}: !fir.ref<i32> {fir.read_only, llvm.readonly}) attributes
+func.func @test_bindc_definition(%arg0: !fir.ref<i32> {fir.read_only}) attributes {fir.bindc_name = "test_bindc_definition", fir.proc_attrs = #fir.proc_attrs<bind_c>} {
+ return
+}
+
+// Do not apply readonly to a declaration of an external C procedure: its body
+// is not constrained by the Fortran INTENT appearing in the local interface.
+// Use a module-style FIR name so this exercises the BIND(C)-declaration check,
+// rather than the generic check that skips non-module declarations.
+// CHECK-LABEL: func.func private @_QMreadonly_modPbindc_declaration(
+// CHECK-SAME: !fir.ref<i32> {fir.read_only}) attributes
+func.func private @_QMreadonly_modPbindc_declaration(!fir.ref<i32> {fir.read_only}) attributes {fir.bindc_name = "bindc_declaration", fir.proc_attrs = #fir.proc_attrs<bind_c>}
+
+// A declaration of a Fortran module procedure carries the Fortran contract.
+// CHECK-LABEL: func.func private @_QMreadonly_modPfortran_declaration(
+// CHECK-SAME: !fir.ref<i32> {fir.read_only, llvm.readonly})
+func.func private @_QMreadonly_modPfortran_declaration(!fir.ref<i32> {fir.read_only})
+
+// fir.box is not a ReferenceType. The marker is intentionally inert until
+// descriptor-level readonly is implemented.
+// CHECK-LABEL: func.func private @test_box(
+// CHECK-SAME: %{{.*}}: !fir.box<!fir.array<?xi32>> {fir.read_only}) {
+func.func private @test_box(%arg0: !fir.box<!fir.array<?xi32>> {fir.read_only}) {
+ return
+}
More information about the flang-commits
mailing list