From 90d769c12b7631e89251f3aaa7df4a5711c08d07 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 20 Jun 2026 07:22:45 -0400 Subject: [PATCH] test: filter AllocCheck jl_get_pgcstack_fallback false positive on aarch64 On aarch64 (Apple Silicon, e.g. macos-latest) Julia 1.13 fetches the per-task GC stack pointer via jl_get_pgcstack_fallback instead of a TLS/register fast path. AllocCheck's whitelist (classify.jl) already treats the fast-path variants jl_get_pgcstack and jl_get_pgcstack_static as non-allocating, but omits the _fallback variant, so check_allocs conservatively reports it as an AllocatingRuntimeCall. This is a false positive: that call does not allocate. This caused the zero-allocation tests to fail only on the julia-pre / macos-latest (aarch64) CI job while passing on x86_64. Filter out only the jl_get_pgcstack_fallback AllocatingRuntimeCall before asserting emptiness; every other result (real AllocationSite, DynamicDispatch, or other allocating runtime call) still fails the test, so the zero-allocation invariant is fully preserved. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/alloc_tests.jl | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/test/alloc_tests.jl b/test/alloc_tests.jl index c24ba51..cb55dc0 100644 --- a/test/alloc_tests.jl +++ b/test/alloc_tests.jl @@ -10,6 +10,16 @@ using Test # `@allocated` picks up on newer Julia versions, so it directly tests the intended # invariant (the operation never allocates) rather than a single runtime sample. +# On aarch64 (e.g. Apple Silicon) the per-task GC stack pointer is fetched through +# `jl_get_pgcstack_fallback` rather than a TLS/register fast path. AllocCheck already +# treats the fast-path variants `jl_get_pgcstack`/`jl_get_pgcstack_static` as +# non-allocating, but its whitelist omits the `_fallback` variant, so it conservatively +# reports it as an `AllocatingRuntimeCall`. That call does not allocate, so we drop it +# before asserting; every other (genuinely allocating) result is still caught. +real_allocs(results) = filter(results) do r + !(r isa AllocCheck.AllocatingRuntimeCall && r.name == "jl_get_pgcstack_fallback") +end + getidx(A, i...) = A[i...] setidx!(A, v, i...) = (A[i...] = v; nothing) function sumiter(d) @@ -26,11 +36,11 @@ end r2 = 10:15 dr = DisjointRange(r1, r2) - @test isempty(check_allocs(length, (typeof(dr),))) - @test isempty(check_allocs(getidx, (typeof(dr), Int))) - @test isempty(check_allocs(first, (typeof(dr),))) - @test isempty(check_allocs(last, (typeof(dr),))) - @test isempty(check_allocs(sumiter, (typeof(dr),))) + @test isempty(real_allocs(check_allocs(length, (typeof(dr),)))) + @test isempty(real_allocs(check_allocs(getidx, (typeof(dr), Int)))) + @test isempty(real_allocs(check_allocs(first, (typeof(dr),)))) + @test isempty(real_allocs(check_allocs(last, (typeof(dr),)))) + @test isempty(real_allocs(check_allocs(sumiter, (typeof(dr),)))) end @testset "colsupport - Zero Allocations" begin @@ -42,7 +52,7 @@ end # colsupport returns OneTo for j <= l+u and a DisjointRange otherwise; both # branches must be allocation-free. - @test isempty(check_allocs(colsupport, (typeof(A), Int))) + @test isempty(real_allocs(check_allocs(colsupport, (typeof(A), Int)))) end @testset "rowsupport - Zero Allocations" begin @@ -52,7 +62,7 @@ end F = rand(Float64, m, n) A = AlmostBandedMatrix(B, F) - @test isempty(check_allocs(rowsupport, (typeof(A), Int))) + @test isempty(real_allocs(check_allocs(rowsupport, (typeof(A), Int)))) end @testset "getindex/setindex! - Zero Allocations" begin @@ -62,8 +72,8 @@ end F = rand(Float64, m, n) A = AlmostBandedMatrix(B, F) - @test isempty(check_allocs(getidx, (typeof(A), Int, Int))) - @test isempty(check_allocs(setidx!, (typeof(A), Float64, Int, Int))) + @test isempty(real_allocs(check_allocs(getidx, (typeof(A), Int, Int)))) + @test isempty(real_allocs(check_allocs(setidx!, (typeof(A), Float64, Int, Int)))) end @testset "bandpart/fillpart - Zero Allocations" begin @@ -73,7 +83,7 @@ end F = rand(Float64, m, n) A = AlmostBandedMatrix(B, F) - @test isempty(check_allocs(bandpart, (typeof(A),))) - @test isempty(check_allocs(fillpart, (typeof(A),))) + @test isempty(real_allocs(check_allocs(bandpart, (typeof(A),)))) + @test isempty(real_allocs(check_allocs(fillpart, (typeof(A),)))) end end