Skip to content

Commit a7212a6

Browse files
kmbandyclaude
andcommitted
docs(dsws): correct entry != re-dispatch in pool-economy spec + plan
Propagate the Pool-T7 root cause (fixed in b01c722) back into the design docs so the "unified dispatch" language can't reintroduce it. - Spec §2: `.Ldispatch` is RE-DISPATCH ONLY, not a unified entry+re-dispatch mechanism. First entry runs the full role `_alloc`/`_init`/`_follow`; only a post-bail wave lands on `_follow`. Also corrected the inaccurate "INITFLAG already consumed" rationale (INITFLAG is written once, never cleared; the real reason re-dispatch skips `_init` is it would reset `s35`). - Spec §3: seed arms fall into the full role entry, NOT `.Ldispatch`. - Plan architecture summary + Task 3: Step 3 no longer flips the seed arms to `.Ldispatch` (that was the exact instruction that caused the brick); the arms stay on `.Lcompute`/`.Lafeed`/`.Lbfeed`. Task 3 retitled/re-scoped to re-dispatch only; commit-message template updated. No code change (docs only). Fix itself is b01c722. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0132aDSBLwusCJ4KzHQTnvdu
1 parent b01c722 commit a7212a6

2 files changed

Lines changed: 53 additions & 23 deletions

File tree

docs/superpowers/plans/2026-07-01-dsws-phaseb-pool-economy.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@
66
8-wave partition into a lean-start grow-into-budget pool that rebalances its
77
{compute, A-feed, B-feed} mix at runtime.
88

