Skip to content

Commit d045e23

Browse files
committed
[ConstraintElim] Refactor GEP offset collection.
Move GEP offset collection to separate helper function and collect variable and constant offsets in OffsetResult. For now, this only supports 1 VariableOffset, but the new code structure can be more easily extended to handle more offsets in the future. The refactoring drops the check that the VariableOffset >= -1 * constant offset. This is not needed to check whether the constraint is monotonically increasing. The constant factors can be ignored, the constraint will be monotonically increasing if all variables are positive. See https://alive2.llvm.org/ce/z/ah2uSQ, https://alive2.llvm.org/ce/z/NCADNZ
1 parent 57a0a9a commit d045e23

File tree

3 files changed

+67
-65
lines changed

3 files changed

+67
-65
lines changed

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,55 @@ struct Decomposition {
366366
}
367367
};
368368

369+
// Variable and constant offsets for a chain of GEPs, with base pointer BasePtr.
370+
struct OffsetResult {
371+
Value *BasePtr;
372+
APInt ConstantOffset;
373+
MapVector<Value *, APInt> VariableOffsets;
374+
bool AllInbounds;
375+
376+
OffsetResult() : BasePtr(nullptr), ConstantOffset(0, uint64_t(0)) {}
377+
378+
OffsetResult(GEPOperator &GEP, const DataLayout &DL)
379+
: BasePtr(GEP.getPointerOperand()), AllInbounds(GEP.isInBounds()) {
380+
ConstantOffset = APInt(DL.getIndexTypeSizeInBits(BasePtr->getType()), 0);
381+
}
382+
};
369383
} // namespace
370384

