Skip to content

W1.1 §4.4–4.5: steps 3–5 are premised on a producer shape that does not exist - #509

Merged
gstoner merged 3 commits into
mainfrom
agent/rocm-typed-accumulator
Aug 4, 2026
Merged

gstoner merged 3 commits into
mainfrom
agent/rocm-typed-accumulator

Conversation

@gstoner

@gstoner gstoner commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Docs only. Two findings that stop work rather than start it, both measured while attempting the next steps.

§4.5 — step 3 is not a migration

FragmentPackOp::verify requires exactly one !tile.tile input. What the five tile.mma construction sites actually pass:

site operands
TileIRLoweringPass ×2 tile.async_copy results — tensors
GenerateWMMAGemmKernel lane-level vectors (toFragvector::BitCastOp)
GenerateWMMALinearAttnKernel same
GenerateWMMAFlashAttnKernel same

Zero producers pass a !tile.tile. No operand can be wrapped in fragment_pack — the typed contract expects tile.view → fragment_pack → tile.mma, and every producer supplies a tensor or a vector whose lane math it has already done.

That's a division-of-labour mismatch, not a syntax gap: the typed form assumes the compiler does the lane mapping (materializeFragmentPack); the hand-written generators do it themselves. Both are coherent — they're different models.

So:

  • Step 3 ("migrate the 5 sites, one per PR") is a rewrite of working, numerically-verified generators, including the production ROCm GEMM lane.
  • Step 5 ("delete the permissive branch") is unreachable as written — deleting it breaks every existing producer.

§4.4 — gap 2 is coupled to step 3

TileToROCM's typed branch has no producer and no test: zero C++ emitters of fragment_pack/fragment_zero, zero occurrences in runtime.py, zero fixtures pairing a typed fragment with a ROCm lowering. And relaxing its FragmentZeroOp check alone would reproduce the NVIDIA defect — it synthesises its own accumulator (addOperands({*a, *b, zero})) where the untyped branch threads the real one.

The choice — yours, not mine

option cost
(a) restructure producers to tile.view → fragment_pack rewrites proven kernels for no measured benefit
(b) widen fragment_pack to accept tensors/vectors discards what the typed contract buys
(c) scope the typed form to synthesized kernels (Decision #28 lane); permissive branch becomes a declared compatibility boundary recommended

Under (c), step 5 changes from "delete the permissive branch" to "the branch is a declared compatibility envelope, and any path where the compiler owns lane mapping must use the typed form" — a Decision #32-style declared boundary rather than an open TODO.

It also closes W1.1 honestly: steps 1, 2 and 2b's guard are real contract improvements that landed and are gated; steps 3–5 as written were premised on a producer shape that doesn't exist.

No code written for steps 3/4 — their premise doesn't hold.

🤖 Generated with Claude Code

gstoner and others added 3 commits August 4, 2026 01:41
Closes the first of the two gaps W1_1_TYPING_DESIGN.md §4.3 identified.

`GenerateWMMAGemmKernel{via-tile=true}` emits `tile.mma %a, %b, %acc` at the
Tile-IR seam, and its own comment says the op "flows through
rocm-wave-lds-pipeline + lower-tile-to-rocm". Neither runtime pipeline contained
that pass. `tile.mma` therefore survived to LLVM translation and the build died
with

    cannot be converted to LLVM IR: missing `LLVMTranslationDialectInterface`
    registration for dialect for op: tile.mma

so W1.1's Tile-IR seam was unreachable from the lane that actually executes --
including the canonical pipeline, which already runs `tessera-tile-ir-lowering`
and `rocm-wave-lds-pipeline` and still had no tile -> ROCm lowering after the
generator.

Both pipelines now run `lower-tile-to-rocm{arch=<chip>}` after
`generate-wmma-gemm-kernel`.

── Verified, in both directions ──

Default path unchanged: with via-tile off there is no `tile.mma` to lower and
the emitted hsaco is BYTE-IDENTICAL with and without the pass (diffed at the
tessera-opt level before making the change, since adding a pass to a working
production lane needs that evidence, not an assumption).

via-tile now reachable: injecting only `via-tile=true` -- no hand-added lowering
-- compiles and runs bit-identical to the production `tessera_rocm.wmma` lane on
gfx1151 at 64^3, 256^3 and 128x96x64, matching numpy to ~1e-05. The accumulator
survives the round trip.

Every measurement carried the bogus-option control (inject an invalid pass
option, require ok=False), because an earlier run of this experiment reported
bit-identical output while the injection silently never applied.

`arch=` is mandatory and separately gated: `lower-tile-to-rocm` defaults to a
CDNA part and emits `llvm.amdgcn.mfma.contract`, an MFMA intrinsic wrong for
RDNA 3.5 that does not resolve. The pipeline string stays syntactically valid,
so this fails at link time rather than parse time -- hence a gate, not a comment.

Gates: `test_rocm_pipeline_tile_lowering.py` counts lowerings against generators
(so a third lane added without one fails here), forbids the arch-less spelling,
and compares numerics on hardware. Teeth verified by deleting one pipeline's
pass: the structural and hardware tests both fail.

Remaining from §4.3: `TileToROCM`'s TYPED branch still requires a
`FragmentZeroOp` accumulator.

14442 unit, mypy 0, ruff clean, docs in sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…work

§4.3 listed two remaining ROCm gaps. The first is closed (PR #508). This records
why the second should NOT be picked up next, so it is not mistaken for ready
work.

`TileToROCM`'s typed fragment branch has no producer and no test. Measured:
zero C++ passes emit `fragment_pack`/`fragment_zero` (only the two consumers and
the verifier mention them), zero occurrences in `runtime.py`, zero lit fixtures
pairing a typed fragment with a ROCm lowering, zero tests asserting its
diagnostic, and `materializeFragmentPack` is local to that one file.

The fix is also not the obvious one. Relaxing the `FragmentZeroOp` check alone
would reproduce the NVIDIA defect exactly -- the typed branch synthesises its own
accumulator (`addOperands({*a, *b, zero})`) where the untyped branch threads the
real one (`addOperands({mmaData[0], mmaData[1], acc})`). Accepting a non-zero
accumulator without threading it would silently discard it. Threading needs an
accumulator-side counterpart of `materializeFragmentPack`, which A and B have
and the accumulator does not.

That work is unverifiable today: with no producer there is no program to run and
so no numeric gate. Building it now repeats the mistake §4.3 corrected --
infrastructure for a path nothing executes, sized by reasoning rather than
measurement. It belongs with step 3: whichever producer first emits typed
fragments for ROCm must carry the accumulator materialisation, and that
migration's numeric gate covers both.

Docs only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured on starting step 3. `FragmentPackOp::verify` requires exactly one
`!tile.tile` input. What the five `tile.mma` construction sites actually pass:

  TileIRLoweringPass x2         tile.async_copy results -- TENSORS
  GenerateWMMAGemmKernel        lane-level VECTORS (toFrag -> vector::BitCastOp)
  GenerateWMMALinearAttnKernel  same
  GenerateWMMAFlashAttnKernel   same

Zero producers pass a `!tile.tile` or a `tile.view` result, so no operand can be
wrapped in `fragment_pack`. The inventory warned to expect per-producer
surprises in step 3; the surprise is not per-producer, it is all of them.

This is a division-of-labour mismatch rather than a syntax gap. The typed form
assumes the COMPILER does the lane mapping (`materializeFragmentPack`); the
hand-written generators do it themselves and hand over finished vectors. Both
are coherent, and they are different models.

So step 3 as written is a rewrite of working, numerically-verified generators --
including the production ROCm GEMM lane -- and step 5 ("delete the permissive
branch") is unreachable, since deleting it breaks every existing producer.

Three options recorded, recommending (c): scope the typed form to synthesized
kernels (the Decision #28 lane) and treat the permissive branch as a DECLARED
compatibility boundary between two legitimate models. (a) rewrites proven
kernels for no measured benefit; (b) widening fragment_pack discards what the
typed contract buys.

Not choosing unilaterally -- it changes W1.1's endpoint. No code written for
steps 3/4, because their premise does not hold.

Docs only.

Co-Authored-By: Claude Opus 5 <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: 506f4cf689

ℹ️ 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 +137 to +139
assert base.get("ok") is True, base.get("reason")
assert tiled.get("ok") is True, tiled.get("reason")
assert float(np.max(np.abs(base["output"] - tiled["output"]))) == 0.0, (

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 Verify that the compiled HSACO actually launched

When the generated module cannot load—for example, because the live AMD chip differs from _rocm_chip()'s default gfx1151_rocm_compiled_gemm_impl raises _RocmCompiledUnavailable, _execute_rocm_compiled_gemm silently runs the hand-written WMMA fallback, and rt.launch still returns ok=True. Thus both calls can execute the same fallback and satisfy these assertions even though the tile.mma HSACO never ran; TESSERA_STRICT_DISPATCH does not reject these envelope failures. Make the test fail if _execute_rocm_wmma_artifact is reached, or otherwise verify the intended chip and compiled module launch before comparing outputs.

AGENTS.md reference: AGENTS.md:L14-L16

Useful? React with 👍 / 👎.

@gstoner
gstoner merged commit c8a48e7 into main Aug 4, 2026
14 checks passed
@gstoner
gstoner deleted the agent/rocm-typed-accumulator branch August 7, 2026 23:47
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