#include "arm_yolo26n.h" #include #include #include #include #include #include #include #include #include "buffer_validation.h" #include "fixed_graph.h" #include "fixed_schedule.h" #include "model_store.h" namespace { // Owns the complete lifetime of one runtime instance. Members are declared in // dependency order; explicit teardown releases graph runtimes before their // shared workspace, threadpool, scratch buffers, and model storage disappear. struct NativeRuntime { NativeRuntime() = default; NativeRuntime(const NativeRuntime&) = delete; NativeRuntime& operator=(const NativeRuntime&) = delete; ~NativeRuntime() { for (xnn_runtime_t runtime : runtimes) { if (runtime) xnn_delete_runtime(runtime); } if (workspace) xnn_release_workspace(workspace); if (threadpool) pthreadpool_destroy(threadpool); std::free(arena); std::free(project); } ModelStore model; ScheduleState schedule; std::array runtimes{}; xnn_workspace_t workspace = nullptr; pthreadpool_t threadpool = nullptr; uint8_t* arena = nullptr; uint16_t* project = nullptr; std::atomic calls{0}; }; bool Allocate(void** output, size_t bytes) { return bytes != 0 && posix_memalign(output, yolo26::kTensorAlignment, bytes) == 0; } bool Initialize(NativeRuntime* native, const void* model_data, size_t model_size) { // Validate model-derived dimensions before creating any native execution // resources. The destructor handles every partially initialized state. if (!native->model.Load(model_data, model_size) || !InitializeSchedule(native->model.resolution(), native->model.class_count(), &native->schedule) || xnn_initialize(nullptr) != xnn_status_success || xnn_create_workspace(&native->workspace) != xnn_status_success) { return false; } native->threadpool = pthreadpool_create(yolo26::kThreadCount); if (!native->threadpool || pthreadpool_get_threads_count(native->threadpool) != yolo26::kThreadCount || !Allocate(reinterpret_cast(&native->arena), native->schedule.arena_bytes) || !Allocate(reinterpret_cast(&native->project), native->schedule.project_bytes)) { return false; } const GraphContext graphs{ &native->model, native->workspace, native->threadpool, &native->runtimes, }; return BuildFixedGraphs(graphs) && ConfigureFixedProjection(native->runtimes); } } // namespace extern "C" y26_native_runtime* y26_native_create(const void* model_data, size_t model_size) { if (!model_data) return nullptr; auto runtime = std::make_unique(); if (!Initialize(runtime.get(), model_data, model_size)) return nullptr; return reinterpret_cast(runtime.release()); } extern "C" y26_native_status y26_native_execute(y26_native_runtime* runtime, const float* input, size_t input_bytes, float* output, size_t output_bytes) { auto* native = reinterpret_cast(runtime); if (!native || !input || !output || input_bytes != native->schedule.input_bytes || output_bytes != native->schedule.output_bytes || yolo26::BuffersOverlap(input, input_bytes, output, output_bytes)) { return Y26_NATIVE_INVALID_ARGUMENT; } if (!RunFixedSchedule(native->schedule, native->runtimes, native->threadpool, native->arena, native->project, input, output)) { return Y26_NATIVE_EXECUTION_FAILED; } if (!OutputIsFinite(native->schedule, output)) return Y26_NATIVE_NONFINITE_OUTPUT; ++native->calls; return Y26_NATIVE_OK; } extern "C" void y26_native_destroy(y26_native_runtime* runtime) { delete reinterpret_cast(runtime); } extern "C" uint64_t y26_native_calls(const y26_native_runtime* runtime) { const auto* native = reinterpret_cast(runtime); return native ? native->calls.load() : 0; } extern "C" uint32_t y26_native_graphs(const y26_native_runtime* runtime) { return runtime ? kGraphCount : 0; } extern "C" uint32_t y26_native_resolution(const y26_native_runtime* runtime) { const auto* native = reinterpret_cast(runtime); return native ? native->model.resolution() : 0; } extern "C" uint32_t y26_native_class_count(const y26_native_runtime* runtime) { const auto* native = reinterpret_cast(runtime); return native ? native->model.class_count() : 0; } extern "C" size_t y26_native_input_bytes(const y26_native_runtime* runtime) { const auto* native = reinterpret_cast(runtime); return native ? native->schedule.input_bytes : 0; } extern "C" size_t y26_native_output_bytes(const y26_native_runtime* runtime) { const auto* native = reinterpret_cast(runtime); return native ? native->schedule.output_bytes : 0; } extern "C" size_t y26_native_arena_bytes(const y26_native_runtime* runtime) { const auto* native = reinterpret_cast(runtime); return native ? native->schedule.arena_bytes : 0; }