diff --git a/src/runtime.jl b/src/runtime.jl index 205092bc..f169676f 100644 --- a/src/runtime.jl +++ b/src/runtime.jl @@ -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 @@ -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))) diff --git a/test/native.jl b/test/native.jl index 08953131..1daa5bb6 100644 --- a/test/native.jl +++ b/test/native.jl @@ -1,3 +1,5 @@ +import InteractiveUtils + @testset "reflection" begin mod = @eval module $(gensym()) f(x::Int) = x @@ -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.