Alexander Richardson 3a4b351ba1
[IR] Introduce the ptrtoaddr instruction
This introduces a new `ptrtoaddr` instruction which is similar to
`ptrtoint` but has two differences:

1) Unlike `ptrtoint`, `ptrtoaddr` does not capture provenance
2) `ptrtoaddr` only extracts (and then extends/truncates) the low
   index-width bits of the pointer

For most architectures, difference 2) does not matter since index (address)
width and pointer representation width are the same, but this does make a
difference for architectures that have pointers that aren't just plain
integer addresses such as AMDGPU fat pointers or CHERI capabilities.

This commit introduces textual and bitcode IR support as well as basic code
generation, but optimization passes do not handle the new instruction yet
so it may result in worse code than using ptrtoint. Follow-up changes will
update capture tracking, etc. for the new instruction.

RFC: https://discourse.llvm.org/t/clarifiying-the-semantics-of-ptrtoint/83987/54

Reviewed By: nikic

Pull Request: https://github.com/llvm/llvm-project/pull/139357
2025-08-08 10:12:39 -07:00

114 lines
3.0 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=x86_64-linux-gnu < %s -o - | FileCheck %s --check-prefix=CHECK
define i1 @ptrtoaddr_1(ptr %p) {
; CHECK-LABEL: ptrtoaddr_1:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: xorb $1, %al
; CHECK-NEXT: # kill: def $al killed $al killed $rax
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr ptr %p to i64
%trunc = trunc i64 %addr to i1
%ret = xor i1 %trunc, 1
ret i1 %ret
}
define i8 @ptrtoaddr_8(ptr %p) {
; CHECK-LABEL: ptrtoaddr_8:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: notb %al
; CHECK-NEXT: # kill: def $al killed $al killed $rax
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr ptr %p to i64
%trunc = trunc i64 %addr to i8
%ret = xor i8 %trunc, -1
ret i8 %ret
}
define i16 @ptrtoaddr_16(ptr %p) {
; CHECK-LABEL: ptrtoaddr_16:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: notl %eax
; CHECK-NEXT: # kill: def $ax killed $ax killed $rax
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr ptr %p to i64
%trunc = trunc i64 %addr to i16
%ret = xor i16 %trunc, -1
ret i16 %ret
}
define i32 @ptrtoaddr_32(ptr %p) {
; CHECK-LABEL: ptrtoaddr_32:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: notl %eax
; CHECK-NEXT: # kill: def $eax killed $eax killed $rax
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr ptr %p to i64
%trunc = trunc i64 %addr to i32
%ret = xor i32 %trunc, -1
ret i32 %ret
}
define i64 @ptrtoaddr_64(ptr %p) {
; CHECK-LABEL: ptrtoaddr_64:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: notq %rax
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr ptr %p to i64
%ret = xor i64 %addr, -1
ret i64 %ret
}
define i128 @ptrtoaddr_128(ptr %p) {
; CHECK-LABEL: ptrtoaddr_128:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: notq %rax
; CHECK-NEXT: movq $-1, %rdx
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr ptr %p to i64
%ext = zext i64 %addr to i128
%ret = xor i128 %ext, -1
ret i128 %ret
}
define <2 x i64> @ptrtoaddr_vec(<2 x ptr> %p) {
; CHECK-LABEL: ptrtoaddr_vec:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pcmpeqd %xmm1, %xmm1
; CHECK-NEXT: pxor %xmm1, %xmm0
; CHECK-NEXT: retq
entry:
%addr = ptrtoaddr <2 x ptr> %p to <2 x i64>
%ret = xor <2 x i64> %addr, <i64 -1, i64 -1>
ret <2 x i64> %ret
}
; UTC_ARGS: --disable
@foo = global [16 x i8] zeroinitializer
@addr = global i64 ptrtoaddr (ptr @foo to i64)
; CHECK: addr:
; CHECK-NEXT: .quad foo
; CHECK-NEXT: .size addr, 8
@addr_plus_one = global i64 ptrtoaddr (ptr getelementptr (i8, ptr @foo, i64 1) to i64)
; CHECK: addr_plus_one:
; CHECK-NEXT: .quad foo+1
; CHECK-NEXT: .size addr_plus_one, 8
@const_addr = global i64 ptrtoaddr (ptr getelementptr (i8, ptr null, i64 1) to i64)
; CHECK: const_addr:
; CHECK-NEXT: .quad 0+1
; CHECK-NEXT: .size const_addr, 8