diff --git a/.github/scripts/check_dispatch_surface.sh b/.github/scripts/check_dispatch_surface.sh new file mode 100755 index 00000000..6583ed6e --- /dev/null +++ b/.github/scripts/check_dispatch_surface.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Runs cmake/validate-dispatch-surface.cmake over the fixtures in +# tests/cmake/dispatch-surface and checks each verdict. +# +# valid-*.cmake must be accepted. +# invalid-*.cmake must be rejected, with a message containing the substring +# given by that fixture's `# EXPECT-ERROR:` line. +# +# Needs nothing but cmake -- no compiler, no dependencies, no build directory. + +set -uo pipefail + +root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +fixtures="${root}/tests/cmake/dispatch-surface" +validator="${root}/cmake/validate-dispatch-surface.cmake" +x86_src_dir="${root}/include/svs/multi-arch/x86" + +if [[ ! -d ${fixtures} ]]; then + echo "no fixture directory: ${fixtures}" >&2 + exit 1 +fi + +# The default declaration must itself be valid -- checked as its own case so that +# a broken default is reported here rather than only at configure time. +run_validator() { + cmake "-DSVS_DISPATCH_SURFACE_FILE=$1" "-DSVS_X86_SRC_DIR=${x86_src_dir}" \ + -P "${validator}" 2>&1 +} + +# CMake indents and line-wraps error text, so compare against a whitespace- +# collapsed copy of the output. +flatten() { tr '\n' ' ' | tr -s '[:space:]' ' '; } + +failures=0 +checked=0 + +check_accepted() { + local fixture=$1 name=$2 output status + output=$(run_validator "${fixture}") + status=$? + if ((status != 0)); then + echo "FAIL ${name}: expected to be accepted, but validation failed:" >&2 + echo "${output}" | sed 's/^/ /' >&2 + ((failures++)) + else + echo "ok ${name}: accepted" + fi + ((checked++)) +} + +check_rejected() { + local fixture=$1 name=$2 expected output status + expected=$(sed -n 's/^# EXPECT-ERROR: *//p' "${fixture}") + if [[ -z ${expected} ]]; then + echo "FAIL ${name}: fixture has no '# EXPECT-ERROR:' line" >&2 + ((failures++)) + ((checked++)) + return + fi + + output=$(run_validator "${fixture}") + status=$? + if ((status == 0)); then + echo "FAIL ${name}: expected rejection, but validation succeeded" >&2 + ((failures++)) + elif [[ $(printf '%s' "${output}" | flatten) != *"${expected}"* ]]; then + echo "FAIL ${name}: rejected, but not for the stated reason." >&2 + echo " expected: ${expected}" >&2 + echo "${output}" | sed 's/^/ actual: /' >&2 + ((failures++)) + else + echo "ok ${name}: rejected (${expected})" + fi + ((checked++)) +} + +check_accepted "${root}/cmake/dispatch-surface.cmake" "dispatch-surface.cmake (default)" + +for fixture in "${fixtures}"/*.cmake; do + name=$(basename "${fixture}") + case ${name} in + valid-*) check_accepted "${fixture}" "${name}" ;; + invalid-*) check_rejected "${fixture}" "${name}" ;; + *) + echo "FAIL ${name}: fixture name must start with valid- or invalid-" >&2 + ((failures++)) + ((checked++)) + ;; + esac +done + +echo +if ((failures != 0)); then + echo "${failures} of ${checked} dispatch-surface checks failed" >&2 + exit 1 +fi +echo "all ${checked} dispatch-surface checks passed" diff --git a/.github/workflows/dispatch-surface.yml b/.github/workflows/dispatch-surface.yml new file mode 100644 index 00000000..df633524 --- /dev/null +++ b/.github/workflows/dispatch-surface.yml @@ -0,0 +1,106 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The set of distance kernels compiled ahead of time is declared once, in +# cmake/dispatch-surface.cmake, and generated from there. Two things have to stay +# true for that to be worth anything: the declaration must be checked rather than +# trusted, and it must be genuinely configurable -- a knob nobody turns is a knob +# that quietly stops working. + +name: Dispatch Surface + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + declaration: + name: declaration is checked, committed header is current + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v6 + + # Every fixture in tests/cmake/dispatch-surface, plus the default + # declaration. Needs nothing but cmake. + - name: Accept and reject declarations + run: .github/scripts/check_dispatch_surface.sh + + # include/svs/core/distance/dispatch_surface.h is generated but committed, so + # that a bare `-I include` compile works without cmake. A configure refreshes + # it; if that produces a diff, either the declaration changed without a + # reconfigure or the header was edited by hand. + - name: Configure with the default surface + run: | + cmake -B "${{ runner.temp }}/build" -S "${GITHUB_WORKSPACE}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DSVS_BUILD_TESTS=NO \ + -DSVS_BUILD_BINARIES=NO + + - name: Committed header matches the declaration + run: | + if ! git diff --exit-code -- include/svs/core/distance/dispatch_surface.h; then + echo "::error::include/svs/core/distance/dispatch_surface.h is stale." \ + "It is generated from cmake/dispatch-surface.cmake -- re-run cmake" \ + "and commit the result. Do not edit it by hand." + exit 1 + fi + + non-default-surface: + name: builds and tests with a non-default surface + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v6 + + # valid-reduced.cmake shares no extent with the default declaration, so a + # build that silently fell back to the committed header would fail to + # compile rather than pass by accident. + - name: Configure + run: | + cmake -B "${{ runner.temp }}/build" -S "${GITHUB_WORKSPACE}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DSVS_BUILD_TESTS=YES \ + -DSVS_BUILD_BINARIES=NO \ + -DSVS_DISPATCH_SURFACE_FILE="${GITHUB_WORKSPACE}/tests/cmake/dispatch-surface/valid-reduced.cmake" + + - name: Build + working-directory: ${{ runner.temp }}/build + run: make -j$(nproc) + + # Correctness must not depend on which extents have a fixed-extent kernel: + # an extent without one is served by the svs::Dynamic kernel instead. The + # long-running tests are covered by the default-surface build. + - name: Run tests + env: + CTEST_OUTPUT_ON_FAILURE: 1 + working-directory: ${{ runner.temp }}/build/tests + run: ctest -C Release -LE long + + # Overriding the surface for one build must not rewrite the committed header. + - name: Committed header was left alone + run: | + if ! git diff --exit-code -- include/svs/core/distance/dispatch_surface.h; then + echo "::error::A build with an overridden dispatch surface rewrote the" \ + "committed header. Only the default declaration may refresh it." + exit 1 + fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index baf69799..df9502bd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,3 +34,21 @@ repos: args: [--markdown-linebreak-ext=md] exclude: .*\.svg$ - id: mixed-line-ending + + - repo: local + hooks: + # Cheap enough to run on every touch of the declaration, and it needs + # nothing but cmake -- no compiler, no build directory. + - id: dispatch-surface + name: dispatch surface declaration + entry: .github/scripts/check_dispatch_surface.sh + language: script + pass_filenames: false + # (?x) ignores the layout below; without it the alternation is one + # unreadable line and every edit to it risks silently matching nothing. + files: | + (?x)^( + cmake/(dispatch-surface|validate-dispatch-surface)\.cmake + |tests/cmake/dispatch-surface/.*\.cmake + |\.github/scripts/check_dispatch_surface\.sh + )$ diff --git a/cmake/generate-dispatch-surface.cmake b/cmake/generate-dispatch-surface.cmake index 65aaa7c4..b1aa9a7c 100644 --- a/cmake/generate-dispatch-surface.cmake +++ b/cmake/generate-dispatch-surface.cmake @@ -27,12 +27,11 @@ set(SVS_DISPATCH_SURFACE_FILE "${SVS_DEFAULT_DISPATCH_SURFACE_FILE}" CACHE FILEPATH "Declaration of the ahead-of-time distance-kernel dispatch surface" ) -if(NOT EXISTS "${SVS_DISPATCH_SURFACE_FILE}") - message(FATAL_ERROR - "SVS_DISPATCH_SURFACE_FILE does not exist: ${SVS_DISPATCH_SURFACE_FILE}" - ) -endif() -include("${SVS_DISPATCH_SURFACE_FILE}") + +# Reads the declaration and rejects it if it is malformed. Also runnable on its +# own -- see .github/scripts/check_dispatch_surface.sh. +set(SVS_X86_SRC_DIR "${PROJECT_SOURCE_DIR}/include/svs/multi-arch/x86") +include("${CMAKE_CURRENT_LIST_DIR}/validate-dispatch-surface.cmake") file(REAL_PATH "${SVS_DISPATCH_SURFACE_FILE}" svs_surface_real) file(REAL_PATH "${SVS_DEFAULT_DISPATCH_SURFACE_FILE}" svs_default_surface_real) @@ -54,92 +53,15 @@ set_property( ) ##### -##### Validate the extent list -##### - -if(NOT SVS_SUPPORTED_DIMS) - message(FATAL_ERROR - "SVS_SUPPORTED_DIMS is empty in ${SVS_DISPATCH_SURFACE_FILE}. At least " - "one fixed extent is required." - ) -endif() - -foreach(dim IN LISTS SVS_SUPPORTED_DIMS) - if(NOT dim MATCHES "^[1-9][0-9]*$") - message(FATAL_ERROR - "SVS_SUPPORTED_DIMS contains '${dim}', which is not a positive " - "integer. svs::Dynamic is required and is appended automatically, " - "so it must not be listed." - ) - endif() -endforeach() - -set(svs_dims_sorted ${SVS_SUPPORTED_DIMS}) -list(REMOVE_DUPLICATES svs_dims_sorted) -list(LENGTH SVS_SUPPORTED_DIMS svs_dims_given) -list(LENGTH svs_dims_sorted svs_dims_unique) -if(NOT svs_dims_given EQUAL svs_dims_unique) - message(FATAL_ERROR - "SVS_SUPPORTED_DIMS contains duplicate extents. Every extent must " - "appear exactly once." - ) -endif() - -# svs::Dynamic is mandatory: it is what serves every dimensionality without a -# fixed-extent kernel, and the library is incorrect without it. -set(svs_dim_list ${SVS_SUPPORTED_DIMS} "svs::Dynamic") -list(LENGTH svs_dim_list SVS_GEN_DIM_COUNT) - -##### -##### Validate the ISA levels -##### - -if(NOT SVS_ISA_LEVELS) - message(FATAL_ERROR "SVS_ISA_LEVELS is empty in ${SVS_DISPATCH_SURFACE_FILE}.") -endif() - -set(svs_seen_levels) -set(svs_seen_infixes) -foreach(level_spec IN LISTS SVS_ISA_LEVELS) - string(REPLACE "|" ";" level_fields "${level_spec}") - list(LENGTH level_fields nfields) - if(NOT nfields EQUAL 3) - message(FATAL_ERROR - "Malformed SVS_ISA_LEVELS entry '${level_spec}': expected exactly " - "three '|'-separated fields ||." - ) - endif() - list(GET level_fields 0 level) - list(GET level_fields 1 arch) - list(GET level_fields 2 infix) - foreach(field level arch infix) - if(NOT ${field}) - message(FATAL_ERROR - "Malformed SVS_ISA_LEVELS entry '${level_spec}': ${field} is empty." - ) - endif() - endforeach() - if(level IN_LIST svs_seen_levels) - message(FATAL_ERROR "Duplicate ISA level '${level}' in SVS_ISA_LEVELS.") - endif() - if(infix IN_LIST svs_seen_infixes) - message(FATAL_ERROR - "Duplicate TU infix '${infix}' in SVS_ISA_LEVELS; infixes name " - "generated files and must be unique." - ) - endif() - list(APPEND svs_seen_levels ${level}) - list(APPEND svs_seen_infixes ${infix}) -endforeach() - -##### -##### Generate the header +##### Build the macro bodies ##### # Line continuations are emitted with a trailing backslash; the generated macros # are one logical line each. +set(SVS_GEN_DIM_COUNT ${SVS_DIM_COUNT}) + set(SVS_GEN_DIM_LOOP "\\\n") -foreach(dim IN LISTS svs_dim_list) +foreach(dim IN LISTS SVS_DIM_LIST) string(APPEND SVS_GEN_DIM_LOOP " M(${dim}) \\\n") endforeach() string(APPEND SVS_GEN_DIM_LOOP " /* end */") @@ -147,7 +69,6 @@ string(APPEND SVS_GEN_DIM_LOOP " /* end */") set(SVS_GEN_TARGET_LOOP "\\\n") set(SVS_GEN_LEVEL_LOOP "\\\n") set(SVS_DISPATCH_TU_SPECS) -set(svs_x86_src_dir "${PROJECT_SOURCE_DIR}/include/svs/multi-arch/x86") foreach(level_spec IN LISTS SVS_ISA_LEVELS) string(REPLACE "|" ";" level_fields "${level_spec}") list(GET level_fields 0 level) @@ -155,22 +76,15 @@ foreach(level_spec IN LISTS SVS_ISA_LEVELS) list(GET level_fields 2 infix) string(APPEND SVS_GEN_LEVEL_LOOP " M(${level}) \\\n") - foreach(dim IN LISTS svs_dim_list) + foreach(dim IN LISTS SVS_DIM_LIST) string(APPEND SVS_GEN_TARGET_LOOP " M(${dim}, ${level}) \\\n") endforeach() - # One translation unit per level, named after the level's infix. The file - # itself is short -- it loops over the generated extent list -- but it is - # committed rather than generated, because the private repository compiles - # these sources by path. - set(tu_src "${svs_x86_src_dir}/${infix}.cpp") - if(NOT EXISTS "${tu_src}") - message(FATAL_ERROR - "ISA level '${level}' has no translation unit: expected ${tu_src}. " - "Adding a level to SVS_ISA_LEVELS requires creating that file." - ) - endif() - list(APPEND SVS_DISPATCH_TU_SPECS "${tu_src}|${level}|${arch}|${infix}") + # One translation unit per level, committed rather than generated because the + # downstream repository compiles these sources by path. Validation checks it exists. + list(APPEND SVS_DISPATCH_TU_SPECS + "${SVS_X86_SRC_DIR}/${infix}.cpp|${level}|${arch}|${infix}" + ) list(APPEND svs_level_report "AVX_AVAILABILITY::${level} -march=${arch} ${infix}.cpp" ) @@ -217,7 +131,7 @@ endif() list(LENGTH SVS_ISA_LEVELS svs_level_count) string(REPLACE ";" " " svs_dims_display "${SVS_SUPPORTED_DIMS}") message(STATUS - "Dispatch surface: ${SVS_GEN_DIM_COUNT} extents x ${svs_level_count} ISA levels" + "Dispatch surface: ${SVS_DIM_COUNT} extents x ${svs_level_count} ISA levels" ) message(STATUS " extents: ${svs_dims_display} svs::Dynamic") foreach(entry IN LISTS svs_level_report) diff --git a/cmake/validate-dispatch-surface.cmake b/cmake/validate-dispatch-surface.cmake new file mode 100644 index 00000000..7bf1f21a --- /dev/null +++ b/cmake/validate-dispatch-surface.cmake @@ -0,0 +1,148 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +##### +##### Reads and checks a dispatch-surface declaration. +##### +##### This is deliberately free of build-system state so that it runs in script +##### mode as well as during configure: +##### +##### cmake -DSVS_DISPATCH_SURFACE_FILE= \ +##### -DSVS_X86_SRC_DIR= \ +##### -P cmake/validate-dispatch-surface.cmake +##### +##### Inputs: +##### SVS_DISPATCH_SURFACE_FILE -- the declaration to read +##### SVS_X86_SRC_DIR -- where per-level translation units live +##### +##### Outputs: +##### SVS_SUPPORTED_DIMS, SVS_ISA_LEVELS -- verbatim from the declaration +##### SVS_DIM_LIST -- extents, with svs::Dynamic appended +##### SVS_DIM_COUNT -- length of SVS_DIM_LIST +##### SVS_FIXED_DIM_COUNT -- length of SVS_SUPPORTED_DIMS +##### + +## In script mode there is no cmake_minimum_required, so policies default to OLD. +## Both of these are load-bearing here: CMP0007 keeps an empty `|`-field from +## vanishing when the entry is split, and CMP0057 enables `IN_LIST`. +cmake_policy(PUSH) +cmake_policy(SET CMP0007 NEW) +cmake_policy(SET CMP0057 NEW) + +if(NOT SVS_DISPATCH_SURFACE_FILE) + message(FATAL_ERROR "SVS_DISPATCH_SURFACE_FILE is not set.") +endif() +if(NOT EXISTS "${SVS_DISPATCH_SURFACE_FILE}") + message(FATAL_ERROR + "SVS_DISPATCH_SURFACE_FILE does not exist: ${SVS_DISPATCH_SURFACE_FILE}" + ) +endif() +if(NOT SVS_X86_SRC_DIR) + message(FATAL_ERROR "SVS_X86_SRC_DIR is not set.") +endif() + +# The declaration is plain CMake: it sets SVS_SUPPORTED_DIMS and SVS_ISA_LEVELS +# and does nothing else. Clear them first so that a declaration which forgets one +# is reported as empty rather than inheriting a value from the caller. +set(SVS_SUPPORTED_DIMS) +set(SVS_ISA_LEVELS) +include("${SVS_DISPATCH_SURFACE_FILE}") + +##### +##### The extent list +##### + +if(NOT SVS_SUPPORTED_DIMS) + message(FATAL_ERROR + "SVS_SUPPORTED_DIMS is empty in ${SVS_DISPATCH_SURFACE_FILE}. At least " + "one fixed extent is required." + ) +endif() + +foreach(dim IN LISTS SVS_SUPPORTED_DIMS) + if(NOT dim MATCHES "^[1-9][0-9]*$") + message(FATAL_ERROR + "SVS_SUPPORTED_DIMS contains '${dim}', which is not a positive " + "integer. svs::Dynamic is required and is appended automatically, " + "so it must not be listed." + ) + endif() +endforeach() + +set(svs_dims_deduped ${SVS_SUPPORTED_DIMS}) +list(REMOVE_DUPLICATES svs_dims_deduped) +list(LENGTH SVS_SUPPORTED_DIMS SVS_FIXED_DIM_COUNT) +list(LENGTH svs_dims_deduped svs_dims_unique) +if(NOT SVS_FIXED_DIM_COUNT EQUAL svs_dims_unique) + message(FATAL_ERROR + "SVS_SUPPORTED_DIMS contains duplicate extents. Every extent must " + "appear exactly once." + ) +endif() + +# svs::Dynamic is mandatory: it is what serves every dimensionality without a +# fixed-extent kernel, and the library is incorrect without it. +set(SVS_DIM_LIST ${SVS_SUPPORTED_DIMS} "svs::Dynamic") +list(LENGTH SVS_DIM_LIST SVS_DIM_COUNT) + +##### +##### The ISA levels +##### + +if(NOT SVS_ISA_LEVELS) + message(FATAL_ERROR "SVS_ISA_LEVELS is empty in ${SVS_DISPATCH_SURFACE_FILE}.") +endif() + +set(svs_seen_levels) +set(svs_seen_infixes) +foreach(level_spec IN LISTS SVS_ISA_LEVELS) + string(REPLACE "|" ";" level_fields "${level_spec}") + list(LENGTH level_fields nfields) + if(NOT nfields EQUAL 3) + message(FATAL_ERROR + "Malformed SVS_ISA_LEVELS entry '${level_spec}': expected exactly " + "three '|'-separated fields ||." + ) + endif() + list(GET level_fields 0 level) + list(GET level_fields 1 arch) + list(GET level_fields 2 infix) + foreach(field level arch infix) + if(NOT ${field}) + message(FATAL_ERROR + "Malformed SVS_ISA_LEVELS entry '${level_spec}': ${field} is empty." + ) + endif() + endforeach() + if(level IN_LIST svs_seen_levels) + message(FATAL_ERROR "Duplicate ISA level '${level}' in SVS_ISA_LEVELS.") + endif() + if(infix IN_LIST svs_seen_infixes) + message(FATAL_ERROR + "Duplicate TU infix '${infix}' in SVS_ISA_LEVELS; infixes name " + "generated files and must be unique." + ) + endif() + if(NOT EXISTS "${SVS_X86_SRC_DIR}/${infix}.cpp") + message(FATAL_ERROR + "ISA level '${level}' has no translation unit: expected " + "${SVS_X86_SRC_DIR}/${infix}.cpp. Adding a level to SVS_ISA_LEVELS " + "requires creating that file." + ) + endif() + list(APPEND svs_seen_levels ${level}) + list(APPEND svs_seen_infixes ${infix}) +endforeach() + +cmake_policy(POP) diff --git a/tests/cmake/dispatch-surface/invalid-duplicate-extent.cmake b/tests/cmake/dispatch-surface/invalid-duplicate-extent.cmake new file mode 100644 index 00000000..fe809c05 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-duplicate-extent.cmake @@ -0,0 +1,17 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# EXPECT-ERROR: SVS_SUPPORTED_DIMS contains duplicate extents +set(SVS_SUPPORTED_DIMS 128 256 128) +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/invalid-duplicate-infix.cmake b/tests/cmake/dispatch-surface/invalid-duplicate-infix.cmake new file mode 100644 index 00000000..8e05c06f --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-duplicate-infix.cmake @@ -0,0 +1,22 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Two levels cannot share one translation unit: it is compiled once, at one +# instruction budget. +# EXPECT-ERROR: Duplicate TU infix 'avx2' +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS + "AVX2|haswell|avx2" + "AVX512|cascadelake|avx2" +) diff --git a/tests/cmake/dispatch-surface/invalid-duplicate-level.cmake b/tests/cmake/dispatch-surface/invalid-duplicate-level.cmake new file mode 100644 index 00000000..09110983 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-duplicate-level.cmake @@ -0,0 +1,21 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# One enumerator cannot have two instruction budgets. +# EXPECT-ERROR: Duplicate ISA level 'AVX2' +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS + "AVX2|haswell|avx2" + "AVX2|cascadelake|avx512" +) diff --git a/tests/cmake/dispatch-surface/invalid-dynamic-listed.cmake b/tests/cmake/dispatch-surface/invalid-dynamic-listed.cmake new file mode 100644 index 00000000..127527fc --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-dynamic-listed.cmake @@ -0,0 +1,18 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# svs::Dynamic is appended automatically and must not be spelled out. +# EXPECT-ERROR: svs::Dynamic is required and is appended automatically +set(SVS_SUPPORTED_DIMS 128 svs::Dynamic) +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/invalid-level-empty-field.cmake b/tests/cmake/dispatch-surface/invalid-level-empty-field.cmake new file mode 100644 index 00000000..5f548ba9 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-level-empty-field.cmake @@ -0,0 +1,17 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# EXPECT-ERROR: arch is empty +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS "AVX2||avx2") diff --git a/tests/cmake/dispatch-surface/invalid-level-missing-field.cmake b/tests/cmake/dispatch-surface/invalid-level-missing-field.cmake new file mode 100644 index 00000000..2f71f45f --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-level-missing-field.cmake @@ -0,0 +1,18 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The TU infix is absent. +# EXPECT-ERROR: expected exactly three '|'-separated fields +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS "AVX2|haswell") diff --git a/tests/cmake/dispatch-surface/invalid-level-without-tu.cmake b/tests/cmake/dispatch-surface/invalid-level-without-tu.cmake new file mode 100644 index 00000000..3ffc6e49 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-level-without-tu.cmake @@ -0,0 +1,22 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Adding a level means adding the translation unit that defines its kernels. +# Without this check the level would silently contribute nothing. +# EXPECT-ERROR: has no translation unit +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS + "AVX2|haswell|avx2" + "AVX512VNNI|sapphirerapids|avx512vnni" +) diff --git a/tests/cmake/dispatch-surface/invalid-missing-extents.cmake b/tests/cmake/dispatch-surface/invalid-missing-extents.cmake new file mode 100644 index 00000000..b5cfb55a --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-missing-extents.cmake @@ -0,0 +1,17 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The variable is never set at all. +# EXPECT-ERROR: SVS_SUPPORTED_DIMS is empty +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/invalid-no-extents.cmake b/tests/cmake/dispatch-surface/invalid-no-extents.cmake new file mode 100644 index 00000000..d303cde7 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-no-extents.cmake @@ -0,0 +1,17 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# EXPECT-ERROR: SVS_SUPPORTED_DIMS is empty +set(SVS_SUPPORTED_DIMS) +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/invalid-no-levels.cmake b/tests/cmake/dispatch-surface/invalid-no-levels.cmake new file mode 100644 index 00000000..c3c2b4a9 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-no-levels.cmake @@ -0,0 +1,17 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# EXPECT-ERROR: SVS_ISA_LEVELS is empty +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS) diff --git a/tests/cmake/dispatch-surface/invalid-non-numeric-extent.cmake b/tests/cmake/dispatch-surface/invalid-non-numeric-extent.cmake new file mode 100644 index 00000000..2af43245 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-non-numeric-extent.cmake @@ -0,0 +1,17 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# EXPECT-ERROR: SVS_SUPPORTED_DIMS contains '12a' +set(SVS_SUPPORTED_DIMS 128 12a) +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/invalid-zero-extent.cmake b/tests/cmake/dispatch-surface/invalid-zero-extent.cmake new file mode 100644 index 00000000..c74f51e9 --- /dev/null +++ b/tests/cmake/dispatch-surface/invalid-zero-extent.cmake @@ -0,0 +1,18 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A zero-length vector has no kernel to compile. +# EXPECT-ERROR: SVS_SUPPORTED_DIMS contains '0' +set(SVS_SUPPORTED_DIMS 0 128) +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/valid-minimal.cmake b/tests/cmake/dispatch-surface/valid-minimal.cmake new file mode 100644 index 00000000..089a50ee --- /dev/null +++ b/tests/cmake/dispatch-surface/valid-minimal.cmake @@ -0,0 +1,19 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The smallest declaration that is still a library: one fixed extent, one ISA +# level. Every other dimensionality is served by the svs::Dynamic kernel. + +set(SVS_SUPPORTED_DIMS 128) +set(SVS_ISA_LEVELS "AVX2|haswell|avx2") diff --git a/tests/cmake/dispatch-surface/valid-reduced.cmake b/tests/cmake/dispatch-surface/valid-reduced.cmake new file mode 100644 index 00000000..4a9bfcc0 --- /dev/null +++ b/tests/cmake/dispatch-surface/valid-reduced.cmake @@ -0,0 +1,25 @@ +# Copyright 2026 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A surface that shares no extent with the default declaration, so a build using +# it cannot accidentally pass by reusing a committed header. Both ISA levels are +# kept so that runtime dispatch is still exercised on an AVX-512 host. +# +# Built and tested by the `non-default surface` CI job. + +set(SVS_SUPPORTED_DIMS 32 384) +set(SVS_ISA_LEVELS + "AVX2|haswell|avx2" + "AVX512|cascadelake|avx512" +)