Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .ci/scripts/wheel/test_cpp_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@
# the operators are registered twice, which aborts at startup.
_KERNEL_SYMBOLS = ("torch::executor::native::abs_out",)

# A representative symbol from the XNNPACK delegate. A second definer means the
# process carries two copies of the delegate.
_XNNPACK_SYMBOLS = (
"executorch::backends::xnnpack::XnnpackBackendOptions::workspace_manager",
)

# `nm -DC` prints "<hexaddr> <kind> <name>" for a definition and
# " U <name>" for an undefined reference.
_DEFINED = re.compile(r"^[0-9a-fA-F]+\s+(?P<kind>[A-Za-z])\s+(?P<name>.+)$")
Expand Down Expand Up @@ -148,6 +154,11 @@ def test_single_kernel_registration() -> None:
_assert_single_definer(_KERNEL_SYMBOLS, "set of CPU kernels")


def test_single_xnnpack_delegate() -> None:
"""Exactly one shipped library may define the XNNPACK delegate."""
_assert_single_definer(_XNNPACK_SYMBOLS, "XNNPACK delegate")


def test_cpp_consumer(work_dir: Path) -> None:
"""A standalone C++ app builds and runs against the installed wheel."""
assert shutil.which("cmake") is not None, "cmake is required to build a consumer"
Expand Down Expand Up @@ -249,4 +260,5 @@ def run_tests(work_dir: Path) -> None:
test_single_backend_registry()
test_single_threadpool()
test_single_kernel_registration()
test_single_xnnpack_delegate()
test_cpp_consumer(work_dir)
31 changes: 20 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1202,9 +1202,15 @@ if(EXECUTORCH_BUILD_PYBIND)
endif()

if(EXECUTORCH_BUILD_XNNPACK)
# need to explicitly specify XNNPACK and xnnpack-microkernels-prod here
# otherwise uses XNNPACK and microkernel-prod symbols from libtorch_cpu
list(APPEND _dep_libs xnnpack_backend XNNPACK xnnpack-microkernels-prod)
if(EXECUTORCH_BUILD_SHARED)
# The delegate bundles XNNPACK and its microkernels, so naming them again
# here would ship a second copy.
list(APPEND _dep_libs xnnpack_backend)
Comment thread
shoumikhin marked this conversation as resolved.
else()
# need to explicitly specify XNNPACK and xnnpack-microkernels-prod here
# otherwise uses XNNPACK and microkernel-prod symbols from libtorch_cpu
list(APPEND _dep_libs xnnpack_backend XNNPACK xnnpack-microkernels-prod)
endif()
endif()

if(EXECUTORCH_BUILD_VULKAN)
Expand Down Expand Up @@ -1259,14 +1265,17 @@ if(EXECUTORCH_BUILD_PYBIND)
target_compile_options(portable_lib PUBLIC ${_pybind_compile_options})
target_link_libraries(portable_lib PRIVATE ${_dep_libs})
executorch_target_link_shared_runtime(portable_lib)
# The operators register themselves from a static initializer, so nothing here
# references a symbol from the kernels library and some linkers drop it, which
# surfaces at runtime as a missing kernel rather than a link error.
if(TARGET optimized_native_cpu_ops_lib)
executorch_target_retain_shared_library(
portable_lib optimized_native_cpu_ops_lib
)
endif()
# These libraries register their operators or their backend from a static
# initializer, so nothing here references a symbol from them and some linkers
# drop them from DT_NEEDED. That surfaces at runtime as a missing kernel or an
# unregistered backend rather than as a link error.
foreach(_retained_component optimized_native_cpu_ops_lib xnnpack_backend)
if(TARGET ${_retained_component})
executorch_target_retain_shared_library(
portable_lib ${_retained_component}
)
endif()
endforeach()

# Set RPATH to find PyTorch and backend libraries relative to the installation
# location. This goes from executorch/extension/pybindings up to
Expand Down
34 changes: 32 additions & 2 deletions backends/xnnpack/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,41 @@ set(xnnpack_third_party pthreadpool extension_threadpool cpuinfo)
include(cmake/Dependencies.cmake)

list(TRANSFORM _xnnpack_backend__srcs PREPEND "${EXECUTORCH_ROOT}/")
add_library(xnnpack_backend ${_xnnpack_backend__srcs})
# Build the delegate as a shared library for the wheel so a process has one copy
# of it, and keep it static everywhere else so no other build changes.
if(EXECUTORCH_BUILD_SHARED)
set(_xnnpack_backend_library_type SHARED)
else()
set(_xnnpack_backend_library_type STATIC)
endif()
add_library(
xnnpack_backend ${_xnnpack_backend_library_type} ${_xnnpack_backend__srcs}
)
target_link_libraries(
xnnpack_backend PUBLIC ${xnnpack_third_party} executorch_core xnnpack_schema
xnnpack_backend PUBLIC ${xnnpack_third_party} xnnpack_schema
extension_threadpool
)
if(EXECUTORCH_BUILD_SHARED)
Comment thread
shoumikhin marked this conversation as resolved.
set_target_properties(
xnnpack_backend
PROPERTIES OUTPUT_NAME executorch_xnnpack_backend
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION_MAJOR}"
)
# XNNPACK and its microkernels are forced static, so bundle them inside this
# library instead of making every consumer supply them.
executorch_target_whole_archive(xnnpack_backend XNNPACK)
executorch_target_whole_archive(xnnpack_backend xnnpack-microkernels-prod)
target_link_libraries(xnnpack_backend PUBLIC executorch_shared)
if(NOT APPLE)
# Ships beside the runtime in the wheel's lib/ directory.
set_target_properties(
xnnpack_backend PROPERTIES BUILD_RPATH "$ORIGIN" INSTALL_RPATH "$ORIGIN"
)
endif()
else()
target_link_libraries(xnnpack_backend PUBLIC executorch_core)
endif()
target_include_directories(
xnnpack_backend PUBLIC ${_common_include_directories}
)
Expand Down
17 changes: 17 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,23 @@ def run(self): # noqa C901
),
dependent_cmake_flags=["EXECUTORCH_BUILD_SHARED"],
),
# Install the XNNPACK delegate beside them, so a process has one
# copy of it instead of one per component that uses it.
BuiltFile(
src_dir="%CMAKE_CACHE_DIR%/backends/xnnpack/",
src_name=(
"libexecutorch_xnnpack_backend.so."
f"{get_runtime_soname_major()}.*"
),
dst=(
"executorch/lib/libexecutorch_xnnpack_backend.so."
f"{get_runtime_soname_major()}"
),
dependent_cmake_flags=[
"EXECUTORCH_BUILD_SHARED",
"EXECUTORCH_BUILD_XNNPACK",
],
),
# Install the prebuilt pybindings extension wrapper for the runtime,
# portable kernels, and a selection of backends. This lets users
# load and execute .pte files from python.
Expand Down
Loading