Skip to content

MQ4-Lloyd (qt=52): per-tensor Lloyd codebook in the MQ4V2 container, -15% KLD at 4 bpw; mq4-pro prefill fix; AWQ rmsnorm rewrite - #768

Open
Kaden-Schutt wants to merge 131 commits into
masterfrom
mq4-lloyd
Open

Kaden-Schutt wants to merge 131 commits into
masterfrom
mq4-lloyd

Conversation

@Kaden-Schutt

Copy link
Copy Markdown
Collaborator

What

MQ4L tiers (mq4l-xt / mq4l / mq4l-pro, quant type 52 MQ4G256V2Lloyd): the MQ4V2 tiers re-quantized with a per-tensor 16-level Lloyd-Max codebook instead of the uniform 4-bit grid. Byte-compatible MQ4V2 container (136 B groups, AWQ sidecars, identical size) plus a 64 B <tensor>.lloyd_levels sidecar per 4-bit tensor. Levels are constrained to centered E4M3-representable values so the FP8 prefill path and the f16 decode path share one exact codebook; the loader folds the centering into the group zero-points.

WT2 KLD, 24 chunks, gfx1201, prefill scoring, Q8 KV (same protocol as the HF model card):

tier uniform MQ4V2 (AWQ'd) MQ4L Δ
xt 0.048190 0.040826 −15.3%
base 0.040519 0.034639 −14.5%
pro 0.033540 0.028404 −15.3%

Speed, gfx1201 (R9700), same card interleaved A/B, warm, HIPFIRE_VERIFY_GRAPH=0: xt uniform 36.49–36.54 tg / 1410–1415 pp512 / 1276–1281 pp8192; mq4l-xt 36.14–36.17 / 1395–1400 / 1264–1267 (−1.0% / −1.2%). Shipped-xt master reference on the same card: 36.55 / 1417 / 1284 (this branch: 36.58 / 1412 / 1280 — no uniform-path change).

Runtime coverage: gfx1201 prefill (FP8-WMMA LUT twins) + decode (fused scalar LUT kernels); gfx1100/gfx1151 decode (T1 admission). gfx11 batched prefill fails closed for now — the F16-WMMA-LUT route was gated and abandoned (+61% vs F16 base on the XTX; docs/plans/2026-09-15-lloyd-gfx11-port.md); the MMQ byte-LUT route is planned (docs/plans/2026-09-15-lloyd-gfx11-mmq-lut.md) as the next PR.

Fixes found on the way

  • mq4-pro batched prefill was incoherent on gfx1201 master (079eb152d): gated_norm_f32_batched / sigmoid_mul_f32 rewrite a stable batched scratch without invalidating the pointer-keyed F16 x-cache; the Q8 out_proj (gemm_q8_0_residual_wmma_gfx12) consumed the previous layer's activations. Masked while the F16 MQ4V2 launchers evicted the single-slot cache; exposed once all three FP8 prefill paths became default (gfx1201: FP8-WMMA MQ4v2 prefill GEMMs (+28% pp512), opt-in; GDN permlanex16 fix #757). 1-chunk WT2 KLD 6.42 → 0.019; 24-chunk 0.0335. Cherry-pickable.
  • AWQ rmsnorm-rotate rewrite: fused_rmsnorm_mq_rotate_awq is now derived from fused_rmsnorm_mq_rotate.hip (-DHIPFIRE_RMSNORM_AWQ) instead of a stale LDS-staged fork: 18.6 → 13.3 µs at K=5120 on gfx1201, +2.5% decode on every AWQ'd MQ4V2 artifact (fresh xt 35.6 → 36.5). Removes the gfx1100-only _direct variant and HIPFIRE_GFX1100_AWQ_NORM_DIRECT.
  • FP8-LUT residual odd-tail padding seeded from Y (residual semantics); qkvza/gate_up/qkv LUT select keyed on the wave-uniform slab start row (SGPR; launchers require m % 16 == 0).

Hugging Face

hipfire-models/qwen3.8-27b: mq4l-xt / mq4l / mq4l-pro added; mq4-xt re-issued with its AWQ sidecars (the prior upload lacked them: KLD 0.057 → 0.048; new sha256 80e7c624…, pinned in AGENTS.md §5); mq4 and mq4-pro byte-identical to the fresh v2 tiers, untouched. Model card updated.

Validation

  • KLD ladder above; 1-chunk prefill-vs-per-token agreement on every kernel change.
  • scripts/serve_harness.py battery on mq4l-xt (serve path, --speculation off): 5/5 stop, runaway/empty/attractor/retrieval_miss 0, decoded text read.
  • Greedy generation on mq4l vs mq4: coherent, correct.
  • cargo test --lib on rdna-compute / hipfire-dispatch / hipfire-runtime / hipfire-quantize / hipfire-arch-qwen35; crate maps + coverage tests; rustfmt on changed files.

Known

  • Intermittent bench runs land in an eager (non-graph) state (~29 tok/s flat) regardless of cache warmth; HIPFIRE_AR_GRAPH_TRACE=1 shows capture+replay on re-run. Harness artifact under investigation; not caused by this branch (seen on shipped-xt/master arms too).

…id-mul producers

gated_norm_f32_batched and sigmoid_mul_f32 rewrite a stable batched
scratch (dn_normed_batch / fa_attn_out_batch) every layer without
dropping the pointer-keyed ensure_fp16_x cache. The Q8 residual WMMA
consumer (gemm_q8_0_residual_wmma_gfx12, i.e. the mq4-pro tier's
out_proj) then reuses layer L's converted activations at layer L+1.

The F16 MQ4V2 GEMM launchers happened to evict the single-slot cache
between layers; with all three gfx1201 FP8 prefill paths default-on
(#757) nothing evicts it, so mq4-pro batched prefill went incoherent:
WT2 KLD 6.42 -> 0.019 (1 chunk), 24-chunk 0.0335 after the fix.
Any single FP8 flag off masked it.

Producers now call invalidate_x_caches_for(out), matching the gemv.rs
producer contract.
…book in the MQ4V2 container

Quantizer: --format mq4v2-lloyd emits qt=52 tensors (byte-identical 136 B
MQ4V2 groups) plus F32[16] <tensor>.lloyd_levels sidecars: a per-tensor
16-level Lloyd-Max codebook, constrained to centered levels {7.5+e : e
finite E4M3} so the FP8 prefill path and the f16 decode path share one
codebook exactly. AWQ sidecars compose as for qt=44.

Runtime: loader rewrites group zero-points (zp' = f16(zp + 7.5*sc)) so
both paths decode w = sc*C[q] + zp'; lloyd_lut.rs derives the E4M3 [u32;4]
and f16 [u32;8] LUTs. Kernels: LUT twins of the FP8 WMMA prefill family
(-DHIPFIRE_FP8_LUT_ARG), of the MQ4V2 GEMV/residual decode kernels and of
the fused gate_up/qkv/qkvza decode kernels (-DHIPFIRE_MQ4G256V2_LUT),
selected by the WEIGHT dtype; every route fails closed on a missing LUT.
Dispatch: three Fused*Mq4G256V2Lloyd keys guarded on lloyd_lut_f16, the
SwiGLU-residual variant admits qt=52 through the LUT residual kernel.

Fixes found on the way:
- FP8-LUT residual wrapper: odd-tail padding copied an uninitialized temp
  over Y (dropped the residual stream on every odd-sized prefill chunk).
- Lloyd qkvza FP8: per-slab codebook select keyed on the wave-uniform slab
  start row (SGPR) instead of the lane row; re-admits the S2BT8 geometry
  (launcher requires all four m % 16 == 0, fails closed otherwise).
- fused_rmsnorm_mq_rotate_awq is now derived from fused_rmsnorm_mq_rotate
  (-DHIPFIRE_RMSNORM_AWQ): same prefetch/reduction tree, 13.3 us vs the
  retired LDS-staged fork's 18.6 us at K=5120 on gfx1201 (+2.5% decode on
  every AWQ'd MQ4V2 artifact). Removes the gfx1100-only _direct variant
  and HIPFIRE_GFX1100_AWQ_NORM_DIRECT.

WT2 KLD (24 chunks, gfx1201, prefill scoring, q8 KV): xt 0.0482 -> 0.0408,
base 0.0405 -> 0.0346, pro 0.0335 -> 0.0284 vs the AWQ'd uniform tiers.
Decode gfx1201 xt: 36.2 vs 36.5 tok/s uniform; prefill pp512 1374 vs 1412.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…ne, docs, CHANGELOG

Kernel: gate_up and qkv FP8-LUT symbols now select the per-tensor
codebook once per 16-row slab keyed on the wave-uniform slab start row
(as qkvza already did), replacing the per-lane candidate evaluation
(mq4_pick2/3). Host launchers fail closed unless every projection m is a
multiple of 16. pp512 gfx1201 per-kernel vs uniform: gate_up +1.8% (was
+3.0%), qkv +2.0% (was +7%), total prefill -1.6% (was -6.4%). 1-chunk WT2
KLD prefill unchanged (L-base 0.020986).

CI: crate maps regenerated (quant_mq4v2_lloyd.rs, lloyd_lut.rs),
coverage_tests anchors for MQ4G256V2Lloyd and the three Fused*Lloyd keys.
Docs: QUANTIZATION.md qt=52 inventory, quant-formats/mq4-v2.md §11 Lloyd
variant, CHANGELOG Unreleased, stale references to the retired AWQ
rmsnorm fork rewritten. Plan: docs/plans/2026-09-15-lloyd-fp8-prefill-gap.md.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…on on gfx1151

T0 (gate-first): gemm_mq4g256v2_residual_wmma.hip gains a
-DHIPFIRE_MQ4G256V2_LUT arm (LDS-staged 16-entry f16 codebook, decode
w = sc*C[q] + zp'), symbol gemm_mq4g256v2_residual_wmma_gfx11_lloyd,
launcher gemm_mq4g256v2_residual_wmma_gfx11_lloyd (base tile, fail-closed
off gfx11 wave32 WMMA / k%256). Uniform symbol's preprocessed output
unchanged. Throwaway differential: examples/tmp_lloyd_gfx11_diff.rs
(to be deleted before merge). Gate criteria and the rest of the port:
docs/plans/2026-09-15-lloyd-gfx11-port.md.

T1: mq4g256v2_scalar_fusion_ok admits gfx1151 (fused Lloyd decode kernels
are scalar wave32); gfx1150/1152 refusal tests; stale fused_qkv.rs comment.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…fx1151 decode admission

T0 gate on the XTX: F16-LUT residual twin +61% vs the uniform F16 base tile
(N=384), +71% vs the production MMQ route (N=512); criterion was +10%.
Correctness passed (rel-L2 2.7e-4 vs LUT GEMV). Kernel arm, const,
launcher and throwaway reverted; plan annotated. Lloyd tiers stay
gfx12-only for batched prefill (fail-closed elsewhere); gfx1151 fused
decode admission (T1) retained.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
… KLD, rustfmt on changed files

Registry: files qwen3.8-27b.mq4l{-xt,,-pro}, tags qwen3.8:27b-mq4l{-xt,,-pro},
WT2 KLD in descs, MQ4 DFlash sidecar pairing, mq4-xt re-issued as the AWQ'd
artifact (KLD 0.048, 14.99 GB). Docs/CHANGELOG follow the naming. rustfmt
applied to every .rs changed vs master (CI's changed-file gate).

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…l arms

Raises hipfire-arch-qwen35 bypass 121 -> 128 and bypass_total ceiling
241 -> 248. Traded: the seven gemm_*_fp8_lloyd calls in prefill.rs land as
raw launches now so the Lloyd tiers ship on gfx1201; the registry keys with
LUT plumbing that retire them come with the gfx11 MMQ-LUT route
(docs/plans/2026-09-15-lloyd-gfx11-mmq-lut.md), which needs the same keys.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
… MMQ/iu4 LUT prerequisite)

lloyd_lut_c16_from_levels: code = round_ties_even(16*(L-7.5)), |code|<=120,
LE-packed signed bytes in [u32;4]; exact for every E4M3-on-grid level with
|e|>=1 (unit-tested), ties-to-even for near-center levels. Field carried on
WeightTensor/WeightRef next to the E4M3 and f16 LUTs and filled at the
existing sidecar sites. No dispatch/kernel consumer yet; the gfx11 iu4-split
twin (docs/plans/2026-09-15-lloyd-gfx11-iu4-split.md) is the consumer.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…ile temp fold

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…vert arm, annotate plan

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…m kept for A/B)

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…EMV/pad/direct N policy, prefill routing (U4)

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…ents)

Rows r and r+8 shared a bank at stride 44 (44r mod 32 = 12r); stride 42
(10r mod 32) visits all 16 even start banks once, keeps b64 alignment,
and shrinks LDS 31744 -> 30720 B. Halo (gfx1151), mq4-xt pp512:
full_set_occ3 1722 -> 1688 us, full_add_occ3 1684 -> 1643 us, prefill
876 -> 861 ms; eval output byte-identical (md5 cc7f5a26...); 190 VGPR,
0 spills. The A1 software-pipeline variant was screened in the same
lane and rejected (+40%, 256 VGPR + 40 spills) and is removed with the
HIPFIRE_IU4_VARIANT hook; note the previous 'loader(qwen35)' commit
accidentally carried that hook code. Plan: docs/plans/2026-09-15-halo-prefill-plan.md §5-6.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…nd HIPFIRE_FA2_GFX11_VARIANT (gate pending)

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…ECAR), shared block_i4_128 quant recipe (gate pending)

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…itted IU4 sidecars

B1 (attention_q8_0_fa2_gqa.gfx11): fa2_swiz = 2*bit_reverse4(c) with four
aligned int32x2 LDS loads per K/V fragment. 7-shape oracle (Q8-K and
fwht3-K, b1..b512, L1..L32768) bitwise-equal vs the incumbent. Halo mq4-xt
pp8192 per-call 7269/7293/7407 -> 6902/7004/7046 us (-4..-5%). Removing
the two compiler memory clobbers (B2) was screened and rejected (gave back
half of B1); they stay.

C2: RMSNorm/FWHT and SwiGLU/FWHT batched producers emit the IU4
block_i4_128 representation (shared kernels/src/block_i4_128_quant.hip
recipe, identical arithmetic to quantize_int4_mmq_ds128) through a
type-state prepared handle on gfx1151+IU4 for the 128 attention/FFN
inputs and 64 down inputs per chunk; the standalone quantizer stays for
the 64 wo/output residual inputs and every non-admitted shape.
quantize_int4 launches 256 -> 64 per 512-token chunk; eval output
byte-identical (md5 cc7f5a26...); Halo pp512 profile 860 -> 837 ms.

Plan: docs/plans/2026-09-15-halo-prefill-plan.md §7-§9.
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…ghtTensor literals, bank +14 dispatch-bypass for the gfx11 MMQ-LUT arms, drop the tmp_lloyd_mmq_diff throwaway, regenerate crate maps

bypass: hipfire-arch-qwen35 128 -> 142 (+14 net: the seven gemm_*_mmq_lloyd
prefill arms mirror the FP8-LUT arms' registry-key debt; retired together
when the LUT keys land), ceiling 248 -> 262. rustfmt on changed files.

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…bypass ceiling raise

RATCHET-RAISE: bypass_total 241 -> 262, traded for shipping the MQ4-Lloyd (qt=52) prefill arms as raw launches (7 gfx12 FP8-LUT + 7 gfx11 MMQ-LUT gemm_*_lloyd calls in prefill.rs) ahead of the registry keys with LUT plumbing that retire them
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
@Kaden-Schutt Kaden-Schutt added the ratchet-raise Maintainer approval for a declared RATCHET-RAISE in this PR label Sep 15, 2026
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…f, exact gfx1151) builds group-major duplicates of every IU4-consumed projection at load (~15 GB on 27B); bit-exact; pp2048 IU4 -5% in profile; decode unchanged 14.66"

