Support Julia 1.13's julia.atomicmodify pseudo-intrinsic#2880
Open
vchuravy wants to merge 1 commit into
Open
Conversation
Julia 1.13 (JuliaLang/julia#57010) emits atomic modify operations on integer-typed pointer-free fields as a variadic pseudo-intrinsic {old, new} = julia.atomicmodify.iN.pAS(ptr, op, ordering, syncscope, args...) which atomically performs old = *ptr; new = op(old, args...); *ptr = new, and is only expanded to atomicrmw/cmpxchg by Julia's ExpandAtomicModify pass after GC lowering. Previously these calls hit the generic call differentiation path, which rejects variadic calls with "Number of arg operands != function parameters". TypeAnalysis: unify the pointee of ptr with both result elements and analyze op interprocedurally, relating the forwarded arguments with the modified memory. This in particular lets activity analysis prove atomic counter/lock manipulations inactive. AdjointGenerator: keep the pseudo-intrinsic intact in generated code (it must survive until Julia's expansion pass) and handle it akin to atomicrmw: - fully inactive calls are replayed via the constant fallback, - non-differentiable (integer/pointer) modifications within duplicated memory are replicated on the shadow location with the primal arguments, keeping counters and lock states of shadow objects consistent, - in forward mode, ops recognized as linear in (old, value) (fadd/fsub/xchg through bitcasts) compute the tangent by applying the same op to the shadow location and shadow value, - in reverse mode (with inactive result), fadd/fsub accumulate the adjoint of the value operand from a load of the shadow location with downgraded ordering (negated for fsub), - everything else reports "Active atomic modify not yet handled". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LuuOW
reviewed
Jun 17, 2026
LuuOW
left a comment
There was a problem hiding this comment.
Technical audit: code patterns and logic verified.
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.
Julia 1.13 (JuliaLang/julia#57010) emits atomic modify operations on integer-typed pointer-free fields as a variadic pseudo-intrinsic
{old, new} = julia.atomicmodify.iN.pAS(ptr, op, ordering, syncscope, args...)which atomically performs
old = *ptr; new = op(old, args...); *ptr = new, whereop's parameteri(i >= 1) is forwarded from call operandi + 3. It is only expanded toatomicrmw/cmpxchg loops by Julia'sExpandAtomicModifypass after GC lowering, so Enzyme must keep it intact in generated code. Previously these calls hit the generic call differentiation path, which rejects variadic calls withNumber of arg operands != function parameters(EnzymeAD/Enzyme.jl#3086).TypeAnalysis
Unify the pointee of
ptrwith both result elements and analyzeopinterprocedurally, relating the forwarded arguments with the modified memory. In particular this lets activity analysis prove atomic counter/lock manipulations inactive (Julia annotates real-world IR withenzyme_typealready, but the interprocedural step recovers types for the modified value and forwarded operands).AdjointGenerator
Handle the call akin to
atomicrmw:(old, value)(fadd/fsub/xchg through bitcasts) compute the tangent by applying the same op to the shadow location and shadow value,Active atomic modify not yet handled.Note that on Julia 1.13 codegen only emits the intrinsic for integer element types (float fields still lower to inline cmpxchg loops), so the float paths are exercised by the included lit tests; they implement the intrinsic's full semantics for when emission widens.
Companion Enzyme.jl change (running
ExpandAtomicModifyin its post-AD legalization pipeline): EnzymeAD/Enzyme.jl branchvc/atomicmodify. Tested end-to-end on Julia 1.13.0-rc1: the previously crashinglocksandthreadstest suites pass, including reverse mode over atomic counters in duplicated structs.🤖 Generated with Claude Code