[llvm] [InstCombine] Support ptrtoint of gep folds for chain of geps (PR #137323)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 25 05:47:36 PDT 2025
https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/137323
>From af46eae33526c7c721f552be7590793cb401a42a Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 25 Apr 2025 12:34:03 +0200
Subject: [PATCH 1/2] [InstCombine] Add tests for ptrtoint of gep chain (NFC)
---
llvm/test/Transforms/InstCombine/cast_ptr.ll | 64 ++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/cast_ptr.ll b/llvm/test/Transforms/InstCombine/cast_ptr.ll
index 40ba21b24435b..bd6d817bd65a1 100644
--- a/llvm/test/Transforms/InstCombine/cast_ptr.ll
+++ b/llvm/test/Transforms/InstCombine/cast_ptr.ll
@@ -432,3 +432,67 @@ define i32 @ptr_add_in_int_extra_use2(i32 %x) {
%r = ptrtoint ptr %p2 to i32
ret i32 %r
}
+
+define i32 @ptrtoint_of_inttoptr_multiple_gep(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @ptrtoint_of_inttoptr_multiple_gep(
+; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[X:%.*]] to ptr
+; CHECK-NEXT: [[PTR2:%.*]] = getelementptr nuw i16, ptr [[PTR]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[PTR3:%.*]] = getelementptr i32, ptr [[PTR2]], i32 [[Z:%.*]]
+; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[PTR3]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %ptr = inttoptr i32 %x to ptr
+ %ptr2 = getelementptr nuw i16, ptr %ptr, i32 %y
+ %ptr3 = getelementptr i32, ptr %ptr2, i32 %z
+ %r = ptrtoint ptr %ptr3 to i32
+ ret i32 %r
+}
+
+define i32 @ptrtoint_of_inttoptr_multiple_gep_extra_use(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @ptrtoint_of_inttoptr_multiple_gep_extra_use(
+; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[X:%.*]] to ptr
+; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i16, ptr [[PTR]], i32 [[Y:%.*]]
+; CHECK-NEXT: call void @use_ptr(ptr [[PTR2]])
+; CHECK-NEXT: [[PTR3:%.*]] = getelementptr i32, ptr [[PTR2]], i32 [[Z:%.*]]
+; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[PTR3]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %ptr = inttoptr i32 %x to ptr
+ %ptr2 = getelementptr i16, ptr %ptr, i32 %y
+ call void @use_ptr(ptr %ptr2)
+ %ptr3 = getelementptr i32, ptr %ptr2, i32 %z
+ %r = ptrtoint ptr %ptr3 to i32
+ ret i32 %r
+}
+
+define i32 @ptrtoint_of_null_multiple_gep(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @ptrtoint_of_null_multiple_gep(
+; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i16, ptr null, i32 [[X:%.*]]
+; CHECK-NEXT: [[PTR3:%.*]] = getelementptr nuw i32, ptr [[PTR2]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[PTR4:%.*]] = getelementptr i64, ptr [[PTR3]], i32 [[Z:%.*]]
+; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[PTR4]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %ptr2 = getelementptr i16, ptr null, i32 %x
+ %ptr3 = getelementptr nuw i32, ptr %ptr2, i32 %y
+ %ptr4 = getelementptr i64, ptr %ptr3, i32 %z
+ %r = ptrtoint ptr %ptr4 to i32
+ ret i32 %r
+}
+
+define i32 @ptrtoint_of_null_multiple_gep_extra_use(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @ptrtoint_of_null_multiple_gep_extra_use(
+; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i16, ptr null, i32 [[X:%.*]]
+; CHECK-NEXT: call void @use_ptr(ptr [[PTR2]])
+; CHECK-NEXT: [[PTR3:%.*]] = getelementptr nuw i32, ptr [[PTR2]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[PTR4:%.*]] = getelementptr i64, ptr [[PTR3]], i32 [[Z:%.*]]
+; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[PTR4]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %ptr2 = getelementptr i16, ptr null, i32 %x
+ call void @use_ptr(ptr %ptr2)
+ %ptr3 = getelementptr nuw i32, ptr %ptr2, i32 %y
+ %ptr4 = getelementptr i64, ptr %ptr3, i32 %z
+ %r = ptrtoint ptr %ptr4 to i32
+ ret i32 %r
+}
>From cd7497be6da6a3921dd1148d0590324a625e40e8 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 25 Apr 2025 14:42:32 +0200
Subject: [PATCH 2/2] [InstCombine] Support ptrtoint of gep folds for chain of
geps
Support the ptrtoint(gep null, x) -> x and
ptrtoint(gep inttoptr(x), y) -> x+y folds for the case where there
is a chain of geps that ends in null or inttoptr. This avoids some
regressions from #137297.
While here, also be a bit more careful about edge cases like
pointer to vector splats and mismatched pointer and index size.
---
.../InstCombine/InstCombineCasts.cpp | 61 ++++++++++++-------
.../InstCombine/InstCombineInternal.h | 1 +
llvm/test/Transforms/InstCombine/cast_ptr.ll | 54 +++++++++++++---
3 files changed, 84 insertions(+), 32 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 1a95636f37ed7..d6c99366e6f00 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2067,6 +2067,42 @@ Instruction *InstCombinerImpl::visitIntToPtr(IntToPtrInst &CI) {
return nullptr;
}
+Value *InstCombinerImpl::foldPtrToIntOfGEP(Type *IntTy, Value *Ptr) {
+ // Look through chain of one-use GEPs.
+ Type *PtrTy = Ptr->getType();
+ SmallVector<GEPOperator *> GEPs;
+ while (true) {
+ auto *GEP = dyn_cast<GEPOperator>(Ptr);
+ if (!GEP || !GEP->hasOneUse())
+ break;
+ GEPs.push_back(GEP);
+ Ptr = GEP->getPointerOperand();
+ }
+
+ // Don't handle case where GEP converts from pointer to vector.
+ if (GEPs.empty() || PtrTy != Ptr->getType())
+ return nullptr;
+
+ // Check whether we know the integer value of the base pointer.
+ Value *Res;
+ Type *IdxTy = DL.getIndexType(PtrTy);
+ if (match(Ptr, m_OneUse(m_IntToPtr(m_Value(Res)))) &&
+ Res->getType() == IntTy && IntTy == IdxTy) {
+ // pass
+ } else if (isa<ConstantPointerNull>(Ptr)) {
+ Res = Constant::getNullValue(IdxTy);
+ } else {
+ return nullptr;
+ }
+
+ // Perform the entire operation on integers instead.
+ for (GEPOperator *GEP : reverse(GEPs)) {
+ Value *Offset = EmitGEPOffset(GEP);
+ Res = Builder.CreateAdd(Res, Offset, "", GEP->hasNoUnsignedWrap());
+ }
+ return Builder.CreateZExtOrTrunc(Res, IntTy);
+}
+
Instruction *InstCombinerImpl::visitPtrToInt(PtrToIntInst &CI) {
// If the destination integer type is not the intptr_t type for this target,
// do a ptrtoint to intptr_t then do a trunc or zext. This allows the cast
@@ -2093,29 +2129,8 @@ Instruction *InstCombinerImpl::visitPtrToInt(PtrToIntInst &CI) {
Mask->getType() == Ty)
return BinaryOperator::CreateAnd(Builder.CreatePtrToInt(Ptr, Ty), Mask);
- if (auto *GEP = dyn_cast<GEPOperator>(SrcOp)) {
- // Fold ptrtoint(gep null, x) to multiply + constant if the GEP has one use.
- // While this can increase the number of instructions it doesn't actually
- // increase the overall complexity since the arithmetic is just part of
- // the GEP otherwise.
- if (GEP->hasOneUse() &&
- isa<ConstantPointerNull>(GEP->getPointerOperand())) {
- return replaceInstUsesWith(CI,
- Builder.CreateIntCast(EmitGEPOffset(GEP), Ty,
- /*isSigned=*/false));
- }
-
- // (ptrtoint (gep (inttoptr Base), ...)) -> Base + Offset
- Value *Base;
- if (GEP->hasOneUse() &&
- match(GEP->getPointerOperand(), m_OneUse(m_IntToPtr(m_Value(Base)))) &&
- Base->getType() == Ty) {
- Value *Offset = EmitGEPOffset(GEP);
- auto *NewOp = BinaryOperator::CreateAdd(Base, Offset);
- NewOp->setHasNoUnsignedWrap(GEP->hasNoUnsignedWrap());
- return NewOp;
- }
- }
+ if (Value *V = foldPtrToIntOfGEP(Ty, SrcOp))
+ return replaceInstUsesWith(CI, V);
Value *Vec, *Scalar, *Index;
if (match(SrcOp, m_OneUse(m_InsertElt(m_IntToPtr(m_Value(Vec)),
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 9923719c3443d..324738ef8c88e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -661,6 +661,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
/// folded operation.
void PHIArgMergedDebugLoc(Instruction *Inst, PHINode &PN);
+ Value *foldPtrToIntOfGEP(Type *IntTy, Value *Ptr);
Instruction *foldGEPICmp(GEPOperator *GEPLHS, Value *RHS, CmpPredicate Cond,
Instruction &I);
Instruction *foldSelectICmp(CmpPredicate Pred, SelectInst *SI, Value *RHS,
diff --git a/llvm/test/Transforms/InstCombine/cast_ptr.ll b/llvm/test/Transforms/InstCombine/cast_ptr.ll
index bd6d817bd65a1..1cf3e32556b38 100644
--- a/llvm/test/Transforms/InstCombine/cast_ptr.ll
+++ b/llvm/test/Transforms/InstCombine/cast_ptr.ll
@@ -2,7 +2,7 @@
; Tests to make sure elimination of casts is working correctly
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
-target datalayout = "p:32:32-p1:32:32-p2:16:16"
+target datalayout = "p:32:32-p1:32:32-p2:16:16-p3:32:32:32:16"
@global = global i8 0
@@ -435,10 +435,10 @@ define i32 @ptr_add_in_int_extra_use2(i32 %x) {
define i32 @ptrtoint_of_inttoptr_multiple_gep(i32 %x, i32 %y, i32 %z) {
; CHECK-LABEL: @ptrtoint_of_inttoptr_multiple_gep(
-; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[X:%.*]] to ptr
-; CHECK-NEXT: [[PTR2:%.*]] = getelementptr nuw i16, ptr [[PTR]], i32 [[Y:%.*]]
-; CHECK-NEXT: [[PTR3:%.*]] = getelementptr i32, ptr [[PTR2]], i32 [[Z:%.*]]
-; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[PTR3]] to i32
+; CHECK-NEXT: [[PTR2_IDX:%.*]] = shl nuw i32 [[Y:%.*]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[X:%.*]], [[PTR2_IDX]]
+; CHECK-NEXT: [[PTR3_IDX:%.*]] = shl i32 [[Z:%.*]], 2
+; CHECK-NEXT: [[R:%.*]] = add i32 [[TMP1]], [[PTR3_IDX]]
; CHECK-NEXT: ret i32 [[R]]
;
%ptr = inttoptr i32 %x to ptr
@@ -465,12 +465,26 @@ define i32 @ptrtoint_of_inttoptr_multiple_gep_extra_use(i32 %x, i32 %y, i32 %z)
ret i32 %r
}
+define i32 @ptrtoint_of_inttoptr_index_type(i32 %x, i16 %y) {
+; CHECK-LABEL: @ptrtoint_of_inttoptr_index_type(
+; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[X:%.*]] to ptr addrspace(3)
+; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i16, ptr addrspace(3) [[PTR]], i16 [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr addrspace(3) [[PTR2]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %ptr = inttoptr i32 %x to ptr addrspace(3)
+ %ptr2 = getelementptr i16, ptr addrspace(3) %ptr, i16 %y
+ %r = ptrtoint ptr addrspace(3) %ptr2 to i32
+ ret i32 %r
+}
+
define i32 @ptrtoint_of_null_multiple_gep(i32 %x, i32 %y, i32 %z) {
; CHECK-LABEL: @ptrtoint_of_null_multiple_gep(
-; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i16, ptr null, i32 [[X:%.*]]
-; CHECK-NEXT: [[PTR3:%.*]] = getelementptr nuw i32, ptr [[PTR2]], i32 [[Y:%.*]]
-; CHECK-NEXT: [[PTR4:%.*]] = getelementptr i64, ptr [[PTR3]], i32 [[Z:%.*]]
-; CHECK-NEXT: [[R:%.*]] = ptrtoint ptr [[PTR4]] to i32
+; CHECK-NEXT: [[PTR2_IDX:%.*]] = shl i32 [[X:%.*]], 1
+; CHECK-NEXT: [[PTR3_IDX:%.*]] = shl nuw i32 [[Y:%.*]], 2
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[PTR2_IDX]], [[PTR3_IDX]]
+; CHECK-NEXT: [[PTR4_IDX:%.*]] = shl i32 [[Z:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = add i32 [[TMP1]], [[PTR4_IDX]]
; CHECK-NEXT: ret i32 [[R]]
;
%ptr2 = getelementptr i16, ptr null, i32 %x
@@ -496,3 +510,25 @@ define i32 @ptrtoint_of_null_multiple_gep_extra_use(i32 %x, i32 %y, i32 %z) {
%r = ptrtoint ptr %ptr4 to i32
ret i32 %r
}
+
+define i32 @ptrtoint_of_null_index_type(i16 %x) {
+; CHECK-LABEL: @ptrtoint_of_null_index_type(
+; CHECK-NEXT: [[PTR_IDX:%.*]] = shl i16 [[X:%.*]], 1
+; CHECK-NEXT: [[R:%.*]] = zext i16 [[PTR_IDX]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %ptr = getelementptr i16, ptr addrspace(3) null, i16 %x
+ %r = ptrtoint ptr addrspace(3) %ptr to i32
+ ret i32 %r
+}
+
+define <2 x i32> @ptrtoint_of_null_splat(<2 x i16> %x) {
+; CHECK-LABEL: @ptrtoint_of_null_splat(
+; CHECK-NEXT: [[PTR:%.*]] = getelementptr i16, ptr addrspace(3) null, <2 x i16> [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = ptrtoint <2 x ptr addrspace(3)> [[PTR]] to <2 x i32>
+; CHECK-NEXT: ret <2 x i32> [[R]]
+;
+ %ptr = getelementptr i16, ptr addrspace(3) null, <2 x i16> %x
+ %r = ptrtoint <2 x ptr addrspace(3)> %ptr to <2 x i32>
+ ret <2 x i32> %r
+}
More information about the llvm-commits
mailing list