[flang-commits] [flang] [flang] Mark visible Cray pointer associations as aliasing (PR #221350)
via flang-commits
flang-commits at lists.llvm.org
Thu Sep 17 09:50:10 PDT 2026
https://github.com/junfengd-nv updated https://github.com/llvm/llvm-project/pull/221350
>From ae7ac5caa2854e24524710f91592824f7342b84e Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Fri, 4 Sep 2026 10:15:20 -0700
Subject: [PATCH 1/4] [flang] Treat visible Cray pointer associations as
aliasing for TBAA
When ptr = loc(var) is seen in the same procedure, tag var like TARGET so
accesses through the pointee are not treated as no-alias by default.
---
flang/docs/Aliasing.md | 11 ++--
.../lib/Optimizer/Transforms/AddAliasTags.cpp | 55 +++++++++++++++++--
flang/test/Transforms/tbaa-cray-pointer.fir | 18 +++++-
3 files changed, 72 insertions(+), 12 deletions(-)
diff --git a/flang/docs/Aliasing.md b/flang/docs/Aliasing.md
index 19cb0fe72886c..4460b5afec681 100644
--- a/flang/docs/Aliasing.md
+++ b/flang/docs/Aliasing.md
@@ -281,13 +281,14 @@ print *, target
end
```
-By default, optimizations assume that Cray pointers do not alias any other
-variables. In the above example, it is assumed that `handle` and `target` do
-not alias, and optimizations will treat them as separate entities.
+By default, optimizations assume that Cray pointers do not alias other
+variables unless the association is visible in the same procedure. In the
+above example, `ptr = loc(target)` causes accesses through `handle` and
+`target` to be treated as potentially aliasing.
In order to disable optimizations that assume that there is no aliasing between
-Cray pointer targets and entities they alias with, add the TARGET attribute to
-variables aliasing with a Cray pointer (the `target` variable in this example).
+Cray pointer targets and entities they alias with when the association is not
+visible, add the TARGET attribute to variables aliasing with a Cray pointer.
There is also a flag, `-funsafe-cray-pointers`, which causes the compiler
to assume that cray pointers alias with all data whether or not it has the
diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
index d2cd1f0072d22..812cf2dd2cc04 100644
--- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
+++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
@@ -214,6 +214,12 @@ class PassState {
}
void processFunctionScopes(mlir::func::FuncOp func);
+ // Find variables whose addresses are assigned to Cray pointers in the same
+ // function. Such variables are pointer targets for the purpose of TBAA even
+ // when they do not have the Fortran TARGET attribute.
+ void collectCrayPointerTargets(mlir::ModuleOp module);
+ bool isCrayPointerTarget(const fir::AliasAnalysis::Source &source,
+ mlir::func::FuncOp func) const;
// For the given fir.declare returns the dominating fir.dummy_scope
// operation.
fir::DummyScopeOp getDeclarationScope(fir::DeclareOp declareOp);
@@ -320,6 +326,12 @@ class PassState {
llvm::DenseMap<mlir::func::FuncOp, llvm::SmallVector<fir::DummyScopeOp, 16>>
sortedScopeOperations;
+ // Declaration sites of variables whose addresses are visibly assigned to a
+ // Cray pointer, grouped by function so this does not change interprocedural
+ // aliasing assumptions.
+ llvm::DenseMap<mlir::func::FuncOp, llvm::DenseSet<mlir::Operation *>>
+ crayPointerTargets;
+
// Local pass cache for derived types that contain descriptor
// member(s), to avoid the cost of isRecordWithDescriptorMember().
llvm::DenseSet<mlir::Type> typesContainingDescriptors;
@@ -332,6 +344,35 @@ class PassState {
bool declToStorageMapComputed = false;
};
+void PassState::collectCrayPointerTargets(mlir::ModuleOp module) {
+ module.walk([&](fir::StoreOp store) {
+ const fir::AliasAnalysis::Source &pointerSource =
+ getSource(store.getMemref());
+ if (!pointerSource.isCrayPointer())
+ return;
+
+ const fir::AliasAnalysis::Source &targetSource =
+ getSource(store.getValue());
+ mlir::Operation *targetDecl = targetSource.origin.instantiationPoint;
+ if (!targetDecl || !targetSource.isData() ||
+ targetSource.kind == fir::AliasAnalysis::SourceKind::Indirect ||
+ targetSource.kind == fir::AliasAnalysis::SourceKind::Unknown)
+ return;
+
+ if (mlir::func::FuncOp func =
+ store->getParentOfType<mlir::func::FuncOp>())
+ crayPointerTargets[func].insert(targetDecl);
+ });
+}
+
+bool PassState::isCrayPointerTarget(
+ const fir::AliasAnalysis::Source &source, mlir::func::FuncOp func) const {
+ mlir::Operation *decl = source.origin.instantiationPoint;
+ auto funcIt = crayPointerTargets.find(func);
+ return decl && funcIt != crayPointerTargets.end() &&
+ funcIt->second.contains(decl);
+}
+
// Process fir.dummy_scope operations in the given func:
// sort them according to the dominance information, and
// associate a unique (within the current function) scope name
@@ -670,6 +711,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
const fir::AliasAnalysis::Source &source = state.getSource(memref);
LLVM_DEBUG(llvm::dbgs() << "Got source " << source << "\n");
+ bool isCrayPointerTarget = state.isCrayPointerTarget(source, func);
// Process the scopes, if not processed yet.
state.processFunctionScopes(func);
@@ -704,7 +746,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
// POINTERS can alias with any POINTER or TARGET. Assume that TARGET dummy
// arguments might alias with each other (because of the "TARGET" hole for
// dummy arguments). See flang/docs/Aliasing.md.
- if (source.isTargetOrPointer()) {
+ if (source.isTargetOrPointer() || isCrayPointerTarget) {
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
} else if (!name.empty()) {
tag = state.getFuncTreeWithScope(func, scopeOp)
@@ -778,7 +820,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
if (source.isPointer()) {
// Pointers can alias with any pointer or target.
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
- } else if (source.isTarget()) {
+ } else if (source.isTarget() || isCrayPointerTarget) {
// Targets could alias with any pointer but not with each other.
addTagUsingStorageDesc(
&state.getMutableFuncTreeWithScope(func, scopeOp).targetDataTree);
@@ -801,7 +843,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
// Targets could alias with any pointer but not with each other so they
// get their own node inside of the target data tree.
- else if (source.isTarget())
+ else if (source.isTarget() || isCrayPointerTarget)
tag = state.getFuncTreeWithScope(func, scopeOp)
.targetDataTree.getTag(name);
else
@@ -865,12 +907,14 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "Found reference to POINTER allocation at " << *op << "\n");
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
- } else if (name && source.isTarget() && state.attachLocalAllocTag()) {
+ } else if (name && (source.isTarget() || isCrayPointerTarget) &&
+ state.attachLocalAllocTag()) {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "Found reference to TARGET allocation at " << *op << "\n");
tag = state.getFuncTreeWithScope(func, scopeOp)
.targetDataTree.getTag(*name);
- } else if (source.isTarget() && state.attachLocalAllocTag()) {
+ } else if ((source.isTarget() || isCrayPointerTarget) &&
+ state.attachLocalAllocTag()) {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: couldn't find a name for TARGET allocation " << *op
<< "\n");
@@ -930,6 +974,7 @@ void AddAliasTagsPass::runOnOperation() {
localAllocsThreshold.getPosition()
? std::optional<unsigned>(localAllocsThreshold)
: std::nullopt);
+ state.collectCrayPointerTargets(module);
module.walk(
[&](fir::FirAliasTagOpInterface op) { runOnAliasInterface(op, state); });
diff --git a/flang/test/Transforms/tbaa-cray-pointer.fir b/flang/test/Transforms/tbaa-cray-pointer.fir
index 7c668b1b903ba..53f69ca713067 100644
--- a/flang/test/Transforms/tbaa-cray-pointer.fir
+++ b/flang/test/Transforms/tbaa-cray-pointer.fir
@@ -1,4 +1,5 @@
// RUN: fir-opt -unsafe-cray-pointers --fir-add-alias-tags %s | FileCheck %s
+// RUN: fir-opt --fir-add-alias-tags %s | FileCheck %s --check-prefix=DEFAULT
// Fortran source:
// subroutine test()
@@ -12,10 +13,18 @@
// CHECK-NEXT: #[[ANY_ACCESS:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[TBAA_ROOT]], 0>}>
// CHECK-NEXT: #[[ANY_DATA:.*]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[ANY_ACCESS]], 0>}>
// CHECK-NEXT: #[[ANY_DATA_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[ANY_DATA]], access_type = #[[ANY_DATA]], offset = 0>
-// CHECK-NEXT: #[[ALLOCATED_DATA:.*]] = #llvm.tbaa_type_desc<id = "allocated data", members = {<#[[ANY_DATA]], 0>}>
-// CHECK-NEXT: #[[B:.*]] = #llvm.tbaa_type_desc<id = "allocated data/_QFtestEb", members = {<#[[ALLOCATED_DATA]], 0>}>
+// CHECK-NEXT: #[[TARGET_DATA:.*]] = #llvm.tbaa_type_desc<id = "target data", members = {<#[[ANY_DATA]], 0>}>
+// CHECK-NEXT: #[[B:.*]] = #llvm.tbaa_type_desc<id = "target data/_QFtestEb", members = {<#[[TARGET_DATA]], 0>}>
// CHECK-NEXT: #[[B_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[B]], access_type = #[[B]], offset = 0>
+// DEFAULT: #[[DEFAULT_ROOT:.*]] = #llvm.tbaa_root<id = "Flang function root _QPtest">
+// DEFAULT: #[[DEFAULT_ANY_ACCESS:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[DEFAULT_ROOT]], 0>}>
+// DEFAULT: #[[DEFAULT_ANY_DATA:.*]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[DEFAULT_ANY_ACCESS]], 0>}>
+// DEFAULT: #[[DEFAULT_TARGET_DATA:.*]] = #llvm.tbaa_type_desc<id = "target data", members = {<#[[DEFAULT_ANY_DATA]], 0>}>
+// DEFAULT: #[[DEFAULT_TARGET_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[DEFAULT_TARGET_DATA]], access_type = #[[DEFAULT_TARGET_DATA]], offset = 0>
+// DEFAULT: #[[DEFAULT_B:.*]] = #llvm.tbaa_type_desc<id = "target data/_QFtestEb", members = {<#[[DEFAULT_TARGET_DATA]], 0>}>
+// DEFAULT: #[[DEFAULT_B_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[DEFAULT_B]], access_type = #[[DEFAULT_B]], offset = 0>
+
module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr<270> = dense<32> : vector<4xi64>, !llvm.ptr<271> = dense<32> : vector<4xi64>, !llvm.ptr<272> = dense<64> : vector<4xi64>, i8 = dense<[8, 32]> : vector<2xi64>, i16 = dense<[16, 32]> : vector<2xi64>, i64 = dense<64> : vector<2xi64>, i128 = dense<128> : vector<2xi64>, !llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little", "dlti.mangling_mode" = "e", "dlti.legal_int_widths" = array<i32: 32, 64>, "dlti.stack_alignment" = 128 : i64, "dlti.function_pointer_alignment" = #dlti.function_pointer_alignment<32, function_dependent = true>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"} {
// CHECK-LABEL: func.func @_QPtest()
func.func @_QPtest() {
@@ -35,8 +44,13 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr<270> = dense<32> : vec
%9 = fir.convert %5 : (!fir.ref<f32>) -> i64
fir.store %9 to %3 : !fir.ref<i64>
// CHECK: fir.store {{.*}} to {{.*}} {tbaa = [#[[ANY_DATA_TAG]]]} : !fir.ref<i64>
+ %10 = fir.load %6 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+ %11 = fir.box_addr %10 : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+ fir.store %cst to %11 : !fir.ptr<f32>
+// DEFAULT: fir.store {{.*}} to {{.*}} {tbaa = [#[[DEFAULT_TARGET_TAG]]]} : !fir.ptr<f32>
fir.store %cst to %5 : !fir.ref<f32>
// CHECK: fir.store {{.*}} to {{.*}} {tbaa = [#[[B_TAG]]]} : !fir.ref<f32>
+// DEFAULT: fir.store {{.*}} to {{.*}} {tbaa = [#[[DEFAULT_B_TAG]]]} : !fir.ref<f32>
return
}
}
>From 531aabc1fcfc9afc0a7a77ec9cafefed60611690 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Fri, 4 Sep 2026 14:42:43 -0700
Subject: [PATCH 2/4] [flang][NFC] clang-format AddAliasTags.cpp
---
flang/lib/Optimizer/Transforms/AddAliasTags.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
index 812cf2dd2cc04..dbd95f5379b71 100644
--- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
+++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
@@ -359,14 +359,13 @@ void PassState::collectCrayPointerTargets(mlir::ModuleOp module) {
targetSource.kind == fir::AliasAnalysis::SourceKind::Unknown)
return;
- if (mlir::func::FuncOp func =
- store->getParentOfType<mlir::func::FuncOp>())
+ if (mlir::func::FuncOp func = store->getParentOfType<mlir::func::FuncOp>())
crayPointerTargets[func].insert(targetDecl);
});
}
-bool PassState::isCrayPointerTarget(
- const fir::AliasAnalysis::Source &source, mlir::func::FuncOp func) const {
+bool PassState::isCrayPointerTarget(const fir::AliasAnalysis::Source &source,
+ mlir::func::FuncOp func) const {
mlir::Operation *decl = source.origin.instantiationPoint;
auto funcIt = crayPointerTargets.find(func);
return decl && funcIt != crayPointerTargets.end() &&
>From 4a71239be705c7b9f945e1301c803eeb4d7a01a0 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Tue, 8 Sep 2026 16:23:57 -0700
Subject: [PATCH 3/4] [flang] Mark visible Cray pointer targets during lowering
When a Cray pointer assignment p = loc(x) is visible in a procedure, temporarily give x the TARGET attribute before lowering. This makes the aliasing information available to all FIR optimizations instead of handling it only during TBAA emission. Restore the semantic attribute after lowering the procedure so hidden associations keep the default aliasing assumptions.
---
flang/docs/Aliasing.md | 4 +-
flang/lib/Lower/Bridge.cpp | 55 +++++++++++++++++++
flang/lib/Lower/ConvertVariable.cpp | 3 +-
.../lib/Optimizer/Transforms/AddAliasTags.cpp | 54 ++----------------
.../HLFIR/visible-cray-pointer-target.f90 | 38 +++++++++++++
flang/test/Lower/cray-pointer.f90 | 4 +-
flang/test/Transforms/tbaa-cray-pointer.fir | 18 +-----
7 files changed, 107 insertions(+), 69 deletions(-)
create mode 100644 flang/test/Lower/HLFIR/visible-cray-pointer-target.f90
diff --git a/flang/docs/Aliasing.md b/flang/docs/Aliasing.md
index 4460b5afec681..7ca86a402736b 100644
--- a/flang/docs/Aliasing.md
+++ b/flang/docs/Aliasing.md
@@ -284,7 +284,9 @@ end
By default, optimizations assume that Cray pointers do not alias other
variables unless the association is visible in the same procedure. In the
above example, `ptr = loc(target)` causes accesses through `handle` and
-`target` to be treated as potentially aliasing.
+`target` to be treated as potentially aliasing. Lowering gives `target` the
+TARGET attribute in FIR so that this information is available to all FIR
+optimizations.
In order to disable optimizations that assume that there is no aliasing between
Cray pointer targets and entities they alias with when the association is not
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 4af51dfb569f2..d524f7f332cbe 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -6647,10 +6647,63 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
}
+ /// Add TARGET while lowering entities used in visible Cray pointer
+ /// associations. The attributes are restored before lowering another
+ /// procedure so that hidden associations retain the default no-alias
+ /// behavior.
+ void markVisibleCrayPointerTargets(
+ Fortran::lower::pft::EvaluationList &evaluationList,
+ llvm::SmallVectorImpl<Fortran::semantics::Symbol *> &modifiedSymbols) {
+ for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
+ eval.visit(Fortran::common::visitors{
+ [&](const Fortran::parser::AssignmentStmt &stmt) {
+ if (!stmt.typedAssignment || !stmt.typedAssignment->v)
+ return;
+ const Fortran::evaluate::Assignment &assignment =
+ *stmt.typedAssignment->v;
+ const Fortran::semantics::Symbol *pointer =
+ Fortran::evaluate::GetLastSymbol(assignment.lhs);
+ if (!pointer || !pointer->GetUltimate().test(
+ Fortran::semantics::Symbol::Flag::CrayPointer))
+ return;
+
+ const Fortran::evaluate::ProcedureRef *procedure =
+ Fortran::evaluate::UnwrapProcedureRef(assignment.rhs);
+ const Fortran::evaluate::SpecificIntrinsic *intrinsic =
+ procedure ? procedure->proc().GetSpecificIntrinsic() : nullptr;
+ if (!intrinsic || intrinsic->name != "loc" ||
+ procedure->arguments().size() != 1 ||
+ !procedure->arguments()[0])
+ return;
+
+ const Fortran::lower::SomeExpr *targetExpr =
+ procedure->arguments()[0]->UnwrapExpr();
+ const Fortran::semantics::Symbol *target =
+ targetExpr ? Fortran::evaluate::GetFirstSymbol(*targetExpr)
+ : nullptr;
+ if (!target)
+ return;
+
+ auto &ultimate =
+ const_cast<Fortran::semantics::Symbol &>(target->GetUltimate());
+ if (!ultimate.attrs().test(Fortran::semantics::Attr::TARGET)) {
+ ultimate.attrs().set(Fortran::semantics::Attr::TARGET);
+ modifiedSymbols.push_back(&ultimate);
+ }
+ },
+ [](const auto &) {}});
+ if (eval.hasNestedEvaluations())
+ markVisibleCrayPointerTargets(eval.getNestedEvaluations(),
+ modifiedSymbols);
+ }
+ }
+
/// Lower a procedure (nest).
void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) {
setCurrentPosition(funit.getStartingSourceLoc());
setCurrentFunctionUnit(&funit);
+ llvm::SmallVector<Fortran::semantics::Symbol *> crayPointerTargets;
+ markVisibleCrayPointerTargets(funit.evaluationList, crayPointerTargets);
for (int entryIndex = 0, last = funit.entryPointList.size();
entryIndex < last; ++entryIndex) {
funit.setActiveEntry(entryIndex);
@@ -6674,6 +6727,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
funit.setActiveEntry(0);
setCurrentFunctionUnit(nullptr);
+ for (Fortran::semantics::Symbol *symbol : crayPointerTargets)
+ symbol->attrs().reset(Fortran::semantics::Attr::TARGET);
for (Fortran::lower::pft::ContainedUnit &unit : funit.containedUnitList)
if (auto *f = std::get_if<Fortran::lower::pft::FunctionLikeUnit>(&unit))
lowerFunc(*f); // internal procedure
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 5d9fdf48bd067..bfe03951f2c83 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -782,7 +782,8 @@ static mlir::Value createNewLocal(Fortran::lower::AbstractConverter &converter,
const Fortran::semantics::Symbol &ultimateSymbol =
var.getSymbol().GetUltimate();
llvm::StringRef symNm = toStringRef(ultimateSymbol.name());
- bool isTarg = var.isTarget();
+ bool isTarg = var.isTarget() ||
+ ultimateSymbol.attrs().test(Fortran::semantics::Attr::TARGET);
// Do not allocate storage for cray pointee. The address inside the cray
// pointer will be used instead when using the pointee. Allocating space
diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
index dbd95f5379b71..d2cd1f0072d22 100644
--- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
+++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
@@ -214,12 +214,6 @@ class PassState {
}
void processFunctionScopes(mlir::func::FuncOp func);
- // Find variables whose addresses are assigned to Cray pointers in the same
- // function. Such variables are pointer targets for the purpose of TBAA even
- // when they do not have the Fortran TARGET attribute.
- void collectCrayPointerTargets(mlir::ModuleOp module);
- bool isCrayPointerTarget(const fir::AliasAnalysis::Source &source,
- mlir::func::FuncOp func) const;
// For the given fir.declare returns the dominating fir.dummy_scope
// operation.
fir::DummyScopeOp getDeclarationScope(fir::DeclareOp declareOp);
@@ -326,12 +320,6 @@ class PassState {
llvm::DenseMap<mlir::func::FuncOp, llvm::SmallVector<fir::DummyScopeOp, 16>>
sortedScopeOperations;
- // Declaration sites of variables whose addresses are visibly assigned to a
- // Cray pointer, grouped by function so this does not change interprocedural
- // aliasing assumptions.
- llvm::DenseMap<mlir::func::FuncOp, llvm::DenseSet<mlir::Operation *>>
- crayPointerTargets;
-
// Local pass cache for derived types that contain descriptor
// member(s), to avoid the cost of isRecordWithDescriptorMember().
llvm::DenseSet<mlir::Type> typesContainingDescriptors;
@@ -344,34 +332,6 @@ class PassState {
bool declToStorageMapComputed = false;
};
-void PassState::collectCrayPointerTargets(mlir::ModuleOp module) {
- module.walk([&](fir::StoreOp store) {
- const fir::AliasAnalysis::Source &pointerSource =
- getSource(store.getMemref());
- if (!pointerSource.isCrayPointer())
- return;
-
- const fir::AliasAnalysis::Source &targetSource =
- getSource(store.getValue());
- mlir::Operation *targetDecl = targetSource.origin.instantiationPoint;
- if (!targetDecl || !targetSource.isData() ||
- targetSource.kind == fir::AliasAnalysis::SourceKind::Indirect ||
- targetSource.kind == fir::AliasAnalysis::SourceKind::Unknown)
- return;
-
- if (mlir::func::FuncOp func = store->getParentOfType<mlir::func::FuncOp>())
- crayPointerTargets[func].insert(targetDecl);
- });
-}
-
-bool PassState::isCrayPointerTarget(const fir::AliasAnalysis::Source &source,
- mlir::func::FuncOp func) const {
- mlir::Operation *decl = source.origin.instantiationPoint;
- auto funcIt = crayPointerTargets.find(func);
- return decl && funcIt != crayPointerTargets.end() &&
- funcIt->second.contains(decl);
-}
-
// Process fir.dummy_scope operations in the given func:
// sort them according to the dominance information, and
// associate a unique (within the current function) scope name
@@ -710,7 +670,6 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
const fir::AliasAnalysis::Source &source = state.getSource(memref);
LLVM_DEBUG(llvm::dbgs() << "Got source " << source << "\n");
- bool isCrayPointerTarget = state.isCrayPointerTarget(source, func);
// Process the scopes, if not processed yet.
state.processFunctionScopes(func);
@@ -745,7 +704,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
// POINTERS can alias with any POINTER or TARGET. Assume that TARGET dummy
// arguments might alias with each other (because of the "TARGET" hole for
// dummy arguments). See flang/docs/Aliasing.md.
- if (source.isTargetOrPointer() || isCrayPointerTarget) {
+ if (source.isTargetOrPointer()) {
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
} else if (!name.empty()) {
tag = state.getFuncTreeWithScope(func, scopeOp)
@@ -819,7 +778,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
if (source.isPointer()) {
// Pointers can alias with any pointer or target.
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
- } else if (source.isTarget() || isCrayPointerTarget) {
+ } else if (source.isTarget()) {
// Targets could alias with any pointer but not with each other.
addTagUsingStorageDesc(
&state.getMutableFuncTreeWithScope(func, scopeOp).targetDataTree);
@@ -842,7 +801,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
// Targets could alias with any pointer but not with each other so they
// get their own node inside of the target data tree.
- else if (source.isTarget() || isCrayPointerTarget)
+ else if (source.isTarget())
tag = state.getFuncTreeWithScope(func, scopeOp)
.targetDataTree.getTag(name);
else
@@ -906,14 +865,12 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "Found reference to POINTER allocation at " << *op << "\n");
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
- } else if (name && (source.isTarget() || isCrayPointerTarget) &&
- state.attachLocalAllocTag()) {
+ } else if (name && source.isTarget() && state.attachLocalAllocTag()) {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "Found reference to TARGET allocation at " << *op << "\n");
tag = state.getFuncTreeWithScope(func, scopeOp)
.targetDataTree.getTag(*name);
- } else if ((source.isTarget() || isCrayPointerTarget) &&
- state.attachLocalAllocTag()) {
+ } else if (source.isTarget() && state.attachLocalAllocTag()) {
LLVM_DEBUG(llvm::dbgs().indent(2)
<< "WARN: couldn't find a name for TARGET allocation " << *op
<< "\n");
@@ -973,7 +930,6 @@ void AddAliasTagsPass::runOnOperation() {
localAllocsThreshold.getPosition()
? std::optional<unsigned>(localAllocsThreshold)
: std::nullopt);
- state.collectCrayPointerTargets(module);
module.walk(
[&](fir::FirAliasTagOpInterface op) { runOnAliasInterface(op, state); });
diff --git a/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90 b/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90
new file mode 100644
index 0000000000000..b513a73be1fd8
--- /dev/null
+++ b/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90
@@ -0,0 +1,38 @@
+! RUN: bbc -emit-hlfir -o - -I nowhere %s | FileCheck %s
+
+subroutine visible_association()
+ real :: pointee, associated, unrelated
+ integer(8) :: ptr
+ pointer(ptr, pointee)
+ ptr = loc(associated)
+ pointee = 1.0
+ print *, associated, unrelated
+end
+
+! CHECK-LABEL: func.func @_QPvisible_association()
+! CHECK-DAG: %[[ASSOCIATED_ALLOC:.*]] = fir.alloca f32 {bindc_name = "associated", fir.target, uniq_name = "_QFvisible_associationEassociated"}
+! CHECK-DAG: %[[ASSOCIATED:.*]]:2 = hlfir.declare %[[ASSOCIATED_ALLOC]] {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFvisible_associationEassociated"}
+! CHECK-DAG: fir.alloca f32 {bindc_name = "unrelated", uniq_name = "_QFvisible_associationEunrelated"}
+
+subroutine nested_association(flag)
+ logical :: flag
+ real :: pointee, associated
+ integer(8) :: ptr
+ pointer(ptr, pointee)
+ if (flag) then
+ ptr = loc(associated)
+ end if
+end
+
+! CHECK-LABEL: func.func @_QPnested_association(
+! CHECK-DAG: %[[NESTED_ALLOC:.*]] = fir.alloca f32 {bindc_name = "associated", fir.target, uniq_name = "_QFnested_associationEassociated"}
+! CHECK-DAG: hlfir.declare %[[NESTED_ALLOC]] {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFnested_associationEassociated"}
+
+subroutine ordinary_loc()
+ real :: object
+ integer(8) :: address
+ address = loc(object)
+end
+
+! CHECK-LABEL: func.func @_QPordinary_loc()
+! CHECK: fir.alloca f32 {bindc_name = "object", uniq_name = "_QFordinary_locEobject"}
diff --git a/flang/test/Lower/cray-pointer.f90 b/flang/test/Lower/cray-pointer.f90
index a3eb4f455d118..148514e37625e 100644
--- a/flang/test/Lower/cray-pointer.f90
+++ b/flang/test/Lower/cray-pointer.f90
@@ -388,8 +388,8 @@ subroutine test_ptr()
ptr = loc(x)
! CHECK: %[[ptr_addr:.*]] = fir.address_of(@_QMmod_cray_ptrEptr) : !fir.ref<i64>
! CHECK: %[[ptr:.*]]:2 = hlfir.declare %[[ptr_addr]] {fortran_attrs = #fir.var_attrs<cray_pointer>, uniq_name = "_QMmod_cray_ptrEptr"}
-! CHECK: %[[x_alloc:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFtest_ptrEx"}
-! CHECK: %[[x:.*]]:2 = hlfir.declare %[[x_alloc]]
+! CHECK: %[[x_alloc:.*]] = fir.alloca i32 {bindc_name = "x", fir.target, uniq_name = "_QFtest_ptrEx"}
+! CHECK: %[[x:.*]]:2 = hlfir.declare %[[x_alloc]] {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtest_ptrEx"}
! CHECK: %[[xbox:.*]] = fir.embox %[[x]]#0 : (!fir.ref<i32>) -> !fir.box<i32>
! CHECK: %[[xboxAddr:.*]] = fir.box_addr %[[xbox]] : (!fir.box<i32>) -> !fir.ref<i32>
! CHECK: %[[addr_x:.*]] = fir.convert %[[xboxAddr]] : (!fir.ref<i32>) -> i64
diff --git a/flang/test/Transforms/tbaa-cray-pointer.fir b/flang/test/Transforms/tbaa-cray-pointer.fir
index 53f69ca713067..7c668b1b903ba 100644
--- a/flang/test/Transforms/tbaa-cray-pointer.fir
+++ b/flang/test/Transforms/tbaa-cray-pointer.fir
@@ -1,5 +1,4 @@
// RUN: fir-opt -unsafe-cray-pointers --fir-add-alias-tags %s | FileCheck %s
-// RUN: fir-opt --fir-add-alias-tags %s | FileCheck %s --check-prefix=DEFAULT
// Fortran source:
// subroutine test()
@@ -13,18 +12,10 @@
// CHECK-NEXT: #[[ANY_ACCESS:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[TBAA_ROOT]], 0>}>
// CHECK-NEXT: #[[ANY_DATA:.*]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[ANY_ACCESS]], 0>}>
// CHECK-NEXT: #[[ANY_DATA_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[ANY_DATA]], access_type = #[[ANY_DATA]], offset = 0>
-// CHECK-NEXT: #[[TARGET_DATA:.*]] = #llvm.tbaa_type_desc<id = "target data", members = {<#[[ANY_DATA]], 0>}>
-// CHECK-NEXT: #[[B:.*]] = #llvm.tbaa_type_desc<id = "target data/_QFtestEb", members = {<#[[TARGET_DATA]], 0>}>
+// CHECK-NEXT: #[[ALLOCATED_DATA:.*]] = #llvm.tbaa_type_desc<id = "allocated data", members = {<#[[ANY_DATA]], 0>}>
+// CHECK-NEXT: #[[B:.*]] = #llvm.tbaa_type_desc<id = "allocated data/_QFtestEb", members = {<#[[ALLOCATED_DATA]], 0>}>
// CHECK-NEXT: #[[B_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[B]], access_type = #[[B]], offset = 0>
-// DEFAULT: #[[DEFAULT_ROOT:.*]] = #llvm.tbaa_root<id = "Flang function root _QPtest">
-// DEFAULT: #[[DEFAULT_ANY_ACCESS:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[DEFAULT_ROOT]], 0>}>
-// DEFAULT: #[[DEFAULT_ANY_DATA:.*]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[DEFAULT_ANY_ACCESS]], 0>}>
-// DEFAULT: #[[DEFAULT_TARGET_DATA:.*]] = #llvm.tbaa_type_desc<id = "target data", members = {<#[[DEFAULT_ANY_DATA]], 0>}>
-// DEFAULT: #[[DEFAULT_TARGET_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[DEFAULT_TARGET_DATA]], access_type = #[[DEFAULT_TARGET_DATA]], offset = 0>
-// DEFAULT: #[[DEFAULT_B:.*]] = #llvm.tbaa_type_desc<id = "target data/_QFtestEb", members = {<#[[DEFAULT_TARGET_DATA]], 0>}>
-// DEFAULT: #[[DEFAULT_B_TAG:.*]] = #llvm.tbaa_tag<base_type = #[[DEFAULT_B]], access_type = #[[DEFAULT_B]], offset = 0>
-
module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr<270> = dense<32> : vector<4xi64>, !llvm.ptr<271> = dense<32> : vector<4xi64>, !llvm.ptr<272> = dense<64> : vector<4xi64>, i8 = dense<[8, 32]> : vector<2xi64>, i16 = dense<[16, 32]> : vector<2xi64>, i64 = dense<64> : vector<2xi64>, i128 = dense<128> : vector<2xi64>, !llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little", "dlti.mangling_mode" = "e", "dlti.legal_int_widths" = array<i32: 32, 64>, "dlti.stack_alignment" = 128 : i64, "dlti.function_pointer_alignment" = #dlti.function_pointer_alignment<32, function_dependent = true>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"} {
// CHECK-LABEL: func.func @_QPtest()
func.func @_QPtest() {
@@ -44,13 +35,8 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr<270> = dense<32> : vec
%9 = fir.convert %5 : (!fir.ref<f32>) -> i64
fir.store %9 to %3 : !fir.ref<i64>
// CHECK: fir.store {{.*}} to {{.*}} {tbaa = [#[[ANY_DATA_TAG]]]} : !fir.ref<i64>
- %10 = fir.load %6 : !fir.ref<!fir.box<!fir.ptr<f32>>>
- %11 = fir.box_addr %10 : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
- fir.store %cst to %11 : !fir.ptr<f32>
-// DEFAULT: fir.store {{.*}} to {{.*}} {tbaa = [#[[DEFAULT_TARGET_TAG]]]} : !fir.ptr<f32>
fir.store %cst to %5 : !fir.ref<f32>
// CHECK: fir.store {{.*}} to {{.*}} {tbaa = [#[[B_TAG]]]} : !fir.ref<f32>
-// DEFAULT: fir.store {{.*}} to {{.*}} {tbaa = [#[[DEFAULT_B_TAG]]]} : !fir.ref<f32>
return
}
}
>From a3a8c9ae7b4df350943b6f3b0351dc9a900971a8 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Thu, 17 Sep 2026 09:44:28 -0700
Subject: [PATCH 4/4] [flang] Track visible Cray pointer targets without
mutating TARGET
Keep the semantic TARGET attribute unchanged and only scan procedures
that declare or import a Cray pointer, so hidden associations stay
no-alias for later FIR passes.
---
flang/docs/Aliasing.md | 14 ++++++
flang/include/flang/Lower/AbstractConverter.h | 7 +++
flang/lib/Lower/Bridge.cpp | 49 ++++++++++++-------
flang/lib/Lower/ConvertVariable.cpp | 6 ++-
.../HLFIR/visible-cray-pointer-target.f90 | 11 +++++
5 files changed, 66 insertions(+), 21 deletions(-)
diff --git a/flang/docs/Aliasing.md b/flang/docs/Aliasing.md
index 7ca86a402736b..0e26d67d06509 100644
--- a/flang/docs/Aliasing.md
+++ b/flang/docs/Aliasing.md
@@ -288,6 +288,20 @@ above example, `ptr = loc(target)` causes accesses through `handle` and
TARGET attribute in FIR so that this information is available to all FIR
optimizations.
+If the association is not visible, Flang keeps the default assumption that the
+Cray pointee does not alias the target:
+
+```
+integer(kind=8) :: target(10)
+integer(kind=8) :: ptr
+integer(kind=8) :: handle(10)
+pointer(ptr, handle)
+call associate_target_to_cray_ptr(ptr, target) ! does ptr = loc(target)
+target = 1
+print *, target
+end
+```
+
In order to disable optimizations that assume that there is no aliasing between
Cray pointer targets and entities they alias with when the association is not
visible, add the TARGET attribute to variables aliasing with a Cray pointer.
diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index 7a50f74ff4259..ae246d3188bd8 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -283,6 +283,13 @@ class AbstractConverter {
virtual const Fortran::lower::pft::FunctionLikeUnit *
getCurrentFunctionUnit() const = 0;
+ /// Returns true if \p sym is the target of a Cray pointer association that
+ /// is visible in the procedure being lowered. Such symbols are given the
+ /// TARGET attribute in FIR so that the aliasing between the Cray pointee and
+ /// the target is visible to all FIR passes.
+ virtual bool
+ isVisibleCrayPointerTarget(const Fortran::semantics::Symbol &sym) const = 0;
+
/// Check support of Multi-image features if -fcoarray is provided
virtual void checkCoarrayEnabled() = 0;
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index d524f7f332cbe..84082fcad6b06 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1338,6 +1338,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return currentFunctionUnit;
}
+ bool isVisibleCrayPointerTarget(
+ const Fortran::semantics::Symbol &sym) const override final {
+ return visibleCrayPointerTargets.contains(&sym.GetUltimate());
+ }
+
void checkCoarrayEnabled() override final {
if (!getFoldingContext().languageFeatures().IsEnabled(
Fortran::common::LanguageFeature::Coarray))
@@ -6647,13 +6652,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
}
- /// Add TARGET while lowering entities used in visible Cray pointer
- /// associations. The attributes are restored before lowering another
- /// procedure so that hidden associations retain the default no-alias
- /// behavior.
- void markVisibleCrayPointerTargets(
- Fortran::lower::pft::EvaluationList &evaluationList,
- llvm::SmallVectorImpl<Fortran::semantics::Symbol *> &modifiedSymbols) {
+ /// Collect entities used in visible Cray pointer associations.
+ void collectVisibleCrayPointerTargets(
+ Fortran::lower::pft::EvaluationList &evaluationList) {
for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
eval.visit(Fortran::common::visitors{
[&](const Fortran::parser::AssignmentStmt &stmt) {
@@ -6684,26 +6685,33 @@ class FirConverter : public Fortran::lower::AbstractConverter {
if (!target)
return;
- auto &ultimate =
- const_cast<Fortran::semantics::Symbol &>(target->GetUltimate());
- if (!ultimate.attrs().test(Fortran::semantics::Attr::TARGET)) {
- ultimate.attrs().set(Fortran::semantics::Attr::TARGET);
- modifiedSymbols.push_back(&ultimate);
- }
+ visibleCrayPointerTargets.insert(&target->GetUltimate());
},
[](const auto &) {}});
if (eval.hasNestedEvaluations())
- markVisibleCrayPointerTargets(eval.getNestedEvaluations(),
- modifiedSymbols);
+ collectVisibleCrayPointerTargets(eval.getNestedEvaluations());
}
}
+ /// Return whether \p scope declares or imports a Cray pointer.
+ bool hasCrayPointer(const Fortran::semantics::Scope &scope) {
+ if (!scope.crayPointers().empty())
+ return true;
+ for (Fortran::semantics::SymbolRef symbol : scope.GetSymbols())
+ if (symbol->GetUltimate().test(
+ Fortran::semantics::Symbol::Flag::CrayPointer))
+ return true;
+ return false;
+ }
+
/// Lower a procedure (nest).
void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) {
setCurrentPosition(funit.getStartingSourceLoc());
setCurrentFunctionUnit(&funit);
- llvm::SmallVector<Fortran::semantics::Symbol *> crayPointerTargets;
- markVisibleCrayPointerTargets(funit.evaluationList, crayPointerTargets);
+ assert(visibleCrayPointerTargets.empty() &&
+ "Cray pointer targets must not outlive a procedure");
+ if (hasCrayPointer(funit.getScope()))
+ collectVisibleCrayPointerTargets(funit.evaluationList);
for (int entryIndex = 0, last = funit.entryPointList.size();
entryIndex < last; ++entryIndex) {
funit.setActiveEntry(entryIndex);
@@ -6727,8 +6735,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
funit.setActiveEntry(0);
setCurrentFunctionUnit(nullptr);
- for (Fortran::semantics::Symbol *symbol : crayPointerTargets)
- symbol->attrs().reset(Fortran::semantics::Attr::TARGET);
+ visibleCrayPointerTargets.clear();
for (Fortran::lower::pft::ContainedUnit &unit : funit.containedUnitList)
if (auto *f = std::get_if<Fortran::lower::pft::FunctionLikeUnit>(&unit))
lowerFunc(*f); // internal procedure
@@ -6917,6 +6924,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::parser::CharBlock currentPosition;
TypeInfoConverter typeInfoConverter;
+ /// Symbols associated with Cray pointers in the current procedure.
+ llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4>
+ visibleCrayPointerTargets;
+
/// Counter of `scf.execute_region` ops created when
/// `--wrap-unstructured-constructs-in-execute-region` is enabled for the
/// currently-lowered function.
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index bfe03951f2c83..d763c47c7c72e 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -782,8 +782,8 @@ static mlir::Value createNewLocal(Fortran::lower::AbstractConverter &converter,
const Fortran::semantics::Symbol &ultimateSymbol =
var.getSymbol().GetUltimate();
llvm::StringRef symNm = toStringRef(ultimateSymbol.name());
- bool isTarg = var.isTarget() ||
- ultimateSymbol.attrs().test(Fortran::semantics::Attr::TARGET);
+ bool isTarg =
+ var.isTarget() || converter.isVisibleCrayPointerTarget(ultimateSymbol);
// Do not allocate storage for cray pointee. The address inside the cray
// pointer will be used instead when using the pointee. Allocating space
@@ -2095,6 +2095,8 @@ static void genDeclareSymbol(Fortran::lower::AbstractConverter &converter,
fir::FortranVariableFlagsEnum extraFlags = {};
if (isCapturedInInternalProcedure(converter, sym))
extraFlags = extraFlags | fir::FortranVariableFlagsEnum::internal_assoc;
+ if (converter.isVisibleCrayPointerTarget(sym))
+ extraFlags = extraFlags | fir::FortranVariableFlagsEnum::target;
fir::FortranVariableFlagsAttr attributes =
Fortran::lower::translateSymbolAttributes(builder.getContext(), sym,
extraFlags);
diff --git a/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90 b/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90
index b513a73be1fd8..ff12fc7744a8e 100644
--- a/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90
+++ b/flang/test/Lower/HLFIR/visible-cray-pointer-target.f90
@@ -36,3 +36,14 @@ subroutine ordinary_loc()
! CHECK-LABEL: func.func @_QPordinary_loc()
! CHECK: fir.alloca f32 {bindc_name = "object", uniq_name = "_QFordinary_locEobject"}
+
+subroutine hidden_association(ptr, associated)
+ real :: pointee, associated
+ integer(8) :: ptr
+ pointer(ptr, pointee)
+ call associate_target_to_cray_ptr(ptr, associated)
+end
+
+! CHECK-LABEL: func.func @_QPhidden_association(
+! CHECK: hlfir.declare %{{.*}} dummy_scope %{{.*}} {uniq_name = "_QFhidden_associationEassociated"}
+! CHECK-NOT: fortran_attrs = #fir.var_attrs<target>
More information about the flang-commits
mailing list