char-fork/llama.cpp

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

Martin Changggml-et: Initial ET backend (#24179)082b326fc

char/aliceai-t5-moe
20.1 KiB508 linesraw
1#include "ggml-et-kernels.h"
2
3#include "ggml-et-kernels-embed.hpp"
4#include "ggml-et-uberkernel-kernel-map.h"
5#include "ggml-impl.h"
6
7#include <cstdlib>
8#include <cstring>
9#include <fstream>
10
11#define ET_TRACE_DECODER_IMPL
12#include <et-trace/decoder.h>
13#include <et-trace/layout.h>
14
15static constexpr size_t GGML_ET_UBERKERNEL_PARAM_ALIGN = 64;
16
17static size_t ggml_et_align_up(size_t value, size_t alignment) {
18    return (value + alignment - 1) & ~(alignment - 1);
19}
20
21static size_t ggml_et_next_capacity(size_t current_capacity, size_t required_capacity) {
22    if (current_capacity == 0) {
23        return required_capacity;
24    }
25
26    size_t next_capacity = current_capacity;
27    while (next_capacity < required_capacity) {
28        next_capacity *= 2;
29    }
30
31    return next_capacity;
32}
33
34static ggml_backend_et_uberkernel_slot & ggml_et_uberkernel_current_slot(ggml_backend_et_uberkernel_context * uk_ctx) {
35    return uk_ctx->slots[uk_ctx->current_slot];
36}
37
38// Wait for any in-flight launch that previously used this slot to finish,
39// so the host vectors and device buffers are safe to mutate / free.
40static void ggml_et_uberkernel_slot_wait(ggml_backend_et_uberkernel_slot &     slot,
41                                         const std::shared_ptr<rt::IRuntime> & runtime) {
42    if (!slot.has_pending || !runtime) {
43        return;
44    }
45    runtime->waitForEvent(slot.pending_event);
46    slot.has_pending = false;
47}
48
49static void ggml_et_uberkernel_reset_segment(ggml_backend_et_uberkernel_context * uk_ctx) {
50    if (!uk_ctx) {
51        return;
52    }
53
54    uk_ctx->shire_mask = 0;
55    auto & slot        = ggml_et_uberkernel_current_slot(uk_ctx);
56    // Drain any prior launch on this slot before clearing its host buffers.
57    // begin_graph and abort_graph both come through here; in either case we
58    // must not yank the source memory out from under an in-flight DMA.
59    ggml_et_uberkernel_slot_wait(slot, ggml_et_runtime());
60    slot.insts.clear();
61    slot.params_blob.clear();
62}
63
64static bool ggml_et_uberkernel_ensure_slot_capacity(ggml_backend_et_uberkernel_slot & slot,
65                                                    ggml_backend_et_device_context *  dev_ctx,
66                                                    size_t                            insts_size,
67                                                    size_t                            params_size) {
68    std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
69    if (!dev_ctx || !runtime) {
70        return false;
71    }
72
73    try {
74        if (slot.device_insts == nullptr || insts_size > slot.device_insts_capacity) {
75            const size_t new_capacity = ggml_et_next_capacity(slot.device_insts_capacity, insts_size);
76            if (slot.device_insts) {
77                runtime->freeDevice(dev_ctx->rtid, slot.device_insts);
78            }
79            slot.device_insts          = runtime->mallocDevice(dev_ctx->rtid, new_capacity);
80            slot.device_insts_capacity = slot.device_insts ? new_capacity : 0;
81        }
82
83        if (slot.device_params == nullptr || params_size > slot.device_params_capacity) {
84            const size_t new_capacity = ggml_et_next_capacity(slot.device_params_capacity, params_size);
85            if (slot.device_params) {
86                runtime->freeDevice(dev_ctx->rtid, slot.device_params);
87            }
88            slot.device_params          = runtime->mallocDevice(dev_ctx->rtid, new_capacity);
89            slot.device_params_capacity = slot.device_params ? new_capacity : 0;
90        }
91    } catch (const std::exception & e) {
92        GGML_LOG_ERROR("ET: Failed to resize uberkernel buffers: %s\n", e.what());
93        return false;
94    }
95
96    return slot.device_insts != nullptr && slot.device_params != nullptr;
97}
98
99// Get embedded kernel data by name
100static std::vector<std::byte> ggml_et_get_embedded_kernel(const std::string & kernel_name) {
101    auto it = ggml_et_embedded_kernels.find(kernel_name);
102    if (it == ggml_et_embedded_kernels.end()) {
103        GGML_LOG_ERROR("ET: Unknown embedded kernel: %s\n", kernel_name.c_str());
104        return {};
105    }
106
107    const unsigned char * data = it->second.first;
108    uint64_t              size = it->second.second;
109
110    std::vector<std::byte> buffer(size);
111    std::memcpy(buffer.data(), data, size);
112
113    return buffer;
114}
115
116// Read kernel from file (for development/override)
117static std::vector<std::byte> ggml_et_read_kernel_file(const std::string & kernel_path) {
118    std::ifstream file(kernel_path, std::ios::binary | std::ios::ate);
119    if (!file) {
120        return {};
121    }
122
123    auto size = file.tellg();
124    file.seekg(0, std::ios::beg);
125
126    std::vector<std::byte> buffer(size);
127    file.read(reinterpret_cast<char *>(buffer.data()), size);
128
129    return buffer;
130}
131
132// Load kernel from file or embedded data
133bool ggml_et_load_kernel(ggml_backend_et_device_context * dev_ctx, const std::string & kernel_name) {
134    std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
135    if (!runtime) {
136        GGML_LOG_ERROR("ET: Runtime not available for kernel loading\n");
137        return false;
138    }
139
140    // Check if kernel already loaded
141    if (dev_ctx->loaded_kernels.find(kernel_name) != dev_ctx->loaded_kernels.end()) {
142        GGML_LOG_DEBUG("ET: Kernel %s already loaded on device %d\n", kernel_name.c_str(), dev_ctx->devidx);
143        return true;
144    }
145
146    std::vector<std::byte> kernel_data;
147    const char *           kernels_path = getenv("GGML_ET_KERNELS_PATH");
148
149    // If GGML_ET_KERNELS_PATH is set, try to load from file first
150    if (kernels_path) {
151        std::string kernel_file = std::string(kernels_path) + "/" + kernel_name + ".elf";
152        kernel_data             = ggml_et_read_kernel_file(kernel_file);
153
154        if (!kernel_data.empty()) {
155            GGML_LOG_INFO("ET: Loading kernel %s from file: %s\n", kernel_name.c_str(), kernel_file.c_str());
156        } else {
157            GGML_LOG_INFO("ET: Kernel file not found: %s, falling back to embedded\n", kernel_file.c_str());
158        }
159    }
160
161    // If no file data, use embedded kernel
162    if (kernel_data.empty()) {
163        kernel_data = ggml_et_get_embedded_kernel(kernel_name);
164        if (kernel_data.empty()) {
165            GGML_LOG_ERROR("ET: Failed to get kernel data for %s\n", kernel_name.c_str());
166            return false;
167        }
168    }
169
170    try {
171        // Load kernel code using device's default stream
172        auto load_result = runtime->loadCode(dev_ctx->default_stream, kernel_data.data(), kernel_data.size());
173        runtime->waitForEvent(load_result.event_);
174
175        // Store kernel handle
176        dev_ctx->loaded_kernels[kernel_name] = load_result.kernel_;
177        return true;
178
179    } catch (const std::exception & e) {
180        GGML_LOG_ERROR("ET: Failed to load kernel %s: %s\n", kernel_name.c_str(), e.what());
181        return false;
182    }
183}
184
185static bool ggml_et_launch_kernel_internal(ggml_backend_et_device_context * dev_ctx,
186                                           const std::string &              kernel_name,
187                                           void *                           params,
188                                           size_t                           params_size,
189                                           uint64_t                         shire_mask,
190                                           bool                             enable_print,
191                                           bool                             sync_error_check,
192                                           rt::EventId *                    out_event = nullptr) {
193    std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
194    if (!runtime) {
195        GGML_LOG_ERROR("ET: Runtime not available for kernel launch\n");
196        return false;
197    }
198
199    // Lazy loading: check if kernel is loaded, load if needed
200    auto kernel_it = dev_ctx->loaded_kernels.find(kernel_name);
201    if (kernel_it == dev_ctx->loaded_kernels.end()) {
202        // Kernel not loaded - load it
203        if (!ggml_et_load_kernel(dev_ctx, kernel_name)) {
204            GGML_LOG_ERROR("ET: Failed to lazy-load kernel %s\n", kernel_name.c_str());
205            return false;
206        }
207
208        // Update iterator after successful load
209        kernel_it = dev_ctx->loaded_kernels.find(kernel_name);
210        if (kernel_it == dev_ctx->loaded_kernels.end()) {
211            GGML_LOG_ERROR("ET: Kernel %s not found after loading\n", kernel_name.c_str());
212            return false;
213        }
214    }
215
216    rt::KernelId kernel_id = kernel_it->second;
217
218    try {
219        // Setup kernel launch options
220        rt::KernelLaunchOptions k_opts;
221        k_opts.setShireMask(shire_mask);  // Default: all shires (0xFFFFFFFF)
222        k_opts.setBarrier(true);          // Wait for completion
223        k_opts.setFlushL3(false);         // No L3 flush needed
224        if (enable_print) {
225            k_opts.setUserTracing(reinterpret_cast<uint64_t>(dev_ctx->trace_buffer),
226                                  static_cast<uint32_t>(ET_TRACE_BUFFER_SIZE),
227                                  0,                      // threshold
228                                  shire_mask,             // shire mask
229                                  0xFFFFFFFFFFFFFFFFULL,  // threadMask - all threads
230                                  0xFFFFFFFFU,            // eventMask - all events
231                                  0xFFFFFFFFU             // filterMask - all levels
232            );
233        }
234
235        if (sync_error_check) {
236            runtime->waitForStream(dev_ctx->default_stream);
237            auto errors = runtime->retrieveStreamErrors(dev_ctx->default_stream);
238            if (!errors.empty()) {
239                GGML_LOG_ERROR("ET: Errors detected before kernel \"%s\" launch\n", kernel_name.c_str());
240                for (const auto & error : errors) {
241                    GGML_LOG_ERROR("ET: Error code: %d\n", (int) error.errorCode_);
242                }
243                abort();
244            }
245        }
246
247        rt::EventId launch_event = runtime->kernelLaunch(dev_ctx->default_stream, kernel_id,
248                                                         reinterpret_cast<std::byte *>(params), params_size, k_opts);
249        if (out_event) {
250            *out_event = launch_event;
251        }
252
253        if (enable_print) {
254            std::vector<std::byte> host_trace_buf(ET_TRACE_BUFFER_SIZE);
255            runtime->memcpyDeviceToHost(dev_ctx->default_stream, dev_ctx->trace_buffer, host_trace_buf.data(),
256                                        ET_TRACE_BUFFER_SIZE);
257            runtime->waitForStream(dev_ctx->default_stream);
258            const auto * trace_header = reinterpret_cast<const trace_buffer_std_header_t *>(host_trace_buf.data());
259            const trace_entry_header_t * entry = nullptr;
260            while ((entry = Trace_Decode(trace_header, entry))) {
261                if (entry->type != TRACE_TYPE_STRING) {
262                    continue;
263                }
264                const auto * str_entry = reinterpret_cast<const trace_string_t *>(entry);
265                printf("[hart %d] %s", entry->hart_id, str_entry->string);
266            }
267        }
268
269        if (sync_error_check) {
270            // Already triggered. No need to retrigger
271            if (!enable_print) {
272                runtime->waitForStream(dev_ctx->default_stream);
273            }
274            auto errors = runtime->retrieveStreamErrors(dev_ctx->default_stream);
275            if (!errors.empty()) {
276                GGML_LOG_ERROR("ET: Errors detected during kernel \"%s\" execution\n", kernel_name.c_str());
277                for (const auto & error : errors) {
278                    GGML_LOG_ERROR("ET: Error code: %d\n", (int) error.errorCode_);
279                }
280                abort();
281            }
282        }
283
284        return true;
285    } catch (const std::exception & e) {
286        GGML_LOG_ERROR("ET: Failed to launch kernel %s: %s\n", kernel_name.c_str(), e.what());
287        return false;
288    }
289}
290
291void ggml_et_uberkernel_begin_graph(ggml_backend_et_uberkernel_context * uk_ctx) {
292    if (!uk_ctx) {
293        return;
294    }
295
296    uk_ctx->failed = false;
297    ggml_et_uberkernel_reset_segment(uk_ctx);
298}
299
300static bool ggml_et_launch_uberkernel_segment(ggml_backend_et_device_context *     dev_ctx,
301                                              ggml_backend_et_uberkernel_context * uk_ctx) {
302    if (!uk_ctx || !dev_ctx) {
303        return false;
304    }
305
306    auto & slot = ggml_et_uberkernel_current_slot(uk_ctx);
307    if (slot.insts.empty()) {
308        return true;
309    }
310
311    std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
312    if (!runtime) {
313        GGML_LOG_ERROR("ET: Runtime not available for uberkernel commit\n");
314        uk_ctx->failed = true;
315        return false;
316    }
317
318    const size_t   insts_size  = slot.insts.size() * sizeof(ggml_et_uberkernel_inst);
319    const size_t   params_size = slot.params_blob.size();
320    const uint64_t shire_mask  = uk_ctx->shire_mask;
321    bool           ok          = false;
322
323    try {
324        if (!ggml_et_uberkernel_ensure_slot_capacity(slot, dev_ctx, insts_size, params_size)) {
325            GGML_LOG_ERROR("ET: Failed to allocate uberkernel device buffers\n");
326            uk_ctx->failed = true;
327            // Drop this segment but keep the slot drained so we don't leak
328            // host vectors into the next graph.
329            slot.insts.clear();
330            slot.params_blob.clear();
331            uk_ctx->shire_mask = 0;
332            return false;
333        }
334
335        // Fire-and-forget H2D + launch on default_stream. In-stream FIFO
336        // ordering guarantees the kernel sees fully-uploaded buffers; the
337        // host source bytes (slot.insts / slot.params_blob) stay alive
338        // because we won't touch this slot again until pending_event fires.
339        runtime->memcpyHostToDevice(dev_ctx->default_stream, reinterpret_cast<const std::byte *>(slot.insts.data()),
340                                    slot.device_insts, insts_size, true);
341        runtime->memcpyHostToDevice(dev_ctx->default_stream, slot.params_blob.data(), slot.device_params, params_size,
342                                    true);
343
344        ggml_et_uberkernel_params params = {
345            static_cast<uint32_t>(slot.insts.size()),
346            static_cast<uint32_t>(sizeof(ggml_et_uberkernel_inst)),
347            reinterpret_cast<uint64_t>(slot.device_insts),
348            reinterpret_cast<uint64_t>(slot.device_params),
349        };
350
351        rt::EventId launch_event{};
352        ok = ggml_et_launch_kernel_internal(dev_ctx, "uberkernel", &params, sizeof(params), shire_mask, false, false,
353                                            &launch_event);
354        if (ok) {
355            // The kernelLaunch above is the last thing on default_stream
356            // that touches this slot's device buffers. Recording its event
357            // lets the next reuse of this slot wait on that one event
358            // instead of the whole stream.
359            slot.pending_event = launch_event;
360            slot.has_pending   = true;
361        }
362    } catch (const std::exception & e) {
363        GGML_LOG_ERROR("ET: Failed to commit uberkernel segment: %s\n", e.what());
364    }
365    uk_ctx->failed = !ok;
366
367    if (ok) {
368        uk_ctx->current_slot = (uk_ctx->current_slot + 1) % ggml_backend_et_uberkernel_context::SLOT_COUNT;
369        auto & next          = ggml_et_uberkernel_current_slot(uk_ctx);
370        ggml_et_uberkernel_slot_wait(next, runtime);
371        next.insts.clear();
372        next.params_blob.clear();
373    } else {
374        slot.insts.clear();
375        slot.params_blob.clear();
376    }
377    uk_ctx->shire_mask = 0;
378    return ok;
379}
380
381void ggml_et_uberkernel_abort_graph(ggml_backend_et_uberkernel_context * uk_ctx) {
382    if (!uk_ctx) {
383        return;
384    }
385
386    uk_ctx->failed = false;
387    ggml_et_uberkernel_reset_segment(uk_ctx);
388}
389
390bool ggml_et_uberkernel_failed(const ggml_backend_et_uberkernel_context * uk_ctx) {
391    return uk_ctx && uk_ctx->failed;
392}
393
394static bool ggml_et_launch_uberkernel(ggml_backend_et_device_context * dev_ctx,
395                                      const std::string &              kernel_name,
396                                      void *                           params,
397                                      size_t                           params_size,
398                                      uint64_t                         shire_mask,
399                                      bool                             enable_print,
400                                      bool                             sync_error_check) {
401    if (!dev_ctx) {
402        return false;
403    }
404
405    ggml_backend_et_uberkernel_context * uk_ctx        = &dev_ctx->uberkernel;
406    const uint16_t                       uberkernel_id = ggml_et_uberkernel_kernel_id_from_name(kernel_name.c_str());
407    if (uberkernel_id == GGML_ET_UBERKERNEL_KERNEL_INVALID) {
408        if (!ggml_et_launch_uberkernel_segment(dev_ctx, uk_ctx)) {
409            return false;
410        }
411        return ggml_et_launch_kernel_internal(dev_ctx, kernel_name, params, params_size, shire_mask, enable_print,
412                                              sync_error_check);
413    }
414
415    auto &       slot          = ggml_et_uberkernel_current_slot(uk_ctx);
416    const size_t params_offset = ggml_et_align_up(slot.params_blob.size(), GGML_ET_UBERKERNEL_PARAM_ALIGN);
417    if (params_offset > slot.params_blob.size()) {
418        slot.params_blob.resize(params_offset);
419    }
420
421    const std::byte * params_bytes = reinterpret_cast<const std::byte *>(params);
422    slot.params_blob.insert(slot.params_blob.end(), params_bytes, params_bytes + params_size);
423
424    ggml_et_uberkernel_inst inst = {
425        uberkernel_id,
426        0,
427        static_cast<uint32_t>(params_offset),
428        static_cast<uint32_t>(params_size),
429    };
430    slot.insts.push_back(inst);
431
432    if (slot.insts.size() == 1) {
433        uk_ctx->shire_mask = shire_mask;
434    }
435
436    return true;
437}
438
439bool ggml_et_uberkernel_end_graph(ggml_backend_et_device_context * dev_ctx) {
440    if (!dev_ctx || !dev_ctx->uberkernel_enabled) {
441        return true;
442    }
443
444    return ggml_et_launch_uberkernel_segment(dev_ctx, &dev_ctx->uberkernel);
445}
446
447bool ggml_et_launch_kernel(ggml_backend_et_device_context * dev_ctx,
448                           const std::string &              kernel_name,
449                           void *                           params,
450                           size_t                           params_size,
451                           uint64_t                         shire_mask,
452                           bool                             enable_print,
453                           bool                             sync_error_check) {
454    if (!dev_ctx) {
455        return false;
456    }
457
458    if (!dev_ctx->uberkernel_enabled) {
459        return ggml_et_launch_kernel_internal(dev_ctx, kernel_name, params, params_size, shire_mask, enable_print,
460                                              sync_error_check);
461    }
462
463    return ggml_et_launch_uberkernel(dev_ctx, kernel_name, params, params_size, shire_mask, enable_print,
464                                     sync_error_check);
465}
466
467void ggml_et_unload_kernel(ggml_backend_et_device_context * dev_ctx, const std::string & kernel_name) {
468    std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
469    if (!runtime) {
470        return;
471    }
472
473    auto kernel_it = dev_ctx->loaded_kernels.find(kernel_name);
474    if (kernel_it != dev_ctx->loaded_kernels.end()) {
475        try {
476            runtime->unloadCode(kernel_it->second);
477            dev_ctx->loaded_kernels.erase(kernel_it);
478        } catch (const std::exception & e) {
479            GGML_LOG_ERROR("ET: Failed to unload kernel %s: %s\n", kernel_name.c_str(), e.what());
480        }
481    }
482}
483
484void ggml_et_unload_all_kernels(ggml_backend_et_device_context * dev_ctx) {
485    if (!dev_ctx) {
486        return;
487    }
488
489    // Make a copy of kernel names since ggml_et_unload_kernel modifies the map
490    std::vector<std::string> kernel_names;
491    kernel_names.reserve(dev_ctx->loaded_kernels.size());
492    for (const auto & kernel_pair : dev_ctx->loaded_kernels) {
493        kernel_names.push_back(kernel_pair.first);
494    }
495
496    for (const auto & kernel_name : kernel_names) {
497        ggml_et_unload_kernel(dev_ctx, kernel_name);
498    }
499}
500
501std::vector<std::pair<std::string, rt::KernelId>> ggml_et_get_loaded_kernels(ggml_backend_et_device_context * dev_ctx) {
502    std::vector<std::pair<std::string, rt::KernelId>> loaded_kernels;
503    loaded_kernels.reserve(dev_ctx->loaded_kernels.size());
504    for (const auto & kernel_pair : dev_ctx->loaded_kernels) {
505        loaded_kernels.push_back(kernel_pair);
506    }
507    return loaded_kernels;
508}