This reverts commit ed33d20.
…r full-tile use (load-path independent)"

This reverts commit c57b3b6.
…eight_layout_gm (default off, exact gfx1151), gate/up/down only"

This reverts commit a3b3528.
…fx12 fp8-resident is the honest form

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…sured 2-3% slower on prefill; verdicts recorded

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…wn +16%) — IU4 GEMM is weight-byte-latency-bound, not fill-bound; twin removed

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
… uapi, ERT golden tests); kernel.npu_spillover (default off); registry ModelEntry.xdna + manifest v1 stored-zip parser; load_params projection into LoadCtx.xdna

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
…TOPS (writeback-bound), research artifact only; f32-fold slice C spec on file, undispatched

Signed-off-by: Kaden Schutt <151092359+Kaden-Schutt@users.noreply.github.com>
… producer fusions (gated_norm, attention reduce)

Both units bit-exact with receipts; measured on the canonical AWQ'd trunk
qwen3.8-27b.mq4-xt (sha256 80e7c624…, size 14987185152), graphs ON.

1. gemv_mq4g256v2_residual on gfx1151 was the only MQ4V2 weight-stream kernel
   spilling: 96 VGPR / 36 SGPR / 5 VGPR spills / 24 B scratch per lane, on a
   kernel that is 29.2% of decode. Reuse the shipped gfx1100 row-serialized
   schedule under an exact-gfx1151 macro (HIPFIRE_GFX1151_RESIDUAL_ROW_SERIAL,
   entry gemv_mq4g256v2_residual_row_serial_gfx1151, 32-byte ABI unchanged):
   81 VGPR / 27 SGPR / 0 spills / 0 scratch, 16 waves/SIMD. gfx1100 .text
   md5 unchanged (acdefd61…). Device memcmp 0 mismatches at (5120,5120),
   (5120,6144), (5120,17408), (5119,5120) on both GPUs; greedy md5 identical
   before/after (gfx1151 790828f9…, gfx1100 98bca5f0…). Kernel 160.96 ->
   154.34 us/call, 185.8 -> 193.8 GB/s (99.7% weight bytes). Graphs-ON decode
   A/B in the same tree is neutral (14.595 vs 14.594): gfx1151 AR decode sits
   at 90.8% of the box's measured 240.9 GB/s DRAM ceiling, so per-kernel
   efficiency no longer transfers. The scratch segment was also what made
   Redline PM4 prepare refuse the Halo tape ("GFX10/GFX11 PM4 dispatch does
   not yet support scratch (private=24)"); this is the prerequisite for it.

2. AWQ artifacts issued 64 standalone rotate_x_mq_awq launches/token purely
   because wo.awq_scale.is_none() gated the two shipped producer fusions off.
   Add AWQ twins of the DeltaNet gated_norm->MQ-rotate fusion (48 layers) and
   the full-attention reduce->sigmoid-gate->MQ-rotate fusion (16 layers, also
   removes 16 sigmoid_mul launches). Divide stays after the reduction and
   before signs1/FWHT. Zero spills on all 7 AWQ symbols across gfx1100/
   gfx1151/gfx1201, occupancy 16, gfx1201 non-AWQ unchanged; on-device
   memcmp EXACT on all five oracle rows; greedy md5 6eda7cf0… identical
   baseline/candidate and cross-arch. Launch census 64 -> 0 standalone
   rotates/token. gfx1100 decode 48.66 -> 49.05 (+0.80%, 3 fresh processes,
   gated +0.55%, attention -0.08%, combined +0.80% with non-overlapping
   sample ranges). Adds the missing profile timer on the fused attention
   reducer (it was invisible in every kernel census).

Fixture note: the 14.66 gfx1151 decode floor was set on the non-AWQ trunk;
the canonical AWQ'd fixture reads 14.63-14.64 with identical code (n=4 per
arm, start temps matched), i.e. AWQ costs ~0.14% decode / ~1% prefill, 94%
of the prefill share in the eight exact f32 divides of the SwiGLU AWQ twin.
The daemon's shadow field is named aql_host_us for both transports, and the
harness printed it as `aql=` even under --pm4, so a retained-PM4 result read
as an AQL number. Print `pm4_ib=`/`aql_packets=` per the selected backend
and the signed delta vs HIP. Report keys and daemon protocol unchanged
(product_bench and tests consume them by name).
@Kaden-Schutt
Kaden-Schutt force-pushed the mq4-lloyd branch 10 times, most recently from 6845a29 to 71a5c64 Compare September 17, 2026 19:32
After the last dispatch of a retained IB, emit wait_compute_idle() then
the architecture-matched full acquire: acquire_system() on Legacy
(gfx10/gfx11), acquire_system_gfx12() on Gfx12. CS_PARTIAL_FLUSH drains
shaders but does not write back GL2 for a non-shader next consumer
(SDMA copy). This is ACQUIRE_MEM with the GL2 writeback bits, not a
RELEASE_MEM packet. Unconditional: one packet (+8 dwords) per tape.

