Skip to content

refactor(dispatch): Generate the dispatch surface from a single declaration - #372

Open
ahuber21 wants to merge 7 commits into
dispatch/01-l2-d160-externfrom
dispatch/02-generate-surface
Open

refactor(dispatch): Generate the dispatch surface from a single declaration#372
ahuber21 wants to merge 7 commits into
dispatch/01-l2-d160-externfrom
dispatch/02-generate-surface

Conversation

@ahuber21

@ahuber21 ahuber21 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

The set of distance kernels compiled ahead of time -- extents x ISA levels -- was
written out by hand everywhere it was needed: three extern template blocks,
two per-architecture translation units, and supported_dim_list. Adding an
extent meant editing six lists in agreement, and the preceding commit is what
happens when one of them disagrees.

cmake/dispatch-surface.cmake is now the only place the extent list and the ISA
levels are written down. Everything else is derived from it:

  • include/svs/core/distance/dispatch_surface.h, which drives every
    extern template, every explicit instantiation, and supported_dim_list
  • the object library each level's translation unit is compiled into, and the
    instruction budget it is compiled at

The generated header is committed as well as generated, so that consuming the
headers with a bare -I include and no CMake keeps working.

Configure now prints the surface in full rather than a count -- every extent,
and for each level its enumerator, its instruction budget and its translation
unit:

-- Dispatch surface: 9 extents x 2 ISA levels
--   extents: 64 96 100 128 160 200 512 768 svs::Dynamic
--   level:   AVX_AVAILABILITY::AVX2 -march=haswell avx2.cpp
--   level:   AVX_AVAILABILITY::AVX512 -march=cascadelake avx512.cpp
--   not in the surface: NONE
--            dispatched to, but compiled by no translation unit, so every
--            consumer instantiates those kernels itself, at its own -march

AVX_AVAILABILITY::NONE is a real hole and is now named as one: the entry points
fall back to it, no translation unit compiles it, so every consumer builds those
kernels itself at whatever -march it happens to use. A baseline translation
unit is its own change, not this one.

Seven ctest tests replace what was a paragraph of nm commands in a review
comment. All of them derive their expectations from the declaration, so they hold
for an overridden surface too:

  • dispatch_surface_probe, dispatch_entry_probe -- consumers built at
    -march=x86-64. One names every kernel in the surface directly, the other
    reaches them through the entry points; a declared-but-uninstantiated kernel is
    an undefined symbol in the first, and a kernel the entry points cannot reach is
    one in the second.
  • dispatch_surface_linkage -- the archive defines every kernel the probe names,
    nothing more, and the probe instantiated none of them itself: 864 kernels.
  • dispatch_surface_declaration -- the same 864, but counted from the three
    hand-written sources (the extent list, the type-pair macros, the enumerator
    order) without consulting the generated header, so a generator bug has nowhere
    to hide: 2 levels x 9 extents x 3 distances x {AVX2: 16, AVX512: 16} pairs.
  • dispatch_instructions_avx2, dispatch_instructions_avx512 -- one per level,
    disassembling that level's object file against the instruction budget its
    runtime predicate promises. The AVX2 object must contain ymm and no zmm,
    mask or VNNI encodings; a level that emits what its predicate does not
    guarantee is an illegal-instruction fault on a host the dispatcher considers
    supported, which no symbol-table check can see.
  • dispatch_surface_execution -- the only check that observes a run rather than
    a symbol table: gdb breaks on every level's kernel for one extent and reports
    which one a call through the entry points actually enters. A specialization
    that disappears behind an #if still links and still counts.

No behaviour change: the same 864 kernels, at the same instruction budgets, with
the same mangled names. compute() is still compute(); nothing about the call
chain or the runtime dispatch moves.

Part 2 of 4 of the ISA dispatching v2 milestone.

@ahuber21 ahuber21 added this to the ISA dispatching v2 milestone Aug 24, 2026
@ahuber21

Copy link
Copy Markdown
Contributor Author

@copilot new or modified files should have 2026 in their copyright header. Fix

Copilot AI commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@copilot new or modified files should have 2026 in their copyright header. Fix

Updated the headers in 408513c.

