Skip to content

Merge halo-box/llama.cpp master (catch up 68 commits) - #19

Merged
Nathanw1014 merged 69 commits into
masterfrom
rebase/from-halobox-2026-09-03
Sep 4, 2026
Merged

Merge halo-box/llama.cpp master (catch up 68 commits)#19
Nathanw1014 merged 69 commits into
masterfrom
rebase/from-halobox-2026-09-03

Conversation

@dzannotti

@dzannotti dzannotti commented Sep 3, 2026

Copy link
Copy Markdown

Catches this fork up with 68 commits from its parent, halo-box/llama.cpp.

strix/master    732484c20c8c
halobox/master  ca062e9f32f3

Conflicts

Two, both the same shape — our side of the hunk was empty and the parent adds lines, so the incoming text is taken as-is:

file what came in
ggml/src/ggml-backend.cpp one ggml_is_quantized() condition ([TAG_ALLOC_SIZE_EXPAND])
ggml/src/ggml-metal/ggml-metal-tuning.cpp Metal M2 tuning tables flowing through from upstream — irrelevant to this part, harmless

Compile-checked (llama-server, CPU backend) against exactly those two bases.

What is preserved

This fork's own eight commits are untouched: RDNA3.5 batched mat-vec chunking (#1), the Q8_1 matvec fix (#10), DFlash draft block widths (#4), both reasoning-budget commits (#5, #8), the CI change (#12) and the docs (#6).

harkgill-amd and others added 30 commits August 27, 2026 22:18
* Add ctx-per-slot argument for unifid KV cache

* Swap out ctx fractions for ctx pool slots

* Formatting cleanup

* Remove ctx-pool-slots, make ctx-per-slot an int

* refactor it

---------

Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
Add HVX-accelerated implementations for GGML_OP_LOG and
GGML_UNARY_OP_ABS on the HTP backend.

- Register HTP_OP_UNARY_ABS and HTP_OP_UNARY_LOG in op_remap_to_htp()
- Add ABS and LOG to ggml_backend_hexagon_device_supports_op()
- Implement hvx_abs_f32_aa() in hvx-arith.h using hvx_vec_abs_f32()
- Implement hvx_log_f32_aa() in hvx-log.h using hvx_vec_log_f32()
- Add abs_f32() and log_f32() row-wise dispatch in unary-ops.c
- Define tiled and non-tiled task functions via DEFINE_UNARY_TASK and
  DEFINE_UNARY_TILED_TASK macros
- Route HTP_OP_UNARY_ABS and HTP_OP_UNARY_LOG through execute_op()
  in main.c
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* model: add DSpark support for Nemotron3.5

* Update src/models/dflash.cpp

Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>

---------

Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
…7755)

* tests : run test-save-load-state across all architectures

test-save-load-state previously only ran in ctest against a single
downloaded model (tinyllamas/stories15M), i.e. only the llama arch.

Add a --models DIR mode to test-save-load-state that runs the full
save/load suite over every *.gguf in a directory, reporting a
per-model PASS/FAIL and exiting non-zero if any model fails, and wire
a ctest to run it over all architectures using the existing
generate-models fixture (test-llama-archs). The single-model -m mode
is preserved (still used by ci/run.sh).

Also bump the dummy-model training context in test-llama-archs from
128 to 256 so that the per-sequence context (which is padded up to a
multiple of 256) no longer exceeds n_ctx_train and emits the
"possible training context overflow" warning.

The test is expected to fail until the affected arches are fixed:
deepseek4 (host seq-copy), gemma2/gpt-oss/lfm2 (device seq-copy),
minimax-01 (state load). It aborts at the first arch that crashes.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* tests : match dummy DSA indexer to fused Lightning Indexer kernel

The dummy DSA indexer (deepseek32, glm-dsa, ...) used key_length=64 and head_count=1, so the fused Lightning Indexer op's q tensor was shaped [64, 1, ...]. The Metal fused kernel is fixed to DK=128, NH=64, so it rejected the op and the scheduler fell back to CPU, emitting a 'layer assigned to MTL but Lightning Indexer on CPU' warning. Bump key_length to 128 and the DSA head_count to 64 so the fused op runs on the GPU.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* tests : add --help and document -o in test-llama-archs

Add a --help/-h flag to test-llama-archs and list the existing -o/--out option in the usage text, which was previously missing.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* tests : use 64 indexer heads for deepseek4