Applies to all three terminal sites: single-IB, per-lane, single-lane
phase. Adds Pm4Commands::trailing_release plus a contract unit test
(trailing_release_emits_wait_then_system_acquire: 2+8 dwords, header
0xc0065800). Mirrors upstream e836c13/f325143 byte-for-byte.

Discriminator note (honest negative): a tmp SDMA probe (H2D poison
reset -> retained-tape replay -> D2H readback vs HIP arm, gfx1100)
showed 3840/4096 stale rows/iter in the pre-fix shape, but the trailing
acquire did not move it: per-block atomics proved all 16 blocks execute
with correct IDs while a HIP checker kernel agreed with SDMA that only
block 0 stores surface. That is a different, deeper visibility behavior,
not the terminal-GL2 gap, so the probe is not a valid regression gate
and is removed (tmp file + lab example entry). The fix is correct and
preventive; parity below proves no regression.

Gates: cargo test -p redline-rocr -p rdna-compute --lib green
(251 + 55; dispatch::upload_raw_copy_failure_hip_frees_owner flakes
only under full-suite VRAM pressure, passes in isolation).

Harness receipts (qwen3.8-27b.mq4-xt, kv q8, ctx 128, skip-prefill):
gfx1201 pre:  pm4_ib=26841.0us hip=27627.3us exact=True pass=True
gfx1201 post: pm4_ib=26942.6us hip=27598.9us exact=True pass=True
gfx1201 post: pm4_ib=26908.2us hip=27619.2us exact=True pass=True
gfx1201 post: pm4_ib=26811.6us hip=27560.9us exact=True pass=True
gfx1100 pre:  pm4_ib=20551.3us hip=20537.2us exact=True pass=True
gfx1100 post: pm4_ib=20605.2us hip=20456.9us exact=True pass=True
gfx1100 post: pm4_ib=20227.3us hip=20477.4us exact=True pass=True
gfx1151 pre:  pm4_ib=68013.2us hip=67613.4us exact=True pass=True
gfx1151 post: pm4_ib=68004.7us hip=67667.6us exact=True pass=True
gfx1151 post: pm4_ib=68082.0us hip=67645.0us exact=True pass=True
Tape delta +8 dwords; decode deltas within run noise on all GPUs.
Item 2 — Gfx12RmwAcquirePolicy ladder (redline-rocr/src/pm4.rs:56-88,
acquire_rmw_gfx12 :345-351; CurrentSequential rung routes through the
pre-existing emitter so default tapes are byte-identical, unit test
rmw_acquire_ladder_encodings_are_stable :810). Gfx12 vmem arm in
rdna-compute/src/replay.rs acquire_inter_node :331-345 behind
HIPFIRE_REPLAY_PM4_GFX12_VMEM_ACQUIRE, default OFF (gate :603-614,
test gfx12_vmem_acquire_is_explicit_opt_in :6277); arch-aware call
sites :4803, :5013/:5033/:5323. Legacy path untouched.