@ahuber21 ahuber21 changed the title Generate the dispatch surface from a single declaration refactor(dispatch): Generate the dispatch surface from a single declaration Aug 24, 2026
@ahuber21
ahuber21 force-pushed the dispatch/02-generate-surface branch from c2d57c2 to fe66e35 Compare August 24, 2026 10:28
@ahuber21
ahuber21 marked this pull request as ready for review August 25, 2026 09:41
@ahuber21
ahuber21 requested a review from ethanglaser August 25, 2026 09:41
@mergify

mergify Bot commented Aug 25, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

ahuber21 and others added 6 commits August 25, 2026 17:23
The set of distance kernels compiled ahead of time -- extents x ISA levels
-- was written out by hand in every place that needed it: three extern
template blocks, two per-arch translation units, the `supported_dim_list`
array, and 48 near-identical `SPEC struct` lines in the instantiation
macros. Adding an extent meant editing all of them and hoping none was
missed. One had been: `euclidean.h` was missing d=160 for AVX2 (fixed in
the preceding commit), which silently made consumers instantiate that
kernel locally at their own -march.

Declare the surface once, in `cmake/dispatch-surface.cmake`:

    set(SVS_SUPPORTED_DIMS 64 96 100 128 160 200 512 768)
    set(SVS_ISA_LEVELS
        "AVX2|haswell|avx2"
        "AVX512|cascadelake|avx512"
    )

`cmake/generate-dispatch-surface.cmake` validates it and writes
`include/svs/core/distance/dispatch_surface.h`, which exports
`SVS_FOR_EACH_SUPPORTED_DIM(M)`, `SVS_FOR_EACH_DISPATCH_TARGET(M)` and
`SVS_SUPPORTED_DIM_COUNT`. Everything that used to spell the list out now
loops over one of those. 108 hand-written instantiation lines become 0.

Type pairs stay in C++, in `multi-arch/x86/preprocessor.h`. A pair exists
because an implementation exists for it -- sometimes a hand-written one --
so the list belongs beside those implementations, not in the build system.

`svs::Dynamic` is appended automatically and cannot be listed: it is what
serves every dimensionality without a fixed-extent kernel, and the library
is incorrect without it.

The generated header is committed as well as generated. The build always
compiles against the build-tree copy, placed ahead of the source include
directory, and installs it over the committed one; the committed copy is
refreshed only when the declaration is the default, so overriding the
surface for a one-off build cannot rewrite the tree. Committing it keeps a
bare `-I include` compile working without CMake -- which the downstream
repository relies on, since it compiles `multi-arch/x86/{avx2,avx512}.cpp`
by path with its own CMake.

No behaviour change: the static library exports the same 864 symbols with
the same sizes, and the two arch objects are symbol-identical before and
after, both here and in the downstream build. `[distance]` passes
(134402115 assertions, 13 test cases).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every comment this branch adds now says what the code cannot say for itself
and stops there. The block comments that restated the surrounding code, or
spent five lines on a hazard that takes two, are gone; the hazards themselves
stay, each naming its failure mode.

Comment-only. The non-comment diff against the previous tip is empty.
A kernel that is missing its `extern template` declaration does not
produce an error. The consumer instantiates it locally instead, from the
generic primary template -- and in a baseline consumer translation unit
the vectorized partial specializations are not even visible, since they
are guarded on SVS_AVX2 / SVS_AVX512_F. So the consumer silently gets a
scalar loop where the library has a vectorized kernel, compiled at
whatever -march the consumer happens to use. That is the bug that shipped
for L2 at d=160 with AVX2.

Nothing could catch it, because nothing referenced the whole surface at
once. This adds a consumer that does: tests/multi-arch/x86/link_probe.cpp
names every kernel the surface declares -- every (extent, ISA level) pair,
every element-type pair, all three distances -- and nothing else. It is
compiled at -march=x86-64, like an arbitrary consumer of the headers, and
two tests are run against it:

  dispatch_surface_probe    calls every kernel whose ISA level this host
                            satisfies, so a kernel compiled beyond what
                            its level guarantees faults here
  dispatch_surface_linkage  reads the object's symbol table and requires
                            the kernels it references to be exactly the
                            kernels the library defines

The linkage check is host-independent and covers the whole surface
everywhere; the run covers only what the host can reach.

