char-fork/llama.cpp

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

Aman Guptaggml: add SWIGLU_CLAMP (#27930)0190529ec

char/aliceai-t5-moe
95.4 KiB2584 linesraw
1#include "ggml-et-ops.h"
2
3#include "ggml-et-cpu-compare.h"
4#include "ggml-et-kernels.h"
5#include "ggml-impl.h"
6
7#include <stdio.h>
8
9#include <cstdint>
10
11// CPU comparison configuration - can be enabled for debugging
12static ggml_et_cpu_compare_config rope_cpu_compare_config = {
13    /* .enabled = */ false,
14    /* .use_cpu_result = */ false,  // Replace ET result with CPU result
15    /* .log_differences = */ true,
16    /* .tolerance = */ 1e-5f,
17    /* .max_log_elements = */ 4096
18};
19
20static ggml_et_cpu_compare_config rms_norm_cpu_compare_config = {
21    /* .enabled = */ false,
22    /* .use_cpu_result = */ false,
23    /* .log_differences = */ true,
24    /* .tolerance = */ 1e-5f,
25    /* .max_log_elements = */ 4096
26};
27
28static ggml_et_cpu_compare_config norm_cpu_compare_config = {
29    /* .enabled = */ false,
30    /* .use_cpu_result = */ false,
31    /* .log_differences = */ true,
32    /* .tolerance = */ 1e-5f,
33    /* .max_log_elements = */ 4096
34};
35
36static ggml_et_cpu_compare_config l2_norm_cpu_compare_config = {
37    /* .enabled = */ false,
38    /* .use_cpu_result = */ false,
39    /* .log_differences = */ true,
40    /* .tolerance = */ 1e-5f,
41    /* .max_log_elements = */ 4096
42};
43
44static ggml_et_cpu_compare_config group_norm_cpu_compare_config = {
45    /* .enabled = */ false,
46    /* .use_cpu_result = */ false,
47    /* .log_differences = */ true,
48    /* .tolerance = */ 1e-5f,
49    /* .max_log_elements = */ 4096
50};
51
52static ggml_et_cpu_compare_config im2col_cpu_compare_config = {
53    /* .enabled = */ false,
54    /* .use_cpu_result = */ false,
55    /* .log_differences = */ true,
56    /* .tolerance = */ 1e-5f,
57    /* .max_log_elements = */ 4096
58};
59
60static ggml_et_cpu_compare_config unary_cpu_compare_config = {
61    /* .enabled = */ false,
62    /* .use_cpu_result = */ false,
63    /* .log_differences = */ true,
64    /* .tolerance = */ 1e-4f,
65    /* .max_log_elements = */ 4096
66};
67
68static ggml_et_cpu_compare_config sum_rows_cpu_compare_config = {
69    /* .enabled = */ false,
70    /* .use_cpu_result = */ false,
71    /* .log_differences = */ true,
72    /* .tolerance = */ 1e-5f,
73    /* .max_log_elements = */ 4096
74};
75
76static ggml_et_cpu_compare_config clamp_cpu_compare_config = {
77    /* .enabled = */ false,
78    /* .use_cpu_result = */ false,
79    /* .log_differences = */ true,
80    /* .tolerance = */ 1e-6f,
81    /* .max_log_elements = */ 4096
82};
83
84static ggml_et_cpu_compare_config mean_cpu_compare_config = {
85    /* .enabled = */ false,
86    /* .use_cpu_result = */ false,
87    /* .log_differences = */ true,
88    /* .tolerance = */ 1e-5f,
89    /* .max_log_elements = */ 4096
90};
91
92static ggml_et_cpu_compare_config sqr_cpu_compare_config = {
93    /* .enabled = */ false,
94    /* .use_cpu_result = */ false,
95    /* .log_differences = */ true,
96    /* .tolerance = */ 1e-6f,
97    /* .max_log_elements = */ 4096
98};
99
100static ggml_et_cpu_compare_config elmap_cpu_compare_config = {
101    /* .enabled = */ false,
102    /* .use_cpu_result = */ false,
103    /* .log_differences = */ true,
104    /* .tolerance = */ 1e-6f,
105    /* .max_log_elements = */ 4096
106};
107
108static ggml_et_cpu_compare_config glu_cpu_compare_config = {
109    /* .enabled = */ false,
110    /* .use_cpu_result = */ false,
111    /* .log_differences = */ true,
112    /* .tolerance = */ 1e-5f,
113    /* .max_log_elements = */ 4096
114};
115
116static ggml_et_cpu_compare_config mul_mat_cpu_compare_config = {
117    /* .enabled = */ false,
118    /* .use_cpu_result = */ false,
119    /* .log_differences = */ true,
120    /* .tolerance = */ 0.01,
121    /* .max_log_elements = */ 4096
122};
123
124static ggml_et_cpu_compare_config mul_mat_id_cpu_compare_config = {
125    /* .enabled = */ false,
126    /* .use_cpu_result = */ false,
127    /* .log_differences = */ true,
128    /* .tolerance = */ 0.01,
129    /* .max_log_elements = */ 4096
130};
131
132static ggml_et_cpu_compare_config softmax_cpu_compare_config = {
133    /* .enabled = */ false,
134    /* .use_cpu_result = */ false,
135    /* .log_differences = */ true,
136    /* .tolerance = */ 1e-5f,
137    /* .max_log_elements = */ 1024
138};
139
140static ggml_et_cpu_compare_config get_rows_cpu_compare_config = {
141    /* .enabled = */ false,
142    /* .use_cpu_result = */ false,
143    /* .log_differences = */ true,
144    /* .tolerance = */ 1e-6f,
145    /* .max_log_elements = */ 2048
146};
147
148static ggml_et_cpu_compare_config pad_cpu_compare_config = {
149    /* .enabled = */ false,
150    /* .use_cpu_result = */ false,
151    /* .log_differences = */ true,
152    /* .tolerance = */ 1e-6f,
153    /* .max_log_elements = */ 4096
154};
155
156static ggml_et_cpu_compare_config cont_cpu_compare_config = {
157    /* .enabled = */ false,
158    /* .use_cpu_result = */ false,
159    /* .log_differences = */ true,
160    /* .tolerance = */ 1e-6f,
161    /* .max_log_elements = */ 4096
162};
163
164static ggml_et_cpu_compare_config concat_cpu_compare_config = {
165    /* .enabled = */ false,
166    /* .use_cpu_result = */ false,
167    /* .log_differences = */ true,
168    /* .tolerance = */ 1e-6f,
169    /* .max_log_elements = */ 4096
170};
171
172static ggml_et_cpu_compare_config cumsum_cpu_compare_config = {
173    /* .enabled = */ false,
174    /* .use_cpu_result = */ false,
175    /* .log_differences = */ true,
176    /* .tolerance = */ 1e-6f,
177    /* .max_log_elements = */ 4096
178};
179
180static ggml_et_cpu_compare_config repeat_cpu_compare_config = {
181    /* .enabled = */ false,
182    /* .use_cpu_result = */ false,
183    /* .log_differences = */ true,
184    /* .tolerance = */ 1e-6f,
185    /* .max_log_elements = */ 4096
186};
187
188static ggml_et_cpu_compare_config ssm_conv_cpu_compare_config = {
189    /* .enabled = */ false,
190    /* .use_cpu_result = */ false,
191    /* .log_differences = */ true,
192    /* .tolerance = */ 1e-6f,
193    /* .max_log_elements = */ 4096
194};
195
196static ggml_et_cpu_compare_config rwkv_wkv6_cpu_compare_config = {
197    /* .enabled = */ false,
198    /* .use_cpu_result = */ false,
199    /* .log_differences = */ true,
200    /* .tolerance = */ 1e-4f,
201    /* .max_log_elements = */ 4096
202};
203
204static ggml_et_cpu_compare_config rwkv_wkv7_cpu_compare_config = {
205    /* .enabled = */ false,
206    /* .use_cpu_result = */ false,
207    /* .log_differences = */ true,
208    /* .tolerance = */ 1e-4f,
209    /* .max_log_elements = */ 4096
210};
211
212static ggml_et_cpu_compare_config set_rows_cpu_compare_config = {
213    /* .enabled = */ false,
214    /* .use_cpu_result = */ false,
215    /* .log_differences = */ true,
216    /* .tolerance = */ 1e-6f,
217    /* .max_log_elements = */ 2048
218};
219
220bool ggml_et_op_rms_norm_mul(ggml_backend_et_device_context * dev_ctx,
221                             const ggml_tensor *              rms_norm_node,
222                             const ggml_tensor *              mul_node) {
223    ET_PERF_START();
224
225    if (!dev_ctx || !rms_norm_node || !mul_node) {
226        GGML_LOG_ERROR("ET: Invalid parameters for fused RMS_NORM_MUL operation\n");
227        return false;
228    }
229
230    if (!rms_norm_node->src[0]) {
231        GGML_LOG_ERROR("ET: Fused RMS_NORM_MUL missing required input\n");
232        return false;
233    }
234
235    // Extract weights: the MUL operand that isn't the rms_norm output
236    const ggml_tensor * weights = (mul_node->src[0] == rms_norm_node) ? mul_node->src[1] : mul_node->src[0];
237
238    if (!weights) {
239        GGML_LOG_ERROR("ET: Fused RMS_NORM_MUL missing weights tensor\n");
240        return false;
241    }
242
243    float eps;
244    memcpy(&eps, rms_norm_node->op_params, sizeof(float));
245
246    ggml_et_rms_norm_mul_params params;
247    params.src0 = *rms_norm_node->src[0];  // input to normalize
248    params.src1 = *weights;                // normalization weights
249    params.dst  = *mul_node;               // final output
250    params.eps  = eps;
251
252    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "rms_norm_mul_f32", &params, sizeof(params), 0xFFFFFFFF);
253
254    ET_PERF_END_EXT("RMS_NORM_MUL", "rms_norm_mul_f32", mul_node, "eps=%.6f", (double) eps);
255    return kernel_result;
256}
257
258bool ggml_et_op_scale(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
259    ET_PERF_START();
260
261    if (!dev_ctx || !node) {
262        GGML_LOG_ERROR("ET: Invalid parameters for SCALE operation\n");
263        return false;
264    }
265
266    if (!node->src[0]) {
267        GGML_LOG_ERROR("ET: SCALE operation missing required input\n");
268        return false;
269    }
270
271    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
272        GGML_LOG_ERROR("ET: SCALE operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
273                       ggml_type_name(node->src[0]->type));
274        return false;
275    }
276
277    float scale, bias;
278    memcpy(&scale, (const float *) node->op_params + 0, sizeof(float));
279    memcpy(&bias, (const float *) node->op_params + 1, sizeof(float));
280
281    ggml_et_scale_params params;
282    params.src0  = *node->src[0];
283    params.dst   = *node;
284    params.scale = scale;
285    params.bias  = bias;
286
287    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "scale_f32", &params, sizeof(params), 0xFFFFFFFF);
288
289    ET_PERF_END_EXT("SCALE", "scale_f32", node, "scale=%.6f|bias=%.6f", (double) scale, (double) bias);
290    return kernel_result;
291}
292
293bool ggml_et_op_sqr(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
294    ET_PERF_START();
295
296    if (!dev_ctx || !node) {
297        GGML_LOG_ERROR("ET: Invalid parameters for SQR operation\n");
298        return false;
299    }
300
301    if (!node->src[0]) {
302        GGML_LOG_ERROR("ET: SQR operation missing required input\n");
303        return false;
304    }
305
306    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
307        GGML_LOG_ERROR("ET: SQR operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
308                       ggml_type_name(node->src[0]->type));
309        return false;
310    }
311
312    ggml_et_sqr_params params;
313    params.src0 = *node->src[0];  // F32 input tensor
314    params.dst  = *node;          // F32 output tensor
315
316    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
317    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
318    bool                    cpu_comparison_active = false;
319    if (sqr_cpu_compare_config.enabled) {
320        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_SQR)) {
321            cpu_comparison_active = true;
322        } else {
323            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for SQR operation\n");
324        }
325    }
326
327    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "sqr_f32", &params, sizeof(params), 0xFFFFFFFF);
328
329    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
330    if (cpu_comparison_active) {
331        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &sqr_cpu_compare_config)) {
332            GGML_LOG_WARN("ET: CPU comparison failed for SQR operation\n");
333        }
334        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
335    }
336
337    ET_PERF_END("SQR", "sqr_f32", node);
338    return kernel_result;
339}
340
341bool ggml_et_op_sum_rows(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
342    ET_PERF_START();
343
344    if (!dev_ctx || !node) {
345        GGML_LOG_ERROR("ET: Invalid parameters for SUM_ROWS operation\n");
346        return false;
347    }
348
349    if (!node->src[0]) {
350        GGML_LOG_ERROR("ET: SUM_ROWS operation missing required input\n");
351        return false;
352    }
353
354    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
355        GGML_LOG_ERROR("ET: SUM_ROWS operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
356                       ggml_type_name(node->src[0]->type));
357        return false;
358    }
359
360    ggml_et_sum_rows_params params;
361    params.src0 = *node->src[0];
362    params.dst  = *node;
363
364    // Phase 1: Initialize CPU comparison context
365    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
366    bool                    cpu_comparison_active = false;
367    if (sum_rows_cpu_compare_config.enabled) {
368        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_SUM_ROWS)) {
369            cpu_comparison_active = true;
370        } else {
371            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for SUM_ROWS operation\n");
372        }
373    }
374
375    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "sum_rows_f32", &params, sizeof(params), 0xFFFFFFFF);
376
377    // Phase 2: Execute CPU computation and compare
378    if (cpu_comparison_active) {
379        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &sum_rows_cpu_compare_config)) {
380            GGML_LOG_WARN("ET: CPU comparison failed for SUM_ROWS operation\n");
381        }
382        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
383    }
384
385    ET_PERF_END("SUM_ROWS", "sum_rows_f32", node);
386    return kernel_result;
387}
388
389bool ggml_et_op_mean(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
390    ET_PERF_START();
391
392    if (!dev_ctx || !node) {
393        GGML_LOG_ERROR("ET: Invalid parameters for MEAN operation\n");
394        return false;
395    }
396
397    if (!node->src[0]) {
398        GGML_LOG_ERROR("ET: MEAN operation missing required input\n");
399        return false;
400    }
401
402    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
403        GGML_LOG_ERROR("ET: MEAN operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
404                       ggml_type_name(node->src[0]->type));
405        return false;
406    }
407
408    ggml_et_mean_params params;
409    params.src0 = *node->src[0];
410    params.dst  = *node;
411
412    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
413    bool                    cpu_comparison_active = false;
414    if (mean_cpu_compare_config.enabled) {
415        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_MEAN)) {
416            cpu_comparison_active = true;
417        } else {
418            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for MEAN operation\n");
419        }
420    }
421
422    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "mean_f32", &params, sizeof(params), 0xFFFFFFFF);
423
424    if (cpu_comparison_active) {
425        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &mean_cpu_compare_config)) {
426            GGML_LOG_WARN("ET: CPU comparison failed for MEAN operation\n");
427        }
428        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
429    }
430
431    ET_PERF_END("MEAN", "mean_f32", node);
432    return kernel_result;
433}
434
435bool ggml_et_op_clamp(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
436    ET_PERF_START();
437
438    if (!dev_ctx || !node) {
439        GGML_LOG_ERROR("ET: Invalid parameters for CLAMP operation\n");
440        return false;
441    }
442
443    if (!node->src[0]) {
444        GGML_LOG_ERROR("ET: CLAMP operation missing required input\n");
445        return false;
446    }
447
448    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
449        GGML_LOG_ERROR("ET: CLAMP operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
450                       ggml_type_name(node->src[0]->type));
451        return false;
452    }
453
454    ggml_et_clamp_params params;
455    params.src0 = *node->src[0];
456    params.dst  = *node;
457    // op_params layout per ggml.c::ggml_clamp: { min, max } as floats
458    memcpy(&params.min_val, (const float *) node->op_params + 0, sizeof(float));
459    memcpy(&params.max_val, (const float *) node->op_params + 1, sizeof(float));
460
461    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
462    bool                    cpu_comparison_active = false;
463    if (clamp_cpu_compare_config.enabled) {
464        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_CLAMP)) {
465            cpu_comparison_active = true;
466        } else {
467            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for CLAMP operation\n");
468        }
469    }
470
471    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "clamp_f32", &params, sizeof(params), 0xFFFFFFFF);
472
473    if (cpu_comparison_active) {
474        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &clamp_cpu_compare_config)) {
475            GGML_LOG_WARN("ET: CPU comparison failed for CLAMP operation\n");
476        }
477        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
478    }
479
480    ET_PERF_END("CLAMP", "clamp_f32", node);
481    return kernel_result;
482}
483
484bool ggml_et_op_unary(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
485    ET_PERF_START();
486
487    if (!dev_ctx || !node) {
488        GGML_LOG_ERROR("ET: Invalid parameters for UNARY operation\n");
489        return false;
490    }
491
492    if (!node->src[0]) {
493        GGML_LOG_ERROR("ET: UNARY operation missing required input\n");
494        return false;
495    }
496
497    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
498        GGML_LOG_ERROR("ET: UNARY operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
499                       ggml_type_name(node->src[0]->type));
500        return false;
501    }
502
503    const ggml_unary_op uop     = ggml_get_unary_op(node);
504    const char *        op_name = ggml_unary_op_name(uop);
505
506    ggml_et_unary_params params;
507    params.src0     = *node->src[0];  // F32 input tensor
508    params.dst      = *node;          // F32 output tensor
509    params.unary_op = (int32_t) uop;
510
511    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
512    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
513    bool                    cpu_comparison_active = false;
514    if (unary_cpu_compare_config.enabled) {
515        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_UNARY)) {
516            cpu_comparison_active = true;
517        } else {
518            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for UNARY/%s operation\n", op_name);
519        }
520    }
521
522    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "unary_f32", &params, sizeof(params), 0xFFFFFFFF);
523
524    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
525    if (cpu_comparison_active) {
526        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &unary_cpu_compare_config)) {
527            GGML_LOG_WARN("ET: CPU comparison failed for UNARY/%s operation\n", op_name);
528        }
529        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
530    }
531
532    ET_PERF_END_EXT("UNARY", "unary_f32", node, "op=%s", op_name);
533    return kernel_result;
534}
535
536bool ggml_et_op_mul(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
537    // Delegate to generic element map operation
538    return ggml_et_op_elmap(dev_ctx, node);
539}
540
541bool ggml_et_op_add(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
542    // Delegate to generic element map operation
543    return ggml_et_op_elmap(dev_ctx, node);
544}
545
546bool ggml_et_op_sub(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
547    // Delegate to generic element map operation
548    return ggml_et_op_elmap(dev_ctx, node);
549}
550
551bool ggml_et_op_elmap(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
552    ET_PERF_START();
553
554    if (!dev_ctx || !node) {
555        GGML_LOG_ERROR("ET: Invalid parameters for element map operation\n");
556        return false;
557    }
558
559    if (!node->src[0] || !node->src[1]) {
560        GGML_LOG_ERROR("ET: Element map operation missing required inputs\n");
561        return false;
562    }
563
564    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32 || node->src[1]->type != GGML_TYPE_F32) {
565        GGML_LOG_ERROR("ET: Element map operation with unsupported types: dst=%s src0=%s src1=%s\n",
566                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
567                       ggml_type_name(node->src[1]->type));
568        return false;
569    }
570
571    const char * op_name = ggml_op_name(node->op);
572
573    ggml_et_elmap_params params;
574    params.src0 = *node->src[0];
575    params.src1 = *node->src[1];
576    params.dst  = *node;  // F32 output tensor (op type stored in dst.op)
577
578    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
579    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
580    bool                    cpu_comparison_active = false;
581    if (elmap_cpu_compare_config.enabled) {
582        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, node->op)) {
583            cpu_comparison_active = true;
584        } else {
585            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for %s operation\n", op_name);
586        }
587    }
588
589    // fprintf(stderr, "ET: el_map s0 [%ld, %ld, %ld, %ld] s1 [%ld, %ld, %ld, %ld]\n",
590    //     node->src[0]->ne[0], node->src[0]->ne[1], node->src[0]->ne[2], node->src[0]->ne[3],
591    //     node->src[1]->ne[0], node->src[1]->ne[1], node->src[1]->ne[2], node->src[1]->ne[3]);
592
593    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "el_map_f32", &params, sizeof(params), 0xFFFFFFFF);
594
595    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
596    if (cpu_comparison_active) {
597        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &elmap_cpu_compare_config)) {
598            GGML_LOG_WARN("ET: CPU comparison failed for %s operation\n", op_name);
599        }
600        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
601    }
602
603    ET_PERF_END(op_name, "el_map_f32", node);
604    return kernel_result;
605}
606
607bool ggml_et_op_glu(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
608    ET_PERF_START();
609
610    // Validate inputs
611    if (!dev_ctx || !node) {
612        GGML_LOG_ERROR("ET: Invalid parameters for GLU operation\n");
613        return false;
614    }
615
616    if (!node->src[0]) {
617        GGML_LOG_ERROR("ET: GLU operation missing required input\n");
618        return false;
619    }
620
621    const bool is_split_mode = node->src[1] != nullptr;
622
623    // Only support F32 (as validated by supports_op)
624    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32 ||
625        (is_split_mode && node->src[1]->type != GGML_TYPE_F32)) {
626        return false;
627    }
628
629    // Extract GLU operation parameters from op_params
630    int32_t glu_op_type = ggml_get_op_params_i32(node, 0);  // GLU variant (REGLU, GEGLU, SWIGLU, etc.)
631    int32_t swapped     = ggml_get_op_params_i32(node, 1);  // Whether gate/value are swapped
632
633    // Supported variants
634    switch (glu_op_type) {
635        case GGML_GLU_OP_REGLU:
636        case GGML_GLU_OP_GEGLU:
637        case GGML_GLU_OP_SWIGLU:
638        case GGML_GLU_OP_SWIGLU_OAI:
639        case GGML_GLU_OP_SWIGLU_CLAMP:
640        case GGML_GLU_OP_GEGLU_ERF:
641        case GGML_GLU_OP_GEGLU_QUICK:
642            break;
643        default:
644            GGML_LOG_ERROR("ET: GLU operation with unsupported variant: %s\n",
645                           ggml_glu_op_name((ggml_glu_op) glu_op_type));
646            return false;
647    }
648
649    // Get GLU operation name for logging
650    const char * glu_op_name = ggml_glu_op_name((ggml_glu_op) glu_op_type);
651
652    // Pack parameters. Single-tensor mode is encoded by zeroing src1.
653    ggml_et_glu_params params = {};
654    params.src0               = *node->src[0];
655    if (is_split_mode) {
656        params.src1 = *node->src[1];
657    }
658    params.dst         = *node;
659    params.glu_op_type = glu_op_type;
660    params.swapped     = swapped;
661    params.alpha       = 0.0f;
662    params.limit       = 0.0f;
663    if (glu_op_type == GGML_GLU_OP_SWIGLU_OAI) {
664        params.alpha = ggml_get_op_params_f32(node, 2);
665    }
666    if (glu_op_type == GGML_GLU_OP_SWIGLU_OAI || glu_op_type == GGML_GLU_OP_SWIGLU_CLAMP) {
667        params.limit = ggml_get_op_params_f32(node, 3);
668    }
669    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
670    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
671    bool                    cpu_comparison_active = false;
672    if (glu_cpu_compare_config.enabled) {
673        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_GLU)) {
674            cpu_comparison_active = true;
675        } else {
676            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for %s operation\n", glu_op_name);
677        }
678    }
679
680    // Launch ET kernel
681    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "glu_f32", &params, sizeof(params), 0xFFFFFFFF);
682
683    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
684    if (cpu_comparison_active) {
685        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &glu_cpu_compare_config)) {
686            GGML_LOG_WARN("ET: CPU comparison failed for %s operation\n", glu_op_name);
687        }
688        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
689    }
690
691    ET_PERF_END("GLU", "glu_f32", node);
692    return kernel_result;
693}
694
695bool ggml_et_op_mul_mat(ggml_backend_et_device_context * dev_ctx,
696                        const ggml_tensor *              node,
697                        const ggml_tensor *              add_node) {
698    ET_PERF_START();
699
700    if (!dev_ctx || !node) {
701        GGML_LOG_ERROR("ET: Invalid parameters for MUL_MAT operation\n");
702        return false;
703    }
704
705    if (!node->src[0] || !node->src[1]) {
706        GGML_LOG_ERROR("ET: MUL_MAT operation missing required inputs\n");
707        return false;
708    }
709
710    // Fused MM+ADD: when add_node is non-NULL the caller has already validated
711    // (Q8_0 weights, F32 acts, exact-shape ADD with stride parity to dst) via
712    // ggml_et_can_fuse({MUL_MAT, ADD}). The kernel writes dst = mm + bias and
713    // the ADD's output replaces MM's as the actual dst.
714    const ggml_tensor * fused_dst   = add_node ? add_node : node;
715    const ggml_tensor * bias_tensor = nullptr;
716    if (add_node) {
717        bias_tensor = (add_node->src[0] == node) ? add_node->src[1] : add_node->src[0];
718    }
719
720    const char * kernel_name;
721    const char * src0_type_name;
722
723    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_Q4_0 && node->src[1]->type == GGML_TYPE_F32 &&
724        node->src[1]->ne[1] >= 53 &&      // N >= 53
725        node->src[0]->ne[1] % 16 == 0 &&  // M % TILE_M
726        node->src[0]->ne[0] % 32 == 0) {  // K % BLOCK_K (Q4_0 block)
727
728        // Matrix engine for N >= 53; partial N (via n_cur-1) and errata padding are handled in-kernel.
729        kernel_name    = "mul_mat_Q4_0_matrix_engine";
730        src0_type_name = "Q4_0";
731
732    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_Q4_0 &&
733               node->src[1]->type == GGML_TYPE_F32) {
734        kernel_name    = "mul_mat_Q4_0";  // N < 53, or M % 16 != 0 or K % 32 != 0
735        src0_type_name = "Q4_0";
736
737    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_Q8_0 &&
738               node->src[1]->type == GGML_TYPE_F32) {
739        kernel_name    = "mul_mat_Q8_0";
740        src0_type_name = "Q8_0";
741
742    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F16 &&
743               node->src[1]->type == GGML_TYPE_F16 && node->ne[0] % 16 == 0 && node->src[0]->ne[0] % 16 == 0 &&
744               node->src[0]->ne[1] % 16 == 0 && node->src[1]->ne[0] != 1) {
745        kernel_name    = "mul_mat_f16_matrix_engine";
746        src0_type_name = "F16";
747
748    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F16 &&
749               (node->src[1]->type == GGML_TYPE_F16 || node->src[1]->type == GGML_TYPE_F32)) {
750        kernel_name    = "mul_mat_f16";
751        src0_type_name = "F16";
752
753    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32 &&
754               node->src[1]->type == GGML_TYPE_F32 && node->ne[0] % 16 == 0 && node->src[0]->ne[0] % 16 == 0 &&
755               node->src[0]->ne[1] % 16 == 0 && node->src[1]->ne[0] != 1) {  // GEMV is faster with the generic path
756
757        kernel_name    = "mul_mat_f32_matrix_engine";
758        src0_type_name = "F32";
759    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32 &&
760               (node->src[1]->type == GGML_TYPE_F16 || node->src[1]->type == GGML_TYPE_F32)) {
761        kernel_name    = "mul_mat_f32";
762        src0_type_name = "F32";
763    } else {
764        GGML_LOG_ERROR("ET: MUL_MAT operation with unsupported types: dst=%s src0=%s src1=%s\n",
765                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
766                       ggml_type_name(node->src[1]->type));
767        return false;
768    }
769
770    ggml_et_binary_params params;
771    params.src0 = *node->src[0];  // weight matrix
772    params.src1 = *node->src[1];  // activation matrix
773    params.dst  = *fused_dst;     // output (= add_node when fused, else node)
774
775    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
776    bool                    cpu_comparison_active = false;
777    if (mul_mat_cpu_compare_config.enabled) {
778        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, fused_dst, GGML_OP_MUL_MAT)) {
779            cpu_comparison_active = true;
780        } else {
781            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for MUL_MAT operation\n");
782        }
783    }
784
785    bool kernel_result;
786    if (node->src[0]->type == GGML_TYPE_Q8_0) {
787        // Q8_0 kernel always takes the extended struct. bias.data is non-NULL
788        // only on the fused path; otherwise the kernel skips the add entirely.
789        ggml_et_mm_q8_params q8_params = {};
790        q8_params.src0                 = params.src0;
791        q8_params.src1                 = params.src1;
792        q8_params.dst                  = params.dst;
793        if (bias_tensor) {
794            q8_params.bias = *bias_tensor;
795        }
796        kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &q8_params, sizeof(q8_params), 0xFFFFFFFF);
797    } else {
798        // Non-Q8 MM kernels don't yet support fused-add; the graph fuse check
799        // already rejects non-Q8 pairs, so add_node is always nullptr here.
800        kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
801    }
802
803    // printf("Tensor error:");
804    // if (params.src0.data != NULL)
805    // {
806    //     printf("Ptr OK\n");
807    //     printf("node->data ptr = %p\n", node->data);
808    //     // if (once < 100){
809    //     //     // uint64_t * host_data = (uint64_t *) node->data;
810    //     //     // printf("Tensor error: %lu\n", host_data[0]);
811
812    //     //     // printf("Tensor error:");
813    //     //     once++;
814    //     // }
815    // }
816
817    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
818    if (cpu_comparison_active) {
819        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, fused_dst, &mul_mat_cpu_compare_config)) {
820            GGML_LOG_WARN("ET: CPU comparison failed for MUL_MAT operation\n");
821        }
822        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
823    }
824
825    {
826        // Calculate actual FLOPs including batch/sequence dimensions
827        // dst shape: [M, N, ne2, ne3] where M=ne[1], N=ne[0]
828        int64_t m   = node->ne[1];
829        int64_t n   = node->ne[0];
830        int64_t k   = node->src[0]->ne[0];
831        int64_t ne2 = node->ne[2];
832        int64_t ne3 = node->ne[3];
833
834        // Total FLOPs = (batch_size) * M * N * (2*K - 1)
835        // Each MxN matrix-matrix multiply does M*N*(2*K-1) FLOPs
836        // Broadcasting is handled by repeating computation, so count actual operations
837        int64_t batch_size  = ne2 * ne3;
838        int64_t total_flops = batch_size * m * n * (2 * k - 1);
839
840        char kernel_variant[64];
841        snprintf(kernel_variant, sizeof(kernel_variant), "%s_%sx%s", kernel_name, src0_type_name,
842                 ggml_type_name(node->src[1]->type));
843        ET_PERF_END_EXT("MUL_MAT", kernel_variant, node, "flops=%" PRId64, total_flops);
844    }
845    return kernel_result;
846}
847
848bool ggml_et_op_mul_mat_id(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
849    ET_PERF_START();
850    if (!dev_ctx || !node) {
851        GGML_LOG_ERROR("ET: Invalid parameters for MUL_MAT_ID operation\n");
852        return false;
853    }
854
855    if (!node->src[0] || !node->src[1] || !node->src[2]) {
856        GGML_LOG_ERROR("ET: MUL_MAT_ID operation missing required inputs\n");
857        return false;
858    }
859
860    const char * kernel_name;
861    const char * src0_type_name;
862
863    // Support Q8_0/Q4_0/F16/F32 x F32 -> F32 matrix multiplication with expert selection
864    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_Q8_0 && node->src[1]->type == GGML_TYPE_F32 &&
865        node->src[2]->type == GGML_TYPE_I32) {
866        kernel_name    = "mul_mat_id_Q8_0";
867        src0_type_name = "Q8_0";
868
869    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_Q4_0 &&
870               node->src[1]->type == GGML_TYPE_F32 && node->src[2]->type == GGML_TYPE_I32) {
871        kernel_name    = "mul_mat_id_Q4_0";
872        src0_type_name = "Q4_0";
873
874    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F16 &&
875               node->src[1]->type == GGML_TYPE_F32 && node->src[2]->type == GGML_TYPE_I32) {
876        kernel_name    = "mul_mat_id_f32";
877        src0_type_name = "F16";
878
879    } else if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32 &&
880               node->src[1]->type == GGML_TYPE_F32 && node->src[2]->type == GGML_TYPE_I32) {
881        kernel_name    = "mul_mat_id_f32";
882        src0_type_name = "F32";
883
884    } else {
885        GGML_LOG_ERROR("ET: MUL_MAT_ID operation with unsupported types: dst=%s src0=%s src1=%s src2=%s\n",
886                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
887                       ggml_type_name(node->src[1]->type), ggml_type_name(node->src[2]->type));
888        return false;
889    }
890
891    // Pack parameters - copy full tensor structures
892    ggml_et_mul_mat_id_params params;
893    params.src0 = *node->src[0];  // Expert weight matrices (Q8_0/F16/F32)
894    params.src1 = *node->src[1];  // Activation matrix (F32)
895    params.src2 = *node->src[2];  // Expert indices (I32)
896    params.dst  = *node;          // Output matrix (F32)
897
898    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
899    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
900    bool                    cpu_comparison_active = false;
901    if (mul_mat_id_cpu_compare_config.enabled) {
902        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_MUL_MAT_ID)) {
903            cpu_comparison_active = true;
904        } else {
905            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for MUL_MAT_ID operation\n");
906        }
907    }
908
909    // Launch ET kernel
910    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
911
912    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
913    if (cpu_comparison_active) {
914        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &mul_mat_id_cpu_compare_config)) {
915            GGML_LOG_WARN("ET: CPU comparison failed for MUL_MAT_ID operation\n");
916        }
917        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
918    }
919
920    // Calculate FLOPs (approximate - similar to MUL_MAT but with expert routing overhead)
921    // Each expert computation is similar to a MUL_MAT, but we only compute for selected experts
922    int64_t K             = node->src[0]->ne[0];
923    int64_t M             = node->src[0]->ne[1];
924    int64_t n_expert_used = node->src[2]->ne[0];
925    int64_t batch         = node->src[2]->ne[1];
926
927    int64_t total_flops = batch * n_expert_used * M * (2 * K - 1);
928
929    char kernel_variant[64];
930    snprintf(kernel_variant, sizeof(kernel_variant), "%s_%sx%s", kernel_name, src0_type_name,
931             ggml_type_name(node->src[1]->type));
932    ET_PERF_END_EXT("MUL_MAT_ID", kernel_variant, node, "flops=%" PRId64 "|n_expert=%lld|n_expert_used=%lld",
933                    total_flops, (long long) node->src[0]->ne[2], (long long) n_expert_used);
934
935    return kernel_result;
936}
937
938bool ggml_et_op_rope(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
939    ET_PERF_START();
940
941    if (!dev_ctx || !node) {
942        GGML_LOG_ERROR("ET: Invalid parameters for ROPE operation\n");
943        return false;
944    }
945
946    if (!node->src[0] || !node->src[1]) {
947        GGML_LOG_ERROR("ET: ROPE operation missing required inputs\n");
948        return false;
949    }
950
951    const char * kernel_name;
952
953    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32 && node->src[1]->type == GGML_TYPE_I32) {
954        kernel_name = "rope_f32";
955    } else {
956        return false;
957    }
958
959    // Pack parameters - copy full tensor structures and op_params
960    ggml_et_rope_params params;
961    params.src0 = *node->src[0];                       // F32 input tensor
962    params.src1 = *node->src[1];                       // I32 position tensor
963    if (node->src[2]) {
964        params.src2 = *node->src[2];                   // F32 frequency factors (optional)
965    } else {
966        memset(&params.src2, 0, sizeof(params.src2));  // Zero if not provided
967    }
968    params.dst = *node;                                // F32 output tensor
969
970    params.rope_params.n_past     = ((const int32_t *) node->op_params)[0];
971    params.rope_params.n_dims     = ((const int32_t *) node->op_params)[1];
972    params.rope_params.mode       = ((const int32_t *) node->op_params)[2];
973    params.rope_params.n_ctx      = ((const int32_t *) node->op_params)[3];
974    params.rope_params.n_ctx_orig = ((const int32_t *) node->op_params)[4];
975    memcpy(&params.rope_params.freq_base, (const int32_t *) node->op_params + 5, sizeof(float));
976    memcpy(&params.rope_params.freq_scale, (const int32_t *) node->op_params + 6, sizeof(float));
977    memcpy(&params.rope_params.ext_factor, (const int32_t *) node->op_params + 7, sizeof(float));
978    memcpy(&params.rope_params.attn_factor, (const int32_t *) node->op_params + 8, sizeof(float));
979    memcpy(&params.rope_params.beta_fast, (const int32_t *) node->op_params + 9, sizeof(float));
980    memcpy(&params.rope_params.beta_slow, (const int32_t *) node->op_params + 10, sizeof(float));
981    if (params.rope_params.mode & GGML_ROPE_TYPE_MROPE) {
982        memcpy(params.rope_params.sections, (const int32_t *) node->op_params + 11, sizeof(int32_t) * 4);
983    } else {
984        memset(params.rope_params.sections, 0, sizeof(params.rope_params.sections));
985    }
986
987    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
988    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
989    bool                    cpu_comparison_active = false;
990    if (rope_cpu_compare_config.enabled) {
991        GGML_LOG_DEBUG("ET: Initializing CPU comparison for ROPE operation\n");
992        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_ROPE)) {
993            cpu_comparison_active = true;
994        } else {
995            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for ROPE operation\n");
996        }
997    }
998
999    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1000
1001    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1002    if (cpu_comparison_active) {
1003        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &rope_cpu_compare_config)) {
1004            GGML_LOG_WARN("ET: CPU comparison failed for ROPE operation\n");
1005        }
1006        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1007    }
1008
1009    ET_PERF_END_EXT("ROPE", kernel_name, node, "mode=0x%x|n_dims=%d|freq_base=%.2f|freq_scale=%.2f",
1010                    params.rope_params.mode, params.rope_params.n_dims, (double) params.rope_params.freq_base,
1011                    (double) params.rope_params.freq_scale);
1012    return kernel_result;
1013}
1014
1015bool ggml_et_op_rms_norm(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1016    ET_PERF_START();
1017
1018    if (!dev_ctx || !node) {
1019        GGML_LOG_ERROR("ET: Invalid parameters for RMS_NORM operation\n");
1020        return false;
1021    }
1022
1023    if (!node->src[0]) {
1024        GGML_LOG_ERROR("ET: RMS_NORM operation missing required input\n");
1025        return false;
1026    }
1027
1028    const char * kernel_name;
1029
1030    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32) {
1031        kernel_name = "rms_norm_f32";
1032
1033    } else {
1034        GGML_LOG_ERROR("ET: RMS_NORM operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1035                       ggml_type_name(node->src[0]->type));
1036        return false;
1037    }
1038
1039    float eps;
1040    memcpy(&eps, node->op_params, sizeof(float));
1041
1042    ggml_et_rms_norm_params params;
1043    params.src0 = *node->src[0];  // F32 input tensor
1044    params.dst  = *node;          // F32 output tensor
1045    params.eps  = eps;            // Epsilon parameter for numerical stability
1046
1047    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1048    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1049    bool                    cpu_comparison_active = false;
1050    if (rms_norm_cpu_compare_config.enabled) {
1051        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_RMS_NORM)) {
1052            cpu_comparison_active = true;
1053        } else {
1054            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for RMS_NORM operation\n");
1055        }
1056    }
1057
1058    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1059
1060    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1061    if (cpu_comparison_active) {
1062        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &rms_norm_cpu_compare_config)) {
1063            GGML_LOG_WARN("ET: CPU comparison failed for RMS_NORM operation\n");
1064        }
1065        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1066    }
1067
1068    ET_PERF_END_EXT("RMS_NORM", kernel_name, node, "eps=%.6f", (double) eps);
1069    return kernel_result;
1070}
1071
1072bool ggml_et_op_norm(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1073    ET_PERF_START();
1074
1075    if (!dev_ctx || !node) {
1076        GGML_LOG_ERROR("ET: Invalid parameters for NORM operation\n");
1077        return false;
1078    }
1079
1080    if (!node->src[0]) {
1081        GGML_LOG_ERROR("ET: NORM operation missing required input\n");
1082        return false;
1083    }
1084
1085    const char * kernel_name;
1086
1087    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32) {
1088        kernel_name = "norm_f32";
1089
1090    } else {
1091        GGML_LOG_ERROR("ET: NORM operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1092                       ggml_type_name(node->src[0]->type));
1093        return false;
1094    }
1095
1096    float eps;
1097    memcpy(&eps, node->op_params, sizeof(float));
1098
1099    ggml_et_norm_params params;
1100    params.src0 = *node->src[0];  // F32 input tensor
1101    params.dst  = *node;          // F32 output tensor
1102    params.eps  = eps;            // Epsilon parameter for numerical stability
1103
1104    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1105    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1106    bool                    cpu_comparison_active = false;
1107    if (norm_cpu_compare_config.enabled) {
1108        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_NORM)) {
1109            cpu_comparison_active = true;
1110        } else {
1111            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for NORM operation\n");
1112        }
1113    }
1114
1115    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1116
1117    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1118    if (cpu_comparison_active) {
1119        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &norm_cpu_compare_config)) {
1120            GGML_LOG_WARN("ET: CPU comparison failed for NORM operation\n");
1121        }
1122        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1123    }
1124
1125    ET_PERF_END_EXT("NORM", kernel_name, node, "eps=%.6f", (double) eps);
1126    return kernel_result;
1127}
1128
1129bool ggml_et_op_l2_norm(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1130    ET_PERF_START();
1131
1132    if (!dev_ctx || !node) {
1133        GGML_LOG_ERROR("ET: Invalid parameters for L2_NORM operation\n");
1134        return false;
1135    }
1136
1137    if (!node->src[0]) {
1138        GGML_LOG_ERROR("ET: L2_NORM operation missing required input\n");
1139        return false;
1140    }
1141
1142    const char * kernel_name;
1143
1144    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32) {
1145        kernel_name = "l2_norm_f32";
1146
1147    } else {
1148        GGML_LOG_ERROR("ET: L2_NORM operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1149                       ggml_type_name(node->src[0]->type));
1150        return false;
1151    }
1152
1153    float eps;
1154    memcpy(&eps, node->op_params, sizeof(float));
1155
1156    ggml_et_l2_norm_params params;
1157    params.src0 = *node->src[0];  // F32 input tensor
1158    params.dst  = *node;          // F32 output tensor
1159    params.eps  = eps;            // Epsilon parameter for numerical stability
1160
1161    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1162    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1163    bool                    cpu_comparison_active = false;
1164    if (l2_norm_cpu_compare_config.enabled) {
1165        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_L2_NORM)) {
1166            cpu_comparison_active = true;
1167        } else {
1168            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for L2_NORM operation\n");
1169        }
1170    }
1171
1172    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1173
1174    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1175    if (cpu_comparison_active) {
1176        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &l2_norm_cpu_compare_config)) {
1177            GGML_LOG_WARN("ET: CPU comparison failed for L2_NORM operation\n");
1178        }
1179        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1180    }
1181
1182    ET_PERF_END_EXT("L2_NORM", kernel_name, node, "eps=%.6f", (double) eps);
1183    return kernel_result;
1184}
1185
1186bool ggml_et_op_group_norm(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1187    ET_PERF_START();
1188
1189    if (!dev_ctx || !node) {
1190        GGML_LOG_ERROR("ET: Invalid parameters for GROUP_NORM operation\n");
1191        return false;
1192    }
1193
1194    if (!node->src[0]) {
1195        GGML_LOG_ERROR("ET: GROUP_NORM operation missing required input\n");
1196        return false;
1197    }
1198
1199    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
1200        GGML_LOG_ERROR("ET: GROUP_NORM operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1201                       ggml_type_name(node->src[0]->type));
1202        return false;
1203    }
1204
1205    const int32_t n_groups = ggml_get_op_params_i32(node, 0);
1206    float         eps;
1207    memcpy(&eps, (const float *) node->op_params + 1, sizeof(float));
1208
1209    ggml_et_group_norm_params params;
1210    params.src0     = *node->src[0];
1211    params.dst      = *node;
1212    params.n_groups = n_groups;
1213    params.eps      = eps;
1214
1215    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1216    bool                    cpu_comparison_active = false;
1217    if (group_norm_cpu_compare_config.enabled) {
1218        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_GROUP_NORM)) {
1219            cpu_comparison_active = true;
1220        } else {
1221            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for GROUP_NORM operation\n");
1222        }
1223    }
1224
1225    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "group_norm_f32", &params, sizeof(params), 0xFFFFFFFF);
1226
1227    if (cpu_comparison_active) {
1228        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &group_norm_cpu_compare_config)) {
1229            GGML_LOG_WARN("ET: CPU comparison failed for GROUP_NORM operation\n");
1230        }
1231        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1232    }
1233
1234    ET_PERF_END_EXT("GROUP_NORM", "group_norm_f32", node, "eps=%.6f|n_groups=%d", (double) eps, n_groups);
1235    return kernel_result;
1236}
1237
1238bool ggml_et_op_im2col(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1239    ET_PERF_START();
1240
1241    if (!dev_ctx || !node) {
1242        GGML_LOG_ERROR("ET: Invalid parameters for IM2COL operation\n");
1243        return false;
1244    }
1245
1246    if (!node->src[0] || !node->src[1]) {
1247        GGML_LOG_ERROR("ET: IM2COL operation missing required inputs\n");
1248        return false;
1249    }
1250
1251    const bool supported_types =
1252        (node->type == GGML_TYPE_F32 && node->src[1]->type == GGML_TYPE_F32) ||
1253        (node->type == GGML_TYPE_F16 && (node->src[1]->type == GGML_TYPE_F16 || node->src[1]->type == GGML_TYPE_F32));
1254
1255    if (!supported_types) {
1256        GGML_LOG_ERROR("ET: IM2COL operation with unsupported types: dst=%s src1=%s\n", ggml_type_name(node->type),
1257                       ggml_type_name(node->src[1]->type));
1258        return false;
1259    }
1260
1261    ggml_et_im2col_params params;
1262    params.src0 = *node->src[0];
1263    params.src1 = *node->src[1];
1264    params.dst  = *node;
1265
1266    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1267    bool                    cpu_comparison_active = false;
1268    if (im2col_cpu_compare_config.enabled) {
1269        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_IM2COL)) {
1270            cpu_comparison_active = true;
1271        } else {
1272            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for IM2COL operation\n");
1273        }
1274    }
1275
1276    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "im2col", &params, sizeof(params), 0xFFFFFFFF);
1277
1278    if (cpu_comparison_active) {
1279        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &im2col_cpu_compare_config)) {
1280            GGML_LOG_WARN("ET: CPU comparison failed for IM2COL operation\n");
1281        }
1282        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1283    }
1284
1285    ET_PERF_END("IM2COL", "im2col", node);
1286    return kernel_result;
1287}
1288
1289bool ggml_et_op_conv_2d(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1290    ET_PERF_START();
1291
1292    if (!dev_ctx || !node) {
1293        return false;
1294    }
1295    if (!node->src[0] || !node->src[1]) {
1296        return false;
1297    }
1298    if (!node->data || !node->src[0]->data || !node->src[1]->data) {
1299        return false;
1300    }
1301
1302    // Kernel constraints (mirror supports_op; recheck here as a guard).
1303    const ggml_tensor * flt = node->src[0];  // [Kw, Kh, Cin, Cout]
1304    const ggml_tensor * in  = node->src[1];  // [W,  H,  Cin, N]
1305    if (node->type != GGML_TYPE_F32 || flt->type != GGML_TYPE_F32 || in->type != GGML_TYPE_F32) {
1306        return false;
1307    }
1308
1309    const int32_t s0 = ggml_get_op_params_i32(node, 0);
1310    const int32_t s1 = ggml_get_op_params_i32(node, 1);
1311    const int32_t p0 = ggml_get_op_params_i32(node, 2);
1312    const int32_t p1 = ggml_get_op_params_i32(node, 3);
1313    const int32_t d0 = ggml_get_op_params_i32(node, 4);
1314    const int32_t d1 = ggml_get_op_params_i32(node, 5);
1315
1316    if (s0 < 1 || s1 < 1) {
1317        return false;
1318    }
1319    if (d0 != 1 || d1 != 1) {
1320        return false;
1321    }
1322    if (flt->ne[2] % 16 != 0 || flt->ne[3] % 16 != 0) {
1323        return false;
1324    }
1325    if (in->ne[3] != 1) {
1326        return false;
1327    }
1328    if (node->ne[0] <= 0) {
1329        return false;  // OW > 0 (any width OK; staging path handles non-16)
1330    }
1331    (void) p0;
1332    (void) p1;
1333
1334    ggml_et_binary_params params;
1335    params.src0 = *node->src[0];
1336    params.src1 = *node->src[1];
1337    params.dst  = *node;
1338
1339    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "conv_2d_f32_me", &params, sizeof(params), 0xFFFFFFFFu);
1340
1341    ET_PERF_END("CONV_2D", "conv_2d_f32_me", node);
1342    return kernel_result;
1343}
1344
1345bool ggml_et_op_softmax(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1346    ET_PERF_START();
1347
1348    if (!dev_ctx || !node) {
1349        GGML_LOG_ERROR("ET: Invalid parameters for SOFTMAX operation\n");
1350        return false;
1351    }
1352
1353    if (!node->src[0]) {
1354        GGML_LOG_ERROR("ET: SOFTMAX operation missing required input\n");
1355        return false;
1356    }
1357
1358    const char * kernel_name;
1359
1360    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32) {
1361        kernel_name = "softmax_f32";
1362
1363    } else {
1364        GGML_LOG_ERROR("ET: SOFTMAX operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1365                       ggml_type_name(node->src[0]->type));
1366        return false;
1367    }
1368
1369    // Validate contiguity requirements
1370    if (!ggml_is_contiguous(node)) {
1371        GGML_LOG_ERROR("ET: SOFTMAX operation requires contiguous destination tensor\n");
1372        return false;
1373    }
1374
1375    if (!ggml_is_contiguous(node->src[0])) {
1376        GGML_LOG_ERROR("ET: SOFTMAX operation requires contiguous source tensor\n");
1377        return false;
1378    }
1379
1380    // Check optional mask tensor
1381    if (node->src[1]) {
1382        if (node->src[1]->type != GGML_TYPE_F32) {
1383            GGML_LOG_ERROR("ET: SOFTMAX operation with unsupported mask type: %s (F32 required)\n",
1384                           ggml_type_name(node->src[1]->type));
1385            return false;
1386        }
1387        if (!ggml_is_contiguous(node->src[1])) {
1388            GGML_LOG_ERROR("ET: SOFTMAX operation requires contiguous mask tensor\n");
1389            return false;
1390        }
1391    }
1392
1393    // Check optional sinks tensor
1394    if (node->src[2]) {
1395        if (node->src[2]->type != GGML_TYPE_F32) {
1396            GGML_LOG_ERROR("ET: SOFTMAX operation with unsupported sinks type: %s (F32 required)\n",
1397                           ggml_type_name(node->src[2]->type));
1398            return false;
1399        }
1400        if (!ggml_is_contiguous(node->src[2])) {
1401            GGML_LOG_ERROR("ET: SOFTMAX operation requires contiguous sinks tensor\n");
1402            return false;
1403        }
1404    }
1405
1406    // Extract scale and max_bias from op_params
1407    float scale    = 1.0f;
1408    float max_bias = 0.0f;
1409    if (node->op_params) {
1410        memcpy(&scale, (const float *) node->op_params + 0, sizeof(float));
1411        memcpy(&max_bias, (const float *) node->op_params + 1, sizeof(float));
1412    }
1413
1414    ggml_et_softmax_params params;
1415    params.src0 = *node->src[0];                       // F32 input tensor
1416    if (node->src[1]) {
1417        params.src1 = *node->src[1];                   // F32 mask tensor
1418    } else {
1419        memset(&params.src1, 0, sizeof(params.src1));  // Zero if no mask
1420    }
1421    if (node->src[2]) {
1422        params.src2 = *node->src[2];                   // F32 sinks tensor
1423    } else {
1424        memset(&params.src2, 0, sizeof(params.src2));  // Zero if no sinks
1425    }
1426    params.dst      = *node;                           // F32 output tensor
1427    params.scale    = scale;                           // Scale factor
1428    params.max_bias = max_bias;                        // ALiBi bias
1429
1430    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1431    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1432    bool                    cpu_comparison_active = false;
1433    if (softmax_cpu_compare_config.enabled) {
1434        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_SOFT_MAX)) {
1435            cpu_comparison_active = true;
1436        } else {
1437            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for SOFTMAX operation\n");
1438        }
1439    }
1440
1441    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1442
1443    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1444    if (cpu_comparison_active) {
1445        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &softmax_cpu_compare_config)) {
1446            GGML_LOG_WARN("ET: CPU comparison failed for SOFTMAX operation\n");
1447        }
1448        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1449    }
1450
1451    ET_PERF_END_EXT("SOFTMAX", kernel_name, node, "scale=%.6f|max_bias=%.6f|has_mask=%s", (double) scale,
1452                    (double) max_bias, node->src[1] ? "yes" : "no");
1453    return kernel_result;
1454}
1455
1456bool ggml_et_op_flash_attn_ext(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1457    ET_PERF_START();
1458
1459    if (!dev_ctx || !node) {
1460        GGML_LOG_ERROR("ET: Invalid parameters for FLASH_ATTN_EXT operation\n");
1461        return false;
1462    }
1463
1464    if (!node->src[0] || !node->src[1] || !node->src[2]) {
1465        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT operation missing required inputs\n");
1466        return false;
1467    }
1468
1469    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
1470        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT requires F32 Q and dst, got dst=%s q=%s\n", ggml_type_name(node->type),
1471                       ggml_type_name(node->src[0]->type));
1472        return false;
1473    }
1474
1475    // K and V can be F16 or F32
1476    if ((node->src[1]->type != GGML_TYPE_F32 && node->src[1]->type != GGML_TYPE_F16) ||
1477        (node->src[2]->type != GGML_TYPE_F32 && node->src[2]->type != GGML_TYPE_F16)) {
1478        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT K/V must be F16 or F32, got k=%s v=%s\n", ggml_type_name(node->src[1]->type),
1479                       ggml_type_name(node->src[2]->type));
1480        return false;
1481    }
1482
1483    if (node->src[4] != nullptr) {
1484        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT baseline kernel does not support sinks\n");
1485        return false;
1486    }
1487
1488    // Mask is optional; if present must be F16 or F32
1489    if (node->src[3] != nullptr && node->src[3]->type != GGML_TYPE_F32 && node->src[3]->type != GGML_TYPE_F16) {
1490        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT mask must be F16 or F32, got %s\n", ggml_type_name(node->src[3]->type));
1491        return false;
1492    }
1493
1494    // Q and dst must be row-contiguous F32
1495    if (!ggml_is_contiguous_rows(node) || !ggml_is_contiguous_rows(node->src[0])) {
1496        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT requires row-contiguous Q and dst\n");
1497        return false;
1498    }
1499
1500    if (node->nb[0] != sizeof(float) || node->src[0]->nb[0] != sizeof(float)) {
1501        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT requires contiguous F32 rows for Q and dst\n");
1502        return false;
1503    }
1504
1505    // K/V must have element-sized stride in dim 0
1506    const size_t k_elem = node->src[1]->type == GGML_TYPE_F16 ? 2 : 4;
1507    const size_t v_elem = node->src[2]->type == GGML_TYPE_F16 ? 2 : 4;
1508    if (node->src[1]->nb[0] != k_elem || node->src[2]->nb[0] != v_elem) {
1509        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT K/V must have element-sized stride in dim 0\n");
1510        return false;
1511    }
1512
1513    float scale         = 1.0f;
1514    float max_bias      = 0.0f;
1515    float logit_softcap = 0.0f;
1516    memcpy(&scale, (const float *) node->op_params + 0, sizeof(scale));
1517    memcpy(&max_bias, (const float *) node->op_params + 1, sizeof(max_bias));
1518    memcpy(&logit_softcap, (const float *) node->op_params + 2, sizeof(logit_softcap));
1519
1520    if (max_bias != 0.0f || logit_softcap != 0.0f) {
1521        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT baseline kernel does not support max_bias or logit_softcap\n");
1522        return false;
1523    }
1524
1525    const ggml_prec prec = ggml_flash_attn_ext_get_prec(node);
1526    if (prec != GGML_PREC_F32 && prec != GGML_PREC_DEFAULT) {
1527        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT baseline kernel only supports F32 precision\n");
1528        return false;
1529    }
1530
1531    // dk must match between Q and K; dv must match between V and dst
1532    if (node->src[0]->ne[0] != node->src[1]->ne[0]) {
1533        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT dk mismatch: Q=%lld K=%lld\n", (long long) node->src[0]->ne[0],
1534                       (long long) node->src[1]->ne[0]);
1535        return false;
1536    }
1537
1538    if (node->src[2]->ne[0] != node->ne[0]) {
1539        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT dv mismatch: V=%lld dst=%lld\n", (long long) node->src[2]->ne[0],
1540                       (long long) node->ne[0]);
1541        return false;
1542    }
1543
1544    if (node->src[2]->ne[0] > 512) {
1545        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT dv=%lld exceeds maximum 512\n", (long long) node->src[2]->ne[0]);
1546        return false;
1547    }
1548
1549    if (node->src[0]->ne[0] > 512) {
1550        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT dk=%lld exceeds maximum 512\n", (long long) node->src[0]->ne[0]);
1551        return false;
1552    }
1553
1554    // GQA: n_head_q must be a multiple of n_head_kv
1555    const int64_t nhq = node->src[0]->ne[2];
1556    const int64_t nhk = node->src[1]->ne[2];
1557    if (nhq % nhk != 0) {
1558        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT n_head_q (%lld) not divisible by n_head_kv (%lld)\n", (long long) nhq,
1559                       (long long) nhk);
1560        return false;
1561    }
1562
1563    // K and V must have matching sequence length, heads, and batch dims
1564    if (node->src[1]->ne[1] != node->src[2]->ne[1] || node->src[1]->ne[2] != node->src[2]->ne[2] ||
1565        node->src[1]->ne[3] != node->src[2]->ne[3]) {
1566        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT K/V shape mismatch\n");
1567        return false;
1568    }
1569
1570    // dst layout checks: [dv, nhq, nq, no]
1571    if (node->src[0]->ne[1] != node->ne[2] || node->src[0]->ne[2] != node->ne[1] ||
1572        node->src[0]->ne[3] != node->ne[3]) {
1573        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT dst shape mismatch\n");
1574        return false;
1575    }
1576
1577    // Batch dims: Q batch must match K batch
1578    if (node->src[0]->ne[3] != node->src[1]->ne[3]) {
1579        GGML_LOG_ERROR("ET: FLASH_ATTN_EXT batch dimension mismatch\n");
1580        return false;
1581    }
1582
1583    ggml_et_flash_attn_ext_params params;
1584    memset(&params, 0, sizeof(params));
1585    params.src0 = *node->src[0];
1586    params.src1 = *node->src[1];
1587    params.src2 = *node->src[2];
1588    if (node->src[3] != nullptr) {
1589        params.mask     = *node->src[3];
1590        params.has_mask = 1;
1591    }
1592    params.dst   = *node;
1593    params.scale = scale;
1594
1595    // Use matrix engine kernel when K/V are F16 and dk is a multiple of 32
1596    const char * kernel_name;
1597    if (node->src[1]->type == GGML_TYPE_F16 && node->src[2]->type == GGML_TYPE_F16 && (node->src[0]->ne[0] % 32) == 0) {
1598        kernel_name = "flash_attn_ext_f16_me";
1599    } else {
1600        kernel_name = "flash_attn_ext_f32";
1601    }
1602
1603    const bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1604
1605    ET_PERF_END_EXT("FLASH_ATTN_EXT", kernel_name, node, "scale=%.6f", (double) scale);
1606    return kernel_result;
1607}
1608
1609bool ggml_et_op_get_rows(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1610    ET_PERF_START();
1611
1612    if (!dev_ctx || !node) {
1613        GGML_LOG_ERROR("ET: Invalid parameters for GET_ROWS operation\n");
1614        return false;
1615    }
1616
1617    if (!node->src[0] || !node->src[1]) {
1618        GGML_LOG_ERROR("ET: GET_ROWS operation missing required inputs\n");
1619        return false;
1620    }
1621
1622    const char * kernel_name;
1623
1624    if (node->type == GGML_TYPE_F32 && node->src[1]->type == GGML_TYPE_I32 &&
1625        (node->src[0]->type == GGML_TYPE_F32 || node->src[0]->type == GGML_TYPE_F16 ||
1626         node->src[0]->type == GGML_TYPE_Q4_0 || node->src[0]->type == GGML_TYPE_Q8_0 ||
1627         node->src[0]->type == GGML_TYPE_Q4_K)) {
1628        kernel_name = "get_rows_f32";
1629
1630    } else {
1631        GGML_LOG_ERROR("ET: GET_ROWS operation with unsupported types: dst=%s src0=%s src1=%s\n",
1632                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
1633                       ggml_type_name(node->src[1]->type));
1634        return false;
1635    }
1636
1637    // Validate contiguity requirements
1638    if (!ggml_is_contiguous(node)) {
1639        GGML_LOG_ERROR("ET: GET_ROWS operation requires contiguous destination tensor\n");
1640        return false;
1641    }
1642
1643    if (!ggml_is_contiguous(node->src[0])) {
1644        GGML_LOG_ERROR("ET: GET_ROWS operation requires contiguous data tensor\n");
1645        return false;
1646    }
1647
1648    if (!ggml_is_contiguous(node->src[1])) {
1649        GGML_LOG_ERROR("ET: GET_ROWS operation requires contiguous indices tensor\n");
1650        return false;
1651    }
1652
1653    // Validate dimension constraints from ggml implementation
1654    if (node->src[0]->ne[2] != node->src[1]->ne[1] || node->src[1]->ne[3] != 1) {
1655        GGML_LOG_ERROR(
1656            "ET: GET_ROWS operation dimension constraint failed: src0.ne[2]=%lld != src1.ne[1]=%lld or src1.ne[3]=%lld "
1657            "!= 1\n",
1658            (long long) node->src[0]->ne[2], (long long) node->src[1]->ne[1], (long long) node->src[1]->ne[3]);
1659        return false;
1660    }
1661
1662    ggml_et_get_rows_params params;
1663    params.src0 = *node->src[0];  // Data tensor (F32 or Q8_0)
1664    params.src1 = *node->src[1];  // Indices tensor (I32)
1665    params.dst  = *node;          // Output tensor (F32)
1666
1667    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1668    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1669    bool                    cpu_comparison_active = false;
1670    if (get_rows_cpu_compare_config.enabled) {
1671        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_GET_ROWS)) {
1672            cpu_comparison_active = true;
1673        } else {
1674            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for GET_ROWS operation\n");
1675        }
1676    }
1677
1678    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1679
1680    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1681    if (cpu_comparison_active) {
1682        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &get_rows_cpu_compare_config)) {
1683            GGML_LOG_WARN("ET: CPU comparison failed for GET_ROWS operation\n");
1684        }
1685        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1686    }
1687
1688    ET_PERF_END("GET_ROWS", kernel_name, node);
1689    return kernel_result;
1690}
1691
1692bool ggml_et_op_cont(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1693    ET_PERF_START();
1694
1695    // Validate source tensor exists
1696    if (!node->src[0]) {
1697        GGML_LOG_ERROR("ET: CONT operation missing source tensor\n");
1698        return false;
1699    }
1700
1701    // Validate types match (input and output must be same type)
1702    if (node->type != node->src[0]->type) {
1703        GGML_LOG_ERROR("ET: CONT operation type mismatch: src=%s dst=%s\n", ggml_type_name(node->src[0]->type),
1704                       ggml_type_name(node->type));
1705        return false;
1706    }
1707
1708    // Validate supported types
1709    if (node->type != GGML_TYPE_F32 && node->type != GGML_TYPE_F16) {
1710        GGML_LOG_ERROR("ET: CONT operation unsupported type: %s (only F32 and F16 supported)\n",
1711                       ggml_type_name(node->type));
1712        return false;
1713    }
1714
1715    // Validate contiguity - output must be contiguous, input can be non-contiguous
1716    if (!ggml_is_contiguous(node)) {
1717        GGML_LOG_ERROR("ET: CONT operation requires contiguous output tensor\n");
1718        return false;
1719    }
1720
1721    // Select kernel based on type
1722    const char * kernel_name;
1723    if (node->type == GGML_TYPE_F32) {
1724        kernel_name = "cont_f32";
1725    } else if (node->type == GGML_TYPE_F16) {
1726        kernel_name = "cont_f16";
1727    } else {
1728        GGML_LOG_ERROR("ET: CONT operation with unsupported type: %s\n", ggml_type_name(node->type));
1729        return false;
1730    }
1731
1732    ggml_et_cont_params params;
1733    params.src0 = *node->src[0];  // Input tensor (potentially non-contiguous)
1734    params.dst  = *node;          // Output tensor (contiguous)
1735
1736    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1737    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1738    bool                    cpu_comparison_active = false;
1739    if (cont_cpu_compare_config.enabled) {
1740        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_CONT)) {
1741            cpu_comparison_active = true;
1742        } else {
1743            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for CONT operation\n");
1744        }
1745    }
1746
1747    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1748
1749    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1750    if (cpu_comparison_active) {
1751        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &cont_cpu_compare_config)) {
1752            GGML_LOG_WARN("ET: CPU comparison failed for CONT operation\n");
1753        }
1754        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1755    }
1756
1757    ET_PERF_END("CONT", kernel_name, node);
1758    return kernel_result;
1759}
1760
1761bool ggml_et_op_cumsum(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1762    ET_PERF_START();
1763
1764    if (!dev_ctx || !node || !node->src[0]) {
1765        GGML_LOG_ERROR("ET: Invalid parameters for CUMSUM operation\n");
1766        return false;
1767    }
1768
1769    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
1770        GGML_LOG_ERROR("ET: CUMSUM operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1771                       ggml_type_name(node->src[0]->type));
1772        return false;
1773    }
1774
1775    const char * kernel_name = "cumsum_f32";
1776
1777    ggml_et_cumsum_params params;
1778    params.src0 = *node->src[0];
1779    params.dst  = *node;
1780
1781    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1782    bool                    cpu_comparison_active = false;
1783    if (cumsum_cpu_compare_config.enabled) {
1784        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_CUMSUM)) {
1785            cpu_comparison_active = true;
1786        } else {
1787            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for CUMSUM operation\n");
1788        }
1789    }
1790
1791    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1792
1793    if (cpu_comparison_active) {
1794        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &cumsum_cpu_compare_config)) {
1795            GGML_LOG_WARN("ET: CPU comparison failed for CUMSUM operation\n");
1796        }
1797        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1798    }
1799
1800    ET_PERF_END("CUMSUM", kernel_name, node);
1801    return kernel_result;
1802}
1803
1804bool ggml_et_op_cpy(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1805    ET_PERF_START();
1806
1807    // CPY copies data from src[0] into the layout of dst (which matches src[1])
1808    // For same-type with contiguous dst, this is identical to CONT
1809    if (!node->src[0]) {
1810        GGML_LOG_ERROR("ET: CPY operation missing source tensor\n");
1811        return false;
1812    }
1813
1814    // Scalar / zero-element special path: if any dimension is 0, nothing to copy
1815    const int64_t nelements = node->ne[0] * node->ne[1] * node->ne[2] * node->ne[3];
1816    if (nelements == 0) {
1817        GGML_LOG_DEBUG("ET: CPY no-op (zero elements): ne=[%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 "]\n",
1818                       node->ne[0], node->ne[1], node->ne[2], node->ne[3]);
1819        ET_PERF_END("CPY", "noop", node);
1820        return true;
1821    }
1822
1823    // Only F32 and F16 supported for dst
1824    if (node->type != GGML_TYPE_F32 && node->type != GGML_TYPE_F16) {
1825        GGML_LOG_ERROR("ET: CPY unsupported dst type: %s\n", ggml_type_name(node->type));
1826        return false;
1827    }
1828
1829    // Select kernel based on src/dst type combination
1830    const char * kernel_name;
1831    if (node->src[0]->type == GGML_TYPE_F32 && node->type == GGML_TYPE_F32) {
1832        kernel_name = "cont_f32";
1833    } else if (node->src[0]->type == GGML_TYPE_F16 && node->type == GGML_TYPE_F16) {
1834        kernel_name = "cont_f16";
1835    } else if (node->src[0]->type == GGML_TYPE_F32 && node->type == GGML_TYPE_F16) {
1836        kernel_name = "cpy_f32_f16";
1837    } else {
1838        GGML_LOG_ERROR("ET: CPY unsupported type combination: src=%s dst=%s\n", ggml_type_name(node->src[0]->type),
1839                       ggml_type_name(node->type));
1840        return false;
1841    }
1842
1843    ggml_et_cont_params params;
1844    params.src0 = *node->src[0];
1845    params.dst  = *node;
1846
1847    // CPU comparison for debugging
1848    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1849    bool                    cpu_comparison_active = false;
1850    if (cont_cpu_compare_config.enabled) {
1851        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_CPY)) {
1852            cpu_comparison_active = true;
1853        } else {
1854            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for CPY operation\n");
1855        }
1856    }
1857
1858    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1859
1860    if (cpu_comparison_active) {
1861        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &cont_cpu_compare_config)) {
1862            GGML_LOG_WARN("ET: CPU comparison failed for CPY operation\n");
1863        }
1864        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1865    }
1866
1867    ET_PERF_END("CPY", kernel_name, node);
1868    return kernel_result;
1869}
1870
1871bool ggml_et_op_concat(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1872    ET_PERF_START();
1873
1874    if (!dev_ctx || !node) {
1875        GGML_LOG_ERROR("ET: Invalid parameters for CONCAT operation\n");
1876        return false;
1877    }
1878
1879    if (!node->src[0] || !node->src[1]) {
1880        GGML_LOG_ERROR("ET: CONCAT operation missing required inputs\n");
1881        return false;
1882    }
1883
1884    const char * kernel_name;
1885
1886    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32 && node->src[1]->type == GGML_TYPE_F32) {
1887        kernel_name = "concat_f32";
1888
1889    } else {
1890        GGML_LOG_ERROR("ET: CONCAT operation with unsupported types: dst=%s src0=%s src1=%s\n",
1891                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
1892                       ggml_type_name(node->src[1]->type));
1893        return false;
1894    }
1895
1896    int32_t dim;
1897    memcpy(&dim, node->op_params, sizeof(int32_t));
1898
1899    ggml_et_concat_params params;
1900    params.src0 = *node->src[0];
1901    params.src1 = *node->src[1];
1902    params.dst  = *node;
1903    params.dim  = dim;
1904
1905    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1906    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1907    bool                    cpu_comparison_active = false;
1908    if (concat_cpu_compare_config.enabled) {
1909        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_CONCAT)) {
1910            cpu_comparison_active = true;
1911        } else {
1912            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for CONCAT operation\n");
1913        }
1914    }
1915
1916    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1917
1918    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1919    if (cpu_comparison_active) {
1920        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &concat_cpu_compare_config)) {
1921            GGML_LOG_WARN("ET: CPU comparison failed for CONCAT operation\n");
1922        }
1923        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1924    }
1925
1926    ET_PERF_END_EXT("CONCAT", kernel_name, node, "dim=%d", dim);
1927    return kernel_result;
1928}
1929
1930bool ggml_et_op_repeat(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1931    ET_PERF_START();
1932
1933    if (!dev_ctx || !node) {
1934        GGML_LOG_ERROR("ET: Invalid parameters for REPEAT operation\n");
1935        return false;
1936    }
1937
1938    if (!node->src[0]) {
1939        GGML_LOG_ERROR("ET: REPEAT operation missing required input\n");
1940        return false;
1941    }
1942
1943    const char * kernel_name;
1944
1945    if (node->type == GGML_TYPE_F32 && node->src[0]->type == GGML_TYPE_F32) {
1946        // No-op REPEAT (every repeat factor is 1): the output is just a copy
1947        // of the input. Route to cont_f32, whose contiguous fast path handles
1948        // arbitrary sizes (including those rejected by repeat_f32's gate,
1949        // e.g. ne[0]=1).
1950        if (ggml_are_same_shape(node->src[0], node)) {
1951            kernel_name = "cont_f32";
1952        } else {
1953            kernel_name = "repeat_f32";
1954        }
1955
1956    } else {
1957        GGML_LOG_ERROR("ET: REPEAT operation with unsupported types: dst=%s src0=%s\n", ggml_type_name(node->type),
1958                       ggml_type_name(node->src[0]->type));
1959        return false;
1960    }
1961
1962    // ggml_et_cont_params and ggml_et_repeat_params have identical layouts
1963    // (just src0 + dst), so the same payload works for either kernel.
1964    ggml_et_repeat_params params;
1965    params.src0 = *node->src[0];
1966    params.dst  = *node;
1967
1968    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
1969    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
1970    bool                    cpu_comparison_active = false;
1971    if (repeat_cpu_compare_config.enabled) {
1972        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_REPEAT)) {
1973            cpu_comparison_active = true;
1974        } else {
1975            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for REPEAT operation\n");
1976        }
1977    }
1978
1979    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
1980
1981    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
1982    if (cpu_comparison_active) {
1983        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &repeat_cpu_compare_config)) {
1984            GGML_LOG_WARN("ET: CPU comparison failed for REPEAT operation\n");
1985        }
1986        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
1987    }
1988
1989    ET_PERF_END("REPEAT", kernel_name, node);
1990    return kernel_result;
1991}
1992
1993bool ggml_et_op_ssm_conv(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
1994    ET_PERF_START();
1995
1996    if (!dev_ctx || !node || !node->src[0] || !node->src[1]) {
1997        GGML_LOG_ERROR("ET: Invalid parameters for SSM_CONV operation\n");
1998        return false;
1999    }
2000
2001    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32 || node->src[1]->type != GGML_TYPE_F32) {
2002        GGML_LOG_ERROR("ET: SSM_CONV operation with unsupported types: dst=%s src0=%s src1=%s\n",
2003                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
2004                       ggml_type_name(node->src[1]->type));
2005        return false;
2006    }
2007
2008    const char * kernel_name = "ssm_conv_f32";
2009
2010    ggml_et_ssm_conv_params params;
2011    params.src0 = *node->src[0];
2012    params.src1 = *node->src[1];
2013    params.dst  = *node;
2014
2015    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
2016    bool                    cpu_comparison_active = false;
2017    if (ssm_conv_cpu_compare_config.enabled) {
2018        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_SSM_CONV)) {
2019            cpu_comparison_active = true;
2020        } else {
2021            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for SSM_CONV operation\n");
2022        }
2023    }
2024
2025    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
2026
2027    if (cpu_comparison_active) {
2028        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &ssm_conv_cpu_compare_config)) {
2029            GGML_LOG_WARN("ET: CPU comparison failed for SSM_CONV operation\n");
2030        }
2031        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
2032    }
2033
2034    ET_PERF_END("SSM_CONV", kernel_name, node);
2035    return kernel_result;
2036}
2037
2038bool ggml_et_op_ssm_scan(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2039    ET_PERF_START();
2040
2041    if (!dev_ctx || !node) {
2042        GGML_LOG_ERROR("ET: Invalid parameters for SSM_SCAN operation\n");
2043        return false;
2044    }
2045
2046    for (int i = 0; i < 7; ++i) {
2047        if (!node->src[i]) {
2048            GGML_LOG_ERROR("ET: SSM_SCAN missing required input %d\n", i);
2049            return false;
2050        }
2051    }
2052
2053    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32 || node->src[1]->type != GGML_TYPE_F32 ||
2054        node->src[2]->type != GGML_TYPE_F32 || node->src[3]->type != GGML_TYPE_F32 ||
2055        node->src[4]->type != GGML_TYPE_F32 || node->src[5]->type != GGML_TYPE_F32 ||
2056        node->src[6]->type != GGML_TYPE_I32) {
2057        GGML_LOG_ERROR("ET: SSM_SCAN operation with unsupported types\n");
2058        return false;
2059    }
2060
2061    ggml_et_ssm_scan_params params;
2062    params.src0 = *node->src[0];
2063    params.src1 = *node->src[1];
2064    params.src2 = *node->src[2];
2065    params.src3 = *node->src[3];
2066    params.src4 = *node->src[4];
2067    params.src5 = *node->src[5];
2068    params.src6 = *node->src[6];
2069    params.dst  = *node;
2070    params.K    = ggml_get_op_params_i32(node, 0);
2071
2072    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "ssm_scan_f32", &params, sizeof(params), 0xFFFFFFFF);
2073
2074    ET_PERF_END("SSM_SCAN", "ssm_scan_f32", node);
2075    return kernel_result;
2076}
2077
2078bool ggml_et_op_rwkv_wkv6(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2079    ET_PERF_START();
2080
2081    if (!dev_ctx || !node) {
2082        GGML_LOG_ERROR("ET: Invalid parameters for RWKV_WKV6 operation\n");
2083        return false;
2084    }
2085
2086    // Validate all 6 source tensors exist
2087    for (int i = 0; i <= 5; i++) {
2088        if (!node->src[i]) {
2089            GGML_LOG_ERROR("ET: RWKV_WKV6 operation missing src[%d]\n", i);
2090            return false;
2091        }
2092    }
2093
2094    if (node->type != GGML_TYPE_F32) {
2095        GGML_LOG_ERROR("ET: RWKV_WKV6 only supports F32, got %s\n", ggml_type_name(node->type));
2096        return false;
2097    }
2098
2099    const char * kernel_name = "rwkv_wkv6_f32";
2100
2101    const int64_t S      = node->src[0]->ne[0];  // head_size
2102    const int64_t H      = node->src[0]->ne[1];  // num heads
2103    const int64_t T      = node->src[1]->ne[2];  // num tokens
2104    const int64_t n_seqs = node->src[5]->ne[1];  // num sequences
2105    const int64_t C      = S * H;
2106
2107    ggml_et_rwkv_wkv6_params params;
2108    params.k        = (float *) node->src[0]->data;
2109    params.v        = (float *) node->src[1]->data;
2110    params.r        = (float *) node->src[2]->data;
2111    params.tf       = (float *) node->src[3]->data;
2112    params.td       = (float *) node->src[4]->data;
2113    params.state_in = (float *) node->src[5]->data;
2114    params.dst      = (float *) node->data;
2115    params.C        = (int32_t) C;
2116    params.H        = (int32_t) H;
2117    params.S        = (int32_t) S;
2118    params.T        = (int32_t) T;
2119    params.n_seqs   = (int32_t) n_seqs;
2120
2121    // Phase 1: Initialize CPU comparison context
2122    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
2123    bool                    cpu_comparison_active = false;
2124    if (rwkv_wkv6_cpu_compare_config.enabled) {
2125        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_RWKV_WKV6)) {
2126            cpu_comparison_active = true;
2127        } else {
2128            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for RWKV_WKV6 operation\n");
2129        }
2130    }
2131
2132    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
2133
2134    // Phase 2: Execute CPU computation and compare
2135    if (cpu_comparison_active) {
2136        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &rwkv_wkv6_cpu_compare_config)) {
2137            GGML_LOG_WARN("ET: CPU comparison failed for RWKV_WKV6 operation\n");
2138        }
2139        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
2140    }
2141
2142    ET_PERF_END_EXT("RWKV_WKV6", kernel_name, node, "S=%d H=%d T=%d n_seqs=%d", (int) S, (int) H, (int) T,
2143                    (int) n_seqs);
2144    return kernel_result;
2145}
2146
2147bool ggml_et_op_rwkv_wkv7(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2148    ET_PERF_START();
2149
2150    if (!dev_ctx || !node) {
2151        GGML_LOG_ERROR("ET: Invalid parameters for RWKV_WKV7 operation\n");
2152        return false;
2153    }
2154
2155    // Validate all 7 source tensors exist
2156    for (int i = 0; i <= 6; i++) {
2157        if (!node->src[i]) {
2158            GGML_LOG_ERROR("ET: RWKV_WKV7 operation missing src[%d]\n", i);
2159            return false;
2160        }
2161    }
2162
2163    if (node->type != GGML_TYPE_F32) {
2164        GGML_LOG_ERROR("ET: RWKV_WKV7 only supports F32, got %s\n", ggml_type_name(node->type));
2165        return false;
2166    }
2167
2168    const char * kernel_name = "rwkv_wkv7_f32";
2169
2170    const int64_t S      = node->src[2]->ne[0];  // head_size
2171    const int64_t H      = node->src[2]->ne[1];  // num heads
2172    const int64_t T      = node->src[1]->ne[2];  // num tokens
2173    const int64_t n_seqs = node->src[6]->ne[1];  // num sequences
2174    const int64_t C      = S * H;
2175
2176    ggml_et_rwkv_wkv7_params params;
2177    params.r        = (float *) node->src[0]->data;
2178    params.w        = (float *) node->src[1]->data;
2179    params.k        = (float *) node->src[2]->data;
2180    params.v        = (float *) node->src[3]->data;
2181    params.a        = (float *) node->src[4]->data;
2182    params.b        = (float *) node->src[5]->data;
2183    params.state_in = (float *) node->src[6]->data;
2184    params.dst      = (float *) node->data;
2185    params.C        = (int32_t) C;
2186    params.H        = (int32_t) H;
2187    params.S        = (int32_t) S;
2188    params.T        = (int32_t) T;
2189    params.n_seqs   = (int32_t) n_seqs;
2190
2191    // Phase 1: Initialize CPU comparison context
2192    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
2193    bool                    cpu_comparison_active = false;
2194    if (rwkv_wkv7_cpu_compare_config.enabled) {
2195        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_RWKV_WKV7)) {
2196            cpu_comparison_active = true;
2197        } else {
2198            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for RWKV_WKV7 operation\n");
2199        }
2200    }
2201
2202    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
2203
2204    // Phase 2: Execute CPU computation and compare
2205    if (cpu_comparison_active) {
2206        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &rwkv_wkv7_cpu_compare_config)) {
2207            GGML_LOG_WARN("ET: CPU comparison failed for RWKV_WKV7 operation\n");
2208        }
2209        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
2210    }
2211
2212    ET_PERF_END_EXT("RWKV_WKV7", kernel_name, node, "S=%d H=%d T=%d n_seqs=%d", (int) S, (int) H, (int) T,
2213                    (int) n_seqs);
2214    return kernel_result;
2215}
2216
2217static ggml_et_cpu_compare_config gated_delta_net_cpu_compare_config = {
2218    /* .enabled = */ false,
2219    /* .use_cpu_result = */ false,
2220    /* .log_differences = */ true,
2221    /* .tolerance = */ 1e-4f,
2222    /* .max_log_elements = */ 4096
2223};
2224
2225bool ggml_et_op_gated_delta_net(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2226    ET_PERF_START();
2227
2228    if (!dev_ctx || !node) {
2229        GGML_LOG_ERROR("ET: Invalid parameters for GATED_DELTA_NET operation\n");
2230        return false;
2231    }
2232
2233    // Validate all 6 source tensors exist
2234    for (int i = 0; i <= 5; i++) {
2235        if (!node->src[i]) {
2236            GGML_LOG_ERROR("ET: GATED_DELTA_NET operation missing src[%d]\n", i);
2237            return false;
2238        }
2239    }
2240
2241    if (node->type != GGML_TYPE_F32) {
2242        GGML_LOG_ERROR("ET: GATED_DELTA_NET only supports F32, got %s\n", ggml_type_name(node->type));
2243        return false;
2244    }
2245
2246    const char * kernel_name = "gated_delta_net_f32";
2247
2248    const ggml_tensor * src_q     = node->src[0];
2249    const ggml_tensor * src_k     = node->src[1];
2250    const ggml_tensor * src_v     = node->src[2];
2251    const ggml_tensor * src_g     = node->src[3];
2252    const ggml_tensor * src_beta  = node->src[4];
2253    const ggml_tensor * src_state = node->src[5];
2254
2255    const int64_t S_v      = src_v->ne[0];
2256    const int64_t H        = src_v->ne[1];
2257    const int64_t n_tokens = src_v->ne[2];
2258    const int64_t n_seqs   = src_v->ne[3];
2259    const int64_t H_q      = src_q->ne[1];
2260    const int64_t H_k      = src_k->ne[1];
2261    const int64_t n_seqs_q = src_q->ne[3];
2262    const int64_t n_seqs_k = src_k->ne[3];
2263
2264    ggml_et_gated_delta_net_params params;
2265    params.q        = *src_q;
2266    params.k        = *src_k;
2267    params.v        = *src_v;
2268    params.g        = *src_g;
2269    params.beta     = *src_beta;
2270    params.state_in = *src_state;
2271    params.dst      = *node;
2272    params.S_v      = (int32_t) S_v;
2273    params.H        = (int32_t) H;
2274    params.H_q      = (int32_t) H_q;
2275    params.H_k      = (int32_t) H_k;
2276    params.n_tokens = (int32_t) n_tokens;
2277    params.n_seqs   = (int32_t) n_seqs;
2278    params.n_seqs_q = (int32_t) n_seqs_q;
2279    params.n_seqs_k = (int32_t) n_seqs_k;
2280    params.kda      = (src_g->ne[0] == S_v) ? 1 : 0;
2281    params.K        = ggml_get_op_params_i32(node, 0);
2282    params.scale    = 1.0f / sqrtf((float) S_v);
2283
2284    // CPU comparison for debugging
2285    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
2286    bool                    cpu_comparison_active = false;
2287    if (gated_delta_net_cpu_compare_config.enabled) {
2288        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_GATED_DELTA_NET)) {
2289            cpu_comparison_active = true;
2290        } else {
2291            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for GATED_DELTA_NET operation\n");
2292        }
2293    }
2294
2295    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
2296
2297    if (cpu_comparison_active) {
2298        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &gated_delta_net_cpu_compare_config)) {
2299            GGML_LOG_WARN("ET: CPU comparison failed for GATED_DELTA_NET operation\n");
2300        }
2301        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
2302    }
2303
2304    ET_PERF_END_EXT("GATED_DELTA_NET", kernel_name, node, "S_v=%d H=%d n_tokens=%d n_seqs=%d kda=%d", (int) S_v,
2305                    (int) H, (int) n_tokens, (int) n_seqs, params.kda);
2306    return kernel_result;
2307}
2308
2309bool ggml_et_op_set_rows(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2310    ET_PERF_START();
2311
2312    if (!dev_ctx || !node) {
2313        GGML_LOG_ERROR("ET: Invalid parameters for SET_ROWS operation\n");
2314        return false;
2315    }
2316
2317    if (!node->src[0] || !node->src[1] || !node->src[2]) {
2318        GGML_LOG_ERROR(
2319            "ET: SET_ROWS operation missing required inputs (needs src[0]=base, src[1]=indices, src[2]=data)\n");
2320        return false;
2321    }
2322
2323    const char * kernel_name;
2324
2325    // Support F32 data with I64 indices -> F32/F16 output (scatter operation)
2326    if (node->src[0]->type == GGML_TYPE_F32 && node->src[1]->type == GGML_TYPE_I64 &&
2327        (node->type == GGML_TYPE_F32 || node->type == GGML_TYPE_F16)) {
2328        if (node->type == GGML_TYPE_F32 || node->type == GGML_TYPE_F16) {
2329            kernel_name = "set_rows_f32";
2330        } else {
2331            GGML_LOG_ERROR("ET: SET_ROWS unsupported output type: %s\n", ggml_type_name(node->type));
2332            return false;
2333        }
2334
2335    } else {
2336        GGML_LOG_ERROR("ET: SET_ROWS operation with unsupported types: dst=%s src0=%s src1=%s\n",
2337                       ggml_type_name(node->type), ggml_type_name(node->src[0]->type),
2338                       ggml_type_name(node->src[1]->type));
2339        return false;
2340    }
2341
2342    // Validate contiguity requirements
2343    if (!ggml_is_contiguous_rows(node)) {
2344        GGML_LOG_ERROR("ET: SET_ROWS operation requires contiguous-rows destination tensor\n");
2345        return false;
2346    }
2347
2348    if (!ggml_is_contiguous_rows(node->src[0])) {
2349        GGML_LOG_ERROR("ET: SET_ROWS operation requires contiguous-rows source tensor\n");
2350        return false;
2351    }
2352
2353    if (!ggml_is_contiguous(node->src[1])) {
2354        GGML_LOG_ERROR("ET: SET_ROWS operation requires contiguous indices tensor\n");
2355        return false;
2356    }
2357
2358    // Validate dimension constraints from ggml implementation
2359    if (!(node->ne[0] == node->src[0]->ne[0] &&              // same number of columns
2360          node->ne[2] == node->src[0]->ne[2] &&              // same batch size
2361          node->ne[3] == node->src[0]->ne[3] &&              // same outer dimension
2362          node->src[0]->ne[1] == node->src[1]->ne[0] &&      // src rows = index count
2363          node->src[0]->ne[2] % node->src[1]->ne[1] == 0 &&  // batch constraint
2364          node->src[0]->ne[3] % node->src[1]->ne[2] == 0 &&  // outer constraint
2365          node->src[1]->ne[3] == 1)) {                       // indices constraint
2366        GGML_LOG_ERROR("ET: SET_ROWS operation dimension constraint failed\n");
2367        return false;
2368    }
2369
2370    ggml_et_set_rows_params params;
2371    params.src0 = *node->src[0];  // F32 source data tensor
2372    params.src1 = *node->src[1];  // I64 indices tensor
2373    params.dst  = *node;          // F32/F16 destination tensor
2374
2375    // Phase 1: Initialize CPU comparison context and copy source buffers (before ET kernel)
2376    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
2377    bool                    cpu_comparison_active = false;
2378    if (set_rows_cpu_compare_config.enabled) {
2379        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_SET_ROWS)) {
2380            cpu_comparison_active = true;
2381        } else {
2382            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for SET_ROWS operation\n");
2383        }
2384    }
2385
2386    bool kernel_result = ggml_et_launch_kernel(dev_ctx, kernel_name, &params, sizeof(params), 0xFFFFFFFF);
2387
2388    // Phase 2: Execute CPU computation and compare with ET result (after ET kernel)
2389    if (cpu_comparison_active) {
2390        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &set_rows_cpu_compare_config)) {
2391            GGML_LOG_WARN("ET: CPU comparison failed for SET_ROWS operation\n");
2392        }
2393        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
2394    }
2395
2396    ET_PERF_END("SET_ROWS", kernel_name, node);
2397    return kernel_result;
2398}
2399
2400bool ggml_et_op_fill(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2401    ET_PERF_START();
2402
2403    ggml_et_fill_params params;
2404    params.dst = *node;
2405    memcpy(&params.c, node->op_params, sizeof(float));
2406
2407    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "fill_f32", &params, sizeof(params), 0xFFFFFFFF);
2408
2409    ET_PERF_END("FILL", "fill_f32", node);
2410    return kernel_result;
2411}
2412
2413bool ggml_et_op_diag(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2414    ET_PERF_START();
2415
2416    if (!node->src[0]) {
2417        GGML_LOG_ERROR("ET: DIAG operation missing source tensor\n");
2418        return false;
2419    }
2420
2421    ggml_et_diag_params params;
2422    params.src0 = *node->src[0];
2423    params.dst  = *node;
2424
2425    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "diag_f32", &params, sizeof(params), 0xFFFFFFFF);
2426
2427    ET_PERF_END("DIAG", "diag_f32", node);
2428    return kernel_result;
2429}
2430
2431bool ggml_et_op_tri(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2432    ET_PERF_START();
2433
2434    if (!node->src[0]) {
2435        GGML_LOG_ERROR("ET: TRI operation missing source tensor\n");
2436        return false;
2437    }
2438
2439    ggml_et_tri_params params;
2440    params.src0 = *node->src[0];
2441    params.dst  = *node;
2442    memcpy(&params.tri_type, node->op_params, sizeof(int32_t));
2443
2444    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "tri_f32", &params, sizeof(params), 0xFFFFFFFF);
2445
2446    ET_PERF_END("TRI", "tri_f32", node);
2447    return kernel_result;
2448}
2449
2450bool ggml_et_op_solve_tri(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2451    ET_PERF_START();
2452
2453    if (!node->src[0] || !node->src[1]) {
2454        GGML_LOG_ERROR("ET: SOLVE_TRI operation missing source tensor(s)\n");
2455        return false;
2456    }
2457
2458    ggml_et_solve_tri_params params;
2459    params.src0 = *node->src[0];  // A (lower-triangular)
2460    params.src1 = *node->src[1];  // B (RHS)
2461    params.dst  = *node;          // X (solution)
2462
2463    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "solve_tri_f32", &params, sizeof(params), 0xFFFFFFFF);
2464
2465    ET_PERF_END("SOLVE_TRI", "solve_tri_f32", node);
2466    return kernel_result;
2467}
2468
2469bool ggml_et_op_set(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2470    ET_PERF_START();
2471
2472    if (!node->src[0] || !node->src[1]) {
2473        GGML_LOG_ERROR("ET: SET operation missing source tensor(s)\n");
2474        return false;
2475    }
2476
2477    const bool   inplace = (bool) ((const int32_t *) node->op_params)[4];
2478    const size_t offset  = ((const int32_t *) node->op_params)[3];
2479    const size_t nb1     = ((const int32_t *) node->op_params)[0];
2480    const size_t nb2     = ((const int32_t *) node->op_params)[1];
2481    const size_t nb3     = ((const int32_t *) node->op_params)[2];
2482
2483    if (!inplace) {
2484        GGML_LOG_ERROR("ET: SET only supports inplace (inplace=%d)\n", inplace);
2485        return false;
2486    }
2487
2488    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32 || node->src[1]->type != GGML_TYPE_F32) {
2489        GGML_LOG_ERROR("ET: SET only supports F32 (dst=%s src0=%s src1=%s)\n", ggml_type_name(node->type),
2490                       ggml_type_name(node->src[0]->type), ggml_type_name(node->src[1]->type));
2491        return false;
2492    }
2493
2494    if (!ggml_are_same_shape(node, node->src[0])) {
2495        GGML_LOG_ERROR("ET: SET requires same-shape src0 and dst\n");
2496        return false;
2497    }
2498
2499    if (!ggml_is_contiguous(node) || !ggml_is_contiguous(node->src[0]) || !ggml_is_contiguous(node->src[1])) {
2500        GGML_LOG_ERROR("ET: SET requires contiguous dst, src0, and src1\n");
2501        return false;
2502    }
2503
2504    ggml_et_set_params params;
2505    params.src1   = *node->src[1];
2506    params.dst    = *node;
2507    params.nb1    = (int32_t) nb1;
2508    params.nb2    = (int32_t) nb2;
2509    params.nb3    = (int32_t) nb3;
2510    params.offset = (int32_t) offset;
2511
2512    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "set_f32", &params, sizeof(params), 0xFFFFFFFF);
2513
2514    ET_PERF_END("SET", "set_f32", node);
2515    return kernel_result;
2516}
2517
2518bool ggml_et_op_pad(ggml_backend_et_device_context * dev_ctx, const ggml_tensor * node) {
2519    ET_PERF_START();
2520
2521    if (!node->src[0]) {
2522        GGML_LOG_ERROR("ET: PAD operation missing source tensor\n");
2523        return false;
2524    }
2525
2526    if (node->type != GGML_TYPE_F32 || node->src[0]->type != GGML_TYPE_F32) {
2527        GGML_LOG_ERROR("ET: PAD only supports F32 (src=%s dst=%s)\n", ggml_type_name(node->src[0]->type),
2528                       ggml_type_name(node->type));
2529        return false;
2530    }
2531
2532    if (!ggml_is_contiguous(node)) {
2533        GGML_LOG_ERROR("ET: PAD requires contiguous output tensor\n");
2534        return false;
2535    }
2536
2537    if (node->src[0]->nb[0] != sizeof(float)) {
2538        GGML_LOG_ERROR("ET: PAD requires element-contiguous src dim0 (nb[0]=%zu)\n", (size_t) node->src[0]->nb[0]);
2539        return false;
2540    }
2541
2542    // Extract padding parameters from op_params
2543    const int32_t * op_params = (const int32_t *) node->op_params;
2544
2545    ggml_et_pad_params params;
2546    params.src0  = *node->src[0];
2547    params.dst   = *node;
2548    params.lp[0] = op_params[0];
2549    params.rp[0] = op_params[1];
2550    params.lp[1] = op_params[2];
2551    params.rp[1] = op_params[3];
2552    params.lp[2] = op_params[4];
2553    params.rp[2] = op_params[5];
2554    params.lp[3] = op_params[6];
2555    params.rp[3] = op_params[7];
2556
2557    // v1: no dim0 padding
2558    if (params.lp[0] != 0 || params.rp[0] != 0) {
2559        GGML_LOG_ERROR("ET: PAD dim0 padding not supported (lp0=%d rp0=%d)\n", params.lp[0], params.rp[0]);
2560        return false;
2561    }
2562
2563    ggml_et_cpu_compare_ctx cpu_cmp_ctx;
2564    bool                    cpu_comparison_active = false;
2565    if (pad_cpu_compare_config.enabled) {
2566        if (ggml_et_cpu_compare_init_pre(&cpu_cmp_ctx, node, GGML_OP_PAD)) {
2567            cpu_comparison_active = true;
2568        } else {
2569            GGML_LOG_WARN("ET: Failed to initialize CPU comparison for PAD operation\n");
2570        }
2571    }
2572
2573    bool kernel_result = ggml_et_launch_kernel(dev_ctx, "pad_f32", &params, sizeof(params), 0xFFFFFFFF);
2574
2575    if (cpu_comparison_active) {
2576        if (!ggml_et_cpu_compare_compute_and_check(&cpu_cmp_ctx, node, &pad_cpu_compare_config)) {
2577            GGML_LOG_WARN("ET: CPU comparison failed for PAD operation\n");
2578        }
2579        ggml_et_cpu_compare_free(&cpu_cmp_ctx);
2580    }
2581
2582    ET_PERF_END("PAD", "pad_f32", node);
2583    return kernel_result;
2584}