Skip to content

Apple GPU C-ABI signature registry + off-device drift guard + bind_or_skip - #403

Merged
gstoner merged 1 commit into
mainfrom
apple/abi-signature-registry
Jul 14, 2026
Merged

gstoner merged 1 commit into
mainfrom
apple/abi-signature-registry

Conversation

@gstoner

@gstoner gstoner commented Jul 14, 2026

Copy link
Copy Markdown
Owner

What & why

The graph audit of this repo surfaced that the whole Apple-GPU test suite fans
into one bind_symbol() symbol-resolver, and on inspection 87 distinct C-ABI
symbols had their ctypes signatures hand-written at each of ~19 test files + 8
production modules
— signature knowledge with no single source of truth, and
validated only on a real Apple GPU (every such test skips off-device). A
renamed/removed C export or a drifted signature was invisible on ordinary CI.

This PR centralizes that surface and adds the CI-runnable guard that was missing.

Fix 1 — APPLE_ABI registry (single source of truth)

python/tessera/_apple_gpu_dispatch.py now holds the canonical
{symbol: (argtypes, restype)} for all 87 exports, plus:

  • bind_registered(symbol) — bind by name, signature pulled from the registry;
  • expected_symbols() — the full declared ABI set.

The registry was mechanically extracted by AST-parsing every bind_symbol()
call site and proven conflict-free (every symbol resolves to exactly one
evaluated ctypes signature). Per-file aliases were resolved — e.g. runtime.py's
cf = POINTER(c_float) and ci = POINTER(c_int64) (note ci is int64, easy
to get wrong by eye).

Fix 2 — tests/unit/test_apple_gpu_abi_registry.py, two guards

  • Off-device drift net (runs on ordinary CI, no GPU): AST-scans the tree and
    asserts every literal bind_symbol("sym", …) agrees with APPLE_ABI (symbol
    registered + identical evaluated signature, resolving per-file ctypes aliases).
    A drift or an unregistered symbol now fails immediately instead of silently
    skipping. It already earned its keep — rebasing onto main pulled in 4 new
    symbols (optimizer/scatter/sddmm/spmm_csr f32) and the guard flagged
    every one.
  • On-device net (skips without the dylib, like the NVIDIA/ROCm runtime-symbol
    tests): when the runtime loads, every registry symbol must resolve in it — one
    place covering the whole ABI, catching a renamed/removed C export.

Fix 3 — bind_or_skip(symbol) test helper

tests/unit/_apple_gpu_abi_testutil.py — bind by name via the registry, skip
off-device. Migrated the two clean exemplars (commit_and_wait_timeout,
row_major_strides) off inline signatures. The rest can adopt incrementally; the
drift guard keeps their inline signatures honest meanwhile.

Verification done here (Linux/WSL, no Apple GPU)

  • 3 new off-device guard tests pass; on-device net skips cleanly.
  • ruff + mypy clean on the changed module.
  • Full tests/unit/test_apple*.py suite: no new failures — identical
    pre-existing count with and without this change (those are stale-tessera-opt
    build failures on this box, unrelated to ctypes; confirmed by stashing).

⚠️ Continue on Apple silicon (what this box could NOT verify)

This branch was authored on a Linux/WSL box with no Apple GPU, so everything
gated on the runtime dylib is unproven. On an Apple-silicon Mac:

# 1. Environment (Homebrew toolchain, off-venv — see CLAUDE.md)
cd tessera && git fetch origin && git checkout apple/abi-signature-registry

# 2. THE key check — on-device ABI net must PASS (not skip) with the runtime built:
python3 -m pytest tests/unit/test_apple_gpu_abi_registry.py -v
#   Expect: test_dylib_exports_resolve_every_registry_symbol PASSES (all 87
#   registry symbols resolve in the compiled runtime dylib). If it fails, the
#   registry lists a symbol the .mm runtime doesn't export (or vice-versa) —
#   reconcile APPLE_ABI with apple_gpu_runtime.mm.

# 3. The two MIGRATED tests must stay green on-device (they were only collected,
#    not executed, off-device):
python3 -m pytest tests/unit/test_apple_commit_and_wait_timeout.py \
                  tests/unit/test_apple_row_major_strides.py -v

# 4. Full Apple suite regression — no NEW failures vs. main:
python3 -m pytest tests/unit/test_apple*.py -q

Then (optional follow-on, same pattern)

Finish Fix 3 by migrating the remaining exercise-and-skip sites to
bind_or_skip("sym") / bind_registered("sym"), one file at a time, running
each on-device after
. Do NOT migrate the presence-assertion tests (e.g.
test_apple_mlpkg_pk2.py, which does assert fn is not None) or negative tests —
bind_or_skip turns a missing symbol into a skip, which would weaken them. The
drift guard already covers their inline signatures against the registry, so
leaving them is safe.

If the runtime adds/renames a C export, update APPLE_ABI in
python/tessera/_apple_gpu_dispatch.py — the off-device drift guard will point
at any call site that disagrees.

🤖 Generated with Claude Code

…_skip

The graph audit surfaced ~19 Apple-GPU test files (and 8 production modules)
each hand-writing the ctypes signature at their bind_symbol() call site — 87
distinct C-ABI symbols with signature knowledge scattered across the tree, and
validated ONLY on a real Apple GPU (every such test skips off-device). A
renamed/removed export or a drifted signature was invisible to CI.

