Skip to content

Commit cee80ce

Browse files
authored
ci: per-tier scanner SIMD correctness on main; move advisory probes off PR (#121)
1 parent 6da6455 commit cee80ce

11 files changed

Lines changed: 241 additions & 210 deletions

File tree

.github/workflows/avx512.yml

Lines changed: 0 additions & 155 deletions
This file was deleted.

.github/workflows/quality.yml

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
name: Quality Probes
22

33
on:
4-
push:
5-
branches: [main]
64
pull_request:
75
branches: [main]
86
workflow_dispatch:
@@ -11,24 +9,14 @@ on:
119
permissions:
1210
contents: read
1311

14-
# Both jobs are advisory and non-blocking. The clang-format job verifies the tracked
15-
# project sources stay formatted against the root .clang-format (clang-format 20, the
16-
# version pinned below). The sanitizer job uses MSVC AddressSanitizer -- the only
17-
# sanitizer that works on Windows (GCC and Clang on mingw-w64 ship no ASan/UBSan
18-
# runtime for the Windows target, so the build links only under MSVC, and ASan is
19-
# the only sanitizer there). The blocking gate (build, tests, coverage) lives in
20-
# pr-check.yml and is untouched here.
21-
#
22-
# Trigger split (per-job `if` on the event): the clang-format check runs on pull
23-
# requests so style is caught before merge, while the AddressSanitizer probe -- a full
24-
# MSVC ASan build and test run, too slow to be worth repeating on every PR push --
25-
# runs only on push to main (post-merge). workflow_dispatch runs both on demand.
12+
# Advisory, non-blocking. Verifies the tracked project sources stay formatted against the root .clang-format
13+
# (clang-format 20, pinned below) and follow the comment-marker conventions. Runs on pull requests so style is caught
14+
# before merge, and on manual dispatch. The blocking gate (build, tests, coverage) lives in pr-check.yml; the slower
15+
# AddressSanitizer probe runs post-merge in sanitizers.yml.
2616

2717
jobs:
2818
format-check:
2919
name: clang-format (advisory)
30-
# Pull requests (and manual dispatch); skipped on push to main.
31-
if: ${{ github.event_name != 'push' }}
3220
runs-on: windows-latest
3321
continue-on-error: true
3422
steps:
@@ -53,33 +41,3 @@ jobs:
5341
if: ${{ !cancelled() }}
5442
run: python scripts/check_comment_style.py
5543
shell: bash
56-
57-
sanitizers:
58-
name: AddressSanitizer probe (MSVC, advisory)
59-
# Post-merge only: runs on push to main (and manual dispatch), not on pull requests.
60-
if: ${{ github.event_name != 'pull_request' }}
61-
runs-on: windows-latest
62-
continue-on-error: true
63-
steps:
64-
- name: Checkout code
65-
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
66-
with:
67-
submodules: "recursive"
68-
persist-credentials: false
69-
70-
- name: Set up MSVC (x64) -- puts cl and the ASan runtime on PATH
71-
uses: ilammy/msvc-dev-cmd@v1
72-
with:
73-
arch: x64
74-
75-
- name: Configure (MSVC Debug + ASan)
76-
run: cmake --preset msvc-debug-asan
77-
shell: pwsh
78-
79-
- name: Build
80-
run: cmake --build --preset msvc-debug-asan --parallel
81-
shell: pwsh
82-
83-
- name: Run tests under ASan
84-
run: ctest --preset msvc-debug-asan --output-on-failure
85-
shell: pwsh

.github/workflows/sanitizers.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: AddressSanitizer probe
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
# Read-only token: this advisory workflow never writes to the repo.
9+
permissions:
10+
contents: read
11+
12+
# MSVC AddressSanitizer is the only sanitizer that works on Windows (GCC and Clang on mingw-w64 ship no ASan/UBSan
13+
# runtime for the Windows target, so the build links only under MSVC, and ASan is the only sanitizer there). A full
14+
# ASan build and test run is too slow to repeat on every PR push, so it runs post-merge on push to main (and on
15+
# manual dispatch). Advisory and non-blocking; the blocking gate (build, tests, coverage) lives in pr-check.yml.
16+
17+
jobs:
18+
sanitizers:
19+
name: AddressSanitizer probe (MSVC, advisory)
20+
runs-on: windows-latest
21+
continue-on-error: true
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
25+
with:
26+
submodules: "recursive"
27+
persist-credentials: false
28+
29+
- name: Set up MSVC (x64) -- puts cl and the ASan runtime on PATH
30+
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1
31+
with:
32+
arch: x64
33+
34+
- name: Configure (MSVC Debug + ASan)
35+
run: cmake --preset msvc-debug-asan
36+
shell: pwsh
37+
38+
- name: Build
39+
run: cmake --build --preset msvc-debug-asan --parallel
40+
shell: pwsh
41+
42+
- name: Run tests under ASan
43+
run: ctest --preset msvc-debug-asan --output-on-failure
44+
shell: pwsh
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Scanner SIMD tier correctness
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
# Read-only token: this workflow never writes to the repo.
9+
permissions:
10+
contents: read
11+
12+
# Per-tier correctness for the runtime-dispatched scanner SIMD verify tiers, run post-merge on push to main and on
13+
# dispatch (SDE emulation is slow and advisory; the blocking build/test/coverage gate for PRs lives in pr-check.yml).
14+
# A matrix runs one DMK_ENABLE_AVX512=ON test build under Intel SDE emulating a different CPU per leg, so the runtime
15+
# gate selects a different verify body each time: -spr (Sapphire Rapids) selects AVX-512, -hsw (Haswell) selects AVX2,
16+
# -wsm (Westmere) selects SSE2. Each leg runs the full ScannerTest suite (results must match the scalar path) and
17+
# asserts it actually selected the tier it covers. The Scalar tier is not a leg: on x64 SSE2 is the ABI baseline, so
18+
# active_simd_level() never returns Scalar and no SDE chip can force it (its code survives as verify tails and the
19+
# prefilter fallback, exercised within the other legs). Advisory: a leg skips with a warning if SDE cannot be set up.
20+
21+
jobs:
22+
tier-correctness-sde:
23+
name: ${{ matrix.tier }} scanner correctness (Intel SDE, advisory)
24+
runs-on: windows-latest
25+
continue-on-error: true
26+
strategy:
27+
fail-fast: false
28+
matrix:
29+
include:
30+
- tier: AVX-512
31+
chip: spr
32+
- tier: AVX2
33+
chip: hsw
34+
- tier: SSE2
35+
chip: wsm
36+
steps:
37+
- name: Checkout code
38+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
39+
with:
40+
submodules: "recursive"
41+
persist-credentials: false
42+
43+
- name: Set up MSVC (x64)
44+
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1
45+
with:
46+
arch: x64
47+
48+
# One DMK_ENABLE_AVX512=ON build serves every leg: the AVX-512 body is compiled in but only selected once the
49+
# emulated CPU advertises AVX-512, so the -hsw / -wsm legs exercise the AVX2 / SSE2 bodies from the same binary.
50+
- name: Configure (MSVC Debug + AVX-512 tier)
51+
shell: pwsh
52+
run: cmake --preset msvc-debug -DDMK_ENABLE_AVX512=ON
53+
54+
- name: Build tests
55+
shell: pwsh
56+
run: cmake --build build/msvc-debug --target DetourModKit_tests --parallel 3
57+
58+
# Intel SDE executes the selected tier on any CPU, so it validates each tier's results without that hardware.
59+
# The SHA-pinned setup-sde action exposes the kit in SDE_PATH; if it cannot be set up the run step skips with a
60+
# warning rather than failing this advisory job.
61+
- name: Set up Intel SDE
62+
uses: petarpetrovt/setup-sde@31aa4a8e85e109bef00f1d838613fcc6ec421271 # v5.0
63+
continue-on-error: true
64+
with:
65+
# SDE 10.8.0 fails to start from some install folders (the runner's extraction path triggers it), so pin a
66+
# known-good earlier release. The -spr / -hsw / -wsm CPU models are all available in 9.x.
67+
# Ref: https://community.intel.com/t5/Intel-ISA-Extensions/SDE-10-8-fails-to-start-depending-on-the-installation-folder/m-p/1746386
68+
sdeVersion: 9.58.0
69+
environmentVariableName: SDE_PATH
70+
71+
- name: Run ScannerTest under Intel SDE (${{ matrix.tier }} via -${{ matrix.chip }})
72+
shell: pwsh
73+
run: |
74+
# Prefer the dedicated 64-bit launcher (sde64.exe) over the auto-detecting sde.exe.
75+
$sdeExe = @('sde64.exe', 'sde.exe') |
76+
ForEach-Object { Join-Path "$env:SDE_PATH" $_ } |
77+
Where-Object { Test-Path $_ } |
78+
Select-Object -First 1
79+
if (-not $sdeExe)
80+
{
81+
Write-Output "::warning::Intel SDE not available; skipping the ${{ matrix.tier }} correctness run."
82+
exit 0
83+
}
84+
$exe = (Get-ChildItem -Recurse -Path build/msvc-debug -Filter DetourModKit_tests.exe |
85+
Select-Object -First 1).FullName
86+
if (-not $exe) { throw "DetourModKit_tests.exe not found" }
87+
88+
$out = & "$sdeExe" "-${{ matrix.chip }}" -- "$exe" --gtest_filter="ScannerTest.*"
89+
$rc = $LASTEXITCODE
90+
$out | ForEach-Object { Write-Host $_ }
91+
92+
# Assert the emulated CPU actually selected this leg's tier, so a chip that silently picks a different verify
93+
# body cannot pass as coverage for the tier this leg is meant to cover.
94+
$level = $out | Select-String 'Scanner SIMD level:\s*(\S+)' | Select-Object -First 1
95+
if ($level)
96+
{
97+
$observed = $level.Matches[0].Groups[1].Value
98+
Write-Host "Observed tier under -${{ matrix.chip }}: $observed"
99+
if ($observed -ne "${{ matrix.tier }}")
100+
{
101+
throw "Expected ${{ matrix.tier }} under -${{ matrix.chip }} but the scanner selected $observed"
102+
}
103+
}
104+
if ($rc -ne 0) { throw "ScannerTest failed under -${{ matrix.chip }} (${{ matrix.tier }})" }

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ PATH="/c/msys64/mingw64/bin:$PATH" cmake --build build/mingw-release \
9292

9393
Latest scanner bench numbers and methodology live in [docs/analysis/scanner_bench_v3.x/README.md](docs/analysis/scanner_bench_v3.x/README.md). Memory validation-vs-direct-read numbers live in [docs/analysis/memory_bench_v3.x/README.md](docs/analysis/memory_bench_v3.x/README.md).
9494

95-
The opt-in AVX-512 verify tier is gated behind the `DMK_ENABLE_AVX512` CMake option (default OFF). When off it compiles out entirely; when on it is still selected only behind a runtime CPUID + XGETBV check (AVX-512F + AVX-512BW, since the byte-wise masked compare is a BW instruction), so the produced library still runs on CPUs without AVX-512 (it simply falls back to AVX2). The intrinsics are confined to that one tier via a per-function `target` attribute, so enabling the option never bumps the baseline ISA of the rest of the library. Its `>= 30%` verify-throughput gate can only be measured on real AVX-512 hardware, so it is enforced in `.github/workflows/avx512.yml`, which validates the tier's correctness under Intel SDE on every push and runs the throughput gate on an opt-in self-hosted AVX-512 runner.
95+
The opt-in AVX-512 verify tier is gated behind the `DMK_ENABLE_AVX512` CMake option (default OFF). When off it compiles out entirely; when on it is still selected only behind a runtime CPUID + XGETBV check (AVX-512F + AVX-512BW, since the byte-wise masked compare is a BW instruction), so the produced library still runs on CPUs without AVX-512 (it simply falls back to AVX2). The intrinsics are confined to that one tier via a per-function `target` attribute, so enabling the option never bumps the baseline ISA of the rest of the library. Its `>= 30%` verify-throughput gate can only be measured on real AVX-512 hardware. Per-tier correctness (including AVX-512, under Intel SDE) is validated on every push to main by `.github/workflows/simd-tier-correctness.yml`; the throughput gate itself is measured on an AVX-512 host (see `docs/analysis/avx512_verify_icount`).
9696

9797
### Sanitizers (MSVC) and coverage (MinGW)
9898

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ endif()
517517
# selected at runtime behind a CPUID + XGETBV gate. The intrinsics are confined to that one tier via a per-function
518518
# target attribute (GCC/Clang) so the rest of the library stays AVX2-only -- no global /arch:AVX512 or -mavx512 is
519519
# added, and the produced library still runs on CPUs without AVX-512 (the tier is simply never selected). The runtime
520-
# >= 30% throughput gate for this tier can only be measured on an AVX-512 host; see the avx512 CI workflow.
520+
# >= 30% throughput gate for this tier can only be measured on an AVX-512 host (see docs/analysis/avx512_verify_icount).
521521
option(DMK_ENABLE_AVX512 "Compile the opt-in AVX-512 scanner verify tier (runtime-gated)" OFF)
522522

523523
if(DMK_ENABLE_AVX512)

0 commit comments

Comments
 (0)