diff --git a/src/mcgen.jl b/src/mcgen.jl index 2c5e85c3..3fb4a5c6 100644 --- a/src/mcgen.jl +++ b/src/mcgen.jl @@ -35,10 +35,17 @@ function (self::ResolveCPUReferences)(mod::LLVM.Module) for f in functions(mod) fn = LLVM.name(f) if isdeclaration(f) && !LLVM.isintrinsic(f) && startswith(fn, "jl_") - # eagerly resolve the address of the binding - address = ccall(:jl_cglobal, Any, (Any, Any), fn, UInt) - dereferenced = unsafe_load(address) - dereferenced = LLVM.ConstantInt(dereferenced) + # lazily resolve the address of the binding; some symbols only exist + # within the JIT (e.g. `jl_get_pgcstack_resolved`) and cannot be looked up, + # but such symbols are only ever called, not loaded from. + dereferenced = nothing + function resolve_binding() + if dereferenced === nothing + address = ccall(:jl_cglobal, Any, (Any, Any), fn, UInt) + dereferenced = LLVM.ConstantInt(unsafe_load(address)) + end + dereferenced + end function replace_bindings!(value) changed = false @@ -49,7 +56,7 @@ function (self::ResolveCPUReferences)(mod::LLVM.Module) changed |= replace_bindings!(val) elseif isa(val, LLVM.LoadInst) # resolve - replace_uses!(val, dereferenced) + replace_uses!(val, resolve_binding()) erase!(val) # FIXME: iterator invalidation? changed = true diff --git a/test/native.jl b/test/native.jl index 6aebba91..997d334e 100644 --- a/test/native.jl +++ b/test/native.jl @@ -324,8 +324,11 @@ end end @testset "unbound typevars" begin - mod = @eval module $(gensym()) - invalid_kernel() where {unbound} = return + # suppress the warning Julia emits when defining a method with an unbound typevar + mod = redirect_stderr(devnull) do + @eval module $(gensym()) + invalid_kernel() where {unbound} = return + end end @test_throws KernelError Native.code_llvm(devnull, mod.invalid_kernel, Tuple{}) end @@ -456,20 +459,27 @@ end Native.code_llvm(mod.g, Tuple{Int64}; dump_module=true, kernel=true) end - @test @filecheck(begin - @check_not "@{{(julia|j)_expensive_[0-9]+}}" - Native.code_llvm(mod.g, Tuple{Int64}; dump_module=true, kernel=true, always_inline=true) - end) broken=broken + # suppress FileCheck diagnostics when the failure is known and expected + quiet(f) = broken ? redirect_stderr(f, devnull) : f() + + @test quiet() do + @filecheck begin + @check_not "@{{(julia|j)_expensive_[0-9]+}}" + Native.code_llvm(mod.g, Tuple{Int64}; dump_module=true, kernel=true, always_inline=true) + end + end broken=broken @test @filecheck begin @check "@{{(julia|j)_expensive_[0-9]+}}" Native.code_llvm(mod.h, Tuple{Int64}; dump_module=true, kernel=true) end - @test @filecheck(begin - @check_not "@{{(julia|j)_expensive_[0-9]+}}" - Native.code_llvm(mod.h, Tuple{Int64}; dump_module=true, kernel=true, always_inline=true) - end) broken=broken + @test quiet() do + @filecheck begin + @check_not "@{{(julia|j)_expensive_[0-9]+}}" + Native.code_llvm(mod.h, Tuple{Int64}; dump_module=true, kernel=true, always_inline=true) + end + end broken=broken end @testset "function attributes" begin @@ -495,6 +505,24 @@ end end end +@testset "CPU reference resolution" begin + # JIT-private symbols like `jl_get_pgcstack_resolved` (JuliaLang/julia#61527) cannot + # be looked up using `jl_cglobal`, so we should only resolve bindings that are + # actually loaded from, leaving called functions alone. + job, _ = Native.create_job(identity, (Nothing,)) + JuliaContext() do ctx + mod = parse(LLVM.Module, """ + declare void @jl_get_pgcstack_resolved() + + define void @entry() { + call void @jl_get_pgcstack_resolved() + ret void + }""") + GPUCompiler.prepare_execution!(job, mod) + @test haskey(functions(mod), "jl_get_pgcstack_resolved") + end +end + end ############################################################################################