char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Zijun Yuopenvino: OV 2026.2, context-shift, Q5_1 support, gemma4 dense/embedding, and -fa off (#24503)890f1a27e

char/aliceai-t5-moe
5.8 KiB146 linesraw
1#include "../node_context.h"
2#include "../op_table.h"
3#include "../utils.h"
4#include "ggml.h"
5
6#include <memory>
7#include <openvino/op/constant.hpp>
8#include <openvino/op/convert.hpp>
9#include <openvino/op/divide.hpp>
10#include <openvino/op/multiply.hpp>
11#include <openvino/op/shape_of.hpp>
12#include <openvino/op/sigmoid.hpp>
13#include <openvino/op/tile.hpp>
14#include <openvino/op/util/precision_sensitive_attribute.hpp>
15#include <vector>
16
17namespace ov {
18namespace frontend {
19namespace ggml {
20namespace op {
21
22namespace {
23
24bool is_silu_div_pattern(const ov::Output<ov::Node> & numerator,
25                         const ov::Output<ov::Node> & denominator,
26                         const NodeContext & context) {
27    if (context.get_input_size() != 2) {
28        return false;
29    }
30
31    const auto * unary_op = reinterpret_cast<const ggml_unary_op *>(context.get_input_op_params(0));
32    if (unary_op == nullptr || *unary_op != GGML_UNARY_OP_SILU) {
33        return false;
34    }
35
36    auto mul = std::dynamic_pointer_cast<ov::op::v1::Multiply>(numerator.get_node_shared_ptr());
37    if (!mul) {
38        return false;
39    }
40
41    const auto denom_node = denominator.get_node_shared_ptr();
42    const auto mul_input_0 = mul->input_value(0).get_node_shared_ptr();
43    const auto mul_input_1 = mul->input_value(1).get_node_shared_ptr();
44
45    auto sigmoid = std::dynamic_pointer_cast<ov::op::v0::Sigmoid>(mul_input_1);
46    if (mul_input_0 == denom_node && sigmoid && sigmoid->input_value(0).get_node_shared_ptr() == denom_node) {
47        return true;
48    }
49
50    sigmoid = std::dynamic_pointer_cast<ov::op::v0::Sigmoid>(mul_input_0);
51    return mul_input_1 == denom_node && sigmoid && sigmoid->input_value(0).get_node_shared_ptr() == denom_node;
52}
53
54ov::Output<ov::Node> repeat_input_to_match(const NodeContext & context,
55                                           const ov::Output<ov::Node> & input,
56                                           const ov::Output<ov::Node> & target,
57                                           size_t input_index) {
58    const auto input_shape = context.get_input_shape(input_index);
59    const auto target_shape = context.get_input_shape(0);
60
61    if (input_shape == target_shape) {
62        return input;
63    }
64
65    if (input_shape.rank().is_static() && target_shape.rank().is_static()) {
66        const auto rank = static_cast<size_t>(input_shape.rank().get_length());
67        std::vector<int64_t> repeats(rank, 1);
68        bool needs_repeat = false;
69
70        for (size_t axis = 0; axis < rank; ++axis) {
71            FRONT_END_OP_CONVERSION_CHECK(input_shape[axis].is_static() && target_shape[axis].is_static(),
72                                          "DIV repeat requires static dimensions on both inputs");
73
74            const int64_t input_dim = input_shape[axis].get_length();
75            const int64_t target_dim = target_shape[axis].get_length();
76
77            FRONT_END_OP_CONVERSION_CHECK(input_dim > 0 && target_dim > 0 && target_dim % input_dim == 0,
78                                          "DIV input shape ", input_shape, " cannot repeat to match ", target_shape);
79
80            repeats[axis] = target_dim / input_dim;
81            needs_repeat = needs_repeat || repeats[axis] != 1;
82        }
83
84        if (!needs_repeat) {
85            return input;
86        }
87
88        auto repeats_node = ov::op::v0::Constant::create(ov::element::i64, {repeats.size()}, repeats);
89        return std::make_shared<ov::op::v0::Tile>(input, repeats_node);
90    }
91
92    auto input_shape_node = std::make_shared<ov::op::v3::ShapeOf>(input, ov::element::i64);
93    auto target_shape_node = std::make_shared<ov::op::v3::ShapeOf>(target, ov::element::i64);
94    auto repeats_node = std::make_shared<ov::op::v1::Divide>(target_shape_node, input_shape_node);
95    return std::make_shared<ov::op::v0::Tile>(input, repeats_node);
96}
97
98}  // namespace
99
100OutputVector translate_div(const NodeContext & context) {
101    num_inputs_check(context, 2, 2);
102
103    auto input_0 = process_view_input_new(context, 0);
104    auto input_1 = process_view_input_new(context, 1);
105
106    if (is_silu_div_pattern(input_0, input_1, context)) {
107        ov::Output<ov::Node> res = std::make_shared<ov::op::v0::Sigmoid>(input_1);
108        if (res.get_element_type() != context.get_output_type()) {
109            res = std::make_shared<ov::op::v0::Convert>(res, context.get_output_type());
110        }
111        return rename_outputs_with_suffix({res}, context.get_name());
112    }
113
114    input_1 = repeat_input_to_match(context, input_1, input_0, 1);
115
116    const auto output_type = context.get_output_type();
117    const bool use_f32_compute = input_0.get_element_type() != ov::element::f32 ||
118                                 input_1.get_element_type() != ov::element::f32 || output_type != ov::element::f32;
119
120    if (use_f32_compute) {
121        input_0 = std::make_shared<ov::op::v0::Convert>(input_0, ov::element::f32);
122        input_1 = std::make_shared<ov::op::v0::Convert>(input_1, ov::element::f32);
123    }
124
125    ov::Output<ov::Node> res = std::make_shared<ov::op::v1::Divide>(input_0, input_1);
126    if (use_f32_compute) {
127        // Keep the reciprocal/divide path in FP32. Without this hint, the GPU
128        // plugin can still compress the subgraph back to FP16 and overflow on
129        // small shexp gate values (e.g. silu(x) / x in qwen2moe).
130        ov::mark_as_precision_sensitive(res.get_node_shared_ptr()->input(0));
131        ov::mark_as_precision_sensitive(res.get_node_shared_ptr()->input(1));
132    }
133    if (res.get_element_type() != output_type) {
134        auto output_convert = std::make_shared<ov::op::v0::Convert>(res, output_type);
135        if (use_f32_compute) {
136            ov::mark_as_precision_sensitive(output_convert->input(0));
137        }
138        res = output_convert;
139    }
140    return rename_outputs_with_suffix({res}, context.get_name());
141}
142
143}  // namespace op
144}  // namespace ggml
145}  // namespace frontend
146}  // namespace ov