diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 30bf439b9a..3b88467e71 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2197,6 +2197,131 @@ jobs: if: ${{ !cancelled() }} run: python3 -m unittest tests.test_native_abi_evidence_report + # #10782: pay auto-optimize's cold runtime builds HERE, under a budget of + # their own, instead of inside whichever gate step below happens to + # compile first. + # + # `perry compile` resolves `optimized_libs` unconditionally + # (run_pipeline.rs:6169), so the first LINKING compile in this job blocks + # on a nested `cargo build --release -p perry-runtime-static -p + # perry-stdlib-static` into `target/perry-auto-/`. That build does + # not fit the 300s `--compile-timeout` every gate step below runs under, + # and the failure surfaces as `TimeoutExpired` on whichever workload + # happened to compile first (`h1_native_rep_equivalence`, the head of + # both suites). `PERRY_RUNTIME_DIR` does not suppress the rebuild. + # + # TWO cold builds, not one. Every gated workload shares a single + # `target/perry-auto-/`, because the directory hash does not + # include the cross-feature set -- but the BUILD STAMP inside it does, + # and a stamp mismatch re-runs cargo. The harness + # sets `PERRY_GC_TRACE=1` only for workloads carrying `*_traced` runtime + # budgets, and `PERRY_GC_TRACE` adds `perry-runtime/diagnostics` + # (optimized_libs/freshness.rs). `loop_bound_semantics` is the one gated + # workload with no `*_traced` budget, so it is the one compile in the job + # that wants the OTHER feature set. Measured with a debug compiler on an + # M-series mini: 244.6s for the diagnostics variant and 242.1s for the + # non-diagnostics one, each against a 300s budget; every later flip + # between the two is ~4s, because cargo keeps both variants' artifacts. + # Warming only the first variant just moves the timeout from + # `h1_native_rep_equivalence` to `loop_bound_semantics`. + # + # This goes through the HARNESS rather than a bare `perry compile` + # precisely because the harness is what decides `PERRY_GC_TRACE` per + # workload: a hand-rolled compile would warm a stamp no gate step wants. + # `--skip-run` keeps it to the compile; the gate steps own the execution + # and the verdicts, and keep their 300s and their subjects unchanged. + # + # The trap this closes: a pre-warm that silently no-ops is WORSE than no + # pre-warm, because the gates then fail exactly as they do today while + # looking like they were warmed. The two assertions below are the gate on + # the gate. + - name: Pre-warm auto-optimized runtime + if: ${{ !cancelled() && steps.compiler_output_build.outcome == 'success' }} + run: | + set -euo pipefail + prewarm() { + python3 scripts/compiler_output_regression.py capture \ + --perry target/debug/perry \ + --workload "$1" \ + --benchmark-mode smoke \ + --runs 1 \ + --perf-counters off \ + --skip-run \ + --compile-timeout 2400 \ + --out-dir "target/compiler-output-regression/$2" + } + + # Variant A -- `*_traced` budgets, so PERRY_GC_TRACE=1, so + # +perry-runtime/diagnostics. Every gated compile in the job but one. + prewarm h1_native_rep_equivalence prewarm-trace + # Variant B -- no `*_traced` budget, so no PERRY_GC_TRACE. + prewarm loop_bound_semantics prewarm-notrace + # Flip back to A. This leaves the stamp on the variant almost every + # gate step wants, AND it is the liveness probe: a flip is only cheap + # if BOTH variants are in cargo's cache, so its duration is what the + # second assertion below reads. + prewarm h1_native_rep_equivalence prewarm-verify + + # Liveness assertion -- the point of the step, not decoration. + # + # `perry compile` EXITS 0 when auto-optimize fails: the driver prints + # "using prebuilt libraries" and returns `OptimizedLibs::empty()` + # (optimized_libs/driver.rs). So the three captures above exiting 0 + # says nothing about whether anything was warmed, and a pre-warm that + # quietly warmed nothing is worse than no pre-warm at all -- the + # gates below then fail exactly as they do today while the log shows + # a green "Pre-warm" step above them. + # + # Two things are required, and neither alone is enough. Cargo creates + # `target/perry-auto-/` (and a zero-byte + # `.perry-auto-build.lock`) BEFORE it builds anything into it, so the + # directory existing proves nothing; and the build stamp is written + # only after `cargo build` reports success + # (driver.rs, `write(&build_stamp_path, ...)` past the + # `status.success()` check), which is also what the NEXT compile + # consults for freshness. Require a stamped directory holding a + # non-empty `libperry_runtime.a`. + warm=$( + for dir in target/perry-auto-*/; do + [ -s "${dir}.perry-auto-build.stamp" ] || continue + find "$dir" -maxdepth 3 -name 'libperry_runtime.a' -size +0c 2>/dev/null + done + ) || true + if [ -z "$warm" ]; then + echo "::error::pre-warm warmed nothing: no target/perry-auto-*/ holds both a build stamp and a non-empty libperry_runtime.a. Auto-optimize fell back to prebuilt libraries (perry exits 0 when it does), so every gate step below would pay the cold runtime build inside its 300s --compile-timeout." >&2 + echo "--- target/perry-auto-* ---" >&2 + ls -la target/perry-auto-*/ 2>&1 | head -60 >&2 || true + exit 1 + fi + echo "pre-warmed auto-optimized runtime:" + printf '%s\n' "$warm" | while read -r archive; do ls -l "$archive"; done + + # Second assertion: warm, not merely present. The check above proves + # a runtime was built; it cannot tell one warmed variant from two, + # and a job warmed for only one feature set fails at + # `loop_bound_semantics` instead of at `h1_native_rep_equivalence`. + # The third pre-warm above flipped the stamp back to variant A, which + # re-runs cargo -- that is cheap ONLY if variant B is also cached. So + # require that flip to have fitted comfortably inside the budget the + # gate steps get. A cold build here is ~240s; a warm flip is seconds. + python3 - <<'EOF' + import json, sys + budget_s = 300 # every gate step's --compile-timeout + limit_s = budget_s / 2 + m = json.load(open("target/compiler-output-regression/prewarm-verify/manifest.json")) + took = m["commands"]["compile"]["duration_ms"] / 1000 + print(f"post-warm flip compile: {took:.1f}s (must be < {limit_s:.0f}s)") + if took >= limit_s: + print( + f"::error::pre-warm left the runtime cold: a compile after the " + f"warm-up still took {took:.1f}s, more than half the {budget_s}s " + f"--compile-timeout every gate step below runs under. Auto-optimize " + f"is still rebuilding, so a gate step will time out.", + file=sys.stderr, + ) + sys.exit(1) + EOF + - name: Gate native-region proof compiler output if: ${{ !cancelled() && steps.compiler_output_build.outcome == 'success' }} run: | diff --git a/CLAUDE.md b/CLAUDE.md index a0076624b2..646b769fd3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1621 +**Current Version:** 0.5.1622 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index 96325b1467..ba5708dc77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5543,7 +5543,7 @@ checksum = "1473d470930ed48574515a25df34900f3af89c6fa422d903e019121312a9f13e" [[package]] name = "perry" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "base64 0.22.1", @@ -5607,7 +5607,7 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-dispatch", "serde", @@ -5615,7 +5615,7 @@ dependencies = [ [[package]] name = "perry-audio-miniaudio" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "cc", "libc", @@ -5624,7 +5624,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "aho-corasick", "anyhow", @@ -5641,7 +5641,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-hir", @@ -5649,7 +5649,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-hir", @@ -5657,7 +5657,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-dispatch", @@ -5666,7 +5666,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-hir", @@ -5674,7 +5674,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "base64 0.22.1", @@ -5686,7 +5686,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-hir", @@ -5694,7 +5694,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "async-trait", "clap", @@ -5718,14 +5718,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "serde", "serde_json", @@ -5733,7 +5733,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1621" +version = "0.5.1622" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5744,7 +5744,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "clap", @@ -5759,7 +5759,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "block2", "objc2", @@ -5769,7 +5769,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "argon2", "perry-ffi", @@ -5778,7 +5778,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "bcrypt", "perry-ffi", @@ -5786,7 +5786,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "rusqlite", @@ -5794,7 +5794,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "scraper", @@ -5802,7 +5802,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "rust_decimal", @@ -5810,7 +5810,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "rand 0.10.2", @@ -5818,7 +5818,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "perry-runtime", @@ -5826,7 +5826,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "bytes", "lazy_static", @@ -5839,7 +5839,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "bytes", @@ -5871,7 +5871,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "lazy_static", "perry-ffi", @@ -5881,7 +5881,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "bson", "futures-util", @@ -5893,7 +5893,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "chrono", "perry-ffi", @@ -5905,7 +5905,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "bytes", "perry-ffi", @@ -5920,7 +5920,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "lettre", "perry-ffi", @@ -5930,7 +5930,7 @@ dependencies = [ [[package]] name = "perry-ext-parcel-watcher" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "notify", "perry-ffi", @@ -5942,7 +5942,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "printpdf", @@ -5950,7 +5950,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "sqlx", @@ -5959,7 +5959,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "fast_image_resize", "image", @@ -5970,7 +5970,7 @@ dependencies = [ [[package]] name = "perry-ext-streams" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "lazy_static", "perry-ffi", @@ -5979,7 +5979,7 @@ dependencies = [ [[package]] name = "perry-ext-typescript" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-ffi", @@ -5999,7 +5999,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-ffi", "perry-runtime", @@ -6008,7 +6008,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "futures-util", "lazy_static", @@ -6021,7 +6021,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "brotli", "flate2", @@ -6031,7 +6031,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "dashmap 6.2.1", "once_cell", @@ -6041,7 +6041,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-api-manifest", @@ -6061,11 +6061,11 @@ dependencies = [ [[package]] name = "perry-native-registration" -version = "0.5.1621" +version = "0.5.1622" [[package]] name = "perry-parser" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "perry-diagnostics", @@ -6078,7 +6078,7 @@ dependencies = [ [[package]] name = "perry-perex" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perex", "regex", @@ -6086,7 +6086,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "ahash", "base64 0.22.1", @@ -6144,14 +6144,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6234,21 +6234,21 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "perry-hir", ] [[package]] name = "perry-ui" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "dirs", "perry-ffi", @@ -6258,7 +6258,7 @@ dependencies = [ [[package]] name = "perry-ui-android" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "jni", @@ -6273,7 +6273,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "rand 0.10.2", "serde", @@ -6283,7 +6283,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "cairo-rs 0.22.9", @@ -6306,7 +6306,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "block2", @@ -6323,7 +6323,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "block2", @@ -6340,7 +6340,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1621" +version = "0.5.1622" [[package]] name = "perry-ui-test" @@ -6351,11 +6351,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1621" +version = "0.5.1622" [[package]] name = "perry-ui-tvos" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "block2", @@ -6372,7 +6372,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "block2", @@ -6389,7 +6389,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "block2", "libc", @@ -6403,7 +6403,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "libc", @@ -6422,7 +6422,7 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "base64 0.22.1", "libc", @@ -6435,7 +6435,7 @@ dependencies = [ [[package]] name = "perry-updater" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "anyhow", "base64 0.22.1", @@ -6450,7 +6450,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1621" +version = "0.5.1622" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 24bc1b26cd..3aaa869ead 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -317,7 +317,7 @@ codegen-units = 1 codegen-units = 1 [workspace.package] -version = "0.5.1621" +version = "0.5.1622" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/changelog.d/10782-prewarm-compiler-output.md b/changelog.d/10782-prewarm-compiler-output.md new file mode 100644 index 0000000000..d374ee7942 --- /dev/null +++ b/changelog.d/10782-prewarm-compiler-output.md @@ -0,0 +1,81 @@ +Gave the `compiler-output-regression` job's cold auto-optimize runtime builds a +budget of their own, closing the residual #10782 left behind. The job is a +`main-gate` input, so while it was red every merge bypassed it. + +`perry compile` resolves `optimized_libs` unconditionally +(`run_pipeline.rs:6169`), several hundred lines before `--no-link`'s early +return, so the first linking compile in the job blocks on a nested +`cargo build --release -p perry-runtime-static -p perry-stdlib-static` into +`target/perry-auto-/`. `PERRY_RUNTIME_DIR` does not suppress it. #10782 +moved that cost off the harness's `--print-hir --no-link` probe, where +suppressing auto-optimize is inert, but deliberately not off the linking +compile: `runtime_budgets`' `allocations_traced` / `gc_collections_traced` / +`write_barriers_traced` are read out of the executed binary's `PERRY_GC_TRACE` +stderr and are MAXIMA, so a runtime emitting no trace scores 0 and passes all +three vacuously. The build therefore just moved into the linking compile's +300 s `--compile-timeout`, and the gate kept failing with `TimeoutExpired` on +`h1_native_rep_equivalence`, the head of both suites. + +A `Pre-warm auto-optimized runtime` step now does those builds before the +gates, with `--skip-run --compile-timeout 2400`, so each gate step keeps its +300 s and keeps measuring exactly what it measured before. + +**It is two cold builds, not one.** Every gated workload shares a single +`target/perry-auto-/` — the directory hash keys on the stdlib feature +arg, not on the cross-features — but the build stamp *inside* it keys on the +cross-feature set, and a stamp mismatch re-runs cargo. The harness sets +`PERRY_GC_TRACE=1` only for workloads carrying `*_traced` runtime budgets, and +`PERRY_GC_TRACE` adds `perry-runtime/diagnostics` +(`optimized_libs/freshness.rs`). `loop_bound_semantics` is the one gated +workload with no `*_traced` budget, so it is the one compile in the job that +wants the other feature set. Measured with a debug compiler on an M-series +mini, from a cleared `target/perry-auto-*`: + +| compile | duration | budget | +|---|---|---| +| first gated compile, diagnostics variant | 244.6 s | 300 s | +| `loop_bound_semantics`, non-diagnostics variant | 242.1 s | 300 s | +| every later flip between the two variants | ~4 s | 300 s | +| every other gated compile | 3–4 s | 300 s | + +So warming one variant only moves the timeout from `h1_native_rep_equivalence` +to `loop_bound_semantics`, which is 242.1 s into a 300 s budget on a box faster +than a GitHub runner. The step warms both, then flips back to the variant 24 of +the 25 gated compiles want. It goes through the harness rather than a bare +`perry compile` precisely because the harness is what decides `PERRY_GC_TRACE` +per workload — a hand-rolled compile would warm a stamp no gate step wants. + +The step asserts it warmed something, which is the substance of the change +rather than a flourish: `perry compile` **exits 0 when auto-optimize fails** — +the driver prints "using prebuilt libraries" and returns +`OptimizedLibs::empty()` — so a pre-warm that quietly warms nothing is worse +than none at all, leaving the gates to fail exactly as before under a green +"Pre-warm" step. Two assertions, because neither covers the other: + +* A stamped `target/perry-auto-*/` holding a non-empty `libperry_runtime.a`. + Both halves are load-bearing: cargo creates the directory and a zero-byte + `.perry-auto-build.lock` *before* building anything into it, while + `.perry-auto-build.stamp` is written only past the `status.success()` check + and is what the next compile consults for freshness. +* The third pre-warm's own compile duration, which must be under half a gate + step's budget. That compile deliberately flips the stamp, and a flip is cheap + only if *both* variants are in cargo's cache — so it fails a job warmed for + one variant, which the artifact check above cannot see. + +Both were checked against the failures they exist for, not merely exercised. +With the pre-warm neutered (`PERRY_NO_AUTO_OPTIMIZE=1`, runtime served from a +pre-existing archive so the compile still succeeds) the harness exits 0 and the +step goes red on the first assertion, naming the missing directory; its five +states — no `target/`, a directory mid-build, a stamp with a zero-byte archive, +an archive with no stamp, and fully warm — were each checked individually. + +Verified afterwards that the linking compile still runs *with* auto-optimize, +i.e. that the #10782 exclusion is intact: `gc_trace_unavailable` is `False` on +all 23 suite workloads, which is the executed binary's own report that +`perry-runtime/diagnostics` was compiled in, and the harness fails the workload +when it is not. A suppressed linking compile would flip it to `True` rather +than pass vacuously. + +Not done here, and worth its own issue: `--no-link` could decline to +auto-optimize outright, a two-line guard at `run_pipeline.rs:6169`, which fixes +the class rather than this instance. diff --git a/changelog.d/10782-step-liveness-registry.md b/changelog.d/10782-step-liveness-registry.md new file mode 100644 index 0000000000..e52b243925 --- /dev/null +++ b/changelog.d/10782-step-liveness-registry.md @@ -0,0 +1,17 @@ +Registered the new "Pre-warm auto-optimized runtime" step in +`scripts/compiler_output_step_liveness.py`'s `COMPILER_SUBJECTS`. + +That gate refuses to let the `compiler-output-regression` job's post-build step +inventory drift silently, and it caught the addition immediately — which is the +gate working, not a nuisance. Registering the step deliberately is the intended +response. + +It belongs in `COMPILER_SUBJECTS` rather than `UNIT_TEST_SUBJECTS` because it +depends on the build, and it already carries the matching guard +(`!cancelled() && steps.compiler_output_build.outcome == 'success'`) so a +sibling failure cannot hide it. + +Listing it matters beyond satisfying the check: the pre-warm carries its own +liveness assertions and can fail, so it is a subject. Were it removed or +silently disabled, every gate step below would quietly inherit the 300 s +timeout the pre-warm exists to prevent. diff --git a/changelog.d/10804-stale-denial-issue-pointer.md b/changelog.d/10804-stale-denial-issue-pointer.md new file mode 100644 index 0000000000..361ab6e48a --- /dev/null +++ b/changelog.d/10804-stale-denial-issue-pointer.md @@ -0,0 +1,29 @@ +**fix(codegen): point the module-global `Ptr` denial at its real issue.** +`MODULE_GLOBAL_ISSUE` in `expr/slot_rep.rs` — the issue number the optimiser +report prints when a module-level binding is denied a canonical slot — cited +**#7109**. That is a different mechanism: #7109 is the module-init / +program-entry *context* gate, which `MODULE_INIT_CONTEXT` in the same file +still cites correctly. #7109 is closed, and so is #10774, which lifted that +gate. + +So every reader who followed the denial's own pointer landed on a closed issue +about something else, and could reasonably conclude the module-global class was +already handled. That is not hypothetical: one optimisation pass recorded +module-global storage as "less important" on exactly that reading, and a +separate campaign spent a day repeating "#7109 is the blocker" on inherited +belief before checking the issue state. + +Now points at **#10803** (`Ptr` is denied to three storage classes: +module globals, function parameters, and locals escaping into a module global), +with a comment recording why the old pointer was wrong so the correction is not +silently reverted. + +Deliberately unchanged: `MODULE_INIT_CONTEXT`'s `#7109`, which is accurate to +that rule's subject, and the `#7109` fixture string in `opt_report/render.rs`'s +test helper. Only the live user-facing pointer for the module-global storage +class moved. `the_context_gate_is_reported_when_every_value_rule_passed` +asserts the `MODULE_INIT_CONTEXT` pointer and still passes unchanged, which is +what confirms the two were separable. + +Found by cross-session review while handing the module-global lane to another +campaign; the denial classes it names are that campaign's #10803. diff --git a/crates/perry-codegen/src/expr/slot_rep.rs b/crates/perry-codegen/src/expr/slot_rep.rs index 21e77799e8..4f310c313c 100644 --- a/crates/perry-codegen/src/expr/slot_rep.rs +++ b/crates/perry-codegen/src/expr/slot_rep.rs @@ -450,7 +450,15 @@ pub(crate) fn deny_canonical_i32(ctx: &FnCtx<'_>, id: u32, name: &str, denial: C } /// Tracking issue for "a module-level binding can never take a canonical slot". -const MODULE_GLOBAL_ISSUE: &str = "#7109"; +/// +/// This pointed at #7109 until #10803. #7109 is a *different* mechanism — the +/// module-init / program-entry context gate, which `MODULE_INIT_CONTEXT` below +/// still cites correctly — and it is closed, as is #10774 which lifted that +/// gate. A reader who followed this denial's own pointer therefore landed on a +/// closed issue about something else and could reasonably conclude the +/// module-global class was already handled; that is how one optimisation pass +/// came to record module-global storage as "less important". +const MODULE_GLOBAL_ISSUE: &str = "#10803"; /// Tracking issue for the index-use / i32-bound precondition. const NOT_BOUNDED_ISSUE: &str = "#7123"; /// Tracking issue for the profitability refusal — the one denial in this list diff --git a/scripts/compiler_output_step_liveness.py b/scripts/compiler_output_step_liveness.py index b029c3dae6..e92ec29296 100644 --- a/scripts/compiler_output_step_liveness.py +++ b/scripts/compiler_output_step_liveness.py @@ -37,6 +37,12 @@ "Run native ABI evidence report unit tests", ) COMPILER_SUBJECTS = ( + # #10782: pays auto-optimize's two cold runtime builds under a budget of + # its own, so no gate step below absorbs a 343s nested cargo build inside + # its 300s --compile-timeout. It is a subject, not scaffolding: it carries + # its own liveness assertions and can fail, so it must be listed here or a + # silently-removed pre-warm would hand every gate back its timeout. + "Pre-warm auto-optimized runtime", "Gate native-region proof compiler output", "Gate native-ABI proof compiler output", "Gate typed feedback runtime evidence",