deepseek4's indexer head count was set to n_head (8), which does not match the fused Lightning Indexer kernel's fixed NH=64, so the fused op fell back to the CPU backend and emitted a device-mismatch warning. Give it the same fixed 64 as the other indexer archs by dropping it from the n_head ternary (only minimax-m3 keeps n_head, since it does not use the fused Lightning Indexer op).

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* tests : fix dsv4 save-load n_stream mismatch

The dsv4 KV cache keeps per-sequence KV/state streams even in unified mode, so its n_stream equals n_seq_max. The test saved the state in the baseline with n_seq_max=1 but loaded it in the seq-copy tests with n_seq_max=2, so state_read threw an n_stream mismatch. Use n_seq_max=2 in the baseline and state-load tests so the save and load agree.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* context : relax on-device seq-copy chunk alignment

The on-device state seq copy (llama_state_seq_set_data with LLAMA_STATE_SEQ_FLAGS_ON_DEVICE) copied the write-side cpy tensors to the read-side targets 1:1 by index, requiring the writer and reader to emit the same number of chunks in the same order with the same per-chunk sizes. state_write_data chunks per cell-range while state_read_data chunks contiguous-or-per-cell, so the counts diverged for non-contiguous sources (dsv4, SWA) and the copy aborted with "memory buffer mismatch".

All state writers and readers enumerate the same logical data in the same order, differing only in chunking. Copy the flat write-side data into the read-side targets with a byte cursor that walks both tensor lists across their boundaries, so the chunking no longer needs to match. Keep the total-size guard; drop the n_tensors equality check.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* model : fix dangling hparams ref in minimax-01 LA graph input
llm_graph_input_la stored const llama_hparams & hparams, bound to the llm_graph_params temporary in llama_context::process_ubatch. The input object outlives that temporary (it is kept in llm_graph_result::inputs for graph reuse), so set_input() read destroyed stack memory on every graph reuse - test-save-load-state crashed for minimax-01 when the stack region was overwritten (n_layer_all read as 0, abort in llama_hparams::n_head). Store a copy like every other graph input class.
Assisted-by: pi:llama.cpp/Qwen3.8-27B

* context : handle "worst case" graph and add TODO
This is a followup contribution to efeda76 as requested in ggml-org#27668 to add support for additional Apple GPUs. I generated this output using the provided instructions:

```sh
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp

cmake -B build -DGGML_METAL=ON
cmake --build build --target ggml-metal-tuning -j

./build/bin/ggml-metal-tuning fa-vec --dtype f16,q8_0 > fa_vec_rows.txt 2> fa_vec_sweep.log
```

This ran on a MacBook Pro (14-inch, Nov 2024) with Apple M4 Pro. The `ggml-metal-tuning` command completed successfully in 1h 13m 1s with no other notable load on the system.
…#6)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* metal : add fa-vec tunings for M5

This is a followup contribution to efeda76 as requested in ggml-org#27668 to add support for additional Apple GPUs. I generated this output using the provided instructions:

```sh
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp

cmake -B build -DGGML_METAL=ON
cmake --build build --target ggml-metal-tuning -j

./build/bin/ggml-metal-tuning fa-vec --dtype f16,q8_0 > fa_vec_rows.txt 2> fa_vec_sweep.log
```

This ran on a machine with Apple M5.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* metal : add fa-vec tunings for M5 Pro

This adds fa_vec_tuned_table records for Apple M5 Pro to ggml-metal-tuning.cpp.

Contributed by SerayaEryn in ggml-org#27668 (comment) (F16, Q4_0, Q8_0; M5 Pro, 20 GPU cores).

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* metal : add fa-vec tunings for M3 Max

This adds fa_vec_tuned_table records for Apple M3 Max to ggml-metal-tuning.cpp.

Contributed by TeeAaTeeUu in ggml-org#27668 (comment) (F16, Q8_0; M3 Max, MacBook Pro 64GB, low power mode).

Assisted-by: pi:llama.cpp/Qwen3.8-27B

* cont : whitespaces
…rg#27468)

Measured at a live KV length of 34816 (32768 depth plus one 2048 ubatch),
on Qwen3.8 27B Q4_K_S:

  per tensor         4 * 34816 * 256 * 2 B  =  71.3 MB
  staged per call    K and V, so 2x         = 142.6 MB
  traffic per call   read once, write once  = 285.2 MB
  traffic per ubatch 285.2 MB * 16 calls    =   4.56 GB

One ubatch is one ggml_cgraph submission (llama_context::process_ubatch ->
graph_compute), so that 4.56 GB is the cost of a single 2048-token prefill
chunk, and it scales with the live KV length: the first ubatch of the same run,
at seq = 2048, moves 0.27 GB.

