[ValueTracking] Simplify llvm::isPointerOffset()

We still need the code after stripAndAccumulateConstantOffsets() since
it doesn't handle GEPs of scalable types and non-constant but identical
indexes.

Differential Revision: https://reviews.llvm.org/D120523
This commit is contained in:
Arthur Eubanks 2022-02-24 15:30:23 -08:00
parent f6484bd3b0
commit 55cf09ae26
2 changed files with 30 additions and 54 deletions

View File

@ -7097,60 +7097,18 @@ getOffsetFromIndex(const GEPOperator *GEP, unsigned Idx, const DataLayout &DL) {
Optional<int64_t> llvm::isPointerOffset(const Value *Ptr1, const Value *Ptr2, Optional<int64_t> llvm::isPointerOffset(const Value *Ptr1, const Value *Ptr2,
const DataLayout &DL) { const DataLayout &DL) {
Ptr1 = Ptr1->stripPointerCasts(); APInt Offset1(DL.getIndexTypeSizeInBits(Ptr1->getType()), 0);
Ptr2 = Ptr2->stripPointerCasts(); APInt Offset2(DL.getIndexTypeSizeInBits(Ptr2->getType()), 0);
Ptr1 = Ptr1->stripAndAccumulateConstantOffsets(DL, Offset1, true);
Ptr2 = Ptr2->stripAndAccumulateConstantOffsets(DL, Offset2, true);
// Handle the trivial case first. // Handle the trivial case first.
if (Ptr1 == Ptr2) { if (Ptr1 == Ptr2)
return 0; return Offset2.getSExtValue() - Offset1.getSExtValue();
}
const GEPOperator *GEP1 = dyn_cast<GEPOperator>(Ptr1); const GEPOperator *GEP1 = dyn_cast<GEPOperator>(Ptr1);
const GEPOperator *GEP2 = dyn_cast<GEPOperator>(Ptr2); const GEPOperator *GEP2 = dyn_cast<GEPOperator>(Ptr2);
// If one pointer is a GEP see if the GEP is a constant offset from the base,
// as in "P" and "gep P, 1".
// Also do this iteratively to handle the the following case:
// Ptr_t1 = GEP Ptr1, c1
// Ptr_t2 = GEP Ptr_t1, c2
// Ptr2 = GEP Ptr_t2, c3
// where we will return c1+c2+c3.
// TODO: Handle the case when both Ptr1 and Ptr2 are GEPs of some common base
// -- replace getOffsetFromBase with getOffsetAndBase, check that the bases
// are the same, and return the difference between offsets.
auto getOffsetFromBase = [&DL](const GEPOperator *GEP,
const Value *Ptr) -> Optional<int64_t> {
const GEPOperator *GEP_T = GEP;
int64_t OffsetVal = 0;
bool HasSameBase = false;
while (GEP_T) {
auto Offset = getOffsetFromIndex(GEP_T, 1, DL);
if (!Offset)
return None;
OffsetVal += *Offset;
auto Op0 = GEP_T->getOperand(0)->stripPointerCasts();
if (Op0 == Ptr) {
HasSameBase = true;
break;
}
GEP_T = dyn_cast<GEPOperator>(Op0);
}
if (!HasSameBase)
return None;
return OffsetVal;
};
if (GEP1) {
auto Offset = getOffsetFromBase(GEP1, Ptr2);
if (Offset)
return -*Offset;
}
if (GEP2) {
auto Offset = getOffsetFromBase(GEP2, Ptr1);
if (Offset)
return Offset;
}
// Right now we handle the case when Ptr1/Ptr2 are both GEPs with an identical // Right now we handle the case when Ptr1/Ptr2 are both GEPs with an identical
// base. After that base, they may have some number of common (and // base. After that base, they may have some number of common (and
// potentially variable) indices. After that they handle some constant // potentially variable) indices. After that they handle some constant
@ -7166,9 +7124,10 @@ Optional<int64_t> llvm::isPointerOffset(const Value *Ptr1, const Value *Ptr2,
if (GEP1->getOperand(Idx) != GEP2->getOperand(Idx)) if (GEP1->getOperand(Idx) != GEP2->getOperand(Idx))
break; break;
auto Offset1 = getOffsetFromIndex(GEP1, Idx, DL); auto IOffset1 = getOffsetFromIndex(GEP1, Idx, DL);
auto Offset2 = getOffsetFromIndex(GEP2, Idx, DL); auto IOffset2 = getOffsetFromIndex(GEP2, Idx, DL);
if (!Offset1 || !Offset2) if (!IOffset1 || !IOffset2)
return None; return None;
return *Offset2 - *Offset1; return *IOffset2 - *IOffset1 + Offset2.getSExtValue() -
Offset1.getSExtValue();
} }

View File

@ -22,10 +22,9 @@ define void @test_memset_memcpy(ptr %src, i64 %src_size, ptr noalias %dst, i64 %
define void @test_different_gep_source_elements(ptr %src) { define void @test_different_gep_source_elements(ptr %src) {
; CHECK-LABEL: @test_different_gep_source_elements( ; CHECK-LABEL: @test_different_gep_source_elements(
; CHECK-NEXT: [[PB:%.*]] = getelementptr [[B:%.*]], ptr [[SRC:%.*]], i64 0, i32 1 ; CHECK-NEXT: [[PB:%.*]] = getelementptr [[B:%.*]], ptr [[SRC:%.*]], i64 0, i32 1
; CHECK-NEXT: store i64 0, ptr [[PB]], align 4
; CHECK-NEXT: [[PA:%.*]] = getelementptr [[A:%.*]], ptr [[SRC]], i64 0, i32 1 ; CHECK-NEXT: [[PA:%.*]] = getelementptr [[A:%.*]], ptr [[SRC]], i64 0, i32 1
; CHECK-NEXT: [[PA2:%.*]] = getelementptr [[A]], ptr [[SRC]], i64 0, i32 2 ; CHECK-NEXT: [[PA2:%.*]] = getelementptr [[A]], ptr [[SRC]], i64 0, i32 2
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[PA]], i8 0, i64 16, i1 false) ; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[PB]], i8 0, i64 20, i1 false)
; CHECK-NEXT: ret void ; CHECK-NEXT: ret void
; ;
%pb = getelementptr %b, ptr %src, i64 0, i32 1 %pb = getelementptr %b, ptr %src, i64 0, i32 1
@ -37,5 +36,23 @@ define void @test_different_gep_source_elements(ptr %src) {
ret void ret void
} }
define void @test_gep_of_vscale_non_const_gep(ptr %p, i64 %idx) {
; CHECK-LABEL: @test_gep_of_vscale_non_const_gep(
; CHECK-NEXT: [[G1:%.*]] = getelementptr <vscale x 16 x i8>, ptr [[P:%.*]], i64 [[IDX:%.*]], i32 1
; CHECK-NEXT: [[G2:%.*]] = getelementptr <vscale x 16 x i8>, ptr [[P]], i64 [[IDX]], i32 5
; CHECK-NEXT: [[H1:%.*]] = getelementptr i8, ptr [[G1]], i64 2
; CHECK-NEXT: [[H2:%.*]] = getelementptr i8, ptr [[G2]], i64 6
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[H1]], i8 0, i64 16, i1 false)
; CHECK-NEXT: ret void
;
%g1 = getelementptr <vscale x 16 x i8>, ptr %p, i64 %idx, i32 1
%g2 = getelementptr <vscale x 16 x i8>, ptr %p, i64 %idx, i32 5
%h1 = getelementptr i8, ptr %g1, i64 2
%h2 = getelementptr i8, ptr %g2, i64 6
store i64 0, ptr %h1
store i64 0, ptr %h2
ret void
}
declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1) declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1)
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture readonly, i64, i1) declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture readonly, i64, i1)