Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/runtime.jl
Original file line number Diff line number Diff line change
Expand Up @@ -200,18 +200,20 @@ const tag_size = sizeof(tag_type)

const gc_bits = 0x3 # FIXME

# get the type tag of a type at run-time
@llvmgenerated builder function type_tag(::Val{type_name})::tag_type where type_name
T_tag = convert(LLVMType, tag_type)
T_ptag = LLVM.PointerType(T_tag)
T_pjlvalue = convert(LLVMType, Any; allow_boxed=true)

# this isn't really a function, but we abuse it to get the JIT to resolve the address
typ = LLVM.Function(current_module(builder), "jl_" * String(type_name::Symbol) * "_type",
LLVM.FunctionType(T_pjlvalue))

typ_var = bitcast!(builder, typ, T_ptag)
load!(builder, T_tag, typ_var)
# the type tag of a boxed `T`, as Julia writes it: types in `jl_small_typeof` are tagged with
# a small constant, fixed for a given build of Julia, and other types with the address of
# their type object, which is only known at run time. returns an expression for the latter.
function type_tag(@nospecialize(T::Type))
obj = Ref{Any}(zero(T))
tag = GC.@preserve obj begin
ptr = unsafe_load(convert(Ptr{Ptr{tag_type}}, pointer_from_objref(obj)))
unsafe_load(ptr - tag_size) & ~tag_type(15)
end
if tag >= tag_type(64 << 4) # jl_max_tags << 4
return :(reinterpret(tag_type, pointer_from_objref($T)))
else
return tag
end
end

# we use `jl_value_ptr`, a Julia pseudo-intrinsic that can be used to box and unbox values
Expand All @@ -220,9 +222,7 @@ end
sz = sizeof(val)
allocsz = sz + tag_size

# type-tags are ephemeral, so look them up at run time
#tag = unsafe_load(convert(Ptr{tag_type}, type_name))
tag = :( type_tag(Val(type_name)) )
tag = type_tag(val)

quote
ptr = malloc($(Csize_t(allocsz)))
Expand Down
24 changes: 24 additions & 0 deletions test/native.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import InteractiveUtils

@testset "reflection" begin
mod = @eval module $(gensym())
f(x::Int) = x
Expand Down Expand Up @@ -609,6 +611,28 @@ end
end
end

@testset "runtime boxing on the host" begin
# sysimage builds and package images compile the runtime's boxing functions for the
# host, so they can't refer to type objects by symbol: on Windows, these data
# symbols don't link.
for (T, t) in [Int8 => :int8, Int16 => :int16, Int32 => :int32, Int64 => :int64,
UInt8 => :uint8, UInt16 => :uint16, UInt32 => :uint32,
UInt64 => :uint64, Bool => :bool, Float32 => :float32,
Float64 => :float64]
f = getfield(GPUCompiler.Runtime, Symbol("box_$t"))
ir = sprint(io->InteractiveUtils.code_llvm(io, f, Tuple{T}; dump_module=true))
@test !occursin(r"@jl_\w+_type\b", ir)

# the tag is the one Julia writes
tag = GPUCompiler.Runtime.type_tag(T)
if tag isa Expr
@test Core.eval(GPUCompiler.Runtime, tag) == UInt(pointer_from_objref(T))
else
@test GPUCompiler.small_typeof(tag) == Some(T)
end
end
end

@testset "boxed constant materialization" begin
# Since JuliaLang/julia#55045, isbits union constants stay boxed. Device jobs must
# embed copies instead of referring to GC-managed host boxes.
Expand Down
Loading