Skip to content

Drop thrown values on targets that can't throw - #940

Open
maleadt wants to merge 1 commit into
tb/julia-ir-passesfrom
tb/throw-arguments
Open

maleadt wants to merge 1 commit into
tb/julia-ir-passesfrom
tb/throw-arguments

Conversation

@maleadt

@maleadt maleadt commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Targets that cannot throw (!can_throw(job)) report exceptions without inspecting the thrown value, but code still constructed that value. The construction is dead after throw is lowered, yet often survived to the back-end because Julia does not inline into throw blocks and the constructors allocate through the device allocator. For example, Bool(x) constructs an InexactError whose boxed args tuple gets an unordered pointer store (#904), while log1p with Float64 emulation constructs a DomainError with a LazyString (release stores). These GC orderings led to demote_atomics! (#904, #924, #927).

This PR adds drop_throw_arguments!, a Julia IR pass for targets where !can_throw(job). It rewrites throw(x) as throw(nothing). Julia's DCE (compact! + adce_pass!) then removes each construction proven removable_if_unused; constructions with side effects, such as eagerly built message strings, remain. The pass replaces lower_throw!'s LLVM-level erasure of the thrown value, which erased the call's direct argument regardless of what it was.

Measurements

On Julia 1.12 on an M1, these are the numbers of kernels that passed validation but still contained dead exception allocations, GC atomics, or boxes. The measurements use the whole stack; later PRs in the stack do not remove any of the constructs counted here:

  • Metal Base-function sweep: 37 → 0.
  • Same sweep with Float64 emulation (Metal.jl#955 + Software Float64 emulation, and job-scoped device libraries #926): 61 → 0.
  • oneAPI Base sweep, compile-only: 108 → 0.
  • KernelAbstractions' POCL test suite: gpu_convert_kernel! had 8 InexactError allocations and 24 store atomic ptr unordered → 0.
  • OpenCL PoCL tests: 3 DomainError float boxes → 0.

@maleadt
maleadt requested a review from vchuravy September 24, 2026 10:48
@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.28%. Comparing base (1ad3695) to head (946c1d7).

Additional details and impacted files
@@                  Coverage Diff                   @@
##           tb/julia-ir-passes     #940      +/-   ##
======================================================
- Coverage               86.36%   86.28%   -0.09%     
======================================================
  Files                      29       29              
  Lines                    5766     5783      +17     
======================================================
+ Hits                     4980     4990      +10     
- Misses                    786      793       +7     

☔ 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 commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

Looks to be working nicely! And allows us to get rid of the broken atomic demotion pass.

@maleadt
maleadt added this pull request to stack #944 September 24, 2026 15:38
@maleadt
maleadt removed this pull request from stack #944 September 25, 2026 06:13
@maleadt maleadt changed the title Remove dead exception objects in Julia IR, reject allocations with references, and drop the atomics demotion Drop thrown values on targets that can't throw Sep 25, 2026
@maleadt
maleadt changed the base branch from main to tb/julia-ir-passes September 25, 2026 06:14
@maleadt
maleadt added this pull request to stack #951 September 25, 2026 06:14
Targets that cannot throw report exceptions without inspecting the thrown value. The new
Julia IR pass `drop_throw_arguments!` replaces the argument of `throw` with `nothing` on
those targets, allowing Julia's DCE to remove exception construction when proven
removable. Such dead constructions used to survive to the back-end because Julia does not
inline into throw blocks and the constructors allocate through the device allocator (for
example, `InexactError`'s boxed arguments, #904, and `DomainError` with a `LazyString`),
with GC orderings on the stores into those objects.

This supersedes `lower_throw!`'s LLVM-level erasure of the thrown value, which erased the
call's direct argument regardless of what it was.
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.

1 participant