Repository navigation
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## tb/device-libraries #926 +/- ##
=======================================================
+ Coverage 90.92% 91.34% +0.42%
=======================================================
Files 32 42 +10
Lines 7469 8334 +865
=======================================================
+ Hits 6791 7613 +822
- Misses 678 721 +43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
maleadt
added a commit
that referenced
this pull request
Sep 9, 2026
#904 demoted Julia's `unordered` heap-reference accesses, but codegen also stores the type tag of every heap-allocated object with `release` ordering, through the generic pointer the device allocator returns. Apple's back-end cannot legalize that either: on macOS 26 the pipeline compile aborts with XPC_ERROR_CONNECTION_INTERRUPTED, and metal-tt reports "unable to legalize instruction: store release (p0)". It surfaced with Float64 emulation (#926), where DomainError(::Float64, ...) in log1p's throw path is not inlined and its allocation survives to the back-end. The same applies to SPIR-V: OpAtomicLoad/OpAtomicStore only take scalars, so a release-ordered pointer store is just as invalid there as an unordered one. Since device-side atomics go through target intrinsics rather than these instructions, every LLVM atomic load or store that reaches these back-ends is Julia GC bookkeeping, and `demote_atomics!` now strips all of them.
maleadt
force-pushed
the
tb/softfloat
branch
from
September 9, 2026 12:26
b90581d to
b620fd3
Compare
maleadt
force-pushed
the
tb/softfloat
branch
from
September 9, 2026 13:39
b620fd3 to
b68f8e9
Compare
Member
Author
|
Depends on llvm/llvm-project#208026, which I'll add in JuliaPackaging/Yggdrasil#14727 |
maleadt
force-pushed
the
tb/softfloat
branch
3 times, most recently
from
September 11, 2026 11:32
9b4ff44 to
3452534
Compare
maleadt
added a commit
that referenced
this pull request
Oct 20, 2026
Replace the provider hooks from PR #926 with the ordered device-library interface. Legalize callers and Julia-library artifacts at the same late boundary so imported runtime signatures remain ABI-compatible; keep expensive arithmetic helpers out of line.
maleadt
added a commit
that referenced
this pull request
Oct 20, 2026
Replace the provider hooks from PR #926 with the ordered device-library interface. Legalize callers and Julia-library artifacts at the same late boundary so imported runtime signatures remain ABI-compatible; keep expensive arithmetic helpers out of line.
maleadt
added a commit
that referenced
this pull request
Oct 20, 2026
Replace the provider hooks from PR #926 with the ordered device-library interface. Legalize callers and Julia-library artifacts at the same late boundary so imported runtime signatures remain ABI-compatible; keep expensive arithmetic helpers out of line.
maleadt
added a commit
that referenced
this pull request
Oct 20, 2026
Replace the provider hooks from PR #926 with the ordered device-library interface. Legalize callers and Julia-library artifacts at the same late boundary so imported runtime signatures remain ABI-compatible; keep expensive arithmetic helpers out of line.
maleadt
force-pushed
the
tb/device-libraries
branch
from
October 20, 2026 19:22
4f70a38 to
004f464
Compare
maleadt
added a commit
that referenced
this pull request
Oct 20, 2026
Replace the provider hooks from PR #926 with the device-library interface: back-ends select `softfloat_library()` in `device_libraries` and call `SoftFloat.legalize!(job, mod)` from `lower_intrinsics!`. Legalization rebuilds functions whose type changes under their original names, so callers and Julia libraries imported after optimization agree on the integer ABI.
maleadt
force-pushed
the
tb/device-libraries
branch
from
October 23, 2026 13:30
004f464 to
dbe5000
Compare
maleadt
added a commit
that referenced
this pull request
Oct 23, 2026
Replace the provider hooks from PR #926 with the device-library interface: back-ends select `softfloat_library()` in `device_libraries` and call `SoftFloat.legalize!(job, mod)` from `lower_intrinsics!`. Legalization rebuilds functions whose type changes under their original names, so callers and Julia libraries imported after optimization agree on the integer ABI.
Replace the provider hooks from PR #926 with the device-library interface: back-ends select `softfloat_library()` in `device_libraries` and call `SoftFloat.legalize!(job, mod)` from `lower_intrinsics!`. Legalization rebuilds functions whose type changes under their original names, so callers and Julia libraries imported after optimization agree on the integer ABI.
Before LLVM 19, `icmp` could be a constant expression, as found in Base's `sin`. The legalizer rejected every constant expression it did not know how to rebuild, even when neither its type nor its operands changed; return those as they are.
GPUCompiler has no cost-based inliner, so the emulation routines all stayed out of line: a kernel calling `sin` called `gpu_softfloat_neg64`, a single xor, 85 times. Split the routines over two libraries: arithmetic and narrowing conversions (79 to 444 instructions once optimized) stay out of line, the rest (at most 43 instructions) are forced inline.
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.
Add declarative device libraries and late intrinsic lowering
Back-ends currently link their device libraries by hand in
link_libraries!, often with fixed-point loops over vendor bitcode, and the GPU runtime has its own linking path. Some operations also can't be selected on some targets (math intrinsics on vectors, #679, or any binary64 operation on hardware without it) and have to be lowered to library calls. Doing that before optimization hides the operations from LLVM.This PR adds
device_libraries(job), which returns an ordered tuple ofDeviceLibrarys. Each library either exports Julia methods (DeviceLibraryMethod, the mechanism the runtime already uses) or contains LLVM bitcode. Definitions are imported on demand: GPUCompiler searches the libraries in order, and repeats until no library provides any remaining reference. Unresolved references are left for IR validation, and an ABI mismatch between a declaration and its definition is an error. Linkonce definitions shared by several libraries are coalesced by LLVM's linker.Imports happen in two phases:
link_libraries!and the runtime were linked before, so library code is optimized together with the kernel. This is what Clang does with-mlink-builtin-bitcode, and what NVVM, MLIR, Triton and XLA do too.lower_intrinsics!(job, mod)hook has rewritten operations the target can't select into library calls, and again after GC lowering, which introduces runtime calls. Julia libraries imported at this point are lowered by the same hook, so their ABI matches the caller. Late imports are internalized and passed tofinish_imports!(job, mod, imported). CPU-feature and sync-scope lowering are applied to them, then they are force-inlined where requested, and a small cleanup pipeline runs.A library can be marked
always_inline, as AMDGPU needs for the ROCm device libraries. A Julia library can also beoptimized on its own, which suits standalone numerical code such as #926.link_libraries!overrides keep working at both call sites.The intrinsic-rewriting helpers move out of the Metal back-end into
utils.jl(call_declared!,lower_intrinsic_calls!,scalarize_vector_calls!). PTX'snvvm_reflect!now folds__CUDA_PREC_SQRT/__CUDA_PREC_DIVto!fastmath, matching nvcc's defaults. Before, libdevice used its approximate implementations even without fast math.Differences from the first version of this PR
The first version linked all libraries once, late, and tracked library methods and bitcode digests in the kernel cache. I reworked it because:
cufunctionlookup went from 201 ns to 4.85 µs, and an AMDGPU@roc launch=falsefrom 113 ns to 4.2 µs. Kernel cache lookup is now identical tomain. Device libraries are exempt from kernel invalidation for now: library selection must only depend on the job's configuration, and bitcode is read once per session. Plain method-instance edges from GPU inference frames would integrate this with invalidation at no launch cost, without changing this API.Removed compared to the first version:
device_library_bitcode,load_device_library,runtime_imports, digest tracking anduses_legacy_linker.Known regression for back-ends that lower math late
LLVM's cost model treats an intrinsic as a single instruction (
isLoweredToCallreturns false), even when the target will lower it to a library call. Loops over math intrinsics are therefore unrolled as if they were cheap, beforelower_intrinsics!turns each intrinsic into a call. In CUDA.jl, an F32 sum ofsingrows from 123 to 566 SASS instructions. CUDA.jl#3433 keeps the libdevice overlays for the affected functions for now, marked as a workaround, and tests the late lowering path so those overlays can be dropped once LLVM's cost model is fixed.Validation
Tested on an RTX 5080 (sm_120) and an AMD gfx1036 iGPU.
device_library,ptx,metal,nativeandutilspass on 1.10 and nightly.device_librarypasses on 1.13.coregroup passes, plus the new test that exercises raw-intrinsic lowering.main.cufunctiontakes about 200 ns, with no extra allocations.main, apart from improvements: Float16exploses a call, and vector math compiles wheremainfails.main.@cudais 4% slower on 1.12 (1.60 vs 1.53 s) and 8% slower on 1.13 (1.09 vs 1.01 s).main;@roc launch=falsetakes about 110 ns.sincosruns twice as fast.Downstream: #926 (softfloat, stacked on this PR), JuliaGPU/CUDA.jl#3433 and JuliaGPU/AMDGPU.jl#1177.