[llvm] [Loads] Migrate isSafeToLoadUnconditionally() to SimplifyQuery (PR #218424)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 07:25:55 PDT 2026


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/218424

isDereferenceablePointer() was changed to use SimplifyQuery some time ago, do the same for isSafeToLoadUnconditionally().

>From 06b48ff9ef0b140218b337b1d19e3caa02b9d76c Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 24 Aug 2026 16:21:18 +0200
Subject: [PATCH] [Loads] Migrate isSafeToLoadUnconditionally() to
 SimplifyQuery

isDereferenceablePointer() was changed to use SimplifyQuery some
time ago, do the same for isSafeToLoadUnconditionally().
---
 llvm/include/llvm/Analysis/Loads.h            | 25 +++++------
 llvm/lib/Analysis/Loads.cpp                   | 43 ++++++++-----------
 .../InstCombineLoadStoreAlloca.cpp            |  4 +-
 llvm/lib/Transforms/Scalar/SROA.cpp           |  7 +--
 .../Scalar/TailRecursionElimination.cpp       |  2 +-
 .../Transforms/Vectorize/SLPVectorizer.cpp    | 12 +++---
 .../Transforms/Vectorize/VectorCombine.cpp    | 12 +++---
 7 files changed, 47 insertions(+), 58 deletions(-)

diff --git a/llvm/include/llvm/Analysis/Loads.h b/llvm/include/llvm/Analysis/Loads.h
index 9190e47707215..14cdd18671e0b 100644
--- a/llvm/include/llvm/Analysis/Loads.h
+++ b/llvm/include/llvm/Analysis/Loads.h
@@ -69,16 +69,15 @@ LLVM_ABI bool isDereferenceablePointer(const Value *V, const APInt &Size,
 
 /// Return true if we know that executing a load from this value cannot trap.
 ///
-/// If ScanFrom is specified this method performs context-sensitive analysis
-/// and returns true if it is safe to load immediately before ScanFrom.
+/// If SQ.CxtI is specified this method performs context-sensitive analysis
+/// and returns true if it is safe to load immediately before SQ.CxtI.
 ///
 /// If it is not obviously safe to load from the specified pointer, we do a
-/// quick local scan of the basic block containing ScanFrom, to determine if
+/// quick local scan of the basic block containing SQ.CxtI, to determine if
 /// the address is already accessed.
-LLVM_ABI bool isSafeToLoadUnconditionally(
-    Value *V, Align Alignment, const APInt &Size, const DataLayout &DL,
-    Instruction *ScanFrom, AssumptionCache *AC = nullptr,
-    const DominatorTree *DT = nullptr, const TargetLibraryInfo *TLI = nullptr);
+LLVM_ABI bool isSafeToLoadUnconditionally(Value *V, Align Alignment,
+                                          const APInt &Size,
+                                          const SimplifyQuery &SQ);
 
 /// Return true if we can prove that the given load (which is assumed to be
 /// within the specified loop) would access only dereferenceable memory, and
@@ -109,16 +108,14 @@ isReadOnlyLoop(Loop *L, ScalarEvolution *SE, DominatorTree *DT,
 
 /// Return true if we know that executing a load from this 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 SQ.CxtI are specified this method performs context-sensitive analysis
+/// and returns true if it is safe to load immediately before SQ.CxtI.
 ///
 /// If it is not obviously safe to load from the specified pointer, we do a
-/// quick local scan of the basic block containing ScanFrom, to determine if
+/// quick local scan of the basic block containing SQ.CxtI, to determine if
 /// the address is already accessed.
-LLVM_ABI bool isSafeToLoadUnconditionally(
-    Value *V, Type *Ty, Align Alignment, const DataLayout &DL,
-    Instruction *ScanFrom, AssumptionCache *AC = nullptr,
-    const DominatorTree *DT = nullptr, const TargetLibraryInfo *TLI = nullptr);
+LLVM_ABI bool isSafeToLoadUnconditionally(Value *V, Type *Ty, Align Alignment,
+                                          const SimplifyQuery &SQ);
 
 /// Return true if speculation of the given load must be suppressed to avoid
 /// ordering or interfering with an active sanitizer.  If not suppressed,
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index e2c04157f14bc..de9022c540d42 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -453,21 +453,17 @@ bool llvm::mustSuppressSpeculation(const LoadInst &LI) {
   return !LI.isUnordered() || suppressSpeculativeLoadForSanitizers(LI);
 }
 
-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, SimplifyQuery(DL, TLI, DT, AC, ScanFrom))) {
+bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment,
+                                       const APInt &Size,
+                                       const SimplifyQuery &SQ) {
+  if (isDereferenceableAndAlignedPointer(V, Alignment, Size, SQ)) {
     // With sanitizers `Dereferenceable` is not always enough for unconditional
     // load.
-    if (!ScanFrom || !suppressSpeculativeLoadForSanitizers(*ScanFrom))
+    if (!SQ.CxtI || !suppressSpeculativeLoadForSanitizers(*SQ.CxtI))
       return true;
   }
 
-  if (!ScanFrom)
+  if (!SQ.CxtI)
     return false;
 
   if (Size.getBitWidth() > 64)
@@ -479,8 +475,7 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &S
   // from/to.  If so, the previous load or store would have already trapped,
   // so there is no harm doing an extra load (also, CSE will later eliminate
   // the load entirely).
-  BasicBlock::iterator BBI = ScanFrom->getIterator(),
-                       E = ScanFrom->getParent()->begin();
+  auto BBI = SQ.CxtI->getIterator(), E = SQ.CxtI->getParent()->begin();
 
   // We can at least always strip pointer casts even though we can't use the
   // base here.
@@ -495,10 +490,10 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &S
         !isa<LifetimeIntrinsic>(BBI))
       return false;
 
-    Value *AccessedPtr;
+    const Value *AccessedPtr;
     Type *AccessedTy;
     Align AccessedAlign;
-    if (LoadInst *LI = dyn_cast<LoadInst>(BBI)) {
+    if (const auto *LI = dyn_cast<LoadInst>(BBI)) {
       // Ignore volatile loads. The execution of a volatile load cannot
       // be used to prove an address is backed by regular memory; it can,
       // for example, point to an MMIO register.
@@ -507,7 +502,7 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &S
       AccessedPtr = LI->getPointerOperand();
       AccessedTy = LI->getType();
       AccessedAlign = LI->getAlign();
-    } else if (StoreInst *SI = dyn_cast<StoreInst>(BBI)) {
+    } else if (const auto *SI = dyn_cast<StoreInst>(BBI)) {
       // Ignore volatile stores (see comment for loads).
       if (SI->isVolatile())
         continue;
@@ -522,28 +517,24 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &S
 
     // Handle trivial cases.
     if (AccessedPtr == V &&
-        TypeSize::isKnownLE(LoadSize, DL.getTypeStoreSize(AccessedTy)))
+        TypeSize::isKnownLE(LoadSize, SQ.DL.getTypeStoreSize(AccessedTy)))
       return true;
 
     if (AreEquivalentAddressValues(AccessedPtr->stripPointerCasts(), V) &&
-        TypeSize::isKnownLE(LoadSize, DL.getTypeStoreSize(AccessedTy)))
+        TypeSize::isKnownLE(LoadSize, SQ.DL.getTypeStoreSize(AccessedTy)))
       return true;
   }
   return false;
 }
 
 bool llvm::isSafeToLoadUnconditionally(Value *V, Type *Ty, Align Alignment,
-                                       const DataLayout &DL,
-                                       Instruction *ScanFrom,
-                                       AssumptionCache *AC,
-                                       const DominatorTree *DT,
-                                       const TargetLibraryInfo *TLI) {
-  TypeSize TySize = DL.getTypeStoreSize(Ty);
+                                       const SimplifyQuery &SQ) {
+  TypeSize TySize = SQ.DL.getTypeStoreSize(Ty);
   if (TySize.isScalable())
     return false;
-  APInt Size(DL.getIndexTypeSizeInBits(V->getType()), TySize.getFixedValue());
-  return isSafeToLoadUnconditionally(V, Alignment, Size, DL, ScanFrom, AC, DT,
-                                     TLI);
+  APInt Size(SQ.DL.getIndexTypeSizeInBits(V->getType()),
+             TySize.getFixedValue());
+  return isSafeToLoadUnconditionally(V, Alignment, Size, SQ);
 }
 
 /// DefMaxInstsToScan - the default number of maximum instructions
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 839fabb1a7e1c..6cb211295bc9e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1161,9 +1161,9 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
       //  select(Cond, load (addrspacecast(&V1)), load (addrspacecast(&V2))).
       Align Alignment = LI.getAlign();
       if (isSafeToLoadUnconditionally(SI->getOperand(1), LI.getType(),
-                                      Alignment, DL, SI) &&
+                                      Alignment, SQ.getWithInstruction(SI)) &&
           isSafeToLoadUnconditionally(SI->getOperand(2), LI.getType(),
-                                      Alignment, DL, SI)) {
+                                      Alignment, SQ.getWithInstruction(SI))) {
 
         auto MaybeCastedLoadOperand = [&](Value *Op) {
           if (ASC)
diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index e4770154e2998..19ff1d1a6baef 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -1612,7 +1612,8 @@ static bool isSafePHIToSpeculate(PHINode &PN) {
     // If this pointer is always safe to load, or if we can prove that there
     // is already a load in the block, then we can move the load to the pred
     // block.
-    if (isSafeToLoadUnconditionally(InVal, MaxAlign, LoadSize, DL, TI))
+    if (isSafeToLoadUnconditionally(InVal, MaxAlign, LoadSize,
+                                    SimplifyQuery(DL, TI)))
       continue;
 
     return false;
@@ -1708,8 +1709,8 @@ isSafeLoadOfSelectToSpeculate(LoadInst &LI, SelectInst &SI, bool PreserveCFG) {
 
   const DataLayout &DL = SI.getDataLayout();
   for (Value *Value : {SI.getTrueValue(), SI.getFalseValue()})
-    if (isSafeToLoadUnconditionally(Value, LI.getType(), LI.getAlign(), DL,
-                                    &LI))
+    if (isSafeToLoadUnconditionally(Value, LI.getType(), LI.getAlign(),
+                                    SimplifyQuery(DL, &LI)))
       Spec.setAsSpeculatable(/*isTrueVal=*/Value == SI.getTrueValue());
     else if (PreserveCFG)
       return Spec;
diff --git a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
index b3df27433a9f5..880b0bda90d70 100644
--- a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
@@ -411,7 +411,7 @@ static bool canMoveAboveCall(Instruction *I, CallInst *CI, AliasAnalysis *AA) {
       const DataLayout &DL = L->getDataLayout();
       if (isModSet(AA->getModRefInfo(CI, MemoryLocation::get(L))) ||
           !isSafeToLoadUnconditionally(L->getPointerOperand(), L->getType(),
-                                       L->getAlign(), DL, L))
+                                       L->getAlign(), SimplifyQuery(DL, L)))
         return false;
     }
   }
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 9e6d1e43f017a..dca5da197b5d8 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -6346,10 +6346,10 @@ static bool isMaskedLoadCompress(
   LoadVecTy = cast<FixedVectorType>(getWidenedType(ScalarTy, *Diff + 1));
   auto *LI = cast<LoadInst>(Order.empty() ? VL.front() : VL[Order.front()]);
   Align CommonAlignment = LI->getAlign();
-  IsMasked = !isSafeToLoadUnconditionally(
-      Ptr0, LoadVecTy, CommonAlignment, DL,
-      cast<LoadInst>(Order.empty() ? VL.back() : VL[Order.back()]), &AC, &DT,
-      &TLI);
+  SimplifyQuery SQ(
+      DL, &TLI, &DT, &AC,
+      cast<LoadInst>(Order.empty() ? VL.back() : VL[Order.back()]));
+  IsMasked = !isSafeToLoadUnconditionally(Ptr0, LoadVecTy, CommonAlignment, SQ);
   if (IsMasked && !TTI.isLegalMaskedLoad(LoadVecTy, CommonAlignment,
                                          LI->getPointerAddressSpace()))
     return false;
@@ -6394,9 +6394,9 @@ static bool isMaskedLoadCompress(
     // Check for potential segmented(interleaved) loads.
     VectorType *AlignedLoadVecTy = cast<VectorType>(getWidenedType(
         ScalarTy, getFullVectorNumberOfElements(TTI, ScalarTy, *Diff + 1)));
+    SimplifyQuery SQ(DL, &TLI, &DT, &AC, cast<LoadInst>(VL.back()));
     if (!isSafeToLoadUnconditionally(Ptr0, AlignedLoadVecTy, CommonAlignment,
-                                     DL, cast<LoadInst>(VL.back()), &AC, &DT,
-                                     &TLI))
+                                     SQ))
       AlignedLoadVecTy = LoadVecTy;
     if (TTI.isLegalInterleavedAccessType(AlignedLoadVecTy, CompressMask[1],
                                          CommonAlignment,
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 6b9d8791ba35e..7bf08d5c1e835 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -281,8 +281,8 @@ bool VectorCombine::vectorizeLoadInsert(Instruction &I) {
   auto *MinVecTy = VectorType::get(ScalarTy, MinVecNumElts, false);
   unsigned OffsetEltIndex = 0;
   Align Alignment = Load->getAlign();
-  if (!isSafeToLoadUnconditionally(SrcPtr, MinVecTy, Align(1), *DL, Load, SQ.AC,
-                                   SQ.DT)) {
+  if (!isSafeToLoadUnconditionally(SrcPtr, MinVecTy, Align(1),
+                                   SQ.getWithInstruction(Load))) {
     // It is not safe to load directly from the pointer, but we can still peek
     // through gep offsets and check if it safe to load from a base address with
     // updated alignment. If it is, we can shuffle the element(s) into place
@@ -308,8 +308,8 @@ bool VectorCombine::vectorizeLoadInsert(Instruction &I) {
       return false;
     OffsetEltIndex = OffsetEltIndexAP.getZExtValue();
 
-    if (!isSafeToLoadUnconditionally(SrcPtr, MinVecTy, Align(1), *DL, Load,
-                                     SQ.AC, SQ.DT))
+    if (!isSafeToLoadUnconditionally(SrcPtr, MinVecTy, Align(1),
+                                     SQ.getWithInstruction(Load)))
       return false;
 
     // Update alignment with offset value. Note that the offset could be negated
@@ -394,8 +394,8 @@ bool VectorCombine::widenSubvectorLoad(Instruction &I) {
   Value *SrcPtr = Load->getPointerOperand()->stripPointerCasts();
   assert(isa<PointerType>(SrcPtr->getType()) && "Expected a pointer type");
   Align Alignment = Load->getAlign();
-  if (!isSafeToLoadUnconditionally(SrcPtr, Ty, Align(1), *DL, Load, SQ.AC,
-                                   SQ.DT))
+  if (!isSafeToLoadUnconditionally(SrcPtr, Ty, Align(1),
+                                   SQ.getWithInstruction(Load)))
     return false;
 
   Alignment = std::max(SrcPtr->getPointerAlignment(*DL), Alignment);



More information about the llvm-commits mailing list