This changes LangRef to specify that pointer icmp only compares the address bits of the pointers. That is, `icmp pred %a, %b` is equivalent to `icmp pred ptrtoaddr(%a), ptrtoaddr(%b)`. Similarly, it specifies that the `nonnull` attribute requires that the address bits are non-zero. There are a couple of motivations for this: * For inequality comparisons, this is really the only sensible semantics. Relational comparison of address and metadata bits as a single integer is generally meaningless (unless the metadata bits are equal). * This matches (as far as I understand) the behavior of existing CHERI implementations. * LLVM can only reason about the address bits. These semantics allow pointers with non-address bits to receive essentially the same comparison optimization support as ordinary pointers. In terms of implementation, this PR adjusts: * The AMDGPULowerBufferFatPointers pass. * An InstCombine fold that may replace pointers with different non-address bits. * The fold that replaces pointers based on dominating pointer equality. It does not adjust: * ISel, because we don't have in-tree targets where we can show a difference. * Various icmp+ptrtoint transforms, because we'll have to change this code for ptrtoaddr optimization support anyway, and these changes are tightly related. Related discussion starting from: https://discourse.llvm.org/t/clarifiying-the-semantics-of-ptrtoint/83987/60?u=nikic
312 lines
12 KiB
LLVM
312 lines
12 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
|
|
; RUN: opt -S -passes=instcombine < %s | FileCheck %s
|
|
|
|
; The ptrtoaddr folds are also valid for pointers that have external state.
|
|
target datalayout = "pe1:64:64:64:32"
|
|
|
|
declare void @use.i1(i1)
|
|
declare void @use.i32(i32)
|
|
declare void @use.i64(i64)
|
|
|
|
; ptrtoaddr result type is fixed, and can't be combined with integer cast.
|
|
define i32 @ptrtoaddr_trunc(ptr %p) {
|
|
; CHECK-LABEL: define i32 @ptrtoaddr_trunc(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
|
|
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i64 [[P_ADDR]] to i32
|
|
; CHECK-NEXT: ret i32 [[TRUNC]]
|
|
;
|
|
%p.addr = ptrtoaddr ptr %p to i64
|
|
%trunc = trunc i64 %p.addr to i32
|
|
ret i32 %trunc
|
|
}
|
|
|
|
define i128 @ptrtoaddr_zext(ptr %p) {
|
|
; CHECK-LABEL: define i128 @ptrtoaddr_zext(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
|
|
; CHECK-NEXT: [[EXT:%.*]] = zext i64 [[P_ADDR]] to i128
|
|
; CHECK-NEXT: ret i128 [[EXT]]
|
|
;
|
|
%p.addr = ptrtoaddr ptr %p to i64
|
|
%ext = zext i64 %p.addr to i128
|
|
ret i128 %ext
|
|
}
|
|
|
|
define i128 @ptrtoaddr_sext(ptr %p) {
|
|
; CHECK-LABEL: define i128 @ptrtoaddr_sext(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
|
|
; CHECK-NEXT: [[EXT:%.*]] = sext i64 [[P_ADDR]] to i128
|
|
; CHECK-NEXT: ret i128 [[EXT]]
|
|
;
|
|
%p.addr = ptrtoaddr ptr %p to i64
|
|
%ext = sext i64 %p.addr to i128
|
|
ret i128 %ext
|
|
}
|
|
|
|
define i64 @sub_ptrtoaddr(ptr %p, i64 %offset) {
|
|
; CHECK-LABEL: define i64 @sub_ptrtoaddr(
|
|
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: ret i64 [[OFFSET]]
|
|
;
|
|
%p2 = getelementptr i8, ptr %p, i64 %offset
|
|
%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 i64 @sub_ptrtoint_ptrtoaddr(ptr %p, i64 %offset) {
|
|
; CHECK-LABEL: define i64 @sub_ptrtoint_ptrtoaddr(
|
|
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: ret i64 [[OFFSET]]
|
|
;
|
|
%p2 = getelementptr i8, ptr %p, i64 %offset
|
|
%p.int = ptrtoint ptr %p to i64
|
|
%p2.addr = ptrtoaddr ptr %p2 to i64
|
|
%sub = sub i64 %p2.addr, %p.int
|
|
ret i64 %sub
|
|
}
|
|
|
|
define i32 @sub_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
|
|
; CHECK-LABEL: define i32 @sub_ptrtoaddr_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: ret i32 [[OFFSET]]
|
|
;
|
|
%p2 = getelementptr i8, ptr addrspace(1) %p, i32 %offset
|
|
%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 i32 @sub_trunc_ptrtoaddr(ptr %p, i64 %offset) {
|
|
; CHECK-LABEL: define i32 @sub_trunc_ptrtoaddr(
|
|
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[SUB:%.*]] = trunc i64 [[OFFSET]] to i32
|
|
; CHECK-NEXT: ret i32 [[SUB]]
|
|
;
|
|
%p2 = getelementptr i8, ptr %p, i64 %offset
|
|
%p.addr = ptrtoaddr ptr %p to i64
|
|
%p2.addr = ptrtoaddr ptr %p2 to i64
|
|
%p.addr.trunc = trunc i64 %p.addr to i32
|
|
%p2.addr.trunc = trunc i64 %p2.addr to i32
|
|
%sub = sub i32 %p2.addr.trunc, %p.addr.trunc
|
|
ret i32 %sub
|
|
}
|
|
|
|
define i16 @sub_trunc_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
|
|
; CHECK-LABEL: define i16 @sub_trunc_ptrtoaddr_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[SUB:%.*]] = trunc i32 [[OFFSET]] to i16
|
|
; CHECK-NEXT: ret i16 [[SUB]]
|
|
;
|
|
%p2 = getelementptr i8, ptr addrspace(1) %p, i32 %offset
|
|
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
|
|
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
|
|
%p.addr.trunc = trunc i32 %p.addr to i16
|
|
%p2.addr.trunc = trunc i32 %p2.addr to i16
|
|
%sub = sub i16 %p2.addr.trunc, %p.addr.trunc
|
|
ret i16 %sub
|
|
}
|
|
|
|
define i16 @sub_trunc_ptrtoint_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
|
|
; CHECK-LABEL: define i16 @sub_trunc_ptrtoint_ptrtoaddr_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[SUB:%.*]] = trunc i32 [[OFFSET]] to i16
|
|
; CHECK-NEXT: ret i16 [[SUB]]
|
|
;
|
|
%p2 = getelementptr i8, ptr addrspace(1) %p, i32 %offset
|
|
%p.int = ptrtoint ptr addrspace(1) %p to i64
|
|
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
|
|
%p.int.trunc = trunc i64 %p.int to i16
|
|
%p2.addr.trunc = trunc i32 %p2.addr to i16
|
|
%sub = sub i16 %p2.addr.trunc, %p.int.trunc
|
|
ret i16 %sub
|
|
}
|
|
|
|
define i128 @sub_zext_ptrtoaddr(ptr %p, i64 %offset) {
|
|
; CHECK-LABEL: define i128 @sub_zext_ptrtoaddr(
|
|
; CHECK-SAME: ptr [[P:%.*]], i64 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[SUB:%.*]] = zext i64 [[OFFSET]] to i128
|
|
; CHECK-NEXT: ret i128 [[SUB]]
|
|
;
|
|
%p2 = getelementptr nuw i8, ptr %p, i64 %offset
|
|
%p.addr = ptrtoaddr ptr %p to i64
|
|
%p2.addr = ptrtoaddr ptr %p2 to i64
|
|
%p.addr.ext = zext i64 %p.addr to i128
|
|
%p2.addr.ext = zext i64 %p2.addr to i128
|
|
%sub = sub i128 %p2.addr.ext, %p.addr.ext
|
|
ret i128 %sub
|
|
}
|
|
|
|
define i64 @sub_zext_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
|
|
; CHECK-LABEL: define i64 @sub_zext_ptrtoaddr_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[SUB:%.*]] = zext i32 [[OFFSET]] to i64
|
|
; CHECK-NEXT: ret i64 [[SUB]]
|
|
;
|
|
%p2 = getelementptr nuw i8, ptr addrspace(1) %p, i32 %offset
|
|
%p.addr = ptrtoaddr ptr addrspace(1) %p to i32
|
|
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
|
|
%p.addr.ext = zext i32 %p.addr to i64
|
|
%p2.addr.ext = zext i32 %p2.addr to i64
|
|
%sub = sub i64 %p2.addr.ext, %p.addr.ext
|
|
ret i64 %sub
|
|
}
|
|
|
|
define i128 @sub_zext_ptrtoint_ptrtoaddr_addrsize(ptr addrspace(1) %p, i32 %offset) {
|
|
; CHECK-LABEL: define i128 @sub_zext_ptrtoint_ptrtoaddr_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[P2:%.*]] = getelementptr nuw i8, ptr addrspace(1) [[P]], i32 [[OFFSET]]
|
|
; CHECK-NEXT: [[P_INT:%.*]] = ptrtoint ptr addrspace(1) [[P]] to i64
|
|
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[P2]] to i32
|
|
; CHECK-NEXT: [[P_INT_EXT:%.*]] = zext i64 [[P_INT]] to i128
|
|
; CHECK-NEXT: [[P2_ADDR_EXT:%.*]] = zext i32 [[P2_ADDR]] to i128
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i128 [[P2_ADDR_EXT]], [[P_INT_EXT]]
|
|
; CHECK-NEXT: ret i128 [[SUB]]
|
|
;
|
|
%p2 = getelementptr nuw i8, ptr addrspace(1) %p, i32 %offset
|
|
%p.int = ptrtoint ptr addrspace(1) %p to i64
|
|
%p2.addr = ptrtoaddr ptr addrspace(1) %p2 to i32
|
|
%p.int.ext = zext i64 %p.int to i128
|
|
%p2.addr.ext = zext i32 %p2.addr to i128
|
|
%sub = sub i128 %p2.addr.ext, %p.int.ext
|
|
ret i128 %sub
|
|
}
|
|
|
|
; The uses in icmp, ptrtoint, ptrtoaddr should be replaced. The one in the
|
|
; return value should not, as the provenance differs.
|
|
define ptr @gep_sub_ptrtoaddr_different_obj(ptr %p, ptr %p2, ptr %p3) {
|
|
; CHECK-LABEL: define ptr @gep_sub_ptrtoaddr_different_obj(
|
|
; CHECK-SAME: ptr [[P:%.*]], ptr [[P2:%.*]], ptr [[P3:%.*]]) {
|
|
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr [[P]] to i64
|
|
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr [[P2]] to i64
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[P2_ADDR]], [[P_ADDR]]
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[SUB]]
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P2]], [[P3]]
|
|
; CHECK-NEXT: call void @use.i1(i1 [[CMP]])
|
|
; CHECK-NEXT: [[INT:%.*]] = ptrtoint ptr [[P2]] to i64
|
|
; CHECK-NEXT: call void @use.i64(i64 [[INT]])
|
|
; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr ptr [[P2]] to i64
|
|
; CHECK-NEXT: call void @use.i64(i64 [[ADDR]])
|
|
; CHECK-NEXT: ret ptr [[GEP]]
|
|
;
|
|
%p.addr = ptrtoaddr ptr %p to i64
|
|
%p2.addr = ptrtoaddr ptr %p2 to i64
|
|
%sub = sub i64 %p2.addr, %p.addr
|
|
%gep = getelementptr i8, ptr %p, i64 %sub
|
|
%cmp = icmp eq ptr %gep, %p3
|
|
call void @use.i1(i1 %cmp)
|
|
%int = ptrtoint ptr %gep to i64
|
|
call void @use.i64(i64 %int)
|
|
%addr = ptrtoaddr ptr %gep to i64
|
|
call void @use.i64(i64 %addr)
|
|
ret ptr %gep
|
|
}
|
|
|
|
; The use in ptrtoaddr and icmp should be replaced. The use in ptrtoint should
|
|
; not be replaced, as the non-address bits differ. The use in the return value
|
|
; should not be replaced as the provenace differs.
|
|
define ptr addrspace(1) @gep_sub_ptrtoaddr_different_obj_addrsize(ptr addrspace(1) %p, ptr addrspace(1) %p2, ptr addrspace(1) %p3) {
|
|
; CHECK-LABEL: define ptr addrspace(1) @gep_sub_ptrtoaddr_different_obj_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], ptr addrspace(1) [[P2:%.*]], ptr addrspace(1) [[P3:%.*]]) {
|
|
; CHECK-NEXT: [[P_ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[P]] to i32
|
|
; CHECK-NEXT: [[P2_ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[P2]] to i32
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[P2_ADDR]], [[P_ADDR]]
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr addrspace(1) [[P]], i32 [[SUB]]
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr addrspace(1) [[P2]], [[P3]]
|
|
; CHECK-NEXT: call void @use.i1(i1 [[CMP]])
|
|
; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr addrspace(1) [[GEP]] to i64
|
|
; CHECK-NEXT: [[INT:%.*]] = trunc i64 [[TMP1]] to i32
|
|
; CHECK-NEXT: call void @use.i32(i32 [[INT]])
|
|
; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[P2]] to i32
|
|
; CHECK-NEXT: call void @use.i32(i32 [[ADDR]])
|
|
; CHECK-NEXT: ret ptr addrspace(1) [[GEP]]
|
|
;
|
|
%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
|
|
%gep = getelementptr i8, ptr addrspace(1) %p, i32 %sub
|
|
%cmp = icmp eq ptr addrspace(1) %gep, %p3
|
|
call void @use.i1(i1 %cmp)
|
|
%int = ptrtoint ptr addrspace(1) %gep to i32
|
|
call void @use.i32(i32 %int)
|
|
%addr = ptrtoaddr ptr addrspace(1) %gep to i32
|
|
call void @use.i32(i32 %addr)
|
|
ret ptr addrspace(1) %gep
|
|
}
|
|
|
|
define i64 @ptrtoaddr_of_ptrmask(ptr %p, i64 %mask) {
|
|
; CHECK-LABEL: define i64 @ptrtoaddr_of_ptrmask(
|
|
; CHECK-SAME: ptr [[P:%.*]], i64 [[MASK:%.*]]) {
|
|
; CHECK-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr [[P]] to i64
|
|
; CHECK-NEXT: [[ADDR:%.*]] = and i64 [[MASK]], [[TMP1]]
|
|
; CHECK-NEXT: ret i64 [[ADDR]]
|
|
;
|
|
%masked = call ptr @llvm.ptrmask(ptr %p, i64 %mask)
|
|
%addr = ptrtoaddr ptr %masked to i64
|
|
ret i64 %addr
|
|
}
|
|
|
|
define i32 @ptrtoaddr_of_ptrmask_addrsize(ptr addrspace(1) %p, i32 %mask) {
|
|
; CHECK-LABEL: define i32 @ptrtoaddr_of_ptrmask_addrsize(
|
|
; CHECK-SAME: ptr addrspace(1) [[P:%.*]], i32 [[MASK:%.*]]) {
|
|
; CHECK-NEXT: [[TMP1:%.*]] = ptrtoaddr ptr addrspace(1) [[P]] to i32
|
|
; CHECK-NEXT: [[ADDR:%.*]] = and i32 [[MASK]], [[TMP1]]
|
|
; CHECK-NEXT: ret i32 [[ADDR]]
|
|
;
|
|
%masked = call ptr addrspace(1) @llvm.ptrmask(ptr addrspace(1) %p, i32 %mask)
|
|
%addr = ptrtoaddr ptr addrspace(1) %masked to i32
|
|
ret i32 %addr
|
|
}
|
|
|
|
define i64 @ptrtoaddr_of_gep_of_inttoptr(i64 %int, i64 %offset) {
|
|
; CHECK-LABEL: define i64 @ptrtoaddr_of_gep_of_inttoptr(
|
|
; CHECK-SAME: i64 [[INT:%.*]], i64 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[ADDR:%.*]] = add i64 [[INT]], [[OFFSET]]
|
|
; CHECK-NEXT: ret i64 [[ADDR]]
|
|
;
|
|
%ptr = inttoptr i64 %int to ptr
|
|
%gep = getelementptr i8, ptr %ptr, i64 %offset
|
|
%addr = ptrtoaddr ptr %gep to i64
|
|
ret i64 %addr
|
|
}
|
|
|
|
; FIXME: This could be supported by truncating %int before performing the
|
|
; arithmetic.
|
|
define i32 @ptrtoaddr_of_gep_of_inttoptr_addrsize(i64 %int, i32 %offset) {
|
|
; CHECK-LABEL: define i32 @ptrtoaddr_of_gep_of_inttoptr_addrsize(
|
|
; CHECK-SAME: i64 [[INT:%.*]], i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: [[PTR:%.*]] = inttoptr i64 [[INT]] to ptr addrspace(1)
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr addrspace(1) [[PTR]], i32 [[OFFSET]]
|
|
; CHECK-NEXT: [[ADDR:%.*]] = ptrtoaddr ptr addrspace(1) [[GEP]] to i32
|
|
; CHECK-NEXT: ret i32 [[ADDR]]
|
|
;
|
|
%ptr = inttoptr i64 %int to ptr addrspace(1)
|
|
%gep = getelementptr i8, ptr addrspace(1) %ptr, i32 %offset
|
|
%addr = ptrtoaddr ptr addrspace(1) %gep to i32
|
|
ret i32 %addr
|
|
}
|
|
|
|
define i64 @ptrtoaddr_of_gep_of_null(i64 %offset) {
|
|
; CHECK-LABEL: define i64 @ptrtoaddr_of_gep_of_null(
|
|
; CHECK-SAME: i64 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: ret i64 [[OFFSET]]
|
|
;
|
|
%gep = getelementptr i8, ptr null, i64 %offset
|
|
%addr = ptrtoaddr ptr %gep to i64
|
|
ret i64 %addr
|
|
}
|
|
|
|
define i32 @ptrtoaddr_of_gep_of_null_addrsize(i32 %offset) {
|
|
; CHECK-LABEL: define i32 @ptrtoaddr_of_gep_of_null_addrsize(
|
|
; CHECK-SAME: i32 [[OFFSET:%.*]]) {
|
|
; CHECK-NEXT: ret i32 [[OFFSET]]
|
|
;
|
|
%gep = getelementptr i8, ptr addrspace(1) null, i32 %offset
|
|
%addr = ptrtoaddr ptr addrspace(1) %gep to i32
|
|
ret i32 %addr
|
|
}
|