From ccc1769a6a0c64212d7ae389e3ff76551994235a Mon Sep 17 00:00:00 2001 From: Mostafa Faheem Date: Thu, 27 Aug 2026 20:01:11 +0300 Subject: [PATCH 1/2] ggml-openvino : fix 2D/3D view input shape inference --- ggml/src/ggml-openvino/openvino/utils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ggml/src/ggml-openvino/openvino/utils.cpp b/ggml/src/ggml-openvino/openvino/utils.cpp index 8bb7678ee38..2b95c071a31 100644 --- a/ggml/src/ggml-openvino/openvino/utils.cpp +++ b/ggml/src/ggml-openvino/openvino/utils.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace ov { @@ -336,6 +337,15 @@ ov::Output process_view_input_new(const NodeContext & context, int inp } } + // some weight tensors have 2D/3D shapes which would be incompatible with the + // 4D slices in this utility. unsqueeze to 4D in order to avoid this issue. + ov::Rank rank = input.get_partial_shape().rank(); + if (rank.is_static() && rank.get_length() == 2){ + input = std::make_shared(input, ov::op::v0::Constant::create(ov::element::i64, {2}, {0, 1})); + } else if (rank.is_static() && rank.get_length() == 3){ + input = std::make_shared(input, ov::op::v0::Constant::create(ov::element::i64, {1}, {0})); + } + // In static mode, use Split instead of Slice for single-dimension reductions. // This ensures NPUW's FOLD doesn't parametrize per-layer slice indices (which // would introduce dynamic shapes). A shared Split node sits outside the repeated From e055c160f360556fca4741521ec76a08d08ac34e Mon Sep 17 00:00:00 2001 From: Mostafa Faheem Date: Fri, 28 Aug 2026 09:15:49 -0700 Subject: [PATCH 2/2] [no ci] enable mmproj attention pattern and fix reshape case --- ggml/src/ggml-openvino/ggml-decoder.cpp | 2 +- ggml/src/ggml-openvino/ggml-openvino.cpp | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/ggml/src/ggml-openvino/ggml-decoder.cpp b/ggml/src/ggml-openvino/ggml-decoder.cpp index 006e005cb7a..7fdf4e0fd0d 100644 --- a/ggml/src/ggml-openvino/ggml-decoder.cpp +++ b/ggml/src/ggml-openvino/ggml-decoder.cpp @@ -271,7 +271,7 @@ int GgmlOvDecoder::compute_op_case(const ggml_tensor * node) const { if (src->ne[2] * src->ne[3] == node->ne[1]) { op_case = 5; } - } else if (src->ne[0] * src->ne[1] * src->ne[2] == node->ne[1]) { + } else if (src->ne[0] * src->ne[1] * src->ne[2] == node->ne[1] && is_kvcache(src, node)) { op_case = 3; } else if (name.find("linear_attn_qkv_mixed") == 0 || name.find("alpha") == 0) { op_case = 6; diff --git a/ggml/src/ggml-openvino/ggml-openvino.cpp b/ggml/src/ggml-openvino/ggml-openvino.cpp index 4b1789713d1..3ad14cc8101 100644 --- a/ggml/src/ggml-openvino/ggml-openvino.cpp +++ b/ggml/src/ggml-openvino/ggml-openvino.cpp @@ -909,20 +909,32 @@ static bool has_non_contiguous_view_input(const ggml_tensor * op) { static bool is_supported_flash_attn_pattern(const ggml_tensor * op) { // Each Q/K/V input must follow one of: - // PERMUTE -> VIEW -> base (view_src==nullptr) (llama KV-cache path) - // PERMUTE -> RESHAPE -> base (view_src==nullptr) (whisper Q) - // VIEW -> base (view_src==nullptr) (whisper K/V from kv_pad) + // (CPY ->) PERMUTE -> VIEW / RESHAPE / CONCAT / RMS_NORM (llama, whisper, gemma4v) + // (CPY ->) VIEW -> base (whisper K/V from kv_pad) for (int i = 0; i < 3; i++) { const ggml_tensor * src = op->src[i]; + if (src == nullptr) { + return false; + } + if (src->op == GGML_OP_CPY) { + src = src->src[0]; + if (src == nullptr) { + return false; + } + } if (src->op == GGML_OP_PERMUTE) { if (src->src[0] == nullptr) { return false; } - if (src->src[0]->op != GGML_OP_VIEW && src->src[0]->op != GGML_OP_RESHAPE) { + const enum ggml_op inner_op = src->src[0]->op; + if (inner_op != GGML_OP_VIEW && inner_op != GGML_OP_RESHAPE && inner_op != GGML_OP_CONCAT && + inner_op != GGML_OP_RMS_NORM) { return false; } - if (src->src[0]->src[0] == nullptr || src->src[0]->src[0]->view_src != nullptr) { - return false; + if (inner_op == GGML_OP_VIEW || inner_op == GGML_OP_RESHAPE) { + if (src->src[0]->src[0] == nullptr || src->src[0]->src[0]->view_src != nullptr) { + return false; + } } } else if (src->op == GGML_OP_VIEW) { if (src->src[0] == nullptr || src->src[0]->view_src != nullptr) {