Skip to content
Draft
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
6 changes: 5 additions & 1 deletion src/driver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,11 @@ const __llvm_initialized = Ref(false)
finish_linked_module!(job, ir)

# Materialize isbits and Bool boxes; bake addresses for other objects.
portable = relocate_gvs!(ir, gv_to_value)
# Code running against the live Julia runtime must keep referring to the
# real GC-rooted objects: a materialized box is read-only module data, and
# the collector crashes if such a pointer reaches GC-tracked memory.
portable = relocate_gvs!(ir, gv_to_value;
materialize = !uses_julia_runtime(job))
portable || mark_session_dependent!(job)

if job.config.optimize
Expand Down
40 changes: 27 additions & 13 deletions src/jlgen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,8 @@ function compile_method_instance(@nospecialize(job::CompilerJob))
end

"""
relocate_gvs!(mod::LLVM.Module, gv_to_value::Dict{String, Ptr{Cvoid}})
relocate_gvs!(mod::LLVM.Module, gv_to_value::Dict{String, Ptr{Cvoid}};
materialize::Bool = true)

Resolve globals that refer to Julia objects. `jl_true`/`jl_false`, which are
absent from `gv_to_value`, become canonical module-local boxes. Ordinary
Expand All @@ -773,12 +774,20 @@ Apart from the dedicated Bool globals, GVs present in `mod` but missing from
`gv_to_value` remain declarations, which back-ends will reject loudly
(undefined symbol) rather than silently folding to null.

Pass `materialize = false` for code that executes against the live Julia
runtime (see `uses_julia_runtime`). A materialized box is a *replica* of the
object living in the module's read-only data, not a heap object: as soon as
such a pointer is stored into GC-tracked memory the collector will try to set
mark bits in it and crash. Host code therefore has to keep referring to the
real, GC-rooted object by its address.

Returns `true` if the module is session-portable afterwards: no absolute host
address was written (neither a baked slot nor a materialized header carrying
a non-smalltag type pointer).
"""
function relocate_gvs!(mod::LLVM.Module, gv_to_value::Dict{String, Ptr{Cvoid}})
portable = materialize_bool_singletons!(mod)
function relocate_gvs!(mod::LLVM.Module, gv_to_value::Dict{String, Ptr{Cvoid}};
materialize::Bool = true)
portable = materialize_bool_singletons!(mod; materialize)
mod_gvs = globals(mod)
for (name, init) in gv_to_value
# Bools are resolved by name above.
Expand All @@ -796,11 +805,10 @@ function relocate_gvs!(mod::LLVM.Module, gv_to_value::Dict{String, Ptr{Cvoid}})
val = nothing
if init != C_NULL
obj = Base.unsafe_pointer_to_objref(init)
# Zero-sized objects remain identity tokens. Type objects also stay
# identity tokens: some are isbits singletons (e.g. `Union{}`, whose
# `typeof` is the isbits singleton `Core.TypeofBottom`), but `sizeof`
# is undefined for uninhabited types and would throw here.
if isbitstype(typeof(obj)) && !(obj isa Type) && sizeof(obj) > 0 && !(obj isa Bool)
# Zero-sized objects remain identity tokens. Type objects (e.g.
# `Union{}`, whose `Core.TypeofBottom` is an isbits singleton) also
# stay identity tokens: `sizeof` is undefined for uninhabited types.
if materialize && isbitstype(typeof(obj)) && !(obj isa Type) && sizeof(obj) > 0 && !(obj isa Bool)
val, hdr = materialize_box!(mod, gv, obj, init)
# non-smalltag headers carry a host DataType pointer
portable &= hdr < UInt(64 << 4) # jl_max_tags << 4
Expand All @@ -819,7 +827,9 @@ function relocate_gvs!(mod::LLVM.Module, gv_to_value::Dict{String, Ptr{Cvoid}})
end

# Bool JuliaVariables are absent from `gv_to_value`; define one device box per name.
function materialize_bool_singletons!(mod::LLVM.Module)
# With `materialize = false` the host's `jl_true`/`jl_false` are referenced directly
# instead, so that the pointers stay GC-legal.
function materialize_bool_singletons!(mod::LLVM.Module; materialize::Bool = true)
portable = true
mod_gvs = globals(mod)
for (name, obj) in ("jl_true" => true, "jl_false" => false)
Expand All @@ -833,13 +843,17 @@ function materialize_bool_singletons!(mod::LLVM.Module)
end

init = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj)
val, hdr = materialize_box!(mod, gv, obj, init)
if materialize
val, hdr = materialize_box!(mod, gv, obj, init)
# Stay conservative if Bool stops using a smalltag.
portable &= hdr < UInt(64 << 4) # jl_max_tags << 4
else
val = const_inttoptr(ConstantInt(Int64(init)), global_value_type(gv))
portable = false
end
initializer!(gv, val)
constant!(gv, true)
linkage!(gv, LLVM.API.LLVMPrivateLinkage)

# Stay conservative if Bool stops using a smalltag.
portable &= hdr < UInt(64 << 4) # jl_max_tags << 4
end
return portable
end
Expand Down
3 changes: 2 additions & 1 deletion src/rtlib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ function emit_function!(mod, config::CompilerConfig, source::MethodInstance, met
# Resolve constgv mappings before their metadata is discarded. Dedicated Bool
# globals are resolved after linking into the toplevel module.
if !isempty(meta.gv_to_value)
portable = relocate_gvs!(new_mod, meta.gv_to_value)
portable = relocate_gvs!(new_mod, meta.gv_to_value;
materialize = !uses_julia_runtime(rt_job))
portable || mark_session_dependent!(rt_job)
end

Expand Down
Loading