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.
319 lines
11 KiB
LLVM
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
|
|
}
|