Repository navigation
Conversation
`CompilerConfig` gets a `codegen` field: `:julia` for Julia's code generator, as today, or `:juliacodegen` for JuliaCodegen.jl, a code generator written in Julia (Julia 1.12 and later). Its default comes from the `codegen` preference. It is part of the configuration, so cached artifacts are keyed on it; inference caches are not. JuliaCodegen.jl is a weak dependency. Its extension registers a code generator with the contract of `compile_method_instance`'s code generation: from the `(CodeInstance, CodeInfo)` pairs inference produced to the module, the functions of each method instance, and the objects of its global variables. It compiles them with `JuliaCodegen.emit_into!`, using the job's target, data layout and debug info, no pgcstack argument, and the job's safepoints. The job's entry gets the name native code gives it, and unsupported constructs call native code (which validation reports on targets without Julia's runtime). Loading the extension alone changes nothing, and selecting `:juliacodegen` without it is an error before compilation starts. When the preference selects it, loading GPUCompiler also loads JuliaCodegen.jl if the environment provides it. Precompilation workloads of packages that don't load JuliaCodegen.jl keep using Julia's code generator. Tests (`test/juliacodegen.jl`, run where JuliaCodegen.jl is available): the configuration, the requirements, loading through the preference, a precompilation workload, that JuliaCodegen.jl ran (its ledger), and a corpus of kernels: - for PTX, GCN, SPIR-V, Metal, native and BPF, the entry has the same signature and calling convention with either code generator; - on the native target, the kernels give the same results. Assisted-by: Claude Code (Opus 5.5)
…a in CI JuliaCodegen.jl, JuliaIR.jl and the LLVM.jl changes they need are not released yet: - LLVM.jl comes from `[sources]`; - JuliaCodegen.jl and JuliaIR.jl come from the branches in `.ci/juliacodegen.jl`, added to the test environment on Julia 1.12 and later only, as JuliaCodegen.jl needs 1.12. CI builds LLVMExtra from the LLVM.jl branch and gets a `codegen` axis: `julia` everywhere, `juliacodegen` from Julia 1.12 on. The `juliacodegen` jobs check that the preference was applied, and that JuliaCodegen.jl compiled code (its ledger). `deps/build.jl` does the same for the assert jobs. To be dropped once the packages are released. Assisted-by: Claude Code (Opus 5.5)
vchuravy
force-pushed
the
vc/juliacodegen
branch
from
October 20, 2026 01:03
b26980b to
3e423ef
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft for CI only, not for merging. Like #1026, this depends on unreleased work, all taken from branches until it is released:
tb/inline-api, which needs an LLVMExtra build from source).The commit titled
[temp]sets up CI for that and will be dropped.GPUCompiler lowers Julia IR to LLVM IR with Julia's own code generator, through
jl_emit_nativeon the(CodeInstance, CodeInfo)pairs its interpreter inferred. This PR lets a compilation hand the same pairs to JuliaCodegen.jl instead, a code generator written in Julia. It's a first step towards a middle end that GPUCompiler controls: device policies, Enzyme hooks, and no dependence on the C++ codegen's ABI.CompilerConfiggets acodegenfield,:julia(as today) or:juliacodegen(Julia 1.12 and later). Its default comes from thecodegenpreference. It is part of the configuration, so cached artifacts are keyed on it; inference caches are not.compile_method_instance's code generation. It returns the module; for each method instance itsfunc(jlcall) andspecfunc(specsig) entries; andgv_to_valuefor the relocation machinery.JuliaCodegen.emit_into!with the job's target, data layout and debug info (llvm_debug_info),gcstack_arg=false, andcan_safepoint(job), as theCodegenParamsGPUCompiler gives Julia's code generator. ForNoDebug, it still asks for locations, as Julia's code generator emits them unless Julia runs with-g0: validation's backtraces read them, then they are stripped.julia_f_N).JULIACODEGEN_UNSUPPORTED=error, they throw instead, which says what they are.:juliacodegenwithout it is an error before compilation starts. When the preference selects it, loading GPUCompiler also loads JuliaCodegen.jl if the environment provides it.test/native/precompile.jlnow expects that when the preference selects JuliaCodegen.jl.Tests (
test/juliacodegen.jl)The file runs where JuliaCodegen.jl is available;
runtests.jlloads it if the environment has it. It covers:JULIACODEGEN_LEDGER);kernelboth ways), the entry has the same function type and calling convention with either code generator;All 204 tests pass locally on 1.12.
The whole suite with the preference set is the real differential. Locally on 1.12:
codegenjuliajuliacodegenThe
juliacodegenrow started at 149 failures and 15 errors. Getting it down produced the JuliaCodegen.jl fixes in JuliaLLVM/JuliaCodegen.jl#16 to #25: native code's conventions (specsig for varargs and unions, aggregates by reference, jlcall entries calling specsig entries), error paths through the runtime,getfieldwith dynamic indices,svec, the names and small-tag slots of objects' globals,llvmcalldetails, and locations for backtraces. The 10 that remain test the shape of native code's IR, which JuliaCodegen.jl doesn't produce:nested insertvalue(SPIR-V, 4) andreplace i128 allocas(SPIR-V, 4): native code builds those aggregates, or returns them, through stack memory; JuliaCodegen.jl keeps them in SSA values.merged relocation slots(Metal): native code loads object addresses where they are used, and LLVM merges the loads; JuliaCodegen.jl loads each once per function.smalltag boxed union constant demotion(Metal): the kernel reads the union's payload, never the constant's box, so there is nothing to demote.CI on this PR (temporary)
.ci/juliacodegen.jl build, in MSYS2 on Windows) and sets uptest/with that LLVM.jl checkout. From Julia 1.12 on, it also adds JuliaCodegen.jl and JuliaIR.jl. Coverage is not collected meanwhile.codegenaxis:juliaeverywhere,juliacodegenfrom 1.12 on. Thejuliacodegenjobs check that the preference was applied and that JuliaCodegen.jl compiled code (the ledger). They arecontinue-on-errorwhile JuliaCodegen.jl is experimental, so they report without failing the workflow.juliacodegenvariant per back end, like [Draft, for CI] Take Julia's LLVM passes from JuliaIR.jl, as an opt-in #1026's ports variants, can follow.🤖 Generated with Claude Code