Skip to content

ci: perry compile --no-link pays for a full runtime build it discards — 343s nested cargo inside a 300s budget (intermittent compiler-output-regression timeout) #10782

Description

@proggeramlug

compiler-output-regression has been red on every main sweep for at least 34 hours — continuously from 60922041cd (2026-09-18T17:56) through 3f5b5b1424 (2026-09-20T03:11), 26 sweeps with no green in between. It is part of main-gate, so main-gate is red on every train.

It is not a compiler hang, and the log reads exactly like one:

subprocess.TimeoutExpired: Command '[.../target/debug/perry', 'compile',
  '.../benchmarks/compiler_output/fixtures/h1_native_rep_equivalence.ts',
  '-o', '.../hir-probe.o', '--print-hir', '--no-link', '--no-cache',
  '--verify-native-regions']' timed out after 300 seconds

All four steps fail (native-region proof, native-ABI proof, positive vectorization, HIR fact rewrite) because they share the fixture list and the same fixture stalls each one.

What is actually happening

perry compile is not compiling for 300 seconds. It spends about one second compiling and then blocks on a nested cargo build, because auto-optimize fires and rebuilds the runtime from source.

Reproduced locally with the v0.5.1611 release binary, the same command line:

run result
as CI runs it still going at >300 s
PERRY_NO_AUTO_OPTIMIZE=1 1.2 s, rc=0

The stalled process is at 0.0% CPU with a child — the discriminator that separates "spinning" from "waiting". The child is:

cargo build --release -p perry-runtime-static -p perry-stdlib-static
  --no-default-features --features perry-runtime/full,...
  --out-dir .../target/perry-auto-a51d6650d9b10028/release/...

i.e. a full cold-cache runtime build, inside a 300-second budget, on a CI runner, from a debug compiler.

The one-line cause

scripts/compiler_output_harness/capture.py:137 builds the compiler environment as

env = {**os.environ, "PERRY_LLVM_KEEP_IR": "1", "PERRY_NO_CACHE": "1"}

PERRY_NO_AUTO_OPTIMIZE is not among them, and nothing in .github/workflows/test.yml's job env sets it either. --compile-timeout defaults to 300 (cli.py:47).

Several other harnesses in this repo already set it deliberately — gc_instrument_smoke.sh:40, gc_root_dominance_corpus.sh:320, run_doc_tests.sh:22 — and auto_opt_app_patterns.sh:53 goes the other way and refuses to run if it is set, because testing that path is its whole point. So the convention exists; this harness is simply missing from it.

The fix needs one judgement call, not a blanket export

The capture steps that failed all pass --no-link --print-hir: for those, auto-optimize cannot affect the artifact under test and suppressing it is unambiguously right.

But the harness also has --run-timeout and run_perf_stat paths that execute a produced binary, where the linked runtime is part of what is being measured. Do not set PERRY_NO_AUTO_OPTIMIZE=1 globally in capture.py without establishing which suites run a binary and whether the auto-optimized runtime is load-bearing for them. Getting this wrong swaps a loud red for a quiet measurement change.

Also worth deciding: whether a 300-second compile timeout should be allowed to encompass a nested cargo build at all, or whether perry compile --no-link should decline to auto-optimize on its own — the second would fix the class rather than this instance.

Incidental

Each triggered run leaves a target/perry-auto-<hash>/ directory behind; the one my local reproduction created was 377 MB after a partial build. On CI that is inside the job, but it is the same leak tracked for the gap sweep.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions