char-fork/llama.cpp

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

cwritersycl: rfc: Use radix select for top_k (#28670)21f6b0d22

char/aliceai-t5-moe
284.0 KiB7176 linesraw
1//
2// MIT license
3// Copyright (C) 2024 Intel Corporation
4// SPDX-License-Identifier: MIT
5//
6
7//
8// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
9// See https://llvm.org/LICENSE.txt for license information.
10// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
11//
12
13#include <algorithm>
14#include <array>
15#include <assert.h>
16#include <atomic>
17#include <cinttypes>
18#include <cstddef>
19#include <cstdint>
20#include <cstdlib>
21#include <float.h>
22#include <limits>
23#include <optional>
24#include <stdint.h>
25#include <stdio.h>
26#include <vector>
27#include <cmath>
28#include <iostream>
29#include <fstream>
30#include <stdio.h>
31#include <stdlib.h>
32#include <regex>
33
34#include <sycl/sycl.hpp>
35#include <sycl/backend.hpp>
36#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
37#include <level_zero/ze_api.h>
38#include <level_zero/zes_api.h>
39#endif
40#if defined(GGML_SYCL_GRAPH) && SYCL_EXT_ONEAPI_ASYNC_MEMORY_ALLOC
41#    include <sycl/ext/oneapi/experimental/async_alloc/async_alloc.hpp>
42#endif
43#if SYCL_EXT_ONEAPI_VIRTUAL_MEM
44#    include <sycl/ext/oneapi/virtual_mem/physical_mem.hpp>
45#    include <sycl/ext/oneapi/virtual_mem/virtual_mem.hpp>
46#    define GGML_SYCL_SUPPORT_VMM
47#endif
48#if defined(__INTEL_LLVM_COMPILER)
49    #define GGML_SYCL_DMMV_HAS_ESIMD
50#endif
51#include <sycl/half_type.hpp>
52
53#include "ggml.h"
54#include "ggml-sycl.h"
55#include "ggml-impl.h"
56#include "ggml-backend-impl.h"
57
58#include "ggml-sycl/add-id.hpp"
59#include "ggml-sycl/backend.hpp"
60#include "ggml-sycl/common.hpp"
61#include "ggml-sycl/element_wise.hpp"
62#include "ggml-sycl/fwht.hpp"
63#include "ggml-sycl/gemm.hpp"
64#include "ggml-sycl/getrows.hpp"
65#include "ggml-sycl/mem.hpp"
66#include "ggml-sycl/norm.hpp"
67#include "ggml-sycl/presets.hpp"
68#include "ggml-sycl/quantize.hpp"
69#include "ggml-sycl/repeat_back.hpp"
70#include "ggml-sycl/set_rows.hpp"
71#include "ggml-sycl/set.hpp"
72#include "ggml-sycl/dsv4-hc.hpp"
73#include "ggml-sycl/lightning-indexer.hpp"
74#include "ggml-sycl/conv2d.hpp"
75#include "ggml-sycl/conv2d-dw.hpp"
76#include "ggml-sycl/conv2d-transpose.hpp"
77#include "ggml-sycl/ssm_conv.hpp"
78#include "ggml-sycl/sycl_hw.hpp"
79#include "ggml-sycl/ssm_scan.hpp"
80#include "ggml-sycl/fill.hpp"
81#include "ggml-sycl/cumsum.hpp"
82#include "ggml-sycl/diag.hpp"
83#include "ggml-sycl/opt-step.hpp"
84#include "ggml-sycl/solve_tri.hpp"
85#include "ggml-sycl/gated_delta_net.hpp"
86#include "ggml-sycl/pool.hpp"
87#include "ggml-sycl/cross_entropy_loss.hpp"
88
89#define MEM_SIZE_2M	0x00200000
90#define MEM_SIZE_1G	0x40000000
91
92static bool g_sycl_loaded = false;
93int g_ggml_sycl_debug = 0;
94int g_ggml_sycl_dev_debug = 0;
95int g_ggml_sycl_enable_optimize = 1;
96int g_ggml_sycl_enable_graph = 0;
97int g_ggml_sycl_enable_dnn = 1;
98int g_ggml_sycl_fa_onednn = 1;
99int g_ggml_sycl_fa_onednn_max_kv = 0;
100int g_ggml_sycl_enable_mkl_fa = 1;
101int g_ggml_sycl_memtrace = 0;
102int g_ggml_sycl_memtrace_step = 64;
103int g_ggml_sycl_enable_vmm = 1;
104int g_ggml_sycl_enable_fusion = 1;
105int g_ggml_sycl_enable_esimd = 1;
106int g_ggml_sycl_prioritize_dmmv = 0;
107int g_ggml_sycl_use_async_mem_op = 0;
108int g_ggml_sycl_use_async_mem_op_requested = 1;
109int g_ggml_sycl_use_level_zero_api = 0;
110int g_ggml_sycl_enable_flash_attention = 1;
111int g_ggml_sycl_dev2dev_memcpy = DEV2DEV_MEMCPY_SYCL;
112int g_ggml_sycl_usm_system = 0;
113int g_ggml_sycl_enable_host_pinned_mem = 1;
114int g_ggml_sycl_host_pinned_mem_2g = 0;
115int g_ggml_sycl_get_mem_api = MEMORY_API_TYPE_LEVEL_ZERO;
116
117static ggml_sycl_device_info ggml_sycl_init() {
118    GGML_SYCL_DEBUG("[SYCL] call ggml_sycl_init\n");
119    ggml_sycl_device_info info = {};
120
121    // Do not hard crash when there exists no SYCL devices.
122    // We want to allow the user to use non-SYCL tools when SYCL is compiled (such as llama-quantize)
123    try {
124        info.device_count = dpct::dev_mgr::instance().device_count();
125    } catch (sycl::exception const &exc) {
126        GGML_LOG_INFO("%s: no SYCL device available: %s\n", __func__, exc.what());
127        info.device_count = 0;
128    }
129    if (info.device_count == 0) {
130        GGML_LOG_ERROR("%s: failed to initialize: %s\n", GGML_SYCL_NAME, __func__);
131        return info;
132    }
133
134    GGML_ASSERT(info.device_count <= GGML_SYCL_MAX_DEVICES);
135
136    int64_t total_vram = 0;
137/* This is a bit misleading;  reserved for later */
138// #if defined(SYCL_USE_XMX)
139//     GGML_LOG_INFO("%s: SYCL_USE_XMX: yes\n", __func__);
140// #else
141//     GGML_LOG_INFO("%s: SYCL_USE_XMX: no\n", __func__);
142// #endif
143    for (int i = 0; i < info.device_count; ++i) {
144        dpct::device_info prop;
145        auto & device = dpct::dev_mgr::instance().get_device(i);
146
147        SYCL_CHECK(CHECK_TRY_ERROR(dpct::get_device_info(
148            prop, device)));
149
150#if !defined(GGML_SYCL_SUPPORT_VMM)
151        info.devices[i].vmm = 0;
152#else
153        info.devices[i].vmm = device.has(sycl::aspect::ext_oneapi_virtual_mem);
154        if (info.devices[i].vmm) {
155            // NB: SYCL's get_mem_granularity always returns the _minimum_ granularity,
156            // but the L0 API requires a larger page size for allocs above 2 MiB and
157            // rejects non-multiples with UR_RESULT_ERROR_INVALID_VALUE [sic].
158            // Here we clamp it to 2 MiB for simplicity, but other devices may require
159            // calling zeVirtualMemQueryPageSize or yet unexposed public API.
160            const size_t physical_page = 2ull << 20; // 2 MiB
161            info.devices[i].vmm_granularity = std::max<size_t>(
162                sycl::ext::oneapi::experimental::get_mem_granularity(
163                    device, sycl::context(device)),
164                physical_page);
165        }
166#endif
167
168        info.default_tensor_split[i] = total_vram;
169        total_vram += prop.get_global_mem_size();
170
171        info.devices[i].cc =
172            100 * prop.get_major_version() + 10 * prop.get_minor_version();
173        info.devices[i].nsm = prop.get_max_compute_units() / 16; //16: Number of Xe Cores
174        info.devices[i].opt_feature.reorder = device.ext_oneapi_architecture_is(syclex::arch_category::intel_gpu);
175        info.devices[i].smpbo = prop.get_local_mem_size();
176        info.devices[i].warp_size = WARP_SIZE;
177        info.devices[i].usm_system_support = device.has(sycl::aspect::usm_system_allocations);
178
179        info.max_work_group_sizes[i] = prop.get_max_work_group_size();
180        info.devices[i].max_wg_per_cu = info.max_work_group_sizes[i] / prop.get_max_compute_units();
181        info.devices[i].hw_info = get_device_hw_info(&device);
182
183        // Only check GPU devices; CPU devices use OpenCL and would otherwise
184        // disable Level Zero for the GPUs on systems without ONEAPI_DEVICE_SELECTOR set.
185        if (device.is_gpu() && device.default_queue().get_backend() != sycl::backend::ext_oneapi_level_zero) {
186            GGML_LOG_WARN("SYCL GPU device %d does not use Level Zero backend, disabling Level Zero memory API\n", i);
187            info.ext_oneapi_level_zero = false;
188        }
189
190#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
191        if (info.ext_oneapi_level_zero && device.is_gpu() && device.default_queue().get_backend() == sycl::backend::ext_oneapi_level_zero) {
192            ze_device_handle_t ze_dev = sycl::get_native<sycl::backend::ext_oneapi_level_zero>(device.default_queue().get_device());
193            ze_device_properties_t props = {};
194            props.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
195            ze_result_t r = zeDeviceGetProperties(ze_dev, &props);
196            if (r == ZE_RESULT_SUCCESS) {
197                info.devices[i].l0_device_type_valid = true;
198                info.devices[i].l0_discrete_gpu = !(props.flags & ZE_DEVICE_PROPERTY_FLAG_INTEGRATED);
199            }
200        }
201#endif
202    }
203
204    for (int id = 0; id < info.device_count; ++id) {
205        info.default_tensor_split[id] /= total_vram;
206    }
207
208#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
209    //update g_ggml_sycl_use_level_zero_api according to the device support
210    g_ggml_sycl_use_level_zero_api =
211        info.ext_oneapi_level_zero && g_ggml_sycl_use_level_zero_api;
212#endif
213
214    return info;
215}
216
217const ggml_sycl_device_info & ggml_sycl_info() {
218    static ggml_sycl_device_info info = ggml_sycl_init();
219    return info;
220}
221
222static void print_device_detail(int id, sycl::device &device, std::string device_type) {
223
224    dpct::device_info prop;
225    SYCL_CHECK(CHECK_TRY_ERROR(
226        dpct::get_device_info(prop, device)));
227
228    std::string version;
229    version += std::to_string(prop.get_major_version());
230    version += ".";
231    version += std::to_string(prop.get_minor_version());
232
233    device_type = std::regex_replace(device_type, std::regex("ext_oneapi_"), "");
234    std::string name = std::string(prop.get_name());
235    name = std::regex_replace(name, std::regex("\\(R\\)"), "");
236    name = std::regex_replace(name, std::regex("\\(TM\\)"), "");
237
238    auto global_mem_size = prop.get_global_mem_size()/1000000;
239    GGML_LOG_INFO("|%2d|%19s|%39s|%7s|%7d|%8d|%5d|%6luM|%21s|\n", id, device_type.c_str(),
240            name.c_str(), version.c_str(), prop.get_max_compute_units(),
241            prop.get_max_work_group_size(), prop.get_max_sub_group_size(),
242            global_mem_size, device.get_info<sycl::info::device::driver_version>().c_str());
243}
244
245static void print_device_opt_feature(int device_count) {
246    GGML_LOG_INFO("SYCL Optimization Feature:\n");
247    GGML_LOG_INFO(
248        "|ID|        Device Type|Reorder|\n");
249    GGML_LOG_INFO(
250        "|--|-------------------|-------|\n");
251    std::map<std::string, size_t> DeviceNums;
252    for (int id = 0; id < device_count; ++id) {
253      sycl::device device = dpct::dev_mgr::instance().get_device(id);
254      std::string backend_type = get_device_backend_and_type(device);
255      int type_id = DeviceNums[backend_type]++;
256      std::stringstream device_type;
257      device_type << "[" << backend_type << ":" << std::to_string(type_id)
258                  << "]";
259      std::string device_type_s = device_type.str();
260      device_type_s = std::regex_replace(device_type_s, std::regex("ext_oneapi_"), "");
261      GGML_LOG_INFO("|%2d|%19s|%7s|\n", id, device_type_s.c_str(),
262        ggml_sycl_info().devices[id].opt_feature.reorder ? "Y": "N");
263    }
264
265}
266void ggml_backend_sycl_print_sycl_devices() {
267    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_print_sycl_devices\n");
268    int device_count = dpct::dev_mgr::instance().device_count();
269    std::map<std::string, size_t> DeviceNums;
270    GGML_LOG_INFO("Found %d SYCL devices:\n", device_count);
271
272    GGML_LOG_INFO(
273        "|  |                   |                                       |      "
274        " |Max    |        |Max  |Global |                     |\n");
275    GGML_LOG_INFO(
276        "|  |                   |                                       |      "
277        " |compute|Max work|sub  |mem    |                     |\n");
278    GGML_LOG_INFO(
279        "|ID|        Device Type|                                   "
280        "Name|Version|units  |group   |group|size   |       Driver version|\n");
281    GGML_LOG_INFO(
282        "|--|-------------------|---------------------------------------|------"
283        "-|-------|--------|-----|-------|---------------------|\n");
284
285    for (int id = 0; id < device_count; ++id) {
286      sycl::device device = dpct::dev_mgr::instance().get_device(id);
287      std::string backend_type = get_device_backend_and_type(device);
288      int type_id = DeviceNums[backend_type]++;
289      std::stringstream device_type;
290      device_type << "[" << backend_type << ":" << std::to_string(type_id)
291                  << "]";
292      print_device_detail(id, device, device_type.str());
293    }
294
295    print_device_opt_feature(device_count);
296}
297
298static const char* dev2dev_int2str(int dev2dev) {
299    if (dev2dev == DEV2DEV_MEMCPY_SYCL) {
300        return "SYCL API";
301    } else if (dev2dev == DEV2DEV_MEMCPY_L0) {
302        return "Level Zero API";
303    } else if (dev2dev == DEV2DEV_MEMCPY_FORWARD) {
304        return "Host Forward";
305    } else {
306        return "Unknown";
307    }
308}
309
310/*
311* There are several entry APIs to be called as first function in SYCL backend in different cases.
312* It's the first internal function to be called by them in SYCL backend.
313* This function is used to do initialize work for the SYCL backend and set the global variables.
314*/
315#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
316static ze_result_t init_zes() {
317    ze_result_t res = zesInit(0);
318    if (res != ZE_RESULT_SUCCESS) {
319        GGML_SYCL_DEBUG("Warning: [%s] zesInit failed with code %d. Sysman free-memory query be unavailable.\n",
320                            __func__, (int) res);
321    }
322    return res;
323}
324
325ze_result_t get_zes_init_res() {
326    static ze_result_t zes_init_res = init_zes();
327    GGML_SYCL_DEBUG("[SYCL] call %s: zesInit result: %d\n", __func__, (int) zes_init_res);
328    return zes_init_res;
329}
330#endif
331
332void initialize_sycl_begining() {
333#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
334    //must be called in initialization stage, before any other Level Zero API calls
335    GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
336    get_zes_init_res();
337#endif
338}
339
340static void ggml_check_sycl() try {
341    GGML_SYCL_DEBUG("[SYCL] ggml_check_sycl()\n");
342    static bool initialized = false;
343
344    if (!initialized) {
345        initialize_sycl_begining();
346
347        g_ggml_sycl_debug = ggml_sycl_get_env("GGML_SYCL_DEBUG", 0);
348        g_ggml_sycl_dev_debug = ggml_sycl_get_env("GGML_SYCL_DEV_DEBUG", 0);
349        g_ggml_sycl_enable_optimize = ggml_sycl_get_env("GGML_SYCL_ENABLE_OPT", 1);
350        g_ggml_sycl_enable_graph = ggml_sycl_get_env("GGML_SYCL_ENABLE_GRAPH", 0);
351        g_ggml_sycl_enable_dnn = ggml_sycl_get_env("GGML_SYCL_ENABLE_DNN", 1);
352        g_ggml_sycl_fa_onednn = ggml_sycl_get_env("GGML_SYCL_FA_ONEDNN", 1);
353        g_ggml_sycl_fa_onednn_max_kv = ggml_sycl_get_env("GGML_SYCL_FA_ONEDNN_MAX_KV", 0);
354        g_ggml_sycl_enable_mkl_fa = ggml_sycl_get_env("GGML_SYCL_ENABLE_MKL_FA", 1);
355        g_ggml_sycl_memtrace = ggml_sycl_get_env("GGML_SYCL_MEMTRACE", 0);
356        g_ggml_sycl_memtrace_step = ggml_sycl_get_env("GGML_SYCL_MEMTRACE_STEP", 64);
357        g_ggml_sycl_enable_vmm = ggml_sycl_get_env("GGML_SYCL_ENABLE_VMM", 1);
358        g_ggml_sycl_enable_fusion = ggml_sycl_get_env("GGML_SYCL_ENABLE_FUSION", 1);
359        g_ggml_sycl_enable_esimd = ggml_sycl_get_env("GGML_SYCL_ENABLE_ESIMD", 1);
360        g_ggml_sycl_prioritize_dmmv = ggml_sycl_get_env("GGML_SYCL_PRIORITIZE_DMMV", 0);
361
362#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
363        g_ggml_sycl_use_level_zero_api = ggml_sycl_get_env("GGML_SYCL_USE_LEVEL_ZERO_API", 1);
364#else
365        g_ggml_sycl_use_level_zero_api = 0;
366#endif
367        g_ggml_sycl_dev2dev_memcpy = ggml_sycl_get_env("GGML_SYCL_DEV2DEV_MEMCPY", DEV2DEV_MEMCPY_SYCL);
368        g_ggml_sycl_get_mem_api = ggml_sycl_get_env("GGML_SYCL_GET_MEM_API", MEMORY_API_TYPE_LEVEL_ZERO);
369        if (g_ggml_sycl_use_level_zero_api == 0) {
370            g_ggml_sycl_dev2dev_memcpy = DEV2DEV_MEMCPY_SYCL;
371            g_ggml_sycl_get_mem_api = MEMORY_API_TYPE_SYCL;
372        }
373
374#ifdef SYCL_FLASH_ATTN
375        g_ggml_sycl_enable_flash_attention = ggml_sycl_get_env("GGML_SYCL_ENABLE_FLASH_ATTN", 1);
376#else
377        g_ggml_sycl_enable_flash_attention = 0;
378#endif
379
380        g_ggml_sycl_usm_system = ggml_sycl_get_env("GGML_SYCL_USM_SYSTEM", 0);
381        g_ggml_sycl_enable_host_pinned_mem =
382            ggml_sycl_get_env("GGML_SYCL_ENABLE_HOST_PINNED_MEM", 1);
383
384        g_ggml_sycl_host_pinned_mem_2g =
385            ggml_sycl_get_env("GGML_SYCL_HOST_PINNED_MEM_2G", 0) & g_ggml_sycl_enable_host_pinned_mem;
386
387        GGML_SYCL_DEBUG("[SYCL] call ggml_check_sycl\n");
388
389        GGML_LOG_INFO("Build with Macros:\n");
390#if defined(GGML_SYCL_DNNL)
391        GGML_LOG_INFO("  GGML_SYCL_DNNL: yes\n");
392#else
393        GGML_LOG_INFO("  GGML_SYCL_DNNL: no\n");
394#endif
395
396#if defined(GGML_SYCL_F16)
397        GGML_LOG_INFO("  GGML_SYCL_F16: yes\n");
398#else
399        GGML_LOG_INFO("  GGML_SYCL_F16: no\n");
400#endif
401
402#if defined(GGML_SYCL_FORCE_MMQ)
403        GGML_LOG_INFO("  GGML_SYCL_FORCE_MMQ: yes\n");
404#else
405        GGML_LOG_INFO("  GGML_SYCL_FORCE_MMQ: no\n");
406#endif
407
408#if defined(GGML_SYCL_GRAPH)
409        GGML_LOG_INFO("  GGML_SYCL_GRAPH: yes\n");
410#else
411        GGML_LOG_INFO("  GGML_SYCL_GRAPH: no\n");
412#endif
413
414#if defined(GGML_SYCL_SUPPORT_LEVEL_ZERO_API)
415        GGML_LOG_INFO("  GGML_SYCL_SUPPORT_LEVEL_ZERO_API: yes\n");
416#else
417        GGML_LOG_INFO("  GGML_SYCL_SUPPORT_LEVEL_ZERO_API: no\n");
418#endif
419#if defined(GGML_SYCL_SUPPORT_VMM)
420        GGML_LOG_INFO("  GGML_SYCL_SUPPORT_VMM: yes\n");
421#else
422        GGML_LOG_INFO("  GGML_SYCL_SUPPORT_VMM: no\n");
423#endif
424
425        GGML_LOG_INFO("Running with Environment Variables:\n");
426        GGML_LOG_INFO("  GGML_SYCL_DEBUG: %d\n", g_ggml_sycl_debug);
427        GGML_LOG_INFO("  GGML_SYCL_DEV_DEBUG: %d\n", g_ggml_sycl_dev_debug);
428
429#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
430        GGML_LOG_INFO("  GGML_SYCL_DEV2DEV_MEMCPY: %d (%s)\n", g_ggml_sycl_dev2dev_memcpy, dev2dev_int2str(g_ggml_sycl_dev2dev_memcpy));
431        GGML_LOG_INFO("  GGML_SYCL_GET_MEM_API: %d (%s)\n", g_ggml_sycl_get_mem_api, mem_api_int2str(g_ggml_sycl_get_mem_api));
432#else
433        GGML_LOG_INFO("  GGML_SYCL_DEV2DEV_MEMCPY: %d (%s), enable to SYCL API since missing GGML_SYCL_SUPPORT_LEVEL_ZERO_API\n",
434                      g_ggml_sycl_dev2dev_memcpy, dev2dev_int2str(g_ggml_sycl_dev2dev_memcpy));
435        GGML_LOG_INFO("  GGML_SYCL_GET_MEM_API: %d (%s), enable to SYCL API since missing GGML_SYCL_SUPPORT_LEVEL_ZERO_API\n",
436                      g_ggml_sycl_get_mem_api, mem_api_int2str(g_ggml_sycl_get_mem_api));
437#endif
438
439#if defined(GGML_SYCL_DNNL)
440        GGML_LOG_INFO("  GGML_SYCL_ENABLE_DNN: %d\n", g_ggml_sycl_enable_dnn);
441        GGML_LOG_INFO("  GGML_SYCL_FA_ONEDNN: %d\n", g_ggml_sycl_fa_onednn);
442#else
443        GGML_LOG_INFO("  GGML_SYCL_ENABLE_DNN: DNN disabled by compile flag\n");
444        GGML_LOG_INFO("  GGML_SYCL_FA_ONEDNN: %d\n", g_ggml_sycl_fa_onednn);
445#endif
446        GGML_LOG_INFO("  GGML_SYCL_FA_ONEDNN_MAX_KV: %d\n", g_ggml_sycl_fa_onednn_max_kv);
447        GGML_LOG_INFO("  GGML_SYCL_ENABLE_MKL_FA: %d\n", g_ggml_sycl_enable_mkl_fa);
448        GGML_LOG_INFO("  GGML_SYCL_MEMTRACE: %d\n", g_ggml_sycl_memtrace);
449        GGML_LOG_INFO("  GGML_SYCL_MEMTRACE_STEP: %d\n", g_ggml_sycl_memtrace_step);
450#ifdef SYCL_FLASH_ATTN
451        GGML_LOG_INFO("  GGML_SYCL_ENABLE_FLASH_ATTN: %d\n", g_ggml_sycl_enable_flash_attention);
452#else
453        GGML_LOG_INFO("  GGML_SYCL_ENABLE_FLASH_ATTN: %d disabled by compile flag\n",
454            g_ggml_sycl_enable_flash_attention);
455#endif
456
457#ifdef GGML_SYCL_GRAPH
458        GGML_LOG_INFO("  GGML_SYCL_ENABLE_GRAPH: %d\n", g_ggml_sycl_enable_graph);
459#else
460        GGML_LOG_INFO("  GGML_SYCL_ENABLE_GRAPH: graph disabled by compile flag\n");
461#endif
462
463        GGML_LOG_INFO("  GGML_SYCL_ENABLE_OPT: %d\n", g_ggml_sycl_enable_optimize);
464
465#if defined(GGML_SYCL_SUPPORT_VMM)
466        GGML_LOG_INFO("  GGML_SYCL_ENABLE_VMM: %d\n", g_ggml_sycl_enable_vmm);
467#else
468        GGML_LOG_INFO("  GGML_SYCL_ENABLE_VMM: virtual memory extension is not available\n");
469#endif
470
471        GGML_LOG_INFO("  GGML_SYCL_ENABLE_FUSION: %d\n", g_ggml_sycl_enable_fusion);
472
473#if defined(__INTEL_LLVM_COMPILER)
474        GGML_LOG_INFO("  GGML_SYCL_ENABLE_ESIMD: %d\n", g_ggml_sycl_enable_esimd);
475#else
476        GGML_LOG_INFO("  GGML_SYCL_ENABLE_ESIMD: %d disabled by compile flag\n", g_ggml_sycl_enable_esimd);
477#endif
478
479        GGML_LOG_INFO("  GGML_SYCL_PRIORITIZE_DMMV: %d\n", g_ggml_sycl_prioritize_dmmv);
480
481        g_ggml_sycl_use_async_mem_op_requested = ggml_sycl_get_env("GGML_SYCL_USE_ASYNC_MEM_OP", 1);
482        GGML_LOG_INFO("  GGML_SYCL_USE_ASYNC_MEM_OP: %d\n", g_ggml_sycl_use_async_mem_op_requested);
483
484#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
485        GGML_LOG_INFO("  GGML_SYCL_USE_LEVEL_ZERO_API: %d\n", g_ggml_sycl_use_level_zero_api);
486#else
487        GGML_LOG_INFO("  GGML_SYCL_USE_LEVEL_ZERO_API: Disable Level Zero API usage by compile flag\n");
488#endif
489
490        GGML_LOG_INFO("  GGML_SYCL_USM_SYSTEM: %d\n", g_ggml_sycl_usm_system);
491        GGML_LOG_INFO("  GGML_SYCL_ENABLE_HOST_PINNED_MEM: %d\n", g_ggml_sycl_enable_host_pinned_mem);
492        GGML_LOG_INFO("  GGML_SYCL_HOST_PINNED_MEM_2G: %d\n", g_ggml_sycl_host_pinned_mem_2g);
493
494/* NOT REMOVE, keep it for next optimize for XMX.
495#if defined(SYCL_USE_XMX)
496        fprintf(stderr, "%s: SYCL_USE_XMX: yes\n", __func__);
497#else
498        fprintf(stderr, "%s: SYCL_USE_XMX: no\n", __func__);
499#endif
500*/
501        // Async USM allocation/free is also useful outside the graph path: it avoids the host waits in the reorder
502        // staging path while preserving queue ordering semantics. Graph support still depends on the extension being
503        // available, but it no longer needs to control the non-graph fast path.
504#if defined(GGML_SYCL_GRAPH) && SYCL_EXT_ONEAPI_ASYNC_MEMORY_ALLOC
505        g_ggml_sycl_use_async_mem_op = g_ggml_sycl_use_async_mem_op_requested || g_ggml_sycl_enable_graph;
506        if (g_ggml_sycl_use_async_mem_op) {
507            for (unsigned int i = 0; i < dpct::dev_mgr::instance().device_count(); ++i) {
508                if (!dpct::dev_mgr::instance().get_device(i).has(sycl::aspect::ext_oneapi_async_memory_alloc)) {
509                    g_ggml_sycl_use_async_mem_op = 0;
510                    break;
511                }
512            }
513        }
514#endif
515        if (CHECK_TRY_ERROR(g_all_sycl_device_count =
516                            dpct::dev_mgr::instance().device_count()) != 0) {
517            initialized = true;
518            g_sycl_loaded = false;
519            return;
520        }
521        GGML_ASSERT(g_all_sycl_device_count <= GGML_SYCL_MAX_DEVICES);
522
523        initialized = true;
524        g_sycl_loaded = true;
525        ggml_backend_sycl_print_sycl_devices();
526    }
527}
528catch (sycl::exception const &exc) {
529  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
530            << ", line:" << __LINE__ << std::endl;
531  std::exit(1);
532}
533
534/*
535device_index: device index from 0 to n (continue numbers).
536    It is used for device select/set in SYCL backend internal data structure.
537*/
538inline void check_allow_gpu_index(const int device_index) {
539  if (device_index >= ggml_sycl_info().device_count) {
540    char error_buf[256];
541    snprintf(
542        error_buf,
543        sizeof(error_buf),
544        "%s error: device_index:%d is out of range: [0-%d]",
545        __func__,
546        device_index,
547        ggml_sycl_info().device_count - 1);
548    GGML_LOG_ERROR("%s\n", error_buf);
549    assert(false);
550  }
551}
552
553GGML_API void ggml_backend_sycl_get_gpu_list(int *id_list, int max_len) try {
554    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_get_gpu_list\n");
555    for(int i=0;i<max_len;i++) id_list[i] = -1;
556
557    for (int i=0;i< ggml_sycl_info().device_count;i++){
558        if (i>=max_len) break;
559        id_list[i] = i;
560    }
561    return;
562}
563catch (sycl::exception const &exc) {
564  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
565            << ", line:" << __LINE__ << std::endl;
566  std::exit(1);
567}
568
569inline void free_aligned_mem_host(void * memblock) {
570#ifdef _WIN32
571    _aligned_free(memblock);
572#else
573    free(memblock);
574#endif
575}
576
577// sycl buffer
578
579struct ggml_backend_sycl_buffer_context {
580    int device;
581    void * dev_ptr = nullptr;
582    queue_ptr stream;
583    std::string name;
584    optimize_feature opt_feature;
585    std::vector<ggml_tensor_extra_gpu *> tensor_extras;
586    bool is_usm_system;
587
588    ggml_backend_sycl_buffer_context(int device, void * dev_ptr, queue_ptr stream, bool is_usm_system) :
589        device(device), dev_ptr(dev_ptr), stream(stream), is_usm_system(is_usm_system) {
590            check_allow_gpu_index(device);
591            name = (GGML_SYCL_NAME + std::to_string(device));
592            opt_feature = ggml_sycl_info().devices[device].opt_feature;
593        }
594
595    ~ggml_backend_sycl_buffer_context() {
596        if (dev_ptr != nullptr) {
597            ggml_sycl_set_device(device);
598            if (is_usm_system)
599                free_aligned_mem_host(dev_ptr);
600            else
601                SYCL_CHECK(CHECK_TRY_ERROR(ggml_sycl_free_device(dev_ptr, *stream)));
602        }
603
604        //release extra used by tensors
605        for (ggml_tensor_extra_gpu * extra : tensor_extras) {
606            release_extra_gpu(extra);
607        }
608
609    }
610};
611
612static const char * ggml_backend_sycl_buffer_type_get_name(ggml_backend_buffer_type_t buft);
613
614static bool ggml_backend_buffer_is_sycl(ggml_backend_buffer_t buffer) {
615    return buffer->buft->iface.get_name == ggml_backend_sycl_buffer_type_get_name;
616}
617
618static void
619ggml_backend_sycl_buffer_free_buffer(ggml_backend_buffer_t buffer) try {
620    ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context;
621    ggml_sycl_set_device(ctx->device);
622
623    delete ctx;
624}
625catch (sycl::exception const &exc) {
626  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
627            << ", line:" << __LINE__ << std::endl;
628  std::exit(1);
629}
630
631static void * ggml_backend_sycl_buffer_get_base(ggml_backend_buffer_t buffer) {
632    ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context;
633    return ctx->dev_ptr;
634}
635
636static enum ggml_status
637ggml_backend_sycl_buffer_init_tensor(ggml_backend_buffer_t buffer,
638                                     ggml_tensor *tensor) try {
639    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
640    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor, "\n").c_str());
641    ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *)buffer->context;
642
643    if (tensor->view_src != NULL) {
644        assert(tensor->view_src->buffer->buft == buffer->buft);
645        return GGML_STATUS_SUCCESS;
646    }
647
648    if (g_ggml_sycl_enable_optimize) {
649        // set reorder extra buffer based on supported type
650        switch (tensor->type) {
651            case GGML_TYPE_Q4_0:
652            case GGML_TYPE_Q8_0:
653            case GGML_TYPE_Q2_K:
654            case GGML_TYPE_Q3_K:
655            case GGML_TYPE_Q4_K:
656            case GGML_TYPE_Q5_K:
657            case GGML_TYPE_Q6_K:{
658                ggml_tensor_extra_gpu * extra = new ggml_tensor_extra_gpu{};
659                tensor->extra                 = extra;
660                ctx->tensor_extras.push_back(extra);
661                break;
662            }
663            default:
664                break;
665        }
666    }
667
668    if (ggml_is_quantized(tensor->type)) {
669        // initialize padding to 0 to avoid possible NaN values
670        size_t original_size = ggml_nbytes(tensor);
671        size_t padded_size = ggml_backend_buft_get_alloc_size(buffer->buft, tensor);
672
673        if (padded_size > original_size && tensor->view_src == nullptr) {
674            SYCL_CHECK(CHECK_TRY_ERROR(ctx->stream->memset(
675                (char *)tensor->data + original_size, 0,
676                padded_size - original_size).wait()));
677        }
678    }
679    return GGML_STATUS_SUCCESS;
680}
681catch (sycl::exception const &exc) {
682  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
683            << ", line:" << __LINE__ << std::endl;
684  std::exit(1);
685}
686
687static void ggml_backend_sycl_buffer_set_tensor(ggml_backend_buffer_t buffer,
688                                                ggml_tensor *tensor,
689                                                const void *data, size_t offset,
690                                                size_t size) try {
691    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
692    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
693    GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset);
694    ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context;
695    ggml_sycl_set_device(ctx->device);
696    auto stream = &(dpct::dev_mgr::instance().get_device(ctx->device).default_queue());
697    SYCL_CHECK(CHECK_TRY_ERROR(dpct::dev_mgr::instance().get_device(ctx->device).queues_wait_and_throw()));
698#ifndef _WIN32
699    // Note: Use host buffer to save the data from mmap(), then copy to device. It's workaround for mmap() issue on PVC GPU.
700    // This function will be called during load model from disk. Use memory buffer replace dynamic won't save more time and brings potential memory leak risk here.
701    char * host_buf = (char *) malloc(size);
702    memcpy(host_buf, data, size);
703    SYCL_CHECK(CHECK_TRY_ERROR((*stream).memcpy((char *) tensor->data + offset, host_buf, size).wait()));
704    free(host_buf);
705#else
706    SYCL_CHECK(CHECK_TRY_ERROR((*stream).memcpy((char *) tensor->data + offset, data, size).wait()));
707#endif
708}
709catch (sycl::exception const &exc) {
710  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
711            << ", line:" << __LINE__ << std::endl;
712  std::exit(1);
713}
714
715static void ggml_backend_sycl_buffer_get_tensor(ggml_backend_buffer_t buffer,
716                                                const ggml_tensor *tensor,
717                                                void *data, size_t offset,
718                                                size_t size) try {
719    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
720    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
721    GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset);
722    ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context;
723
724    ggml_sycl_set_device(ctx->device);
725    auto stream = dpct::dev_mgr::instance().get_device(ctx->device).default_queue();
726
727    SYCL_CHECK(CHECK_TRY_ERROR(
728        stream.memcpy(data, (const char *)tensor->data + offset, size)
729            .wait()));
730}
731catch (sycl::exception const &exc) {
732  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
733            << ", line:" << __LINE__ << std::endl;
734  std::exit(1);
735}
736
737#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
738static bool ggml_sycl_is_l0_discrete_gpu(int device) {
739    return ggml_sycl_info().devices[device].l0_discrete_gpu;
740}
741#endif
742
743static void dev2dev_memcpy(int device_dst, sycl::queue &q_dst, int device_src, sycl::queue &q_src, void *ptr_dst,
744                    const void *ptr_src, size_t size) {
745
746#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
747    if (g_ggml_sycl_dev2dev_memcpy == DEV2DEV_MEMCPY_L0) {
748        // Use Level Zero direct copy for dGPU-to-dGPU transfers.
749        const bool l0_copy_supported =
750            ggml_sycl_is_l0_discrete_gpu(device_dst) && ggml_sycl_is_l0_discrete_gpu(device_src);
751        if (g_ggml_sycl_use_level_zero_api && l0_copy_supported) {
752            auto ze_ctx = sycl::get_native<sycl::backend::ext_oneapi_level_zero>(q_dst.get_context());
753            auto ze_dev = sycl::get_native<sycl::backend::ext_oneapi_level_zero>(q_dst.get_device());
754            ze_command_queue_desc_t cq_desc = {ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC, nullptr, 0, 0,
755                                            0, ZE_COMMAND_QUEUE_MODE_SYNCHRONOUS, ZE_COMMAND_QUEUE_PRIORITY_NORMAL};
756            ze_command_list_handle_t cl;
757            ze_result_t r = zeCommandListCreateImmediate(ze_ctx, ze_dev, &cq_desc, &cl);
758            if (r == ZE_RESULT_SUCCESS) {
759                GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by L0\n");
760                r = zeCommandListAppendMemoryCopy(cl, ptr_dst, ptr_src, size, nullptr, 0, nullptr);
761                zeCommandListDestroy(cl);
762                if (r == ZE_RESULT_SUCCESS) {
763                    return;
764                }
765            }
766        }
767    }
768#endif
769
770    if (g_ggml_sycl_dev2dev_memcpy == DEV2DEV_MEMCPY_SYCL) {
771        if (q_dst.get_device().ext_oneapi_can_access_peer(q_src.get_device(),
772                                                          sycl::ext::oneapi::peer_access::access_supported)) {
773            GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by SYCL\n");
774            q_dst.get_device().ext_oneapi_enable_peer_access(q_src.get_device());
775            SYCL_CHECK(CHECK_TRY_ERROR(q_dst.memcpy(ptr_dst, ptr_src, size).wait()));
776            return;
777        }
778    }
779
780    // Host-staged copy
781    if(g_ggml_sycl_dev2dev_memcpy == DEV2DEV_MEMCPY_FORWARD) {
782        GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by host forward for setting GGML_SYCL_DEV2DEV_MEMCPY=2\n");
783    } else {
784        GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by host forward for SYCL/L0 fallback\n");
785    }
786    char *host_buf = (char *)malloc(size);
787    q_src.memcpy(host_buf, (const char *)ptr_src, size).wait();
788    q_dst.memcpy((char *)ptr_dst, host_buf, size).wait();
789    free(host_buf);
790}
791
792static bool
793ggml_backend_sycl_buffer_cpy_tensor(ggml_backend_buffer_t buffer,
794                                    const ggml_tensor *src,
795                                    ggml_tensor *dst) try {
796    bool is_cpy_supported = ggml_backend_buffer_is_sycl(src->buffer);
797    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
798    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": dst", dst).c_str());
799    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(" src", src).c_str());
800    GGML_SYCL_DEBUG(" is_cpy_supported=%d\n", is_cpy_supported);
801    if (is_cpy_supported) {
802        ggml_backend_sycl_buffer_context * src_ctx = (ggml_backend_sycl_buffer_context *)src->buffer->context;
803        ggml_backend_sycl_buffer_context * dst_ctx = (ggml_backend_sycl_buffer_context *)dst->buffer->context;
804
805        ggml_sycl_set_device(src_ctx->device);
806        /*
807        DPCT1009:198: SYCL uses exceptions to report errors and does not use the
808        error codes. The original code was commented out and a warning string
809        was inserted. You need to rewrite this code.
810        */
811        SYCL_CHECK(CHECK_TRY_ERROR(
812            dpct::dev_mgr::instance().get_device(src_ctx->device).queues_wait_and_throw()));
813        ggml_sycl_set_device(dst_ctx->device);
814        /*
815        DPCT1009:199: SYCL uses exceptions to report errors and does not use the
816        error codes. The original code was commented out and a warning string
817        was inserted. You need to rewrite this code.
818        */
819        SYCL_CHECK(CHECK_TRY_ERROR(
820            dpct::dev_mgr::instance().get_device(dst_ctx->device).queues_wait_and_throw()));
821        /*
822        DPCT1009:200: SYCL uses exceptions to report errors and does not use the
823        error codes. The original code was commented out and a warning string
824        was inserted. You need to rewrite this code.
825        */
826
827        queue_ptr stream_dst = dst_ctx->stream;
828        queue_ptr stream_src = src_ctx->stream;
829        size_t size = ggml_nbytes(src);
830
831        //todo. it's dirty solutino to walkaroud known issue:device2device cross GPUs.
832        dev2dev_memcpy(dst_ctx->device, *stream_dst, src_ctx->device, *stream_src, dst->data, src->data, size);
833
834//todo, it's known issue:error in device2device cross GPUs. reused when the issue is fixed. DON"T remove
835#if 0
836        SYCL_CHECK(CHECK_TRY_ERROR((*stream).memcpy(
837            (char *)dst->data, (const char *)src->data, size).wait()));
838
839        /*
840        DPCT1009:201: SYCL uses exceptions to report errors and does not use the
841        error codes. The original code was commented out and a warning string
842        was inserted. You need to rewrite this code.
843        */
844        SYCL_CHECK(CHECK_TRY_ERROR(
845            dpct::dev_mgr::instance().get_device(dst_ctx->device).queues_wait_and_throw()));
846#endif
847        return true;
848    }
849    return false;
850    GGML_UNUSED(buffer);
851} catch (const sycl::exception & exc) {
852    std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl;
853    std::exit(1);
854}
855
856static void ggml_backend_sycl_buffer_clear(ggml_backend_buffer_t buffer,
857                                           uint8_t value) try {
858    GGML_SYCL_DEBUG("[SYCL] call %s: size=%zu\n", __func__, buffer->size);
859    ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *) buffer->context;
860
861    ggml_sycl_set_device(ctx->device);
862    queue_ptr stream = ctx->stream;
863    SYCL_CHECK(
864        CHECK_TRY_ERROR(dpct::get_current_device().queues_wait_and_throw()));
865
866    constexpr size_t MAX_CHUNK = 2ULL << 30;  // 2 GiB
867    for (size_t off = 0; off < buffer->size; off += MAX_CHUNK) {
868        size_t chunk = std::min(buffer->size - off, MAX_CHUNK);
869        SYCL_CHECK(CHECK_TRY_ERROR(
870            (*stream)
871                .memset(static_cast<char*>(ctx->dev_ptr) + off, value, chunk)
872                .wait()
873        ));
874    }
875}
876catch (sycl::exception const &exc) {
877  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
878            << ", line:" << __LINE__ << std::endl;
879  std::exit(1);
880}
881
882static void ggml_backend_sycl_buffer_memset_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, uint8_t value,
883                                                   size_t offset, size_t size) {
884    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
885    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
886    GGML_SYCL_DEBUG(" size=%zu offset=%zu value=%u\n", size, offset, value);
887    ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *) buffer->context;
888    SYCL_CHECK(ggml_sycl_set_device(ctx->device));
889    auto stream = &(dpct::dev_mgr::instance().get_device(ctx->device).default_queue());
890    if (size == 0) {
891        return;  // Nothing to do
892    }
893    if (tensor->data == nullptr) {
894        GGML_ABORT("Error: Tensor data pointer is null.\n");
895    }
896    void * target_ptr = static_cast<char *>(tensor->data) + offset;
897    SYCL_CHECK(CHECK_TRY_ERROR((*stream).memset(target_ptr, value, size)));
898    SYCL_CHECK(CHECK_TRY_ERROR((*stream).wait()));
899}
900
901static void ggml_backend_sycl_buffer_reset(ggml_backend_buffer_t buffer) {
902    GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
903    if (buffer == nullptr) {
904        return;
905    }
906
907    ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *) buffer->context;
908
909    if (ctx != nullptr) {
910        for (ggml_tensor_extra_gpu * extra : ctx->tensor_extras) {
911            release_extra_gpu(extra);
912        }
913        ctx->tensor_extras.clear();  // reset the tensor_extras vector
914    }
915}
916
917static const ggml_backend_buffer_i ggml_backend_sycl_buffer_interface = {
918    /* .free_buffer     = */ ggml_backend_sycl_buffer_free_buffer,
919    /* .get_base        = */ ggml_backend_sycl_buffer_get_base,
920    /* .init_tensor     = */ ggml_backend_sycl_buffer_init_tensor,
921    /* .memset_tensor   = */ ggml_backend_sycl_buffer_memset_tensor,
922    /* .set_tensor      = */ ggml_backend_sycl_buffer_set_tensor,
923    /* .get_tensor      = */ ggml_backend_sycl_buffer_get_tensor,
924    /* .set_tensor_2d   = */ NULL,
925    /* .get_tensor_2d   = */ NULL,
926    /* .cpy_tensor      = */ ggml_backend_sycl_buffer_cpy_tensor,
927    /* .clear           = */ ggml_backend_sycl_buffer_clear,
928    /* .reset           = */ ggml_backend_sycl_buffer_reset,
929};
930
931// sycl buffer type
932struct ggml_backend_sycl_buffer_type_context {
933    int device;
934    std::string name;
935
936    // each buffer type has its own stream
937    queue_ptr stream = nullptr;
938};
939
940static const char * ggml_backend_sycl_buffer_type_get_name(ggml_backend_buffer_type_t buft) {
941    ggml_backend_sycl_buffer_type_context * ctx = (ggml_backend_sycl_buffer_type_context *)buft->context;
942
943    return ctx->name.c_str();
944}
945
946static bool check_usm_system(int device, size_t size) {
947    bool use_usm_system = g_ggml_sycl_usm_system && size >= ((size_t)4 * MEM_SIZE_1G);
948
949    if (use_usm_system && !ggml_sycl_info().devices[device].usm_system_support) {
950        GGML_LOG_INFO("Device does not support USM system allocations\n");
951        use_usm_system = false;
952    }
953
954    return use_usm_system;
955}
956
957inline void * aligned_malloc_host(size_t alignment, size_t size) {
958#ifdef _WIN32
959    return _aligned_malloc(size, alignment);
960#else
961    return aligned_alloc(alignment, size);
962#endif
963}
964
965static ggml_backend_buffer_t
966ggml_backend_sycl_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft,
967                                           size_t size) try {
968    GGML_SYCL_DEBUG("[SYCL] call %s: size=%zu\n", __func__, size);
969    ggml_check_sycl();
970
971    ggml_backend_sycl_buffer_type_context * buft_ctx = (ggml_backend_sycl_buffer_type_context *)buft->context;
972    ggml_sycl_set_device(buft_ctx->device);
973    const queue_ptr stream = buft_ctx->stream;
974    size = std::max(size, (size_t)1); // syclMalloc returns null for size 0
975    /*
976    Alignment below ensures best performance. While in theory it could lead to
977    wasting memory, this is acceptable because in practice only few buffers are
978    allocated and even less exceed the minimum size accepted here for USM system
979    allocations.
980     */
981    size_t alignment = MEM_SIZE_2M;
982    size_t aligned_size = ((size + alignment - 1) / alignment) * alignment;
983    bool use_usm_system = check_usm_system(buft_ctx->device, aligned_size);
984
985    void * dev_ptr;
986    if (use_usm_system) {
987        GGML_SYCL_DEBUG("[SYCL] allocating %zu Bytes with USM system\n", size);
988        dev_ptr = (void *)aligned_malloc_host(alignment, aligned_size);
989        if (!dev_ptr) {
990            GGML_LOG_ERROR("%s: can't allocate %zu Bytes of memory on host\n", __func__, size);
991            return nullptr;
992        }
993    } else {
994        SYCL_CHECK(CHECK_TRY_ERROR(dev_ptr = (void *)ggml_sycl_malloc_device(size, *stream, GGML_SYCL_MEM_BUFFER)));
995        if (!dev_ptr) {
996          GGML_LOG_ERROR("%s: can't allocate %zu Bytes of memory on device\n", __func__, size);
997          return nullptr;
998        }
999    }
1000    ggml_backend_sycl_buffer_context * ctx = new  ggml_backend_sycl_buffer_context(buft_ctx->device, dev_ptr, buft_ctx->stream, use_usm_system);
1001    return ggml_backend_buffer_init(buft, ggml_backend_sycl_buffer_interface, ctx, size);
1002}
1003catch (sycl::exception const &exc) {
1004  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
1005            << ", line:" << __LINE__ << std::endl;
1006  std::exit(1);
1007}
1008
1009static size_t ggml_backend_sycl_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) {
1010    return SYCL_BUFFER_ALIGNMENT;
1011    GGML_UNUSED(buft);
1012}
1013
1014static size_t ggml_backend_sycl_buffer_type_get_max_size(ggml_backend_buffer_type_t buft) {
1015    size_t max_alloc_size = dpct::get_current_device().get_max_mem_alloc_size();
1016    if (g_ggml_sycl_host_pinned_mem_2g) {
1017        return std::min(max_alloc_size, (size_t) 2LL*1024*1024*1024);
1018    } else {
1019        return max_alloc_size;
1020    }
1021    GGML_UNUSED(buft);
1022}
1023
1024static size_t ggml_backend_sycl_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const ggml_tensor * tensor) {
1025    // Reserve the additional scratch so it's visible to the graph allocator
1026    size_t size = tensor->op == GGML_OP_FLASH_ATTN_EXT
1027        ? ggml_sycl_flash_attn_ext_get_alloc_size(tensor)
1028        : ggml_nbytes(tensor);
1029    int64_t ne0 = tensor->ne[0];
1030
1031    if (ggml_is_quantized(tensor->type)) {
1032        if (ne0 % MATRIX_ROW_PADDING != 0) {
1033            size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING);
1034        }
1035    }
1036
1037    return size;
1038
1039    GGML_UNUSED(buft);
1040}
1041
1042static const ggml_backend_buffer_type_i ggml_backend_sycl_buffer_type_interface = {
1043    /* .get_name         = */ ggml_backend_sycl_buffer_type_get_name,
1044    /* .alloc_buffer     = */ ggml_backend_sycl_buffer_type_alloc_buffer,
1045    /* .get_alignment    = */ ggml_backend_sycl_buffer_type_get_alignment,
1046    /* .get_max_size     = */ ggml_backend_sycl_buffer_type_get_max_size,
1047    /* .get_alloc_size   = */ ggml_backend_sycl_buffer_type_get_alloc_size,
1048    /* .is_host          = */ NULL,
1049};
1050
1051ggml_backend_buffer_type_t ggml_backend_sycl_buffer_type(int device) {
1052    static std::mutex mutex;
1053    std::lock_guard<std::mutex> lock(mutex);
1054
1055
1056    auto dev_count = ggml_backend_sycl_get_device_count();
1057
1058    if (device>=dev_count or device<0) {
1059        GGML_LOG_ERROR("ggml_backend_sycl_buffer_type error: device_index:%d is out of range [0, %d], miss to call ggml_backend_sycl_set_single_device()\n",
1060            device, dev_count-1);
1061        GGML_ASSERT(device<dev_count);
1062    }
1063    static struct ggml_backend_buffer_type ggml_backend_sycl_buffer_types[GGML_SYCL_MAX_DEVICES];
1064
1065    static bool ggml_backend_sycl_buffer_type_initialized = false;
1066
1067    if (!ggml_backend_sycl_buffer_type_initialized) {
1068        for (int i = 0; i < dev_count; i++) {
1069            auto & device_i = dpct::dev_mgr::instance().get_device(i);
1070            queue_ptr stream = &(device_i.default_queue());
1071            ggml_backend_sycl_buffer_types[i] = {
1072                /* .iface    = */ ggml_backend_sycl_buffer_type_interface,
1073                /* .device   = */ ggml_backend_reg_dev_get(ggml_backend_sycl_reg(), i),
1074                /* .context  = */ new ggml_backend_sycl_buffer_type_context{i, GGML_SYCL_NAME + std::to_string(i), stream},
1075            };
1076        }
1077        ggml_backend_sycl_buffer_type_initialized = true;
1078    }
1079    return &ggml_backend_sycl_buffer_types[device];
1080}
1081
1082static ggml_backend_buffer_type_t ggml_backend_sycl_buffer_type(ggml_backend_sycl_context * ctx) {
1083    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_buffer_type\n");
1084
1085    int device = ctx->device;
1086    if (device>=ggml_sycl_info().device_count or device<0) {
1087        GGML_LOG_ERROR("ggml_backend_sycl_buffer_type error: device_index:%d is out of range [0, %d], miss to call ggml_backend_sycl_set_single_device()\n",
1088            device, ggml_sycl_info().device_count-1);
1089        GGML_ASSERT(device<ggml_sycl_info().device_count);
1090    }
1091    static struct ggml_backend_buffer_type ggml_backend_sycl_buffer_types[GGML_SYCL_MAX_DEVICES];
1092
1093    static bool ggml_backend_sycl_buffer_type_initialized = false;
1094
1095    if (!ggml_backend_sycl_buffer_type_initialized) {
1096        for (int i = 0; i < ggml_sycl_info().device_count; i++) {
1097            ggml_backend_sycl_buffer_types[i] = {
1098                /* .iface    = */ ggml_backend_sycl_buffer_type_interface,
1099                /* .device   = */ nullptr,
1100                /* .context  = */ new ggml_backend_sycl_buffer_type_context{i, GGML_SYCL_NAME + std::to_string(i), ctx->stream(i, 0)},
1101            };
1102        }
1103        ggml_backend_sycl_buffer_type_initialized = true;
1104    }
1105    return &ggml_backend_sycl_buffer_types[device];
1106}
1107
1108// sycl split buffer
1109
1110static int64_t get_row_rounding(ggml_type type, const std::array<float, GGML_SYCL_MAX_DEVICES> & tensor_split) {
1111    int64_t min_compute_capability = INT_MAX;
1112    int64_t max_compute_capability = INT_MIN;
1113    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1114        if (tensor_split[i] < (i + 1 < ggml_sycl_info().device_count ? tensor_split[i + 1] : 1.0f)) {
1115            if (min_compute_capability > ggml_sycl_info().devices[i].cc) {
1116                min_compute_capability = ggml_sycl_info().devices[i].cc;
1117            }
1118            if (max_compute_capability < ggml_sycl_info().devices[i].cc) {
1119                max_compute_capability = ggml_sycl_info().devices[i].cc;
1120            }
1121        }
1122    }
1123
1124    switch(type) {
1125        case GGML_TYPE_Q1_0:
1126        case GGML_TYPE_Q4_0:
1127        case GGML_TYPE_Q4_1:
1128            return max_compute_capability >= VER_GEN9 ? 128 : 64;
1129        case GGML_TYPE_Q5_0:
1130        case GGML_TYPE_Q5_1:
1131        case GGML_TYPE_Q8_0:
1132            return 64;
1133        case GGML_TYPE_F16:
1134        case GGML_TYPE_F32:
1135            return 1;
1136        case GGML_TYPE_Q2_K:
1137        case GGML_TYPE_Q3_K:
1138        case GGML_TYPE_Q4_K:
1139        case GGML_TYPE_Q5_K:
1140        case GGML_TYPE_IQ2_XXS:
1141        case GGML_TYPE_IQ2_XS:
1142        case GGML_TYPE_IQ2_S:
1143        case GGML_TYPE_IQ1_S:
1144        case GGML_TYPE_IQ1_M:
1145        case GGML_TYPE_IQ3_XXS:
1146        case GGML_TYPE_IQ4_XS:
1147        case GGML_TYPE_IQ4_NL:
1148            return max_compute_capability >= VER_GEN9 ? 128 : 64;
1149        case GGML_TYPE_IQ3_S:
1150            return max_compute_capability >= VER_GEN9 ? 128 : 64;
1151        case GGML_TYPE_Q6_K:
1152            return 64;
1153        default:
1154            GGML_ABORT("fatal error");
1155    }
1156}
1157
1158static void get_row_split(int64_t * row_low, int64_t * row_high, const ggml_tensor * tensor, const std::array<float, GGML_SYCL_MAX_DEVICES> & tensor_split, int id) {
1159    const int64_t nrows = ggml_nrows(tensor);
1160    const int64_t rounding = get_row_rounding(tensor->type, tensor_split);
1161
1162    *row_low = id == 0 ? 0 : nrows*tensor_split[id];
1163    *row_low -= *row_low % rounding;
1164    if (id == ggml_sycl_info().device_count - 1) {
1165        *row_high = nrows;
1166    } else {
1167        *row_high = nrows*tensor_split[id + 1];
1168        *row_high -= *row_high % rounding;
1169    }
1170}
1171
1172static size_t ggml_nbytes_split(const struct ggml_tensor * tensor, int nrows_split) {
1173    static_assert(GGML_MAX_DIMS == 4, "GGML_MAX_DIMS is not 4 - update this function");
1174
1175    return nrows_split*ggml_row_size(tensor->type, tensor->ne[0]);
1176}
1177
1178struct ggml_backend_sycl_split_buffer_type_context {
1179    std::array<float, GGML_SYCL_MAX_DEVICES> tensor_split;
1180};
1181
1182struct ggml_backend_sycl_split_buffer_context {
1183    ~ggml_backend_sycl_split_buffer_context() try {
1184        for (ggml_tensor_extra_gpu * extra : tensor_extras) {
1185            release_extra_gpu(extra, streams);
1186        }
1187    }
1188    catch (sycl::exception const &exc) {
1189      std::cerr << exc.what() << "Exception caught at file:" << __FILE__
1190                << ", line:" << __LINE__ << std::endl;
1191      std::exit(1);
1192    }
1193
1194    std::vector<ggml_tensor_extra_gpu *> tensor_extras;
1195    std::vector<queue_ptr> streams;
1196};
1197
1198static void ggml_backend_sycl_split_buffer_free_buffer(ggml_backend_buffer_t buffer) {
1199    ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context;
1200    delete ctx;
1201}
1202
1203static void * ggml_backend_sycl_split_buffer_get_base(ggml_backend_buffer_t buffer) {
1204    // the pointers are stored in the tensor extras, this is just a dummy address and never dereferenced
1205    return (void *)0x1000;
1206
1207    GGML_UNUSED(buffer);
1208}
1209
1210static enum ggml_status
1211ggml_backend_sycl_split_buffer_init_tensor(ggml_backend_buffer_t buffer,
1212                                           ggml_tensor *tensor) try {
1213    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
1214    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor, "\n").c_str());
1215    GGML_ASSERT(tensor->view_src == nullptr); // views of split tensors are not supported
1216
1217    ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context;
1218    ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *)buffer->buft->context;
1219
1220    const int64_t ne0 = tensor->ne[0];
1221
1222    ggml_tensor_extra_gpu * extra = new ggml_tensor_extra_gpu{};
1223
1224    ctx->tensor_extras.push_back(extra);
1225    ctx->streams.push_back(&(dpct::get_current_device().default_queue()));
1226
1227    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1228        int64_t row_low, row_high;
1229        get_row_split(&row_low, &row_high, tensor, buft_ctx->tensor_split, i);
1230
1231        int64_t nrows_split = row_high - row_low;
1232        if (nrows_split == 0) {
1233            continue;
1234        }
1235
1236        size_t size = ggml_nbytes_split(tensor, nrows_split);
1237        const size_t original_size = size;
1238
1239        // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses
1240        if (ne0 % MATRIX_ROW_PADDING != 0) {
1241            size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING);
1242        }
1243
1244        ggml_sycl_set_device(i);
1245        const queue_ptr stream = ctx->streams[i];
1246        char * buf;
1247        SYCL_CHECK(CHECK_TRY_ERROR(buf = (char *)ggml_sycl_malloc_device(size, *stream, GGML_SYCL_MEM_BUFFER)));
1248        if (!buf) {
1249            char err_buf[1024];
1250            snprintf(err_buf, 1023, "%s: can't allocate %zu Bytes of memory on device\n", __func__, size);
1251            throw std::runtime_error(err_buf);
1252        }
1253        // set padding to 0 to avoid possible NaN values
1254        if (size > original_size) {
1255            /*
1256            DPCT1009:209: SYCL uses exceptions to report errors and does not use
1257            the error codes. The original code was commented out and a warning
1258            string was inserted. You need to rewrite this code.
1259            */
1260            SYCL_CHECK(CHECK_TRY_ERROR(
1261                (*stream)
1262                    .memset(buf + original_size, 0, size - original_size)
1263                    .wait()));
1264        }
1265
1266        extra->data_device[i] = buf;
1267
1268        for (int64_t is = 0; is < GGML_SYCL_MAX_STREAMS; ++is) {
1269            /*
1270            DPCT1009:210: SYCL uses exceptions to report errors and does not use
1271            the error codes. The original code was commented out and a warning
1272            string was inserted. You need to rewrite this code.
1273            */
1274            SYCL_CHECK(
1275                CHECK_TRY_ERROR(extra->events[i][is] = new sycl::event()));
1276        }
1277    }
1278    tensor->extra = extra;
1279    return GGML_STATUS_SUCCESS;
1280}
1281catch (sycl::exception const &exc) {
1282  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
1283            << ", line:" << __LINE__ << std::endl;
1284  std::exit(1);
1285}
1286
1287static void
1288ggml_backend_sycl_split_buffer_set_tensor(ggml_backend_buffer_t buffer,
1289                                          ggml_tensor *tensor, const void *data,
1290                                          size_t offset, size_t size) try {
1291    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
1292    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
1293    GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset);
1294    // split tensors must always be set in their entirety at once
1295    GGML_ASSERT(offset == 0);
1296    GGML_ASSERT(size == ggml_nbytes(tensor));
1297
1298    ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context;
1299    ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *)buffer->buft->context;
1300
1301    const int64_t ne0 = tensor->ne[0];
1302    const size_t nb1 = tensor->nb[1];
1303    ggml_tensor_extra_gpu * extra = (ggml_tensor_extra_gpu *)tensor->extra;
1304
1305    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1306        int64_t row_low, row_high;
1307        get_row_split(&row_low, &row_high, tensor, buft_ctx->tensor_split, i);
1308
1309        int64_t nrows_split = row_high - row_low;
1310        if (nrows_split == 0) {
1311            continue;
1312        }
1313
1314        const size_t offset_split = row_low*nb1;
1315        size_t size = ggml_nbytes_split(tensor, nrows_split);
1316        const size_t original_size = size;
1317
1318        // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses
1319        if (ne0 % MATRIX_ROW_PADDING != 0) {
1320            size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING);
1321        }
1322
1323        const char * buf_host = (const char *)data + offset_split;
1324        /*
1325        DPCT1009:211: SYCL uses exceptions to report errors and does not use the
1326        error codes. The original code was commented out and a warning string
1327        was inserted. You need to rewrite this code.
1328        */
1329        ggml_sycl_set_device(i);
1330        const queue_ptr stream = ctx->streams[i];
1331        SYCL_CHECK(CHECK_TRY_ERROR(
1332            (*stream)
1333                .memcpy(extra->data_device[i], buf_host, original_size)
1334                .wait()));
1335    }
1336}
1337catch (sycl::exception const &exc) {
1338  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
1339            << ", line:" << __LINE__ << std::endl;
1340  std::exit(1);
1341}
1342
1343static void
1344ggml_backend_sycl_split_buffer_get_tensor(ggml_backend_buffer_t buffer,
1345                                          const ggml_tensor *tensor, void *data,
1346                                          size_t offset, size_t size) try {
1347    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
1348    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
1349    GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset);
1350    // split tensors must always be set in their entirety at once
1351    GGML_ASSERT(offset == 0);
1352    GGML_ASSERT(size == ggml_nbytes(tensor));
1353
1354    ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context;
1355    ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *)buffer->buft->context;
1356
1357    const int64_t ne0 = tensor->ne[0];
1358    const size_t nb1 = tensor->nb[1];
1359    ggml_tensor_extra_gpu * extra = (ggml_tensor_extra_gpu *)tensor->extra;
1360
1361    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1362        int64_t row_low, row_high;
1363        get_row_split(&row_low, &row_high, tensor, buft_ctx->tensor_split, i);
1364
1365        int64_t nrows_split = row_high - row_low;
1366        if (nrows_split == 0) {
1367            continue;
1368        }
1369
1370        const size_t offset_split = row_low*nb1;
1371        size_t size = ggml_nbytes_split(tensor, nrows_split);
1372        const size_t original_size = size;
1373
1374        // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses
1375        if (ne0 % MATRIX_ROW_PADDING != 0) {
1376            size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING);
1377        }
1378
1379        char * buf_host = (char *)data + offset_split;
1380        /*
1381        DPCT1009:212: SYCL uses exceptions to report errors and does not use the
1382        error codes. The original code was commented out and a warning string
1383        was inserted. You need to rewrite this code.
1384        */
1385        ggml_sycl_set_device(i);
1386        const queue_ptr stream = ctx->streams[i];
1387        SYCL_CHECK(CHECK_TRY_ERROR(
1388            (*stream)
1389                .memcpy(buf_host, extra->data_device[i], original_size)
1390                .wait()));
1391    }
1392}
1393catch (sycl::exception const &exc) {
1394  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
1395            << ", line:" << __LINE__ << std::endl;
1396  std::exit(1);
1397}
1398
1399static void ggml_backend_sycl_split_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) {
1400    GGML_UNUSED(buffer);
1401    GGML_UNUSED(value);
1402}
1403
1404static struct ggml_backend_buffer_i ggml_backend_sycl_split_buffer_interface = {
1405    /* .free_buffer     = */ ggml_backend_sycl_split_buffer_free_buffer,
1406    /* .get_base        = */ ggml_backend_sycl_split_buffer_get_base,
1407    /* .init_tensor     = */ ggml_backend_sycl_split_buffer_init_tensor,
1408    /* .memset_tensor   = */ NULL,
1409    /* .set_tensor      = */ ggml_backend_sycl_split_buffer_set_tensor,
1410    /* .get_tensor      = */ ggml_backend_sycl_split_buffer_get_tensor,
1411    /* .set_tensor_2d   = */ NULL,
1412    /* .get_tensor_2d   = */ NULL,
1413    /* .cpy_tensor      = */ NULL,
1414    /* .clear           = */ ggml_backend_sycl_split_buffer_clear,
1415    /* .reset           = */ NULL,
1416};
1417
1418// sycl split buffer type
1419
1420static const char * ggml_backend_sycl_split_buffer_type_get_name(ggml_backend_buffer_type_t buft) {
1421    return GGML_SYCL_NAME "_Split";
1422
1423    GGML_UNUSED(buft);
1424}
1425
1426static bool ggml_backend_buffer_is_sycl_split(ggml_backend_buffer_t buffer) {
1427   return buffer->buft->iface.get_name == ggml_backend_sycl_split_buffer_type_get_name;
1428}
1429
1430static ggml_backend_buffer_t ggml_backend_sycl_split_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) {
1431    // since we don't know the exact split after rounding, we cannot allocate the device buffers at this point
1432    // instead, we allocate them for each tensor separately in init_tensor
1433    // however, the size still represents the maximum cumulative size of all the device buffers after the tensors are allocated,
1434    // as returned by get_alloc_size. this limit is enforced during tensor allocation by ggml-alloc, so it must be correct.
1435    ggml_backend_sycl_split_buffer_context * ctx = new ggml_backend_sycl_split_buffer_context();
1436
1437    return ggml_backend_buffer_init(buft, ggml_backend_sycl_split_buffer_interface, ctx, size);
1438}
1439
1440static size_t ggml_backend_sycl_split_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) {
1441    return SYCL_BUFFER_ALIGNMENT;
1442    GGML_UNUSED(buft);
1443}
1444
1445static size_t ggml_backend_sycl_split_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const ggml_tensor * tensor) {
1446    ggml_backend_sycl_split_buffer_type_context * ctx = (ggml_backend_sycl_split_buffer_type_context *)buft->context;
1447
1448    size_t total_size = 0;
1449
1450    const int64_t ne0 = tensor->ne[0];
1451
1452    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1453        int64_t row_low, row_high;
1454        get_row_split(&row_low, &row_high, tensor, ctx->tensor_split, i);
1455
1456        int64_t nrows_split = row_high - row_low;
1457        if (nrows_split == 0) {
1458            continue;
1459        }
1460
1461        total_size += ggml_nbytes_split(tensor, nrows_split);
1462
1463        // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses
1464        if (ne0 % MATRIX_ROW_PADDING != 0) {
1465            total_size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING);
1466        }
1467    }
1468
1469    return total_size;
1470}
1471
1472static bool ggml_backend_sycl_split_buffer_type_is_host(ggml_backend_buffer_type_t buft) {
1473    return false;
1474
1475    GGML_UNUSED(buft);
1476}
1477
1478static ggml_backend_buffer_type_i ggml_backend_sycl_split_buffer_type_interface = {
1479    /* .get_name         = */ ggml_backend_sycl_split_buffer_type_get_name,
1480    /* .alloc_buffer     = */ ggml_backend_sycl_split_buffer_type_alloc_buffer,
1481    /* .get_alignment    = */ ggml_backend_sycl_split_buffer_type_get_alignment,
1482    /* .get_max_size     = */ NULL, // defaults to SIZE_MAX
1483    /* .get_alloc_size   = */ ggml_backend_sycl_split_buffer_type_get_alloc_size,
1484    /* .is_host          = */ ggml_backend_sycl_split_buffer_type_is_host,
1485};
1486
1487ggml_backend_buffer_type_t ggml_backend_sycl_split_buffer_type(const float * tensor_split) {
1488    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_split_buffer_type\n");
1489
1490    static std::mutex mutex;
1491    std::lock_guard<std::mutex> lock(mutex);
1492
1493    ggml_check_sycl();
1494    // FIXME: this is not thread safe
1495    static std::map<std::array<float, GGML_SYCL_MAX_DEVICES>, struct ggml_backend_buffer_type> buft_map;
1496
1497    std::array<float, GGML_SYCL_MAX_DEVICES> tensor_split_arr = {};
1498
1499    bool all_zero = tensor_split == nullptr || std::all_of(tensor_split, tensor_split + GGML_SYCL_MAX_DEVICES, [](float x) { return x == 0.0f; });
1500    if (all_zero) {
1501        tensor_split_arr = ggml_sycl_info().default_tensor_split;
1502    } else {
1503        float split_sum = 0.0f;
1504        for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1505            tensor_split_arr[i] = split_sum;
1506            split_sum += tensor_split[i];
1507        }
1508        for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
1509            tensor_split_arr[i] /= split_sum;
1510        }
1511    }
1512
1513    auto it = buft_map.find(tensor_split_arr);
1514    if (it != buft_map.end()) {
1515        return &it->second;
1516    }
1517
1518    struct ggml_backend_buffer_type buft {
1519        /* .iface   = */ ggml_backend_sycl_split_buffer_type_interface,
1520        /* .device  = */ ggml_backend_reg_dev_get(ggml_backend_sycl_reg(), 0),
1521        /* .context = */ new ggml_backend_sycl_split_buffer_type_context{tensor_split_arr},
1522    };
1523
1524    auto result = buft_map.emplace(tensor_split_arr, buft);
1525    return &result.first->second;
1526}
1527
1528// host buffer type
1529
1530struct ggml_backend_sycl_device_context {
1531    int device;
1532    std::string name;
1533    std::string description;
1534    int op_offload_min_batch_size;
1535};
1536
1537static const char * ggml_backend_sycl_host_buffer_type_name(ggml_backend_buffer_type_t buft) {
1538    return GGML_SYCL_NAME "_Host";
1539
1540    GGML_UNUSED(buft);
1541}
1542
1543//host pinned memory
1544static void * ggml_backend_sycl_host_malloc(size_t size) {
1545    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_host_malloc\n");
1546    void * ptr = nullptr;
1547    try {
1548        ggml_check_sycl();
1549        // USM host memory is page-locked and device-accessible by construction
1550        auto & q = dpct::dev_mgr::instance().get_device(0).default_queue();
1551        ptr = sycl::malloc_host(size, q, sycl::property_list{});
1552    } catch (...) {
1553        ptr = nullptr;
1554    }
1555    if (ptr == nullptr) {
1556        GGML_LOG_WARN("%s: failed to allocate %.2f MiB of pinned memory\n", __func__,
1557                size / 1024.0 / 1024.0);
1558    }
1559
1560    return ptr;
1561}
1562
1563static void ggml_backend_sycl_host_buffer_free_buffer(ggml_backend_buffer_t buffer) {
1564    if (buffer->context == nullptr) {
1565        return;
1566    }
1567    if (g_ggml_sycl_enable_host_pinned_mem) {
1568        auto & q = dpct::dev_mgr::instance().get_device(0).default_queue();
1569        SYCL_CHECK(CHECK_TRY_ERROR(sycl::free(buffer->context, q)));
1570    } else {
1571        free_aligned_mem_host((void *) buffer->context);
1572    }
1573}
1574
1575static ggml_backend_buffer_t ggml_backend_sycl_host_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) {
1576    void * ptr = g_ggml_sycl_enable_host_pinned_mem ? ggml_backend_sycl_host_malloc(size) :
1577                                                      aligned_malloc_host(TENSOR_ALIGNMENT, size);
1578    if (ptr == nullptr) {
1579        // fallback to cpu buffer
1580        return ggml_backend_buft_alloc_buffer(ggml_backend_cpu_buffer_type(), size);
1581    }
1582
1583    // FIXME: this is a hack to avoid having to implement a new buffer type
1584    ggml_backend_buffer_t buffer = ggml_backend_cpu_buffer_from_ptr(ptr, size);
1585    buffer->buft = buft;
1586    buffer->iface.free_buffer = ggml_backend_sycl_host_buffer_free_buffer;
1587
1588    return buffer;
1589}
1590
1591static size_t ggml_backend_sycl_host_buffer_type_get_max_size(ggml_backend_buffer_type_t buft) {
1592
1593    if (g_ggml_sycl_enable_host_pinned_mem) {
1594        ggml_backend_sycl_device_context * dev_ctx = (ggml_backend_sycl_device_context *) buft->device->context;
1595        size_t max_alloc_size = dpct::dev_mgr::instance().get_device(dev_ctx->device).get_max_mem_alloc_size();
1596        if (g_ggml_sycl_host_pinned_mem_2g) {
1597            return std::min(max_alloc_size, (size_t) 2LL*1024*1024*1024);
1598        } else {
1599            return max_alloc_size;
1600        }
1601    } else {
1602        return SIZE_MAX;
1603    }
1604}
1605
1606ggml_backend_buffer_type_t ggml_backend_sycl_host_buffer_type() {
1607    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_host_buffer_type\n");
1608    static struct ggml_backend_buffer_type ggml_backend_sycl_buffer_type_host = {
1609        /* .iface    = */ {
1610            /* .get_name         = */ ggml_backend_sycl_host_buffer_type_name,
1611            /* .alloc_buffer     = */ ggml_backend_sycl_host_buffer_type_alloc_buffer,
1612            /* .get_alignment    = */ ggml_backend_cpu_buffer_type()->iface.get_alignment,
1613            /* .get_max_size     = */ ggml_backend_sycl_host_buffer_type_get_max_size,
1614            /* .get_alloc_size   = */ ggml_backend_cpu_buffer_type()->iface.get_alloc_size,
1615            /* .is_host          = */ ggml_backend_cpu_buffer_type()->iface.is_host,
1616        },
1617        /* .device   = */ ggml_backend_reg_dev_get(ggml_backend_sycl_reg(), 0),
1618        /* .context  = */ nullptr,
1619    };
1620
1621    return &ggml_backend_sycl_buffer_type_host;
1622}
1623
1624// buffer pool for sycl (legacy)
1625struct ggml_sycl_pool_leg : public ggml_sycl_pool {
1626    static const int MAX_SYCL_BUFFERS = 256;
1627
1628    int device;
1629    queue_ptr qptr;
1630    struct ggml_sycl_buffer {
1631        void * ptr = nullptr;
1632        size_t size = 0;
1633    };
1634
1635    ggml_sycl_buffer buffer_pool[MAX_SYCL_BUFFERS] = {};
1636    size_t pool_size = 0;
1637
1638    explicit ggml_sycl_pool_leg(queue_ptr qptr_, int device_) : device(device_), qptr(qptr_) {}
1639
1640    ~ggml_sycl_pool_leg() {
1641#ifdef DEBUG_SYCL_POOL
1642        int    n_cached    = 0;
1643        size_t bytes_cached = 0;
1644        for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) {
1645            if (buffer_pool[i].ptr != nullptr) {
1646                ++n_cached;
1647                bytes_cached += buffer_pool[i].size;
1648            }
1649        }
1650        GGML_LOG_INFO("%s: %d buffers, cached = %.2f MiB\n", __func__,
1651                      n_cached, bytes_cached / 1024.0 / 1024.0);
1652        const auto slots = format_slots_in_alloc_order();
1653        if (!slots.empty()) {
1654            GGML_LOG_INFO("%s: slots MiB: %s\n", __func__, slots.c_str());
1655        }
1656#endif
1657
1658        for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) {
1659            ggml_sycl_buffer & b = buffer_pool[i];
1660            if (b.ptr != nullptr) {
1661                SYCL_CHECK(CHECK_TRY_ERROR(ggml_sycl_free_device(b.ptr, *qptr)));
1662                pool_size -= b.size;
1663            }
1664        }
1665        GGML_ASSERT(pool_size == 0);
1666    }
1667
1668#ifdef DEBUG_SYCL_POOL
1669    std::string format_slots_in_alloc_order() const {
1670        std::string line;
1671        char buf[32];
1672        bool first = true;
1673        for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) {
1674            if (buffer_pool[i].ptr == nullptr) {
1675                continue;
1676            }
1677            if (!first) {
1678                line += '/';
1679            }
1680            first = false;
1681            snprintf(buf, sizeof(buf), "%.2f", buffer_pool[i].size / 1024.0 / 1024.0);
1682            line += buf;
1683        }
1684        return line;
1685    }
1686#endif
1687
1688    void * alloc(size_t size, size_t * actual_size) override {
1689#ifdef DEBUG_sycl_MALLOC
1690        int nnz = 0;
1691        size_t max_size = 0;
1692#endif
1693        size_t best_diff = 1ull << 36;
1694        int ibest = -1;
1695        for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) {
1696            ggml_sycl_buffer& b = buffer_pool[i];
1697            if (b.ptr != nullptr) {
1698#ifdef DEBUG_sycl_MALLOC
1699                ++nnz;
1700                if (b.size > max_size) max_size = b.size;
1701#endif
1702                if (b.size >= size) {
1703                    size_t diff = b.size - size;
1704                    if (diff < best_diff) {
1705                        best_diff = diff;
1706                        ibest = i;
1707                        if (!best_diff) {
1708                            void * ptr = b.ptr;
1709                            *actual_size = b.size;
1710                            b.ptr = nullptr;
1711                            b.size = 0;
1712                            return ptr;
1713                        }
1714                    }
1715                }
1716            }
1717        }
1718        if (ibest >= 0) {
1719            ggml_sycl_buffer& b = buffer_pool[ibest];
1720            void * ptr = b.ptr;
1721            *actual_size = b.size;
1722            b.ptr = nullptr;
1723            b.size = 0;
1724            return ptr;
1725        }
1726        void * ptr;
1727        size_t look_ahead_size = (size_t) (1.05 * size);
1728
1729        SYCL_CHECK(CHECK_TRY_ERROR(ptr = (void *)ggml_sycl_malloc_device(look_ahead_size, *qptr, GGML_SYCL_MEM_POOL_LEG)));
1730        if (!ptr) {
1731            GGML_LOG_ERROR("%s: can't allocate %zu Bytes of memory on device/GPU\n", __func__, look_ahead_size);
1732            return nullptr;
1733        }
1734
1735        *actual_size = look_ahead_size;
1736        pool_size += look_ahead_size;
1737
1738#ifdef DEBUG_SYCL_MALLOC
1739        GGML_LOG_DEBUG("%s[%d]: %d buffers, max_size = %u MB, pool_size = %u MB, requested %u MB\n", __func__, id, nnz,
1740                (uint32_t)(max_size/1024/1024), (uint32_t)(g_sycl_pool_size[id]/1024/1024), (uint32_t)(size/1024/1024));
1741#endif
1742
1743        // GGML_SYCL_DEBUG("ggml_sycl_pool_malloc_leg look_ahead_size=%zu, return %p\n", look_ahead_size, ptr);
1744        return ptr;
1745    }
1746
1747    void free(void * ptr, size_t size) override {
1748        for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) {
1749            ggml_sycl_buffer& b = buffer_pool[i];
1750            if (b.ptr == nullptr) {
1751                b.ptr = ptr;
1752                b.size = size;
1753                return;
1754            }
1755        }
1756        GGML_LOG_WARN("WARNING: sycl buffer pool full, increase MAX_sycl_BUFFERS\n");
1757        SYCL_CHECK(CHECK_TRY_ERROR(ggml_sycl_free_device(ptr, *qptr)));
1758        pool_size -= size;
1759    }
1760};
1761
1762// pool with virtual memory management
1763#if defined(GGML_SYCL_SUPPORT_VMM)
1764struct ggml_sycl_pool_vmm : public ggml_sycl_pool {
1765    static const size_t SYCL_POOL_VMM_MAX_SIZE = 1ull << 35; // 32 GB
1766
1767    int           device;
1768    sycl::context ctx;
1769    sycl::device  dev;
1770
1771    uintptr_t pool_addr = 0;
1772    size_t    pool_used = 0;
1773    size_t    pool_size = 0;
1774    size_t    granularity;
1775
1776    // physical_mem owns the commits (unlike cuMemMap)
1777    struct mapping {
1778        sycl::ext::oneapi::experimental::physical_mem phys;
1779        void * map_ptr;
1780    };
1781    std::vector<mapping> mappings;
1782
1783    explicit ggml_sycl_pool_vmm(queue_ptr qptr_, int device_) :
1784        device(device_),
1785        ctx(qptr_->get_context()),
1786        dev(qptr_->get_device()),
1787        granularity(ggml_sycl_info().devices[device_].vmm_granularity) {
1788    }
1789
1790    ~ggml_sycl_pool_vmm() {
1791        if (pool_addr == 0) {
1792            return;
1793        }
1794
1795        // Per spec, unmap must (a) match the exact (ptr, size) of an earlier
1796        // physical_mem::map() call and (b) precede destruction of the
1797        // physical_mem objects (their dtors won't unmap).
1798        for (auto & m : mappings) {
1799            SYCL_CHECK(CHECK_TRY_ERROR(sycl::ext::oneapi::experimental::unmap(
1800                m.map_ptr, m.phys.size(), ctx)));
1801        }
1802        SYCL_CHECK(CHECK_TRY_ERROR(sycl::ext::oneapi::experimental::free_virtual_mem(
1803            pool_addr, SYCL_POOL_VMM_MAX_SIZE, ctx)));
1804    }
1805
1806    void * alloc(size_t size, size_t * actual_size) override {
1807        // round up the allocation size to the alignment to ensure that all allocations are aligned for all data types
1808        size = GGML_PAD(size, SYCL_BUFFER_ALIGNMENT);
1809
1810        size_t avail = pool_size - pool_used;
1811
1812        if (size > avail) {
1813            // round up to the next multiple of the granularity
1814            size_t reserve_size = GGML_PAD(size - avail, granularity);
1815
1816            GGML_ASSERT(pool_size + reserve_size <= SYCL_POOL_VMM_MAX_SIZE);
1817
1818            if (ggml_sycl_memtrace_enabled()) {
1819                GGML_LOG_INFO(GGML_SYCL_MEMTRACE_TAG " pool_vmm[%d] committing %5zu MiB (pool %5zu -> %5zu MiB)\n",
1820                              device, reserve_size / (1024 * 1024), pool_size / (1024 * 1024),
1821                              (pool_size + reserve_size) / (1024 * 1024));
1822                ggml_sycl_memtrace_report("before pool_vmm commit");
1823            }
1824
1825            // allocate more physical memory
1826            std::optional<sycl::ext::oneapi::experimental::physical_mem> phys;
1827            SYCL_CHECK(CHECK_TRY_ERROR(phys.emplace(dev, ctx, reserve_size)));
1828
1829            // reserve virtual address space (if not already reserved)
1830            if (pool_addr == 0) {
1831                SYCL_CHECK(CHECK_TRY_ERROR(
1832                    pool_addr = sycl::ext::oneapi::experimental::reserve_virtual_mem(
1833                        SYCL_POOL_VMM_MAX_SIZE, ctx)));
1834            }
1835
1836            // map at the end of the pool
1837            void * map_ptr = nullptr;
1838            SYCL_CHECK(CHECK_TRY_ERROR(
1839                map_ptr = phys->map(pool_addr + pool_size, reserve_size,
1840                                    sycl::ext::oneapi::experimental::address_access_mode::read_write)));
1841
1842            // stash these so we could unmap this exact range in dtor
1843            mappings.push_back({
1844                std::move(*phys),
1845                map_ptr,
1846            });
1847
1848            // add to the pool
1849            pool_size += reserve_size;
1850            ggml_sycl_memtrace_add(GGML_SYCL_MEM_POOL_VMM, map_ptr, reserve_size);
1851
1852#ifdef DEBUG_SYCL_MALLOC
1853            GGML_LOG_INFO("sycl pool[%d]: size increased to %llu MB (reserved %llu MB)\n",
1854                          device, (unsigned long long) (pool_size/1024/1024),
1855                          (unsigned long long) (reserve_size/1024/1024));
1856#endif
1857        }
1858
1859        GGML_ASSERT(pool_addr != 0);
1860
1861        void * ptr = reinterpret_cast<void *>(pool_addr + pool_used);
1862        *actual_size = size;
1863        pool_used += size;
1864
1865#ifdef DEBUG_SYCL_MALLOC
1866        GGML_LOG_INFO("sycl pool[%d]: allocated %llu bytes at %p\n", device, (unsigned long long) size, ptr);
1867#endif
1868
1869        return ptr;
1870    }
1871
1872    void free(void * ptr, size_t size) override {
1873#ifdef DEBUG_SYCL_MALLOC
1874        GGML_LOG_INFO("sycl pool[%d]: freed %llu bytes at %p\n", device, (unsigned long long) size, ptr);
1875#endif
1876
1877        pool_used -= size;
1878
1879        // all deallocations must be in reverse order of the allocations
1880        GGML_ASSERT(ptr == reinterpret_cast<void *>(pool_addr + pool_used));
1881    }
1882};
1883#endif // defined(GGML_SYCL_SUPPORT_VMM)
1884
1885struct ggml_sycl_pool_host : public ggml_sycl_pool {
1886    queue_ptr qptr;
1887    int       device;
1888
1889    inline static int counter{ 0 };
1890
1891    struct ggml_sycl_buffer {
1892        void * ptr  = nullptr;
1893        size_t size = 0;
1894    };
1895
1896    // Set arbitrarly to 64
1897    static constexpr int          MAX_POOL_SIZE{ 64 };
1898    std::vector<ggml_sycl_buffer> buffer_pool = std::vector<ggml_sycl_buffer>(MAX_POOL_SIZE);
1899    size_t                        pool_size   = 0;
1900
1901    explicit ggml_sycl_pool_host(queue_ptr qptr_, int device_) : qptr(qptr_), device(device_) {}
1902
1903    ~ggml_sycl_pool_host() {
1904        for (int i = 0; i < MAX_POOL_SIZE; ++i) {
1905            ggml_sycl_buffer & b = buffer_pool[i];
1906            if (b.ptr != nullptr) {
1907                SYCL_CHECK(CHECK_TRY_ERROR(sycl::free(b.ptr, *qptr)));
1908                b.ptr = nullptr;
1909                pool_size -= b.size;
1910                b.size = 0;
1911            }
1912        }
1913        counter = 0;
1914    }
1915
1916    void * alloc(size_t size, size_t * actual_size) override {
1917        if (counter == MAX_POOL_SIZE) {
1918            ggml_sycl_buffer b               = buffer_pool[0];
1919            void *           ptr             = b.ptr;
1920            *actual_size                     = b.size;
1921            counter                          = 1;
1922            return ptr;
1923        }
1924        ggml_sycl_buffer & b = buffer_pool[counter];
1925
1926        if (b.ptr == nullptr) {
1927            void * ptr;
1928
1929            SYCL_CHECK(CHECK_TRY_ERROR(ptr = (void *) sycl::malloc_host(size, *qptr)));
1930            if (!ptr) {
1931                GGML_LOG_ERROR("%s: can't allocate %zu Bytes of memory on host\n", __func__, size);
1932                return nullptr;
1933            }
1934            pool_size += size;
1935            *actual_size = size;
1936            counter      = counter + 1;
1937            return ptr;
1938        } else {
1939            ++counter;
1940            b.size = size;
1941            return b.ptr;
1942        }
1943    }
1944
1945    void free(void * ptr, size_t size) override {
1946        // if the pool is not completed add the pointer to it in place of the first nullptr found.
1947        // Otherwise do nothing, pointers will be freed once the pool is deallocated.
1948        for (int i = 0; i < MAX_POOL_SIZE; ++i) {
1949            ggml_sycl_buffer & b = buffer_pool[i];
1950            if (b.ptr == nullptr) {
1951                b.ptr  = ptr;
1952                b.size = size;
1953                return;
1954            }
1955        }
1956    }
1957};
1958
1959std::unique_ptr<ggml_sycl_pool> ggml_backend_sycl_context::new_pool_for_host(queue_ptr qptr, int device) {
1960    // return pool for the host to speed up memory management
1961    return std::unique_ptr<ggml_sycl_pool>(new ggml_sycl_pool_host(qptr, device));
1962}
1963
1964std::unique_ptr<ggml_sycl_pool> ggml_backend_sycl_context::new_pool_for_device(queue_ptr qptr, int device) {
1965#if defined(GGML_SYCL_SUPPORT_VMM)
1966    if (g_ggml_sycl_enable_vmm && ggml_sycl_info().devices[device].vmm) {
1967        return std::unique_ptr<ggml_sycl_pool>(new ggml_sycl_pool_vmm(qptr, device));
1968    }
1969#endif // defined(GGML_SYCL_SUPPORT_VMM)
1970    return std::unique_ptr<ggml_sycl_pool>(new ggml_sycl_pool_leg(qptr, device));
1971}
1972
1973
1974std::unique_ptr<ggml_sycl_fattn_kv_buffers> ggml_backend_sycl_context::new_fattn_kv_buffers(queue_ptr qptr, int device) {
1975    return std::unique_ptr<ggml_sycl_fattn_kv_buffers>(new ggml_sycl_fattn_kv_buffers(qptr, device));
1976}
1977
1978/// kernels
1979typedef void (*ggml_sycl_op_mul_mat_t)(
1980    ggml_backend_sycl_context & ctx,
1981    const ggml_tensor *src0, const ggml_tensor *src1, ggml_tensor *dst,
1982    const char *src0_dd_i, const float *src1_ddf_i, const char *src1_ddq_i,
1983    float *dst_dd_i, const int64_t row_low, const int64_t row_high,
1984    const int64_t src1_ncols, const int64_t src1_padded_row_size,
1985    const queue_ptr &stream);
1986
1987
1988
1989static void mul_mat_p021_f16_f32(
1990    const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst,
1991    const int ncols_x, const int nrows_x, const int nchannels_x, const int nchannels_y,
1992    const sycl::nd_item<3> &item_ct1) {
1993
1994    const sycl::half *x = (const sycl::half *)vx;
1995
1996    const int row_x = item_ct1.get_local_range(1) * item_ct1.get_group(1) +
1997                      item_ct1.get_local_id(1);
1998    const int channel = item_ct1.get_local_range(0) * item_ct1.get_group(0) +
1999                        item_ct1.get_local_id(0);
2000    const int channel_x = channel / (nchannels_y / nchannels_x);
2001
2002    const int nrows_y = ncols_x;
2003    const int nrows_dst = nrows_x;
2004    const int row_dst = row_x;
2005
2006    float tmp = 0.0f;
2007
2008    for (int col_x0 = 0; col_x0 < ncols_x;
2009         col_x0 += item_ct1.get_local_range(2)) {
2010        const int col_x = col_x0 + item_ct1.get_local_id(2);
2011
2012        if (col_x >= ncols_x) {
2013            break;
2014        }
2015
2016        // x is transposed and permuted
2017        const int ix = row_x*nchannels_x*ncols_x + channel_x*ncols_x + col_x;
2018        const float xi =
2019            sycl::vec<sycl::half, 1>(x[ix])
2020                .convert<float, sycl::rounding_mode::automatic>()[0];
2021
2022        const int row_y = col_x;
2023
2024
2025        // y is not transposed but permuted
2026        const int iy = channel*nrows_y + row_y;
2027
2028        tmp += xi * y[iy];
2029    }
2030
2031    // dst is not transposed and not permuted
2032    const int idst = channel*nrows_dst + row_dst;
2033
2034    // sum up partial sums and write back result
2035#pragma unroll
2036    for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
2037        tmp +=
2038            dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), tmp, mask);
2039    }
2040
2041    if (item_ct1.get_local_id(2) == 0) {
2042        dst[idst] = tmp;
2043    }
2044}
2045
2046static void mul_mat_vec_nc_f16_f32( // nc == non-contiguous
2047    const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst, const int ncols_x, const int nrows_x,
2048    const int row_stride_x, const int channel_stride_x,const int channel_stride_y, const int channel_x_divisor,
2049    const sycl::nd_item<3> &item_ct1) {
2050
2051    const sycl::half *x = (const sycl::half *)vx;
2052
2053    const int row_x = item_ct1.get_local_range(1) * item_ct1.get_group(1) +
2054                      item_ct1.get_local_id(1);
2055    const int channel = item_ct1.get_local_range(0) * item_ct1.get_group(0) +
2056                        item_ct1.get_local_id(0);
2057    const int channel_x = channel / channel_x_divisor;
2058
2059    const int nrows_dst = nrows_x;
2060    const int row_dst   = row_x;
2061
2062    const int idst = channel*nrows_dst + row_dst;
2063
2064    float tmp = 0.0f;
2065
2066    for (int col_x0 = 0; col_x0 < ncols_x;
2067         col_x0 += item_ct1.get_local_range(2)) {
2068        const int col_x = col_x0 + item_ct1.get_local_id(2);
2069
2070        if (col_x >= ncols_x) {
2071            break;
2072        }
2073
2074        const int row_y = col_x;
2075
2076        const int ix = channel_x*channel_stride_x + row_x*row_stride_x + col_x;
2077        const int iy = channel * channel_stride_y + row_y;
2078
2079        const float xi =
2080            sycl::vec<sycl::half, 1>(x[ix])
2081                .convert<float, sycl::rounding_mode::automatic>()[0];
2082
2083        tmp += xi * y[iy];
2084    }
2085
2086    // sum up partial sums and write back result
2087#pragma unroll
2088    for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
2089        tmp +=
2090            dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), tmp, mask);
2091    }
2092
2093    if (item_ct1.get_local_id(2) == 0) {
2094        dst[idst] = tmp;
2095    }
2096}
2097
2098static void k_sum_rows_f32(const float * x, float * dst, const int ncols,
2099                           const sycl::nd_item<3> &item_ct1) {
2100    const int row = item_ct1.get_group(1);
2101    const int col = item_ct1.get_local_id(2);
2102
2103    float sum = 0.0f;
2104    for (int i = col; i < ncols; i += item_ct1.get_local_range(2)) {
2105        sum += x[row * ncols + i];
2106    }
2107
2108    sum = warp_reduce_sum(sum, item_ct1);
2109
2110    if (col == 0) {
2111        dst[row] = sum;
2112    }
2113}
2114
2115
2116template<typename T>
2117static inline void ggml_sycl_swap(T & a, T & b) {
2118    T tmp = a;
2119    a = b;
2120    b = tmp;
2121}
2122
2123template <ggml_sort_order order>
2124__dpct_inline__ static void
2125k_argsort_f32_i32(const float *x, int *dst, const int ncols, int ncols_pad,
2126                  const int tasks_per_thread, const sycl::nd_item<3> &item_ct1,
2127                  uint8_t *dpct_local) {
2128    // bitonic sort
2129    int col_index =  item_ct1.get_local_id(2);
2130    int row = item_ct1.get_group(1);
2131
2132    for (int i = 0; i < tasks_per_thread; i++) {
2133        int col = col_index * tasks_per_thread + i;
2134        if (col >= ncols_pad) {
2135            return;
2136        }
2137    }
2138
2139    const float * x_row = x + row * ncols;
2140    auto dst_row = (int *)dpct_local;
2141
2142    // initialize indices
2143    for (int i=0;i<tasks_per_thread;i++){
2144        int col = col_index*tasks_per_thread+i;
2145        dst_row[col] = col;
2146    }
2147
2148    item_ct1.barrier(sycl::access::fence_space::local_space);
2149
2150    for (int k = 2; k <= ncols_pad; k *= 2) {
2151        for (int j = k / 2; j > 0; j /= 2) {
2152            for (int i = 0; i < tasks_per_thread; i++) {
2153                int col = col_index * tasks_per_thread + i;
2154                int ixj = col ^ j;
2155                if (ixj > col) {
2156                    if ((col & k) == 0) {
2157                        if (dst_row[col] >= ncols ||
2158                            (dst_row[ixj] < ncols &&
2159                             (order == GGML_SORT_ORDER_ASC
2160                                  ? x_row[dst_row[col]] > x_row[dst_row[ixj]]
2161                                  : x_row[dst_row[col]] <
2162                                        x_row[dst_row[ixj]]))) {
2163                            ggml_sycl_swap(dst_row[col], dst_row[ixj]);
2164                        }
2165                    } else {
2166                        if (dst_row[ixj] >= ncols ||
2167                            (dst_row[col] < ncols &&
2168                             (order == GGML_SORT_ORDER_ASC
2169                                  ? x_row[dst_row[col]] < x_row[dst_row[ixj]]
2170                                  : x_row[dst_row[col]] >
2171                                        x_row[dst_row[ixj]]))) {
2172                            ggml_sycl_swap(dst_row[col], dst_row[ixj]);
2173                        }
2174                    }
2175                }
2176                item_ct1.barrier(sycl::access::fence_space::local_space);
2177            }
2178        }
2179    }
2180
2181    // copy the result to dst without the padding
2182    for (int i = 0; i < tasks_per_thread; i++) {
2183        int col = col_index * tasks_per_thread + i;
2184        if (col < ncols) {
2185            dst[row * ncols + col] = dst_row[col];
2186        }
2187    }
2188}
2189
2190static void diag_mask_inf_f32(const float * x, float * dst, const int ncols, const int rows_per_channel, const int n_past,
2191                              const sycl::nd_item<3> &item_ct1) {
2192    const int col = item_ct1.get_local_range(1) * item_ct1.get_group(1) +
2193                    item_ct1.get_local_id(1);
2194    const int row = item_ct1.get_local_range(2) * item_ct1.get_group(2) +
2195                    item_ct1.get_local_id(2);
2196
2197    if (col >= ncols) {
2198        return;
2199    }
2200
2201    const int i = row*ncols + col;
2202    //dst[i] = col > (n_past + row % rows_per_channel) ? -INFINITY : x[i];
2203    //dst[i] = x[i] - (col > n_past + row % rows_per_channel) * INT_MAX; // equivalent within rounding error but slightly faster on GPU
2204    dst[i] = x[i] - (col > n_past + row % rows_per_channel) * FLT_MAX;
2205}
2206
2207static void scale_f32(const float * x, float * dst, const float scale, const float bias, const int k,
2208                      const sycl::nd_item<3> &item_ct1) {
2209    const int i = item_ct1.get_local_range(2) * item_ct1.get_group(2) +
2210                  item_ct1.get_local_id(2);
2211
2212    if (i >= k) {
2213        return;
2214    }
2215
2216    dst[i] = scale * x[i] + bias;
2217}
2218
2219
2220static void ggml_mul_mat_p021_f16_f32_sycl(const void *vx, const float *y,
2221                                           float *dst, const int ncols_x,
2222                                           const int nrows_x,
2223                                           const int nchannels_x,
2224                                           const int nchannels_y,
2225                                           queue_ptr stream) {
2226
2227    const sycl::range<3> block_nums(nchannels_y, nrows_x, 1);
2228    const sycl::range<3> block_dims(1, 1, WARP_SIZE);
2229    {
2230        dpct::has_capability_or_fail(stream->get_device(),
2231                                     {sycl::aspect::fp16});
2232
2233        stream->parallel_for(
2234            sycl::nd_range<3>(block_nums * block_dims, block_dims),
2235            [=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
2236                mul_mat_p021_f16_f32(vx, y, dst, ncols_x, nrows_x, nchannels_x,
2237                                     nchannels_y, item_ct1);
2238            });
2239    }
2240}
2241
2242static void ggml_mul_mat_vec_nc_f16_f32_sycl(
2243    const void *vx, const float *y, float *dst, const int ncols_x,
2244    const int nrows_x, const int row_stride_x, const int nchannels_x,
2245    const int nchannels_y, const int channel_stride_x, const int channel_stride_y, queue_ptr stream) {
2246
2247    const sycl::range<3> block_nums(nchannels_y, nrows_x, 1);
2248    const sycl::range<3> block_dims(1, 1, WARP_SIZE);
2249    {
2250        dpct::has_capability_or_fail(stream->get_device(),
2251                                     {sycl::aspect::fp16});
2252
2253        stream->parallel_for(
2254            sycl::nd_range<3>(block_nums * block_dims, block_dims),
2255            [=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
2256                mul_mat_vec_nc_f16_f32(vx, y, dst, ncols_x, nrows_x,
2257                                       row_stride_x, channel_stride_x, channel_stride_y,
2258                                       nchannels_y / nchannels_x, item_ct1);
2259            });
2260    }
2261}
2262
2263
2264
2265static void scale_f32_sycl(const float *x, float *dst, const float scale, const float bias,
2266                           const int k, queue_ptr stream) {
2267    const int num_blocks = (k + SYCL_SCALE_BLOCK_SIZE - 1) / SYCL_SCALE_BLOCK_SIZE;
2268    stream->parallel_for(
2269        sycl::nd_range<3>(sycl::range<3>(1, 1, num_blocks) *
2270                              sycl::range<3>(1, 1, SYCL_SCALE_BLOCK_SIZE),
2271                          sycl::range<3>(1, 1, SYCL_SCALE_BLOCK_SIZE)),
2272        [=](sycl::nd_item<3> item_ct1) {
2273            scale_f32(x, dst, scale, bias, k, item_ct1);
2274        });
2275}
2276
2277
2278static void sum_rows_f32_sycl(const float *x, float *dst, const int ncols,
2279                              const int nrows, queue_ptr stream) {
2280    const sycl::range<3> block_dims(1, 1, WARP_SIZE);
2281    const sycl::range<3> block_nums(1, nrows, 1);
2282    stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims),
2283                         [=](sycl::nd_item<3> item_ct1)
2284                             [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
2285                                 k_sum_rows_f32(x, dst, ncols, item_ct1);
2286                             });
2287}
2288
2289static int next_power_of_2(int x) {
2290    int n = 1;
2291    while (n < x) {
2292        n *= 2;
2293    }
2294    return n;
2295}
2296
2297static void init_argsort_indices_padded(
2298        int * idx,
2299        const int nrows,
2300        const int ncols_pad,
2301        const sycl::nd_item<1> & item_ct1) {
2302    const size_t gid = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0);
2303    const size_t total = (size_t) nrows * (size_t) ncols_pad;
2304
2305    if (gid >= total) {
2306        return;
2307    }
2308
2309    idx[gid] = (int) (gid % (size_t) ncols_pad);
2310}
2311
2312template <ggml_sort_order order>
2313static void argsort_f32_i32_global_pass(const float *            x,
2314                                        int *                    idx,
2315                                        const int                ncols,
2316                                        const int                nrows,
2317                                        const int                ncols_pad,
2318                                        const int                j,
2319                                        const int                k,
2320                                        const sycl::nd_item<1> & item_ct1) {
2321    const size_t gid   = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0);
2322    const size_t total = (size_t) nrows * (size_t) ncols_pad;
2323
2324    if (gid >= total) {
2325        return;
2326    }
2327
2328    const int row = (int) (gid / (size_t) ncols_pad);
2329    const int col = (int) (gid % (size_t) ncols_pad);
2330    const int ixj = col ^ j;
2331
2332    if (ixj <= col || ixj >= ncols_pad) {
2333        return;
2334    }
2335
2336    const size_t base  = (size_t) row * (size_t) ncols_pad;
2337    const size_t pos_a = base + (size_t) col;
2338    const size_t pos_b = base + (size_t) ixj;
2339
2340    const int a = idx[pos_a];
2341    const int b = idx[pos_b];
2342
2343    bool do_swap = false;
2344
2345    if ((col & k) == 0) {
2346        if (a >= ncols ||
2347            (b < ncols &&
2348             (order == GGML_SORT_ORDER_ASC ?
2349                  x[(size_t) row * (size_t) ncols + (size_t) a] > x[(size_t) row * (size_t) ncols + (size_t) b] :
2350                  x[(size_t) row * (size_t) ncols + (size_t) a] < x[(size_t) row * (size_t) ncols + (size_t) b]))) {
2351            do_swap = true;
2352        }
2353    } else {
2354        if (b >= ncols ||
2355            (a < ncols &&
2356             (order == GGML_SORT_ORDER_ASC ?
2357                  x[(size_t) row * (size_t) ncols + (size_t) a] < x[(size_t) row * (size_t) ncols + (size_t) b] :
2358                  x[(size_t) row * (size_t) ncols + (size_t) a] > x[(size_t) row * (size_t) ncols + (size_t) b]))) {
2359            do_swap = true;
2360        }
2361    }
2362
2363    if (do_swap) {
2364        idx[pos_a] = b;
2365        idx[pos_b] = a;
2366    }
2367}
2368
2369static void copy_argsort_indices_unpadded(const int *              idx_padded,
2370                                          int *                    dst,
2371                                          const int                nrows,
2372                                          const int                ncols,
2373                                          const int                ncols_pad,
2374                                          const sycl::nd_item<1> & item_ct1) {
2375    const size_t gid   = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0);
2376    const size_t total = (size_t) nrows * (size_t) ncols;
2377
2378    if (gid >= total) {
2379        return;
2380    }
2381
2382    const int row = (int) (gid / (size_t) ncols);
2383    const int col = (int) (gid % (size_t) ncols);
2384
2385    dst[(size_t) row * (size_t) ncols + (size_t) col] = idx_padded[(size_t) row * (size_t) ncols_pad + (size_t) col];
2386}
2387
2388static void argsort_f32_i32_sycl(const float *x, int *dst, const int ncols,
2389                                 const int nrows, ggml_sort_order order,
2390                                 queue_ptr stream, int device, ggml_sycl_pool & pool) {
2391    // bitonic sort requires ncols to be power of 2
2392    const int ncols_pad = next_power_of_2(ncols);
2393    const size_t shared_mem = (size_t) ncols_pad * sizeof(int);
2394    const size_t smpbo = ggml_sycl_info().devices[device].smpbo;
2395
2396    if (shared_mem > smpbo) {
2397        ggml_sycl_pool_alloc<int> idx_padded_alloc(pool, (size_t) nrows * (size_t) ncols_pad);
2398        int *                     idx_padded = idx_padded_alloc.get();
2399
2400        constexpr size_t block_size     = 256;
2401        const size_t     total_padded   = (size_t) nrows * (size_t) ncols_pad;
2402        const size_t     nblocks_padded = (total_padded + block_size - 1) / block_size;
2403
2404        stream->parallel_for(
2405            sycl::nd_range<1>(sycl::range<1>(nblocks_padded * block_size), sycl::range<1>(block_size)),
2406            [=](sycl::nd_item<1> item_ct1) { init_argsort_indices_padded(idx_padded, nrows, ncols_pad, item_ct1); });
2407
2408        for (int k = 2; k <= ncols_pad; k *= 2) {
2409            for (int j = k / 2; j > 0; j /= 2) {
2410                if (order == GGML_SORT_ORDER_ASC) {
2411                    stream->parallel_for(
2412                        sycl::nd_range<1>(sycl::range<1>(nblocks_padded * block_size), sycl::range<1>(block_size)),
2413                        [=](sycl::nd_item<1> item_ct1) {
2414                            argsort_f32_i32_global_pass<GGML_SORT_ORDER_ASC>(x, idx_padded, ncols, nrows, ncols_pad, j,
2415                                                                             k, item_ct1);
2416                        });
2417                } else if (order == GGML_SORT_ORDER_DESC) {
2418                    stream->parallel_for(
2419                        sycl::nd_range<1>(sycl::range<1>(nblocks_padded * block_size), sycl::range<1>(block_size)),
2420                        [=](sycl::nd_item<1> item_ct1) {
2421                            argsort_f32_i32_global_pass<GGML_SORT_ORDER_DESC>(x, idx_padded, ncols, nrows, ncols_pad, j,
2422                                                                              k, item_ct1);
2423                        });
2424                } else {
2425                    GGML_ABORT("invalid sort order");
2426                }
2427            }
2428        }
2429
2430        const size_t total   = (size_t) nrows * (size_t) ncols;
2431        const size_t nblocks = (total + block_size - 1) / block_size;
2432        stream->parallel_for(sycl::nd_range<1>(sycl::range<1>(nblocks * block_size), sycl::range<1>(block_size)),
2433                             [=](sycl::nd_item<1> item_ct1) {
2434                                 copy_argsort_indices_unpadded(idx_padded, dst, nrows, ncols, ncols_pad, item_ct1);
2435                             });
2436
2437        return;
2438    }
2439
2440    int nth = 1;
2441    int max_block_size = ggml_sycl_info().max_work_group_sizes[device];
2442    while (nth < ncols_pad && nth < max_block_size)
2443        nth *= 2;
2444    if (nth > max_block_size)
2445        nth = max_block_size;
2446
2447    const int tasks_per_thread = ncols_pad / nth;
2448
2449    const sycl::range<3> block_dims(1, 1, nth);
2450    const sycl::range<3> block_nums(1, nrows, 1);
2451
2452    if (order == GGML_SORT_ORDER_ASC) {
2453        stream->submit([&](sycl::handler &cgh) {
2454            sycl::local_accessor<uint8_t, 1> dpct_local_acc_ct1(
2455                sycl::range<1>(shared_mem), cgh);
2456
2457            cgh.parallel_for(
2458                sycl::nd_range<3>(block_nums * block_dims, block_dims),
2459                [=](sycl::nd_item<3> item_ct1) {
2460                    k_argsort_f32_i32<GGML_SORT_ORDER_ASC>(
2461                        x, dst, ncols, ncols_pad, tasks_per_thread, item_ct1,
2462                        dpct_local_acc_ct1
2463                            .get_multi_ptr<sycl::access::decorated::no>()
2464                            .get());
2465                });
2466        });
2467    } else if (order == GGML_SORT_ORDER_DESC) {
2468        stream->submit([&](sycl::handler &cgh) {
2469            sycl::local_accessor<uint8_t, 1> dpct_local_acc_ct1(
2470                sycl::range<1>(shared_mem), cgh);
2471
2472            cgh.parallel_for(
2473                sycl::nd_range<3>(block_nums * block_dims, block_dims),
2474                [=](sycl::nd_item<3> item_ct1) {
2475                    k_argsort_f32_i32<GGML_SORT_ORDER_DESC>(
2476                        x, dst, ncols, ncols_pad, tasks_per_thread, item_ct1,
2477                        dpct_local_acc_ct1
2478                            .get_multi_ptr<sycl::access::decorated::no>()
2479                            .get());
2480                });
2481        });
2482    } else {
2483        GGML_ABORT("fatal error");
2484    }
2485}
2486
2487// Scan and block merge, shared by every launch shape below so a partitioned row uses the
2488// same insertion order as an unpartitioned one.
2489//
2490// src_map  != nullptr: report src_map[col] instead of col, so a merge pass can carry the
2491//                      original column index through.
2492// out_vals != nullptr: also emit the k winning values, for a later merge pass.
2493// swap01:              emit in the output order the single-pass path uses.
2494static void top_k_scan_merge_f32(
2495    const float *   src_vals,
2496    const int32_t * src_map,
2497    const int       begin,
2498    const int       end,
2499    const int       k,
2500    const int       block_size,
2501    float *         shared_vals,
2502    int *           shared_idx,
2503    float *         out_vals,
2504    int32_t *       out_idx,
2505    const bool      swap01,
2506    const sycl::nd_item<1> & item_ct1
2507) {
2508    const int tid = item_ct1.get_local_id(0);
2509
2510    // The running top-k lives in SLM (shared local memory) rather than a private array:
2511    // an array indexed by a runtime position cannot be register-allocated, so a private
2512    // one lands in scratch, i.e. device memory, and insertion is this kernel's dominant
2513    // cost.
2514    //
2515    // Lane-strided (lv[i * block_size]) rather than lane-blocked (lv[i]) so a given i is
2516    // contiguous across lanes; a k-strided layout would put every lane of a shift step in
2517    // the same SLM bank.
2518    float * lv = shared_vals + tid;
2519    int * li = shared_idx + tid;
2520
2521    for (int i = 0; i < k; i++) {
2522        lv[i * block_size] = -FLT_MAX;
2523        li[i * block_size] = -1;
2524    }
2525
2526    // The k-th best, cached in a register. The reject test is taken for the large
2527    // majority of elements scanned, and in that case touches no memory.
2528    float kth = -FLT_MAX;
2529
2530    for (int col = begin + tid; col < end; col += block_size) {
2531        float val = src_vals[col];
2532
2533        if (val > kth) {
2534            int pos = k - 1;
2535            while (pos > 0 && val > lv[(pos - 1) * block_size]) {
2536                pos--;
2537            }
2538
2539            for (int i = k - 1; i > pos; i--) {
2540                lv[i * block_size] = lv[(i - 1) * block_size];
2541                li[i * block_size] = li[(i - 1) * block_size];
2542            }
2543            lv[pos * block_size] = val;
2544            li[pos * block_size] = src_map ? src_map[col] : col;
2545
2546            kth = lv[(k - 1) * block_size];
2547        }
2548    }
2549
2550    item_ct1.barrier(sycl::access::fence_space::local_space);
2551
2552    if (tid != 0) {
2553        return;
2554    }
2555
2556    // Same treatment for the merge accumulator, past the per-lane region.
2557    float * fv = shared_vals + (size_t) k * block_size;
2558    int * fi = shared_idx + (size_t) k * block_size;
2559
2560    for (int i = 0; i < k; i++) {
2561        fv[i] = -FLT_MAX;
2562        fi[i] = -1;
2563    }
2564
2565    float fkth = -FLT_MAX;
2566
2567    // Candidates are visited in the same (t, i) order as before, so tie-breaking is
2568    // unchanged.
2569    for (int t = 0; t < block_size; t++) {
2570        for (int i = 0; i < k; i++) {
2571            float val = shared_vals[i * block_size + t];
2572
2573            if (val <= fkth) {
2574                // Lane t's list is sorted descending, so once one of its entries loses
2575                // to the k-th best, every later entry loses too. fkth only rises, so
2576                // that stays true for the rest of the merge. This turns the merge from
2577                // block_size*k steps into roughly block_size plus the candidates
2578                // accepted.
2579                break;
2580            }
2581
2582            int idx = shared_idx[i * block_size + t];
2583
2584            int pos = k - 1;
2585            while (pos > 0 && val > fv[pos - 1]) {
2586                pos--;
2587            }
2588
2589            for (int j = k - 1; j > pos; j--) {
2590                fv[j] = fv[j - 1];
2591                fi[j] = fi[j - 1];
2592            }
2593            fv[pos] = val;
2594            fi[pos] = idx;
2595
2596            fkth = fv[k - 1];
2597        }
2598    }
2599
2600    if (out_vals) {
2601        for (int i = 0; i < k; i++) {
2602            out_vals[i] = fv[i];
2603        }
2604    }
2605
2606    for (int i = 0; i < k; i++) {
2607        out_idx[i] = fi[i];
2608    }
2609
2610    if (swap01 && k > 1) {
2611        int32_t temp = out_idx[0];
2612        out_idx[0] = out_idx[1];
2613        out_idx[1] = temp;
2614    }
2615}
2616
2617static void top_k_f32_sycl(
2618    ggml_backend_sycl_context & ctx,
2619    const float * src,
2620    int32_t * dst_indices,
2621    const int64_t ncols,
2622    const int64_t nrows,
2623    const int k,
2624    dpct::queue_ptr main_stream
2625) {
2626    // A row is scanned by exactly one work-group, so a vocabulary-sized row leaves the
2627    // rest of the device idle. What the scan is short of is memory requests in flight,
2628    // not bandwidth or per-request latency, so lanes in flight is the lever: split the
2629    // row across independent work-groups, have each emit its partition's top-k, and
2630    // merge those nsplit*k candidates in a second launch.
2631    //
2632    // split_block trades parallelism against SLM residency. Its cost is
2633    // (split_block + 1) * k * 8 bytes of SLM per group, so at the k <= 32 ceiling 128
2634    // lanes need about 33 KB, which leaves a single resident group per Xe-core. Revisit
2635    // if the supported k ever grows.
2636    constexpr int split_block = 128;
2637    constexpr int max_splits  = 128;
2638    constexpr int min_cols    = 8192;
2639
2640    int nsplit = 1;
2641    if (ncols >= min_cols) {
2642        // A partition is then always >= split_block = 128 columns, hence always more than
2643        // the k <= 32 ceiling, so no pass is ever padded with -FLT_MAX sentinels.
2644        const int64_t want = ncols / split_block;
2645        nsplit = (int) (want > max_splits ? max_splits : want);
2646    }
2647
2648    if (nsplit > 1) {
2649        const int nchunk = (int) ((ncols + nsplit - 1) / nsplit);
2650        const size_t ncand = (size_t) nrows * nsplit * k;
2651
2652        ggml_sycl_pool_alloc<float>   part_vals(ctx.pool(), ncand);
2653        ggml_sycl_pool_alloc<int32_t> part_idx(ctx.pool(), ncand);
2654
2655        float *   pv = part_vals.get();
2656        int32_t * pi = part_idx.get();
2657
2658        const sycl::range<1> block_dims(split_block);
2659
2660        main_stream->submit([&](sycl::handler &cgh) {
2661            sycl::local_accessor<float, 1> shared_vals(sycl::range<1>((split_block + 1) * k), cgh);
2662            sycl::local_accessor<int, 1> shared_idx(sycl::range<1>((split_block + 1) * k), cgh);
2663
2664            cgh.parallel_for(
2665                sycl::nd_range<1>(sycl::range<1>(nrows * nsplit) * block_dims, block_dims),
2666                [=](sycl::nd_item<1> item_ct1) {
2667                    const int grp = item_ct1.get_group(0);
2668                    const int row = grp / nsplit;
2669                    const int part = grp % nsplit;
2670
2671                    const int begin = part * nchunk;
2672                    int end = begin + nchunk;
2673                    if (end > (int) ncols) {
2674                        end = (int) ncols;
2675                    }
2676
2677                    top_k_scan_merge_f32(
2678                        src + (int64_t) row * ncols, nullptr, begin, end, k, split_block,
2679                        shared_vals.get_multi_ptr<sycl::access::decorated::no>().get(),
2680                        shared_idx.get_multi_ptr<sycl::access::decorated::no>().get(),
2681                        pv + (size_t) grp * k, pi + (size_t) grp * k, false, item_ct1);
2682                });
2683        });
2684
2685        main_stream->submit([&](sycl::handler &cgh) {
2686            sycl::local_accessor<float, 1> shared_vals(sycl::range<1>((split_block + 1) * k), cgh);
2687            sycl::local_accessor<int, 1> shared_idx(sycl::range<1>((split_block + 1) * k), cgh);
2688
2689            cgh.parallel_for(
2690                sycl::nd_range<1>(sycl::range<1>(nrows) * block_dims, block_dims),
2691                [=](sycl::nd_item<1> item_ct1) {
2692                    const int row = item_ct1.get_group(0);
2693                    const size_t off = (size_t) row * nsplit * k;
2694
2695                    top_k_scan_merge_f32(
2696                        pv + off, pi + off, 0, nsplit * k, k, split_block,
2697                        shared_vals.get_multi_ptr<sycl::access::decorated::no>().get(),
2698                        shared_idx.get_multi_ptr<sycl::access::decorated::no>().get(),
2699                        nullptr, dst_indices + (int64_t) row * k, true, item_ct1);
2700                });
2701        });
2702
2703        return;
2704    }
2705
2706    const int block_size = 128;
2707
2708    const sycl::range<1> block_dims(block_size);
2709    const sycl::range<1> grid_dims(nrows);
2710
2711    main_stream->submit([&](sycl::handler &cgh) {
2712        sycl::local_accessor<float, 1> shared_vals(sycl::range<1>((block_size + 1) * k), cgh);
2713        sycl::local_accessor<int, 1> shared_idx(sycl::range<1>((block_size + 1) * k), cgh);
2714
2715        cgh.parallel_for(
2716            sycl::nd_range<1>(grid_dims * block_dims, block_dims),
2717            [=](sycl::nd_item<1> item_ct1) {
2718                const int row = item_ct1.get_group(0);
2719
2720                if (row >= nrows) return;
2721
2722                top_k_scan_merge_f32(
2723                    src + (int64_t) row * ncols, nullptr, 0, (int) ncols, k, block_size,
2724                    shared_vals.get_multi_ptr<sycl::access::decorated::no>().get(),
2725                    shared_idx.get_multi_ptr<sycl::access::decorated::no>().get(),
2726                    nullptr, dst_indices + (int64_t) row * k, true, item_ct1);
2727            });
2728    });
2729}
2730
2731static void argmax_f32_i32_sycl(const float *x, int *dst, const int ncols,
2732                               const int nrows, queue_ptr stream) {
2733    const sycl::range<3> block_dims(1, 1, SYCL_ARGMAX_BLOCK_SIZE);
2734    const sycl::range<3> block_nums(1, nrows, 1);
2735    const size_t shared_mem = 256 * sizeof(float);
2736
2737    stream->submit([&](sycl::handler &cgh) {
2738        sycl::local_accessor<float, 1> shared_data(
2739            sycl::range<1>(shared_mem/sizeof(float)), cgh);
2740        sycl::local_accessor<int, 1> shared_indices(
2741            sycl::range<1>(shared_mem/sizeof(float)), cgh);
2742
2743        cgh.parallel_for(
2744            sycl::nd_range<3>(block_nums * block_dims, block_dims),
2745            [=](sycl::nd_item<3> item_ct1) {
2746                const int tid = item_ct1.get_local_id(2);
2747                const int row = item_ct1.get_global_id(1);
2748
2749                float max_val = -INFINITY;
2750                int max_idx = -1;
2751
2752                for (int col = tid; col < ncols; col += 256) {
2753                    float val = x[row * ncols + col];
2754                    if (val > max_val) {
2755                        max_val = val;
2756                        max_idx = col;
2757                    }
2758                }
2759
2760                shared_data[tid] = max_val;
2761                shared_indices[tid] = max_idx;
2762                item_ct1.barrier(sycl::access::fence_space::local_space);
2763
2764                for (int stride = 256/2; stride > 0; stride >>= 1) {
2765                    if (tid < stride) {
2766                        float val1 = shared_data[tid];
2767                        float val2 = shared_data[tid + stride];
2768                        if (val2 > val1) {
2769                            shared_data[tid] = val2;
2770                            shared_indices[tid] = shared_indices[tid + stride];
2771                        }
2772                    }
2773                    item_ct1.barrier(sycl::access::fence_space::local_space);
2774                }
2775
2776
2777                if (tid == 0) {
2778                    dst[row] = shared_indices[0];
2779                }
2780            });
2781    });
2782}
2783static void diag_mask_inf_f32_sycl(const float *x, float *dst,
2784                                   const int ncols_x, const int nrows_x,
2785                                   const int rows_per_channel, const int n_past,
2786                                   queue_ptr stream) {
2787    const sycl::range<3> block_dims(1, SYCL_DIAG_MASK_INF_BLOCK_SIZE, 1);
2788    const int block_num_x = (ncols_x + SYCL_DIAG_MASK_INF_BLOCK_SIZE - 1) / SYCL_DIAG_MASK_INF_BLOCK_SIZE;
2789    const sycl::range<3> block_nums(1, block_num_x, nrows_x);
2790    stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims),
2791                         [=](sycl::nd_item<3> item_ct1) {
2792                             diag_mask_inf_f32(x, dst, ncols_x,
2793                                               rows_per_channel, n_past,
2794                                               item_ct1);
2795                         });
2796}
2797
2798static dpct::err0 ggml_sycl_cpy_tensor_2d(void *dst,
2799                                          const struct ggml_tensor *src,
2800                                          int64_t i3, int64_t i2,
2801                                          int64_t i1_low, int64_t i1_high,
2802                                          queue_ptr stream) try {
2803
2804    dpct::memcpy_direction kind;
2805    char * src_ptr;
2806    if (ggml_backend_buffer_is_host(src->buffer)) {
2807        kind = dpct::host_to_device;
2808        //GGML_SYCL_DEBUG("%s: Host buffer type src tensor\n", __func__);
2809        src_ptr = (char *) src->data;
2810        // GGML_SYCL_DEBUG("ggml_sycl_cpy_tensor_2d  GGML_BACKEND_TYPE_CPU src_ptr %p\n", src_ptr);
2811    } else if (ggml_backend_buffer_is_sycl(src->buffer)) {
2812        // If buffer is a SYCL buffer
2813        //GGML_SYCL_DEBUG("%s: SYCL buffer type src tensor\n", __func__);
2814        kind    = dpct::device_to_device;
2815        src_ptr = (char *) src->data;
2816    } else if (ggml_backend_buffer_is_sycl_split(src->buffer)) {
2817        /*
2818        If buffer is a SYCL split buffer
2819        */
2820        //GGML_SYCL_DEBUG("%s: Split buffer type src tensor\n", __func__);
2821        GGML_ASSERT(i1_low == 0 && i1_high == src->ne[1]);
2822        kind = dpct::device_to_device;
2823        ggml_tensor_extra_gpu * extra = (ggml_tensor_extra_gpu *) src->extra;
2824        int id;
2825        SYCL_CHECK(CHECK_TRY_ERROR(
2826            id = get_current_device_id()));
2827        // GGML_SYCL_DEBUG("current device index %d\n", id);
2828        src_ptr = (char *) extra->data_device[id];
2829    } else {
2830        // GGML_SYCL_DEBUG("GGML_ABORT("fatal error")\n");
2831        GGML_ABORT("fatal error");
2832    }
2833    char * dst_ptr = (char *) dst;
2834
2835    GGML_TENSOR_LOCALS_1(int64_t, ne, src, ne);
2836    GGML_TENSOR_LOCALS(int64_t, nb, src, nb);
2837    const enum ggml_type type = src->type;
2838    const int64_t ts = ggml_type_size(type);
2839    const int64_t bs = ggml_blck_size(type);
2840    int64_t i1_diff = i1_high - i1_low;
2841
2842    const char * x = src_ptr + i1_low*nb1 + i2*nb2 + i3*nb3;
2843    if (nb0 == ts && nb1 == ts*ne0/bs) {
2844        // GGML_SYCL_DEBUG("stream->memcpy: dst_ptr=%p, x=%p, size=%lu\n", dst_ptr, x, i1_diff * nb1);
2845        // return CHECK_TRY_ERROR(stream->memcpy(dst_ptr, x, i1_diff * nb1));
2846        return CHECK_TRY_ERROR(dpct::async_dpct_memcpy(dst_ptr, x, i1_diff * nb1,
2847                                    kind, *stream));
2848
2849    } else if (nb0 == ts) {
2850        return CHECK_TRY_ERROR(
2851            dpct::async_dpct_memcpy(dst_ptr, ts * ne0 / bs, x, nb1,
2852                                    ts * ne0 / bs, i1_diff, kind, *stream));
2853    } else {
2854        for (int64_t i1 = 0; i1 < i1_diff; i1++) {
2855            const void * rx = (const void *) ((const char *) x + i1*nb1);
2856            void * rd = (void *) (dst_ptr + i1*ts*ne0/bs);
2857            // pretend the row is a matrix with cols=1
2858            dpct::err0 r = CHECK_TRY_ERROR(dpct::async_dpct_memcpy(
2859                rd, ts / bs, rx, nb0, ts / bs, ne0, kind, *stream));
2860            /*
2861            DPCT1001:85: The statement could not be removed.
2862            */
2863            /*
2864            DPCT1000:86: Error handling if-stmt was detected but could not be
2865            rewritten.
2866            */
2867            if (r != 0) return r;
2868        }
2869        return 0;
2870    }
2871}
2872catch (sycl::exception const &exc) {
2873  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
2874            << ", line:" << __LINE__ << std::endl;
2875  std::exit(1);
2876}
2877
2878inline void ggml_sycl_op_mul_mat_sycl(
2879    ggml_backend_sycl_context & ctx,
2880    const ggml_tensor *src0, const ggml_tensor *src1, ggml_tensor *dst,
2881    const char *src0_dd_i, const float *src1_ddf_i, const char *src1_ddq_i,
2882    float *dst_dd_i, const int64_t row_low, const int64_t row_high,
2883    const int64_t src1_ncols, const int64_t src1_padded_row_size,
2884    const queue_ptr &stream) try {
2885
2886    GGML_ASSERT(src0_dd_i  != nullptr);
2887    GGML_ASSERT(src1_ddf_i != nullptr);
2888    GGML_ASSERT(dst_dd_i   != nullptr);
2889
2890    const int64_t ne00 = src0->ne[0];
2891    const int64_t ne10 = src1->ne[0];
2892    GGML_ASSERT(ne00 == ne10);
2893
2894    const int64_t row_diff = row_high - row_low;
2895
2896    int id;
2897    SYCL_CHECK(
2898        CHECK_TRY_ERROR(id = get_current_device_id()));
2899
2900    const int64_t ne0 = dst->ne[0]; // used by MKL only
2901    // the main device has a larger memory buffer to hold the results from all GPUs
2902    // ldc == nrows of the matrix that cuBLAS writes into
2903    int ldc = id == ctx.device ? ne0 : row_diff; // used by MKL only
2904
2905#ifdef GGML_SYCL_F16
2906    bool use_fp16 = true;  // TODO(Yu) SYCL capability check
2907#else
2908    bool use_fp16 = false;
2909#endif
2910
2911#if GGML_SYCL_DNNL && defined(GGML_SYCL_HAS_BF16)
2912    // Fast path for bf16 src0
2913    if (src0->type == GGML_TYPE_BF16 && g_ggml_sycl_enable_dnn && ggml_is_contiguous(src0) &&
2914        row_diff == src0->ne[1]) {
2915        using bf16_t = sycl::ext::oneapi::bfloat16;
2916        ggml_sycl_pool_alloc<bf16_t> src1_as_bf16(ctx.pool(), src1_ncols*ne10);
2917        if (src1->type != GGML_TYPE_BF16) {
2918            const to_bf16_sycl_t to_bf16_sycl = ggml_get_to_bf16_sycl(src1->type, dst);
2919            GGML_ASSERT(to_bf16_sycl != nullptr);
2920            to_bf16_sycl(src1_ddf_i, src1_as_bf16.get(), src1_ncols*ne10, stream);
2921        } else {
2922            stream->memcpy(src1_as_bf16.get(), src1_ddf_i, src1_ncols*ne10*sizeof(bf16_t));
2923        }
2924        DnnlGemmWrapper::row_gemm(ctx, row_diff, src1_ncols, ne10,
2925                                  src0_dd_i, DnnlGemmWrapper::to_dt<bf16_t>(),
2926                                  src1_as_bf16.get(), DnnlGemmWrapper::to_dt<bf16_t>(),
2927                                  dst_dd_i, DnnlGemmWrapper::to_dt<float>(), stream);
2928        GGML_UNUSED(dst);
2929        GGML_UNUSED(src1_ddq_i);
2930        GGML_UNUSED(src1_padded_row_size);
2931        return;
2932    }
2933#endif
2934
2935    if ((src0->type == GGML_TYPE_F16 || ggml_is_quantized(src0->type)) && use_fp16 && ggml_is_contiguous(src0) &&
2936        row_diff == src0->ne[1] && dst->op_params[0] == GGML_PREC_DEFAULT) {
2937        ggml_sycl_pool_alloc<sycl::half> src0_as_f16(ctx.pool());
2938        if (src0->type != GGML_TYPE_F16) {
2939            scope_op_debug_print scope_dbg_print(__func__, "/to_fp16_sycl", dst, /*num_src=*/2,
2940                                                 " : converting src0 to fp16");
2941            const to_fp16_sycl_t to_fp16_sycl = ggml_get_to_fp16_sycl(src0->type, dst);
2942            GGML_ASSERT(to_fp16_sycl != nullptr);
2943            size_t ne = row_diff*ne00;
2944            src0_as_f16.alloc(ne);
2945            to_fp16_sycl(src0_dd_i, src0_as_f16.get(), ne, stream);
2946        }
2947        const sycl::half *src0_ptr = src0->type == GGML_TYPE_F16
2948                                         ? (const sycl::half *)src0_dd_i
2949                                         : src0_as_f16.get();
2950
2951        ggml_sycl_pool_alloc<sycl::half> src1_as_f16(ctx.pool());
2952        if (src1->type != GGML_TYPE_F16) {
2953            scope_op_debug_print scope_dbg_print(__func__, "/to_fp16_sycl", dst, /*num_src=*/2,
2954                                                 " : converting src1 to fp16");
2955            const to_fp16_sycl_t to_fp16_sycl = ggml_get_to_fp16_sycl(src1->type, dst);
2956            GGML_ASSERT(to_fp16_sycl != nullptr);
2957            size_t ne = src1_ncols*ne10;
2958            src1_as_f16.alloc(ne);
2959            to_fp16_sycl(src1_ddf_i, src1_as_f16.get(), ne, stream);
2960        }
2961        const sycl::half *src1_ptr = src1->type == GGML_TYPE_F16
2962                ? (const sycl::half *)src1->data + src1_padded_row_size
2963                                         : src1_as_f16.get();
2964
2965#if GGML_SYCL_DNNL
2966        if (g_ggml_sycl_enable_dnn) {
2967                DnnlGemmWrapper::row_gemm(ctx,row_diff, src1_ncols , ne10, src0_ptr,
2968                                     DnnlGemmWrapper::to_dt<sycl::half>(), src1_ptr, DnnlGemmWrapper::to_dt<sycl::half>(),
2969                                      dst_dd_i, DnnlGemmWrapper::to_dt<float>(), stream);
2970        }
2971        else
2972#endif
2973        {
2974            const float alpha = 1.0f;
2975            const float beta  = 0.0f;
2976            SYCL_CHECK(CHECK_TRY_ERROR(dpct::gemm(
2977                *stream, oneapi::mkl::transpose::trans,
2978                oneapi::mkl::transpose::nontrans, row_diff, src1_ncols, ne10,
2979                &alpha, src0_ptr, dpct::library_data_t::real_half, ne00,
2980                src1_ptr, dpct::library_data_t::real_half, ne10, &beta,
2981                dst_dd_i, dpct::library_data_t::real_float, ldc,
2982                dpct::library_data_t::real_float)));
2983        }
2984    } else {
2985        ggml_sycl_pool_alloc<float> src0_ddq_as_f32(ctx.pool());
2986        ggml_sycl_pool_alloc<float> src1_ddq_as_f32(ctx.pool());
2987        if (src0->type != GGML_TYPE_F32) {
2988            scope_op_debug_print scope_dbg_print(__func__, "/to_fp32_sycl", dst, /*num_src=*/2,
2989                                                 " : converting src0 to fp32");
2990            const to_fp32_sycl_t to_fp32_sycl = ggml_get_to_fp32_sycl(src0->type, dst);
2991            GGML_ASSERT(to_fp32_sycl != nullptr);
2992            src0_ddq_as_f32.alloc(row_diff*ne00);
2993            to_fp32_sycl(src0_dd_i, src0_ddq_as_f32.get(), row_diff*ne00, stream);
2994        }
2995        if (src1->type != GGML_TYPE_F32) {
2996            scope_op_debug_print scope_dbg_print(__func__, "/to_fp32_sycl", dst, /*num_src=*/2,
2997                                                 " : converting src1 to fp32");
2998            const to_fp32_sycl_t to_fp32_sycl = ggml_get_to_fp32_sycl(src1->type, dst);
2999            GGML_ASSERT(to_fp32_sycl != nullptr);
3000            src1_ddq_as_f32.alloc(src1_ncols*ne10);
3001            to_fp32_sycl(src1_ddf_i, src1_ddq_as_f32.get(), src1_ncols*ne10, stream);
3002        }
3003        const float * src0_ddf_i = src0->type == GGML_TYPE_F32 ? (const float *) src0_dd_i : src0_ddq_as_f32.get();
3004        const float * src1_ddf1_i = src1->type == GGML_TYPE_F32 ? (const float *) src1_ddf_i : src1_ddq_as_f32.get();
3005
3006        {
3007#if GGML_SYCL_DNNL
3008            const int64_t gemm_flops = (int64_t)row_diff * src1_ncols * ne10;
3009            const bool use_mkl_direct = gemm_flops < 256 * 256 * 256;
3010            if (g_ggml_sycl_enable_dnn && !use_mkl_direct) {
3011                DnnlGemmWrapper::row_gemm(ctx, row_diff, src1_ncols, ne10, src0_ddf_i,
3012                                          DnnlGemmWrapper::to_dt<float>(), src1_ddf1_i, DnnlGemmWrapper::to_dt<float>(),
3013                                          dst_dd_i, DnnlGemmWrapper::to_dt<float>(), stream);
3014            }
3015            else
3016#endif
3017            {
3018                const float alpha = 1.0f;
3019                const float beta  = 0.0f;
3020                SYCL_CHECK(CHECK_TRY_ERROR(oneapi::mkl::blas::column_major::gemm(
3021                    *stream, oneapi::mkl::transpose::trans, oneapi::mkl::transpose::nontrans, row_diff,
3022                    src1_ncols, ne10, dpct::get_value(&alpha, *stream), src0_ddf_i, ne00, src1_ddf1_i, ne10,
3023                    dpct::get_value(&beta, *stream), dst_dd_i, ldc)));
3024            }
3025        }
3026    }
3027    GGML_UNUSED(dst);
3028    GGML_UNUSED(src1_ddq_i);
3029    GGML_UNUSED(src1_padded_row_size);
3030}
3031catch (sycl::exception const &exc) {
3032  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
3033            << ", line:" << __LINE__ << std::endl;
3034  std::exit(1);
3035}
3036
3037inline void ggml_sycl_op_sum(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
3038    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3039    GGML_ASSERT( dst->type == GGML_TYPE_F32);
3040    dpct::queue_ptr main_stream = ctx.stream();
3041    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3042    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3043    float *       dst_dd  = static_cast<float *>(dst->data);
3044
3045    const int64_t ne = ggml_nelements(dst->src[0]);
3046
3047    sum_rows_f32_sycl(src0_dd, dst_dd, ne, 1, main_stream);
3048}
3049
3050inline void ggml_sycl_op_sum_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3051    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3052    GGML_ASSERT( dst->type == GGML_TYPE_F32);
3053    dpct::queue_ptr main_stream = ctx.stream();
3054    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3055    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3056    float *       dst_dd  = static_cast<float *>(dst->data);
3057
3058    const int64_t ncols = dst->src[0]->ne[0];
3059    const int64_t nrows = ggml_nrows(dst->src[0]);
3060
3061    sum_rows_f32_sycl(src0_dd, dst_dd, ncols, nrows, main_stream);
3062}
3063
3064inline void ggml_sycl_op_mean(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3065    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3066    GGML_ASSERT(dst->type == GGML_TYPE_F32);
3067
3068    dpct::queue_ptr main_stream = ctx.stream();
3069    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3070
3071    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3072    float *       dst_dd  = static_cast<float *>(dst->data);
3073
3074    const int64_t ncols = dst->src[0]->ne[0];
3075    const int64_t nrows = ggml_nrows(dst->src[0]);
3076
3077    sum_rows_f32_sycl(src0_dd, dst_dd, ncols, nrows, main_stream);
3078
3079    main_stream->parallel_for(
3080        sycl::range<1>(nrows),
3081        [=](sycl::id<1> row) {
3082            dst_dd[row] /= ncols;
3083        }
3084    );
3085}
3086
3087
3088inline void ggml_sycl_op_argsort(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3089    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3090    GGML_ASSERT(dst->type == GGML_TYPE_I32);
3091    dpct::queue_ptr main_stream = ctx.stream();
3092    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3093    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3094    int32_t *       dst_dd  = static_cast<int32_t *>(dst->data);
3095
3096
3097    const int64_t ncols = dst->src[0]->ne[0];
3098    const int64_t nrows = ggml_nrows(dst->src[0]);
3099
3100    enum ggml_sort_order order = (enum ggml_sort_order) dst->op_params[0];
3101
3102    argsort_f32_i32_sycl(src0_dd, (int *)dst_dd, ncols, nrows, order,
3103                         main_stream, ctx.device, ctx.pool());
3104}
3105
3106static void ggml_sycl_op_top_k(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3107    const ggml_tensor * src0 = dst->src[0];
3108
3109    GGML_ASSERT(src0);
3110    GGML_ASSERT(src0->type == GGML_TYPE_F32);
3111    GGML_ASSERT(dst->type == GGML_TYPE_I32);
3112    GGML_ASSERT(ggml_is_contiguous(src0));
3113
3114    dpct::queue_ptr main_stream = ctx.stream();
3115    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3116
3117    const float * src0_dd = static_cast<const float *>(src0->data);
3118    int32_t * dst_dd = static_cast<int32_t *>(dst->data);
3119
3120    const int k = dst->ne[0];
3121    const int64_t ncols = src0->ne[0];
3122    const int64_t nrows = ggml_nrows(src0);
3123
3124    GGML_ASSERT(k > 0);
3125    GGML_ASSERT(k <= ncols);
3126
3127    if (k <= SYCL_TOP_K_SCAN_MERGE_MAX_K) {
3128        top_k_f32_sycl(ctx, src0_dd, dst_dd, ncols, nrows, k, main_stream);
3129    } else {
3130        ggml_sycl_top_k_radix(ctx, src0_dd, dst_dd, ncols, nrows, k, main_stream);
3131    }
3132}
3133
3134inline void ggml_sycl_op_argmax(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3135    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3136    GGML_ASSERT( dst->type == GGML_TYPE_I32);
3137
3138    dpct::queue_ptr main_stream = ctx.stream();
3139    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3140    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3141    int32_t *       dst_dd  = static_cast<int32_t *>(dst->data);
3142
3143    const int64_t ncols = dst->src[0]->ne[0];
3144    const int64_t nrows = ggml_nrows(dst->src[0]);
3145
3146    argmax_f32_i32_sycl(src0_dd, dst_dd, ncols, nrows, main_stream);
3147}
3148
3149inline void ggml_sycl_op_diag_mask_inf(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3150    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3151    GGML_ASSERT( dst->type == GGML_TYPE_F32);
3152    dpct::queue_ptr main_stream = ctx.stream();
3153    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3154    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3155    float *       dst_dd  = static_cast<float *>(dst->data);
3156
3157    const int64_t ne00 = dst->src[0]->ne[0];
3158    const int64_t ne01 = dst->src[0]->ne[1];
3159    const int nrows0 = ggml_nrows(dst->src[0]);
3160
3161    const int n_past = ((int32_t *) dst->op_params)[0];
3162
3163    diag_mask_inf_f32_sycl(src0_dd, dst_dd, ne00, nrows0, ne01, n_past, main_stream);
3164}
3165
3166static void tri_f32_sycl(
3167    const float * src,
3168    float * dst,
3169    const int64_t ne0,
3170    const int64_t ne1,
3171    const int64_t ne2,
3172    const int64_t ne3,
3173    const ggml_tri_type ttype,
3174    dpct::queue_ptr main_stream
3175) {
3176    const size_t total = (size_t) ne0 * (size_t) ne1 * (size_t) ne2 * (size_t) ne3;
3177
3178    main_stream->parallel_for(sycl::range<1>(total), [=](sycl::id<1> tid) {
3179        const int64_t idx = (int64_t) tid[0];
3180
3181        const int64_t i0 = idx % ne0;
3182        const int64_t t1 = idx / ne0;
3183        const int64_t i1 = t1 % ne1;
3184
3185        bool keep = false;
3186        switch (ttype) {
3187            case GGML_TRI_TYPE_LOWER:      keep = (i0 <  i1); break;
3188            case GGML_TRI_TYPE_LOWER_DIAG: keep = (i0 <= i1); break;
3189            case GGML_TRI_TYPE_UPPER:      keep = (i0 >  i1); break;
3190            case GGML_TRI_TYPE_UPPER_DIAG: keep = (i0 >= i1); break;
3191            default: keep = false; break;
3192        }
3193
3194        dst[idx] = keep ? src[idx] : 0.0f;
3195    });
3196}
3197
3198static void ggml_sycl_op_tri(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3199    const ggml_tensor * src0 = dst->src[0];
3200    GGML_ASSERT(src0);
3201
3202    GGML_ASSERT(src0->type == GGML_TYPE_F32);
3203    GGML_ASSERT(dst->type  == GGML_TYPE_F32);
3204    GGML_ASSERT(ggml_is_contiguous(src0));
3205    GGML_ASSERT(ggml_is_contiguous(dst));
3206    GGML_ASSERT(ggml_are_same_shape(src0, dst));
3207
3208    dpct::queue_ptr main_stream = ctx.stream();
3209    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3210
3211    const float * src0_dd = static_cast<const float *>(src0->data);
3212    float *       dst_dd  = static_cast<float *>(dst->data);
3213
3214    const ggml_tri_type ttype = (ggml_tri_type) ggml_get_op_params_i32(dst, 0);
3215
3216    const int64_t ne0 = src0->ne[0];
3217    const int64_t ne1 = src0->ne[1];
3218    const int64_t ne2 = src0->ne[2];
3219    const int64_t ne3 = src0->ne[3];
3220
3221    tri_f32_sycl(src0_dd, dst_dd, ne0, ne1, ne2, ne3, ttype, main_stream);
3222}
3223
3224
3225inline void ggml_sycl_op_scale(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3226    GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
3227    GGML_ASSERT( dst->type == GGML_TYPE_F32);
3228    dpct::queue_ptr main_stream = ctx.stream();
3229    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3230    const float * src0_dd = static_cast<const float *>(dst->src[0]->data);
3231    float *       dst_dd  = static_cast<float *>(dst->data);
3232
3233    float scale;
3234    float bias;
3235    memcpy(&scale, (float *) dst->op_params + 0, sizeof(float));
3236    memcpy(&bias,  (float *) dst->op_params + 1, sizeof(float));
3237
3238    scale_f32_sycl(src0_dd, dst_dd, scale, bias, ggml_nelements(dst->src[0]), main_stream);
3239    /*
3240    DPCT1010:87: SYCL uses exceptions to report errors and does not use the
3241    error codes. The call was replaced with 0. You need to rewrite this code.
3242    */
3243    SYCL_CHECK(0);
3244}
3245
3246static void ggml_sycl_set_peer_access(const int n_tokens, int main_device) {
3247    static bool peer_access_enabled = false;
3248
3249    const bool enable_peer_access = n_tokens <= GGML_SYCL_PEER_MAX_BATCH_SIZE;
3250
3251    if (peer_access_enabled == enable_peer_access) {
3252        return;
3253    }
3254
3255#ifdef NDEBUG
3256    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
3257        SYCL_CHECK(ggml_sycl_set_device(i));
3258    }
3259
3260    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
3261        SYCL_CHECK(ggml_sycl_set_device(i));
3262
3263        for (int id_other = 0; id_other < ggml_sycl_info().device_count; ++id_other) {
3264            if (i == id_other) {
3265                continue;
3266            }
3267            if (i != main_device && id_other != main_device) {
3268                continue;
3269            }
3270
3271            // int can_access_peer;
3272            // SYCL_CHECK(syclDeviceCanAccessPeer(&can_access_peer, id, id_other));
3273            // if (can_access_peer) {
3274            //     if (enable_peer_access) {
3275            //         SYCL_CHECK(syclDeviceEnablePeerAccess(id_other, 0));
3276            //     } else {
3277            //         SYCL_CHECK(syclDeviceDisablePeerAccess(id_other));
3278            //     }
3279            // }
3280        }
3281    }
3282#endif // NDEBUG
3283
3284    peer_access_enabled = enable_peer_access;
3285}
3286
3287template <template <int> typename quantize_f>
3288static void ggml_sycl_op_mul_mat(ggml_backend_sycl_context & ctx, const ggml_tensor *src0,
3289                                 const ggml_tensor *src1, ggml_tensor *dst,
3290                                 ggml_sycl_op_mul_mat_t op) try {
3291
3292    GGML_TENSOR_LOCALS(int64_t, ne0, src0, ne);
3293
3294    GGML_TENSOR_LOCALS(int64_t, ne1, src1, ne);
3295    const int64_t nrows1 = ggml_nrows(src1);
3296
3297    GGML_ASSERT(ne03 == ne13);
3298
3299    const int64_t ne0 = dst->ne[0];
3300    const int64_t ne1 = dst->ne[1];
3301
3302    const int nb2 = dst->nb[2];
3303    const int nb3 = dst->nb[3];
3304
3305    GGML_ASSERT(!ggml_backend_buffer_is_sycl_split(dst->buffer));
3306    GGML_ASSERT(!ggml_backend_buffer_is_sycl_split(src1->buffer));
3307    GGML_ASSERT(src1->type == GGML_TYPE_F32 || (src1->ne[2] == 1 && src1->ne[3] == 1));
3308
3309    GGML_ASSERT(ne12 >= ne02 && ne12 % ne02 == 0);
3310
3311    const int64_t i02_divisor = ne12 / ne02;
3312
3313    const size_t src0_ts = ggml_type_size(src0->type);
3314    const size_t src0_bs = ggml_blck_size(src0->type);
3315    const size_t q8_1_ts = sizeof(block_q8_1);
3316    const size_t q8_1_bs = QK8_1;
3317
3318    ggml_tensor_extra_gpu * src0_extra = (ggml_tensor_extra_gpu *) src0->extra;
3319    ggml_tensor_extra_gpu * src1_extra = (ggml_tensor_extra_gpu *) src1->extra;
3320
3321    const bool src0_is_contiguous = ggml_is_contiguous(src0);
3322    const bool src1_is_contiguous = ggml_is_contiguous(src1);
3323
3324    int64_t src1_padded_col_size = GGML_PAD(ne10, MATRIX_ROW_PADDING);
3325
3326    const bool split = ggml_backend_buffer_is_sycl_split(src0->buffer);
3327    GGML_ASSERT(!(split && ne02 > 1));
3328    GGML_ASSERT(!(split && ne03 > 1));
3329    GGML_ASSERT(!(split && ne02 < ne12));
3330
3331    std::array<float, GGML_SYCL_MAX_DEVICES> tensor_split;
3332    if (split) {
3333        // TODO: check that src0->buffer->buft is a split buffer type, replace GGML_BACKEND_TYPE_GPU_SPLIT check
3334        // GGML_ASSERT(src0->buffer != nullptr && src0->buffer->buft == ...);
3335        ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *) src0->buffer->buft->context;
3336        tensor_split = buft_ctx->tensor_split;
3337    }
3338
3339    struct dev_data {
3340        ggml_sycl_pool_alloc<char> src0_dd_alloc;
3341        ggml_sycl_pool_alloc<float> src1_ddf_alloc;
3342        ggml_sycl_pool_alloc<char> src1_ddq_alloc;
3343        ggml_sycl_pool_alloc<float> dst_dd_alloc;
3344
3345        char *src0_dd = nullptr;
3346        float *src1_ddf = nullptr; // float
3347        char *src1_ddq = nullptr;  // q8_1
3348        float *dst_dd = nullptr;
3349
3350        int64_t row_low;
3351        int64_t row_high;
3352    };
3353
3354    dev_data dev[GGML_SYCL_MAX_DEVICES];
3355
3356    int used_devices = 0;
3357    queue_ptr main_stream = ctx.stream();
3358
3359    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
3360        // by default, use all rows
3361        dev[i].row_low  = 0;
3362        dev[i].row_high = ne01;
3363
3364        // for multi GPU, get the row boundaries from tensor split
3365        // and round to mul_mat_q tile sizes
3366        if (split) {
3367            const int64_t rounding = get_row_rounding(src0->type, tensor_split);
3368
3369            if (i != 0) {
3370                dev[i].row_low  = ne01*tensor_split[i];
3371                if (dev[i].row_low < ne01) {
3372                    dev[i].row_low -= dev[i].row_low % rounding;
3373                }
3374            }
3375
3376            if (i != ggml_sycl_info().device_count - 1) {
3377                dev[i].row_high  = ne01*tensor_split[i + 1];
3378                if (dev[i].row_high < ne01) {
3379                    dev[i].row_high -= dev[i].row_high % rounding;
3380                }
3381            }
3382        }
3383    }
3384
3385    constexpr bool quantize_enabled = !std::is_same_v<quantize_f<QK8_1 / WARP_SIZE>,
3386                                                      no_quantize_q8_1<QK8_1 / WARP_SIZE>>;
3387    for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
3388        if ((!split && i != ctx.device) || dev[i].row_low == dev[i].row_high) {
3389            continue;
3390        }
3391
3392        used_devices++;
3393
3394        const bool src1_on_device = i == ctx.device;
3395        const bool  dst_on_device = i == ctx.device;
3396
3397        ggml_sycl_set_device(i);
3398        queue_ptr stream = ctx.stream(i, 0);
3399
3400        if (src0_is_contiguous) {
3401            dev[i].src0_dd = (char *) src0->data;
3402        } else {
3403            dev[i].src0_dd = dev[i].src0_dd_alloc.alloc(ctx.pool(i), ggml_nbytes(src0));
3404        }
3405
3406        if (src1_on_device && src1_is_contiguous) {
3407            dev[i].src1_ddf = (float *) src1->data;
3408        } else {
3409            dev[i].src1_ddf = dev[i].src1_ddf_alloc.alloc(ctx.pool(i), ggml_nelements(src1));
3410        }
3411
3412        if constexpr(quantize_enabled) {
3413            dev[i].src1_ddq = dev[i].src1_ddq_alloc.alloc(ctx.pool(i), nrows1*src1_padded_col_size*q8_1_ts/q8_1_bs);
3414
3415            if (src1_on_device && src1_is_contiguous) {
3416                scope_op_debug_print scope_dbg_print(__func__, "/quantize_row_q8_1_sycl", dst,
3417                                                     /*num_src=*/2, " : converting src1 to Q8_1");
3418                try {
3419                    quantize_row_q8_1_sycl<quantize_f>(dev[i].src1_ddf, dev[i].src1_ddq, ne10, nrows1, src1_padded_col_size, stream);
3420                } catch (sycl::exception const &exc) {
3421                    std::cerr << "Quantize_row_q8_1_sycl error" << exc.what() << "Exception caught at file:" << __FILE__
3422                              << ", line:" << __LINE__ << std::endl;
3423                    std::exit(1);
3424                }
3425            }
3426        }
3427
3428        if (dst_on_device) {
3429            dev[i].dst_dd = (float *) dst->data;
3430        } else {
3431            const size_t size_dst_ddf = split ? (dev[i].row_high - dev[i].row_low)*ne1 : ggml_nelements(dst);
3432            dev[i].dst_dd = dev[i].dst_dd_alloc.alloc(ctx.pool(i), size_dst_ddf);
3433        }
3434    }
3435
3436    // if multiple devices are used they need to wait for the main device
3437    // here an event is recorded that signals that the main device has finished calculating the input data
3438    if (split && used_devices > 1) {
3439        ggml_sycl_set_device(ctx.device);
3440        SYCL_CHECK(CHECK_TRY_ERROR(
3441            *src0_extra->events[ctx.device][0] =
3442                ctx.stream()->ext_oneapi_submit_barrier()));
3443    }
3444
3445    const int64_t src1_col_stride = split && used_devices > 1 ? MUL_MAT_SRC1_COL_STRIDE : ne11;
3446    for (int64_t src1_col_0 = 0; src1_col_0 < ne11; src1_col_0 += src1_col_stride) {
3447        const int64_t is = split ? (src1_col_0/src1_col_stride) % GGML_SYCL_MAX_STREAMS : 0;
3448        const int64_t src1_ncols = src1_col_0 + src1_col_stride > ne11 ? ne11 - src1_col_0 : src1_col_stride;
3449        for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
3450            if ((!split && i != ctx.device) || dev[i].row_low == dev[i].row_high) {
3451                continue;
3452            }
3453
3454            const bool src1_on_device = i == ctx.device;
3455            const bool  dst_on_device = i == ctx.device;
3456            const int64_t row_diff = dev[i].row_high - dev[i].row_low;
3457
3458            ggml_sycl_set_device(i);
3459            queue_ptr stream = ctx.stream(i, is);
3460
3461            // wait for main GPU data if necessary
3462            if (split && (i != ctx.device || is != 0)) {
3463                SYCL_CHECK(CHECK_TRY_ERROR(stream->ext_oneapi_submit_barrier(
3464                    {*src0_extra->events[ctx.device][0]})));
3465            }
3466
3467            for (int64_t i0 = 0; i0 < ne13*ne12; ++i0) {
3468                const int64_t i03 = i0 / ne12;
3469                const int64_t i02 = i0 % ne12;
3470
3471                const size_t src1_ddq_i_offset = (i0*ne11 + src1_col_0) * src1_padded_col_size*q8_1_ts/q8_1_bs;
3472
3473                // for split tensors the data begins at i0 == i0_offset_low
3474                char  *  src0_dd_i =  dev[i].src0_dd + (i0/i02_divisor) * (ne01*ne00*src0_ts)/src0_bs;
3475                float * src1_ddf_i = dev[i].src1_ddf + (i0*ne11 + src1_col_0) * ne10;
3476                char  * src1_ddq_i = dev[i].src1_ddq +  src1_ddq_i_offset;
3477                float *   dst_dd_i =   dev[i].dst_dd + (i0*ne1  + src1_col_0) * (dst_on_device ? ne0 : row_diff);
3478
3479                // the main device memory buffer can be on VRAM scratch, with space for all partial results
3480                // in that case an offset on dst_ddf_i is needed
3481                if (i == ctx.device) {
3482                    dst_dd_i += dev[i].row_low; // offset is 0 if no tensor split
3483                }
3484
3485                // copy src0, src1 to device if necessary
3486                if (src1_is_contiguous) {
3487                    if (i != ctx.device) {
3488                        if constexpr (quantize_enabled) {
3489                            char * src1_ddq_i_source = dev[ctx.device].src1_ddq + src1_ddq_i_offset;
3490                            SYCL_CHECK(
3491                                CHECK_TRY_ERROR(stream
3492                                                    ->memcpy(src1_ddq_i, src1_ddq_i_source,
3493                                                             src1_ncols * src1_padded_col_size * q8_1_ts / q8_1_bs)
3494                                                    .wait()));
3495                        } else {
3496                            float * src1_ddf_i_source = (float *) src1_extra->data_device[ctx.device];
3497                            src1_ddf_i_source += (i0 * ne11 + src1_col_0) * ne10;
3498
3499                            SYCL_CHECK(
3500                                CHECK_TRY_ERROR(dev2dev_memcpy(i, *stream, ctx.device, *main_stream, src1_ddf_i, src1_ddf_i_source,
3501                                                               src1_ncols * ne10 * sizeof(float))));
3502                        }
3503                    }
3504                } else {
3505                    if (src1_on_device) {
3506                        SYCL_CHECK(ggml_sycl_cpy_tensor_2d(src1_ddf_i, src1, i03, i02, src1_col_0,
3507                                                           src1_col_0 + src1_ncols, stream));
3508                    } else {
3509                        GGML_ABORT("src1 is non-contiguous and not on device");
3510                    }
3511
3512                    if constexpr (quantize_enabled) {
3513                        scope_op_debug_print scope_dbg_print(__func__, "/quantize_row_q8_1_sycl", dst,
3514                                                             /*num_src=*/2, " : converting src1 to Q8_1");
3515                        try {
3516                            quantize_row_q8_1_sycl<quantize_q8_1>(src1_ddf_i, src1_ddq_i, ne10, src1_ncols,
3517                                                                  src1_padded_col_size, stream);
3518                        } catch (const sycl::exception & exc) {
3519                            std::cerr << "Quantize_row_q8_1_sycl error" << exc.what()
3520                                      << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl;
3521                            std::exit(1);
3522                        }
3523                    }
3524                }
3525
3526                if (src1_col_0 == 0 && !src0_is_contiguous && i02 % i02_divisor == 0) {
3527                    SYCL_CHECK(ggml_sycl_cpy_tensor_2d(src0_dd_i, src0, i03, i02/i02_divisor, dev[i].row_low, dev[i].row_high, stream));
3528                }
3529                if (src1->type == GGML_TYPE_F16) {
3530                    src1_padded_col_size = (i0 * ne11 + src1_col_0) * ne10;
3531                }
3532                // do the computation
3533                SYCL_CHECK(CHECK_TRY_ERROR(op(ctx, src0, src1, dst, src0_dd_i, src1_ddf_i, src1_ddq_i, dst_dd_i,
3534                    dev[i].row_low, dev[i].row_high, src1_ncols, src1_padded_col_size, stream)));
3535
3536                // copy dst to host or other device if necessary
3537                if (!dst_on_device) {
3538                    void * dst_off_device = dst->data;
3539                    if (split) {
3540                        // src0 = weight matrix is saved as a transposed matrix for better memory layout.
3541                        // dst is NOT transposed.
3542                        // The outputs of matrix matrix multiplications can therefore NOT simply be concatenated for >1 GPU.
3543                        // Instead they need to be copied to the correct slice in ne0 = dst row index.
3544                        // If dst is a vector with ne0 == 1 then you don't have to do this but it still produces correct results.
3545                        float * dhf_dst_i = (float *) ((char *) dst_off_device + i02*nb2 + i03*nb3);
3546                        GGML_ASSERT(dst->nb[1] == ne0*sizeof(float));
3547                        dhf_dst_i += src1_col_0*ne0 + dev[i].row_low;
3548
3549                        SYCL_CHECK(CHECK_TRY_ERROR(dpct::async_dpct_memcpy(
3550                            dhf_dst_i, ne0 * sizeof(float), dst_dd_i,
3551                            row_diff * sizeof(float), row_diff * sizeof(float),
3552                            src1_ncols, dpct::device_to_device, *stream)));
3553                    } else {
3554                        float * dhf_dst_i = (float *) ((char *) dst_off_device + i02*nb2 + i03*nb3);
3555                        GGML_ASSERT(dst->nb[1] == ne0*sizeof(float));
3556                        dhf_dst_i += src1_col_0*ne0;
3557                        SYCL_CHECK(CHECK_TRY_ERROR(
3558                            stream->memcpy(dhf_dst_i, dst_dd_i,
3559                                           src1_ncols * ne0 * sizeof(float)).wait()));
3560                    }
3561                }
3562
3563                // add event for the main device to wait on until other device is done
3564                if (split && (i != ctx.device || is != 0)) {
3565                    SYCL_CHECK(CHECK_TRY_ERROR(
3566                        *src0_extra->events[i][is] =
3567                            stream->ext_oneapi_submit_barrier()));
3568                }
3569            }
3570        }
3571    }
3572
3573    // main device waits for all other devices to be finished
3574    if (split && ggml_sycl_info().device_count > 1) {
3575        int64_t is_max = (ne11 + MUL_MAT_SRC1_COL_STRIDE - 1) / MUL_MAT_SRC1_COL_STRIDE;
3576        is_max = is_max <= GGML_SYCL_MAX_STREAMS ? is_max : GGML_SYCL_MAX_STREAMS;
3577
3578        ggml_sycl_set_device(ctx.device);
3579        for (int i = 0; i < ggml_sycl_info().device_count; ++i) {
3580            if (dev[i].row_low == dev[i].row_high) {
3581                continue;
3582            }
3583            for (int64_t is = 0; is < is_max; ++is) {
3584                SYCL_CHECK(CHECK_TRY_ERROR(
3585                    ctx.stream()->ext_oneapi_submit_barrier(
3586                        {*src0_extra->events[i][is]})));
3587            }
3588        }
3589    }
3590}
3591catch (sycl::exception const &exc) {
3592  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
3593            << ", line:" << __LINE__ << std::endl;
3594  std::exit(1);
3595}
3596
3597static void ggml_sycl_repeat_back(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3598    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
3599    ggml_sycl_op_repeat_back(ctx, dst);
3600}
3601
3602static void ggml_sycl_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3603    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
3604    ggml_sycl_op_get_rows(ctx, dst);
3605}
3606
3607static void ggml_sycl_norm(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3608    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
3609    ggml_sycl_op_norm(ctx, dst);
3610}
3611
3612static void ggml_sycl_rms_norm(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3613    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
3614    ggml_sycl_op_rms_norm(ctx, dst);
3615}
3616
3617static void ggml_sycl_rms_norm_back(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3618    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
3619    ggml_sycl_op_rms_norm_back(ctx, dst);
3620}
3621
3622static void ggml_sycl_l2_norm(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3623    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
3624    ggml_sycl_op_l2_norm(ctx, dst);
3625}
3626
3627static void ggml_sycl_group_norm(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
3628    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
3629    ggml_sycl_op_group_norm(ctx, dst);
3630}
3631
3632static void ggml_sycl_mul_mat_vec_p021(ggml_backend_sycl_context & ctx, const ggml_tensor *src0,
3633                                       const ggml_tensor *src1,
3634                                       ggml_tensor *dst) try {
3635    GGML_ASSERT(ggml_is_permuted(src0) && ggml_is_permuted(src1));
3636    GGML_ASSERT(!ggml_backend_buffer_is_sycl_split(src0->buffer));
3637    GGML_ASSERT(src0->nb[0] <= src0->nb[1] && src0->nb[2] <= src0->nb[3]); // 0213 permutation
3638    GGML_ASSERT(src1->nb[0] <= src1->nb[1] && src1->nb[2] <= src1->nb[3]); // 0213 permutation
3639    GGML_ASSERT(src0->type == GGML_TYPE_F16);
3640    GGML_ASSERT(src1->type == GGML_TYPE_F32);
3641
3642    const int64_t ne00 = src0->ne[0];
3643    const int64_t ne01 = src0->ne[1];
3644    const int64_t ne02 = src0->ne[2];
3645
3646    const int64_t ne12 = src1->ne[2];
3647
3648    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3649    queue_ptr main_stream = ctx.stream();
3650
3651    void  * src0_ddq = src0->data;
3652    float * src1_ddf = (float *) src1->data;
3653    float * dst_ddf  = (float *) dst->data;
3654
3655    ggml_mul_mat_p021_f16_f32_sycl(src0_ddq, src1_ddf, dst_ddf, ne00, ne01, ne02, ne12, main_stream);
3656}
3657catch (sycl::exception const &exc) {
3658  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
3659            << ", line:" << __LINE__ << std::endl;
3660  std::exit(1);
3661}
3662
3663static void ggml_sycl_mul_mat_vec_nc(ggml_backend_sycl_context & ctx, const ggml_tensor *src0,
3664                                     const ggml_tensor *src1,
3665                                     ggml_tensor *dst) try {
3666    GGML_ASSERT(!ggml_is_transposed(src0));
3667    GGML_ASSERT(!ggml_is_transposed(src1));
3668    GGML_ASSERT(!ggml_is_permuted(src0));
3669    GGML_ASSERT(!ggml_backend_buffer_is_sycl_split(src0->buffer));
3670    GGML_ASSERT(src0->type == GGML_TYPE_F16);
3671    GGML_ASSERT(src1->type == GGML_TYPE_F32);
3672    GGML_ASSERT(src1->ne[1] == 1);
3673    GGML_ASSERT(src1->ne[3] == 1);
3674
3675    const int64_t ne00 = src0->ne[0];
3676    const int64_t ne01 = src0->ne[1];
3677    const int64_t ne02 = src0->ne[2];
3678
3679    const int64_t nb01 = src0->nb[1];
3680    const int64_t nb02 = src0->nb[2];
3681
3682    const int64_t ne12 = src1->ne[2];
3683    const int64_t nb11 = src1->nb[1];
3684
3685    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3686    queue_ptr main_stream = ctx.stream();
3687
3688    void  * src0_ddq = src0->data;
3689    float * src1_ddf = (float *) src1->data;
3690    float * dst_ddf  = (float *) dst->data;
3691
3692    const int64_t row_stride_x = nb01 / sizeof(sycl::half);
3693    const int64_t channel_stride_x = nb02 / sizeof(sycl::half);
3694    const int64_t channel_stride_y = nb11 / sizeof(float);
3695
3696    ggml_mul_mat_vec_nc_f16_f32_sycl(src0_ddq, src1_ddf, dst_ddf, ne00, ne01, row_stride_x, ne02, ne12, channel_stride_x,channel_stride_y, main_stream);
3697}
3698catch (sycl::exception const &exc) {
3699  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
3700            << ", line:" << __LINE__ << std::endl;
3701  std::exit(1);
3702}
3703
3704static void k_compute_batched_ptrs(const sycl::half * src0_as_f16, const sycl::half * src1_as_f16, void * dst,
3705                                   const void ** ptrs_src, void ** ptrs_dst, int64_t ne12, int64_t ne13, int64_t ne23,
3706                                   size_t nb02, size_t nb03, size_t nb12, size_t nb13, size_t nbd2, size_t nbd3,
3707                                   int64_t r2, int64_t r3, const sycl::nd_item<3> & item_ct1) {
3708    const int64_t i13 = item_ct1.get_group(2) * item_ct1.get_local_range(2) + item_ct1.get_local_id(2);
3709    const int64_t i12 = item_ct1.get_group(1) * item_ct1.get_local_range(1) + item_ct1.get_local_id(1);
3710
3711    if (i13 >= ne13 || i12 >= ne12) {
3712        return;
3713    }
3714
3715    const int64_t i03 = i13 / r3;
3716    const int64_t i02 = i12 / r2;
3717
3718    const uint8_t * src0_bytes = reinterpret_cast<const uint8_t *>(src0_as_f16);
3719    const uint8_t * src1_bytes = reinterpret_cast<const uint8_t *>(src1_as_f16);
3720    uint8_t *       dst_bytes  = static_cast<uint8_t *>(dst);
3721
3722    ptrs_src[0 * ne23 + i12 + i13 * ne12] = src0_bytes + i02 * nb02 + i03 * nb03;
3723    ptrs_src[1 * ne23 + i12 + i13 * ne12] = src1_bytes + i12 * nb12 + i13 * nb13;
3724    ptrs_dst[0 * ne23 + i12 + i13 * ne12] = dst_bytes + i12 * nbd2 + i13 * nbd3;
3725}
3726
3727static void ggml_sycl_mul_mat_batched_sycl(ggml_backend_sycl_context & ctx, const ggml_tensor * src0,
3728                                           const ggml_tensor * src1, ggml_tensor * dst) try {
3729    GGML_ASSERT(!ggml_is_transposed(src0));
3730    GGML_ASSERT(!ggml_is_transposed(src1));
3731    GGML_ASSERT(!ggml_backend_buffer_is_sycl_split(src0->buffer));
3732    GGML_ASSERT(src0->type == GGML_TYPE_F16);
3733    GGML_ASSERT(dst->type == GGML_TYPE_F32);
3734
3735    GGML_TENSOR_BINARY_OP_LOCALS
3736
3737    // TODO: see https://github.com/ggml-org/llama.cpp/pull/13155
3738    // Batched mul_mat requires a rewrite to support both oneDNN and non-contiguous dst
3739    GGML_ASSERT(ggml_is_contiguous(dst));
3740
3741    SYCL_CHECK(ggml_sycl_set_device(ctx.device));
3742    queue_ptr queue = ctx.stream();
3743
3744    dpct::has_capability_or_fail(queue->get_device(), { sycl::aspect::fp16 });
3745
3746    const sycl::half * src0_f16 = static_cast<const sycl::half *>(src0->data);
3747    float *            dst_ddf  = static_cast<float *>(dst->data);
3748
3749    const sycl::half * src1_f16       = static_cast<const sycl::half *>(src1->data);
3750#if GGML_SYCL_DNNL
3751    const size_t       type_size_src0 = ggml_type_size(src0->type);
3752#endif
3753    const size_t       type_size_src1 = ggml_type_size(src1->type);
3754
3755    bool is_src0_cont_2 = ggml_is_contiguous_2(src0);
3756    bool is_src1_cont_2 = ggml_is_contiguous_2(src1);
3757
3758    // SRC1 strides
3759    int64_t                          s11 = nb11 / type_size_src1;
3760    int64_t                          s12 = nb12 / type_size_src1;
3761    int64_t                          s13 = nb13 / type_size_src1;
3762    ggml_sycl_pool_alloc<sycl::half> src1_f16_alloc(ctx.pool());
3763
3764    // convert src1 to fp16
3765    if (src1->type != GGML_TYPE_F16) {
3766        scope_op_debug_print    scope_dbg_print(__func__, "/to_fp16_nc_sycl", dst, /*num_src=*/2,
3767                                                " : converting src1 to fp16");
3768
3769#if GGML_SYCL_DNNL
3770        // iterate tensor dims and find the slowest moving dim and stride
3771        int last_dim=0;
3772        int last_str=0;
3773        size_t largest_str=0;
3774        for(int i = 0; i< 4; i++){
3775            // last stride is always the largest
3776            if(src1->nb[i] == largest_str){
3777                if(src1->ne[last_dim] == 1){
3778                    last_str = i;
3779                    last_dim = i;
3780                }
3781            }
3782            if(src1->nb[i] > largest_str){
3783                largest_str = src1->nb[i];
3784                last_str = i;
3785                last_dim = i;
3786            }
3787
3788        }
3789        // oneDNN handles strided data and does not need overhead of ggml_get_to_fp16_nc_sycl
3790        const int64_t ne_src1 = src1->nb[last_str] * src1->ne[last_dim] / type_size_src1;
3791        src1_f16_alloc.alloc(ne_src1);
3792        const to_fp16_sycl_t to_fp16_sycl = ggml_get_to_fp16_sycl(src1->type, dst);
3793        GGML_ASSERT(to_fp16_sycl != nullptr);
3794        to_fp16_sycl(src1_f16, src1_f16_alloc.get(), ne_src1, queue);
3795# else
3796        const int64_t ne_src1 = ggml_nelements(src1);
3797        src1_f16_alloc.alloc(ne_src1);
3798        const to_fp16_nc_sycl_t to_fp16_nc_sycl = ggml_get_to_fp16_nc_sycl(src1->type);
3799        GGML_ASSERT(to_fp16_nc_sycl != nullptr);
3800        to_fp16_nc_sycl(src1_f16, src1_f16_alloc.get(), ne10, ne11, ne12, ne13, s11, s12, s13, queue);
3801#endif
3802
3803        src1_f16 = src1_f16_alloc.get();
3804        s11      = ne10;
3805        s12      = ne11 * s11;
3806        s13      = ne12 * s12;
3807
3808        is_src1_cont_2 = true;
3809    }
3810
3811    ggml_sycl_pool_alloc<sycl::half> dst_f16(ctx.pool());
3812
3813    dpct::library_data_t mkl_compute_type = dpct::library_data_t::real_float;
3814    dpct::library_data_t mkl_data_type    = dpct::library_data_t::real_float;
3815
3816    // dst strides
3817    size_t nbd2 = dst->nb[2];
3818    size_t nbd3 = dst->nb[3];
3819
3820    const float alpha_f32 = 1.0f;
3821    const float beta_f32  = 0.0f;
3822
3823    const void * alpha = &alpha_f32;
3824    const void * beta  = &beta_f32;
3825
3826    GGML_ASSERT(ne12 % ne02 == 0);
3827    GGML_ASSERT(ne13 % ne03 == 0);
3828    GGML_ASSERT(ne01 == static_cast<int64_t>(nb1/nb0));
3829    GGML_ASSERT(ne10 == ne00);
3830
3831    // broadcast factors
3832    const int64_t r2 = ne12 / ne02;
3833    const int64_t r3 = ne13 / ne03;
3834
3835#if GGML_SYCL_DNNL
3836    if (g_ggml_sycl_enable_dnn) {
3837            int64_t str_a0 = nb00 / type_size_src0;
3838            int64_t str_a1 = nb01 / type_size_src0;
3839            int64_t str_a2 = nb02 / type_size_src0;
3840
3841            int64_t str_b0 = nb10 / type_size_src1;
3842            int64_t str_b1 = nb11 / type_size_src1;
3843            int64_t str_b2 = nb12 / type_size_src1;
3844
3845            auto launch_gemm_for_batches = [&ctx, queue](const sycl::half *src0,
3846                                                const sycl::half *src1, float *dst,
3847                                                int64_t a0, int64_t a1, int64_t batcha,
3848                                                int64_t /*b0*/, int64_t b1, int64_t batchb,
3849                                                int64_t sa0, int64_t sa1, int64_t sa2,
3850                                                int64_t sb0, int64_t sb1, int64_t sb2,
3851                                                int64_t sd2) {
3852                bool supported_broadcast = batchb == batcha ? true
3853                        : batchb == 1 || batcha == 1        ? true
3854                                                            : false;
3855                if (supported_broadcast) {
3856                    DnnlGemmWrapper::gemm(ctx, a1, b1, a0, src0,
3857                            DnnlGemmWrapper::to_dt<sycl::half>(), sa0, sa1, sa2, src1,
3858                            DnnlGemmWrapper::to_dt<sycl::half>(), sb0, sb1, sb2, dst,
3859                            DnnlGemmWrapper::to_dt<float>(), queue, batcha, batchb);
3860                } else {
3861                    // iterate over batches from smaller set of matrices (matrix 0)
3862                    int64_t batches0 = batcha;
3863                    int64_t batches1 = batchb;
3864
3865                    if (batches0 > batches1) {
3866                        int64_t num_mul_mats = batches1;
3867                        int64_t sub_batch = batches0 / num_mul_mats;
3868                        // src0 is batched and bigger, shift and multiply with src1
3869                        for (int64_t i0 = 0; i0 < num_mul_mats; i0++) {
3870                            const sycl::half *src0_shifted = src0 + (sa2 * i0 * sub_batch);
3871                            const sycl::half *src1_shifted = src1 + (sb2 * i0);
3872                            float *dst_shifted = dst + (sd2 * i0 * sub_batch);
3873                            DnnlGemmWrapper::gemm(ctx, a1, b1, a0, src0_shifted,
3874                                    DnnlGemmWrapper::to_dt<sycl::half>(), sa0, sa1, sa2,
3875                                    src1_shifted, DnnlGemmWrapper::to_dt<sycl::half>(), sb0,
3876                                    sb1, sb2, dst_shifted, DnnlGemmWrapper::to_dt<float>(),
3877                                    queue, sub_batch, 1);
3878                        }
3879                    } else {
3880                        int64_t num_mul_mats = batches0;
3881                        int64_t sub_batch = batches1 / num_mul_mats;
3882                        // src1 is batched and bigger, shift and multiply with src0
3883                        for (int64_t i1 = 0; i1 < num_mul_mats; i1++) {
3884                            const sycl::half *src0_shifted = src0 + (sa2 * i1);
3885                            const sycl::half *src1_shifted = src1 + (sb2 * i1 * sub_batch);
3886                            float *dst_shifted = dst + (sd2 * i1 * sub_batch);
3887                            DnnlGemmWrapper::gemm(ctx, a1, b1, a0, src0_shifted,
3888                                    DnnlGemmWrapper::to_dt<sycl::half>(), sa0, sa1, sa2,
3889                                    src1_shifted, DnnlGemmWrapper::to_dt<sycl::half>(), sb0,
3890                                    sb1, sb2, dst_shifted, DnnlGemmWrapper::to_dt<float>(),
3891                                    queue, 1, sub_batch);
3892                        }
3893                    }
3894                }
3895            };
3896
3897            const bool cont_batches_dim2_a = nb02 * ne02 == nb03;
3898            const bool cont_batches_dim2_b = nb12 * ne12 == nb13;
3899            const bool cont_batches_dim3_a = ne02 == 1 && nb02 * ne01 == nb03;
3900            const bool cont_batches_dim3_b = ne12 == 1 && nb12 * ne11 == nb13;
3901            if (cont_batches_dim2_a && cont_batches_dim2_b) {
3902                // A batch is considered contiguous if the dimension 2 is not strided
3903                int64_t batches0 = ne02 * ne03;
3904                int64_t batches1 = ne12 * ne13;
3905                launch_gemm_for_batches(src0_f16, src1_f16, dst_ddf, ne00, ne01, batches0,
3906                        ne10, ne11, batches1, str_a0, str_a1, str_a2, str_b0, str_b1,
3907                        str_b2, nb2 / sizeof(float));
3908            } else if (cont_batches_dim3_a && cont_batches_dim3_b) {
3909                // This case is similar to the one above with the difference that only the batch in dimension 3 is used and the dimension 2 is of size 1.
3910                int64_t batches0 = ne02 * ne03;
3911                int64_t batches1 = ne12 * ne13;
3912                int64_t str_a3 = nb03 / type_size_src0;
3913                int64_t str_b3 = nb13 / type_size_src1;
3914                launch_gemm_for_batches(src0_f16, src1_f16, dst_ddf, ne00, ne01, batches0,
3915                        ne10, ne11, batches1, str_a0, str_a1, str_a3, str_b0, str_b1,
3916                        str_b3, nb2 / sizeof(float));
3917            } else {
3918                for (int64_t b_a = 0; b_a < ne03; b_a++) {
3919                    const sycl::half *src0_f16_shifted
3920                            = src0_f16 + (nb03 * b_a / type_size_src0);
3921                    const sycl::half *src1_f16_shifted
3922                            = src1_f16 + (nb13 * b_a / type_size_src1);
3923                    float *dst_shifted = dst_ddf + (nb3 * b_a / sizeof(float));
3924                    int64_t batches0 = ne02;
3925                    int64_t batches1 = ne12;
3926                    launch_gemm_for_batches(src0_f16_shifted, src1_f16_shifted, dst_shifted,
3927                            ne00, ne01, batches0, ne10, ne11, batches1, str_a0, str_a1,
3928                            str_a2, str_b0, str_b1, str_b2, nb2 / sizeof(float));
3929                }
3930            }
3931
3932    }
3933    else
3934#endif
3935    {
3936        if (r2 == 1 && r3 == 1 && is_src0_cont_2 && is_src1_cont_2) {
3937            // with a [0, 2, 1, 3] perm. and ne02==1 the matrix strides need to be determined from dim 3:
3938            const int64_t sma = ne02 == 1 ? nb03/nb00 : nb02/nb00;
3939            const int64_t smb = ne12 == 1 ? s13       : s12;
3940
3941            // there is no broadcast and src0, src1 are contiguous across dims 2, 3
3942            SYCL_CHECK(CHECK_TRY_ERROR(dpct::gemm_batch(*queue, oneapi::mkl::transpose::trans,
3943                                                        oneapi::mkl::transpose::nontrans, ne01, ne11, ne10, alpha,
3944                                                        src0_f16, dpct::library_data_t::real_half, nb01 / nb00, sma,
3945                                                        src1_f16, dpct::library_data_t::real_half, s11, smb, beta, dst_ddf,
3946                                                        mkl_data_type, ne0, ne1 * ne0, ne12 * ne13, mkl_compute_type)));
3947        } else {
3948            const int ne23 = ne12 * ne13;
3949
3950            ggml_sycl_pool_alloc<const void *>         ptrs_src(ctx.pool(), 2 * ne23);
3951            ggml_sycl_pool_alloc<void *>               ptrs_dst(ctx.pool(), 1 * ne23);
3952            ggml_sycl_pool_alloc<matrix_info_t<float>> matrix_info(ctx.host_pool(), 1);
3953
3954            sycl::range<3> block_dims(1, ne12, ne13);
3955            queue->submit([&](sycl::handler & cgh) {
3956                const void ** ptrs_src_get = ptrs_src.get();
3957                void **       ptrs_dst_get = ptrs_dst.get();
3958                size_t        nb12_scaled  = src1->type == GGML_TYPE_F16 ? nb12 : s12 * sizeof(sycl::half);
3959                size_t        nb13_scaled  = src1->type == GGML_TYPE_F16 ? nb13 : s13 * sizeof(sycl::half);
3960                cgh.parallel_for(sycl::nd_range<3>(block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) {
3961                    k_compute_batched_ptrs(src0_f16, src1_f16, dst_ddf, ptrs_src_get, ptrs_dst_get, ne12, ne13, ne23, nb02,
3962                                           nb03, nb12_scaled, nb13_scaled, nbd2, nbd3, r2, r3, item_ct1);
3963                });
3964            });
3965
3966            SYCL_CHECK(CHECK_TRY_ERROR(dpct::gemm_batch(
3967                *queue, oneapi::mkl::transpose::trans, oneapi::mkl::transpose::nontrans, ne01, ne11, ne10, alpha,
3968                (const void **) (ptrs_src.get() + 0 * ne23), dpct::library_data_t::real_half, nb01 / nb00,
3969                (const void **) (ptrs_src.get() + 1 * ne23), dpct::library_data_t::real_half, s11, beta,
3970                (void **) (ptrs_dst.get() + 0 * ne23), mkl_data_type, ne0, ne23, mkl_compute_type, matrix_info.get())));
3971        }
3972    }
3973} catch (const sycl::exception & exc) {
3974    std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl;
3975    std::exit(1);
3976}
3977
3978enum class mul_mat_algo {
3979    DMMV         = 0,
3980    MMVQ         = 1,
3981    MUL_MAT_SYCL = 2,
3982};
3983
3984inline bool ggml_sycl_supports_mmq(enum ggml_type type) {
3985    // TODO: accuracy issues in MMQ
3986    GGML_UNUSED(type);
3987    return false;
3988}
3989
3990inline bool ggml_sycl_supports_reorder_mul_mat_sycl(enum ggml_type type) {
3991    switch (type) {
3992        case GGML_TYPE_Q1_0:
3993        case GGML_TYPE_Q4_0:
3994        case GGML_TYPE_Q8_0:
3995            return true;
3996        case GGML_TYPE_Q2_K:
3997        case GGML_TYPE_Q3_K:
3998        case GGML_TYPE_Q4_K:
3999        case GGML_TYPE_Q5_K:
4000        case GGML_TYPE_Q6_K:
4001            return !g_ggml_sycl_prioritize_dmmv;
4002        default:
4003            return false;
4004    }
4005}
4006
4007inline bool ggml_sycl_supports_reorder_dmmv(enum ggml_type type) {
4008    switch (type) {
4009        case GGML_TYPE_Q1_0:
4010        case GGML_TYPE_Q4_0:
4011        case GGML_TYPE_Q8_0:
4012        case GGML_TYPE_Q2_K:
4013        case GGML_TYPE_Q3_K:
4014        case GGML_TYPE_Q4_K:
4015        case GGML_TYPE_Q5_K:
4016        case GGML_TYPE_Q6_K:
4017            return true;
4018        default:
4019            return false;
4020    }
4021}
4022
4023inline bool ggml_sycl_supports_reorder_mmvq(enum ggml_type type) {
4024    switch (type) {
4025        case GGML_TYPE_Q1_0:
4026        case GGML_TYPE_Q4_0:
4027        case GGML_TYPE_Q8_0:
4028        case GGML_TYPE_Q2_K:
4029        case GGML_TYPE_Q3_K:
4030        case GGML_TYPE_Q4_K:
4031        case GGML_TYPE_Q5_K:
4032        case GGML_TYPE_Q6_K:
4033            return true;
4034        default:
4035            return false;
4036    }
4037}
4038
4039static bool ggml_sycl_supports_reorder_esimd(enum ggml_type type) {
4040#ifdef GGML_SYCL_DMMV_HAS_ESIMD
4041    switch (type) {
4042        case GGML_TYPE_Q2_K:
4043        case GGML_TYPE_Q3_K:
4044        case GGML_TYPE_Q4_K:
4045        case GGML_TYPE_Q5_K:
4046        case GGML_TYPE_Q6_K:
4047            return true;
4048        default:
4049            return false;
4050    }
4051#else
4052    GGML_UNUSED(type);
4053    return false;
4054#endif
4055}
4056
4057static bool ggml_sycl_supports_dmmv(enum ggml_type type) {
4058    switch (type) {
4059        case GGML_TYPE_Q1_0:
4060        case GGML_TYPE_Q4_0:
4061        case GGML_TYPE_Q4_1:
4062        case GGML_TYPE_Q5_0:
4063        case GGML_TYPE_Q5_1:
4064        case GGML_TYPE_Q8_0:
4065        case GGML_TYPE_Q2_K:
4066        case GGML_TYPE_Q3_K:
4067        case GGML_TYPE_Q4_K:
4068        case GGML_TYPE_Q5_K:
4069        case GGML_TYPE_Q6_K:
4070        case GGML_TYPE_F16:
4071        case GGML_TYPE_BF16:
4072            return true;
4073        default:
4074            return false;
4075    }
4076}
4077
4078// Helper functions to unify device memory allocation for both async and sync paths
4079static inline void * sycl_ext_malloc_device(dpct::queue_ptr stream, size_t size) {
4080    bool use_async = g_ggml_sycl_use_async_mem_op;
4081#if defined(GGML_SYCL_GRAPH) && SYCL_EXT_ONEAPI_ASYNC_MEMORY_ALLOC
4082    if (use_async) {
4083        void * ptr = syclex::async_malloc(*stream, sycl::usm::alloc::device, size);
4084        ggml_sycl_memtrace_add(GGML_SYCL_MEM_ASYNC, ptr, size);
4085        return ptr;
4086    }
4087#else
4088    // If async allocation extension is not available, use_async should always be false.
4089    GGML_ASSERT(!use_async);
4090#endif
4091    return ggml_sycl_malloc_device(size, *stream);
4092}
4093
4094static inline void sycl_ext_free(dpct::queue_ptr stream, void * ptr) {
4095    bool use_async = g_ggml_sycl_use_async_mem_op;
4096#if defined(GGML_SYCL_GRAPH) && SYCL_EXT_ONEAPI_ASYNC_MEMORY_ALLOC
4097    if (use_async) {
4098        ggml_sycl_memtrace_del(ptr);
4099        syclex::async_free(*stream, ptr);
4100        return;
4101    }
4102#else
4103    // If async allocation extension is not available, use_async should always be false.
4104    GGML_ASSERT(!use_async);
4105#endif
4106    ggml_sycl_free_device(ptr, *stream);
4107}
4108
4109// RAII wrapper for temporary reorder buffers with optional host memory fallback.
4110// When device allocation fails and GGML_SYCL_HOST_MEM_FALLBACK is enabled,
4111// falls back to host memory so the reorder kernel can still run (over PCIe).
4112// Device access to host memory requires Linux kernel 6.8+ (Ubuntu 26.04+).
4113struct sycl_reorder_temp_buffer {
4114    void *          ptr  = nullptr;
4115    dpct::queue_ptr stream;
4116
4117    sycl_reorder_temp_buffer(dpct::queue_ptr stream, size_t size) : stream(stream) {
4118        ptr = sycl_ext_malloc_device(stream, size);
4119#ifdef GGML_SYCL_HOST_MEM_FALLBACK
4120        if (!ptr) {
4121            ptr = sycl::malloc_host(size, *stream);
4122            if (ptr) {
4123                host_fallback = true;
4124                GGML_LOG_WARN("%s: device alloc of %zu bytes failed, using host memory fallback\n", __func__, size);
4125            }
4126        }
4127#endif
4128    }
4129
4130    ~sycl_reorder_temp_buffer() {
4131        if (!ptr) {
4132            return;
4133        }
4134        if (host_fallback) {
4135            sycl::free(ptr, *stream);
4136        } else {
4137            sycl_ext_free(stream, ptr);
4138        }
4139    }
4140
4141    explicit operator bool() const { return ptr != nullptr; }
4142
4143    sycl_reorder_temp_buffer(const sycl_reorder_temp_buffer &)            = delete;
4144    sycl_reorder_temp_buffer & operator=(const sycl_reorder_temp_buffer &) = delete;
4145
4146private:
4147    bool host_fallback = false;
4148};
4149
4150static bool reorder_qw_q4_0(uint8_t * data_device, const int ncols, const int nrows, size_t size, size_t offset,
4151                            dpct::queue_ptr stream) {
4152    sycl_reorder_temp_buffer tmp(stream, size);
4153    if (!tmp) {
4154        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4155        return false;
4156    }
4157    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4158
4159    sycl::event copy_event;
4160    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4161    if (!g_ggml_sycl_use_async_mem_op) {
4162        copy_event.wait();
4163    }
4164
4165    GGML_ASSERT((size % sizeof(block_q4_0) == 0));
4166    GGML_ASSERT((offset % sizeof(block_q4_0) == 0));
4167    int offset_blks = offset / sizeof(block_q4_0);
4168    auto qs_ptr      = data_device + offset_blks * QK4_0 / 2;
4169    auto d_ptr = (sycl::half*)(qs_ptr + ncols * nrows / 2) + offset_blks;
4170
4171    auto reorder_event = stream->parallel_for(
4172        size / sizeof(block_q4_0),
4173            [=](auto i) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
4174            const block_q4_0* x = (const block_q4_0*)tmp_buf;
4175            const int ib = i;
4176
4177            for (int j = 0; j < QK4_0/2; j ++)
4178            {
4179                *(qs_ptr + ib * QK4_0 / 2 + j) = x[ib].qs[j];
4180            }
4181            *(d_ptr + ib) = x[ib].d;
4182        });
4183    if (!g_ggml_sycl_use_async_mem_op) {
4184        reorder_event.wait_and_throw();
4185    }
4186    return true;
4187}
4188
4189static bool reorder_qw_q8_0(uint8_t * data_device, const int ncols, const int nrows, size_t size, size_t offset,
4190                            dpct::queue_ptr stream) {
4191    sycl_reorder_temp_buffer tmp(stream, size);
4192    if (!tmp) {
4193        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4194        return false;
4195    }
4196    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4197
4198    sycl::event copy_event;
4199    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4200    if (!g_ggml_sycl_use_async_mem_op) {
4201        copy_event.wait();
4202    }
4203
4204    GGML_ASSERT((size % sizeof(block_q8_0) == 0));
4205    GGML_ASSERT((offset % sizeof(block_q8_0) == 0));
4206    int offset_blks = offset / sizeof(block_q8_0);
4207    auto qs_ptr = data_device + offset_blks * QK8_0;
4208    auto d_ptr = (sycl::half*)(qs_ptr + ncols * nrows) + offset_blks;
4209
4210    auto reorder_event = stream->parallel_for(
4211        size / sizeof(block_q8_0),
4212            [=](auto i) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
4213            const block_q8_0* x = (const block_q8_0*)tmp_buf;
4214            const int ib = i;
4215
4216            for (int j = 0; j < QK8_0; j++)
4217            {
4218                *((int8_t*)qs_ptr + ib * QK8_0 + j) = x[ib].qs[j];
4219            }
4220            *(d_ptr + ib) = x[ib].d;
4221        });
4222    if (!g_ggml_sycl_use_async_mem_op) {
4223        reorder_event.wait_and_throw();
4224    }
4225    return true;
4226}
4227
4228static bool reorder_qw_q4_k(uint8_t * data_device, size_t size, size_t offset, dpct::queue_ptr stream) {
4229    GGML_ASSERT(size % sizeof(block_q4_K) == 0);
4230    GGML_ASSERT(offset % sizeof(block_q4_K) == 0);
4231
4232    const int nblocks = size / sizeof(block_q4_K);
4233
4234    sycl_reorder_temp_buffer tmp(stream, size);
4235    if (!tmp) {
4236        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4237        return false;
4238    }
4239    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4240
4241    sycl::event copy_event;
4242    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4243    if (!g_ggml_sycl_use_async_mem_op) {
4244        copy_event.wait();
4245    }
4246
4247    auto * qs_ptr     = data_device;
4248    auto * scales_ptr = qs_ptr + QK_K / 2 * nblocks;
4249    auto * dm_ptr     = (sycl::half2 *) (scales_ptr + K_SCALE_SIZE * nblocks);
4250
4251    auto reorder_event = stream->parallel_for(nblocks, [=](auto i) {
4252        const block_q4_K * x  = (const block_q4_K *) tmp_buf;
4253        const int          ib = i;
4254
4255        for (int j = 0; j < QK_K / 2; ++j) {
4256            qs_ptr[ib * (QK_K / 2) + j] = x[ib].qs[j];
4257        }
4258
4259        for (int j = 0; j < K_SCALE_SIZE; ++j) {
4260            scales_ptr[ib * K_SCALE_SIZE + j] = x[ib].scales[j];
4261        }
4262
4263        dm_ptr[ib] = x[ib].dm;
4264    });
4265    if (!g_ggml_sycl_use_async_mem_op) {
4266        reorder_event.wait_and_throw();
4267    }
4268    return true;
4269}
4270
4271// Reorder each expert slice into a self-contained SoA layout.
4272static bool reorder_qw_q4_k_moe(uint8_t * data_device, size_t expert_bytes, int64_t n_expert, dpct::queue_ptr stream) {
4273    GGML_ASSERT(expert_bytes % sizeof(block_q4_K) == 0);
4274    const int    blocks_per_expert = (int) (expert_bytes / sizeof(block_q4_K));
4275    const size_t total_bytes       = expert_bytes * (size_t) n_expert;
4276
4277    sycl_reorder_temp_buffer tmp(stream, total_bytes);
4278    if (!tmp) {
4279        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, total_bytes);
4280        return false;
4281    }
4282    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4283
4284    sycl::event copy_event;
4285    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, total_bytes)));
4286    if (!g_ggml_sycl_use_async_mem_op) {
4287        copy_event.wait();
4288    }
4289
4290    const int total_blocks = blocks_per_expert * (int) n_expert;
4291    auto reorder_event = stream->parallel_for(total_blocks, [=](auto gb_) {
4292        const int          gb   = gb_;
4293        const int          e    = gb / blocks_per_expert;
4294        const int          ib   = gb % blocks_per_expert;
4295        const block_q4_K * x    = (const block_q4_K *) (tmp_buf + (size_t) e * expert_bytes);
4296        uint8_t *          base = data_device + (size_t) e * expert_bytes;
4297
4298        auto * qs_ptr     = base;
4299        auto * scales_ptr = qs_ptr + QK_K / 2 * blocks_per_expert;
4300        auto * dm_ptr     = (sycl::half2 *) (scales_ptr + K_SCALE_SIZE * blocks_per_expert);
4301
4302        for (int j = 0; j < QK_K / 2; ++j) {
4303            qs_ptr[ib * (QK_K / 2) + j] = x[ib].qs[j];
4304        }
4305        for (int j = 0; j < K_SCALE_SIZE; ++j) {
4306            scales_ptr[ib * K_SCALE_SIZE + j] = x[ib].scales[j];
4307        }
4308        dm_ptr[ib] = x[ib].dm;
4309    });
4310    if (!g_ggml_sycl_use_async_mem_op) {
4311        reorder_event.wait_and_throw();
4312    }
4313    return true;
4314}
4315
4316// Reorder each Q5_K expert slice into [qs][qh][scales][dm].
4317static bool reorder_qw_q5_k_moe(uint8_t * data_device, size_t expert_bytes, int64_t n_expert, dpct::queue_ptr stream) {
4318    GGML_ASSERT(expert_bytes % sizeof(block_q5_K) == 0);
4319    const int    blocks_per_expert = (int) (expert_bytes / sizeof(block_q5_K));
4320    const size_t total_bytes       = expert_bytes * (size_t) n_expert;
4321
4322    sycl_reorder_temp_buffer tmp(stream, total_bytes);
4323    if (!tmp) {
4324        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, total_bytes);
4325        return false;
4326    }
4327    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4328
4329    sycl::event copy_event;
4330    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, total_bytes)));
4331    if (!g_ggml_sycl_use_async_mem_op) {
4332        copy_event.wait();
4333    }
4334
4335    const int total_blocks = blocks_per_expert * (int) n_expert;
4336    auto reorder_event = stream->parallel_for(total_blocks, [=](auto gb_) {
4337        const int          gb   = gb_;
4338        const int          e    = gb / blocks_per_expert;
4339        const int          ib   = gb % blocks_per_expert;
4340        const block_q5_K * x    = (const block_q5_K *) (tmp_buf + (size_t) e * expert_bytes);
4341        uint8_t *          base = data_device + (size_t) e * expert_bytes;
4342
4343        auto * qs_ptr     = base;
4344        auto * qh_ptr     = qs_ptr + (QK_K / 2) * blocks_per_expert;
4345        auto * scales_ptr = qh_ptr + (QK_K / 8) * blocks_per_expert;
4346        auto * dm_ptr     = (sycl::half2 *) (scales_ptr + K_SCALE_SIZE * blocks_per_expert);
4347
4348        for (int j = 0; j < QK_K / 2; ++j) {
4349            qs_ptr[ib * (QK_K / 2) + j] = x[ib].qs[j];
4350        }
4351        for (int j = 0; j < QK_K / 8; ++j) {
4352            qh_ptr[ib * (QK_K / 8) + j] = x[ib].qh[j];
4353        }
4354        for (int j = 0; j < K_SCALE_SIZE; ++j) {
4355            scales_ptr[ib * K_SCALE_SIZE + j] = x[ib].scales[j];
4356        }
4357        dm_ptr[ib] = x[ib].dm;
4358    });
4359    if (!g_ggml_sycl_use_async_mem_op) {
4360        reorder_event.wait_and_throw();
4361    }
4362    return true;
4363}
4364
4365// Reorder each Q6_K expert slice into [ql][qh][scales][d].
4366static bool reorder_qw_q6_k_moe(uint8_t * data_device, size_t expert_bytes, int64_t n_expert, dpct::queue_ptr stream) {
4367    GGML_ASSERT(expert_bytes % sizeof(block_q6_K) == 0);
4368    const int    blocks_per_expert = (int) (expert_bytes / sizeof(block_q6_K));
4369    const size_t total_bytes       = expert_bytes * (size_t) n_expert;
4370
4371    sycl_reorder_temp_buffer tmp(stream, total_bytes);
4372    if (!tmp) {
4373        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, total_bytes);
4374        return false;
4375    }
4376    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4377
4378    sycl::event copy_event;
4379    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, total_bytes)));
4380    if (!g_ggml_sycl_use_async_mem_op) {
4381        copy_event.wait();
4382    }
4383
4384    const int total_blocks = blocks_per_expert * (int) n_expert;
4385    auto reorder_event = stream->parallel_for(total_blocks, [=](auto gb_) {
4386        const int          gb   = gb_;
4387        const int          e    = gb / blocks_per_expert;
4388        const int          ib   = gb % blocks_per_expert;
4389        const block_q6_K * x    = (const block_q6_K *) (tmp_buf + (size_t) e * expert_bytes);
4390        uint8_t *          base = data_device + (size_t) e * expert_bytes;
4391
4392        auto * ql_ptr     = base;
4393        auto * qh_ptr     = ql_ptr + (QK_K / 2) * blocks_per_expert;
4394        auto * scales_ptr = qh_ptr + (QK_K / 4) * blocks_per_expert;
4395        auto * d_ptr      = (sycl::half *) (scales_ptr + (QK_K / 16) * blocks_per_expert);
4396
4397        for (int j = 0; j < QK_K / 2; ++j) {
4398            ql_ptr[ib * (QK_K / 2) + j] = x[ib].ql[j];
4399        }
4400        for (int j = 0; j < QK_K / 4; ++j) {
4401            qh_ptr[ib * (QK_K / 4) + j] = x[ib].qh[j];
4402        }
4403        for (int j = 0; j < QK_K / 16; ++j) {
4404            scales_ptr[ib * (QK_K / 16) + j] = x[ib].scales[j];
4405        }
4406        d_ptr[ib] = x[ib].d;
4407    });
4408    if (!g_ggml_sycl_use_async_mem_op) {
4409        reorder_event.wait_and_throw();
4410    }
4411    return true;
4412}
4413
4414static bool reorder_qw_q2_k(uint8_t * data_device, size_t size, size_t offset, dpct::queue_ptr stream) {
4415    GGML_ASSERT(size % sizeof(block_q2_K) == 0);
4416    GGML_ASSERT(offset % sizeof(block_q2_K) == 0);
4417
4418    const int nblocks = size / sizeof(block_q2_K);
4419
4420    sycl_reorder_temp_buffer tmp(stream, size);
4421    if (!tmp) {
4422        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4423        return false;
4424    }
4425    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4426
4427    sycl::event copy_event;
4428    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4429    if (!g_ggml_sycl_use_async_mem_op) {
4430        copy_event.wait();
4431    }
4432
4433    auto *        qs_ptr     = data_device;
4434    auto *        scales_ptr = qs_ptr + (QK_K / 4) * nblocks;
4435    sycl::half2 * dm_ptr     = (sycl::half2 *) (scales_ptr + (QK_K / 16) * nblocks);
4436
4437    auto reorder_event = stream->parallel_for(nblocks, [=](auto i) {
4438        const block_q2_K * x  = (const block_q2_K *) tmp_buf;
4439        const int          ib = i;
4440
4441        for (int j = 0; j < QK_K / 4; ++j) {
4442            qs_ptr[ib * (QK_K / 4) + j] = x[ib].qs[j];
4443        }
4444
4445        for (int j = 0; j < QK_K / 16; ++j) {
4446            scales_ptr[ib * (QK_K / 16) + j] = x[ib].scales[j];
4447        }
4448
4449        dm_ptr[ib] = x[ib].dm;
4450    });
4451    if (!g_ggml_sycl_use_async_mem_op) {
4452        reorder_event.wait_and_throw();
4453    }
4454    return true;
4455}
4456
4457static bool reorder_qw_q3_k(uint8_t * data_device, size_t size, size_t offset, dpct::queue_ptr stream) {
4458    GGML_ASSERT(size % sizeof(block_q3_K) == 0);
4459    GGML_ASSERT(offset % sizeof(block_q3_K) == 0);
4460
4461    const int nblocks = size / sizeof(block_q3_K);
4462
4463    sycl_reorder_temp_buffer tmp(stream, size);
4464    if (!tmp) {
4465        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4466        return false;
4467    }
4468    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4469
4470    sycl::event copy_event;
4471    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4472    if (!g_ggml_sycl_use_async_mem_op) {
4473        copy_event.wait();
4474    }
4475
4476    auto *       qs_ptr     = data_device;
4477    auto *       hmask_ptr  = qs_ptr + (QK_K / 4) * nblocks;
4478    auto *       scales_ptr = hmask_ptr + (QK_K / 8) * nblocks;
4479    sycl::half * d_ptr      = (sycl::half *) (scales_ptr + 12 * nblocks);
4480
4481    auto reorder_event = stream->parallel_for(nblocks, [=](auto i) {
4482        const block_q3_K * x  = (const block_q3_K *) tmp_buf;
4483        const int          ib = i;
4484
4485        for (int j = 0; j < QK_K / 4; ++j) {
4486            qs_ptr[ib * (QK_K / 4) + j] = x[ib].qs[j];
4487        }
4488
4489        for (int j = 0; j < QK_K / 8; ++j) {
4490            hmask_ptr[ib * (QK_K / 8) + j] = x[ib].hmask[j];
4491        }
4492
4493        for (int j = 0; j < 12; ++j) {
4494            scales_ptr[ib * 12 + j] = x[ib].scales[j];
4495        }
4496
4497        d_ptr[ib] = x[ib].d;
4498    });
4499    if (!g_ggml_sycl_use_async_mem_op) {
4500        reorder_event.wait_and_throw();
4501    }
4502    return true;
4503}
4504
4505static bool reorder_qw_q5_k(uint8_t * data_device, size_t size, size_t offset, dpct::queue_ptr stream) {
4506    GGML_ASSERT(size % sizeof(block_q5_K) == 0);
4507    GGML_ASSERT(offset % sizeof(block_q5_K) == 0);
4508
4509    const int nblocks = size / sizeof(block_q5_K);
4510
4511    sycl_reorder_temp_buffer tmp(stream, size);
4512    if (!tmp) {
4513        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4514        return false;
4515    }
4516    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4517
4518    sycl::event copy_event;
4519    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4520    if (!g_ggml_sycl_use_async_mem_op) {
4521        copy_event.wait();
4522    }
4523
4524    auto * qs_ptr     = data_device;
4525    auto * qh_ptr     = qs_ptr + (QK_K / 2) * nblocks;
4526    auto * scales_ptr = qh_ptr + (QK_K / 8) * nblocks;
4527    auto * dm_ptr     = (sycl::half2 *) (scales_ptr + K_SCALE_SIZE * nblocks);
4528
4529    auto reorder_event = stream->parallel_for(nblocks, [=](auto i) {
4530        const block_q5_K * x  = (const block_q5_K *) tmp_buf;
4531        const int          ib = i;
4532
4533        for (int j = 0; j < QK_K / 2; ++j) {
4534            qs_ptr[ib * (QK_K / 2) + j] = x[ib].qs[j];
4535        }
4536
4537        for (int j = 0; j < QK_K / 8; ++j) {
4538            qh_ptr[ib * (QK_K / 8) + j] = x[ib].qh[j];
4539        }
4540
4541        for (int j = 0; j < K_SCALE_SIZE; ++j) {
4542            scales_ptr[ib * K_SCALE_SIZE + j] = x[ib].scales[j];
4543        }
4544
4545        dm_ptr[ib] = x[ib].dm;
4546    });
4547    if (!g_ggml_sycl_use_async_mem_op) {
4548        reorder_event.wait_and_throw();
4549    }
4550    return true;
4551}
4552
4553static bool reorder_qw_q6_k(uint8_t * data_device, size_t size, size_t offset, dpct::queue_ptr stream) {
4554    GGML_ASSERT(size % sizeof(block_q6_K) == 0);
4555    GGML_ASSERT(offset % sizeof(block_q6_K) == 0);
4556
4557    const int nblocks = size / sizeof(block_q6_K);
4558
4559    sycl_reorder_temp_buffer tmp(stream, size);
4560    if (!tmp) {
4561        GGML_LOG_WARN("%s: failed to allocate %zu bytes for reorder temp buffer, skipping reorder\n", __func__, size);
4562        return false;
4563    }
4564    uint8_t * tmp_buf = static_cast<uint8_t *>(tmp.ptr);
4565
4566    sycl::event copy_event;
4567    SYCL_CHECK(CHECK_TRY_ERROR(copy_event = stream->memcpy(tmp_buf, data_device, size)));
4568    if (!g_ggml_sycl_use_async_mem_op) {
4569        copy_event.wait();
4570    }
4571
4572    auto *       ql_ptr     = data_device;
4573    auto *       qh_ptr     = ql_ptr + (QK_K / 2) * nblocks;
4574    auto *       scales_ptr = qh_ptr + (QK_K / 4) * nblocks;
4575    sycl::half * dm_ptr     = (sycl::half *) (scales_ptr + (QK_K / 16) * nblocks);
4576
4577    auto reorder_event = stream->parallel_for(nblocks, [=](auto i) {
4578        const block_q6_K * x  = (const block_q6_K *) tmp_buf;
4579        const int          ib = i;
4580
4581        const uint8_t * ql              = x[ib].ql;
4582        const uint8_t * qh              = x[ib].qh;
4583        uint8_t *       base_ql_ptr     = ql_ptr + (QK_K / 2) * ib;
4584        uint8_t *       base_qh_ptr     = qh_ptr + (QK_K / 4) * ib;
4585        uint8_t *       base_scales_ptr = scales_ptr + (QK_K / 16) * ib;
4586
4587        for (int j = 0; j < QK_K / 2; ++j) {
4588            base_ql_ptr[j] = ql[j];
4589        }
4590        for (int j = 0; j < QK_K / 4; ++j) {
4591            base_qh_ptr[j] = qh[j];
4592        }
4593
4594        for (int j = 0; j < QK_K / 16; ++j) {
4595            base_scales_ptr[j] = x[ib].scales[j];
4596        }
4597
4598        dm_ptr[ib] = x[ib].d;
4599    });
4600    if (!g_ggml_sycl_use_async_mem_op) {
4601        reorder_event.wait_and_throw();
4602    }
4603    return true;
4604}
4605
4606static bool reorder_qw(const ggml_tensor * src0, dpct::queue_ptr stream) {
4607    uint8_t * data_device = (uint8_t *) src0->data;
4608    size_t ncols = src0->ne[0];
4609    size_t nrows = src0->ne[1];
4610    size_t size = ggml_nbytes(src0);
4611
4612    // MoE expert weights are addressed per expert via nb[2], so each slice must
4613    // remain self-contained after reorder.
4614    if (src0->ne[2] > 1) {
4615        GGML_ASSERT((size_t) size == (size_t) src0->ne[2] * src0->nb[2]);
4616        switch (src0->type) {
4617            case GGML_TYPE_Q4_K:
4618                return reorder_qw_q4_k_moe(data_device, src0->nb[2], src0->ne[2], stream);
4619            case GGML_TYPE_Q5_K:
4620                return reorder_qw_q5_k_moe(data_device, src0->nb[2], src0->ne[2], stream);
4621            case GGML_TYPE_Q6_K:
4622                return reorder_qw_q6_k_moe(data_device, src0->nb[2], src0->ne[2], stream);
4623            default:
4624                return false;
4625        }
4626    }
4627
4628    switch (src0->type) {
4629        case GGML_TYPE_Q4_0:
4630            return reorder_qw_q4_0(data_device, ncols, nrows, size, 0, stream);
4631        case GGML_TYPE_Q8_0:
4632            return reorder_qw_q8_0(data_device, ncols, nrows, size, 0, stream);
4633        case GGML_TYPE_Q2_K:
4634            return reorder_qw_q2_k(data_device, size, 0, stream);
4635        case GGML_TYPE_Q3_K:
4636            return reorder_qw_q3_k(data_device, size, 0, stream);
4637        case GGML_TYPE_Q4_K:
4638            return reorder_qw_q4_k(data_device, size, 0, stream);
4639        case GGML_TYPE_Q5_K:
4640            return reorder_qw_q5_k(data_device, size, 0, stream);
4641        case GGML_TYPE_Q6_K:
4642            return reorder_qw_q6_k(data_device, size, 0, stream);
4643        default:
4644            return false;
4645    }
4646}
4647
4648static bool should_reorder_tensor(ggml_backend_sycl_context& ctx, const ggml_tensor * dst) {
4649    return g_ggml_sycl_enable_optimize && //allow optimize, controlled by $GGML_SYCL_ENABLE_OPT
4650           ctx.opt_feature.reorder &&      //allow this device due to good perf, skip the devices with bad perf.
4651           dst->op == GGML_OP_MUL_MAT &&   //limit to some supported cases of Q4_0, to do for more cases.
4652           // ne[1] <= 8 so multi-column decode (spec / MTP verify) also bootstraps the reorder;
4653           // all reorderable types have a _switch_ncols kernel.
4654           dst->src[1]->ne[1] <= 8 && dst->src[1]->ne[2]==1 && dst->src[1]->ne[3]==1;
4655}
4656
4657static void opt_for_reorder(ggml_backend_sycl_context * ctx, const ggml_tensor * src0, const ggml_tensor * /* src1 */,
4658                            ggml_tensor * dst, mul_mat_algo mm_algorithm) {
4659    if (!should_reorder_tensor(*ctx, dst)) {
4660        return;
4661    }
4662
4663    ggml_tensor_extra_gpu * extra = static_cast<ggml_tensor_extra_gpu *>(src0->extra);
4664    if (!extra || extra->optimized_feature.reorder) {
4665        return;  // Skip permutations and already reordered tensors
4666    }
4667
4668    switch (mm_algorithm) {
4669        case mul_mat_algo::DMMV:
4670            if (!ggml_sycl_supports_reorder_dmmv(src0->type)) {
4671                return;
4672            }
4673            break;
4674        case mul_mat_algo::MMVQ:
4675            if (!ggml_sycl_supports_reorder_mmvq(src0->type)) {
4676                return;
4677            }
4678            break;
4679        case mul_mat_algo::MUL_MAT_SYCL:
4680            if (!ggml_sycl_supports_reorder_mul_mat_sycl(src0->type)) {
4681                return;
4682            }
4683            break;
4684    }
4685
4686    if (reorder_qw(src0, ctx->stream())) {
4687        extra->optimized_feature.reorder = true;  // Used to decode/dequan in next steps and avoid re-reordering
4688    }
4689}
4690
4691// Lazily reorder supported MoE expert weights once their fused path is used.
4692static void opt_for_reorder_id(ggml_backend_sycl_context * ctx, const ggml_tensor * src0) {
4693    if (!g_ggml_sycl_enable_optimize || !ctx->opt_feature.reorder) {
4694        return;
4695    }
4696    if (src0->type != GGML_TYPE_Q4_K && src0->type != GGML_TYPE_Q5_K && src0->type != GGML_TYPE_Q6_K) {
4697        return;
4698    }
4699    ggml_tensor_extra_gpu * extra = static_cast<ggml_tensor_extra_gpu *>(src0->extra);
4700    if (!extra || extra->optimized_feature.reorder) {
4701        return;
4702    }
4703    if (reorder_qw(src0, ctx->stream())) {
4704        extra->optimized_feature.reorder = true;
4705    }
4706}
4707
4708
4709static bool can_use_dequantize_mul_mat_vec(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
4710    // The F16/BF16 qk=1 kernel iterates with stride 2*DMMV_X, requiring ne[0] to be
4711    // a multiple of 2*DMMV_X. Quantized types use block-structured kernels that only
4712    // need ne[0] % DMMV_X == 0.
4713    const int64_t dmmv_x_required = (src0->type == GGML_TYPE_BF16 || src0->type == GGML_TYPE_F16) ?
4714                                    2*GGML_SYCL_DMMV_X : GGML_SYCL_DMMV_X;
4715    return ggml_sycl_supports_dmmv(src0->type) && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32 &&
4716           src0->ne[0] % dmmv_x_required == 0 && src1->ne[1] == 1;
4717}
4718
4719static bool can_use_mul_mat_vec_q(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
4720    return ggml_is_quantized(src0->type) && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32 &&
4721           src1->ne[1] <= MMVQ_MAX_BATCH_SIZE;
4722}
4723
4724static void ggml_sycl_mul_mat(ggml_backend_sycl_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
4725    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
4726
4727    // Handle HADAMARAD hint given from further up the pipeline and pass it to the correct
4728    // kernel.
4729    //
4730    // The op check is not redundant: this backend also routes MUL_MAT_ID through here with a
4731    // stack copy of dst, which carries MUL_MAT_ID's own op_params. ggml_mul_mat_set_hint()
4732    // asserts GGML_OP_MUL_MAT for the same reason.
4733    if (dst->op == GGML_OP_MUL_MAT && ggml_get_op_params_i32(dst, 1) == GGML_HINT_SRC0_IS_HADAMARD &&
4734        ggml_sycl_op_fwht(ctx, src1, dst)) {
4735        return;
4736    }
4737
4738    const bool split = ggml_backend_buffer_is_sycl_split(src0->buffer);
4739    int64_t min_compute_capability = INT_MAX;
4740
4741    if (split) {
4742        ggml_backend_sycl_split_buffer_type_context * buft_ctx =
4743            (ggml_backend_sycl_split_buffer_type_context *) src0->buffer->buft->context;
4744        auto & tensor_split = buft_ctx->tensor_split;
4745        for (int id = 0; id < ggml_sycl_info().device_count; ++id) {
4746            // skip devices that are not going to do any work:
4747            if (tensor_split[id] >= (id + 1 < ggml_sycl_info().device_count ? tensor_split[id + 1] : 1.0f)) {
4748                continue;
4749            }
4750
4751            if (min_compute_capability > ggml_sycl_info().devices[id].cc) {
4752                min_compute_capability = ggml_sycl_info().devices[id].cc;
4753            }
4754        }
4755    } else {
4756        min_compute_capability = ggml_sycl_info().devices[ctx.device].cc;
4757    }
4758
4759    // check data types and tensor shapes for custom matrix multiplication kernels:
4760    bool use_dequantize_mul_mat_vec = can_use_dequantize_mul_mat_vec(src0, src1, dst);
4761
4762    bool use_mul_mat_vec_q = can_use_mul_mat_vec_q(src0, src1, dst);
4763
4764    bool use_mul_mat_q =  ggml_sycl_supports_mmq(src0->type)
4765        && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32;
4766
4767
4768    // mmvq and mmq need the __dp4a instruction which is available for gen12+
4769    // Workaround in https://github.com/ggml-org/llama.cpp/commit/95f84d5ce8b449a9b16009434aca800df504a02e
4770    use_mul_mat_q = use_mul_mat_q && (src0->type != GGML_TYPE_IQ2_XXS);
4771#ifdef SYCL_USE_XMX
4772    use_mul_mat_q = use_mul_mat_q && (src1->ne[1] <= MMQ_MAX_BATCH_SIZE);
4773#endif // SYCL_USE_XMX
4774
4775    // When reorder is enabled, both ESIMD, MMVQ and DMMV kernels may be used. For
4776    // best performance use ESIMD when supported, followed by MMVQ, and finally DMMV.
4777    // But the reordered ESIMD path cannot be used without reordered MMVQ. A later
4778    // multi-token call (ne[1] in 2..8) will take the MMVQ path and it would read the
4779    // reordered bytes as if they were still the unreordered layout.
4780
4781    if (!g_ggml_sycl_prioritize_dmmv && ((should_reorder_tensor(ctx, dst) &&
4782                                          ggml_sycl_supports_reorder_mmvq(src0->type)))) {
4783        bool use = g_ggml_sycl_enable_esimd && ggml_sycl_supports_reorder_esimd(src0->type);
4784        // Arc770 get benefit with Q4_0 by skipping MMVQ path
4785        if (!(ggml_sycl_info().devices[ctx.device].hw_info.arch ==
4786                    gpu_arch::intel_gpu_acm_g10 &&
4787                src0->type == GGML_TYPE_Q4_0)) {
4788            use = use || !use_mul_mat_vec_q;
4789        }
4790        use_dequantize_mul_mat_vec = use_dequantize_mul_mat_vec && use;
4791    }
4792
4793    if (!split && src0->type == GGML_TYPE_F16 && ggml_is_permuted(src0) && ggml_is_permuted(src1) && src1->ne[1] == 1) {
4794        // TODO: Refactor and cleanup of mul mat dispatching.
4795        if (src0->ne[3] == 1 && src1->ne[3] == 1) {
4796            // KQ single-batch
4797            // mmv p021 was specific for these dimensions
4798            ggml_sycl_mul_mat_vec_p021(ctx, src0, src1, dst);
4799        } else {
4800            // The kernel from the if path is faster for that specific case, but does not support all mul mats.
4801            ggml_sycl_mul_mat_batched_sycl(ctx, src0, src1, dst);
4802        }
4803    } else if (!split && src0->type == GGML_TYPE_F16 && !ggml_is_contiguous(src0) && !ggml_is_transposed(src1) && src1->ne[1] == 1 && src1->ne[3] == 1) {
4804        // KQV single-batch
4805        ggml_sycl_mul_mat_vec_nc(ctx, src0, src1, dst);
4806    } else if (!split && src0->type == GGML_TYPE_F16 && !ggml_is_transposed(src0) && !ggml_is_transposed(src1) && src1->ne[2] * src1->ne[3] > 1) {
4807        // KQ + KQV multi-batch
4808        ggml_sycl_mul_mat_batched_sycl(ctx, src0, src1, dst);
4809    } else if (use_dequantize_mul_mat_vec) {
4810        opt_for_reorder(&ctx, src0, src1, dst, mul_mat_algo::DMMV);
4811        ggml_sycl_op_mul_mat<no_quantize_q8_1>(ctx, src0, src1, dst, ggml_sycl_op_dequantize_mul_mat_vec);
4812    } else if (use_mul_mat_vec_q) {
4813        opt_for_reorder(&ctx, src0, src1, dst, mul_mat_algo::MMVQ);
4814        ggml_tensor_extra_gpu * extra = static_cast<ggml_tensor_extra_gpu *>(src0->extra);
4815        if (extra && extra->optimized_feature.reorder) {
4816            ggml_sycl_op_mul_mat<quantize_and_reorder_q8_1_soa>(ctx, src0, src1, dst, ggml_sycl_op_mul_mat_vec_q);
4817        } else {
4818            ggml_sycl_op_mul_mat<quantize_q8_1>(ctx, src0, src1, dst, ggml_sycl_op_mul_mat_vec_q);
4819        }
4820    } else if (use_mul_mat_q) {
4821        ggml_sycl_op_mul_mat<quantize_q8_1>(ctx, src0, src1, dst, ggml_sycl_op_mul_mat_q);
4822    } else {
4823        ggml_sycl_op_mul_mat<no_quantize_q8_1>(ctx, src0, src1, dst, ggml_sycl_op_mul_mat_sycl);
4824    }
4825}
4826
4827// Fused dense-FFN mat-vec for the {mul_mat(gate), mul_mat(up), GLU} subgraph at node_idx.
4828// Returns false if it declined, in which case the caller runs the three nodes normally.
4829static bool ggml_sycl_mul_mat_glu_mmvq_fused(ggml_backend_sycl_context & ctx, ggml_cgraph * cgraph, int node_idx) {
4830    if (!ggml_sycl_can_fuse(cgraph, node_idx, { GGML_OP_MUL_MAT, GGML_OP_MUL_MAT, GGML_OP_GLU }, {})) {
4831        return false;
4832    }
4833
4834    ggml_tensor *       glu  = cgraph->nodes[node_idx + 2];
4835    ggml_tensor *       gate = glu->src[0];
4836    ggml_tensor *       up   = glu->src[1];
4837    const ggml_tensor * wu   = up->src[0];
4838    const ggml_tensor * wg   = gate->src[0];
4839    const ggml_tensor * act  = up->src[1];
4840
4841    // this writes glu->data directly rather than the per-device row slices that
4842    // ggml_sycl_op_mul_mat() stitches back together, so it cannot serve split weights
4843    if (ggml_backend_buffer_is_sycl_split(wu->buffer) || ggml_backend_buffer_is_sycl_split(wg->buffer)) {
4844        return false;
4845    }
4846
4847    // with DMMV prioritised the unfused path would not have gone through mmvq at all
4848    if (g_ggml_sycl_prioritize_dmmv) {
4849        return false;
4850    }
4851
4852    // install the reorder (SoA) layout the fused kernel needs, as the unfused mmvq path would;
4853    // a no-op once done. after the bail checks so a declined op does not pay for it.
4854    opt_for_reorder(&ctx, wu, act, up, mul_mat_algo::MMVQ);
4855    opt_for_reorder(&ctx, wg, act, gate, mul_mat_algo::MMVQ);
4856
4857    const auto * extra_u = static_cast<const ggml_tensor_extra_gpu *>(wu->extra);
4858    const auto * extra_g = static_cast<const ggml_tensor_extra_gpu *>(wg->extra);
4859    if (!extra_u || !extra_g || !extra_u->optimized_feature.reorder || !extra_g->optimized_feature.reorder) {
4860        return false;
4861    }
4862
4863    // log the up mat-mul: glu's own srcs are the two intermediates the fusion never materialises
4864    scope_op_debug_print scope_dbg_print(__func__, up, /*num_src=*/2, " : fused with gate + GLU");
4865
4866    const int64_t ne00 = wu->ne[0];
4867    const int64_t ne11 = act->ne[1];
4868
4869    const queue_ptr stream           = ctx.stream();
4870    const int       src1_padded_cols = GGML_PAD((int) ne00, MATRIX_ROW_PADDING);
4871
4872    // one activation, quantized once and fully consumed into src1_ddq before the GEMV on this
4873    // in-order queue, so glu->data aliasing the dead activation needs no memory-range check
4874    ggml_sycl_pool_alloc<char> src1_q8_alloc(ctx.pool(),
4875                                             (size_t) ne11 * src1_padded_cols * sizeof(block_q8_1) / QK8_1);
4876    char *                     src1_ddq = src1_q8_alloc.get();
4877
4878    quantize_row_q8_1_sycl<quantize_and_reorder_q8_1_soa>((const float *) act->data, src1_ddq, (int) ne00, (int) ne11,
4879                                                          src1_padded_cols, stream);
4880
4881    return ggml_sycl_mul_mat_vec_q_glu_reorder(wu->type, ggml_get_glu_op(glu), wu->data, wg->data, src1_ddq,
4882                                               (float *) glu->data, (int) ne00, (int) wu->ne[1], (int) ne11,
4883                                               /*stride_col_y_bytes=*/src1_padded_cols * (int) sizeof(block_q8_1) /
4884                                                   QK8_1,
4885                                               /*stride_col_dst=*/(int) glu->ne[0], stream);
4886}
4887
4888// Batch the run of consecutive L2_NORM siblings starting at node_idx into one launch.
4889// Returns the number of extra graph nodes consumed, or 0 if the run is shorter than two
4890// (the caller then runs the norm through the per-tensor kernel).
4891static int ggml_sycl_l2_norm_batch_fused(ggml_backend_sycl_context & ctx, ggml_cgraph * cgraph, int node_idx) {
4892    const ggml_tensor * node = cgraph->nodes[node_idx];
4893    if (ggml_sycl_info().device_count != 1 || node->type != GGML_TYPE_F32 ||
4894        node->src[0]->type != GGML_TYPE_F32 || node->src[0]->ne[0] >= 1024) {
4895        return 0;
4896    }
4897
4898    ggml_tensor * batch[GGML_SYCL_L2_BATCH_MAX];
4899    int           count = 0;
4900    int           last  = node_idx;
4901    float         eps0;
4902    memcpy(&eps0, node->op_params, sizeof(float));
4903
4904    // Conservative aliasing test: the batched norms run concurrently in one kernel,
4905    // so none may read what another writes, and none may write where another writes.
4906    auto overlaps = [](const ggml_tensor * a, const ggml_tensor * b) {
4907        const char * ab = (const char *) a->data;
4908        const char * bb = (const char *) b->data;
4909        return ab < bb + ggml_nbytes(b) && bb < ab + ggml_nbytes(a);
4910    };
4911
4912    for (int j = node_idx; j < cgraph->n_nodes && count < GGML_SYCL_L2_BATCH_MAX; ++j) {
4913        ggml_tensor * nj = cgraph->nodes[j];
4914        if (ggml_is_empty(nj) || nj->op == GGML_OP_RESHAPE || nj->op == GGML_OP_TRANSPOSE ||
4915            nj->op == GGML_OP_VIEW || nj->op == GGML_OP_PERMUTE || nj->op == GGML_OP_NONE ||
4916            (nj->flags & GGML_TENSOR_FLAG_COMPUTE) == 0) {
4917            continue;  // not a launch; cannot break a run of adjacent norms
4918        }
4919        if (nj->op != GGML_OP_L2_NORM || nj->type != GGML_TYPE_F32 ||
4920            nj->src[0]->type != GGML_TYPE_F32 || !ggml_are_same_shape(nj, node) ||
4921            !ggml_are_same_shape(nj->src[0], node->src[0])) {
4922            break;  // any other launch ends the run
4923        }
4924        bool same_nb = true;
4925        for (int d = 0; d < GGML_MAX_DIMS; ++d) {
4926            if (nj->nb[d] != node->nb[d] || nj->src[0]->nb[d] != node->src[0]->nb[d]) {
4927                same_nb = false;
4928                break;
4929            }
4930        }
4931        if (!same_nb) {
4932            break;  // one nb[] stride set is shared by the whole batch
4933        }
4934        float epsj;
4935        memcpy(&epsj, nj->op_params, sizeof(float));
4936        if (epsj != eps0) {
4937            break;  // eps mismatch ends the run
4938        }
4939        bool indep = true;
4940        for (int k = 0; k < count; ++k) {
4941            if (overlaps(nj->src[0], batch[k]) || overlaps(nj, batch[k])) {
4942                indep = false;
4943                break;
4944            }
4945        }
4946        if (!indep) {
4947            break;  // an overlapping tensor would race inside one launch
4948        }
4949        batch[count++] = nj;
4950        last           = j;
4951    }
4952    if (count < 2) {
4953        return 0;  // a lone norm falls through to the per-tensor kernel
4954    }
4955    ggml_sycl_l2_norm_batch(ctx, batch, count);
4956    return last - node_idx;
4957}
4958
4959
4960__dpct_inline__ static void k_copy_src1_to_contiguous(
4961    const char *__restrict__ src1_original, char *__restrict__ src1_contiguous,
4962    const mmid_row_mapping *__restrict__ row_mapping,
4963    int64_t ne11, int64_t ne10, size_t nb11, size_t nb12,
4964    const sycl::nd_item<3> &item_ct1) {
4965    const int32_t src1_row = item_ct1.get_group(2);
4966
4967    const int32_t iid1 = row_mapping[src1_row].i2;
4968    const int32_t id   = row_mapping[src1_row].i1;
4969
4970    const int64_t i11 = id % ne11;
4971    const int64_t i12 = iid1;
4972
4973    const float * src1_row_original = (const float *)(src1_original + i11*nb11 + i12*nb12);
4974    float * src1_row_contiguous = (float *)(src1_contiguous + src1_row*nb11);
4975
4976#pragma unroll
4977    for (int i = item_ct1.get_local_id(2); i < ne10;
4978         i += item_ct1.get_local_range(2)) {
4979        src1_row_contiguous[i] = src1_row_original[i];
4980    }
4981}
4982
4983__dpct_inline__ static void k_copy_dst_from_contiguous(
4984    char *__restrict__ dst_original, const char *__restrict__ dst_contiguous,
4985    const mmid_row_mapping *__restrict__ row_mapping, int64_t ne0, size_t nb1,
4986    size_t nb2, const sycl::nd_item<3> &item_ct1) {
4987    int32_t i = item_ct1.get_group(2);
4988
4989    const int32_t i1 = row_mapping[i].i1;
4990    const int32_t i2 = row_mapping[i].i2;
4991
4992    const float * dst_row_contiguous = (const float *)(dst_contiguous + i*nb1);
4993    float * dst_row_original = (float *)(dst_original + i1*nb1 + i2*nb2);
4994
4995#pragma unroll
4996    for (int j = item_ct1.get_local_id(2); j < ne0;
4997         j += item_ct1.get_local_range(2)) {
4998        dst_row_original[j] = dst_row_contiguous[j];
4999    }
5000}
5001
5002// Fused MoE TG fast path. Returns false to fall back to the per-expert loop below.
5003static bool ggml_sycl_mul_mat_id_mmvq_fused(
5004    ggml_backend_sycl_context & ctx, const ggml_tensor * src0,
5005    const ggml_tensor * src1, const ggml_tensor * ids, ggml_tensor * dst)
5006{
5007    const int64_t ne10 = src1->ne[0];
5008    const int64_t ne11 = src1->ne[1];
5009    const int64_t ne12 = src1->ne[2];
5010    if (ne12 != 1) return false;
5011    if (src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) return false;
5012    if (ne10 != src0->ne[0] || ne10 % QK8_1 != 0) return false;
5013    if (!ggml_is_contiguous(src1)) return false;
5014
5015    const int64_t n_ids_per_group = ids->ne[0];
5016    if (ids->ne[1] != 1) return false;
5017    if (ne11 != 1 && ne11 != n_ids_per_group) return false;
5018
5019    const queue_ptr stream           = ctx.stream();
5020    const int       src1_padded_cols = GGML_PAD((int) ne10, MATRIX_ROW_PADDING);
5021    const int       n_experts_used   = (int) n_ids_per_group;
5022    const int       nrows            = (int) src0->ne[1];
5023
5024    // Lazily reorder the (Q4_K) expert weights into a per-expert SoA layout, then run the reorder
5025    // GEMV. Placed after the bail checks so a non-dispatchable op does not pay the reorder cost.
5026    opt_for_reorder_id(&ctx, src0);
5027    const ggml_tensor_extra_gpu * src0_extra =
5028        static_cast<const ggml_tensor_extra_gpu *>(src0->extra);
5029    const bool use_reorder = src0_extra && src0_extra->optimized_feature.reorder;
5030
5031    ggml_sycl_pool_alloc<char> src1_q8_alloc(ctx.pool(),
5032        (size_t) ne11 * src1_padded_cols * sizeof(block_q8_1) / QK8_1);
5033    char * src1_ddq = src1_q8_alloc.get();
5034    if (use_reorder) {
5035        quantize_row_q8_1_sycl<quantize_and_reorder_q8_1_soa>(
5036            (const float *) src1->data, src1_ddq, (int) ne10, (int) ne11,
5037            src1_padded_cols, stream);
5038    } else {
5039        quantize_row_q8_1_sycl<quantize_q8_1>(
5040            (const float *) src1->data, src1_ddq, (int) ne10, (int) ne11,
5041            src1_padded_cols, stream);
5042    }
5043
5044    const size_t bytes_per_qrow = (size_t) src1_padded_cols * sizeof(block_q8_1) / QK8_1;
5045    const size_t src1_row_stride = (ne11 == 1) ? 0 : bytes_per_qrow;
5046
5047    if (use_reorder) {
5048        return ggml_sycl_mul_mat_vec_q_id_reorder(
5049            src0->type, src0->data, src1_ddq, (const int32_t *) ids->data,
5050            (float *) dst->data, (int) ne10, nrows, n_experts_used,
5051            /*expert_weight_stride=*/ src0->nb[2],
5052            /*dst_row_stride=*/ dst->nb[1],
5053            src1_row_stride, stream);
5054    }
5055    return ggml_sycl_mul_mat_vec_q_id(
5056        src0->type, src0->data, src1_ddq, (const int32_t *) ids->data,
5057        (float *) dst->data, (int) ne10, nrows, n_experts_used,
5058        /*expert_weight_stride=*/ src0->nb[2],
5059        /*dst_row_stride=*/ dst->nb[1],
5060        src1_row_stride, stream);
5061}
5062
5063// counting sort of the routed rows by expert id (row_id_i, as chosen by the router):
5064// builds a projection of a memory layout where each expert's slice is contiguous
5065static void mmid_counting_sort_rows(
5066        const ggml_tensor * ids, const char * ids_host,
5067        int64_t n_ids, int64_t n_as, int64_t n_routed_rows,
5068        std::vector<int64_t> & expert_counts,
5069        std::vector<int64_t> & expert_row_offsets,
5070        std::vector<mmid_row_mapping> & routed_row_src) {
5071
5072    // frequencies: how many routed rows each expert "owns"
5073    expert_counts.assign(n_as, 0);
5074    for (int64_t iid1 = 0; iid1 < ids->ne[1]; iid1++) {
5075        for (int64_t id = 0; id < n_ids; id++) {
5076            const int32_t row_id_i = *(const int32_t *) (ids_host + iid1*ids->nb[1] + id*ids->nb[0]);
5077            GGML_ASSERT(row_id_i >= 0 && row_id_i < n_as);
5078            expert_counts[row_id_i]++;
5079        }
5080    }
5081
5082    // where each expert's slice starts (row indices) and the previous ends
5083    expert_row_offsets.assign(n_as + 1, 0);
5084    for (int64_t i02 = 0; i02 < n_as; i02++) {
5085        expert_row_offsets[i02 + 1] = expert_row_offsets[i02] + expert_counts[i02];
5086    }
5087
5088    std::vector<int64_t> expert_row_next = expert_row_offsets;
5089    routed_row_src.resize(n_routed_rows);
5090    for (int64_t iid1 = 0; iid1 < ids->ne[1]; iid1++) {
5091        for (int64_t id = 0; id < n_ids; id++) {
5092            const int32_t row_id_i = *(const int32_t *) (ids_host + iid1*ids->nb[1] + id*ids->nb[0]);
5093            GGML_ASSERT(row_id_i >= 0 && row_id_i < n_as);
5094
5095            // find and validate the next free row for a given expert (row_id_i)
5096            const int64_t routed_row = expert_row_next[row_id_i]++;
5097            GGML_ASSERT(routed_row >= expert_row_offsets[row_id_i]);
5098            GGML_ASSERT(routed_row < expert_row_offsets[row_id_i + 1]);
5099            routed_row_src[routed_row] = {(int32_t) id, (int32_t) iid1};
5100        }
5101    }
5102}
5103
5104static void ggml_sycl_mul_mat_id(ggml_backend_sycl_context & ctx,
5105                                 ggml_tensor *dst) try {
5106    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/3);
5107    const ggml_tensor *src0 = dst->src[0];
5108    const ggml_tensor *src1 = dst->src[1];
5109    GGML_ASSERT(!ggml_backend_buffer_is_sycl_split(src0->buffer) && "mul_mat_id does not support split buffers");
5110
5111    const ggml_tensor *ids = dst->src[2];
5112    GGML_TENSOR_BINARY_OP_LOCALS
5113
5114    const queue_ptr stream = ctx.stream();
5115
5116    const int64_t n_as = ne02;
5117    const int64_t n_ids = ids->ne[0];
5118
5119    if (ne12 == 1) {
5120        if (ggml_sycl_mul_mat_id_mmvq_fused(ctx, src0, src1, ids, dst)) {
5121            return;
5122        }
5123    }
5124
5125    std::vector<char> ids_host(ggml_nbytes(ids));
5126    const char * ids_dev = (const char *) ids->data;
5127
5128    SYCL_CHECK(CHECK_TRY_ERROR(
5129        stream->memcpy(ids_host.data(), ids_dev, ggml_nbytes(ids))));
5130
5131    // also ensures ctx.mmid_row_mapping_host is drained before we use it again
5132    SYCL_CHECK(CHECK_TRY_ERROR(stream->wait()));
5133
5134    ggml_tensor src0_row = *src0;
5135    ggml_tensor src1_row = *src1;
5136    ggml_tensor dst_row = *dst;
5137
5138    char *src0_original = (char *)src0->data;
5139    char *src1_original = (char *)src1->data;
5140    char *dst_original = (char *)dst->data;
5141
5142    src0_row.ne[2] = 1;
5143    src0_row.ne[3] = 1;
5144    src0_row.nb[3] = nb02;
5145
5146    src1_row.ne[1] = 1;
5147    src1_row.ne[2] = 1;
5148    src1_row.ne[3] = 1;
5149    src1_row.nb[2] = nb11;
5150    src1_row.nb[3] = nb11;
5151
5152    dst_row.ne[1] = 1;
5153    dst_row.ne[2] = 1;
5154    dst_row.ne[3] = 1;
5155    dst_row.nb[2] = nb1;
5156    dst_row.nb[3] = nb1;
5157    if (ne12 == 1) {
5158        for (int64_t iid1 = 0; iid1 < ids->ne[1]; iid1++) {
5159            for (int64_t id = 0; id < n_ids; id++) {
5160                const int32_t i02 = *(const int32_t *) (ids_host.data() + iid1*ids->nb[1] + id*ids->nb[0]);
5161                GGML_ASSERT(i02 >= 0 && i02 < n_as);
5162
5163                const int64_t i11 = id % ne11;
5164                const int64_t i12 = iid1;
5165
5166                const int64_t i1 = id;
5167                const int64_t i2 = i12;
5168
5169            src0_row.data = src0_original + i02*nb02;
5170            src1_row.data = src1_original + i11*nb11 + i12*nb12;
5171            dst_row.data = dst_original + i1*nb1 + i2*nb2;
5172
5173            ggml_sycl_mul_mat(ctx, &src0_row, &src1_row, &dst_row);
5174            }
5175        }
5176    } else {
5177        const int64_t n_routed_rows = ids->ne[1] * n_ids;
5178        ggml_sycl_pool_alloc<char> src1_contiguous(ctx.pool(), sizeof(float)*n_routed_rows*ne10);
5179        ggml_sycl_pool_alloc<char>  dst_contiguous(ctx.pool(), sizeof(float)*n_routed_rows*ne0);
5180
5181        src1_row.data = src1_contiguous.get();
5182        dst_row.data  =  dst_contiguous.get();
5183
5184        // how many "owned" routed rows to pass to each expert
5185        std::vector<int64_t> expert_row_counts;
5186        // where each expert's slice starts and the previous ends (row indices, right-exclusive)
5187        std::vector<int64_t> expert_row_offsets;
5188        // the sources (slot/token pairs) of contiguous rows to guide k_copy_src1_to_contiguous
5189        std::vector<mmid_row_mapping> & routed_row_src = ctx.mmid_row_mapping_host;
5190
5191        mmid_counting_sort_rows(ids, ids_host.data(), n_ids, n_as, n_routed_rows,
5192                                expert_row_counts, expert_row_offsets, routed_row_src);
5193
5194        ggml_sycl_pool_alloc<mmid_row_mapping> dev_row_mapping(ctx.pool(), n_routed_rows);
5195        SYCL_CHECK(CHECK_TRY_ERROR(
5196                stream->memcpy(dev_row_mapping.get(), routed_row_src.data(), n_routed_rows*sizeof(mmid_row_mapping))));
5197
5198        const unsigned int max_work_group_size = ggml_sycl_info().max_work_group_sizes[ctx.device];
5199        assert(max_work_group_size % (WARP_SIZE * WARP_SIZE) == 0);
5200
5201        {
5202            sycl::range<3> block_dims(1, 1, std::min((unsigned int)ne10, max_work_group_size));
5203            sycl::range<3> grid_dims(1, 1, n_routed_rows);
5204            stream->submit([&](sycl::handler &cgh) {
5205                char *__restrict src1_contiguous_get =
5206                    src1_contiguous.get();
5207                mmid_row_mapping *__restrict dev_row_mapping_get =
5208                    dev_row_mapping.get();
5209
5210                cgh.parallel_for(
5211                    sycl::nd_range<3>(grid_dims * block_dims, block_dims),
5212                    [=](sycl::nd_item<3> item_ct1) {
5213                        k_copy_src1_to_contiguous(
5214                            src1_original, src1_contiguous_get,
5215                            dev_row_mapping_get,
5216                            ne11, ne10, nb11, nb12,
5217                            item_ct1);
5218                    });
5219            });
5220        }
5221
5222        for (int64_t i02 = 0; i02 < n_as; i02++) {
5223            const int64_t num_src1_rows = expert_row_counts[i02];
5224
5225            if (num_src1_rows == 0) {
5226                continue;
5227            }
5228
5229            const int64_t expert_row_offset = expert_row_offsets[i02];
5230
5231            src0_row.data = src0_original + i02*nb02;
5232
5233            GGML_ASSERT(nb11 == sizeof(float)*ne10);
5234            GGML_ASSERT(nb1 == sizeof(float)*ne0);
5235            src1_row.data = src1_contiguous.get() + expert_row_offset*nb11;
5236            src1_row.ne[1] = num_src1_rows;
5237
5238            src1_row.nb[1] = nb11;
5239            src1_row.nb[2] = num_src1_rows*nb11;
5240            src1_row.nb[3] = num_src1_rows*nb11;
5241
5242            dst_row.data = dst_contiguous.get() + expert_row_offset*nb1;
5243            dst_row.ne[1] = num_src1_rows;
5244            dst_row.nb[1] = nb1;
5245            dst_row.nb[2] = num_src1_rows*nb1;
5246            dst_row.nb[3] = num_src1_rows*nb1;
5247
5248            ggml_sycl_mul_mat(ctx, &src0_row, &src1_row, &dst_row);
5249        }
5250
5251        {
5252            sycl::range<3> block_dims(1, 1, std::min((unsigned int)ne0, max_work_group_size));
5253            sycl::range<3> grid_dims(1, 1, n_routed_rows);
5254            stream->submit([&](sycl::handler &cgh) {
5255                const char *__restrict dst_contiguous_get =
5256                    dst_contiguous.get();
5257                const mmid_row_mapping *__restrict dev_row_mapping_get =
5258                    dev_row_mapping.get();
5259
5260                cgh.parallel_for(
5261                    sycl::nd_range<3>(grid_dims * block_dims, block_dims),
5262                    [=](sycl::nd_item<3> item_ct1) {
5263                        k_copy_dst_from_contiguous(dst_original,
5264                                                   dst_contiguous_get,
5265                                                   dev_row_mapping_get,
5266                                                   ne0, nb1, nb2, item_ct1);
5267                    });
5268            });
5269        }
5270    }
5271}
5272catch (sycl::exception const &exc) {
5273  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5274            << ", line:" << __LINE__ << std::endl;
5275  std::exit(1);
5276}
5277
5278static void ggml_sycl_scale(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5279    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5280    ggml_sycl_op_scale(ctx, dst);
5281}
5282
5283static void ggml_sycl_diag_mask_inf(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5284    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5285    ggml_sycl_op_diag_mask_inf(ctx, dst);
5286}
5287
5288static void ggml_sycl_pool2d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5289    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5290    ggml_sycl_op_pool2d(ctx, dst);
5291}
5292
5293static void ggml_sycl_pool1d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5294    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5295    ggml_sycl_op_pool1d(ctx, dst);
5296}
5297
5298static void ggml_sycl_im2col(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5299    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
5300    ggml_sycl_op_im2col(ctx, dst);
5301}
5302
5303static void ggml_sycl_im2col_3d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5304    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
5305    ggml_sycl_op_im2col_3d(ctx, dst);
5306}
5307
5308static void ggml_sycl_col2im_1d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5309    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5310    ggml_sycl_op_col2im_1d(ctx, dst);
5311}
5312
5313static void ggml_sycl_conv_3d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5314    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
5315    ggml_sycl_op_conv_3d(ctx, dst);
5316}
5317
5318static void ggml_sycl_sum(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5319    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5320    GGML_ASSERT(ggml_is_contiguous(dst->src[0]));
5321    ggml_sycl_op_sum(ctx, dst);
5322}
5323
5324static void ggml_sycl_sum_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5325    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5326    GGML_ASSERT(ggml_is_contiguous(dst->src[0]));
5327    ggml_sycl_op_sum_rows(ctx, dst);
5328}
5329
5330static void ggml_sycl_mean(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5331    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5332    GGML_ASSERT(ggml_is_contiguous(dst->src[0]));
5333    ggml_sycl_op_mean(ctx, dst);
5334}
5335
5336static void ggml_sycl_argsort(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5337    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5338    GGML_ASSERT(ggml_is_contiguous(dst->src[0]));
5339    ggml_sycl_op_argsort(ctx, dst);
5340}
5341
5342static void ggml_sycl_argmax(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
5343    scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
5344    GGML_ASSERT(ggml_is_contiguous(dst->src[0]));
5345    ggml_sycl_op_argmax(ctx, dst);
5346}
5347
5348
5349static void ggml_sycl_set_main_device(const int main_device) try {
5350    if (dpct::get_current_device_id() == static_cast<unsigned int> (main_device)) {
5351        return;
5352    }
5353    check_allow_gpu_index(main_device);
5354    dpct::select_device(main_device);
5355
5356    if (g_ggml_sycl_debug) {
5357        dpct::device_info prop;
5358        SYCL_CHECK(CHECK_TRY_ERROR(dpct::get_device_info(
5359            prop, dpct::dev_mgr::instance().get_device(main_device))));
5360        GGML_LOG_INFO("Using device %d (%s) as main device\n",
5361                main_device, prop.get_name());
5362    }
5363}
5364catch (sycl::exception const &exc) {
5365  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5366            << ", line:" << __LINE__ << std::endl;
5367  std::exit(1);
5368}
5369
5370static bool ggml_sycl_compute_forward(ggml_backend_sycl_context & ctx, struct ggml_tensor * dst) try {
5371    GGML_SYCL_DEBUG("[SYCL] ggml_sycl_compute_forward: dst=%s, op=%s\n", dst->name, ggml_op_name(dst->op));
5372    if (!g_sycl_loaded) return false;
5373
5374    if (dst->src[0] != nullptr && ggml_backend_buffer_is_sycl_split(dst->src[0]->buffer)) {
5375        ggml_sycl_set_peer_access(dst->src[1]->ne[1], ctx.device);
5376    }
5377
5378    switch (dst->op) {
5379        case GGML_OP_ARGMAX:
5380            ggml_sycl_argmax(ctx, dst);
5381            break;
5382        case GGML_OP_CONV_2D:
5383            ggml_sycl_op_conv2d(ctx, dst);
5384            break;
5385        case GGML_OP_CONV_2D_DW:
5386            ggml_sycl_op_conv2d_dw(ctx, dst);
5387            break;
5388        case GGML_OP_CONV_3D:
5389            ggml_sycl_conv_3d(ctx, dst);
5390            break;
5391        case GGML_OP_CONV_TRANSPOSE_1D:
5392            ggml_sycl_op_conv_transpose_1d(ctx, dst);
5393            break;
5394        case GGML_OP_CONV_TRANSPOSE_2D:
5395            ggml_sycl_op_conv2d_transpose(ctx, dst);
5396            break;
5397        case GGML_OP_REPEAT:
5398            ggml_sycl_repeat(ctx, dst);
5399            break;
5400        case GGML_OP_REPEAT_BACK:
5401            ggml_sycl_repeat_back(ctx, dst);
5402            break;
5403        case GGML_OP_GET_ROWS:
5404            ggml_sycl_get_rows(ctx, dst);
5405            break;
5406        case GGML_OP_SET:
5407            ggml_sycl_op_set(ctx, dst);
5408            break;
5409        case GGML_OP_SET_ROWS:
5410            ggml_sycl_op_set_rows(ctx, dst);
5411            break;
5412        case GGML_OP_DSV4_HC_PRE:
5413            ggml_sycl_op_dsv4_hc_pre(ctx, dst);
5414            break;
5415        case GGML_OP_DSV4_HC_COMB:
5416            ggml_sycl_op_dsv4_hc_comb(ctx, dst);
5417            break;
5418        case GGML_OP_DSV4_HC_POST:
5419            ggml_sycl_op_dsv4_hc_post(ctx, dst);
5420            break;
5421        case GGML_OP_LIGHTNING_INDEXER:
5422            ggml_sycl_op_lightning_indexer(ctx, dst);
5423            break;
5424        case GGML_OP_DUP:
5425            ggml_sycl_dup(ctx, dst);
5426            break;
5427        case GGML_OP_ADD:
5428        case GGML_OP_ADD1: // TODO: more efficient implementation
5429            ggml_sycl_add(ctx, dst);
5430            break;
5431        case GGML_OP_ADD_ID:
5432            ggml_sycl_add_id(ctx, dst);
5433            break;
5434        case GGML_OP_SUB:
5435            ggml_sycl_sub(ctx, dst);
5436            break;
5437        case GGML_OP_COUNT_EQUAL:
5438            ggml_sycl_count_equal(ctx, dst);
5439            break;
5440        case GGML_OP_ACC:
5441            ggml_sycl_acc(ctx, dst);
5442            break;
5443        case GGML_OP_MUL:
5444            ggml_sycl_mul(ctx, dst);
5445            break;
5446        case GGML_OP_LOG:
5447            ggml_sycl_log(ctx, dst);
5448            break;
5449        case GGML_OP_DIV:
5450            ggml_sycl_div(ctx, dst);
5451            break;
5452        case GGML_OP_UNARY:
5453            switch (ggml_get_unary_op(dst)) {
5454                case GGML_UNARY_OP_NEG:
5455                    ggml_sycl_neg(ctx, dst);
5456                    break;
5457                case GGML_UNARY_OP_STEP:
5458                    ggml_sycl_step(ctx, dst);
5459                    break;
5460                case GGML_UNARY_OP_GELU:
5461                    ggml_sycl_gelu(ctx, dst);
5462                    break;
5463                case GGML_UNARY_OP_SILU:
5464                    ggml_sycl_silu(ctx, dst);
5465                    break;
5466                case GGML_UNARY_OP_GELU_QUICK:
5467                    ggml_sycl_gelu_quick(ctx, dst);
5468                    break;
5469                case GGML_UNARY_OP_GELU_ERF:
5470                    ggml_sycl_gelu_erf(ctx, dst);
5471                    break;
5472                case GGML_UNARY_OP_TANH:
5473                    ggml_sycl_tanh(ctx, dst);
5474                    break;
5475                case GGML_UNARY_OP_RELU:
5476                    ggml_sycl_relu(ctx, dst);
5477                    break;
5478                case GGML_UNARY_OP_SIGMOID:
5479                    ggml_sycl_sigmoid(ctx, dst);
5480                    break;
5481                case GGML_UNARY_OP_HARDSIGMOID:
5482                    ggml_sycl_hardsigmoid(ctx, dst);
5483                    break;
5484                case GGML_UNARY_OP_HARDSWISH:
5485                    ggml_sycl_hardswish(ctx, dst);
5486                    break;
5487                case GGML_UNARY_OP_EXP:
5488                    ggml_sycl_exp(ctx, dst);
5489                    break;
5490                case GGML_UNARY_OP_EXPM1:
5491                    ggml_sycl_expm1(ctx, dst);
5492                    break;
5493                case GGML_UNARY_OP_SOFTPLUS:
5494                    ggml_sycl_softplus(ctx, dst);
5495                    break;
5496                case GGML_UNARY_OP_SGN:
5497                    ggml_sycl_sgn(ctx, dst);
5498                    break;
5499                case GGML_UNARY_OP_ABS:
5500                    ggml_sycl_abs(ctx, dst);
5501                    break;
5502                case GGML_UNARY_OP_ELU:
5503                    ggml_sycl_elu(ctx, dst);
5504                    break;
5505                case GGML_UNARY_OP_XIELU:
5506                    ggml_sycl_xielu(ctx, dst);
5507                    break;
5508                case GGML_UNARY_OP_FLOOR:
5509                    ggml_sycl_floor(ctx, dst);
5510                    break;
5511                case GGML_UNARY_OP_CEIL:
5512                    ggml_sycl_ceil(ctx, dst);
5513                    break;
5514                case GGML_UNARY_OP_ROUND:
5515                    ggml_sycl_round(ctx, dst);
5516                    break;
5517                case GGML_UNARY_OP_TRUNC:
5518                    ggml_sycl_trunc(ctx, dst);
5519                    break;
5520                default:
5521                    return false;
5522            }
5523            break;
5524        case GGML_OP_GLU:
5525            switch (ggml_get_glu_op(dst)) {
5526                case GGML_GLU_OP_REGLU:
5527                    ggml_sycl_reglu(ctx, dst);
5528                    break;
5529                case GGML_GLU_OP_GEGLU:
5530                    ggml_sycl_geglu(ctx, dst);
5531                    break;
5532                case GGML_GLU_OP_SWIGLU:
5533                    ggml_sycl_swiglu(ctx, dst);
5534                    break;
5535                case GGML_GLU_OP_SWIGLU_OAI:
5536                    ggml_sycl_swiglu_oai(ctx, dst);
5537                    break;
5538                case GGML_GLU_OP_SWIGLU_CLAMP:
5539                    ggml_sycl_swiglu_clamp(ctx, dst);
5540                    break;
5541                case GGML_GLU_OP_GEGLU_ERF:
5542                    ggml_sycl_geglu_erf(ctx, dst);
5543                    break;
5544                case GGML_GLU_OP_GEGLU_QUICK:
5545                    ggml_sycl_geglu_quick(ctx, dst);
5546                    break;
5547                default:
5548                    return false;
5549            }
5550            break;
5551        case GGML_OP_NORM:
5552            ggml_sycl_norm(ctx, dst);
5553            break;
5554        case GGML_OP_GROUP_NORM:
5555            ggml_sycl_group_norm(ctx, dst);
5556            break;
5557        case GGML_OP_CONCAT:
5558            ggml_sycl_op_concat(ctx, dst);
5559            break;
5560        case GGML_OP_PAD_REFLECT_1D:
5561            ggml_sycl_op_pad_reflect_1d(ctx,dst);
5562            break;
5563        case GGML_OP_UPSCALE:
5564            ggml_sycl_upscale(ctx, dst);
5565            break;
5566        case GGML_OP_PAD:
5567            ggml_sycl_pad(ctx, dst);
5568            break;
5569        case GGML_OP_LEAKY_RELU:
5570            ggml_sycl_leaky_relu(ctx, dst);
5571            break;
5572        case GGML_OP_RMS_NORM_BACK:
5573            ggml_sycl_rms_norm_back(ctx, dst);
5574            break;
5575        case GGML_OP_RMS_NORM:
5576            ggml_sycl_rms_norm(ctx, dst);
5577            break;
5578        case GGML_OP_L2_NORM:
5579            ggml_sycl_l2_norm(ctx, dst);
5580            break;
5581        case GGML_OP_MUL_MAT:
5582            if (dst->src[0]->ne[3] != dst->src[1]->ne[3]) {
5583                return false;
5584            }
5585            /* ggml_sycl_mul_mat_id is dependent on ggml_sycl_mul_mat */
5586            ggml_sycl_mul_mat(ctx, dst->src[0], dst->src[1], dst);
5587            break;
5588        case GGML_OP_MUL_MAT_ID:
5589            if (dst->src[0]->ne[3] != dst->src[1]->ne[3]) {
5590                return false;
5591            }
5592            ggml_sycl_mul_mat_id(ctx, dst);
5593            break;
5594        case GGML_OP_OUT_PROD:
5595            ggml_sycl_op_out_prod(ctx, dst);
5596            break;
5597        case GGML_OP_SCALE:
5598            ggml_sycl_scale(ctx, dst);
5599            break;
5600        case GGML_OP_SQR:
5601            ggml_sycl_sqr(ctx, dst);
5602            break;
5603        case GGML_OP_SQRT:
5604            ggml_sycl_sqrt(ctx, dst);
5605            break;
5606        case GGML_OP_SIN:
5607            ggml_sycl_sin(ctx, dst);
5608            break;
5609        case GGML_OP_COS:
5610            ggml_sycl_cos(ctx, dst);
5611            break;
5612        case GGML_OP_CLAMP:
5613            ggml_sycl_clamp(ctx, dst);
5614            break;
5615        case GGML_OP_CPY:
5616            ggml_sycl_cpy(ctx, dst->src[0], dst->src[1]);
5617            break;
5618        case GGML_OP_CONT:
5619            ggml_sycl_dup(ctx, dst);
5620            break;
5621        case GGML_OP_NONE:
5622        case GGML_OP_RESHAPE:
5623        case GGML_OP_VIEW:
5624        case GGML_OP_PERMUTE:
5625        case GGML_OP_TRANSPOSE:
5626            GGML_SYCL_DEBUG("%s: Tensor NO-OP\n", __func__);
5627            break;
5628        case GGML_OP_TRI:
5629            ggml_sycl_op_tri(ctx, dst);
5630            break;
5631        case GGML_OP_DIAG_MASK_INF:
5632            ggml_sycl_diag_mask_inf(ctx, dst);
5633            break;
5634        case GGML_OP_SOFT_MAX:
5635            ggml_sycl_op_soft_max(ctx, dst);
5636            break;
5637        case GGML_OP_SOFT_MAX_BACK:
5638            ggml_sycl_op_soft_max_back(ctx, dst);
5639            break;
5640        case GGML_OP_CROSS_ENTROPY_LOSS:
5641            ggml_sycl_cross_entropy_loss(ctx, dst);
5642            break;
5643        case GGML_OP_CROSS_ENTROPY_LOSS_BACK:
5644            ggml_sycl_cross_entropy_loss_back(ctx, dst);
5645            break;
5646        case GGML_OP_ROPE:
5647            ggml_sycl_rope(ctx, dst);
5648            break;
5649        case GGML_OP_ROPE_BACK:
5650            ggml_sycl_rope_back(ctx, dst);
5651            break;
5652        case GGML_OP_IM2COL:
5653            ggml_sycl_im2col(ctx, dst);
5654            break;
5655        case GGML_OP_IM2COL_3D:
5656            ggml_sycl_im2col_3d(ctx, dst);
5657            break;
5658        case GGML_OP_COL2IM_1D:
5659            ggml_sycl_col2im_1d(ctx, dst);
5660            break;
5661        case GGML_OP_POOL_2D:
5662            ggml_sycl_pool2d(ctx, dst);
5663            break;
5664        case GGML_OP_POOL_1D:
5665            ggml_sycl_pool1d(ctx, dst);
5666            break;
5667        case GGML_OP_SUM:
5668            ggml_sycl_sum(ctx, dst);
5669            break;
5670        case GGML_OP_SUM_ROWS:
5671            ggml_sycl_sum_rows(ctx, dst);
5672            break;
5673        case GGML_OP_MEAN:
5674            ggml_sycl_mean(ctx, dst);
5675            break;
5676        case GGML_OP_ARGSORT:
5677            ggml_sycl_argsort(ctx, dst);
5678            break;
5679        case GGML_OP_TOP_K:
5680            ggml_sycl_op_top_k(ctx, dst);
5681            break;
5682        case GGML_OP_TIMESTEP_EMBEDDING:
5683            ggml_sycl_op_timestep_embedding(ctx, dst);
5684            break;
5685        case GGML_OP_RWKV_WKV6:
5686            ggml_sycl_op_rwkv_wkv6(ctx, dst);
5687            break;
5688        case GGML_OP_RWKV_WKV7:
5689            ggml_sycl_op_rwkv_wkv7(ctx, dst);
5690            break;
5691        case GGML_OP_GATED_LINEAR_ATTN:
5692            ggml_sycl_op_gated_linear_attn(ctx, dst);
5693            break;
5694        case GGML_OP_GATED_DELTA_NET:
5695            ggml_sycl_gated_delta_net(ctx, dst);
5696            break;
5697        case GGML_OP_OPT_STEP_ADAMW:
5698            ggml_sycl_opt_step_adamw(ctx, dst);
5699            break;
5700        case GGML_OP_OPT_STEP_SGD:
5701            ggml_sycl_opt_step_sgd(ctx, dst);
5702            break;
5703        case GGML_OP_SSM_CONV:
5704            ggml_sycl_ssm_conv(ctx, dst);
5705            break;
5706        case GGML_OP_SSM_SCAN:
5707            ggml_sycl_ssm_scan(ctx, dst);
5708            break;
5709        case GGML_OP_FILL:
5710            ggml_sycl_fill(ctx, dst);
5711            break;
5712        case GGML_OP_CUMSUM:
5713            ggml_sycl_cumsum(ctx, dst);
5714            break;
5715        case GGML_OP_DIAG:
5716            ggml_sycl_diag(ctx, dst);
5717            break;
5718        case GGML_OP_SOLVE_TRI:
5719            ggml_sycl_solve_tri(ctx, dst);
5720            break;
5721        case GGML_OP_ROLL:
5722            ggml_sycl_roll(ctx, dst);
5723            break;
5724        case GGML_OP_ARANGE:
5725            ggml_sycl_arange(ctx, dst);
5726            break;
5727        case GGML_OP_FLASH_ATTN_EXT:
5728            ggml_sycl_flash_attn_ext(ctx, dst);
5729            break;
5730        default:
5731            return false;
5732    }
5733
5734    return true;
5735} catch (sycl::exception & e) {
5736    std::cerr << e.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl;
5737    std::cerr << "Error OP "<<ggml_op_name(dst->op)<< std::endl;
5738    std::exit(1);
5739}
5740
5741GGML_API void ggml_backend_sycl_get_device_description(int device, char *description,
5742                                      size_t description_size) try {
5743    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_get_device_description\n");
5744    dpct::device_info prop;
5745    SYCL_CHECK(CHECK_TRY_ERROR(dpct::get_device_info(
5746        prop, dpct::dev_mgr::instance().get_device(device))));
5747    snprintf(description, description_size, "%s", prop.get_name());
5748}
5749catch (sycl::exception const &exc) {
5750  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5751            << ", line:" << __LINE__ << std::endl;
5752  std::exit(1);
5753}
5754
5755bool sycl_get_mem_info(int device, size_t * free, size_t * total) {
5756    GGML_SYCL_DEBUG("[SYCL] [%s] g_ggml_sycl_get_mem_api=%d\n",
5757        __func__, g_ggml_sycl_get_mem_api);
5758
5759    MemoryAPIType mem_api_type = MemoryAPIType::MEMORY_API_TYPE_SYCL;
5760
5761#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
5762    mem_api_type = get_zes_init_res() == ZE_RESULT_SUCCESS ?
5763        (MemoryAPIType) g_ggml_sycl_get_mem_api : MemoryAPIType::MEMORY_API_TYPE_SYCL;
5764#else
5765    mem_api_type = MemoryAPIType::MEMORY_API_TYPE_SYCL;
5766#endif
5767    bool res = get_memory_size(dpct::dev_mgr::instance().get_device(device),
5768        *free, *total, mem_api_type);
5769    GGML_SYCL_DEBUG("[SYCL] [%s] total = %zu free = %zu\n", __func__, *total, *free);
5770    return res;
5771}
5772
5773void ggml_backend_sycl_get_device_memory(int device, size_t * free, size_t * total) try {
5774    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_get_device_memory\n");
5775    if (!sycl_get_mem_info(device, free, total)) {
5776        GGML_ABORT("[%s] failed to get device memory size", __func__);
5777    }
5778    ggml_sycl_memtrace_report_device("device memory query", device, *free, *total);
5779} catch (const sycl::exception & exc) {
5780    std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl;
5781    std::exit(1);
5782}
5783
5784////////////////////////////////////////////////////////////////////////////////
5785
5786// backend
5787
5788static const char * ggml_backend_sycl_get_name(ggml_backend_t backend) {
5789
5790    ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
5791
5792    return sycl_ctx->name.c_str();
5793}
5794
5795static void ggml_backend_sycl_free(ggml_backend_t backend) {
5796    ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
5797
5798    delete sycl_ctx;
5799    delete backend;
5800}
5801
5802static void ggml_backend_sycl_set_tensor_async(ggml_backend_t backend,
5803                                               ggml_tensor *tensor,
5804                                               const void *data, size_t offset,
5805                                               size_t size) try {
5806    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
5807    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
5808    GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset);
5809    ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
5810    ggml_backend_buffer_t buf = tensor->view_src ? tensor->view_src->buffer : tensor->buffer;
5811
5812    GGML_ASSERT(buf->buft == ggml_backend_sycl_buffer_type(sycl_ctx->device) && "unsupported buffer type");
5813    const queue_ptr stream = sycl_ctx->stream(sycl_ctx->device, 0);
5814    SYCL_CHECK(CHECK_TRY_ERROR(
5815        (stream)->memcpy((char *)tensor->data + offset, data, size)));
5816}
5817catch (sycl::exception const &exc) {
5818  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5819            << ", line:" << __LINE__ << std::endl;
5820  std::exit(1);
5821}
5822
5823static void ggml_backend_sycl_get_tensor_async(ggml_backend_t backend,
5824                                               const ggml_tensor *tensor,
5825                                               void *data, size_t offset,
5826                                               size_t size) try {
5827    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
5828    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str());
5829    GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset);
5830    ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
5831    ggml_backend_buffer_t buf = tensor->view_src ? tensor->view_src->buffer : tensor->buffer;
5832
5833    GGML_ASSERT(buf->buft == ggml_backend_sycl_buffer_type(sycl_ctx->device) && "unsupported buffer type");
5834    const queue_ptr stream = sycl_ctx->stream(sycl_ctx->device, 0);
5835    SYCL_CHECK(CHECK_TRY_ERROR((stream)->memcpy(
5836        data, (const char *)tensor->data + offset, size)));
5837}
5838catch (sycl::exception const &exc) {
5839  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5840            << ", line:" << __LINE__ << std::endl;
5841  std::exit(1);
5842}
5843
5844static bool ggml_backend_sycl_cpy_tensor_async(ggml_backend_t backend,
5845                                               const ggml_tensor *src,
5846                                               ggml_tensor *dst) try {
5847    ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
5848    bool is_cpy_supported                = dst->buffer->buft == ggml_backend_sycl_buffer_type(sycl_ctx->device) &&
5849                            ggml_backend_buffer_is_sycl(src->buffer);
5850    GGML_SYCL_DEBUG("[SYCL] call %s", __func__);
5851    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": dst", dst).c_str());
5852    GGML_SYCL_DEBUG("%s", debug_get_tensor_str(" src", src).c_str());
5853    GGML_SYCL_DEBUG(" is_cpy_supported=%d\n", is_cpy_supported);
5854    if (is_cpy_supported) {
5855        /*
5856        DPCT1009:215: SYCL uses exceptions to report errors and does not use the
5857        error codes. The original code was commented out and a warning string
5858        was inserted. You need to rewrite this code.
5859        */
5860        const queue_ptr stream = sycl_ctx->stream(sycl_ctx->device, 0);
5861        SYCL_CHECK(CHECK_TRY_ERROR((stream)->memcpy(
5862            dst->data, src->data, ggml_nbytes(dst))));
5863        return true;
5864    }
5865
5866    return false;
5867}
5868catch (sycl::exception const &exc) {
5869  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5870            << ", line:" << __LINE__ << std::endl;
5871  std::exit(1);
5872}
5873
5874static void ggml_backend_sycl_synchronize(ggml_backend_t backend) try {
5875    GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
5876    ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
5877    const queue_ptr stream = sycl_ctx->stream(sycl_ctx->device, 0);
5878    SYCL_CHECK(CHECK_TRY_ERROR((stream)->wait()));
5879
5880    GGML_UNUSED(backend);
5881}
5882catch (sycl::exception const &exc) {
5883  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
5884            << ", line:" << __LINE__ << std::endl;
5885  std::exit(1);
5886}
5887
5888static bool ggml_sycl_is_view_or_noop(const ggml_tensor * t) {
5889    return ggml_is_empty(t) || t->op == GGML_OP_RESHAPE || t->op == GGML_OP_TRANSPOSE ||
5890           t->op == GGML_OP_VIEW || t->op == GGML_OP_PERMUTE || t->op == GGML_OP_NONE;
5891}
5892
5893// match gated_delta_net + the strided cpy that scatters its state snapshots into the cache
5894// (slot i -> rollback group i, slot 0 newest), so the kernel can write them and skip the cpy.
5895// returns the number of following nodes to skip (0 = no fusion)
5896// ported from ggml_cuda_try_gdn_cache_fusion - pure graph inspection, backend-agnostic
5897static int ggml_sycl_try_gdn_cache_fusion(const ggml_cgraph * cgraph, int node_idx,
5898                                          ggml_sycl_gated_delta_net_fused_cache & fused_state_cpy) {
5899    if (!g_ggml_sycl_enable_fusion) {
5900        return 0;
5901    }
5902
5903    const ggml_tensor * gdn = cgraph->nodes[node_idx];
5904    // the kernel skips the snapshot tail, so the gdn output must not be a graph output, and the cpy
5905    // found below is taken to be its only reader, as it is in every graph that builds this op
5906    if (gdn->op != GGML_OP_GATED_DELTA_NET || gdn->type != GGML_TYPE_F32 ||
5907        (gdn->flags & GGML_TENSOR_FLAG_OUTPUT)) {
5908        return 0;
5909    }
5910
5911    const ggml_tensor * src_v     = gdn->src[2];
5912    const int64_t       S_v       = src_v->ne[0];
5913    const int64_t       H         = src_v->ne[1];
5914    const int64_t       n_tokens  = src_v->ne[2];
5915    const int64_t       n_seqs    = src_v->ne[3];
5916    const int64_t       D         = S_v * S_v * H;
5917    const int64_t       K         = ggml_get_op_params_i32(gdn, 0); // snapshot slot count
5918    const int64_t       n_written = std::min<int64_t>(n_tokens, K); // newest n_written slots are written
5919
5920    // snapshot tail starts right after the attention scores
5921    const size_t tail_off = ggml_row_size(GGML_TYPE_F32, S_v * H * n_tokens * n_seqs);
5922
5923    // the cpy must be the first node the compute loop below runs, so nothing can read the cache first.
5924    // skip exactly what that loop skips: views, no-ops, and nodes the graph does not compute.
5925    const ggml_tensor * cpy  = nullptr;
5926    int                 skip = 0;
5927    for (int j = node_idx + 1; j < cgraph->n_nodes && cpy == nullptr; ++j) {
5928        const ggml_tensor * n = cgraph->nodes[j];
5929        if (ggml_sycl_is_view_or_noop(n) || (n->flags & GGML_TENSOR_FLAG_COMPUTE) == 0) {
5930            continue;
5931        }
5932        if (n->op != GGML_OP_CPY || (n->flags & GGML_TENSOR_FLAG_OUTPUT)) {
5933            return 0;
5934        }
5935        cpy  = n;
5936        skip = j - node_idx;
5937    }
5938    if (cpy == nullptr) {
5939        return 0;
5940    }
5941
5942    const ggml_tensor * src = cpy->src[0]; // view of the gdn snapshot tail
5943    const ggml_tensor * dst = cpy->src[1]; // cache view the kernel writes to
5944
5945    // src must be this gdn's snapshot tail (contiguous, at the tail offset)
5946    if (src->op != GGML_OP_VIEW || src->view_src != gdn || src->view_offs != tail_off ||
5947        !ggml_is_contiguous(src)) {
5948        return 0;
5949    }
5950
5951    // dst is the [D, n_seqs, n_written] cache view, with the per-seq stride D that the kernel assumes.
5952    // ggml_cpy pins src to the same element count, so src needs no shape check of its own.
5953    const std::array<int64_t, GGML_MAX_DIMS> expected_ne = { D, n_seqs, n_written, 1 };
5954    if (dst->op != GGML_OP_VIEW || dst->type != GGML_TYPE_F32 || dst->data == nullptr ||
5955        !std::equal(expected_ne.begin(), expected_ne.end(), dst->ne) ||
5956        dst->nb[0] != ggml_type_size(GGML_TYPE_F32) ||
5957        dst->nb[1] != (size_t) ggml_row_size(GGML_TYPE_F32, D)) {
5958        return 0;
5959    }
5960
5961    fused_state_cpy.data        = (float *) dst->data; // rollback group 0 (newest)
5962    fused_state_cpy.slot_stride = K > 1 ? (int64_t) (dst->nb[2] / sizeof(float)) : 0;
5963    return skip;
5964}
5965
5966static void ggml_backend_sycl_graph_compute_impl(ggml_backend_sycl_context * sycl_ctx, ggml_cgraph * cgraph) {
5967    ggml_sycl_set_main_device(sycl_ctx->device);
5968
5969    for (int i = 0; i < cgraph->n_nodes; i++) {
5970        ggml_tensor * node = cgraph->nodes[i];
5971        if (ggml_sycl_is_view_or_noop(node)) {
5972            continue;
5973        }
5974        if ((node->flags & GGML_TENSOR_FLAG_COMPUTE) == 0) {
5975            continue;
5976        }
5977
5978        const int nodes_to_skip = ggml_sycl_fuse(*sycl_ctx, cgraph, i);
5979        if (nodes_to_skip != 0) {
5980            i += nodes_to_skip;
5981            continue;
5982        }
5983#ifndef NDEBUG
5984        assert(node->buffer->buft == ggml_backend_sycl_buffer_type(sycl_ctx->device));
5985        for (int j = 0; j < GGML_MAX_SRC; j++) {
5986            if (node->src[j] != nullptr) {
5987                assert(node->src[j]->buffer->buft == ggml_backend_sycl_buffer_type(sycl_ctx->device));
5988            }
5989        }
5990#endif
5991        // gated_delta_net -> cpy: scatter recurrent-state snapshots into the cache
5992        if (node->op == GGML_OP_GATED_DELTA_NET) {
5993            ggml_sycl_gated_delta_net_fused_cache fused_state_cpy;
5994            const int gdn_nodes_to_skip = ggml_sycl_try_gdn_cache_fusion(cgraph, i, fused_state_cpy);
5995            if (gdn_nodes_to_skip > 0) {
5996                ggml_sycl_op_gated_delta_net_fused_cache(*sycl_ctx, node, fused_state_cpy);
5997                i += gdn_nodes_to_skip;
5998                continue;
5999            }
6000        }
6001        if (node->op == GGML_OP_RMS_NORM &&
6002            ggml_sycl_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL, GGML_OP_ADD }, {})) {
6003            ggml_sycl_op_rms_norm_fused_add(*sycl_ctx, node, cgraph->nodes[i + 1], cgraph->nodes[i + 2]);
6004            i += 2;
6005            continue;
6006        }
6007        if (node->op == GGML_OP_RMS_NORM &&
6008            ggml_sycl_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL }, {})) {
6009            ggml_sycl_op_rms_norm_fused(*sycl_ctx, node, cgraph->nodes[i + 1]);
6010            i++;
6011            continue;
6012        }
6013        if (node->op == GGML_OP_ADD &&
6014            ggml_sycl_can_fuse(cgraph, i, { GGML_OP_ADD, GGML_OP_ADD }, {})) {
6015            ggml_sycl_op_add_add_fused(*sycl_ctx, node, cgraph->nodes[i + 1]);
6016            i++;
6017            continue;
6018        }
6019        if (node->op == GGML_OP_UNARY &&
6020            ggml_sycl_can_fuse(cgraph, i, { GGML_OP_UNARY, GGML_OP_MUL }, { ggml_get_unary_op(node) })) {
6021            ggml_sycl_op_unary_mul_fused(*sycl_ctx, node, cgraph->nodes[i + 1]);
6022            i++;
6023            continue;
6024        }
6025
6026        // Batch consecutive independent same-shape F32 L2_NORM siblings (the GDN q/k
6027        // norms) into one launch; sources are strided views of the fused qkv buffer, so
6028        // the scan skips the interleaved view nodes instead of breaking on them.
6029        if (node->op == GGML_OP_L2_NORM) {
6030            const int l2_batch_skip = ggml_sycl_l2_norm_batch_fused(*sycl_ctx, cgraph, i);
6031            if (l2_batch_skip > 0) {
6032                i += l2_batch_skip;
6033                continue;
6034            }
6035        }
6036
6037        if (node->op == GGML_OP_MUL_MAT && ggml_sycl_mul_mat_glu_mmvq_fused(*sycl_ctx, cgraph, i)) {
6038            i += 2;
6039            continue;
6040        }
6041
6042        bool ok = ggml_sycl_compute_forward(*sycl_ctx, node);
6043        if (!ok) {
6044            GGML_LOG_ERROR("%s: error: op not supported %s (%s)\n", __func__, node->name, ggml_op_name(node->op));
6045        }
6046        GGML_ASSERT(ok);
6047    }
6048}
6049
6050#ifdef GGML_SYCL_GRAPH
6051static bool check_graph_compatibility(ggml_cgraph * cgraph) {
6052    if (ggml_sycl_info().device_count > 1) {
6053        // A sycl_ex::command_graph object can only be created for a single device
6054        GGML_LOG_INFO("%s: disabling SYCL graphs due to multiple devices\n", __func__);
6055        return false;
6056    }
6057
6058    for (int i = 0; i < cgraph->n_nodes; i++) {
6059        const ggml_op node_op = cgraph->nodes[i]->op;
6060        switch (node_op) {
6061            default:
6062                break;
6063            case GGML_OP_CONCAT:
6064                // ggml_sycl_op_concat() does a blocking host wait after memcpy operations,
6065                // but wait() can't be called on the events returned by a queue recording
6066                // to a graph.
6067                [[fallthrough]];
6068            case GGML_OP_MUL_MAT_ID:
6069                // ggml_sycl_mul_mat_id() does a blocking host wait on the sycl queue after
6070                // submitting a memcpy operation, but wait() can't be called on a queue that
6071                // is recording to a graph.
6072                GGML_LOG_INFO("%s: disabling SYCL graphs due to unsupported node type %s\n", __func__,
6073                              ggml_op_name(node_op));
6074                return false;
6075            case GGML_OP_MUL_MAT:
6076                // We cannot use graphs with ggml_sycl_mul_mat() when SYCL async memory allocation extensions are not available,
6077                // as SYCL malloc / free and host wait calls are not supported when recording to a graph which are all present
6078                // in reordering.
6079                if (!g_ggml_sycl_use_async_mem_op) {
6080                    GGML_LOG_INFO(
6081                        "%s: disabling SYCL graphs due to unsupported node type when using a compiler without the "
6082                        "oneAPI async memory allocation extension "
6083                        "%s\n",
6084                        __func__, ggml_op_name(node_op));
6085                    return false;
6086                }
6087        }
6088    }
6089    return true;
6090}
6091#endif
6092
6093static ggml_status ggml_backend_sycl_graph_compute(ggml_backend_t backend, ggml_cgraph * cgraph) {
6094    auto * sycl_ctx = static_cast<ggml_backend_sycl_context *>(backend->context);
6095
6096#ifdef GGML_SYCL_GRAPH
6097    bool use_sycl_graph = false;
6098    if (g_ggml_sycl_enable_graph) {
6099        use_sycl_graph = check_graph_compatibility(cgraph);
6100    }
6101    if (use_sycl_graph) {
6102        const bool graph_support = dpct::get_device(sycl_ctx->device).has(sycl::aspect::ext_oneapi_limited_graph);
6103        if (!graph_support) {
6104            GGML_SYCL_DEBUG("[SYCL-GRAPH] can not use graphs on device:%d\n", sycl_ctx->device);
6105            ggml_backend_sycl_graph_compute_impl(sycl_ctx, cgraph);
6106            return GGML_STATUS_SUCCESS;
6107        }
6108
6109        sycl_ex::command_graph model_sycl_graph(*(sycl_ctx->stream()), {sycl_ex::property::graph::assume_buffer_outlives_graph{}});
6110
6111        model_sycl_graph.begin_recording(*(sycl_ctx->stream()));
6112        ggml_backend_sycl_graph_compute_impl(sycl_ctx, cgraph);
6113        model_sycl_graph.end_recording();
6114
6115        const bool graph_update_support = dpct::get_device(sycl_ctx->device).has(sycl::aspect::ext_oneapi_graph);
6116        if (!sycl_ctx->exec_graph || !graph_update_support) {
6117            auto exec_graph = graph_update_support ? model_sycl_graph.finalize(sycl_ex::property::graph::updatable{}) :
6118                                                     model_sycl_graph.finalize();
6119            sycl_ctx->exec_graph = std::make_unique<
6120                sycl_ex::command_graph<sycl_ex::graph_state::executable>>(exec_graph);
6121        } else {
6122            try {
6123                sycl_ctx->exec_graph->update(model_sycl_graph);
6124                GGML_SYCL_DEBUG("[SYCL-GRAPH] update success\n");
6125            } catch (sycl::exception const & e) {
6126                GGML_SYCL_DEBUG("[SYCL-GRAPH] Exception when updating graph, %s\n", e.what());
6127                auto exec_graph = model_sycl_graph.finalize({sycl_ex::property::graph::updatable{}});
6128                sycl_ctx->exec_graph = std::make_unique<
6129                    sycl_ex::command_graph<sycl_ex::graph_state::executable>>(exec_graph);
6130            }
6131        }
6132
6133        sycl_ctx->stream()->ext_oneapi_graph(*(sycl_ctx->exec_graph));
6134    } else
6135#endif
6136    {
6137        ggml_backend_sycl_graph_compute_impl(sycl_ctx, cgraph);
6138    }
6139    return GGML_STATUS_SUCCESS;
6140}
6141
6142static void ggml_backend_sycl_event_record(ggml_backend_t backend, ggml_backend_event_t event)
6143try
6144{
6145    ggml_backend_sycl_context *sycl_ctx =
6146        (ggml_backend_sycl_context *)backend->context;
6147
6148    sycl::event *sycl_event = static_cast<sycl::event *>(event->context);
6149
6150    const queue_ptr &stream = sycl_ctx->stream(sycl_ctx->device, 0);
6151    // Record the current state of the queue
6152    SYCL_CHECK(CHECK_TRY_ERROR(*sycl_event = stream->ext_oneapi_submit_barrier()));
6153}
6154catch (sycl::exception const &exc)
6155{
6156    std::cerr << exc.what() << "Exception caught at file:" << __FILE__
6157              << ", line:" << __LINE__ << std::endl;
6158    std::exit(1);
6159}
6160
6161static void ggml_backend_sycl_event_wait(ggml_backend_t backend, ggml_backend_event_t event) try {
6162    GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
6163    sycl::event* sycl_event = static_cast<sycl::event*>(event->context);
6164
6165    if (ggml_backend_is_sycl(backend)) {
6166        SYCL_CHECK(CHECK_TRY_ERROR(sycl_event->wait()));
6167    } else
6168        GGML_ABORT("fatal error");
6169} catch (sycl::exception const& exc) {
6170    std::cerr << exc.what() << "Exception caught at file:" << __FILE__
6171              << ", line:" << __LINE__ << std::endl;
6172    std::exit(1);
6173}
6174
6175static ggml_backend_i ggml_backend_sycl_interface = {
6176    /* .get_name                = */ ggml_backend_sycl_get_name,
6177    /* .free                    = */ ggml_backend_sycl_free,
6178    /* .set_tensor_async        = */ ggml_backend_sycl_set_tensor_async,
6179    /* .get_tensor_async        = */ ggml_backend_sycl_get_tensor_async,
6180    /* .set_tensor_2d_async     = */ NULL,
6181    /* .get_tensor_2d_async     = */ NULL,
6182    /* .cpy_tensor_async        = */ NULL, // ggml_backend_sycl_cpy_tensor_async,
6183                                           // // TODO: update for the new
6184                                           // interface
6185    /* .synchronize             = */ ggml_backend_sycl_synchronize,
6186    /* .graph_plan_create       = */ NULL,
6187    /* .graph_plan_free         = */ NULL,
6188    /* .graph_plan_update       = */ NULL,
6189    /* .graph_plan_compute      = */ NULL,
6190    /* .graph_compute           = */ ggml_backend_sycl_graph_compute,
6191    /* .event_record            = */ ggml_backend_sycl_event_record,
6192    /* .event_wait              = */ ggml_backend_sycl_event_wait,
6193    /* .graph_optimize          = */ NULL,
6194};
6195
6196static ggml_guid_t ggml_backend_sycl_guid() {
6197    static ggml_guid guid = { 0x58, 0x05, 0x13, 0x8f, 0xcd, 0x3a, 0x61, 0x9d, 0xe7, 0xcd, 0x98, 0xa9, 0x03, 0xfd, 0x7c, 0x53 };
6198    return &guid;
6199}
6200
6201bool ggml_backend_is_sycl(ggml_backend_t backend) {
6202    return backend != NULL && ggml_guid_matches(backend->guid, ggml_backend_sycl_guid());
6203}
6204
6205int ggml_backend_sycl_get_device_count() {
6206    return ggml_sycl_info().device_count;
6207}
6208
6209
6210// backend device
6211
6212static const char * ggml_backend_sycl_device_get_name(ggml_backend_dev_t dev) {
6213    ggml_backend_sycl_device_context * ctx = (ggml_backend_sycl_device_context *)dev->context;
6214    return ctx->name.c_str();
6215}
6216
6217static const char * ggml_backend_sycl_device_get_description(ggml_backend_dev_t dev) {
6218    ggml_backend_sycl_device_context * ctx = (ggml_backend_sycl_device_context *)dev->context;
6219    return ctx->description.c_str();
6220}
6221
6222static void ggml_backend_sycl_device_get_memory(ggml_backend_dev_t dev, size_t * free, size_t * total) {
6223    GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
6224    ggml_backend_sycl_device_context * ctx = (ggml_backend_sycl_device_context *) dev->context;
6225    if (!sycl_get_mem_info(ctx->device, free, total)) {
6226        GGML_ABORT("[%s] failed to get device memory size", __func__);
6227    }
6228    GGML_SYCL_DEBUG("[SYCL] call %s total %zu free %zu\n", __func__, *total, *free);
6229    ggml_sycl_memtrace_report_device("device memory query (dev)", ctx->device, *free, *total);
6230}
6231
6232static enum ggml_backend_dev_type ggml_backend_sycl_device_get_type(ggml_backend_dev_t dev) {
6233    ggml_backend_sycl_device_context * ctx = (ggml_backend_sycl_device_context *)dev->context;
6234    const sycl_device_info & info = ggml_sycl_info().devices[ctx->device];
6235    if (info.l0_device_type_valid && !info.l0_discrete_gpu) {
6236        return GGML_BACKEND_DEVICE_TYPE_IGPU;
6237    }
6238    return GGML_BACKEND_DEVICE_TYPE_GPU;
6239}
6240
6241static void ggml_backend_sycl_device_get_props(ggml_backend_dev_t dev, ggml_backend_dev_props * props) {
6242    props->name        = ggml_backend_sycl_device_get_name(dev);
6243    props->description = ggml_backend_sycl_device_get_description(dev);
6244    props->type        = ggml_backend_sycl_device_get_type(dev);
6245    ggml_backend_sycl_device_get_memory(dev, &props->memory_free, &props->memory_total);
6246
6247    bool host_buffer = getenv("GGML_SYCL_NO_PINNED") == nullptr;
6248#ifdef GGML_SYCL_NO_PEER_COPY
6249    bool events = false;
6250#else
6251    bool events = true;
6252#endif
6253
6254    props->caps = {
6255        /* .async                 = */ true,
6256        /* .host_buffer           = */ host_buffer,
6257        /* .buffer_from_host_ptr  = */ false,
6258        /* .events                = */ events,
6259        /* .mmap_support          = */ true,
6260    };
6261}
6262
6263static ggml_backend_t ggml_backend_sycl_device_init(ggml_backend_dev_t dev, const char * params) {
6264    GGML_UNUSED(params);
6265    ggml_backend_sycl_device_context * ctx = (ggml_backend_sycl_device_context *)dev->context;
6266    return ggml_backend_sycl_init(ctx->device);
6267}
6268
6269static ggml_backend_buffer_type_t ggml_backend_sycl_device_get_buffer_type(ggml_backend_dev_t dev) {
6270    ggml_backend_sycl_device_context * ctx = (ggml_backend_sycl_device_context *)dev->context;
6271    return ggml_backend_sycl_buffer_type(ctx->device);
6272}
6273
6274static ggml_backend_buffer_type_t ggml_backend_sycl_device_get_host_buffer_type(ggml_backend_dev_t dev) {
6275    GGML_UNUSED(dev);
6276    return ggml_backend_sycl_host_buffer_type();
6277}
6278
6279static ggml_backend_buffer_t ggml_backend_sycl_device_buffer_from_host_ptr(ggml_backend_dev_t dev, void * ptr, size_t size, size_t max_tensor_size) {
6280    GGML_UNUSED(dev);
6281    GGML_UNUSED(ptr);
6282    GGML_UNUSED(size);
6283    GGML_UNUSED(max_tensor_size);
6284    return nullptr;
6285}
6286
6287static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
6288    ggml_backend_sycl_device_context *sycl_ctx =
6289        (ggml_backend_sycl_device_context *)dev->context;
6290    int device = sycl_ctx->device;
6291    switch (op->op) {
6292        case GGML_OP_CONV_TRANSPOSE_1D:
6293            {
6294                ggml_type src0_type = op->src[0]->type;
6295                ggml_type src1_type = op->src[1]->type;
6296                if (src0_type == GGML_TYPE_F32 && src1_type == GGML_TYPE_F32) {
6297                    return true;
6298                }
6299                return false;
6300            }
6301        case GGML_OP_CONV_2D:
6302        case GGML_OP_CONV_2D_DW:
6303        case GGML_OP_CONV_TRANSPOSE_2D:
6304            return true;
6305        case GGML_OP_UNARY:
6306            switch (ggml_get_unary_op(op)) {
6307                case GGML_UNARY_OP_SGN:
6308                case GGML_UNARY_OP_ABS:
6309                case GGML_UNARY_OP_NEG:
6310                case GGML_UNARY_OP_STEP:
6311                case GGML_UNARY_OP_RELU:
6312                case GGML_UNARY_OP_HARDSIGMOID:
6313                case GGML_UNARY_OP_TANH:
6314                case GGML_UNARY_OP_GELU:
6315                case GGML_UNARY_OP_SILU:
6316                case GGML_UNARY_OP_SIGMOID:
6317                case GGML_UNARY_OP_HARDSWISH:
6318                case GGML_UNARY_OP_GELU_QUICK:
6319                case GGML_UNARY_OP_GELU_ERF:
6320                case GGML_UNARY_OP_EXP:
6321                case GGML_UNARY_OP_EXPM1:
6322                case GGML_UNARY_OP_SOFTPLUS:
6323                case GGML_UNARY_OP_ELU:
6324                case GGML_UNARY_OP_XIELU:
6325                case GGML_UNARY_OP_CEIL:
6326                    return true;
6327                case GGML_UNARY_OP_FLOOR:
6328                case GGML_UNARY_OP_ROUND:
6329                case GGML_UNARY_OP_TRUNC:
6330                    return true;
6331                default:
6332                    return false;
6333            }
6334        case GGML_OP_GLU:
6335            switch (ggml_get_glu_op(op)) {
6336                case GGML_GLU_OP_REGLU:
6337                case GGML_GLU_OP_GEGLU:
6338                case GGML_GLU_OP_SWIGLU:
6339                case GGML_GLU_OP_SWIGLU_OAI:
6340                case GGML_GLU_OP_GEGLU_ERF:
6341                case GGML_GLU_OP_GEGLU_QUICK:
6342                case GGML_GLU_OP_SWIGLU_CLAMP:
6343                    return ggml_is_contiguous_1(op->src[0]);
6344                default:
6345                    return false;
6346            }
6347            break;
6348        case GGML_OP_MUL_MAT:
6349        case GGML_OP_MUL_MAT_ID:
6350            {
6351                struct ggml_tensor * a = op->src[0];
6352                struct ggml_tensor * b = op->src[1];
6353
6354                if (a->ne[3] != b->ne[3]) {
6355                    return false;
6356                }
6357
6358                ggml_type src0_type = op->src[0]->type;
6359
6360                // TODO: The configuration below needs more work to be supported with oneDNN
6361                if (ggml_is_permuted(a) && !ggml_is_contiguous(a) &&
6362                    a->ne[2] > 1 && a->ne[3] > 1 && src0_type == GGML_TYPE_F16) {
6363                  return false;
6364                }
6365
6366                // TODO: This specific configuration can fail with oneDNN and needs more debugging
6367                if (!ggml_is_permuted(a) && ggml_is_permuted(b) && b->ne[2] > 1 && b->ne[3] > 1 &&
6368                    a->ne[0] > 128 && a->ne[2] == 1 && src0_type == GGML_TYPE_F16) {
6369                    return false;
6370                }
6371
6372                if (src0_type == GGML_TYPE_TQ2_0 || src0_type == GGML_TYPE_TQ1_0) {
6373                    return false;
6374                }
6375
6376                return true;
6377            }
6378        case GGML_OP_OUT_PROD:
6379            return op->type == GGML_TYPE_F32 &&
6380                   (op->src[0]->type == GGML_TYPE_F32 ||
6381                    (op->src[0]->type == GGML_TYPE_Q1_0 && op->src[0]->ne[2] == op->src[1]->ne[2] &&
6382                     op->src[0]->ne[3] == op->src[1]->ne[3])) &&
6383                   op->src[1]->type == GGML_TYPE_F32;
6384        case GGML_OP_GET_ROWS:
6385            {
6386                switch (op->src[0]->type) {
6387                    case GGML_TYPE_I32:
6388                    case GGML_TYPE_F16:
6389                    case GGML_TYPE_BF16:
6390                    case GGML_TYPE_F32:
6391                    case GGML_TYPE_Q1_0:
6392                    case GGML_TYPE_MXFP4:
6393                    case GGML_TYPE_NVFP4:
6394                    case GGML_TYPE_IQ2_XXS:
6395                    case GGML_TYPE_IQ2_XS:
6396                    case GGML_TYPE_IQ2_S:
6397                    case GGML_TYPE_IQ3_XXS:
6398                    case GGML_TYPE_IQ1_S:
6399                    case GGML_TYPE_IQ1_M:
6400                    case GGML_TYPE_IQ3_S:
6401                    case GGML_TYPE_IQ4_NL:
6402                    case GGML_TYPE_IQ4_XS:
6403                    case GGML_TYPE_Q2_K:
6404                    case GGML_TYPE_Q3_K:
6405                    case GGML_TYPE_Q4_0:
6406                    case GGML_TYPE_Q4_1:
6407                    case GGML_TYPE_Q4_K:
6408                    case GGML_TYPE_Q5_0:
6409                    case GGML_TYPE_Q5_1:
6410                    case GGML_TYPE_Q5_K:
6411                    case GGML_TYPE_Q6_K:
6412                    case GGML_TYPE_Q8_0:
6413                        return true;
6414                    default:
6415                        return false;
6416                }
6417            }
6418         case GGML_OP_SET:
6419               return (op->type == GGML_TYPE_F32) &&
6420                      (op->src[0] && op->src[1]) &&
6421                      (op->src[0]->type == GGML_TYPE_F32) &&
6422                      (op->src[1]->type == GGML_TYPE_F32);
6423
6424        case GGML_OP_SET_ROWS:
6425            {
6426                if (op->type == GGML_TYPE_TQ2_0 || op->type == GGML_TYPE_TQ1_0) {
6427                    return false;
6428                }
6429                auto res = (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16 ||
6430                            op->src[0]->type == GGML_TYPE_BF16) &&
6431                           (op->src[1]->type == GGML_TYPE_I64 || op->src[1]->type == GGML_TYPE_I32);
6432                return res;
6433            }
6434            break;
6435        case GGML_OP_DSV4_HC_PRE:
6436            return op->src[0]->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32 &&
6437                op->type == GGML_TYPE_F32;
6438        case GGML_OP_DSV4_HC_COMB:
6439            return op->src[0]->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32 &&
6440                op->src[2]->type == GGML_TYPE_F32 && op->type == GGML_TYPE_F32;
6441        case GGML_OP_DSV4_HC_POST:
6442            return op->src[0]->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32 &&
6443                op->src[2]->type == GGML_TYPE_F32 && op->src[3]->type == GGML_TYPE_F32 &&
6444                op->type == GGML_TYPE_F32;
6445        case GGML_OP_LIGHTNING_INDEXER:
6446            return op->src[0]->type == GGML_TYPE_F32 &&
6447                (op->src[1]->type == GGML_TYPE_F16 || op->src[1]->type == GGML_TYPE_F32 ||
6448                 op->src[1]->type == GGML_TYPE_BF16 || op->src[1]->type == GGML_TYPE_Q8_0 ||
6449                 op->src[1]->type == GGML_TYPE_Q5_1 || op->src[1]->type == GGML_TYPE_Q5_0 ||
6450                 op->src[1]->type == GGML_TYPE_Q4_1 || op->src[1]->type == GGML_TYPE_Q4_0 ||
6451                 op->src[1]->type == GGML_TYPE_IQ4_NL) &&
6452                op->src[2]->type == GGML_TYPE_F32 &&
6453                op->src[3]->type == GGML_TYPE_F16 &&
6454                op->type == GGML_TYPE_F32 &&
6455                op->src[0]->ne[0] == WARP_SIZE * 8;
6456        case GGML_OP_CPY:
6457            {
6458                ggml_type src0_type = op->src[0]->type;
6459                ggml_type src1_type = op->src[1]->type;
6460
6461                if (src0_type == GGML_TYPE_F16) {
6462                    if (src1_type == GGML_TYPE_Q2_K ||
6463                        src1_type == GGML_TYPE_Q3_K ||
6464                        src1_type == GGML_TYPE_Q4_K ||
6465                        src1_type == GGML_TYPE_Q5_K ||
6466                        src1_type == GGML_TYPE_Q6_K ||
6467                        src1_type == GGML_TYPE_IQ2_XXS ||
6468                        src1_type == GGML_TYPE_IQ2_XS ||
6469                        src1_type == GGML_TYPE_IQ2_S ||
6470                        src1_type == GGML_TYPE_IQ3_XXS ||
6471                        src1_type == GGML_TYPE_IQ1_S ||
6472                        src1_type == GGML_TYPE_IQ1_M ||
6473                        src1_type == GGML_TYPE_IQ3_S ||
6474                        src1_type == GGML_TYPE_IQ4_XS) {
6475                        return false;
6476                    }
6477                }
6478
6479                if (src0_type == GGML_TYPE_BF16) {
6480                    if (src1_type == GGML_TYPE_Q4_0 || //big error in ut
6481                        src1_type == GGML_TYPE_Q4_1 || //big error in ut
6482                        src1_type == GGML_TYPE_Q8_0 || //big error in ut
6483                        src1_type == GGML_TYPE_Q2_K ||
6484                        src1_type == GGML_TYPE_Q3_K ||
6485                        src1_type == GGML_TYPE_Q4_K ||
6486                        src1_type == GGML_TYPE_Q5_K ||
6487                        src1_type == GGML_TYPE_Q6_K ||
6488                        src1_type == GGML_TYPE_IQ2_XXS ||
6489                        src1_type == GGML_TYPE_IQ2_XS ||
6490                        src1_type == GGML_TYPE_IQ2_S ||
6491                        src1_type == GGML_TYPE_IQ3_XXS ||
6492                        src1_type == GGML_TYPE_IQ1_S ||
6493                        src1_type == GGML_TYPE_IQ1_M ||
6494                        src1_type == GGML_TYPE_IQ3_S ||
6495                        src1_type == GGML_TYPE_IQ4_XS) {
6496                        return false;
6497                    }
6498                }
6499
6500                if (src0_type == GGML_TYPE_F32) {
6501                    if (src1_type == GGML_TYPE_Q2_K ||
6502                        src1_type == GGML_TYPE_Q3_K ||
6503                        src1_type == GGML_TYPE_Q4_K ||
6504                        src1_type == GGML_TYPE_Q5_K ||
6505                        src1_type == GGML_TYPE_Q6_K ||
6506                        src1_type == GGML_TYPE_IQ2_XXS ||
6507                        src1_type == GGML_TYPE_IQ2_XS ||
6508                        src1_type == GGML_TYPE_IQ2_S ||
6509                        src1_type == GGML_TYPE_IQ3_XXS ||
6510                        src1_type == GGML_TYPE_IQ1_S ||
6511                        src1_type == GGML_TYPE_IQ1_M ||
6512                        src1_type == GGML_TYPE_IQ3_S ||
6513                        src1_type == GGML_TYPE_IQ4_XS) {
6514                        return false;
6515                    }
6516                }
6517
6518                if (src1_type == GGML_TYPE_F32) {
6519                    if (src0_type == GGML_TYPE_Q1_0 ||
6520                        src0_type == GGML_TYPE_NVFP4 ||
6521                        src0_type == GGML_TYPE_Q2_K ||
6522                        src0_type == GGML_TYPE_Q3_K ||
6523                        src0_type == GGML_TYPE_Q4_K ||
6524                        src0_type == GGML_TYPE_Q5_K ||
6525                        src0_type == GGML_TYPE_Q6_K ||
6526                        src0_type == GGML_TYPE_IQ2_XXS ||
6527                        src0_type == GGML_TYPE_IQ2_XS ||
6528                        src0_type == GGML_TYPE_IQ2_S ||
6529                        src0_type == GGML_TYPE_IQ3_XXS ||
6530                        src0_type == GGML_TYPE_IQ1_S ||
6531                        src0_type == GGML_TYPE_IQ1_M ||
6532                        src0_type == GGML_TYPE_IQ3_S ||
6533                        src0_type == GGML_TYPE_IQ4_NL ||
6534                        src0_type == GGML_TYPE_IQ4_XS
6535                    ) {
6536                        return false;
6537                    }
6538                }
6539
6540                if (src0_type == src1_type) {
6541                    if (src1_type == GGML_TYPE_IQ2_XXS ||
6542                        src1_type == GGML_TYPE_IQ2_XS ||
6543                        src1_type == GGML_TYPE_IQ2_S ||
6544                        src1_type == GGML_TYPE_IQ3_XXS ||
6545                        src1_type == GGML_TYPE_IQ3_S ||
6546                        src1_type == GGML_TYPE_IQ1_S ||
6547                        src1_type == GGML_TYPE_IQ1_M ||
6548                        src1_type == GGML_TYPE_TQ2_0 ||
6549                        src1_type == GGML_TYPE_TQ1_0) {
6550                        return false;
6551                    }
6552                }
6553
6554                if (src0_type == GGML_TYPE_TQ2_0 || src1_type == GGML_TYPE_TQ2_0 ||
6555                    src0_type == GGML_TYPE_TQ1_0 || src1_type == GGML_TYPE_TQ1_0) {
6556                    return false;
6557                }
6558
6559                return true;
6560            }
6561        case GGML_OP_REPEAT_BACK:
6562            {
6563                ggml_type src0_type = op->src[0]->type;
6564                return src0_type == GGML_TYPE_F32;
6565            }
6566        case GGML_OP_CONCAT:
6567        case GGML_OP_DUP:
6568        case GGML_OP_ARGMAX:
6569        case GGML_OP_NONE:
6570        case GGML_OP_RESHAPE:
6571        case GGML_OP_VIEW:
6572        case GGML_OP_PERMUTE:
6573        case GGML_OP_TRANSPOSE:
6574        case GGML_OP_ADD:
6575        case GGML_OP_ADD1:
6576        case GGML_OP_ADD_ID:
6577        case GGML_OP_SUB:
6578        case GGML_OP_COUNT_EQUAL:
6579        case GGML_OP_MUL:
6580        case GGML_OP_DIV:
6581        case GGML_OP_REPEAT:
6582            return true;
6583        case GGML_OP_PAD_REFLECT_1D:
6584            return ggml_is_contiguous(op->src[0]) && op-> type == GGML_TYPE_F32 && op->src[0]->type == GGML_TYPE_F32;
6585        case GGML_OP_SQR:
6586        case GGML_OP_SQRT:
6587        case GGML_OP_SIN:
6588        case GGML_OP_COS:
6589        case GGML_OP_CLAMP:
6590        case GGML_OP_LOG:
6591        case GGML_OP_NORM:
6592        case GGML_OP_L2_NORM:
6593        case GGML_OP_GROUP_NORM:
6594        case GGML_OP_RMS_NORM:
6595            return true;
6596        case GGML_OP_RMS_NORM_BACK:
6597            return ggml_is_contiguous(op->src[0]);
6598        case GGML_OP_SCALE:
6599            return true;
6600        case GGML_OP_CONT:
6601            return true;
6602        case GGML_OP_TRI:
6603            {
6604                const ggml_tensor * src0 = op->src[0];
6605                return src0 &&
6606                       op->type == GGML_TYPE_F32 &&
6607                       ggml_is_contiguous(src0);
6608            }
6609        case GGML_OP_DIAG_MASK_INF:
6610            return true;
6611        case GGML_OP_SOFT_MAX:
6612            return true;
6613        case GGML_OP_SOFT_MAX_BACK: {
6614            float max_bias = 0.0f;
6615            memcpy(&max_bias, (const float *) op->op_params + 1, sizeof(float));
6616            return max_bias == 0.0f;
6617        }
6618        case GGML_OP_ROPE:
6619        case GGML_OP_ROPE_BACK:
6620        case GGML_OP_IM2COL:
6621        case GGML_OP_IM2COL_3D:
6622        case GGML_OP_UPSCALE:
6623            return true;
6624        case GGML_OP_COL2IM_1D:
6625            return ggml_is_contiguous(op->src[0]) &&
6626                   (op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16
6627#ifdef GGML_SYCL_HAS_BF16
6628                    || op->type == GGML_TYPE_BF16
6629#endif
6630                   ) &&
6631                   op->src[0]->type == op->type;
6632        case GGML_OP_CONV_3D:
6633            return op->type == GGML_TYPE_F32 &&
6634                   (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16) &&
6635                   op->src[1]->type == GGML_TYPE_F32 &&
6636                   ggml_is_contiguous(op->src[0]) &&
6637                   ggml_is_contiguous(op->src[1]);
6638        case GGML_OP_SUM:
6639        case GGML_OP_SUM_ROWS:
6640        case GGML_OP_MEAN:
6641            return ggml_is_contiguous(op->src[0]);
6642        case GGML_OP_ARGSORT:
6643            return true;
6644        case GGML_OP_TOP_K: {
6645            const ggml_tensor * src0 = op->src[0];
6646            const int k = op->ne[0];
6647            return src0 &&
6648                op->type == GGML_TYPE_I32 &&
6649                src0->type == GGML_TYPE_F32 &&
6650                ggml_is_contiguous(src0) &&
6651                k > 0 && k <= src0->ne[0];
6652        }
6653        case GGML_OP_POOL_2D:
6654        case GGML_OP_POOL_1D:
6655        case GGML_OP_ACC:
6656            return true;
6657        case GGML_OP_PAD:
6658            if (ggml_get_op_params_i32(op, 8) != 0) {
6659                return false;
6660            }
6661            return true;
6662        case GGML_OP_LEAKY_RELU:
6663        case GGML_OP_TIMESTEP_EMBEDDING:
6664        case GGML_OP_RWKV_WKV6:
6665        case GGML_OP_RWKV_WKV7:
6666        case GGML_OP_GATED_LINEAR_ATTN:
6667        case GGML_OP_GATED_DELTA_NET:
6668        case GGML_OP_OPT_STEP_ADAMW:
6669        case GGML_OP_OPT_STEP_SGD:
6670            return true;
6671        case GGML_OP_SSM_CONV:
6672            return op->type == GGML_TYPE_F32 &&
6673                   op->src[0]->type == GGML_TYPE_F32 &&
6674                   op->src[1]->type == GGML_TYPE_F32;
6675        case GGML_OP_ROLL:
6676            return op->type == GGML_TYPE_F32;
6677        case GGML_OP_ARANGE:
6678            return op->type == GGML_TYPE_F32;
6679        case GGML_OP_SSM_SCAN:
6680            if (op->src[3]->ne[0] == 1) {
6681                // Mamba2
6682                // (kernel only supports (d_state == 128 || d_state == 256) && d_head % WARP_SIZE == 0)
6683                return (op->src[0]->ne[0] == 128 || op->src[0]->ne[0] == 256) && op->src[0]->ne[1] % WARP_SIZE == 0;
6684            } else {
6685                // TODO Mamba-1 not yet ported to SYCL
6686                return false;
6687            }
6688        case GGML_OP_FILL:
6689        case GGML_OP_CUMSUM:
6690        case GGML_OP_DIAG:
6691        case GGML_OP_CROSS_ENTROPY_LOSS:
6692        case GGML_OP_CROSS_ENTROPY_LOSS_BACK:
6693            return true;
6694        case GGML_OP_SOLVE_TRI:
6695            return op->src[0]->ne[0] <= SYCL_SOLVE_TRI_MAX_N && op->src[1]->ne[0] <= SYCL_SOLVE_TRI_MAX_K;
6696        case GGML_OP_FLASH_ATTN_EXT:
6697            return ggml_sycl_flash_attn_ext_supported(device, op);
6698        default:
6699            return false;
6700    }
6701
6702    GGML_UNUSED(dev);
6703}
6704
6705static bool ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
6706    bool res = do_ggml_backend_sycl_device_supports_op(dev, op);
6707    GGML_SYCL_DEBUG("[SYCL] call %s op->op=%s op->type=%s -> %s\n", __func__, ggml_op_name(op->op),
6708                    ggml_type_name(op->type), res ? "true" : "false");
6709    return res;
6710}
6711
6712static bool ggml_backend_sycl_device_supports_buft(ggml_backend_dev_t dev, ggml_backend_buffer_type_t buft) {
6713    if (buft->iface.get_name != ggml_backend_sycl_buffer_type_get_name) {
6714        return false;
6715    }
6716    ggml_backend_sycl_buffer_type_context * buft_ctx = (ggml_backend_sycl_buffer_type_context *)buft->context;
6717    ggml_backend_sycl_device_context * sycl_ctx = (ggml_backend_sycl_device_context *)dev->context;
6718    return buft_ctx->device == sycl_ctx->device;
6719}
6720
6721static int64_t get_op_batch_size(const ggml_tensor * op) {
6722    switch (op->op) {
6723        case GGML_OP_GET_ROWS:
6724            return 0;
6725        case GGML_OP_MUL_MAT:
6726            return op->ne[1];
6727        case GGML_OP_MUL_MAT_ID:
6728        case GGML_OP_ROPE:
6729            return op->ne[2];
6730        default:
6731            return ggml_nrows(op);
6732    }
6733}
6734
6735static bool ggml_backend_sycl_device_offload_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
6736    ggml_backend_sycl_device_context * sycl_ctx = (ggml_backend_sycl_device_context *)dev->context;
6737    return get_op_batch_size(op) >= sycl_ctx->op_offload_min_batch_size;
6738}
6739
6740static ggml_backend_event_t
6741ggml_backend_sycl_device_event_new(ggml_backend_dev_t dev) {
6742
6743#ifdef GGML_SYCL_NO_PEER_COPY
6744    return nullptr;
6745#else
6746  sycl::event *event_ptr = new sycl::event();
6747
6748  return new ggml_backend_event{
6749      /* .device = */ dev,
6750      /* .context = */ event_ptr,
6751  };
6752#endif
6753}
6754
6755static void ggml_backend_sycl_device_event_free(ggml_backend_dev_t dev, ggml_backend_event_t event) try {
6756  GGML_UNUSED(dev);
6757  if (event == nullptr) {
6758    return;
6759  }
6760
6761  if (event->context != nullptr) {
6762    sycl::event *sycl_event = static_cast<sycl::event *>(event->context);
6763    delete sycl_event;
6764    event->context = nullptr;
6765  }
6766
6767  delete event;
6768} catch (sycl::exception const &exc) {
6769  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
6770            << ", line:" << __LINE__ << std::endl;
6771  std::exit(1);
6772}
6773
6774
6775static void ggml_backend_sycl_device_event_synchronize(ggml_backend_dev_t dev, ggml_backend_event_t event) try {
6776  GGML_UNUSED(dev);
6777  GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
6778
6779  sycl::event *sycl_event = static_cast<sycl::event *>(event->context);
6780  SYCL_CHECK(CHECK_TRY_ERROR(sycl_event->wait()));
6781} catch (sycl::exception const &exc) {
6782  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
6783            << ", line:" << __LINE__ << std::endl;
6784  std::exit(1);
6785}
6786
6787static const ggml_backend_device_i ggml_backend_sycl_device_interface = {
6788    /* .get_name                = */ ggml_backend_sycl_device_get_name,
6789    /* .get_description         = */ ggml_backend_sycl_device_get_description,
6790    /* .get_memory              = */ ggml_backend_sycl_device_get_memory,
6791    /* .get_type                = */ ggml_backend_sycl_device_get_type,
6792    /* .get_props               = */ ggml_backend_sycl_device_get_props,
6793    /* .init_backend            = */ ggml_backend_sycl_device_init,
6794    /* .get_buffer_type         = */ ggml_backend_sycl_device_get_buffer_type,
6795    /* .get_host_buffer_type    = */ ggml_backend_sycl_device_get_host_buffer_type,
6796    /* .buffer_from_host_ptr    = */ ggml_backend_sycl_device_buffer_from_host_ptr,
6797    /* .supports_op             = */ ggml_backend_sycl_device_supports_op,
6798    /* .supports_buft           = */ ggml_backend_sycl_device_supports_buft,
6799    /* .offload_op              = */ ggml_backend_sycl_device_offload_op,
6800    /* .event_new               = */ ggml_backend_sycl_device_event_new,
6801    /* .event_free              = */ ggml_backend_sycl_device_event_free,
6802    /* .event_synchronize       = */ ggml_backend_sycl_device_event_synchronize,
6803};
6804
6805// backend reg
6806
6807struct ggml_backend_sycl_reg_context {
6808    std::vector<ggml_backend_dev_t> devices;
6809};
6810
6811static const char * ggml_backend_sycl_reg_get_name(ggml_backend_reg_t reg) {
6812    GGML_UNUSED(reg);
6813    return GGML_SYCL_NAME;
6814}
6815
6816static size_t ggml_backend_sycl_reg_get_device_count(ggml_backend_reg_t reg) {
6817    ggml_backend_sycl_reg_context * ctx = (ggml_backend_sycl_reg_context *)reg->context;
6818    return ctx->devices.size();
6819}
6820
6821static ggml_backend_dev_t ggml_backend_sycl_reg_get_device(ggml_backend_reg_t reg, size_t index) {
6822    ggml_backend_sycl_reg_context * ctx = (ggml_backend_sycl_reg_context *)reg->context;
6823    GGML_ASSERT(index < ctx->devices.size());
6824    return ctx->devices[index];
6825}
6826
6827// ==========================================================================
6828// Tensor parallelism (--split-mode tensor) for the SYCL backend.
6829//
6830// The meta-backend invokes these three entry points via get_proc_address:
6831//   * ggml_backend_sycl_comm_init             - one-time per-graph setup
6832//   * ggml_backend_sycl_comm_allreduce_tensor - per-allreduce step
6833//   * ggml_backend_sycl_comm_free             - tear-down
6834//
6835// For N=2 (dual-GPU), this is a degenerate ring allreduce with dual paths
6836// chosen by tensor size:
6837//
6838//   * Small (nelem < 32K): FP32 direct memcpy + per-device ADD
6839//     kernel. The kernel depends_on() its corresponding memcpy event
6840//     so it doesn't read partial data. Both devices run in parallel.
6841//
6842//   * Large (nelem >= 32K): BF16-compressed. Each device compresses
6843//     its FP32 partial to BF16 locally, cross-device memcpys
6844//     to the peer (half the PCI bandwidth), where it is decompressed
6845//     and added into the local FP32 partial. 6 SYCL submissions per
6846//     allreduce (2 compress + 2 memcpy + 2 decompress-add) vs the
6847//     4 for the small path, but the bandwidth saving > 6 GB/s PCIe x 2
6848//     dominates for larger tensors.
6849//
6850// Storage: A persistent uint8_t buffer per device, sized to
6851// 4 * nelem bytes. Both paths reinterpret the same bytes (small path
6852// as nelem floats; large path as outbox + inbox = 2*nelem uint16_t
6853// each, using the full 4*nelem byte budget either way). Single
6854// alloc+free per device keeps the SYCL pool's strict-LIFO invariant
6855// trivial.
6856//
6857// For non-(N=2 FP32 contiguous) cases, comm_init or comm_allreduce_tensor
6858// returns null/false, causing the meta-backend to use its generic
6859// butterfly all-reduce fallback.
6860// ==========================================================================
6861
6862struct ggml_backend_sycl_comm_context {
6863    std::vector<ggml_backend_t> backends;
6864    // ONE persistent per-device byte buffer, 4*nelem bytes.  Both the
6865    // FP32 small-tensor path and the BF16 large-tensor path share it
6866    // by reinterpreting.
6867    std::unique_ptr<ggml_sycl_pool_alloc<uint8_t>> buf0;
6868    std::unique_ptr<ggml_sycl_pool_alloc<uint8_t>> buf1;
6869    int64_t buf_nelem = 0;
6870};
6871
6872void * ggml_backend_sycl_comm_init(ggml_backend_t * backends, size_t n_backends) try {
6873    for (size_t i = 0; i < n_backends; ++i) {
6874        if (!ggml_backend_is_sycl(backends[i])) {
6875            return nullptr;
6876        }
6877    }
6878
6879    // Initial version: N=2 only. For N!=2, returning null makes the
6880    // meta-backend skip this backend-specific allreduce entirely.
6881    if (n_backends != 2) {
6882        return nullptr;
6883    }
6884
6885    auto * ctx = new ggml_backend_sycl_comm_context;
6886    ctx->backends.assign(backends, backends + n_backends);
6887    auto * sctx0 = (ggml_backend_sycl_context *) backends[0]->context;
6888    auto * sctx1 = (ggml_backend_sycl_context *) backends[1]->context;
6889    ctx->buf0 = std::make_unique<ggml_sycl_pool_alloc<uint8_t>>(sctx0->pool());
6890    ctx->buf1 = std::make_unique<ggml_sycl_pool_alloc<uint8_t>>(sctx1->pool());
6891    return ctx;
6892}
6893catch (const sycl::exception &) { return nullptr; }
6894catch (...)                     { return nullptr; }
6895
6896void ggml_backend_sycl_comm_free(void * comm_ctx_v) {
6897    auto * comm_ctx = static_cast<ggml_backend_sycl_comm_context *>(comm_ctx_v);
6898    if (comm_ctx == nullptr) {
6899        return;
6900    }
6901
6902    // Sync both per-device queues so the pool_alloc destructors don't
6903    // return memory still in use by the last kernel.
6904    if (comm_ctx->backends.size() == 2) {
6905        auto * sctx0 = (ggml_backend_sycl_context *) comm_ctx->backends[0]->context;
6906        auto * sctx1 = (ggml_backend_sycl_context *) comm_ctx->backends[1]->context;
6907        try {
6908            sctx0->stream()->wait();
6909            sctx1->stream()->wait();
6910        } catch (...) { /* best effort during shutdown */ }
6911    }
6912
6913    delete comm_ctx;
6914}
6915
6916bool ggml_backend_sycl_comm_allreduce_tensor(void * comm_ctx_v, struct ggml_tensor ** tensors) try {
6917    if (comm_ctx_v == nullptr) {
6918        return false;
6919    }
6920
6921    auto * comm_ctx = static_cast<ggml_backend_sycl_comm_context *>(comm_ctx_v);
6922    const size_t n_backends = comm_ctx->backends.size();
6923
6924    // Fast path: N=2, F32/F16, contiguous, matching shapes.
6925    if (n_backends != 2) {
6926        return false;
6927    }
6928    // Accept F32 or F16 inputs natively (types must match). F16 takes the
6929    // direct 2-byte memcpy + add path below; other types return false so the
6930    // meta-backend uses its generic all-reduce.
6931    if (tensors[0]->type != tensors[1]->type) {
6932        return false;
6933    }
6934    if (tensors[0]->type != GGML_TYPE_F32 && tensors[0]->type != GGML_TYPE_F16) {
6935        return false;
6936    }
6937    if (!ggml_is_contiguous(tensors[0]) || !ggml_is_contiguous(tensors[1])) {
6938        return false;
6939    }
6940    if (ggml_nelements(tensors[0]) != ggml_nelements(tensors[1])) {
6941        return false;
6942    }
6943
6944    const int64_t nelem  = ggml_nelements(tensors[0]);
6945    const size_t  nbytes = ggml_nbytes(tensors[0]);
6946    if (nelem == 0) {
6947        return true;
6948    }
6949
6950    auto * ctx0 = (ggml_backend_sycl_context *) comm_ctx->backends[0]->context;
6951    auto * ctx1 = (ggml_backend_sycl_context *) comm_ctx->backends[1]->context;
6952    queue_ptr q0 = ctx0->stream();
6953    queue_ptr q1 = ctx1->stream();
6954
6955    // Grow per-device byte buffers if needed (4 * nelem bytes each).
6956    if (comm_ctx->buf_nelem < nelem) {
6957        comm_ctx->buf0->realloc(nelem * 4);
6958        comm_ctx->buf1->realloc(nelem * 4);
6959        comm_ctx->buf_nelem = nelem;
6960    }
6961    uint8_t * buf0 = comm_ctx->buf0->get();
6962    uint8_t * buf1 = comm_ctx->buf1->get();
6963
6964    // F16 native path: direct 2-byte cross-device copy + add, skipping the
6965    // F32 round-trip the meta-backend fallback would force. Cross-device copies
6966    // go through dev2dev_memcpy because the two devices are in separate SYCL
6967    // contexts (a raw peer-USM q->memcpy would be a silent no-op).
6968    if (tensors[0]->type == GGML_TYPE_F16) {
6969        sycl::half * f16_out0 = (sycl::half *) tensors[0]->data;
6970        sycl::half * f16_out1 = (sycl::half *) tensors[1]->data;
6971        sycl::half * f16_tmp0 = (sycl::half *) buf0;
6972        sycl::half * f16_tmp1 = (sycl::half *) buf1;
6973
6974        q0->wait();
6975        q1->wait();
6976        dev2dev_memcpy(ctx0->device, *q0, ctx1->device, *q1, f16_tmp0, tensors[1]->data, nbytes);
6977        dev2dev_memcpy(ctx1->device, *q1, ctx0->device, *q0, f16_tmp1, tensors[0]->data, nbytes);
6978
6979        q0->submit([&](sycl::handler & h) {
6980            h.parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
6981                f16_out0[i] = (sycl::half) ((float) f16_out0[i] + (float) f16_tmp0[i]);
6982            });
6983        });
6984        q1->submit([&](sycl::handler & h) {
6985            h.parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
6986                f16_out1[i] = (sycl::half) ((float) f16_out1[i] + (float) f16_tmp1[i]);
6987            });
6988        });
6989        return true;
6990    }
6991
6992    float * out0 = (float *) tensors[0]->data;
6993    float * out1 = (float *) tensors[1]->data;
6994
6995    // BF16 threshold: above this, the PCIe savings from halving the
6996    // cross-device bytes outweigh the 2 extra compress kernels.
6997    // Below: stay on the FP32 fast path.  Threshold mirrors the CUDA
6998    // NCCL allreduce pattern for n_backends=2.
6999    static constexpr int64_t BF16_THRESHOLD = 32768;
7000
7001    if (nelem < BF16_THRESHOLD) {
7002        // FP32 small path: 4 SYCL submissions per allreduce.
7003        float * tmp0 = (float *) buf0;
7004        float * tmp1 = (float *) buf1;
7005
7006        // COMM-D2D-FIX: the two devices are in SEPARATE SYCL contexts, so a raw
7007        // q->memcpy of a peer USM pointer is a silent no-op. Route cross-device
7008        // copies through dev2dev_memcpy (L0 direct copy / host staging). It is
7009        // synchronous, so wait for the local partials to be produced first.
7010        q0->wait();
7011        q1->wait();
7012        dev2dev_memcpy(ctx0->device, *q0, ctx1->device, *q1, tmp0, tensors[1]->data, nbytes);
7013        dev2dev_memcpy(ctx1->device, *q1, ctx0->device, *q0, tmp1, tensors[0]->data, nbytes);
7014
7015        q0->submit([&](sycl::handler & h) {
7016            h.parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
7017                out0[i] += tmp0[i];
7018            });
7019        });
7020        q1->submit([&](sycl::handler & h) {
7021            h.parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
7022                out1[i] += tmp1[i];
7023            });
7024        });
7025        return true;
7026    }
7027
7028    // BF16 large path: 6 SYCL submissions per allreduce, but the
7029    // cross-device memcpy is HALF the bytes. Pure bit-shift
7030    // conversion (no rounding) — matches ggml's truncating fp32->bf16.
7031    uint16_t * outbox0 = (uint16_t *) buf0;
7032    uint16_t * inbox0  = outbox0 + nelem;
7033    uint16_t * outbox1 = (uint16_t *) buf1;
7034    uint16_t * inbox1  = outbox1 + nelem;
7035
7036    // Phase A: compress each device's local partial in parallel.
7037    sycl::event c0 = q0->parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
7038        outbox0[i] = (uint16_t) (sycl::bit_cast<uint32_t>(out0[i]) >> 16);
7039    });
7040
7041    sycl::event c1 = q1->parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
7042        outbox1[i] = (uint16_t) (sycl::bit_cast<uint32_t>(out1[i]) >> 16);
7043    });
7044
7045    // Phase B: COMM-D2D-FIX-BF16 cross-device copy of compressed bytes via
7046    // dev2dev_memcpy (separate SYCL contexts; sync copy after compress).
7047    const size_t bf16_bytes = nelem * sizeof(uint16_t);
7048    c0.wait();
7049    c1.wait();
7050    dev2dev_memcpy(ctx0->device, *q0, ctx1->device, *q1, inbox0, outbox1, bf16_bytes);
7051    dev2dev_memcpy(ctx1->device, *q1, ctx0->device, *q0, inbox1, outbox0, bf16_bytes);
7052
7053    // Phase C: decompress + add into local FP32 partial.
7054    q0->submit([&](sycl::handler & h) {
7055        h.parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
7056            out0[i] += sycl::bit_cast<float>(((uint32_t) inbox0[i]) << 16);
7057        });
7058    });
7059
7060    q1->submit([&](sycl::handler & h) {
7061        h.parallel_for(sycl::range<1>(nelem), [=](sycl::id<1> i) {
7062            out1[i] += sycl::bit_cast<float>(((uint32_t) inbox1[i]) << 16);
7063        });
7064    });
7065
7066    return true;
7067}
7068catch (const sycl::exception &) { return false; }
7069catch (...)                     { return false; }
7070
7071static void *ggml_backend_sycl_reg_get_proc_address(ggml_backend_reg_t reg, const char *name) {
7072    GGML_UNUSED(reg);
7073
7074    if (strcmp(name, "ggml_backend_split_buffer_type") == 0) {
7075        return (void *)ggml_backend_sycl_split_buffer_type;
7076    }
7077
7078    // Tensor parallelism (--split-mode tensor) entry points.
7079    if (strcmp(name, "ggml_backend_comm_init") == 0) {
7080        return (void *)ggml_backend_sycl_comm_init;
7081    }
7082    if (strcmp(name, "ggml_backend_comm_free") == 0) {
7083        return (void *)ggml_backend_sycl_comm_free;
7084    }
7085    if (strcmp(name, "ggml_backend_comm_allreduce_tensor") == 0) {
7086        return (void *)ggml_backend_sycl_comm_allreduce_tensor;
7087    }
7088
7089    // SYCL doesn't support registering host memory, left here for reference
7090    // "ggml_backend_register_host_buffer"
7091    // "ggml_backend_unregister_host_buffer"
7092    GGML_UNUSED(name);
7093    return nullptr;
7094}
7095
7096static const ggml_backend_reg_i ggml_backend_sycl_reg_interface = {
7097    /* .get_name          = */ ggml_backend_sycl_reg_get_name,
7098    /* .get_device_count  = */ ggml_backend_sycl_reg_get_device_count,
7099    /* .get_device        = */ ggml_backend_sycl_reg_get_device,
7100    /* .get_proc_address  = */ ggml_backend_sycl_reg_get_proc_address,
7101};
7102
7103
7104// backend registry
7105
7106ggml_backend_reg_t ggml_backend_sycl_reg() {
7107    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_reg\n");
7108    static ggml_backend_reg reg;
7109    static bool initialized = false;
7110
7111    {
7112        static std::mutex mutex;
7113        std::lock_guard<std::mutex> lock(mutex);
7114        if (!initialized) {
7115            ggml_check_sycl();
7116            ggml_backend_sycl_reg_context * ctx = new ggml_backend_sycl_reg_context;
7117            const int min_batch_size = getenv("GGML_OP_OFFLOAD_MIN_BATCH") ? atoi(getenv("GGML_OP_OFFLOAD_MIN_BATCH")) : 32;
7118
7119            for (int i = 0; i < ggml_sycl_info().device_count; i++) {
7120                ggml_backend_sycl_device_context * dev_ctx = new ggml_backend_sycl_device_context;
7121                dev_ctx->device = i;
7122                dev_ctx->name = GGML_SYCL_NAME + std::to_string(i);
7123
7124                ggml_sycl_set_device(i);
7125
7126                dpct::device_info prop;
7127                SYCL_CHECK(CHECK_TRY_ERROR(dpct::get_device_info(
7128                    prop, dpct::dev_mgr::instance().get_device(i))));
7129
7130                dev_ctx->description = prop.get_name();
7131                dev_ctx->op_offload_min_batch_size = min_batch_size;
7132
7133                ggml_backend_dev_t dev = new ggml_backend_device {
7134                    /* .iface       = */ ggml_backend_sycl_device_interface,
7135                    /* .reg         = */ &reg,
7136                    /* .context     = */ dev_ctx
7137                };
7138                ctx->devices.push_back(dev);
7139            }
7140
7141            reg = ggml_backend_reg {
7142                /* .api_version = */ GGML_BACKEND_API_VERSION,
7143                /* .iface       = */ ggml_backend_sycl_reg_interface,
7144                /* .context     = */ ctx
7145            };
7146        }
7147
7148        initialized = true;
7149    }
7150
7151    return &reg;
7152}
7153
7154ggml_backend_t ggml_backend_sycl_init(int device) {
7155    GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_init\n");
7156    ggml_check_sycl();
7157
7158    check_allow_gpu_index(device);
7159
7160    ggml_backend_sycl_context * ctx = new ggml_backend_sycl_context(device);
7161    if (ctx == nullptr) {
7162        GGML_LOG_ERROR("%s: error: failed to allocate context\n", __func__);
7163        return nullptr;
7164    };
7165
7166    ggml_backend_t sycl_backend = new ggml_backend {
7167        /* .guid    = */ ggml_backend_sycl_guid(),
7168        /* .iface   = */ ggml_backend_sycl_interface,
7169        /* .device  = */ ggml_backend_reg_dev_get(ggml_backend_sycl_reg(), device),
7170        /* .context = */ ctx
7171    };
7172
7173    return sycl_backend;
7174}
7175
7176GGML_BACKEND_DL_IMPL(ggml_backend_sycl_reg)