Skip to content

[InstCombine] Canonicalize more geps with constant gep bases and constant offsets. #110033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2731,6 +2731,36 @@ Value *InstCombiner::getFreelyInvertedImpl(Value *V, bool WillInvertAllUses,
return nullptr;
}

/// Return true if we should canonicalize the gep to an i8 ptradd.
static bool shouldCanonicalizeGEPToPtrAdd(GetElementPtrInst &GEP) {
Value *PtrOp = GEP.getOperand(0);
Type *GEPEltType = GEP.getSourceElementType();
if (GEPEltType->isIntegerTy(8))
return false;

// Canonicalize scalable GEPs to an explicit offset using the llvm.vscale
// intrinsic. This has better support in BasicAA.
if (GEPEltType->isScalableTy())
return true;

// gep i32 p, mul(O, C) -> gep i8, p, mul(O, C*4) to fold the two multiplies
// together.
if (GEP.getNumIndices() == 1 &&
match(GEP.getOperand(1),
m_OneUse(m_CombineOr(m_Mul(m_Value(), m_ConstantInt()),
m_Shl(m_Value(), m_ConstantInt())))))
return true;

// gep (gep %p, C1), %x, C2 is expanded so the two constants can
// possibly be merged together.
auto PtrOpGep = dyn_cast<GEPOperator>(PtrOp);
return PtrOpGep && PtrOpGep->hasAllConstantIndices() &&
any_of(GEP.indices(), [](Value *V) {
const APInt *C;
return match(V, m_APInt(C)) && !C->isZero();
});
}

Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
Value *PtrOp = GEP.getOperand(0);
SmallVector<Value *, 8> Indices(GEP.indices());
Expand Down Expand Up @@ -2812,19 +2842,11 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
GEP.getNoWrapFlags()));
}

// Canonicalize
// - scalable GEPs to an explicit offset using the llvm.vscale intrinsic.
// This has better support in BasicAA.
// - gep i32 p, mul(O, C) -> gep i8, p, mul(O, C*4) to fold the two
// multiplies together.
if (GEPEltType->isScalableTy() ||
(!GEPEltType->isIntegerTy(8) && GEP.getNumIndices() == 1 &&
match(GEP.getOperand(1),
m_OneUse(m_CombineOr(m_Mul(m_Value(), m_ConstantInt()),
m_Shl(m_Value(), m_ConstantInt())))))) {
if (shouldCanonicalizeGEPToPtrAdd(GEP)) {
Value *Offset = EmitGEPOffset(cast<GEPOperator>(&GEP));
return replaceInstUsesWith(
GEP, Builder.CreatePtrAdd(PtrOp, Offset, "", GEP.getNoWrapFlags()));
Value *NewGEP =
Builder.CreatePtrAdd(PtrOp, Offset, "", GEP.getNoWrapFlags());
return replaceInstUsesWith(GEP, NewGEP);
}

// Check to see if the inputs to the PHI node are getelementptr instructions.
Expand Down
14 changes: 10 additions & 4 deletions llvm/test/Transforms/InstCombine/canonicalize-gep-constglob.ll
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ define ptr @x12(i64 %x) {
; CHECK-LABEL: define ptr @x12(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [10 x [10 x [10 x i32]]], ptr getelementptr inbounds (i8, ptr @glob, i64 36), i64 0, i64 [[X]], i64 1, i64 2
; CHECK-NEXT: [[GEP_IDX:%.*]] = mul nsw i64 [[X]], 400
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr getelementptr inbounds (i8, ptr @glob, i64 84), i64 [[GEP_IDX]]
; CHECK-NEXT: ret ptr [[GEP]]
;
entry:
Expand All @@ -19,7 +20,10 @@ define ptr @x1y(i64 %x, i64 %y) {
; CHECK-LABEL: define ptr @x1y(
; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [10 x [10 x [10 x i32]]], ptr getelementptr inbounds (i8, ptr @glob, i64 36), i64 0, i64 [[X]], i64 2, i64 [[Y]]
; CHECK-NEXT: [[GEP_IDX:%.*]] = mul nsw i64 [[X]], 400
; CHECK-NEXT: [[GEP_IDX1:%.*]] = shl nsw i64 [[Y]], 2
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr getelementptr inbounds (i8, ptr @glob, i64 116), i64 [[GEP_IDX]]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[GEP_IDX1]]
; CHECK-NEXT: ret ptr [[GEP]]
;
entry:
Expand Down Expand Up @@ -55,8 +59,10 @@ define i32 @twoloads(i64 %x) {
; CHECK-LABEL: define i32 @twoloads(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds [10 x [10 x [10 x i32]]], ptr getelementptr inbounds (i8, ptr @glob, i64 50), i64 0, i64 [[X]], i64 2, i64 1
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr inbounds [10 x [10 x [10 x i32]]], ptr getelementptr inbounds (i8, ptr @glob, i64 36), i64 0, i64 [[X]], i64 2, i64 4
; CHECK-NEXT: [[GEP1_IDX:%.*]] = mul nsw i64 [[X]], 400
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr getelementptr inbounds (i8, ptr @glob, i64 134), i64 [[GEP1_IDX]]
; CHECK-NEXT: [[GEP2_IDX:%.*]] = mul nsw i64 [[X]], 400
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr getelementptr inbounds (i8, ptr @glob, i64 132), i64 [[GEP2_IDX]]
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[GEP1]], align 4
; CHECK-NEXT: [[B:%.*]] = load i32, ptr [[GEP2]], align 4
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[B]]
Expand Down
13 changes: 6 additions & 7 deletions llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll
Original file line number Diff line number Diff line change
Expand Up @@ -140,26 +140,25 @@ define ptr @notDivisible(ptr %p) {
ret ptr %2
}

; Negative test. Two GEP should not be merged if not both offsets are constant
; or divisible by the other's size.
define ptr @partialConstant2(ptr %p, i64 %a) {
; CHECK-LABEL: @partialConstant2(
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 4
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [4 x i64], ptr [[TMP1]], i64 [[A:%.*]], i64 2
; CHECK-NEXT: [[DOTIDX:%.*]] = shl nsw i64 [[A:%.*]], 5
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 20
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 [[DOTIDX]]
; CHECK-NEXT: ret ptr [[TMP2]]
;
%1 = getelementptr inbounds i32, ptr %p, i64 1
%2 = getelementptr inbounds [4 x i64], ptr %1, i64 %a, i64 2
ret ptr %2
}

; Negative test. Two GEP should not be merged if there is another use of the
; first GEP by the second GEP.
define ptr @partialConstant3(ptr %p) {
; CHECK-LABEL: @partialConstant3(
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 4
; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [4 x i64], ptr [[TMP1]], i64 [[TMP2]], i64 2
; CHECK-NEXT: [[DOTIDX:%.*]] = shl nsw i64 [[TMP2]], 5
; CHECK-NEXT: [[DOTOFFS:%.*]] = or disjoint i64 [[DOTIDX]], 16
; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[DOTOFFS]]
; CHECK-NEXT: ret ptr [[TMP3]]
;
%1 = getelementptr inbounds i32, ptr %p, i64 1
Expand Down
Loading