[InstComb] Fold inttoptr (add (ptrtoint %B), %O) -> GEP for ICMP users. (#153421)

Replace inttoptr (add (ptrtoint %B), %O) with (getelementptr i8, %B, %o)
if all users are ICmp instruction, which in turn means only the address
value is compared. We should be able to do this, if the src pointer,
the integer type and the destination pointer types have the same
bitwidth and address space.

A common source of such (inttoptr (add (ptrtoint %B), %O)) is from
various iterations in libc++.

In practice this triggers in a number of files in Clang and various open
source projects, including cppcheck, diamond, llama and more.

Alive2 Proof with constant offset: https://alive2.llvm.org/ce/z/K_5N_B

PR: https://github.com/llvm/llvm-project/pull/153421
This commit is contained in:
Florian Hahn 2025-08-21 16:36:25 +01:00 committed by GitHub
parent 0594bad039
commit 1b0b59ae43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 25 deletions

View File

@ -2072,6 +2072,19 @@ Instruction *InstCombinerImpl::visitIntToPtr(IntToPtrInst &CI) {
return new IntToPtrInst(P, CI.getType());
}
// Replace (inttoptr (add (ptrtoint %Base), %Offset)) with
// (getelementptr i8, %Base, %Offset) if all users are ICmps.
Value *Base;
Value *Offset;
if (match(CI.getOperand(0),
m_OneUse(m_c_Add(m_PtrToIntSameSize(DL, m_Value(Base)),
m_Value(Offset)))) &&
CI.getType()->getPointerAddressSpace() ==
Base->getType()->getPointerAddressSpace() &&
all_of(CI.users(), IsaPred<ICmpInst>)) {
return GetElementPtrInst::Create(Builder.getInt8Ty(), Base, Offset);
}
if (Instruction *I = commonCastTransforms(CI))
return I;

View File

@ -32,10 +32,8 @@ define i32 @g(i32 %x) {
define i1 @inttoptr_add_ptrtoint_used_by_single_icmp(ptr %src, ptr %p2) {
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_single_icmp(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[I]], 10
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 10
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2]], [[P:%.*]]
; CHECK-NEXT: ret i1 [[C]]
;
%i = ptrtoint ptr %src to i64
@ -47,10 +45,8 @@ define i1 @inttoptr_add_ptrtoint_used_by_single_icmp(ptr %src, ptr %p2) {
define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_operands_swapped(ptr %src, ptr %p2) {
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_single_icmp_operands_swapped(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[I]], 10
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 10
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2]], [[P:%.*]]
; CHECK-NEXT: ret i1 [[C]]
;
%i = ptrtoint ptr %src to i64
@ -62,10 +58,8 @@ define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_operands_swapped(ptr %src,
define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_constant_offset(ptr %src, i64 %off, ptr %p2) {
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_single_icmp_constant_offset(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[OFF:%.*]], [[I]]
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 [[OFF:%.*]]
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2]], [[P:%.*]]
; CHECK-NEXT: ret i1 [[C]]
;
%i = ptrtoint ptr %src to i64
@ -77,10 +71,8 @@ define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_constant_offset(ptr %src, i
define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_constant_offset_operands_swapped(ptr %src, i64 %off, ptr %p2) {
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_single_icmp_constant_offset_operands_swapped(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[OFF:%.*]], [[I]]
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 [[OFF:%.*]]
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2]], [[P:%.*]]
; CHECK-NEXT: ret i1 [[C]]
;
%i = ptrtoint ptr %src to i64
@ -137,6 +129,23 @@ define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_int_type_does_not_match_ptr
ret i1 %c
}
define i1 @inttoptr_add_multiple_users_ptrtoint_used_by_single_icmp(ptr %src, ptr %p2) {
; CHECK-LABEL: @inttoptr_add_multiple_users_ptrtoint_used_by_single_icmp(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[I]], 10
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: call void @bar(i64 [[A]])
; CHECK-NEXT: ret i1 [[C]]
;
%i = ptrtoint ptr %src to i64
%a = add i64 %i, 10
%p = inttoptr i64 %a to ptr
%c = icmp eq ptr %p, %p2
call void @bar(i64 %a)
ret i1 %c
}
define i1 @multiple_inttoptr_add_ptrtoint_used_by_single_icmp(ptr %src) {
; CHECK-LABEL: @multiple_inttoptr_add_ptrtoint_used_by_single_icmp(
; CHECK-NEXT: ret i1 false
@ -181,10 +190,8 @@ define i1 @inttoptr_add_ptrtoint_used_by_single_icmp_in_different_bb(i1 %bc, ptr
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_single_icmp_in_different_bb(
; CHECK-NEXT: br i1 [[BC:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[I]], 10
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 10
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P2]], [[P:%.*]]
; CHECK-NEXT: ret i1 [[C]]
; CHECK: else:
; CHECK-NEXT: ret i1 false
@ -204,11 +211,9 @@ else:
define i1 @inttoptr_add_ptrtoint_used_by_multiple_icmps(ptr %src, ptr %p2, ptr %p3) {
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_multiple_icmps(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[SRC:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = add i64 [[I]], 10
; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[A]] to ptr
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[P2:%.*]], [[P]]
; CHECK-NEXT: [[C_2:%.*]] = icmp eq ptr [[P3:%.*]], [[P]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[SRC:%.*]], i64 10
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[P2]], [[P:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp eq ptr [[P2]], [[P3:%.*]]
; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[C_1]], [[C_2]]
; CHECK-NEXT: ret i1 [[XOR]]
;
@ -222,6 +227,7 @@ define i1 @inttoptr_add_ptrtoint_used_by_multiple_icmps(ptr %src, ptr %p2, ptr %
}
declare void @foo(ptr)
declare void @bar(i64)
define i1 @inttoptr_add_ptrtoint_used_by_multiple_icmps_and_other_user(ptr %src, ptr %p2, ptr %p3) {
; CHECK-LABEL: @inttoptr_add_ptrtoint_used_by_multiple_icmps_and_other_user(