Commit a1ad97f3 for whisper.cpp

commit a1ad97f301da4b3f210f5040ba7f81b6147f0c10
Author: Aman Gupta <amangupta052@gmail.com>
Date:   Tue Nov 3 16:51:10 2026 +0800

    meta: support GLM-5.3 next sm tensor (llama/30499)

    * meta: support GLM-5.3 next sm tensor

    * glm5-next : cont q_absorbed for -sm tensor with non-unified KV

    * meta : keep GGML_OP_ARANGE in ggml.h order

    * glm5-next : split MLA heads in pairs for -sm tensor

    The MLA cache has a single KV head, so the heads on a device are its GQA
    ratio. The HIP tile FA kernel for D=512 needs an even GQA ratio; with 3
    devices the 16 heads of the test model were split 5/5/6 and it aborted.

    * meta : keep the split for views inside one slab of the split axis

    With a non-unified KV cache, build_attn_mha splits q into streams with a
    view. For a permuted q, e.g. the absorbed query of glm5-next, that view
    aborted with "view of permuted tensor not implemented". The split is now
    kept if every other dim of the source and the view stays inside one slab
    of the split axis. The per-device strides are only rescaled above it.

    From tetherto/qvac-fabric-llm.cpp#353.

    Co-authored-by: Pratik Narola <pratik.narola@tether.io>

    * glm5-next : drop the q_absorbed cont

    The meta backend can now split the per-stream view of the permuted q, so
    the cont is not needed for -sm tensor with a non-unified KV cache. With
    the cont, the q of flash attention was not contiguous, and when FA ran on
    the CPU (WebGPU) the meta get_tensor aborted.

    ---------

    Co-authored-by: Pratik Narola <pratik.narola@tether.io>

diff --git a/ggml/src/ggml-backend-meta.cpp b/ggml/src/ggml-backend-meta.cpp
index c30bdae4..aba00464 100644
--- a/ggml/src/ggml-backend-meta.cpp
+++ b/ggml/src/ggml-backend-meta.cpp
@@ -489,6 +489,32 @@ static struct ggml_tensor * ggml_backend_meta_buffer_simple_tensor(const struct
     return it->second[index];
 }

+// bytes spanned by all dims except skip_dim
+static size_t ggml_backend_meta_inner_extent(const struct ggml_tensor * tensor, int skip_dim) {
+    size_t extent = 0;
+    for (int i = 0; i < GGML_MAX_DIMS; i++) {
+        if (i != skip_dim && tensor->ne[i] > 1) {
+            extent += (size_t) (tensor->ne[i] - 1) * tensor->nb[i];
+        }
+    }
+    return extent;
+}
+
+// the view dim that holds the split axis of src, or -1
+// all other dims of src and view must stay inside one slab of that axis
+static int ggml_backend_meta_view_slab_dim(const struct ggml_tensor * view, const struct ggml_tensor * src, int axis) {
+    const size_t slab = src->nb[axis];
+    if (ggml_backend_meta_inner_extent(src, axis) >= slab) {
+        return -1;
+    }
+    for (int dim = 0; dim < GGML_MAX_DIMS; dim++) {
+        if (view->ne[dim] == src->ne[axis] && view->nb[dim] == slab) {
+            return ggml_backend_meta_inner_extent(view, dim) < slab ? dim : -1;
+        }
+    }
+    return -1;
+}
+
 static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(const struct ggml_tensor * tensor, bool assume_sync);

 static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
@@ -723,6 +749,16 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
         if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED || src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_PARTIAL) {
             return src_ss[0];
         }
+        // a view that only regroups dims inside one slab of the split axis keeps the split,
+        // the per-device strides are only rescaled above nb[axis] (e.g. the KV streams of a permuted q)
+        if (axis >= 0 && axis < GGML_MAX_DIMS && tensor->view_offs == tensor->src[0]->view_offs) {
+            const int dim = ggml_backend_meta_view_slab_dim(tensor, tensor->src[0], axis);
+            if (dim >= 0) {
+                ggml_backend_meta_split_state ret = src_ss[0];
+                ret.axis = ggml_backend_meta_split_axis(dim);
+                return ret;
+            }
+        }
         GGML_ABORT("view of permuted tensor not implemented");
         //return {GGML_BACKEND_SPLIT_AXIS_UNKNOWN, {0}, {1}, 1};
     };
@@ -1020,8 +1056,13 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
                 split_state = handle_pad(src_ss);
             } break;
             case GGML_OP_PAD_REFLECT_1D:
-            case GGML_OP_ROLL:
-            case GGML_OP_ARANGE:
+            case GGML_OP_ROLL: {
+                split_state = handle_generic(src_ss, /*scalar_only =*/ true);
+            } break;
+            case GGML_OP_ARANGE: {
+                // no sources, every device computes the full range
+                split_state = {GGML_BACKEND_SPLIT_AXIS_MIRRORED, {0}, {1}, 1};
+            } break;
             case GGML_OP_TIMESTEP_EMBEDDING: {
                 split_state = handle_generic(src_ss, /*scalar_only =*/ true);
             } break;