[llvm] [LICM][SimplifyCFG] Ignore frees for writable dereferenceability check (PR #202589)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 05:26:36 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Nikita Popov (nikic)

<details>
<summary>Changes</summary>

Both of these places only explicitly check for  dereferenceability because this is required for the `writable` attribute. Actual dereferenceability has already been established at this point, e.g. based on a prior access. As such, we can ignore frees here. We only care that the argument has an appropriately sized `dereferenceable` attribute.

---
Full diff: https://github.com/llvm/llvm-project/pull/202589.diff


6 Files Affected:

- (modified) llvm/include/llvm/Analysis/Loads.h (+9-3) 
- (modified) llvm/lib/Analysis/Loads.cpp (+34-24) 
- (modified) llvm/lib/Transforms/Scalar/LICM.cpp (+5-1) 
- (modified) llvm/lib/Transforms/Utils/SimplifyCFG.cpp (+5-2) 
- (modified) llvm/test/Transforms/LICM/scalar-promote.ll (+1) 
- (modified) llvm/test/Transforms/SimplifyCFG/speculate-store.ll (+1) 


``````````diff
diff --git a/llvm/include/llvm/Analysis/Loads.h b/llvm/include/llvm/Analysis/Loads.h
index 00510c11632b9..c48f2ee76b743 100644
--- a/llvm/include/llvm/Analysis/Loads.h
+++ b/llvm/include/llvm/Analysis/Loads.h
@@ -39,25 +39,31 @@ class TargetLibraryInfo;
 /// Return true if this is always a dereferenceable pointer. If the context
 /// instruction is specified perform context-sensitive analysis and return true
 /// if the pointer is dereferenceable at the specified instruction.
+/// If \p IgnoreFree is set, ignore potential frees of the object.
 LLVM_ABI bool isDereferenceablePointer(const Value *V, Type *Ty,
-                                       const SimplifyQuery &Q);
+                                       const SimplifyQuery &Q,
+                                       bool IgnoreFree = false);
 
 /// Returns true if V is always a dereferenceable pointer with alignment
 /// greater or equal than requested. If the context instruction is specified
 /// performs context-sensitive analysis and returns true if the pointer is
 /// dereferenceable at the specified instruction.
+/// If \p IgnoreFree is set, ignore potential frees of the object.
 LLVM_ABI bool isDereferenceableAndAlignedPointer(const Value *V, Type *Ty,
                                                  Align Alignment,
-                                                 const SimplifyQuery &Q);
+                                                 const SimplifyQuery &Q,
+                                                 bool IgnoreFree = false);
 
 /// Returns true if V is always dereferenceable for Size byte with alignment
 /// greater or equal than requested. If the context instruction is specified
 /// performs context-sensitive analysis and returns true if the pointer is
 /// dereferenceable at the specified instruction.