On the default surface the two sets match exactly at 864 kernels, and on
the reduced surface used by the non-default-surface CI job, at 288. All
three failure modes were confirmed to fire: dropping the L2 extern block
reports 288 kernels instantiated by the probe itself, and checking against
an archive missing the AVX-512 translation unit reports its 432 kernels as
declared but never instantiated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"9 extents (8 fixed + svs::Dynamic) x 2 ISA levels" says nothing about which
extents, which levels, or what instruction budget each level compiles at, so
reading the log gave no way to tell a correct surface from a plausible one.

Also name the AVX_AVAILABILITY enumerators that are not in the surface, since
that is the question the old count invited and could not answer: NONE is
dispatched to but has no translation unit, so every consumer instantiates its
kernels itself, at the consumer's own -march.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four checks, each closing a failure mode the link probe cannot see.

dispatch_surface_declaration derives what the library must contain from the
three hand-written sources -- the extent list and ISA levels, the type-pair
lists, and the AVX_AVAILABILITY enumerator order -- and never reads the
generated header. The linkage check compares the archive against a probe built
from that header, so a generator that dropped an extent would drop it from both
and still agree; this one has nowhere to hide. It also checks the entry-point
consumer, whose kernels must all come from the archive: one it defines itself
is an extern declaration that is missing.

dispatch_instructions_<level>, one test per ISA level, disassembles the level's
object file and holds it to a budget table keyed by -march. A level guarantees
only what its runtime predicate tests, so an instruction outside that budget
faults on a host the dispatcher routes there -- and no symbol-table check can
see it.

dispatch_surface_execution is the only check that observes a kernel run rather
than exist: a specialization lost behind an `#if` still links and still counts.
It breaks on every level's kernel for one extent and confirms the run enters
the level this host satisfies. Weaker levels are covered by hosts that satisfy
only those.

dispatch_entry_probe reaches the kernels through the entry points rather than
by naming the Impl classes, which is what makes the consumer half of the
declaration check meaningful.

nm, objdump and gdb are each optional: a missing tool skips its tests rather
than failing the build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Answer the review on the declaration's maintenance story: cmake/dispatch-surface.cmake
now states what to edit for an extent, a level, a type pair or an instruction budget,
and why AVX_AVAILABILITY::NONE has no row. Move the four checker scripts to
cmake/dispatch-checks/ with a README, and make the preprocessor.h type-pair comment
stand without the refactoring for context.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ahuber21
ahuber21 force-pushed the dispatch/02-generate-surface branch from dd2db8d to 5fafe1d Compare August 25, 2026 15:23
@rfsaliev
rfsaliev requested a balanced review from Copilot August 28, 2026 08:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Centralizes x86 distance-kernel extents and ISA levels into one generated dispatch declaration.

Changes:

  • Generates kernel declarations, instantiations, and supported dimensions from one CMake surface.
  • Adds linkage, instruction, declaration, and runtime dispatch checks.
  • Installs the generated header and documents maintenance workflows.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
CMakeLists.txt Installs the generated surface header.
cmake/AGENTS.md Documents dispatch ownership.
cmake/dispatch-surface.cmake Declares extents and ISA levels.
cmake/generate-dispatch-surface.cmake Validates and generates the surface.
cmake/multi-arch.cmake Builds ISA objects from the declaration.
cmake/templates/dispatch_surface.h.in Defines the generated-header template.
cmake/dispatch-checks/README.md Documents dispatch checks.
cmake/dispatch-checks/check-dispatch-declaration.cmake Validates declared kernel counts.
cmake/dispatch-checks/check-dispatch-execution.cmake Verifies runtime routing.
cmake/dispatch-checks/check-dispatch-instructions.cmake Inspects ISA instruction budgets.
cmake/dispatch-checks/check-dispatch-linkage.cmake Verifies kernel linkage.
include/svs/core/distance/cosine.h Generates cosine extern templates.
include/svs/core/distance/dispatch_surface.h Commits the default generated surface.
include/svs/core/distance/distance_core.h Generates supported dimensions.
include/svs/core/distance/euclidean.h Generates L2 extern templates.
include/svs/core/distance/inner_product.h Generates IP extern templates.
include/svs/multi-arch/x86/avx2.cpp Generates AVX2 instantiations.
include/svs/multi-arch/x86/avx512.cpp Generates AVX512 instantiations.
include/svs/multi-arch/x86/preprocessor.h Defines reusable instantiation macros.
tests/CMakeLists.txt Enables multi-architecture tests.
tests/multi-arch/CMakeLists.txt Registers dispatch probes and checks.
tests/multi-arch/x86/entry_probe.cpp Exercises public dispatch entry points.
tests/multi-arch/x86/host_levels.h Models host ISA predicates.
tests/multi-arch/x86/link_probe.cpp References every declared kernel.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cmake/AGENTS.md Outdated
"x86-64||ymm zmm mask vnni"
"haswell|ymm|zmm mask vnni"
"skylake-avx512|zmm|vnni"
"cascadelake|zmm|"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be fixed in 375 -- and all PRs will be merged together as a stack.