Item 3 — upstream lane partition verbatim: redline-dispatch/src/lanes.rs
(840 lines, measured_lanes :151) + src/partition.rs (225 lines);
QueuePolicy::Auto consults measured_lanes first
(aql/queue_policy.rs:46-55). Only default change: gfx1030 lanes
1->4; gfx1100/1151/1201 pinned by unit test (:102-109, gfx1201 stays 2).

Item 4 — CU-mask primitives in redline-rocr/src/runtime.rs
(create_with_cu_mask :1187+, CU_MASK_REDUCED handling, abi.rs
re-exports); 7 unit tests; no default wired.

Triage (gfx1201, qwen3.8-27b.mq4-xt, q8, --skip-prefill
--decode-context 128, ROCR_VISIBLE_DEVICES=2): the first version of
this pull regressed the retained-PM4 tape — the single-queue
pre-dispatch arm called acquire_inter_node instead of
gfx12_system_acquire AND the `else if acquire` arm was dropped, so
ordinary dependent boundaries emitted no acquire at all (30676 ->
25164 dwords, exact=False). Bisect (replay.rs reverted, everything
else on) returned exact=True, attributing it to replay.rs, not the
ladder/lanes/CU-mask. Fixed here: pre-dispatch arm restores
gfx12_system_acquire() by default (replay.rs:5018-5025) with the
HipLlvmVmemL1 rung reachable only under the explicit opt-in; the
ordinary-dependency arm is restored (:5026-5039).

