@@ -762,6 +762,16 @@ static int get_swa_window_from_mask(const ggml_tensor * mask) {
762762 return window > 0 ? (int ) window : -1 ;
763763}
764764
765+ // The KQ product that the SOFT_MAX of an attention reads: directly, or through the logit soft-capping of gemma-2
766+ // (SCALE, TANH, SCALE)
767+ static const ggml_tensor * softmax_scores_source (const ggml_tensor * scores) {
768+ while (scores != nullptr && (scores->op == GGML_OP_SCALE ||
769+ (scores->op == GGML_OP_UNARY && ggml_get_unary_op (scores) == GGML_UNARY_OP_TANH ))) {
770+ scores = scores->src [0 ];
771+ }
772+ return scores;
773+ }
774+
765775std::pair<ModelParams, ComputeParams> GgmlOvDecoder::compute_llm_params (ggml_cgraph * cgraph, bool is_static) {
766776 ModelParams model_params;
767777 ComputeParams compute_params;
@@ -804,12 +814,12 @@ std::pair<ModelParams, ComputeParams> GgmlOvDecoder::compute_llm_params(ggml_cgr
804814 break ;
805815 }
806816 break ;
807- case GGML_OP_SOFT_MAX :
808- // 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
809- if (node-> src [ 0 ] != nullptr && node-> src [ 1 ] != nullptr && node->src [0 ]-> op == GGML_OP_MUL_MAT &&
810- node->src [0 ]-> src [ 0 ] != nullptr && node-> src [ 0 ] ->src [1 ] != nullptr &&
811- node ->src [0 ] ->src [0 ]->op == GGML_OP_PERMUTE && node-> src [ 0 ] ->src [0 ]->src [0 ] != nullptr &&
812- node-> src [ 0 ] ->src [0 ]->src [0 ]->op == GGML_OP_VIEW ) {
817+ case GGML_OP_SOFT_MAX : {
818+ // case 2: node op is SOFT_MAX, its scores come from a MUL_MAT (see softmax_scores_source) & the src 0 of MUL_MAT is PERMUTE & the permuted tensor src is the view of cache k
819+ const ggml_tensor * kq = softmax_scores_source ( node->src [0 ]);
820+ if (kq != nullptr && node->src [1 ] != nullptr && kq-> op == GGML_OP_MUL_MAT && kq ->src [0 ] != nullptr &&
821+ kq ->src [1 ] != nullptr && kq ->src [0 ]->op == GGML_OP_PERMUTE && kq ->src [0 ]->src [0 ] != nullptr &&
822+ kq ->src [0 ]->src [0 ]->op == GGML_OP_VIEW ) {
813823 return 2 ;
814824 }
815825 // 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
@@ -819,6 +829,7 @@ std::pair<ModelParams, ComputeParams> GgmlOvDecoder::compute_llm_params(ggml_cgr
819829 return 3 ;
820830 }
821831 break ;
832+ }
822833 default :
823834 break ;
824835 }
@@ -841,6 +852,22 @@ std::pair<ModelParams, ComputeParams> GgmlOvDecoder::compute_llm_params(ggml_cgr
841852 }
842853 };
843854
855+ // The PERMUTE of the K cache view an attention node reads, for the same src layouts
856+ auto get_attention_cache_k_permute = [&get_attention_pattern_case](const ggml_tensor * node) -> ggml_tensor * {
857+ switch (get_attention_pattern_case (node)) {
858+ case 0 :
859+ return node->src [1 ];
860+ case 1 :
861+ return node->src [1 ]->src [0 ];
862+ case 2 :
863+ return softmax_scores_source (node->src [0 ])->src [0 ];
864+ case 3 :
865+ return node->src [0 ]->src [0 ]->src [0 ];
866+ default :
867+ return nullptr ;
868+ }
869+ };
870+
844871 // Pre-pass: classify sliding-window vs full-attention layers.
845872 //
846873 // An interleaved-SWA model keeps two KV caches and two attention masks, and hands each layer
@@ -874,14 +901,7 @@ std::pair<ModelParams, ComputeParams> GgmlOvDecoder::compute_llm_params(ggml_cgr
874901 if (mask == nullptr ) {
875902 continue ;
876903 }
877- const ggml_tensor * cache_k_permute = nullptr ;
878- switch (get_attention_pattern_case (cgraph->nodes [i])) {
879- case 0 : cache_k_permute = cgraph->nodes [i]->src [1 ]; break ;
880- case 1 : cache_k_permute = cgraph->nodes [i]->src [1 ]->src [0 ]; break ;
881- case 2 : cache_k_permute = cgraph->nodes [i]->src [0 ]->src [0 ]; break ;
882- default : cache_k_permute = cgraph->nodes [i]->src [0 ]->src [0 ]->src [0 ]; break ;
883- }
884- const ggml_tensor * cache_k_view = cache_k_permute->src [0 ];
904+ const ggml_tensor * cache_k_view = get_attention_cache_k_permute (cgraph->nodes [i])->src [0 ];
885905 if (cache_k_view->op != GGML_OP_VIEW ) {
886906 continue ;
887907 }
@@ -950,26 +970,23 @@ std::pair<ModelParams, ComputeParams> GgmlOvDecoder::compute_llm_params(ggml_cgr
950970 node->src [2 ]->ne [0 ] == 1 && is_kvcache (node->src [2 ]->src [0 ], nullptr )) {
951971 model_params.v_trans = true ;
952972 }
973+ if (node->op == GGML_OP_FLASH_ATTN_EXT ) {
974+ float logit_softcap = 0 .0f ;
975+ memcpy (&logit_softcap, (const float *) node->op_params + 2 , sizeof (float ));
976+ model_params.softcap_attn = model_params.softcap_attn || logit_softcap != 0 .0f ;
977+ }
953978 const int attention_pattern_case = get_attention_pattern_case (node);
954979 if (attention_pattern_case != -1 ) {
955- ggml_tensor * cache_k_permute = nullptr ;
980+ ggml_tensor * cache_k_permute = get_attention_cache_k_permute (node) ;
956981 ggml_tensor * mask = nullptr ;
957982
958983 switch (attention_pattern_case) {
959984 case 0 :
960- cache_k_permute = node->src [1 ];
961- mask = node->src [3 ];
962- break ;
963985 case 1 :
964- cache_k_permute = node->src [1 ]->src [0 ];
965986 mask = node->src [3 ];
966987 break ;
967988 case 2 :
968- cache_k_permute = node->src [0 ]->src [0 ];
969- mask = node->src [1 ];
970- break ;
971989 case 3 :
972- cache_k_permute = node->src [0 ]->src [0 ]->src [0 ];
973990 mask = node->src [1 ];
974991 break ;
975992 case 4 :
@@ -1593,8 +1610,9 @@ bool GgmlOvDecoder::is_kvcache_4d(const ggml_tensor * cache) const {
15931610 return false ;
15941611 }
15951612 // `-fa off` (transposed V cache): keep the [1, 1, ctx, n_heads_kv * head_size] layout. The 4-D K cache breaks the
1596- // MUL_MAT attention of the Phi-3 models (Phi-3.5 mini, Phi-4 mini).
1597- if (m_model_params.v_trans ) {
1613+ // MUL_MAT attention of the Phi-3 models (Phi-3.5 mini, Phi-4 mini), and the soft-capped manual attention of
1614+ // gemma-2 with `-fa on` ("could not construct a memory descriptor using strides" in the GPU plugin's oneDNN path).
1615+ if (m_model_params.v_trans || m_model_params.softcap_attn ) {
15981616 return false ;
15991617 }
16001618 if (cache->op != GGML_OP_NONE || cache->view_src != nullptr || !is_kvcache (cache, nullptr )) {
0 commit comments