9-
**Architecture:** One scalar (`s59`)-driven dispatcher unifies entry and
10-
re-dispatch (lands on each role's `_follow`, scalar-only). Every wave already
11-
launches lean-32 (compute grows to `NFV` per-rowblk on demand and shrinks back —
12-
Phase-A behavior); "seeding" a role is therefore just setting `s59` in the
13-
existing lean partition arms, with **no launch-time grow**. The only
14-
`s_alloc_vgpr` GROW in the whole design remains `conv_apply`'s, already audited.
9+
**Architecture:** One scalar (`s59`)-driven dispatcher handles **re-dispatch**
10+
(reached from a `_quiesce` bail; lands on each role's `_follow`, scalar-only).
11+
**First-time entry is DISTINCT** — it seeds `s59` then runs the full role entry
12+
(`.Lcompute`/`.Lafeed`/`.Lbfeed``_alloc``_init``_follow`); it must NOT
13+
reuse `.Ldispatch`, which skips `_alloc`/`_init` (the Pool-T7 brick: corrected
14+
2026-07-02, committed `b01c722df`). Every wave already launches lean-32 (compute
15+
grows to `NFV` per-rowblk on demand and shrinks back — Phase-A behavior);
16+
"seeding" a role is therefore just setting `s59` in the existing lean partition
17+
arms and falling into that role's existing entry, with **no launch-time grow
18+
added**. The only `s_alloc_vgpr` GROW in the whole design remains `conv_apply`'s,
19+
already audited.
1520
`BUDGET` is retuned to the real per-SIMD ceiling so feed→compute grows can
1621
succeed (bidirectional balancing within the launched mix). A cooldown `K` damps
1722
thrash; a `DSWS2_FORCE` hook gives a deterministic first GPU proof. All offline
@@ -141,7 +146,7 @@ Expected: `dsws_ctrl_model: dispatch/cooldown/pool OK` then `dsws_ctrl_model: AL
141146
s_cmp_lt_u32 s24, (NBFEED+NAFEED)
142147
s_cbranch_scc1 .Lseed_afeed
143148
s_mov_b32 s59, NCOMP_SLOT
144-
s_branch .Lcompute // Task 3 retargets these three to .Ldispatch
149+
s_branch .Lcompute // FIRST entry: full role entry (_alloc/_init/_follow). Do NOT retarget to .Ldispatch (Pool-T7 brick).
145150
.Lseed_afeed:
146151
s_mov_b32 s59, NAFEED_SLOT
147152
s_branch .Lafeed
@@ -184,14 +189,20 @@ done
184189

185190
---
186191

187-
## Task 3: The `.Ldispatch` trampoline + tail-branch replacements
192+
## Task 3: The `.Ldispatch` RE-DISPATCH trampoline + tail-branch replacements
193+
194+
> **CORRECTION 2026-07-02 (Pool-T7 brick, committed `b01c722df`).** `.Ldispatch` is
195+
> **RE-DISPATCH ONLY** — reached from a `_quiesce` bail, never from first-time entry.
196+
> The original Step 3 below flipped the Task-2 seed arms to `.Ldispatch`; that skipped
197+
> `_alloc`/`_init` on first entry and hung the first CONV=1 dispatch (desktop brick).
198+
> Step 3 is corrected: the seed arms **stay** on the full role entry.
188199
189200
**Files:**
190-
- Modify: `occ_kernel_dsws.s` (add `.Ldispatch`; replace tail branches at L907, L951, L1097; flip Task-2 seed arms to `.Ldispatch`)
201+
- Modify: `occ_kernel_dsws.s` (add `.Ldispatch`; replace the 3 `_quiesce`-bail tail branches at L907, L951, L1097. **Leave the Task-2 seed arms on `.Lcompute`/`.Lafeed`/`.Lbfeed` — do NOT retarget them to `.Ldispatch`.**)
191202

192203
**Interfaces:**
193204
- Consumes: `s59` (seeded in Task 2, flipped by `conv_apply`), the `_follow` labels `.Lcompute_follow`/`.Lafeed_follow`/`.Lbfeed_follow`.
194-
- Produces: universal per-epoch role dispatch. Every non-claimer wave routes to the `_follow` of the role `s59` names.
205+
- Produces: per-epoch role RE-dispatch after a bail. A bailed non-claimer wave routes to the `_follow` of the role `s59` names. (First entry does not use this — it runs the full role `_alloc`/`_init`/`_follow`.)
195206

196207
- [ ] **Step 1: Add the trampoline** (under `.if DSWS2_CONV`, placed after the three role bodies so all `_follow` labels are in scope):
197208

@@ -216,11 +227,11 @@ done
216227
.endif
217228
```
218229

219-
- [ ] **Step 3: Flip the Task-2 seed arms** from the temporary `.Lcompute`/`.Lafeed`/`.Lbfeed` targets to `.Ldispatch` (three `s_branch .Ldispatch`), so entry uses the same dispatcher. **Verify (name in report):** `.Ldispatch` lands on `_follow` (NOT `_alloc`/`_init`), and `s35` (last-epoch) is preserved across the trampoline so a re-dispatched wave waits for the next epoch.
230+
- [ ] **Step 3: Leave the Task-2 seed arms on the full role entry.** Do **NOT** retarget them to `.Ldispatch`. First-time entry must run `_alloc` (the `s_alloc_vgpr 32` allocator handshake) and `_init` (the `INITFLAG==0xACED` LDS rendezvous + `s35=0` epoch seed) before its first `_follow` — all of which live only inside the role entry. Only a wave that has *already* run those once (i.e. after a `_quiesce` bail) may use `.Ldispatch`. **Verify (name in report):** the seed arms still branch to `.Lcompute`/`.Lafeed`/`.Lbfeed`; `.Ldispatch` is reached only from the 3 bail tail-branches; on re-dispatch it lands on `_follow` (NOT `_alloc`/`_init`) and `s35` is preserved so the re-dispatched wave waits for the next epoch. *(Original Step 3 flipped these to `.Ldispatch` → Pool-T7 brick; corrected 2026-07-02.)*
220231

221232
- [ ] **Step 4: Assemble + byte-identity + RGA** — same commands as Task 2 Step 4. Expected: CONV=0 sha256 unchanged from baseline; 3× `ASSEMBLE_OK`; RGA 0 spills. Additionally grep the object disassembly is not required, but confirm no `s_barrier` was introduced: `grep -c s_barrier occ_kernel_dsws.s` → unchanged from HEAD.
222233

223-
- [ ] **Step 5: Commit** (*only if kmbandy asks*): `feat(dsws): universal s59 dispatch trampoline (entry + re-dispatch)`
234+
- [ ] **Step 5: Commit** (*only if kmbandy asks*): `feat(dsws): s59 re-dispatch trampoline (bail → _follow; first entry stays on role entry)`
224235

225236
---
226237

docs/superpowers/specs/2026-07-01-dsws-phaseb-pool-economy-design.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,34 @@ wave-uniform trampoline:
9191
s_branch .Lbfeed_follow
9292
```
9393

94+
> **CORRECTION 2026-07-02 (Pool-T7 brick root cause).** `.Ldispatch` is a
95+
> **RE-DISPATCH-ONLY** trampoline — it is reached from a per-super-tile `_quiesce`
96+
> bail, **never from first-time entry**. Entry and re-dispatch are **NOT** the same
97+
> mechanism: first entry must run the full role `_alloc`/`_init` (see §3), and only
98+
> re-dispatch may land on `_follow`. The original text below said entry could reuse
99+
> `.Ldispatch`; that routing skipped `_alloc`/`_init` on first entry (no
100+
> `s_alloc_vgpr 32` handshake, no INITFLAG rendezvous, garbage `s35`) and hung the
101+
> first CONV=1 dispatch → desktop brick. Points 1–2 hold **for re-dispatch**; point 3
102+
> is corrected below.
103+
94104
Three properties make this safe and cheap:
95105

96-
1. **Lands on `_follow`, skipping `_alloc`/`_init`.** `conv_apply` already set
97-
the wave's VGPR footprint to the target role's size, and `_init` (INITFLAG
98-
wait) ran once at launch. Re-running `_alloc` would wrongly resize the wave;
99-
re-running `_init` would deadlock (INITFLAG already consumed).
106+
1. **On RE-DISPATCH, lands on `_follow`, skipping `_alloc`/`_init`.** `conv_apply`
107+
already set the wave's VGPR footprint to the target role's size, and the wave
108+
already ran `_alloc`/`_init` once at first entry. Re-running `_alloc` would
109+
wrongly resize the wave; re-running `_init` would reset `s35 = 0`, breaking the
110+
"wait for the *next* epoch" contract (INITFLAG is written once, `0xACED`, and is
111+
never cleared — so it is the `s35` reset, not INITFLAG, that `_follow` preserves).
112+
**First entry must NOT take this path** — it has run neither `_alloc` nor `_init`.
100113
2. **Preserves the last-seen-epoch register** (`s35`). A re-dispatched wave
101114
therefore waits for the *next* epoch in its new role rather than
102115
re-processing the epoch it just converted in.
103-
3. **Entry and re-dispatch are the same mechanism.** The entry wid-branch is
104-
repurposed to *seed* `s59` (and grow the seed-compute waves), then fall into
105-
`.Ldispatch`. A non-converting wave reads back its own role each epoch and
106-
loops exactly as today.
116+
3. **Entry and re-dispatch are DISTINCT.** First entry runs the full role entry
117+
(`.Lcompute`/`.Lafeed`/`.Lbfeed``_alloc``_init``_follow`), with the
118+
wid-branch *seeding* `s59` first so the role register is set before any later
119+
re-dispatch reads it. Only after a bail does a wave use `.Ldispatch`. A
120+
non-converting wave, once past first entry, reads back its own role each epoch
121+
via `.Ldispatch` and loops exactly as today.
107122

108123
The claimer (wid-0) is **excluded**: it branches to `.Lclaimer` at entry (before
109124
the seed/`.Ldispatch` path), runs its own `.Lclaim_loop`, never converts, and is
@@ -129,9 +144,13 @@ claimer. So launch/init is unchanged from Phase A except one addition:
129144

130145
**Seed `s59` by wid** (under `DSWS2_CONV`), in the *existing* partition arms —
131146
`[0,NBFEED)` → B-feed, `[NBFEED,NBFEED+NAFEED)` → A-feed, rest → compute — each
132-
arm gains a single `s_mov_b32 s59, <slot>` and then falls into `.Ldispatch`.
133-
**No launch-time `s_alloc_vgpr`** (compute's footprint is handled per-rowblk as
134-
today). There are no `N_POOL`/`SEED_*` defsyms: the launched mix
147+
arm gains a single `s_mov_b32 s59, <slot>` and then **falls into the full role
148+
entry** (`.Lbfeed`/`.Lafeed`/`.Lcompute``_alloc``_init``_follow`).
149+
**It must NOT branch to `.Ldispatch`** (that skips `_alloc`/`_init` on a wave that
150+
has run neither → the Pool-T7 brick; see §2 correction). `.Ldispatch` is entered
151+
only later, from a `_quiesce` bail. **No launch-time `s_alloc_vgpr` is *added***
152+
the seed reuses the role entry's existing `_alloc` (compute's `NFV` footprint is
153+
still handled per-rowblk). There are no `N_POOL`/`SEED_*` defsyms: the launched mix
135154
`NCOMP/NAFEED/NBFEED` *is* the seed and its sum *is* the pool size.
136155

137156
**Defsyms:**

0 commit comments

Comments
 (0)