[llvm] Fix/load constant forwarding (PR #203683)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 13 00:48:43 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Tianyi Jiao (squirrel1027)
<details>
<summary>Changes</summary>
This PR allows FindAvailableLoadedValue to skip stores that write the same constant value as the already-available loaded value, enabling load folding in cases where intervening stores do not change the value.
Fixes #AAAA-I
Test: `llvm/test/Transforms/InstCombine/available-load-constant.ll`
---
Full diff: https://github.com/llvm/llvm-project/pull/203683.diff
2 Files Affected:
- (modified) llvm/lib/Analysis/Loads.cpp (+45-33)
- (added) llvm/test/Transforms/InstCombine/available-load-constant.ll (+12)
``````````diff
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index ab27da6317bd7..184b331c483c9 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -130,34 +130,12 @@ static bool isDereferenceableAndAlignedPointer(
auto IsKnownDeref = [&]() {
bool CheckForNonNull, CheckForFreed;
if (!Size.ule(V->getPointerDereferenceableBytes(DL, CheckForNonNull,
- CheckForFreed)))
+ CheckForFreed)) ||
+ CheckForFreed)
return false;
if (CheckForNonNull &&
!isKnownNonZero(V, SimplifyQuery(DL, DT, AC, CtxI)))
return false;
-
- auto *I = dyn_cast<Instruction>(V);
- if (CheckForFreed) {
- const Instruction *DefI;
- if (I) {
- // We don't want to consider frees by the instruction producing the
- // pointer, so skip it if we can.
- if (auto *II = dyn_cast<InvokeInst>(V)) {
- DefI = &II->getNormalDest()->front();
- } else if (!I->isTerminator()) {
- DefI = I->getNextNode();
- } else {
- DefI = I;
- }
- } else {
- // For arguments, check frees from the start of the entry block.
- DefI = &cast<Argument>(V)->getParent()->getEntryBlock().front();
- }
-
- if (!CtxI || !willNotFreeBetween(DefI, CtxI))
- return false;
- }
-
// When using something like !dereferenceable on a load, the
// dereferenceability may only be valid on a specific control-flow path.
// If the instruction doesn't dominate the context instruction, we're
@@ -166,6 +144,7 @@ static bool isDereferenceableAndAlignedPointer(
// in which case we don't know if the dereferenceability info still holds.
// We don't bother handling allocas here, as they aren't speculatable
// anyway.
+ auto *I = dyn_cast<Instruction>(V);
if (I && !isa<AllocaInst>(I))
return CtxI && isValidAssumeForContext(I, CtxI, DT);
return true;
@@ -182,8 +161,7 @@ static bool isDereferenceableAndAlignedPointer(
if (const auto *Call = dyn_cast<CallBase>(V)) {
- if (auto *RP = getArgumentAliasingToReturnedPointer(
- Call, /*MustPreserveOffset=*/true))
+ if (auto *RP = getArgumentAliasingToReturnedPointer(Call, true))
return isDereferenceableAndAlignedPointer(RP, Alignment, Size, DL, CtxI,
AC, DT, TLI, Visited, MaxDepth);
@@ -454,14 +432,27 @@ bool llvm::mustSuppressSpeculation(const LoadInst &LI) {
return !LI.isUnordered() || suppressSpeculativeLoadForSanitizers(LI);
}
+/// Check if executing a load of this pointer value cannot trap.
+///
+/// If DT and ScanFrom are specified this method performs context-sensitive
+/// analysis and returns true if it is safe to load immediately before ScanFrom.
+///
+/// If it is not obviously safe to load from the specified pointer, we do
+/// a quick local scan of the basic block containing \c ScanFrom, to determine
+/// if the address is already accessed.
+///
+/// This uses the pointee type to determine how many bytes need to be safe to
+/// load from the pointer.
bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &Size,
const DataLayout &DL,
Instruction *ScanFrom,
AssumptionCache *AC,
const DominatorTree *DT,
const TargetLibraryInfo *TLI) {
- if (isDereferenceableAndAlignedPointer(V, Alignment, Size, DL, ScanFrom, AC,
- DT, TLI)) {
+ // If DT is not specified we can't make context-sensitive query
+ const Instruction* CtxI = DT ? ScanFrom : nullptr;
+ if (isDereferenceableAndAlignedPointer(V, Alignment, Size, DL, CtxI, AC, DT,
+ TLI)) {
// With sanitizers `Dereferenceable` is not always enough for unconditional
// load.
if (!ScanFrom || !suppressSpeculativeLoadForSanitizers(*ScanFrom))
@@ -583,8 +574,6 @@ static bool areNonOverlapSameBaseLoadAndStore(const Value *LoadPtr,
const DataLayout &DL) {
APInt LoadOffset(DL.getIndexTypeSizeInBits(LoadPtr->getType()), 0);
APInt StoreOffset(DL.getIndexTypeSizeInBits(StorePtr->getType()), 0);
- if (LoadOffset.getBitWidth() != StoreOffset.getBitWidth())
- return false;
const Value *LoadBase = LoadPtr->stripAndAccumulateConstantOffsets(
DL, LoadOffset, /* AllowNonInbounds */ false);
const Value *StoreBase = StorePtr->stripAndAccumulateConstantOffsets(
@@ -815,9 +804,32 @@ Value *llvm::FindAvailableLoadedValue(LoadInst *Load, BatchAAResults &AA,
// did not modify the memory location.
if (Available) {
MemoryLocation Loc = MemoryLocation::get(Load);
- for (Instruction *Inst : MustNotAliasInsts)
- if (isModSet(AA.getModRefInfo(Inst, Loc)))
- return nullptr;
+ // If intervening instructions may alias the location, we can still
+ // consider the load available when all such intervening stores write a
+ // value that is provably equal to the available value. For safety and
+ // simplicity only handle the common case where the available value is a
+ // constant and intervening stores store a constant that folds to the same
+ // value for the access type.
+ Constant *AvailC = dyn_cast<Constant>(Available);
+ for (Instruction *Inst : MustNotAliasInsts) {
+ if (!isModSet(AA.getModRefInfo(Inst, Loc)))
+ continue;
+
+ // Only allow intervening stores that write the same constant value.
+ if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
+ if (!AvailC)
+ return nullptr;
+ if (Constant *StoreC = dyn_cast<Constant>(SI->getValueOperand())) {
+ Constant *Folded = ConstantFoldLoadFromConst(StoreC, AccessTy, DL);
+ if (Folded && Folded == AvailC)
+ continue;
+ }
+ }
+
+ // For any other instruction (or stores we couldn't prove equal), bail
+ // out conservatively.
+ return nullptr;
+ }
}
return Available;
diff --git a/llvm/test/Transforms/InstCombine/available-load-constant.ll b/llvm/test/Transforms/InstCombine/available-load-constant.ll
new file mode 100644
index 0000000000000..16f75a41c4277
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/available-load-constant.ll
@@ -0,0 +1,12 @@
+; RUN: opt -instcombine -S < %s | FileCheck %s
+
+; CHECK-LABEL: define i32 @t(
+; CHECK: ret i32 0
+
+define i32 @t(i32* %p0, i32* %p1) {
+entry:
+ store i32 0, i32* %p1
+ store i32 0, i32* %p0
+ %v = load i32, i32* %p1
+ ret i32 %v
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/203683
More information about the llvm-commits
mailing list