Commit 6ed812057 for llama.cpp

commit 6ed812057cacb9e855582192ceeef08716e5ed76
Author: Aaron Teo <aaron.teo1@ibm.com>
Date:   Mon Nov 2 20:11:07 2026 +0800

    ggml-zdnn: share buffers with host (#31481)

    Signed-off-by: Aaron Teo <aaron.teo1@ibm.com>

diff --git a/ggml/src/ggml-zdnn/common.hpp b/ggml/src/ggml-zdnn/common.hpp
index 99dd56fca..7ed044f91 100644
--- a/ggml/src/ggml-zdnn/common.hpp
+++ b/ggml/src/ggml-zdnn/common.hpp
@@ -34,6 +34,8 @@ struct ggml_backend_zdnn_device_context {

     size_t max_size;

+    int32_t n_threads;  // CPU threads
+
     char name[128];
 };

diff --git a/ggml/src/ggml-zdnn/ggml-zdnn.cpp b/ggml/src/ggml-zdnn/ggml-zdnn.cpp
index b3536ee28..71ae85b3e 100644
--- a/ggml/src/ggml-zdnn/ggml-zdnn.cpp
+++ b/ggml/src/ggml-zdnn/ggml-zdnn.cpp
@@ -12,6 +12,7 @@
 #include <vector>
 #include <memory>
 #include <csignal>  // raise(SIGTRAP)
+#include <thread>
 #include <unistd.h>

 static void ggml_zdnn_compute_forward_mul_mat(
@@ -85,6 +86,15 @@ static enum ggml_status ggml_zdnn_graph_compute(ggml_backend_t backend, ggml_cgr
     GGML_UNUSED(ctx_dev);
 }

+static int64_t ggml_zdnn_mul_mat_min_batch(ggml_type type, int32_t n_threads) {
+    switch (type) {
+        case GGML_TYPE_Q8_0:
+            return std::max<int64_t>(1, n_threads - 2);
+        default:
+            return 1;
+    }
+}
+
 static bool ggml_zdnn_supports_op(const ggml_backend_zdnn_device_context * ctx_dev, const ggml_tensor * op) {
     switch (op->op) {
         case GGML_OP_NONE:
@@ -112,6 +122,11 @@ static bool ggml_zdnn_supports_op(const ggml_backend_zdnn_device_context * ctx_d
                         return false;
                 }

+                // offload smaller batches to the CPU
+                if (ne1 < ggml_zdnn_mul_mat_min_batch(weights->type, ctx_dev->n_threads)) {
+                    return false;
+                }
+
                 switch (weights->type) {
                     case GGML_TYPE_F32:
                     case GGML_TYPE_F16:
@@ -150,6 +165,7 @@ static ggml_backend_zdnn_device_context g_ggml_ctx_dev_main = {
     /* .has_parmblkformat_0   = */ false,
     /* .has_parmblkformat_1   = */ false,
     /* .max_size              = */ 0,
+    /* .n_threads             = */ 1,
     /* .name                  = */ "",
 };

@@ -164,6 +180,8 @@ static int ggml_backend_zdnn_device_acq(ggml_backend_zdnn_device_context * ctx)
         ctx->has_parmblkformat_0 = zdnn_is_nnpa_parmblk_fmt_installed(1, NNPA_PARMBLKFORMAT_0);
         ctx->has_parmblkformat_1 = zdnn_is_nnpa_parmblk_fmt_installed(1, NNPA_PARMBLKFORMAT_1);
         ctx->max_size = zdnn_get_nnpa_max_dim_idx_size();
+        // llama.cpp uses every core by default, and splits the first graph before it sets the threads
+        ctx->n_threads = std::max<int32_t>(1, (int32_t)std::thread::hardware_concurrency());
         strncpy(ctx->name, GGML_ZDNN_NAME, sizeof(ctx->name) - 1);
     }

@@ -452,8 +470,7 @@ static size_t ggml_backend_zdnn_buffer_type_get_alignment(ggml_backend_buffer_ty
 }

 static bool ggml_backend_zdnn_buffer_type_is_host(ggml_backend_buffer_type_t buft) {
-    /* while it resides in host memory, additional transformation is needed */
-    return false;
+    return true;

     GGML_UNUSED(buft);
 }
@@ -673,6 +690,9 @@ static void ggml_backend_zdnn_set_n_threads(ggml_backend_t backend, int n_thread

     ggml_backend_zdnn_context * ctx = (ggml_backend_zdnn_context *)backend->context;
     ctx->n_threads = n_threads;
+
+    ggml_backend_zdnn_device_context * ctx_dev = (ggml_backend_zdnn_device_context *)backend->device->context;
+    ctx_dev->n_threads = n_threads;
 }

 static void * ggml_backend_zdnn_get_proc_address(ggml_backend_reg_t reg, const char * name) {
diff --git a/ggml/src/ggml-zdnn/mmq.cpp b/ggml/src/ggml-zdnn/mmq.cpp
index 757a522c9..0c4472845 100644
--- a/ggml/src/ggml-zdnn/mmq.cpp
+++ b/ggml/src/ggml-zdnn/mmq.cpp
@@ -1,6 +1,7 @@
 #include "ggml.h"
 #include "mmq.hpp"
 #include "quantize.hpp"
+#include "utils.hpp"

 #include <algorithm>
 #include <future>
@@ -189,6 +190,11 @@ void ggml_zdnn_mul_mat_q(
     ggml_backend_zdnn_buffer * weights_extra = (ggml_backend_zdnn_buffer *)weights->extra;
     ggml_backend_zdnn_buffer * output_extra  = (ggml_backend_zdnn_buffer *)output->extra;

+    // weights read straight into a host buffer skip set_tensor, so transform them on first use
+    if (!weights_extra->ztensor.is_transformed) {
+        ggml_zdnn_load_tensor_q(weights_extra, weights);
+    }
+
     // starting threads costs more than it saves for small inputs, e.g. during token generation
     const int32_t n_threads = ne10*ne11 >= 65536 ? ctx->n_threads : 1;