Attribution table (pm4_ib / hip host us, cmd dwords):
| build                                         | exact | pass | pm4_ib | hip    | dwords |
|-----------------------------------------------|-------|------|--------|--------|--------|
| 01d3ca7 baseline (items stashed)            | True  | True | 26895.2| 27641.8| 30676  |
| items 2-4, pre-fix default                    | False | False| 26699.3| 27653.9| 25164  |
| items 2-4, pre-fix + VMEM opt-in=1            | False | False| 26694.9| 27657.6| 25164  |
| items 3+4 + ladder, replay.rs @ HEAD          | True  | True | 26793.2| 27674.5| 30676  |
| items 2-4, fixed default                      | True  | True | 26852.2| 27763.1| 30676  |
| items 2-4, fixed + VMEM opt-in=1              | True  | True | 26752.6| 27718.4| 30676  |

Phase-plan line absent from the daemon log in every run
(single-queue path; report queue_id=2). Baseline HIP hashes
(logits d00b3d5061e56007 kv 134628dbefbcc5e8 rec 1ecb80c8174d99db)
reproduced exactly post-fix. Opt-in rung acknowledged in daemon log
("[redline] gfx12 PM4 VMEM RMW acquire rung enabled"); opt-in timing
-0.37% vs fixed default on pm4_ib.

