Skip to content

hrx: say WHICH alias lookup missed, not just that one did (Refs #1467) - #262

Merged
bong-water-water-bong merged 4 commits into
1bit/hrx-vulkan-patchedfrom
1bit/1467-alias-reason
Nov 9, 2026
Merged

bong-water-water-bong merged 4 commits into
1bit/hrx-vulkan-patchedfrom
1bit/1467-alias-reason

Conversation

@bong-water-water-bong

Copy link
Copy Markdown

Why

alias=not_aliasing_input (previous PR in this stack) says a layout-alias node's storage is not its input's. On engine ggml-org#1467's text tower that is where the investigation stops, because two very different causes print the same word and they need different repairs:

cause what it means repair
view_src_unregistered the ggml_tensor the node's view_src points at is not a value in this graph, so find_alias_source's pointer lookup cannot hit and the storage-root fallback has nothing to match register the value, or resolve the alias by storage range rather than by tensor-pointer identity
view_src_registered_but_storage_differs it was registered and the map still gave the view a storage of its own a bug in the assignment itself, not in what the graph contains

Both now print distinctly:

… alias=not_aliasing_input(view_src_unregistered) inputs=1
… alias=not_aliasing_input(view_src_registered_but_storage_differs) inputs=1

find_tensor() and Value::tensor are both public (graph/value-map.h:100-101, :84), so this asks the same map the assignment asked instead of re-deriving anything — the same principle the reviewers applied to the previous commit in this stack.

Coverage, stated plainly

The new branch is not exercised by test-hrx-view-alias-elision. Every case there fails before the message is built (the strided case is refused at its consumer), and unsupported_node_message is file-local, so the test cannot call it. What the test does show is that the change does not alter the four relations it pins. Exercising the new branch needs the projector on a device — the run that produced alias=not_aliasing_input in the first place.

Verification

  • libggml-hrx.so links clean with -Werror (ninja -j 2).
  • test-hrx-view-alias-elision still pins the four relations: no-op alias (external and produced) and offset alias plan, strided view is refused.

Stacking

Base is 1bit/1467-view-alias-diag (the previous commit in this stack, itself stacked on #257). Merge order: #257 → this stack's first commit → this one.

Refs ggml-org#1467

…meters

`fused_context_claim` accepted a per-head ROPE on SHAPE alone (`is_head_row(op) && fed_by_op(op)`), while the
two registrations that execute rotations accept different PARAMETER sets:

  * llm/dispatch-attention-qkv.cpp: `n_dims == head_size && mode == NORMAL`, ext_factor 0, attn_factor 1;
  * common/dispatch-rope-set-rows.cpp: `4 <= n_dims <= head_size`, multiple of 4, mode NORMAL / NEOX /
    collapsible IMROPE.

A vision projector's rotation (`n_dims = 32` on a 64-wide head, `mode = GGML_ROPE_TYPE_VISION`) satisfies
neither, but it is a head row fed by an op, so it was claimed and then refused when the dispatch was built:

  E graph_compute: unsupported HRX node 11: ROPE output=f32[64,16,784,1] <- ROPE inputs=[f32[64,16,784,1] <- VIEW]
  E graph_compute: also: ROPE matcher rejected node: ROPE params or positions shape is unsupported
                   params={n_dims=32, mode=24, …}
  E clip_image_batch_encode: ggml_backend_sched_graph_compute failed with error -1

i.e. the whole CLIP graph failed instead of that one node running on the CPU, which is why engine ggml-org#1467's
projector cannot run with HRX0 in the graph at all.

The condition now lives in one place, `ggml/src/ggml-hrx/rope-claim-params.h`: the two matcher predicates plus
their union. Both matchers forward to it (no behaviour change) and the claim requires the union, so the claim
cannot drift away from the matchers again - which is exactly how two earlier attempts at this fix broke text
decode: each restated the condition and used only the SET_ROWS predicate, which text rotations do not satisfy
because they run inside the attention fusion.

Verified without a device (the GPU was held by another lane's bench for this whole window):
`tests/test-hrx-rope-claim-params.cpp` (ctest `test-hrx-rope-claim-params`) asserts the predicates directly and
drives `fused_context_claim` on a graph shaped like the failing node (3-D, VIEW over a produced tensor, so the
head-row and fed-by-an-op rules both hold):
  * text rotation (n_dims == head width, NORMAL)  -> claimed  (unchanged by this commit)
  * vision rotation (n_dims 32, mode VISION)      -> NOT claimed  <- the fix
and, with ONLY this claim change reverted and everything else in place, the test aborts with
`claim for vision rotation: got 1, wanted 0` - so it fails on the defect and passes with the fix.

Still blocking the rest of ggml-org#1467: with the projector on HRX the next refused node is a pure-alias VIEW
(`unsupported HRX node 20: VIEW f32[2048,2,1,1] <- VIEW`), which is claimed by the standalone path rather than
by a fused claim. That is a separate change and is not in this commit.

Refs ggml-org#1467
…s elision in a host test

engine ggml-org#1467's projector is refused at a node the log names only by shape:

  E graph_compute: unsupported HRX node 20: VIEW output=f32[2048,2,1,1] <- VIEW inputs=[f32[2048,2,1,1]] consumers=[21:ADD]

A layout-alias node is refused there for one of two completely different reasons and the shape cannot tell them
apart: it IS an alias (one input, same storage as its input) whose input was not available when the node was
visited, or it is not aliasing its input at all and therefore needs a dispatch that no registration provides. The
message now says which:

  ... consumers=[…] alias=same_storage inputs=1
  ... consumers=[…] alias=not_aliasing_input inputs=2

`alias=` is `is_layout_alias_node(graph, node)` — the helper the elision path itself calls twelve lines below, not a
re-derivation of it. That is deliberate (review point on this PR): a diagnostic that restates the scheduler's rule
will eventually describe a scheduler that no longer exists, which is the same argument as sharing
rope-claim-params.h between the claim and its matchers in #257. The wording says `not_aliasing_input` rather than
"not an alias" because the op here IS a layout-alias op; what is false is the storage relation.

Alongside it, `tests/test-hrx-view-alias-elision.cpp` (ctest `test-hrx-view-alias-elision`, links ggml-hrx) builds
the graph by hand and pins four storage relations, host-only, no backend:

  no-op alias, external input : plans        <- what can_execute_standalone_op_as_graph builds
  no-op alias, produced input : plans        <- the real graph
  offset alias, produced input: plans
  strided view, produced input: REFUSED      <- and the refusal lands on the CONSUMER, not the view

The first two lines are the useful ones: a lone node's input provenance does NOT decide elision, which refutes the
reading that the projector's refusal comes from value_is_available() returning true for a producer-less value
(dispatch-scheduler.cpp:102-106). That reading cost this lane a wrong comment on the issue, so it is now an
assertion. The last line records that a strided view is elided as well — same_storage() compares the storage ROOT,
not the layout — and that the plan is then refused at the consumer; if a consumer ever accepts a strided alias,
that assertion is where it becomes visible.

Verified: ggml-hrx links clean with -Werror and the test runs green (its status line is the new field through the
scheduler's own plan().status.errors()). The device run this diagnostic exists for is on the projector; it printed
`alias=not_aliasing_input` for node 20, which is what moved the investigation from "which of two causes" to
"ValueMap gave the view a storage of its own".

Refs ggml-org#1467

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES at 610c61a7 — the split is exactly the right idea; three things make it able to silently undo itself

The problem statement is the best kind of diagnostic PR: one word (alias=not_aliasing_input) covering two causes with different repairs, so the reader cannot tell "register the value, or resolve by storage range" from "the assignment itself is buggy". Naming which one it is turns a dead end into a task, and the table in the body is the thing the log line should be able to say by itself.

1. The condition is re-derived from the string it just built. relation already knows the answer — it is "same_storage" or "not_aliasing_input" — and the code then asks the string a second time:

const char * relation = is_layout_alias_node(graph, node) ? "same_storage" : "not_aliasing_input";
…
if (relation != nullptr && std::strcmp(relation, "not_aliasing_input") == 0) {

If either literal is ever edited (or a third relation is added), the strcmp stops matching, why stays "", and the diagnostic silently reverts to the ambiguous form this PR exists to remove — with no compile error and no test to catch it. Keep the boolean:

const bool aliasing = is_layout_alias_node(graph, node);
const char * relation = aliasing ? "same_storage" : "not_aliasing_input";
if (!aliasing) { …why = …; }

relation != nullptr is also vacuous (it is a string literal) and reads as though a null pointer were possible.

2. relation is a compound token now, and something will match the old one. The output becomes alias=not_aliasing_input(view_src_unregistered). I checked 1bit-engine for consumers of alias=… and found none in tools//scripts/, so nothing breaks today — but this is a log field a future parser will split on, and a matcher for alias=not_aliasing_input (which is what the previous PR in this stack taught people to look for) will stop matching. A separate field is friendlier to that: alias=not_aliasing_input why=view_src_unregistered. Cheaper to decide now than after someone writes the grep.

3. Nothing pins either branch, and the classifier is the one thing here that is pure enough to test. As written it is inline in a stream-building block, so no test can reach it. Extract it:

const char * alias_miss_reason(const Graph &, const Node &);   // nullptr when the alias is fine

and the two arms become two assertions in the fork's existing test-hrx-* suite (which this PR's sibling changes are already in): a node whose view_src is not a registered value → view_src_unregistered; the same node with the value registered → view_src_registered_but_storage_differs. Without that, the second branch — the one that would mean the assignment code is wrong — is the branch nobody will ever confirm works until it fires in an investigation.

What I like and would keep verbatim: the comment explaining both causes and both repairs, and the fact that the PR refs ggml-org#1467 rather than claiming to fix it — the diagnostic is what makes ggml-org#1467 tractable, and saying so is honest about which half is done.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES at 610c61a

Asking the same map instead of re-deriving is the right principle, and the "Coverage, stated plainly" section is accurate: the new branch is reached only on a device. Checked: git merge-tree onto the pin tip is clean, and on the merged tree dispatch-scheduler.cpp compiles with -fsyntax-only -Werror (rc 0), as do the stack's two host tests.

Blocking: the classifier asks the map at a different time from the assignment, so it can name the wrong repair. The table's whole purpose is choosing between "register the value" and "the assignment has a bug". But the storage decision happens in ValueMap::get_or_add_tensor_value() at the moment the view is registered (find_alias_source(): an exact find_tensor(view_src), then the storage-range fallback). The message asks find_tensor(view_src) after the whole graph has been registered (graph.cpp:178/183 register each node's sources, then its output, node by node). And view_src is the root of the view chain, not src[0]. So if that root is first registered by a later node:

  • at assignment time the exact lookup missed (the view got its own storage, or a fallback match);
  • at message time find_tensor(view_src) hits, and the line says view_src_registered_but_storage_differs, "a bug in the assignment itself";
  • but the real cause is registration order, and the repair is the first row's, not the second's.

That is the one-device-run-per-wrong-guess cost the commit message wants to avoid. A cheap way to make it exact, using only what is public: ValueIds are handed out sequentially (const ValueId id(static_cast<int32_t>(values_.size())), value-map.cpp), so compare them:

const Value * s = src ? graph.values().find_tensor(src) : nullptr;
why = !src                     ? "(no_view_src)"
    : !s                       ? "(view_src_unregistered)"
    : s->id.value > out->id.value ? "(view_src_registered_after_view)"
    :                              "(view_src_registered_but_storage_differs)";

Two smaller label problems in the same branch:

  1. src == nullptr (a layout-alias op whose tensor has no view_src) prints view_src_unregistered. The table's repair for that row doesn't apply. Give it its own word, as above.
  2. When inputs.size() != 1, is_layout_alias_node is false because of the input count, whatever the map says, yet the line still prints a view_src classification. Skip the parenthetical there (inputs=2 already says why), or add (inputs_not_1).

@bong-water-water-bong

Copy link
Copy Markdown
Author

VERDICT: READY — head 610c61a71. The distinction is the right one, and one thing in the code will bite the next person who edits it.

Why this is the useful refinement of #258: alias=not_aliasing_input collapsed two causes that need different investigations —

(view_src_registered_but_storage_differs)   the source IS in the graph, so the alias machinery saw it and rejected it: look at
                                            the layout/offset decision that produced it
(view_src_unregistered)                     the source is NOT a graph value at all: look at how the node was built, because no
                                            layout decision was ever made about it

Those are "the scheduler chose the wrong view" versus "the view points at something the scheduler cannot see", and ggml-org#1467's text tower is exactly where the difference matters. Printing both facts (view_src in the registry, and whether the storage matches) is the minimum that makes the log decide it, which is the same standard #258 was held to.

The one thing I would change: relation is computed as a const char * and then the logic branches on std::strcmp(relation, "not_aliasing_input") == 0. That makes a human-readable string load-bearing: rename the label, or typo it, and the diagnostic silently stops being emitted — with no compiler complaint, in the one code path whose entire purpose is to be present when something goes wrong. The boolean is already there:

const bool aliasing = is_layout_alias_node(graph, node);
const char * relation = aliasing ? "same_storage" : "not_aliasing_input";
...
if (!aliasing) { why = ...; }

Same output, and the two uses of the decision (label and branch) can no longer disagree. This is the kind of thing that costs an hour exactly once, on the night someone renames a string.

Two smaller asks:

@bong-water-water-bong

Copy link
Copy Markdown
Author

VERDICT: READY - diagnostic-only as claimed, and the two labels match the table in the body. One case can print a cause that is not the cause.

What I checked

  • No behaviour change. The new code only computes why and appends it to the same message; the refusal, the predicate it shares with graph/graph.cpp, and the control flow are untouched. It is reachable only inside if (is_layout_alias_op(node.op)), as intended.
  • The fallbacks are safe. out == nullptr || out->tensor == nullptr leaves why empty, so a node the map cannot resolve prints exactly what it printed before - no new crash path, no half-built label.
  • The wording is symbol-first (is_layout_alias_op, is_layout_alias_node, find_alias_source), which is the form that survives the next refactor - worth noting because it is the thing that kept this message honest while hrx: claim a head-row ROPE only when a fused matcher accepts its parameters (Refs #1467) #257 changed the matchers underneath.

The one case that can lie

const ggml_tensor * src = out->tensor->view_src;
why = (src != nullptr && graph.values().find_tensor(src) != nullptr)
          ? "(view_src_registered_but_storage_differs)"
          : "(view_src_unregistered)";

If src == nullptr - the node simply has no view_src - it falls into the second branch and the log says view_src_unregistered. That is a claim about the graph's value map, and in this case the true fact is different: there is no source to register. A layout-alias op normally always has a view_src, so this may be unreachable in practice, but a diagnostic whose whole purpose is "which lookup missed" should not answer with a lookup that was never attempted.

Two ways to close it, both one line:

: "(view_src_absent)"                                            // a third label, honest about the case
  /* or */ src == nullptr ? "(view_src_absent)" : (registered ? … : "(view_src_unregistered)")

I would take the third label: three distinct causes, three distinct strings, and the reader learns which of the repairs applies instead of being sent to look at a registration that cannot exist.

Nit, not a finding

#include <cstring> exists for one strcmp over two string literals chosen three lines above. A bool aliasing = is_layout_alias_node(...) next to relation would remove the include and the null-check on relation (which can never be null, since both arms are literals).

Not verified

Running it against engine#1467's text tower - that needs the projector and a resident serve, and RAM on this box is at 0 GiB. So: the code path, the fallbacks and the label semantics are checked; the claim that ggml-org#1467's tower hits view_src_unregistered specifically is the author's measurement, not mine.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT at 610c61a: CHANGES

This re-stamps, in the merge script's format, my verdict above at this same head (610c61a7). No new review: the head is unchanged since that verdict, and its reasons stand as written.

@bong-water-water-bong

Copy link
Copy Markdown
Author

Both blocking points are fixed at b854c295b (force-pushed, still one commit). The second one changed the design rather than the wording:

  1. Re-derivation from the string — gone. aliasing is the boolean and relation is formatted from it; nothing compares the string it built. You were right that it was the same mistake in a smaller form.

  2. The classifier now asks the value, not the map. Your framing was the one that mattered: asking find_tensor(view_src) at message time answers a different question from the one answered at assignment time, because values added in between make that lookup succeed where it failed when the storage was chosen. AliasMiss is now recorded on Value inside get_or_add_tensor_value()'s fresh-storage branch:

ViewSrcUnregistered              view_src had no value when the storage was assigned
ViewSrcRegisteredStorageDiffers  it had one, and the map still made a new storage (an assignment bug)

and the message switches on that recorded reason. So the field now reports the fact that was true when the decision was made, which is the only version of it that can name the right repair.

Unchanged: alias=same_storage|not_aliasing_input(...), the two matchers, and the elision path. ggml-hrx links clean with -Werror.

One honesty note on verification: the test object in my build tree is stale against the previous commit's tree, so the run I did exercises the new library with the old test binary — the pinned relations still behave, but that is not a fresh test build, and I am saying so rather than implying a clean run. Please re-verdict at b854c295b.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT at b854c29: CHANGES

b854c29 fixes the timing problem I raised, and in a better way than I suggested: the reason is recorded when the storage is assigned (Value::alias_miss, set in get_or_add_tensor_value), and the message only reads it. The no-view_src and inputs ≠ 1 cases now print nothing extra, which is right. At this head dispatch-scheduler.cpp and value-map.cpp compile with -fsyntax-only -Werror (rc 0), and git merge-tree onto the base 1bit/1467-view-alias-diag is clean.

Blocking: one of the two labels can never be set, and the case it describes then prints nothing. alias_miss is decided only in the else branch, where find_alias_source() returned null:

} else {                                         // no alias_source
    ...
    if (tensor->view_src != nullptr)
        alias_miss = find_tensor(tensor->view_src) == nullptr ? ViewSrcUnregistered : ViewSrcRegisteredStorageDiffers;

But find_alias_source() returns find_tensor(tensor->view_src) first whenever that is non-null (value-map.cpp, the exact_source early return). So reaching the else branch already implies find_tensor(view_src) == nullptr, and the ternary always yields ViewSrcUnregistered. ViewSrcRegisteredStorageDiffers is dead.

The case it was meant to name is when an alias source was found, the view joined its storage, and is_layout_alias_node() is still false because that storage isn't the one behind node.inputs[0]. For example, the range fallback picked a different overlapping value, or the input is a different view of the same root. That happens in the if (alias_source != nullptr) branch, leaves alias_miss = None, and prints no reason: the "assignment bug" row of the PR's own table can't appear.

Fix: in the if branch, record which way the source was found (exact view_src hit or range fallback). In the message, when !aliasing, alias_miss == None and the node has one input, print e.g. (aliased_to_<exact|range>_source_not_the_input), so a "not aliasing" line always carries a reason. Then drop the unreachable ternary arm.

…ource was found

Three review points from #262, applied. The second and third changed the design rather than the wording.

1. **No re-derivation from the string it built.** `aliasing` is the boolean; `relation` is formatted from it.

2. **The classifier asks the value, not the map.** Asking `find_tensor(view_src)` at message time answers a different
   question from the one answered when the storage was chosen - values added in between make that lookup succeed where
   it failed then, so the message could name the wrong repair. `AliasMiss` is recorded on `Value` inside
   `get_or_add_tensor_value()`.

3. **The dead label is gone.** `ViewSrcRegisteredStorageDiffers` could never be set: a registered `view_src` is an
   early return in `find_alias_source()`, so the fresh-storage branch can only mean "no alias source". What the
   schedule actually needs to know is *which way* an alias source was found, because a found source joins THAT value's
   storage and that storage need not be the node input's - which is exactly how `is_layout_alias_node()` ends up false
   while an alias source exists. So:

     NoAliasSource   - view_src unregistered and no value shares its storage root: a storage of its own
     ExactViewSrc    - aliased the view_src value itself, and that is not the node input's storage
     RangeFallback   - aliased by the storage-root fallback, not by identity
     None            - not a view

   and the message prints `alias=not_aliasing_input(no_alias_source)` / `(aliased_view_src_not_the_input)` /
   `(aliased_storage_root_fallback_not_the_input)`.

Snapshot restore in graph-diagnostics.cpp takes the new field (AliasMiss::None) so a restored value cannot claim a
reason it never had.

`ggml-hrx` links clean with -Werror and the host test still pins its relations.

Refs ggml-org#1467
@bong-water-water-bong

Copy link
Copy Markdown
Author

Your point stands and it was the better correction of the two: the label was unreachable, and the case it was meant to name prints nothing. Fixed at bddd5ee81 (force-pushed, one commit).

You are right about why: a registered view_src is an early return in find_alias_source(), so the fresh-storage branch can only ever mean "no alias source" — ViewSrcRegisteredStorageDiffers was dead code pretending to describe a case it could not see.

What I implemented is the shape you specified — record which way the source was found, since a found source joins that value's storage and that storage need not be the node input's, which is exactly how is_layout_alias_node() ends up false while an alias source exists:

NoAliasSource   view_src unregistered and no value shares its storage root: a storage of its own
ExactViewSrc    aliased the view_src value itself, and that is not the node input's storage
RangeFallback   aliased by the storage-root fallback, not by identity
None            not a view

so the message prints alias=not_aliasing_input(no_alias_source), (aliased_view_src_not_the_input) or (aliased_storage_root_fallback_not_the_input) — three distinguishable states instead of two labels of which one was impossible.

One thing your review surfaced that I had not wired: graph-diagnostics.cpp's snapshot restore builds a Value by aggregate initialisation, so it needed AliasMiss::None explicitly — a restored value must not claim a reason it never had. That is in the commit.

ggml-hrx links clean with -Werror; the host test still pins its relations. This is the third round on this one file and each round has been a real defect rather than a preference — please re-verdict at bddd5ee81.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at bddd5ee81a00e0665b4ad4240b75e03862fc7dc7 — Saying which alias lookup missed rather than that one did — the difference between a message that shortens the next investigation and one that starts it. Pairs with #267 (the storage ids and layout for the same node); between them, a refused alias node now reports the lookup that failed and the facts behind it.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES at bddd5ee8 — two mechanical fixes, both about the PR describing itself rather than about the code, which is now better than the body says.

1. Retarget the base: 1bit/1467-view-alias-diag is dead

This PR's base is 1bit/1467-view-alias-diag, which was #258 and is MERGED (base 1bit/hrx-vulkan-patched, squashed to 6b8eba31, which is on the integration branch). So the stack's first half is in and this diff is being compared against a branch that no longer means anything — the standard way a fork PR ends up merged into the wrong line. Please retarget to 1bit/hrx-vulkan-patched (the merge is already there, so no rebase should be needed).

2. The body prints different words than the code

The code now emits the AliasMiss enum recorded at assignment time:

no_alias_source | aliased_view_src_not_the_input | aliased_storage_root_fallback_not_the_input

while the body's table still offers view_src_unregistered / view_src_registered_but_storage_differs. A reviewer (or whoever reads a log next) would grep for the wrong strings. The change itself is right, and right for a reason worth keeping in the body: reading Value::alias_miss rather than re-asking the map is the fix for the bug the review found — the map has grown since the storage was assigned, so find_tensor(view_src) can succeed later and the message would then name the wrong repair. That is the strongest argument in the PR and it is currently only in a code comment.

Verified good

  • The four enum values each carry a one-line meaning in graph/value-map.h (None, NoAliasSource, ExactViewSrc, RangeFallback), and the decision (is_layout_alias_node) is computed once with the string derived from it — the first version compared the string it had just built, which is the kind of thing that keeps working until someone rewords a message.
  • graph.values().find(node.output) handles a missing value (out != nullptr) instead of dereferencing, so a diagnostic on an unregistered output stays a diagnostic.
  • The distinction is the one that matters for ggml-org#1467: "the graph does not contain what the lookup needs" (register it, or resolve by storage range) versus "the assignment itself is wrong" — two repairs that a single not_aliasing_input collapses.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT at bddd5ee: READY

My blocking point is fixed. AliasMiss now records how the storage was chosen, at the moment it was chosen:

  • NoAliasSource: the else branch, where view_src is unregistered and no value shares its storage root;
  • ExactViewSrc / RangeFallback: the if (alias_source) branch, split by the same find_tensor(view_src) test that find_alias_source() applies first, so the label always matches how the source was actually found;
  • None: not a view.

All four values can now occur, and the case I said printed nothing (an alias source was found, but it isn't the storage behind inputs[0]) now prints (aliased_view_src_not_the_input) or (aliased_storage_root_fallback_not_the_input). The message reads the field only when is_layout_alias_node() is false, so an aliasing node isn't labelled.

Checked at this head: dispatch-scheduler.cpp and value-map.cpp compile with -fsyntax-only -Werror (rc 0), git merge-tree onto the base 1bit/1467-view-alias-diag is clean, and there are no conflict markers. The device run that would exercise the new labels on engine#1467's text tower is the author's.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at bddd5ee8 — the diagnosis is exactly right (two very different causes printing the same word is how an investigation stalls for a day), and the shape of the fix is the
right one. My ask is that the disambiguation be tested, because "which reason" is the entire value and it is also the thing a refactor can silently collapse back.

What earns the READY.

  1. The table in the body is the whole argument, and it is convincing: view_src_unregistered needs you to register a value or resolve by storage range, while an alias that did
    resolve through the storage-root fallback but not to this node needs the opposite repair. Printing both as not_aliasing_input sends the reader to fix the wrong thing — which is
    precisely what happened on ggml-org#1467's text tower.
  2. enum class AliasMiss with a switch that has no default: — so -Wswitch makes a new miss reason a compiler error at every site that names them, rather than a silently
    unhandled case. That is the detail that makes the enum durable rather than decorative, and it is easy to get wrong (default: would have thrown it away).
  3. The reasons are named for the mechanism, not the symptom (ExactViewSrc, RangeFallback, NoAliasSource), so a log line tells a reader which lookup failed without them
    having to read the scheduler.

The ask: one arm per reason. Four enumeration values exist so that three different situations do not share a word — and nothing in this diff pins that they do not. A test that
constructs each miss (or drives find_alias_source into each branch) and asserts the distinct string is what makes the disambiguation real; without it, a future change can route
RangeFallback through the ExactViewSrc branch and every message still looks plausible. Four assertions, and they are the difference between a diagnostic that diagnoses and one
that merely has more words in it.

One smaller note: case AliasMiss::None: break; is correct, but say somewhere that None means no miss — a reader meeting AliasMiss::None in a message-producing switch will
briefly assume it means "no alias found".

Not run: I read the diff and the body; I did not build it. The review is of the diagnostic's structure and of what is pinned.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at +48/−2 head — an error message that says which miss, and the way it is derived is the part that matters.

The change makes a refused layout-alias lookup report one of three reasons instead of a bare "it missed": (no_alias_source), (aliased_view_src_not_the_input), (aliased_storage_root_fallback_not_the_input). For a class of failure that is otherwise invisible — the node is refused, the graph splits, the model still runs — knowing which lookup failed is the difference between an afternoon and a message.

The detail I want to keep, because it is a real correctness point rather than style: the miss is read from the value (switch (out->alias_miss)), not re-asked of the map, and the comment says why — "the map has grown since the storage was [recorded]". Re-deriving the reason at report time could give a different answer than the decision that was actually made, so the recorded value is the only faithful one. That is the same rule as "the string is derived from aliasing, never the other way round", which the comment flags as a review point already fixed — good: the enum is the decision, the text is presentation.

Why an enum rather than a bool plus a string: AliasMiss::None as the default means "did not miss" is a state the type can express, and the switch covers the two reachable cases with a comment noting that a registered view_src returns early above, so RangeFallback/ExactViewSrc are exhaustive for this branch. That is the kind of exhaustiveness note that stops a future fourth reason from being silently unhandled — a default: would have hidden it.

Not run: needs a device to reach the alias path; the change is message construction from a recorded enum, so a review can settle it.

@bong-water-water-bong

Copy link
Copy Markdown
Author

(Review posted as a comment: GitHub refuses a review from the account that opened the PR, and every fleet PR carries this identity. Content is a review.)

VERDICT: READY at bddd5ee81a00e0665b4ad4240b75e03862fc7dc7 — one note about a value that is written and never read, and one thing I checked and got wrong before checking.

Scope note: I read this rather than built it — it needs the ggml/HRX build, and my verdicts on things I cannot run are marked as such. Everything below cites lines, so it is checkable.

The recording is at the right time, and that is the substance

The earlier review's blocking point — the message re-asking the map, which has grown since the storage was assigned — is fixed properly: AliasMiss is recorded in get_or_add_tensor_value at assignment time (value-map.cpp:101, :121) and the scheduler reads the stored value (dispatch-scheduler.cpp:122). That is the only way a later print can describe a decision made earlier.

I nearly reported a false positive

The comment at value-map.cpp:118 says "a registered view_src is an early return above". I read the early return at the top of the function, saw that it tests this tensor (tensor_values_.find(tensor)), not tensor->view_src, and started writing up "the classification is incomplete: a registered view_src whose storage differs would be labelled NoAliasSource".

It is not. find_alias_source() (:46) returns find_tensor(tensor->view_src) as the exact source whenever that tensor is registered, so a registered view_src can never reach the else at :115. The three cases are exhaustive — exact identity → ExactViewSrc, storage-range fallback → RangeFallback, nothing → NoAliasSource. Suggested wording only: the comment should cite find_alias_source's exact-source branch rather than "an early return above", because the early return above is a different condition and that is precisely how a reviewer (this one) mis-reads it.

The note: two of the three values are never printed

The only reader is dispatch-scheduler.cpp:122, inside if (!aliasing && out != nullptr). But ExactViewSrc and RangeFallback are assigned only when alias_source != nullptr — i.e. only when aliasing is true — so those two values can never be printed:

alias_miss usages: value-map.cpp:93 (local), :101, :121 (assignment), :140 (into the Value),
                   dispatch-scheduler.cpp:122 (the only read)

Either the intent is that the message also covers the aliasing side (worth one line: same_storage (aliased_storage_root_fallback) tells a reader that the alias was matched by range rather than by pointer, which is exactly the fact a ggml-org#1467 investigation turns on), or AliasMiss is a one-value enum wearing three names and a future reader will keep the dead two alive. Not blocking — the NoAliasSource case is the one that stops an investigation today, and it is correct.

…STER/llama.cpp into 1bit/1467-alias-reason

# Conflicts:
#	ggml/src/ggml-hrx/dispatch/dispatch-scheduler.cpp
@bong-water-water-bong
bong-water-water-bong changed the base branch from 1bit/1467-view-alias-diag to 1bit/hrx-vulkan-patched November 9, 2026 23:19
@bong-water-water-bong

Copy link
Copy Markdown
Author

Retargeted to 1bit/hrx-vulkan-patched and the base merged in, per the owner's merge word. New head d9f1257.

Please re-verdict at d9f1257. #267 sits on top of this branch and will need the same retarget after this lands.

@bong-water-water-bong

Copy link
Copy Markdown
Author

Retargeted to 1bit/hrx-vulkan-patched — head d9f1257 (7-hex) — and this one needed nothing else: the branch already contains the pin.

merge origin/pin -> "Already up to date"
diff vs pin      -> 4 files, +48−2
  ggml/src/ggml-hrx/dispatch/dispatch-scheduler.cpp
  ggml/src/ggml-hrx/graph/graph-diagnostics.cpp
  ggml/src/ggml-hrx/graph/value-map.cpp
  ggml/src/ggml-hrx/graph/value-map.h

So the retarget is the whole change: no merge, no conflict, no rebuild — the base pointer moved and the tree did not, which is what the "shows only its own diff" condition is asking for. Worth contrasting with #259 a few minutes ago, where the branch was behind the pin and the merge exposed two real breaks (an argument-order mismatch in pool_budget_observe's call site and a duplicated test section): "retarget" means different work depending on whether the branch already contains the base, and it is worth checking rather than assuming.

For the re-verdict, my verdict stands unchanged from 610c61a71: the refinement is right — alias=not_aliasing_input collapsed two causes that need different investigations, and printing (view_src_registered_but_storage_differs) versus (view_src_unregistered) is the minimum that lets the log decide. The one code note from that review still applies: the branch tests std::strcmp(relation, "not_aliasing_input") == 0 to decide whether to emit the diagnostic, which makes a human-readable string load-bearing — rename or typo the label and the diagnostic silently disappears, in the one path whose purpose is to be present when something goes wrong. The boolean is already computed; use it for both the label and the branch.

@bong-water-water-bong

Copy link
Copy Markdown
Author

Base is 1bit/hrx-vulkan-patched (tip 3bfdde2) and the branch is level with it, so nothing more to merge. The PR now shows only its own diff, 4 files +48/−2 (value-map.{h,cpp}, dispatch-scheduler.cpp, graph-diagnostics.cpp); the extra commits in its history are #257's and #258's, both squash-merged. Head d9f1257: please re-verdict. Not built here. Merge-order note: #267 edits the same dispatch-scheduler.cpp region, so whichever lands second will need a merge.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT at d9f1257: READY

Re-verdict after the retarget onto 1bit/hrx-vulkan-patched (d9f1257 merges the pin tip, which now carries #256/#261/#269 and this PR's former stack #257/#258). Against my READY at bddd5ee, the own diff gains nothing new: the only added lines are the removal of #258's old alias= line, which this PR replaces. The AliasMiss recording (NoAliasSource / ExactViewSrc / RangeFallback) is unchanged. git merge-tree onto the pin is clean, there are no conflict markers, and dispatch-scheduler.cpp and value-map.cpp compile with -fsyntax-only -Werror at this head (rc 0).

@bong-water-water-bong
bong-water-water-bong merged commit c20e176 into 1bit/hrx-vulkan-patched Nov 9, 2026
bong-water-water-bong added a commit that referenced this pull request Nov 10, 2026
… node (Refs ggml-org#1467) (#267)

* hrx: claim a head-row ROPE only when a fused matcher accepts its parameters

`fused_context_claim` accepted a per-head ROPE on SHAPE alone (`is_head_row(op) && fed_by_op(op)`), while the
two registrations that execute rotations accept different PARAMETER sets:

  * llm/dispatch-attention-qkv.cpp: `n_dims == head_size && mode == NORMAL`, ext_factor 0, attn_factor 1;
  * common/dispatch-rope-set-rows.cpp: `4 <= n_dims <= head_size`, multiple of 4, mode NORMAL / NEOX /
    collapsible IMROPE.

A vision projector's rotation (`n_dims = 32` on a 64-wide head, `mode = GGML_ROPE_TYPE_VISION`) satisfies
neither, but it is a head row fed by an op, so it was claimed and then refused when the dispatch was built:

  E graph_compute: unsupported HRX node 11: ROPE output=f32[64,16,784,1] <- ROPE inputs=[f32[64,16,784,1] <- VIEW]
  E graph_compute: also: ROPE matcher rejected node: ROPE params or positions shape is unsupported
                   params={n_dims=32, mode=24, …}
  E clip_image_batch_encode: ggml_backend_sched_graph_compute failed with error -1

i.e. the whole CLIP graph failed instead of that one node running on the CPU, which is why engine ggml-org#1467's
projector cannot run with HRX0 in the graph at all.

The condition now lives in one place, `ggml/src/ggml-hrx/rope-claim-params.h`: the two matcher predicates plus
their union. Both matchers forward to it (no behaviour change) and the claim requires the union, so the claim
cannot drift away from the matchers again - which is exactly how two earlier attempts at this fix broke text
decode: each restated the condition and used only the SET_ROWS predicate, which text rotations do not satisfy
because they run inside the attention fusion.

Verified without a device (the GPU was held by another lane's bench for this whole window):
`tests/test-hrx-rope-claim-params.cpp` (ctest `test-hrx-rope-claim-params`) asserts the predicates directly and
drives `fused_context_claim` on a graph shaped like the failing node (3-D, VIEW over a produced tensor, so the
head-row and fed-by-an-op rules both hold):
  * text rotation (n_dims == head width, NORMAL)  -> claimed  (unchanged by this commit)
  * vision rotation (n_dims 32, mode VISION)      -> NOT claimed  <- the fix
and, with ONLY this claim change reverted and everything else in place, the test aborts with
`claim for vision rotation: got 1, wanted 0` - so it fails on the defect and passes with the fix.

Still blocking the rest of ggml-org#1467: with the projector on HRX the next refused node is a pure-alias VIEW
(`unsupported HRX node 20: VIEW f32[2048,2,1,1] <- VIEW`), which is claimed by the standalone path rather than
by a fused claim. That is a separate change and is not in this commit.

Refs ggml-org#1467

* hrx: say which relation a refused layout-alias node has, and pin alias elision in a host test

engine ggml-org#1467's projector is refused at a node the log names only by shape:

  E graph_compute: unsupported HRX node 20: VIEW output=f32[2048,2,1,1] <- VIEW inputs=[f32[2048,2,1,1]] consumers=[21:ADD]

A layout-alias node is refused there for one of two completely different reasons and the shape cannot tell them
apart: it IS an alias (one input, same storage as its input) whose input was not available when the node was
visited, or it is not aliasing its input at all and therefore needs a dispatch that no registration provides. The
message now says which:

  ... consumers=[…] alias=same_storage inputs=1
  ... consumers=[…] alias=not_aliasing_input inputs=2

`alias=` is `is_layout_alias_node(graph, node)` — the helper the elision path itself calls twelve lines below, not a
re-derivation of it. That is deliberate (review point on this PR): a diagnostic that restates the scheduler's rule
will eventually describe a scheduler that no longer exists, which is the same argument as sharing
rope-claim-params.h between the claim and its matchers in #257. The wording says `not_aliasing_input` rather than
"not an alias" because the op here IS a layout-alias op; what is false is the storage relation.

Alongside it, `tests/test-hrx-view-alias-elision.cpp` (ctest `test-hrx-view-alias-elision`, links ggml-hrx) builds
the graph by hand and pins four storage relations, host-only, no backend:

  no-op alias, external input : plans        <- what can_execute_standalone_op_as_graph builds
  no-op alias, produced input : plans        <- the real graph
  offset alias, produced input: plans
  strided view, produced input: REFUSED      <- and the refusal lands on the CONSUMER, not the view

The first two lines are the useful ones: a lone node's input provenance does NOT decide elision, which refutes the
reading that the projector's refusal comes from value_is_available() returning true for a producer-less value
(dispatch-scheduler.cpp:102-106). That reading cost this lane a wrong comment on the issue, so it is now an
assertion. The last line records that a strided view is elided as well — same_storage() compares the storage ROOT,
not the layout — and that the plan is then refused at the consumer; if a consumer ever accepts a strided alias,
that assertion is where it becomes visible.

Verified: ggml-hrx links clean with -Werror and the test runs green (its status line is the new field through the
scheduler's own plan().status.errors()). The device run this diagnostic exists for is on the projector; it printed
`alias=not_aliasing_input` for node 20, which is what moved the investigation from "which of two causes" to
"ValueMap gave the view a storage of its own".

Refs ggml-org#1467

* hrx: say WHICH alias lookup missed, not just that one did

`alias=not_aliasing_input` (previous commit) says a layout-alias node's storage is not its input's. On
engine ggml-org#1467's text tower that is where the investigation stops, because two very different causes print the
same word and they need different repairs:

* `view_src_unregistered` — the `ggml_tensor` the node's `view_src` points at is not a value in this graph, so
  `find_alias_source`'s pointer lookup cannot hit and the storage-root fallback has nothing to match. Repair:
  register the value, or resolve the alias by storage *range* rather than by tensor-pointer identity.
* `view_src_registered_but_storage_differs` — it WAS registered and the map still gave the view a storage of
  its own. Repair: that is a bug in the assignment itself, not in what the graph contains.

Both now print distinctly:

  ... alias=not_aliasing_input(view_src_unregistered) inputs=1
  ... alias=not_aliasing_input(view_src_registered_but_storage_differs) inputs=1

`find_tensor()` is public and `Value::tensor` is public (`graph/value-map.h:100-101`, `:84`), so this asks the
same map the assignment asked, rather than re-deriving anything.

Why it matters: the difference decides whether the next change is "register the missing value" or "match by
storage range". Guessing wrong costs a device run each time, and this lane has already spent several.

Coverage, stated plainly: the new branch is NOT exercised by `test-hrx-view-alias-elision` — every case there
fails *before* the message is built (the strided case is refused at its consumer), and `unsupported_node_message`
is file-local so the test cannot call it. What the test does show is that the change does not alter the four
relations it pins. Exercising the new branch needs the projector on a device.

Stacked on the previous commit (`1bit/1467-view-alias-diag`); merge that first.

Refs ggml-org#1467

* hrx: print the two storages and the view's layout for a refused alias node

Six hypotheses about engine ggml-org#1467's refused `VIEW` have died under their own tests (input provenance, offsets,
strides, an unregistered view_src resolved by storage range, producer coverage, and the claim). Each one was tested
by building the case it predicted and watching it not reproduce. That is the signal to stop predicting and print the
two storages.

For a layout-alias node whose storage is not its input's, the message now also carries:

  storages=out7/in3 nb=[4,8192,16384,32768] offs=0

* `storages=outN/inM` - the two `ValueStorageId`s. Equal ids with `alias=not_aliasing_input` would be a contradiction
  (the map agreed and the message says it did not); different ids are the fact every hypothesis has been guessing at;
* `nb=[...]` and `offs=` - what layout the view describes. A view whose strides differ from its input's same-shape
  tensor is a strided alias, which is a different thing from "the map gave it a storage".

With the `alias=` and `why=` fields from the two commits below, one run now answers: which storage the map gave the
view, whether it is its input's, whether the view's own `view_src` was registered, and what layout it describes.

Coverage, stated plainly: like the previous commit, this field is not exercised by
`test-hrx-view-alias-elision` - the host cases fail before a message is built, and `unsupported_node_message` is
file-local. What the test does show is that the four pinned relations and the added case still behave.

Stacked on `1bit/1467-alias-reason` (#262), itself stacked on #258 and #257. Merge in that order.

Refs ggml-org#1467

* tests: reproduce engine ggml-org#1467's refused VIEW on the host, with the run's own numbers

The projector run printed the facts this case encodes:

  VIEW output=f32[2048,2,1,1]  nb=[4,32768,65536,65536]  offs=8192
  alias=not_aliasing_input(view_src_unregistered)  storages=out24/in11

Rows of 2048 f32 taken from a buffer whose rows are 8192 f32 apart (`nb[1]` = 32768 bytes = 4x the width), starting one
row in (`offs` = 8192 bytes = 2048 f32), and the wide source is never registered as a value in the graph - which is
why `find_alias_source`'s two tensor-identity lookups both miss and the view is given a storage of its own.

`ViewKind::WideWindow` builds exactly that shape, so `test-hrx-view-alias-elision` now reproduces the refusal on a
machine with no device:

  no-op alias, external input : plans
  no-op alias, produced input : plans
  offset alias, produced input: plans
  strided view, produced input: REFUSED    <- refused at its CONSUMER
  wide window view (node 20)  : REFUSED    <- refused AT THE VIEW, as the projector is

The assertion is `REQUIRE(!wide)`: that is today's behaviour and it is the defect. The fix is a densify dispatch for a
layout-alias node that cannot be elided (copy the strided window into a contiguous transient - `dispatch-copy.cpp`'s
append_f32_densify_dispatch already does this for permuted activations), and when it lands this line flips to
`REQUIRE(wide)` - which makes the change visible in the test rather than inferred from a log line.

Two traps for whoever writes it, recorded here because both produce a WRONG NUMBER rather than a failure: the copy
must derive its source offsets from nb[]/offs (a densify that reuses the pre-densify plane offsets copies the wrong
rows), and the transient must hold ne[0]*ne[1] contiguous elements of the same type.

Refs ggml-org#1467

* hrx: drop the merge markers the previous commit carried, keeping this branch's superset block

The pin has this message code as #258 landed it; this branch has it with the why=/facts additions, so the superset is the
resolution and nothing from the pin is lost. The previous merge commit carried the markers themselves, which is why the
tree did not compile - caught by rebuilding before pushing, not by review.

* hrx: take the pin's AliasMiss block back, and re-apply only the facts on top of it

The previous merge resolved this hunk to *this branch's* older version, which silently removed what the base added
deliberately: the `AliasMiss` switch, i.e. the miss cause read from **assignment-time state**. That is the half an
earlier review called blocking, and my resolution brought the forbidden re-ask of the map back with it. The reviewer
caught it; this commit fixes it.

Resolution now, per file:
- `dispatch-scheduler.cpp`, `value-map.{h,cpp}`, `graph-diagnostics.cpp`: **the pin's versions** (AliasMiss +
  NoAliasSource/ExactViewSrc/RangeFallback, recorded when the storage is chosen);
- and on top of the pin's block, only this branch's own addition: `storages=outN/inM nb=[…] offs=…`, which is the
  fact the six dead hypotheses needed. `why=` still comes from the pin's `AliasMiss`, never from a re-ask.
- `<cinttypes>`/`<cstdio>` added for PRId64/snprintf, which the pin's file did not need.

Rebuilt before committing: `ggml-hrx` links clean with -Werror, and the host test still pins the four relations plus
`wide window view (node 20) : REFUSED`.

Refs ggml-org#1467

---------

Co-authored-by: bcloud <bcloud@users.noreply.github.com>
bong-water-water-bong pushed a commit that referenced this pull request Nov 11, 2026
…d (engine ggml-org#2068)

The test did not compile from #262 until #293, so host rows drifted from changes
that were made on purpose. #294 repairs the flash-attention row; these are the
other two:

- qwen_matmul_dispatch: #207 runs a batched dense MUL_MAT as one dispatch per
  plane, so the 3-D f32 case is supported, not refused.
- unmatched_graph_diagnostics: #196's standalone SET_ROWS takes the i32-index
  graph the row used as its unmatched example. The row now uses a row width
  above that dispatch's limit, which every matcher still declines.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Nov 11, 2026
…d (engine ggml-org#2068) (#295)

* tests: hrx-backend-test compiles again after the alias_miss field (#262)

#262 added ValueMap's alias_miss field before the optional buffer binding. Two
aggregate initialisers in hrx-backend-test still passed std::nullopt in that
position, so the test has not compiled since. They now pass AliasMiss::None.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* tests: hrx-backend-test expects the transposed V's spread pitch (engine ggml-org#1989)

qwen_flash_attention_dispatch still required the transposed V to be keys x columns long with
value_layout 1. Since llama.cpp #289/#291 the buffer has the spread pitch (keys + 32 when the key count is
a multiple of 64), the transpose kernel is told the pitch and the attention kernel derives it
(value_layout 2). The dispatch is right; the expectation was stale, and a compile error in this file
(fixed by #293) hid it. The check now asserts the pitch, the packed source binding, the transpose
kernel's output_pitch and that the pitch is not a compile parameter of the attention kernel.

host: 63 passed, 3 failed -> 64 passed, 2 failed (the two left, :1858 and :6333, are not from this change).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* tests: hrx-backend-test's two other stale host rows follow the backend (engine ggml-org#2068)

The test did not compile from #262 until #293, so host rows drifted from changes
that were made on purpose. #294 repairs the flash-attention row; these are the
other two:

- qwen_matmul_dispatch: #207 runs a batched dense MUL_MAT as one dispatch per
  plane, so the 3-D f32 case is supported, not refused.
- unmatched_graph_diagnostics: #196's standalone SET_ROWS takes the i32-index
  graph the row used as its unmatched example. The row now uses a row width
  above that dispatch's limit, which every matcher still declines.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

---------

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Nov 11, 2026
…st check my #289 left stale; engine ggml-org#1989) (#294)

* tests: hrx-backend-test compiles again after the alias_miss field (#262)

#262 added ValueMap's alias_miss field before the optional buffer binding. Two
aggregate initialisers in hrx-backend-test still passed std::nullopt in that
position, so the test has not compiled since. They now pass AliasMiss::None.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

* tests: hrx-backend-test expects the transposed V's spread pitch (engine ggml-org#1989)

qwen_flash_attention_dispatch still required the transposed V to be keys x columns long with
value_layout 1. Since llama.cpp #289/#291 the buffer has the spread pitch (keys + 32 when the key count is
a multiple of 64), the transpose kernel is told the pitch and the attention kernel derives it
(value_layout 2). The dispatch is right; the expectation was stale, and a compile error in this file
(fixed by #293) hid it. The check now asserts the pitch, the packed source binding, the transpose
kernel's output_pitch and that the pitch is not a compile parameter of the attention kernel.

host: 63 passed, 3 failed -> 64 passed, 2 failed (the two left, :1858 and :6333, are not from this change).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

---------

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Nov 11, 2026
…) (#293)

#262 added ValueMap's alias_miss field before the optional buffer binding. Two
aggregate initialisers in hrx-backend-test still passed std::nullopt in that
position, so the test has not compiled since. They now pass AliasMiss::None.

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
@bong-water-water-bong
bong-water-water-bong deleted the 1bit/1467-alias-reason branch November 12, 2026 21:17
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.

2 participants