Repository navigation
Metal: give every loop a single exit block - #1079
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1079 +/- ##
===========================================
+ Coverage 74.85% 91.90% +17.04%
===========================================
Files 31 31
Lines 7490 7731 +241
===========================================
+ Hits 5607 7105 +1498
+ Misses 1883 626 -1257 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`lower_unreachable_control_flow!` turns every exception into a branch to the kernel's return, which makes each bounds check an exit of the loops around it. Apple's back-end, which has to structurize control flow, hangs on loops with many such exits, or silently skips code after them (Metal.jl#1127). Run FixIrreducible and UnifyLoopExits after the last SimplifyCFG, as LLVM does before structurizing for AMDGPU. Before LLVM 24 these passes only handle branches, so switches are lowered first, and LLVM 20-22 crash on loops without exits, so functions with blocks that can't return are left as they are.
maleadt
force-pushed
the
tb/metal-loop-exits
branch
from
November 5, 2026 13:28
b023e64 to
548f540
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.
Fixes JuliaGPU/Metal.jl#1127.
lower_unreachable_control_flow!turns every exception into a branch to the kernel's return, so each bounds check becomes an exit of every loop around it. Apple's back-end has to structurize control flow, and with enough such exits it hangs inMTLCompilerServiceor silently skips code. On an M1 (macOS 27, Metal.jl 1.12.0):mwe.jlcompiles for K=14 and hangs for K=15 and 16;mwe_wrong.jlskips the final store for K=4 and 6, and hangs for K=8;BoundsError;viewhangs under--check-bounds=yeson Julia 1.10, which is what turned AcceleratedKernels.jl#199's Metal CI red.LLVM's AMDGPU pipeline runs FixIrreducible and UnifyLoopExits before StructurizeCFG for the same reason. This runs them at the end of
lower_air!. It has to come after the last SimplifyCFG, which would merge the exit block back into separate exits. Two LLVM bugs need working around:With the change, on the same M1:
mwe.jlK=15/16 andmwe_wrong.jlK=4/6/8 compile and give correct results;Run time of bounds-checked loop kernels changes by up to 1.5%: a grid-stride sum takes 1639–1650 µs instead of 1635–1637 µs, and 8 unrolled checked loads per step take 1388 µs instead of 1367 µs (median of 50, three runs). Compile time is unchanged.
Metal.jl's test suite passes, except
relocations, which fails the same way without this change.