[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