[llvm] [InferAddressSpaces] Infer pointer stored and then loaded from global variable (PR #159755)
Wenju He via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 19 03:56:41 PDT 2025
https://github.com/wenju-he updated https://github.com/llvm/llvm-project/pull/159755
>From 349dbc623cf7c490e969289b716fb94b60f71e79 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 19 Sep 2025 12:42:34 +0200
Subject: [PATCH 1/2] [InferAddressSpaces] Infer pointer stored and then loaded
from global variable
Load of a global variable producing a (generic) pointer is treated as an
address expression if every user of the global are local loads or stores
in the same function, with the store actually writing to the global.
The load's pointer AS is inferred from the stored pointer operands.
The test is reduced from `SYCL-CTS/test_hierarchical hierarchical_implicit_barriers`.
---
.../Transforms/Scalar/InferAddressSpaces.cpp | 52 +++++++++++++++++--
.../AMDGPU/gv-store-load.ll | 51 ++++++++++++++++++
2 files changed, 100 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Transforms/InferAddressSpaces/AMDGPU/gv-store-load.ll
diff --git a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
index 3ad87545953ff..179820bf3eb3e 100644
--- a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
@@ -304,10 +304,24 @@ static bool isNoopPtrIntCastPair(const Operator *I2P, const DataLayout &DL,
(P2IOp0AS == I2PAS || TTI->isNoopAddrSpaceCast(P2IOp0AS, I2PAS));
}
+// Returns true if every user of a given GV are "simple" loads or stores in the
+// same function, with the store actually writing to GV.
+static bool isLocallyAccessedBySimpleLoadsStores(const GlobalVariable *GV,
+ const Function *F) {
+ return all_of(GV->users(), [=](const User *U) {
+ if (const auto *SI = dyn_cast<StoreInst>(U))
+ return SI->getPointerOperand() == GV && SI->getFunction() == F;
+ if (const auto *LI = dyn_cast<LoadInst>(U))
+ return LI->getFunction() == F;
+ return false;
+ });
+}
+
// Returns true if V is an address expression.
// TODO: Currently, we only consider:
// - arguments
// - phi, bitcast, addrspacecast, and getelementptr operators
+// - load
static bool isAddressExpression(const Value &V, const DataLayout &DL,
const TargetTransformInfo *TTI) {
@@ -335,6 +349,16 @@ static bool isAddressExpression(const Value &V, const DataLayout &DL,
}
case Instruction::IntToPtr:
return isNoopPtrIntCastPair(Op, DL, TTI);
+ case Instruction::Load: {
+ const auto *LI = cast<LoadInst>(Op);
+ if (LI->getType()->isPtrOrPtrVectorTy()) {
+ // Heuristic: treat load-of-GV as an address expression only if the GV is
+ // locally accessed by load and store.
+ if (const auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand()))
+ return isLocallyAccessedBySimpleLoadsStores(GV, LI->getFunction());
+ }
+ return TTI->getAssumedAddrSpace(&V) != UninitializedAddressSpace;
+ }
default:
// That value is an address expression if it has an assumed address space.
return TTI->getAssumedAddrSpace(&V) != UninitializedAddressSpace;
@@ -342,6 +366,8 @@ static bool isAddressExpression(const Value &V, const DataLayout &DL,
}
// Returns the pointer operands of V.
+// If V is a load from a global variable G, also collect the pointer values
+// stored into G.
//
// Precondition: V is an address expression.
static SmallVector<Value *, 2>
@@ -373,6 +399,20 @@ getPointerOperands(const Value &V, const DataLayout &DL,
auto *P2I = cast<Operator>(Op.getOperand(0));
return {P2I->getOperand(0)};
}
+ case Instruction::Load: {
+ assert(V.getType()->isPtrOrPtrVectorTy());
+ if (const auto *GV = cast<GlobalVariable>(Op.getOperand(0))) {
+ assert(isLocallyAccessedBySimpleLoadsStores(
+ GV, cast<LoadInst>(&V)->getFunction()));
+ SmallVector<Value *, 2> PtrOps;
+ for (const auto *U : GV->users())
+ if (const auto *SI = dyn_cast<StoreInst>(U);
+ SI && SI->getPointerOperand() == GV)
+ PtrOps.push_back(cast<Operator>(U)->getOperand(0));
+ return PtrOps;
+ }
+ return {};
+ }
default:
llvm_unreachable("Unexpected instruction type.");
}
@@ -561,9 +601,11 @@ InferAddressSpacesImpl::collectFlatAddressExpressions(Function &F) const {
PushPtrOperand(GEP->getPointerOperand());
} else if (auto *LI = dyn_cast<LoadInst>(&I))
PushPtrOperand(LI->getPointerOperand());
- else if (auto *SI = dyn_cast<StoreInst>(&I))
+ else if (auto *SI = dyn_cast<StoreInst>(&I)) {
+ if (SI->getValueOperand()->getType()->isPtrOrPtrVectorTy())
+ PushPtrOperand(SI->getValueOperand());
PushPtrOperand(SI->getPointerOperand());
- else if (auto *RMW = dyn_cast<AtomicRMWInst>(&I))
+ } else if (auto *RMW = dyn_cast<AtomicRMWInst>(&I))
PushPtrOperand(RMW->getPointerOperand());
else if (auto *CmpX = dyn_cast<AtomicCmpXchgInst>(&I))
PushPtrOperand(CmpX->getPointerOperand());
@@ -755,6 +797,10 @@ Value *InferAddressSpacesImpl::cloneInstructionWithNewAddressSpace(
// back.
return new AddrSpaceCastInst(Src, NewPtrType);
}
+ case Instruction::Load:
+ if (I->getType()->isPtrOrPtrVectorTy())
+ return new AddrSpaceCastInst(I, NewPtrType);
+ return nullptr;
default:
llvm_unreachable("Unexpected opcode");
}
@@ -866,7 +912,7 @@ Value *InferAddressSpacesImpl::cloneValueWithNewAddressSpace(
I, NewAddrSpace, ValueWithNewAddrSpace, PredicatedAS, PoisonUsesToFix);
if (Instruction *NewI = dyn_cast_or_null<Instruction>(NewV)) {
if (NewI->getParent() == nullptr) {
- NewI->insertBefore(I->getIterator());
+ NewI->insertAfter(I->getIterator());
NewI->takeName(I);
NewI->setDebugLoc(I->getDebugLoc());
}
diff --git a/llvm/test/Transforms/InferAddressSpaces/AMDGPU/gv-store-load.ll b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/gv-store-load.ll
new file mode 100644
index 0000000000000..8c3274758b929
--- /dev/null
+++ b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/gv-store-load.ll
@@ -0,0 +1,51 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -passes=infer-address-spaces %s | FileCheck %s
+
+ at WGCopy = internal unnamed_addr addrspace(3) global ptr poison, align 16
+
+; Function Attrs: nounwind
+define void @gv_store_load() {
+; CHECK-LABEL: define void @gv_store_load() {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[AGG_TMP1617:%.*]] = alloca i64, align 8, addrspace(5)
+; CHECK-NEXT: [[IS:%.*]] = call i1 @is_leader()
+; CHECK-NEXT: br i1 [[IS]], label %[[LEADER:.*]], label %[[MERGE:.*]]
+; CHECK: [[LEADER]]:
+; CHECK-NEXT: br label %[[MERGE]]
+; CHECK: [[MERGE]]:
+; CHECK-NEXT: [[AGG_TMP_I_SROA_0_0:%.*]] = phi ptr addrspace(5) [ [[AGG_TMP1617]], %[[LEADER]] ], [ undef, %[[ENTRY]] ]
+; CHECK-NEXT: [[TMP0:%.*]] = addrspacecast ptr addrspace(5) [[AGG_TMP_I_SROA_0_0]] to ptr
+; CHECK-NEXT: br i1 [[IS]], label %[[LEADER_I:.*]], label %[[EXIT:.*]]
+; CHECK: [[LEADER_I]]:
+; CHECK-NEXT: store ptr [[TMP0]], ptr addrspace(3) @WGCopy, align 16
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr addrspace(3) @WGCopy, align 16
+; CHECK-NEXT: [[AGG_TMP_I_SROA_0_0_COPYLOAD:%.*]] = addrspacecast ptr [[TMP1]] to ptr addrspace(5)
+; CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr addrspace(5) [[AGG_TMP_I_SROA_0_0_COPYLOAD]], align 8
+; CHECK-NEXT: ret void
+;
+entry:
+ %agg.tmp1617 = alloca i64, align 8, addrspace(5)
+ %is = call i1 @is_leader()
+ br i1 %is, label %leader, label %merge
+
+leader: ; preds = %entry
+ %group.ascast.i = addrspacecast ptr addrspace(5) %agg.tmp1617 to ptr
+ br label %merge
+
+merge: ; preds = %leader, %entry
+ %agg.tmp.i.sroa.0.0 = phi ptr [ %group.ascast.i, %leader ], [ undef, %entry ]
+ br i1 %is, label %leader.i, label %exit
+
+leader.i: ; preds = %merge
+ store ptr %agg.tmp.i.sroa.0.0, ptr addrspace(3) @WGCopy, align 16
+ br label %exit
+
+exit: ; preds = %leader.i, %merge
+ %agg.tmp.i.sroa.0.0.copyload = load ptr, ptr addrspace(3) @WGCopy, align 16
+ %15 = load i64, ptr %agg.tmp.i.sroa.0.0.copyload, align 8
+ ret void
+}
+
+declare i1 @is_leader()
>From 4d2ad490a531b5a5714ad3d1d6fa24e579ed2310 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 19 Sep 2025 12:56:30 +0200
Subject: [PATCH 2/2] update per copilot review
---
llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
index 179820bf3eb3e..f2d63fa9ffa82 100644
--- a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
@@ -406,8 +406,7 @@ getPointerOperands(const Value &V, const DataLayout &DL,
GV, cast<LoadInst>(&V)->getFunction()));
SmallVector<Value *, 2> PtrOps;
for (const auto *U : GV->users())
- if (const auto *SI = dyn_cast<StoreInst>(U);
- SI && SI->getPointerOperand() == GV)
+ if (isa<StoreInst>(U))
PtrOps.push_back(cast<Operator>(U)->getOperand(0));
return PtrOps;
}
More information about the llvm-commits
mailing list