diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 1ea3efb27937d..1f4a6f793404c 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -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(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 Indices(GEP.indices()); @@ -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(&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. diff --git a/llvm/test/Transforms/InstCombine/canonicalize-gep-constglob.ll b/llvm/test/Transforms/InstCombine/canonicalize-gep-constglob.ll index f1f01a1884a22..53585dfde48b2 100644 --- a/llvm/test/Transforms/InstCombine/canonicalize-gep-constglob.ll +++ b/llvm/test/Transforms/InstCombine/canonicalize-gep-constglob.ll @@ -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: @@ -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: @@ -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]] diff --git a/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll index 509d19c8c00c7..edec54495c1ae 100644 --- a/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll +++ b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll @@ -140,12 +140,11 @@ 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 @@ -153,13 +152,13 @@ define ptr @partialConstant2(ptr %p, i64 %a) { 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