+/// If \p IgnoreFree is set, ignore potential frees of the object.
 LLVM_ABI bool isDereferenceableAndAlignedPointer(const Value *V,
                                                  Align Alignment,
                                                  const APInt &Size,
-                                                 const SimplifyQuery &Q);
+                                                 const SimplifyQuery &Q,
+                                                 bool IgnoreFree = false);
 
 /// Return true if we know that executing a load from this value cannot trap.
 ///
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 16fb1e6c9beff..d6c7ed029631b 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -33,7 +33,7 @@ static bool isAligned(const Value *Base, Align Alignment,
 }
 
 static bool isDereferenceableAndAlignedPointerViaAssumption(
-    const Value *Ptr, Align Alignment, const SimplifyQuery &SQ,
+    const Value *Ptr, Align Alignment, const SimplifyQuery &SQ, bool IgnoreFree,
     function_ref<bool(const RetainedKnowledge &RK)> CheckSize) {
   if (!SQ.CxtI)
     return false;
@@ -41,7 +41,7 @@ static bool isDereferenceableAndAlignedPointerViaAssumption(
   /// be proven by an assume if needed.
   RetainedKnowledge AlignRK;
   RetainedKnowledge DerefRK;
-  bool PtrCanBeFreed = Ptr->canBeFreed();
+  bool PtrCanBeFreed = Ptr->canBeFreed() && !IgnoreFree;
   bool IsAligned = Ptr->getPointerAlignment(SQ.DL) >= Alignment;
   return getKnowledgeForValue(
       Ptr, {Attribute::Dereferenceable, Attribute::Alignment}, *SQ.AC,
@@ -68,7 +68,8 @@ static bool isDereferenceableAndAlignedPointerViaAssumption(
 /// a simple load or store.
 static bool isDereferenceableAndAlignedPointer(
     const Value *V, Align Alignment, const APInt &Size, const SimplifyQuery &SQ,
-    SmallPtrSetImpl<const Value *> &Visited, unsigned MaxDepth) {
+    bool IgnoreFree, SmallPtrSetImpl<const Value *> &Visited,
+    unsigned MaxDepth) {
   assert(V->getType()->isPointerTy() && "Base must be pointer");
 
   // Recursion limit.
@@ -102,22 +103,25 @@ static bool isDereferenceableAndAlignedPointer(
     // addrspacecast, so we can't do arithmetic directly on the APInt values.
     return isDereferenceableAndAlignedPointer(
         Base, Alignment, Offset + Size.sextOrTrunc(Offset.getBitWidth()), SQ,
-        Visited, MaxDepth);
+        IgnoreFree, Visited, MaxDepth);
   }
 
   // bitcast instructions are no-ops as far as dereferenceability is concerned.
   if (const BitCastOperator *BC = dyn_cast<BitCastOperator>(V)) {
     if (BC->getSrcTy()->isPointerTy())
       return isDereferenceableAndAlignedPointer(BC->getOperand(0), Alignment,
-                                                Size, SQ, Visited, MaxDepth);
+                                                Size, SQ, IgnoreFree, Visited,
+                                                MaxDepth);
   }
 
   // Recurse into both hands of select.
   if (const SelectInst *Sel = dyn_cast<SelectInst>(V)) {
     return isDereferenceableAndAlignedPointer(Sel->getTrueValue(), Alignment,
-                                              Size, SQ, Visited, MaxDepth) &&
+                                              Size, SQ, IgnoreFree, Visited,
+                                              MaxDepth) &&
            isDereferenceableAndAlignedPointer(Sel->getFalseValue(), Alignment,
-                                              Size, SQ, Visited, MaxDepth);
+                                              Size, SQ, IgnoreFree, Visited,
+                                              MaxDepth);
   }
 
   auto IsKnownDeref = [&]() {
@@ -129,7 +133,7 @@ static bool isDereferenceableAndAlignedPointer(
       return false;
 
     auto *I = dyn_cast<Instruction>(V);
-    if (CheckForFreed) {
+    if (CheckForFreed && !IgnoreFree) {
       const Instruction *DefI;
       if (I) {
         // We don't want to consider frees by the instruction producing the
@@ -177,7 +181,7 @@ static bool isDereferenceableAndAlignedPointer(
     if (auto *RP = getArgumentAliasingToReturnedPointer(
             Call, /*MustPreserveOffset=*/true))
       return isDereferenceableAndAlignedPointer(RP, Alignment, Size, SQ,
-                                                Visited, MaxDepth);
+                                                IgnoreFree, Visited, MaxDepth);
 
     // If we have a call we can't recurse through, check to see if this is an
     // allocation function for which we can establish an minimum object size.
@@ -208,35 +212,40 @@ static bool isDereferenceableAndAlignedPointer(
 
   // For gc.relocate, look through relocations
   if (const GCRelocateInst *RelocateInst = dyn_cast<GCRelocateInst>(V))
-    return isDereferenceableAndAlignedPointer(
-        RelocateInst->getDerivedPtr(), Alignment, Size, SQ, Visited, MaxDepth);
+    return isDereferenceableAndAlignedPointer(RelocateInst->getDerivedPtr(),
+                                              Alignment, Size, SQ, IgnoreFree,
+                                              Visited, MaxDepth);
 
   if (const AddrSpaceCastOperator *ASC = dyn_cast<AddrSpaceCastOperator>(V))
-    return isDereferenceableAndAlignedPointer(ASC->getOperand(0), Alignment,
-                                              Size, SQ, Visited, MaxDepth);
+    return isDereferenceableAndAlignedPointer(
+        ASC->getOperand(0), Alignment, Size, SQ, IgnoreFree, Visited, MaxDepth);
 
-  return SQ.AC && isDereferenceableAndAlignedPointerViaAssumption(
-                      V, Alignment, SQ, [Size](const RetainedKnowledge &RK) {
-                        return RK.ArgValue >= Size.getZExtValue();
-                      });
+  return SQ.AC &&
+         isDereferenceableAndAlignedPointerViaAssumption(
+             V, Alignment, SQ, IgnoreFree, [Size](const RetainedKnowledge &RK) {
+               return RK.ArgValue >= Size.getZExtValue();
+             });
 }
 
 bool llvm::isDereferenceableAndAlignedPointer(const Value *V, Align Alignment,
                                               const APInt &Size,
-                                              const SimplifyQuery &SQ) {
+                                              const SimplifyQuery &SQ,
+                                              bool IgnoreFree) {
   // Note: At the moment, Size can be zero.  This ends up being interpreted as
   // a query of whether [Base, V] is dereferenceable and V is aligned (since
   // that's what the implementation happened to do).  It's unclear if this is
   // the desired semantic, but at least SelectionDAG does exercise this case.
 
   SmallPtrSet<const Value *, 32> Visited;
-  return ::isDereferenceableAndAlignedPointer(V, Alignment, Size, SQ, Visited,
+  return ::isDereferenceableAndAlignedPointer(V, Alignment, Size, SQ,
+                                              IgnoreFree, Visited,
                                               /*MaxDepth=*/16);
 }
 
 bool llvm::isDereferenceableAndAlignedPointer(const Value *V, Type *Ty,
                                               Align Alignment,
-                                              const SimplifyQuery &SQ) {
+                                              const SimplifyQuery &SQ,
+                                              bool IgnoreFree) {
   // For unsized types or scalable vectors we don't know exactly how many bytes
   // are dereferenced, so bail out.
   if (!Ty->isSized() || Ty->isScalableTy())
@@ -249,12 +258,13 @@ bool llvm::isDereferenceableAndAlignedPointer(const Value *V, Type *Ty,
 
   APInt AccessSize(SQ.DL.getPointerTypeSizeInBits(V->getType()),
                    SQ.DL.getTypeStoreSize(Ty));
-  return isDereferenceableAndAlignedPointer(V, Alignment, AccessSize, SQ);
+  return isDereferenceableAndAlignedPointer(V, Alignment, AccessSize, SQ,
+                                            IgnoreFree);
 }
 
 bool llvm::isDereferenceablePointer(const Value *V, Type *Ty,
-                                    const SimplifyQuery &SQ) {
-  return isDereferenceableAndAlignedPointer(V, Ty, Align(1), SQ);
+                                    const SimplifyQuery &SQ, bool IgnoreFree) {
+  return isDereferenceableAndAlignedPointer(V, Ty, Align(1), SQ, IgnoreFree);
 }
 
 /// Test if A and B will obviously have the same value.
@@ -409,7 +419,7 @@ bool llvm::isDereferenceableAndAlignedInLoop(
   }
   SimplifyQuery SQ(DL, &DT, AC, CtxI);
   return isDereferenceableAndAlignedPointerViaAssumption(
-             Base, Alignment, SQ,
+             Base, Alignment, SQ, /*IgnoreFree=*/false,
              [&SE, AccessSizeSCEV, &LoopGuards](const RetainedKnowledge &RK) {
                return SE.isKnownPredicate(
                    CmpInst::ICMP_ULE,
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index c5b8eb4b07f0b..caa3835b840fa 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2149,9 +2149,13 @@ bool llvm::promoteLoopAccessesToScalars(
   if (StoreSafety == StoreSafetyUnknown) {
     Value *Object = getUnderlyingObject(SomePtr);
     bool ExplicitlyDereferenceableOnly;
+    // The dereferenceability query here is only required to satisfy the
+    // writable contract, actual dereferenceability has already been proven
+    // above. As such, we can ignore frees.
     if (isWritableObject(Object, ExplicitlyDereferenceableOnly) &&
         (!ExplicitlyDereferenceableOnly ||
-         isDereferenceablePointer(SomePtr, AccessTy, MDL)) &&
+         isDereferenceablePointer(SomePtr, AccessTy, MDL,
+                                  /*IgnoreFree=*/true)) &&
         isThreadLocalObject(Object, CurLoop, DT, TTI))
       StoreSafety = StoreSafe;
   }
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 4b21b20beb895..222d43579abc3 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3073,13 +3073,16 @@ static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB,
           LI->isSimple() && LI->getAlign() >= StoreToHoist->getAlign()) {
         Value *Obj = getUnderlyingObject(StorePtr);
         bool ExplicitlyDereferenceableOnly;
+        // The dereferenceability query here is only required to satisfy the
+        // writable contract, actual dereferenceability is proven by the
+        // presence of an access. As such, we can ignore frees.
         if (isWritableObject(Obj, ExplicitlyDereferenceableOnly) &&
             capturesNothing(
                 PointerMayBeCaptured(Obj, CaptureComponents::Provenance)
                     .WithoutRet) &&
             (!ExplicitlyDereferenceableOnly ||
-             isDereferenceablePointer(StorePtr, StoreTy,
-                                      LI->getDataLayout()))) {
+             isDereferenceablePointer(StorePtr, StoreTy, LI->getDataLayout(),
+                                      /*IgnoreFree=*/true))) {
           // Found a previous load, return it.
           return LI;
         }
diff --git a/llvm/test/Transforms/LICM/scalar-promote.ll b/llvm/test/Transforms/LICM/scalar-promote.ll
index 3af65df55a099..ee25ba90c214e 100644
--- a/llvm/test/Transforms/LICM/scalar-promote.ll
+++ b/llvm/test/Transforms/LICM/scalar-promote.ll
@@ -1,5 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 6
 ; RUN: opt < %s -passes=licm -S | FileCheck %s
+; RUN: opt < %s -passes=licm -use-dereferenceable-at-point-semantics -S | FileCheck %s
 ; RUN: opt -aa-pipeline=tbaa,basic-aa -passes='require<aa>,require<target-ir>,require<scalar-evolution>,require<opt-remark-emit>,loop-mssa(licm)' -S %s | FileCheck %s
 target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"
 
diff --git a/llvm/test/Transforms/SimplifyCFG/speculate-store.ll b/llvm/test/Transforms/SimplifyCFG/speculate-store.ll
index 161ec380d31cf..57f6fffaadc29 100644
--- a/llvm/test/Transforms/SimplifyCFG/speculate-store.ll
+++ b/llvm/test/Transforms/SimplifyCFG/speculate-store.ll
@@ -1,5 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 ; RUN: opt -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S < %s | FileCheck %s
+; RUN: opt -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -use-dereferenceable-at-point-semantics -S < %s | FileCheck %s
 
 define void @ifconvertstore(ptr %A, i32 %B, i32 %C, i32 %D) {
 ; CHECK-LABEL: @ifconvertstore(

``````````

</details>


https://github.com/llvm/llvm-project/pull/202589


More information about the llvm-commits mailing list