|
| 1 | +// Copyright (C) 2018-2026 Intel Corporation |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | +// |
| 4 | + |
| 5 | +#include "right_align_mask_slice_for_conv.hpp" |
| 6 | + |
| 7 | +#include "openvino/op/ops.hpp" |
| 8 | +#include "openvino/openvino.hpp" |
| 9 | +#include "openvino/pass/manager.hpp" |
| 10 | +#include "openvino/pass/matcher_pass.hpp" |
| 11 | +#include "openvino/pass/pass.hpp" |
| 12 | +#include "openvino/pass/pattern/op/wrap_type.hpp" |
| 13 | +#include "openvino/pass/validate.hpp" |
| 14 | + |
| 15 | +namespace opp = ov::pass::pattern; |
| 16 | + |
| 17 | +namespace { |
| 18 | + |
| 19 | +// diagnostics warnings on OPENVINO_MATCHER_PASS_RTTI() definition: visibility hidden |
| 20 | +#ifdef __GNUC__ |
| 21 | +# pragma GCC diagnostic push |
| 22 | +# pragma GCC diagnostic ignored "-Wattributes" |
| 23 | +#endif |
| 24 | + |
| 25 | +class RightAlignMaskSliceForConvImpl : public ov::pass::MatcherPass { |
| 26 | +public: |
| 27 | + // In LFM2-like models, where `attention_mask` is also consumed by the Conv subgraph, |
| 28 | + // the Slice operation is inserted to extract the current tokens from that `attention_mask` |
| 29 | + // and pass to the Conv subgraph. |
| 30 | + // This is done to allow skipping processing of some tokens in the prefill stage. |
| 31 | + // Slice is done from zero till the number of current tokens, assuming that in most cases |
| 32 | + // tokens from the right are not the useful ones. |
| 33 | + // However, in NPUW static-shape mode, we place useful current tokens exactly to the right end |
| 34 | + // in the prefill stage. |
| 35 | + // Therefore, Slice operation will extract the left part of mask, that in case of chunked |
| 36 | + // prefill will be padded either with zeroes (in the first iteration) or with ones |
| 37 | + // (in subsequent iterations) but for past tokens. |
| 38 | + // This pass re-align the Slice operation to extract the right part of the mask, |
| 39 | + // that contains useful current tokens. |
| 40 | + OPENVINO_MATCHER_PASS_RTTI("ov::npuw::RightAlignMaskSliceForConvImpl"); |
| 41 | + explicit RightAlignMaskSliceForConvImpl() { |
| 42 | + auto attention_mask = opp::wrap_type<ov::op::v0::Parameter>(); |
| 43 | + auto attention_mask_slice = opp::wrap_type<ov::op::v8::Slice>( |
| 44 | + {attention_mask, opp::any_input(), opp::any_input(), opp::any_input(), opp::any_input()}); |
| 45 | + auto unsqueeze = opp::wrap_type<ov::op::v0::Unsqueeze>({attention_mask_slice, opp::any_input()}); |
| 46 | + auto convert = opp::wrap_type<ov::op::v0::Convert>({unsqueeze}); |
| 47 | + auto multiply = opp::wrap_type<ov::op::v1::Multiply>({convert, opp::any_input()}); |
| 48 | + auto add = opp::wrap_type<ov::op::v1::Add>({multiply, opp::any_input()}); |
| 49 | + auto multiply_with_embedd = opp::wrap_type<ov::op::v1::Multiply>({opp::any_input(), add}); |
| 50 | + auto matmul = opp::wrap_type<ov::op::v0::MatMul>({multiply_with_embedd, opp::any_input()}); |
| 51 | + auto transpose = opp::wrap_type<ov::op::v1::Transpose>({matmul, opp::any_input()}); |
| 52 | + // VariadicSplit into B,C,~h in LFM2. |
| 53 | + auto variadic_split = |
| 54 | + opp::wrap_type<ov::op::v1::VariadicSplit>({transpose, opp::any_input(), opp::any_input()}); |
| 55 | + |
| 56 | + auto callback = [=](opp::Matcher& m) { |
| 57 | + auto& node_to_output = m.get_pattern_value_map(); |
| 58 | + |
| 59 | + auto matched_attention_mask = node_to_output.at(attention_mask).get_node_shared_ptr(); |
| 60 | + const auto names = matched_attention_mask->output(0).get_names(); |
| 61 | + if (names.count(ov::npuw::attention_mask_name) == 0 && |
| 62 | + matched_attention_mask->get_friendly_name() != ov::npuw::attention_mask_name) { |
| 63 | + return false; |
| 64 | + } |
| 65 | + auto matched_attention_mask_slice = node_to_output.at(attention_mask_slice).get_node_shared_ptr(); |
| 66 | + const auto slice_start_const = ov::as_type_ptr<ov::op::v0::Constant>( |
| 67 | + matched_attention_mask_slice->input_value(1).get_node_shared_ptr()); |
| 68 | + if (!slice_start_const || slice_start_const->cast_vector<int64_t>() != std::vector<int64_t>{0}) { |
| 69 | + return false; |
| 70 | + } |
| 71 | + auto matched_attention_mask_unsqueeze = node_to_output.at(unsqueeze).get_node_shared_ptr(); |
| 72 | + |
| 73 | + auto const_zero = std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{}, 0); |
| 74 | + auto const_one = std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{}, 1); |
| 75 | + auto mask_shape_of = std::make_shared<ov::op::v3::ShapeOf>(matched_attention_mask); |
| 76 | + auto mask_len = std::make_shared<ov::op::v8::Gather>(mask_shape_of, const_one, const_zero); |
| 77 | + auto mask_len_rank_one = std::make_shared<ov::op::v0::Unsqueeze>(mask_len, const_zero); |
| 78 | + // 2nd argument to Slice is stop point: |
| 79 | + auto current_len = matched_attention_mask_slice->input(2).get_source_output().get_node_shared_ptr(); |
| 80 | + auto current_offset = std::make_shared<ov::op::v1::Subtract>(mask_len, current_len); |
| 81 | + |
| 82 | + auto const_one_rank_one = std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{1}, 1); |
| 83 | + auto new_slice = std::make_shared<ov::op::v8::Slice>(matched_attention_mask, |
| 84 | + current_offset, |
| 85 | + mask_len_rank_one, |
| 86 | + const_one_rank_one, |
| 87 | + const_one_rank_one); |
| 88 | + matched_attention_mask_unsqueeze->input(0).replace_source_output(new_slice); |
| 89 | + return true; |
| 90 | + }; |
| 91 | + register_matcher(std::make_shared<opp::Matcher>(variadic_split, "RightAlignMaskSliceForConvImpl"), |
| 92 | + std::move(callback)); |
| 93 | + } |
| 94 | +}; |
| 95 | + |
| 96 | +#ifdef __GNUC__ |
| 97 | +# pragma GCC diagnostic pop |
| 98 | +#endif |
| 99 | +} // anonymous namespace |
| 100 | + |
| 101 | +bool ov::npuw::RightAlignMaskSliceForConv::run_on_model(const std::shared_ptr<ov::Model>& model) { |
| 102 | + ov::pass::Manager manager("right-align-mask-slice-for-conv"); |
| 103 | + manager.set_per_pass_validation(true); |
| 104 | + manager.register_pass<RightAlignMaskSliceForConvImpl>(); |
| 105 | + manager.run_passes(model); |
| 106 | + return true; |
| 107 | +} |
0 commit comments