Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6cefb54
OpenVINO backend: 1) enable gpt-oss moe on OV bk; 2) enable mxfp4 sup…
zhaixuejun1993 Jun 23, 2026
52e8dbd
OpenVINO backend: disable TOPK_MOE op test
zhaixuejun1993 Jun 24, 2026
51a6f6f
OpenVINO Backend: Add op FILL support
zhaixuejun1993 Jun 25, 2026
b96af6b
OpenVINO backend: enable set rows with multi dims
zhaixuejun1993 Jun 25, 2026
048fe05
fix the name missmatch in setrow + view
zhaixuejun1993 Jun 25, 2026
9689180
OpenVINO backend: enable op GGML_UNARY_OP_SIGMOID
zhaixuejun1993 Jun 25, 2026
aa6196e
OpenVINO Backend: enable SQR & SQRT
zhaixuejun1993 Jun 25, 2026
195f95d
OpenVINO backend: 1) ensure unique node names for OpenVINO; 2) add or…
zhaixuejun1993 May 27, 2026
dac654e
OpenVINO backend: enable fallback for openVINO to CPU backend
zhaixuejun1993 May 27, 2026
4bfba96
OpenVINO backend: fix accurace issue in gemma3n arch test
zhaixuejun1993 Jun 30, 2026
2bb2ceb
fix mpt failed case
zhaixuejun1993 Jun 30, 2026
fe273f8
OpenVINO backend: clean nodeinfo
zhaixuejun1993 Jul 3, 2026
55d430b
OpenVINO Backend: enable zero-size copy for view
zhaixuejun1993 Jul 7, 2026
69579e3
add concat ssm_conv in compute_dynamic_dim
wine99 May 21, 2026
4746b9b
OpenVINO backend: disable EXP with FP32, which failed in op test. Roo…
zhaixuejun1993 Jun 16, 2026
1999445
OpenVINO backend: fix CPY op test failed issue
zhaixuejun1993 Jun 16, 2026
02a01c4
OpenVINO backend: fix GATED_DELTA_NET op test failed issue
zhaixuejun1993 Jun 16, 2026
252f7a9
handle in-place op, handle qwen35 dynamic clearing of cache in cgraph
wine99 Jun 18, 2026
e29ab2b
handle qwen35 dynamic clearing of cache correctly
wine99 Jun 23, 2026
d7d74d0
Enable qwen35 dense multi seq
wine99 Jun 29, 2026
150e220
Fix qwen35 9b gqa
wine99 Jun 30, 2026
c9e989c
Fix after rebase
wine99 Jul 1, 2026
7e4a7af
Disable SOLVE_TRI
wine99 Jul 7, 2026
105a68d
openvino: fix NEOX RoPE accuracy on GPU stateful (mixed-rank Multiply)
cavusmustafa Jul 7, 2026
204d7a6
OpenVINO backend: 1) remove the unique name in llama.cpp; 2) add new …
zhaixuejun1993 Jul 8, 2026
916a6cd
OpenVINO Backenb: remove changes in llama.cpp
zhaixuejun1993 Jul 8, 2026
dc94242
Doc change (use x64 Native Tools Command Prompt for VS)
virajwad Jul 9, 2026
1832c44
Cleaner sentence
virajwad Jul 9, 2026
5ddd803
OpenVINO Backend: cache key upgrade includes all src name
zhaixuejun1993 Jul 9, 2026
ab6cd7e
OpenVINO Backend: enable llama arch test on ci
zhaixuejun1993 Jul 10, 2026
4b30e3e
OpenVINO Backend: move parameter node creating from decoder into tran…
zhaixuejun1993 Jul 3, 2026
0733933
OpenVINO Backend: create extra input ov node move from decoder to tra…
zhaixuejun1993 Jul 3, 2026
396adc3
Merge pull request #253 from ravi9/pr-252-rebased
ravi9 Jul 14, 2026
8e57990
fix for op regression due to is_model_splitted
suryasidd Jul 17, 2026
4250de7
Merge pull request #259 from cavusmustafa/ggml-openvino-decode-fastpa…
cavusmustafa Jul 21, 2026
7384d34
OpenVINO Backend: Upgrade graph cache to use buffer pointers instead …
mostafafaheem Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/build-openvino.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,16 @@ jobs:

