[llvm] [InferAddressSpaces] Infer pointer stored and then loaded from global variable (PR #159755)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 19 03:54:45 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: Wenju He (wenju-he)
<details>
<summary>Changes</summary>
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`.
---
Full diff: https://github.com/llvm/llvm-project/pull/159755.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp (+49-3)
- (added) llvm/test/Transforms/InferAddressSpaces/AMDGPU/gv-store-load.ll (+51)
``````````diff
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()
``````````
</details>
https://github.com/llvm/llvm-project/pull/159755
More information about the llvm-commits
mailing list