[llvm] Fix/load constant forwarding (PR #203683)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 13 00:48:44 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

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