Skip to content

Commit

Permalink
Updates for Julia 1.12 (#656)
Browse files Browse the repository at this point in the history
  • Loading branch information
maleadt authored Jan 10, 2025
1 parent b692685 commit ae520af
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 62 deletions.
70 changes: 40 additions & 30 deletions src/jlgen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,32 @@ end # HAS_INTEGRATED_CACHE
## codegen/inference integration

function ci_cache_populate(interp, cache, mi, min_world, max_world)
if VERSION >= v"1.12.0-DEV.15"
inferred_ci = CC.typeinf_ext_toplevel(interp, mi, CC.SOURCE_MODE_FORCE_SOURCE) # or SOURCE_MODE_FORCE_SOURCE_UNCACHED?
codeinfos = Pair{CodeInstance, CodeInfo}[]
@static if VERSION >= v"1.12.0-DEV.1434"
# see typeinfer.jl: typeinf_ext_toplevel
ci = CC.typeinf_ext(interp, mi, CC.SOURCE_MODE_NOT_REQUIRED)
inspected = IdSet{CodeInstance}()
tocompile = CodeInstance[ci]
while !isempty(tocompile)
callee = pop!(tocompile)
callee in inspected && continue
push!(inspected, callee)
# now make sure everything has source code, if desired
mi = CC.get_ci_mi(callee)
def = mi.def
if CC.use_const_api(callee)
src = CC.codeinfo_for_const(interp, mi, ci.rettype_const)
else
# TODO: typeinf_code could return something with different edges/ages/owner/abi (needing an update to callee), which we don't handle here
src = CC.typeinf_code(interp, mi, true)
end
if src isa CodeInfo
CC.collectinvokes!(tocompile, src)
push!(codeinfos, callee => src)
end
end
elseif VERSION >= v"1.12.0-DEV.15"
inferred_ci = CC.typeinf_ext_toplevel(interp, mi, CC.SOURCE_MODE_FORCE_SOURCE)
@assert inferred_ci !== nothing "Inference of $mi failed"

# inference should have populated our cache
Expand Down Expand Up @@ -512,13 +536,13 @@ function ci_cache_populate(interp, cache, mi, min_world, max_world)
end
end

return ci::CodeInstance
return codeinfos
end

function ci_cache_lookup(cache, mi, min_world, max_world)
wvc = WorldView(cache, min_world, max_world)
ci = CC.get(wvc, mi, nothing)
if ci !== nothing && ci.inferred === nothing
if VERSION < v"1.12.0-DEV.1434" && ci !== nothing && ci.inferred === nothing
# if for some reason we did end up with a codeinfo without inferred source, e.g.,
# because of calling `Base.return_types` which only sets rettyp, pretend we didn't
# run inference so that we re-infer now and not during codegen (which is disallowed)
Expand All @@ -543,23 +567,6 @@ end
CompilationPolicyExtern = 1
end

# HACK: in older versions of Julia, `jl_create_native` doesn't take a world argument
# but instead always generates code for the current world. note that this doesn't
# actually change the world age, but just spoofs the counter `jl_create_native` reads.
# XXX: Base.get_world_counter is supposed to be monotonically increasing and is runtime global.
macro in_world(world, ex)
quote
actual_world = Base.get_world_counter()
world_counter = cglobal(:jl_world_counter, Csize_t)
unsafe_store!(world_counter, $(esc(world)))
try
$(esc(ex))
finally
unsafe_store!(world_counter, actual_world)
end
end
end

"""
precompile(job::CompilerJob)
Expand All @@ -574,10 +581,7 @@ function Base.precompile(@nospecialize(job::CompilerJob))
# populate the cache
interp = get_interpreter(job)
cache = CC.code_cache(interp)
if ci_cache_lookup(cache, job.source, job.world, job.world) === nothing
ci_cache_populate(interp, cache, job.source, job.world, job.world)
return ci_cache_lookup(cache, job.source, job.world, job.world) !== nothing
end
ci_cache_populate(interp, cache, job.source, job.world, job.world)
return true
end

Expand All @@ -589,10 +593,7 @@ function compile_method_instance(@nospecialize(job::CompilerJob))
# populate the cache
interp = get_interpreter(job)
cache = CC.code_cache(interp)
if ci_cache_lookup(cache, job.source, job.world, job.world) === nothing
ci_cache_populate(interp, cache, job.source, job.world, job.world)
@assert ci_cache_lookup(cache, job.source, job.world, job.world) !== nothing
end
populated = ci_cache_populate(interp, cache, job.source, job.world, job.world)

# create a callback to look-up function in our cache,
# and keep track of the method instances we needed.
Expand Down Expand Up @@ -639,7 +640,16 @@ function compile_method_instance(@nospecialize(job::CompilerJob))
Metadata(ConstantInt(DEBUG_METADATA_VERSION()))
end

native_code = if VERSION >= v"1.12.0-DEV.1667"
native_code = if VERSION >= v"1.12.0-DEV.1823"
codeinfos = Any[]
for (ci, src) in populated
# each item in the list should be a CodeInstance followed by a CodeInfo
# indicating something to compile
push!(codeinfos, ci::CodeInstance)
push!(codeinfos, src::CodeInfo)
end
@ccall jl_emit_native(codeinfos::Vector{Any}, ts_mod::LLVM.API.LLVMOrcThreadSafeModuleRef, Ref(params)::Ptr{Base.CodegenParams}, #=extern linkage=# false::Cint)::Ptr{Cvoid}
elseif VERSION >= v"1.12.0-DEV.1667"
ccall(:jl_create_native, Ptr{Cvoid},
(Vector{MethodInstance}, LLVM.API.LLVMOrcThreadSafeModuleRef, Ptr{Base.CodegenParams}, Cint, Cint, Cint, Csize_t, Ptr{Cvoid}),
[job.source], ts_mod, Ref(params), CompilationPolicyExtern, #=imaging mode=# 0, #=external linkage=# 0, job.world, Base.unsafe_convert(Ptr{Nothing}, lookup_cb))
Expand Down
23 changes: 22 additions & 1 deletion src/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,30 @@ function check_ir!(job, errors::Vector{IRError}, inst::LLVM.CallInst)
@safe_debug "Decoding arguments to jl_reresolve_binding_value_seqcst failed" inst bb=LLVM.parent(inst)
push!(errors, (DELAYED_BINDING, bt, nothing))
end
elseif startswith(fn, "tojlinvoke")
try
fun, args, nargs = arguments(inst)
fun = first(operands(fun::ConstantExpr))::ConstantInt
fun = convert(Int, fun)
fun = Ptr{Cvoid}(fun)
fun = Base.unsafe_pointer_to_objref(fun)::Base.Function
push!(errors, (DYNAMIC_CALL, bt, fun))
# XXX: an invoke trampoline happens when codegen doesn't have access to code
# which suggests a GPUCompiler.jl bug. throw an error instead?
catch e
@safe_debug "Decoding arguments to jl_invoke failed" inst bb = LLVM.parent(inst)
push!(errors, (DYNAMIC_CALL, bt, nothing))
end
elseif fn == "jl_invoke" || fn == "ijl_invoke"
# most invokes are contained in a trampoline handled above,
# but some direct ones remain (e.g., with `@nospecialize`)
# XXX: this shouldn't be true on 1.12+ anymore; jl_invoke is always trampolined
caller = LLVM.parent(LLVM.parent(inst))
if startswith(LLVM.name(caller), "tojlinvoke")
return
end
try
f, args, nargs, meth = arguments(inst)
fun, args, nargs, meth = arguments(inst)
meth = first(operands(meth::ConstantExpr))::ConstantInt
meth = convert(Int, meth)
meth = Ptr{Cvoid}(meth)
Expand Down
16 changes: 8 additions & 8 deletions test/gcn_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ end
end

asm = sprint(io->GCN.code_native(io, parent, Tuple{Int64}; dump_module=true))
@test occursin(r"s_add_u32.*julia_child_.*@rel32@", asm)
@test occursin(r"s_addc_u32.*julia_child_.*@rel32@", asm)
@test occursin(r"s_add_u32.*(julia|j)_child_.*@rel32@", asm)
@test occursin(r"s_addc_u32.*(julia|j)_child_.*@rel32@", asm)
end

@testset "kernel functions" begin
Expand Down Expand Up @@ -89,10 +89,10 @@ end
end

asm = sprint(io->GCN.code_native(io, mod.parent1, Tuple{Int}; dump_module=true))
@test occursin(r"\.type.*julia_[[:alnum:]_.]*child_\d*,@function", asm)
@test occursin(r"\.type.*(julia|j)_[[:alnum:]_.]*child_\d*,@function", asm)

asm = sprint(io->GCN.code_native(io, mod.parent2, Tuple{Int}; dump_module=true))
@test occursin(r"\.type.*julia_[[:alnum:]_.]*child_\d*,@function", asm)
@test occursin(r"\.type.*(julia|j)_[[:alnum:]_.]*child_\d*,@function", asm)
end

@testset "child function reuse bis" begin
Expand All @@ -115,12 +115,12 @@ end
end

asm = sprint(io->GCN.code_native(io, mod.parent1, Tuple{Int}; dump_module=true))
@test occursin(r"\.type.*julia_[[:alnum:]_.]*child1_\d*,@function", asm)
@test occursin(r"\.type.*julia_[[:alnum:]_.]*child2_\d*,@function", asm)
@test occursin(r"\.type.*(julia|j)_[[:alnum:]_.]*child1_\d*,@function", asm)
@test occursin(r"\.type.*(julia|j)_[[:alnum:]_.]*child2_\d*,@function", asm)

asm = sprint(io->GCN.code_native(io, mod.parent2, Tuple{Int}; dump_module=true))
@test occursin(r"\.type.*julia_[[:alnum:]_.]*child1_\d*,@function", asm)
@test occursin(r"\.type.*julia_[[:alnum:]_.]*child2_\d*,@function", asm)
@test occursin(r"\.type.*(julia|j)_[[:alnum:]_.]*child1_\d*,@function", asm)
@test occursin(r"\.type.*(julia|j)_[[:alnum:]_.]*child2_\d*,@function", asm)
end

@testset "indirect sysimg function use" begin
Expand Down
16 changes: 8 additions & 8 deletions test/native_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ using Test
@test contains(ir, "MethodInstance for identity")

ir = sprint(io->GPUCompiler.code_llvm(io, job))
@test contains(ir, "julia_identity")
@test contains(ir, r"(julia|j)_identity")

asm = sprint(io->GPUCompiler.code_native(io, job))
@test contains(asm, "julia_identity")
@test contains(asm, r"(julia|j)_identity")
end

@testset "compilation" begin
Expand Down Expand Up @@ -187,7 +187,7 @@ end
ir = sprint(io->Native.code_llvm(io, valid_kernel, Tuple{}; optimize=false, dump_module=true))

# module should contain our function + a generic call wrapper
@test occursin(r"define\ .* void\ @.*julia_valid_kernel.*\(\)"x, ir)
@test occursin(r"define\ .* void\ @.*(julia|j)_valid_kernel.*\(\)"x, ir)
@test !occursin("define %jl_value_t* @jlcall_", ir)

# there should be no debug metadata
Expand All @@ -208,7 +208,7 @@ end
parent(i) = child(i)

ir = sprint(io->Native.code_llvm(io, parent, Tuple{Int}))
@test occursin(r"call .+ @julia.+child.+", ir)
@test occursin(r"call .+ @(julia|j).+child.+", ir)
end

@testset "sysimg" begin
Expand Down Expand Up @@ -295,18 +295,18 @@ end
end

ir = sprint(io->Native.code_llvm(io, mod.g, Tuple{Int64}; dump_module=true, kernel=true))
@test occursin(r"^define.*julia_f_expensive"m, ir)
@test occursin(r"^define.*(julia|j)_f_expensive"m, ir)

ir = sprint(io->Native.code_llvm(io, mod.g, Tuple{Int64}; dump_module=true, kernel=true,
always_inline=true))
@test !occursin(r"^define.*julia_f_expensive"m, ir)
@test !occursin(r"^define.*(julia|j)_f_expensive"m, ir)

ir = sprint(io->Native.code_llvm(io, mod.h, Tuple{Int64}; dump_module=true, kernel=true,
always_inline=true))
@test !occursin(r"^define.*julia_f_expensive"m, ir)
@test !occursin(r"^define.*(julia|j)_f_expensive"m, ir)

ir = sprint(io->Native.code_llvm(io, mod.h, Tuple{Int64}; dump_module=true, kernel=true))
@test occursin(r"^define.*julia_f_expensive"m, ir)
@test occursin(r"^define.*(julia|j)_f_expensive"m, ir)
end

@testset "function attributes" begin
Expand Down
28 changes: 14 additions & 14 deletions test/ptx_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ end
end

ir = sprint(io->PTX.code_llvm(io, mod.kernel, Tuple{mod.Aggregate}))
@test occursin(r"@julia_kernel\w*\(({ i64 }|\[1 x i64\])\* ", ir) ||
occursin(r"@julia_kernel\w*\(ptr ", ir)
@test occursin(r"@(julia|j)_kernel\w*\(({ i64 }|\[1 x i64\])\* ", ir) ||
occursin(r"@(julia|j)_kernel\w*\(ptr ", ir)

ir = sprint(io->PTX.code_llvm(io, mod.kernel, Tuple{mod.Aggregate}; kernel=true))
@test occursin(r"@_Z6kernel9Aggregate\(.*({ i64 }|\[1 x i64\]) ", ir)
Expand Down Expand Up @@ -89,7 +89,7 @@ end
end

ir = sprint(io->PTX.code_llvm(io, mod.kernel, Tuple{}))
@test occursin(r"@julia_kernel\w*\(\)", ir)
@test occursin(r"@(julia|j)_kernel\w*\(\)", ir)

ir = sprint(io->PTX.code_llvm(io, mod.kernel, Tuple{}; kernel=true))
@test occursin("@_Z6kernel([1 x i64] %state)", ir)
Expand Down Expand Up @@ -117,10 +117,10 @@ end
@test occursin(r"@_Z6kernelP5Int64\(\[1 x i64\] %state", ir)

# child1 doesn't use the state
@test occursin(r"@julia_child1\w*\((i64|i8\*|ptr)", ir)
@test occursin(r"@(julia|j)_child1\w*\((i64|i8\*|ptr)", ir)

# child2 does
@test occursin(r"@julia_child2\w*\(\[1 x i64\] %state", ir)
@test occursin(r"@(julia|j)_child2\w*\(\[1 x i64\] %state", ir)

# can't have the unlowered intrinsic
@test !occursin("julia.gpu.state_getter", ir)
Expand Down Expand Up @@ -149,7 +149,7 @@ end
end

asm = sprint(io->PTX.code_native(io, mod.parent, Tuple{Int64}))
@test occursin(r"call.uni\s+julia_child_"m, asm)
@test occursin(r"call.uni\s+(julia|j)_child_"m, asm)
end

@testset "kernel functions" begin
Expand All @@ -167,8 +167,8 @@ end
asm = sprint(io->PTX.code_native(io, mod.entry, Tuple{Int64};
kernel=true, dump_module=true))
@test occursin(".visible .entry _Z5entry5Int64", asm)
@test !occursin(".visible .func julia_nonentry", asm)
@test occursin(".func julia_nonentry", asm)
@test !occursin(r"\.visible \.func (julia|j)_nonentry", asm)
@test occursin(r"\.func (julia|j)_nonentry", asm)

@testset "property_annotations" begin
asm = sprint(io->PTX.code_native(io, mod.entry, Tuple{Int64}; kernel=true))
Expand Down Expand Up @@ -214,10 +214,10 @@ end
end

asm = sprint(io->PTX.code_native(io, mod.parent1, Tuple{Int}))
@test occursin(".func julia_child_", asm)
@test occursin(r"\.func (julia|j)_child_", asm)

asm = sprint(io->PTX.code_native(io, mod.parent2, Tuple{Int}))
@test occursin(".func julia_child_", asm)
@test occursin(r"\.func (julia|j)_child_", asm)
end

@testset "child function reuse bis" begin
Expand All @@ -241,12 +241,12 @@ end
end

asm = sprint(io->PTX.code_native(io, mod.parent1, Tuple{Int}))
@test occursin(".func julia_child1_", asm)
@test occursin(".func julia_child2_", asm)
@test occursin(r"\.func (julia|j)_child1_", asm)
@test occursin(r"\.func (julia|j)_child2_", asm)

asm = sprint(io->PTX.code_native(io, mod.parent2, Tuple{Int}))
@test occursin(".func julia_child1_", asm)
@test occursin(".func julia_child2_", asm)
@test occursin(r"\.func (julia|j)_child1_", asm)
@test occursin(r"\.func (julia|j)_child2_", asm)
end

@testset "indirect sysimg function use" begin
Expand Down
2 changes: 1 addition & 1 deletion test/spirv_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ end
end

asm = sprint(io->SPIRV.code_native(io, kernel, Tuple{Bool}; kernel=true))
@test occursin(r"OpFunctionCall %void %julia_error", asm)
@test occursin(r"OpFunctionCall %void %(julia|j)_error", asm)
end

end
Expand Down

0 comments on commit ae520af

Please sign in to comment.