| ; 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 | |
| ; Sized objects with no alignment to exploit, so that the folds below can only | |
| ; come from the object's size. | |
| @g5 = global [5 x i32] zeroinitializer, align 1 | |
| @g5.as1 = addrspace(1) global [5 x i32] zeroinitializer, align 1 | |
| @g5.decl = external global [5 x i32], align 1 | |
| @g5.weak = extern_weak global [5 x i32], align 1 | |
| ; An alias may resolve to a weak undefined symbol, and thus to the null address. | |
| @aliasee = global [5 x i32] zeroinitializer, align 1 | |
| @alias = weak alias [5 x i32], ptr @aliasee | |
| 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 | |
| } | |
| define i1 @icmp_ptrtoaddr_0() { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_0() { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %cmp = icmp ne i64 ptrtoaddr (ptr @g to i64), 0 | |
| ret i1 %cmp | |
| } | |
| ; This fails to fold because we currently don't assume that globals are located | |
| ; at a non-null address for non-default address spaces. | |
| define i1 @icmp_ptrtoaddr_0_addrsize() { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_0_addrsize() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 ptrtoaddr (ptr addrspace(1) @g.as1 to i32), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp ne i32 ptrtoaddr (ptr addrspace(1) @g.as1 to i32), 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoint_0_addrsize() { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoint_0_addrsize() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 ptrtoint (ptr addrspace(1) @g.as1 to i64), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp ne i64 ptrtoint (ptr addrspace(1) @g.as1 to i64), 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoaddr_ptrtoaddr() { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_ptrtoaddr() { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %cmp = icmp ne i64 ptrtoaddr (ptr @g to i64), ptrtoaddr (ptr @g2 to i64) | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoaddr_ptrtoaddr_addrsize() { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_ptrtoaddr_addrsize() { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %cmp = icmp ne i32 ptrtoaddr (ptr addrspace(1) @g.as1 to i32), ptrtoaddr (ptr addrspace(1) @g2.as1 to i32) | |
| ret i1 %cmp | |
| } | |
| ; This could still be folded because the address being non-equal also implies | |
| ; that all pointer bits together are non-equal. | |
| define i1 @icmp_ptrtoint_ptrtoint_addrsize() { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoint_ptrtoint_addrsize() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 ptrtoint (ptr addrspace(1) @g.as1 to i64), ptrtoint (ptr addrspace(1) @g2.as1 to i64) | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp ne i64 ptrtoint (ptr addrspace(1) @g.as1 to i64), ptrtoint (ptr addrspace(1) @g2.as1 to i64) | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_relational_ptrtoaddr_ptrtoaddr() { | |
| ; CHECK-LABEL: define i1 @icmp_relational_ptrtoaddr_ptrtoaddr() { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %cmp = icmp ult i64 ptrtoaddr (ptr @g to i64), ptrtoaddr (ptr getelementptr inbounds (i8, ptr @g, i64 1) to i64) | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_relational_ptrtoaddr_ptrtoaddr_addrsize() { | |
| ; CHECK-LABEL: define i1 @icmp_relational_ptrtoaddr_ptrtoaddr_addrsize() { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %cmp = icmp ult i32 ptrtoaddr (ptr addrspace(1) @g.as1 to i32), ptrtoaddr (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @g.as1, i32 1) to i32) | |
| ret i1 %cmp | |
| } | |
| ; This could still be folded because we know that the non-address bits must be | |
| ; the same, as GEP does not modify them. | |
| define i1 @icmp_relational_ptrtoint_ptrtoint_addrsize() { | |
| ; CHECK-LABEL: define i1 @icmp_relational_ptrtoint_ptrtoint_addrsize() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 ptrtoint (ptr addrspace(1) @g.as1 to i64), ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @g.as1, i64 1) to i64) | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp ult i64 ptrtoint (ptr addrspace(1) @g.as1 to i64), ptrtoint (ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @g.as1, i64 1) to i64) | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoaddr_ptrtoaddr_dyn(ptr %a) { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_ptrtoaddr_dyn( | |
| ; CHECK-SAME: ptr [[A:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %gep = getelementptr i8, ptr %a, i64 1 | |
| %a.addr = ptrtoaddr ptr %a to i64 | |
| %gep.addr = ptrtoaddr ptr %gep to i64 | |
| %cmp = icmp ne i64 %a.addr, %gep.addr | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoaddr_ptrtoaddr_dyn_addrsize(ptr addrspace(1) %a) { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_ptrtoaddr_dyn_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) [[A:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %gep = getelementptr i8, ptr addrspace(1) %a, i32 1 | |
| %a.addr = ptrtoaddr ptr addrspace(1) %a to i32 | |
| %gep.addr = ptrtoaddr ptr addrspace(1) %gep to i32 | |
| %cmp = icmp ne i32 %a.addr, %gep.addr | |
| ret i1 %cmp | |
| } | |
| ; This could still be folded, because the non-address bits being non-equal | |
| ; implies that all bits taken together are also non-equal. | |
| define i1 @icmp_ptrtoint_ptrtoint_dyn_addrsize(ptr addrspace(1) %a) { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoint_ptrtoint_dyn_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) [[A:%.*]]) { | |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr addrspace(1) [[A]], i32 1 | |
| ; CHECK-NEXT: [[A_ADDR:%.*]] = ptrtoint ptr addrspace(1) [[A]] to i64 | |
| ; CHECK-NEXT: [[GEP_ADDR:%.*]] = ptrtoint ptr addrspace(1) [[GEP]] to i64 | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[A_ADDR]], [[GEP_ADDR]] | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %gep = getelementptr i8, ptr addrspace(1) %a, i32 1 | |
| %a.addr = ptrtoint ptr addrspace(1) %a to i64 | |
| %gep.addr = ptrtoint ptr addrspace(1) %gep to i64 | |
| %cmp = icmp ne i64 %a.addr, %gep.addr | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoaddr_null_dyn(ptr nonnull %a) { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_null_dyn( | |
| ; CHECK-SAME: ptr nonnull [[A:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %a.addr = ptrtoaddr ptr %a to i64 | |
| %cmp = icmp ne i64 %a.addr, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoaddr_null_dyn_addrsize(ptr addrspace(1) nonnull %a) { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoaddr_null_dyn_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) nonnull [[A:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %a.addr = ptrtoaddr ptr addrspace(1) %a to i32 | |
| %cmp = icmp ne i32 %a.addr, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @icmp_ptrtoint_null_dyn_addrsize(ptr addrspace(1) nonnull %a) { | |
| ; CHECK-LABEL: define i1 @icmp_ptrtoint_null_dyn_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) nonnull [[A:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %a.addr = ptrtoint ptr addrspace(1) %a to i64 | |
| %cmp = icmp ne i64 %a.addr, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @non_zero_ptrtoaddr(ptr nonnull %p, i64 %unknown) { | |
| ; CHECK-LABEL: define i1 @non_zero_ptrtoaddr( | |
| ; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[UNKNOWN:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %p.addr = ptrtoaddr ptr %p to i64 | |
| %or = or i64 %p.addr, %unknown | |
| %cmp = icmp ne i64 %or, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @non_zero_ptrtoaddr_addrsize(ptr addrspace(1) nonnull %p, i32 %unknown) { | |
| ; CHECK-LABEL: define i1 @non_zero_ptrtoaddr_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) nonnull [[P:%.*]], i32 [[UNKNOWN:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %p.addr = ptrtoaddr ptr addrspace(1) %p to i32 | |
| %or = or i32 %p.addr, %unknown | |
| %cmp = icmp ne i32 %or, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @non_zero_ptrtoint_equal_addrsize(ptr addrspace(1) nonnull %p, i32 %unknown) { | |
| ; CHECK-LABEL: define i1 @non_zero_ptrtoint_equal_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) nonnull [[P:%.*]], i32 [[UNKNOWN:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %p.addr = ptrtoint ptr addrspace(1) %p to i32 | |
| %or = or i32 %p.addr, %unknown | |
| %cmp = icmp ne i32 %or, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @non_zero_ptrtoint_larger_addrsize(ptr addrspace(1) nonnull %p, i64 %unknown) { | |
| ; CHECK-LABEL: define i1 @non_zero_ptrtoint_larger_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) nonnull [[P:%.*]], i64 [[UNKNOWN:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %p.addr = ptrtoint ptr addrspace(1) %p to i64 | |
| %or = or i64 %p.addr, %unknown | |
| %cmp = icmp ne i64 %or, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @non_zero_ptrtoint_smaller_addrsize(ptr addrspace(1) nonnull %p, i16 %unknown) { | |
| ; CHECK-LABEL: define i1 @non_zero_ptrtoint_smaller_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) nonnull [[P:%.*]], i16 [[UNKNOWN:%.*]]) { | |
| ; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoint ptr addrspace(1) [[P]] to i16 | |
| ; CHECK-NEXT: [[OR:%.*]] = or i16 [[P_ADDR]], [[UNKNOWN]] | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i16 [[OR]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %p.addr = ptrtoint ptr addrspace(1) %p to i16 | |
| %or = or i16 %p.addr, %unknown | |
| %cmp = icmp ne i16 %or, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @ptrtoaddr_diff_non_equal(ptr %p0, ptr %p1) { | |
| ; CHECK-LABEL: define i1 @ptrtoaddr_diff_non_equal( | |
| ; CHECK-SAME: ptr [[P0:%.*]], ptr [[P1:%.*]]) { | |
| ; CHECK-NEXT: [[I0:%.*]] = ptrtoaddr ptr [[P0]] to i64 | |
| ; CHECK-NEXT: [[I1:%.*]] = ptrtoaddr ptr [[P1]] to i64 | |
| ; CHECK-NEXT: [[DIFF:%.*]] = sub i64 [[I0]], [[I1]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[DIFF]], 12 | |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %i0 = ptrtoaddr ptr %p0 to i64 | |
| %i1 = ptrtoaddr ptr %p1 to i64 | |
| %diff = sub i64 %i0, %i1 | |
| %cond = icmp eq i64 %diff, 12 | |
| call void @llvm.assume(i1 %cond) | |
| %cmp = icmp eq ptr %p0, %p1 | |
| ret i1 %cmp | |
| } | |
| define i1 @ptrtoaddr_diff_non_equal_addrsize(ptr addrspace(1) %p0, ptr addrspace(1) %p1) { | |
| ; CHECK-LABEL: define i1 @ptrtoaddr_diff_non_equal_addrsize( | |
| ; CHECK-SAME: ptr addrspace(1) [[P0:%.*]], ptr addrspace(1) [[P1:%.*]]) { | |
| ; CHECK-NEXT: [[I0:%.*]] = ptrtoaddr ptr addrspace(1) [[P0]] to i32 | |
| ; CHECK-NEXT: [[I1:%.*]] = ptrtoaddr ptr addrspace(1) [[P1]] to i32 | |
| ; CHECK-NEXT: [[DIFF:%.*]] = sub i32 [[I0]], [[I1]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[DIFF]], 12 | |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %i0 = ptrtoaddr ptr addrspace(1) %p0 to i32 | |
| %i1 = ptrtoaddr ptr addrspace(1) %p1 to i32 | |
| %diff = sub i32 %i0, %i1 | |
| %cond = icmp eq i32 %diff, 12 | |
| call void @llvm.assume(i1 %cond) | |
| %cmp = icmp eq ptr addrspace(1) %p0, %p1 | |
| ret i1 %cmp | |
| } | |
| define i1 @ptrtoaddr_diff_non_equal_addrsize_commuted(ptr addrspace(1) %p0, ptr addrspace(1) %p1) { | |
| ; CHECK-LABEL: define i1 @ptrtoaddr_diff_non_equal_addrsize_commuted( | |
| ; CHECK-SAME: ptr addrspace(1) [[P0:%.*]], ptr addrspace(1) [[P1:%.*]]) { | |
| ; CHECK-NEXT: [[I0:%.*]] = ptrtoaddr ptr addrspace(1) [[P0]] to i32 | |
| ; CHECK-NEXT: [[I1:%.*]] = ptrtoaddr ptr addrspace(1) [[P1]] to i32 | |
| ; CHECK-NEXT: [[DIFF:%.*]] = sub i32 [[I0]], [[I1]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[DIFF]], 12 | |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %i0 = ptrtoaddr ptr addrspace(1) %p0 to i32 | |
| %i1 = ptrtoaddr ptr addrspace(1) %p1 to i32 | |
| %diff = sub i32 %i0, %i1 | |
| %cond = icmp eq i32 %diff, 12 | |
| call void @llvm.assume(i1 %cond) | |
| %cmp = icmp eq ptr addrspace(1) %p1, %p0 | |
| ret i1 %cmp | |
| } | |
| ; An offset that stays within the object cannot produce a null address, because | |
| ; no allocated object crosses the unsigned address space boundary. | |
| define i1 @addr_plus_offset_inrange() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_inrange() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @g5 to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; The address one past the end of an object is covered by the same guarantee. | |
| define i1 @addr_plus_offset_one_past_end() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_one_past_end() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @g5 to i64), i64 20), 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @addr_plus_offset_inrange_ne() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_inrange_ne() { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %cmp = icmp ne i64 add (i64 ptrtoaddr (ptr @g5 to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; A declaration's type still gives its size. | |
| define i1 @addr_plus_offset_declaration() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_declaration() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @g5.decl to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: nothing is known beyond the end of the object. | |
| define i1 @addr_plus_offset_past_end() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_past_end() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 add (i64 ptrtoaddr (ptr @g5 to i64), i64 21), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @g5 to i64), i64 21), 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: an extern_weak symbol may be null. | |
| define i1 @addr_plus_offset_extern_weak() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_extern_weak() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 add (i64 ptrtoaddr (ptr @g5.weak to i64), i64 15), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @g5.weak to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; No allocated object crosses the address space boundary in any address space, | |
| ; so the fold does not need null to be an invalid address. | |
| define i1 @addr_plus_offset_non_zero_as() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_non_zero_as() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i32 add (i32 ptrtoaddr (ptr addrspace(1) @g5.as1 to i32), i32 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; Likewise, an object that starts at the zero address is all that | |
| ; null_pointer_is_valid permits, and a non-zero offset into it is still | |
| ; non-zero. | |
| define i1 @addr_plus_offset_null_pointer_is_valid() null_pointer_is_valid { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_null_pointer_is_valid( | |
| ; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @g5 to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; The same holds for objects that are not constants. | |
| define i1 @addr_plus_offset_alloca() { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_alloca() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %a = alloca [5 x i32], align 1 | |
| %addr = ptrtoaddr ptr %a to i64 | |
| %sum = add i64 %addr, 16 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @addr_plus_offset_dereferenceable(ptr dereferenceable(20) %p) { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_dereferenceable( | |
| ; CHECK-SAME: ptr dereferenceable(20) [[P:%.*]]) { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %addr = ptrtoaddr ptr %p to i64 | |
| %sum = add i64 %addr, 16 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @addr_plus_offset_dereferenceable_past_end(ptr dereferenceable(20) %p) { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_dereferenceable_past_end( | |
| ; CHECK-SAME: ptr dereferenceable(20) [[P:%.*]]) { | |
| ; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64 | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i64 [[ADDR]], 21 | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUM]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %addr = ptrtoaddr ptr %p to i64 | |
| %sum = add i64 %addr, 21 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| ; The same reasoning applies to ptrtoint when its result is the address. | |
| define i1 @int_plus_offset_inrange() { | |
| ; CHECK-LABEL: define i1 @int_plus_offset_inrange() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoint (ptr @g5 to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: a truncated ptrtoint is not an address, so it may wrap to zero. | |
| define i1 @trunc_int_plus_offset_inrange() { | |
| ; CHECK-LABEL: define i1 @trunc_int_plus_offset_inrange() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 add (i32 ptrtoint (ptr @g5 to i32), i32 15), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp eq i32 add (i32 ptrtoint (ptr @g5 to i32), i32 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: the pointer may be null, so there may be no object at all. | |
| define i1 @addr_plus_offset_maybe_null(ptr dereferenceable_or_null(20) %p) { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_maybe_null( | |
| ; CHECK-SAME: ptr dereferenceable_or_null(20) [[P:%.*]]) { | |
| ; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64 | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i64 [[ADDR]], 16 | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUM]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %addr = ptrtoaddr ptr %p to i64 | |
| %sum = add i64 %addr, 16 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: nothing is known about the size of the object. | |
| define i1 @addr_plus_offset_unknown_size(ptr %p) { | |
| ; CHECK-LABEL: define i1 @addr_plus_offset_unknown_size( | |
| ; CHECK-SAME: ptr [[P:%.*]]) { | |
| ; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64 | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i64 [[ADDR]], 16 | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUM]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %addr = ptrtoaddr ptr %p to i64 | |
| %sum = add i64 %addr, 16 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| ; ptrtoint of a non-constant pointer works the same way. | |
| define i1 @int_plus_offset_dereferenceable(ptr dereferenceable(20) %p) { | |
| ; CHECK-LABEL: define i1 @int_plus_offset_dereferenceable( | |
| ; CHECK-SAME: ptr dereferenceable(20) [[P:%.*]]) { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %addr = ptrtoint ptr %p to i64 | |
| %sum = add i64 %addr, 16 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: a truncated ptrtoint is not an address and may wrap to zero. | |
| define i1 @trunc_int_plus_offset(ptr dereferenceable(20) %p) { | |
| ; CHECK-LABEL: define i1 @trunc_int_plus_offset( | |
| ; CHECK-SAME: ptr dereferenceable(20) [[P:%.*]]) { | |
| ; CHECK-NEXT: [[ADDR:%.*]] = ptrtoint ptr [[P]] to i32 | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i32 [[ADDR]], 16 | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SUM]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %addr = ptrtoint ptr %p to i32 | |
| %sum = add i32 %addr, 16 | |
| %cmp = icmp eq i32 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| ; A ptrtoint wider than the address keeps the address in its low bits, and any | |
| ; bits above it can only make the result more non-zero. Here the address size | |
| ; is 32 and the pointer representation is 64 bits wide. | |
| define i1 @wide_int_plus_offset_non_zero_as() { | |
| ; CHECK-LABEL: define i1 @wide_int_plus_offset_non_zero_as() { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoint (ptr addrspace(1) @g5.as1 to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @wide_int_plus_offset_dereferenceable(ptr addrspace(1) dereferenceable(20) %p) { | |
| ; CHECK-LABEL: define i1 @wide_int_plus_offset_dereferenceable( | |
| ; CHECK-SAME: ptr addrspace(1) dereferenceable(20) [[P:%.*]]) { | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| %addr = ptrtoint ptr addrspace(1) %p to i64 | |
| %sum = add i64 %addr, 16 | |
| %cmp = icmp eq i64 %sum, 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: an alias may resolve to a weak undefined symbol, and thus to the | |
| ; null address, so nothing is known about the object it designates. | |
| define i1 @addr_of_alias_eq_zero() { | |
| ; CHECK-LABEL: define i1 @addr_of_alias_eq_zero() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 ptrtoaddr (ptr @alias to i64), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp eq i64 ptrtoaddr (ptr @alias to i64), 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @int_of_alias_ne_zero() { | |
| ; CHECK-LABEL: define i1 @int_of_alias_ne_zero() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 ptrtoint (ptr @alias to i64), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp ne i64 ptrtoint (ptr @alias to i64), 0 | |
| ret i1 %cmp | |
| } | |
| define i1 @alias_plus_offset() { | |
| ; CHECK-LABEL: define i1 @alias_plus_offset() { | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 add (i64 ptrtoaddr (ptr @alias to i64), i64 15), 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %cmp = icmp eq i64 add (i64 ptrtoaddr (ptr @alias to i64), i64 15), 0 | |
| ret i1 %cmp | |
| } | |
| ; Negative: a vector of pointers has no single object to take the size of. | |
| define <2 x i1> @vec_addr_plus_offset(<2 x ptr> %p) { | |
| ; CHECK-LABEL: define <2 x i1> @vec_addr_plus_offset( | |
| ; CHECK-SAME: <2 x ptr> [[P:%.*]]) { | |
| ; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr <2 x ptr> [[P]] to <2 x i64> | |
| ; CHECK-NEXT: [[SUM:%.*]] = add <2 x i64> [[ADDR]], splat (i64 16) | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i64> [[SUM]], zeroinitializer | |
| ; CHECK-NEXT: ret <2 x i1> [[CMP]] | |
| ; | |
| %addr = ptrtoaddr <2 x ptr> %p to <2 x i64> | |
| %sum = add <2 x i64> %addr, splat (i64 16) | |
| %cmp = icmp eq <2 x i64> %sum, zeroinitializer | |
| ret <2 x i1> %cmp | |
| } |