diff --git a/test/runtests.jl b/test/runtests.jl index 1d044c8..580e1c5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,12 @@ using JuliaFEM, Test +# Shared zero-allocation / LLVM IR helpers for topic tests (loaded once). +include(joinpath(@__DIR__, "zero_alloc_helpers.jl")) +include(joinpath(@__DIR__, "test_api_contract.jl")) +include(joinpath(@__DIR__, "test_pkg_hygiene.jl")) +include(joinpath(@__DIR__, "test_inference_smoke.jl")) + # The active test tree mirrors `src/`. Every subdirectory below has its # own `runtests.jl` that bundles the topic into a single `@testset`. # Adding a new topic = adding the directory to `topics` below. @@ -20,11 +26,12 @@ const TOPICS = [ "domains/heat", "domains/darcy", "domains/thermo_elastic", + "domains/poroelastic", + "domains/thermo_poroelastic", "dofs", "interface", "assemblers", "sparse", - "io", "validation", "reference", "docs", @@ -35,6 +42,7 @@ const TOPICS = [ # if none match, run the full suite with a warning. # # `Pkg.test(; test_args=["assemblers", ...])` forwards those strings to ARGS. +# Only names from `TOPICS` below are accepted (e.g. `"assemblers"`), not file paths. function _topics_from_args() requested = String[s for s in ARGS if !startswith(s, '-')] isempty(requested) && return TOPICS @@ -84,6 +92,11 @@ end @test LocalField isa Type end + run_api_contract_tests(TOPICS, @__DIR__) + + run_pkg_hygiene_tests() + run_inference_smoke_tests() + for topic in _topics_from_args() include(joinpath(topic, "runtests.jl")) end diff --git a/test/test_api_contract.jl b/test/test_api_contract.jl new file mode 100644 index 0000000..5c8cfa5 --- /dev/null +++ b/test/test_api_contract.jl @@ -0,0 +1,104 @@ +# SPDX-FileCopyrightText: 2015-2026 Jukka Aho +# SPDX-License-Identifier: MIT + +""" +Cross-cutting API contract tests for LLM-heavy workflows. + +1. Every `export` name listed in `src/exports.jl` resolves in `JuliaFEM` + after `using JuliaFEM` (catches dropped bindings / typos in exports). +2. Every directory listed in `TOPICS` in `test/runtests.jl` contains a + `runtests.jl` entry point (catches forgotten wiring when adding topics). + +Call `run_api_contract_tests(TOPICS, test_root_dir)` from `test/runtests.jl` +inside the main `@testset` so results nest correctly (`include` alone would +evaluate child testsets at module scope). +""" + +using Test + +""" +Parse `src/exports.jl` export statements, including comma-continued blocks. + +Each comma-separated token is validated with `Base.Meta.parse`, so Unicode +identifiers (for example `owned_norm²_packed`) and macro exports (`@DOFSet`) +are accepted. Returns a `Vector{Symbol}` of unique exported names (order +preserved). +""" +function _parse_exported_symbols(exports_path::String) + lines = readlines(exports_path) + names = Symbol[] + seen = Set{Symbol}() + i = 1 + while i ≤ length(lines) + raw = lines[i] + stripped = lstrip(raw) + if !startswith(stripped, "export ") + i += 1 + continue + end + rest = stripped[begin+length("export "):end] + hash = findfirst('#', rest) + if hash !== nothing + rest = rest[1:prevind(rest, hash)] + end + buf = IOBuffer() + print(buf, rest) + i += 1 + while i ≤ length(lines) + cont = lines[i] + t = lstrip(cont) + isempty(t) && break + startswith(t, "export ") && break + startswith(t, '#') && break + print(buf, ' ') + h2 = findfirst('#', cont) + chunk = h2 === nothing ? cont : cont[1:prevind(cont, h2)] + print(buf, strip(chunk)) + i += 1 + end + blob = String(take!(buf)) + for part in split(blob, ',') + tok = strip(part) + isempty(tok) && continue + ex = Base.Meta.parse(tok; raise = false) + sym = if ex isa Symbol + ex::Symbol + elseif ex isa Expr && ex.head === :macrocall && length(ex.args) ≥ 1 && ex.args[1] isa Symbol + ex.args[1]::Symbol + else + error("Unexpected export token in exports.jl: $(repr(tok))") + end + if !(sym in seen) + push!(names, sym) + push!(seen, sym) + end + end + end + return names +end + +""" +Run nested `@testset`s under the caller's current testset context. +""" +function run_api_contract_tests(topics, test_root_dir::String) + @testset "TOPICS directories have runtests.jl" begin + for topic in topics + p = joinpath(test_root_dir, topic, "runtests.jl") + @test isfile(p) + end + @test length(topics) == length(unique(topics)) + end + + @testset "exports.jl names are defined on JuliaFEM" begin + exports_path = joinpath(test_root_dir, "..", "src", "exports.jl") + @test isfile(exports_path) + syms = _parse_exported_symbols(exports_path) + @test length(syms) ≥ 200 + missing = [s for s in syms if !isdefined(JuliaFEM, s)] + if !isempty(missing) + @error "exports.jl lists symbols not defined on JuliaFEM" missing = missing + end + @test isempty(missing) + end + return nothing +end diff --git a/test/test_inference_smoke.jl b/test/test_inference_smoke.jl new file mode 100644 index 0000000..587d712 --- /dev/null +++ b/test/test_inference_smoke.jl @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2015-2026 Jukka Aho +# SPDX-License-Identifier: MIT + +""" +Curated `@inferred` checks on small, stable API entry points. + +These catch accidental type-instability regressions on hot metadata paths +without running a whole-package abstract interpretation tool. +""" + +using JuliaFEM +using Tensors: SymmetricTensor, Tensor +using Test + +function run_inference_smoke_tests() + @testset "dof_size inferrable" begin + @test (@inferred dof_size(Float64)) === 1 + @test (@inferred dof_size(Vec{2})) === 2 + @test (@inferred dof_size(Vec{3})) === 3 + @test (@inferred dof_size(Tensor{2, 3})) === 9 + @test (@inferred dof_size(SymmetricTensor{2, 3})) === 6 + end + + @testset "topology counts inferrable" begin + @test (@inferred nnodes(Hex8)) === 8 + @test (@inferred nnodes(Tet4)) === 4 + @test (@inferred dim(Hex8())) === 3 + @test (@inferred dim(Tet4())) === 3 + end + + @testset "DOF set metadata inferrable" begin + S = @DOFSet{u::DOF{Displacement{3}, Vertex}} + @test (@inferred field_count(S)) === 1 + @test (@inferred is_single_field(S)) === true + Sm = @DOFSet{ + T::DOF{Temperature, Vertex}, + u::DOF{Displacement{3}, Vertex}, + } + @test (@inferred field_count(Sm)) === 2 + @test (@inferred is_single_field(Sm)) === false + end + + return nothing +end diff --git a/test/test_pkg_hygiene.jl b/test/test_pkg_hygiene.jl new file mode 100644 index 0000000..4205d40 --- /dev/null +++ b/test/test_pkg_hygiene.jl @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2015-2026 Jukka Aho +# SPDX-License-Identifier: MIT + +""" +Test-only package hygiene via Aqua.jl. + +Ambiguities, unbound-args scanning, piracy detection, and persistent-task +audits are disabled here: they are either too noisy or too slow for a large +numerical codebase on every `Pkg.test`. + +`stale_deps` ignores `CSV`: it is a direct dependency for optional I/O paths +but is not loaded by `using JuliaFEM` alone, which is what Aqua probes. + +`deps_compat` skips compat requirements for standard-library packages listed +in `[deps]` and turns off the extras table: adding compat entries for every +stdlib and test-only extra is policy noise for this repository. +""" + +using Aqua +using Test + +function run_pkg_hygiene_tests(m::Module = JuliaFEM) + Aqua.test_all( + m; + ambiguities = false, + unbound_args = false, + undefined_exports = true, + project_extras = true, + stale_deps = (; ignore = [:CSV]), + deps_compat = (; + ignore = [:LinearAlgebra, :Logging, :SparseArrays], + check_extras = false, + ), + piracies = false, + persistent_tasks = false, + undocumented_names = false, + ) + return nothing +end diff --git a/test/testdata/cantilever_beam.msh b/test/testdata/cantilever_beam.msh deleted file mode 100644 index e46d778..0000000 --- a/test/testdata/cantilever_beam.msh +++ /dev/null @@ -1,989 +0,0 @@ -$MeshFormat -4.1 0 8 -$EndMeshFormat -$PhysicalNames -3 -2 1 "FixedEnd" -2 6 "PressureSurface" -3 1 "Bulk" -$EndPhysicalNames -$Entities -8 12 6 1 -1 0 0 1 0 -2 0 0 0 0 -3 0 1 1 0 -4 0 1 0 0 -5 10 0 1 0 -6 10 0 0 0 -7 10 1 1 0 -8 10 1 0 0 -1 -1e-07 -1e-07 -9.999999994736442e-08 1e-07 1e-07 1.0000001 0 2 2 -1 -2 -1e-07 -9.999999994736442e-08 0.9999999000000001 1e-07 1.0000001 1.0000001 0 2 1 -3 -3 -1e-07 0.9999999000000001 -9.999999994736442e-08 1e-07 1.0000001 1.0000001 0 2 4 -3 -4 -1e-07 -9.999999994736442e-08 -1e-07 1e-07 1.0000001 1e-07 0 2 2 -4 -5 9.999999900000001 -1e-07 -9.999999994736442e-08 10.0000001 1e-07 1.0000001 0 2 6 -5 -6 9.999999900000001 -9.999999994736442e-08 0.9999999000000001 10.0000001 1.0000001 1.0000001 0 2 5 -7 -7 9.999999900000001 0.9999999000000001 -9.999999994736442e-08 10.0000001 1.0000001 1.0000001 0 2 8 -7 -8 9.999999900000001 -9.999999994736442e-08 -1e-07 10.0000001 1.0000001 1e-07 0 2 6 -8 -9 -1.000000002804313e-07 -1e-07 -1e-07 10.0000001 1e-07 1e-07 0 2 2 -6 -10 -1.000000002804313e-07 -1e-07 0.9999999000000001 10.0000001 1e-07 1.0000001 0 2 1 -5 -11 -1.000000002804313e-07 0.9999999000000001 -1e-07 10.0000001 1.0000001 1e-07 0 2 4 -8 -12 -1.000000002804313e-07 0.9999999000000001 0.9999999000000001 10.0000001 1.0000001 1.0000001 0 2 3 -7 -1 -1e-07 -9.999999994736442e-08 -9.999999994736442e-08 1e-07 1.0000001 1.0000001 1 1 4 -1 4 3 -2 -2 9.999999900000001 -9.999999994736442e-08 -9.999999994736442e-08 10.0000001 1.0000001 1.0000001 0 4 -5 8 7 -6 -3 -1.000000002804313e-07 -1e-07 -9.999999994736442e-08 10.0000001 1e-07 1.0000001 0 4 -9 1 10 -5 -4 -1.000000002804313e-07 0.9999999000000001 -9.999999994736442e-08 10.0000001 1.0000001 1.0000001 0 4 -11 3 12 -7 -5 -1.000000002804313e-07 -9.999999994736442e-08 -1e-07 10.0000001 1.0000001 1e-07 0 4 -4 9 8 -11 -6 -1.000000002804313e-07 -9.999999994736442e-08 0.9999999000000001 10.0000001 1.0000001 1.0000001 1 2 4 -2 10 6 -12 -1 -1.000000002804313e-07 -9.999999994736442e-08 -9.999999994736442e-08 10.0000001 1.0000001 1.0000001 1 3 6 -1 2 -3 4 -5 6 -$EndEntities -$Nodes -27 190 1 190 -0 1 0 1 -1 -0 0 1 -0 2 0 1 -2 -0 0 0 -0 3 0 1 -3 -0 1 1 -0 4 0 1 -4 -0 1 0 -0 5 0 1 -5 -10 0 1 -0 6 0 1 -6 -10 0 0 -0 7 0 1 -7 -10 1 1 -0 8 0 1 -8 -10 1 0 -1 1 0 1 -9 -0 0 0.5 -1 2 0 1 -10 -0 0.5 1 -1 3 0 1 -11 -0 1 0.5 -1 4 0 1 -12 -0 0.5 0 -1 5 0 1 -13 -10 0 0.5 -1 6 0 1 -14 -10 0.5 1 -1 7 0 1 -15 -10 1 0.5 -1 8 0 1 -16 -10 0.5 0 -1 9 0 19 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -0.5 0 0 -1 0 0 -1.5 0 0 -2 0 0 -2.5 0 0 -3 0 0 -3.5 0 0 -4 0 0 -4.5 0 0 -5 0 0 -5.5 0 0 -6 0 0 -6.5 0 0 -7 0 0 -7.5 0 0 -8 0 0 -8.5 0 0 -9 0 0 -9.5 0 0 -1 10 0 19 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -0.5 0 1 -1 0 1 -1.5 0 1 -2 0 1 -2.5 0 1 -3 0 1 -3.5 0 1 -4 0 1 -4.5 0 1 -5 0 1 -5.5 0 1 -6 0 1 -6.5 0 1 -7 0 1 -7.5 0 1 -8 0 1 -8.5 0 1 -9 0 1 -9.5 0 1 -1 11 0 19 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -0.5 1 0 -1 1 0 -1.5 1 0 -2 1 0 -2.5 1 0 -3 1 0 -3.5 1 0 -4 1 0 -4.5 1 0 -5 1 0 -5.5 1 0 -6 1 0 -6.5 1 0 -7 1 0 -7.5 1 0 -8 1 0 -8.5 1 0 -9 1 0 -9.5 1 0 -1 12 0 19 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -0.5 1 1 -1 1 1 -1.5 1 1 -2 1 1 -2.5 1 1 -3 1 1 -3.5 1 1 -4 1 1 -4.5 1 1 -5 1 1 -5.5 1 1 -6 1 1 -6.5 1 1 -7 1 1 -7.5 1 1 -8 1 1 -8.5 1 1 -9 1 1 -9.5 1 1 -2 1 0 4 -93 -94 -95 -96 -0 0.75 0.75 -0 0.625 0.3749999999999999 -0 0.28125 0.2812500000000001 -0 0.3457031249999999 0.6542968750000001 -2 2 0 4 -97 -98 -99 -100 -10 0.75 0.75 -10 0.625 0.3749999999999999 -10 0.28125 0.2812500000000001 -10 0.3457031249999999 0.6542968750000001 -2 3 0 22 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -1.75 0 0.5069585776743641 -0.75 0 0.5669872981077807 -2.749380588907528 0 0.5238365255897386 -3.749132824470539 0 0.4823627640453123 -4.750867175529461 0 0.4823627640453123 -7.251506891338066 0 0.4995938179975555 -9.25 0 0.4330127018922193 -5.750619411092472 0 0.5238365255897386 -8.251734351058921 0 0.5191392280307945 -1.25 0 0.48123057673124 -2.249926260584229 0 0.4890493032942006 -6.25020208203005 0 0.4813856891854416 -4.25 0 0.4941209213484373 -6.750284828894686 0 0.4968299178638328 -8.75371528672418 0 0.5225617672120302 -5.250247764436988 0 0.5010332149391751 -7.750540207066164 0 0.5031221743380583 -3.249752235563011 0 0.5010332149391751 -0.3287258330795123 0 0.3668411415816576 -9.671274166920488 0 0.6331588584183425 -0.289906066861805 0 0.7110089675965732 -9.710093933138193 0 0.2889910324034273 -2 4 0 22 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -1.25 1 0.5227636741068051 -2.249380588907528 1 0.5238365255897386 -3.249132824470539 1 0.4823627640453123 -4.25 1 0.47858229244173 -5.250867175529461 1 0.4823627640453123 -6.250619411092472 1 0.5224905590412035 -7.25 1 0.52141770755827 -8.25 1 0.52141770755827 -9.25 1 0.5669872981077807 -0.75 1 0.4330127018922193 -1.749926260584229 1 0.4909308623933007 -6.75007373941577 1 0.4860879465643809 -7.75 1 0.485960226149746 -8.75 1 0.4747073272589484 -3.74985547074509 1 0.4934908427478403 -4.75014452925491 1 0.4934908427478403 -5.750247764436988 1 0.5008088871810858 -2.749752235563011 1 0.5010332149391751 -0.3287258330795123 1 0.6331588584183424 -9.671274166920488 1 0.3668411415816575 -9.710093933138195 1 0.7110089675965732 -0.2899060668618049 1 0.2889910324034266 -2 5 0 22 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -2.249132824470539 0.5176372359546877 0 -3.750619411092472 0.5238365255897387 0 -4.751214045741245 0.5005353055285795 0 -6.751734351058921 0.5191392280307945 0 -0.75 0.5669872981077807 0 -5.751734351058921 0.4808607719692055 0 -7.750619411092472 0.4761634744102614 0 -8.75 0.4930414223256358 0 -1.249380588907528 0.4775094409587964 0 -2.751069516486335 0.4938777785477007 0 -4.250218268670681 0.4882846280387501 0 -8.25007373941577 0.5109506967057993 0 -9.25 0.5511587314970074 0 -1.749752235563011 0.499191112818914 0 -5.250491399466694 0.4968993462496308 0 -7.250392293691899 0.4992171170735092 0 -3.250281487929801 0.5029523840229065 0 -6.25057811701964 0.5 0 -0.3287258330795123 0.3668411415816576 0 -9.672020287404903 0.363394611684695 0 -9.703490779236464 0.7027288461524623 0 -0.289906066861805 0.7110089675965734 0 -2 6 0 22 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -2.25 0.518123521556161 1 -0.75 0.5669872981077807 1 -3.250619411092472 0.4761634744102612 1 -4.25 0.4930414223256358 1 -5.25 0.4930414223256358 1 -6.25 0.4930414223256358 1 -7.25 0.4772363258931948 1 -8.25 0.4772363258931948 1 -9.25 0.4330127018922193 1 -1.249380588907528 0.4775094409587964 1 -3.75007373941577 0.5109506967057993 1 -4.75 0.4976804741085452 1 -5.75 0.4976804741085452 1 -6.75 0.5110784171204343 1 -7.75 0.5137193056244123 1 -8.75 0.5174254524836885 1 -1.749896764817921 0.4992721604191595 1 -2.750103235182078 0.4990478326610704 1 -9.671274166920488 0.6331588584183425 1 -0.3287258330795123 0.3668411415816576 1 -9.710093933138195 0.2889910324034269 1 -0.289906066861805 0.7110089675965734 1 -3 1 0 2 -189 -190 -9.510944495345575 0.4892527009302248 0.5185495759302249 -0.4890555046544248 0.4814504240697751 0.510747299069775 -$EndNodes -$Elements -3 534 1 534 -2 1 2 14 -1 9 1 96 -2 1 10 96 -3 2 9 95 -4 12 2 95 -5 10 3 93 -6 3 11 93 -7 11 4 94 -8 4 12 94 -9 95 9 96 -10 10 93 96 -11 93 11 94 -12 94 12 95 -13 93 94 96 -14 94 95 96 -2 6 2 86 -15 1 186 10 -16 36 186 1 -17 10 188 3 -18 3 188 74 -19 14 187 5 -20 5 187 54 -21 7 185 14 -22 92 185 7 -23 186 188 10 -24 185 187 14 -25 37 168 36 -26 168 186 36 -27 38 176 37 -28 37 176 168 -29 39 183 38 -30 38 183 176 -31 40 167 39 -32 167 183 39 -33 41 184 40 -34 40 184 167 -35 42 169 41 -36 169 184 41 -37 43 177 42 -38 42 177 169 -39 44 170 43 -40 170 177 43 -41 45 178 44 -42 44 178 170 -43 46 171 45 -44 171 178 45 -45 47 179 46 -46 46 179 171 -47 48 172 47 -48 172 179 47 -49 49 180 48 -50 48 180 172 -51 50 173 49 -52 173 180 49 -53 51 181 50 -54 50 181 173 -55 52 174 51 -56 174 181 51 -57 53 182 52 -58 52 182 174 -59 54 175 53 -60 175 182 53 -61 54 187 175 -62 74 168 75 -63 74 188 168 -64 75 176 76 -65 168 176 75 -66 76 183 77 -67 176 183 76 -68 77 167 78 -69 77 183 167 -70 78 184 79 -71 167 184 78 -72 79 169 80 -73 79 184 169 -74 80 177 81 -75 169 177 80 -76 81 170 82 -77 81 177 170 -78 82 178 83 -79 170 178 82 -80 83 171 84 -81 83 178 171 -82 84 179 85 -83 171 179 84 -84 85 172 86 -85 85 179 172 -86 86 180 87 -87 172 180 86 -88 87 173 88 -89 87 180 173 -90 88 181 89 -91 173 181 88 -92 89 174 90 -93 89 181 174 -94 90 182 91 -95 174 182 90 -96 91 175 92 -97 91 182 175 -98 175 185 92 -99 168 188 186 -100 175 187 185 -3 1 4 434 -101 147 138 178 171 -102 155 113 147 178 -103 140 118 125 184 -104 138 155 147 178 -105 162 180 172 114 -106 127 116 147 159 -107 127 139 179 159 -108 127 179 116 159 -109 183 158 145 101 -110 155 113 178 170 -111 140 118 154 125 -112 140 103 118 184 -113 113 105 147 178 -114 178 138 155 170 -115 116 127 147 171 -116 114 160 173 106 -117 158 183 145 133 -118 171 179 116 127 -119 112 162 172 114 -120 154 161 125 118 -121 167 111 140 184 -122 108 179 139 159 -123 179 108 116 159 -124 134 180 172 162 -125 123 158 133 183 -126 103 140 118 154 -127 135 117 181 106 -128 147 138 171 127 -129 155 177 113 170 -130 148 180 162 114 -131 111 154 145 140 -132 183 145 111 101 -133 103 140 111 184 -134 118 125 104 161 -135 111 154 140 103 -136 172 128 134 162 -137 125 104 161 137 -138 180 148 162 134 -139 155 177 104 113 -140 125 118 169 184 -141 177 155 104 137 -142 160 173 148 114 -143 126 155 138 170 -144 159 139 108 150 -145 177 126 155 137 -146 126 177 155 170 -147 130 117 181 135 -148 124 133 145 183 -149 111 124 183 167 -150 124 111 183 145 -151 140 145 111 124 -152 146 161 104 137 -153 111 140 124 167 -154 115 152 136 189 -155 148 173 180 114 -156 104 137 155 146 -157 130 109 156 152 -158 177 137 104 169 -159 125 118 104 169 -160 181 174 117 130 -161 110 176 123 190 -162 123 153 110 190 -163 110 158 153 123 -164 125 104 137 169 -165 136 182 115 189 -166 109 130 117 174 -167 109 130 136 152 -168 136 115 109 152 -169 109 174 182 130 -170 130 182 109 136 -171 115 136 109 182 -172 157 136 152 189 -173 123 132 153 190 -174 152 115 107 189 -175 136 131 182 189 -176 123 176 168 190 -177 110 102 176 190 -178 182 175 115 189 -179 132 149 153 190 -180 152 107 157 189 -181 110 153 102 190 -182 136 157 131 189 -183 132 123 168 190 -184 175 182 131 189 -185 149 102 153 190 -186 168 176 102 190 -187 175 107 115 189 -188 171 138 178 83 -189 113 177 43 170 -190 178 82 138 170 -191 79 140 125 184 -192 39 183 111 101 -193 177 113 43 104 -194 178 113 44 170 -195 83 171 138 127 -196 114 30 160 106 -197 111 167 183 39 -198 178 171 45 116 -199 82 126 138 170 -200 139 127 65 159 -201 65 139 159 150 -202 105 113 44 178 -203 169 79 125 184 -204 162 139 66 150 -205 45 105 178 116 -206 137 126 81 177 -207 177 126 81 170 -208 66 128 139 162 -209 180 48 172 114 -210 161 154 22 118 -211 109 156 152 33 -212 112 29 162 114 -213 30 160 148 114 -214 148 162 29 114 -215 167 140 78 184 -216 125 60 154 140 -217 112 172 48 114 -218 64 127 147 159 -219 154 161 60 125 -220 127 147 138 64 -221 115 109 152 33 -222 139 85 179 128 -223 158 110 19 101 -224 103 22 154 118 -225 151 160 31 106 -226 85 172 179 128 -227 114 173 49 106 -228 151 109 117 32 -229 23 104 161 118 -230 31 117 151 106 -231 104 118 42 169 -232 59 145 154 140 -233 140 167 78 124 -234 148 68 134 160 -235 129 134 68 160 -236 173 49 180 114 -237 151 156 109 32 -238 133 124 77 183 -239 134 67 148 162 -240 42 177 104 169 -241 19 110 158 153 -242 67 134 128 162 -243 69 135 129 160 -244 154 21 111 145 -245 135 69 151 160 -246 153 18 110 102 -247 76 123 133 183 -248 167 77 124 183 -249 154 111 21 103 -250 59 145 140 124 -251 117 51 174 181 -252 176 183 38 101 -253 104 23 161 146 -254 158 145 58 133 -255 51 109 117 174 -256 47 179 172 108 -257 104 113 24 155 -258 123 76 176 183 -259 38 110 176 101 -260 108 172 47 112 -261 133 145 58 124 -262 155 24 104 146 -263 130 181 89 135 -264 18 153 149 102 -265 174 181 89 130 -266 144 166 55 190 -267 90 182 174 130 -268 90 182 130 136 -269 153 56 132 123 -270 56 149 153 132 -271 107 115 152 34 -272 157 136 72 152 -273 157 165 142 189 -274 152 157 107 34 -275 131 182 91 136 -276 37 176 110 102 -277 168 123 75 176 -278 175 115 53 182 -279 144 55 132 190 -280 149 55 166 190 -281 131 136 72 157 -282 122 164 35 189 -283 175 182 91 131 -284 142 131 157 189 -285 188 96 93 190 -286 132 123 75 168 -287 168 176 37 102 -288 149 17 102 190 -289 141 74 188 190 -290 175 53 115 107 -291 120 99 122 189 -292 100 99 120 189 -293 164 122 99 189 -294 149 163 17 190 -295 141 188 93 190 -296 185 92 143 189 -297 163 119 17 190 -298 143 98 97 189 -299 92 185 175 189 -300 119 102 17 190 -301 163 95 119 190 -302 185 143 97 189 -303 97 100 185 189 -304 142 73 165 157 -305 95 96 119 190 -306 120 107 54 189 -307 36 186 168 190 -308 164 99 98 189 -309 141 93 94 190 -310 186 96 188 190 -311 187 120 54 189 -312 186 36 121 190 -313 163 94 95 190 -314 149 17 18 102 -315 149 132 55 190 -316 122 35 107 189 -317 56 153 57 123 -318 73 142 131 157 -319 80 79 125 169 -320 39 111 167 40 -321 43 177 104 42 -322 98 143 142 189 -323 171 83 84 127 -324 74 168 188 190 -325 126 82 81 170 -326 144 94 166 190 -327 33 109 156 32 -328 180 49 48 114 -329 98 142 165 189 -330 66 67 128 162 -331 68 69 129 160 -332 158 19 20 101 -333 139 65 66 150 -334 77 76 133 183 -335 51 117 50 181 -336 178 44 105 45 -337 59 154 60 140 -338 84 85 179 139 -339 29 30 148 114 -340 30 31 160 106 -341 79 78 140 184 -342 138 82 178 83 -343 116 159 27 26 -344 98 165 164 189 -345 172 48 47 112 -346 154 21 22 103 -347 147 63 138 64 -348 93 96 94 190 -349 92 131 143 189 -350 94 144 141 190 -351 171 46 45 116 -352 161 22 23 118 -353 92 175 131 189 -354 43 44 113 170 -355 70 71 130 156 -356 21 20 111 145 -357 97 98 100 189 -358 163 166 94 190 -359 180 86 87 134 -360 105 25 147 26 -361 69 70 135 151 -362 183 39 38 101 -363 36 168 102 190 -364 175 54 107 189 -365 100 187 185 189 -366 121 119 96 190 -367 188 10 93 96 -368 161 61 60 125 -369 33 152 115 34 -370 181 88 89 135 -371 81 80 137 177 -372 100 120 187 189 -373 121 96 186 190 -374 24 23 104 146 -375 145 59 58 124 -376 58 57 158 133 -377 64 65 127 159 -378 63 62 155 126 -379 28 108 150 27 -380 113 25 24 155 -381 173 87 88 129 -382 172 85 86 128 -383 90 174 89 130 -384 36 102 121 190 -385 187 54 175 189 -386 31 151 117 32 -387 41 42 118 169 -388 103 40 41 184 -389 50 49 173 106 -390 179 47 46 108 -391 28 29 162 112 -392 98 99 100 189 -393 146 62 61 137 -394 157 164 165 189 -395 182 90 91 136 -396 94 96 95 190 -397 120 100 13 99 -398 132 141 144 190 -399 76 75 123 176 -400 38 176 110 37 -401 52 53 115 182 -402 95 119 2 163 -403 13 120 99 122 -404 97 143 15 98 -405 163 149 166 190 -406 78 77 124 167 -407 67 68 134 148 -408 71 72 136 152 -409 131 142 143 189 -410 10 188 186 96 -411 51 52 109 174 -412 72 73 131 157 -413 9 119 95 96 -414 185 14 100 97 -415 107 120 122 189 -416 168 186 188 190 -417 110 19 18 153 -418 35 107 157 34 -419 143 15 98 142 -420 168 37 36 102 -421 75 74 132 168 -422 91 92 175 131 -423 175 54 53 107 -424 16 98 164 99 -425 175 185 187 189 -426 119 121 102 190 -427 163 12 95 94 -428 8 98 142 165 -429 93 94 11 141 -430 120 5 100 187 -431 1 186 121 96 -432 99 164 6 122 -433 119 9 121 96 -434 187 14 100 185 -435 15 8 98 142 -436 16 98 165 164 -437 100 13 5 120 -438 186 1 10 96 -439 94 144 11 141 -440 12 163 166 94 -441 95 2 12 163 -442 119 2 9 95 -443 6 16 164 99 -444 3 93 141 188 -445 7 185 143 97 -446 4 166 144 94 -447 11 3 93 141 -448 185 7 14 97 -449 56 55 149 132 -450 98 8 16 165 -451 17 2 119 163 -452 144 11 4 94 -453 166 4 12 94 -454 5 14 100 187 -455 121 9 1 96 -456 99 6 13 122 -457 93 3 10 188 -458 143 15 7 97 -459 122 164 6 35 -460 5 120 54 187 -461 1 186 36 121 -462 7 185 92 143 -463 74 3 141 188 -464 8 142 73 165 -465 4 166 55 144 -466 129 135 173 160 -467 106 173 135 160 -468 106 135 173 181 -469 151 106 135 160 -470 135 106 151 117 -471 147 113 25 105 -472 147 25 113 155 -473 101 145 20 158 -474 101 20 145 111 -475 134 172 86 128 -476 134 86 172 180 -477 178 116 147 171 -478 147 116 178 105 -479 127 179 84 171 -480 127 84 179 139 -481 152 130 71 156 -482 152 71 130 136 -483 108 159 27 116 -484 27 159 108 150 -485 101 158 123 183 -486 123 158 101 110 -487 123 176 101 183 -488 101 176 123 110 -489 155 137 62 146 -490 155 62 137 126 -491 116 147 26 105 -492 26 147 116 159 -493 184 111 40 167 -494 184 40 111 103 -495 179 128 108 139 -496 108 128 179 172 -497 138 155 63 147 -498 63 155 138 126 -499 106 181 50 117 -500 106 50 181 173 -501 179 116 46 171 -502 46 116 179 108 -503 137 169 80 125 -504 80 169 137 177 -505 160 134 173 129 -506 160 173 134 148 -507 180 134 173 148 -508 118 184 41 169 -509 41 184 118 103 -510 123 158 57 133 -511 123 57 158 153 -512 137 161 61 146 -513 137 61 161 125 -514 130 135 151 117 -515 130 109 151 156 -516 151 109 130 117 -517 109 182 52 115 -518 52 182 109 174 -519 35 189 157 107 -520 157 189 35 164 -521 190 74 132 141 -522 190 132 74 168 -523 135 173 88 129 -524 135 88 173 181 -525 162 128 108 172 -526 108 112 162 172 -527 162 139 108 128 -528 108 139 162 150 -529 134 173 87 180 -530 134 87 173 129 -531 151 130 70 135 -532 70 130 151 156 -533 162 108 28 112 -534 28 108 162 150 -$EndElements diff --git a/test/zero_alloc_helpers.jl b/test/zero_alloc_helpers.jl new file mode 100644 index 0000000..eb6c0d9 --- /dev/null +++ b/test/zero_alloc_helpers.jl @@ -0,0 +1,76 @@ +# SPDX-FileCopyrightText: 2015-2026 Jukka Aho +# SPDX-License-Identifier: MIT + +""" +Shared helpers for zero-allocation regression tests. + +`@allocated` can miss some paths or mask one-shot caches; scanning optimized +LLVM IR for Julia GC entrypoints catches heap traffic inside compiled code. +Names differ across Julia versions (`jl_*` vs `ijl_*`, `julia.gc_alloc`, …); +`count_llvm_gc_alloc_sites` tracks the union used in practice. + +Note: `Pkg.test` runs Julia with `--check-bounds=yes` (`JLOptions().check_bounds == 1`). +Some small indexed kernels then show GC-related `call` lines in optimized IR even when +`@allocated` is zero. Prefer `@allocated` plus LLVM scans for large stable kernels +(`assemble!`, `apply_K!`, …); for tight index loops, gate IR scans or accept that CI +skips the IR assertion. +""" + +using InteractiveUtils: code_llvm + +# Every needle must appear only inside a `call` line in IR we care about. +const _LLVM_GC_ALLOC_MARKERS = ( + "julia.gc_alloc", + "jl_gc_pool_alloc", + "jl_gc_big_alloc", + "jl_gc_alloc_typed", + "jl_gc_small_alloc", + "ijl_gc_pool_alloc", + "ijl_gc_big_alloc", + "ijl_gc_alloc_typed", + "ijl_gc_small_alloc", +) + +""" + count_llvm_gc_alloc_sites(ir::AbstractString) -> Int + +Count LLVM `call` lines that invoke a Julia GC allocation runtime function. +Used on the output of `llvm_ir` / `InteractiveUtils.code_llvm`. +""" +function count_llvm_gc_alloc_sites(ir::AbstractString) + n = 0 + for line in eachsplit(ir, '\n') + occursin("call ", line) || continue + for needle in _LLVM_GC_ALLOC_MARKERS + if occursin(needle, line) + n += 1 + break + end + end + end + return n +end + +""" + llvm_ir(f, ::Type{<:Tuple}; optimize::Bool=true) -> String + +Optimized LLVM IR for `f` at argument tuple type `T`, as a single string. +""" +function llvm_ir(@nospecialize(f), @nospecialize(T::Type{<:Tuple}); optimize::Bool = true) + io = IOBuffer() + code_llvm(io, f, T; optimize = optimize) + return String(take!(io)) +end + +""" + llvm_gc_alloc_site_count(f, ::Type{<:Tuple}; optimize::Bool=true) -> Int + +Convenience: `count_llvm_gc_alloc_sites` ∘ `llvm_ir`. +""" +function llvm_gc_alloc_site_count( + @nospecialize(f), + @nospecialize(T::Type{<:Tuple}); + optimize::Bool = true, +) + return count_llvm_gc_alloc_sites(llvm_ir(f, T; optimize = optimize)) +end