char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
511f9c137
char/aliceai-t5-moe
1#include "ggml-decoder.h" 2 3#include "ggml-impl.h" 4#include "ggml-openvino-extra.h" 5#include "ggml-openvino.h" 6#include "ggml-quants.h" 7#include "ggml.h" 8#include "utils.h" 9 10#include <algorithm> 11#include <cassert> 12#include <cstddef> 13#include <cstdint> 14#include <cstdlib> 15#include <fstream> 16#include <iomanip> 17#include <map> 18#include <memory> 19#include <mutex> 20#include <openvino/core/dimension.hpp> 21#include <openvino/core/except.hpp> 22#include <openvino/core/node.hpp> 23#include <openvino/core/partial_shape.hpp> 24#include <openvino/core/type/bfloat16.hpp> 25#include <openvino/core/type/element_type.hpp> 26#include <openvino/core/type/float16.hpp> 27#include <openvino/op/constant.hpp> 28#include <openvino/op/convert.hpp> 29#include <openvino/runtime/tensor.hpp> 30#include <ostream> 31#include <set> 32#include <stdexcept> 33#include <string> 34#include <cstring> 35#include <unordered_map> 36#include <vector> 37 38GgmlOvDecoder ::GgmlOvDecoder (ggml_cgraph * cgraph , 39ModelParams & model_params , 40ComputeParams & compute_params , 41 std::map < std::string , std::shared_ptr < ov::Node >> & model_weights , 42bool is_static , 43bool is_stateful , 44bool model_is_splitted , 45bool is_prefill , 46int prefill_chunk_size ) : 47m_is_static (is_static ), 48m_is_stateful (is_stateful ), 49m_is_prefill (is_prefill ), 50m_naive (false), 51m_prefill_chunk_size (prefill_chunk_size ), 52m_model_is_splitted (model_is_splitted ), 53m_cgraph (cgraph ), 54m_model_weights (model_weights ), 55m_model_params (model_params ), 56m_compute_params (compute_params ) { 57static bool printed_address_map = false; 58if (!printed_address_map ) { 59if (ggml_openvino_getenv_int ("GGML_OPENVINO_PRINT_CGRAPH_TENSOR_ADDRESS" )) { 60printed_address_map = true; 61print_tensor_address_map (cgraph ); 62 } 63 } 64 65validate_cgraph (); 66 67set_input_output (); 68compute_node_dynamic_dims (); 69compute_model_inputs (); 70compute_model_outputs (); 71 72for (int node_n = 0 ;node_n < cgraph -> n_nodes ;node_n ++ ) { 73m_node_info_list [node_n ].node_op_case = compute_op_case (m_node_info_list [node_n ].node ); 74m_node_info_list [node_n ].node_op_type = compute_op_type (m_node_info_list [node_n ].node ); 75 } 76 77add_extra_inputs (); 78} 79 80void GgmlOvDecoder ::update_io (ggml_cgraph * cgraph ) { 81m_cgraph = cgraph ; 82m_model_inputs .clear (); 83m_model_outputs .clear (); 84m_node_info_list .clear (); 85set_input_output (); 86compute_model_inputs (); 87compute_model_outputs (); 88} 89 90GgmlOvDecoder ::GgmlOvDecoder (ggml_cgraph * cgraph , std::map < std::string , std::shared_ptr < ov::Node >> & model_weights ) { 91m_cgraph = cgraph ; 92m_model_weights = model_weights ; 93m_naive = true; 94set_input_output (); 95compute_model_inputs (); 96compute_model_outputs (); 97for (int node_n = 0 ;node_n < cgraph -> n_nodes ;node_n ++ ) { 98m_node_info_list [node_n ].node_op_case = compute_op_case (m_node_info_list [node_n ].node ); 99m_node_info_list [node_n ].node_op_type = compute_op_type (m_node_info_list [node_n ].node ); 100 } 101} 102 103namespace { 104bool is_inplace_op (const ggml_tensor * node ) { 105return node -> op == GGML_OP_SET_ROWS || node -> op == GGML_OP_CPY || (node -> op == GGML_OP_SCALE && node -> view_src ); 106} 107 108bool is_same_shape (const ggml_tensor * a ,const ggml_tensor * b ) { 109for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 110if (a -> ne [i ]!= b -> ne [i ]) { 111return false; 112 } 113 } 114return true; 115} 116 117bool is_conv_states_all_tensor (const ggml_tensor * tensor ) { 118return tensor != nullptr && strncmp (tensor -> name ,"conv_states_all" ,strlen ("conv_states_all" ))== 0 ; 119} 120 121// CPY writing the tail of conv_input (the concat of the previous conv state and the new tokens) 122// back into a slot block of the recurrent state cache. Detected structurally because the rollback 123// variant (cparams.n_rs_seq > 0) emits one such CPY per snapshot slot without naming them. 124bool is_conv_state_writeback (const ggml_tensor * node ) { 125return node -> op == GGML_OP_CPY && node -> view_src != nullptr && GgmlOvDecoder ::is_kvcache (node -> view_src ,nullptr )&& 126node -> src [0 ]!= nullptr && node -> src [0 ]-> op == GGML_OP_VIEW && node -> src [0 ]-> src [0 ]!= nullptr && 127node -> src [0 ]-> src [0 ]-> op == GGML_OP_CONCAT && node -> src [1 ]!= nullptr && node -> src [1 ]-> op == GGML_OP_VIEW && 128node -> src [1 ]-> view_src == node -> view_src ; 129} 130 131// MoE expert aggregation (build_moe_ffn in llama-graph.cpp): each expert plane is 132// `ggml_view_2d(experts, n_embd, n_tokens, experts->nb[2], i*experts->nb[1])` and the planes 133// are summed with a chain of ADDs: moe_out = ((view_0 + view_1) + view_2) + ... + view_{n-1}. 134// Detected structurally by walking the ADD chain and checking every leaf is a same-shape, 135// same-stride VIEW of one common base tensor, indexed by a distinct expert-plane offset, and 136// that the chain covers every plane of that base (leaf count == base->ne[1]). Only the 137// outermost ADD of the chain satisfies this (inner ADDs see fewer leaves than base->ne[1]). 138bool is_moe_expert_sum_add (const ggml_tensor * node ) { 139 std::vector < const ggml_tensor *> leaves ; 140const ggml_tensor * cur = node ; 141while (cur -> op == GGML_OP_ADD ) { 142if (cur -> src [0 ]== nullptr || cur -> src [1 ]== nullptr ) { 143return false; 144 } 145leaves .push_back (cur -> src [1 ]); 146cur = cur -> src [0 ]; 147 } 148leaves .push_back (cur ); 149 150const ggml_tensor * base = nullptr ; 151 std::set < int64_t > plane_indices ; 152for (const ggml_tensor * leaf :leaves ) { 153if (leaf -> op != GGML_OP_VIEW || leaf -> src [0 ]== nullptr ) { 154return false; 155 } 156const ggml_tensor * leaf_base = leaf -> src [0 ]; 157if (base == nullptr ) { 158base = leaf_base ; 159 }else if (leaf_base != base ) { 160return false; 161 } 162if (leaf -> ne [0 ]!= base -> ne [0 ]|| leaf -> ne [1 ]!= base -> ne [2 ]|| leaf -> ne [2 ]!= 1 || leaf -> ne [3 ]!= 1 || 163leaf -> nb [1 ]!= base -> nb [2 ]) { 164return false; 165 } 166if (base -> nb [1 ]== 0 || leaf -> view_offs %base -> nb [1 ]!= 0 ) { 167return false; 168 } 169int64_t plane = static_cast < int64_t > (leaf -> view_offs /base -> nb [1 ]); 170if (plane < 0 || plane >=base -> ne [1 ]|| !plane_indices .insert (plane ).second ) { 171return false; 172 } 173 } 174 175return base != nullptr && base -> ne [1 ]> 1 && plane_indices .size ()== static_cast < size_t > (base -> ne [1 ]); 176} 177}// namespace 178 179static std::string get_tensor_ov_name (const ggml_cgraph * cgraph ,const ggml_tensor * tensor ) { 180if (tensor == nullptr ) { 181return "" ; 182 } 183const size_t hash_pos = ggml_hash_find (& cgraph -> visited_hash_set ,tensor ); 184if (((tensor -> flags & GGML_TENSOR_FLAG_COMPUTE )|| GgmlOvDecoder ::is_kvcache (tensor ,nullptr ))&& 185hash_pos != GGML_HASHSET_FULL && ggml_bitset_get (cgraph -> visited_hash_set .used ,hash_pos )) { 186return std::string (tensor -> name )+ "#" + std::to_string (hash_pos ); 187 } 188return tensor -> name ; 189} 190 191static std::string get_tensor_graph_input_ov_name (const GgmlOvDecoder * decoder , 192const ggml_cgraph * cgraph , 193const ggml_tensor * tensor , 194const ggml_tensor * op ) { 195if (GgmlOvDecoder ::is_inp_pos (tensor ,op )) { 196return "inp_pos" ; 197 } 198if (GgmlOvDecoder ::is_inp_emb (tensor ,op )) { 199return "embd" ; 200 } 201if (decoder -> is_stateful ()&& GgmlOvDecoder ::is_inp_mask (tensor ,op )) { 202return std::string (tensor -> name ).find ("swa" )== std::string::npos ?"self_kq_mask" :"self_kq_mask_swa" ; 203 } 204return get_tensor_ov_name (cgraph ,tensor ); 205} 206 207void GgmlOvDecoder ::set_input_output () { 208for (int node_n = 0 ;node_n < m_cgraph -> n_nodes ;node_n ++ ) { 209auto * node = m_cgraph -> nodes [node_n ]; 210 211NodeInfo current_node_info ; 212auto node_name = get_tensor_ov_name (m_cgraph ,node ); 213 214current_node_info .node = node ; 215current_node_info .node_name = node_name ; 216current_node_info .node_op_case = 0 ; 217current_node_info .data_addr = node -> data ; 218 219for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 220auto * src = node -> src [i ]; 221if (src == nullptr ) { 222continue ; 223 } 224auto src_name = get_tensor_ov_name (m_cgraph ,src ); 225if (src -> flags & GGML_TENSOR_FLAG_INPUT ) { 226src_name = get_tensor_graph_input_ov_name (this ,m_cgraph ,src ,node ); 227 } 228current_node_info .node_inputs [src_name ]= src ; 229current_node_info .node_inputs_names .push_back (src_name ); 230 231if (src -> op == GGML_OP_VIEW ) { 232// Traverse upward through nested VIEW operations 233 std::remove_reference_t < decltype(current_node_info .node_inputs_views [src_name ])> view_chain ; 234auto current = src ; 235 236while (current != nullptr ) { 237auto current_name = get_tensor_ov_name (m_cgraph ,current ); 238if (current -> flags & GGML_TENSOR_FLAG_INPUT ) { 239current_name = get_tensor_graph_input_ov_name (this ,m_cgraph ,current ,node ); 240 } 241view_chain .emplace_back (current_name ,current ); 242// If current src is also a VIEW, continue traversing 243if (current -> src [0 ]!= nullptr && current -> src [0 ]-> op == GGML_OP_VIEW ) { 244current = current -> src [0 ]; 245 }else { 246break ; 247 } 248 } 249 250// Assign all collected view inputs to node_inputs_views 251current_node_info .node_inputs_views [src_name ]= view_chain ; 252 } 253 } 254 255m_node_info_list .push_back (current_node_info ); 256 } 257} 258 259int GgmlOvDecoder ::compute_op_case (const ggml_tensor * node )const { 260int op_case = 0 ; 261switch (node -> op ) { 262case GGML_OP_RESHAPE : { 263auto name = std::string (node -> name ); 264auto * src = node -> src [0 ]; 265if (src -> op == GGML_OP_RESHAPE && src -> src [0 ]-> ne [0 ]== node -> ne [0 ]&& src -> src [0 ]-> ne [1 ]== node -> ne [1 ]) { 266op_case = 4 ; 267 }else if (node -> ne [0 ]* node -> ne [1 ]== src -> ne [0 ]) { 268op_case = 1 ; 269 }else if (src -> ne [0 ]* src -> ne [1 ]== node -> ne [0 ]) { 270op_case = 2 ; 271if (src -> ne [2 ]* src -> ne [3 ]== node -> ne [1 ]) { 272op_case = 5 ; 273 } 274 }else if (src -> ne [0 ]* src -> ne [1 ]* src -> ne [2 ]== node -> ne [1 ]) { 275op_case = 3 ; 276 }else if (name .find ("linear_attn_qkv_mixed" )== 0 || name .find ("alpha" )== 0 ) { 277op_case = 6 ; 278 }else if (name .find ("linear_attn_out" )== 0 ) { 279op_case = 7 ; 280 }else if (name .find ("state_predelta" )== 0 ) { 281op_case = 8 ; 282 } 283break ; 284 } 285case GGML_OP_PERMUTE : { 286if (node -> src [0 ]-> op != GGML_OP_VIEW ) { 287op_case = 1 ; 288 }else if (node -> src [0 ]-> src [0 ]-> op == GGML_OP_NONE ) { 289// kv cache tensor 290 std::string src_name (node -> view_src -> name ); 291int layer = extract_layer_from_name (src_name ).value (); 292if (ggml_is_contiguous (node -> src [0 ])) { 293// - 19: [ 64, 8, 256, 1] VIEW cache_k_l0 (view) [ 2, 128, 1024, 1048576] 294// [ 512, 1024, 1, 1] 0: NONE cache_k_l0 [ 2, 1024, 1048576, 1048576] 295// - 20: [ 64, 256, 8, 1] PERMUTE cache_k_l0 (view) (permuted) [ 2, 1024, 128, 1048576] 296// [ 64, 8, 256, 1] 0: VIEW cache_k_l0 (view) [ 2, 128, 1024, 1048576] 297if (!is_swa_layer (layer )) { 298op_case = 3 ; 299 }else { 300op_case = 4 ; 301 } 302 }else { 303// special case of cache v when `-fa off` 304// - 17: [ 256, 8, 64, 1] VIEW cache_v_l0 (view) [ 2, 131072, 2048, 1048576] 305// [ 512, 1024, 1, 1] 0: NONE cache_v_l0 [ 2, 1024, 1048576, 1048576] 306// - 18: [ 256, 64, 8, 1] PERMUTE cache_v_l0 (view) (permuted) [ 2, 2048, 131072, 1048576] 307// [ 256, 8, 64, 1] 0: VIEW cache_v_l0 (view) [ 2, 131072, 2048, 1048576] 308if (!is_swa_layer (layer )) { 309op_case = 5 ; 310 }else { 311op_case = 6 ; 312 } 313 } 314 }else { 315// rope'ed query tensor 316op_case = 2 ; 317 } 318break ; 319 } 320case GGML_OP_MUL_MAT : { 321if (node -> src [0 ]-> op == GGML_OP_VIEW && node -> src [1 ]-> op == GGML_OP_VIEW ) { 322op_case = 3 ; 323 }else if (node -> src [1 ]-> op == GGML_OP_SOFT_MAX ) { 324// In the case of `-fa off`, softmax is used, v_trans=true, the dynamic dim is ne[0] for cache_v 325op_case = 2 ; 326 } 327break ; 328 } 329case GGML_OP_GET_ROWS : { 330if (node -> src [1 ]-> op == GGML_OP_VIEW ) { 331// GET_ROWS gathering recurrent state cache rows via the inp->s_copy index list: 332// src[0] is a reshape of cache_r/cache_s, src[1] is a view of the s_copy leaf. 333// op_case 3: main view (active sequences, view offset 0) 334// op_case 4: extra view (defrag remainder, nonzero view offset) 335if (node -> src [0 ]-> op == GGML_OP_RESHAPE && node -> src [0 ]-> src [0 ]!= nullptr && 336is_kvcache (node -> src [0 ]-> src [0 ],nullptr )) { 337op_case = node -> src [1 ]-> view_offs == 0 ?1 :2 ; 338 } 339 } 340break ; 341 } 342case GGML_OP_ROPE : { 343const int mode = node -> op_params [2 ]; 344switch (mode ) { 345case GGML_ROPE_TYPE_NEOX : { 346op_case = 1 ; 347break ; 348 } 349case GGML_ROPE_TYPE_IMROPE : { 350op_case = 2 ; 351break ; 352 } 353default : 354op_case = 0 ; 355break ; 356 } 357break ; 358 } 359case GGML_OP_VIEW : { 360if (m_is_static && node -> src [0 ]!= nullptr && 361 (node -> src [0 ]-> op == GGML_OP_GATED_DELTA_NET || node -> src [0 ]-> op == GGML_OP_CONCAT )) { 362// VIEW slicing a GATED_DELTA_NET combined [attn|state] output, or the conv_input 363// CONCAT. The consuming CPY/RMS_NORM op recovers the true window at runtime via 364// ssm_state_size / the fixed conv kernel width, so this VIEW must stay an identity 365// pass-through of the full source here too (it already is on the dynamic path); 366// otherwise the generic static-mode Slice below would bake in the *captured* 367// cgraph's token count, which is wrong once the compiled static model runs with a 368// different token count (prefill chunk size or 1). 369op_case = 1 ; 370break ; 371 } 372if (node -> src [0 ]-> op == GGML_OP_VIEW ) { 373auto * src = node -> src [0 ]; 374if (ggml_nelements (node )!= ggml_nelements (src )) { 375// throw std::runtime_error("Unsupported VIEW case"); 376 } 377op_case = 0 ; 378if (m_model_is_splitted && m_model_inputs .find (get_tensor_ov_name (m_cgraph ,src ))!= m_model_inputs .end ()) { 379op_case = 0 ; 380 } 381 } 382 { 383auto * src = node -> src [0 ]; 384if (ggml_nelements (node )!= ggml_nelements (src )) { 385// Case 4: select one slice on src dim1 (via view offset), keep src dim2 as output dim1. 386// Typical pattern: 387// src: ne=[N, M, K, 1], nb=[b0, b1, b2, b3] 388// dst: ne=[N, K, 1, 1], nb=[b0, b2, b3, b3] 389if (node -> ne [0 ]== src -> ne [0 ]&& node -> ne [1 ]== src -> ne [2 ]&& node -> ne [2 ]== 1 && 390node -> nb [0 ]== src -> nb [0 ]&& node -> nb [1 ]== src -> nb [2 ]&& src -> ne [1 ]> 1 ) { 391op_case = 0 ; 392break ; 393 } 394 395// General case 3: shape differs from source (one or more dims) and is handled as VIEW slicing. 396int diff_count = 0 ; 397for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 398if (node -> ne [i ]!= src -> ne [i ]) { 399diff_count ++ ; 400 } 401// if node ne[i] > src ne[i], case = 0 402if (node -> ne [i ]> src -> ne [i ]) { 403return 0 ; 404 } 405 } 406if (diff_count >=1 ) { 407op_case = 0 ; 408 } 409 } 410 } 411break ; 412 } 413case GGML_OP_RMS_NORM : { 414if (node -> src [0 ]-> op == GGML_OP_VIEW ) { 415if (is_same_shape (node -> src [0 ]-> src [0 ],node -> src [0 ])) { 416op_case = 1 ; 417 }else if (node -> src [0 ]-> src [0 ]-> op == GGML_OP_GATED_DELTA_NET ) { 418op_case = 2 ; 419 } 420 } 421break ; 422 } 423case GGML_OP_POOL_2D : { 424const ggml_op_pool pool_mode = static_cast < ggml_op_pool > (node -> op_params [0 ]); 425switch (pool_mode ) { 426case GGML_OP_POOL_MAX : { 427op_case = 1 ; 428break ; 429 } 430case GGML_OP_POOL_AVG : { 431op_case = 2 ; 432break ; 433 } 434default : 435op_case = 0 ; 436break ; 437 } 438break ; 439 } 440case GGML_OP_CPY : { 441if (node -> src [0 ]-> op == GGML_OP_VIEW ) { 442if (node -> src [0 ]-> src [0 ]-> op == GGML_OP_GATED_DELTA_NET ) { 443op_case = 1 ; 444 }else if (is_conv_state_writeback (node )) { 445op_case = 2 ; 446break ; 447 }else if (is_conv_states_all_tensor (node -> view_src )&& node -> src [1 ]!= nullptr && 448node -> src [1 ]-> op == GGML_OP_VIEW && node -> src [1 ]-> view_src == node -> view_src ) { 449op_case = 4 ; 450break ; 451 } 452 }else if (node -> src [0 ]-> op == GGML_OP_GET_ROWS && node -> src [1 ]!= nullptr && 453node -> src [1 ]-> op == GGML_OP_VIEW && node -> src [1 ]-> view_src != nullptr && 454is_kvcache (node -> src [1 ]-> view_src ,nullptr )) { 455// s_copy defrag remainder writeback: gathered extra state rows copied back into the cache 456op_case = 3 ; 457 }else if (node -> src [1 ]!= nullptr && node -> src [1 ]-> op == GGML_OP_VIEW && node -> src [1 ]-> view_src != nullptr ) { 458// op_case 5: KV write for decoder self-attention (dynamic write offset) 459// op_case 6: KV write for encoder self-attn or cross-attn (static offset) 460const ggml_tensor * kv_buf = node -> src [1 ]-> view_src ; 461if (kv_buf -> ne [1 ]== 1 && kv_buf -> ne [2 ]== 1 && kv_buf -> ne [3 ]== 1 ) { 462op_case = 6 ; 463// Forward-scan the graph for a FLASH_ATTN_EXT that reads from 464// the same buffer. Having a mask (src[3] != nullptr) implies 465// decoder self-attention and the write offset is dynamic. 466for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 467const ggml_tensor * n = m_cgraph -> nodes [i ]; 468if (n -> op != GGML_OP_FLASH_ATTN_EXT ) { 469continue ; 470 } 471// K (src[1]) and V (src[2]) are 3-D views whose view_src is 472// the flat KV buffer we are writing to. 473if ((n -> src [1 ]!= nullptr && n -> src [1 ]-> view_src == kv_buf )|| 474 (n -> src [2 ]!= nullptr && n -> src [2 ]-> view_src == kv_buf )) { 475if (n -> src [3 ]!= nullptr ) { 476op_case = 5 ;// decoder self-attention: mask present 477 } 478break ; 479 } 480 } 481 } 482 } 483break ; 484 } 485case GGML_OP_ADD : { 486if (is_moe_expert_sum_add (node )) { 487// Outermost ADD of a MoE expert-plane sum chain: translated as a single 488// ReduceSum over the base tensor instead of N-1 chained Adds over N Slices. 489op_case = 1 ; 490 } 491break ; 492 } 493case GGML_OP_SCALE : { 494if (node -> view_src && node -> buffer -> usage == GGML_BACKEND_BUFFER_USAGE_ANY ) { 495op_case = 1 ; 496 } 497break ; 498 } 499case GGML_OP_L2_NORM : { 500if (std::string (node -> name ).find ("predelta" )!= std::string::npos ) { 501op_case = 1 ; 502 } 503break ; 504 } 505case GGML_OP_FLASH_ATTN_EXT : { 506if (node -> src [1 ]!= nullptr && node -> src [1 ]-> op == GGML_OP_VIEW && node -> src [1 ]-> view_src != nullptr ) { 507const ggml_tensor * kv_buf = node -> src [1 ]-> view_src ; 508if (kv_buf -> ne [1 ]== 1 && kv_buf -> ne [2 ]== 1 && kv_buf -> ne [3 ]== 1 ) { 509op_case = (node -> src [3 ]!= nullptr ) ?1 :2 ; 510 } 511 } 512break ; 513 } 514default : 515break ; 516 } 517return op_case ; 518} 519 520std::optional < int > extract_layer_from_name (const std::string & name ) { 521size_t pos1 = name .find ("_l" ); 522if (pos1 == std::string::npos ) { 523return std::nullopt ; 524 } 525pos1 += 2 ; 526size_t pos2 = name .find (' ' ,pos1 ); 527if (pos2 == std::string::npos ) { 528pos2 = name .length (); 529 } 530 std::string layer_str = name .substr (pos1 ,pos2 - pos1 ); 531int layer = std::stoi (layer_str ); 532return layer ; 533} 534 535std::pair < ModelParams ,ComputeParams > GgmlOvDecoder ::compute_llm_params (ggml_cgraph * cgraph ,bool is_static ) { 536ModelParams model_params ; 537ComputeParams compute_params ; 538auto get_attention_pattern_case = [](const ggml_tensor * node )-> int { 539if (node == nullptr ) { 540return -1 ; 541 } 542 543switch (node -> op ) { 544case GGML_OP_FLASH_ATTN_EXT : 545if (node -> src [0 ]== nullptr || node -> src [1 ]== nullptr ) { 546return -1 ; 547 } 548switch (node -> src [1 ]-> op ) { 549case GGML_OP_PERMUTE : 550// case 0: src[1] is PERMUTE of a cache VIEW, mask required 551if (node -> src [3 ]!= nullptr && node -> src [1 ]-> src [0 ]!= nullptr && 552node -> src [1 ]-> src [0 ]-> op == GGML_OP_VIEW ) { 553return 0 ; 554 } 555break ; 556case GGML_OP_CPY : 557// case 1: src[1] is CPY of a PERMUTE(VIEW), mask required 558if (node -> src [3 ]!= nullptr && node -> src [1 ]-> src [0 ]!= nullptr && 559node -> src [1 ]-> src [0 ]-> op == GGML_OP_PERMUTE && node -> src [1 ]-> src [0 ]-> src [0 ]!= nullptr && 560node -> src [1 ]-> src [0 ]-> src [0 ]-> op == GGML_OP_VIEW ) { 561return 1 ; 562 } 563break ; 564case GGML_OP_VIEW : 565// cases 4/5/6: whisper - K is a direct non-contiguous VIEW_3D of a KV cache 566if (node -> src [1 ]-> view_src != nullptr ) { 567if (node -> src [3 ]!= nullptr ) { 568return 4 ;// decoder self-attention 569 }else { 570return 5 ;// cross-attention or encoder self-attention 571 }; 572 } 573break ; 574default : 575break ; 576 } 577break ; 578case GGML_OP_SOFT_MAX : 579// case 2: node op is SOFT_MAX, src 0 not null & op is MUL_MAT & the src 0 of MUL_MAT is PERMUTE & the permuted tensor src is the view of cache k 580if (node -> src [0 ]!= nullptr && node -> src [1 ]!= nullptr && node -> src [0 ]-> op == GGML_OP_MUL_MAT && 581node -> src [0 ]-> src [0 ]!= nullptr && node -> src [0 ]-> src [1 ]!= nullptr && 582node -> src [0 ]-> src [0 ]-> op == GGML_OP_PERMUTE && node -> src [0 ]-> src [0 ]-> src [0 ]!= nullptr && 583node -> src [0 ]-> src [0 ]-> src [0 ]-> op == GGML_OP_VIEW ) { 584return 2 ; 585 } 586// case 3: node op is SOFT_MAX, src 0 not null & op is ADD & the src 0 of ADD is MUL_MAT & the src 0 of MUL_MAT is PERMUTE 587if (node -> src [0 ]-> op == GGML_OP_ADD && node -> src [0 ]-> src [0 ]!= nullptr && 588node -> src [0 ]-> src [0 ]-> op == GGML_OP_MUL_MAT && node -> src [0 ]-> src [0 ]-> src [0 ]!= nullptr && 589node -> src [0 ]-> src [0 ]-> src [0 ]-> op == GGML_OP_PERMUTE ) { 590return 3 ; 591 } 592break ; 593default : 594break ; 595 } 596 597return -1 ; 598 }; 599 600bool rope_seen = false; 601for (int i = 0 ;i < cgraph -> n_nodes ;i ++ ) { 602auto * node = cgraph -> nodes [i ]; 603 std::string name = std::string (node -> name ); 604const int attention_pattern_case = get_attention_pattern_case (node ); 605if (attention_pattern_case != -1 ) { 606ggml_tensor * cache_k_permute = nullptr ; 607ggml_tensor * mask = nullptr ; 608 609switch (attention_pattern_case ) { 610case 0 : 611cache_k_permute = node -> src [1 ]; 612mask = node -> src [3 ]; 613break ; 614case 1 : 615cache_k_permute = node -> src [1 ]-> src [0 ]; 616mask = node -> src [3 ]; 617break ; 618case 2 : 619cache_k_permute = node -> src [0 ]-> src [0 ]; 620mask = node -> src [1 ]; 621break ; 622case 3 : 623cache_k_permute = node -> src [0 ]-> src [0 ]-> src [0 ]; 624mask = node -> src [1 ]; 625break ; 626case 4 : 627case 5 : { 628// whisper: K is a direct VIEW_3D of the KV buffer, no PERMUTE node 629auto * cache_k_view = node -> src [1 ];// VIEW_3D of kv_self.k or kv_cross.k` 630compute_params .token_len_per_seq = node -> src [0 ]-> ne [1 ]; 631if (attention_pattern_case == 4 ) { 632compute_params .attention_size = cache_k_view -> ne [1 ]; 633 }else { 634compute_params .attention_size_static = cache_k_view -> ne [1 ]; 635 } 636continue ; 637 } 638default : 639break ; 640 } 641 642assert (cache_k_permute != nullptr ); 643 644model_params .head_size = cache_k_permute -> ne [0 ]; 645model_params .n_heads_kv = cache_k_permute -> ne [2 ]; 646compute_params .input_len = node -> src [0 ]-> ne [1 ]; 647compute_params .token_len_per_seq = node -> src [0 ]-> ne [1 ]; 648 649auto * cache_k_view = cache_k_permute -> src [0 ]; 650if (cache_k_view -> op != GGML_OP_VIEW || mask == nullptr ) { 651continue ; 652 } 653 654ggml_tensor * cache_k = cache_k_view -> src [0 ]; 655int layer = extract_layer_from_name (cache_k -> name ).value (); 656 657 std::string mask_name (mask -> name ); 658 659model_params .kv_buffer_ctx_id = ggml_backend_openvino_buffer_get_ctx_id (cache_k -> buffer ); 660if (mask_name .find ("swa" )!= std::string::npos ) { 661model_params .swa_layers .push_back (layer ); 662model_params .ctx_per_seq_swa = cache_k -> ne [1 ]; 663 }else { 664model_params .ctx_per_seq = cache_k -> ne [1 ]; 665model_params .n_seq = cache_k -> ne [2 ]; 666 } 667 668compute_params .n_seq_active = mask -> ne [3 ]; 669auto seq_size = cache_k -> ne [0 ]* cache_k -> ne [1 ]* ggml_type_size (cache_k -> type ); 670size_t offset ; 671memcpy (& offset ,cache_k_view -> op_params ,sizeof (size_t )); 672compute_params .seq_active_start = offset /seq_size ; 673 674if (mask_name .find ("swa" )!= std::string::npos ) { 675compute_params .attention_size_swa = mask -> ne [0 ]; 676 }else { 677compute_params .attention_size = mask -> ne [0 ]; 678 } 679if (is_static ) { 680compute_params .attention_size = model_params .ctx_per_seq ; 681compute_params .attention_size_swa = model_params .ctx_per_seq_swa ; 682compute_params .token_len_per_seq = 1 ; 683 } 684 } 685 686if (node -> op == GGML_OP_MUL_MAT && node -> src [0 ]-> op == GGML_OP_PERMUTE && 687node -> src [0 ]-> src [0 ]-> op == GGML_OP_VIEW && is_kvcache (node -> src [0 ]-> view_src ,node -> view_src )) { 688if (node -> src [1 ]-> op == GGML_OP_PERMUTE && node -> src [1 ]-> src [0 ]-> op == GGML_OP_VIEW && 689node -> src [1 ]-> src [0 ]-> src [0 ]-> op == GGML_OP_ROPE ) { 690compute_params .attention_size = node -> ne [0 ]; 691 } 692 } 693 694// if the node op is TRANSPOSE and its input is PERMUTE and the source of the PERMUTE is VIEW, then get the attention size with the TRANSPOSE node ne[0] (in case no GGML_OP_FLASH_ATTN_EXT) 695if (node -> op == GGML_OP_TRANSPOSE && node -> src [0 ]-> op == GGML_OP_PERMUTE && 696node -> src [0 ]-> src [0 ]-> op == GGML_OP_VIEW ) { 697compute_params .attention_size = node -> ne [0 ]; 698if (is_static ) { 699compute_params .attention_size = model_params .ctx_per_seq ; 700 } 701 } 702if (node -> op == GGML_OP_ROPE ) { 703if (compute_params .token_len_per_seq == -1 && node -> src [1 ]!= nullptr ) { 704compute_params .token_len_per_seq = ggml_nelements (node -> src [1 ]); 705 } 706 707// When multiple ROPE ops in the graph disagree on op_params (e.g. gemma4's 708// mixed SWA/non-SWA layers with different n_dims or freq_base), we cannot 709// share a single precomputed rope_sin/rope_cos. Track divergence so the 710// translator falls back to per-op make_sin_cos in that case. 711 static_assert(sizeof (model_params .rope_params )== sizeof (int32_t )* 15 ,"rope_params size" ); 712if (!rope_seen ) { 713memcpy (model_params .rope_params ,node -> op_params ,sizeof (int32_t )* 15 ); 714rope_seen = true; 715 }else if (memcmp (model_params .rope_params ,node -> op_params ,sizeof (int32_t )* 15 )!= 0 ) { 716model_params .mixed_rope_params = true; 717 } 718 } 719if (node -> op == GGML_OP_GATED_DELTA_NET ) { 720model_params .state_size = node -> src [0 ]-> ne [0 ]; 721 } 722if (node -> op == GGML_OP_SCALE && node -> view_src != nullptr && is_kvcache (node -> view_src ,nullptr )) { 723compute_params .cache_rs_reset_len = ggml_nelements (node ) /node -> view_src -> ne [0 ]; 724compute_params .cache_rs_reset_idx = node -> src [0 ]-> view_offs /node -> view_src -> ne [0 ]; 725 } 726// Capture the destination slot block of every recurrent state cache writeback, plus the 727// conv_input window the conv state writeback copies. The active sequences occupy a 728// contiguous slot block [begin, begin + n_seqs) of the cache; the block and the window move 729// with the batch, so they are fed to the cached model as runtime inputs. 730if (node -> op == GGML_OP_CPY && node -> view_src != nullptr && is_kvcache (node -> view_src ,nullptr )&& 731node -> src [1 ]!= nullptr && node -> src [1 ]-> op == GGML_OP_VIEW && node -> src [1 ]-> view_src == node -> view_src ) { 732const bool is_conv = is_conv_state_writeback (node ); 733const bool is_gdn = node -> src [0 ]-> op == GGML_OP_VIEW && node -> src [0 ]-> src [0 ]!= nullptr && 734node -> src [0 ]-> src [0 ]-> op == GGML_OP_GATED_DELTA_NET ; 735const bool is_extra = node -> src [0 ]-> op == GGML_OP_GET_ROWS ; 736 737const ggml_tensor * dest_view = node -> src [1 ]; 738const ggml_tensor * cache = node -> view_src ; 739const size_t row_bytes = cache -> ne [0 ]* ggml_type_size (cache -> type ); 740if (row_bytes > 0 && (is_conv || is_gdn || is_extra )) { 741ComputeParams ::RsWriteback writeback ; 742writeback .slot_begin = (int ) (dest_view -> view_offs /row_bytes ); 743if (is_conv ) { 744writeback .src_begin = (int ) (node -> src [0 ]-> view_offs /node -> src [0 ]-> view_src -> nb [0 ]); 745 }else if (is_gdn ) { 746writeback .src_begin = (int ) (node -> src [0 ]-> view_offs /node -> src [0 ]-> view_src -> nb [1 ]); 747 } 748compute_params .rs_writebacks [get_tensor_ov_name (cgraph ,node )]= writeback ; 749 } 750if (is_conv || is_gdn ) { 751compute_params .s_copy_active_slot_len = (int )dest_view -> ne [1 ]; 752 } 753 } 754 } 755auto * output_tensor = cgraph -> nodes [cgraph -> n_nodes - 1 ]; 756compute_params .output_len = output_tensor -> ne [1 ]; 757// for NPU, output_len is always 1 except for llama-perplexity 758if (is_static && compute_params .output_len == 0 ) { 759compute_params .output_len = 1 ; 760 } 761model_params .ctx = model_params .ctx_per_seq * model_params .n_seq ; 762return {model_params ,compute_params }; 763} 764 765void GgmlOvDecoder ::validate_cgraph ()const { 766if (m_model_params .n_seq > 1 && m_is_static == true) { 767throw std::runtime_error ("n_seq > 1 is not supported on NPU. Try setting -np 1." ); 768 } 769} 770 771ov::PartialShape GgmlOvDecoder ::get_graph_input_shape (const ggml_tensor * op , 772const ggml_tensor * input , 773int dynamic_dim_index )const { 774if (m_naive ) { 775return input != nullptr ? ov::PartialShape {get_shape (input )} : ov::PartialShape {get_shape (op )}; 776 } 777auto name = std::string (input -> name ); 778 ov::PartialShape input_shape ; 779 780if (is_inp_tok (input ,op )|| is_inp_pos (input ,op )) { 781// tokens or positions 782int len = m_is_static ? (m_is_prefill ?m_prefill_chunk_size :1 ) :-1 ; 783if (m_is_static && is_inp_pos (input ,op )) { 784// IMROPE stacks n_planes (t/h/w/e) position planes back to back 785len *=get_inp_pos_n_planes (op ); 786 } 787input_shape = ov::PartialShape {1 ,1 ,1 ,len }; 788 789 }else if (is_output_idx (input ,op )) { 790// output index 791input_shape = ov::PartialShape {1 ,1 ,1 ,m_is_static ?m_compute_params .output_len :-1 }; 792 793 }else if (is_inp_mask (input ,op )) { 794// mask 795if (m_is_static ) { 796input_shape = ov::PartialShape {1 ,1 ,m_is_prefill ?m_prefill_chunk_size :1 ,m_model_params .ctx }; 797 }else if (m_is_stateful ) { 798input_shape = ov::PartialShape {1 ,1 ,-1 ,-1 }; 799 }else { 800input_shape = ov::PartialShape {-1 ,1 ,-1 ,-1 }; 801 } 802 803 }else if (is_kvcache (input ,op )) { 804// kvcache 805input_shape = ov::PartialShape {get_shape (input )}; 806// Whisper.cpp uses a fixed size 1D KV buffer [N, 1, 1, 1] (GGML) or [1, 1, 1, N] (OV). 807// the token fill level is handled by token_len_per_seq + dynamic mask input. 808// skip dynamic dim and stateful reshape for this layout. 809const bool is_flat_kv = (input -> ne [1 ]== 1 && input -> ne [2 ]== 1 && input -> ne [3 ]== 1 ); 810if (!m_is_static && !is_flat_kv ) { 811// do not fix ctx size to make llama-bench work across test params 812input_shape [2 ]= -1 ; 813 } 814if (is_stateful ()&& !is_flat_kv ) { 815// Convert stateless KV cache layout [1, 1, seq, n_heads_kv * head_size] 816// to stateful layout [1, seq, n_heads_kv, head_size]. 817assert (input_shape .size ()== 4 && input_shape [0 ]== 1 && input_shape [1 ]== 1 && 818input_shape [2 ].is_dynamic ()&& 819input_shape [3 ]== (m_model_params .n_heads_kv * m_model_params .head_size )); 820input_shape = {input_shape [0 ], ov::Dimension ::dynamic (),m_model_params .n_heads_kv , 821m_model_params .head_size }; 822 } 823 824 }else if (is_kv_idx (input ,op )) { 825// kv update index 826int len = m_is_static ? (m_is_prefill ?m_prefill_chunk_size :1 ) :-1 ; 827input_shape = ov::PartialShape {1 ,1 ,1 ,len }; 828 829 }else if (is_inp_s_copy (input ,op )|| is_s_copy_leaf (input )) { 830// On NPU the total slot count (n_seq_max) is fixed at translation time, so the s_copy 831// index list has a static length; on CPU/GPU it may change across compiles (defrag). 832input_shape = m_is_static ? ov::PartialShape {get_shape (input )} : ov::PartialShape {1 ,1 ,1 ,-1 }; 833 834 }else { 835input_shape = ov::PartialShape {get_shape (input )}; 836 } 837if (dynamic_dim_index != -1 && m_model_is_splitted ) { 838input_shape [3 - dynamic_dim_index ]= -1 ; 839 } 840if (op -> op == GGML_OP_SOFT_MAX && op -> src [1 ]!= nullptr && op -> src [1 ]-> op == GGML_OP_NONE && 841op -> src [1 ]-> flags & GGML_TENSOR_FLAG_INPUT && op -> src [1 ]== input ) { 842// for softmax input mask, the shape is [1, 1, seq_active, seq_active], where seq_active is determined by the input active sequence length instead of the kv cache sequence length 843input_shape [2 ]= -1 ; 844input_shape [3 ]= -1 ; 845 } 846return input_shape ; 847} 848 849bool GgmlOvDecoder ::is_s_copy_leaf (const ggml_tensor * tensor )const { 850if (tensor == nullptr || tensor -> op != GGML_OP_NONE || m_cgraph == nullptr ) { 851return false; 852 } 853for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 854const ggml_tensor * node = m_cgraph -> nodes [i ]; 855if (node -> op != GGML_OP_GET_ROWS || node -> src [0 ]== nullptr || node -> src [1 ]== nullptr ) { 856continue ; 857 } 858// The index list may reach the s_copy leaf through one or more VIEWs. 859const ggml_tensor * idx = node -> src [1 ]; 860while (idx != nullptr && idx -> op == GGML_OP_VIEW ) { 861idx = idx -> src [0 ]; 862 } 863if (idx != tensor ) { 864continue ; 865 } 866// The gathered data must be a recurrent state cache (cache_r/cache_s). 867const ggml_tensor * data = node -> src [0 ]; 868while (data != nullptr && (data -> op == GGML_OP_VIEW || data -> op == GGML_OP_RESHAPE )) { 869data = data -> src [0 ]; 870 } 871if (data != nullptr && is_kvcache (data ,nullptr )) { 872return true; 873 } 874 } 875return false; 876} 877 878void GgmlOvDecoder ::add_extra_inputs () { 879// Extra inputs: 880// 1. `attention_size`, used in FLASH_ATTN where the shape of the matmul's are 256 aligned, 881// see llama_kv_cache_unified::get_n_kv and llama_kv_cache_unified::get_padding. 882// 2. `n_seq_active` and `seq_active_start`, used in FLASH_ATTN_EXT to indicate the active sequences in the batch 883 884auto create_1d_input = [this ](const std::string & name ,int64_t value ,bool force_parameter = false) { 885m_model_extra_inputs [name ]= {ov::element::i64 , ov::Shape {1 },value ,force_parameter || !m_is_static }; 886 }; 887 888if (m_compute_params .attention_size != -1 ) { 889create_1d_input ("attention_size" ,m_compute_params .attention_size ); 890 } 891if (m_compute_params .attention_size_static != -1 ) { 892create_1d_input ("attention_size_static" ,m_compute_params .attention_size_static ); 893 } 894if (m_compute_params .attention_size_swa != -1 ) { 895create_1d_input ("attention_size_swa" ,m_compute_params .attention_size_swa ); 896 } 897create_1d_input ("n_seq_active" ,m_compute_params .n_seq_active ); 898create_1d_input ("seq_active_start" ,m_compute_params .seq_active_start ); 899create_1d_input ("seq_active_end" ,m_compute_params .seq_active_start + m_compute_params .n_seq_active ); 900if (m_compute_params .token_len_per_seq != -1 ) { 901create_1d_input ("token_len_per_seq" ,m_compute_params .token_len_per_seq ); 902 } 903// create_1d_input("token_len", m_compute_params.token_len_per_seq * m_compute_params.n_seq_active); 904 905if (m_compute_params .cache_rs_reset_idx != -1 ) { 906// Whether/which cache slot to reset varies per compute call (e.g. a new sequence starting 907// vs. continued decoding). can_reuse_statically() does not invalidate the cached static 908// model on ComputeParams changes, so these must stay runtime Parameters even when static 909// (scale.cpp op_case 1 only uses them in value comparisons, never as Slice bounds, so this 910// does not reintroduce dynamic shapes). 911create_1d_input ("cache_rs_reset_idx" ,m_compute_params .cache_rs_reset_idx ,/*force_parameter=*/ true); 912create_1d_input ("cache_rs_reset_len" ,m_compute_params .cache_rs_reset_len ,/*force_parameter=*/ true); 913 } 914 915if (m_compute_params .s_copy_active_slot_len != -1 ) { 916create_1d_input ("s_copy_active_slot_len" ,m_compute_params .s_copy_active_slot_len ); 917if (m_is_static ) { 918// Number of real tokens in the current prefill chunk. The last chunk is padded with 919// fabricated token ids; attention masks them out, but the recurrent (GDN/conv) path 920// would otherwise fold them into cache_r/cache_s permanently. Varies per chunk, so it 921// must stay a runtime Parameter; it is only compared against a Range or used as Gather 922// indices, so it does not make any shape dynamic. 923create_1d_input ("chunk_valid_len" ,get_static_n_tokens (),/*force_parameter=*/ true); 924 } 925 } 926 927for (const auto & [node_name ,writeback ] :m_compute_params .rs_writebacks ) { 928create_1d_input ("rs_slot_begin_" + node_name ,writeback .slot_begin ); 929if (!m_is_static ) { 930create_1d_input ("rs_src_begin_" + node_name ,writeback .src_begin ); 931 } 932 } 933} 934 935bool GgmlOvDecoder ::node_is_used_as_src (const int node_idx ) { 936ggml_tensor * node = m_cgraph -> nodes [node_idx ]; 937for (int i = node_idx ;i < m_cgraph -> n_nodes ;i ++ ) { 938ggml_tensor * other_node = m_cgraph -> nodes [i ]; 939for (int j = 0 ;j < GGML_MAX_SRC ;j ++ ) { 940if (other_node -> src [j ]== node ) { 941return true; 942 } 943 } 944 } 945return false; 946} 947 948void GgmlOvDecoder ::compute_model_inputs () { 949m_model_inputs .clear (); 950m_inputs .clear (); 951for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 952ggml_tensor * node = m_cgraph -> nodes [i ]; 953// the node op is NONE means this node maybe as input of later nodes, we should add it to model inputs for this node. 954if (node -> op == GGML_OP_NONE && node_is_used_as_src (i )) { 955 std::string node_name = get_tensor_ov_name (m_cgraph ,node ); 956if (m_model_weights .find (node_name )== m_model_weights .end ()) { 957m_inputs [node_name ]= node ; 958m_model_inputs [node_name ]= {get_ov_type (node ), 959get_graph_input_shape (node ,nullptr ,m_node_dynamic_dims [node ])}; 960 } 961continue ; 962 } 963for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 964auto * src = node -> src [i ]; 965if (src == nullptr ) { 966continue ; 967 } 968 std::string src_name = get_tensor_ov_name (m_cgraph ,src ); 969if (src -> flags & GGML_TENSOR_FLAG_INPUT ) { 970src_name = get_tensor_graph_input_ov_name (this ,m_cgraph ,src ,node ); 971 } 972if (m_model_weights .find (src_name )!= m_model_weights .end ()) { 973continue ; 974 } 975 976bool is_intermediate_node = false; 977for (const auto & node_info :m_node_info_list ) { 978if (node_info .node == src ) { 979is_intermediate_node = true; 980break ; 981 } 982 } 983if (is_intermediate_node ) { 984continue ; 985 } 986if (m_model_inputs .find (src_name )!= m_model_inputs .end ()) { 987continue ; 988 } 989 990m_inputs [src_name ]= src ; 991 992ggml_backend_buffer * buffer = src -> buffer ; 993// GGML_BACKEND_BUFFER_USAGE_ANY are kv caches 994if (buffer -> usage == GGML_BACKEND_BUFFER_USAGE_ANY ) { 995if (auto it = std::find (m_model_params .kv_names .begin (),m_model_params .kv_names .end (),src_name ); 996it == m_model_params .kv_names .end ()) { 997m_model_params .kv_names .push_back (src_name ); 998 } 999 } 1000// Resolve nested VIEW nodes by following src[0] until the first non-VIEW tensor. 1001while (src -> op == GGML_OP_VIEW && src -> src [0 ]!= nullptr ) { 1002src = src -> src [0 ]; 1003src_name = get_tensor_ov_name (m_cgraph ,src ); 1004 } 1005m_inputs [src_name ]= src ; 1006m_model_inputs [src_name ]= {get_ov_type (src ), 1007get_graph_input_shape (node ,src ,m_node_dynamic_dims [src ])}; 1008 } 1009 } 1010} 1011 1012void GgmlOvDecoder ::compute_model_outputs () { 1013m_model_outputs .clear (); 1014m_model_output_names .clear (); 1015for (int node_n = 0 ;node_n < m_cgraph -> n_nodes ;node_n ++ ) { 1016auto * cur_node = m_cgraph -> nodes [node_n ]; 1017// if the node op is NONE means this node is not used at all, we can skip it directly without adding to model outputs. 1018if (cur_node -> op == GGML_OP_NONE || cur_node -> op == GGML_OP_VIEW || cur_node -> op == GGML_OP_RESHAPE ) { 1019continue ; 1020 } 1021auto cur_node_use_count = m_cgraph -> use_counts [ggml_hash_find (& m_cgraph -> visited_hash_set ,cur_node )]; 1022if (cur_node_use_count == 0 ) { 1023// The output of in-place ops is the view_src tensor, which is updated in place. We should use the view_src name as the output name to make sure it can be correctly matched with the later ops that use the view_src. 1024if (cur_node != nullptr && ::is_inplace_op (cur_node )&& ggml_nbytes (cur_node )> 0 ) { 1025cur_node = cur_node -> view_src ; 1026 } 1027 }else { 1028int input_use_count = 0 ; 1029for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 1030ggml_tensor * node = m_cgraph -> nodes [i ]; 1031for (int j = 0 ;j < GGML_MAX_SRC ;j ++ ) { 1032if (node -> src [j ]!= NULL && node -> src [j ]== cur_node ) { 1033input_use_count ++ ; 1034 } 1035 } 1036 } 1037if (input_use_count == cur_node_use_count ) { 1038cur_node = nullptr ; 1039 } 1040 } 1041if (cur_node != nullptr ) { 1042 std::string cur_node_name = get_tensor_ov_name (m_cgraph ,cur_node ); 1043m_model_outputs [cur_node_name ]= cur_node ; 1044m_model_output_names .insert (cur_node_name ); 1045 } 1046 } 1047} 1048 1049const ggml_tensor * GgmlOvDecoder ::get_tensor_used_op (const ggml_tensor * tensor )const { 1050if (tensor == nullptr ) { 1051return nullptr ; 1052 } 1053for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 1054const auto * node = m_cgraph -> nodes [i ]; 1055for (int j = 0 ;j < GGML_MAX_SRC ;j ++ ) { 1056if (node -> src [j ]== tensor ) { 1057return node ; 1058 } 1059 } 1060 } 1061return nullptr ; 1062} 1063 1064const ggml_tensor * GgmlOvDecoder ::get_tensor_from_name (const std::string & name )const { 1065for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 1066const auto * node = m_cgraph -> nodes [i ]; 1067for (int j = 0 ;j < GGML_MAX_SRC ;j ++ ) { 1068const auto * src = node -> src [j ]; 1069if (src == nullptr ) { 1070break ; 1071 } 1072if (get_tensor_ov_name (m_cgraph ,src )== name ) { 1073return src ; 1074 } 1075 } 1076 } 1077return nullptr ; 1078} 1079 1080std::map < std::string , std::string > GgmlOvDecoder ::get_kv_param_res_names ()const { 1081 std::map < std::string , std::string > kv_param_res_names ; 1082for (const auto & name :m_model_params .kv_names ) { 1083kv_param_res_names [name ]= name ; 1084 } 1085return kv_param_res_names ; 1086} 1087 1088// MUL_MAT_ID's src[0] is the [k, m, n_expert] expert-weight tensor. It is always a constant per-expert 1089// weight table -- never a computed activation -- regardless of whether the backend happened to mark its 1090// buffer as GGML_BACKEND_BUFFER_USAGE_WEIGHTS (test-backend-ops, for example, never sets that usage 1091// flag, unlike real inference). Without this, non-quantized (F16/F32/BF16) expert weights would fall 1092// through the check below as "not a weight", get decoded as a Parameter/activation instead of a 1093// Constant, and crash GatherMatmul's "only constant weights are supported" check. 1094static bool is_mul_mat_id_expert_weight (const ggml_tensor * node ,int src_index ) { 1095return node -> op == GGML_OP_MUL_MAT_ID && src_index == 0 ; 1096} 1097 1098std::map < std::string , std::shared_ptr < ov::Node >> GgmlOvDecoder ::create_weight_nodes (ggml_cgraph * cgraph ,bool naive ) { 1099 std::map < std::string , std::shared_ptr < ov::Node >> model_weights ; 1100auto * nodes = cgraph -> nodes ; 1101auto n_nodes = cgraph -> n_nodes ; 1102for (int node_i = 0 ;node_i < n_nodes ;node_i ++ ) { 1103auto * node = nodes [node_i ]; 1104for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 1105auto * src = node -> src [i ]; 1106if (src == nullptr ) { 1107continue ; 1108 } 1109 1110 std::string src_name = get_tensor_ov_name (cgraph ,src ); 1111if (is_rope_freqs_weight (src ,node )) { 1112src_name = "rope_freqs.weight" ; 1113 } 1114if (!src -> view_src ) { 1115ggml_backend_buffer * buffer = src -> buffer ; 1116if (buffer -> usage == GGML_BACKEND_BUFFER_USAGE_WEIGHTS || ggml_is_quantized (src -> type )|| 1117is_mul_mat_id_expert_weight (node ,i )) { 1118if (model_weights .find (src_name )== model_weights .end ()) { 1119auto weight_node = create_weight_node (src ,naive ); 1120weight_node -> set_friendly_name (src_name ); 1121model_weights [src_name ]= weight_node ; 1122 } 1123 } 1124 } 1125 } 1126 } 1127return model_weights ; 1128} 1129 1130// Process-lifetime cache for weight nodes built from NON-OpenVINO buffers (e.g. the 1131// token_embd.weight copy that lives in a CPU/mmap buffer and feeds GET_ROWS). Such 1132// tensors have no OV buffer context to own a cached extra, so without this they are 1133// re-extracted/re-requantized on every (re)compile — for token_embd that is a ~1-2 GB 1134// F32 dequant each time. Keyed by tensor->data, which is stable for the process and 1135// uniquely identifies the immutable weight bytes. OV-buffer weights keep using the 1136// per-tensor extra cache and never reach here. 1137static std::mutex g_nonov_weight_cache_mutex ; 1138static std::unordered_map < const void * , std::shared_ptr < ov::Node >> g_nonov_weight_cache ; 1139 1140std::set < std::string > GgmlOvDecoder ::collect_weight_names (ggml_cgraph * cgraph ) { 1141// Mirrors the name-selection logic of create_weight_nodes() but builds no nodes, 1142// so topology checks don't trigger weight extraction/requantization. 1143 std::set < std::string > names ; 1144for (int node_i = 0 ;node_i < cgraph -> n_nodes ;node_i ++ ) { 1145auto * node = cgraph -> nodes [node_i ]; 1146for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 1147auto * src = node -> src [i ]; 1148if (src == nullptr ) { 1149continue ; 1150 } 1151 std::string src_name (src -> name ); 1152if (is_rope_freqs_weight (src ,node )) { 1153src_name = "rope_freqs.weight" ; 1154 } 1155if (!src -> view_src ) { 1156ggml_backend_buffer * buffer = src -> buffer ; 1157if (buffer -> usage == GGML_BACKEND_BUFFER_USAGE_WEIGHTS || ggml_is_quantized (src -> type )) { 1158names .insert (src_name ); 1159 } 1160 } 1161 } 1162 } 1163return names ; 1164} 1165 1166std::shared_ptr < ov::Node > GgmlOvDecoder ::create_weight_node (ggml_tensor * tensor ,bool naive ) { 1167const bool is_ov_buffer = ggml_backend_buffer_is_openvino (tensor -> buffer ); 1168 1169// Check if we have a pre-built constant from the OpenVINO backend buffer 1170// This is set during ggml_backend_openvino_buffer_set_tensor 1171if (tensor -> extra ) { 1172OPENVINO_ASSERT (is_ov_buffer ,"Unsupported weight tensor: " + std::string (tensor -> name )+ 1173" Possibly this is a cpu backend repacked quantized weights" ); 1174// Cast to our extra base type and check the type 1175auto * extra_base = static_cast < ggml_openvino_extra_base *> (tensor -> extra ); 1176 1177if (extra_base -> type == ggml_openvino_extra_base::Type ::WEIGHT ) { 1178// F16/F32/BF16 weight with shared-memory constant 1179auto * weight_extra = static_cast < ggml_openvino_weight_extra *> (tensor -> extra ); 1180if (weight_extra -> weight_node ) { 1181// GGML_LOG_DEBUG("%s: using pre-built weight node for %s\n", __func__, tensor->name); 1182return weight_extra -> weight_node ; 1183 } 1184 }else if (extra_base -> type == ggml_openvino_extra_base::Type ::QUANTIZED_WEIGHT ) { 1185// Quantized weight with pre-extracted data 1186auto * quant_extra = static_cast < ggml_openvino_quantized_weight_extra *> (tensor -> extra ); 1187if (quant_extra -> weight_node ) { 1188// GGML_LOG_DEBUG("%s: using pre-extracted quantized weight node for %s\n", __func__, tensor->name); 1189return quant_extra -> weight_node ; 1190 } 1191 } 1192 } 1193 1194// MUL_MAT_ID expert weights are 3D GGML tensors [k, m, n_expert]. 1195// Keep the full reversed 4D shape when materializing non-quantized constants, 1196// otherwise the expert dimension is collapsed and later Gather/MatMul logic 1197// only sees a single expert slice. 1198if (!ggml_is_quantized (tensor -> type )&& (tensor -> ne [2 ]> 1 || tensor -> ne [3 ]> 1 )) { 1199auto weight_tensor = ov::Tensor (get_ov_type (tensor ),get_shape (tensor ),tensor -> data ); 1200auto weight_node = std::make_shared < ov::op::v0::Constant > (weight_tensor ); 1201weight_node -> set_friendly_name (tensor -> name ); 1202return weight_node ; 1203 } 1204 1205// Non-OV-buffer weights (CPU/mmap, e.g. the GET_ROWS token_embd copy) have no buffer 1206// context to cache an extra in, so memoize them here keyed by their (stable) data 1207// pointer to avoid re-extracting on every recompile. Opt-in via 1208// GGML_OPENVINO_REDUCE_COMPILE_MEM or GGML_OPENVINO_MEMORY_OPTIMIZE. Skip 1209// for `naive` (test/naive path) since use_bias changes the produced node. 1210const bool cacheable_nonov = ggml_openvino_reduce_compile_mem_enabled ()&& !is_ov_buffer && 1211 !naive && tensor -> data != nullptr ; 1212if (cacheable_nonov ) { 1213 std::lock_guard < std::mutex > lock (g_nonov_weight_cache_mutex ); 1214auto it = g_nonov_weight_cache .find (tensor -> data ); 1215if (it != g_nonov_weight_cache .end ()) { 1216return it -> second ; 1217 } 1218 } 1219 1220// There are three cases where we need to create a new weight node: 1221// 1. weights are in openvino_host_buffer. Weight loading to host buffer will not trigger backend_buffer_set_tensor 1222// 2. weights are in cpu/cpu_mapped buffer. On token_embd.weight goes to case 1 or 2, depending on whether mmap or direct_io is used 1223// 3. test-backend-ops. buffers in test-backend-ops does not set USAGE_WEIGHT so backend_buffer_set_tensor will not create weight node 1224 1225// GGML_LOG_DEBUG("%s: creating new weight node for %s\n", __func__, tensor->name); 1226static const std::set < ggml_type > weight_types = {GGML_TYPE_F32 ,GGML_TYPE_F16 ,GGML_TYPE_BF16 ,GGML_TYPE_Q8_0 , 1227GGML_TYPE_Q4_0 ,GGML_TYPE_Q4_1 ,GGML_TYPE_Q5_1 ,GGML_TYPE_Q4_K , 1228GGML_TYPE_Q5_K ,GGML_TYPE_Q6_K ,GGML_TYPE_MXFP4 }; 1229if (weight_types .find (tensor -> type )== weight_types .end ()) { 1230throw std::runtime_error ("Unexpected weight tensor type: " + std::string (tensor -> name )+ " with type " + 1231ggml_type_name (tensor -> type )); 1232 } 1233 1234OvWeight ov_weight ; 1235if (ggml_is_quantized (tensor -> type )) { 1236auto use_bias = naive ; 1237if (is_ov_buffer ) { 1238// For quantized weights, copy raw data to a temp buffer first because 1239// process_weight_tensor reads from data and writes extracted results 1240// (weights/scales/zp) to output_base_ptr — they would overlap if both 1241// point to tensor->data. 1242size_t raw_size = ggml_nbytes (tensor ); 1243 std::vector < uint8_t > tmp (raw_size ); 1244memcpy (tmp .data (),tensor -> data ,raw_size ); 1245ov_weight = process_weight_tensor (tensor ,tmp .data (),tensor -> data ,use_bias ); 1246 }else { 1247ov_weight = process_weight_tensor (tensor ,tensor -> data ,nullptr ,use_bias ); 1248 } 1249 }else { 1250// For non-quantized weights (F16/F32/BF16), data is already in tensor->data. 1251// process_weight_tensor will create an ov::Tensor wrapping tensor->data directly. 1252ov_weight = process_weight_tensor (tensor ,tensor -> data ,tensor -> data ); 1253 } 1254 1255ov_weight .weight_node -> set_friendly_name (tensor -> name ); 1256if (!is_ov_buffer ) { 1257if (cacheable_nonov ) { 1258 std::lock_guard < std::mutex > lock (g_nonov_weight_cache_mutex ); 1259// Another thread may have inserted concurrently; keep the first. 1260auto [it ,inserted ]= g_nonov_weight_cache .emplace (tensor -> data ,ov_weight .weight_node ); 1261return it -> second ; 1262 } 1263return ov_weight .weight_node ; 1264 } 1265 1266ggml_openvino_extra_base * extra ; 1267if (ov_weight .is_quantized ()) { 1268extra = new ggml_openvino_quantized_weight_extra (std::move (ov_weight .weights ), std::move (ov_weight .scales ), 1269 std::move (ov_weight .zp ),ov_weight .weight_node ); 1270 }else { 1271extra = new ggml_openvino_weight_extra (std::move (ov_weight .weights ),ov_weight .weight_node ); 1272 } 1273ggml_openvino_buffer_register_extra (tensor ,extra ); 1274 1275return ov_weight .weight_node ; 1276} 1277 1278void GgmlOvDecoder ::dump_cgraph (const ggml_cgraph * cgraph , std::string & filename ) { 1279 std::ofstream file (filename ); 1280if (!file .is_open ()) { 1281 std::cerr <<"Failed to open file" << std::endl ; 1282return ; 1283 } 1284 1285file <<"=== GRAPH ===\n" ; 1286 1287// clang-format off 1288file <<"n_nodes = " <<cgraph -> n_nodes <<"\n" ; 1289file <<" " << std::setw (3 ) <<"nodes" 1290 << std::setw (15 ) <<"shape" 1291 << std::setw (20 ) <<"op" 1292 << std::setw (20 ) <<"name" 1293 << std::setw (3 ) <<" " 1294 << std::setw (62 ) <<"stride" 1295 << std::setw (20 ) <<"buffer_type" 1296 <<"\n" ; 1297for (int i = 0 ;i < cgraph -> n_nodes ;i ++ ) { 1298ggml_tensor * node = cgraph -> nodes [i ]; 1299 1300// Get buffer type name 1301const char * buf_name = "none" ; 1302ggml_backend_buffer_t buf = node -> view_src ?node -> view_src -> buffer :node -> buffer ; 1303if (buf ) { 1304buf_name = ggml_backend_buffer_name (buf ); 1305 } 1306 1307file <<" - " << std::setw (3 ) <<i <<": [ " 1308 << std::setw (5 ) <<node -> ne [0 ] <<", " 1309 << std::setw (5 ) <<node -> ne [1 ] <<", " 1310 << std::setw (5 ) <<node -> ne [2 ] <<", " 1311 << std::setw (5 ) <<node -> ne [3 ] <<"] " 1312 << std::left << std::setw (20 ) <<ggml_op_name (node -> op ) << std::right <<" " 1313 << std::left << std::setw (45 ) <<node -> name << std::right 1314 << std::setw (2 ) <<"[ " 1315 << std::setw (0 ) <<node -> nb [0 ] <<", " 1316 << std::setw (5 ) <<node -> nb [1 ] <<", " 1317 << std::setw (5 ) <<node -> nb [2 ] <<", " 1318 << std::setw (5 ) <<node -> nb [3 ] <<"] " 1319 << std::right << std::setw (15 ) <<buf_name << std::right 1320 <<"\n" ; 1321 1322for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 1323if (auto * src = node -> src [i ]) { 1324// Get buffer type name for source 1325const char * src_buf_name = "none" ; 1326ggml_backend_buffer_t src_buf = src -> view_src ?src -> view_src -> buffer :src -> buffer ; 1327if (src_buf ) { 1328src_buf_name = ggml_backend_buffer_name (src_buf ); 1329 } 1330 1331file << std::setw (10 ) <<" [ " 1332 << std::setw (5 ) <<src -> ne [0 ] <<", " 1333 << std::setw (5 ) <<src -> ne [1 ] <<", " 1334 << std::setw (5 ) <<src -> ne [2 ] <<", " 1335 << std::setw (5 ) <<src -> ne [3 ] <<"] " 1336 << std::setw (12 ) 1337 <<i <<": " << std::left << std::setw (12 ) <<ggml_op_name (src -> op ) << std::right ; 1338file << std::left << std::setw (30 ) <<src -> name << std::right 1339 << std::setw (16 ) <<"[ " 1340 << std::setw (0 ) <<src -> nb [0 ] <<", " 1341 << std::setw (5 ) <<src -> nb [1 ] <<", " 1342 << std::setw (5 ) <<src -> nb [2 ] <<", " 1343 << std::setw (5 ) <<src -> nb [3 ] <<"] " 1344 << std::right << std::setw (15 ) <<src_buf_name << std::right 1345 <<"\n" ; 1346 } 1347 } 1348 } 1349 1350file <<"n_leafs = " <<cgraph -> n_leafs <<"\n" ; 1351for (int i = 0 ;i < cgraph -> n_leafs ;i ++ ) { 1352ggml_tensor * node = cgraph -> leafs [i ]; 1353 1354// Get buffer type name for leaf 1355const char * leaf_buf_name = "none" ; 1356ggml_backend_buffer_t leaf_buf = node -> view_src ?node -> view_src -> buffer :node -> buffer ; 1357if (leaf_buf ) { 1358leaf_buf_name = ggml_backend_buffer_name (leaf_buf ); 1359 } 1360 1361file <<" - " << std::setw (3 ) <<i <<": [ " 1362 << std::setw (5 ) <<node -> ne [0 ] <<", " 1363 << std::setw (5 ) <<node -> ne [1 ] <<"] " 1364 << std::setw (8 ) <<ggml_op_name (node -> op ) <<" " 1365 << std::setw (16 ) <<ggml_get_name (node ) 1366 << std::setw (20 ) <<leaf_buf_name <<"\n" ; 1367 } 1368// clang-format on 1369file <<"========================================\n" ; 1370 1371file .close (); 1372} 1373 1374void print_tensor_address_map (const ggml_cgraph * cgraph ) { 1375 std::map < void * , std::vector < std::string >> address_map ; 1376for (int node_n = 0 ;node_n < cgraph -> n_nodes ;node_n ++ ) { 1377auto * node = cgraph -> nodes [node_n ]; 1378if (node -> data ) { 1379auto it = address_map .find (node -> data ); 1380if (it == address_map .end ()) { 1381address_map [node -> data ]= std::vector < std::string > (); 1382 } 1383address_map [node -> data ].push_back (node -> name ); 1384 } 1385 } 1386for (const auto & pair :address_map ) { 1387 std::cout <<"Address: " <<pair .first << std::endl ; 1388for (const auto & name :pair .second ) { 1389 std::cout <<name <<" ; " ; 1390 } 1391 std::cout << std::endl << std::endl ; 1392 } 1393} 1394 1395ov::Shape GgmlOvDecoder ::get_shape (const ggml_tensor * tensor ) { 1396 std::vector < size_t > shape ; 1397for (int i = GGML_MAX_DIMS - 1 ;i >=0 ;-- i ) { 1398shape .push_back (static_cast < size_t > (tensor -> ne [i ])); 1399 } 1400return shape ; 1401} 1402 1403std::vector < size_t > GgmlOvDecoder ::get_stride (const ggml_tensor * tensor ) { 1404 std::vector < size_t > stride ; 1405for (int i = GGML_MAX_DIMS - 1 ;i >=0 ;-- i ) { 1406stride .push_back (static_cast < size_t > (tensor -> nb [i ])); 1407 } 1408return stride ; 1409} 1410 1411ov::element::Type GgmlOvDecoder ::get_ov_type (const ggml_tensor * tensor ) { 1412switch (tensor -> type ) { 1413case GGML_TYPE_F64 : 1414return ov::element::f64 ; 1415case GGML_TYPE_F32 : 1416return ov::element::f32 ; 1417case GGML_TYPE_F16 : 1418return ov::element::f16 ; 1419case GGML_TYPE_BF16 : 1420return ov::element::bf16 ; 1421case GGML_TYPE_I8 : 1422return ov::element::i8 ; 1423case GGML_TYPE_I16 : 1424return ov::element::i16 ; 1425case GGML_TYPE_I32 : 1426return ov::element::i32 ; 1427case GGML_TYPE_I64 : 1428return ov::element::i64 ; 1429default : 1430return ov::element::dynamic ; 1431 } 1432} 1433 1434ov::PartialShape GgmlOvDecoder ::get_input_shape (int node_idx ,const std::string & name )const { 1435return ov::PartialShape (get_shape (m_node_info_list [node_idx ].node_inputs .at (name ))); 1436} 1437 1438std::vector < size_t > GgmlOvDecoder ::get_input_stride (int node_idx ,const std::string & name )const { 1439return get_stride (m_node_info_list [node_idx ].node_inputs .at (name )); 1440} 1441 1442size_t GgmlOvDecoder ::get_view_input_size (int node_idx ,const std::string & name )const { 1443auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1444if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1445return it -> second .size (); 1446 } 1447return 0 ; 1448} 1449 1450size_t GgmlOvDecoder ::get_view_input_offset (int node_idx ,const std::string & name ,size_t view_index )const { 1451auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1452if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1453if (view_index < it -> second .size ()) { 1454return it -> second [view_index ].second -> view_offs ; 1455 } 1456 } 1457return 0 ; 1458} 1459 1460size_t GgmlOvDecoder ::get_view_input_src_offset (int node_idx ,const std::string & name ,size_t view_index )const { 1461auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1462if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1463if (view_index < it -> second .size ()) { 1464auto * view_tensor = it -> second [view_index ].second ; 1465if (view_tensor && view_tensor -> src [0 ]) { 1466return view_tensor -> src [0 ]-> view_offs ; 1467 } 1468 } 1469 } 1470return 0 ; 1471} 1472 1473std::vector < size_t > GgmlOvDecoder ::get_view_input_stride (int node_idx , 1474const std::string & name , 1475size_t view_index )const { 1476auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1477if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1478if (view_index < it -> second .size ()) { 1479return get_stride (it -> second [view_index ].second ); 1480 } 1481 } 1482return {}; 1483} 1484 1485std::vector < size_t > GgmlOvDecoder ::get_view_input_src_stride (int node_idx , 1486const std::string & name , 1487size_t view_index )const { 1488auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1489if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1490if (view_index < it -> second .size ()) { 1491auto * view_tensor = it -> second [view_index ].second ; 1492if (view_tensor && view_tensor -> src [0 ]) { 1493return get_stride (view_tensor -> src [0 ]); 1494 } 1495 } 1496 } 1497return {}; 1498} 1499 1500ov::Shape GgmlOvDecoder ::get_view_input_ggml_shape (int node_idx ,const std::string & name ,size_t view_index )const { 1501auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1502if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1503if (view_index < it -> second .size ()) { 1504return get_shape (it -> second [view_index ].second ); 1505 } 1506 } 1507return {}; 1508} 1509 1510ov::Shape GgmlOvDecoder ::get_view_input_src_ggml_shape (int node_idx , 1511const std::string & name , 1512size_t view_index )const { 1513auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1514if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1515if (view_index < it -> second .size ()) { 1516auto * view_tensor = it -> second [view_index ].second ; 1517if (view_tensor && view_tensor -> src [0 ]) { 1518return get_shape (view_tensor -> src [0 ]); 1519 } 1520 } 1521 } 1522return {}; 1523} 1524 1525ov::PartialShape GgmlOvDecoder ::get_view_input_ov_shape (int node_idx , 1526const std::string & name , 1527size_t view_index )const { 1528auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1529if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1530if (view_index < it -> second .size ()) { 1531auto * tensor = it -> second [view_index ].second ; 1532 ov::PartialShape shape = ov::PartialShape {get_shape (tensor )}; 1533 1534// Check if this tensor has a dynamic dimension 1535auto dynamic_it = m_node_dynamic_dims .find (tensor ); 1536if (dynamic_it != m_node_dynamic_dims .end ()&& dynamic_it -> second != -1 ) { 1537int dynamic_dim_index = dynamic_it -> second ; 1538// GGML uses reverse indexing, so convert to OpenVINO indexing 1539shape [3 - dynamic_dim_index ]= m_is_static ?get_static_n_tokens () :-1 ; 1540 } 1541 1542return shape ; 1543 } 1544 } 1545return {}; 1546} 1547 1548ov::PartialShape GgmlOvDecoder ::get_view_input_src_ov_shape (int node_idx , 1549const std::string & name , 1550size_t view_index )const { 1551auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1552if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1553if (view_index < it -> second .size ()) { 1554auto * view_tensor = it -> second [view_index ].second ; 1555if (view_tensor && view_tensor -> src [0 ]) { 1556auto * src_tensor = view_tensor -> src [0 ]; 1557 ov::PartialShape shape = ov::PartialShape {get_shape (src_tensor )}; 1558 1559// Check if this tensor has a dynamic dimension 1560auto dynamic_it = m_node_dynamic_dims .find (src_tensor ); 1561if (dynamic_it != m_node_dynamic_dims .end ()&& dynamic_it -> second != -1 ) { 1562int dynamic_dim_index = dynamic_it -> second ; 1563// GGML uses reverse indexing, so convert to OpenVINO indexing 1564shape [3 - dynamic_dim_index ]= m_is_static ?get_static_n_tokens () :-1 ; 1565 } 1566 1567return shape ; 1568 } 1569 } 1570 } 1571return {}; 1572} 1573 1574std::string GgmlOvDecoder ::get_view_input_name (int node_idx ,const std::string & name ,size_t view_index )const { 1575auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1576if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1577if (view_index < it -> second .size ()) { 1578return it -> second [view_index ].first ; 1579 } 1580 } 1581return "" ; 1582} 1583 1584std::string GgmlOvDecoder ::get_view_input_src_name (int node_idx ,const std::string & name ,size_t view_index )const { 1585auto it = m_node_info_list [node_idx ].node_inputs_views .find (name ); 1586if (it != m_node_info_list [node_idx ].node_inputs_views .end ()) { 1587if (view_index < it -> second .size ()) { 1588auto * view_tensor = it -> second [view_index ].second ; 1589if (view_tensor && view_tensor -> src [0 ]) { 1590return get_tensor_ov_name (m_cgraph ,view_tensor -> src [0 ]); 1591 } 1592 } 1593 } 1594return "" ; 1595} 1596 1597ov::element::Type GgmlOvDecoder ::get_input_type (int node_idx ,const std::string & name )const { 1598return get_ov_type (m_node_info_list [node_idx ].node_inputs .at (name )); 1599} 1600 1601size_t GgmlOvDecoder ::get_input_size ()const { 1602return m_model_inputs .size (); 1603} 1604 1605size_t GgmlOvDecoder ::get_input_size (int node_idx )const { 1606return m_node_info_list [node_idx ].node_inputs_names .size (); 1607} 1608 1609std::vector < std::string > GgmlOvDecoder ::get_input_names (int node_idx )const { 1610return m_node_info_list [node_idx ].node_inputs_names ; 1611} 1612 1613ov::PartialShape GgmlOvDecoder ::get_output_shape (int node_idx )const { 1614auto * ggml_tensor = m_node_info_list [node_idx ].node ; 1615return ov::PartialShape (get_shape (ggml_tensor )); 1616} 1617 1618ov::element::Type GgmlOvDecoder ::get_output_type (const int node_idx )const { 1619return get_ov_type (m_node_info_list [node_idx ].node ); 1620} 1621 1622std::vector < size_t > GgmlOvDecoder ::get_output_stride (int node_idx )const { 1623auto * ggml_tensor = m_node_info_list [node_idx ].node ; 1624return get_stride (ggml_tensor ); 1625} 1626 1627std::vector < std::string > GgmlOvDecoder ::get_output_names (int node_idx )const { 1628return {m_node_info_list [node_idx ].node_name }; 1629} 1630 1631std::string GgmlOvDecoder ::get_inplace_op_src (int node_idx )const { 1632auto * node = m_node_info_list [node_idx ].node ; 1633if (!::is_inplace_op (node )|| node -> view_src == nullptr || ggml_nbytes (node )== 0 ) { 1634return "" ; 1635 } 1636const int op_case = m_node_info_list [node_idx ].node_op_case ; 1637if (node -> op == GGML_OP_CPY && (op_case == 1 || op_case == 2 || op_case == 3 )&& 1638m_compute_params .s_copy_active_slot_len == -1 ) { 1639return "" ; 1640 } 1641return get_tensor_ov_name (m_cgraph ,node -> view_src ); 1642} 1643 1644bool GgmlOvDecoder ::is_view_like_alias_of (int node_idx ,const std::string & view_src_name )const { 1645auto * node = m_node_info_list [node_idx ].node ; 1646if (node -> view_src == nullptr || get_tensor_ov_name (m_cgraph ,node -> view_src )!= view_src_name ) { 1647return false; 1648 } 1649return node -> op == GGML_OP_RESHAPE || node -> op == GGML_OP_VIEW ; 1650} 1651 1652const std::string & GgmlOvDecoder ::get_op_name ()const { 1653static const std::string unknown_name = "UNKNOWN_OP_NAME" ; 1654return unknown_name ; 1655} 1656 1657int32_t GgmlOvDecoder ::get_op_dynamic_dim (int node_idx )const { 1658auto it = m_node_dynamic_dims .find (m_node_info_list [node_idx ].node ); 1659if (it == m_node_dynamic_dims .end ()) { 1660return -1 ; 1661 } 1662return it -> second ; 1663} 1664 1665const std::string & GgmlOvDecoder ::get_op_name (int node_idx )const { 1666return m_node_info_list [node_idx ].node_name ; 1667} 1668 1669int32_t * GgmlOvDecoder ::get_input_op_params (int node_idx ,const std::string & name )const { 1670return m_node_info_list [node_idx ].node_inputs .at (name )-> op_params ; 1671} 1672 1673int32_t * GgmlOvDecoder ::get_output_op_params (int node_idx )const { 1674return m_node_info_list [node_idx ].node -> op_params ; 1675} 1676 1677size_t GgmlOvDecoder ::get_output_op_offset (int node_idx )const { 1678return m_node_info_list [node_idx ].node -> view_offs ; 1679} 1680 1681void GgmlOvDecoder ::visit_subgraph (std::function < void (std::shared_ptr < GgmlDecoder > ,int node_idx )> node_visitor )const { 1682for (int node_idx = 0 ;node_idx < m_cgraph -> n_nodes ;node_idx ++ ) { 1683if (m_cgraph -> nodes [node_idx ]-> op == GGML_OP_NONE ) { 1684continue ; 1685 } 1686node_visitor (std::make_shared < GgmlOvDecoder > (* this ),node_idx ); 1687 } 1688} 1689 1690std::string GgmlOvDecoder ::compute_op_type (const ggml_tensor * node ) { 1691switch (node -> op ) { 1692case GGML_OP_UNARY : 1693return std::string ("GGML_UNARY_OP_" )+ ggml_unary_op_name (ggml_get_unary_op (node )); 1694case GGML_OP_GLU : 1695return std::string ("GGML_GLU_OP_" )+ ggml_glu_op_name (ggml_get_glu_op (node )); 1696default : 1697return std::string ("GGML_OP_" )+ ggml_op_name (node -> op ); 1698 } 1699} 1700 1701const std::string & GgmlOvDecoder ::get_op_type (int node_idx )const { 1702return m_node_info_list [node_idx ].node_op_type ; 1703} 1704 1705const std::string & GgmlOvDecoder ::get_op_type ()const { 1706static const std::string unknown_op = "UNKNOWN_GGML_OP" ; 1707return unknown_op ; 1708} 1709 1710void GgmlOvDecoder ::compute_node_dynamic_dims () { 1711auto visit_node = [& ](auto && self ,ggml_tensor * node )-> void { 1712if (!node ) { 1713return ; 1714 } 1715 1716if (node -> op == GGML_OP_CPY ) { 1717m_node_dynamic_dims [node ]= -1 ; 1718 } 1719 1720if (m_node_dynamic_dims .count (node )) { 1721return ; 1722 } 1723for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 1724ggml_tensor * src = node -> src [i ]; 1725if (src == nullptr ) { 1726continue ; 1727 } 1728struct ggml_tensor * root_src = nullptr ; 1729// if (src->org_src) { 1730// root_src = src->org_src; 1731// } 1732if (root_src ) { 1733if (is_inp_tok (root_src ,node )|| is_inp_pos (root_src ,node )|| is_output_idx (root_src ,node )) { 1734m_node_dynamic_dims [root_src ]= 0 ; 1735m_node_dynamic_dims [src ]= m_node_dynamic_dims [root_src ]; 1736continue ; 1737 } 1738self (self ,root_src ); 1739m_node_dynamic_dims [src ]= m_node_dynamic_dims [root_src ]; 1740 }else { 1741if (is_inp_tok (src ,node )|| is_inp_pos (src ,node )|| is_output_idx (src ,node )) { 1742m_node_dynamic_dims [src ]= 0 ; 1743continue ; 1744 } 1745if (node -> op == GGML_OP_VIEW && src -> op == GGML_OP_NONE && !is_stateful ()&& !m_model_is_splitted ) { 1746m_node_dynamic_dims [src ]= 1 ; 1747continue ; 1748 } 1749self (self ,src ); 1750 } 1751 } 1752switch (node -> op ) { 1753case GGML_OP_NONE : 1754m_node_dynamic_dims [node ]= -1 ; 1755break ; 1756case GGML_OP_GET_ROWS : 1757m_node_dynamic_dims [node ]= -1 ; 1758if (m_node_dynamic_dims [node -> src [1 ]]!= -1 ) { 1759auto dynamic_dim_idx = m_node_dynamic_dims [node -> src [1 ]]; 1760if (dynamic_dim_idx == 0 ) { 1761m_node_dynamic_dims [node ]= 1 ; 1762 }else { 1763auto dynamic_dim_stride = node -> src [1 ]-> nb [dynamic_dim_idx ] /ggml_type_size (node -> src [1 ]-> type )* 1764ggml_type_size (node -> src [0 ]-> type ); 1765for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 1766if (dynamic_dim_stride == node -> src [0 ]-> nb [i ]) { 1767m_node_dynamic_dims [node ]= i ; 1768break ; 1769 } 1770 } 1771 } 1772// OPENVINO_ASSERT(dynamic_dim_value == node->ne[m_node_dynamic_dims[node]], 1773// "Dynamic dim value mismatch for node: " + std::string(node->name) + 1774// " and its src[1]: " + std::string(node->src[1]->name)); 1775 } 1776break ; 1777case GGML_OP_MUL : 1778case GGML_OP_MUL_MAT : 1779m_node_dynamic_dims [node ]= -1 ; 1780if (m_node_dynamic_dims [node -> src [0 ]]!= -1 ) { 1781m_node_dynamic_dims [node ]= m_node_dynamic_dims [node -> src [0 ]]; 1782 } 1783if (m_node_dynamic_dims [node -> src [1 ]]!= -1 ) { 1784m_node_dynamic_dims [node ]= m_node_dynamic_dims [node -> src [1 ]]; 1785 } 1786break ; 1787case GGML_OP_PERMUTE : 1788m_node_dynamic_dims [node ]= -1 ; 1789if (m_node_dynamic_dims [node -> src [0 ]]!= -1 ) { 1790auto dynamic_dim_idx = m_node_dynamic_dims [node -> src [0 ]]; 1791// auto dynamic_dim_value = node->src[0]->ne[dynamic_dim_idx]; 1792for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 1793if (node -> op_params [i ]== dynamic_dim_idx ) { 1794m_node_dynamic_dims [node ]= i ; 1795break ; 1796 } 1797 } 1798// OPENVINO_ASSERT(dynamic_dim_value == node->ne[m_node_dynamic_dims[node]], 1799// "Dynamic dim value mismatch for node: " + std::string(node->name) + 1800// " and its src[0]: " + std::string(node->src[0]->name)); 1801 } 1802break ; 1803case GGML_OP_VIEW : { 1804// Use stride-based matching: the stride of a VIEW dimension directly 1805// encodes which source dimension it indexes into, so it uniquely 1806// identifies the dynamic dim even when two dims share the same size. 1807m_node_dynamic_dims [node ]= -1 ; 1808if (m_node_dynamic_dims [node -> src [0 ]]!= -1 ) { 1809if (node -> src [0 ]-> op == GGML_OP_NONE ) { 1810m_node_dynamic_dims [node ]= m_node_dynamic_dims [node -> src [0 ]]; 1811break ; 1812 } 1813auto dynamic_dim_idx = m_node_dynamic_dims [node -> src [0 ]]; 1814auto dynamic_dim_value = node -> src [0 ]-> ne [dynamic_dim_idx ]; 1815auto dynamic_dim_stride = 1816node -> src [0 ]-> nb [dynamic_dim_idx ] /ggml_type_size (node -> src [0 ]-> type )* ggml_type_size (node -> type ); 1817for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 1818if (node -> nb [i ]== dynamic_dim_stride ) { 1819m_node_dynamic_dims [node ]= i ; 1820break ; 1821 } 1822 } 1823if (m_node_dynamic_dims [node ]!= -1 && dynamic_dim_value != node -> ne [m_node_dynamic_dims [node ]]) { 1824m_node_dynamic_dims [node ]= -1 ; 1825GGML_LOG_WARN ("ggml-openvino: dynamic dim value mismatch for VIEW node '%s', src[0]: '%s'\n" , 1826node -> name ,node -> src [0 ]-> name ); 1827 } 1828 } 1829break ; 1830 } 1831case GGML_OP_TRANSPOSE : 1832case GGML_OP_RESHAPE : { 1833if (is_same_shape (node -> src [0 ],node )) { 1834m_node_dynamic_dims [node ]= m_node_dynamic_dims [node -> src [0 ]]; 1835break ; 1836 } 1837// RESHAPE requires src[0] to be contiguous, so both src and result 1838// have standard compact strides: nb[i] = type_size * prod(ne[0..i-1]). 1839// Match src->nb[dynamic_dim] against result->nb[i] to find the output 1840// dimension whose flat-memory boundary aligns with the source dynamic 1841// boundary. This is unambiguous (result strides are strictly monotone) 1842// and handles merged-lower-dim cases that ne-value matching misses. 1843m_node_dynamic_dims [node ]= -1 ; 1844if (m_node_dynamic_dims [node -> src [0 ]]!= -1 ) { 1845auto dynamic_dim_idx = m_node_dynamic_dims [node -> src [0 ]]; 1846auto dynamic_dim_stride = node -> src [0 ]-> nb [dynamic_dim_idx ]; 1847for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 1848if (node -> nb [i ]== dynamic_dim_stride && node -> ne [i ]== node -> src [0 ]-> ne [dynamic_dim_idx ]) { 1849m_node_dynamic_dims [node ]= i ; 1850break ; 1851 } 1852 } 1853if (m_node_dynamic_dims [node ]== -1 ) { 1854GGML_LOG_WARN ("ggml-openvino: cannot determine dynamic dim for RESHAPE node '%s'\n" ,node -> name ); 1855 } 1856 } 1857break ; 1858 } 1859case GGML_OP_FLASH_ATTN_EXT : { 1860// Output shape is hard-coded in ggml_flash_attn_ext as: 1861// ne = { v->ne[0], q->ne[2], q->ne[1], q->ne[3] } 1862// i.e. output dim 0 <- v dim 0 (head_size, static) 1863// output dim 1 <- q dim 2 (n_heads, static) 1864// output dim 2 <- q dim 1 (n_tokens, potentially dynamic) 1865// output dim 3 <- q dim 3 (batch, static) 1866// Using the fixed q-dim -> output-dim mapping table. 1867// q is src[0]; the mapping from q's dynamic dim to the output dim is: 1868// q dim 1 -> output dim 2 1869// q dim 2 -> output dim 1 1870// q dim 3 -> output dim 3 1871// q dim 0 -> output dim 0 (head_size axis, unlikely to be dynamic) 1872constexpr int q_to_out [GGML_MAX_DIMS ]= {0 ,2 ,1 ,3 }; 1873m_node_dynamic_dims [node ]= -1 ; 1874if (m_node_dynamic_dims [node -> src [0 ]]!= -1 ) { 1875auto q_dynamic_dim = m_node_dynamic_dims [node -> src [0 ]]; 1876m_node_dynamic_dims [node ]= q_to_out [q_dynamic_dim ]; 1877 } 1878break ; 1879 } 1880case GGML_OP_CONT : 1881m_node_dynamic_dims [node ]= -1 ; 1882if (m_node_dynamic_dims [node -> src [0 ]]!= -1 ) { 1883auto dynamic_dim_idx = m_node_dynamic_dims [node -> src [0 ]]; 1884if (ggml_are_same_shape (node ,node -> src [0 ])) { 1885m_node_dynamic_dims [node ]= dynamic_dim_idx ; 1886 }else { 1887size_t src_logical_nb [GGML_MAX_DIMS ]; 1888src_logical_nb [0 ]= ggml_type_size (node -> src [0 ]-> type ); 1889src_logical_nb [1 ]= src_logical_nb [0 ]* (node -> src [0 ]-> ne [0 ] /ggml_blck_size (node -> src [0 ]-> type )); 1890for (int i = 2 ;i < GGML_MAX_DIMS ;i ++ ) { 1891src_logical_nb [i ]= src_logical_nb [i - 1 ]* node -> src [0 ]-> ne [i - 1 ]; 1892 } 1893 1894auto dynamic_dim_stride = src_logical_nb [dynamic_dim_idx ] /ggml_type_size (node -> src [0 ]-> type )* 1895ggml_type_size (node -> type ); 1896int matched_dim_count = 0 ; 1897int first_matched_dim = -1 ; 1898for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 1899if (node -> nb [i ]== dynamic_dim_stride && node -> ne [i ]== node -> src [0 ]-> ne [dynamic_dim_idx ]) { 1900if (first_matched_dim == -1 ) { 1901first_matched_dim = i ; 1902 } 1903m_node_dynamic_dims [node ]= i ; 1904matched_dim_count ++ ; 1905 } 1906 } 1907if (matched_dim_count > 1 && node -> src [0 ]-> ne [dynamic_dim_idx ]== 1 ) { 1908// Single-token capture: every trailing dim is size 1 with the same stride, so 1909// the match is ambiguous. The lowest index is the real axis; the rest are 1910// ggml's size-1 padding. Bailing out here would bake the captured token count 1911// into the static prefill model, which then runs with a different one. 1912m_node_dynamic_dims [node ]= first_matched_dim ; 1913 }else if (matched_dim_count != 1 ) { 1914m_node_dynamic_dims [node ]= -1 ; 1915GGML_LOG_WARN ("ggml-openvino: cannot determine dynamic dim for CONT node '%s', src[0]: '%s'\n" , 1916node -> name ,node -> src [0 ]-> name ); 1917 } 1918 } 1919 } 1920break ; 1921case GGML_OP_CONCAT : 1922for (int i = 0 ;i < GGML_MAX_DIMS ;i ++ ) { 1923if (node -> src [0 ]-> ne [i ]!= node -> ne [i ]) { 1924m_node_dynamic_dims [node ]= i ; 1925break ; 1926 } 1927 } 1928break ; 1929case GGML_OP_SSM_CONV : 1930case GGML_OP_GATED_DELTA_NET : 1931m_node_dynamic_dims [node ]= 1 ; 1932break ; 1933case GGML_OP_RMS_NORM : 1934case GGML_OP_L2_NORM : 1935case GGML_OP_NORM : 1936case GGML_OP_ADD : 1937case GGML_OP_SUB : 1938case GGML_OP_GLU : 1939case GGML_OP_ROPE : 1940case GGML_OP_SCALE : 1941case GGML_OP_SOFT_MAX : 1942case GGML_OP_ARGSORT : 1943case GGML_OP_ADD_ID : 1944case GGML_OP_UNARY : 1945case GGML_OP_CUMSUM : 1946case GGML_OP_FILL : 1947case GGML_OP_SET : 1948case GGML_OP_DIAG : 1949case GGML_OP_TRI : 1950case GGML_OP_REPEAT : 1951// Shape-preserving elementwise ops: the dynamic dim is unchanged from src[0]. 1952// DIV/CLAMP are used in the MoE routing-weight normalization 1953// (sum_rows -> clamp -> div). If they are left untracked here the dynamic 1954// (token) dim is lost there, the captured prefill token count gets baked into 1955// the downstream reshapes, and every decoder layer after layer 0 turns static 1956// (which then triggers the GPU in-place-concat KV-cache corruption). 1957case GGML_OP_DIV : 1958case GGML_OP_CLAMP : 1959case GGML_OP_PAD : 1960m_node_dynamic_dims [node ]= m_node_dynamic_dims [node -> src [0 ]]; 1961break ; 1962case GGML_OP_SUM_ROWS : 1963// SUM_ROWS reduces ggml axis 0 to size 1 and preserves all other axes, so the 1964// dynamic dim is preserved unless it was axis 0 (then it is summed away). 1965m_node_dynamic_dims [node ]= 1966 (m_node_dynamic_dims [node -> src [0 ]]== 0 ) ?-1 :m_node_dynamic_dims [node -> src [0 ]]; 1967break ; 1968case GGML_OP_MUL_MAT_ID : 1969case GGML_OP_SOLVE_TRI : 1970m_node_dynamic_dims [node ]= m_node_dynamic_dims [node -> src [1 ]]; 1971break ; 1972case GGML_OP_CPY : 1973case GGML_OP_SET_ROWS : 1974m_node_dynamic_dims [node ]= -1 ; 1975break ; 1976case GGML_OP_IM2COL : { 1977m_node_dynamic_dims [node ]= -1 ; 1978if (m_node_dynamic_dims [node -> src [1 ]]!= -1 ) { 1979const bool is_2D = node -> op_params [6 ]== 1 ; 1980const int src_dyn = m_node_dynamic_dims [node -> src [1 ]]; 1981if (is_2D ) { 1982if (src_dyn == 0 ) { 1983m_node_dynamic_dims [node ]= 1 ;// IW -> OW 1984 }else if (src_dyn == 1 ) { 1985m_node_dynamic_dims [node ]= 2 ;// IH -> OH 1986 }else if (src_dyn == 3 ) { 1987m_node_dynamic_dims [node ]= 3 ;// N -> N 1988 } 1989 }else { 1990if (src_dyn == 0 ) { 1991m_node_dynamic_dims [node ]= 1 ;// IW -> OW 1992 }else if (src_dyn == 2 ) { 1993m_node_dynamic_dims [node ]= 2 ;// N -> N (1D: b->ne[2] is the batch/channel dim) 1994 } 1995 } 1996if (m_node_dynamic_dims [node ]!= -1 ) { 1997OPENVINO_ASSERT (node -> src [1 ]-> ne [src_dyn ]== node -> ne [m_node_dynamic_dims [node ]], 1998"Dynamic dim value mismatch for IM2COL node: " + std::string (node -> name )+ 1999" and its src[1]: " + std::string (node -> src [1 ]-> name )); 2000 } 2001 } 2002break ; 2003 } 2004default : 2005GGML_LOG_DEBUG ("ggml-openvino: compute_node_dynamic_dims: unhandled op %s for node '%s'\n" , 2006ggml_op_name (node -> op ),node -> name ); 2007break ; 2008 } 2009 }; 2010 2011for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 2012ggml_tensor * node = m_cgraph -> nodes [i ]; 2013visit_node (visit_node ,node ); 2014 } 2015 2016// print the nodes in m_cgraph name & shape with the dynamic dim (the dynamic dim is the dimension with -1 in m_node_dynamic_dims) for debugging 2017if (0 ) { 2018for (int i = 0 ;i < m_cgraph -> n_nodes ;i ++ ) { 2019ggml_tensor * node = m_cgraph -> nodes [i ]; 2020int dynamic_dim = m_node_dynamic_dims [node ]; 2021 std::cout <<"[" <<i <<"] " <<"node_name: " <<node -> name <<" op: " <<ggml_op_name (node -> op ) 2022 <<" shape: [" ; 2023for (int j = 0 ;j < 4 ;j ++ ) { 2024if (j == dynamic_dim ) { 2025 std::cout <<"*" ; 2026 }else { 2027 std::cout <<node -> ne [j ]; 2028 } 2029if (j < 3 ) { 2030 std::cout <<", " ; 2031 } 2032 } 2033 std::cout <<"]" << std::endl ; 2034// print the src name & shape with the dynamic dim for debugging 2035for (int j = 0 ;j < GGML_MAX_SRC ;j ++ ) { 2036ggml_tensor * src = node -> src [j ]; 2037if (src == nullptr ) { 2038continue ; 2039 } 2040int src_dynamic_dim = m_node_dynamic_dims [src ]; 2041 std::cout <<" [" <<j <<"] src_name: " <<src -> name <<" [" ; 2042for (int k = 0 ;k < 4 ;k ++ ) { 2043if (k == src_dynamic_dim ) { 2044 std::cout <<"*" ; 2045 }else { 2046 std::cout <<src -> ne [k ]; 2047 } 2048if (k < 3 ) { 2049 std::cout <<", " ; 2050 } 2051 } 2052 std::cout <<"]" << std::endl ; 2053 } 2054 std::cout << std::endl ; 2055 } 2056 } 2057}