diff --git a/.github/workflows/event-pull_request.yml b/.github/workflows/event-pull_request.yml index 1dba65a53..af1be19a6 100644 --- a/.github/workflows/event-pull_request.yml +++ b/.github/workflows/event-pull_request.yml @@ -70,6 +70,10 @@ jobs: uses: ./.github/workflows/codeql-analysis.yml secrets: inherit + spaces-isa-baseline: + uses: ./.github/workflows/spaces-isa-baseline.yml + secrets: inherit + spellcheck: runs-on: ubuntu-latest steps: @@ -88,6 +92,7 @@ jobs: - sanitizer - coverage - codeql-analysis + - spaces-isa-baseline - spellcheck runs-on: ubuntu-latest if: ${{ !cancelled() }} diff --git a/.github/workflows/spaces-isa-baseline.yml b/.github/workflows/spaces-isa-baseline.yml new file mode 100644 index 000000000..7527c72e4 --- /dev/null +++ b/.github/workflows/spaces-isa-baseline.yml @@ -0,0 +1,77 @@ +name: spaces ISA baseline guard + +# These jobs assert negatives: that configuring with an ISA-raising flag injected through CXXFLAGS +# makes CMake fail, because src/VecSim/spaces/CMakeLists.txt rejects it. They are kept separate +# from the main build jobs, since a green run here proves nothing about the build itself, only that +# each rejection still fires. Every case below was a real bypass at some point in review, so a case +# that stops failing is a regression, not a cleanup opportunity. + +on: [workflow_call, workflow_dispatch] + +jobs: + reject-isa-override: + name: "configure must fail: CXXFLAGS=${{ matrix.flag }}" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + # A native baseline is unknown at build time, so it can raise the fallback's ISA above + # any deployment CPU. + - flag: -march=native + - flag: -mtune=native + - flag: -mcpu=native + # An explicit feature flag is NOT cancelled by a later -march= baseline: measured on + # gcc 13, "-mavx2 ... -march=x86-64" still emitted hundreds of AVX instructions into the + # scalar fallback. + - flag: -mavx2 + - flag: -mavx512f + - flag: -mfma + steps: + - name: checkout + uses: actions/checkout@v6 + with: + submodules: recursive + - name: assert configure fails on an inherited ${{ matrix.flag }} + env: + CXXFLAGS: ${{ matrix.flag }} + run: | + set +e + output=$(cmake -S . -B build-reject -DVECSIM_BUILD_TESTS=OFF 2>&1) + status=$? + echo "$output" + set -e + if [ "$status" -eq 0 ]; then + echo "Expected the configure to fail because of the inherited ${{ matrix.flag }}, but it succeeded." + exit 1 + fi + if ! echo "$output" | grep -q -- "Refusing to configure"; then + echo "Configure failed, but not with the expected rejection message." + exit 1 + fi + + accept-explicit-baseline: + name: "configure must succeed and warn: CXXFLAGS=-march=x86-64-v2" + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + submodules: recursive + - name: assert an explicit non-native baseline still configures + env: + CXXFLAGS: -march=x86-64-v2 + run: | + set +e + output=$(cmake -S . -B build-accept -DVECSIM_BUILD_TESTS=OFF 2>&1) + status=$? + echo "$output" + set -e + if [ "$status" -ne 0 ]; then + echo "A consumer declaring a legitimate deployment floor must still be able to build." + exit 1 + fi + if ! echo "$output" | grep -qi "baseline"; then + echo "Expected a warning naming the baseline, so the difference is not silent." + exit 1 + fi diff --git a/cmake/tier_probe.cmake b/cmake/tier_probe.cmake new file mode 100644 index 000000000..40df4d625 --- /dev/null +++ b/cmake/tier_probe.cmake @@ -0,0 +1,77 @@ +# Probe whether the current toolchain can actually build a SIMD tier, instead of inferring the +# toolchain's capability from a side channel (a compiler flag check alone, or a binutils version +# table). CHECK_CXX_COMPILER_FLAG only asks the compiler whether it recognizes a flag; it does +# not ask whether the compiler and assembler can carry a real translation unit through to a +# finished object under the tier's complete flag combination. This probe does that: it +# try_compiles the tier's own source file under the tier's own flags, so a flag combination that +# the compiler accepts individually but rejects together, or a flag whose instructions the +# assembler cannot emit, fails here rather than reaching the build. +# +# vecsim_tier_compiles( SOURCE FLAGS [NAME