diff --git a/CMakeLists.txt b/CMakeLists.txt index fc427d517a9..ced04c54b95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -738,9 +738,6 @@ endif() if(EXECUTORCH_BUILD_PYBIND) - # Add codegen tools subdirectory for selective_build pybind module - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/codegen/tools) - if(NOT EXECUTORCH_BUILD_EXTENSION_DATA_LOADER) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extension/data_loader) endif() @@ -749,6 +746,9 @@ if(EXECUTORCH_BUILD_PYBIND) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/devtools) endif() + # Add codegen tools subdirectory for selective_build pybind module + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/codegen/tools) + # Create bundled_module target only for pybindings when bundled_program exists # This target has hard dependencies on devtools generated headers if(TARGET bundled_program) diff --git a/codegen/tools/CMakeLists.txt b/codegen/tools/CMakeLists.txt index 489a96aafb6..1cc9f21fa79 100644 --- a/codegen/tools/CMakeLists.txt +++ b/codegen/tools/CMakeLists.txt @@ -1,5 +1,6 @@ # Copyright (c) Meta Platforms, Inc. and affiliates. # All rights reserved. +# Copyright 2025 Arm Limited and/or its affiliates. # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. @@ -28,6 +29,10 @@ target_compile_options( ) # Link against required libraries +if(TARGET bundled_program) + target_compile_definitions(selective_build PRIVATE -DET_BUNDLE_IO) + target_link_libraries(selective_build PRIVATE bundled_program) +endif() target_link_libraries(selective_build PRIVATE executorch_core program_schema) # Install the module diff --git a/codegen/tools/selective_build.cpp b/codegen/tools/selective_build.cpp index d33ff12ec9f..e1e23b4b0e4 100644 --- a/codegen/tools/selective_build.cpp +++ b/codegen/tools/selective_build.cpp @@ -1,16 +1,21 @@ /* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. + * Copyright 2025 Arm Limited and/or its affiliates. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ +#include +#include #include #include -#include -#include +#ifdef ET_BUNDLE_IO +#include +#include +#endif namespace py = pybind11; @@ -186,8 +191,39 @@ get_kernel_tensor_metadatas_from_execution_plan( const executorch_flatbuffer::Program* _get_program_from_buffer( const py::bytes& buffer) { + // Access the Python bytes without copying and get raw pointer/size. + const std::string_view sv = buffer.cast(); + void* buf_ptr = const_cast(static_cast(sv.data())); + const size_t buf_len = sv.size(); +#ifdef ET_BUNDLE_IO + + // If this is a bundled program, extract the inner ExecuTorch program bytes. + if (executorch::bundled_program::is_bundled_program(buf_ptr, buf_len)) { + const void* program_data = nullptr; + size_t program_size = 0; + + const auto status = executorch::bundled_program::get_program_data( + buf_ptr, // serialized BundledProgram start + buf_len, // total size of the BundledProgram blob + &program_data, // [out] pointer to inner .pte bytes + &program_size // [out] size of inner .pte bytes + ); + + if (status != ::executorch::runtime::Error::Ok || program_data == nullptr || + program_size == 0) { + throw std::runtime_error( + "bundled_program::get_program_data() failed or returned empty data"); + } + + // program_data points directly at the flatbuffer-encoded Program region. + return executorch_flatbuffer::GetProgram( + reinterpret_cast(program_data)); + } +#endif + // Otherwise treat the buffer as a raw .pte (flatbuffer Program with optional + // extended header). return executorch_flatbuffer::GetProgram( - buffer.cast().data()); + reinterpret_cast(sv.data())); } py::list _get_program_operators(const executorch_flatbuffer::Program* program) { diff --git a/docs/source/backends-arm-ethos-u.md b/docs/source/backends-arm-ethos-u.md index 0a5d1dded74..4b4cd625d6e 100644 --- a/docs/source/backends-arm-ethos-u.md +++ b/docs/source/backends-arm-ethos-u.md @@ -268,8 +268,7 @@ You can see how this coupling between the memory mode and runtime application i The arm_executor_runner supports [bundled-io](https://docs.pytorch.org/executorch/0.4/bundled-io.html) and [ETdump](https://docs.pytorch.org/executorch/stable/etdump.html) debugging tools. -To enable bundled-io, set `EXECUTORCH_BUILD_DEVTOOLS` when building Executorch and `DET_BUNDLE_IO` when building the executor_runner. Currently using bundled-io requires specifying your -non delegated Aten ops manually by setting `EXECUTORCH_SELECT_OPS_LIST`. To enable ETdump, set `EXECUTORCH_BUILD_ARM_ETDUMP` when building Executorch and `DEXECUTORCH_ENABLE_EVENT_TRACER` +To enable bundled-io, set `EXECUTORCH_BUILD_DEVTOOLS` when building Executorch and `DET_BUNDLE_IO` when building the executor_runner. To enable ETdump, set `EXECUTORCH_BUILD_ARM_ETDUMP` when building Executorch and `DEXECUTORCH_ENABLE_EVENT_TRACER` when building the executor_runner. diff --git a/examples/arm/executor_runner/CMakeLists.txt b/examples/arm/executor_runner/CMakeLists.txt index 4e4a8eeb409..d5038a1a6b8 100644 --- a/examples/arm/executor_runner/CMakeLists.txt +++ b/examples/arm/executor_runner/CMakeLists.txt @@ -235,10 +235,10 @@ list( -Map=arm_executor_runner.map ) -# Prefer to generate kernel bindings from model file if possible, which is when -# 1. Not building for semihosting 2. Not building with bundleio If that is not -# the case, fallback to select_ops_list If the model file does not contain any -# aten ops, a workaround is currently needed to avoid crashing. +# Figure out which ops to include: For semihosting build, use +# (user-set)SELECT_OPS_MODEL variable. For normal build, use +# EXECUTORCH_SELECT_OPS_MODEL to include ops automatically. If the pte contains +# no undelegated ops, use neither. execute_process( COMMAND python "${ET_DIR_PATH}/codegen/tools/gen_oplist.py" @@ -264,11 +264,6 @@ elseif(${FOUND_OPS_IN_FILE}) message( "gen_oplist: EXECUTORCH_SELECT_OPS_MODEL=${ET_PTE_FILE_PATH} is used to auto generate ops from" ) -elseif(NOT ${FOUND_OPS_IN_FILE} AND ${ET_BUNDLE_IO}) - set(EXECUTORCH_SELECT_OPS_MODEL "") - message( - "gen_oplist: Building with ET_BUNDLE_IO and .bpte is not supported to auto generate ops from will use EXECUTORCH_SELECT_OPS_LIST=${EXECUTORCH_SELECT_OPS_LIST}" - ) else() set(EXECUTORCH_SELECT_OPS_LIST "") set(EXECUTORCH_SELECT_OPS_MODEL "") diff --git a/examples/arm/run.sh b/examples/arm/run.sh index 8f5dec85ad4..2e2184a1b0c 100755 --- a/examples/arm/run.sh +++ b/examples/arm/run.sh @@ -53,8 +53,8 @@ function help() { echo " --no_delegate Do not delegate the model (can't override builtin models)" echo " --no_quantize Do not quantize the model (can't override builtin models)" echo " --portable_kernels= TO BE DEPRECATED: Alias to select_ops_list." - echo " --select_ops_list= Comma separated list of portable (non delegated) kernels to include Default: ${select_ops_list}" - echo " NOTE: This is used when select_ops_model is not possible to use, e.g. for semihosting or bundleio." + echo " --select_ops_list= Comma separated list of portable (non delagated) kernels to include Default: ${select_ops_list}" + echo " NOTE: This is only used when building for semihosting." echo " See https://docs.pytorch.org/executorch/stable/kernel-library-selective-build.html for more information." echo " --target= Target to build and run for Default: ${target}" echo " --output= Target build output folder Default: ${output_folder}"