Skip to content

Commit 46d4a8c

Browse files
committed
[Loads] Support dereferenceable assumption with variable size.
Update isDereferenceableAndAlignedPointer to make use of dereferenceable assumptions with variable sizes via SCEV. To do so, factor out the logic to check via an assumption to a helper, and use SE to check if the access size is less than the dereferenceable size.
1 parent 65e44b4 commit 46d4a8c

File tree

4 files changed

+119
-36
lines changed

4 files changed

+119
-36
lines changed

llvm/include/llvm/Analysis/AssumeBundleQueries.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ void fillMapFromAssume(AssumeInst &Assume, RetainedKnowledgeMap &Result);
9999
struct RetainedKnowledge {
100100
Attribute::AttrKind AttrKind = Attribute::None;
101101
uint64_t ArgValue = 0;
102+
Value *IRArgValue = nullptr;
102103
Value *WasOn = nullptr;
103104
bool operator==(RetainedKnowledge Other) const {
104105
return AttrKind == Other.AttrKind && WasOn == Other.WasOn &&

llvm/lib/Analysis/AssumeBundleQueries.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ llvm::getKnowledgeFromBundle(AssumeInst &Assume,
114114
};
115115
if (BOI.End - BOI.Begin > ABA_Argument)
116116
Result.ArgValue = GetArgOr1(0);
117+
Result.IRArgValue = getValueFromBundleOpInfo(Assume, BOI, ABA_Argument);
117118
if (Result.AttrKind == Attribute::Alignment)
118119
if (BOI.End - BOI.Begin > ABA_Argument + 1)
119120
Result.ArgValue = MinAlign(Result.ArgValue, GetArgOr1(1));

llvm/lib/Analysis/Loads.cpp

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,35 @@ static bool isAligned(const Value *Base, Align Alignment,
3131
return Base->getPointerAlignment(DL) >= Alignment;
3232
}
3333

34+
static bool isDereferenceableAndAlignedPointerViaAssumption(
35+
const Value *Ptr, Align Alignment,
36+
function_ref<bool(const RetainedKnowledge &RK)> CheckSize,
37+
const DataLayout &DL, const Instruction *CtxI, AssumptionCache *AC,
38+
const DominatorTree *DT) {
39+
if (!CtxI || Ptr->canBeFreed())
40+
return false;
41+
/// Look through assumes to see if both dereferencability and alignment can
42+
/// be proven by an assume if needed.
43+
RetainedKnowledge AlignRK;
44+
RetainedKnowledge DerefRK;
45+
bool IsAligned = Ptr->getPointerAlignment(DL) >= Alignment;
46+
return getKnowledgeForValue(
47+
Ptr, {Attribute::Dereferenceable, Attribute::Alignment}, AC,
48+
[&](RetainedKnowledge RK, Instruction *Assume, auto) {
49+
if (!isValidAssumeForContext(Assume, CtxI, DT))
50+
return false;
51+
if (RK.AttrKind == Attribute::Alignment)
52+
AlignRK = std::max(AlignRK, RK);
53+
if (RK.AttrKind == Attribute::Dereferenceable)
54+
DerefRK = std::max(DerefRK, RK);
55+
IsAligned |= AlignRK && AlignRK.ArgValue >= Alignment.value();
56+
if (IsAligned && DerefRK && CheckSize(DerefRK))
57+
return true; // We have found what we needed so we stop looking
58+
return false; // Other assumes may have better information. so
59+
// keep looking
60+
});
61+
}
62+
3463
/// Test if V is always a pointer to allocated and suitably aligned memory for
3564
/// a simple load or store.
3665
static bool isDereferenceableAndAlignedPointer(
@@ -174,33 +203,41 @@ static bool isDereferenceableAndAlignedPointer(
174203
// information for values that cannot be freed in the function.
175204
// TODO: More precisely check if the pointer can be freed between assumption
176205
// and use.
177-
if (CtxI && !V->canBeFreed()) {
178-
/// Look through assumes to see if both dereferencability and alignment can
179-
/// be proven by an assume if needed.
180-
RetainedKnowledge AlignRK;
181-
RetainedKnowledge DerefRK;
182-
bool IsAligned = V->getPointerAlignment(DL) >= Alignment;
183-
if (getKnowledgeForValue(
184-
V, {Attribute::Dereferenceable, Attribute::Alignment}, AC,
185-
[&](RetainedKnowledge RK, Instruction *Assume, auto) {
186-
if (!isValidAssumeForContext(Assume, CtxI, DT))
187-
return false;
188-
if (RK.AttrKind == Attribute::Alignment)
189-
AlignRK = std::max(AlignRK, RK);
190-
if (RK.AttrKind == Attribute::Dereferenceable)
191-
DerefRK = std::max(DerefRK, RK);
192-
IsAligned |= AlignRK && AlignRK.ArgValue >= Alignment.value();
193-
if (IsAligned && DerefRK &&
194-
DerefRK.ArgValue >= Size.getZExtValue())
195-
return true; // We have found what we needed so we stop looking
196-
return false; // Other assumes may have better information. so
197-
// keep looking
198-
}))
199-
return true;
206+
if (CtxI) {
207+
const Value *UO = getUnderlyingObjectAggressive(V);
208+
if (!V->canBeFreed() || (UO && !UO->canBeFreed())) {
209+
/// Look through assumes to see if both dereferencability and alignment
210+
/// can be proven by an assume if needed.
211+
RetainedKnowledge AlignRK;
212+
RetainedKnowledge DerefRK;
213+
bool IsAligned = V->getPointerAlignment(DL) >= Alignment;
214+
if (getKnowledgeForValue(
215+
V, {Attribute::Dereferenceable, Attribute::Alignment}, AC,
216+
[&](RetainedKnowledge RK, Instruction *Assume, auto) {
217+
if (!isValidAssumeForContext(Assume, CtxI, DT))
218+
return false;
219+
if (RK.AttrKind == Attribute::Alignment)
220+
AlignRK = std::max(AlignRK, RK);
221+
if (RK.AttrKind == Attribute::Dereferenceable)
222+
DerefRK = std::max(DerefRK, RK);
223+
IsAligned |= AlignRK && AlignRK.ArgValue >= Alignment.value();
224+
if (IsAligned && DerefRK &&
225+
DerefRK.ArgValue >= Size.getZExtValue())
226+
return true; // We have found what we needed so we stop
227+
// looking
228+
return false; // Other assumes may have better information. so
229+
// keep looking
230+
}))
231+
return true;
232+
}
200233
}
201234

202-
// If we don't know, assume the worst.
203-
return false;
235+
return isDereferenceableAndAlignedPointerViaAssumption(
236+
V, Alignment,
237+
[Size](const RetainedKnowledge &RK) {
238+
return RK.ArgValue >= Size.getZExtValue();
239+
},
240+
DL, CtxI, AC, DT);
204241
}
205242

206243
bool llvm::isDereferenceableAndAlignedPointer(
@@ -317,8 +354,8 @@ bool llvm::isDereferenceableAndAlignedInLoop(
317354
return false;
318355

319356
const SCEV *MaxBECount =
320-
Predicates ? SE.getPredicatedConstantMaxBackedgeTakenCount(L, *Predicates)
321-
: SE.getConstantMaxBackedgeTakenCount(L);
357+
Predicates ? SE.getPredicatedSymbolicMaxBackedgeTakenCount(L, *Predicates)
358+
: SE.getSymbolicMaxBackedgeTakenCount(L);
322359
if (isa<SCEVCouldNotCompute>(MaxBECount))
323360
return false;
324361

@@ -334,9 +371,11 @@ bool llvm::isDereferenceableAndAlignedInLoop(
334371

335372
Value *Base = nullptr;
336373
APInt AccessSize;
374+
const SCEV *AccessSizeSCEV = nullptr;
337375
if (const SCEVUnknown *NewBase = dyn_cast<SCEVUnknown>(AccessStart)) {
338376
Base = NewBase->getValue();
339377
AccessSize = MaxPtrDiff;
378+
AccessSizeSCEV = PtrDiff;
340379
} else if (auto *MinAdd = dyn_cast<SCEVAddExpr>(AccessStart)) {
341380
if (MinAdd->getNumOperands() != 2)
342381
return false;
@@ -360,12 +399,20 @@ bool llvm::isDereferenceableAndAlignedInLoop(
360399
return false;
361400

362401
AccessSize = MaxPtrDiff + Offset->getAPInt();
402+
AccessSizeSCEV = SE.getAddExpr(PtrDiff, Offset);
363403
Base = NewBase->getValue();
364404
} else
365405
return false;
366406

367407
Instruction *HeaderFirstNonPHI = &*L->getHeader()->getFirstNonPHIIt();
368-
return isDereferenceableAndAlignedPointer(Base, Alignment, AccessSize, DL,
408+
return isDereferenceableAndAlignedPointerViaAssumption(
409+
Base, Alignment,
410+
[&SE, PtrDiff](const RetainedKnowledge &RK) {
411+
return SE.isKnownPredicate(CmpInst::ICMP_ULE, PtrDiff,
412+
SE.getSCEV(RK.IRArgValue));
413+
},
414+
DL, HeaderFirstNonPHI, AC, &DT) ||
415+
isDereferenceableAndAlignedPointer(Base, Alignment, AccessSize, DL,
369416
HeaderFirstNonPHI, AC, &DT);
370417
}
371418

llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-variable-size.ll

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,32 @@ define void @deref_assumption_in_preheader_too_small_non_constant_trip_count_acc
185185
; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 [[N]], [[N_MOD_VF]]
186186
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
187187
; CHECK: [[VECTOR_BODY]]:
188-
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
188+
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[PRED_LOAD_CONTINUE2:.*]] ]
189189
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0
190-
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[A]], i64 [[TMP0]]
191190
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[TMP0]]
192191
; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 0
193192
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP3]], align 1
194193
; CHECK-NEXT: [[TMP4:%.*]] = icmp sge <2 x i32> [[WIDE_LOAD]], zeroinitializer
195-
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[TMP1]], i32 0
196-
; CHECK-NEXT: [[WIDE_LOAD1:%.*]] = load <2 x i32>, ptr [[TMP5]], align 1
194+
; CHECK-NEXT: [[TMP15:%.*]] = xor <2 x i1> [[TMP4]], splat (i1 true)
195+
; CHECK-NEXT: [[TMP5:%.*]] = extractelement <2 x i1> [[TMP15]], i32 0
196+
; CHECK-NEXT: br i1 [[TMP5]], label %[[PRED_LOAD_IF:.*]], label %[[PRED_LOAD_CONTINUE:.*]]
197+
; CHECK: [[PRED_LOAD_IF]]:
198+
; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[TMP0]]
199+
; CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP16]], align 1
200+
; CHECK-NEXT: [[TMP18:%.*]] = insertelement <2 x i32> poison, i32 [[TMP17]], i32 0
201+
; CHECK-NEXT: br label %[[PRED_LOAD_CONTINUE]]
202+
; CHECK: [[PRED_LOAD_CONTINUE]]:
203+
; CHECK-NEXT: [[TMP9:%.*]] = phi <2 x i32> [ poison, %[[VECTOR_BODY]] ], [ [[TMP18]], %[[PRED_LOAD_IF]] ]
204+
; CHECK-NEXT: [[TMP10:%.*]] = extractelement <2 x i1> [[TMP15]], i32 1
205+
; CHECK-NEXT: br i1 [[TMP10]], label %[[PRED_LOAD_IF1:.*]], label %[[PRED_LOAD_CONTINUE2]]
206+
; CHECK: [[PRED_LOAD_IF1]]:
207+
; CHECK-NEXT: [[TMP11:%.*]] = add i64 [[INDEX]], 1
208+
; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[TMP11]]
209+
; CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP12]], align 1
210+
; CHECK-NEXT: [[TMP14:%.*]] = insertelement <2 x i32> [[TMP9]], i32 [[TMP13]], i32 1
211+
; CHECK-NEXT: br label %[[PRED_LOAD_CONTINUE2]]
212+
; CHECK: [[PRED_LOAD_CONTINUE2]]:
213+
; CHECK-NEXT: [[WIDE_LOAD1:%.*]] = phi <2 x i32> [ [[TMP9]], %[[PRED_LOAD_CONTINUE]] ], [ [[TMP14]], %[[PRED_LOAD_IF1]] ]
197214
; CHECK-NEXT: [[PREDPHI:%.*]] = select <2 x i1> [[TMP4]], <2 x i32> [[WIDE_LOAD]], <2 x i32> [[WIDE_LOAD1]]
198215
; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[C]], i64 [[TMP0]]
199216
; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i32 0
@@ -268,15 +285,32 @@ define void @deref_assumption_in_preheader_too_small2_non_constant_trip_count_ac
268285
; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 [[N]], [[N_MOD_VF]]
269286
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
270287
; CHECK: [[VECTOR_BODY]]:
271-
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
288+
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[PRED_LOAD_CONTINUE2:.*]] ]
272289
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0
273-
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[A]], i64 [[TMP0]]
274290
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[TMP0]]
275291
; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 0
276292
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP3]], align 1
277293
; CHECK-NEXT: [[TMP4:%.*]] = icmp sge <2 x i32> [[WIDE_LOAD]], zeroinitializer
278-
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[TMP1]], i32 0
279-
; CHECK-NEXT: [[WIDE_LOAD1:%.*]] = load <2 x i32>, ptr [[TMP5]], align 1
294+
; CHECK-NEXT: [[TMP15:%.*]] = xor <2 x i1> [[TMP4]], splat (i1 true)
295+
; CHECK-NEXT: [[TMP5:%.*]] = extractelement <2 x i1> [[TMP15]], i32 0
296+
; CHECK-NEXT: br i1 [[TMP5]], label %[[PRED_LOAD_IF:.*]], label %[[PRED_LOAD_CONTINUE:.*]]
297+
; CHECK: [[PRED_LOAD_IF]]:
298+
; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[TMP0]]
299+
; CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP16]], align 1
300+
; CHECK-NEXT: [[TMP18:%.*]] = insertelement <2 x i32> poison, i32 [[TMP17]], i32 0
301+
; CHECK-NEXT: br label %[[PRED_LOAD_CONTINUE]]
302+
; CHECK: [[PRED_LOAD_CONTINUE]]:
303+
; CHECK-NEXT: [[TMP9:%.*]] = phi <2 x i32> [ poison, %[[VECTOR_BODY]] ], [ [[TMP18]], %[[PRED_LOAD_IF]] ]
304+
; CHECK-NEXT: [[TMP10:%.*]] = extractelement <2 x i1> [[TMP15]], i32 1
305+
; CHECK-NEXT: br i1 [[TMP10]], label %[[PRED_LOAD_IF1:.*]], label %[[PRED_LOAD_CONTINUE2]]
306+
; CHECK: [[PRED_LOAD_IF1]]:
307+
; CHECK-NEXT: [[TMP11:%.*]] = add i64 [[INDEX]], 1
308+
; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[TMP11]]
309+
; CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP12]], align 1
310+
; CHECK-NEXT: [[TMP14:%.*]] = insertelement <2 x i32> [[TMP9]], i32 [[TMP13]], i32 1
311+
; CHECK-NEXT: br label %[[PRED_LOAD_CONTINUE2]]
312+
; CHECK: [[PRED_LOAD_CONTINUE2]]:
313+
; CHECK-NEXT: [[WIDE_LOAD1:%.*]] = phi <2 x i32> [ [[TMP9]], %[[PRED_LOAD_CONTINUE]] ], [ [[TMP14]], %[[PRED_LOAD_IF1]] ]
280314
; CHECK-NEXT: [[PREDPHI:%.*]] = select <2 x i1> [[TMP4]], <2 x i32> [[WIDE_LOAD]], <2 x i32> [[WIDE_LOAD1]]
281315
; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[C]], i64 [[TMP0]]
282316
; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i32 0

0 commit comments

Comments
 (0)