[InstCombine] Combine opaque pointer GEPs with mismatching element types

Currently, two GEPs will only be combined if the result element
type of one is the same as the source element type of the other.
However, this means we may miss folding opportunities where the
second GEP could be rewritten using a different element type. This
is especially relevant for opaque pointers, where constant GEPs
often use i8 element type.

Address this by converting GEP indices to offsets, adding them,
and then converting them back to indices. The first (inner) GEP
is allowed to have variable indices as well, in which case only
the constant suffix is converted into an offset.

This should address the regression reported in
https://reviews.llvm.org/D123300#3467615.

Differential Revision: https://reviews.llvm.org/D124459
This commit is contained in:
Nikita Popov 2022-04-26 14:27:09 +02:00
parent 86c770346c
commit c103f5e9da
2 changed files with 71 additions and 18 deletions

View File

@ -1955,13 +1955,11 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
// Combine Indices - If the source pointer to this getelementptr instruction
// is a getelementptr instruction with matching element type, combine the
// indices of the two getelementptr instructions into a single instruction.
if (Src->getResultElementType() != GEP.getSourceElementType())
return nullptr;
if (!shouldMergeGEPs(*cast<GEPOperator>(&GEP), *Src))
return nullptr;
if (Src->getNumOperands() == 2 && GEP.getNumOperands() == 2 &&
if (Src->getResultElementType() == GEP.getSourceElementType() &&
Src->getNumOperands() == 2 && GEP.getNumOperands() == 2 &&
Src->hasOneUse()) {
Value *GO1 = GEP.getOperand(1);
Value *SO1 = Src->getOperand(1);
@ -2024,6 +2022,68 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
if (SrcGEP->getNumOperands() == 2 && shouldMergeGEPs(*Src, *SrcGEP))
return nullptr; // Wait until our source is folded to completion.
// For constant GEPs, use a more general offset-based folding approach.
// Only do this for opaque pointers, as the result element type may change.
Type *PtrTy = Src->getType()->getScalarType();
if (PtrTy->isOpaquePointerTy() && GEP.hasAllConstantIndices() &&
(Src->hasOneUse() || Src->hasAllConstantIndices())) {
// Split Src into a variable part and a constant suffix.
gep_type_iterator GTI = gep_type_begin(*Src);
Type *BaseType = GTI.getIndexedType();
bool IsFirstType = true;
unsigned NumVarIndices = 0;
for (auto Pair : enumerate(Src->indices())) {
if (!isa<ConstantInt>(Pair.value())) {
BaseType = GTI.getIndexedType();
IsFirstType = false;
NumVarIndices = Pair.index() + 1;
}
++GTI;
}
// Determine the offset for the constant suffix of Src.
APInt Offset(DL.getIndexTypeSizeInBits(PtrTy), 0);
if (NumVarIndices != Src->getNumIndices()) {
// FIXME: getIndexedOffsetInType() does not handled scalable vectors.
if (isa<ScalableVectorType>(BaseType))
return nullptr;
SmallVector<Value *> ConstantIndices;
if (!IsFirstType)
ConstantIndices.push_back(
Constant::getNullValue(Type::getInt32Ty(GEP.getContext())));
append_range(ConstantIndices, drop_begin(Src->indices(), NumVarIndices));
Offset += DL.getIndexedOffsetInType(BaseType, ConstantIndices);
}
// Add the offset for GEP (which is fully constant).
if (!GEP.accumulateConstantOffset(DL, Offset))
return nullptr;
// Convert the total offset back into indices.
SmallVector<APInt> ConstIndices =
DL.getGEPIndicesForOffset(BaseType, Offset);
if (!Offset.isZero() || (!IsFirstType && !ConstIndices[0].isZero()))
return nullptr;
SmallVector<Value *> Indices;
append_range(Indices, drop_end(Src->indices(),
Src->getNumIndices() - NumVarIndices));
for (const APInt &Idx : drop_begin(ConstIndices, !IsFirstType))
Indices.push_back(ConstantInt::get(GEP.getContext(), Idx));
return isMergedGEPInBounds(*Src, *cast<GEPOperator>(&GEP))
? GetElementPtrInst::CreateInBounds(Src->getSourceElementType(),
Src->getOperand(0), Indices,
GEP.getName())
: GetElementPtrInst::Create(Src->getSourceElementType(),
Src->getOperand(0), Indices,
GEP.getName());
}
if (Src->getResultElementType() != GEP.getSourceElementType())
return nullptr;
SmallVector<Value*, 8> Indices;
// Find out whether the last index in the source GEP is a sequential idx.

View File

@ -181,8 +181,7 @@ define ptr @geps_combinable(ptr %a) {
define ptr @geps_combinable_different_elem_type1(ptr %a) {
; CHECK-LABEL: @geps_combinable_different_elem_type1(
; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 0, i32 1
; CHECK-NEXT: [[A3:%.*]] = getelementptr { i32, i32 }, ptr [[A2]], i64 0, i32 1
; CHECK-NEXT: [[A3:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 1
; CHECK-NEXT: ret ptr [[A3]]
;
%a2 = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1
@ -192,8 +191,7 @@ define ptr @geps_combinable_different_elem_type1(ptr %a) {
define ptr @geps_combinable_different_elem_type2(ptr %a) {
; CHECK-LABEL: @geps_combinable_different_elem_type2(
; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 0, i32 1
; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A2]], i64 4
; CHECK-NEXT: [[A3:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 1
; CHECK-NEXT: ret ptr [[A3]]
;
%a2 = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1
@ -203,8 +201,7 @@ define ptr @geps_combinable_different_elem_type2(ptr %a) {
define ptr @geps_combinable_different_elem_type3(ptr %a) {
; CHECK-LABEL: @geps_combinable_different_elem_type3(
; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 0, i32 1
; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A2]], i64 8
; CHECK-NEXT: [[A3:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 1, i32 1
; CHECK-NEXT: ret ptr [[A3]]
;
%a2 = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1
@ -225,9 +222,7 @@ define ptr @geps_combinable_different_elem_type4(ptr %a) {
define ptr @geps_combinable_different_elem_type5(ptr %a) {
; CHECK-LABEL: @geps_combinable_different_elem_type5(
; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 0, i32 1
; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A2]], i64 -4
; CHECK-NEXT: ret ptr [[A3]]
; CHECK-NEXT: ret ptr [[A:%.*]]
;
%a2 = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1
%a3 = getelementptr i8, ptr %a2, i64 -4
@ -236,8 +231,7 @@ define ptr @geps_combinable_different_elem_type5(ptr %a) {
define ptr @geps_combinable_different_elem_type6(ptr %a, i64 %idx) {
; CHECK-LABEL: @geps_combinable_different_elem_type6(
; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 [[IDX:%.*]]
; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A2]], i64 4
; CHECK-NEXT: [[A3:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 [[IDX:%.*]], i32 1
; CHECK-NEXT: ret ptr [[A3]]
;
%a2 = getelementptr { i32, i32 }, ptr %a, i64 %idx
@ -269,8 +263,7 @@ define ptr @geps_combinable_different_elem_type8(ptr %a, i64 %idx) {
define ptr @geps_combinable_different_elem_type9(ptr %a, i64 %idx) {
; CHECK-LABEL: @geps_combinable_different_elem_type9(
; CHECK-NEXT: [[A2:%.*]] = getelementptr inbounds { { i32, i32 } }, ptr [[A:%.*]], i64 [[IDX:%.*]], i32 0, i32 1
; CHECK-NEXT: [[A3:%.*]] = getelementptr inbounds i8, ptr [[A2]], i64 -4
; CHECK-NEXT: [[A3:%.*]] = getelementptr inbounds { { i32, i32 } }, ptr [[A:%.*]], i64 [[IDX:%.*]]
; CHECK-NEXT: ret ptr [[A3]]
;
%a2 = getelementptr inbounds { { i32, i32 } }, ptr %a, i64 %idx, i32 0, i32 1
@ -284,7 +277,7 @@ define ptr @geps_combinable_different_elem_type_extra_use1(ptr %a) {
; CHECK-LABEL: @geps_combinable_different_elem_type_extra_use1(
; CHECK-NEXT: [[A2:%.*]] = getelementptr { i32, i32 }, ptr [[A:%.*]], i64 0, i32 1
; CHECK-NEXT: call void @use(ptr [[A2]])
; CHECK-NEXT: [[A3:%.*]] = getelementptr i8, ptr [[A2]], i64 4
; CHECK-NEXT: [[A3:%.*]] = getelementptr { i32, i32 }, ptr [[A]], i64 1
; CHECK-NEXT: ret ptr [[A3]]
;
%a2 = getelementptr { i32, i32 }, ptr %a, i32 0, i32 1