@rfsaliev rfsaliev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change is pretty big with high review/maintenance cost.
It seems like the huge AI generated change to be reviewed by AI.
Scripts should be simplified and well structured.

@@ -0,0 +1,72 @@
/*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand, this file is autogenerated from dispatch_surface.h.in.
Why do we need to track it in repository?
Suggesting to remove the file from repository but generate it in binary directory during config/build.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a default version for reference. If someone researched the code on GH only, or on a fresh checkout without prior compilation there are no missing files.

@@ -0,0 +1,66 @@
<!--

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like files in this directory intended for tests.
IMHO it makes sense to move them to /test

@@ -0,0 +1,250 @@
# Copyright 2026 Intel Corporation

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the cmake code here is pretty complicated.
Writing it in form of straightforward script leads high maintenance costs.
Please, modify the code to make it more structured, e.g. split to functions.

# Distance kernels, and nothing else in the archive. Mangled names are used
# throughout: demangled ones carry `[clone .isra.0]` suffixes that differ between
# a local instantiation and an explicit one.
set(svs_kernel_regex "_ZN3svs8distance.*Impl")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mangling rules are compiler-specific.
Are you sure that the pattern will work for all supported compilers/versions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a concern I raised with the agent. Don't have the exact reply, but it was along the lines of:

  • This regex will work for everything we currently use
  • Checking our own emitted mangled symbols gives us more visibility/control over what we release. A change here is probably not intentional.

The second point sounded quite reasonable to me. So I figured it's worth giving this a shot. If we find the maintenance cost is too high, we can improve the tests.
From my understand though, these symbols should have been stable for the last few months and I don't see any changes on the horizon.

@rfsaliev

Copy link
Copy Markdown
Member

@copilot, review the PR from readability/maintainability perspective, focus on cmake scripts and suggest changes to make scripts clearer and shorter, propose replacing complicated cmake code blocks with external tools if any or 'probing' sources for existing validation scripts.
Find an opportunity to re-use cmake code in form of functions/macros

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@rfsaliev

Copy link
Copy Markdown
Member

Review: readability & maintainability of the CMake scripts

The single-declaration → generated-surface design is sound, but the implementation reimplements a lot of general-purpose logic — C++ name demangling, header parsing, symbol tallying, list truncation, argument validation — in raw cmake -P scripts, and re-codes the same building blocks in each of the four checkers. That's the ~800 lines that are hard to review by hand. The highest-leverage changes:

1. Move the checkers out of CMake into one Python script (biggest win)

check-dispatch-{declaration,linkage,instructions,execution}.cmake are string/regex/list programs that happen to run under cmake -P. Each one:

  • shells out to nm/objdump/gdb with the same ~8-line execute_process boilerplate,
  • splits output on \n, regex-matches, dedups, and tallies into set(prefix_HAVE_${key} ...) pseudo-dictionaries (CMake has no maps, so this is emulated via variable-name interpolation — the most error-prone pattern here),
  • re-implements "show first N, then … and M more".

All of this is a handful of lines each in Python with subprocess, re, collections.Counter, and real dicts. A single dispatch_checks.py with subcommands (linkage, declaration, instructions, execution), invoked via add_test(COMMAND ${Python3_EXECUTABLE} dispatch_checks.py ...), would replace all four .cmake files, be unit-testable on its own, and be human-reviewable. This is the primary recommendation. If a Python dependency is unacceptable, do 2.–5. instead.

2. Extract the duplicated checker helpers into one shared module

If the scripts stay in CMake, factor the repeats into cmake/dispatch-checks/lib.cmake and include() it. These appear in near-identical form in 2–4 files each:

  • Required-arg validation — the foreach(required ...) if(NOT ${required}) message(FATAL_ERROR ...) block and its EXISTS twin (all four checkers).
  • nm symbol extractionsvs_symbols() is defined twice with different signatures (linkage uses cmake_parse_arguments, declaration uses a positional arg), and the execution check inlines a third copy.
  • "report first N"svs_report_symbols (linkage) and svs_report_first (declaration) are the same function.
# cmake/dispatch-checks/lib.cmake
include_guard(GLOBAL)

function(svs_require)          # svs_require(SVS_NM SVS_ARCHIVE ...)
  foreach(var IN LISTS ARGN)
    if(NOT ${var})
      message(FATAL_ERROR "${var} is not set.")
    endif()
  endforeach()
endfunction()

function(svs_require_files)    # svs_require_files(SVS_ARCHIVE SVS_CONSUMER_OBJECT)
  foreach(var IN LISTS ARGN)
    if(NOT EXISTS "${${var}}")
      message(FATAL_ERROR "${var} does not exist: ${${var}}")
    endif()
  endforeach()
endfunction()

# Mangled names of symbols in <file> matching <regex>.
function(svs_nm_symbols out_var file regex)
  cmake_parse_arguments(arg "" "" "NM_ARGS" ${ARGN})
  execute_process(COMMAND "${SVS_NM}" ${arg_NM_ARGS} "${file}"
    OUTPUT_VARIABLE raw ERROR_VARIABLE err RESULT_VARIABLE status)
  if(NOT status EQUAL 0)
    message(FATAL_ERROR "${SVS_NM} failed on ${file}: ${err}")
  endif()
  set(symbols "")
  string(REPLACE "\n" ";" lines "${raw}")
  foreach(line IN LISTS lines)
    if(line MATCHES "${regex}")
      string(REGEX MATCH "[^ \t]+$" symbol "${line}")
      list(APPEND symbols "${symbol}")
    endif()
  endforeach()
  list(REMOVE_DUPLICATES symbols)
  set(${out_var} "${symbols}" PARENT_SCOPE)
endfunction()

function(svs_report_first list_var limit)
  set(shown ${${list_var}})
  list(LENGTH shown count)
  if(count GREATER limit)
    list(SUBLIST shown 0 ${limit} shown)
  endif()
  foreach(entry IN LISTS shown)
    message("    ${entry}")
  endforeach()
  if(count GREATER limit)
    math(EXPR rest "${count} - ${limit}")
    message("    ... and ${rest} more")
  endif()
endfunction()

That removes ~120 duplicated lines and gives one definition of "read symbols from a file."

3. Stop reconstructing mangled names in check-dispatch-declaration.cmake

This is the file that most needs attention (346 lines). It rebuilds Itanium ABI mangled names in CMake:

set(svs_mangled_classes)           # "<len><class>", e.g. 5L2Impl
foreach(class IN LISTS svs_distance_classes)
  string(LENGTH "${class}" len)
  list(APPEND svs_mangled_classes "${len}${class}")
endforeach()
set(svs_expected_extents ${SVS_SUPPORTED_DIMS} "18446744073709551615")  # Dynamic == SIZE_MAX
set(svs_kernel_regex "^_ZN3svs8distance([0-9]+...)ILm([0-9]+)E.*AVX_AVAILABILITYE([0-9]+)E")

This hard-codes the Itanium ABI (_ZN, length-prefixing, Lm…E for size_t, positional enum digit) into the test — exactly the compiler-specific fragility already raised in the inline thread. It silently doesn't apply to MSVC and depends on GCC/Clang keeping this scheme. Two simplifications:

  • Compare counts, not reconstructed names. The declaration's purpose ("a generator bug that drops an extent drops it from both probe and archive") is satisfied by an independent count: expected = levels × extents × distances × pairs_per_level, all known numbers (the type-pair count is one grep). Assert the archive defines exactly that many distance...Impl...compute symbols and no foreign ones — no length-prefixing, no Lm…E, no SIZE_MAX literal. If per-(level,extent) presence is still wanted, check it on demangled output via c++filt using readable substrings like L2Impl<64, and AVX_AVAILABILITY::AVX2.
  • This checker also largely re-does what check-dispatch-linkage.cmake already does with a compiled probe. Consider whether both are needed, or reduce declaration to just the independent count linkage can't provide.

4. Parse the ISA-level table once — reuse via a function

SVS_ISA_LEVELS rows ("AVX2|haswell|avx2") are split with string(REPLACE "|" ";") + list(GET ...) in five places: twice in generate-dispatch-surface.cmake (validation + generation loops), in multi-arch.cmake, in tests/multi-arch/CMakeLists.txt, and inside the declaration checker. Same for the derived SVS_DISPATCH_TU_SPECS. Provide one parser:

function(svs_parse_isa_level spec out_level out_arch out_infix)
  string(REPLACE "|" ";" f "${spec}")
  list(LENGTH f n)
  if(NOT n EQUAL 3)
    message(FATAL_ERROR "Malformed ISA level '${spec}': expected <level>|<arch>|<infix>.")
  endif()
  list(GET f 0 v0)  list(GET f 1 v1)  list(GET f 2 v2)
  set(${out_level} "${v0}" PARENT_SCOPE)
  set(${out_arch}  "${v1}" PARENT_SCOPE)
  set(${out_infix} "${v2}" PARENT_SCOPE)
endfunction()

The "|"-joined encoding (structs-as-strings) is itself a smell; parallel lists (SVS_ISA_LEVEL_NAMES, _ARCHS, _INFIXES) indexed together would remove the encode/decode entirely.

5. Emit a machine-readable manifest from the generator; stop re-parsing headers in tests

generate-dispatch-surface.cmake and check-dispatch-declaration.cmake both regex-parse C++ headers:

  • enum class AVX_AVAILABILITY[ \t\r\n]*{([^}]*)} from distance_core.h,
  • #define SVS_TYPE_PAIRS_... bodies (with line-continuation folding and one-level alias following) from preprocessor.h.

Regex-parsing C++ to recover enum order and macro arity is inherently brittle. The generator already holds the authoritative extent list, level list, and type-pair count at configure time — write them once to a generated dispatch_surface.manifest.cmake (or JSON) and have every test read that instead. One parser, one source of truth; the "ground truth 2/3" sections of the declaration checker disappear.

6. generate-dispatch-surface.cmake: split into functions (per the inline comment)

It's a 250-line straight-line script mixing validation, header emission, and reporting. Structure it as:

svs_validate_extents(SVS_SUPPORTED_DIMS)
svs_validate_levels(SVS_ISA_LEVELS)     # dedup names + infixes, field count
svs_emit_dispatch_header(...)           # configure_file into build (+ optional source)
svs_report_surface(...)                 # the STATUS block

The two separate foreach(level_spec ...) passes then collapse into one now that svs_parse_isa_level exists.

7. Smaller points

  • multi-arch.cmake: lib_name is keyed by infix but obj_name by arch (${arch}_obj). If two levels ever share a -march, the object target name collides. Key both by infix, or assert arch uniqueness.
  • Committed generated header (dispatch_surface.h): the "refresh committed copy + clang-format off to avoid a regenerate loop" mechanism is a maintenance trap. Prefer generating only into the build tree and dropping the committed copy; if it must stay for bare -I include use, add a CI check that fails when it's stale rather than silently rewriting it every configure.
  • cmake/dispatch-checks/ location: these are test drivers and belong under tests (per the existing inline comment). Keep only surface generation under cmake.
  • tests/multi-arch/CMakeLists.txt: the CMAKE_NM/CMAKE_OBJDUMP "use if defined else find_program" pattern repeats; wrap in a small svs_tool(out CACHEVAR NAMES ...) helper.
  • Comment volume: several files are >50% prose (dispatch-surface.cmake ~85% comments). Move the multi-paragraph rationale into the existing README.md and keep inline comments to one-line "why," so the logic is visible without scrolling.

Suggested priority

  1. 1 (Python) or 2 (shared cmake module) — removes the bulk of the duplication.
  2. 4 + 5 — one parser, one manifest; deletes most of the header-regex and level-splitting.
  3. 3 — kills the hand-rolled mangling in the declaration checker (also resolves the compiler-portability thread).
  4. 6, 7 — structural polish.

Net effect: the four checkers collapse toward one tool + one shared library, the ISA table is parsed in exactly one place, and no test re-derives C++ mangling or re-parses headers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants