Skip to content

fix(hip): zero masked V per row in the wide prompt tile - #2357

Merged
ashhart merged 5 commits into
ashhart:mainfrom
laudney:pr-hip-wide-odd-tail
Nov 7, 2026
Merged

ashhart merged 5 commits into
ashhart:mainfrom
laudney:pr-hip-wide-odd-tail

Conversation

@laudney

@laudney laudney commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

Chunk-start test ("a query row's prompt attention keeps its bits wherever its chunk starts") main this PR
gfx1151 code objects on RDNA 3.5 (see Environment) 135 pass, 4 skip, 1 fail (slices differ at index 11011) 137 pass, 4 skip, 0 fail
gfx1152 (Radeon 860M) 133 pass, 6 skip, 1 fail (index 11011) 135 pass, 6 skip, 0 fail
gfx1201 (Radeon AI PRO R9700) not run here (#2262 measured it on this chip) 138 pass, 3 skip, 0 fail
New contract entries: non-finite V only at keys past a row's causal edge, gate 1 main this PR
gfx1201, paged and flat, fp16 and bf16 cache red (4 of 4: NaN where float64 gives finite) clean (4 of 4)
gfx1151 code objects, same four not run clean (4 of 4)
Cold prefill tok/s, Qwen3.6-35B-A3B 4-bit, gfx1201, median of 8 alternating pairs (4 AB, 4 BA) main this PR
2048 tokens 2782 2782.5 (+0.02%)
8192 tokens 2612.5 2613 (+0.02%)
32768 tokens 2105.5 2108.5 (+0.14%)
Prompt logits, Qwen3.6-35B-A3B 4-bit, gfx1201 main this PR
2100 tokens (chunks 1024 + 1024 + 52) 5e43279595b35798 5e43279595b35798 (byte-identical)
4097 tokens (last chunk is 1 key) 25ffc4e18acaac52 25ffc4e18acaac52 (byte-identical)

Before: the 64-row prompt tile (fa_dot2) multiplies P by V two keys at a time and zeroes V only past the chunk's span. A key inside the span but past a row's causal edge feeds p = 0 times its real V, which is −0 for a negative value, while a run whose chunk ends at that key feeds +0. RDNA 3.5's v_dot2_f32_{bf16,f16} round a −0 lane apart from a +0 lane (the probe in #2317), so a row's bytes depended on where its chunk started. On every chip, an infinite or NaN V at such a key also gave NaN in rows that must not see it (0 × inf).

After:

  • A caps fact. Caps.dot2_signed_zero is true for RDNA 3.5 (gfx1150 to gfx1153) and reaches the kernels as TF_DOT2_SIGNED_ZERO.
  • RDNA 3.5: the served tile (tf_fa_{paged,wide}_*) ANDs each V pair with the row's own visible-key mask, so every key past a row's edge enters as +0 in every run. This is the first commit's fix; the gfx1151 pin moves to 0xe9e35ddbb2a6ad10.
  • Other chips: the served tile is main's code. On gfx1201 its disassembly equals main's instruction for instruction for all five symbols (only the trailing s_code_end padding differs). After it, an edge pass (tf_fa_{paged,wide}_edge_*, same grid and arguments) scans, per block, only the V rows its live rows are masked from: keys q_pos0 + q0 + 1 up to the end of the last 64-key tile read, at most 126 keys of one KV head. A block whose keys are all finite returns at once, so a finite prompt keeps main's bytes; a block with a non-finite value reruns the masked tile and writes its rows. On RDNA 3.5 the edge symbols are trap bodies and are never launched. The scan reads its (at most three) page ids once before its loop; a per-thread table[] read in the loop pushed the paged edge kernel to 256 VGPRs with 44 bytes of scratch.
  • Image tile (tf_fa_full_bf16, non-causal): main's code and records, unchanged.
  • Contract: four entries (paged and flat, fp16 and bf16 cache) plant +inf, −inf and NaN only past rows' edges, over a span one page past the last row so the last tile holds keys no row sees, and expect a clean gate 1. Each has a planted control, tf_ctl_fa_{paged,wide}_unmasked_*: main's tile without the edge pass, built from the same prefill.hpp, which must go red at gate 1.
  • Records: the served tile's VGPRs are main's again on gfx1030, gfx1100 and gfx1201 (paged 231, wide 230; gfx1030's paged f16 231 and wide f16 230, so the 255 and 252 of the first version are gone); gfx1151's column keeps 239 and 236. The edge kernels: 233 to 236 VGPRs on gfx1100 and gfx1201, 249 to 254 on gfx1030, 0 scratch everywhere, gfx1151 not served.

Which sums change: on RDNA 3.5, the P × V sum of a row whose causal edge falls inside a key pair (the partner lane now enters as +0, not −0). Elsewhere no finite sum changes; a row past a non-finite key's edge now gets that key's +0 instead of NaN.

Checks on this head

  • Head commit: a465f1f86 on b3933ecd3, five commits: the original fix, the caps fact, the edge pass, the contract entries, the changelog line. The GPU runs used the same commits on 6433d511a; the upstream commits since touch no HIP or Qwen3.5/3.6 code.
  • Lean checker, python3 tools/zig/lean_check.py, on the base and on this head: 252 and 252, 0 new (lean_new.py), all inherited.
  • Tests run, with the command and pass, fail and skip counts:
    • zig build hip-kernel-test -Dhip=true -Dhip-arch=gfx1201: 138 pass, 3 skip, 0 fail. With the edge launch turned off locally (not committed), only the new edge test fails ("head 0 row 0 dim 7: -nan where float64 gives finite").
    • zig build hip-kernel-test -Dhip=true -Dhip-arch=gfx1152: 135 pass, 6 skip, 0 fail.
    • HSA_OVERRIDE_GFX_VERSION=11.5.1 zig build hip-kernel-test -Dhip=true -Dhip-arch=gfx1151 on the same gfx1152: 137 pass, 4 skip, 0 fail (the pin, the chunk-start test and the new edge test included).
    • The new kernel test, attention_edge.zig: for both cache types, paged and flat, at q_pos0 87, 150 rows (a partial last row block) and a span of 256 (past the last row), every output has float64's class; every dim with no planted value is byte-equal to the tile alone in every row; and planted chunks cut at 1 and 87 give the uncut bytes, paged and flat.
    • zig build hip-kernel-contract-limits -Dhip=true: every prompt, image and edge record current on gfx1030, gfx1100, gfx1151 and gfx1201; it exits 1 for the 5 stale records main has too (tf_moe_combine_gate on gfx1030, gfx1100 and gfx1151; the WMMA 4-bit 64-row item tile on gfx1100 and gfx1151).
    • zig build test -Dhip=true: 612 of 612 steps pass.
    • zig fmt --check build.zig zig and git diff --check: pass.
  • CI: green on a465f1f86 (7 of 7: zig, zig-windows, zig-macos-dev-tools, fmt, release-paths, benchmark-clients on Ubuntu and Windows).
  • Not run, and why: a real gfx1151 (Strix Halo); the gfx1151 rows run gfx1151 code objects on a gfx1152. gfx1100 and gfx1030 hardware: they take the edge pass, whose records are current for them, but their speed is not measured. tools/bench_concurrent.py and tools/bench_openai.py: see the Receipt.

Receipt

  • Environment, with the TensorFold commit, Zig and backend toolchain, machine and GPU, checkpoint and revision: main 5daafaf18 against this branch's commits on 6433d511a; Zig 0.17.0; ROCm hipcc HIP 7.16.26385 (AMD clang 24); Radeon AI PRO R9700 (gfx1201) and Radeon 860M (gfx1152), Linux; mlx-community/Qwen3.6-35B-A3B-4bit at 38740b847e4cb78f352aba30aa41c76e08e6eb46.
  • Paths and platforms the change reaches, which I ran and which I could not: HIP prompt attention through pagedCausal (served) and the 64-row branch of tf_causal, on every HIP chip with the wide tile. Ran gfx1201 and gfx1152, and gfx1151 code objects on gfx1152. Could not run gfx1151, gfx1100 or gfx1030 hardware.
  • Exactness, from tools/bench_concurrent.py --alone --serial or token_sha against "draft": false: not run. On gfx1201 the served tile is main's code (same instructions) and the edge pass writes nothing for a finite prompt; tf-qwen35-test logits gives byte-identical prompt logits to main at 2100 and 4097 tokens. On RDNA 3.5 the bits change by design (the pin), and the chunk-start test shows they no longer depend on the chunk start.
  • Main against this branch, same machine and checkpoint, posted before asking for a run on our machines (or why you can't): the tables above. @AbrahamCete, could you run hip-kernel-test and your prompt timings on your gfx1151? It is the one chip I reach only through gfx1151 code objects on a gfx1152.
  • Decode speed before and after, from tools/bench_openai.py: n/a, the 64-row tile and the edge pass run only in prompt attention.
  • Prompt speed before and after: cold prefill from tf-qwen35-test prefill (median of 3 cold prefills after a warm-up, fresh caches), 8 pairs on gfx1201, 4 main-first and 4 branch-first. The medians are within 0.15% at 2k, 8k and 32k, and the branch is faster in some pairs and slower in others. The first run of each session is fast whichever binary it is, which is why the medians, and not the means, are given; the raw runs are below.
  • Licence of any new weights or drafter, or "n/a": n/a.
Raw prefill runs (tok/s, run order)
2048  main   2862 2789 2782 2779 | 2796 2775 2782 2774
2048  branch 2789 2782 2782 2783 | 2823 2779 2828 2778
8192  main   2669 2617 2613 2612 | 2621 2609 2612 2608
8192  branch 2618 2615 2615 2609 | 2389 2591 2646 2611
32768 main   2114 2111 2104 2106 | 2066 2105 2107 2098
32768 branch 2113 2109 2108 2107 | 2109 2106 2109 2105

Runs 1-4 were main then branch; runs 5-8 branch then main.

Kernel contract, only when a GPU kernel is new or changed
  • Kernels registered with the contract, and the kernel each one replaces: tf_fa_paged_{f16,bf16}, tf_fa_wide_{f16,bf16} change in place on RDNA 3.5 and are main's elsewhere; the new tf_fa_{paged,wide}_edge_{f16,bf16} run after them where dot2_signed_zero is false. The new entries test the served dispatch, tile and edge pass together.
  • A planted control for each new kernel, and each one fails as planted: tf_ctl_fa_{paged,wide}_unmasked_{f16,bf16}, red at gate 1 on both chips below.
  • Each backend's SUMMARY line and exit status:
    • zig build hip-kernel-contract -Dhip=true -Dhip-arch=gfx1201 on this branch: SUMMARY gfx1201 111 kernels: 111 pass gates 1-4, 0 fail, 2 known findings; 19 controls: 19 red as planted, 0 missed; 0 limit problems; gates 1464632 ms, limits 3000 ms, exit 0.
    • The same on main 5daafaf18 with the four new entries added (no controls, as main has no prefill.hpp): SUMMARY gfx1201 111 kernels: 107 pass gates 1-4, 4 fail, 2 known findings; 15 controls: 15 red as planted, 0 missed; 0 limit problems; gates 1455760 ms, limits 3517 ms, exit 1: the 4 failures are the new entries, each "126 of 49152 outputs differ in class; first at head 0 element 7: nan where float64 gives finite".
    • HSA_OVERRIDE_GFX_VERSION=11.5.1 zig build hip-kernel-contract -Dhip=true -Dhip-arch=gfx1151 on a gfx1152: SUMMARY gfx1151 109 kernels: 109 pass gates 1-4, 0 fail, 2 known findings; 18 controls: 18 red as planted, 0 missed; 2 limit problems; gates 298650 ms, limits 1667 ms, exit 1. The 2 limit problems are the inherited stale records of tf_moe_combine_gate and the WMMA 4-bit item tile.
  • Chips I could not run: gfx1151 hardware, gfx1100, gfx1030.

Checklist

  • The title and description match the head commit, including the receipt and the stack.

  • New tests fail before the change, pass after it, and skip cleanly without their dependency.

  • Comments and docstrings are one line. No measurements or history in the source.

  • No personal data, machine names, internal hosts or local paths in the diff or its commits. No AI attribution lines.

  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off.

  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (Not run end to end; the chunk-start tests cover a resumed prompt against a fresh one at the kernel, finite and planted.)

  • No precision traded for speed. If the bits change, the description says which sums change and why.

  • Decode and prompt processing are no slower than main at one stream and the widest admitted; with --drafter, no slower than without it, greedy and sampled. (Measured: prompt processing at one stream on gfx1201, medians within 0.15%; decode is not reached. Not measured: the widest admitted width and drafts.)

@ashhart

ashhart commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Thanks @laudney. The per-row mask matches what #2317 settled on, and the logits you posted show gfx1201
byte-identical with RDNA 3.5 moving to one set of bits whatever the chunk start. Two asks before it lands.
First, a CHANGELOG line under Unreleased ending "(#2357). Thanks to @laudney.", saying RDNA 3.5's prompt bits
change, gfx1201's don't, and gfx1201 prefill is 0.8 to 0.9% slower at 2k to 8k.
Second, a contract control for the masked value itself. Today the prefill tile's gate-1 plant mixes an infinity
past a row's causal edge with one under fp32's least weight (contract_prefill.zig, plant/under). The second
still fails after this change, so the known finding passes either way, and dropping the mask would leave the contract
green. Could you add an entry of its own that plants Inf/NaN in V only at keys past a row's causal edge, inside the
span and the row's 64-key tile, and expects a clean gate 1? Please post its SUMMARY on this head and on main, where it
should go red. One detail for the description: on gfx1030 the paged f16 tile goes from 231 to 255 VGPRs and wide f16
from 230 to 252. Occupancy looks unchanged, but the body mentions only the 239 figure.

One more, on speed: the mask changes no bits on gfx1201, so gfx1201 shouldn't pay 0.8 to 0.9% for it. Could gfx1201
keep main's path, with the mask where RDNA 3.5 needs it for finite bits? A non-finite V on gfx1201 can take an
exceptional path that runs only when a scan finds one, as #2106 did for Nemotron, so finite prompts there stay at main's
bytes and speed. If you'd rather keep one order everywhere, an ABBA on gfx1201 showing the gap is within noise would do.

@AbrahamCete

Copy link
Copy Markdown
Contributor

@laudney, here are the gfx1151 runs. They ran on a Ryzen AI Max+ 395 (Radeon 8060S, gfx1151), Ubuntu 26.04, Zig 0.17.0 and ROCm 10.1's hipcc (HIP 7.16.26385), in clean git archive trees of main 6433d51 and this head f3c4835.

  • zig build hip-kernel-test -Dhip=true -Dhip-arch=gfx1151:

  • zig build hip-kernel-contract -Dhip=true -Dhip-arch=gfx1151 on this head exits 1 with SUMMARY gfx1151 105 kernels: 105 pass gates 1-4, 0 fail, 2 known findings; 14 controls: 14 red as planted, 0 missed; 2 limit problems; gates 240084 ms, limits 2136 ms. The 2 limit problems are the inherited ones you list: tf_moe_combine_gate, and the WMMA 4-bit 64-row item tile (141 VGPRs against 176 recorded).

  • Prefill from tf-qwen35-test check --only speed, with runs alternated main, PR, main, PR. Each figure is the mean of two (tok/s):

    2048 tokens, main → PR 8192 tokens, main → PR
    Qwen3.8-27B 4-bit (revision 22d8d538) 386 → 380 (−1.6%) 370 → 368 (−0.3%)
    Qwen3.6-35B-A3B 4-bit 1960 → 1964 (+0.2%) 1792 → 1786 (−0.3%)

If the head changes for the review asks, I can run these again.

laudney added a commit to laudney/TensorFold that referenced this pull request Nov 6, 2026
The per-row V mask changes no bits on gfx1201 but cost 0.8 to 0.9%
of its prompt speed (ashhart#2357 review). The tile's body moves to
prefill.hpp with MASKED and EDGE instances: the served tile is masked
only where dot2_signed_zero holds and is main's code elsewhere, the
same instructions on gfx1201. There a new edge launch follows each
tile: a block scans the V rows its rows are masked from, page ids
read once so it does not spill, and reruns masked only when one is
non-finite. A kernel test checks float64's classes, the tile's own
bytes in every unplanted dim, and chunk starts, paged and flat.
laudney added a commit to laudney/TensorFold that referenced this pull request Nov 6, 2026
The prompt tile's gate-1 plant mixed an infinity past a row's causal
edge with one under fp32's least weight, which still fails, so the
contract stayed green with no mask or edge pass at all (ashhart#2357
review). Four entries, flat and paged in both cache types, plant
+inf, -inf and NaN only past rows' edges over a span a page past the
last row and expect a clean gate 1. Each has a planted control, main's
tile without the edge pass, which must go red at that gate.
laudney added a commit to laudney/TensorFold that referenced this pull request Nov 6, 2026
The ashhart#2357 review asked for an Unreleased line that states what users
see: RDNA 3.5's prompt rows no longer depend on their chunk start, so
its prompt bits change; other chips keep main's tile, bits and, on
gfx1201, prompt speed within noise; and a non-finite value past a
row's causal edge adds nothing to that row on every chip.
@laudney
laudney force-pushed the pr-hip-wide-odd-tail branch from f3c4835 to a465f1f Compare November 6, 2026 16:43
@laudney

laudney commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @ashhart. I took the arch-gated path; the description has the full receipt. Ask by ask, on head a465f1f86:

Ask Change Check
gfx1201 keeps main's path and speed Caps.dot2_signed_zero (true on RDNA 3.5 only) picks the served tile: masked on RDNA 3.5, main's code elsewhere (28888605a) gfx1201 disassembly of the five tile symbols equals main's; prompt logits byte-identical at 2100 and 4097 tokens; 8 ABBA pairs, medians 2782 → 2782.5, 2612.5 → 2613, 2105.5 → 2108.5 tok/s at 2k, 8k, 32k
A non-finite V on gfx1201 takes an exceptional path only when a scan finds one Edge pass after the tile: each block scans only the V rows its live rows are masked from (to the end of the last tile read) and reruns masked only when one is non-finite; no flag buffer, a trap on RDNA 3.5 (28888605a) New kernel test (classes, unplanted dims byte-equal to the tile alone, planted chunk cuts at 1 and 87, paged and flat, both cache types); with the edge launch off, only that test fails
A contract entry of its own, clean on the head, red on main Four entries, paged and flat in both cache types, plant +inf, −inf and NaN only past rows' edges, over a span a page past the last row; each with a planted control, main's tile without the edge pass (54f9cc921) gfx1201: 111 kernels: 111 pass gates 1-4, 0 fail, 2 known findings; 19 controls: 19 red as planted. Main with the four entries: 111 kernels: 107 pass gates 1-4, 4 fail (the four new entries: NaN where float64 gives finite)
CHANGELOG line Under Unreleased, ending "(#2357). Thanks to @laudney." (a465f1f86); it says gfx1201's speed is main's within noise, since the 0.8-0.9% is gone —
gfx1030's VGPRs in the description The served tile is main's on gfx1030 again (paged f16 231, wide f16 230); the description lists the edge kernels' 249 to 254 there hip-kernel-contract-limits: every prompt and edge record current on all four chips

gfx1100 and gfx1030 take the edge pass too, as their dot2 behaviour is unmeasured; I have no hardware for either. @AbrahamCete, a hip-kernel-test run and your prompt timings on a real gfx1151 would close the last gap.

@AbrahamCete

Copy link
Copy Markdown
Contributor

@laudney, these runs are on a465f1f against its base b3933ec, with the same gfx1151 setup as above: Ryzen AI Max+ 395, ROCm 10.1's hipcc and clean git archive trees.

  • zig build hip-kernel-test -Dhip=true -Dhip-arch=gfx1151:

    • Base: 135 pass, 4 skip, 1 fail. The failure is the chunk-start test, at index 11011.
    • This head: 137 pass, 4 skip, 0 fail, out of 141 with the new edge-pass test.
  • zig build hip-kernel-contract -Dhip=true -Dhip-arch=gfx1151 on this head exits 1 with SUMMARY gfx1151 109 kernels: 109 pass gates 1-4, 0 fail, 2 known findings; 18 controls: 18 red as planted, 0 missed; 2 limit problems; gates 265741 ms, limits 2184 ms.

    • The four new entries, non-finite values only past a row's edge on the paged and flat tiles in both cache types, pass nonfinite, repeat and shapes. Their four controls are red as planted.
    • The 2 limit problems are the same inherited ones: tf_moe_combine_gate and the WMMA 4-bit item tile.
  • Prefill from tf-qwen35-test check --only speed, with runs alternated base, PR, base, PR. Each figure is the mean of two (tok/s):

    2048 tokens, base → PR 8192 tokens, base → PR
    Qwen3.8-27B 4-bit 385.5 → 383.0 (−0.6%) 369.5 → 368.0 (−0.4%)
    Qwen3.6-35B-A3B 4-bit 1960 → 1953 (−0.4%) 1797 → 1784.5 (−0.7%)

@ashhart

ashhart commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Thanks @laudney. This covers everything: gfx1201 keeps main's tile, with disassembly and logits equal to main and ABBA medians within noise; the edge pass runs the masked tile only when its scan finds a non-finite value; and the four edge entries are clean on the head, red on main, each with a planted control. Thanks @AbrahamCete for the gfx1151 runs on both heads. We'll fold the gfx1151 prefill cost (under 0.7% at 2k and 8k) into the CHANGELOG line, and re-pin the record count to main's at landing.

@ashhart
ashhart force-pushed the pr-hip-wide-odd-tail branch from a465f1f to f87a21d Compare November 6, 2026 20:56
ashhart pushed a commit to laudney/TensorFold that referenced this pull request Nov 6, 2026
The per-row V mask changes no bits on gfx1201 but cost 0.8 to 0.9%
of its prompt speed (ashhart#2357 review). The tile's body moves to
prefill.hpp with MASKED and EDGE instances: the served tile is masked
only where dot2_signed_zero holds and is main's code elsewhere, the
same instructions on gfx1201. There a new edge launch follows each
tile: a block scans the V rows its rows are masked from, page ids
read once so it does not spill, and reruns masked only when one is
non-finite. A kernel test checks float64's classes, the tile's own
bytes in every unplanted dim, and chunk starts, paged and flat.
ashhart pushed a commit to laudney/TensorFold that referenced this pull request Nov 6, 2026
The prompt tile's gate-1 plant mixed an infinity past a row's causal
edge with one under fp32's least weight, which still fails, so the
contract stayed green with no mask or edge pass at all (ashhart#2357
review). Four entries, flat and paged in both cache types, plant
+inf, -inf and NaN only past rows' edges over a span a page past the
last row and expect a clean gate 1. Each has a planted control, main's
tile without the edge pass, which must go red at that gate.
@ashhart

ashhart commented Nov 6, 2026

Copy link
Copy Markdown
Owner

As said above, I've rebased this onto today's main and pushed it to your branch (head f87a21d166). Main has gained one HIP record since, so the family test now pins 197 and 175. I dropped the CHANGELOG commit; its line goes in at landing, with the gfx1151 prefill cost. Your four commits keep your authorship. It's in the landing queue.

laudney and others added 5 commits November 7, 2026 04:36
On RDNA 3.5, v_dot2_f32_bf16 and v_dot2_f32_f16 round a sum
differently when one lane's product is -0 instead of +0. The 64-row
prompt tile multiplies P by V two keys at a time and zeroed V only
past the span, so when a row saw an odd number of keys in its last
tile, the partner key fed +0 in a run whose chunk ended there but p
= 0 times its real V, a -0 for a negative value, in a run whose
chunk ended later, and a row's bytes hung on where its chunk began
(ashhart#2317). Each row now zeroes every value past its own causal edge,
so all runs feed the same dot2 lanes on every chip. This also drops
the 0 x inf NaN past the causal edge from the prefill contract's
known finding, adds cut 1 to the chunk-start test, moves the gfx1151
pin to the new bits (gfx1152 reproduced the old pin exactly), and
refreshes the prompt and image tiles' VGPR records on every chip.
RDNA 3.5's dot2 rounds a sum with a -0 lane apart from the same sum
with +0, which made ashhart#2317's prompt rows depend on their chunk start,
while gfx1201's does not. A caps field, dot2_signed_zero, records
that fact per generation and reaches the kernels as
TF_DOT2_SIGNED_ZERO, so the prompt tile can keep main's code on the
chips that do not need the per-row mask.
The per-row V mask changes no bits on gfx1201 but cost 0.8 to 0.9%
of its prompt speed (ashhart#2357 review). The tile's body moves to
prefill.hpp with MASKED and EDGE instances: the served tile is masked
only where dot2_signed_zero holds and is main's code elsewhere, the
same instructions on gfx1201. There a new edge launch follows each
tile: a block scans the V rows its rows are masked from, page ids
read once so it does not spill, and reruns masked only when one is
non-finite. A kernel test checks float64's classes, the tile's own
bytes in every unplanted dim, and chunk starts, paged and flat.
The prompt tile's gate-1 plant mixed an infinity past a row's causal
edge with one under fp32's least weight, which still fails, so the
contract stayed green with no mask or edge pass at all (ashhart#2357
review). Four entries, flat and paged in both cache types, plant
+inf, -inf and NaN only past rows' edges over a span a page past the
last row and expect a clean gate 1. Each has a planted control, main's
tile without the edge pass, which must go red at that gate.
@ashhart
ashhart force-pushed the pr-hip-wide-odd-tail branch from f87a21d to 4bea033 Compare November 7, 2026 05:07
@ashhart
ashhart merged commit c9c47dd into ashhart:main Nov 7, 2026
0 of 7 checks passed
ashhart pushed a commit that referenced this pull request Nov 7, 2026
The per-row V mask changes no bits on gfx1201 but cost 0.8 to 0.9%
of its prompt speed (#2357 review). The tile's body moves to
prefill.hpp with MASKED and EDGE instances: the served tile is masked
only where dot2_signed_zero holds and is main's code elsewhere, the
same instructions on gfx1201. There a new edge launch follows each
tile: a block scans the V rows its rows are masked from, page ids
read once so it does not spill, and reruns masked only when one is
non-finite. A kernel test checks float64's classes, the tile's own
bytes in every unplanted dim, and chunk starts, paged and flat.
ashhart pushed a commit that referenced this pull request Nov 7, 2026
The prompt tile's gate-1 plant mixed an infinity past a row's causal
edge with one under fp32's least weight, which still fails, so the
contract stayed green with no mask or edge pass at all (#2357
review). Four entries, flat and paged in both cache types, plant
+inf, -inf and NaN only past rows' edges over a span a page past the
last row and expect a clean gate 1. Each has a planted control, main's
tile without the edge pass, which must go red at that gate.
@ashhart

ashhart commented Nov 7, 2026

Copy link
Copy Markdown
Owner

Thank you @laudney. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes.

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.

HIP: prompt attention's chunk-start test fails on gfx1151 with the wide 64-row tile

3 participants