Skip to content

Preserve @oneapi arguments behind a function barrier - #685

Merged
maleadt merged 1 commit into
mainfrom
tb/preserve-union-singletons
Oct 14, 2026
Merged

maleadt merged 1 commit into
mainfrom
tb/preserve-union-singletons

Conversation

@maleadt

@maleadt maleadt commented Oct 13, 2026

Copy link
Copy Markdown
Member

@oneapi preserved its arguments with GC.@preserve directly in the caller's code. There an argument can be a union of singleton types, like the function in for op in (max, min). On Julia 1.12 and 1.13.0–1.13.1, codegen passes a null operand to gc_preserve_begin for such a value, and LLVM's LazyCallGraph segfaults while compiling the caller (JuliaLang/julia#63482). The upstream fix, JuliaLang/julia#63483, is on release-1.13 but not yet in a release, and hasn't been backported to 1.12.

This moves the body of the expansion into an inlined compile_and_launch function, as CUDA.jl does. The arguments now have concrete types where they're preserved: the call is either split per type and inlined, or dispatched dynamically. For inferred call sites the generated code is the same as before. Call sites whose argument types aren't inferred now dispatch once instead of at each step, which saves 160 bytes per launch for @oneapi k(args...) with args::Vector{Any}.

Fixes #669

`@oneapi` wrapped its arguments in `GC.@preserve` in the caller's code,
where an argument can be a union of singleton types, e.g. the function in
`for op in (max, min)`. Julia 1.12 and 1.13.0-1.13.1 emit a null operand
for preserving such a value, crashing LLVM while compiling the caller
(JuliaLang/julia#63482). Pass the arguments to a function instead, as
CUDA.jl does, so that they have concrete types where they are preserved.

Fixes #669
@github-actions

Copy link
Copy Markdown
Contributor

Your PR requires formatting changes to meet the project's style guidelines.
Please consider running Runic (git runic main) to apply these changes.

Click here to view the suggested changes.
diff --git a/src/compiler/execution.jl b/src/compiler/execution.jl
index 1f240ef..1b51bbd 100644
--- a/src/compiler/execution.jl
+++ b/src/compiler/execution.jl
@@ -114,8 +114,13 @@ macro oneapi(ex...)
     # FIXME: macro hygiene wrt. escaping kwarg values (this broke with 1.5)
     #        we esc() the whole thing now, necessitating gensyms...
     push!(code.args,
-        :($compile_and_launch($f, ($(var_exprs...),), $(Val(launch)),
-                              (; $(call_kwargs...)), (; $(compiler_kwargs...)))))
+        :(
+            $compile_and_launch(
+                $f, ($(var_exprs...),), $(Val(launch)),
+                (; $(call_kwargs...)), (; $(compiler_kwargs...))
+            )
+        )
+    )
 
     return esc(quote
         let
@@ -130,10 +135,12 @@ end
 # arguments have concrete types when they are preserved: `GC.@preserve` of a value that is a
 # union of singletons, e.g. `op` in `for op in (max, min)`, crashes codegen on Julia 1.12 and
 # 1.13.0-1.13.1 (JuliaLang/julia#63482).
-@inline function compile_and_launch(f::F, args::Tuple, ::Val{launch},
-                                    call_kwargs::NamedTuple,
-                                    compiler_kwargs::NamedTuple) where {F,launch}
-    GC.@preserve f args begin
+@inline function compile_and_launch(
+        f::F, args::Tuple, ::Val{launch},
+        call_kwargs::NamedTuple,
+        compiler_kwargs::NamedTuple
+    ) where {F, launch}
+    return GC.@preserve f args begin
         kernel_f = kernel_convert(f)
         kernel_args = map(kernel_convert, args)
         kernel_tt = Tuple{map(Core.Typeof, kernel_args)...}
diff --git a/test/execution.jl b/test/execution.jl
index d1babb8..8f3bdc1 100644
--- a/test/execution.jl
+++ b/test/execution.jl
@@ -313,30 +313,30 @@ end
 end
 
 
-@testset "arguments of a union of singleton types" begin
-    # JuliaLang/julia#63482: `GC.@preserve` of such a value crashed codegen
-    function kernel(a, op)
-        @inbounds a[1] = op(a[1], Int16(1))
-        return
-    end
+    @testset "arguments of a union of singleton types" begin
+        # JuliaLang/julia#63482: `GC.@preserve` of such a value crashed codegen
+        function kernel(a, op)
+            @inbounds a[1] = op(a[1], Int16(1))
+            return
+        end
 
-    function compile(a)
-        kernels = []
-        for op in (max, min)
-            push!(kernels, @oneapi launch=false kernel(a, op))
+        function compile(a)
+            kernels = []
+            for op in (max, min)
+                push!(kernels, @oneapi launch = false kernel(a, op))
+            end
+            kernels
         end
-        kernels
-    end
-    @test length(compile(oneArray(Int16[5]))) == 2
+        @test length(compile(oneArray(Int16[5]))) == 2
 
-    function launch(a)
-        for op in (max, min)
-            @oneapi kernel(a, op)
+        function launch(a)
+            for op in (max, min)
+                @oneapi kernel(a, op)
+            end
+            Array(a)
         end
-        Array(a)
+        @test launch(oneArray(Int16[5])) == Int16[1]
     end
-    @test launch(oneArray(Int16[5])) == Int16[1]
-end
 
 
 @testset "immutables" begin

@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.62%. Comparing base (fa0f23a) to head (c203b6b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #685      +/-   ##
==========================================
- Coverage   81.17%   80.62%   -0.56%     
==========================================
  Files          52       51       -1     
  Lines        4085     3963     -122     
==========================================
- Hits         3316     3195     -121     
+ Misses        769      768       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@maleadt
maleadt merged commit f04879e into main Oct 14, 2026
5 of 6 checks passed
@maleadt
maleadt deleted the tb/preserve-union-singletons branch October 14, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Host JIT segfaults compiling @oneapi in a loop over a tuple of functions

1 participant