Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 52 additions & 12 deletions CLAUDE.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ authoritative status source). The canonical lowering pipelines registered in
|------|--------|
| `tessera-lower-to-x86` | implemented / lit-testable; hardware-runtime via the CPU JIT + native CPU ABI + AVX-512 compiled lanes. The AMX lane emits and stays artifact-only **by direction, not by hardware availability**: AMX is a retired target, superseded by ACE (AI Compute Extensions, agreed jointly by AMD and Intel), so the AMX ops remain an IR-level contract with no `amx.*` lowering. x86 native execution proof means AVX-512 |
| `tessera-lower-to-gpu` (NVIDIA SM90 WGMMA/TMA) | implemented / lit-testable (SM90 WGMMA has no hardware-execution proof yet) |
| `tessera-nvidia-pipeline-{sm90,sm100,sm120}` (per-SM aliases) | implemented / lit-testable. Two sm_120 lanes execute on consumer Blackwell, and they are **not** the same thing: the canonical matmul compiles Graph→Schedule→Tile through `tessera-opt` and is packaged to PTX via `tessera-nvidia-opt` + `mlir-opt` (this is the compiled route, and the one the direction above builds out), while the older `ptx_emit.py` + `libtessera_nvidia_gemm.so` GEMM is a separate bootstrap emit/runtime lane that bypasses the IR pipeline. **The compiled lane requires `-DTESSERA_ENABLE_CUDA=ON` with `-DTESSERA_BUILD_NVIDIA_BACKEND=ON`.** Three configurations behave differently and are easy to conflate: with CUDA **on**, `tessera-opt` is a full driver carrying `core-tessera-ir` + `nvidia-backend` together and the scheduled lanes run; with the backend **on but CUDA off** it is the lean hardware-free artifact driver — the NVIDIA Target IR dialect *is* registered (that is the supported host-free artifact configuration Decision #19 exists for), but the core scheduling spine is not linked, so scheduled pipelines are unavailable; with the backend **off** the dialect is not built at all and a scheduled matmul fails at `--tessera-schedule-to-tile` with `requires the registered NVIDIA Target IR dialect`, having touched no GPU |
| `tessera-nvidia-pipeline-{sm90,sm100,sm120}` (per-SM aliases) | implemented / lit-testable. Two sm_120 lanes execute on consumer Blackwell, and they are **not** the same thing: the canonical matmul compiles Graph→Schedule→Tile through `tessera-opt` and is packaged to PTX via `tessera-nvidia-opt` + `mlir-opt` (this is the compiled route, and the one the direction above builds out), while the older `ptx_emit.py` + `libtessera_nvidia_gemm.so` GEMM is a separate bootstrap emit/runtime lane that bypasses the IR pipeline. **The compiled lane requires `-DTESSERA_ENABLE_CUDA=ON` with `-DTESSERA_BUILD_NVIDIA_BACKEND=ON`.** Three configurations behave differently and are easy to conflate: with CUDA **on**, `tessera-opt` is a full driver carrying `core-tessera-ir` + `nvidia-backend` together and the scheduled lanes run; with the backend **on but CUDA off** it is the lean hardware-free artifact driver — the NVIDIA Target IR dialect *is* registered (that is the supported host-free artifact configuration Decision #19 exists for), but the core dialect and passes are not **registered** — `TesseraPM` is linked and PUBLIC-links `TesseraIR`/`TesseraScheduleIR`, so this is a registration gap, not a link gap — and scheduled pipelines are therefore unavailable; with the backend **off** the dialect is not built at all and a scheduled matmul fails at `--tessera-schedule-to-tile` with `requires the registered NVIDIA Target IR dialect`, having touched no GPU |
| `tessera-lower-to-rocm` | implemented / lit-testable / hardware-runtime on capable gfx1151 (RDNA3.5) hosts via HIP (WMMA matmul + attention family) |
| `tessera-lower-to-apple_cpu` (artifact) / `tessera-lower-to-apple_cpu-runtime` (Accelerate) | implemented / lit-testable / hardware-runtime |
| `tessera-lower-to-apple_gpu` (artifact) / `tessera-lower-to-apple_gpu-runtime` (MPS + custom MSL) | implemented / lit-testable / hardware-runtime |
Expand Down
148 changes: 145 additions & 3 deletions docs/audit/backend/nvidia/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,148 @@ last_updated: 2026-08-29

# NVIDIA compiler test-suite evaluation and rearchitecture

## `NVIDIA-DELEGATE-CONTRACT-2026-08-30` — the fast-path boundary is real; NVIDIA goes first

**Enabling step for the bootstrap prune, and it had to land before any
deletion.**

*Corrected 2026-08-30, by measuring rather than assuming.* This section first
said "the 19 NVIDIA bootstrap packagers contain legitimate fast paths — vendor
library entries, hand-tuned kernels, inline PTX". **They contain none.**
`nvidia_native.py` has zero references to NVRTC, cuBLAS/cuDNN/CUTLASS, any
`.so`, or raw device source; 13 of its 19 bootstrap packagers construct Tile
IR and compile it through `tessera-opt`. NVIDIA's real delegation surface is
`ptx_emit.py`, `emit/nvidia_cuda.py` and `runtime.py` — different files.

Across all four backends the same holds: **24 of 34 bootstrap packagers are
IR-constructing, 1 delegates** (`bootstrap_prune_gap.md`). So the prune is
overwhelmingly an *absorption* job — moving Graph → Schedule → Tile into the
compiled route — not a delegation-migration job. The boundary below was still
the right thing to land first, but for the delegation surface that actually
exists, not for these packagers.

NVIDIA was
chosen over ROCm because it has both the largest gap (19 of 34 bootstrap
packagers) and **working profiling tools**, which matters more than gap size:
Decision #28's arbiter is *measured*, so a delegation boundary on a target
that cannot be profiled is bookkeeping rather than a candidate.

**What `tessera_nvidia.kernel_call` was.** A summary line and nothing else.
It inherited `TesseraNVIDIA_Op`'s shared `attr-dict`, so `callee` — the single
fact naming *what is delegated to* — rode as an unvalidated discardable
attribute. An emitter could name any symbol, or none, and still verify. The
dialect header says why it existed: Python emitters "may add
`tessera_nvidia.kernel_call`", and registering it "keeps the emitted surface
parseable". It was a parse-compatibility stub **for the bootstrap packagers
being pruned** — Decision #29's anti-pattern exactly.

**Both pathways are now declared, as two ops rather than one with a mode.**

| Op | Delegate is | Required contract |
|---|---|---|
| `kernel_call` | a named CUDA kernel or host C-ABI symbol | `callee`, `arch`, `binding` ∈ {`cuda_kernel`,`c_abi`}, `provenance` ∈ {`vendor_library`,`handwritten_kernel`}, `accuracy` |
| `inline_ptx` | PTX text embedded in the artifact | `ptx`, `constraints`, `arch`, `accuracy`, optional `has_side_effects` |

They are separate ops because the delegate differs in kind: one is a binding
resolved at link/launch time, the other is text carried in the artifact. An
empty `callee` is an unresolved-symbol error; an empty `ptx` body is a
*silently successful no-op*. One op with a mode attribute would need a
verifier that decides which half of its own attributes to trust — the shape
that lets a malformed candidate through.

**The attributes are the arbiter's inputs, which is what "real" means here.**
`accuracy` is the budget half of "fastest *in-budget* candidate": a delegate
claiming `tolerance_bounded` must state `tolerance`, and `reference_exact`
must not carry one, because two contradictory claims leave a reader unable to
tell which is honoured. It is a semantic key and never defaults (#21a).
`provenance` is what lets the arbiter tell delegated from compiler-generated
work when scoring; `binding` separates two pathways whose launch costs and
failure modes differ.

*Evidence (The-Super-Bear, full driver):* `tessera-nvidia-opt` builds clean;
the positive fixture parses both ops with full attributes; the new negative
fixture `nvidia_delegate_contract_invalid.mlir` rejects **7 cases** —
empty callee, bounded-without-a-bound, exact-carrying-a-tolerance,
non-positive tolerance, unknown `binding`, empty constraints, empty ptx.
NVIDIA lit suite **60/60**.

*Arbiter integration landed:* `DelegatedCandidate`
(`emit/delegate_contract.py`) derives tier from `provenance` and the F4 budget
from `accuracy`/`tolerance`/`tolerance_rel`, so a delegate cannot claim in
Python a budget it did not declare in IR. The ROCm equivalent is still owed.

### Gaps found by stress-testing this design (2026-08-30)

Two were live defects in the contract as first shipped and are **fixed**:

* **Determinism was undeclarable.** Tessera guarantees
`@jit(deterministic=True)`, and a split-K delegate accumulating with atomics
is not reproducible run to run — the arbiter could have selected one inside
a deterministic region. `determinism` is now a required enum. Same shape as
the Decision #5 scar: a guarantee defeated through a path nobody checked.
* **The accuracy claim was absolute-only** while `Candidate` already carried
both atol *and* rtol. An absolute bound is meaningless without the result's
magnitude — 1e-6 is vacuous at 1e6 and unsatisfiable at 1e-9 — so a delegate
whose real claim is relative had to overclaim. `tolerance_rel` added;
either or both now satisfy a bounded claim.

Open, ordered by whether the design is *wrong* versus merely incomplete:

1. **Per-op accuracy budgets do not compose (mathematical, unsound as stated).**
Five delegates each within 1e-3 do not give an end-to-end result within
1e-3; propagation depends on conditioning. A graph can be assembled
entirely from in-budget candidates and land outside any budget with nothing
detecting it. Needs a graph-level check, or the composition claim must be
withdrawn.
2. **Fusion foreclosure is not costed (algorithmic; biases the arbiter).**
A delegated GEMM cannot be fused into, so selecting it forecloses epilogue
fusion. The honest comparison is *(delegate + separate epilogue + DRAM
round-trip)* vs *(fused compiled kernel)*. A greedy per-op arbiter
over-selects delegates on exactly the graphs where fusion is the win.
3. **`kernel_call` does not verify operands against the callee ABI.** The op
requires `constraints` for inline PTX on the argument that unstated
constraints become silent miscompiles — and then leaves the symbol path
unchecked. Inconsistent; `tessera_x86.abi_call` has the same hole.
4. **No delegate versioning.** cuBLAS 12 and 13 differ numerically and in
performance; with no version or ABI hash, a cached measurement from one
applies to the other. That is the stale-baseline failure this queue already
recorded once for the Krylov ratchet.
5. **Accuracy uses a vocabulary parallel to `numeric_policy`.** Decision #15a
puts accumulator contracts there; `reference_exact` cannot even be honoured
for float reductions, where result depends on accumulation order. This is a
Decision #32 information-loss issue inside the op meant to prevent them.
6. **`has_side_effects` is one bit.** Reads, writes and barriers have
different legality; one bit forces treating any side-effecting asm as a
full barrier, which costs real performance. MLIR has `MemoryEffects`.
7. **Shape-bucket boundaries are undefined.** The sm_120 macro-CTA threshold
is one number (67,108,864 FLOPs) measured once under WSL, and this file
already says it is not global selector authority. Coarse buckets apply an
M=4096 measurement at M=17.
8. **No measurement statistic or hysteresis.** "Fastest" by mean, median or
min, over how many reps? Without a minimum effect size the arbiter thrashes
inside noise.
9. **The arbiter is on the wrong side of the prune (architectural).** It lives
in Python. Pruning the Python backend path while keeping a Python arbiter
keeps the seam. Probable resolution: arbitration is legitimately *outside*
the IR pipeline because it requires execution, like PGO — but then the
contract must be stated: IR declares candidates, an orchestrator measures
and selects, selection is recorded back as an attribute.
10. **`tessera_rocm.mfma` vs `rocdl.mfma`** — measure whether the Target IR op
carries a contract ROCDL cannot, per Decision #19's amended membership
test. If it mirrors, it is Decision #31 duplication.

**Sequencing rule for the Apple/x86 operator expansion.** Add each op only
when its producer and its consumer land with it. Apple needs ~12 ops and x86
~8; landing the families ahead of the passes that produce them manufactures
exactly the unconsumed-declaration anti-pattern (Decision #29) this contract
work exists to remove. One op proven end-to-end beats twelve declared.

**And when x86's `avx512_gemm_microkernel` is decomposed into primitives, the
microkernel must survive as a Tier-3 candidate rather than being replaced.**
If it is hand-scheduled, decomposing it and hoping LLVM re-schedules is the
"generic IR caps the ceiling" trap applied within x86 — the arbiter should
decide, not the refactor.

Cross-backend sync `AVX512-MARKER-AND-AMX-CONSUMER-2026-08-30` — **shared
marker vocabulary and conftest boundary changed; per-backend outcome below.**
`hardware_avx512` joins `policy.MARKERS`, the PR marker expression and its
Expand Down Expand Up @@ -154,9 +296,9 @@ TesseraNVIDIAConversion)`, which is not gated on leanness.

| Config | NVIDIA Target IR | Core spine | Scheduled lanes |
|---|---|---|---|
| backend ON + `ENABLE_CUDA=ON` | registered | linked | run |
| backend ON + CUDA off (**lean**) | **registered** | not linked | unavailable — missing *core*, not the dialect |
| backend OFF (what this box has) | not built | linked | fail with `requires the registered NVIDIA Target IR dialect` |
| backend ON + `ENABLE_CUDA=ON` | registered | linked + registered | run |
| backend ON + CUDA off (**lean**) | **registered** | linked, **not registered** | unavailable — missing core *registration*, not the dialect |
| backend OFF (what this box has) | not built | linked + registered | fail with `requires the registered NVIDIA Target IR dialect` |

The middle row is the **supported host-free artifact configuration** that
Decision #19's hardware-free Target IR exists to enable; `_tessera_opt_lean_permitted`
Expand Down
15 changes: 15 additions & 0 deletions docs/audit/generated/bootstrap_prune_gap.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ the bootstrap row can go.
| Backends with a bootstrap module | 4 |
| `package_*` functions total | 49 |
| — **bootstrap** (re-enter Graph IR; prune target) | 34 |
| · of the bootstrap, construct Tile IR then run `tessera-opt` | 24 |
| · of the bootstrap, **delegate** (runtime compiler / library / object) | 1 |
| · of the bootstrap, both | 1 |
| · of the bootstrap, other (wrapper / dispatcher) | 8 |
| — compiled-route packagers (consume a lowered artifact) | 15 |
| Lines in those modules | 8738 |
| Classified families | 24 |
Expand Down Expand Up @@ -95,6 +99,17 @@ covered because a same-named family is compiled.

## How to read a closing gap

**Measured, and it redirects the work:** the bootstrap surface is
overwhelmingly *IR-constructing*, not delegating. Most packagers build
Tile IR in Python and then compile it through `tessera-opt`, so the
MLIR pipeline already runs from Tile onward and what bypasses it is
Graph → Schedule → Tile. Those retire by **absorption**, and there is
no fast path in them to preserve. The genuine delegation surface —
the one the Target IR boundary exists for — is elsewhere
(`ptx_emit.py`, `emit/nvidia_cuda.py`, `runtime.py`). A plan that
treats the whole bootstrap surface as fast paths to re-express scopes
the wrong work; this table exists partly to stop that.

A family leaves this table one of two ways, and only these two:

1. **Absorbed** — the mainline compiler grows an admission predicate
Expand Down
Loading