Summary
On aarch64 (Apple Silicon), check_allocs reports a spurious AllocatingRuntimeCall for allocation-free functions. The culprit is the never-allocates whitelist in src/classify.jl: it lists "get_pgcstack" and "get_pgcstack_static" but omits "get_pgcstack_fallback" — the variant aarch64 codegen emits to fetch the per-task GC stack pointer.
Root cause
In fn_may_allocate (src/classify.jl, ~L64-71):
function fn_may_allocate(name::AbstractString; ignore_throw::Bool)
if name in ("egal__unboxed", ...,
"gc_safepoint", "gc_collect", "genericmemory_owner", "get_pgcstack", "get_pgcstack_static") || occursin(r"^unbox_.*", name)
return false # these functions never allocate
...
get_pgcstack_fallback is not in the tuple, so fn_may_allocate("get_pgcstack_fallback") returns true, and classify_runtime_fn("jl_get_pgcstack_fallback") therefore returns (:runtime, true). On aarch64, codegen emits a call to jl_get_pgcstack_fallback (rather than jl_get_pgcstack / jl_get_pgcstack_static) to obtain the per-task GC stack pointer, so genuinely allocation-free functions are flagged with a spurious AllocatingRuntimeCall.
MWE
julia> using AllocCheck
# BEFORE the fix (v0.2.5):
julia> AllocCheck.classify_runtime_fn("jl_get_pgcstack_fallback"; ignore_throw=true)
(:runtime, true) # spurious: claims it may allocate
# the already-fixed sibling, for comparison:
julia> AllocCheck.classify_runtime_fn("jl_get_pgcstack_static"; ignore_throw=true)
(:runtime, false)
Actual: (:runtime, true) — jl_get_pgcstack_fallback is treated as possibly-allocating.
Expected: (:runtime, false) — it never allocates, exactly like jl_get_pgcstack and jl_get_pgcstack_static.
After the fix:
julia> AllocCheck.classify_runtime_fn("jl_get_pgcstack_fallback"; ignore_throw=true)
(:runtime, false)
Fix
Add "get_pgcstack_fallback" to the never-allocates tuple in fn_may_allocate, right next to "get_pgcstack_static":
"gc_safepoint", "gc_collect", "genericmemory_owner", "get_pgcstack", "get_pgcstack_static",
"get_pgcstack_fallback") || occursin(r"^unbox_.*", name)
Precedent
This is the same class of fix as e44be86 ("fix: add get_pgcstack_static to non-allocating allowlist"), which added the _static variant for the same reason. get_pgcstack_fallback is simply a third pgcstack-fetch variant that the whitelist missed.
Downstream report
SciML/FastAlmostBandedMatrices.jl#68 — that package's zero-allocation tests fail only on macOS/aarch64, traced to this false positive.
PR with the one-line fix to follow.
Summary
On aarch64 (Apple Silicon),
check_allocsreports a spuriousAllocatingRuntimeCallfor allocation-free functions. The culprit is the never-allocates whitelist insrc/classify.jl: it lists"get_pgcstack"and"get_pgcstack_static"but omits"get_pgcstack_fallback"— the variant aarch64 codegen emits to fetch the per-task GC stack pointer.Root cause
In
fn_may_allocate(src/classify.jl, ~L64-71):get_pgcstack_fallbackis not in the tuple, sofn_may_allocate("get_pgcstack_fallback")returnstrue, andclassify_runtime_fn("jl_get_pgcstack_fallback")therefore returns(:runtime, true). On aarch64, codegen emits a call tojl_get_pgcstack_fallback(rather thanjl_get_pgcstack/jl_get_pgcstack_static) to obtain the per-task GC stack pointer, so genuinely allocation-free functions are flagged with a spuriousAllocatingRuntimeCall.MWE
Actual:
(:runtime, true)—jl_get_pgcstack_fallbackis treated as possibly-allocating.Expected:
(:runtime, false)— it never allocates, exactly likejl_get_pgcstackandjl_get_pgcstack_static.After the fix:
Fix
Add
"get_pgcstack_fallback"to the never-allocates tuple infn_may_allocate, right next to"get_pgcstack_static":Precedent
This is the same class of fix as e44be86 ("fix: add get_pgcstack_static to non-allocating allowlist"), which added the
_staticvariant for the same reason.get_pgcstack_fallbackis simply a third pgcstack-fetch variant that the whitelist missed.Downstream report
SciML/FastAlmostBandedMatrices.jl#68 — that package's zero-allocation tests fail only on macOS/aarch64, traced to this false positive.
PR with the one-line fix to follow.