Repository navigation
hrx: do not fuse away a value that something outside the programme reads (engine #533) - #146
bong-water-water-bong wants to merge 3 commits into
Conversation
…-only value (engine ggml-org#533 verify) The fused postops export declares its bias and residual bindings with read access whether or not the kernel uses them, and the kernel only loads them when has_bias / has_residual is set. For an absent slot the dispatcher bound the command's own output transient, which the command-program verifier rejects: VERIFY-ERROR: command 2 kind=Kernel bindings=10 deps=1 binding bias value=5 origin=Transient access=Read range=[0, 16384) reads transient value 5 before write Bind a value the programme already reads instead - the weight - for those placeholder slots. The kernel never loads it (has_bias / has_residual guard the read), so nothing changes numerically. before: [KNOWN-FAIL ggml-org#533] common_mul_mat_id_postops_dispatch VERIFY-ERROR: command 2 ... binding bias value=5 ... reads transient value 5 before write after: [PASS] common_mul_mat_id_postops_dispatch (tag retired in tests/hrx-backend-test.cpp) Model check (Qwen3-Coder-30B-A3B Q4_K_M, the postops+next_rmsnorm user): the family enabled vs GGML_HRX_DISABLE_DISPATCH=common.mul_mat_id_postops.f32_f32_wmma gives identical greedy text and identical per-token top-5 logprobs on three prompts (128/126/29 tokens, max |dlogprob| = 0), and an identical second pass on the same server (cold vs warm). 90 W cap, PCM pad (23 Oct). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…r before the kernel (engine ggml-org#533) match_gated_delta_net_rmsnorm_q8 replaces the last dispatch of the matched programme with the fused selected_snapshot_projection_rms_gate_q8 kernel, then cleared the whole dispatch list and pushed that one back. For a one-token graph the programme has two dispatches, so clearing dropped the first (the projections / gate producer) even though its nodes stay in covered_nodes - the fused kernel then reads raw_gate from a command that is emitted later, and the command-program verifier rejects the plan: VERIFY-ERROR: command 6 kind=Kernel bindings=5 deps=1 binding raw_gate value=33 origin=Transient access=Read range=[0, 24576) reads transient value 33 before write by command 7 Replace only the dispatch the fused kernel supersedes (back()) instead of clearing the list, so the producer stays ahead of its consumer in the programme. The planner orders commands by append position and gives each a linear dependency (dispatch/command-program.cpp), which is what makes the reversal fatal rather than merely untidy. Model check (Qwen3.8-27B UD-Q4_K_XL --mtp, the GDN selected-snapshot user): the family enabled vs GGML_HRX_DISABLE_DISPATCH=llm.gated_delta_net.selected_snapshot gives identical greedy text and identical per-token top-5 logprobs on three prompts (128/128/26 tokens, max |dlogprob| = 0), and an identical second pass on the same server (cold vs warm). 90 W cap, PCM pad (23 Oct). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ads (engine ggml-org#533) Stacked on #133 (`f7071dde`). Two changes, both about the same failure mode: a fused programme that takes over a node while a reader of that node's output stays outside it. - **Matcher guard** (`dispatch-gated-delta-net.cpp`): before the selected-snapshot programme upgrades its last dispatch to the fused kernel, every consumer of the snapshot value and of the cache must be covered by the programme (`consumers_covered_by`). If one is not, the fusion is refused and the unfused programme is used. A reader scheduled before the fused kernel that writes the value is exactly what the command-program verifier rejects with "reads transient value N before write". - **Test assertion** (`hrx-backend-test.cpp`): `run_gdn_selected_rms_q8_dispatch_checks` forbade *any* `ggml_copy_f32` in the plan. The snapshot copy is a legitimate part of the fused programme, so the case now requires that the copy precedes the fused kernel and feeds one of its bindings, and still forbids the fallback kernels (`ggml_get_rows_f32`, `ggml_rmsnorm_gate_f32_q8_1_x4`). **Measured, and it does not close the issue.** With #133 + this, at `2ab3114966`-era hrx-system `82804197bc`, host planner, `GGML_HRX=ON`: ``` host: 66 case(s) — 59 passed, 1 failed, 6 known-fail, 0 unexpected-pass [FAIL] gdn_selected_rms_q8_dispatch hrx-backend-test.cpp:10255: command_program_verifies(commands) VERIFY-ERROR: command 4 kind=Kernel bindings=2 deps=1 binding source value=10 origin=Transient access=Read range=[0, 3145728) reads transient value 10 before write ``` So `gdn_selected_rms_q8_dispatch` still fails, on the snapshot copy: value 10 has no producer before command 4 even with the guard in place, which means the copy's consumer node is already inside the programme's covered set while its dispatch is not in the list — the same shape as the bug #133 fixed for the first dispatch, one level down. The next step is to make the covered/dropped pair impossible rather than to detect it: when the fused kernel replaces a dispatch, the nodes that dispatch covered must be dropped from `covered_nodes` too, and the verifier's "reads before write" is what will confirm it. `ggml-org#533` therefore needs one more change beyond #133 and this PR; the `ggml-org#533` marker stays on the row, and the two sibling cases (`qwen_attention_postprocess_dispatch`, `qwen_routed_gate_up_dispatch`) are untouched here. Host and device run details are in the PR comments; nothing about this is claimed beyond what the verifier output shows. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Holding #133 + #146 unmerged until gdn_selected_rms_q8_dispatch passes the command-program verifier: the matcher guard and the corrected test assertion are right, but the remaining 'reads transient value before write' in the selected-snapshot programme is the actual ggml-org#533 defect. Merge both together once that case is green on the host planner and on HRX0. |
|
Dump taken, cause identified, and it is not fixed by the guard in this branch — so #133 + #146 do not close
Cause: value 29 is the snapshot cache. Command 4 copies transient 10 into it; the fused kernel also writes Why the guard here does not help: Next step, and it is a matcher decision, not a scheduler one: refuse the fusion when the pre-fusion snapshot State written to Device run of the suite at this branch (HRX0, one ≤20 min hold): |
|
Option 1 tested — it does not work for this case, and the reason is worth recording. Branch is unchanged What I tried: in Result: the guard never fires — the case still reports the fused kernel in the plan and the same So ggml-org#533's remaining failure is a scheduler ordering/withdrawal problem, not a matcher decision:
Filed as engine ggml-org#570 with this dump, the two sibling rows that show the same class Verdict for ggml-org#533: #133 fixes row 1 ( |
…(engine ggml-org#533, ggml-org#570) (#150) Registry-level fusion-order guard (dispatch-fusion-order.{h,cpp}): a match whose non-write-only binding reads transient data that no earlier dispatch (in the plan or in the match) wrote is refused and falls through to the next registration; GGML_HRX_FUSION_ORDER_GUARD=0 disables, GGML_HRX_LOG_FUSION_ORDER=1 logs. Also: routed-ffn gate_up_swiglu_f16_wmma only when every GLU consumer is a Q4_K/Q6_K routed down projection (a real defect: other down weights read an unwritten f32 value). hrx-backend-test host 60/0/6 -> 63/0/3, device 11/11, test-backend-ops 314/314, KLD identical on Qwen3.5-4B / ZAYA1-8B / Qwen3.8-27B with 0 refusals, 27B draft-mtp completions identical, hw-gate PASS. Supersedes #133 and #146. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Superseded by #150 (merged): a registry-level read-before-write guard closes engine ggml-org#533 / ggml-org#570 for all matchers; #133's kept copy read a value nothing writes in the fused programme (the 'value 10' error), and #146's consumers_covered_by is covered by the guard's checks 1 and 3. |
hrx: do not fuse away a value that something outside the programme reads (engine ggml-org#533)
Stacked on #133 (
f7071dde). Two changes, both about the same failure mode: a fused programme that takes over anode while a reader of that node's output stays outside it.
dispatch-gated-delta-net.cpp): before the selected-snapshot programme upgrades its lastdispatch to the fused kernel, every consumer of the snapshot value and of the cache must be covered by the
programme (
consumers_covered_by). If one is not, the fusion is refused and the unfused programme is used. Areader scheduled before the fused kernel that writes the value is exactly what the command-program verifier
rejects with "reads transient value N before write".
hrx-backend-test.cpp):run_gdn_selected_rms_q8_dispatch_checksforbade anyggml_copy_f32in the plan. The snapshot copy is a legitimate part of the fused programme, so the case nowrequires that the copy precedes the fused kernel and feeds one of its bindings, and still forbids the fallback
kernels (
ggml_get_rows_f32,ggml_rmsnorm_gate_f32_q8_1_x4).Measured, and it does not close the issue. With #133 + this, at
2ab3114966-era hrx-system82804197bc,host planner,
GGML_HRX=ON:So
gdn_selected_rms_q8_dispatchstill fails, on the snapshot copy: value 10 has no producer before command 4even with the guard in place, which means the copy's consumer node is already inside the programme's covered set
while its dispatch is not in the list — the same shape as the bug #133 fixed for the first dispatch, one level
down. The next step is to make the covered/dropped pair impossible rather than to detect it: when the fused
kernel replaces a dispatch, the nodes that dispatch covered must be dropped from
covered_nodestoo, and theverifier's "reads before write" is what will confirm it.
#533therefore needs one more change beyond #133 and this PR; the#533marker stays on the row, and the twosibling cases (
qwen_attention_postprocess_dispatch,qwen_routed_gate_up_dispatch) are untouched here.Host and device run details are in the PR comments; nothing about this is claimed beyond what the verifier output
shows.
🤖 Generated with Claude Code