/* * Copyright (c) 2024 MediaTek Inc. * * Licensed under the BSD License (the "License"); you may not use this file * except in compliance with the License. See the license file in the root * directory of this source tree for more details. */ #include "NeuronBackend.h" #include "NeuronBufferAllocator.h" #include "NeuronLog.h" #include "NeuronPayloadHeader.h" #include "api/NeuronAdapter.h" #include "executorch/runtime/core/error.h" #include #include #include #include namespace executorch { namespace backends { namespace neuron { using executorch::runtime::ArrayRef; using executorch::runtime::BackendExecutionContext; using executorch::runtime::BackendInitContext; using executorch::runtime::CompileSpec; using executorch::runtime::DelegateHandle; using executorch::runtime::Error; using executorch::runtime::EValue; using executorch::runtime::FreeableBuffer; using executorch::runtime::MemoryAllocator; using executorch::runtime::Result; const char kHighAddrKey[] = "HighAddr"; const char kImportForeverKey[] = "ImportForever"; Result NeuronBackend::init( BackendInitContext& context, FreeableBuffer* processed, ArrayRef compile_specs) const { NeuronDelegateSetting setting; for (auto& compile_spec : compile_specs) { if (std::strcmp(compile_spec.key, kHighAddrKey) == 0) { setting.mHighAddr = *static_cast(compile_spec.value.buffer); LogInfo("NeuronBackend", "IsHighAddr Enable : %d", setting.mHighAddr); } else if (std::strcmp(compile_spec.key, kImportForeverKey) == 0) { setting.mImportForever = *static_cast(compile_spec.value.buffer); LogInfo( "NeuronBackend", "IsImportForever Enable : %d", setting.mImportForever); } else { LogWarn("NeuronBackend", "unknown compile spec: %s", compile_spec.key); } } auto Payload = NeuronPayload(processed->data(), processed->size()); LogInfo( "NeuronBackend", "version %u, input %u, output %u, length %u, payload size: %zu", Payload.Header.Version, Payload.Header.InputCount, Payload.Header.OutputCount, Payload.Header.DataLen, processed->size()); MemoryAllocator* runtime_allocator = context.get_runtime_allocator(); NeuronExecuTorchDelegate* delegate = ET_ALLOCATE_INSTANCE_OR_RETURN_ERROR( runtime_allocator, NeuronExecuTorchDelegate); new (delegate) NeuronExecuTorchDelegate(); if (delegate == nullptr) { return nullptr; } auto res = delegate->LoadCompiledNetwork(Payload, setting); return res == NEURON_NO_ERROR ? delegate : nullptr; } Error NeuronBackend::execute( ET_UNUSED BackendExecutionContext& context, DelegateHandle* handle, EValue** args) const { NeuronExecuTorchDelegate* delegate = reinterpret_cast(handle); return delegate->execute(context, args); } void NeuronBackend::destroy(DelegateHandle* handle) const { if (handle != nullptr) { NeuronExecuTorchDelegate* delegate = reinterpret_cast(handle); delegate->~NeuronExecuTorchDelegate(); } } bool NeuronBackend::is_available() const { return true; } Error NeuronExecuTorchDelegate::execute( BackendExecutionContext& context, EValue** args) const { if (HintNeuronBackend(args) != NEURON_NO_ERROR) { return Error::InvalidState; }; auto allocator = dynamic_cast( context.get_temp_allocator()); size_t inputCount = mInputSizes.size(), outputCount = mOutputSizes.size(); for (int i = 0; i < inputCount; i++) { auto data_ptr = args[i]->toTensor().data_ptr(); auto data_size = args[i]->toTensor().nbytes(); if (IsCached(i, data_ptr)) { continue; }; auto unit = allocator != nullptr ? allocator->Find(data_ptr) : nullptr; if (unit) { UpdateCache(i, data_ptr); size_t offset = (char*)data_ptr - (char*)unit->GetAddress(); mExecutor.SetInputOutputFromMemory( i, unit->GetNeuronMemory(), offset, data_size); } else { mExecutor.SetInputOutput(i, data_ptr, data_size); } } for (int o = inputCount; o < inputCount + outputCount; o++) { auto data_ptr = args[o]->toTensor().data_ptr(); auto data_size = args[o]->toTensor().nbytes(); auto output_index = o - inputCount; if (IsCached(output_index, data_ptr)) { continue; }; auto unit = allocator != nullptr ? allocator->Find(data_ptr) : nullptr; if (unit) { UpdateCache(output_index, data_ptr); size_t offset = (char*)data_ptr - (char*)unit->GetAddress(); mExecutor.SetInputOutputFromMemory( output_index, unit->GetNeuronMemory(), offset, data_size); } else { mExecutor.SetInputOutput( output_index, data_ptr, data_size); } } return mExecutor.Compute() == NEURON_NO_ERROR ? Error::Ok : Error::InvalidState; }; int NeuronExecuTorchDelegate::HintNeuronBackend(EValue** args) const { auto HintImportForever = [this](EValue** args) -> int { auto& allocator = GET_NEURON_ALLOCATOR; size_t inputCount = mInputSizes.size(), outputCount = mOutputSizes.size(); for (int i = 0; i < inputCount; i++) { auto data_ptr = args[i]->toTensor().data_ptr(); if (mHasImported.count(data_ptr)) { continue; } auto unit = allocator.Find(data_ptr); if (unit) { mExecutor.SetInputOutputFromMemory( i, unit->GetNeuronMemory(), 0, unit->GetSize()); mHasImported.insert(data_ptr); } } for (int o = inputCount; o < inputCount + outputCount; o++) { auto data_ptr = args[o]->toTensor().data_ptr(); if (mHasImported.count(data_ptr)) { continue; } auto output_index = o - inputCount; auto unit = allocator.Find(data_ptr); if (unit) { mExecutor.SetInputOutputFromMemory( output_index, unit->GetNeuronMemory(), 0, unit->GetSize()); mHasImported.insert(data_ptr); } } return NEURON_NO_ERROR; }; if (mSettings.mImportForever) { CHECK_NO_ERROR(HintImportForever(args)); } return NEURON_NO_ERROR; } } // namespace neuron } // namespace backends } // namespace executorch namespace { auto cls = executorch::backends::neuron::NeuronBackend(); executorch::runtime::Backend backend{"NeuropilotBackend", &cls}; static auto success_with_compiler = executorch::runtime::register_backend(backend); } // namespace