Nikita Popov ec26f219ac
[InstSimplify] Support ptrtoaddr in simplifyGEPInst() (#164262)
This adds support for ptrtoaddr in the `ptradd p, ptrtoaddr(p2) -
ptrtoaddr(p) -> p2` fold.

This fold requires that p and p2 have the same underlying object
(otherwise the provenance may not be the same).

The argument I would like to make here is that because the underlying
objects are the same (and the pointers in the same address space), the
non-address bits of the pointer must be the same. Looking at some
specific cases of underlying object relationship:

 * phi/select: Trivially true.
* getelementptr: Only modifies address bits, non-address bits must
remain the same.
* addrspacecast round-trip cast: Must preserve all bits because we
optimize such round-trip casts away.
* non-interposable global alias: I'm a bit unsure about this one, but I
guess the alias and the aliasee must have the same non-address bits?
* various intrinsics like launder.invariant.group, ptrmask. I think
these all either preserve all pointer bits (like the invariant.group
ones) or at least the non-address bits (like ptrmask). There are some
interesting cases like amdgcn.make.buffer.rsrc, but those are cross
address-space.

-----

There is a second `gep (gep p, C), (sub 0, ptrtoint(p)) -> C` transform
in this function, which I am not extending to handle ptrtoaddr, adding
negative tests instead. This transform is overall dubious for provenance
reasons, but especially dubious with ptrtoaddr, as then we don't have
the guarantee that provenance of `p` has been exposed.
2025-10-21 09:27:07 +02:00

319 lines
11 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -passes=instsimplify -S | FileCheck %s
; The ptrtoaddr folds are also valid for pointers that have external state.
target datalayout = "pe1:64:64:64:32"
@g = external global i8
@g2 = external global i8
@g.as1 = external addrspace(1) global i8
@g2.as1 = external addrspace(1) global i8
define i64 @ptrtoaddr_inttoptr_arg(i64 %a) {
; CHECK-LABEL: define i64 @ptrtoaddr_inttoptr_arg(
; CHECK-SAME: i64 [[A:%.*]]) {
; CHECK-NEXT: ret i64 [[A]]
;
%toptr = inttoptr i64 %a to ptr
%toaddr = ptrtoaddr ptr %toptr to i64
ret i64 %toaddr
}
define i32 @ptrtoaddr_inttoptr_arg_addrsize(i32 %a) {
; CHECK-LABEL: define i32 @ptrtoaddr_inttoptr_arg_addrsize(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: ret i32 [[A]]
;
%toptr = inttoptr i32 %a to ptr addrspace(1)
%toaddr = ptrtoaddr ptr addrspace(1) %toptr to i32
ret i32 %toaddr
}
define i32 @ptrtoaddr_inttoptr() {
; CHECK-LABEL: define i32 @ptrtoaddr_inttoptr() {
; CHECK-NEXT: ret i32 -1
;
%toptr = inttoptr i32 -1 to ptr addrspace(1)
%toaddr = ptrtoaddr ptr addrspace(1) %toptr to i32
ret i32 %toaddr
}
define i32 @ptrtoaddr_inttoptr_diff_size1() {
; CHECK-LABEL: define i32 @ptrtoaddr_inttoptr_diff_size1() {
; CHECK-NEXT: ret i32 -1
;
%toptr = inttoptr i64 -1 to ptr addrspace(1)
%toaddr = ptrtoaddr ptr addrspace(1) %toptr to i32
ret i32 %toaddr
}
define i32 @ptrtoaddr_inttoptr_diff_size2() {
; CHECK-LABEL: define i32 @ptrtoaddr_inttoptr_diff_size2() {
; CHECK-NEXT: ret i32 65535
;
%toptr = inttoptr i16 -1 to ptr addrspace(1)
%toaddr = ptrtoaddr ptr addrspace(1) %toptr to i32
ret i32 %toaddr
}
define i64 @ptrtoaddr_inttoptr_noas1() {
; CHECK-LABEL: define i64 @ptrtoaddr_inttoptr_noas1() {
; CHECK-NEXT: ret i64 1
;
%toptr = getelementptr i8, ptr null, i64 1
%toaddr = ptrtoaddr ptr %toptr to i64
ret i64 %toaddr
}
define i64 @ptr2addr2_inttoptr_noas2() {
; CHECK-LABEL: define i64 @ptr2addr2_inttoptr_noas2() {
; CHECK-NEXT: ret i64 123
;
%toptr = inttoptr i64 123 to ptr
%toaddr = ptrtoaddr ptr %toptr to i64
ret i64 %toaddr
}
define i64 @ptrtoaddr_inttoptr_noas_diff_size1() {
; CHECK-LABEL: define i64 @ptrtoaddr_inttoptr_noas_diff_size1() {
; CHECK-NEXT: ret i64 4294967295
;
%toptr = inttoptr i32 -1 to ptr
%toaddr = ptrtoaddr ptr %toptr to i64
ret i64 %toaddr
}
define i64 @ptrtoaddr_inttoptr_noas_diff_size2() {
; CHECK-LABEL: define i64 @ptrtoaddr_inttoptr_noas_diff_size2() {
; CHECK-NEXT: ret i64 -1
;
%toptr = inttoptr i128 -1 to ptr
%toaddr = ptrtoaddr ptr %toptr to i64
ret i64 %toaddr
}
define i64 @ptrtoaddr_gep_null() {
; CHECK-LABEL: define i64 @ptrtoaddr_gep_null() {
; CHECK-NEXT: ret i64 42
;
%toaddr = ptrtoaddr ptr getelementptr (i8, ptr null, i64 42) to i64
ret i64 %toaddr
}
define i32 @ptrtoaddr_gep_null_addrsize() {
; CHECK-LABEL: define i32 @ptrtoaddr_gep_null_addrsize() {
; CHECK-NEXT: ret i32 42
;
%toaddr = ptrtoaddr ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i32 42) to i32
ret i32 %toaddr
}
define i64 @ptrtoaddr_gep_sub() {
; CHECK-LABEL: define i64 @ptrtoaddr_gep_sub() {
; CHECK-NEXT: ret i64 sub (i64 ptrtoaddr (ptr @g to i64), i64 ptrtoaddr (ptr @g2 to i64))
;
%toaddr = ptrtoaddr ptr getelementptr (i8, ptr @g, i64 sub (i64 0, i64 ptrtoaddr (ptr @g2 to i64))) to i64
ret i64 %toaddr
}
define i32 @ptrtoaddr_gep_sub_addrsize() {
; CHECK-LABEL: define i32 @ptrtoaddr_gep_sub_addrsize() {
; CHECK-NEXT: ret i32 sub (i32 ptrtoaddr (ptr addrspace(1) @g.as1 to i32), i32 ptrtoaddr (ptr addrspace(1) @g2.as1 to i32))
;
%toaddr = ptrtoaddr ptr addrspace(1) getelementptr (i8, ptr addrspace(1) @g.as1, i32 sub (i32 0, i32 ptrtoaddr (ptr addrspace(1) @g2.as1 to i32))) to i32
ret i32 %toaddr
}
; Don't fold inttoptr of ptrtoaddr away. inttoptr will pick a previously
; exposed provenance, which is not necessarily that of @g (especially as
; ptrtoaddr does not expose the provenance.)
define ptr @inttoptr_of_ptrtoaddr() {
; CHECK-LABEL: define ptr @inttoptr_of_ptrtoaddr() {
; CHECK-NEXT: ret ptr inttoptr (i64 ptrtoaddr (ptr @g to i64) to ptr)
;
%toptr = inttoptr i64 ptrtoaddr (ptr @g to i64) to ptr
ret ptr %toptr
}
define i64 @ptrtoaddr_sub_consts_unrelated() {
; CHECK-LABEL: define i64 @ptrtoaddr_sub_consts_unrelated() {
; CHECK-NEXT: ret i64 sub (i64 ptrtoaddr (ptr @g to i64), i64 ptrtoaddr (ptr @g2 to i64))
;
%sub = sub i64 ptrtoaddr (ptr @g to i64), ptrtoaddr (ptr @g2 to i64)
ret i64 %sub
}
define i64 @ptrtoaddr_sub_consts_offset() {
; CHECK-LABEL: define i64 @ptrtoaddr_sub_consts_offset() {
; CHECK-NEXT: ret i64 42
;
%sub = sub i64 ptrtoaddr (ptr getelementptr (i8, ptr @g, i64 42) to i64), ptrtoaddr (ptr @g to i64)
ret i64 %sub
}
define i32 @ptrtoaddr_sub_consts_offset_addrsize() {
; CHECK-LABEL: define i32 @ptrtoaddr_sub_consts_offset_addrsize() {
; CHECK-NEXT: ret i32 42
;
%sub = sub i32 ptrtoaddr (ptr addrspace(1) getelementptr (i8, ptr addrspace(1) @g.as1, i32 42) to i32), ptrtoaddr (ptr addrspace(1) @g.as1 to i32)
ret i32 %sub
}
define i64 @ptrtoaddr_sub_known_offset(ptr %p) {
; CHECK-LABEL: define i64 @ptrtoaddr_sub_known_offset(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: ret i64 42
;
%p2 = getelementptr inbounds i8, ptr %p, i64 42
%p.addr = ptrtoaddr ptr %p to i64
%p2.addr = ptrtoaddr ptr %p2 to i64
%sub = sub i64 %p2.addr, %p.addr
ret i64 %sub
}
define i32 @ptrtoaddr_sub_known_offset_addrsize(ptr addrspace(1) %p) {
; CHECK-LABEL: define i32 @ptrtoaddr_sub_known_offset_addrsize(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
; CHECK-NEXT: ret i32 42
;
%p2 = getelementptr inbounds i8, ptr addrspace(1) %p, i32 42
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
%sub = sub i32 %p2.addr, %p.addr
ret i32 %sub
}
define i64 @ptrtoaddr_of_ptradd_of_sub(i64 %x, ptr %p) {
; CHECK-LABEL: define i64 @ptrtoaddr_of_ptradd_of_sub(
; CHECK-SAME: i64 [[X:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: ret i64 [[X]]
;
%p.addr = ptrtoaddr ptr %p to i64
%sub = sub i64 %x, %p.addr
%ptradd = getelementptr i8, ptr %p, i64 %sub
%ptradd.addr = ptrtoaddr ptr %ptradd to i64
ret i64 %ptradd.addr
}
define i32 @ptrtoaddr_of_ptradd_of_sub_addrsize(i32 %x, ptr addrspace(1) %p) {
; CHECK-LABEL: define i32 @ptrtoaddr_of_ptradd_of_sub_addrsize(
; CHECK-SAME: i32 [[X:%.*]], ptr addrspace(1) [[P:%.*]]) {
; CHECK-NEXT: ret i32 [[X]]
;
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
%sub = sub i32 %x, %p.addr
%ptradd = getelementptr i8, ptr addrspace(1) %p, i32 %sub
%ptradd.addr = ptrtoaddr ptr addrspace(1) %ptradd to i32
ret i32 %ptradd.addr
}
define ptr @gep_of_sub_ptrtoaddr_unrelated_pointers(ptr %p, ptr %p2, i64 %x) {
; CHECK-LABEL: define ptr @gep_of_sub_ptrtoaddr_unrelated_pointers(
; CHECK-SAME: ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr [[P2]] to i64
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[P2_ADDR]], [[P_ADDR]]
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[P]], i64 [[SUB]]
; CHECK-NEXT: ret ptr [[GEP2]]
;
%p2.addr = ptrtoaddr ptr %p2 to i64
%p.addr = ptrtoaddr ptr %p to i64
%sub = sub i64 %p2.addr, %p.addr
%gep2 = getelementptr i8, ptr %p, i64 %sub
ret ptr %gep2
}
define ptr @gep_of_sub_ptrtoaddr(ptr %p, i64 %x) {
; CHECK-LABEL: define ptr @gep_of_sub_ptrtoaddr(
; CHECK-SAME: ptr [[P:%.*]], i64 [[X:%.*]]) {
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
; CHECK-NEXT: ret ptr [[GEP1]]
;
%gep1 = getelementptr i8, ptr %p, i64 %x
%gep1.addr = ptrtoaddr ptr %gep1 to i64
%p.addr = ptrtoaddr ptr %p to i64
%sub = sub i64 %gep1.addr, %p.addr
%gep2 = getelementptr i8, ptr %p, i64 %sub
ret ptr %gep2
}
define ptr addrspace(1) @gep_of_sub_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %x) {
; CHECK-LABEL: define ptr addrspace(1) @gep_of_sub_ptrtoaddr_addrsize(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr addrspace(1) [[P]], i32 [[X]]
; CHECK-NEXT: ret ptr addrspace(1) [[GEP1]]
;
%gep1 = getelementptr i8, ptr addrspace(1) %p, i32 %x
%gep1.addr = ptrtoaddr ptr addrspace(1) %gep1 to i32
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
%sub = sub i32 %gep1.addr, %p.addr
%gep2 = getelementptr i8, ptr addrspace(1) %p, i32 %sub
ret ptr addrspace(1) %gep2
}
define ptr @gep_of_sub_ptrtoaddr_ashr(ptr %p, i64 %x) {
; CHECK-LABEL: define ptr @gep_of_sub_ptrtoaddr_ashr(
; CHECK-SAME: ptr [[P:%.*]], i64 [[X:%.*]]) {
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
; CHECK-NEXT: ret ptr [[GEP1]]
;
%gep1 = getelementptr i8, ptr %p, i64 %x
%gep1.addr = ptrtoaddr ptr %gep1 to i64
%p.addr = ptrtoaddr ptr %p to i64
%sub = sub i64 %gep1.addr, %p.addr
%ashr = ashr i64 %sub, 1
%gep2 = getelementptr i16, ptr %p, i64 %ashr
ret ptr %gep2
}
define ptr addrspace(1) @gep_of_sub_ptrtoaddr_ashr_addrsize(ptr addrspace(1) %p, i32 %x) {
; CHECK-LABEL: define ptr addrspace(1) @gep_of_sub_ptrtoaddr_ashr_addrsize(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[X:%.*]]) {
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i8, ptr addrspace(1) [[P]], i32 [[X]]
; CHECK-NEXT: ret ptr addrspace(1) [[GEP1]]
;
%gep1 = getelementptr i8, ptr addrspace(1) %p, i32 %x
%gep1.addr = ptrtoaddr ptr addrspace(1) %gep1 to i32
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
%sub = sub i32 %gep1.addr, %p.addr
%sdiv = sdiv i32 %sub, 3
%gep2 = getelementptr [3 x i8], ptr addrspace(1) %p, i32 %sdiv
ret ptr addrspace(1) %gep2
}
; Not folding this to inttoptr(123), as this may have different provenance from
; %p, and the use of ptrtoaddr implies that the provenance of %p may not be
; exposed, such that inttoptr cannot recover it.
define ptr @gep_gep_neg_ptrtoaddr(ptr %p) {
; CHECK-LABEL: define ptr @gep_gep_neg_ptrtoaddr(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 123
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
; CHECK-NEXT: [[P_ADDR_NEG:%.*]] = sub i64 0, [[P_ADDR]]
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[GEP1]], i64 [[P_ADDR_NEG]]
; CHECK-NEXT: ret ptr [[GEP2]]
;
%gep1 = getelementptr inbounds i8, ptr %p, i64 123
%p.addr = ptrtoaddr ptr %p to i64
%p.addr.neg = sub i64 0, %p.addr
%gep2 = getelementptr i8, ptr %gep1, i64 %p.addr.neg
ret ptr %gep2
}
define ptr @gep_gep_inv_ptrtoaddr(ptr %p) {
; CHECK-LABEL: define ptr @gep_gep_inv_ptrtoaddr(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 123
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
; CHECK-NEXT: [[P_ADDR_INV:%.*]] = xor i64 [[P_ADDR]], -1
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[GEP1]], i64 [[P_ADDR_INV]]
; CHECK-NEXT: ret ptr [[GEP2]]
;
%gep1 = getelementptr inbounds i8, ptr %p, i64 123
%p.addr = ptrtoaddr ptr %p to i64
%p.addr.inv = xor i64 %p.addr, -1
%gep2 = getelementptr i8, ptr %gep1, i64 %p.addr.inv
ret ptr %gep2
}