Fix 1 — APPLE_ABI registry (single source of truth). `_apple_gpu_dispatch.py`
now carries the canonical {symbol: (argtypes, restype)} for all 87 exports,
plus `bind_registered(symbol)` (bind by name, signature from the registry) and
`expected_symbols()`. Registry was mechanically extracted by AST-parsing every
bind_symbol() call site and verified conflict-free: every production + test-only
symbol resolves to exactly one evaluated ctypes signature (aliases like
runtime.py's `cf = POINTER(c_float)` / `ci = POINTER(c_int64)` resolved per file
— `ci` is int64, not int32, which a naive reader would get wrong).

Fix 2 — test_apple_gpu_abi_registry.py, two guards:
  * off-device drift net (runs on ordinary CI, no GPU): AST-scans the tree and
    asserts every literal bind_symbol("sym", ...) agrees with APPLE_ABI (symbol
    registered + identical evaluated signature, resolving per-file ctypes
    aliases). A drifted or unregistered signature now fails immediately instead
    of silently skipping. (It already earned its keep: the rebase onto main
    pulled in 4 new symbols — optimizer/scatter/sddmm/spmm_csr f32 — and this
    guard flagged every one as unregistered.)
  * on-device net (skips without the dylib, like the NVIDIA/ROCm symbol tests):
    when the runtime loads, every registry symbol must resolve in it — one place
    covering the whole ABI, catching a renamed/removed C export.

Fix 3 — bind_or_skip(symbol) test helper (tests/unit/_apple_gpu_abi_testutil.py):
binds by name via the registry, skipping off-device. Migrated the two clean
exemplars (commit_and_wait_timeout, row_major_strides) off inline signatures;
remaining files can adopt incrementally — the drift guard keeps their inline
signatures honest against the registry meanwhile.

Verified off-device: 3 new guard tests pass, on-device net skips; ruff + mypy
clean; full apple suite unchanged vs. base (same pre-existing tessera-opt build-
staleness failures with and without this change). The on-device dylib guard and
the migrated tests need an Apple-silicon run to confirm green — see the PR body.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5d6b26152

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +172 to +173
if sym in txt:
referenced.add(sym)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude the registry from the unused-symbol scan

When a symbol is added only to APPLE_ABI and never bound anywhere else, this test still marks it as referenced because _SCAN_ROOTS includes python/tessera, so _apple_gpu_dispatch.py itself is scanned and the registry definition contains every key as a string. That makes orphaned effectively unable to catch stale or mistyped registry entries; exclude the registry file or count references outside the APPLE_ABI definition.

Useful? React with 👍 / 👎.

@gstoner
gstoner merged commit e0cfd92 into main Jul 14, 2026
14 checks passed
@gstoner
gstoner deleted the apple/abi-signature-registry branch July 14, 2026 15:16
gstoner added a commit that referenced this pull request Jul 14, 2026
…ified_jit prose (#404)

Three related pieces of cleanup on top of the Apple GPU C-ABI registry (#403):

1. Reconcile the ABI registry with the runtime (the registry net now PASSES
   on-device). The registry listed two device-limit probe symbols the runtime
   never exported, so the strict net (test_dylib_exports_resolve_every_registry
   _symbol) failed. Add them to apple_gpu_runtime.mm (+ the non-Darwin stub):
     - tessera_apple_gpu_max_threadgroup_memory_length ([device
       maxThreadgroupMemoryLength]; 0 = "use static floor")
     - tessera_apple_gpu_family_integer (raw MTLGPUFamilyApple* value, e.g.
       Apple7 == 1007; -1 sentinel)
   Enum values + selectors grounded in the on-machine SDK headers (Decision #27).
   apple_target.probe_apple_runtime_limits already binds these defensively.

2. Fix 4 pre-existing stale tests (all test-side, not product regressions):
   - test_apple_gpu_tiny_decode..._kv_cache: diagnostic wording unified to
     "KV-cache mutation ..." in c53010c; Decision #21 contract still holds.
   - test_apple_gpu_simple_moe...: tessera.moe gained a native Apple GPU compute
     lane (_APPLE_GPU_MOE_COMPUTE_OPS) → now metal_runtime; assert that +
     numerical proof. Renamed to ..._runs_metal_runtime.
   - test_apple_gpu_multi_op_with_non_gpu_op_stays_metal_artifact: moe is no
     longer a non-lane op; swap to tessera.flip (+ lane_for self-guard) to keep
     the conservative-residency-gate guard meaningful.
   - test_compile_loads_real_metal_package (pk1): c53010c's blanket
     "compiled"→"device_verified_jit" rename clobbered a repr assertion +
     docstrings; revert to "compiled".

3. Repo-wide follow-on to (2): c53010c intentionally renamed the *status token*
   compiled→device_verified_jit but over-reached into English prose. Revert
   every word-usage back to "compiled" (diagnostic message strings, NVRTC-/
   HIPRTC-compiled, emit/* local variable names, docstrings/comments) while
   preserving all genuine status-token references (quoted "device_verified_jit"
   values, backtick doc-refs, and status-name prose like `native/
   device_verified_jit`, `= device_verified_jit`, `fused (x86) /
   device_verified_jit (rocm)`). Each reversion git-verified against the
   pre-c53010c image.

Generated dashboards regenerated (new ABI symbols, reworded notes, flip test
count); drift gate clean (22 in sync). Apple suite: 2060 passed, 3 skipped.
The emit/* variable renames are covered by test_kernel_cache /
test_dynamic_shape_emit / test_spectral_candidates / test_tpp_candidates.

Retest target: CUDA (sm_120) + ROCm (gfx1151) — this branch touches the
nvidia/rocm emit + manifest prose and needs the ROCm-enabled tessera-opt those
boxes have (the local Mac build is CPU+Apple only).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant