| 426 | } |
| 427 | |
| 428 | void apply(module_pass_manager& mpm, const match::matcher_result& r) const |
| 429 | { |
| 430 | auto& mm = mpm.get_module(); |
| 431 | auto attn_group_ins = r.instructions["group"]; |
| 432 | auto* submod = attn_group_ins->module_inputs().front(); |
| 433 | |
| 434 | // TODO: for this pass of flash decoding, if LSE attn, do not do flash decoding |
| 435 | auto return_ins = std::prev(submod->end()); |
| 436 | assert(return_ins->name() == "@return" and |
| 437 | "Last instruction must be a @return instruction"); |
| 438 | if(return_ins->inputs().size() > 1) |
| 439 | return; |
| 440 | |
| 441 | // get gemm1 and gemm2 |
| 442 | auto [gemm1, gemm2] = get_gemms(submod); |
| 443 | |
| 444 | // TODO: for this first pass of flash decoding, assuming no input fusion / not supporting |
| 445 | auto q_param = gemm1->inputs()[0]; |
| 446 | auto k_param = gemm1->inputs()[1]; |
| 447 | auto v_param = gemm2->inputs()[1]; |
| 448 | assert(q_param->name() == "@param" and "Q should be a parameter"); |
| 449 | assert(k_param->name() == "@param" and "K should be a parameter"); |
| 450 | assert(v_param->name() == "@param" and "V should be a parameter"); |
| 451 | |
| 452 | // check if N dimension is evenly divisible by num_splits |
| 453 | if(k_param->get_shape().lens().back() % groups != 0) |
| 454 | return; |
| 455 | |
| 456 | // get Q, V, K shapes from gemms |
| 457 | auto qkv_shapes = get_qkv_shapes(q_param, k_param, v_param); |
| 458 | |
| 459 | // check shapes are ok and get flash decoding transformed shapes (Q', V', K') |
| 460 | auto transform_info = get_transformed_shapes(qkv_shapes); |
| 461 | |
| 462 | // create mapping from submodule params to main module inputs |
| 463 | auto group_inputs = attn_group_ins->inputs(); |
| 464 | auto map_param_to_main = map_submod_params_to_inputs(submod, group_inputs); |
| 465 | |
| 466 | // get actual Q, K, V instructions from main module |
| 467 | auto q = map_param_to_main.at(q_param); |
| 468 | auto k = map_param_to_main.at(k_param); |
| 469 | auto v = map_param_to_main.at(v_param); |
| 470 | |
| 471 | // insert reshape operations before group, for Q, K, V |
| 472 | auto q_ndim = q->get_shape().lens().size(); |
| 473 | int64_t g_axis = q_ndim - 2; |
| 474 | |
| 475 | // Q: [B, M, k] -> [B, G, M, k] via unsqueeze + broadcast |
| 476 | auto q_unsqueeze = |
| 477 | mm.insert_instruction(attn_group_ins, make_op("unsqueeze", {{"axes", {g_axis}}}), q); |
| 478 | auto q_reshaped = |
| 479 | mm.insert_instruction(attn_group_ins, |
| 480 | make_op("multibroadcast", {{"out_lens", transform_info.q_shape}}), |
| 481 | q_unsqueeze); |
| 482 | |
| 483 | // K: [B, k, N] -> [B, G, k, N/G] via reshape + transpose |
| 484 | auto k_reshaped_intermediate = mm.insert_instruction( |
| 485 | attn_group_ins, make_op("reshape", {{"dims", transform_info.k_intermediate}}), k); |
nothing calls this directly
no test coverage detected