[llvm] Fix/load constant forwarding (PR #203683)
Tianyi Jiao via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 13 00:47:47 PDT 2026
https://github.com/squirrel1027 created https://github.com/llvm/llvm-project/pull/203683
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`
>From 61621fe434e67b375c63f28e5a8dac2a16704b1e Mon Sep 17 00:00:00 2001
From: Tianyi Jiao <2917163464 at qq.com>
Date: Sat, 13 Jun 2026 15:42:02 +0800
Subject: [PATCH 1/2] Update Loads.cpp
Allow skipping stores that write the same constant value.
---
llvm/lib/Analysis/Loads.cpp | 78 +++++++++++++++++++++----------------
1 file changed, 45 insertions(+), 33 deletions(-)
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;
>From 51bc45488cd8e47a41ac231c974dc47ba1fecdfa Mon Sep 17 00:00:00 2001
From: Tianyi Jiao <2917163464 at qq.com>
Date: Sat, 13 Jun 2026 15:45:07 +0800
Subject: [PATCH 2/2] Create available-load-constant.ll
---
.../InstCombine/available-load-constant.ll | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/available-load-constant.ll
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
+}
More information about the llvm-commits
mailing list