diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 1aa27828..4593cd13 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -19,34 +19,62 @@ jobs: fail-fast: false matrix: version: ['1.10', '1.11', '1.12-nightly', 'nightly'] - os: [ubuntu-latest, macOS-latest, windows-latest] - arch: [x64] + os: [ubuntu-24.04, ubuntu-24.04-arm, macOS-15, macOS-15-intel, windows-2025] + arch: [x64, arm64] llvm_args: [''] + exclude: + # unsupported combinations + - os: ubuntu-24.04 + arch: arm64 + - os: windows-2025 + arch: arm64 + - os: ubuntu-24.04-arm + arch: x64 + - os: macOS-15 + arch: x64 + - os: macOS-15-intel + arch: arm64 include: # starting with Julia 1.10, we can enable opaque pointers # from Julia 1.12 on, this is the default. - version: '1.10' - os: 'ubuntu-latest' + os: 'ubuntu-24.04' arch: 'x64' llvm_args: '--opaque-pointers' - version: '1.10' - os: 'macOS-latest' + os: 'ubuntu-24.04-arm' + arch: 'arm64' + llvm_args: '--opaque-pointers' + - version: '1.10' + os: 'macOS-15' + arch: 'arm64' + llvm_args: '--opaque-pointers' + - version: '1.10' + os: 'macOS-15-intel' arch: 'x64' llvm_args: '--opaque-pointers' - version: '1.10' - os: 'windows-latest' + os: 'windows-2025' arch: 'x64' llvm_args: '--opaque-pointers' - version: '1.11' - os: 'ubuntu-latest' + os: 'ubuntu-24.04' arch: 'x64' llvm_args: '--opaque-pointers' - version: '1.11' - os: 'macOS-latest' + os: 'ubuntu-24.04-arm' + arch: 'arm64' + llvm_args: '--opaque-pointers' + - version: '1.11' + os: 'macOS-15' + arch: 'arm64' + llvm_args: '--opaque-pointers' + - version: '1.11' + os: 'macOS-15-intel' arch: 'x64' llvm_args: '--opaque-pointers' - version: '1.11' - os: 'windows-latest' + os: 'windows-2025' arch: 'x64' llvm_args: '--opaque-pointers' steps: @@ -85,7 +113,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-24.04] arch: [x64] llvm_args: ['', '--opaque-pointers'] include: diff --git a/src/jlgen.jl b/src/jlgen.jl index 1bff8a0c..f9cf153c 100644 --- a/src/jlgen.jl +++ b/src/jlgen.jl @@ -321,10 +321,10 @@ CC.isoverlayed(::StackedMethodTable) = true # no need to fall back to the parent method view return result end - + parent_result = CC.findall(sig, table.parent; limit)::Union{Nothing, CC.MethodLookupResult} parent_result === nothing && return nothing #too many matches - + # merge the parent match results with the internal method table return CC.MethodLookupResult( CC.vcat(result.matches, parent_result.matches), @@ -354,13 +354,13 @@ else # no need to fall back to the parent method view return CC.MethodMatchResult(result, true) end - + parent_result = CC.findall(sig, table.parent; limit)::Union{Nothing, CC.MethodMatchResult} parent_result === nothing && return nothing #too many matches - + overlayed = parent_result.overlayed | !CC.isempty(result) parent_result = parent_result.matches::CC.MethodLookupResult - + # merge the parent match results with the internal method table return CC.MethodMatchResult( CC.MethodLookupResult( @@ -589,7 +589,11 @@ function ci_cache_populate(interp, cache, mi, min_world, max_world) # now make sure everything has source code, if desired mi = CC.get_ci_mi(callee) if CC.use_const_api(callee) - src = CC.codeinfo_for_const(interp, mi, ci.rettype_const) + if VERSION >= v"1.13.0-DEV.1121" + src = CC.codeinfo_for_const(interp, mi, CC.WorldRange(callee.min_world, callee.max_world), callee.edges, callee.rettype_const) + else + src = CC.codeinfo_for_const(interp, mi, callee.rettype_const) + end 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) @@ -801,11 +805,25 @@ function compile_method_instance(@nospecialize(job::CompilerJob)) end end - if VERSION >= v"1.12.0-DEV.1703" - # on sufficiently recent versions of Julia, we can query the MIs compiled. - # this is required after the move to `invokce(::CodeInstance)`, because our + if VERSION >= v"1.13.0-DEV.1120" + # on sufficiently recent versions of Julia, we can query the CIs compiled. + # this is required after the move to `invoke(::CodeInstance)`, because our # lookup function (used to populate method_instances) isn't always called then. + num_cis = Ref{Csize_t}(0) + @ccall jl_get_llvm_cis(native_code::Ptr{Cvoid}, num_cis::Ptr{Csize_t}, + C_NULL::Ptr{Cvoid})::Nothing + resize!(method_instances, num_cis[]) + @ccall jl_get_llvm_cis(native_code::Ptr{Cvoid}, num_cis::Ptr{Csize_t}, + method_instances::Ptr{Cvoid})::Nothing + + for (i, ci) in enumerate(method_instances) + method_instances[i] = ci.def::MethodInstance + end + + elseif VERSION >= v"1.12.0-DEV.1703" + # slightly older versions of Julia used MIs directly + num_mis = Ref{Csize_t}(0) @ccall jl_get_llvm_mis(native_code::Ptr{Cvoid}, num_mis::Ptr{Csize_t}, C_NULL::Ptr{Cvoid})::Nothing diff --git a/test/native.jl b/test/native.jl index cba496fb..da08764f 100644 --- a/test/native.jl +++ b/test/native.jl @@ -340,7 +340,10 @@ end end @testset "always_inline" begin - # XXX: broken by JuliaLang/julia#51599, see JuliaGPU/GPUCompiler.jl#527 + # XXX: broken by JuliaLang/julia#51599, see JuliaGPU/GPUCompiler.jl#527. + # yet somehow this works on 1.12? + broken = VERSION >= v"1.13-" + mod = @eval module $(gensym()) import ..sink expensive(x) = $(foldl((e, _) -> :($sink($e) + $sink(x)), 1:100; init=:x)) @@ -359,20 +362,20 @@ end Native.code_llvm(mod.g, Tuple{Int64}; dump_module=true, kernel=true) end - @test @filecheck begin + @test @filecheck(begin check"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"CHECK: @{{(julia|j)_expensive_[0-9]+}}" Native.code_llvm(mod.h, Tuple{Int64}; dump_module=true, kernel=true) end - @test @filecheck begin + @test @filecheck(begin check"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 @@ -659,7 +662,7 @@ end a[1] = a[1]^2 return end - + function dkernel(a) ptr = Enzyme.deferred_codegen(typeof(kernel), Tuple{Vector{Float64}}) ccall(ptr, Cvoid, (Vector{Float64},), a) diff --git a/test/runtests.jl b/test/runtests.jl index 3280357e..243a96b0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -330,73 +330,85 @@ println("Testing finished in $elapsed") # construct a testset to render the test results o_ts = Test.DefaultTestSet("Overall") -Test.push_testset(o_ts) -completed_tests = Set{String}() -for (testname, (resp,)) in results - push!(completed_tests, testname) - if isa(resp, Test.DefaultTestSet) - Test.push_testset(resp) - Test.record(o_ts, resp) - Test.pop_testset() - elseif isa(resp, Tuple{Int,Int}) - fake = Test.DefaultTestSet(testname) - for i in 1:resp[1] - Test.record(fake, Test.Pass(:test, nothing, nothing, nothing, nothing)) - end - for i in 1:resp[2] - Test.record(fake, Test.Broken(:test, nothing)) - end - Test.push_testset(fake) - Test.record(o_ts, fake) - Test.pop_testset() - elseif isa(resp, RemoteException) && isa(resp.captured.ex, Test.TestSetException) - println("Worker $(resp.pid) failed running test $(testname):") - Base.showerror(stdout, resp.captured) - println() - fake = Test.DefaultTestSet(testname) - for i in 1:resp.captured.ex.pass - Test.record(fake, Test.Pass(:test, nothing, nothing, nothing, nothing)) - end - for i in 1:resp.captured.ex.broken - Test.record(fake, Test.Broken(:test, nothing)) - end - for t in resp.captured.ex.errors_and_fails - Test.record(fake, t) - end - Test.push_testset(fake) - Test.record(o_ts, fake) - Test.pop_testset() +function with_testset(f, testset) + @static if VERSION >= v"1.13.0-DEV.1044" + Test.@with_testset testset f() else - if !isa(resp, Exception) - resp = ErrorException(string("Unknown result type : ", typeof(resp))) + Test.push_testset(testset) + try + f() + finally + Test.pop_testset() end - # If this test raised an exception that is not a remote testset exception, - # i.e. not a RemoteException capturing a TestSetException that means - # the test runner itself had some problem, so we may have hit a segfault, - # deserialization errors or something similar. Record this testset as Errored. - fake = Test.DefaultTestSet(testname) - Test.record(fake, Test.Error(:nontest_error, testname, nothing, Any[(resp, [])], LineNumberNode(1))) - Test.push_testset(fake) - Test.record(o_ts, fake) - Test.pop_testset() end end -for test in tests - (test in completed_tests) && continue - fake = Test.DefaultTestSet(test) - Test.record(fake, Test.Error(:test_interrupted, test, nothing, - [("skipped", [])], LineNumberNode(1))) - Test.push_testset(fake) - Test.record(o_ts, fake) - Test.pop_testset() +with_testset(o_ts) do + completed_tests = Set{String}() + for (testname, (resp,)) in results + push!(completed_tests, testname) + if isa(resp, Test.DefaultTestSet) + with_testset(resp) do + Test.record(o_ts, resp) + end + elseif isa(resp, Tuple{Int,Int}) + fake = Test.DefaultTestSet(testname) + for i in 1:resp[1] + Test.record(fake, Test.Pass(:test, nothing, nothing, nothing, nothing)) + end + for i in 1:resp[2] + Test.record(fake, Test.Broken(:test, nothing)) + end + with_testset(fake) do + Test.record(o_ts, fake) + end + elseif isa(resp, RemoteException) && isa(resp.captured.ex, Test.TestSetException) + println("Worker $(resp.pid) failed running test $(testname):") + Base.showerror(stdout, resp.captured) + println() + fake = Test.DefaultTestSet(testname) + for i in 1:resp.captured.ex.pass + Test.record(fake, Test.Pass(:test, nothing, nothing, nothing, nothing)) + end + for i in 1:resp.captured.ex.broken + Test.record(fake, Test.Broken(:test, nothing)) + end + for t in resp.captured.ex.errors_and_fails + Test.record(fake, t) + end + with_testset(fake) do + Test.record(o_ts, fake) + end + else + if !isa(resp, Exception) + resp = ErrorException(string("Unknown result type : ", typeof(resp))) + end + # If this test raised an exception that is not a remote testset exception, + # i.e. not a RemoteException capturing a TestSetException that means + # the test runner itself had some problem, so we may have hit a segfault, + # deserialization errors or something similar. Record this testset as Errored. + fake = Test.DefaultTestSet(testname) + Test.record(fake, Test.Error(:nontest_error, testname, nothing, Base.ExceptionStack([(exception=resp,backtrace=[])]), LineNumberNode(1))) + with_testset(fake) do + Test.record(o_ts, fake) + end + end + end + for test in tests + (test in completed_tests) && continue + fake = Test.DefaultTestSet(test) + Test.record(fake, Test.Error(:test_interrupted, test, nothing, Base.ExceptionStack([(exception="skipped",backtrace=[])]), LineNumberNode(1))) + with_testset(fake) do + Test.record(o_ts, fake) + end + end end println() Test.print_test_results(o_ts, 1) -if !o_ts.anynonpass +if (VERSION >= v"1.13.0-DEV.1037" && !Test.anynonpass(o_ts)) || + (VERSION < v"1.13.0-DEV.1037" && !o_ts.anynonpass) println(" \033[32;1mSUCCESS\033[0m") else println(" \033[31;1mFAILURE\033[0m\n") Test.print_test_errors(o_ts) throw(Test.FallbackTestSetException("Test run finished with errors")) end - diff --git a/test/setup.jl b/test/setup.jl index 5744a675..38534e9f 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -11,14 +11,15 @@ for file in readdir(joinpath(@__DIR__, "helpers")) end using .FileCheck +if VERSION >= v"1.13.0-DEV.1044" +using Base.ScopedValues +end + ## entry point function runtests(f, name) - old_print_setting = Test.TESTSET_PRINT_ENABLE[] - Test.TESTSET_PRINT_ENABLE[] = false - - try + function inner() # generate a temporary module to execute the tests in mod_name = Symbol("Test", rand(1:100), "Main_", replace(name, '/' => '_')) mod = @eval(Main, module $mod_name end) @@ -61,8 +62,21 @@ function runtests(f, name) GC.gc(true) res - finally - Test.TESTSET_PRINT_ENABLE[] = old_print_setting + end + + old_print_setting = Test.TESTSET_PRINT_ENABLE[] + @static if VERSION >= v"1.13.0-DEV.1044" + @with Test.TESTSET_PRINT_ENABLE=>false begin + inner() + end + else + old_print_setting = Test.TESTSET_PRINT_ENABLE[] + Test.TESTSET_PRINT_ENABLE[] = false + try + inner() + finally + Test.TESTSET_PRINT_ENABLE[] = old_print_setting + end end end diff --git a/test/spirv.jl b/test/spirv.jl index be7f2651..772531bf 100644 --- a/test/spirv.jl +++ b/test/spirv.jl @@ -78,7 +78,7 @@ end backend, supports_fp16=false)) do msg occursin("unsupported use of half value", msg) && occursin("[1] unsafe_store!", msg) && - occursin("[2] kernel", msg) + occursin(r"\[\d+\] kernel", msg) end @test_throws_message(InvalidIRError, @@ -86,7 +86,7 @@ end backend, supports_fp64=false)) do msg occursin("unsupported use of double value", msg) && occursin("[1] unsafe_store!", msg) && - occursin("[2] kernel", msg) + occursin(r"\[\d+\] kernel", msg) end end