Skip to content

Commit

Permalink
Switch LLVM codegen of Ptr{T} to an actual pointer type. (#53687)
Browse files Browse the repository at this point in the history
This PR switches our code generation for `Ptr{T}` from `i64` to an
actual LLVM pointer type (`ptr` when using opaque pointers, an untyped
`i8*` otherwise). The main motivation is to simplify `llvmcall` usage
(doing away with the `inttoptr`/`ptrtoint` conversions), and also make
it possible to simply use `ccall` to call intrinsics with `Ptr`-valued
arguments (where we currently always need `llvmcall` for converting to
an actual pointer).

Changing codegen like this is a breaking change for `llvmcall` users,
but I've added backwards compatibility and a deprecation warning.

Before:

```llvm
julia> @code_llvm pointer([])
define i64 @julia_pointer_1542(ptr noundef nonnull align 8 dereferenceable(24) %"x::Array") #0 {
top:
; ┌ @ pointer.jl:65 within `cconvert`
   %0 = load ptr, ptr %"x::Array", align 8
; └
; ┌ @ pointer.jl:90 within `unsafe_convert`
; │┌ @ pointer.jl:30 within `convert`
    %bitcast_coercion = ptrtoint ptr %0 to i64
    ret i64 %bitcast_coercion
; └└
}
```

After:

```llvm
julia> @code_llvm pointer([])
define ptr @julia_pointer_3880(ptr noundef nonnull align 8 dereferenceable(24) %"x::Array") #0 {
top:
; ┌ @ pointer.jl:65 within `cconvert`
   %0 = load ptr, ptr %"x::Array", align 8
; └
; ┌ @ pointer.jl:90 within `unsafe_convert`
; │┌ @ pointer.jl:30 within `convert`
    ret ptr %0
; └└
}
```

This also simplifies "real code", e.g., when `ccall` converts an Array
to a pointer, resulting in some more optimization opportunities.
  • Loading branch information
maleadt authored Mar 21, 2024
1 parent 8e8b533 commit 09400e4
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 97 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ Language changes
Compiler/Runtime improvements
-----------------------------

- Generated LLVM IR now uses actual pointer types instead of passing pointers as integers.
This affects `llvmcall`: Inline LLVM IR should be updated to use `i8*` or `ptr` instead of
`i32` or `i64`, and remove unneeded `ptrtoint`/`inttoptr` conversions. For compatibility,
IR with integer pointers is still supported, but generates a deprecation warning. ([#53687])

Command-line option changes
---------------------------

Expand Down
12 changes: 6 additions & 6 deletions base/atomics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,13 @@ for typ in atomictypes
irt = "$ilt, $ilt*"
@eval getindex(x::Atomic{$typ}) =
GC.@preserve x llvmcall($"""
%ptr = inttoptr i$WORD_SIZE %0 to $lt*
%ptr = bitcast i8* %0 to $lt*
%rv = load atomic $rt %ptr acquire, align $(gc_alignment(typ))
ret $lt %rv
""", $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x))
@eval setindex!(x::Atomic{$typ}, v::$typ) =
GC.@preserve x llvmcall($"""
%ptr = inttoptr i$WORD_SIZE %0 to $lt*
%ptr = bitcast i8* %0 to $lt*
store atomic $lt %1, $lt* %ptr release, align $(gc_alignment(typ))
ret void
""", Cvoid, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v)
Expand All @@ -379,7 +379,7 @@ for typ in atomictypes
if typ <: Integer
@eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) =
GC.@preserve x llvmcall($"""
%ptr = inttoptr i$WORD_SIZE %0 to $lt*
%ptr = bitcast i8* %0 to $lt*
%rs = cmpxchg $lt* %ptr, $lt %1, $lt %2 acq_rel acquire
%rv = extractvalue { $lt, i1 } %rs, 0
ret $lt %rv
Expand All @@ -388,7 +388,7 @@ for typ in atomictypes
else
@eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) =
GC.@preserve x llvmcall($"""
%iptr = inttoptr i$WORD_SIZE %0 to $ilt*
%iptr = bitcast i8* %0 to $ilt*
%icmp = bitcast $lt %1 to $ilt
%inew = bitcast $lt %2 to $ilt
%irs = cmpxchg $ilt* %iptr, $ilt %icmp, $ilt %inew acq_rel acquire
Expand All @@ -411,15 +411,15 @@ for typ in atomictypes
if typ <: Integer
@eval $fn(x::Atomic{$typ}, v::$typ) =
GC.@preserve x llvmcall($"""
%ptr = inttoptr i$WORD_SIZE %0 to $lt*
%ptr = bitcast i8* %0 to $lt*
%rv = atomicrmw $rmw $lt* %ptr, $lt %1 acq_rel
ret $lt %rv
""", $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v)
else
rmwop === :xchg || continue
@eval $fn(x::Atomic{$typ}, v::$typ) =
GC.@preserve x llvmcall($"""
%iptr = inttoptr i$WORD_SIZE %0 to $ilt*
%iptr = bitcast i8* %0 to $ilt*
%ival = bitcast $lt %1 to $ilt
%irv = atomicrmw $rmw $ilt* %iptr, $ilt %ival acq_rel
%rv = bitcast $ilt %irv to $lt
Expand Down
7 changes: 5 additions & 2 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ add_tfunc(sdiv_int, 2, 2, math_tfunc, 20)
add_tfunc(udiv_int, 2, 2, math_tfunc, 20)
add_tfunc(srem_int, 2, 2, math_tfunc, 20)
add_tfunc(urem_int, 2, 2, math_tfunc, 20)
add_tfunc(add_ptr, 2, 2, math_tfunc, 1)
add_tfunc(sub_ptr, 2, 2, math_tfunc, 1)
add_tfunc(neg_float, 1, 1, math_tfunc, 1)
add_tfunc(add_float, 2, 2, math_tfunc, 2)
add_tfunc(sub_float, 2, 2, math_tfunc, 2)
Expand Down Expand Up @@ -662,6 +660,9 @@ function pointer_eltype(@nospecialize(ptr))
return Any
end

@nospecs function pointerarith_tfunc(𝕃::AbstractLattice, ptr, offset)
return ptr
end
@nospecs function pointerref_tfunc(𝕃::AbstractLattice, a, i, align)
return pointer_eltype(a)
end
Expand Down Expand Up @@ -705,6 +706,8 @@ end
end
return ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T
end
add_tfunc(add_ptr, 2, 2, pointerarith_tfunc, 1)
add_tfunc(sub_ptr, 2, 2, pointerarith_tfunc, 1)
add_tfunc(pointerref, 3, 3, pointerref_tfunc, 4)
add_tfunc(pointerset, 4, 4, pointerset_tfunc, 5)
add_tfunc(atomic_fence, 1, 1, atomic_fence_tfunc, 4)
Expand Down
4 changes: 2 additions & 2 deletions base/pointer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ isless(x::Ptr{T}, y::Ptr{T}) where {T} = x < y
<(x::Ptr, y::Ptr) = UInt(x) < UInt(y)
-(x::Ptr, y::Ptr) = UInt(x) - UInt(y)

+(x::Ptr, y::Integer) = oftype(x, add_ptr(UInt(x), (y % UInt) % UInt))
-(x::Ptr, y::Integer) = oftype(x, sub_ptr(UInt(x), (y % UInt) % UInt))
+(x::Ptr, y::Integer) = add_ptr(x, (y % UInt) % UInt)
-(x::Ptr, y::Integer) = sub_ptr(x, (y % UInt) % UInt)
+(x::Integer, y::Ptr) = y + x

unsigned(x::Ptr) = UInt(x)
Expand Down
3 changes: 1 addition & 2 deletions base/task.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,7 @@ const _state_index = findfirst(==(:_state), fieldnames(Task))
@eval function load_state_acquire(t)
# TODO: Replace this by proper atomic operations when available
@GC.preserve t llvmcall($("""
%ptr = inttoptr i$(Sys.WORD_SIZE) %0 to i8*
%rv = load atomic i8, i8* %ptr acquire, align 8
%rv = load atomic i8, i8* %0 acquire, align 8
ret i8 %rv
"""), UInt8, Tuple{Ptr{UInt8}},
Ptr{UInt8}(pointer_from_objref(t) + fieldoffset(Task, _state_index)))
Expand Down
Loading

0 comments on commit 09400e4

Please sign in to comment.