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
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]>
294 lines
16 KiB
YAML
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; }
|