Reproduce the two measured inputs with:

  GGML_SCHED_DEBUG=2 llama-bench -m MODEL -p 8 -n 0 -r 1 -ngl 0 \
      -fa on -ctk f16 -ctv f16 -v > nd.txt 2>&1
  grep -E 'n_layer|n_head_kv|n_embd_head_k' nd.txt
  awk '/node #  0 /{g++} g==1 && /\(FLASH_ATTN\)/{n++} END{print n+0}' nd.txt
Route quantized KV decode to TILE on Xe2 (BMG) only, keep VEC on other archs until validated there.
… and new ops (ggml-org#27843)

* OpenVINO Backend: Fuse IM2COL + MatMul convolution into OpenVINO convolution

* ci:ggml-ov: Skip recurrent state rollback tests

* ci:ggml-ov: Skip recurrent state rollback tests

* Update OPENVINO.md

* ggml-openvino : add env-var gated op support debugging

* Fix ggml_rope_set_offset case

* OpenVINO backend: Support Whisper.cpp

* Fix code style

* openvino : enable qwen35 on NPU

Static shapes:
- get_graph_input_shape() left the s_copy / s_copy-leaf inputs dynamic
  ([1,1,1,-1]) even in static mode, which propagated a dynamic slot dim through
  GET_ROWS into the conv/GDN state, the state reshapes and the GDN output.
- With -np 1 the s_copy defrag remainder gathers zero rows; short-circuit that
  CPY to the untouched cache instead of emitting a degenerate Slice/Concat, and
  skip binding its zero-byte ggml tensor as an output (the dynamic path already
  did the latter, the static path wrote the full cache over a 0-byte buffer).

Token-count independence:
- In static mode the compiled model's token count is the prefill chunk size or
  1, not the captured cgraph's. Offsets derived from the captured count were
  therefore wrong. Anchor the GDN state slice at the end of the packed
  [attn | state] output and drop the rs_src_begin runtime inputs, and make
  VIEWs over the GDN output / conv_input pass through so the consumer does the
  slicing.
- CONT could not identify its token axis when the graph was captured with a
  single token (every trailing dim has the same stride and size 1) and baked
  the captured shape into the prefill model.

Chunked prefill:
- The last chunk is padded with fabricated tokens. Attention masks them, but
  the recurrent path folded them into cache_r/cache_s permanently. Add a
  chunk_valid_len runtime input, use it to zero g and beta for padded steps
  (making the recurrence an exact identity) and to end the conv snapshot window
  at the last valid token, and disable the recurrent-cache reset after the
  first chunk so earlier chunks are not wiped.
- get_is_prefill() and the chunk loop bound read inp_pos->ne[0] directly, but
  IMROPE stacks 4 position planes, so every decode step was run through the
  padded prefill model and the loop ran extra out-of-bounds chunks.

cache_rs_reset_idx/len now stay runtime Parameters in static mode, since
can_reuse_statically() does not invalidate the cached model on ComputeParams
changes. Add GGML_OPENVINO_FORCE_STATIC to exercise the static path on CPU.

* Update to OpenVINO 2026.3.1

* ggml-openvino: forward NPU compilation mode parameters

Add GGML_OPENVINO_NPU_COMPILE_CONFIG to the backend's cached environment so callers can configure the NPU compiler without using the generic property escape hatch.

When the value is non-empty, pass it to OpenVINO as NPU_COMPILATION_MODE_PARAMS. This enables settings such as optimization-level=3 for NPU compilation while preserving the existing behavior when the variable is unset and leaving CPU and GPU configuration unchanged.

Document the variable, its NPU-only scope, and the optimization-level=3 example in the OpenVINO backend runtime configuration table.

* ggml-openvino : support RELU, POOL_2D, QUICK_GEGLU, and ROLL ops

* reorder op table

* exclude GPU/NPU failing POOL_2D case

* move op type detection to compute_op_case

* Relax rope supported cases

* Fix pool case

* Update openvino doc, gpu driver in ov docker

* openvino: remove unused static remote context branch

* openvino: parallelize static model build

* Apply editorconfig

---------

Co-authored-by: Mostafa Faheem <mostafaaafaheem@gmail.com>
Co-authored-by: Ravi Panchumarthy <ravi.panchumarthy@intel.com>
Co-authored-by: zhaixuejun1993 <xuejun.zhai@intel.com>
This adds fa_vec_tuned_table records for Apple M4 to ggml-metal-tuning.cpp.

Includes F16, Q4_0, Q4_1, Q5_0, Q5_1, and Q8_0. (M4, 10 GPU Cores)

Co-authored-by: Strongtut <8432058+Strongtut@users.noreply.github.com>
…gml-org#26686)

* vulkan: add hoisting support for row IDs and expert count in shaders

* use hoisted row ids in coopmat2

* vulkan: address review feedback on count_experts
- use vk_op_count_experts_push_constants instead of a raw uint vector
- apply the fastdiv trick to the ne00 div/mod in count_experts
- compute the per-expert offsets with subgroupExclusiveAdd when the
  device supports it, keeping the serial path as fallback
- document the data_d layout and the hoisted_row_id_words bound
- drop a leftover debug print in ggml_vk_matmul_id

* vulkan: use init_pushconst_fastdiv for count_experts push constants

* vulkan: refine comments for row ID hoisting and data layout in count_experts shader

* Whitespace

---------

Co-authored-by: Jeff Bolz <jbolz@nvidia.com>
* ggml : fix conv_transpose_2d for multiple batches

ggml_compute_forward_conv_transpose_2d_impl only computed the first
batch (ne[3] of the destination); every batch after the first was left
as zero. Both the src1 permutation and the main compute loop now iterate
over the batch dimension, and the work buffer size in ggml_graph_plan is
scaled by the src1 batch count so the extra permuted batches fit. A
multi-batch test case is added to test-backend-ops.

Fixes ggml-org/ggml#1448

* metal : fix conv_transpose_2d for multiple batches

The kernel only computed batch 0 of the input (src1->ne[3]); every
output batch after the first was left as zero, so multi-batch
conv_transpose_2d results diverged from the CPU reference.

The grid now covers all batches (OW x OH x OC x N), the kernel decodes
the batch from the grid z coordinate and offsets both the input and
destination indices accordingly. nb3 is passed in the kernel args.

Assisted-by: pi:llama.cpp/Qwen3.8-27B

---------

Co-authored-by: Georgi Gerganov <ggerganov@gmail.com>
…ggml-org#27812)

* vulkan: fix missing view-alias dependencies in ggml_vk_graph_optimize

is_src_of doesn't treat two views of one tensor as dependent, so the optimizer reorders nodes across aliased reads and writes. 

Result: silently wrong tokens under greedy decoding, different output on every server start, and invalid speculative-decoding acceptance, with nothing logged.

Hits Qwen3.8's recurrent state (and any model with view-aliased state) on AMD and NVIDIA Vulkan.  CUDA is clean. 

Compare view_src bases on both sides.

Fixes ggml-org#27805

* vulkan: don't treat view/no-op nodes as aliasing dependencies

Nodes whose op is NONE, RESHAPE, TRANSPOSE, VIEW or PERMUTE execute nothing, so aliasing through them is not a real dependency. The previous base comparison matched them anyway, which only costs the optimizer reordering freedom.

Co-authored-by: Jeff Bolz <jbolz@nvidia.com>

* vulkan: make the lambda parameter const and capture is_empty in is_src_of

Code will not compile without these changes.  
is_src_of has an empty capture list, so is_empty was not visible inside it, and is_empty took a non-const pointer, while is_src_of receives const ones. Other call sites pass non-const pointers, which still convert as usual.

---------

Co-authored-by: Jeff Bolz <jbolz@nvidia.com>
* bench: add --tensor-read-lazy

* rm the alias

* rename to LLAMA_LAZY_MODE_*
* port setup-build.ps1 to setup_sdk.py, to facilitate installation of Hexagon and OpenCL SDKs on Windows

* rename setup_sdk.py -> setup-sdk.py

* flake8 fix: print() -> logger.info()

---------

Co-authored-by: Kristopher Urquhart <kurquhar@qti.qualcom.com>
The N padding is needed for mul_mat, but not mul_mat_id. For mul_mat_id,
we indirect the row index through a shared memory lookup table which avoids
any OOB row coordinate. But that callback doesn't bounds check K, so we
actually need K padding instead.
…ng small divs (ggml-org#27526)

* vulkan: combine duplicated fastdiv functions, rename the one optimizing small divs

* remove one more fastdiv
improve the --fit algorithm to take into account the actual peak
required VRAM for a given context size on a SYCL backend.

This includes both properly accounting for how much VRAM is required
when the allocated context is fully used (which makes the reported
context drop below what it did before, but stop it OOMing) as well
as preventing some overly-conservative calculations which meant too much
VRAM was being reserved.

Tested on a Arc b70 with unsloth's qwen3.8 (Q4_K_XL), able to get 262144 context,
fully usable, with q8_0 KV and MTP and 4k ubatch size using --fit-target 1
ServeurpersoCom and others added 17 commits August 30, 2026 17:27
…g#28011)

for_each_token_in tested all LLAMA_MAX_SEQ sequences for every used cell,
while a cell almost always belongs to one. The scan now stops once the
cell's own sequences have been seen. Same visit order, same callback
arguments, so behaviour is unchanged.

get_prev_tokens is the only caller, so this affects the n-gram path.

RTX PRO 6000, Qwen3.8-Flash-Next UD-Q4_K_XL, fa on, warm runs:

  55k context    generation 56.3 -> 74.3 t/s
  132k context   generation 33.6 -> 50.9 t/s

Prompt processing is unchanged, the scan is amortised over the ubatch
there. The gain follows the number of used cells, so it grows with
context and is invisible on short prompts.
* rpc: avoid serializing buffers from other servers

Only include remote buffer pointers when the buffer belongs to the RPC dispatcher receiving the graph. Add a two-server regression test for cross-server tensor serialization.

Assisted-by: Codex

* cont : add ref

---------

Co-authored-by: Georgi Gerganov <ggerganov@gmail.com>
Co-authored-by: Marshall <assistant@llama.cpp>
#11)

Co-authored-by: Marshall <assistant@llama.cpp>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#13)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…UM_COLS > 4) (#15)

Co-authored-by: Marshall <assistant@llama.cpp>
Co-authored-by: Marshall <assistant@llama.cpp>
A small draft model prefills the prompt and decodes a few lookahead tokens. The
softmax attention of those tokens over the prompt is captured with a ggml eval
callback, smoothed, max-reduced over heads and layers and pooled into chunks.
Only the top scoring chunks go into the target model's KV cache. This is lossy.

Adds llama_set_eval_callback, common/speculative-prefill, the --spec-prefill-*
options, the standalone example and the llama-server hookup. Not ported from
that PR: the llama-bench changes and the python eval scripts.

Measured on Strix Halo / Vulkan with a Qwen3.5-2B estimator, TTFT at p = 0.15:
Qwen3.8-27B 5604 -> 1587 ms, Flash-Next FP4 4920 -> 1645 ms, and 54.9 -> 11.9 s
on a 12k prompt. Decode rate is unchanged. See docs/speculative-prefill.md.

Assisted-by: Claude Opus 5
Co-authored-by: Marshall <assistant@llama.cpp>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Marshall <assistant@llama.cpp>
Catches up 68 commits from the parent fork.

  strix/master   732484c
  halobox/master ca062e9

Two conflicts, both the same shape: our side of the hunk was empty and the parent adds
lines, so the incoming text is taken as it is.

  ggml/src/ggml-backend.cpp             one ggml_is_quantized() condition
                                        ([TAG_ALLOC_SIZE_EXPAND])
  ggml/src/ggml-metal/ggml-metal-tuning.cpp
                                        Metal M2 tuning tables flowing through from
                                        upstream; irrelevant to this part but harmless

Compile-checked (llama-server, CPU backend) against exactly these two bases.

This fork's own eight commits are untouched: the RDNA3.5 batched mat-vec chunking, the
Q8_1 matvec fix, DFlash draft block widths, both reasoning-budget commits, the CI change
and the docs.

Note this only reaches the parent fork's level. halo-box/llama.cpp is itself 80 commits
behind ggml-org and its own catch-up is blocked on a top-k reconciliation: #17 and
upstream have independently implemented radix top-k for large k, with different shaders,
different binding counts (2 vs 5) and different push-constant structs, and upstream has
since deleted ggml_vk_topk_small entirely.

Claude-Session: https://claude.ai/code/session_01JDKcT3SjBYaJKmpRJqTPmq

@Nathanw1014 Nathanw1014 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed as the catch-up it is: mergeable against master, every check that ran is green (four are stuck in the runner queue since yesterday, not failures), and the head builds and passes our repeatability gates on gfx1151. #20 follows on top and replaces the EngramHalo radix top-k with upstream's ggml-org#28032 plus the deterministic slot assignment, so the two radix implementations never coexist on master.

@Nathanw1014
Nathanw1014 merged commit bcfcfc1 into master Sep 4, 2026
13 of 17 checks passed
@Nathanw1014
Nathanw1014 deleted the rebase/from-halobox-2026-09-03 branch September 4, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.