ci: the emulated vector and SME legs bring their own emulator #674

Merged
dcharlot merged 1 commit from ci/quantum-emulator-in-job into main 2026-11-06 13:50:54 -05:00
Owner

What was wrong

The vector unit's sve and rvv legs and the matrix extension's emulated leg set cargo's runner to env, so the host's binfmt handler ran their aarch64 and riscv64 test binaries. Whether a leg could pass therefore depended on which linux runner claimed it:

  • A runner without a handler gets ENOEXEC; the test binary is then run by a shell (…/deps/wai_quantum-…: 1:, : 7:) and the leg fails. A new x86_64 linux runner (7049b355…) failed 4 of 4 such legs on 6 Nov, main's own SME run among them, while the other runners passed 26 of 26.
  • The hosts that have a handler run different versions (8.2.2 on some, 10.2.1 on framework-128gb), while every certificate said "qemu-user 8.2.2 (the host's binfmt handler)".

The change (wai-quantum-vector.yml and wai-quantum-sme.yml only)

  • The job brings its emulator. Each emulated leg fetches Debian's static QEMU 10.0.13 (qemu-user_10.0.13+ds-0+deb13u1_amd64.deb, SHA-256 pinned; from snapshot.debian.org by SHA-1 if the pool has moved on). It fetches once per host into the workflow's cache, under the lock it already holds, keeping only qemu-aarch64 and qemu-riscv64. The .deb is xz-compressed, so the images' own dpkg-deb unpacks it. The images are Ubuntu 16.04 and 18.04, whose apt would give a far older QEMU.
  • It is cargo's runner. CARGO_TARGET_<TRIPLE>_RUNNER="<cache>/qemu-<arch> -L /usr/<arch>-linux-gnu" replaces env and the image's own runner (QEMU 5.1 and 6.1). QEMU_CPU (the SVE length, the RVV vlen/elen) is passed as before. The test binaries spawn no child processes on Linux, so no execve falls back to binfmt.
  • Preflight. Before any build, the leg compiles a trivial C program for the target and runs it exactly as cargo will. It must print the target's machine (aarch64 / riscv64), or the leg fails with "the emulator cannot run a program for : ".
  • The certificates name the emulator that ran: qemu-user 10.0.13 (Debian's static build, run by the job).

No changelog entry: nothing under wai/ changes. This merges cleanly with #666, which edits other lines of both files.

Proof

I took the steps from git (every vector leg with its matrix values, the SME step, and main's sve 128 step as a control) and ran them on the Linux x86_64 build host in the legs' own pinned images. Each container was capped at 6 GiB as in CI. Two environments:

  • A: the host's binfmt handler visible (a foreign program runs when executed directly).
  • B: the same container with a nested user namespace and a fresh, empty binfmt_misc instance (register status only), where a foreign program executed directly gets "Exec format error". That is the situation of a runner without a handler; the host's own table is untouched.
Leg A (handler present) B (no handler)
main's sve 128 step (runner = env) (passes on such hosts in CI) fails, exit 2: …/deps/wai_quantum-…: 1: / : 7:, 0 certificates, as on 7049b355
sve 128 pass, bits=128, 4 certificates, 3/3 mutants pass, bits=128, 4, 3/3
sve 512 pass, bits=512, 4, 3/3 pass, bits=512, 4, 3/3
sve 2048 pass, bits=2048, 4, 3/3 pass, bits=2048, 4, 3/3
rvv 128 pass, bits=128, 4, 3/3 pass, bits=128, 4, 3/3
rvv 1024 pass, bits=1024, 4, 3/3 pass, bits=1024, 4, 3/3
sme pass, sme devices: as declared (cpu-matrix-sme), 4, 3/3 pass, same

bits= is the length the member reads from the unit itself (cntb on SVE, vlenb on RVV), so the configured lengths take effect under the pinned emulator. The preflight's failure path: with a cached "emulator" that runs nothing, the sve 128 leg stops before any test with ::error::the emulator cannot run a … program: <runner>. (The proof ran the commit before a rewording of that message and of the preflight's success line; nothing else differs.)

Also checked: both files parse; check-workflow-concurrency.sh and check-tests-in-ci.py pass.

🤖 Generated with Claude Code

## What was wrong The vector unit's sve and rvv legs and the matrix extension's emulated leg set cargo's runner to `env`, so the **host's** binfmt handler ran their aarch64 and riscv64 test binaries. Whether a leg could pass therefore depended on which `linux` runner claimed it: - A runner without a handler gets ENOEXEC; the test binary is then run by a shell (`…/deps/wai_quantum-…: 1:`, `: 7:`) and the leg fails. A new x86_64 `linux` runner (`7049b355…`) failed 4 of 4 such legs on 6 Nov, main's own SME run among them, while the other runners passed 26 of 26. - The hosts that have a handler run different versions (8.2.2 on some, 10.2.1 on framework-128gb), while every certificate said "qemu-user 8.2.2 (the host's binfmt handler)". ## The change (wai-quantum-vector.yml and wai-quantum-sme.yml only) - **The job brings its emulator.** Each emulated leg fetches Debian's static QEMU **10.0.13** (`qemu-user_10.0.13+ds-0+deb13u1_amd64.deb`, SHA-256 pinned; from snapshot.debian.org by SHA-1 if the pool has moved on). It fetches once per host into the workflow's cache, under the lock it already holds, keeping only `qemu-aarch64` and `qemu-riscv64`. The `.deb` is xz-compressed, so the images' own `dpkg-deb` unpacks it. The images are Ubuntu 16.04 and 18.04, whose apt would give a far older QEMU. - **It is cargo's runner.** `CARGO_TARGET_<TRIPLE>_RUNNER="<cache>/qemu-<arch> -L /usr/<arch>-linux-gnu"` replaces `env` and the image's own runner (QEMU 5.1 and 6.1). `QEMU_CPU` (the SVE length, the RVV `vlen`/`elen`) is passed as before. The test binaries spawn no child processes on Linux, so no `execve` falls back to binfmt. - **Preflight.** Before any build, the leg compiles a trivial C program for the target and runs it exactly as cargo will. It must print the target's machine (`aarch64` / `riscv64`), or the leg fails with "the emulator cannot run a program for <target>: <runner>". - **The certificates name the emulator that ran:** `qemu-user 10.0.13 (Debian's static build, run by the job)`. No changelog entry: nothing under `wai/` changes. This merges cleanly with #666, which edits other lines of both files. ## Proof I took the steps from git (every vector leg with its matrix values, the SME step, and main's sve 128 step as a control) and ran them on the Linux x86_64 build host in the legs' own pinned images. Each container was capped at 6 GiB as in CI. Two environments: - **A:** the host's binfmt handler visible (a foreign program runs when executed directly). - **B:** the same container with a nested user namespace and a fresh, empty `binfmt_misc` instance (`register status` only), where a foreign program executed directly gets "Exec format error". That is the situation of a runner without a handler; the host's own table is untouched. | Leg | A (handler present) | B (no handler) | |---|---|---| | main's sve 128 step (`runner = env`) | (passes on such hosts in CI) | **fails, exit 2**: `…/deps/wai_quantum-…: 1:` / `: 7:`, 0 certificates, as on `7049b355` | | sve 128 | pass, `bits=128`, 4 certificates, 3/3 mutants | pass, `bits=128`, 4, 3/3 | | sve 512 | pass, `bits=512`, 4, 3/3 | pass, `bits=512`, 4, 3/3 | | sve 2048 | pass, `bits=2048`, 4, 3/3 | pass, `bits=2048`, 4, 3/3 | | rvv 128 | pass, `bits=128`, 4, 3/3 | pass, `bits=128`, 4, 3/3 | | rvv 1024 | pass, `bits=1024`, 4, 3/3 | pass, `bits=1024`, 4, 3/3 | | sme | pass, `sme devices: as declared (cpu-matrix-sme)`, 4, 3/3 | pass, same | `bits=` is the length the member reads from the unit itself (`cntb` on SVE, `vlenb` on RVV), so the configured lengths take effect under the pinned emulator. The preflight's failure path: with a cached "emulator" that runs nothing, the sve 128 leg stops before any test with `::error::the emulator cannot run a … program: <runner>`. (The proof ran the commit before a rewording of that message and of the preflight's success line; nothing else differs.) Also checked: both files parse; `check-workflow-concurrency.sh` and `check-tests-in-ci.py` pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ci: the emulated vector and SME legs bring their own emulator
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 8s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
wai-quantum matrix-extension member / does this change touch it (pull_request) Successful in 7s
wai-quantum CPU vector units / does this change touch it (pull_request) Successful in 7s
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Successful in 18s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m9s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Successful in 20s
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Successful in 31s
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Successful in 7s
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Successful in 25s
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Successful in 40s
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Successful in 1m28s
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Successful in 1m29s
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Successful in 29s
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Successful in 13s
bcb8b91fc4
The vector unit's sve and rvv legs and the matrix extension's emulated leg
ran their aarch64 and riscv64 test binaries directly (cargo's runner set to
env), so the host's binfmt handler ran them. Whether that worked depended on
which `linux` runner claimed the leg: one without a handler ran the binary as
a shell script and failed (a new x86_64 runner failed 4 of 4 such legs on
6 Nov, main's own SME run among them), and the hosts that have one run
different versions (8.2.2 on some, 10.2.1 on another) while every certificate
said 8.2.2.

- Each emulated leg fetches Debian's static QEMU 10.0.13 (qemu-user, SHA-256
  pinned; snapshot.debian.org by SHA-1 if the pool has moved on), once per
  host into the workflow's cache under the lock it already holds, and names it
  as cargo's runner with -L to the image's sysroot. QEMU_CPU, the vector
  length and RVV settings, is passed as before.
- A preflight builds a trivial program for the target and runs it the way
  cargo will; an emulator that cannot run it, or a program that does not
  report the target's machine, fails the leg there with a message saying so.
- The certificates name the emulator that ran: qemu-user 10.0.13 (Debian's
  static build, run by the job).

The image's own emulators (5.1, 6.1) are not used, as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!674
No description provided.