Skip to content

Commit

Permalink
[InstCombine] Canonicalize gep T* X, V / sizeof(T) to gep i8* X, V (
Browse files Browse the repository at this point in the history
#76458)

This patch canonicalize `gep T* X, V / sizeof(T)` to `gep i8* X, V`.
Alive2: https://alive2.llvm.org/ce/z/7XGjiB

As this pattern has been handled by the backends, the motivation of this
patch is to reduce the ref count of sdiv, which will enable more
optimizations.
  • Loading branch information
dtcxzyw committed Dec 29, 2023
1 parent c97a767 commit 2128fca
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 23 deletions.
46 changes: 23 additions & 23 deletions llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2469,31 +2469,31 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
DL.getIndexSizeInBits(AS)) {
uint64_t TyAllocSize = DL.getTypeAllocSize(GEPEltType).getFixedValue();

bool Matched = false;
uint64_t C;
Value *V = nullptr;
if (TyAllocSize == 1) {
V = GEP.getOperand(1);
Matched = true;
} else if (match(GEP.getOperand(1),
m_AShr(m_Value(V), m_ConstantInt(C)))) {
if (TyAllocSize == 1ULL << C)
Matched = true;
} else if (match(GEP.getOperand(1),
m_SDiv(m_Value(V), m_ConstantInt(C)))) {
if (TyAllocSize == C)
Matched = true;
// Canonicalize (gep i8* X, (ptrtoint Y)-(ptrtoint X)) to (bitcast Y),
// but only if both point to the same underlying object (otherwise
// provenance is not necessarily retained).
Value *X = GEP.getPointerOperand();
Value *Y;
if (match(GEP.getOperand(1),
m_Sub(m_PtrToInt(m_Value(Y)), m_PtrToInt(m_Specific(X)))) &&
getUnderlyingObject(X) == getUnderlyingObject(Y))
return CastInst::CreatePointerBitCastOrAddrSpaceCast(Y, GEPType);
} else {
// Canonicalize (gep T* X, V / sizeof(T)) to (gep i8* X, V)
Value *V;
if ((has_single_bit(TyAllocSize) &&
match(GEP.getOperand(1),
m_Exact(m_AShr(m_Value(V),
m_SpecificInt(countr_zero(TyAllocSize)))))) ||
match(GEP.getOperand(1),
m_Exact(m_SDiv(m_Value(V), m_SpecificInt(TyAllocSize))))) {
GetElementPtrInst *NewGEP = GetElementPtrInst::Create(
Builder.getInt8Ty(), GEP.getPointerOperand(), V);
NewGEP->setIsInBounds(GEP.isInBounds());
return NewGEP;
}
}

// Canonicalize (gep i8* X, (ptrtoint Y)-(ptrtoint X)) to (bitcast Y), but
// only if both point to the same underlying object (otherwise provenance
// is not necessarily retained).
Value *Y;
Value *X = GEP.getOperand(0);
if (Matched &&
match(V, m_Sub(m_PtrToInt(m_Value(Y)), m_PtrToInt(m_Specific(X)))) &&
getUnderlyingObject(X) == getUnderlyingObject(Y))
return CastInst::CreatePointerBitCastOrAddrSpaceCast(Y, GEPType);
}
}
// We do not handle pointer-vector geps here.
Expand Down
84 changes: 84 additions & 0 deletions llvm/test/Transforms/InstCombine/getelementptr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1453,4 +1453,88 @@ define ptr @const_gep_chain(ptr %p, i64 %a) {
ret ptr %p4
}

define ptr @gep_sdiv(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_sdiv(
; CHECK-NEXT: [[PTR:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[OFF:%.*]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%index = sdiv exact i64 %off, 7
%ptr = getelementptr %struct.C, ptr %p, i64 %index
ret ptr %ptr
}

define <2 x ptr> @gep_sdiv_vec(<2 x ptr> %p, <2 x i64> %off) {
; CHECK-LABEL: @gep_sdiv_vec(
; CHECK-NEXT: [[PTR:%.*]] = getelementptr i8, <2 x ptr> [[P:%.*]], <2 x i64> [[OFF:%.*]]
; CHECK-NEXT: ret <2 x ptr> [[PTR]]
;
%index = sdiv exact <2 x i64> %off, <i64 7, i64 7>
%ptr = getelementptr %struct.C, <2 x ptr> %p, <2 x i64> %index
ret <2 x ptr> %ptr
}

define ptr @gep_sdiv_inbounds(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_sdiv_inbounds(
; CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 [[OFF:%.*]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%index = sdiv exact i64 %off, 7
%ptr = getelementptr inbounds %struct.C, ptr %p, i64 %index
ret ptr %ptr
}

define ptr @gep_ashr(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_ashr(
; CHECK-NEXT: [[PTR:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[OFF:%.*]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%index = ashr exact i64 %off, 2
%ptr = getelementptr i32, ptr %p, i64 %index
ret ptr %ptr
}

; Negative tests

define ptr @gep_i8(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_i8(
; CHECK-NEXT: [[PTR:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[OFF:%.*]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%ptr = getelementptr i8, ptr %p, i64 %off
ret ptr %ptr
}

define ptr @gep_sdiv_mismatched_size(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_sdiv_mismatched_size(
; CHECK-NEXT: [[INDEX:%.*]] = sdiv exact i64 [[OFF:%.*]], 20
; CHECK-NEXT: [[PTR:%.*]] = getelementptr [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[INDEX]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%index = sdiv exact i64 %off, 20
%ptr = getelementptr %struct.C, ptr %p, i64 %index
ret ptr %ptr
}

define ptr @gep_sdiv_without_exact(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_sdiv_without_exact(
; CHECK-NEXT: [[INDEX:%.*]] = sdiv i64 [[OFF:%.*]], 7
; CHECK-NEXT: [[PTR:%.*]] = getelementptr [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 [[INDEX]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%index = sdiv i64 %off, 7
%ptr = getelementptr %struct.C, ptr %p, i64 %index
ret ptr %ptr
}

define ptr @gep_ashr_without_exact(ptr %p, i64 %off) {
; CHECK-LABEL: @gep_ashr_without_exact(
; CHECK-NEXT: [[INDEX:%.*]] = ashr i64 [[OFF:%.*]], 2
; CHECK-NEXT: [[PTR:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 [[INDEX]]
; CHECK-NEXT: ret ptr [[PTR]]
;
%index = ashr i64 %off, 2
%ptr = getelementptr i32, ptr %p, i64 %index
ret ptr %ptr
}

!0 = !{!"branch_weights", i32 2, i32 10}

0 comments on commit 2128fca

Please sign in to comment.