open-standards/.github/workflows/wai-quantum-vector.yml
Transaction Science bcb8b91fc4
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
ci: the emulated vector and SME legs bring their own emulator
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]>
2026-11-06 08:44:46 -05:00

294 lines
16 KiB
YAML

name: wai-quantum CPU vector units
# The fabric's CPU vector-unit members: the gate's fixed-point arithmetic in
# the processor's own vector extension, certified bit-exact against the CPU
# reference's pinned corpus digests.
#
# - avx2 on silicon: the x86-64 processor of whichever `linux` runner takes
# the leg. jetson-hub's has avx2 alone; framework-128gb-linux's has avx512
# too, and there the leg declares both, as the kernel reports them, and
# checks the avx2 member as on any other;
# - avx2 and avx512 on silicon together: framework-128gb's processor, which has
# both, one member each;
# - sve and rvv under a user-mode emulator the job fetches and runs itself
# (Debian's static QEMU, pinned), never the host's binfmt handler, at
# several vector lengths each: the scalable kernels set their length per
# pass, and every leg checks the length the member read from the unit is the
# one the emulator was given, so a leg cannot pass at the wrong length;
# - the macOS runner, whose processor has no vector extension these kernels
# use outside streaming mode, finding no member.
#
# Silicon for sve and rvv: a processor with them runs the same tests with no
# emulator declared.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-vector.yml'
schedule:
- cron: '53 6 * * *'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-vector.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-quantum-vector: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
vector:
name: member · vector unit, ${{ matrix.name }}
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 120
strategy:
fail-fast: false
max-parallel: 1
matrix:
include:
- name: avx2 (x86_64 silicon)
target: x86_64-unknown-linux-gnu
image: ghcr.io/cross-rs/aarch64-unknown-linux-gnu@sha256:7f8308a8734d9fcd2ebbe9a3e4bdea74af293f0799d80c3cc341e340cda49a4c
key: cpu-vector-avx2
bits: 256
emulator: ''
qemu: ''
qemu_cpu: ''
- name: sve 128-bit (aarch64, emulated)
target: aarch64-unknown-linux-gnu
image: ghcr.io/cross-rs/aarch64-unknown-linux-gnu@sha256:7f8308a8734d9fcd2ebbe9a3e4bdea74af293f0799d80c3cc341e340cda49a4c
key: cpu-vector-sve
bits: 128
emulator: qemu-user 10.0.13 (Debian's static build, run by the job)
qemu: aarch64
qemu_cpu: max,sve-default-vector-length=16
- name: sve 512-bit (aarch64, emulated)
target: aarch64-unknown-linux-gnu
image: ghcr.io/cross-rs/aarch64-unknown-linux-gnu@sha256:7f8308a8734d9fcd2ebbe9a3e4bdea74af293f0799d80c3cc341e340cda49a4c
key: cpu-vector-sve
bits: 512
emulator: qemu-user 10.0.13 (Debian's static build, run by the job)
qemu: aarch64
qemu_cpu: max,sve-default-vector-length=64
- name: sve 2048-bit (aarch64, emulated)
target: aarch64-unknown-linux-gnu
image: ghcr.io/cross-rs/aarch64-unknown-linux-gnu@sha256:7f8308a8734d9fcd2ebbe9a3e4bdea74af293f0799d80c3cc341e340cda49a4c
key: cpu-vector-sve
bits: 2048
emulator: qemu-user 10.0.13 (Debian's static build, run by the job)
qemu: aarch64
qemu_cpu: max,sve-default-vector-length=256
- name: rvv 128-bit (riscv64, emulated)
target: riscv64gc-unknown-linux-gnu
image: ghcr.io/cross-rs/riscv64gc-unknown-linux-gnu@sha256:67ee97dd4719390d299edfcf39f226360ccfb1539e08b078ec187d4848415794
key: cpu-vector-rvv
bits: 128
emulator: qemu-user 10.0.13 (Debian's static build, run by the job)
qemu: riscv64
qemu_cpu: rv64,v=true,vlen=128,elen=64
- name: rvv 1024-bit (riscv64, emulated)
target: riscv64gc-unknown-linux-gnu
image: ghcr.io/cross-rs/riscv64gc-unknown-linux-gnu@sha256:67ee97dd4719390d299edfcf39f226360ccfb1539e08b078ec187d4848415794
key: cpu-vector-rvv
bits: 1024
emulator: qemu-user 10.0.13 (Debian's static build, run by the job)
qemu: riscv64
qemu_cpu: rv64,v=true,vlen=1024,elen=64
container:
image: ${{ matrix.image }}
options: --memory=6g --memory-swap=6g
env:
WAI_QUANTUM_VECTOR_EXPECT: ${{ matrix.key }}
WAI_QUANTUM_VECTOR_EMULATOR: ${{ matrix.emulator }}
# The emulator every foreign test binary runs under: Debian's static QEMU,
# SHA-256 pinned, fetched by the job and named as cargo's runner. Not the
# image's own (5.1 for aarch64, 6.1 for riscv64), and not the host's binfmt
# handler, which some `linux` runners lack and others run at another
# version. (Not QEMU_*: qemu-user reads every QEMU_<option> variable as that
# option.)
USER_EMULATOR_VERSION: 10.0.13
USER_EMULATOR_DEB: qemu-user_10.0.13+ds-0+deb13u1_amd64.deb
USER_EMULATOR_SHA256: ca6ede739327a20ae5a3498230c8a3a9a072c586e131bc6bcc1201eb1725e336
USER_EMULATOR_SHA1: dec1373851d29f91642c197f4a339d0d2ad25ed0
steps:
# The image has no Node for a checkout action: the forge's archive of the
# exact commit, its embedded commit id checked.
- name: Fetch the source
shell: bash
run: |
set -euo pipefail
want="${{ github.event.pull_request.head.sha || github.sha }}"
rm -rf src src.tar.gz src.tar && mkdir src
curl -sSf -o src.tar.gz "http://100.64.224.0:3400/Transaction-Science/open-standards/archive/$want.tar.gz"
gzip -dc src.tar.gz > src.tar
got="$(git get-tar-commit-id < src.tar)"
[ "$got" = "$want" ] || { echo "::error::the archive is of $got, not $want"; exit 1; }
tar -xf src.tar -C src --strip-components=1
rm src.tar.gz src.tar
- name: The member's tests
shell: bash
working-directory: src/wai/wai-quantum
run: |
set -euo pipefail
cache=/ci-cache/wai-quantum-vector
mkdir -p "$cache"
exec 9>"$cache/.lock"
flock 9
export RUSTUP_HOME="$cache/rustup" CARGO_HOME="$cache/cargo" CARGO_TARGET_DIR="$cache/target-${{ matrix.target }}"
export PATH="$CARGO_HOME/bin:$PATH"
if ! command -v rustup >/dev/null; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --profile minimal --default-toolchain none
fi
rustup toolchain install
rustup target add ${{ matrix.target }}
# The emulated processor, for the test binaries only.
if [ -n "${{ matrix.qemu_cpu }}" ]; then export QEMU_CPU="${{ matrix.qemu_cpu }}"; fi
# A trivial program for the preflight below: it prints the machine it runs on.
cat > "$RUNNER_TEMP/probe.c" <<'EOF'
#include <stdio.h>
#include <sys/utsname.h>
int main(void) { struct utsname u; if (uname(&u) != 0) return 1; printf("%s\n", u.machine); return 0; }
EOF
if [ -n "${{ matrix.qemu }}" ]; then
# The emulator, fetched once per host into the cache (under the lock above)
# and checked against its pin, then named as cargo's runner, so this leg
# passes or fails the same on every `linux` runner.
q="$cache/qemu-user-$USER_EMULATOR_VERSION"
if [ ! -x "$q/qemu-aarch64" ] || [ ! -x "$q/qemu-riscv64" ]; then
d="$cache/qemu-user-fetch"; rm -rf "$d" && mkdir "$d"
curl -sSfL -o "$d/$USER_EMULATOR_DEB" "https://deb.debian.org/debian/pool/main/q/qemu/$USER_EMULATOR_DEB" \
|| curl -sSfL -o "$d/$USER_EMULATOR_DEB" "https://snapshot.debian.org/file/$USER_EMULATOR_SHA1"
echo "$USER_EMULATOR_SHA256 $d/$USER_EMULATOR_DEB" | sha256sum -c -
dpkg-deb --fsys-tarfile "$d/$USER_EMULATOR_DEB" | tar -x -C "$d" ./usr/bin/qemu-aarch64 ./usr/bin/qemu-riscv64
rm -rf "$q.tmp" && mkdir "$q.tmp" && mv "$d/usr/bin/qemu-aarch64" "$d/usr/bin/qemu-riscv64" "$q.tmp/"
rm -rf "$q" && mv "$q.tmp" "$q" && rm -rf "$d"
fi
runner="$q/qemu-${{ matrix.qemu }} -L /usr/${{ matrix.qemu }}-linux-gnu"
export "CARGO_TARGET_$(printf %s "${{ matrix.target }}" | tr 'a-z-' 'A-Z_')_RUNNER=$runner"
# Preflight: the trivial program, built for the target and run as cargo will
# run the tests. An emulator that cannot run it fails the leg here, saying
# so, rather than as a test that never ran.
"${{ matrix.qemu }}-linux-gnu-gcc" -o "$RUNNER_TEMP/probe" "$RUNNER_TEMP/probe.c"
got="$($runner "$RUNNER_TEMP/probe")" || { echo "::error::the emulator cannot run a program for ${{ matrix.target }}: $runner"; exit 1; }
[ "$got" = "${{ matrix.qemu }}" ] || { echo "::error::the preflight program ran as $got, not ${{ matrix.qemu }}"; exit 1; }
echo "emulator: $("$q/qemu-${{ matrix.qemu }}" --version | head -1), cpu ${QEMU_CPU:-default}; a program for ${{ matrix.target }} runs under it"
fi
# Every unit this processor has must be declared, so on silicon the
# declaration comes from the kernel's own flags, not the probe under test.
if [ "${{ matrix.key }}" = cpu-vector-avx2 ] && grep -qw avx512f /proc/cpuinfo; then
export WAI_QUANTUM_VECTOR_EXPECT=cpu-vector-avx2,cpu-vector-avx512
fi
t="--target ${{ matrix.target }} --features quantum_fabric_vector"
cargo test --locked --release -j 6 $t --lib -- quantum_fabric_vector:: 2>&1 | tee "$cache/unit.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$cache/unit.log" || { echo "::error::no unit test ran"; exit 1; }
report="$(mktemp)"
WAI_QUANTUM_VECTOR_REPORT="$report" cargo test --locked --release -j 6 $t --test vector_member -- --test-threads=1 2>&1 | tee "$cache/device.log"
grep -qE '^test result: ok\. 9 passed; 0 failed' "$cache/device.log" || { echo "::error::not every device test ran and passed"; exit 1; }
cat "$report"
key="${{ matrix.key }}"
grep -qx "vector devices: as declared ($WAI_QUANTUM_VECTOR_EXPECT)" "$report" || { echo "::error::the units found are not the declared ones"; exit 1; }
grep -q "^vector member: key=$key .* bits=${{ matrix.bits }} " "$report" || { echo "::error::the unit's vector length is not ${{ matrix.bits }} bits"; exit 1; }
if [ -n "$WAI_QUANTUM_VECTOR_EMULATOR" ]; then
grep -q "^vector member: key=$key .*emulator:qemu-user" "$report" || { echo "::error::the member does not say it ran under the emulator"; exit 1; }
else
grep -q "^vector member: key=$key .*; silicon;" "$report" || { echo "::error::the member does not say it ran on silicon"; exit 1; }
fi
n="$(grep -c "^vector certified: key=$key .* parity=bit-exact " "$report")"
[ "$n" = 4 ] || { echo "::error::$n of 4 certificates were earned"; exit 1; }
grep -qx "vector mutants: key=$key killed 3 of 3" "$report" || { echo "::error::a kernel mutant survived"; exit 1; }
vector-amd:
name: member · vector units, avx2 and avx512 (framework-128gb silicon)
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux-amd
timeout-minutes: 60
env:
CARGO_PROFILE_DEV_DEBUG: '0'
CARGO_BUILD_JOBS: '12'
steps:
- name: Fetch the source
shell: bash
run: |
set -euo pipefail
want="${{ github.event.pull_request.head.sha || github.sha }}"
rm -rf src && mkdir src && cd src
git init -q
# Fetch the commit itself, not the ref. The host names its own route to
# the forge (WAI_FORGE_URL).
git fetch -q --depth 1 "${WAI_FORGE_URL:-http://100.64.224.0:3400}/Transaction-Science/open-standards.git" "$want"
git checkout -q FETCH_HEAD
got="$(git rev-parse HEAD)"
[ "$got" = "$want" ] || { echo "::error::fetched $got, not $want"; exit 1; }
- name: Both units on silicon
shell: bash
working-directory: src/wai/wai-quantum
run: |
set -euo pipefail
export PATH="$HOME/.cargo/bin:$PATH"
export CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-vector"
# The units this host's processor has, declared here (or by the runner).
export WAI_QUANTUM_VECTOR_EXPECT="${WAI_QUANTUM_AMD_VECTOR_EXPECT:-cpu-vector-avx2,cpu-vector-avx512}"
nice -n 10 cargo test --locked --release --features quantum_fabric_vector --lib -- quantum_fabric_vector:: 2>&1 | tee "$RUNNER_TEMP/unit.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/unit.log" || { echo "::error::no unit test ran"; exit 1; }
report="$RUNNER_TEMP/vector-report.txt"; : > "$report"
WAI_QUANTUM_VECTOR_REPORT="$report" nice -n 10 cargo test --locked --release --features quantum_fabric_vector --test vector_member -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/device.log"
grep -qE '^test result: ok\. 9 passed; 0 failed' "$RUNNER_TEMP/device.log" || { echo "::error::not every device test ran and passed"; exit 1; }
cat "$report"
grep -qx "vector devices: as declared ($WAI_QUANTUM_VECTOR_EXPECT)" "$report" || { echo "::error::the units found are not the declared ones"; exit 1; }
for key in ${WAI_QUANTUM_VECTOR_EXPECT//,/ }; do
case "$key" in cpu-vector-avx2) bits=256 ;; cpu-vector-avx512) bits=512 ;; *) echo "::error::no vector length known for $key"; exit 1 ;; esac
grep -q "^vector member: key=$key .* bits=$bits .*; silicon;" "$report" || { echo "::error::$key: not $bits bits on silicon"; exit 1; }
n="$(grep -c "^vector certified: key=$key .* parity=bit-exact " "$report")"
[ "$n" = 4 ] || { echo "::error::$key: $n of 4 certificates were earned"; exit 1; }
grep -qx "vector mutants: key=$key killed 3 of 3" "$report" || { echo "::error::$key: a kernel mutant survived"; exit 1; }
done
vector-absent:
name: member · vector unit absent (macos)
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: macos
timeout-minutes: 60
env:
WAI_QUANTUM_VECTOR_EXPECT: none
steps:
- uses: actions/checkout@v5
# launchd's PATH omits cargo on the macOS host runner.
- name: Put cargo on PATH
shell: bash
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: No vector unit these kernels use, and the member says so
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
report="$RUNNER_TEMP/vector-report.txt"; : > "$report"
nice -n 10 cargo test --locked --features quantum_fabric_vector --lib -- quantum_fabric_vector:: 2>&1 | tee "$RUNNER_TEMP/unit.log"
grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/unit.log" || { echo "::error::no unit test ran"; exit 1; }
WAI_QUANTUM_VECTOR_REPORT="$report" nice -n 10 cargo test --locked --features quantum_fabric_vector --test vector_member -- --test-threads=1 2>&1 | tee "$RUNNER_TEMP/device.log"
grep -qE '^test result: ok\. 9 passed; 0 failed' "$RUNNER_TEMP/device.log" || { echo "::error::not every device test ran and passed"; exit 1; }
grep -qx 'vector devices: as declared (none)' "$report" || { echo "::error::the absent case was not established"; exit 1; }