385+
// Try to collect variable and constant offsets for \p GEP, partly traversing
386+
// nested GEPs. Returns an OffsetResult with nullptr as BasePtr of collecting
387+
// the offset fails.
388+
static OffsetResult collectOffsets(GEPOperator &GEP, const DataLayout &DL) {
389+
OffsetResult Result(GEP, DL);
390+
unsigned BitWidth = Result.ConstantOffset.getBitWidth();
391+
if (!GEP.collectOffset(DL, BitWidth, Result.VariableOffsets,
392+
Result.ConstantOffset))
393+
return {};
394+
395+
// If we have a nested GEP, check if we can combine the constant offset of the
396+
// inner GEP with the outer GEP.
397+
if (auto *InnerGEP = dyn_cast<GetElementPtrInst>(Result.BasePtr)) {
398+
MapVector<Value *, APInt> VariableOffsets2;
399+
APInt ConstantOffset2(BitWidth, 0);
400+
bool CanCollectInner = InnerGEP->collectOffset(
401+
DL, BitWidth, VariableOffsets2, ConstantOffset2);
402+
// TODO: Support cases with more than 1 variable offset.
403+
if (!CanCollectInner || Result.VariableOffsets.size() > 1 ||
404+
VariableOffsets2.size() > 1 ||
405+
(Result.VariableOffsets.size() >= 1 && VariableOffsets2.size() >= 1)) {
406+
// More than 1 variable index, use outer result.
407+
return Result;
408+
}
409+
Result.BasePtr = InnerGEP->getPointerOperand();
410+
Result.ConstantOffset += ConstantOffset2;
411+
if (Result.VariableOffsets.size() == 0 && VariableOffsets2.size() == 1)
412+
Result.VariableOffsets = VariableOffsets2;
413+
Result.AllInbounds &= InnerGEP->isInBounds();
414+
}
415+
return Result;
416+
}
417+
371418
static Decomposition decompose(Value *V,
372419
SmallVectorImpl<ConditionTy> &Preconditions,
373420
bool IsSigned, const DataLayout &DL);
@@ -385,43 +432,14 @@ static Decomposition decomposeGEP(GEPOperator &GEP,
385432
if (DL.getIndexTypeSizeInBits(GEP.getPointerOperand()->getType()) > 64)
386433
return &GEP;
387434

388-
if (!GEP.isInBounds())
389-
return &GEP;
390-
391435
assert(!IsSigned && "The logic below only supports decomposition for "
392436
"unsinged predicates at the moment.");
393-
Type *PtrTy = GEP.getType()->getScalarType();
394-
unsigned BitWidth = DL.getIndexTypeSizeInBits(PtrTy);
395-
MapVector<Value *, APInt> VariableOffsets;
396-
APInt ConstantOffset(BitWidth, 0);
397-
if (!GEP.collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
437+
const auto &[BasePtr, ConstantOffset, VariableOffsets, AllInbounds] =
438+
collectOffsets(GEP, DL);
439+
if (!BasePtr || !AllInbounds)
398440
return &GEP;
399441

400-
// Handle the (gep (gep ....), C) case by incrementing the constant
401-
// coefficient of the inner GEP, if C is a constant.
402-
auto *InnerGEP = dyn_cast<GEPOperator>(GEP.getPointerOperand());
403-
if (VariableOffsets.empty() && InnerGEP && InnerGEP->getNumOperands() == 2) {
404-
auto Result = decompose(InnerGEP, Preconditions, IsSigned, DL);
405-
Result.add(ConstantOffset.getSExtValue());
406-
407-
if (ConstantOffset.isNegative()) {
408-
unsigned Scale = DL.getTypeAllocSize(InnerGEP->getResultElementType());
409-
int64_t ConstantOffsetI = ConstantOffset.getSExtValue();
410-
if (ConstantOffsetI % Scale != 0)
411-
return &GEP;
412-
// Add pre-condition ensuring the GEP is increasing monotonically and
413-
// can be de-composed.
414-
// Both sides are normalized by being divided by Scale.
415-
Preconditions.emplace_back(
416-
CmpInst::ICMP_SGE, InnerGEP->getOperand(1),
417-
ConstantInt::get(InnerGEP->getOperand(1)->getType(),
418-
-1 * (ConstantOffsetI / Scale)));
419-
}
420-
return Result;
421-
}
422-
423-
Decomposition Result(ConstantOffset.getSExtValue(),
424-
DecompEntry(1, GEP.getPointerOperand()));
442+
Decomposition Result(ConstantOffset.getSExtValue(), DecompEntry(1, BasePtr));
425443
for (auto [Index, Scale] : VariableOffsets) {
426444
auto IdxResult = decompose(Index, Preconditions, IsSigned, DL);
427445
IdxResult.mul(Scale.getSExtValue());

llvm/test/Transforms/ConstraintElimination/gep-add-multiple-indices.ll

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ define i1 @test_inner_gep_multiple_indices_ult_true_all_inbounds(ptr %dst) {
5050
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 0, i64 0
5151
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST]], i64 0, i64 2
5252
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
53-
; CHECK-NEXT: [[C:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
54-
; CHECK-NEXT: ret i1 [[C]]
53+
; CHECK-NEXT: ret i1 true
5554
;
5655
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 0
5756
%upper = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 2
@@ -65,8 +64,7 @@ define i1 @test_inner_gep_multiple_indices_uge_true_all_inbounds(ptr %dst) {
6564
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 0, i64 0
6665
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST]], i64 0, i64 2
6766
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
68-
; CHECK-NEXT: [[C:%.*]] = icmp uge ptr [[GEP_1]], [[DST_0]]
69-
; CHECK-NEXT: ret i1 [[C]]
67+
; CHECK-NEXT: ret i1 true
7068
;
7169
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 0
7270
%upper = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 2
@@ -81,8 +79,7 @@ define i1 @test_inner_gep_multiple_indices_ult_false_all_inbounds(ptr %dst) {
8179
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 0, i64 0
8280
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST]], i64 0, i64 2
8381
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 2
84-
; CHECK-NEXT: [[C:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
85-
; CHECK-NEXT: ret i1 [[C]]
82+
; CHECK-NEXT: ret i1 false
8683
;
8784
entry:
8885
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 0
@@ -98,8 +95,7 @@ define i1 @test_inner_gep_multiple_indices_uge_true_all_inbounds_2(ptr %dst) {
9895
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 0, i64 0
9996
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST]], i64 0, i64 2
10097
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 2
101-
; CHECK-NEXT: [[C:%.*]] = icmp uge ptr [[GEP_1]], [[DST_0]]
102-
; CHECK-NEXT: ret i1 [[C]]
98+
; CHECK-NEXT: ret i1 true
10399
;
104100
entry:
105101
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 0
@@ -213,8 +209,7 @@ define i1 @test_inner_gep_multi_index_outer_gep_last_index_no_overflow_all_inbou
213209
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 0, i64 0
214210
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds ptr, ptr [[DST]], i64 2
215211
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST_0]], i64 1, i64 1
216-
; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
217-
; CHECK-NEXT: ret i1 [[C_1]]
212+
; CHECK-NEXT: ret i1 true
218213
;
219214
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 0
220215
%upper = getelementptr inbounds ptr, ptr %dst, i64 2
@@ -228,8 +223,7 @@ define i1 @test_inner_gep_multi_index_no_overflow_all_inbounds_1(ptr %dst) {
228223
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 0, i64 1
229224
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 2
230225
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
231-
; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
232-
; CHECK-NEXT: ret i1 [[C_1]]
226+
; CHECK-NEXT: ret i1 false
233227
;
234228
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 0, i64 1
235229
%upper = getelementptr inbounds i32, ptr %dst, i64 2
@@ -243,8 +237,7 @@ define i1 @test_inner_gep_multi_index_no_overflow_all_inbounds_2(ptr %dst) {
243237
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 1, i64 0
244238
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 2
245239
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
246-
; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
247-
; CHECK-NEXT: ret i1 [[C_1]]
240+
; CHECK-NEXT: ret i1 false
248241
;
249242
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 1, i64 0
250243
%upper = getelementptr inbounds i32, ptr %dst, i64 2
@@ -258,8 +251,7 @@ define i1 @test_inner_gep_multi_index_no_overflow_all_inbounds_3(ptr %dst) {
258251
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 1, i64 0
259252
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 3
260253
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
261-
; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
262-
; CHECK-NEXT: ret i1 [[C_1]]
254+
; CHECK-NEXT: ret i1 false
263255
;
264256
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 1, i64 0
265257
%upper = getelementptr inbounds i32, ptr %dst, i64 3
@@ -273,8 +265,7 @@ define i1 @test_inner_gep_multi_index_no_overflow_all_inbounds_4(ptr %dst) {
273265
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 1, i64 0
274266
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 4
275267
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
276-
; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
277-
; CHECK-NEXT: ret i1 [[C_1]]
268+
; CHECK-NEXT: ret i1 true
278269
;
279270
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 1, i64 0
280271
%upper = getelementptr inbounds i32, ptr %dst, i64 4
@@ -289,8 +280,7 @@ define i1 @test_inner_gep_multi_index_no_overflow_all_inbounds_5(i64 %off, ptr %
289280
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 2, i64 0
290281
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 5
291282
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
292-
; CHECK-NEXT: [[C_1:%.*]] = icmp ule ptr [[GEP_1]], [[UPPER]]
293-
; CHECK-NEXT: ret i1 [[C_1]]
283+
; CHECK-NEXT: ret i1 true
294284
;
295285
%off.ult = icmp ule i64 %off, 2
296286
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 2, i64 0
@@ -306,8 +296,7 @@ define i1 @test_inner_gep_multi_index_no_overflow_all_inbounds_6(i64 %off, ptr %
306296
; CHECK-NEXT: [[DST_0:%.*]] = getelementptr inbounds [2 x i32], ptr [[DST:%.*]], i64 2, i64 0
307297
; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 5
308298
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i32, ptr [[DST_0]], i64 1
309-
; CHECK-NEXT: [[C_1:%.*]] = icmp ult ptr [[GEP_1]], [[UPPER]]
310-
; CHECK-NEXT: ret i1 [[C_1]]
299+
; CHECK-NEXT: ret i1 false
311300
;
312301
%off.ult = icmp ule i64 %off, 2
313302
%dst.0 = getelementptr inbounds [2 x i32], ptr %dst, i64 2, i64 0

llvm/test/Transforms/ConstraintElimination/gep-sub.ll

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,7 @@ define i1 @gep_sub_ult_var_idx(ptr %dst, ptr %upper, i8 %idx) {
150150
; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
151151
; CHECK-NEXT: [[DST_SUB_1:%.*]] = getelementptr inbounds i8, ptr [[DST_ADD_IDX]], i64 -1
152152
; CHECK-NEXT: [[DST_SUB_2:%.*]] = getelementptr inbounds i8, ptr [[DST_ADD_IDX]], i64 -2
153-
; CHECK-NEXT: [[CMP_SUB_2:%.*]] = icmp ult ptr [[DST_SUB_2]], [[UPPER]]
154-
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 true, [[CMP_SUB_2]]
153+
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 true, true
155154
; CHECK-NEXT: [[DST_SUB_1_SUB_1:%.*]] = getelementptr inbounds i8, ptr [[DST_SUB_1]], i64 -1
156155
; CHECK-NEXT: [[CMP_SUB_1_SUB_1:%.*]] = icmp ult ptr [[DST_SUB_1_SUB_1]], [[UPPER]]
157156
; CHECK-NEXT: [[CMP_SUB_1_SUB_1_EQ:%.*]] = icmp eq ptr [[DST_SUB_1_SUB_1]], [[DST_SUB_2]]
@@ -190,8 +189,7 @@ define i1 @gep_sub_ult_var_idx_sgt_1(ptr %dst, ptr %upper, i8 %idx) {
190189
; CHECK-NEXT: [[DST_SUB_2:%.*]] = getelementptr inbounds i8, ptr [[DST_ADD_IDX]], i64 -2
191190
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 true, true
192191
; CHECK-NEXT: [[DST_SUB_3:%.*]] = getelementptr inbounds i8, ptr [[DST_ADD_IDX]], i64 -3
193-
; CHECK-NEXT: [[CMP_SUB_3:%.*]] = icmp ult ptr [[DST_SUB_3]], [[UPPER]]
194-
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[CMP_SUB_3]]
192+
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], true
195193
; CHECK-NEXT: ret i1 [[RES_2]]
196194
;
197195
%sgt.1 = icmp sgt i8 %idx, 1
@@ -461,8 +459,7 @@ define i1 @gep_sub_1_ult_var_idx_lower_bound_len_ne_0(ptr %lower, ptr %src, i8 %
461459
; CHECK-NEXT: call void @llvm.assume(i1 [[LEN_POS]])
462460
; CHECK-NEXT: [[GEP_LEN:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i8 [[LEN]]
463461
; CHECK-NEXT: [[GEP_SUB_1:%.*]] = getelementptr inbounds i8, ptr [[GEP_LEN]], i8 -1
464-
; CHECK-NEXT: [[RES:%.*]] = icmp ult ptr [[GEP_SUB_1]], [[LOWER]]
465-
; CHECK-NEXT: ret i1 [[RES]]
462+
; CHECK-NEXT: ret i1 false
466463
;
467464
entry:
468465
%len.ne.0 = icmp ne i8 %len, 0
@@ -517,8 +514,7 @@ define i1 @gep_i16_sub_1_uge_inbounds(ptr %dst, ptr %lower) {
517514
; CHECK-NEXT: [[DST_ADD_3:%.*]] = getelementptr inbounds i8, ptr [[DST]], i64 3
518515
; CHECK-NEXT: [[DST_SUB_1:%.*]] = getelementptr inbounds i16, ptr [[DST_ADD_3]], i64 -1
519516
; CHECK-NEXT: [[DST_SUB_2:%.*]] = getelementptr inbounds i16, ptr [[DST_ADD_3]], i64 -2
520-
; CHECK-NEXT: [[CMP_SUB_2:%.*]] = icmp ule ptr [[DST_SUB_2]], [[DST]]
521-
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 false, [[CMP_SUB_2]]
517+
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 false, true
522518
; CHECK-NEXT: [[DST_SUB_3:%.*]] = getelementptr inbounds i16, ptr [[DST_ADD_3]], i64 -3
523519
; CHECK-NEXT: [[CMP_SUB_3:%.*]] = icmp ule ptr [[DST_SUB_3]], [[LOWER]]
524520
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[CMP_SUB_3]]
@@ -579,8 +575,7 @@ define i1 @gep_i32_two_indices_known_lt_and_positive(ptr %a, i8 %idx.1, i8 %idx.
579575
; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr inbounds i32, ptr [[GEP_1]], i8 -3
580576
; CHECK-NEXT: [[GEP_3:%.*]] = getelementptr inbounds i32, ptr [[A]], i8 [[IDX_2]]
581577
; CHECK-NEXT: [[GEP_4:%.*]] = getelementptr inbounds i32, ptr [[GEP_3]], i8 -3
582-
; CHECK-NEXT: [[C:%.*]] = icmp ult ptr [[GEP_2]], [[GEP_4]]
583-
; CHECK-NEXT: ret i1 [[C]]
578+
; CHECK-NEXT: ret i1 true
584579
;
585580
%lt = icmp ult i8 %idx.1, %idx.2
586581
call void @llvm.assume(i1 %lt)

0 commit comments

Comments
 (0)