Unit tests serially (--test-threads=1): rdna-compute 252,
redline-dispatch 97 (+11 dispatch, +8 identity integration),
redline-rocr 55 — all pass.
…erics)

Opt-in HIPFIRE_GFX12_FA2_FP8 / kernel.gfx12_fa2_fp8 (default off; exact
gfx1200/gfx1201 only). Four FP8 SRC consts prepend #define HIPFIRE_FA2_FP8 1;
HIP renames direct/fwht3k/partial/merge entries under the define. Launchers
select _fp8_ symbols when enabled; U0 LDS stays 65536 (// Ua: 32768). No
separate KernelKey — same exact-gfx1201 FA2 predicates.

Receipts (HOME=/home/kaden/.hipfire-homes/ab1 ROCR=1):
- cargo build --release OK; feature_flags lib test OK
- greedy -t0 -n256 graphs ON content_md5 OFF=ON e20d37a5d7fcc4c35be6fc18a8e71bfa ("Paris"/39)
- gfx1100 FA2 .text body e5ed6e67…; gfx1151 cbf1b9d5… (gfx11.hip untouched)
- gfx1201 base/fp8 bodies identical (direct af023e6c… partial 441544af… merge d10261aa…)
…PU selection

`_spawn` unconditionally set HIP_VISIBLE_DEVICES=0 in the child even when the
parent selected the GPU with ROCR_VISIBLE_DEVICES alone, so the daemon carried
both selectors (they remap differently). Default to GPU 0 only when the caller
set neither. Found by ChunkPlan while writing the chunk-widening serve gates;
affects every serve_harness battery/chain run on a multi-GPU box.
grow_scratch_buffer retained every replaced buffer once any hipGraph had
been captured (std::mem::forget). On gfx1100 five growing prompt shapes
retained +199.6/+411.1/+754.9/+1459.9/+2906.8 MB = 5.7 GB, OOMing a
multi-turn DFlash session on turn 3. Freeing under a live graph is not
safe either (measured: every qwen35 turn empty, HipError 700).

Fix (design A: invalidate-on-growth; per-pointer attribution is
impossible since captured graphs store only baked device pointers, so
wholesale by design; no reservation, no admitted ceiling needed):
- grow_scratch_buffer always syncs + frees; the forget branch is gone.
- Every Gpu scratch caller checks the factored scratch_will_grow
  predicate before delegating and drops captured execution state via
  Gpu::invalidate_for_scratch_growth, which reuses the model-swap
  teardown invalidate_for_layout_growth (AR + verify/replay graphs +
  retained Redline rearm). Graphs re-capture lazily; one re-capture
  per growth event, growth monotonic and bounded by largest shape.
- Same treatment for the Drop-less GpuTensor growers
  (gemv_residual_tmp, paro_x, paro_fused) which leaked unconditionally.
- Never invalidates mid-capture/record (would clear in-flight blobs).

Receipts (gfx1201, ROCR_VISIBLE_DEVICES=0, HOME=ab0):
- tmp_scratch_leak_probe pre-fix steps: 39.8/75.5/151.0 MB (full new
  sizes retained); post-fix: 18.9/37.7/75.5 MB (increments only),
  stale graph gone, re-capture + replay clean, no HipError 700.
- cargo test -p rdna-compute --lib: 256 passed (incl. 3 new
  scratch_growth_tests: will-grow predicate, invalidation contract,
  geometry helpers).
- serve A/B single prompt (qwen3.8-27b.mq4-xt): without fix
  finish=stop coherent 39w; with fix finish=stop coherent, decode 36.6.
- serve chain 5 turns (fix): 5/5 finish=stop, 0 empty, growing ctx
  72->1364, avg_decode 36.1 (1 capture + 1254 replays in trace).
Gates remaining at commit time: serve floor A/B on growth-shaped
battery, hipx gfx1100 floor + chain.
Replace the two-perm + v_mul mask expand with an affine-hi
(q>=8 -> 0x50+(q-8)) single-table perm plus a v_perm blend keyed
on the top nibble bit. Bit-exact: exhaustive on-device check of
all 65536 x16 x 2 halves (probe2 candidate B). LUT-arg (Lloyd)
path untouched (codegen .text md5 identical).

Probe gfx1201 (load-fed, 3 runs <1pc spread): expand-only 0.74x,
expand+wmma 0.77x. Per-4-chain ISA: -8 v_mul_lo_u32, -12 v_and,
-4 add3, -8 bfi, +8 and_or, +8 or, +4 add_nc (106 -> 94 VALU).
Metadata unchanged on all 16 uniform symbols (VGPR/SGPR/LDS/
spill identical, e.g. bt12 238/42/0/0). Eager profiler
(prefill 8192, GRAPH=0): gate_up 1921.03 -> 1868.38,
residual 716.19 -> 706.42, qkvza 885.45 -> 864.19,
qkv 829.04 -> 810.01 us/call, identical call counts.
Graphs-ON bench: base == new within 0.4pc quiet (fp8 route is
eager-only by dispatch design); greedy md5 identical;
decode tg64@128 36.5 tok/s both arms. gfx1100/1151 TU compile
fails loud at the new #error (was: wmma builtin error).
…unk; FA2 fp8 plan corrected by the CPU screen

The discarded non-AWQ upload (14980361216 B, 9f91556f…) was found under three
paths today — ~/.hipfire/models/qwen3.8-27b.mq4-xt on the local box (symlink
into the August ladder artifact), /srv/hw-gate/models/qwen3.8-27b.mq4-xt (real
file, root-owned, NOT fixed here), and previously on hipx — and silently carried
a day of gfx1201 numbers. The local symlink now points at the canonical
80e7c624… artifact; the non-AWQ twin is named qwen3.8-27b.noawq-xt on both
boxes. check_fixture.sh fails closed on the known lookalike by size (instant)
and optionally verifies the sha256.

FA2 fp8 plan (docs/plans/2026-09-17-gfx1201-fa2-fp8-legs.md) corrected by the
CPU screen on real layer-35 Q/K/V: e4m3 is exact only to |14|, so the K/V
planes are NOT lossless (max-abs 0.096 / tail 0.0063); stage a moves from
memcmp to tolerance + KLD. Stage c (fp8 P) is killed: 31.5 % of P underflows
at x64, 13.6 % at x256, vs a 0.1 % gate. PV stays f16. Expected row a+b:
~1580/1580/1500/1210 from 1464/1435/1327/1044.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ratchet-raise Maintainer approval for a declared RATCHET-RAISE in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant