#include "ggml-et.h"
#include "ggml-backend-impl.h"
#include "ggml-backend.h"
#include "ggml-et-common.h"
#include "ggml-et-kernels.h"
#include "ggml-et-memops.h"
#include "ggml-et-ops.h"
#include "ggml-impl.h"
#include "ggml.h"
#include <stdarg.h>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#if __has_include(<filesystem>)
# include <filesystem>
namespace fs = std::filesystem;
#elif __has_include(<experimental/filesystem>)
# include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#else
# error "cannot include the filesystem library"
#endif
static void ggml_et_dump_tensor_metadata(const ggml_tensor * ggtensor, size_t indent_level, const char * title) {
std::string spaces(indent_level, ' ');
fprintf(stderr,
"%s%s: %s\n"
"%s type: %s\n"
"%s ne: %lld %lld %lld %lld\n"
"%s nb: %zu %zu %zu %zu\n"
"%s op: %s\n"
"%s data: %p\n"
"%s src0: %p\n",
spaces.c_str(), title, ggtensor->name, spaces.c_str(), ggml_type_name(ggtensor->type), spaces.c_str(), (long long) ggtensor->ne[0],
(long long) ggtensor->ne[1], (long long) ggtensor->ne[2], (long long) ggtensor->ne[3], spaces.c_str(),
ggtensor->nb[0], ggtensor->nb[1], ggtensor->nb[2], ggtensor->nb[3], spaces.c_str(), ggml_op_name(ggtensor->op),
spaces.c_str(), ggtensor->data, spaces.c_str(), (void *) ggtensor->src[0]);
}
static void ggml_et_dump_operator_metadata(const ggml_tensor * ggtensor) {
GGML_ASSERT(ggtensor != NULL);
ggml_et_dump_tensor_metadata(ggtensor, 0, "GGML tensor");
for (int i = 0; i < GGML_MAX_SRC && ggtensor->src[i]; i++) {
char arr[16];
int n = snprintf(arr, sizeof(arr), "src[%i]->name", i);
GGML_ASSERT((unsigned) n < sizeof(arr) && "printed too much data to stack buffer");
ggml_et_dump_tensor_metadata(ggtensor->src[i], 2, arr);
}
if (ggtensor->view_src) {
ggml_et_dump_tensor_metadata(ggtensor, 2, "view_src");
}
}
static struct ggml_et_driver {
std::shared_ptr<dev::IDeviceLayer> device_layer;
std::shared_ptr<rt::IRuntime> runtime;
std::unique_ptr<std::ofstream> profile_stream;
std::unique_ptr<std::ofstream> kernel_id_stream;
std::vector<std::pair<std::string, rt::KernelId>> kernel_map;
bool profiling_enabled = false;
} _drv;
static std::string ggml_et_get_default_et_path() {
const char * const env_vars[] = { "ET_TOOLCHAIN", "TOOLCHAIN_ROOT" };
for (const char * var : env_vars) {
if (const char * et_path = std::getenv(var)) {
if (et_path && *et_path != '\0') {
return fs::path(et_path).string();
}
}
}
return fs::path("/opt/et").string();
}
static inline auto ggml_et_get_default_sysemu_options() {
constexpr uint64_t kSysEmuMaxCycles = std::numeric_limits<uint64_t>::max();
constexpr uint64_t kSysEmuMinionShiresMask = 0x1FFFFFFFFu;
const std::string et_path = ggml_et_get_default_et_path() + "/";
emu::SysEmuOptions sysEmuOptions;
sysEmuOptions.bootromTrampolineToBL2ElfPath =
et_path + "lib/esperanto-fw/BootromTrampolineToBL2/BootromTrampolineToBL2.elf";
sysEmuOptions.spBL2ElfPath =
et_path + "lib/esperanto-fw/ServiceProcessorBL2/fast-boot/ServiceProcessorBL2_fast-boot.elf";
sysEmuOptions.machineMinionElfPath = et_path + "lib/esperanto-fw/MachineMinion/MachineMinion.elf";
sysEmuOptions.masterMinionElfPath = et_path + "lib/esperanto-fw/MasterMinion/MasterMinion.elf";
sysEmuOptions.workerMinionElfPath = et_path + "lib/esperanto-fw/WorkerMinion/WorkerMinion.elf";
sysEmuOptions.executablePath = et_path + "bin/sys_emu";
const std::vector<std::string> required_files = {
sysEmuOptions.bootromTrampolineToBL2ElfPath, sysEmuOptions.spBL2ElfPath,
sysEmuOptions.machineMinionElfPath, sysEmuOptions.masterMinionElfPath,
sysEmuOptions.workerMinionElfPath, sysEmuOptions.executablePath,
};
for (const auto & file : required_files) {
if (!fs::exists(file) || fs::file_size(file) == 0) {
GGML_LOG_ERROR("ET: Unable to find required sysemu file: %s", file.c_str());
GGML_LOG_ERROR("ET: Confirm et-platform is correctly installed at configured path.");
abort();
}
}
sysEmuOptions.runDir = (fs::current_path().string() + "/");
sysEmuOptions.maxCycles = kSysEmuMaxCycles;
sysEmuOptions.minionShiresMask = kSysEmuMinionShiresMask;
sysEmuOptions.puUart0Path = sysEmuOptions.runDir + "pu_uart0_tx.log";
sysEmuOptions.puUart1Path = sysEmuOptions.runDir + "pu_uart1_tx.log";
sysEmuOptions.spUart0Path = sysEmuOptions.runDir + "spio_uart0_tx.log";
sysEmuOptions.spUart1Path = sysEmuOptions.runDir + "spio_uart1_tx.log";
sysEmuOptions.startGdb = false;
sysEmuOptions.memcheck = false;
return sysEmuOptions;
}
static void ggml_et_driver_cleanup();
static bool ggml_et_driver_init() {
if (_drv.runtime != nullptr) {
assert(_drv.device_layer != nullptr);
} else {
try {
#if defined GGML_ET_SYSEMU && GGML_ET_SYSEMU
_drv.device_layer = dev::IDeviceLayer::createSysEmuDeviceLayer(ggml_et_get_default_sysemu_options());
#else
_drv.device_layer = dev::IDeviceLayer::createPcieDeviceLayer();
#endif
_drv.runtime = rt::IRuntime::create(_drv.device_layer);
const char * profile_path = getenv("GGML_ET_PROFILE");
if (profile_path) {
std::string output_path = std::string(profile_path) + "/et_runtime_trace.json";
std::string kernel_id_path = std::string(profile_path) + "/kernel_id.json";
_drv.profile_stream = std::make_unique<std::ofstream>(output_path);
_drv.kernel_id_stream = std::make_unique<std::ofstream>(kernel_id_path);
if (!_drv.profile_stream->is_open()) {
GGML_LOG_ERROR("ET: Failed to open profiling output file: %s", output_path.c_str());
abort();
}
if (!_drv.kernel_id_stream->is_open()) {
GGML_LOG_ERROR("ET: Failed to open profiling kernel map: %s", kernel_id_path.c_str());
abort();
}
auto * profiler = _drv.runtime->getProfiler();
profiler->start(*_drv.profile_stream, rt::IProfiler::OutputType::Json);
_drv.profiling_enabled = true;
GGML_LOG_INFO("ET: Runtime profiler started (JSON format)");
std::atexit(ggml_et_driver_cleanup);
}
} catch (const std::exception & e) {
GGML_LOG_ERROR("ggml_et: %s", e.what());
if (_drv.device_layer != nullptr) {
_drv.device_layer.reset();
}
if (_drv.runtime != nullptr) {
_drv.runtime.reset();
}
return false;
}
}
return true;
}
static std::shared_ptr<dev::IDeviceLayer> ggml_et_devicelayer() {
return _drv.device_layer;
}
std::shared_ptr<rt::IRuntime> ggml_et_runtime() {
return _drv.runtime;
}
static void ggml_et_driver_cleanup() {
if (_drv.profiling_enabled && _drv.runtime) {
GGML_LOG_INFO("ET: Stopping runtime profiler");
auto * profiler = _drv.runtime->getProfiler();
profiler->stop();
_drv.profiling_enabled = false;
if (_drv.profile_stream) {
_drv.profile_stream->close();
_drv.profile_stream.reset();
}
if (_drv.kernel_id_stream && !_drv.kernel_map.empty()) {
auto & os = *_drv.kernel_id_stream;
os << "{\n";
for (size_t i = 0; i < _drv.kernel_map.size(); i++) {
os << " \"" << _drv.kernel_map[i].first << "\": " << (int) _drv.kernel_map[i].second;
if (i + 1 < _drv.kernel_map.size()) {
os << ",";
}
os << "\n";
}
os << "}\n";
_drv.kernel_id_stream->close();
_drv.kernel_id_stream.reset();
}
}
}
static ggml_backend_dev_t ggml_backend_et_reg_get_device(ggml_backend_reg_t reg, size_t devidx);
static void ggml_backend_et_buffer_free_buffer(ggml_backend_buffer_t buffer) {
ggml_backend_et_buffer_context * ctx = (ggml_backend_et_buffer_context *) buffer->context;
if (ctx->data != nullptr) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (runtime) {
runtime->freeDevice(ctx->rtid, static_cast<std::byte *>(ctx->data));
}
}
delete ctx;
}
static void * ggml_backend_et_buffer_get_base(ggml_backend_buffer_t buffer) {
ggml_backend_et_buffer_context * ctx = (ggml_backend_et_buffer_context *) buffer->context;
return ctx->data;
}
static ggml_status ggml_backend_et_buffer_init_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor) {
if (tensor->view_src != NULL) {
return GGML_STATUS_SUCCESS;
}
const size_t original_size = ggml_nbytes(tensor);
const size_t padded_size = ggml_backend_buft_get_alloc_size(buffer->buft, tensor);
if (padded_size > original_size) {
const size_t padding_size = padded_size - original_size;
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) buffer->buft->device->context;
if (!dev_ctx) {
GGML_LOG_ERROR("ET: Failed to get device context for padding clear");
return GGML_STATUS_FAILED;
}
std::byte * padding_ptr = static_cast<std::byte *>(tensor->data) + original_size;
if (!ggml_et_memset(dev_ctx, padding_ptr, 0, padding_size)) {
GGML_LOG_ERROR("ET: Failed to clear padding using memset kernel for tensor %s", tensor->name);
return GGML_STATUS_FAILED;
}
}
return GGML_STATUS_SUCCESS;
}
static void ggml_backend_et_buffer_set_tensor(ggml_backend_buffer_t buffer,
ggml_tensor * tensor,
const void * data,
size_t offset,
size_t size) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime) {
return;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) buffer->buft->device->context;
rt::StreamId stream = dev_ctx->default_stream;
std::byte * dst_ptr = static_cast<std::byte *>(tensor->data) + offset;
const std::byte * src_ptr = static_cast<const std::byte *>(data);
rt::EventId event = runtime->memcpyHostToDevice(stream, src_ptr, dst_ptr, size, true );
runtime->waitForEvent(event);
}
static void ggml_backend_et_buffer_get_tensor(ggml_backend_buffer_t buffer,
const ggml_tensor * tensor,
void * data,
size_t offset,
size_t size) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime) {
return;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) buffer->buft->device->context;
rt::StreamId stream = dev_ctx->default_stream;
const std::byte * src_ptr = static_cast<const std::byte *>(tensor->data) + offset;
std::byte * dst_ptr = static_cast<std::byte *>(data);
rt::EventId event = runtime->memcpyDeviceToHost(stream, src_ptr, dst_ptr, size, true );
runtime->waitForEvent(event);
}
static bool ggml_backend_et_buffer_cpy_tensor(ggml_backend_buffer_t buffer,
const ggml_tensor * src,
ggml_tensor * dst) {
GGML_UNUSED(buffer);
GGML_UNUSED(src);
GGML_UNUSED(dst);
return false;
}
static void ggml_backend_et_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) {
ggml_backend_et_buffer_context * ctx = (ggml_backend_et_buffer_context *) buffer->context;
if (ctx->size == 0 || ctx->data == nullptr) {
return;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) buffer->buft->device->context;
if (!dev_ctx) {
GGML_LOG_ERROR("ET: Failed to get device context for buffer clear");
return;
}
if (!ggml_et_memset(dev_ctx, ctx->data, value, ctx->size)) {
GGML_LOG_ERROR("ET: buffer_clear failed using memset kernel");
return;
}
GGML_LOG_DEBUG("ET: Buffer cleared successfully using memops kernel");
}
static const struct ggml_backend_buffer_i ggml_backend_et_buffer_i = {
ggml_backend_et_buffer_free_buffer,
ggml_backend_et_buffer_get_base,
ggml_backend_et_buffer_init_tensor,
NULL,
ggml_backend_et_buffer_set_tensor,
ggml_backend_et_buffer_get_tensor,
NULL,
NULL,
ggml_backend_et_buffer_cpy_tensor,
ggml_backend_et_buffer_clear,
NULL,
};
static const char * ggml_backend_et_buffer_type_get_name(ggml_backend_buffer_type_t buft) {
GGML_UNUSED(buft);
return GGML_ET_NAME;
}
static ggml_backend_buffer_t ggml_backend_et_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) {
ggml_backend_et_buffer_type_context * btctx = (ggml_backend_et_buffer_type_context *) buft->context;
ggml_backend_et_buffer_context * ctx = new ggml_backend_et_buffer_context;
ctx->devidx = btctx->devidx;
ctx->size = size;
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime) {
delete ctx;
return nullptr;
}
std::vector<rt::DeviceId> rtids = runtime->getDevices();
if (static_cast<size_t>(btctx->devidx) >= rtids.size()) {
delete ctx;
return nullptr;
}
ctx->rtid = rtids[btctx->devidx];
ctx->data = runtime->mallocDevice(ctx->rtid, size);
if (ctx->data == nullptr) {
delete ctx;
return nullptr;
}
return ggml_backend_buffer_init(buft, ggml_backend_et_buffer_i, ctx, size);
}
static size_t ggml_backend_et_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime || !buft->device) {
return GGML_MEM_ALIGN;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) buft->device->context;
rt::DeviceProperties prop = runtime->getDeviceProperties(dev_ctx->rtid);
return prop.cacheLineSize_;
}
static size_t ggml_backend_et_buffer_type_get_max_size(ggml_backend_buffer_type_t buft) {
if (buft->device) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) buft->device->context;
return dev_ctx->total_mem;
}
return SIZE_MAX;
}
static size_t ggml_backend_et_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const ggml_tensor * tensor) {
GGML_UNUSED(buft);
return ggml_nbytes_pad(tensor);
}
static bool ggml_backend_et_buffer_type_is_host(ggml_backend_buffer_type_t buft) {
GGML_UNUSED(buft);
return false;
}
static const struct ggml_backend_buffer_type_i ggml_backend_et_buffer_type_i = {
ggml_backend_et_buffer_type_get_name,
ggml_backend_et_buffer_type_alloc_buffer,
NULL,
ggml_backend_et_buffer_type_get_alignment,
ggml_backend_et_buffer_type_get_max_size,
ggml_backend_et_buffer_type_get_alloc_size,
NULL,
ggml_backend_et_buffer_type_is_host,
};
static const char * ggml_backend_et_get_name(ggml_backend_t backend) {
GGML_UNUSED(backend);
return GGML_ET_NAME;
}
static void ggml_backend_et_free(ggml_backend_t backend) {
ggml_backend_et_context * et_ctx = (ggml_backend_et_context *) backend->context;
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
ggml_backend_dev_t dev = ggml_backend_et_reg_get_device(ggml_backend_et_reg(), et_ctx->devidx);
if (dev && dev->context) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
if (_drv.profiling_enabled) {
auto kernels = ggml_et_get_loaded_kernels(dev_ctx);
_drv.kernel_map.insert(_drv.kernel_map.end(), kernels.begin(), kernels.end());
}
ggml_et_unload_all_kernels(dev_ctx);
if (runtime) {
if (dev_ctx->trace_buffer) {
runtime->freeDevice(dev_ctx->rtid, dev_ctx->trace_buffer);
dev_ctx->trace_buffer = nullptr;
}
runtime->waitForStream(dev_ctx->default_stream);
for (auto & slot : dev_ctx->uberkernel.slots) {
if (slot.device_insts) {
runtime->freeDevice(dev_ctx->rtid, slot.device_insts);
slot.device_insts = nullptr;
}
if (slot.device_params) {
runtime->freeDevice(dev_ctx->rtid, slot.device_params);
slot.device_params = nullptr;
}
slot.has_pending = false;
}
}
}
delete et_ctx;
delete backend;
}
static ggml_backend_buffer_type_t ggml_backend_et_get_default_buffer_type(ggml_backend_t backend) {
ggml_backend_et_context * et_ctx = (ggml_backend_et_context *) backend->context;
return ggml_backend_et_buffer_type(et_ctx->devidx);
}
static void ggml_backend_et_set_tensor_async(ggml_backend_t backend,
ggml_tensor * tensor,
const void * data,
size_t offset,
size_t size) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime) {
return;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) backend->device->context;
rt::StreamId stream = dev_ctx->default_stream;
std::byte * dst_ptr = static_cast<std::byte *>(tensor->data) + offset;
const std::byte * src_ptr = static_cast<const std::byte *>(data);
runtime->memcpyHostToDevice(stream, src_ptr, dst_ptr, size, true );
}
static void ggml_backend_et_get_tensor_async(ggml_backend_t backend,
const ggml_tensor * tensor,
void * data,
size_t offset,
size_t size) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime) {
return;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) backend->device->context;
rt::StreamId stream = dev_ctx->default_stream;
const std::byte * src_ptr = static_cast<const std::byte *>(tensor->data) + offset;
std::byte * dst_ptr = static_cast<std::byte *>(data);
runtime->memcpyDeviceToHost(stream, src_ptr, dst_ptr, size, true );
}
static bool ggml_backend_et_cpy_tensor_async(ggml_backend_t backend_src,
ggml_backend_t backend_dst,
const ggml_tensor * src,
ggml_tensor * dst) {
GGML_UNUSED(backend_src);
GGML_UNUSED(backend_dst);
GGML_UNUSED(src);
GGML_UNUSED(dst);
return false;
}
static void ggml_backend_et_synchronize(ggml_backend_t backend) {
std::shared_ptr<rt::IRuntime> runtime = ggml_et_runtime();
if (!runtime) {
return;
}
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) backend->device->context;
runtime->waitForStream(dev_ctx->default_stream);
auto errors = runtime->retrieveStreamErrors(dev_ctx->default_stream);
if (errors.empty()) {
return;
}
for (const auto & err : errors) {
GGML_LOG_ERROR("ET: stream error detected at synchronization point. Code: %d,Type: %d\n", (int) err.errorCode_,
(int) err.errorContext_.value()[0].type_);
}
abort();
}
static bool ggml_et_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializer_list<ggml_op> ops) {
if (!ggml_can_fuse(cgraph, node_idx, ops)) {
return false;
}
if (ops.size() == 2 && ops.begin()[0] == GGML_OP_MUL_MAT && ops.begin()[1] == GGML_OP_ADD) {
const ggml_tensor * mm = cgraph->nodes[node_idx];
const ggml_tensor * add = cgraph->nodes[node_idx + 1];
if (mm->type != GGML_TYPE_F32 || mm->src[0]->type != GGML_TYPE_Q8_0 || mm->src[1]->type != GGML_TYPE_F32) {
return false;
}
if (add->type != GGML_TYPE_F32) {
return false;
}
if (add->src[0] != mm && add->src[1] != mm) {
return false;
}
const ggml_tensor * bias = (add->src[0] == mm) ? add->src[1] : add->src[0];
if (bias->type != GGML_TYPE_F32) {
return false;
}
for (int i = 0; i < GGML_MAX_DIMS; ++i) {
if (bias->ne[i] != mm->ne[i]) {
return false;
}
}
if (!ggml_is_contiguous(bias) || !ggml_is_contiguous(mm)) {
return false;
}
for (int i = 0; i < GGML_MAX_DIMS; ++i) {
if ((int64_t) bias->nb[i] != (int64_t) add->nb[i]) {
return false;
}
}
}
if (ops.size() == 2 && ops.begin()[0] == GGML_OP_RMS_NORM && ops.begin()[1] == GGML_OP_MUL) {
const ggml_tensor * rms_norm = cgraph->nodes[node_idx];
const ggml_tensor * mul = cgraph->nodes[node_idx + 1];
if (rms_norm->src[0]->type != GGML_TYPE_F32 || mul->type != GGML_TYPE_F32) {
return false;
}
const ggml_tensor * weights = (mul->src[0] == rms_norm) ? mul->src[1] : mul->src[0];
if (weights->type != GGML_TYPE_F32) {
return false;
}
if (!ggml_is_contiguous(rms_norm->src[0]) || !ggml_is_contiguous_rows(weights)) {
return false;
}
if (rms_norm->src[0]->ne[0] % 16 != 0 || weights->ne[0] % 16 != 0) {
return false;
}
if (weights->ne[0] != rms_norm->src[0]->ne[0]) {
return false;
}
}
return true;
}
static ggml_status ggml_backend_et_graph_compute(ggml_backend_t backend, ggml_cgraph * cgraph) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) backend->device->context;
ggml_et_uberkernel_begin_graph(&dev_ctx->uberkernel);
for (int i = 0; i < cgraph->n_nodes; i++) {
ggml_tensor * node = cgraph->nodes[i];
if (node->op == GGML_OP_NONE || node->op == GGML_OP_VIEW || node->op == GGML_OP_RESHAPE ||
node->op == GGML_OP_PERMUTE || node->op == GGML_OP_TRANSPOSE) {
continue;
}
if (ggml_et_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) {
ggml_et_op_rms_norm_mul(dev_ctx, node, cgraph->nodes[i + 1]);
i++; continue;
}
if (ggml_et_can_fuse(cgraph, i, { GGML_OP_MUL_MAT, GGML_OP_ADD })) {
ggml_et_op_mul_mat(dev_ctx, node, cgraph->nodes[i + 1]);
i++; continue;
}
switch (node->op) {
case GGML_OP_SQR:
ggml_et_op_sqr(dev_ctx, node);
break;
case GGML_OP_UNARY:
ggml_et_op_unary(dev_ctx, node);
break;
case GGML_OP_SUM_ROWS:
ggml_et_op_sum_rows(dev_ctx, node);
break;
case GGML_OP_MEAN:
ggml_et_op_mean(dev_ctx, node);
break;
case GGML_OP_CLAMP:
ggml_et_op_clamp(dev_ctx, node);
break;
case GGML_OP_MUL:
ggml_et_op_mul(dev_ctx, node);
break;
case GGML_OP_ADD:
ggml_et_op_add(dev_ctx, node);
break;
case GGML_OP_SUB:
ggml_et_op_sub(dev_ctx, node);
break;
case GGML_OP_CUMSUM:
ggml_et_op_cumsum(dev_ctx, node);
break;
case GGML_OP_MUL_MAT:
ggml_et_op_mul_mat(dev_ctx, node);
break;
case GGML_OP_MUL_MAT_ID:
ggml_et_op_mul_mat_id(dev_ctx, node);
break;
case GGML_OP_ROPE:
ggml_et_op_rope(dev_ctx, node);
break;
case GGML_OP_RMS_NORM:
ggml_et_op_rms_norm(dev_ctx, node);
break;
case GGML_OP_NORM:
ggml_et_op_norm(dev_ctx, node);
break;
case GGML_OP_L2_NORM:
ggml_et_op_l2_norm(dev_ctx, node);
break;
case GGML_OP_GROUP_NORM:
ggml_et_op_group_norm(dev_ctx, node);
break;
case GGML_OP_SCALE:
ggml_et_op_scale(dev_ctx, node);
break;
case GGML_OP_GLU:
ggml_et_op_glu(dev_ctx, node);
break;
case GGML_OP_SOFT_MAX:
ggml_et_op_softmax(dev_ctx, node);
break;
case GGML_OP_IM2COL:
ggml_et_op_im2col(dev_ctx, node);
break;
case GGML_OP_CONV_2D:
ggml_et_op_conv_2d(dev_ctx, node);
break;
case GGML_OP_FLASH_ATTN_EXT:
ggml_et_op_flash_attn_ext(dev_ctx, node);
break;
case GGML_OP_GET_ROWS:
ggml_et_op_get_rows(dev_ctx, node);
break;
case GGML_OP_CONT:
ggml_et_op_cont(dev_ctx, node);
break;
case GGML_OP_CPY:
ggml_et_op_cpy(dev_ctx, node);
break;
case GGML_OP_CONCAT:
ggml_et_op_concat(dev_ctx, node);
break;
case GGML_OP_REPEAT:
ggml_et_op_repeat(dev_ctx, node);
break;
case GGML_OP_SSM_CONV:
ggml_et_op_ssm_conv(dev_ctx, node);
break;
case GGML_OP_SSM_SCAN:
ggml_et_op_ssm_scan(dev_ctx, node);
break;
case GGML_OP_PAD:
ggml_et_op_pad(dev_ctx, node);
break;
case GGML_OP_SET_ROWS:
ggml_et_op_set_rows(dev_ctx, node);
break;
case GGML_OP_FILL:
ggml_et_op_fill(dev_ctx, node);
break;
case GGML_OP_DIAG:
ggml_et_op_diag(dev_ctx, node);
break;
case GGML_OP_TRI:
ggml_et_op_tri(dev_ctx, node);
break;
case GGML_OP_SOLVE_TRI:
ggml_et_op_solve_tri(dev_ctx, node);
break;
case GGML_OP_SET:
ggml_et_op_set(dev_ctx, node);
break;
case GGML_OP_RWKV_WKV6:
ggml_et_op_rwkv_wkv6(dev_ctx, node);
break;
case GGML_OP_RWKV_WKV7:
ggml_et_op_rwkv_wkv7(dev_ctx, node);
break;
case GGML_OP_GATED_DELTA_NET:
ggml_et_op_gated_delta_net(dev_ctx, node);
break;
default:
ggml_et_uberkernel_abort_graph(&dev_ctx->uberkernel);
GGML_LOG_ERROR("ET: Unsupported operation in graph: %s", ggml_op_name(node->op));
return GGML_STATUS_FAILED;
}
if (ggml_et_uberkernel_failed(&dev_ctx->uberkernel)) {
ggml_et_uberkernel_abort_graph(&dev_ctx->uberkernel);
return GGML_STATUS_FAILED;
}
}
if (!ggml_et_uberkernel_end_graph(dev_ctx)) {
ggml_et_uberkernel_abort_graph(&dev_ctx->uberkernel);
return GGML_STATUS_FAILED;
}
return GGML_STATUS_SUCCESS;
}
static bool et_ggml_is_row_contiguous(const ggml_tensor * t) {
return t->nb[0] == ggml_type_size(t->type);
}
static bool ggml_backend_et_device_supports_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
GGML_UNUSED(dev);
bool supported = false;
switch (op->op) {
case GGML_OP_CUMSUM:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->src[0]->nb[0] == sizeof(float) && ggml_is_contiguous(op);
break;
case GGML_OP_SQR:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->ne[0] % 16 == 0 && ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]);
break;
case GGML_OP_SUM_ROWS:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->src[0]->ne[0] % 16 == 0 && ggml_is_contiguous(op->src[0]);
break;
case GGML_OP_MEAN:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op->src[0]);
break;
case GGML_OP_CLAMP:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]);
break;
case GGML_OP_UNARY:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_nelements(op) % 16 == 0 && op->nb[0] == sizeof(float) && op->src[0]->nb[0] == sizeof(float)) {
switch (ggml_get_unary_op(op)) {
case GGML_UNARY_OP_ABS:
case GGML_UNARY_OP_SGN:
case GGML_UNARY_OP_NEG:
case GGML_UNARY_OP_STEP:
case GGML_UNARY_OP_TANH:
case GGML_UNARY_OP_ELU:
case GGML_UNARY_OP_RELU:
case GGML_UNARY_OP_SIGMOID:
case GGML_UNARY_OP_GELU:
case GGML_UNARY_OP_GELU_QUICK:
case GGML_UNARY_OP_SILU:
case GGML_UNARY_OP_HARDSWISH:
case GGML_UNARY_OP_HARDSIGMOID:
case GGML_UNARY_OP_EXP:
case GGML_UNARY_OP_EXPM1:
case GGML_UNARY_OP_SOFTPLUS:
case GGML_UNARY_OP_GELU_ERF:
case GGML_UNARY_OP_FLOOR:
case GGML_UNARY_OP_CEIL:
case GGML_UNARY_OP_ROUND:
case GGML_UNARY_OP_TRUNC:
supported = true;
break;
default:
break;
}
}
break;
case GGML_OP_MUL:
case GGML_OP_ADD:
case GGML_OP_SUB:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && op->nb[0] == sizeof(float) &&
op->src[0]->nb[0] == sizeof(float) &&
(op->src[1]->nb[0] == sizeof(float) || op->src[1]->ne[0] == 1) &&
op->nb[1] == op->ne[0] * sizeof(float);
break;
case GGML_OP_MUL_MAT:
if (op->type == GGML_TYPE_F32 &&
((op->src[0]->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32) ||
(op->src[0]->type == GGML_TYPE_F16 && op->src[1]->type == GGML_TYPE_F16)) &&
op->ne[0] % 16 == 0 && op->src[0]->ne[1] % 16 == 0 && op->src[0]->ne[0] % 16 == 0 && ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1])) {
supported = true;
} else if (op->type == GGML_TYPE_F32 && op->src[0] &&
(op->src[0]->type == GGML_TYPE_F16 || op->src[0]->type == GGML_TYPE_F32) && op->src[1] &&
(op->src[1]->type == GGML_TYPE_F16 || op->src[1]->type == GGML_TYPE_F32)) {
bool src0_first_dim_contiguous = (op->src[0]->nb[0] == ggml_type_size(op->src[0]->type));
bool src1_first_dim_contiguous = (op->src[1]->nb[0] == ggml_type_size(op->src[1]->type));
bool dst_first_dim_contiguous = (op->nb[0] == sizeof(float));
bool dst_properly_ordered = true;
for (int d = 0; d < 3; d++) {
if (op->ne[d] > 1 && op->ne[d + 1] > 1 && op->nb[d] > op->nb[d + 1]) {
dst_properly_ordered = false;
}
}
supported = src0_first_dim_contiguous && src1_first_dim_contiguous && dst_first_dim_contiguous &&
dst_properly_ordered;
} else if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_Q8_0 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32) {
bool src0_first_dim_contiguous = (op->src[0]->nb[0] == ggml_type_size(op->src[0]->type));
bool src1_first_dim_contiguous = (op->src[1]->nb[0] == ggml_type_size(op->src[1]->type));
bool dst_first_dim_contiguous = (op->nb[0] == sizeof(float));
bool dst_properly_ordered = true;
for (int d = 0; d < 3; d++) {
if (op->ne[d] > 1 && op->ne[d + 1] > 1 && op->nb[d] > op->nb[d + 1]) {
dst_properly_ordered = false;
}
}
supported = src0_first_dim_contiguous && src1_first_dim_contiguous && dst_first_dim_contiguous &&
dst_properly_ordered;
} else if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_Q4_0 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32) {
bool src0_first_dim_contiguous = (op->src[0]->nb[0] == ggml_type_size(op->src[0]->type));
bool src1_first_dim_contiguous = (op->src[1]->nb[0] == ggml_type_size(op->src[1]->type));
bool dst_first_dim_contiguous = (op->nb[0] == sizeof(float));
bool dst_properly_ordered = true;
for (int d = 0; d < 3; d++) {
if (op->ne[d] > 1 && op->ne[d + 1] > 1 && op->nb[d] > op->nb[d + 1]) {
dst_properly_ordered = false;
}
}
supported = src0_first_dim_contiguous && src1_first_dim_contiguous && dst_first_dim_contiguous &&
dst_properly_ordered;
} else {
supported = false;
}
break;
case GGML_OP_MUL_MAT_ID:
if (op->type == GGML_TYPE_F32 && op->src[0] &&
(op->src[0]->type == GGML_TYPE_Q8_0 || op->src[0]->type == GGML_TYPE_Q4_0 ||
op->src[0]->type == GGML_TYPE_F16 || op->src[0]->type == GGML_TYPE_F32) &&
op->src[1] && op->src[1]->type == GGML_TYPE_F32 && op->src[2] && op->src[2]->type == GGML_TYPE_I32) {
bool src0_first_dim_contiguous = (op->src[0]->nb[0] == ggml_type_size(op->src[0]->type));
bool src1_first_dim_contiguous = (op->src[1]->nb[0] == ggml_type_size(op->src[1]->type));
bool src2_first_dim_contiguous = (op->src[2]->nb[0] == ggml_type_size(op->src[2]->type));
bool dst_first_dim_contiguous = (op->nb[0] == sizeof(float));
bool dst_properly_ordered = true;
for (int d = 0; d < 3; d++) {
if (op->ne[d] > 1 && op->ne[d + 1] > 1 && op->nb[d] > op->nb[d + 1]) {
dst_properly_ordered = false;
}
}
bool dims_valid = (op->src[0]->ne[3] == 1) && (op->src[1]->ne[3] == 1) && (op->src[2]->ne[2] == 1 && op->src[2]->ne[3] == 1) && (op->src[2]->ne[1] == op->src[1]->ne[2]) && (op->src[0]->ne[0] == op->src[1]->ne[0]) && (op->src[2]->ne[0] % op->src[1]->ne[1] == 0);
supported = src0_first_dim_contiguous && src1_first_dim_contiguous && src2_first_dim_contiguous &&
dst_first_dim_contiguous && dst_properly_ordered && dims_valid;
} else {
supported = false;
}
break;
case GGML_OP_ROPE:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_I32 && ggml_is_contiguous(op) && et_ggml_is_row_contiguous(op->src[0])) {
const int mode = ggml_get_op_params_i32(op, 2);
const int ndims = ggml_get_op_params_i32(op, 1);
const bool is_normal = mode == GGML_ROPE_TYPE_NORMAL;
const bool is_neox = mode == GGML_ROPE_TYPE_NEOX;
const bool is_imrope = mode == GGML_ROPE_TYPE_IMROPE;
const bool zero_view_offset = op->src[0]->view_src == nullptr || op->src[0]->view_offs == 0;
const bool has_sections = ggml_get_op_params_i32(op, 11) > 0 || ggml_get_op_params_i32(op, 12) > 0 ||
ggml_get_op_params_i32(op, 13) > 0;
const bool zero_rot_offset = ggml_get_op_params_i32(op, 15) == 0;
supported =
zero_view_offset && zero_rot_offset && ndims <= 512 &&
(is_normal || (is_neox && ndims % 16 == 0) || (is_imrope && ndims % 16 == 0 && has_sections));
} else {
supported = false;
}
break;
case GGML_OP_RMS_NORM:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->ne[0] % 16 == 0 && ggml_is_contiguous(op) && et_ggml_is_row_contiguous(op->src[0]);
break;
case GGML_OP_NORM:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->ne[0] % 16 == 0 && ggml_is_contiguous(op) && et_ggml_is_row_contiguous(op->src[0]);
break;
case GGML_OP_L2_NORM:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->ne[0] % 16 == 0 && ggml_is_contiguous(op) && et_ggml_is_row_contiguous(op->src[0]);
break;
case GGML_OP_GROUP_NORM:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && et_ggml_is_row_contiguous(op->src[0]) &&
ggml_get_op_params_i32(op, 0) > 0;
break;
case GGML_OP_IM2COL:
supported = op->src[0] && op->src[1] &&
((op->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32) ||
(op->type == GGML_TYPE_F16 &&
(op->src[1]->type == GGML_TYPE_F16 || op->src[1]->type == GGML_TYPE_F32))) &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[1]) &&
op->nb[0] == ggml_type_size(op->type) && op->src[1]->nb[0] == ggml_type_size(op->src[1]->type);
break;
case GGML_OP_CONV_2D:
{
if (!op->src[0] || !op->src[1]) {
supported = false;
break;
}
if (op->type != GGML_TYPE_F32 || op->src[0]->type != GGML_TYPE_F32 ||
op->src[1]->type != GGML_TYPE_F32) {
supported = false;
break;
}
if (!ggml_is_contiguous(op) || !ggml_is_contiguous(op->src[0]) || !ggml_is_contiguous(op->src[1])) {
supported = false;
break;
}
const ggml_tensor * flt = op->src[0]; const ggml_tensor * in = op->src[1]; const int32_t s0 = ggml_get_op_params_i32(op, 0);
const int32_t s1 = ggml_get_op_params_i32(op, 1);
const int32_t p0 = ggml_get_op_params_i32(op, 2);
const int32_t p1 = ggml_get_op_params_i32(op, 3);
const int32_t d0 = ggml_get_op_params_i32(op, 4);
const int32_t d1 = ggml_get_op_params_i32(op, 5);
const int64_t Kw = flt->ne[0];
const int64_t Kh = flt->ne[1];
const int64_t Cin = flt->ne[2];
const int64_t Cout = flt->ne[3];
const int64_t H = in->ne[1];
(void) in->ne[0];
if (s0 < 1 || s1 < 1 || !(d0 == 1 && d1 == 1) || Cin % 16 != 0 || Cout % 16 != 0 || in->ne[3] != 1) {
supported = false;
break;
}
const int64_t OW = op->ne[0];
const int64_t OH = op->ne[1];
if (OW <= 0 || OH <= 0) {
supported = false;
break;
}
(void) p0;
(void) p1;
const int64_t Hp = H + 2 * p1;
const int64_t OW_pad = (OW + 15) & ~15;
const int64_t Wp_a = OW_pad;
const bool need_stage = (OW % 16 != 0);
const int64_t stage_bytes = need_stage ? (Cout * OH * OW_pad * 4) : 0;
const int64_t L2SCP_BUDGET = 1500 * 1024;
const int64_t scratch_bytes = 32 * 2 * 16 * 16 * 4;
const int64_t budget = L2SCP_BUDGET - stage_bytes - scratch_bytes;
const int64_t per_KT_bytes = Kh * Kw * Cout * 16 * 4 + Kw * 16 * Hp * Wp_a * 4;
const int64_t K_TILES = Cin / 16;
int64_t chunk_KT_calc;
int64_t n_chunks_calc;
if (K_TILES * per_KT_bytes <= budget) {
chunk_KT_calc = K_TILES;
n_chunks_calc = 1;
} else {
chunk_KT_calc = K_TILES;
while (chunk_KT_calc > 1 && 2 * chunk_KT_calc * per_KT_bytes > budget) {
chunk_KT_calc--;
}
while (chunk_KT_calc > 1 && K_TILES % chunk_KT_calc != 0) {
chunk_KT_calc--;
}
if (chunk_KT_calc < 1) {
supported = false;
break;
}
n_chunks_calc = K_TILES / chunk_KT_calc;
}
if (n_chunks_calc > 1) {
const int64_t M_TILES = Cout / 16;
const int64_t w_tiles = (OW + 15) / 16;
const int64_t total_tiles = OH * w_tiles * M_TILES;
const int64_t max_workers = (need_stage ? 32 : 1024) * 2;
if (total_tiles > max_workers) {
supported = false;
break;
}
}
supported = true;
break;
}
case GGML_OP_SCALE:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]) && (ggml_nelements(op) % 16 == 0);
break;
case GGML_OP_GLU:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_nelements(op) % 16 == 0 && ggml_is_contiguous_1(op) && ggml_is_contiguous_1(op->src[0])) {
ggml_glu_op glu_type = ggml_get_glu_op(op);
const bool supported_variant = glu_type == GGML_GLU_OP_SWIGLU || glu_type == GGML_GLU_OP_SWIGLU_OAI ||
glu_type == GGML_GLU_OP_SWIGLU_CLAMP || glu_type == GGML_GLU_OP_GEGLU ||
glu_type == GGML_GLU_OP_GEGLU_ERF ||
glu_type == GGML_GLU_OP_GEGLU_QUICK || glu_type == GGML_GLU_OP_REGLU;
if (op->src[1]) {
supported = supported_variant && op->src[1]->type == GGML_TYPE_F32 &&
ggml_is_contiguous_1(op->src[1]) && op->src[0]->ne[0] == op->ne[0] &&
op->src[1]->ne[0] == op->ne[0];
} else {
supported = supported_variant && op->src[0]->ne[0] == 2 * op->ne[0];
}
} else {
supported = false;
}
break;
case GGML_OP_SOFT_MAX:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]) && op->src[0]->ne[0] > 1) {
if (op->src[1]) {
supported = op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[1]);
if (!supported) {
break;
}
}
if (op->src[2]) {
supported = op->src[2]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[2]);
} else {
supported = true;
}
} else {
supported = false;
}
break;
case GGML_OP_SSM_SCAN:
supported = op->type == GGML_TYPE_F32 && ggml_is_contiguous(op) && op->src[0] &&
op->src[0]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[0]) && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && op->src[2] && op->src[2]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op->src[2]) && op->src[3] && op->src[3]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op->src[3]) && op->src[4] && op->src[4]->type == GGML_TYPE_F32 &&
op->src[5] && op->src[5]->type == GGML_TYPE_F32 && op->src[6] &&
op->src[6]->type == GGML_TYPE_I32 && ggml_is_contiguous(op->src[6]) &&
op->src[1]->nb[0] == sizeof(float) && op->src[4]->nb[0] == sizeof(float) &&
op->src[5]->nb[0] == sizeof(float) &&
op->src[1]->nb[1] == (size_t) op->src[1]->ne[0] * sizeof(float) &&
op->src[4]->nb[1] == (size_t) op->src[4]->ne[0] * sizeof(float) &&
op->src[5]->nb[1] == (size_t) op->src[5]->ne[0] * sizeof(float) &&
op->src[0]->ne[0] == op->src[4]->ne[0] && op->src[0]->ne[1] == op->src[1]->ne[0] &&
op->src[0]->ne[2] == op->src[1]->ne[1] && op->src[1]->ne[2] == op->src[2]->ne[1] &&
op->src[1]->ne[3] == op->src[2]->ne[2] && op->src[4]->ne[2] == op->src[1]->ne[2] &&
op->src[4]->ne[3] == op->src[1]->ne[3] && ggml_are_same_shape(op->src[4], op->src[5]) &&
op->src[6]->ne[0] == op->src[1]->ne[3] && op->src[3]->ne[1] == op->src[1]->ne[1] &&
(op->src[3]->ne[0] == 1 || op->src[3]->ne[0] == op->src[0]->ne[0]) &&
(op->src[1]->ne[1] % op->src[4]->ne[1] == 0);
break;
case GGML_OP_FLASH_ATTN_EXT:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
(op->src[1]->type == GGML_TYPE_F32 || op->src[1]->type == GGML_TYPE_F16) && op->src[2] &&
(op->src[2]->type == GGML_TYPE_F32 || op->src[2]->type == GGML_TYPE_F16) && op->src[4] == nullptr &&
ggml_is_contiguous_rows(op) && ggml_is_contiguous_rows(op->src[0])) {
float max_bias = 0.0f;
float logit_softcap = 0.0f;
memcpy(&max_bias, (const float *) op->op_params + 1, sizeof(max_bias));
memcpy(&logit_softcap, (const float *) op->op_params + 2, sizeof(logit_softcap));
const ggml_prec prec = ggml_flash_attn_ext_get_prec(op);
bool mask_ok = (op->src[3] == nullptr) || (op->src[3]->type == GGML_TYPE_F32) ||
(op->src[3]->type == GGML_TYPE_F16);
const int64_t nhq = op->src[0]->ne[2];
const int64_t nhk = op->src[1]->ne[2];
const size_t k_elem = op->src[1]->type == GGML_TYPE_F16 ? 2 : 4;
const size_t v_elem = op->src[2]->type == GGML_TYPE_F16 ? 2 : 4;
const bool me_eligible = op->src[1]->type == GGML_TYPE_F16 && op->src[2]->type == GGML_TYPE_F16 &&
(op->src[0]->ne[0] % 32) == 0;
supported = me_eligible && mask_ok && (prec == GGML_PREC_F32 || prec == GGML_PREC_DEFAULT) &&
max_bias == 0.0f && logit_softcap == 0.0f && op->src[0]->nb[0] == sizeof(float) &&
op->src[1]->nb[0] == k_elem && op->src[2]->nb[0] == v_elem && op->nb[0] == sizeof(float) &&
op->src[0]->ne[0] == op->src[1]->ne[0] && op->src[2]->ne[0] == op->ne[0] && op->src[2]->ne[0] <= 512 && op->src[0]->ne[0] <= 512 && nhq % nhk == 0 && op->src[0]->ne[1] == op->ne[2] && op->src[0]->ne[2] == op->ne[1] &&
op->src[0]->ne[3] == op->ne[3] && op->src[1]->ne[1] == op->src[2]->ne[1] &&
op->src[1]->ne[2] == op->src[2]->ne[2] && op->src[1]->ne[3] == op->src[2]->ne[3] &&
op->src[0]->ne[3] == op->src[1]->ne[3];
} else {
supported = false;
}
break;
case GGML_OP_GET_ROWS:
if (op->type == GGML_TYPE_F32 && op->src[0] &&
(op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16 ||
op->src[0]->type == GGML_TYPE_Q4_0 || op->src[0]->type == GGML_TYPE_Q8_0 ||
op->src[0]->type == GGML_TYPE_Q4_K) &&
op->src[1] && op->src[1]->type == GGML_TYPE_I32 && ggml_is_contiguous(op) &&
ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1])) {
supported = (op->src[0]->ne[2] == op->src[1]->ne[1]) && (op->src[1]->ne[3] == 1);
} else {
supported = false;
}
break;
case GGML_OP_CONT:
if ((op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16) && op->src[0] &&
op->src[0]->type == op->type && ggml_is_contiguous(op)) {
if (op->data && op->src[0]->data && op->data == op->src[0]->data) {
GGML_LOG_WARN("ET: CONT operation detected aliased tensors (dst == src0), unsupported");
supported = false;
} else {
supported = true;
}
} else {
supported = false;
}
break;
case GGML_OP_CPY:
if (op->src[0]) {
const int64_t nelements = op->ne[0] * op->ne[1] * op->ne[2] * op->ne[3];
if (nelements == 0) {
supported = true;
} else if ((op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16) && op->src[0]->type == op->type &&
ggml_is_contiguous(op)) {
if (op->data && op->src[0]->data && op->data == op->src[0]->data) {
GGML_LOG_WARN("ET: CPY operation detected aliased tensors, unsupported");
supported = false;
} else {
supported = true;
}
} else if (op->type == GGML_TYPE_F16 && op->src[0]->type == GGML_TYPE_F32 && ggml_is_contiguous(op)) {
supported = true;
} else {
supported = false;
}
} else {
supported = false;
}
break;
case GGML_OP_CONCAT:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous(op)) {
const int32_t dim = ((const int32_t *) op->op_params)[0];
if (dim == 0 && op->src[0]->ne[0] % 16 == 0 && op->src[1]->ne[0] % 16 == 0 &&
ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1])) {
supported = true;
} else if (dim == 0 && ((op->src[0]->nb[0] % sizeof(float) == 0) || op->src[0]->ne[0] == 1) &&
((op->src[1]->nb[0] % sizeof(float) == 0) || op->src[1]->ne[0] == 1)) {
supported = true;
} else if (op->ne[0] % 16 == 0 && op->src[0]->ne[0] % 16 == 0 && op->src[1]->ne[0] % 16 == 0 &&
ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1])) {
supported = true;
}
}
break;
case GGML_OP_SSM_CONV:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && op->src[0]->nb[0] == sizeof(float) &&
op->src[1]->nb[0] == sizeof(float) && op->src[0]->nb[1] == op->src[0]->ne[0] * sizeof(float) &&
op->src[1]->nb[1] == op->src[1]->ne[0] * sizeof(float) && ggml_is_contiguous(op) &&
op->src[1]->ne[1] == op->src[0]->ne[1] && op->ne[0] == op->src[0]->ne[1] &&
op->ne[1] == op->src[0]->ne[0] - op->src[1]->ne[0] + 1 && op->ne[2] == op->src[0]->ne[2];
break;
case GGML_OP_PAD:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && (op->ne[0] % 16 == 0 || 16 % op->ne[0] == 0) &&
op->src[0]->nb[0] == sizeof(float)) {
const int32_t lp0 = ((const int32_t *) op->op_params)[0];
const int32_t rp0 = ((const int32_t *) op->op_params)[1];
const bool circular = (bool) ((const int32_t *) op->op_params)[8];
if (lp0 == 0 && rp0 == 0 && !circular) {
supported = true;
} else {
supported = false;
}
} else {
supported = false;
}
break;
case GGML_OP_REPEAT:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]) && ggml_are_same_shape(op->src[0], op)) {
supported = true;
} else if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
(op->src[0]->ne[0] == 1 || op->src[0]->ne[0] % 16 == 0) && op->ne[0] % 16 == 0 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]) && op->ne[0] % op->src[0]->ne[0] == 0 &&
op->ne[1] % op->src[0]->ne[1] == 0 && op->ne[2] % op->src[0]->ne[2] == 0 &&
op->ne[3] % op->src[0]->ne[3] == 0) {
supported = true;
} else {
supported = false;
}
break;
case GGML_OP_FILL:
supported = op->type == GGML_TYPE_F32 && ggml_is_contiguous(op) && op->ne[0] % 16 == 0;
break;
case GGML_OP_DIAG:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
op->ne[0] % 16 == 0 && op->ne[0] == op->ne[1] && op->src[0]->ne[0] == op->ne[0] &&
op->src[0]->ne[1] == 1 && ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]);
break;
case GGML_OP_TRI:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]);
break;
case GGML_OP_SOLVE_TRI:
supported = op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && op->src[0]->ne[0] == op->src[0]->ne[1] &&
op->src[0]->ne[1] == op->src[1]->ne[1] && op->src[1]->ne[0] % 16 == 0 &&
ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]);
break;
case GGML_OP_SET:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous(op) && ggml_is_contiguous(op->src[0]) &&
ggml_is_contiguous(op->src[1]) && ggml_are_same_shape(op, op->src[0]) && op->src[1]->ne[0] % 16 == 0) {
const bool inplace = (bool) ((const int32_t *) op->op_params)[4];
const size_t nb1 = ((const int32_t *) op->op_params)[0];
const size_t nb2 = ((const int32_t *) op->op_params)[1];
const size_t nb3 = ((const int32_t *) op->op_params)[2];
const size_t offset = ((const int32_t *) op->op_params)[3];
const size_t nb0 = ggml_element_size(op);
const size_t im0 = op->src[1]->ne[0] == 0 ? 0 : op->src[1]->ne[0] - 1;
const size_t im1 = op->src[1]->ne[1] == 0 ? 0 : op->src[1]->ne[1] - 1;
const size_t im2 = op->src[1]->ne[2] == 0 ? 0 : op->src[1]->ne[2] - 1;
const size_t im3 = op->src[1]->ne[3] == 0 ? 0 : op->src[1]->ne[3] - 1;
const bool view_bounds_ok = offset + im0 * nb0 + im1 * nb1 + im2 * nb2 + im3 * nb3 <= ggml_nbytes(op);
const bool cacheline_aligned =
(nb1 % 64 == 0) && (nb2 % 64 == 0) && (nb3 % 64 == 0) && (offset % 64 == 0);
supported = inplace && view_bounds_ok && cacheline_aligned;
}
break;
case GGML_OP_RWKV_WKV6:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && op->src[2] && op->src[2]->type == GGML_TYPE_F32 && op->src[3] &&
op->src[3]->type == GGML_TYPE_F32 && op->src[4] && op->src[4]->type == GGML_TYPE_F32 && op->src[5] &&
op->src[5]->type == GGML_TYPE_F32 && op->src[0]->ne[0] % 8 == 0 && ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]) && ggml_is_contiguous(op->src[2]) &&
ggml_is_contiguous(op->src[3]) && ggml_is_contiguous(op->src[4]) && ggml_is_contiguous(op->src[5])) {
supported = true;
} else {
supported = false;
}
break;
case GGML_OP_RWKV_WKV7:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] &&
op->src[1]->type == GGML_TYPE_F32 && op->src[2] && op->src[2]->type == GGML_TYPE_F32 && op->src[3] &&
op->src[3]->type == GGML_TYPE_F32 && op->src[4] && op->src[4]->type == GGML_TYPE_F32 && op->src[5] &&
op->src[5]->type == GGML_TYPE_F32 && op->src[6] && op->src[6]->type == GGML_TYPE_F32 &&
op->src[2]->ne[0] % 8 == 0 && ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]) && ggml_is_contiguous(op->src[2]) &&
ggml_is_contiguous(op->src[3]) && ggml_is_contiguous(op->src[4]) && ggml_is_contiguous(op->src[5]) &&
ggml_is_contiguous(op->src[6])) {
supported = true;
} else {
supported = false;
}
break;
case GGML_OP_GATED_DELTA_NET:
if (op->type == GGML_TYPE_F32 && op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] && op->src[1]->type == GGML_TYPE_F32 && op->src[2] && op->src[2]->type == GGML_TYPE_F32 && op->src[3] && op->src[3]->type == GGML_TYPE_F32 && op->src[4] && op->src[4]->type == GGML_TYPE_F32 && op->src[5] && op->src[5]->type == GGML_TYPE_F32 && op->src[2]->ne[0] % 8 == 0 && (op->src[3]->ne[0] == 1 || op->src[3]->ne[0] == op->src[2]->ne[0]) && op->src[4]->ne[0] == 1 && et_ggml_is_row_contiguous(op->src[0]) && et_ggml_is_row_contiguous(op->src[1]) &&
et_ggml_is_row_contiguous(op->src[2]) && ggml_is_contiguous(op->src[3]) &&
ggml_is_contiguous(op->src[4]) && ggml_is_contiguous(op->src[5])) {
supported = true;
} else {
supported = false;
}
break;
case GGML_OP_VIEW:
case GGML_OP_PERMUTE:
case GGML_OP_TRANSPOSE:
case GGML_OP_RESHAPE:
supported = true;
break;
case GGML_OP_SET_ROWS:
if (op->src[0] && op->src[0]->type == GGML_TYPE_F32 && op->src[1] && op->src[1]->type == GGML_TYPE_I64 &&
(op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16) && ggml_is_contiguous_rows(op) &&
ggml_is_contiguous_rows(op->src[0]) && ggml_is_contiguous(op->src[1])) {
supported = (op->ne[0] == op->src[0]->ne[0]) && (op->ne[2] == op->src[0]->ne[2]) && (op->ne[3] == op->src[0]->ne[3]) && (op->src[0]->ne[1] == op->src[1]->ne[0]) && (op->src[0]->ne[2] % op->src[1]->ne[1] == 0) && (op->src[0]->ne[3] % op->src[1]->ne[2] == 0) && (op->src[1]->ne[3] == 1); } else {
supported = false;
}
break;
case GGML_OP_NONE:
supported = true;
break;
default:
supported = false;
break;
}
return supported;
}
static bool ggml_backend_et_device_supports_buft(ggml_backend_dev_t dev, ggml_backend_buffer_type_t buft) {
GGML_UNUSED(dev);
return buft->iface.get_name == ggml_backend_et_buffer_type_get_name;
}
static bool ggml_backend_et_device_offload_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
if (op->op == GGML_OP_GET_ROWS) {
return false;
}
return true;
GGML_UNUSED(dev);
}
static const struct ggml_backend_i ggml_backend_et_i = {
ggml_backend_et_get_name,
ggml_backend_et_free,
ggml_backend_et_set_tensor_async,
ggml_backend_et_get_tensor_async,
NULL,
NULL,
NULL,
ggml_backend_et_synchronize,
NULL,
NULL,
NULL,
NULL,
ggml_backend_et_graph_compute,
NULL,
NULL,
NULL,
};
static const char * ggml_backend_et_device_get_name(ggml_backend_dev_t dev) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
return dev_ctx->name.c_str();
}
static const char * ggml_backend_et_device_get_description(ggml_backend_dev_t dev) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
return dev_ctx->desc.c_str();
}
static void ggml_backend_et_device_get_memory(ggml_backend_dev_t dev, size_t * free, size_t * total) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
*free = dev_ctx->total_mem;
*total = dev_ctx->total_mem;
}
static enum ggml_backend_dev_type ggml_backend_et_device_get_type(ggml_backend_dev_t dev) {
GGML_UNUSED(dev);
return GGML_BACKEND_DEVICE_TYPE_GPU;
}
static void ggml_backend_et_device_get_props(ggml_backend_dev_t dev, struct ggml_backend_dev_props * props) {
GGML_UNUSED(dev);
props->name = ggml_backend_et_device_get_name(dev);
props->description = ggml_backend_et_device_get_description(dev);
props->type = ggml_backend_et_device_get_type(dev);
ggml_backend_et_device_get_memory(dev, &props->memory_free, &props->memory_total);
props->device_id = NULL; props->caps = {
true,
false,
false,
false,
true,
};
}
static ggml_backend_t ggml_backend_et_device_init_backend(ggml_backend_dev_t dev, const char * params) {
GGML_UNUSED(params);
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
return ggml_backend_et_init(dev_ctx->devidx);
}
static ggml_backend_buffer_type_t ggml_backend_et_device_get_buffer_type(ggml_backend_dev_t dev) {
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
return dev_ctx->buftype;
}
static ggml_backend_buffer_type_t ggml_backend_et_device_get_host_buffer_type(ggml_backend_dev_t dev) {
GGML_UNUSED(dev);
return ggml_backend_cpu_buffer_type();
}
static const struct ggml_backend_device_i ggml_backend_et_device_i = {
ggml_backend_et_device_get_name,
ggml_backend_et_device_get_description,
ggml_backend_et_device_get_memory,
ggml_backend_et_device_get_type,
ggml_backend_et_device_get_props,
ggml_backend_et_device_init_backend,
ggml_backend_et_device_get_buffer_type,
ggml_backend_et_device_get_host_buffer_type,
NULL,
ggml_backend_et_device_supports_op,
ggml_backend_et_device_supports_buft,
ggml_backend_et_device_offload_op,
NULL,
NULL,
NULL,
};
static const char * ggml_backend_et_reg_get_name(ggml_backend_reg_t reg) {
GGML_UNUSED(reg);
return GGML_ET_NAME;
}
static size_t ggml_backend_et_reg_get_device_count(ggml_backend_reg_t reg) {
ggml_backend_et_reg_ctx * ctx = (ggml_backend_et_reg_ctx *) reg->context;
return ctx->devices.size();
}
static ggml_backend_dev_t ggml_backend_et_reg_get_device(ggml_backend_reg_t reg, size_t devidx) {
ggml_backend_et_reg_ctx * ctx = (ggml_backend_et_reg_ctx *) reg->context;
if (devidx >= ctx->devices.size()) {
return nullptr;
}
return ctx->devices[devidx];
}
static void * ggml_backend_et_get_proc_address(ggml_backend_reg_t reg, const char * name) {
GGML_UNUSED(reg);
GGML_UNUSED(name);
return nullptr;
}
static const struct ggml_backend_reg_i ggml_backend_et_reg_i = {
ggml_backend_et_reg_get_name,
ggml_backend_et_reg_get_device_count,
ggml_backend_et_reg_get_device,
ggml_backend_et_get_proc_address,
};
ggml_backend_reg_t ggml_backend_et_reg(void) {
static ggml_backend_reg_t _reg = []() -> ggml_backend_reg_t {
ggml_backend_et_reg_ctx * ctx = new ggml_backend_et_reg_ctx;
if (!ggml_et_driver_init()) {
return nullptr;
}
ggml_backend_reg_t r = new ggml_backend_reg{
GGML_BACKEND_API_VERSION,
ggml_backend_et_reg_i,
nullptr, };
std::vector<rt::DeviceId> rtids = ggml_et_runtime()->getDevices();
for (int i = 0; i < ggml_et_devicelayer()->getDevicesCount(); i++) {
ggml_backend_dev_t dev = new ggml_backend_device{
ggml_backend_et_device_i,
r,
nullptr };
rt::DeviceId rtid = rtids[i];
rt::DeviceProperties prop = ggml_et_runtime()->getDeviceProperties(rtid);
ggml_backend_et_device_context * dev_ctx = new ggml_backend_et_device_context;
dev_ctx->devidx = i;
dev_ctx->rtid = rtid;
dev_ctx->name = GGML_ET_NAME + std::to_string(i);
dev_ctx->desc = "ET device " + std::to_string(i);
dev_ctx->total_mem = static_cast<size_t>(prop.memorySize_);
{
const char * env = getenv("GGML_ET_UBERKERNEL");
dev_ctx->uberkernel_enabled = env && env[0] != '\0' && strcmp(env, "0") != 0;
}
ggml_backend_et_buffer_type_context * bufty_ctx = new ggml_backend_et_buffer_type_context;
bufty_ctx->devidx = i;
bufty_ctx->name = GGML_ET_NAME + std::to_string(i);
dev_ctx->buftype = new ggml_backend_buffer_type{ ggml_backend_et_buffer_type_i,
dev,
bufty_ctx };
dev_ctx->default_stream = ggml_et_runtime()->createStream(rtid);
dev_ctx->trace_buffer = ggml_et_runtime()->mallocDevice(rtid, ET_TRACE_BUFFER_SIZE);
for (auto & slot : dev_ctx->uberkernel.slots) {
slot.insts.reserve(256);
slot.params_blob.reserve(1 << 20);
slot.device_insts_capacity = 256 * sizeof(ggml_et_uberkernel_inst);
slot.device_params_capacity = 1 << 20;
slot.device_insts = ggml_et_runtime()->mallocDevice(rtid, slot.device_insts_capacity);
slot.device_params = ggml_et_runtime()->mallocDevice(rtid, slot.device_params_capacity);
if (slot.device_insts == nullptr) {
slot.device_insts_capacity = 0;
}
if (slot.device_params == nullptr) {
slot.device_params_capacity = 0;
}
}
dev->context = dev_ctx;
ctx->devices.push_back(dev);
}
r->context = ctx;
return r;
}();
return _reg;
}
ggml_guid_t ggml_backend_et_guid(void) {
static ggml_guid guid = { 0x4b, 0xe0, 0x72, 0x88, 0xc0, 0xf6, 0x29, 0xb4,
0x79, 0x9f, 0x70, 0x68, 0x71, 0x0f, 0x6d, 0xc8 };
return &guid;
}
ggml_backend_t ggml_backend_et_init(size_t devidx) {
if (!ggml_et_driver_init()) {
return nullptr;
}
if (devidx >= (size_t) ggml_backend_et_get_device_count()) {
return nullptr;
}
ggml_backend_et_context * ctx = new ggml_backend_et_context;
ctx->devidx = (int) devidx;
ggml_backend_t backend = new ggml_backend{
ggml_backend_et_guid(),
ggml_backend_et_i,
ggml_backend_et_reg_get_device(ggml_backend_et_reg(), devidx),
ctx,
};
return backend;
}
bool ggml_backend_is_et(ggml_backend_t backend) {
return backend != NULL && ggml_guid_matches(backend->guid, ggml_backend_et_guid());
}
int ggml_backend_et_get_device_count(void) {
return ggml_backend_et_reg_get_device_count(ggml_backend_et_reg());
}
void ggml_backend_et_get_device_description(int devidx, char * description, size_t description_size) {
if (devidx < 0 || devidx >= ggml_backend_et_get_device_count()) {
snprintf(description, description_size, "ET Device %d (invalid)", devidx);
return;
}
ggml_backend_dev_t dev = ggml_backend_et_reg_get_device(ggml_backend_et_reg(), devidx);
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
snprintf(description, description_size, "%s", dev_ctx->desc.c_str());
}
void ggml_backend_et_get_device_memory(int devidx, size_t * free, size_t * total) {
if (devidx < 0 || devidx >= ggml_backend_et_get_device_count()) {
*free = 0;
*total = 0;
return;
}
ggml_backend_dev_t dev = ggml_backend_et_reg_get_device(ggml_backend_et_reg(), devidx);
ggml_backend_et_device_get_memory(dev, free, total);
}
ggml_backend_buffer_type_t ggml_backend_et_buffer_type(size_t dev_num) {
if (dev_num >= (size_t) ggml_backend_et_get_device_count()) {
return nullptr;
}
ggml_backend_dev_t dev = ggml_backend_et_reg_get_device(ggml_backend_et_reg(), dev_num);
ggml_backend_et_device_context * dev_ctx = (ggml_backend_et_device_context *) dev->context;
return dev_ctx->buftype;
}
ggml_backend_buffer_type_t ggml_backend_et_host_buffer_type(void) {
static ggml_backend_buffer_type host_buffer_type = {
ggml_backend_et_buffer_type_i,
nullptr,
nullptr,
};
return &host_buffer_type;
}
GGML_BACKEND_DL_IMPL(ggml_backend_et_reg)