| /* |
| * Copyright (c) Qualcomm Innovation Center, Inc. |
| * All rights reserved. |
| * |
| * This source code is licensed under the BSD-style license found in the |
| * LICENSE file in the root directory of this source tree. |
| */ |
| |
| #include <executorch/backends/qualcomm/aot/ir/qcir_utils.h> |
| #include <executorch/backends/qualcomm/qc_binary_info_generated.h> |
| #include <executorch/backends/qualcomm/runtime/QnnManager.h> |
| #include <executorch/backends/qualcomm/runtime/SharedBuffer.h> |
| #include <executorch/backends/qualcomm/runtime/Utils.h> |
| #include <executorch/backends/qualcomm/runtime/backends/QnnBackendCommon.h> |
| #include <executorch/backends/qualcomm/runtime/backends/QnnImplementation.h> |
| #include <executorch/extension/tensor/tensor.h> |
| #include <algorithm> |
| #include <cstdlib> |
| #include <cstring> |
| #include <fstream> |
| #include <string> |
| |
| namespace executorch { |
| namespace backends { |
| namespace qnn { |
| |
| using executorch::runtime::Error; |
| |
| bool CompareExportedInput( |
| const std::shared_ptr<TensorWrapper>& a, |
| const std::shared_ptr<TensorWrapper>& b) { |
| // Using the order of the nodes as external_id in AOT |
| // to extract the right arg from *args at runtime |
| int numA = std::stoi(a->GetName().substr(a->GetName().find('_') + 1)); |
| int numB = std::stoi(b->GetName().substr(b->GetName().find('_') + 1)); |
| return numA < numB; |
| } |
| |
| QnnManager::~QnnManager() { |
| backend_params_ptr_.reset(new BackendConfigParameters()); |
| logger_.reset(); |
| qnn_loaded_backend_.TerminateAllBackends(); |
| } |
| |
| QnnManager::QnnManager( |
| const QnnExecuTorchOptions* options, |
| const QnnExecuTorchContextBinary& qnn_executorch_context_binary) |
| : qnn_context_blob_(qnn_executorch_context_binary), |
| qnn_loaded_backend_(""), |
| // options' life cycle is decided by compiler specs which is |
| // kept by executorch runtime framework |
| // please pay attention to any potential seg fault |
| options_(options) { |
| QnnExecuTorchBackendType backend_type = |
| options->backend_options()->backend_type(); |
| std::string library_path = options->library_path()->str(); |
| |
| if (options->log_level() >= QnnExecuTorchLogLevel::kLogLevelInfo) { |
| QNN_EXECUTORCH_LOG_INFO( |
| "soc_model in soc_info: %s", |
| EnumNameQcomChipset(options_->soc_info()->soc_model())); |
| QNN_EXECUTORCH_LOG_INFO( |
| "backend_type: %s", EnumNameQnnExecuTorchBackendType(backend_type)); |
| QNN_EXECUTORCH_LOG_INFO("graph_name: %s", options_->graph_name()->c_str()); |
| QNN_EXECUTORCH_LOG_INFO("library_path: %s", library_path.c_str()); |
| QNN_EXECUTORCH_LOG_INFO("dump intermediate outputs: %s", IsTensorDump()); |
| QNN_EXECUTORCH_LOG_INFO( |
| "log_level: %s", EnumNameQnnExecuTorchLogLevel(options_->log_level())); |
| QNN_EXECUTORCH_LOG_INFO( |
| "profile_level: %s", |
| EnumNameQnnExecuTorchProfileLevel(options_->profile_level())); |
| QNN_EXECUTORCH_LOG_INFO( |
| "the size of qnn context binary: %d", |
| qnn_executorch_context_binary.nbytes); |
| QNN_EXECUTORCH_LOG_INFO( |
| "Is on-device graph construction: %d", options->online_prepare()); |
| QNN_EXECUTORCH_LOG_INFO( |
| "Enable shared buffer: %d", options->shared_buffer()); |
| } |
| |
| if (library_path.empty()) { |
| switch (backend_type) { |
| case QnnExecuTorchBackendType::kHtpBackend: |
| library_path = htp_library_name_; |
| break; |
| case QnnExecuTorchBackendType::kDspBackend: |
| library_path = dsp_library_name_; |
| break; |
| case QnnExecuTorchBackendType::kGpuBackend: |
| library_path = gpu_library_name_; |
| break; |
| default: |
| QNN_EXECUTORCH_LOG_ERROR("Unknown backend type: %d", backend_type); |
| break; |
| } |
| } |
| qnn_loaded_backend_ = QnnImplementation(library_path); |
| backend_params_ptr_ = std::make_unique<BackendConfigParameters>(); |
| } |
| |
| Error QnnManager::LoadQnnLibrary() { |
| Error ret = qnn_loaded_backend_.Load(nullptr); |
| return ret; |
| } |
| |
| Error QnnManager::PreRegisterMem() { |
| SharedBuffer& shared_buffer_manager = SharedBuffer::GetSharedBufferManager(); |
| for (const auto info : shared_buffer_manager.GetCustomMemTensorInfoSet()) { |
| void* unaligned_custom_mem_base = |
| shared_buffer_manager.GetUnAlignedAddr(info.custom_mem); |
| |
| size_t tensor_offset = (static_cast<char*>(info.custom_mem) - |
| static_cast<char*>(unaligned_custom_mem_base)) + |
| info.pos; |
| size_t total_custom_mem_size = |
| shared_buffer_manager.GetAllocatedSize(info.custom_mem); |
| |
| int32_t mem_fd = shared_buffer_manager.MemToFd(unaligned_custom_mem_base); |
| if (mem_fd == -1) { |
| QNN_EXECUTORCH_LOG_WARN( |
| "PreRegisterMem failed to get file descriptor.", |
| "custom_mem: %p", |
| "tensor_addr: %p", |
| "pos: %uz", |
| "tensor_bytes: %uz", |
| "shape: %p", |
| "rank: %zu", |
| "qnn_dtype: %X", |
| info.custom_mem, |
| info.tensor_addr, |
| info.pos, |
| info.tensor_bytes, |
| info.shape, |
| info.rank, |
| info.dtype); |
| return Error::Internal; |
| } |
| |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_mem_manager_ptr_->PreRegisterCustomMemHandle( |
| mem_fd, |
| unaligned_custom_mem_base, |
| total_custom_mem_size, |
| tensor_offset, |
| info) == Error::Ok, |
| Internal, |
| "Fail to register to shared memory."); |
| } |
| return Error::Ok; |
| } |
| |
| Error QnnManager::RegisterMem( |
| void* data_ptr, |
| const std::shared_ptr<TensorWrapper>& tensor_wrapper) { |
| SharedBuffer& shared_buffer_manager = SharedBuffer::GetSharedBufferManager(); |
| // Not enable shared buffer |
| if (!options_->shared_buffer()) |
| return Error::Internal; |
| |
| if (backend_params_ptr_->qnn_mem_manager_ptr_ == nullptr) { |
| QNN_EXECUTORCH_LOG_WARN( |
| "Backend %s doesn't supported shared buffer.", |
| EnumNameQnnExecuTorchBackendType( |
| options_->backend_options()->backend_type())); |
| return Error::Internal; |
| } |
| |
| void* custom_mem_base = shared_buffer_manager.GetCustomMemBase(data_ptr); |
| if (custom_mem_base != nullptr) { |
| return RegisterCustomMem(data_ptr, custom_mem_base, tensor_wrapper); |
| } |
| return RegisterIonMem(data_ptr, tensor_wrapper); |
| } |
| |
| Error QnnManager::RegisterIonMem( |
| void* data_ptr, |
| const std::shared_ptr<TensorWrapper>& tensor_wrapper) { |
| SharedBuffer& shared_buffer_manager = SharedBuffer::GetSharedBufferManager(); |
| if (!shared_buffer_manager.IsAllocated(data_ptr)) { |
| // It means two scenarios here: |
| // 1. the input and output partitioned graph |
| // 2. Actually, user doesn't allocate shared buffer with |
| // QnnExecuTorchAllocCustomMem API |
| return Error::Internal; |
| } else if (backend_params_ptr_->qnn_mem_manager_ptr_->IsRegistered( |
| tensor_wrapper->GetMemHandle(), data_ptr)) { |
| if (options_->log_level() >= QnnExecuTorchLogLevel::kLogLevelInfo) |
| QNN_EXECUTORCH_LOG_INFO( |
| "Tensor name %s has been registered shared memory.", |
| tensor_wrapper->GetName().c_str()); |
| return Error::Ok; |
| } |
| |
| int32_t mem_fd = shared_buffer_manager.MemToFd(data_ptr); |
| if (mem_fd == -1) { |
| QNN_EXECUTORCH_LOG_WARN( |
| "Tensor name %s is failed to get file descriptor.", |
| tensor_wrapper->GetName().c_str()); |
| return Error::Internal; |
| } |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_mem_manager_ptr_->RegisterIonMem( |
| tensor_wrapper, mem_fd, data_ptr) == Error::Ok, |
| Internal, |
| "Fail to register to shared memory."); |
| |
| return Error::Ok; |
| } |
| |
| Error QnnManager::RegisterCustomMem( |
| void* data_ptr, |
| void* custom_mem_base, |
| const std::shared_ptr<TensorWrapper>& tensor_wrapper) { |
| if (backend_params_ptr_->qnn_mem_manager_ptr_->IsRegistered( |
| tensor_wrapper->GetMemHandle(), data_ptr)) { |
| if (options_->log_level() >= QnnExecuTorchLogLevel::kLogLevelInfo) |
| QNN_EXECUTORCH_LOG_INFO( |
| "Tensor name %s has been registered shared memory.", |
| tensor_wrapper->GetName().c_str()); |
| return Error::Ok; |
| } |
| |
| CustomMemTensorInfo info{ |
| custom_mem_base, |
| data_ptr, |
| static_cast<size_t>( |
| static_cast<char*>(data_ptr) - static_cast<char*>(custom_mem_base)), |
| tensor_wrapper->GetBytes(), |
| tensor_wrapper->GetDims(), |
| tensor_wrapper->GetRank(), |
| qnn_dtype_to_scalar_type_[tensor_wrapper->GetDataType()]}; |
| |
| Qnn_MemHandle_t pre_registered_handle = |
| backend_params_ptr_->qnn_mem_manager_ptr_->GetPreRegisteredHandle(info); |
| if (pre_registered_handle != nullptr) { |
| if (options_->log_level() >= QnnExecuTorchLogLevel::kLogLevelInfo) { |
| QNN_EXECUTORCH_LOG_INFO( |
| "Tensor name %s found a pre-registered memHandle.", |
| tensor_wrapper->GetName().c_str()); |
| } |
| return backend_params_ptr_->qnn_mem_manager_ptr_->SetMemHandle( |
| tensor_wrapper, data_ptr, pre_registered_handle); |
| } |
| |
| SharedBuffer& shared_buffer_manager = SharedBuffer::GetSharedBufferManager(); |
| void* unaligned_custom_mem_base = |
| shared_buffer_manager.GetUnAlignedAddr(custom_mem_base); |
| |
| size_t tensor_offset = static_cast<char*>(custom_mem_base) - |
| static_cast<char*>(unaligned_custom_mem_base) + info.pos; |
| size_t total_custom_mem_size = |
| shared_buffer_manager.GetAllocatedSize(custom_mem_base); |
| |
| int32_t mem_fd = shared_buffer_manager.MemToFd(unaligned_custom_mem_base); |
| if (mem_fd == -1) { |
| QNN_EXECUTORCH_LOG_WARN( |
| "Tensor name %s failed to get file descriptor.", |
| tensor_wrapper->GetName().c_str()); |
| return Error::Internal; |
| } |
| |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_mem_manager_ptr_->RegisterCustomMem( |
| tensor_wrapper, |
| mem_fd, |
| data_ptr, |
| unaligned_custom_mem_base, |
| total_custom_mem_size, |
| tensor_offset) == Error::Ok, |
| Internal, |
| "Fail to register to shared memory."); |
| |
| return Error::Ok; |
| } |
| |
| Error QnnManager::Init() { |
| ET_CHECK_OR_RETURN_ERROR( |
| LoadQnnLibrary() == Error::Ok, Internal, "Fail to load Qnn library"); |
| logger_ = std::make_unique<QnnLogger>( |
| qnn_loaded_backend_, LoggingCallback, options_->log_level()); |
| if (backend_params_ptr_->backend_init_state_ == |
| BackendInitializeState::UNINITIALIZED) { |
| QNN_EXECUTORCH_LOG_INFO( |
| "Initialize Qnn backend " |
| "parameters for Qnn executorch backend type %d", |
| options_->backend_options()->backend_type()); |
| backend_params_ptr_ = QnnBackendFactory().Create( |
| qnn_loaded_backend_, logger_.get(), qnn_context_blob_, options_); |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_ != nullptr, Internal, "Failed to load Qnn backend.") |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_backend_cache_ptr_->Configure() == Error::Ok, |
| Internal, |
| "Fail to configure Qnn backend cache"); |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_backend_ptr_->Configure() == Error::Ok, |
| Internal, |
| "Fail to configure Qnn backend"); |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_device_ptr_->Configure() == Error::Ok, |
| Internal, |
| "Fail to configure Qnn device"); |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_context_ptr_->Configure() == Error::Ok, |
| Internal, |
| "Fail to configure Qnn context"); |
| for (const std::string& graph_name : |
| backend_params_ptr_->qnn_context_ptr_->GetGraphNames()) { |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_graph_ptr_->Configure(graph_name) == |
| Error::Ok, |
| Internal, |
| "Fail to configure Qnn graph"); |
| } |
| backend_params_ptr_->backend_init_state_ = |
| BackendInitializeState::INITIALIZED; |
| } |
| |
| #if defined(__aarch64__) |
| ET_CHECK_OR_RETURN_ERROR( |
| PreRegisterMem() == Error::Ok, |
| Internal, |
| "Fail to pre register custom memory handle"); |
| #endif |
| return Error::Ok; |
| } |
| |
| Error QnnManager::AllocateTensor(const std::string& graph_name) { |
| std::vector<Qnn_Tensor_t> input_tensors = |
| backend_params_ptr_->qnn_context_ptr_->GetGraphInputs(graph_name); |
| std::vector<Qnn_Tensor_t> output_tensors = |
| backend_params_ptr_->qnn_context_ptr_->GetGraphOutputs(graph_name); |
| |
| for (auto& tensor : input_tensors) { |
| std::shared_ptr<TensorWrapper> tensor_wrapper = CreateTensorWrapper(tensor); |
| tensor_wrapper->UpdateQnnTensorMeta(tensor); |
| input_tensors_[graph_name].emplace_back(std::move(tensor_wrapper)); |
| } |
| if (!options_->is_from_context_binary()) { |
| std::sort( |
| input_tensors_[graph_name].begin(), |
| input_tensors_[graph_name].end(), |
| CompareExportedInput); |
| } |
| for (size_t i = 0; i < output_tensors.size(); ++i) { |
| std::shared_ptr<TensorWrapper> tensor_wrapper = |
| CreateTensorWrapper(output_tensors[i]); |
| tensor_wrapper->UpdateQnnTensorMeta(output_tensors[i]); |
| const std::string& tensor_name = tensor_wrapper->GetName(); |
| // this is required by identifying shared buffer mechanism |
| // info might be missed if context binary came from qnn_converter |
| if (options_->is_from_context_binary() && |
| tensor_name.find("output_") == std::string::npos) { |
| tensor_wrapper->SetName("output_" + tensor_name); |
| } |
| if (IsTensorDump()) { |
| tensor_wrapper->AllocateDataBuffer(); |
| } |
| output_tensors_[graph_name].emplace_back(std::move(tensor_wrapper)); |
| } |
| return Error::Ok; |
| } |
| |
| Error QnnManager::AllocateTensor( |
| const std::string& graph_name, |
| std::vector<std::shared_ptr<TensorWrapper>>& inputs, |
| std::vector<std::shared_ptr<TensorWrapper>>& outputs) { |
| input_tensors_[graph_name] = std::move(inputs); |
| // TODO: suuport per-tensor dump in online prepare mode |
| // should be achievable with some pre-process |
| if (!options_->is_from_context_binary()) { |
| std::sort( |
| input_tensors_[graph_name].begin(), |
| input_tensors_[graph_name].end(), |
| CompareExportedInput); |
| } |
| output_tensors_[graph_name] = std::move(outputs); |
| return Error::Ok; |
| } |
| |
| Error QnnManager::Execute( |
| const std::string& graph_name, |
| const std::vector<Qnn_Tensor_t>& input_tensor_structs, |
| std::vector<Qnn_Tensor_t>& output_tensor_structs, |
| executorch::runtime::EventTracer* event_tracer) { |
| Qnn_ErrorHandle_t error = QNN_SUCCESS; |
| |
| error = backend_params_ptr_->qnn_graph_ptr_->GraphExecute( |
| graph_name, input_tensor_structs, output_tensor_structs); |
| |
| if (error != QNN_SUCCESS) { |
| QNN_EXECUTORCH_LOG_ERROR( |
| "qnn_graph_execute failed. Error %d", QNN_GET_ERROR_CODE(error)); |
| return Error::Internal; |
| } |
| if (IsTensorDump()) { |
| // TODO: Need to handle the graph which is partitioned. |
| // Maybe we could use graph name. |
| for (std::size_t out_idx = 0; out_idx < output_tensor_structs.size(); |
| ++out_idx) { |
| const Qnn_Tensor_t& output_tensor = output_tensor_structs[out_idx]; |
| std::vector<executorch::aten::SizesType> sizes( |
| QNN_VER_PTR(output_tensor)->dimensions, |
| QNN_VER_PTR(output_tensor)->dimensions + |
| QNN_VER_PTR(output_tensor)->rank); |
| |
| auto dump_tensor = executorch::extension::from_blob( |
| QNN_VER_PTR(output_tensor)->clientBuf.data, |
| sizes, |
| qnn_dtype_to_scalar_type_[QNN_VER_PTR(output_tensor)->dataType]); |
| |
| executorch::runtime::event_tracer_log_output_delegate< |
| executorch::aten::Tensor>( |
| event_tracer, |
| QNN_VER_PTR(output_tensor)->name, |
| /*delegate_debug_id=*/ |
| static_cast<executorch::runtime::DebugHandle>(-1), |
| *dump_tensor); |
| } |
| } |
| |
| return Error::Ok; |
| } |
| |
| Error QnnManager::ProfileExecuteData( |
| const std::string& graph_name, |
| executorch::runtime::EventTracer* event_tracer) { |
| Qnn_ErrorHandle_t error = QNN_SUCCESS; |
| if (options_->profile_level() != QnnExecuTorchProfileLevel::kProfileOff) { |
| error = backend_params_ptr_->qnn_graph_ptr_->ProfileExecuteData( |
| graph_name, event_tracer); |
| if (error != QNN_SUCCESS) { |
| QNN_EXECUTORCH_LOG_ERROR( |
| " Failed to profile. Error %d", QNN_GET_ERROR_CODE(error)); |
| return Error::Internal; |
| } |
| } |
| return Error::Ok; |
| } |
| |
| void QnnManager::Destroy() { |
| QNN_EXECUTORCH_LOG_INFO("Destroy Qnn backend parameters"); |
| backend_params_ptr_.reset(new BackendConfigParameters()); |
| logger_.reset(); |
| |
| qnn_loaded_backend_.TerminateAllBackends(); |
| } |
| |
| bool QnnManager::IsNodeSupportedByBackend( |
| std::vector<std::shared_ptr<OpWrapper>>& op_wrappers) { |
| Qnn_ErrorHandle_t error = QNN_SUCCESS; |
| |
| for (std::shared_ptr<OpWrapper>& op_wrapper : op_wrappers) { |
| for (const auto& param : op_wrapper->GetParams()) { |
| // unused? |
| // auto* p_tensor_param = dynamic_cast<TensorParamWrapper*>(param.get()); |
| if (param->PopulateQnnParam() != Error::Ok) { |
| QNN_EXECUTORCH_LOG_WARN( |
| "Qnn Backend op validation failed " |
| "with PopulateQnnParam: %d", |
| QNN_GET_ERROR_CODE(error)); |
| return false; |
| } |
| } |
| |
| error = backend_params_ptr_->qnn_backend_ptr_->BackendValidateOpConfig( |
| op_wrapper->GetOpConfig()); |
| if (error != QNN_SUCCESS) { |
| QNN_EXECUTORCH_LOG_WARN( |
| "Qnn Backend op validation failed with error: %d", |
| QNN_GET_ERROR_CODE(error)); |
| |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| Error QnnManager::GetContextBinary( |
| QnnExecuTorchContextBinary& qnn_executorch_context_binary) { |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_context_ptr_->GetContextBinary( |
| qnn_executorch_context_binary) == Error::Ok, |
| Internal, |
| "Fail to get context binary."); |
| |
| return Error::Ok; |
| } |
| |
| Error QnnManager::CompileQcir() { |
| flatbuffers::Verifier verifier_binary_info( |
| static_cast<const uint8_t* const>(qnn_context_blob_.buffer), |
| qnn_context_blob_.nbytes); |
| if (!qnn_delegate::VerifyBinaryInfoBuffer(verifier_binary_info)) { |
| QNN_EXECUTORCH_LOG_ERROR("Fail to verify binary info"); |
| return Error::Internal; |
| } |
| |
| auto binary_info = qnn_delegate::GetBinaryInfo(qnn_context_blob_.buffer); |
| flatbuffers::Verifier verifier_qcir( |
| binary_info->data()->data(), binary_info->data()->size()); |
| if (!qcir::VerifyContextBuffer(verifier_qcir)) { |
| QNN_EXECUTORCH_LOG_ERROR("Fail to verify qcir format"); |
| return Error::Internal; |
| } |
| |
| auto context = qcir::GetContext(binary_info->data()->data()); |
| for (const auto& graph : *context->graphs()) { |
| // qcir tensors to TensorWrapper |
| std::vector<std::shared_ptr<TensorWrapper>> graph_inputs, graph_outputs, |
| tensors; |
| for (const auto& tensor : *graph->tensors()) { |
| tensors.emplace_back(CreateTensorWrapper(ToTensor(tensor))); |
| if (tensor->type() == qcir::TensorType::WRITE) { |
| graph_inputs.push_back(tensors.back()); |
| } else if (tensor->type() == qcir::TensorType::READ) { |
| graph_outputs.push_back(tensors.back()); |
| } |
| } |
| std::vector<std::shared_ptr<OpWrapper>> op_wrappers; |
| // qcir graph node to OpWrapper |
| for (const auto& node : *graph->nodes()) { |
| std::shared_ptr<OpWrapper> op = std::make_shared<OpWrapper>( |
| node->name()->str(), |
| node->package_name()->str(), |
| node->type_name()->str()); |
| |
| // qcir input tensors to OpWrapper input tensors |
| std::vector<std::shared_ptr<TensorWrapper>> inputs; |
| for (uint32_t index : *node->inputs()) { |
| inputs.push_back(tensors[index]); |
| } |
| op->AddInputTensors(inputs); |
| |
| // qcir output tensors to OpWrapper output tensors |
| std::vector<std::shared_ptr<TensorWrapper>> outputs; |
| for (uint32_t index : *node->outputs()) { |
| outputs.push_back(tensors[index]); |
| } |
| op->AddOutputTensors(outputs); |
| |
| // qcir operator param to OpWrapper param |
| for (uint32_t index : *node->params()) { |
| const auto& tensor = graph->tensors()->Get(index); |
| std::string name = tensor->name()->str(); |
| Qnn_DataType_t dtype = ToDataType(tensor->dtype()); |
| if (tensor->shape()->size() != 0) { |
| // add tensor param |
| op->AddTensorParam( |
| name, |
| dtype, |
| tensor->shape()->size(), |
| tensor->shape()->data(), |
| tensor->data()->data()); |
| } else { |
| // add scalar param |
| switch (dtype) { |
| case Qnn_DataType_t::QNN_DATATYPE_INT_32: |
| op->AddScalarParam( |
| name, |
| dtype, |
| *reinterpret_cast<const int32_t*>(tensor->data()->Data())); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_INT_16: |
| op->AddScalarParam( |
| name, |
| dtype, |
| *reinterpret_cast<const int16_t*>(tensor->data()->Data())); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_INT_8: |
| op->AddScalarParam( |
| name, dtype, static_cast<int8_t>(*tensor->data()->Data())); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_UINT_32: |
| op->AddScalarParam( |
| name, |
| dtype, |
| *reinterpret_cast<const uint32_t*>(tensor->data()->Data())); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_UINT_16: |
| op->AddScalarParam( |
| name, |
| dtype, |
| *reinterpret_cast<const uint16_t*>(tensor->data()->Data())); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_UINT_8: |
| op->AddScalarParam(name, dtype, *tensor->data()->Data()); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_FLOAT_32: |
| case Qnn_DataType_t::QNN_DATATYPE_FLOAT_16: |
| op->AddScalarParam( |
| name, |
| dtype, |
| *reinterpret_cast<const float*>(tensor->data()->Data())); |
| break; |
| case Qnn_DataType_t::QNN_DATATYPE_BOOL_8: |
| op->AddScalarParam(name, dtype, *tensor->data()->Data()); |
| break; |
| default: |
| QNN_EXECUTORCH_LOG_ERROR( |
| "Invalid scalar type: %s", tensor->name()->c_str()); |
| break; |
| } |
| } |
| } |
| op_wrappers.push_back(std::move(op)); |
| } |
| |
| ET_CHECK_OR_RETURN_ERROR( |
| Compile(graph->name()->str(), op_wrappers) == Error::Ok, |
| Internal, |
| "Fail to compile graph from qcir with graph_name: %s", |
| graph->name()->str().c_str()); |
| |
| ET_CHECK_OR_RETURN_ERROR( |
| AllocateTensor(graph->name()->str(), graph_inputs, graph_outputs) == |
| Error::Ok, |
| Internal, |
| "Fail to allocate tensor for qcir with graph_name: %s", |
| graph->name()->str().c_str()); |
| } |
| |
| return Error::Ok; |
| } |
| |
| Error QnnManager::Compile( |
| const std::string& graph_name, |
| std::vector<std::shared_ptr<OpWrapper>>& op_wrappers) { |
| Qnn_ErrorHandle_t error = QNN_SUCCESS; |
| |
| for (std::shared_ptr<OpWrapper>& op_wrapper : op_wrappers) { |
| for (const auto& tensor_wrapper : op_wrapper->GetInputTensors()) { |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_graph_ptr_->EnsureTensorInQnnGraph( |
| graph_name, tensor_wrapper) == Error::Ok, |
| Internal, |
| "Tensor name %s isn't added to Qnn Graph", |
| tensor_wrapper->GetName().c_str()); |
| } |
| |
| for (const auto& tensor_wrapper : op_wrapper->GetOutputTensors()) { |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_graph_ptr_->EnsureTensorInQnnGraph( |
| graph_name, tensor_wrapper) == Error::Ok, |
| Internal, |
| "Tensor name %s isn't added to Qnn Graph", |
| tensor_wrapper->GetName().c_str()); |
| } |
| |
| for (const auto& param : op_wrapper->GetParams()) { |
| auto* p_tensor_param = dynamic_cast<TensorParamWrapper*>(param.get()); |
| if (p_tensor_param != nullptr) { |
| ET_CHECK_OR_RETURN_ERROR( |
| backend_params_ptr_->qnn_graph_ptr_->EnsureTensorInQnnGraph( |
| graph_name, p_tensor_param->GetTensorWrapper()) == Error::Ok, |
| Internal, |
| "Param tensor name %s isn't added to Qnn Graph", |
| p_tensor_param->GetName().c_str()); |
| } |
| ET_CHECK_OR_RETURN_ERROR( |
| param->PopulateQnnParam() == Error::Ok, |
| Internal, |
| "Fail to configure Qnn backend"); |
| } |
| |
| error = backend_params_ptr_->qnn_graph_ptr_->GraphAddNode( |
| graph_name, op_wrapper->GetOpConfig()); |
| if (error != QNN_SUCCESS) { |
| QNN_EXECUTORCH_LOG_ERROR( |
| "Failed to add node to Qnn Graph with error: %d", |
| QNN_GET_ERROR_CODE(error)); |
| return Error::Internal; |
| } |
| } |
| |
| error = backend_params_ptr_->qnn_graph_ptr_->GraphFinalize(graph_name); |
| if (error != QNN_SUCCESS) { |
| QNN_EXECUTORCH_LOG_ERROR( |
| "Failed to finalize Qnn Graph with error: %d", |
| QNN_GET_ERROR_CODE(error)); |
| return Error::Internal; |
| } |
| |
| return Error::Ok; |
| } |
| |
| std::string QnnManager::GetBinarySignature() { |
| flatbuffers::Verifier verifier( |
| static_cast<const uint8_t* const>(qnn_context_blob_.buffer), |
| qnn_context_blob_.nbytes); |
| return VerifyBinaryInfoBuffer(verifier) |
| ? GetBinaryInfo(qnn_context_blob_.buffer)->signature()->str() |
| : ""; |
| } |
| |
| } // namespace qnn |
| } // namespace backends |
| } // namespace executorch |
| void* QnnExecuTorchAllocCustomMem(size_t bytes, size_t alignment) { |
| void* buffer_ptr = |
| executorch::backends::qnn::SharedBuffer::GetSharedBufferManager() |
| .AllocMem(bytes, alignment); |
| return buffer_ptr; |
| } |
| |
| void QnnExecuTorchFreeCustomMem(void* buffer_ptr) { |
| executorch::backends::qnn::SharedBuffer::GetSharedBufferManager().FreeMem( |
| buffer_ptr); |
| } |
| |
| void QnnExecuTorchAddCustomMemTensorAddr(void* tensor_addr, void* custom_mem) { |
| executorch::backends::qnn::SharedBuffer::GetSharedBufferManager() |
| .AddCusomMemTensorAddr(tensor_addr, custom_mem); |
| } |
| |
| void QnnExecuTorchAddCustomMemTensorInfo(const CustomMemTensorInfo& info) { |
| executorch::backends::qnn::SharedBuffer::GetSharedBufferManager() |
| .AddCusomMemTensorInfo(info); |
| } |