- name: Test (CPU)
id: cmake_test_cpu
# TODO: fix and re-enable the `test-llama-archs` test below
run: |
cd ${{ github.workspace }}
ctest --test-dir build/ReleaseOV -L main -E "test-llama-archs" --verbose --timeout 2000
ctest --test-dir build/ReleaseOV -L main --verbose --timeout 2000

- name: Test (GPU)
id: cmake_test_gpu
# TODO: fix and re-enable the `test-llama-archs` test below
run: |
cd ${{ github.workspace }}
export GGML_OPENVINO_DEVICE=GPU
ctest --test-dir build/ReleaseOV -L main -E "test-llama-archs" --verbose --timeout 3000
ctest --test-dir build/ReleaseOV -L main --verbose --timeout 3000

openvino-windows-2022:
runs-on: windows-2022
Expand Down Expand Up @@ -159,11 +157,10 @@ jobs:
- name: Test (CPU)
id: cmake_test_cpu
shell: cmd
# TODO: fix and re-enable the `test-llama-archs` test below
run: |
REM Find extracted OpenVINO folder dynamically
for /d %%i in (openvino_toolkit\*) do set OPENVINO_ROOT=%%i
call "%OPENVINO_ROOT%\setupvars.bat"

cd build
ctest --test-dir ReleaseOV -L main -E "test-llama-archs" -C Release --verbose --timeout 3000
ctest --test-dir ReleaseOV -L main -C Release --verbose --timeout 3000
2 changes: 1 addition & 1 deletion docs/backend/OPENVINO.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ cmake -B build/ReleaseOV -G Ninja -DCMAKE_BUILD_TYPE=Release -DGGML_OPENVINO=ON
cmake --build build/ReleaseOV --parallel
```

- **Windows:** Open a **Developer Command Prompt for VS 2022** (so the MSVC toolchain is on `PATH`), then run:
- **Windows:** Open **x64 Native Tools Command Prompt for VS** (so the MSVC toolchain is on `PATH`), then run:

```cmd
C:\Intel\openvino\setupvars.bat
Expand Down
323 changes: 251 additions & 72 deletions ggml/src/ggml-openvino/ggml-decoder.cpp

Large diffs are not rendered by default.

