1*523fa7a6SAndroid Build Coastguard Worker /* 2*523fa7a6SAndroid Build Coastguard Worker * Copyright (c) Meta Platforms, Inc. and affiliates. 3*523fa7a6SAndroid Build Coastguard Worker * All rights reserved. 4*523fa7a6SAndroid Build Coastguard Worker * 5*523fa7a6SAndroid Build Coastguard Worker * This source code is licensed under the BSD-style license found in the 6*523fa7a6SAndroid Build Coastguard Worker * LICENSE file in the root directory of this source tree. 7*523fa7a6SAndroid Build Coastguard Worker */ 8*523fa7a6SAndroid Build Coastguard Worker 9*523fa7a6SAndroid Build Coastguard Worker #include <executorch/backends/xnnpack/runtime/XNNCompiler.h> 10*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/backend/interface.h> 11*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/core/error.h> 12*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/core/evalue.h> 13*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/platform/profiler.h> 14*523fa7a6SAndroid Build Coastguard Worker 15*523fa7a6SAndroid Build Coastguard Worker #include <memory> 16*523fa7a6SAndroid Build Coastguard Worker #include <mutex> 17*523fa7a6SAndroid Build Coastguard Worker 18*523fa7a6SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wglobal-constructors" 19*523fa7a6SAndroid Build Coastguard Worker 20*523fa7a6SAndroid Build Coastguard Worker namespace executorch { 21*523fa7a6SAndroid Build Coastguard Worker namespace backends { 22*523fa7a6SAndroid Build Coastguard Worker 23*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::ArrayRef; 24*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::Backend; 25*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::BackendExecutionContext; 26*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::BackendInitContext; 27*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::CompileSpec; 28*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::DelegateHandle; 29*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::Error; 30*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::EValue; 31*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::FreeableBuffer; 32*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::Result; 33*523fa7a6SAndroid Build Coastguard Worker 34*523fa7a6SAndroid Build Coastguard Worker class XnnpackBackend final : public ::executorch::runtime::BackendInterface { 35*523fa7a6SAndroid Build Coastguard Worker public: 36*523fa7a6SAndroid Build Coastguard Worker ~XnnpackBackend() = default; 37*523fa7a6SAndroid Build Coastguard Worker XnnpackBackend()38*523fa7a6SAndroid Build Coastguard Worker XnnpackBackend() { 39*523fa7a6SAndroid Build Coastguard Worker // Initialize XNNPACK 40*523fa7a6SAndroid Build Coastguard Worker xnn_status status = xnn_initialize(/*allocator=*/nullptr); 41*523fa7a6SAndroid Build Coastguard Worker if (status != xnn_status_success) { 42*523fa7a6SAndroid Build Coastguard Worker ET_LOG( 43*523fa7a6SAndroid Build Coastguard Worker Error, 44*523fa7a6SAndroid Build Coastguard Worker "Failed to initialize, XNNPACK status: 0x%x", 45*523fa7a6SAndroid Build Coastguard Worker (unsigned int)status); 46*523fa7a6SAndroid Build Coastguard Worker return; 47*523fa7a6SAndroid Build Coastguard Worker } 48*523fa7a6SAndroid Build Coastguard Worker 49*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_SHARED_WORKSPACE 50*523fa7a6SAndroid Build Coastguard Worker // Create a workspace for the XNNExecutor to use. This workspace will be 51*523fa7a6SAndroid Build Coastguard Worker // shared across all delegate instances. 52*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Debug, "Creating XNN workspace"); 53*523fa7a6SAndroid Build Coastguard Worker xnn_workspace_t workspace = nullptr; 54*523fa7a6SAndroid Build Coastguard Worker status = xnn_create_workspace(&workspace); 55*523fa7a6SAndroid Build Coastguard Worker if (status != xnn_status_success) { 56*523fa7a6SAndroid Build Coastguard Worker ET_LOG( 57*523fa7a6SAndroid Build Coastguard Worker Error, 58*523fa7a6SAndroid Build Coastguard Worker "Failed to create XNN workspace, XNNPACK status: 0x%x", 59*523fa7a6SAndroid Build Coastguard Worker (unsigned int)status); 60*523fa7a6SAndroid Build Coastguard Worker workspace = nullptr; 61*523fa7a6SAndroid Build Coastguard Worker return; 62*523fa7a6SAndroid Build Coastguard Worker } 63*523fa7a6SAndroid Build Coastguard Worker workspace_.reset(workspace); 64*523fa7a6SAndroid Build Coastguard Worker ET_LOG(Debug, "Created XNN workspace: %p", workspace_.get()); 65*523fa7a6SAndroid Build Coastguard Worker #endif // ENABLE_XNNPACK_SHARED_WORKSPACE 66*523fa7a6SAndroid Build Coastguard Worker } 67*523fa7a6SAndroid Build Coastguard Worker is_available() const68*523fa7a6SAndroid Build Coastguard Worker bool is_available() const override { 69*523fa7a6SAndroid Build Coastguard Worker return xnn_status_success == xnn_initialize(/*allocator=*/nullptr); 70*523fa7a6SAndroid Build Coastguard Worker } 71*523fa7a6SAndroid Build Coastguard Worker init(BackendInitContext & context,FreeableBuffer * processed,ArrayRef<CompileSpec> compile_specs) const72*523fa7a6SAndroid Build Coastguard Worker Result<DelegateHandle*> init( 73*523fa7a6SAndroid Build Coastguard Worker BackendInitContext& context, 74*523fa7a6SAndroid Build Coastguard Worker FreeableBuffer* processed, 75*523fa7a6SAndroid Build Coastguard Worker ArrayRef<CompileSpec> compile_specs) const override { 76*523fa7a6SAndroid Build Coastguard Worker auto executor = ET_ALLOCATE_INSTANCE_OR_RETURN_ERROR( 77*523fa7a6SAndroid Build Coastguard Worker context.get_runtime_allocator(), xnnpack::delegate::XNNExecutor); 78*523fa7a6SAndroid Build Coastguard Worker 79*523fa7a6SAndroid Build Coastguard Worker // Executor has been allocated but not constructed, ensure that runtime_ is 80*523fa7a6SAndroid Build Coastguard Worker // nullptr by constructing it in place here. NOTE: Since we use placement 81*523fa7a6SAndroid Build Coastguard Worker // new and since this type is not trivially destructible, we must call the 82*523fa7a6SAndroid Build Coastguard Worker // destructor manually in destroy(). 83*523fa7a6SAndroid Build Coastguard Worker new (executor) xnnpack::delegate::XNNExecutor; 84*523fa7a6SAndroid Build Coastguard Worker Error err = xnnpack::delegate::XNNCompiler::compileModel( 85*523fa7a6SAndroid Build Coastguard Worker processed->data(), 86*523fa7a6SAndroid Build Coastguard Worker processed->size(), 87*523fa7a6SAndroid Build Coastguard Worker executor, 88*523fa7a6SAndroid Build Coastguard Worker context.get_runtime_allocator(), 89*523fa7a6SAndroid Build Coastguard Worker workspace_.get()); 90*523fa7a6SAndroid Build Coastguard Worker // This backend does not need its processed data after compiling the model. 91*523fa7a6SAndroid Build Coastguard Worker processed->Free(); 92*523fa7a6SAndroid Build Coastguard Worker 93*523fa7a6SAndroid Build Coastguard Worker if (err != Error::Ok) { 94*523fa7a6SAndroid Build Coastguard Worker // destroy() won't be called on this handle, so we need to clean it up 95*523fa7a6SAndroid Build Coastguard Worker // now. 96*523fa7a6SAndroid Build Coastguard Worker executor->~XNNExecutor(); 97*523fa7a6SAndroid Build Coastguard Worker 98*523fa7a6SAndroid Build Coastguard Worker ET_LOG( 99*523fa7a6SAndroid Build Coastguard Worker Error, "XNNCompiler::compileModel failed: 0x%x", (unsigned int)err); 100*523fa7a6SAndroid Build Coastguard Worker return err; 101*523fa7a6SAndroid Build Coastguard Worker } 102*523fa7a6SAndroid Build Coastguard Worker return executor; 103*523fa7a6SAndroid Build Coastguard Worker } 104*523fa7a6SAndroid Build Coastguard Worker execute(BackendExecutionContext & context,DelegateHandle * handle,EValue ** args) const105*523fa7a6SAndroid Build Coastguard Worker Error execute( 106*523fa7a6SAndroid Build Coastguard Worker BackendExecutionContext& context, 107*523fa7a6SAndroid Build Coastguard Worker DelegateHandle* handle, 108*523fa7a6SAndroid Build Coastguard Worker EValue** args) const override { 109*523fa7a6SAndroid Build Coastguard Worker auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle); 110*523fa7a6SAndroid Build Coastguard Worker 111*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_SHARED_WORKSPACE 112*523fa7a6SAndroid Build Coastguard Worker const std::lock_guard<std::mutex> lock(workspace_mutex_); 113*523fa7a6SAndroid Build Coastguard Worker #endif 114*523fa7a6SAndroid Build Coastguard Worker 115*523fa7a6SAndroid Build Coastguard Worker // Prepare Inputs/Outputs and Propagate Input Shapes 116*523fa7a6SAndroid Build Coastguard Worker Error err = executor->prepare_args(args); 117*523fa7a6SAndroid Build Coastguard Worker if (err != Error::Ok) { 118*523fa7a6SAndroid Build Coastguard Worker return err; 119*523fa7a6SAndroid Build Coastguard Worker } 120*523fa7a6SAndroid Build Coastguard Worker 121*523fa7a6SAndroid Build Coastguard Worker err = executor->forward(context); 122*523fa7a6SAndroid Build Coastguard Worker 123*523fa7a6SAndroid Build Coastguard Worker if (err != Error::Ok) { 124*523fa7a6SAndroid Build Coastguard Worker return err; 125*523fa7a6SAndroid Build Coastguard Worker } 126*523fa7a6SAndroid Build Coastguard Worker 127*523fa7a6SAndroid Build Coastguard Worker // Resize outputs and recast pointers if necessary 128*523fa7a6SAndroid Build Coastguard Worker err = executor->resize_outputs(args); 129*523fa7a6SAndroid Build Coastguard Worker 130*523fa7a6SAndroid Build Coastguard Worker return err; 131*523fa7a6SAndroid Build Coastguard Worker } 132*523fa7a6SAndroid Build Coastguard Worker destroy(DelegateHandle * handle) const133*523fa7a6SAndroid Build Coastguard Worker void destroy(DelegateHandle* handle) const override { 134*523fa7a6SAndroid Build Coastguard Worker if (handle != nullptr) { 135*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_SHARED_WORKSPACE 136*523fa7a6SAndroid Build Coastguard Worker // This is needed to serialize access to xnn_delete_runtime which is not 137*523fa7a6SAndroid Build Coastguard Worker // thread safe. This can heppen when multiple threads call destroy() on 138*523fa7a6SAndroid Build Coastguard Worker // the same backend instance. 139*523fa7a6SAndroid Build Coastguard Worker const std::lock_guard<std::mutex> lock(workspace_mutex_); 140*523fa7a6SAndroid Build Coastguard Worker #endif 141*523fa7a6SAndroid Build Coastguard Worker auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle); 142*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_PROFILING 143*523fa7a6SAndroid Build Coastguard Worker executor->print_avg_op_timings(); 144*523fa7a6SAndroid Build Coastguard Worker #endif 145*523fa7a6SAndroid Build Coastguard Worker // XNNExecutor is not trivially destructible. Since this was constructed 146*523fa7a6SAndroid Build Coastguard Worker // manually in init(), we must destroy it manually here. 147*523fa7a6SAndroid Build Coastguard Worker executor->~XNNExecutor(); 148*523fa7a6SAndroid Build Coastguard Worker } 149*523fa7a6SAndroid Build Coastguard Worker } 150*523fa7a6SAndroid Build Coastguard Worker 151*523fa7a6SAndroid Build Coastguard Worker private: 152*523fa7a6SAndroid Build Coastguard Worker // This is a global workspace for all delegate instances. 153*523fa7a6SAndroid Build Coastguard Worker mutable std::mutex workspace_mutex_; 154*523fa7a6SAndroid Build Coastguard Worker std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> workspace_{ 155*523fa7a6SAndroid Build Coastguard Worker nullptr, 156*523fa7a6SAndroid Build Coastguard Worker &xnn_release_workspace}; 157*523fa7a6SAndroid Build Coastguard Worker }; 158*523fa7a6SAndroid Build Coastguard Worker 159*523fa7a6SAndroid Build Coastguard Worker namespace { 160*523fa7a6SAndroid Build Coastguard Worker auto cls = XnnpackBackend(); 161*523fa7a6SAndroid Build Coastguard Worker Backend backend{"XnnpackBackend", &cls}; 162*523fa7a6SAndroid Build Coastguard Worker static auto success_with_compiler = register_backend(backend); 163*523fa7a6SAndroid Build Coastguard Worker } // namespace 164*523fa7a6SAndroid Build Coastguard Worker 165*523fa7a6SAndroid Build Coastguard Worker } // namespace backends 166*523fa7a6SAndroid Build Coastguard Worker } // namespace executorch 167