char-fork/llama.cpp

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

Aman Guptaggml: add SWIGLU_CLAMP (#27930)0190529ec

char/aliceai-t5-moe
113.3 KiB3070 linesraw
1/*
2 * Copyright (c) 2023-2026 The ggml authors
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 * IN THE SOFTWARE.
21 */
22
23#include "ggml-cann.h"
24
25#include "ggml-backend-impl.h"
26#include "ggml-cann/aclnn_ops.h"
27#include "ggml-cann/common.h"
28#include "ggml-impl.h"
29#include "ggml.h"
30
31#include <acl/acl.h>
32#include <aclnnop/aclnn_trans_matmul_weight.h>
33#include <stdarg.h>
34
35#include <chrono>
36#include <cmath>
37#include <cstdio>
38#include <cstring>
39#include <memory>
40#include <mutex>
41#include <optional>
42#include <queue>
43#include <unordered_map>
44#include <unordered_set>
45#include <vector>
46
47#define GGML_COMMON_DECL_C
48
49#include "ggml-common.h"
50
51#define GGML_CANN_NAME "CANN"
52
53/**
54 * @brief Handles CANN errors by printing an error message and aborting.
55 *
56 * @param stmt The statement that caused the error.
57 * @param func The function in which the error occurred.
58 * @param file The file in which the error occurred.
59 * @param line The line number where the error occurred.
60 * @param msg The error message.
61 */
62[[noreturn]] void ggml_cann_error(const char * stmt, const char * func, const char * file, int line, const char * msg) {
63    int32_t id = -1;
64    aclrtGetDevice(&id);
65
66    GGML_LOG_ERROR("CANN error: %s\n", msg);
67    GGML_LOG_ERROR("  current device: %d, in function %s at %s:%d\n", id, func, file, line);
68    GGML_LOG_ERROR("  %s\n", stmt);
69    // abort with GGML_ASSERT to get a stack trace
70    GGML_ABORT("CANN error");
71}
72
73// Thread-local variable to record the current device of this thread.
74thread_local int g_current_cann_device = -1;
75
76/**
77 * @brief Set the CANN device to be used.
78 *
79 * @param device The target device ID to set.
80 */
81void ggml_cann_set_device(const int32_t device) {
82    // int current_device = -1;
83    // Note: In some CANN versions, if no device has been set yet,
84    //       aclrtGetDevice(&current_device) may return 0 by default.
85    // aclrtGetDevice(&current_device);
86
87    // If the current device is already the target one, no need to switch.
88    if (device == g_current_cann_device) {
89        return;
90    }
91
92    // Switch to the new device.
93    ACL_CHECK(aclrtSetDevice(device));
94
95    // Update the global device record.
96    g_current_cann_device = device;
97}
98
99/**
100 * @brief Get the value of the specified environment variable (name) as lowercase.
101 *        if not empty, return a std::string object
102 */
103std::optional<std::string> get_env_as_lowercase(const std::string & name) {
104    const char * val = std::getenv(name.c_str());
105    if (!val) {
106        return std::nullopt;
107    }
108    std::string res = std::string(val);
109    std::transform(res.begin(), res.end(), res.begin(), ::tolower);
110    return res;
111}
112
113/**
114 * @brief Verify whether the environment variable is a valid value.
115 */
116bool parse_bool(const std::string & value) {
117    static const std::unordered_set<std::string> valid_values = { "on", "1", "yes", "y", "enable", "true" };
118    return valid_values.find(value) != valid_values.end();
119}
120
121/**
122 * @brief Parse a string as an integer, returning 0 if invalid.
123 *
124 * This function attempts to convert the input string `value` to an `int`.
125 * If the string is not a valid integer or is out of the `int` range,
126 * it returns 0.
127 *
128 * @param value The string to parse.
129 * @return The parsed integer, or 0 if conversion fails.
130 */
131int parse_integer(const std::string & value) {
132    try {
133        return std::stoi(value);
134    } catch (...) {
135        return 0;
136    }
137}
138
139/**
140 * @brief Initialize the CANN device information.
141 *
142 * This function initializes the CANN device information by obtaining the
143 * device count and setting the memory allocation granularity for each device.
144 *
145 * @return A structure containing the device information.
146 */
147static ggml_cann_device_info ggml_cann_init() {
148    ggml_cann_device_info info = {};
149
150    aclError err = aclrtGetDeviceCount((uint32_t *) &info.device_count);
151
152    if (err != ACL_SUCCESS) {
153        GGML_LOG_ERROR("%s: failed to initialize CANN: %s\n", __func__, aclGetRecentErrMsg());
154        return info;
155    }
156
157    GGML_ASSERT(info.device_count <= GGML_CANN_MAX_DEVICES);
158
159    for (int id = 0; id < info.device_count; ++id) {
160        aclrtPhysicalMemProp prop = {};
161        prop.handleType           = ACL_MEM_HANDLE_TYPE_NONE;
162        prop.allocationType       = ACL_MEM_ALLOCATION_TYPE_PINNED;
163        prop.memAttr              = ACL_HBM_MEM_HUGE;
164        prop.location.type        = ACL_MEM_LOCATION_TYPE_DEVICE;
165        prop.location.id          = id;
166        prop.reserve              = 0;
167        err                       = aclrtMemGetAllocationGranularity(&prop, ACL_RT_MEM_ALLOC_GRANULARITY_RECOMMENDED,
168                                                                     &info.devices[id].vmm_granularity);
169        info.devices[id].vmm      = err == ACL_SUCCESS;
170
171        size_t free, total;
172        ggml_backend_cann_get_device_memory(id, &free, &total);
173        info.devices[id].total_vram = free;
174    }
175
176    // TODO: add more device info later.
177    return info;
178}
179
180/**
181 * @brief Retrieve the CANN device information.
182 *
183 * This function returns a reference to a structure containing the CANN device
184 * information. The device information is initialized once and reused on
185 * subsequent calls.
186 *
187 * @return A reference to the structure containing the device information.
188 */
189const ggml_cann_device_info & ggml_cann_info() {
190    static ggml_cann_device_info info = ggml_cann_init();
191    return info;
192}
193
194//#define DEBUG_CANN_MALLOC
195/**
196 * @brief A pool of CANN buffers(priority segment buffer).
197 *
198 * This class manages a pool of CANN buffers for a specific device.
199 */
200struct ggml_cann_pool_buf_prio : public ggml_cann_pool {
201    /**
202     * @brief The maximum reuse margin for a buffer.
203     */
204    static const size_t max_reuse_margin = 1ull << 22;  // 4MB
205
206    /**
207     * @brief The minimum free margin for a buffer.
208     */
209    static const size_t min_free_margin = 1ull << 20;  // 1MB
210
211    /**
212     * @brief The alignment for buffer allocation.
213     */
214    static const size_t alignment = 128;
215
216    /**
217     * @brief The device ID associated with this buffer pool.
218     */
219    int device;
220
221    /**
222     * @brief Whether to disable clean during buffer allocation.
223     */
224    bool disable_clean = false;
225
226    /**
227     * @brief Structure representing a CANN buffer.
228     */
229    struct ggml_cann_buffer {
230        void *                                ptr  = nullptr;  ///< Pointer to the buffer.
231        size_t                                size = 0;        ///< Size of the buffer.
232        std::chrono::steady_clock::time_point last_used;       ///< Last used time.
233
234        bool operator>(const ggml_cann_buffer & other) const { return size > other.size; }
235    };
236
237    /**
238     * @brief Array of CANN buffers in the pool.
239     */
240    std::unordered_map<void *, size_t>                                                   buffer_pool;
241    std::priority_queue<ggml_cann_buffer, std::vector<ggml_cann_buffer>, std::greater<>> free_buffers;
242
243    /**
244     * @brief Total size of all buffers in the pool.
245     */
246    size_t pool_size = 0;
247
248    /**
249     * @brief Constructor to initialize the buffer pool for a specific device.
250     *
251     * @param device The device ID to associate with this buffer pool.
252     */
253    explicit ggml_cann_pool_buf_prio(int device) : device(device) {
254        disable_clean = parse_bool(get_env_as_lowercase("GGML_CANN_DISABLE_BUF_POOL_CLEAN").value_or(""));
255    }
256
257    /**
258     * @brief Destructor to free all buffers in the pool.
259     */
260    ~ggml_cann_pool_buf_prio() {
261        ggml_cann_set_device(device);
262        for (auto & [b_ptr, b_size] : buffer_pool) {
263            aclrtFree(b_ptr);
264            pool_size -= b_size;
265        }
266        buffer_pool.clear();
267        GGML_ASSERT(pool_size == 0);
268    }
269
270    /**
271     * @brief Allocate a buffer of the given size.
272     *
273     * @param size The size of the buffer to allocate.
274     * @param actual_size A pointer to a variable to receive the actual size of
275     * the allocated buffer.
276     * @return A pointer to the allocated buffer.
277     */
278    void * alloc(size_t size, size_t * actual_size) override {
279        size = GGML_PAD(size, alignment);
280        if (size == 0) {
281            size = alignment;
282        }
283
284        void * ptr = nullptr;
285        auto   now = std::chrono::steady_clock::now();
286
287        std::vector<ggml_cann_buffer> free_buffers_rest;
288        free_buffers_rest.reserve(free_buffers.size());
289        while (!free_buffers.empty()) {
290            auto b = free_buffers.top();
291            free_buffers.pop();
292
293            if (b.size >= size) {
294                // reuse the buffer if the size is enough
295                const size_t margin = b.size - size;
296                if (margin <= max_reuse_margin) {
297                    *actual_size = b.size;
298                    ptr          = b.ptr;
299#ifdef DEBUG_CANN_MALLOC
300                    GGML_LOG_INFO(
301                        "cann pool[%d]: reused   %p, "
302                        "pool_size = %5u MB, "
303                        "size = %5u MB, "
304                        "margin = %5u MB\n",
305                        device, b.ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576),
306                        (uint32_t) (GGML_PAD(size, 1048576) / 1048576),
307                        (uint32_t) (GGML_PAD(margin, 1048576) / 1048576));
308#endif
309                    break;
310                }
311            }
312
313            bool should_clean = !disable_clean && b.size > min_free_margin &&
314                                std::chrono::duration_cast<std::chrono::milliseconds>(now - b.last_used).count() > 100;
315            if (should_clean) {
316                // free the buffer if the size is needed to be freed
317                ACL_CHECK(aclrtFree(b.ptr));
318                pool_size -= b.size;
319                buffer_pool.erase(b.ptr);
320#ifdef DEBUG_CANN_MALLOC
321                GGML_LOG_INFO(
322                    "cann pool[%d]: clean    %p, "
323                    "pool_size = %5u MB, "
324                    "size = %5u MB\n",
325                    device, b.ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576),
326                    (uint32_t) (GGML_PAD(b.size, 1048576) / 1048576));
327#endif
328                continue;
329            }
330            free_buffers_rest.push_back(b);
331        }
332        for (ggml_cann_buffer & b : free_buffers_rest) {
333            free_buffers.push(std::move(b));
334        }
335
336#ifdef DEBUG_CANN_MALLOC
337        GGML_LOG_INFO("cann pool[%d] free pool_size = %5u MB\n\n", device,
338                      (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576));
339#endif
340        if (ptr != nullptr) {
341            return ptr;
342        }
343
344        // allocate a new buffer if no buffer can be reused
345        ggml_cann_set_device(device);
346        ACL_CHECK(aclrtMalloc(&ptr, size, ACL_MEM_MALLOC_HUGE_FIRST));
347        *actual_size = size;
348        pool_size += size;
349#ifdef DEBUG_CANN_MALLOC
350        GGML_LOG_INFO(
351            "cann pool[%d]: allocate %p, "
352            "pool_size = %5u MB, "
353            "size = %5u MB\n",
354            device, ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576),
355            (uint32_t) (GGML_PAD(size, 1048576) / 1048576));
356#endif
357        buffer_pool.emplace(ptr, size);
358        return ptr;
359    }
360
361    /**
362     * @brief Free a buffer and return it to the pool.
363     *
364     * @param ptr Pointer to the buffer to free.
365     * @param size Size of the buffer to free.
366     */
367    void free(void * ptr, size_t size) override {
368        GGML_UNUSED(size);
369        auto it = buffer_pool.find(ptr);
370        if (it == buffer_pool.end()) {
371            GGML_ABORT("cann pool[%d]: buffer %p not found in pool\n", device, ptr);
372        }
373
374        auto now = std::chrono::steady_clock::now();
375        free_buffers.emplace(ggml_cann_buffer{ ptr, it->second, now });
376#ifdef DEBUG_CANN_MALLOC
377        GGML_LOG_INFO(
378            "cann pool[%d]: return   %p, "
379            "pool_size = %5u MB\n",
380            device, ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576));
381#endif
382    }
383};
384
385/**
386 * @brief A pool of CANN buffers(segment buffer).
387 *
388 * This class manages a pool of CANN buffers for a specific device.
389 */
390struct ggml_cann_pool_buf : public ggml_cann_pool {
391    /**
392     * @brief The maximum reuse margin for a buffer.
393     */
394    static const size_t max_reuse_margin = 1ull << 22;  // 4MB
395
396    /**
397     * @brief The minimum free margin for a buffer.
398     */
399    static const size_t min_free_margin = 1ull << 20;  // 1MB
400
401    /**
402     * @brief The alignment for buffer allocation.
403     */
404    static const size_t alignment = 128;
405
406    /**
407     * @brief The maximum number of buffers in the pool.
408     */
409    static const int MAX_BUFFERS = 256;
410
411    /**
412     * @brief The device ID associated with this buffer pool.
413     */
414    int device;
415
416    /**
417     * @brief Whether to disable clean during buffer allocation.
418     */
419    bool disable_clean = false;
420
421    /**
422     * @brief Structure representing a CANN buffer.
423     */
424    struct ggml_cann_buffer {
425        void *                                ptr  = nullptr;  ///< Pointer to the buffer memory.
426        size_t                                size = 0;        ///< Size of the buffer.
427        bool                                  used = false;    ///< Whether the buffer is currently in use.
428        std::chrono::steady_clock::time_point last_used;       ///< Last used time.
429    };
430
431    /**
432     * @brief Array of CANN buffers in the pool.
433     */
434    ggml_cann_buffer buffer_pool[MAX_BUFFERS] = {};
435
436    /**
437     * @brief Total size of all buffers in the pool.
438     */
439    size_t pool_size = 0;
440
441    /**
442     * @brief Constructor to initialize the buffer pool for a specific device.
443     *
444     * @param device The device ID to associate with this buffer pool.
445     */
446    explicit ggml_cann_pool_buf(int device) : device(device) {
447        disable_clean = parse_bool(get_env_as_lowercase("GGML_CANN_DISABLE_BUF_POOL_CLEAN").value_or(""));
448    }
449
450    /**
451     * @brief Destructor to free all buffers in the pool.
452     */
453    ~ggml_cann_pool_buf() {
454        ggml_cann_set_device(device);
455        for (int i = 0; i < MAX_BUFFERS; ++i) {
456            ggml_cann_buffer & b = buffer_pool[i];
457            if (b.ptr != nullptr) {
458                aclrtFree(b.ptr);
459                pool_size -= b.size;
460            }
461        }
462        GGML_ASSERT(pool_size == 0);
463    }
464
465    /**
466     * @brief Allocate a buffer of the given size.
467     *
468     * @param size The size of the buffer to allocate.
469     * @param actual_size A pointer to a variable to receive the actual size of
470     * the allocated buffer.
471     * @return A pointer to the allocated buffer.
472     */
473    void * alloc(size_t size, size_t * actual_size) override {
474        size = GGML_PAD(size, alignment);
475        if (size == 0) {
476            size = alignment;
477        }
478
479        void * ptr = nullptr;
480        auto   now = std::chrono::steady_clock::now();
481
482        int i = 0;
483        for (; i < MAX_BUFFERS; ++i) {
484            ggml_cann_buffer & b = buffer_pool[i];
485            if (b.ptr == nullptr) {
486                break;
487            }
488            if (b.used) {
489                continue;
490            }
491            if (b.size >= size) {
492                // reuse the buffer if the size is enough
493                const size_t margin = b.size - size;
494                if (margin <= max_reuse_margin) {
495                    *actual_size = b.size;
496                    b.used       = true;
497                    ptr          = b.ptr;
498#ifdef DEBUG_CANN_MALLOC
499                    GGML_LOG_INFO(
500                        "cann pool[%d]: reused   %p, "
501                        "pool_size = %5u MB, "
502                        "size = %5u MB, "
503                        "margin = %5u MB\n",
504                        device, b.ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576),
505                        (uint32_t) (GGML_PAD(size, 1048576) / 1048576),
506                        (uint32_t) (GGML_PAD(margin, 1048576) / 1048576));
507#endif
508                    break;
509                }
510            }
511
512            bool should_clean = !disable_clean && b.size > min_free_margin &&
513                                std::chrono::duration_cast<std::chrono::milliseconds>(now - b.last_used).count() > 100;
514            if (should_clean) {
515                // free the buffer if the size is needed to be freed
516                ACL_CHECK(aclrtFree(b.ptr));
517                pool_size -= b.size;
518#ifdef DEBUG_CANN_MALLOC
519                GGML_LOG_INFO(
520                    "cann pool[%d]: clean    %p, "
521                    "pool_size = %5u MB, "
522                    "size = %5u MB\n",
523                    device, b.ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576),
524                    (uint32_t) (GGML_PAD(b.size, 1048576) / 1048576));
525#endif
526                b.ptr = nullptr;
527            }
528        }
529        if (ptr != nullptr) {
530            return ptr;
531        }
532
533        if (i < MAX_BUFFERS) {
534            // allocate a new buffer if no buffer can be reused
535            ggml_cann_buffer & b = buffer_pool[i];
536            ggml_cann_set_device(device);
537            ACL_CHECK(aclrtMalloc(&b.ptr, size, ACL_MEM_MALLOC_HUGE_FIRST));
538            pool_size += size;
539            *actual_size = size;
540            b.size       = size;
541            b.used       = true;
542            if (i >= MAX_BUFFERS - 8) {
543                GGML_LOG_WARN("cann pool[%d]: slots almost full\n", device);
544            }
545#ifdef DEBUG_CANN_MALLOC
546            GGML_LOG_INFO(
547                "cann pool[%d]: allocate %p, "
548                "pool_size = %5u MB, "
549                "size = %5u MB\n",
550                device, b.ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576),
551                (uint32_t) (GGML_PAD(b.size, 1048576) / 1048576));
552#endif
553            return b.ptr;
554        }
555
556        GGML_ABORT("cann pool[%d]: slots full\n", device);
557    }
558
559    /**
560     * @brief Free a buffer and return it to the pool.
561     *
562     * @param ptr Pointer to the buffer to free.
563     * @param size Size of the buffer to free.
564     */
565    void free(void * ptr, size_t size) override {
566        GGML_UNUSED(size);
567        for (int i = 0; i < MAX_BUFFERS; ++i) {
568            ggml_cann_buffer & b = buffer_pool[i];
569            if (b.ptr != ptr) {
570                continue;
571            }
572            b.used      = false;
573            b.last_used = std::chrono::steady_clock::now();
574#ifdef DEBUG_CANN_MALLOC
575            GGML_LOG_INFO(
576                "cann pool[%d]: return   %p, "
577                "pool_size = %5u MB\n",
578                device, b.ptr, (uint32_t) (GGML_PAD(pool_size, 1048576) / 1048576));
579#endif
580            return;
581        }
582        GGML_ABORT("cann pool[%d]: slots full\n", device);
583    }
584};
585
586/**
587 * @brief A pool of CANN buffers with virtual memory.
588 *
589 * This class manages a pool of CANN buffers with virtual memory for a specific
590 * device.
591 */
592struct ggml_cann_pool_vmm : public ggml_cann_pool {
593    /**
594     * @brief The maximum size of the virtual memory pool (32 GB).
595     */
596    size_t max_size;
597
598    /**
599     * @brief The device ID associated with this buffer pool.
600     */
601    int device;
602
603    /**
604     * @brief Pointer to the start of the virtual memory pool.
605     */
606    void * pool_addr = 0;
607
608    /**
609     * @brief Amount of virtual memory used in the pool.
610     */
611    size_t pool_used = 0;
612
613    /**
614     * @brief Total size of the virtual memory pool.
615     */
616    size_t pool_size = 0;
617
618    /**
619     * @brief Allocation granularity for the virtual memory pool.
620     */
621    size_t granularity;
622
623    /**
624     * @brief Handles for the physical memory allocated.
625     */
626    std::vector<aclrtDrvMemHandle> handles;
627
628    /**
629     * @brief Offsets for the mapped memory regions.
630     */
631    std::vector<void *> map_offsets;
632
633    /**
634     * @brief Constructor to initialize the buffer pool with virtual memory for
635     * a specific device.
636     *
637     * @param device The device ID to associate with this buffer pool.
638     */
639    explicit ggml_cann_pool_vmm(int device) : device(device) {
640        auto dev    = ggml_cann_info().devices[device];
641        granularity = dev.vmm_granularity;
642        max_size    = dev.total_vram;
643    }
644
645    /**
646     * @brief Destructor to free all buffers in the virtual memory pool.
647     */
648    ~ggml_cann_pool_vmm() {
649        if (pool_addr != 0) {
650            for (auto & offset : map_offsets) {
651                ACL_CHECK(aclrtUnmapMem(offset));
652            }
653            for (auto & handle : handles) {
654                ACL_CHECK(aclrtFreePhysical(handle));
655            }
656            ACL_CHECK(aclrtReleaseMemAddress(pool_addr));
657        }
658    }
659
660    /**
661     * @brief Allocate a buffer of the given size in the virtual memory pool.
662     *
663     * @param size The size of the buffer to allocate.
664     * @param actual_size A pointer to a variable to receive the actual size of
665     * the allocated buffer.
666     * @return A pointer to the allocated buffer.
667     */
668    void * alloc(size_t size, size_t * actual_size) override {
669        // round up the allocation size to the alignment to ensure that all
670        // allocations are aligned for all data types
671        const size_t alignment = 128;
672        size                   = GGML_PAD(size, alignment);
673        if (size == 0) {
674            size = alignment;
675        }
676
677        size_t avail = pool_size - pool_used;
678
679        if (size > avail) {
680            // round up to the next multiple of the granularity
681            size_t reserve_size = size - avail;
682            reserve_size        = GGML_PAD(reserve_size, granularity);
683
684            GGML_ASSERT(pool_size + reserve_size <= max_size);
685
686            // allocate more physical memory
687            aclrtPhysicalMemProp prop = {};
688            prop.handleType           = ACL_MEM_HANDLE_TYPE_NONE;
689            prop.allocationType       = ACL_MEM_ALLOCATION_TYPE_PINNED;
690            prop.memAttr              = ACL_HBM_MEM_HUGE;
691            prop.location.type        = ACL_MEM_LOCATION_TYPE_DEVICE;
692            prop.location.id          = device;
693            prop.reserve              = 0;
694            aclrtDrvMemHandle handle;
695            ACL_CHECK(aclrtMallocPhysical(&handle, reserve_size, &prop, 0));
696
697            // reserve virtual address space (if not already reserved)
698            if (pool_addr == 0) {
699                ACL_CHECK(aclrtReserveMemAddress(&pool_addr, max_size, 0, NULL, 1));
700            }
701
702            // map at the end of the pool
703            ACL_CHECK(aclrtMapMem((char *) pool_addr + pool_size, reserve_size, 0, handle, 0));
704
705            handles.push_back(handle);
706            map_offsets.push_back((char *) pool_addr + pool_size);
707
708            // add to the pool
709            pool_size += reserve_size;
710
711#ifdef DEBUG_CANN_MALLOC
712            GGML_LOG_INFO("cann pool[%d]: size increased to %llu MB (reserved %llu MB)\n", device,
713                          (unsigned long long) (pool_size / 1024 / 1024),
714                          (unsigned long long) (reserve_size / 1024 / 1024));
715#endif
716        }
717
718        GGML_ASSERT(pool_addr != 0);
719
720        void * ptr   = (void *) ((char *) pool_addr + pool_used);
721        *actual_size = size;
722        pool_used += size;
723
724#ifdef DEBUG_CANN_MALLOC
725        GGML_LOG_INFO("cann pool[%d]: allocated %llu bytes at %llx\n", device, (unsigned long long) size,
726                      (unsigned long long) ptr);
727#endif
728        return ptr;
729    }
730
731    /**
732     * @brief Free a buffer and return it to the virtual memory pool.
733     *
734     * @param ptr Pointer to the buffer to free.
735     * @param size Size of the buffer to free.
736     */
737    void free(void * ptr, size_t size) override {
738#ifdef DEBUG_CANN_MALLOC
739        GGML_LOG_INFO("cann pool[%d]: freed %llu bytes at %llx\n", device, (unsigned long long) size,
740                      (unsigned long long) ptr);
741#endif
742
743        pool_used -= size;
744
745        // all deallocations must be in reverse order of the allocations
746        GGML_ASSERT(ptr == (void *) ((char *) pool_addr + pool_used));
747    }
748};
749
750/**
751 * @brief Create a new CANN pool for a specific device.
752 *
753 * Factory method to create a new CANN pool object based on the device type.
754 *
755 * @param device The device ID for which to create the pool.
756 * @return A unique pointer to the created CANN pool.
757 */
758std::unique_ptr<ggml_cann_pool> ggml_backend_cann_context::new_pool_for_device(int device) {
759    std::string mem_pool_type = get_env_as_lowercase("GGML_CANN_MEM_POOL").value_or("");
760
761    if (mem_pool_type == "prio") {
762        GGML_LOG_INFO("%s: device %d use buffer pool with priority queue\n", __func__, device);
763        return std::unique_ptr<ggml_cann_pool>(new ggml_cann_pool_buf_prio(device));
764    }
765
766    if (ggml_cann_info().devices[device].vmm && mem_pool_type != "leg") {
767        GGML_LOG_INFO("%s: device %d use vmm pool\n", __func__, device);
768        return std::unique_ptr<ggml_cann_pool>(new ggml_cann_pool_vmm(device));
769    }
770
771    GGML_LOG_INFO("%s: device %d use buffer pool\n", __func__, device);
772    return std::unique_ptr<ggml_cann_pool>(new ggml_cann_pool_buf(device));
773}
774
775// cann buffer
776
777/**
778 * @brief Tracks multi-threaded write progress for a single tensor.
779 *
780 * When multiple threads call set_tensor on different chunks of the same tensor,
781 * this tracker accumulates progress and defers post-processing (quantized format
782 * transform or ND-to-NZ conversion) until all data has been written.
783 */
784struct TensorSetTracker {
785    std::mutex mtx;                   ///< Protects concurrent access to this tracker
786    size_t bytes_written = 0;         ///< Accumulated bytes written so far
787    size_t total_bytes = 0;           ///< Target size (full tensor)
788    std::vector<uint8_t> host_buffer; ///< Host staging buffer for quantized tensors
789};
790
791/**
792 * @brief Context for managing a CANN buffer associated with a specific device.
793 *
794 * This structure holds information about a CANN buffer, including the device
795 * ID, device pointer, and a name derived from GGML_CANN_NAME and the device ID.
796 */
797struct ggml_backend_cann_buffer_context {
798    int32_t device;             ///< The device ID associated with this buffer context.
799    void *  dev_ptr = nullptr;  ///< Pointer to the device memory allocated for the buffer.
800
801    std::mutex tracker_mutex;   ///< Protects the trackers map
802    std::unordered_map<void *, std::unique_ptr<TensorSetTracker>> trackers;
803
804    /**
805     * @brief Constructor to initialize the CANN buffer context.
806     *
807     * @param device The device ID associated with this buffer context.
808     * @param dev_ptr Pointer to the device memory allocated for the buffer.
809     */
810    ggml_backend_cann_buffer_context(int32_t device, void * dev_ptr) : device(device), dev_ptr(dev_ptr) {}
811
812    /**
813     * @brief Destructor to free the device memory allocated for the buffer.
814     */
815    ~ggml_backend_cann_buffer_context() { ACL_CHECK(aclrtFree(dev_ptr)); }
816
817    /**
818     * @brief Get or create a tracker for the given tensor.
819     */
820    TensorSetTracker * get_or_create_tracker(ggml_tensor * tensor) {
821        std::lock_guard<std::mutex> lock(tracker_mutex);
822        auto key = tensor->data;
823        auto it = trackers.find(key);
824        if (it == trackers.end()) {
825            auto tracker = std::make_unique<TensorSetTracker>();
826            tracker->total_bytes = ggml_nbytes(tensor);
827            auto * ptr = tracker.get();
828            trackers[key] = std::move(tracker);
829            return ptr;
830        }
831        return it->second.get();
832    }
833
834    /**
835     * @brief Remove the tracker for the given tensor.
836     */
837    void remove_tracker(ggml_tensor * tensor) {
838        std::lock_guard<std::mutex> lock(tracker_mutex);
839        trackers.erase(tensor->data);
840    }
841};
842
843// cann buffer type
844/**
845 * @brief Structure representing context information for a specific backend
846 * buffer type.
847 */
848struct ggml_backend_cann_buffer_type_context {
849    int32_t     device; /**< Device identifier associated with the buffer context. */
850    std::string name;   /**< Name associated with the buffer context. */
851};
852
853/**
854 * @brief Retrieves the name associated with a CANN buffer type.
855 *
856 * This function returns the descriptive name associated with the specified
857 * CANN buffer type context.
858 *
859 * @param buft Pointer to the buffer type context.
860 * @return Const pointer to the C-style string containing the name.
861 */
862static const char * ggml_backend_cann_buffer_type_name(ggml_backend_buffer_type_t buft) {
863    ggml_backend_cann_buffer_type_context * buft_ctx = (ggml_backend_cann_buffer_type_context *) buft->context;
864
865    return buft_ctx->name.c_str();
866}
867
868/**
869 * @brief Checks if the backend buffer type is associated with the CANN backend.
870 *
871 * This function checks whether the provided backend buffer type is associated
872 * with the CANN backend based on the comparison of its name retrieval function
873 * pointer.
874 *
875 * @param buft Pointer to the backend buffer type to check.
876 * @return bool Returns true if the buffer type is associated with the CANN
877 * backend, otherwise false.
878 */
879static bool ggml_backend_buft_is_cann(ggml_backend_buffer_type_t buft) {
880    return buft->iface.get_name == ggml_backend_cann_buffer_type_name;
881}
882
883/**
884 * @brief Free resources associated with a CANN buffer.
885 *
886 * This function frees the resources associated with a CANN buffer, including
887 * its context.
888 *
889 * @param buffer The CANN buffer to free.
890 */
891static void ggml_backend_cann_buffer_free_buffer(ggml_backend_buffer_t buffer) {
892    ggml_backend_cann_buffer_context * ctx = (ggml_backend_cann_buffer_context *) buffer->context;
893    delete ctx;
894}
895
896/**
897 * @brief Retrieve the base pointer of a CANN buffer.
898 *
899 * This function returns the base pointer of a CANN buffer, which points to the
900 * device memory allocated for the buffer.
901 *
902 * @param buffer The CANN buffer whose base pointer is to be retrieved.
903 * @return A pointer to the base of the device memory allocated for the buffer.
904 */
905static void * ggml_backend_cann_buffer_get_base(ggml_backend_buffer_t buffer) {
906    ggml_backend_cann_buffer_context * ctx = (ggml_backend_cann_buffer_context *) buffer->context;
907    return ctx->dev_ptr;
908}
909
910/**
911 * @brief Transform quantized Q4.0 tensor data into a format suitable for CANN
912 * processing.
913 *
914 * This function transforms quantized Q4.0 tensor data into a format suitable
915 * for CANN processing. It extracts quantization values and scales from the
916 * source data and prepares them in a format expected by CANN operations.
917 *
918 * @param tensor Pointer to the tensor information.
919 * @param src Pointer to the source data in Q4.0 format.
920 * @param dst Pointer to the destination buffer where transformed data will be
921 * stored.
922 */
923static void ggml_backend_cann_transform_q4_0(ggml_tensor * tensor, const void * src, void * dst) {
924    int64_t n_elems     = ggml_nelements(tensor);
925    int64_t groups      = n_elems / QK4_0;
926    size_t  quant_bytes = n_elems * sizeof(uint8_t) / 2;
927
928    uint8_t *  quant_offset = (uint8_t *) dst;
929    uint16_t * scale_offset = (uint16_t *) ((char *) dst + quant_bytes);
930
931    for (int i = 0; i < groups; i++) {
932        const block_q4_0 * group = (const block_q4_0 *) ((const char *) src + i * sizeof(block_q4_0));
933        *scale_offset            = group->d;
934        scale_offset++;
935
936        // 0-15
937        for (int j = 0; j < QK4_0 / 2; j += 2) {
938            (*quant_offset) = (group->qs[j] & 0x0F);
939            (*quant_offset) |= ((group->qs[j + 1] << 4));
940            quant_offset++;
941        }
942
943        // 16-31
944        for (int j = 0; j < QK4_0 / 2; j += 2) {
945            (*quant_offset) = (group->qs[j] >> 4);
946            (*quant_offset) |= (group->qs[j + 1] & 0xF0);
947            quant_offset++;
948        }
949    }
950
951    // put (uint4b_t -8) into int4b_t
952    for (quant_offset = (uint8_t *) dst; quant_offset < (uint8_t *) dst + quant_bytes; quant_offset++) {
953        (*quant_offset) ^= 0x88;
954    }
955}
956
957/**
958 * @brief Transform CANN processed data back into quantized Q4.0 format.
959 *
960 * This function transforms CANN processed data back into quantized Q4.0 format.
961 * It reverses the transformation performed by
962 * ggml_backend_cann_transform_q4_0(), converting the data back into its
963 * original quantized form.
964 *
965 * @param tensor Pointer to the tensor information.
966 * @param src Pointer to the source buffer containing transformed data.
967 * @param dst Pointer to the destination buffer where the Q4.0 formatted data
968 * will be stored.
969 */
970static void ggml_backend_cann_transform_back_q4_0(const ggml_tensor * tensor, void * src, void * dst) {
971    int64_t n_elems     = ggml_nelements(tensor);
972    int64_t groups      = n_elems / QK4_0;
973    size_t  quant_bytes = n_elems * sizeof(uint8_t) / 2;
974
975    uint8_t *  quant_offset = (uint8_t *) src;
976    uint16_t * scale_offset = (uint16_t *) ((char *) src + quant_bytes);
977
978    for (; quant_offset < (uint8_t *) src + quant_bytes; quant_offset++) {
979        (*quant_offset) ^= 0x88;
980    }
981    quant_offset = (uint8_t *) src;
982
983    for (int i = 0; i < groups; i++) {
984        block_q4_0 * group = (block_q4_0 *) ((char *) dst + i * sizeof(block_q4_0));
985        group->d           = *scale_offset;
986        scale_offset++;
987
988        // 0-15
989        for (int j = 0; j < QK4_0 / 2; j += 2) {
990            group->qs[j]     = ((*quant_offset) & 0x0F);
991            group->qs[j + 1] = ((*quant_offset) >> 4);
992            quant_offset++;
993        }
994
995        // 16-31
996        for (int j = 0; j < QK4_0 / 2; j += 2) {
997            group->qs[j] |= ((*quant_offset) << 4);
998            group->qs[j + 1] |= ((*quant_offset) & 0xF0);
999            quant_offset++;
1000        }
1001    }
1002}
1003
1004/**
1005 * @brief Transform quantized Q8.0 tensor data into a format suitable for CANN
1006 * processing.
1007 *
1008 * This function transforms quantized Q8.0 tensor data into a format suitable
1009 * for CANN processing. It extracts quantization values and scales from the
1010 * source data and prepares them in a format expected by CANN operations.
1011 *
1012 * @param tensor Pointer to the tensor information.
1013 * @param src Pointer to the source data in Q8.0 format.
1014 * @param dst Pointer to the destination buffer where transformed data will be
1015 * stored.
1016 */
1017static void ggml_backend_cann_transform_q8_0(ggml_tensor * tensor, const void * src, void * dst) {
1018    int64_t n_elems     = ggml_nelements(tensor);
1019    int64_t groups      = n_elems / QK8_0;
1020    size_t  quant_bytes = n_elems * sizeof(uint8_t);
1021
1022    uint8_t *  quant_offset = (uint8_t *) dst;
1023    uint16_t * scale_offset = (uint16_t *) ((char *) dst + quant_bytes);
1024
1025    for (int i = 0; i < groups; i++) {
1026        const block_q8_0 * group = (const block_q8_0 *) ((const char *) src + i * sizeof(block_q8_0));
1027        *scale_offset            = group->d;
1028        scale_offset++;
1029        size_t group_quant_size = QK8_0 * sizeof(uint8_t);
1030        memcpy(quant_offset, group->qs, group_quant_size);
1031        quant_offset += group_quant_size;
1032    }
1033}
1034
1035/**
1036 * @brief Transform CANN processed data back into quantized Q8.0 format.
1037 *
1038 * This function transforms CANN processed data back into quantized Q8.0 format.
1039 * It reverses the transformation performed by
1040 * ggml_backend_cann_transform_q8_0(), converting the data back into its
1041 * original quantized form.
1042 *
1043 * @param tensor Pointer to the tensor information.
1044 * @param src Pointer to the source buffer containing transformed data.
1045 * @param dst Pointer to the destination buffer where the Q8.0 formatted data
1046 * will be stored.
1047 */
1048static void ggml_backend_cann_transform_back_q8_0(const ggml_tensor * tensor, const void * src, void * dst) {
1049    int64_t n_elems     = ggml_nelements(tensor);
1050    int64_t groups      = n_elems / QK8_0;
1051    size_t  quant_bytes = n_elems * sizeof(uint8_t);
1052
1053    const uint8_t *  quant_offset = (const uint8_t *) src;
1054    const uint16_t * scale_offset = (const uint16_t *) ((const char *) src + quant_bytes);
1055
1056    for (int i = 0; i < groups; i++) {
1057        block_q8_0 * group = (block_q8_0 *) ((char *) dst + i * sizeof(block_q8_0));
1058        group->d           = *scale_offset;
1059        scale_offset++;
1060        size_t group_quant_size = QK8_0 * sizeof(uint8_t);
1061        memcpy(group->qs, quant_offset, group_quant_size);
1062        quant_offset += group_quant_size;
1063    }
1064}
1065
1066/**
1067 * @brief Transform tensor data based on its type for CANN processing.
1068 *
1069 * This function transforms tensor data based on its quantization type for CANN
1070 * processing. It dispatches the transformation based on the tensor's type to
1071 * specialized functions handling Q4.0 and Q8.0 formats.
1072 *
1073 * @param tensor Pointer to the tensor information.
1074 * @param src Pointer to the source data to be transformed.
1075 * @param dst Pointer to the destination buffer where transformed data will be
1076 * stored.
1077 */
1078static void ggml_backend_cann_transform(ggml_tensor * tensor, const void * src, void * dst) {
1079    switch (tensor->type) {
1080        case GGML_TYPE_Q4_0:
1081            ggml_backend_cann_transform_q4_0(tensor, src, dst);
1082            break;
1083        case GGML_TYPE_Q8_0:
1084            ggml_backend_cann_transform_q8_0(tensor, src, dst);
1085            break;
1086        default:
1087            break;
1088    }
1089}
1090
1091/**
1092 * @brief Transform CANN processed data back into tensor data based on its type.
1093 *
1094 * This function transforms CANN processed data back into tensor data based on
1095 * its quantization type for Q4.0 and Q8.0 formats. It dispatches the
1096 * transformation based on the tensor's type to specialized functions.
1097 *
1098 * @param tensor Pointer to the tensor information.
1099 * @param src Pointer to the source data containing CANN processed data.
1100 * @param dst Pointer to the destination buffer where transformed tensor data
1101 * will be stored.
1102 */
1103static void ggml_backend_cann_transform_back(const ggml_tensor * tensor, void * src, void * dst) {
1104    switch (tensor->type) {
1105        case GGML_TYPE_Q4_0:
1106            ggml_backend_cann_transform_back_q4_0(tensor, src, dst);
1107            break;
1108        case GGML_TYPE_Q8_0:
1109            ggml_backend_cann_transform_back_q8_0(tensor, src, dst);
1110            break;
1111        default:
1112            break;
1113    }
1114}
1115
1116/**
1117 * @brief Check if transformation is needed for a given tensor type.
1118 *
1119 * This function checks if transformation is needed for a given tensor type
1120 * to prepare data for CANN processing.
1121 *
1122 * @param type The tensor type to check.
1123 * @return true if transformation is needed, false otherwise.
1124 */
1125static bool need_transform(ggml_type type) {
1126    switch (type) {
1127        case GGML_TYPE_Q4_0:
1128        case GGML_TYPE_Q8_0:
1129            return true;
1130        default:
1131            return false;
1132    }
1133}
1134
1135/**
1136 * @brief Initialize a tensor using data from a CANN buffer.
1137 *
1138 * This function initializes a tensor using data from a CANN buffer.
1139 * It handles special cases such as views and quantization.
1140 *
1141 * @param buffer The CANN buffer from which to initialize the tensor.
1142 * @param tensor Pointer to the tensor to be initialized.
1143 */
1144static enum ggml_status ggml_backend_cann_buffer_init_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor) {
1145    if (tensor->view_src != NULL && tensor->view_offs == 0) {
1146        GGML_ASSERT(tensor->view_src->buffer->buft == buffer->buft);
1147        return GGML_STATUS_SUCCESS;
1148    }
1149
1150    // TODO: cann backend doesn't support quantized yet. Just leave the code
1151    // here.
1152    if (ggml_is_quantized(tensor->type)) {
1153        // Initialize padding to 0 to avoid possible NaN values
1154        size_t original_size = ggml_nbytes(tensor);
1155        size_t padded_size   = ggml_backend_buft_get_alloc_size(buffer->buft, tensor);
1156
1157        if (padded_size > original_size && tensor->view_src == nullptr) {
1158            size_t memset_size = padded_size - original_size;
1159            ACL_CHECK(aclrtMemset((char *) tensor->data + original_size, memset_size, 0, memset_size));
1160        }
1161    }
1162    return GGML_STATUS_SUCCESS;
1163}
1164
1165/**
1166 * @brief Workspace for caching NZ buffers per device.
1167 *
1168 * This struct manages a device buffer used in NZ computations. It supports
1169 * allocation, reallocation, and clearing of cached memory. The struct is
1170 * designed to be used with a global array, one per device.
1171 */
1172struct ggml_cann_nz_workspace {
1173    std::mutex mtx;    // Protects ptr/allocated from concurrent access
1174    void * ptr;        // Pointer to allocated device buffer
1175    size_t allocated;  // Size of currently allocated buffer in bytes
1176
1177    /**
1178     * @brief Constructor. Initializes the workspace with no allocated memory.
1179     */
1180    ggml_cann_nz_workspace() : ptr(nullptr), allocated(0) {}
1181
1182    /**
1183     * @brief Free cached memory and reset the workspace.
1184     *
1185     * If a buffer has been allocated, this function releases it using
1186     * aclrtFree and resets internal state.
1187     */
1188    void clear() {
1189        if (ptr) {
1190            ACL_CHECK(aclrtFree(ptr));
1191            ptr       = nullptr;
1192            allocated = 0;
1193        }
1194    }
1195
1196    /**
1197     * @brief Allocate or reallocate the workspace buffer.
1198     *
1199     * If the requested size is larger than the currently allocated size,
1200     * the old buffer will be freed and a new buffer of the requested size
1201     * will be allocated on the device.
1202     *
1203     * @param new_size Size in bytes to allocate for the workspace.
1204     */
1205    void realloc(size_t new_size) {
1206        if (new_size > allocated) {
1207            clear();
1208            ACL_CHECK(aclrtMalloc(&ptr, new_size, ACL_MEM_MALLOC_HUGE_FIRST));
1209            allocated = new_size;
1210        }
1211    }
1212
1213    /**
1214     * @brief Get the device buffer pointer.
1215     *
1216     * @return Pointer to the allocated buffer, or nullptr if not allocated.
1217     */
1218    void * get() const { return ptr; }
1219};
1220
1221/**
1222 * @brief Global array of NZ workspaces, one per device.
1223 */
1224static ggml_cann_nz_workspace g_nz_workspaces[GGML_CANN_MAX_DEVICES];
1225
1226/**
1227 * @brief Convert tensor weights to NZ format using Ascend CANN API.
1228 *
1229 * This function creates a transposed tensor descriptor and performs the
1230 * TransMatmulWeight operation. Converting tensor formats can significantly
1231 * improve performance on certain hardware.
1232 *
1233 * @param tensor Pointer to the input ggml_tensor containing the weights.
1234 * @param offset Byte offset within the tensor data buffer where weights start.
1235 * @param device device id.
1236 *
1237 * @note The workspace buffer used in this function is managed globally and reused
1238 *       across calls. This reduces overhead from repeated memory allocation and deallocation.
1239 */
1240static void weight_format_to_nz(ggml_tensor * tensor, int device) {
1241    acl_tensor_ptr weightTransposed = ggml_cann_create_tensor(tensor, tensor->ne, tensor->nb, 2, ACL_FORMAT_ND, 0);
1242    uint64_t       workspaceSize    = 0;
1243    aclOpExecutor * executor;
1244
1245    // TransMatmulWeight
1246    ACL_CHECK(aclnnTransMatmulWeightGetWorkspaceSize(weightTransposed.get(), &workspaceSize, &executor));
1247
1248    std::lock_guard<std::mutex> lock(g_nz_workspaces[device].mtx);
1249    // Avoid frequent malloc/free of the workspace.
1250    g_nz_workspaces[device].realloc(workspaceSize);
1251
1252    void * g_nz_workspace = g_nz_workspaces[device].get();
1253
1254    ACL_CHECK(aclnnTransMatmulWeight(g_nz_workspace, workspaceSize, executor, nullptr));
1255}
1256
1257// TODO: need handle tensor which has paddings.
1258/**
1259 * @brief Set tensor data in a CANN buffer.
1260 *
1261 * This function sets tensor data in a CANN buffer, handling transformations
1262 * if needed based on the tensor's type. It supports multi-threaded calls
1263 * where different threads write different chunks of the same tensor.
1264 *
1265 * For quantized tensors (Q4_0/Q8_0), data is staged in a host buffer and
1266 * the format transform is deferred until all chunks are written.
1267 * For NZ weight tensors, chunks are uploaded directly but the ND-to-NZ
1268 * conversion is deferred until all chunks are written.
1269 *
1270 * @param buffer The CANN buffer where the tensor data will be set.
1271 * @param tensor Pointer to the tensor whose data will be set.
1272 * @param data Pointer to the source data to be copied into the tensor.
1273 * @param offset Offset in the source data from where to start copying.
1274 * @param size Size of the data to be copied, in bytes.
1275 */
1276static void ggml_backend_cann_buffer_set_tensor(ggml_backend_buffer_t buffer,
1277                                                ggml_tensor *         tensor,
1278                                                const void *          data,
1279                                                size_t                offset,
1280                                                size_t                size) {
1281    ggml_backend_cann_buffer_context * ctx = (ggml_backend_cann_buffer_context *) buffer->context;
1282
1283    ggml_cann_set_device(ctx->device);
1284
1285    // Only check env once.
1286    static bool weight_to_nz = parse_bool(get_env_as_lowercase("GGML_CANN_WEIGHT_NZ").value_or("on"));
1287
1288    bool is_quantized = need_transform(tensor->type);
1289    bool is_nz        = !is_quantized && tensor->type != GGML_TYPE_BF16 && weight_to_nz &&
1290                 is_matmul_weight((const ggml_tensor *) tensor);
1291
1292    // Plain tensor (not quantized, not NZ): direct copy, no tracking needed
1293    if (!is_quantized && !is_nz) {
1294        ACL_CHECK(aclrtMemcpy((char *) tensor->data + offset, size, data, size, ACL_MEMCPY_HOST_TO_DEVICE));
1295        return;
1296    }
1297
1298    // Single-shot write (full tensor at once): handle directly without tracking overhead
1299    if (offset == 0 && size == ggml_nbytes(tensor)) {
1300        if (is_quantized) {
1301            void * transform_buffer = malloc(size);
1302            ggml_backend_cann_transform(tensor, data, transform_buffer);
1303            ACL_CHECK(aclrtMemcpy(tensor->data, size, transform_buffer, size, ACL_MEMCPY_HOST_TO_DEVICE));
1304            free(transform_buffer);
1305        } else {
1306            // NZ weight
1307            GGML_ASSERT(tensor->ne[2] == 1);
1308            GGML_ASSERT(tensor->ne[3] == 1);
1309            ACL_CHECK(aclrtMemcpy(tensor->data, size, data, size, ACL_MEMCPY_HOST_TO_DEVICE));
1310            weight_format_to_nz(tensor, ctx->device);
1311        }
1312        return;
1313    }
1314
1315    // Chunked write: use tracker to accumulate progress and defer transform/conversion
1316    TensorSetTracker * tracker = ctx->get_or_create_tracker(tensor);
1317    std::unique_lock<std::mutex> lock(tracker->mtx);
1318
1319    if (is_quantized) {
1320        // Stage data in host buffer; transform requires full tensor data
1321        if (tracker->host_buffer.empty()) {
1322            tracker->host_buffer.resize(tracker->total_bytes);
1323        }
1324        memcpy(tracker->host_buffer.data() + offset, data, size);
1325    } else {
1326        // NZ weight: upload chunk to device immediately, defer conversion
1327        ACL_CHECK(aclrtMemcpy((char *) tensor->data + offset, size, data, size, ACL_MEMCPY_HOST_TO_DEVICE));
1328    }
1329
1330    tracker->bytes_written += size;
1331
1332    // All chunks received: perform deferred transform/conversion
1333    if (tracker->bytes_written >= tracker->total_bytes) {
1334        if (is_quantized) {
1335            void * transform_buffer = malloc(tracker->total_bytes);
1336            ggml_backend_cann_transform(tensor, tracker->host_buffer.data(), transform_buffer);
1337            ACL_CHECK(aclrtMemcpy(tensor->data, tracker->total_bytes, transform_buffer, tracker->total_bytes, ACL_MEMCPY_HOST_TO_DEVICE));
1338            free(transform_buffer);
1339        }
1340
1341        if (is_nz) {
1342            GGML_ASSERT(tensor->ne[2] == 1);
1343            GGML_ASSERT(tensor->ne[3] == 1);
1344            weight_format_to_nz(tensor, ctx->device);
1345        }
1346
1347        // Unlock before removing tracker, as remove_tracker destroys the mutex
1348        lock.unlock();
1349        ctx->remove_tracker(tensor);
1350    }
1351}
1352
1353/**
1354 * @brief Get tensor data from a CANN buffer.
1355 *
1356 * This function retrieves tensor data from a CANN buffer, handling
1357 * transformations if needed based on the tensor's type.
1358 *
1359 * @param buffer The CANN buffer from which to retrieve tensor data.
1360 * @param tensor Pointer to the tensor whose data will be retrieved.
1361 * @param data Pointer to the destination buffer where the tensor data will be
1362 * copied.
1363 * @param offset Offset in the destination buffer where to start copying.
1364 * @param size Size of the data to be copied, in bytes.
1365 */
1366static void ggml_backend_cann_buffer_get_tensor(ggml_backend_buffer_t buffer,
1367                                                const ggml_tensor *   tensor,
1368                                                void *                data,
1369                                                size_t                offset,
1370                                                size_t                size) {
1371    ggml_backend_cann_buffer_context * ctx = (ggml_backend_cann_buffer_context *) buffer->context;
1372
1373    ggml_cann_set_device(ctx->device);
1374
1375    if (!need_transform(tensor->type)) {
1376        ACL_CHECK(aclrtMemcpy(data, size, (char *) tensor->data + offset, size, ACL_MEMCPY_DEVICE_TO_HOST));
1377    } else {
1378        void * transform_buffer = malloc(size);
1379        ACL_CHECK(aclrtMemcpy(transform_buffer, size, (char *) tensor->data + offset, size, ACL_MEMCPY_DEVICE_TO_HOST));
1380        ggml_backend_cann_transform_back(tensor, transform_buffer, data);
1381        free(transform_buffer);
1382    }
1383}
1384
1385/**
1386 * @brief Copy tensor data between CANN buffers if possible.
1387 *
1388 * This function copies tensor data between CANN buffers if the source and
1389 * destination buffers are CANN buffers and they meet the necessary conditions
1390 * (same device or devices can access each other).
1391 *
1392 * @param buffer The destination CANN buffer where the tensor data will be
1393 * copied.
1394 * @param src Pointer to the source tensor whose data will be copied.
1395 * @param dst Pointer to the destination tensor where the data will be copied.
1396 * @return true if the copy operation succeeded, false otherwise.
1397 */
1398static bool ggml_backend_cann_buffer_cpy_tensor(ggml_backend_buffer_t buffer,
1399                                                const ggml_tensor *   src,
1400                                                ggml_tensor *         dst) {
1401    if (ggml_backend_buft_is_cann(src->buffer->buft)) {
1402        ggml_backend_cann_buffer_context * src_ctx = (ggml_backend_cann_buffer_context *) src->buffer->context;
1403        ggml_backend_cann_buffer_context * dst_ctx = (ggml_backend_cann_buffer_context *) buffer->context;
1404
1405        size_t memcpy_size = ggml_nbytes(src);
1406        // Same device.
1407        if (src_ctx->device == dst_ctx->device) {
1408            ACL_CHECK(aclrtMemcpy((char *) dst->data, memcpy_size, (const char *) src->data, memcpy_size,
1409                                  ACL_MEMCPY_DEVICE_TO_DEVICE));
1410            return true;
1411        } else {
1412#ifdef ASCEND_310P
1413            // TODO: Support 310p P2P copy
1414            return false;
1415#endif
1416            // Different device but can access by peer.
1417            int32_t canAccessPeer = 0;
1418            ACL_CHECK(aclrtDeviceCanAccessPeer(&canAccessPeer, src_ctx->device, dst_ctx->device));
1419            if (canAccessPeer) {
1420                ggml_cann_set_device(src_ctx->device);
1421                ACL_CHECK(aclrtDeviceEnablePeerAccess(dst_ctx->device, 0));
1422                ACL_CHECK(aclrtMemcpy((char *) dst->data, memcpy_size, (const char *) src->data, memcpy_size,
1423                                      ACL_MEMCPY_DEVICE_TO_DEVICE));
1424                return true;
1425            }
1426        }
1427    }
1428    return false;
1429}
1430
1431/**
1432 * @brief Set a region of a tensor's device memory to a specified value.
1433 *
1434 * @param buffer The CANN buffer containing the tensor.
1435 * @param tensor Pointer to the tensor whose memory will be set.
1436 * @param value The value to which each byte in the region will be set.
1437 * @param offset Byte offset within the tensor's data to start setting.
1438 * @param size Number of bytes to set.
1439 */
1440static void ggml_backend_cann_buffer_memset_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, uint8_t value, size_t offset, size_t size) {
1441    ggml_backend_cann_buffer_context * ctx = (ggml_backend_cann_buffer_context *) buffer->context;
1442
1443    ggml_cann_set_device(ctx->device);
1444    ACL_CHECK(aclrtMemset((char *) tensor->data + offset, size, value, size));
1445}
1446
1447/**
1448 * @brief Clear a CANN buffer by setting all its memory to a specified value.
1449 *
1450 * This function clears a CANN buffer by setting all its memory to a specified
1451 * value.
1452 *
1453 * @param buffer The CANN buffer to be cleared.
1454 * @param value The value to which each byte in the buffer will be set.
1455 */
1456static void ggml_backend_cann_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) {
1457    ggml_backend_cann_buffer_context * ctx = (ggml_backend_cann_buffer_context *) buffer->context;
1458
1459    ggml_cann_set_device(ctx->device);
1460    ACL_CHECK(aclrtMemset(ctx->dev_ptr, buffer->size, value, buffer->size));
1461}
1462
1463/**
1464 * @brief Interface for a CANN buffer in the backend.
1465 *
1466 * This structure defines function pointers to operations that can be performed
1467 * on a CANN buffer within the backend.
1468 */
1469static const ggml_backend_buffer_i ggml_backend_cann_buffer_interface = {
1470    /* .free_buffer     = */ ggml_backend_cann_buffer_free_buffer,
1471    /* .get_base        = */ ggml_backend_cann_buffer_get_base,
1472    /* .init_tensor     = */ ggml_backend_cann_buffer_init_tensor,
1473    /* .memset_tensor   = */ ggml_backend_cann_buffer_memset_tensor,
1474    /* .set_tensor      = */ ggml_backend_cann_buffer_set_tensor,
1475    /* .get_tensor      = */ ggml_backend_cann_buffer_get_tensor,
1476    /* .set_tensor_2d   = */ NULL,
1477    /* .get_tensor_2d   = */ NULL,
1478    /* .cpy_tensor      = */ ggml_backend_cann_buffer_cpy_tensor,
1479    /* .clear           = */ ggml_backend_cann_buffer_clear,
1480    /* .reset           = */ NULL,
1481};
1482
1483/**
1484 * @brief Allocates a new CANN buffer of the specified type and size.
1485 *
1486 * This function allocates a new CANN buffer on the specified device with the
1487 * given size.
1488 *
1489 * @param buft Pointer to the buffer type context.
1490 * @param size Size in bytes of the buffer to allocate.
1491 * @return Pointer to the allocated buffer, or nullptr if allocation fails.
1492 */
1493static ggml_backend_buffer_t ggml_backend_cann_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) {
1494    ggml_backend_cann_buffer_type_context * buft_ctx = (ggml_backend_cann_buffer_type_context *) buft->context;
1495
1496    ggml_cann_set_device(buft_ctx->device);
1497
1498    const size_t alignment = 128;
1499    size                   = GGML_PAD(size, alignment);
1500    if (size == 0) {
1501        size = alignment;
1502    }
1503    void *   dev_ptr;
1504    aclError err = aclrtMalloc(&dev_ptr, size, ACL_MEM_MALLOC_HUGE_FIRST);
1505    if (err != ACL_SUCCESS) {
1506        GGML_LOG_ERROR("%s: allocating %.2f MiB on device %d: aclrtMalloc failed: %s\n", __func__,
1507                       size / 1024.0 / 1024.0, buft_ctx->device, aclGetRecentErrMsg());
1508        return nullptr;
1509    }
1510
1511    ggml_backend_cann_buffer_context * ctx = new ggml_backend_cann_buffer_context(buft_ctx->device, dev_ptr);
1512
1513    return ggml_backend_buffer_init(buft, ggml_backend_cann_buffer_interface, ctx, size);
1514}
1515
1516/**
1517 * @brief Retrieves the memory alignment requirement for CANN buffers of this
1518 * type.
1519 *
1520 * This function returns the alignment requirement in bytes for memory allocated
1521 * by the CANN buffer type.
1522 *
1523 * @param buft Pointer to the buffer type context (unused in this
1524 * implementation).
1525 * @return The alignment requirement in bytes (fixed at 128 bytes for CANN
1526 * buffers).
1527 */
1528static size_t ggml_backend_cann_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) {
1529    return 128;
1530
1531    GGML_UNUSED(buft);
1532}
1533
1534/**
1535 * @brief Calculates the allocation size required for a tensor in a CANN buffer.
1536 *
1537 * Computes the total allocation size needed for storing the tensor's data in a
1538 * CANN buffer, considering any necessary padding or adjustments for quantized
1539 * types.
1540 *
1541 * @param buft Pointer to the buffer type context (unused in this
1542 * implementation).
1543 * @param tensor Pointer to the tensor for which the allocation size is
1544 * calculated.
1545 * @return The total allocation size in bytes required for the tensor in the
1546 * CANN buffer.
1547 */
1548static size_t ggml_backend_cann_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft,
1549                                                           const ggml_tensor *        tensor) {
1550    size_t  size = ggml_nbytes(tensor);
1551    int64_t ne0  = tensor->ne[0];
1552
1553    // Only check env once.
1554    static bool weight_to_nz = parse_bool(get_env_as_lowercase("GGML_CANN_WEIGHT_NZ").value_or("on"));
1555
1556    // last line must bigger than 32, because every single op deal at
1557    // least 32 bytes.
1558    // TODO: quantized type?
1559    // int64_t line_size = ne0 * ggml_element_size(tensor);
1560    // int64_t line_size_align_32 = (line_size + 31) & ~31;
1561    // size += (line_size_align_32 - line_size);
1562    if (ggml_is_quantized(tensor->type)) {
1563        if (ne0 % MATRIX_ROW_PADDING != 0) {
1564            size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING);
1565        }
1566    } else if (weight_to_nz && tensor->type != GGML_TYPE_BF16
1567               && is_matmul_weight((const ggml_tensor *) tensor)) {
1568        // NZ format weight are not support quantized yet.
1569        // If ND tensor transform to NZ, size may changed.
1570        int64_t shape[] = { tensor->ne[1], tensor->ne[0] };
1571        GGML_ASSERT(tensor->ne[2] == 1);
1572        GGML_ASSERT(tensor->ne[3] == 1);
1573        const aclIntArray * acl_shape = aclCreateIntArray(shape, 2);
1574        size_t              new_size;
1575        ACL_CHECK(aclnnCalculateMatmulWeightSizeV2(acl_shape, ggml_cann_type_mapping(tensor->type), &new_size));
1576        ACL_CHECK(aclDestroyIntArray(acl_shape));
1577        size = std::max(size, new_size);
1578    }
1579
1580    return size;
1581
1582    GGML_UNUSED(buft);
1583}
1584
1585static bool ggml_backend_cann_buffer_type_is_host(ggml_backend_buffer_type_t buft) {
1586    return false;
1587
1588    GGML_UNUSED(buft);
1589}
1590
1591/**
1592 * @brief Interface for managing CANN buffer types in the GGML backend.
1593 *
1594 * Provides function pointers for allocating, querying properties, and managing
1595 * memory for CANN buffer types in the GGML backend.
1596 */
1597static const ggml_backend_buffer_type_i ggml_backend_cann_buffer_type_interface = {
1598    /* .get_name         = */ ggml_backend_cann_buffer_type_name,
1599    /* .alloc_buffer     = */ ggml_backend_cann_buffer_type_alloc_buffer,
1600    /* .get_alignment    = */ ggml_backend_cann_buffer_type_get_alignment,
1601    /* .get_max_size     = */ NULL,  // defaults to SIZE_MAX
1602    /* .get_alloc_size   = */ ggml_backend_cann_buffer_type_get_alloc_size,
1603    /* .is_host          = */ ggml_backend_cann_buffer_type_is_host,
1604};
1605
1606/**
1607 * @brief Retrieves the CANN buffer type for a specified device.
1608 *
1609 * This function initializes and returns the buffer type interface associated
1610 * with the given device. It ensures thread-safe access using a mutex.
1611 *
1612 * @param device The device index for which to retrieve the buffer type.
1613 * @return A pointer to the buffer type interface for the specified device, or
1614 * nullptr if the device index is out of range.
1615 */
1616ggml_backend_buffer_type_t ggml_backend_cann_buffer_type(int32_t device) {
1617    static std::mutex           mutex;
1618    std::lock_guard<std::mutex> lock(mutex);
1619
1620    if (device >= ggml_backend_cann_get_device_count()) {
1621        return nullptr;
1622    }
1623
1624    static ggml_backend_buffer_type ggml_backend_cann_buffer_types[GGML_CANN_MAX_DEVICES];
1625
1626    static bool ggml_backend_cann_buffer_type_initialized = false;
1627
1628    if (!ggml_backend_cann_buffer_type_initialized) {
1629        for (int32_t i = 0; i < ggml_cann_info().device_count; i++) {
1630            ggml_backend_cann_buffer_types[i] = {
1631                /* .iface    = */ ggml_backend_cann_buffer_type_interface,
1632                /* .device    = */ ggml_backend_reg_dev_get(ggml_backend_cann_reg(), i),
1633                /* .context  = */
1634                new ggml_backend_cann_buffer_type_context{ i, "CANN" + std::to_string(i) },
1635            };
1636        }
1637        ggml_backend_cann_buffer_type_initialized = true;
1638    }
1639
1640    return &ggml_backend_cann_buffer_types[device];
1641}
1642
1643/**
1644 * @brief Retrieves the name associated with a CANN host buffer type.
1645 *
1646 * This function returns the descriptive name associated with the specified
1647 * CANN host buffer type context.
1648 *
1649 * @param buft Pointer to the host buffer type context.
1650 * @return Const pointer to the C-style string containing the name.
1651 */
1652static const char * ggml_backend_cann_host_buffer_type_name(ggml_backend_buffer_type_t buft) {
1653    return "CANN_Host";
1654
1655    GGML_UNUSED(buft);
1656}
1657
1658/**
1659 * @brief Retrieves the name associated with a CANN host buffer.
1660 *
1661 * This function returns the descriptive name associated with the specified
1662 * CANN host buffer context.
1663 *
1664 * @param buft Pointer to the host buffer context.
1665 * @return Const pointer to the C-style string containing the name.
1666 */
1667static const char * ggml_backend_cann_host_buffer_name(ggml_backend_buffer_t buffer) {
1668    return "CANN_Host";
1669
1670    GGML_UNUSED(buffer);
1671}
1672
1673/**
1674 * @brief Free resources associated with a CANN host buffer.
1675 *
1676 * This function frees the resources associated with a CANN host buffer, including
1677 * its context.
1678 *
1679 * @param buffer The CANN host buffer to free.
1680 */
1681static void ggml_backend_cann_host_buffer_free(ggml_backend_buffer_t buffer) {
1682    ACL_CHECK(aclrtFreeHost(buffer->context));
1683}
1684
1685/**
1686 * @brief Allocates a new CANN host buffer of the specified size.
1687 *
1688 * This function allocates a new CANN host buffer with the given size.
1689 * @param size Size in bytes of the host buffer to allocate.
1690 * @return Pointer to the allocated host buffer, or nullptr if allocation fails.
1691 */
1692static void * ggml_cann_host_malloc(size_t size) {
1693    if (getenv("GGML_CANN_NO_PINNED") != nullptr) {
1694        return nullptr;
1695    }
1696
1697    const size_t alignment = 128;
1698    size                   = GGML_PAD(size, alignment);
1699    if (size == 0) {
1700        size = alignment;
1701    }
1702
1703    void *   hostPtr = nullptr;
1704    aclError err     = aclrtMallocHost((void **) &hostPtr, size);
1705    if (err != ACL_SUCCESS) {
1706        GGML_LOG_WARN("%s: failed to allocate %.2f MiB of pinned memory: %s\n", __func__, size / 1024.0 / 1024.0,
1707                      aclGetRecentErrMsg());
1708        return nullptr;
1709    }
1710    return hostPtr;
1711}
1712
1713/**
1714 * @brief Allocates a new CANN host buffer of the specified type and size.
1715 *
1716 * @param buft Pointer to the host buffer type context.
1717 * @param size Size in bytes of the host buffer to allocate.
1718 * @return Pointer to the allocated host buffer, or CPU buffer pointer if allocation fails.
1719 */
1720static ggml_backend_buffer_t ggml_backend_cann_host_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft,
1721                                                                             size_t                     size) {
1722    void * hostPtr = ggml_cann_host_malloc(size);
1723
1724    if (hostPtr == nullptr) {
1725        // fallback to cpu buffer
1726        return ggml_backend_buft_alloc_buffer(ggml_backend_cpu_buffer_type(), size);
1727    }
1728
1729    ggml_backend_buffer_t buffer = ggml_backend_cpu_buffer_from_ptr(hostPtr, size);
1730    buffer->buft                 = buft;
1731    buffer->iface.free_buffer    = ggml_backend_cann_host_buffer_free;
1732
1733    return buffer;
1734}
1735
1736/**
1737 * @brief Interface for managing CANN host buffer types in the GGML backend.
1738 *
1739 * Provides function pointers for allocating, querying properties, and managing
1740 * memory for CANN buffer types in the GGML backend.
1741 */
1742ggml_backend_buffer_type_t ggml_backend_cann_host_buffer_type() {
1743    static struct ggml_backend_buffer_type ggml_backend_cann_buffer_type_host = {
1744        /* .iface    = */ {
1745                           /* .get_name         = */ ggml_backend_cann_host_buffer_type_name,
1746                           /* .alloc_buffer     = */ ggml_backend_cann_host_buffer_type_alloc_buffer,
1747                           /* .get_alignment    = */ ggml_backend_cpu_buffer_type()->iface.get_alignment,
1748                           /* .get_max_size     = */ NULL,  // defaults to SIZE_MAX
1749            /* .get_alloc_size   = */ ggml_backend_cpu_buffer_type()->iface.get_alloc_size,
1750                           /* .is_host          = */ ggml_backend_cpu_buffer_type()->iface.is_host,
1751                           },
1752        /* .device   = */
1753        ggml_backend_reg_dev_get(ggml_backend_cann_reg(), 0),
1754        /* .context  = */ nullptr,
1755    };
1756
1757    return &ggml_backend_cann_buffer_type_host;
1758}
1759
1760/**
1761 * @brief Computes the forward operation for a given tensor using CANN
1762 * operations.
1763 *
1764 * This function selects the appropriate CANN operation based on the type of
1765 * operation specified in the tensor and performs the computation.
1766 *
1767 * @param ctx The CANN context containing necessary resources and
1768 * configurations.
1769 * @param dst The destination tensor where the result of the computation will be
1770 * stored.
1771 * @return true if the computation was successful; false otherwise.
1772 */
1773static bool ggml_cann_compute_forward(ggml_backend_cann_context & ctx, struct ggml_tensor * dst) {
1774    switch (dst->op) {
1775        case GGML_OP_REPEAT:
1776            ggml_cann_repeat(ctx, dst);
1777            break;
1778        case GGML_OP_GET_ROWS:
1779            ggml_cann_get_rows(ctx, dst);
1780            break;
1781        case GGML_OP_SET_ROWS:
1782            ggml_cann_set_rows(ctx, dst);
1783            break;
1784        case GGML_OP_DUP:
1785            ggml_cann_dup(ctx, dst);
1786            break;
1787        case GGML_OP_ADD:
1788        case GGML_OP_ADD1:
1789            ggml_cann_binary_op<aclnn_add>(ctx, dst);
1790            break;
1791        case GGML_OP_SUB:
1792            ggml_cann_binary_op<aclnn_sub>(ctx, dst);
1793            break;
1794        case GGML_OP_ACC:
1795            ggml_cann_acc(ctx, dst);
1796            break;
1797        case GGML_OP_MUL:
1798            ggml_cann_binary_op<aclnn_mul>(ctx, dst);
1799            break;
1800        case GGML_OP_DIV:
1801            ggml_cann_binary_op<aclnn_div>(ctx, dst);
1802            break;
1803        case GGML_OP_UNARY:
1804            switch (ggml_get_unary_op(dst)) {
1805                case GGML_UNARY_OP_ABS:
1806                    GGML_CANN_CALL_OP_UNARY(Abs);
1807                    break;
1808                case GGML_UNARY_OP_NEG:
1809                    GGML_CANN_CALL_OP_UNARY(Neg);
1810                    break;
1811                case GGML_UNARY_OP_GELU:
1812                case GGML_UNARY_OP_GELU_ERF:
1813                    // aclnnGelu internally uses the erf-based approximation.
1814                    GGML_CANN_CALL_OP_UNARY(Gelu);
1815                    break;
1816                case GGML_UNARY_OP_SILU:
1817                    GGML_CANN_CALL_OP_UNARY(Silu);
1818                    break;
1819                case GGML_UNARY_OP_GELU_QUICK:
1820                    {
1821                        auto lambda = [](ggml_backend_cann_context & ctx, aclTensor * acl_src, aclTensor * acl_dst) {
1822                            GGML_CANN_CALL_ACLNN_OP(ctx, GeluV2, acl_src, 0, acl_dst);
1823                        };
1824                        ggml_cann_op_unary(lambda, ctx, dst);
1825                    }
1826                    break;
1827                case GGML_UNARY_OP_TANH:
1828                    GGML_CANN_CALL_OP_UNARY(Tanh);
1829                    break;
1830                case GGML_UNARY_OP_RELU:
1831                    GGML_CANN_CALL_OP_UNARY(Relu);
1832                    break;
1833                case GGML_UNARY_OP_SIGMOID:
1834                    GGML_CANN_CALL_OP_UNARY(Sigmoid);
1835                    break;
1836                case GGML_UNARY_OP_HARDSIGMOID:
1837                    GGML_CANN_CALL_OP_UNARY(Hardsigmoid);
1838                    break;
1839                case GGML_UNARY_OP_HARDSWISH:
1840                    GGML_CANN_CALL_OP_UNARY(Hardswish);
1841                    break;
1842                case GGML_UNARY_OP_EXP:
1843                    GGML_CANN_CALL_OP_UNARY(Exp);
1844                    break;
1845                case GGML_UNARY_OP_ELU:
1846                    ggml_cann_elu(ctx, dst);
1847                    break;
1848                case GGML_UNARY_OP_SGN:
1849                    GGML_CANN_CALL_OP_UNARY(Sign);
1850                    break;
1851                case GGML_UNARY_OP_STEP:
1852                    ggml_cann_step(ctx, dst);
1853                    break;
1854                case GGML_UNARY_OP_SOFTPLUS:
1855                    ggml_cann_softplus(ctx, dst);
1856                    break;
1857                default:
1858                    return false;
1859            }
1860            break;
1861        case GGML_OP_GLU:
1862            switch (ggml_get_glu_op(dst)) {
1863                case GGML_GLU_OP_REGLU:
1864                    GGML_CANN_CALL_OP_UNARY_GATED(Relu);
1865                    break;
1866                case GGML_GLU_OP_GEGLU:
1867                    ggml_cann_geglu(ctx, dst, 0);  // approximate=0 → tanh
1868                    break;
1869                case GGML_GLU_OP_GEGLU_ERF:
1870                    ggml_cann_geglu(ctx, dst, 1);  // approximate=1 → erf
1871                    break;
1872                case GGML_GLU_OP_SWIGLU:
1873                    ggml_cann_swiglu(ctx, dst);
1874                    break;
1875                case GGML_GLU_OP_SWIGLU_CLAMP:
1876                    ggml_cann_swiglu_clamp(ctx, dst);
1877                    break;
1878                case GGML_GLU_OP_GEGLU_QUICK:
1879                    ggml_cann_geglu_quick(ctx, dst);
1880                    break;
1881                default:
1882                    return false;
1883            }
1884            break;
1885        case GGML_OP_NORM:
1886            ggml_cann_norm(ctx, dst);
1887            break;
1888        case GGML_OP_GROUP_NORM:
1889            ggml_cann_group_norm(ctx, dst);
1890            break;
1891        case GGML_OP_L2_NORM:
1892            ggml_cann_l2_norm(ctx, dst);
1893            break;
1894        case GGML_OP_CROSS_ENTROPY_LOSS:
1895            ggml_cann_cross_entropy_loss(ctx, dst);
1896            break;
1897        case GGML_OP_CONCAT:
1898            ggml_cann_concat(ctx, dst);
1899            break;
1900        case GGML_OP_UPSCALE:
1901            ggml_cann_upsample_nearest2d(ctx, dst);
1902            break;
1903        case GGML_OP_PAD:
1904            ggml_cann_pad(ctx, dst);
1905            break;
1906        case GGML_OP_ARANGE:
1907            ggml_cann_arange(ctx, dst);
1908            break;
1909        case GGML_OP_TIMESTEP_EMBEDDING:
1910            ggml_cann_timestep_embedding(ctx, dst);
1911            break;
1912        case GGML_OP_LEAKY_RELU:
1913            ggml_cann_leaky_relu(ctx, dst);
1914            break;
1915        case GGML_OP_RMS_NORM:
1916            ggml_cann_rms_norm(ctx, dst);
1917            break;
1918        case GGML_OP_MUL_MAT:
1919            ggml_cann_mul_mat(ctx, dst);
1920            break;
1921        case GGML_OP_MUL_MAT_ID:
1922            ggml_cann_mul_mat_id(ctx, dst);
1923            break;
1924        case GGML_OP_SCALE:
1925            ggml_cann_scale(ctx, dst);
1926            break;
1927        case GGML_OP_SQR:
1928            GGML_ASSERT(dst->src[1] == nullptr);
1929            dst->src[1] = dst->src[0];
1930            ggml_cann_binary_op<aclnn_mul>(ctx, dst);
1931            break;
1932        case GGML_OP_SQRT:
1933            GGML_CANN_CALL_OP_UNARY(Sqrt);
1934            break;
1935        case GGML_OP_CLAMP:
1936            ggml_cann_clamp(ctx, dst);
1937            break;
1938        case GGML_OP_CPY:
1939            ggml_cann_cpy(ctx, dst);
1940            break;
1941        case GGML_OP_SET:
1942            ggml_cann_set(ctx, dst);
1943            break;
1944        case GGML_OP_CONT:
1945            ggml_cann_dup(ctx, dst);
1946            break;
1947        case GGML_OP_NONE:
1948        case GGML_OP_RESHAPE:
1949        case GGML_OP_VIEW:
1950        case GGML_OP_PERMUTE:
1951        case GGML_OP_TRANSPOSE:
1952            break;
1953        case GGML_OP_DIAG_MASK_INF:
1954            ggml_cann_diag_mask(ctx, dst, -INFINITY);
1955            break;
1956        case GGML_OP_SOFT_MAX:
1957            ggml_cann_softmax(ctx, dst);
1958            break;
1959        case GGML_OP_ROPE:
1960            ggml_cann_rope(ctx, dst);
1961            break;
1962        case GGML_OP_IM2COL:
1963            ggml_cann_im2col(ctx, dst);
1964            break;
1965        case GGML_OP_POOL_2D:
1966            ggml_cann_pool2d(ctx, dst);
1967            break;
1968        case GGML_OP_SUM:
1969            ggml_cann_sum(ctx, dst);
1970            break;
1971        case GGML_OP_SUM_ROWS:
1972            ggml_cann_sum_rows(ctx, dst);
1973            break;
1974        case GGML_OP_ARGSORT:
1975            ggml_cann_argsort(ctx, dst);
1976            break;
1977        case GGML_OP_ARGMAX:
1978            ggml_cann_argmax(ctx, dst);
1979            break;
1980        case GGML_OP_COS:
1981            ggml_cann_op_unary<aclnn_cos>(ctx, dst);
1982            break;
1983        case GGML_OP_SIN:
1984            ggml_cann_op_unary<aclnn_sin>(ctx, dst);
1985            break;
1986        case GGML_OP_CONV_TRANSPOSE_1D:
1987            ggml_cann_conv_transpose_1d(ctx, dst);
1988            break;
1989        case GGML_OP_LOG:
1990            GGML_CANN_CALL_OP_UNARY(Log);
1991            break;
1992        case GGML_OP_MEAN:
1993            ggml_cann_mean(ctx, dst);
1994            break;
1995        case GGML_OP_PAD_REFLECT_1D:
1996            ggml_cann_pad_reflect_1d(ctx, dst);
1997            break;
1998        case GGML_OP_COUNT_EQUAL:
1999            ggml_cann_count_equal(ctx, dst);
2000            break;
2001        case GGML_OP_FLASH_ATTN_EXT:
2002            ggml_cann_flash_attn_ext(ctx, dst);
2003            break;
2004        case GGML_OP_OUT_PROD:
2005            ggml_cann_out_prod(ctx, dst);
2006            break;
2007        case GGML_OP_GATED_LINEAR_ATTN:
2008            ggml_cann_gated_linear_attn(ctx, dst);
2009            break;
2010        case GGML_OP_SSM_CONV:
2011            ggml_cann_ssm_conv(ctx, dst);
2012            break;
2013        case GGML_OP_CUMSUM:
2014            ggml_cann_cumsum(ctx, dst);
2015            break;
2016        case GGML_OP_TRI:
2017            ggml_cann_tri(ctx, dst);
2018            break;
2019        case GGML_OP_FILL:
2020            ggml_cann_fill(ctx, dst);
2021            break;
2022        case GGML_OP_DIAG:
2023            ggml_cann_diag(ctx, dst);
2024            break;
2025        case GGML_OP_SOLVE_TRI:
2026            ggml_cann_solve_tri(ctx, dst);
2027            break;
2028        default:
2029            return false;
2030    }
2031
2032    return true;
2033}
2034
2035// backend
2036/**
2037 * @brief Retrieves the name associated with the CANN backend.
2038 *
2039 * This function returns the name assigned to the CANN backend, which is stored
2040 * in the context of the provided backend structure.
2041 *
2042 * @param backend Pointer to the CANN backend structure.
2043 * @return A pointer to a constant string representing the backend name.
2044 */
2045static const char * ggml_backend_cann_name(ggml_backend_t backend) {
2046    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2047
2048    return cann_ctx->name.c_str();
2049}
2050
2051/**
2052 * @brief Frees resources associated with the CANN backend.
2053 *
2054 * This function releases resources associated with the CANN backend context
2055 * and resets the device associated with the backend to its initial state.
2056 *
2057 * @param backend Pointer to the CANN backend structure to be freed.
2058 */
2059static void ggml_backend_cann_free(ggml_backend_t backend) {
2060    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2061    ACL_CHECK(aclrtSynchronizeDevice());
2062    ACL_CHECK(aclrtResetDevice(cann_ctx->device));
2063
2064    delete cann_ctx;
2065    delete backend;
2066}
2067
2068/**
2069 * @brief Sets tensor data asynchronously in the CANN backend.
2070 *
2071 * This function asynchronously sets tensor data in the CANN backend.
2072 *
2073 * @param backend Pointer to the CANN backend structure.
2074 * @param tensor Pointer to the tensor structure to set data for.
2075 * @param data Pointer to the host data to copy to the tensor.
2076 * @param offset Offset in bytes within the host data.
2077 * @param size Size of the data to copy in bytes.
2078 */
2079static void ggml_backend_cann_set_tensor_async(ggml_backend_t backend,
2080                                               ggml_tensor *  tensor,
2081                                               const void *   data,
2082                                               size_t         offset,
2083                                               size_t         size) {
2084    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2085    ggml_backend_buffer_t       buf      = tensor->view_src ? tensor->view_src->buffer : tensor->buffer;
2086
2087    GGML_ASSERT(buf->buft == ggml_backend_cann_buffer_type(cann_ctx->device) && "unsupported buffer type");
2088    GGML_ASSERT(!ggml_is_quantized(tensor->type));
2089
2090    ACL_CHECK(aclrtMemcpyAsync((char *) tensor->data + offset, size, data, size, ACL_MEMCPY_HOST_TO_DEVICE,
2091                               cann_ctx->stream()));
2092}
2093
2094/**
2095 * @brief Gets tensor data asynchronously in the CANN backend.
2096 *
2097 * This function asynchronously gets tensor data in the CANN backend.
2098 *
2099 * @param backend Pointer to the CANN backend structure.
2100 * @param tensor Pointer to the tensor structure to get data from.
2101 * @param data Pointer to the host data to copy from the tensor.
2102 * @param offset Offset in bytes within the host data.
2103 * @param size Size of the data to copy in bytes.
2104 */
2105static void ggml_backend_cann_get_tensor_async(ggml_backend_t      backend,
2106                                               const ggml_tensor * tensor,
2107                                               void *              data,
2108                                               size_t              offset,
2109                                               size_t              size) {
2110    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2111    ggml_backend_buffer_t       buf      = tensor->view_src ? tensor->view_src->buffer : tensor->buffer;
2112
2113    GGML_ASSERT(buf->buft == ggml_backend_cann_buffer_type(cann_ctx->device) && "unsupported buffer type");
2114    GGML_ASSERT(!ggml_is_quantized(tensor->type));
2115
2116    ACL_CHECK(aclrtMemcpyAsync(data, size, (char *) tensor->data + offset, size, ACL_MEMCPY_DEVICE_TO_HOST,
2117                               cann_ctx->stream()));
2118}
2119
2120/**
2121 * @brief Asynchronously copies tensor data between CANN backends.
2122 *
2123 * This function copies tensor data asynchronously between two CANN backends. It
2124 * checks if both tensors reside in CANN buffers and whether the devices support
2125 * peer-to-peer access for direct copying. If not, it returns false.
2126 *
2127 * @param backend_src Pointer to the source CANN backend structure.
2128 * @param backend_dst Pointer to the destination CANN backend structure.
2129 * @param src Pointer to the source tensor to copy data from.
2130 * @param dst Pointer to the destination tensor to copy data to.
2131 * @return true if the copy operation succeeds, false otherwise.
2132 */
2133static bool ggml_backend_cann_cpy_tensor_async(ggml_backend_t      backend_src,
2134                                               ggml_backend_t      backend_dst,
2135                                               const ggml_tensor * src,
2136                                               ggml_tensor *       dst) {
2137    GGML_ASSERT(ggml_backend_is_cann(backend_src) || ggml_backend_is_cann(backend_dst));
2138
2139    GGML_ASSERT(!is_matmul_weight((const ggml_tensor *) src));
2140
2141    if (!ggml_backend_buft_is_cann(src->buffer->buft) || !ggml_backend_buft_is_cann(dst->buffer->buft)) {
2142        return false;
2143    }
2144
2145    ggml_backend_buffer_t buf_src = src->view_src ? src->view_src->buffer : src->buffer;
2146    ggml_backend_buffer_t buf_dst = dst->view_src ? dst->view_src->buffer : dst->buffer;
2147
2148    ggml_backend_cann_context * cann_ctx_src = (ggml_backend_cann_context *) backend_src->context;
2149    ggml_backend_cann_context * cann_ctx_dst = (ggml_backend_cann_context *) backend_dst->context;
2150
2151    size_t copy_size = ggml_nbytes(dst);
2152    if (copy_size == 0) {
2153        return true;
2154    }
2155    if (backend_src != backend_dst) {
2156#ifdef ASCEND_310P
2157        // TODO: Support 310p P2P copy
2158        return false;
2159#endif
2160        ggml_backend_cann_buffer_context * buf_ctx_src = (ggml_backend_cann_buffer_context *) buf_src->context;
2161        ggml_backend_cann_buffer_context * buf_ctx_dst = (ggml_backend_cann_buffer_context *) buf_dst->context;
2162
2163        GGML_ASSERT(cann_ctx_src->device == buf_ctx_src->device);
2164        GGML_ASSERT(cann_ctx_dst->device == buf_ctx_dst->device);
2165
2166        int32_t canAccessPeer = 0;
2167        ACL_CHECK(aclrtDeviceCanAccessPeer(&canAccessPeer, cann_ctx_src->device, cann_ctx_dst->device));
2168        if (!canAccessPeer) {
2169            return false;
2170        }
2171
2172        // need open both directions for memcpyasync between devices.
2173        ACL_CHECK(aclrtDeviceEnablePeerAccess(cann_ctx_src->device, 0));
2174        ggml_cann_set_device(cann_ctx_src->device);
2175        ACL_CHECK(aclrtDeviceEnablePeerAccess(cann_ctx_dst->device, 0));
2176
2177        // wait for task_queue empty to keep task order.
2178        ACL_CHECK(aclrtMemcpyAsync(dst->data, copy_size, src->data, copy_size, ACL_MEMCPY_DEVICE_TO_DEVICE,
2179                                   cann_ctx_src->stream()));
2180        // record event on src stream after the copy
2181        // TODO: this event is not effective with acl graph mode, change to use aclrtSynchronizeStream
2182        // if (!cann_ctx_src->copy_event) {
2183        //     ACL_CHECK(aclrtCreateEventWithFlag(&cann_ctx_src->copy_event, ACL_EVENT_SYNC));
2184        // }
2185        // ACL_CHECK(aclrtRecordEvent(cann_ctx_src->copy_event, cann_ctx_src->stream()));
2186
2187        // // wait on dst stream for the copy to complete
2188        // ggml_cann_set_device(cann_ctx_dst->device);
2189        // ACL_CHECK(aclrtStreamWaitEvent(cann_ctx_dst->stream(), cann_ctx_src->copy_event));
2190        ACL_CHECK(aclrtSynchronizeStream(cann_ctx_src->stream()));
2191    } else {
2192        // src and dst are on the same backend
2193        ACL_CHECK(aclrtMemcpyAsync(dst->data, copy_size, src->data, copy_size, ACL_MEMCPY_DEVICE_TO_DEVICE,
2194                                   cann_ctx_dst->stream()));
2195    }
2196
2197    return true;
2198}
2199
2200/**
2201 * @brief Synchronizes a CANN backend.
2202 *
2203 * This function synchronizes the specified CANN backend by waiting for all
2204 * operations in its associated stream to complete.
2205 *
2206 * @param backend Pointer to the CANN backend structure to synchronize.
2207 */
2208static void ggml_backend_cann_synchronize(ggml_backend_t backend) {
2209    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2210    ggml_cann_set_device(cann_ctx->device);
2211    ACL_CHECK(aclrtSynchronizeStream(cann_ctx->stream()));
2212}
2213
2214/**
2215 * @brief Check if CANN backend can fuse the specified operation sequence
2216 *
2217 * This function determines whether an operation sequence starting from the specified node
2218 * can be fused into an optimized operation in the CANN backend. Operation fusion can reduce
2219 * memory access overhead and improve computational efficiency.
2220 *
2221 * @param cgraph Pointer to the computation graph
2222 * @param node_idx Index of the starting node in the computation graph
2223 * @param ops Sequence of operation types to check for fusion
2224 * @return true if the operations can be fused
2225 * @return false if the operations cannot be fused
2226 */
2227static bool ggml_cann_can_fuse(const struct ggml_cgraph *          cgraph,
2228                               int                                 node_idx,
2229                               std::initializer_list<enum ggml_op> ops) {
2230    if (!ggml_can_fuse(cgraph, node_idx, ops)) {
2231        return false;
2232    }
2233
2234    // CANN backend supports fusing ADD + RMS_NORM operations
2235    if ((ops.size() == 2) && ops.begin()[0] == GGML_OP_ADD && ops.begin()[1] == GGML_OP_RMS_NORM) {
2236        ggml_tensor * add_node = cgraph->nodes[node_idx];
2237        // TODO: support broadcast for ADD + RMS_NORM
2238        if (add_node->src[0]->ne[0] != add_node->src[1]->ne[0] || add_node->src[0]->ne[1] != add_node->src[1]->ne[1] ||
2239            add_node->src[0]->ne[2] != add_node->src[1]->ne[2] || add_node->src[0]->ne[3] != add_node->src[1]->ne[3]) {
2240            return false;
2241        }
2242        return true;
2243    }
2244
2245    return false;
2246}
2247
2248/**
2249 * @brief Evaluate the computation graph and optionally capture or execute it using CANN graph API.
2250 *
2251 * If CANN graph execution is enabled and graph capture is required, this function begins
2252 * graph capture, runs the graph, ends capture, and stores the captured graph.
2253 *
2254 * Otherwise, it falls back to op-by-op execution using the CANN compute kernel dispatcher.
2255 *
2256 * @param cann_ctx                     The CANN backend context.
2257 * @param cgraph                       The ggml computation graph.
2258 * @param use_cann_graph               Whether to use CANN graph execution.
2259 * @param cann_graph_capture_required  Whether graph capture is needed due to graph changes.
2260 */
2261static void evaluate_and_capture_cann_graph(ggml_backend_cann_context * cann_ctx,
2262                                            ggml_cgraph *               cgraph,
2263                                            bool                        use_cann_graph,
2264                                            bool                        cann_graph_capture_required) {
2265#ifdef USE_ACL_GRAPH
2266    if (use_cann_graph && cann_graph_capture_required) {  // Begin CANN graph capture
2267        ACL_CHECK(aclmdlRICaptureBegin(cann_ctx->stream(), ACL_MODEL_RI_CAPTURE_MODE_GLOBAL));
2268    }
2269#endif  // USE_ACL_GRAPH
2270    // Only perform the graph execution if CANN graphs are not enabled, or we are capturing the graph.
2271    // With the use of CANN graphs, the execution will be performed by the graph launch.
2272    static bool opt_fusion = parse_bool(get_env_as_lowercase("GGML_CANN_OPERATOR_FUSION").value_or(""));
2273
2274    if (!use_cann_graph || cann_graph_capture_required) {
2275        for (int i = 0; i < cgraph->n_nodes; i++) {
2276            ggml_tensor * node = cgraph->nodes[i];
2277            if (opt_fusion) {
2278                if (ggml_cann_can_fuse(cgraph, i, { GGML_OP_ADD, GGML_OP_RMS_NORM })) {
2279                    ggml_cann_op_add_rms_norm_fused(*cann_ctx, node, cgraph->nodes[i + 1]);
2280                    i++;
2281                    continue;
2282                }
2283            }
2284
2285            if (ggml_is_empty(node) || node->op == GGML_OP_RESHAPE || node->op == GGML_OP_TRANSPOSE ||
2286                node->op == GGML_OP_VIEW || node->op == GGML_OP_PERMUTE || node->op == GGML_OP_NONE) {
2287                continue;
2288            }
2289
2290            if ((node->flags & GGML_TENSOR_FLAG_COMPUTE) == 0) {
2291                continue;
2292            }
2293
2294            bool ok = ggml_cann_compute_forward(*cann_ctx, node);
2295            if (!ok) {
2296                GGML_LOG_ERROR("%s: op not supported %s (%s)\n", __func__, node->name, ggml_op_name(node->op));
2297            }
2298            GGML_ASSERT(ok);
2299        }
2300    }
2301
2302#ifdef USE_ACL_GRAPH
2303    if (use_cann_graph) {
2304        GGML_ASSERT(!cann_ctx->graph_lru_cache.cache_list.empty());
2305        ggml_cann_graph * matched_graph = cann_ctx->graph_lru_cache.cache_list.front();
2306
2307        if (cann_graph_capture_required) {  // End CANN graph capture
2308            ACL_CHECK(aclmdlRICaptureEnd(cann_ctx->stream(), &matched_graph->graph));
2309        }
2310
2311        // Execute CANN graph
2312        ACL_CHECK(aclmdlRIExecuteAsync(matched_graph->graph, cann_ctx->stream()));
2313    }
2314#endif  // USE_ACL_GRAPH
2315}
2316
2317/**
2318 * @brief Computes a computational graph using a CANN backend.
2319 *
2320 * This function computes the operations defined in the computational graph
2321 * using the specified CANN backend.
2322 *
2323 * @param backend Pointer to the CANN backend structure to use for computation.
2324 * @param cgraph Pointer to the computational graph structure containing nodes
2325 *               representing operations to be computed.
2326 * @return enum ggml_status Returns GGML_STATUS_SUCCESS if computation
2327 *         completes successfully, otherwise an appropriate error status.
2328 */
2329static enum ggml_status ggml_backend_cann_graph_compute(ggml_backend_t backend, ggml_cgraph * cgraph) {
2330    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2331    ggml_cann_set_device(cann_ctx->device);
2332    g_nz_workspaces[cann_ctx->device].clear();
2333
2334    // calculate rope cache for fist layer in current device.
2335    cann_ctx->rope_cache.cached = false;
2336
2337    bool graph_capture_required = false;
2338#ifdef USE_ACL_GRAPH
2339    bool use_cann_graph = true;
2340
2341    static bool prefill_use_graph = parse_bool(get_env_as_lowercase("GGML_CANN_PREFILL_USE_GRAPH").value_or(""));
2342    if (!prefill_use_graph) {
2343        // Do not use acl_graph for prefill.
2344        for (int i = 0; i < cgraph->n_nodes; i++) {
2345            ggml_tensor * node = cgraph->nodes[i];
2346            // TODO: Optimize here. Currently, we can only
2347            // get seq_len by FA's input.
2348            if (node->op == GGML_OP_FLASH_ATTN_EXT) {
2349                // Q -> src[0], shape: [B, S, N, D]
2350                use_cann_graph = (node->src[0]->ne[1] == 1);
2351                break;
2352            }
2353        }
2354    }
2355
2356    if (!cann_ctx->acl_graph_mode) {
2357        use_cann_graph = false;
2358    }
2359
2360    if (use_cann_graph) {
2361        // If no matching graph is found, the graph needs to be recaptured.
2362        graph_capture_required = !cann_ctx->graph_lru_cache.find_and_move_to_front(cgraph);
2363
2364        if (graph_capture_required) {
2365            // If no matching graph is found, add a new ACL graph.
2366            ggml_cann_graph * new_graph = ggml_cann_graph::create_from_cgraph(cgraph);
2367            cann_ctx->graph_lru_cache.push(new_graph);
2368
2369            // Pre-load rope cache before graph capture.  During capture the
2370            // stream cannot perform host-to-device memcpy or device memory
2371            // malloc/free.  Running the full cache init now populates the
2372            // cache metadata so these branches are skipped during capture,
2373            // while also warming up the memory pool.
2374            for (int i = 0; i < cgraph->n_nodes; i++) {
2375                ggml_tensor * node = cgraph->nodes[i];
2376                if (node->op == GGML_OP_ROPE) {
2377                    ggml_cann_rope_cache_preload(*cann_ctx, node);
2378                    break;
2379                }
2380            }
2381        }
2382    }
2383#else
2384    bool use_cann_graph = false;
2385#endif  // USE_ACL_GRAPH
2386    evaluate_and_capture_cann_graph(cann_ctx, cgraph, use_cann_graph, graph_capture_required);
2387
2388    return GGML_STATUS_SUCCESS;
2389}
2390
2391/**
2392 * @brief Checks if the CANN backend supports a specific operation.
2393 *
2394 * This function checks whether the specified operation is supported by the
2395 * CANN backend.
2396 *
2397 * @param backend Pointer to the CANN backend structure to check support for
2398 *                the operation.
2399 * @param op Pointer to the tensor representing the operation to check.
2400 * @return bool Returns true if the operation is supported by the backend,
2401 *              otherwise false.
2402 */
2403static bool ggml_backend_cann_supports_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
2404    switch (op->op) {
2405        case GGML_OP_UNARY:
2406            switch (ggml_get_unary_op(op)) {
2407                case GGML_UNARY_OP_ABS:
2408                case GGML_UNARY_OP_NEG:
2409                case GGML_UNARY_OP_GELU:
2410                case GGML_UNARY_OP_SILU:
2411                case GGML_UNARY_OP_RELU:
2412                case GGML_UNARY_OP_SIGMOID:
2413                case GGML_UNARY_OP_HARDSIGMOID:
2414                case GGML_UNARY_OP_HARDSWISH:
2415                case GGML_UNARY_OP_GELU_QUICK:
2416                case GGML_UNARY_OP_TANH:
2417                case GGML_UNARY_OP_EXP:
2418                case GGML_UNARY_OP_ELU:
2419                case GGML_UNARY_OP_SGN:
2420                case GGML_UNARY_OP_STEP:
2421                case GGML_UNARY_OP_GELU_ERF:
2422                case GGML_UNARY_OP_SOFTPLUS:
2423                    return true;
2424                default:
2425                    return false;
2426            }
2427        case GGML_OP_GLU:
2428            switch (ggml_get_glu_op(op)) {
2429                case GGML_GLU_OP_REGLU:
2430                case GGML_GLU_OP_GEGLU:
2431                case GGML_GLU_OP_SWIGLU:
2432                case GGML_GLU_OP_GEGLU_ERF:
2433                case GGML_GLU_OP_GEGLU_QUICK:
2434                case GGML_GLU_OP_SWIGLU_CLAMP:
2435                    return true;
2436                default:
2437                    return false;
2438            }
2439            break;
2440        case GGML_OP_MUL_MAT:
2441            {
2442                switch (op->src[0]->type) {
2443#ifndef ASCEND_310P
2444                    case GGML_TYPE_BF16:
2445#endif
2446                    case GGML_TYPE_F16:
2447                    case GGML_TYPE_F32:
2448                        return true;
2449                    case GGML_TYPE_Q8_0:
2450                    case GGML_TYPE_Q4_0:
2451#ifdef ASCEND_310P
2452                        // Q4 && Q8 per group is not support on 310p device
2453                        return false;
2454#endif
2455                        // only support contiguous for quantized types.
2456                        return ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]);
2457                    default:
2458                        return false;
2459                }
2460            }
2461        case GGML_OP_MUL_MAT_ID:
2462            switch (op->src[0]->type) {
2463                case GGML_TYPE_F16:
2464                case GGML_TYPE_F32:
2465                    return true;
2466                case GGML_TYPE_Q8_0:
2467                case GGML_TYPE_Q4_0:
2468#ifdef ASCEND_310P
2469                    // Q4 && Q8 per group is not support on 310p device
2470                    return false;
2471#endif
2472                    // only support contiguous for quantized types.
2473                    return ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]);
2474                default:
2475                    return false;
2476            }
2477        // embedding
2478        case GGML_OP_GET_ROWS:
2479            {
2480                switch (op->src[0]->type) {
2481                    case GGML_TYPE_F32:
2482                    case GGML_TYPE_F16:
2483#ifndef ASCEND_310P
2484                    case GGML_TYPE_BF16:
2485#endif
2486                    case GGML_TYPE_Q8_0:
2487                        return true;
2488                    default:
2489                        return false;
2490                }
2491            }
2492            break;
2493        case GGML_OP_SET_ROWS:
2494            {
2495                switch (op->type) {
2496                    case GGML_TYPE_F32:
2497                    case GGML_TYPE_F16:
2498#ifndef ASCEND_310P
2499                    case GGML_TYPE_BF16:
2500#endif
2501                        return true;
2502                    default:
2503                        return false;
2504                }
2505            }
2506            break;
2507        case GGML_OP_CPY:
2508            {
2509                ggml_tensor * src = op->src[0];
2510#ifdef ASCEND_310P
2511                if ((op->type != GGML_TYPE_F32 && op->type != GGML_TYPE_F16) ||
2512                    (src->type != GGML_TYPE_F32 && src->type != GGML_TYPE_F16)) {
2513                    // only support F32 and F16 on 310P.
2514                    return false;
2515                }
2516#else
2517                if ((op->type != GGML_TYPE_F32 && op->type != GGML_TYPE_F16 && op->type != GGML_TYPE_BF16) ||
2518                    (src->type != GGML_TYPE_F32 && src->type != GGML_TYPE_F16 && src->type != GGML_TYPE_BF16)) {
2519                    // only support F32, F16 and BF16.
2520                    return false;
2521                }
2522#endif
2523                return true;
2524            }
2525            break;
2526        case GGML_OP_CONT:
2527            {
2528                switch (op->src[0]->type) {
2529                    case GGML_TYPE_F32:
2530                    case GGML_TYPE_F16:
2531#ifndef ASCEND_310P
2532                    case GGML_TYPE_BF16:
2533#endif
2534                        return true;
2535                    default:
2536                        return false;
2537                }
2538            }
2539        case GGML_OP_ROPE:
2540            {
2541                if (((const int32_t *) op->op_params)[15] != 0) {
2542                    return false; // FIXME: support ggml_rope_set_offset
2543                }
2544                if (op->src[0]->ne[0] > 896) {
2545                    return false;
2546                }
2547#ifdef ASCEND_310P
2548                // TODO: Support rope_dim < ne00(dim)
2549                if (op->src[0]->ne[0] != op->op_params[1]) {
2550                    return false;
2551                }
2552                if (!ggml_is_contiguous(op->src[0])) {
2553                    return false;
2554                }
2555#endif
2556                return true;
2557            }
2558        case GGML_OP_UPSCALE:
2559            {
2560                // aclnnUpsampleNearest2dGetWorkspaceSize not support
2561                // selfDimN[2]/outDimN[2] or selfDimC[3]/outDimC[3] not equal
2562                if (op->src[0]->ne[2] * op->ne[3] != op->src[0]->ne[3] * op->ne[2]) {
2563                    return false;
2564                }
2565                if (op->op_params[0] != GGML_SCALE_MODE_NEAREST) {
2566                    return false;
2567                }
2568                if (op->op_params[0] & GGML_SCALE_FLAG_ANTIALIAS) {
2569                    return false;
2570                }
2571                return true;
2572            }
2573        case GGML_OP_POOL_2D:
2574            {
2575                const int32_t * opts = (const int32_t *) op->op_params;
2576#ifdef ASCEND_310P
2577                enum ggml_op_pool opt = static_cast<ggml_op_pool>(opts[0]);
2578                if (opt == GGML_OP_POOL_MAX) {
2579                    return false;
2580                }
2581#endif
2582                const int k0 = opts[1];
2583                const int k1 = opts[2];
2584                const int p0 = opts[5];
2585                const int p1 = opts[6];
2586                // value of paddingH should be at most half of kernelH
2587                // value of paddingW should be at most half of kernelW
2588                return (p0 <= (k0 / 2)) && (p1 <= (k1 / 2));
2589            }
2590        case GGML_OP_SUM:
2591            return ggml_is_contiguous_rows(op->src[0]);
2592        case GGML_OP_L2_NORM:
2593        case GGML_OP_CROSS_ENTROPY_LOSS:
2594        case GGML_OP_DUP:
2595        case GGML_OP_IM2COL:
2596        case GGML_OP_CONCAT:
2597        case GGML_OP_REPEAT:
2598        case GGML_OP_NONE:
2599        case GGML_OP_RESHAPE:
2600        case GGML_OP_VIEW:
2601        case GGML_OP_PERMUTE:
2602        case GGML_OP_TRANSPOSE:
2603        case GGML_OP_NORM:
2604        case GGML_OP_ADD:
2605        case GGML_OP_ADD1:
2606        case GGML_OP_SUB:
2607        case GGML_OP_MUL:
2608        case GGML_OP_DIV:
2609        case GGML_OP_RMS_NORM:
2610        case GGML_OP_SQR:
2611        case GGML_OP_SQRT:
2612        case GGML_OP_CLAMP:
2613        case GGML_OP_DIAG_MASK_INF:
2614        case GGML_OP_SUM_ROWS:
2615        case GGML_OP_ARGSORT:
2616        case GGML_OP_ACC:
2617        case GGML_OP_SET:
2618        case GGML_OP_GROUP_NORM:
2619            return true;
2620        case GGML_OP_PAD:
2621            // TODO: add circular padding support for cann, see https://github.com/ggml-org/llama.cpp/pull/16985
2622            return ggml_get_op_params_i32(op, 8) == 0;
2623        case GGML_OP_ARANGE:
2624        case GGML_OP_TIMESTEP_EMBEDDING:
2625        case GGML_OP_LEAKY_RELU:
2626        case GGML_OP_ARGMAX:
2627        case GGML_OP_COS:
2628        case GGML_OP_SIN:
2629        case GGML_OP_LOG:
2630        case GGML_OP_MEAN:
2631        case GGML_OP_PAD_REFLECT_1D:
2632        case GGML_OP_COUNT_EQUAL:
2633        case GGML_OP_GATED_LINEAR_ATTN:
2634            return true;
2635        case GGML_OP_OUT_PROD:
2636            {
2637#ifdef ASCEND_310P
2638                // Ger is not supported on 310p device
2639                return false;
2640#endif
2641                switch (op->src[0]->type) {
2642                    case GGML_TYPE_F16:
2643                    case GGML_TYPE_F32:
2644                        return true;
2645                    default:
2646                        return false;
2647                }
2648            }
2649        case GGML_OP_CONV_TRANSPOSE_1D:
2650            return true;
2651        case GGML_OP_SCALE:
2652            float bias;
2653            memcpy(&bias, (const float *) (op->op_params) + 1, sizeof(float));
2654            return bias == 0.0f;  // TODO: support bias != 0.0f
2655        case GGML_OP_SOFT_MAX:
2656            // TODO: support attention sinks [TAG_ATTN_SINKS]
2657            if (op->src[2]) {
2658                return false;
2659            }
2660            return true;
2661        case GGML_OP_FLASH_ATTN_EXT:
2662            {
2663#ifdef ASCEND_310P
2664                // FA not support on 310p device
2665                return false;
2666#endif
2667                // derived from [ggml-cuda.cu]
2668                if (op->src[1]->type != GGML_TYPE_F16 || op->src[2]->type != GGML_TYPE_F16) {
2669                    return false;
2670                }
2671                if (op->src[1]->type != GGML_TYPE_F16 && op->src[1]->type != GGML_TYPE_F32 &&
2672                    op->src[1]->type != GGML_TYPE_BF16) {
2673                    return false;
2674                }
2675                if (op->type != GGML_TYPE_F16 && op->type != GGML_TYPE_F32 && op->type != GGML_TYPE_BF16) {
2676                    return false;
2677                }
2678                // TODO: support attention sinks [TAG_ATTN_SINKS]
2679                if (op->src[4]) {
2680                    return false;
2681                }
2682                if (op->src[1]->ne[0] != op->src[2]->ne[0]) {
2683                    // different head sizes of K and V are not supported yet
2684                    return false;
2685                }
2686                float logitSoftcap = 0.0f;
2687                memcpy(&logitSoftcap, (const float *) (op->op_params) + 2, sizeof(float));
2688                if (logitSoftcap != 0.0f) {
2689                    return false;
2690                }
2691                return true;
2692            }
2693        case GGML_OP_SSM_CONV:
2694            return true;
2695        case GGML_OP_CUMSUM:
2696            return op->src[0]->type == GGML_TYPE_F32;
2697        case GGML_OP_TRI:
2698            return op->src[0]->type == GGML_TYPE_F32;
2699        case GGML_OP_FILL:
2700            return op->src[0]->type == GGML_TYPE_F32;
2701        case GGML_OP_DIAG:
2702            return op->src[0]->type == GGML_TYPE_F32;
2703        case GGML_OP_SOLVE_TRI:
2704            return op->src[0]->type == GGML_TYPE_F32;
2705        default:
2706            return false;
2707    }
2708
2709    GGML_UNUSED(dev);
2710}
2711
2712/**
2713 * @brief Records an event on the CANN backend stream.
2714 *
2715 * This function records the given event on the ACL runtime stream associated
2716 * with the backend context.
2717 *
2718 * @param event Pointer to the event structure to be recorded.
2719 */
2720static void ggml_backend_cann_event_record(ggml_backend_t backend, ggml_backend_event_t event) {
2721    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2722    ACL_CHECK(aclrtRecordEvent((aclrtEvent) event->context, cann_ctx->stream()));
2723}
2724
2725/**
2726 * @brief Waits for a recorded event to complete on the CANN backend stream.
2727 *
2728 * This function makes the given backend wait for the event to complete on its
2729 * ACL runtime stream.
2730 *
2731 * @param backend Pointer to the backend structure.
2732 * @param event Pointer to the event structure that the backend needs to wait
2733 * for.
2734 */
2735static void ggml_backend_cann_event_wait(ggml_backend_t backend, ggml_backend_event_t event) {
2736    ggml_backend_cann_context * cann_ctx = (ggml_backend_cann_context *) backend->context;
2737    if (ggml_backend_is_cann(backend)) {
2738        ACL_CHECK(aclrtStreamWaitEvent(cann_ctx->stream(), (aclrtEvent) event->context));
2739    } else {
2740        GGML_ABORT("fatal error");
2741    }
2742}
2743
2744/**
2745 * @brief Structure defining the interface for the CANN backend.
2746 *
2747 * This structure contains function pointers for various operations
2748 * supported by the CANN backend, including name retrieval, memory
2749 * management, tensor operations, synchronization, and event handling.
2750 */
2751static const ggml_backend_i ggml_backend_cann_interface = {
2752    /* .get_name                = */ ggml_backend_cann_name,
2753    /* .free                    = */ ggml_backend_cann_free,
2754    /* .set_tensor_async        = */ ggml_backend_cann_set_tensor_async,
2755    /* .get_tensor_async        = */ ggml_backend_cann_get_tensor_async,
2756    /* .set_tensor_2d_async     = */ NULL,
2757    /* .get_tensor_2d_async     = */ NULL,
2758    /* .cpy_tensor_async        = */ ggml_backend_cann_cpy_tensor_async,
2759    /* .synchronize             = */ ggml_backend_cann_synchronize,
2760    /* .graph_plan_create       = */ NULL,
2761    /* .graph_plan_free         = */ NULL,
2762    /* .graph_plan_update       = */ NULL,
2763    /* .graph_plan_compute      = */ NULL,
2764    /* .graph_compute           = */ ggml_backend_cann_graph_compute,
2765    /* .event_record            = */ ggml_backend_cann_event_record,
2766    /* .event_wait              = */ ggml_backend_cann_event_wait,
2767    /* .graph_optimize          = */ NULL,
2768};
2769
2770/**
2771 * @brief Return the hardcoded GUID for the CANN backend.
2772 *
2773 * This function returns a static GUID which uniquely identifies the CANN
2774 * backend.
2775 *
2776 * @return A pointer to the static GUID.
2777 */
2778static ggml_guid_t ggml_backend_cann_guid() {
2779    static ggml_guid guid = { 0xa1, 0x94, 0xaf, 0xac, 0xbd, 0x4f, 0x47, 0x34,
2780                              0xbe, 0x1a, 0x9e, 0x71, 0x1f, 0x9e, 0xed, 0x64 };
2781    return &guid;
2782}
2783
2784// backend device
2785struct ggml_backend_cann_device_context {
2786    int         device;
2787    std::string name;
2788    std::string description;
2789    int op_offload_min_batch_size;
2790};
2791
2792static const char * ggml_backend_cann_device_get_name(ggml_backend_dev_t dev) {
2793    ggml_backend_cann_device_context * ctx = (ggml_backend_cann_device_context *) dev->context;
2794    return ctx->name.c_str();
2795}
2796
2797static const char * ggml_backend_cann_device_get_description(ggml_backend_dev_t dev) {
2798    ggml_backend_cann_device_context * ctx = (ggml_backend_cann_device_context *) dev->context;
2799    return ctx->description.c_str();
2800}
2801
2802static void ggml_backend_cann_device_get_memory(ggml_backend_dev_t dev, size_t * free, size_t * total) {
2803    ggml_backend_cann_device_context * ctx = (ggml_backend_cann_device_context *) dev->context;
2804    ggml_backend_cann_get_device_memory(ctx->device, free, total);
2805}
2806
2807static enum ggml_backend_dev_type ggml_backend_cann_device_get_type(ggml_backend_dev_t dev) {
2808    GGML_UNUSED(dev);
2809    return GGML_BACKEND_DEVICE_TYPE_GPU;
2810}
2811
2812static void ggml_backend_cann_device_get_props(ggml_backend_dev_t dev, ggml_backend_dev_props * props) {
2813    props->name        = ggml_backend_cann_device_get_name(dev);
2814    props->description = ggml_backend_cann_device_get_description(dev);
2815    props->type        = ggml_backend_cann_device_get_type(dev);
2816    ggml_backend_cann_device_get_memory(dev, &props->memory_free, &props->memory_total);
2817
2818    bool host_buffer = getenv("GGML_CANN_NO_PINNED") == nullptr;
2819
2820    props->caps = {
2821        /* .async                 = */ false,
2822        /* .host_buffer           = */ host_buffer,
2823        /* .buffer_from_host_ptr  = */ false,
2824        /* .events                = */ true,
2825        /* .mmap_support          = */ true,
2826    };
2827}
2828
2829static ggml_backend_t ggml_backend_cann_device_init(ggml_backend_dev_t dev, const char * params) {
2830    GGML_UNUSED(params);
2831    ggml_backend_cann_device_context * ctx = (ggml_backend_cann_device_context *) dev->context;
2832    return ggml_backend_cann_init(ctx->device);
2833}
2834
2835/**
2836 * @brief Checks if the CANN backend supports a specific backend buffer type.
2837 *
2838 * This function determines whether the CANN backend supports the given backend
2839 * buffer type by comparing the device context of the backend and buffer type.
2840 * It returns true if the devices are same between the backend context and
2841 * buffer type context.
2842 *
2843 * @param backend Pointer to the CANN backend.
2844 * @param buft Pointer to the backend buffer type to check.
2845 * @return bool Returns true if the CANN backend supports the buffer type,
2846 *              otherwise false.
2847 */
2848static bool ggml_backend_cann_supports_buft(ggml_backend_dev_t dev, ggml_backend_buffer_type_t buft) {
2849    if (ggml_backend_buft_is_cann(buft)) {
2850        ggml_backend_cann_device_context *      dev_ctx  = (ggml_backend_cann_device_context *) dev->context;
2851        ggml_backend_cann_buffer_type_context * buft_ctx = (ggml_backend_cann_buffer_type_context *) buft->context;
2852        return buft_ctx->device == dev_ctx->device;
2853    }
2854    return false;
2855}
2856
2857static ggml_backend_buffer_type_t ggml_backend_cann_device_get_buffer_type(ggml_backend_dev_t dev) {
2858    ggml_backend_cann_device_context * ctx = (ggml_backend_cann_device_context *) dev->context;
2859    return ggml_backend_cann_buffer_type(ctx->device);
2860}
2861
2862static ggml_backend_buffer_type_t ggml_backend_cann_device_get_host_buffer_type(ggml_backend_dev_t dev) {
2863    GGML_UNUSED(dev);
2864    return ggml_backend_cann_host_buffer_type();
2865}
2866
2867/**
2868 * @brief Determines if a tensor operation should be offloaded to the CANN
2869 * backend.
2870 *
2871 * This function checks if a given tensor operation should be offloaded to the
2872 * CANN backend based on the operation type and the size of the tensor. It
2873 * returns true if the second dimension (ne[1]) of the tensor is greater than or
2874 * equal to the minimum batch size and the operation is not GGML_OP_GET_ROWS.
2875 *
2876 * @param backend Pointer to the CANN backend.
2877 * @param op Pointer to the tensor operation to check.
2878 * @return bool Returns true if the operation should be offloaded, otherwise
2879 * false.
2880 */
2881static bool ggml_backend_cann_offload_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
2882    ggml_backend_cann_device_context * dev_ctx = (ggml_backend_cann_device_context *)dev->context;
2883
2884    return op->ne[1] >= dev_ctx->op_offload_min_batch_size && op->op != GGML_OP_GET_ROWS;
2885}
2886
2887/**
2888 * @brief Creates a new event for the CANN backend device.
2889 *
2890 * This function initializes a new event for the CANN backend by setting the
2891 * device and creating an ACL runtime event. The created event is then wrapped
2892 * in a ggml_backend_event structure and returned.
2893 *
2894 * @param backend Pointer to the CANN backend.
2895 * @return ggml_backend_event_t Returns a pointer to the new event structure.
2896 */
2897static ggml_backend_event_t ggml_backend_cann_device_event_new(ggml_backend_dev_t dev) {
2898    ggml_backend_cann_device_context * dev_ctx = (ggml_backend_cann_device_context *) dev->context;
2899
2900    ggml_cann_set_device(dev_ctx->device);
2901
2902    aclrtEvent event;
2903    ACL_CHECK(aclrtCreateEvent(&event));
2904
2905    return new ggml_backend_event{
2906        /* .device = */ ggml_backend_reg_dev_get(ggml_backend_cann_reg(), dev_ctx->device),
2907        /* .context = */ event,
2908    };
2909}
2910
2911/**
2912 * @brief Frees a CANN backend event.
2913 *
2914 * This function destroys the ACL runtime event associated with the given CANN
2915 * backend event and then deletes the event structure itself.
2916 *
2917 * @param event Pointer to the event structure to be freed.
2918 */
2919static void ggml_backend_cann_device_event_free(ggml_backend_dev_t dev, ggml_backend_event_t event) {
2920    ACL_CHECK(aclrtDestroyEvent((aclrtEvent) event->context));
2921
2922    delete event;
2923    GGML_UNUSED(dev);
2924}
2925
2926/**
2927 * @brief Synchronizes the given event on the CANN backend.
2928 *
2929 * This function waits for the specified event to complete on the ACL runtime.
2930 *
2931 * @param event Pointer to the event structure to be synchronized.
2932 */
2933static void ggml_backend_cann_device_event_synchronize(ggml_backend_dev_t dev, ggml_backend_event_t event) {
2934    ACL_CHECK(aclrtSynchronizeEvent((aclrtEvent) event->context));
2935
2936    GGML_UNUSED(dev);
2937}
2938
2939static const ggml_backend_device_i ggml_backend_cann_device_interface = {
2940    /* .get_name                = */ ggml_backend_cann_device_get_name,
2941    /* .get_description         = */ ggml_backend_cann_device_get_description,
2942    /* .get_memory              = */ ggml_backend_cann_device_get_memory,
2943    /* .get_type                = */ ggml_backend_cann_device_get_type,
2944    /* .get_props               = */ ggml_backend_cann_device_get_props,
2945    /* .init_backend            = */ ggml_backend_cann_device_init,  // called for every card
2946    /* .get_buffer_type         = */ ggml_backend_cann_device_get_buffer_type,
2947    /* .get_host_buffer_type    = */ ggml_backend_cann_device_get_host_buffer_type,
2948    /* .buffer_from_host_ptr    = */ NULL,  // not supported for CANN
2949    /* .supports_op             = */ ggml_backend_cann_supports_op,
2950    /* .supports_buft           = */ ggml_backend_cann_supports_buft,
2951    /* .offload_op              = */ ggml_backend_cann_offload_op,
2952    /* .event_new               = */ ggml_backend_cann_device_event_new,
2953    /* .event_free              = */ ggml_backend_cann_device_event_free,
2954    /* .event_synchronize       = */ ggml_backend_cann_device_event_synchronize,
2955};
2956
2957// backend reg
2958struct ggml_backend_cann_reg_context {
2959    std::vector<ggml_backend_dev_t> devices;
2960};
2961
2962static const char * ggml_backend_cann_reg_get_name(ggml_backend_reg_t reg) {
2963    GGML_UNUSED(reg);
2964    return GGML_CANN_NAME;
2965}
2966
2967static size_t ggml_backend_cann_reg_get_device_count(ggml_backend_reg_t reg) {
2968    ggml_backend_cann_reg_context * ctx = (ggml_backend_cann_reg_context *) reg->context;
2969    return ctx->devices.size();
2970}
2971
2972static ggml_backend_dev_t ggml_backend_cann_reg_get_device(ggml_backend_reg_t reg, size_t index) {
2973    ggml_backend_cann_reg_context * ctx = (ggml_backend_cann_reg_context *) reg->context;
2974    GGML_ASSERT(index < ctx->devices.size());
2975    return ctx->devices[index];
2976}
2977
2978static void * ggml_backend_cann_reg_get_proc_address(ggml_backend_reg_t reg, const char * name) {
2979    GGML_UNUSED(reg);
2980    GGML_UNUSED(name);
2981    // reserved for future use
2982    return nullptr;
2983}
2984
2985static const ggml_backend_reg_i ggml_backend_cann_reg_interface = {
2986    /* .get_name          = */ ggml_backend_cann_reg_get_name,
2987    /* .get_device_count  = */ ggml_backend_cann_reg_get_device_count,
2988    /* .get_device        = */ ggml_backend_cann_reg_get_device,
2989    /* .get_proc_address  = */ ggml_backend_cann_reg_get_proc_address,
2990};
2991
2992// backend registry, called only once for cann backend
2993ggml_backend_reg_t ggml_backend_cann_reg() {
2994    static ggml_backend_reg reg;
2995    static bool             initialized = false;
2996
2997    {
2998        static std::mutex           mutex;
2999        std::lock_guard<std::mutex> lock(mutex);
3000        if (!initialized) {
3001            aclInit(nullptr);
3002            ggml_backend_cann_reg_context * ctx = new ggml_backend_cann_reg_context;
3003            const int min_batch_size = getenv("GGML_OP_OFFLOAD_MIN_BATCH") ? atoi(getenv("GGML_OP_OFFLOAD_MIN_BATCH")) : 32;
3004
3005            for (int i = 0; i < ggml_cann_info().device_count; i++) {
3006                ggml_backend_cann_device_context * dev_ctx = new ggml_backend_cann_device_context();
3007                dev_ctx->description                       = aclrtGetSocName();
3008                dev_ctx->device                            = i;
3009                dev_ctx->name                              = GGML_CANN_NAME + std::to_string(i);
3010                dev_ctx->op_offload_min_batch_size         = min_batch_size;
3011                ggml_cann_set_device(i);
3012                ggml_backend_dev_t dev = new ggml_backend_device{ /* .iface   = */ ggml_backend_cann_device_interface,
3013                                                                  /* .reg     = */ &reg,
3014                                                                  /* .context = */ dev_ctx };
3015                ctx->devices.push_back(dev);
3016            }
3017
3018            reg = ggml_backend_reg{ /* .api_version = */ GGML_BACKEND_API_VERSION,
3019                                    /* .iface       = */ ggml_backend_cann_reg_interface,
3020                                    /* .context     = */ ctx };
3021        }
3022
3023        initialized = true;
3024    }
3025
3026    return &reg;
3027}
3028
3029ggml_backend_t ggml_backend_cann_init(int32_t device) {
3030    aclInit(nullptr);
3031    if (device < 0 || device >= ggml_backend_cann_get_device_count()) {
3032        GGML_LOG_ERROR("%s: error: invalid device %d\n", __func__, device);
3033        return nullptr;
3034    }
3035
3036    ggml_backend_cann_context * ctx = new ggml_backend_cann_context(device);
3037    if (ctx == nullptr) {
3038        GGML_LOG_ERROR("%s: error: failed to allocate context\n", __func__);
3039        return nullptr;
3040    }
3041    ggml_cann_set_device(ctx->device);
3042    ggml_backend_t cann_backend =
3043        new ggml_backend{ /* .guid      = */ ggml_backend_cann_guid(),
3044                          /* .interface = */ ggml_backend_cann_interface,
3045                          /* .device    = */ ggml_backend_reg_dev_get(ggml_backend_cann_reg(), device),
3046                          /* .context   = */ ctx };
3047
3048    return cann_backend;
3049}
3050
3051bool ggml_backend_is_cann(ggml_backend_t backend) {
3052    return backend != NULL && ggml_guid_matches(backend->guid, ggml_backend_cann_guid());
3053}
3054
3055int32_t ggml_backend_cann_get_device_count() {
3056    return ggml_cann_info().device_count;
3057}
3058
3059void ggml_backend_cann_get_device_description(int32_t device, char * description, size_t description_size) {
3060    ggml_cann_set_device(device);
3061    const char * soc_name = aclrtGetSocName();
3062    snprintf(description, description_size, "%s", soc_name);
3063}
3064
3065void ggml_backend_cann_get_device_memory(int32_t device, size_t * free, size_t * total) {
3066    ggml_cann_set_device(device);
3067    ACL_CHECK(aclrtGetMemInfo(ACL_HBM_MEM, free, total));
3068}
3069
3070GGML_BACKEND_DL_IMPL(ggml_backend_cann_reg)