75 changes: 60 additions & 15 deletions ggml/src/ggml-openvino/ggml-decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct ModelParams {
int n_seq = 1;
int n_heads_kv = -1;
int head_size = -1;
int state_size = -1; // for SSM molels, eg qwen35
int32_t rope_params[15];
bool mixed_rope_params = false;
std::vector<int> swa_layers;
Expand Down Expand Up @@ -48,6 +49,37 @@ struct ComputeParams {
int token_len_per_seq = -1;
int past_kv_len = -1;
int output_len = 1;

int cache_rs_reset_idx = -1;
int cache_rs_reset_len = -1;
// SSM/DeltaNet models otionally clear cache_r and cache_s of certain slots in the cgraph
// 3: [ 18432, 4, 1, 1] RESHAPE cache_r_l0 (reshaped)
// [ 18432, 4, 1, 1] 0: NONE cache_r_l0
// 4: [ 18432, 1, 1, 1] VIEW cache_r_l0 (reshaped) (view)
// [ 18432, 4, 1, 1] 0: RESHAPE cache_r_l0 (reshaped)
// 5: [ 18432, 1, 1, 1] SCALE cache_r_l0 (reshaped) (view) (view)
// [ 18432, 1, 1, 1] 0: VIEW cache_r_l0 (reshaped) (view)

int s_copy_active_slot_idx = -1;
int s_copy_active_slot_len = -1;
// SSM/DeltaNet models otionally reorder slots of state cache, to make the active slots contiguous
// leaf_5 is the inp->s_copy in llama-graph.cpp, eg if there are 8 slots in total and slot 3 and 7
// are active in the current batch, leaf_5 will be [3, 7, 5, 6, 4]
// 6: [ 2, 1, 1, 1] VIEW (view)
// [ 2, 1, 1, 1] 0: NONE leaf_5
// 7: [ 18432, 2, 1, 1] GET_ROWS conv_states-0
// [ 18432, 4, 1, 1] 0: RESHAPE cache_r_l0 (reshaped)
// [ 2, 1, 1, 1] 1: VIEW (view)
// 8: [ 0, 1, 1, 1] VIEW (view)
// [ 2, 1, 1, 1] 0: NONE leaf_5
// 9: [ 18432, 0, 1, 1] GET_ROWS node_9
// [ 18432, 4, 1, 1] 0: RESHAPE cache_r_l0 (reshaped)
// [ 0, 1, 1, 1] 1: VIEW (view)
// 10: [ 18432, 0, 1, 1] VIEW cache_r_l0 (view)
// [ 18432, 4, 1, 1] 0: NONE cache_r_l0
// 11: [ 18432, 0, 1, 1] CPY cache_r_l0 (view) (copy of )
// [ 18432, 0, 1, 1] 0: GET_ROWS node_9
// [ 18432, 0, 1, 1] 1: VIEW cache_r_l0 (view)
};

class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {
Expand All @@ -59,8 +91,6 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {
std::map<std::string, ggml_tensor *> node_inputs;
std::map<std::string, std::vector<std::pair<std::string, ggml_tensor *>>> node_inputs_views;
std::vector<std::string> node_inputs_names;
ggml_tensor * node_output;
std::string node_output_name;
int node_op_case = 0;
void * data_addr;
};
Expand Down Expand Up @@ -156,6 +186,10 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {

virtual std::vector<std::string> get_output_names(int node_idx) const override;

virtual std::string get_inplace_op_src(int node_idx) const override;

virtual bool is_view_like_alias_of(int node_idx, const std::string & view_src_name) const override;

virtual const std::string & get_op_type() const override;

virtual const std::string & get_op_type(int node_idx) const override;
Expand All @@ -173,23 +207,19 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {

virtual int get_op_case(int node_idx) const override { return m_node_info_list[node_idx].node_op_case; }

virtual const std::map<std::string, std::shared_ptr<ov::Node>> & get_model_inputs() const override {
virtual const std::map<std::string, ov::frontend::ggml::ModelInputInfo> & get_model_inputs() const override {
return m_model_inputs;
}

virtual const std::map<std::string, std::shared_ptr<ov::Node>> & get_model_extra_inputs() const override {
virtual const std::map<std::string, ov::frontend::ggml::ModelExtraInputInfo> & get_model_extra_inputs() const override {
return m_model_extra_inputs;
}

virtual const std::map<std::string, std::shared_ptr<ov::Tensor>> & get_model_extra_input_values() const {
return m_model_extra_input_values;
}

virtual const std::map<std::string, std::shared_ptr<ov::Node>> & get_model_weights() const override {
return m_model_weights;
}

virtual std::vector<std::string> get_model_output_names() const override { return m_model_output_names; }
virtual std::set<std::string> get_model_output_names() const override { return m_model_output_names; }

const std::map<std::string, ggml_tensor *> & get_model_outputs() const { return m_model_outputs; }

Expand All @@ -214,6 +244,8 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {

virtual bool has_mixed_rope_params() const override { return m_model_params.mixed_rope_params; }

virtual int get_ssm_state_size() const override { return m_model_params.state_size; }

virtual std::map<std::string, std::string> get_kv_param_res_names() const override;

virtual bool is_static() const override { return m_is_static; }
Expand Down Expand Up @@ -287,8 +319,12 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {
return op->op == GGML_OP_ROPE && tensor == op->src[2];
}

// also returns true for cache_s and cache_r in SSM/DeltaNet models
inline static bool is_kvcache(const ggml_tensor * tensor, const ggml_tensor * op) {
return tensor->buffer->usage == GGML_BACKEND_BUFFER_USAGE_ANY ||
if (tensor == nullptr) {
return false;
}
return (tensor->buffer != nullptr && tensor->buffer->usage == GGML_BACKEND_BUFFER_USAGE_ANY) ||
(op != nullptr && op->op == GGML_OP_SET_ROWS && op->src[2] == tensor);
}

Expand All @@ -301,7 +337,13 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {
op->src[1]->op == GGML_OP_NONE;
}

std::string get_graph_input_ov_name(const ggml_tensor * tensor, const ggml_tensor * op) {
// the state permutation index input used in SSM/DeltaNet models (inp->s_copy in llama-graph.cpp)
inline static bool is_inp_s_copy(const ggml_tensor * tensor, const ggml_tensor * op) {
return op->op == GGML_OP_GET_ROWS && tensor == op->src[1] &&
op->src[0]->buffer->usage == GGML_BACKEND_BUFFER_USAGE_ANY;
}

std::string get_graph_input_ov_name(const ggml_tensor * tensor, const ggml_tensor * op) const {
if (is_inp_pos(tensor, op)) {
return "inp_pos";
}
Expand All @@ -321,6 +363,10 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {
void compute_model_inputs();
void compute_model_outputs();

// True if tensor is the inp->s_copy index leaf gathered by a recurrent state cache GET_ROWS
// (possibly through a VIEW), so it gets a dynamic [1,1,1,-1] graph-input shape.
bool is_s_copy_leaf(const ggml_tensor * tensor) const;

// Infer and propagate dynamic-dimension indices for all tensors in the GGML graph.
void compute_node_dynamic_dims();

Expand All @@ -329,12 +375,11 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder {
ggml_cgraph * m_cgraph = nullptr;
std::map<std::string, ggml_tensor *> m_inputs;

std::map<std::string, std::shared_ptr<ov::Node>> m_model_inputs;
std::map<std::string, std::shared_ptr<ov::Node>> m_model_extra_inputs;
std::map<std::string, std::shared_ptr<ov::Tensor>> m_model_extra_input_values;
std::map<std::string, ov::frontend::ggml::ModelInputInfo> m_model_inputs;
std::map<std::string, ov::frontend::ggml::ModelExtraInputInfo> m_model_extra_inputs;
std::map<std::string, std::shared_ptr<ov::Node>> m_model_weights;
std::map<std::string, ggml_tensor *> m_model_outputs;
std::vector<std::string> m_model_output_names;
std::set<std::string> m_model_output_names;
std::vector<NodeInfo> m_node_info_list;
std::map<ggml_tensor *, int> m_node_dynamic_dims;

Expand Down
23 changes: 19 additions & 4 deletions ggml/src/ggml-openvino/ggml-openvino-extra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,24 @@ ggml_openvino_extracted_layout ggml_openvino_get_extracted_layout(const ggml_ten
return layout;
}

// Only handle 2D weight tensors
if (tensor->ne[2] != 1 || tensor->ne[3] != 1) {
// Most quantized weights use the existing 2D extraction path. MXFP4 also
// appears as 3D expert weights for MUL_MAT_ID, so allow that type through.
if (tensor->type != GGML_TYPE_MXFP4 && (tensor->ne[2] != 1 || tensor->ne[3] != 1)) {
return layout;
}

int64_t n_elements = ggml_nelements(tensor);
const size_t alignment = 64; // Good for SIMD

if (tensor->type == GGML_TYPE_MXFP4 && (tensor->ne[2] > 1 || tensor->ne[3] > 1)) {
layout.weights_per_block = 32;
layout.is_symmetric = true;
layout.weights_size = ggml_nbytes(tensor);
layout.weights_offset = 0;
layout.total_size = layout.weights_size;
return layout;
}

// Check if requantization is needed (NPU-specific)
auto requant_type = ggml_openvino_get_requant_type(tensor, use_bias);
if (requant_type.has_value()) {
Expand Down Expand Up @@ -334,6 +344,11 @@ ggml_openvino_extracted_layout ggml_openvino_get_extracted_layout(const ggml_ten
layout.is_symmetric = false;

switch (tensor->type) {
case GGML_TYPE_MXFP4:
layout.is_u4 = true;
layout.is_symmetric = true;
break;

case GGML_TYPE_Q4_0:
layout.is_u4 = true;
layout.is_symmetric = true;
Expand Down Expand Up @@ -369,9 +384,9 @@ ggml_openvino_extracted_layout ggml_openvino_get_extracted_layout(const ggml_ten
// Weights: U4 = n_elements/2 bytes, U8 = n_elements bytes
layout.weights_size = layout.is_u4 ? (n_elements / 2) : n_elements;

// Scales: F16 per block
// Scales: F16 per block, except MXFP4 which stores one E8M0 byte per block.
int64_t n_blocks = n_elements / layout.weights_per_block;
layout.scales_size = n_blocks * sizeof(uint16_t); // F16 = 2 bytes
layout.scales_size = n_blocks * (tensor->type == GGML_TYPE_MXFP4 ? sizeof(uint8_t) : sizeof(uint16_t));
// For symmetric quantization, no zp needed (weights stored as signed)
if (layout.is_symmetric) {
layout.zp_size = 0;
Expand Down
Loading
Loading