char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
511f9c137
char/aliceai-t5-moe
1#include "translate_session.h" 2 3#include "ggml-impl.h" 4#include "ggml-openvino/ggml-openvino-extra.h" 5#include "ggml-openvino/openvino/node_context.h" 6#include "ggml-openvino/openvino/utils.h" 7#include "input_model.h" 8#include "pass/fuse_to_conv.h" 9#include "pass/mark_decompression_convert_constant_folding.h" 10#include "pass/mark_dequantization_subgraph.h" 11#include "pass/squeeze_matmul.h" 12#include "rt_info/weightless_caching_attributes.hpp" 13 14#include <algorithm> 15#include <cstdint> 16#include <cstdlib> 17#include <map> 18#include <memory> 19#include <openvino/core/node.hpp> 20#include <openvino/core/preprocess/pre_post_process.hpp> 21#include <openvino/core/shape.hpp> 22#include <openvino/core/type/element_type.hpp> 23#include <openvino/op/add.hpp> 24#include <openvino/op/broadcast.hpp> 25#include <openvino/op/concat.hpp> 26#include <openvino/op/convert.hpp> 27#include <openvino/op/convert_like.hpp> 28#include <openvino/op/cos.hpp> 29#include <openvino/op/divide.hpp> 30#include <openvino/op/gather.hpp> 31#include <openvino/op/multiply.hpp> 32#include <openvino/op/parameter.hpp> 33#include <openvino/op/range.hpp> 34#include <openvino/op/reshape.hpp> 35#include <openvino/op/result.hpp> 36#include <openvino/op/sin.hpp> 37#include <openvino/op/slice.hpp> 38#include <openvino/op/squeeze.hpp> 39#include <openvino/op/strided_slice.hpp> 40#include <openvino/op/transpose.hpp> 41#include <openvino/op/unsqueeze.hpp> 42#include <openvino/pass/constant_folding.hpp> 43#include <openvino/pass/make_stateful.hpp> 44#include <sstream> 45 46namespace ov { 47namespace frontend { 48namespace ggml { 49 50using namespace ov::op ; 51 52namespace { 53 54std::shared_ptr < ov::op::v0::Parameter > create_parameter (const std::string & name , 55const ModelInputInfo & input_info ) { 56auto param_node = std::make_shared < ov::op::v0::Parameter > (input_info .type ,input_info .shape ); 57param_node -> set_friendly_name (name ); 58param_node -> output (0 ).get_tensor ().set_names ({name }); 59return param_node ; 60} 61 62std::shared_ptr < ov::Node > create_extra_input (const std::string & name ,const ModelExtraInputInfo & input_info ) { 63if (input_info .is_parameter ) { 64auto param_node = std::make_shared < ov::op::v0::Parameter > (input_info .type ,input_info .shape ); 65param_node -> set_friendly_name (name ); 66param_node -> output (0 ).get_tensor ().set_names ({name }); 67return param_node ; 68 } 69 70auto constant = std::make_shared < ov::op::v0::Constant > (input_info .type ,input_info .shape , 71 std::vector < int64_t > {input_info .value }); 72constant -> set_friendly_name (name ); 73return constant ; 74} 75 76ov::pass::MakeStateful ::ParamResPairs get_kv_param_res_pairs ( 77const std::shared_ptr < ov::Model > & model , 78const std::map < std::string , std::string > & kv_param_res_names ) { 79 ov::pass::MakeStateful ::ParamResPairs pairs ; 80const auto & params = model -> get_parameters (); 81const auto & results = model -> get_results (); 82 83for (const auto & param_res :kv_param_res_names ) { 84const auto & param_name = param_res .first ; 85const auto & res_name = param_res .second ; 86 87auto param_it = std::find_if (params .begin (),params .end (), [& ](const std::shared_ptr < v0::Parameter > & node ) { 88return node -> get_friendly_name ()== param_name ; 89 }); 90 91OPENVINO_ASSERT (param_it != params .end (),"The tensor name " ,param_name , 92" is not associated with any of " 93"Parameters in the network." ); 94 95auto res_it = std::find_if (results .begin (),results .end (), [& ](const std::shared_ptr < v0::Result > & node ) { 96return node -> get_friendly_name ()== res_name ; 97 }); 98 99OPENVINO_ASSERT (res_it != results .end (),"The tensor name " ,res_name , 100" is not associated with any of " 101"Results in the network." ); 102 103 std::shared_ptr < ov::op::v0::Parameter > param = * param_it ; 104 std::shared_ptr < ov::op::v0::Result > res = * res_it ; 105pairs .emplace_back (param ,res ); 106 } 107return pairs ; 108} 109 110void add_sliced_mask_stateful (TensorMap & tensor_map ) { 111auto create_sliced_mask = [& ](const std::string & mask_name ,const std::string & sliced_name ) { 112if ((tensor_map .find (mask_name )!= tensor_map .end ())&& 113 (tensor_map .find ("token_len_per_seq" )!= tensor_map .end ())&& 114 (tensor_map .find ("inp_pos" )!= tensor_map .end ())) { 115auto token_len_per_seq = tensor_map .at ("token_len_per_seq" ).get_node_shared_ptr (); 116auto mask = tensor_map .at (mask_name ).get_node_shared_ptr (); 117 std::shared_ptr < ov::Node > mask_sliced = mask ; 118auto one = ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {1 }); 119auto zero = ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {0 }); 120auto three = ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {3 }); 121auto neg_one = ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {-1 }); 122 123auto step = ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {1 }); 124auto axes = ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {-1 }); 125 126auto inp_pos = tensor_map .at ("inp_pos" ).get_node_shared_ptr (); 127auto last_inp_pos = std::make_shared < ov::op::v8::Gather > (inp_pos ,neg_one ,three ); 128auto last_inp_pos_1d = std::make_shared < ov::op::v1::Reshape > ( 129last_inp_pos , ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {1 }), false); 130auto last_inp_pos_cvt = std::make_shared < ov::op::v0::Convert > (last_inp_pos_1d , ov::element::i64 ); 131auto last_inp_pos_inc = std::make_shared < ov::op::v1::Add > (last_inp_pos_cvt ,one ); 132 133mask_sliced = std::make_shared < ov::op::v8::Slice > (mask ,zero ,last_inp_pos_inc ,step ,axes ); 134mask_sliced = std::make_shared < ov::op::v0::Convert > (mask_sliced , ov::element::f16 ); 135mask_sliced -> set_friendly_name (sliced_name ); 136 137tensor_map .insert ({sliced_name ,mask_sliced -> output (0 )}); 138 } 139 }; 140 141create_sliced_mask ("self_kq_mask" ,"KQ_mask_sliced" ); 142create_sliced_mask ("KQ_mask" ,"KQ_mask_sliced" ); 143create_sliced_mask ("self_kq_mask_swa" ,"KQ_mask_swa_sliced" ); 144} 145 146void add_rope_sin_cos (TensorMap & tensor_map ,GgmlDecoder & ggml_model_decoder ) { 147// When ROPE ops in the graph have divergent op_params (e.g. gemma4's mixed 148// SWA/non-SWA layers with different n_dims or freq_base), a shared sin/cos 149// precompute cannot broadcast across every ROPE use. Skip it here and let 150// translate_rope() build sin/cos per-op from its own op_params. 151if (ggml_model_decoder .has_mixed_rope_params ()) { 152return ; 153 } 154int32_t * rope_params = ggml_model_decoder .get_rope_params (); 155if (tensor_map .find ("inp_pos" )== tensor_map .end ()|| rope_params == nullptr ) { 156return ; 157 } 158auto inp_pos = tensor_map .at ("inp_pos" ).get_node_shared_ptr (); 159 std::shared_ptr < ov::Node > rope_freqs_weight ; 160if (tensor_map .find ("rope_freqs.weight" )!= tensor_map .end ()) { 161rope_freqs_weight = tensor_map .at ("rope_freqs.weight" ).get_node_shared_ptr (); 162 } 163 164auto sin_cos = make_sin_cos (rope_params ,inp_pos ,rope_freqs_weight ); 165auto sin_theta = sin_cos .first ; 166auto cos_theta = sin_cos .second ; 167 168cos_theta .get_node_shared_ptr ()-> set_friendly_name ("rope_cos" ); 169sin_theta .get_node_shared_ptr ()-> set_friendly_name ("rope_sin" ); 170tensor_map .insert ({"rope_cos" ,cos_theta }); 171tensor_map .insert ({"rope_sin" ,sin_theta }); 172} 173 174// Create common patterns 175void preprocess (TensorMap & tensor_map ,GgmlDecoder & ggml_model_decoder ) { 176if (ggml_model_decoder .is_stateful ()) { 177add_sliced_mask_stateful (tensor_map ); 178 } 179// This optimization is error-prone 180// add_rope_sin_cos(tensor_map, ggml_model_decoder); 181} 182 183}// namespace 184 185TranslateSession ::TranslateSession (const frontend::InputModel ::Ptr & input_model , 186const std::unordered_map < std::string ,CreatorFunction > & translator_map , 187bool naive ) : 188m_input_model (input_model ), 189m_translator_map (translator_map ), 190m_ov_model (nullptr ), 191m_naive (naive ) {} 192 193std::shared_ptr < Model > TranslateSession ::get_converted_model () { 194if (m_ov_model ) { 195return m_ov_model ; 196 } 197m_ov_model = translate_graph (m_input_model ); 198return m_ov_model ; 199} 200 201std::shared_ptr < Model > TranslateSession ::translate_graph (const frontend::InputModel ::Ptr & input_model ) { 202 ov::ParameterVector params ; 203 ov::ResultVector results ; 204auto tensor_map = std::make_shared < TensorMap > (); 205 std::shared_ptr < Model > resulting_model ; 206 207const auto & ggml_model = std::dynamic_pointer_cast < InputModel > (input_model ); 208 std::shared_ptr < GgmlDecoder > ggml_model_decoder = ggml_model -> get_model_decoder (); 209 210for (const auto & it :ggml_model_decoder -> get_model_inputs ()) { 211auto param_node = create_parameter (it .first ,it .second ); 212params .push_back (param_node ); 213 (* tensor_map )[it .first ]= param_node ; 214 } 215 216for (const auto & it :ggml_model_decoder -> get_model_extra_inputs ()) { 217auto input_node = create_extra_input (it .first ,it .second ); 218if (it .second .is_parameter ) { 219params .push_back (std::dynamic_pointer_cast < ov::op::v0::Parameter > (input_node )); 220 } 221 (* tensor_map )[it .first ]= input_node ; 222 } 223 224for (const auto & it :ggml_model_decoder -> get_model_weights ()) { 225 (* tensor_map )[it .first ]= it .second ; 226 } 227 228auto translate_node = [& ](const std::shared_ptr < GgmlDecoder > & decoder ,int node_idx ) { 229auto operation_type = decoder -> get_op_type (node_idx ); 230if (operation_type == "GGML_OP_NONE" ) { 231return ov::OutputVector {}; 232 } 233 234auto it = m_translator_map .find (operation_type ); 235FRONT_END_OP_CONVERSION_CHECK (it != m_translator_map .end (),"Translation for operation type " ,operation_type , 236" is not implemented." ); 237NodeContext node_context (decoder ,tensor_map ,node_idx ,this ); 238 ov::OutputVector converted_outputs = it -> second (node_context ); 239 240const auto & node_output_names = decoder -> get_output_names (node_idx ); 241FRONT_END_OP_CONVERSION_CHECK (node_output_names .size ()== converted_outputs .size (),"Number of " , 242operation_type ," outputs greater than number of converted outputs, which are " , 243node_output_names .size ()," and " ,converted_outputs .size ()," respectively." ); 244 245for (size_t i = 0 ;i < node_output_names .size ();++ i ) { 246auto output_name = node_output_names [i ]; 247if (i < converted_outputs .size ()&& converted_outputs [i ].get_node_shared_ptr ()!= nullptr ) { 248 (* tensor_map )[output_name ]= converted_outputs [i ]; 249 } 250 } 251return converted_outputs ; 252 }; 253 254// To handle cases like this 255// 3: [ 18432, 1, 1, 1] RESHAPE cache_r_l0 (reshaped)#3 256// [ 18432, 1, 1, 1] 0: NONE cache_r_l0 257// 4: [ 0, 1, 1, 1] VIEW cache_r_l0 (reshaped) (view)#4 258// [ 18432, 1, 1, 1] 0: RESHAPE cache_r_l0 (reshaped)#3 259// 5: [ 0, 1, 1, 1] SCALE cache_r_l0 (reshaped) (view) (view)#5 260// [ 0, 1, 1, 1] 0: VIEW cache_r_l0 (reshaped) (view)#4 261// 6: [ 1, 1, 1, 1] VIEW (view)#6 262// [ 1, 1, 1, 1] 0: NONE leaf_5 263// 7: [ 18432, 1, 1, 1] GET_ROWS conv_states-0#7 264// [ 18432, 1, 1, 1] 0: RESHAPE cache_r_l0 (reshaped)#3 265// [ 1, 1, 1, 1] 1: VIEW (view)#6 266// The scale is in-place which modifies cache_r_l0 (reshaped)#3 267// The translation of scale overwrites cache_r in the tensor_map, 268// but we also need to overwrite the old cache_r_l0 (reshaped)#3 269auto refresh_inplace_aliases = [& ](const std::shared_ptr < GgmlDecoder > & decoder ,int inplace_node_idx , 270const std::string & view_src_name ) { 271for (int node_idx = 0 ;node_idx < inplace_node_idx ;node_idx ++ ) { 272if (decoder -> is_view_like_alias_of (node_idx ,view_src_name )) { 273translate_node (decoder ,node_idx ); 274 } 275 } 276 }; 277 278auto node_visitor = [& ](std::shared_ptr < GgmlDecoder > decoder ,int node_idx ) { 279auto converted_outputs = translate_node (decoder ,node_idx ); 280if (converted_outputs .empty ()) { 281return ; 282 } 283const auto inplace_src = decoder -> get_inplace_op_src (node_idx ); 284if (inplace_src .empty ()) { 285return ; 286 } 287if (converted_outputs [0 ].get_node_shared_ptr ()!= nullptr ) { 288 (* tensor_map )[inplace_src ]= converted_outputs [0 ]; 289 } 290refresh_inplace_aliases (decoder ,node_idx ,inplace_src ); 291 }; 292 293if (!m_naive ) { 294preprocess (* tensor_map ,* ggml_model_decoder ); 295 } 296ggml_model_decoder -> visit_subgraph (node_visitor ); 297 298for (const auto & name :ggml_model_decoder -> get_model_output_names ()) { 299FRONT_END_GENERAL_CHECK (tensor_map -> find (name )!= tensor_map -> end (), 300"Output name not found in tensor map: " ,name ); 301auto result = std::make_shared < v0::Result > (tensor_map -> at (name )); 302result -> set_friendly_name (name ); 303results .push_back (result ); 304 } 305 306// Debug-only hook: GGML_OPENVINO_DEBUG_NODE=<name1>,<name2>,... adds extra 307// Result nodes for arbitrary intermediate tensors (looked up by name in 308// tensor_map), on top of the real model outputs above. These debug 309// Results are deliberately NOT added to ggml_decoder's model outputs, so 310// the caller (ov_graph_compute_dynamic in utils.cpp) will not bind them 311// to any ggml tensor buffer -- OpenVINO allocates its own tensor for 312// them. This avoids the risk of reading a ggml buffer that has since 313// been overwritten by a later in-place op (ggml aggressively reuses 314// buffers), which can happen if trying to inspect an intermediate value 315// via GGML_OPENVINO_DEBUG_OUTPUT by hacking it into a real output. 316// 317// tensor_map keys are usually the plain ggml tensor name (e.g. "embd"), 318// but tensors that are recomputed multiple times in the same cgraph 319// (GGML_TENSOR_FLAG_COMPUTE) are disambiguated with a "#<hash>" suffix 320// (e.g. "cache_k_l0#4853", see get_tensor_ov_name()) which is not 321// predictable ahead of time. To keep the env var usable, a requested 322// name is matched either exactly, or as the "name" part before "#" of a 323// suffixed key (first match wins; ambiguous requests should include the 324// full "name#hash" form seen in a previous run's log/dump). 325if (const char * debug_nodes = ggml_openvino_getenv_str ("GGML_OPENVINO_DEBUG_NODE" )) { 326 std::stringstream ss (debug_nodes ); 327 std::string name ; 328while (std::getline (ss ,name ,',' )) { 329auto it = tensor_map -> find (name ); 330if (it == tensor_map -> end ()) { 331it = std::find_if (tensor_map -> begin (),tensor_map -> end (), [& ](const auto & entry ) { 332return entry .first .compare (0 ,name .size (),name )== 0 && entry .first .size ()> name .size ()&& 333entry .first [name .size ()]== '#' ; 334 }); 335 } 336if (it == tensor_map -> end ()) { 337GGML_LOG_WARN ("GGML_OPENVINO_DEBUG_NODE: node '%s' not found in tensor map, skipping\n" ,name .c_str ()); 338continue ; 339 } 340auto result = std::make_shared < v0::Result > (it -> second ); 341result -> set_friendly_name ("__debug_" + it -> first ); 342results .push_back (result ); 343 } 344 } 345 346 ov::ParameterVector used_params ; 347for (const auto & param :params ) { 348if (!param -> output (0 ).get_target_inputs ().empty ()) { 349used_params .push_back (param ); 350 } 351 } 352// if (auto diff = params.size() - used_params.size()) { 353// GGML_LOG_INFO("%zu parameters are not used in the model.", diff); 354// } 355resulting_model = std::make_shared < Model > (results ,used_params ); 356 357apply_transformations (resulting_model ); 358 359// Set WeightlessCacheAttribute on large constants to avoid unnecessary memory copies 360// in the NPUW plugin. Without this attribute, NPUW's LazyTensor constructor 361// (lazy_tensor.cpp, op::Const::Const) will memcpy every constant "in case export 362// occurs", doubling memory usage per compile_model call. 363// 364// The bin_offset field serves as a unique key (not a real file offset) — this is 365// the same convention the GPU plugin uses for non-IR models (see 366// Plugin::set_weightless_cache_attributes in intel_gpu/src/plugin/plugin.cpp). 367// Each constant must have a distinct bin_offset, otherwise GPU's weightless cache 368// import will map multiple constants to the same data. 369// 370// Small constants (< 16 elements) are excluded since they may be introduced by 371// optimization patterns and the overhead is negligible. 372// 373// Note: use shape_size() rather than byte_size()/element_type().size() - GatherMatmul's default 374// bias is a Constant(element::dynamic, Shape{0}), whose element_type().size() is 0 and would 375// divide by zero. 376size_t offset = 0 ; 377for (auto & node :resulting_model -> get_ordered_ops ()) { 378if (auto cnst = ov::as_type_ptr < ov::op::v0::Constant > (node );cnst && ov::shape_size (cnst -> get_shape ()) >=16 ) { 379auto & rt_info = cnst -> get_rt_info (); 380if (rt_info .find (ov::WeightlessCacheAttribute ::get_type_info_static ())== rt_info .end ()) { 381rt_info [ov::WeightlessCacheAttribute ::get_type_info_static ()]= 382 ov::WeightlessCacheAttribute (cnst -> get_byte_size (),offset ++ ,cnst -> get_element_type ()); 383 } 384 } 385 } 386return resulting_model ; 387} 388 389std::shared_ptr < Model > TranslateSession ::apply_transformations (std::shared_ptr < Model > model ) { 390auto ggml_model_decoder = std::dynamic_pointer_cast < InputModel > (m_input_model )-> get_model_decoder (); 391 { 392 ov::pass::Manager manager ; 393manager .set_per_pass_validation (true); 394manager .register_pass < ov::pass::MarkCompressedFloatConstants > (); 395// Marks the Convert/Subtract/Multiply nodes of our GatherMatmul dequantization chain 396// (make_int4_weights/make_int8_weights, for_gather_matmul=true) with disable_constant_folding, 397// so it survives ConstantFolding regardless of whether the target plugin's own 398// is_decompression_multiply() recognizes GatherMatmul as a valid consumer. 399manager .register_pass < ov::pass::MarkDequantization > ( 400 std::vector < ov::element::Type > {ov::element::u8 , ov::element::i8 , ov::element::u4 , ov::element::i4 }); 401manager .register_pass < pass::FuseToConv > (); 402 403if (ggml_model_decoder -> is_stateful ()) { 404const auto kv_param_res_names = ggml_model_decoder -> get_kv_param_res_names (); 405const auto kv_param_res_pairs = get_kv_param_res_pairs (model ,kv_param_res_names ); 406manager .register_pass < ov::pass::MakeStateful > (kv_param_res_pairs ); 407 } 408 409if (ggml_model_decoder -> is_static ()) { 410manager .register_pass < pass::SqueezeMatmul > (); 411 } 412manager .run_passes (model ); 413if (ggml_model_decoder -> is_stateful ()) { 414 ov::preprocess::PrePostProcessor ppp (model ); 415for (size_t i = 0 ;i < model -> get_output_size ();i ++ ) { 416auto model_output_shape = model -> output (i ).get_partial_shape (); 417if (model_output_shape .rank ().is_static ()&& model_output_shape .rank ().get_length ()== 3 ) { 418ppp .output (i ).postprocess ().custom ([](const ov::Output < ov::Node >& node ) { 419auto axes = ov::op::v0::Constant ::create (ov::element::i32 , ov::Shape {1 }, {0 }); 420return std::make_shared < ov::op::v0::Unsqueeze > (node ,axes ); 421 }); 422 } 423 } 424model = ppp .build (); 425 } 426 } 427return model ; 428} 429 430}// namespace ggml 431}// namespace frontend 432}// namespace ov