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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
96 changes: 83 additions & 13 deletions extension/android/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,25 +115,83 @@ if(TARGET qnn_executorch_backend)
list(APPEND link_libraries qnn_executorch_backend)
endif()

# EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED emit a backend as its own .so so a
# consumer can drop the ones it does not use. A split backend still links
# executorch_core statically, so it carries a second copy of the backend
# registry (registered_backends[] in runtime/backend/interface.cpp).
# Registration reaches the runtime's copy only through PLT interposition: the
# register_backend call in the backend .so is a JUMP_SLOT relocation, so the
# dynamic linker binds it to the first definition in the global lookup scope.
# Two things have to hold for that to be this library's copy:
#
# * libexecutorch_jni.so has to export register_backend. The default version
# script hides everything but JNI_OnLoad/Java_*, so jni/version_script.txt is
# swapped for jni/version_script_shared_backends.txt below whenever a backend
# is split out.
# * The consumer has to load libexecutorch_jni.so ahead of the backend .so in
# the same lookup scope. Naming both in the link line of the library that uses
# them, runtime first, does that: the runtime lands earlier in DT_NEEDED and
# wins the lookup. dlopen()ing a backend .so on its own does not, and the
# backend then registers into its own table while the runtime reports
# DelegateNotFound.
#
# The same interposition is what keeps a split XNNPACK on one thread pool: the
# pthreadpool symbols are exported from here too, so the backend .so binds to
# the pool extension_threadpool configures instead of a second, unconfigured
# copy of its own.
if(TARGET xnnpack_backend)
executorch_target_link_options_shared_lib(xnnpack_backend)
list(
APPEND
link_libraries
xnnpack_backend
XNNPACK
pthreadpool
cpuinfo
xnnpack-microkernels-prod
set(_xnnpack_so_link_libs xnnpack_backend XNNPACK pthreadpool cpuinfo
xnnpack-microkernels-prod
)
if(TARGET kleidiai)
list(APPEND link_libraries kleidiai)
list(APPEND _xnnpack_so_link_libs kleidiai)
endif()

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# Whole-archive the backend so its register_backend static initializer
# fires, and take only the XNNPACK third-party libs with it. The kernel
# registration archives stay in libexecutorch_jni.so, so loading both
# libraries together does not register a kernel twice
# (Error::RegistrationAlreadyRegistered, 0x16).
add_library(
xnnpack_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(xnnpack_backend)
target_link_options_gc_sections(xnnpack_executorch_backend)
target_link_libraries(
xnnpack_executorch_backend PRIVATE ${_xnnpack_so_link_libs}
executorch_core log
)
# executorch_jni still needs cpuinfo headers / lib for thread count
# detection.
list(APPEND link_libraries cpuinfo)
else()
executorch_target_link_options_shared_lib(xnnpack_backend)
list(APPEND link_libraries ${_xnnpack_so_link_libs})
endif()
endif()

if(TARGET vulkan_backend)
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
if(EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED)
# Same shape as the XNNPACK split above: whole-archive vulkan_backend and
# take only vulkan_schema with it, leaving the kernel registration archives
# in libexecutorch_jni.so.
add_library(
vulkan_executorch_backend SHARED
${EXECUTORCH_ROOT}/extension/android/jni/jni_noop.cpp
)
executorch_target_link_options_shared_lib(vulkan_backend)
target_link_options_gc_sections(vulkan_executorch_backend)
target_link_libraries(
vulkan_executorch_backend PRIVATE vulkan_backend vulkan_schema
executorch_core log
)
else()
executorch_target_link_options_shared_lib(vulkan_backend)
list(APPEND link_libraries vulkan_backend)
endif()
endif()

if(EXECUTORCH_BUILD_KERNELS_LLM)
Expand DownExpand Up@@ -242,9 +300,21 @@ target_include_directories(

target_compile_options(executorch_jni PUBLIC ${_common_compile_options})

if(EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
OR EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
)
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script_shared_backends.txt"
)
else()
set(_executorch_jni_version_script
"${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
)
endif()

target_link_options(
executorch_jni PRIVATE
"LINKER:--version-script,${CMAKE_CURRENT_SOURCE_DIR}/jni/version_script.txt"
"LINKER:--version-script,${_executorch_jni_version_script}"
)
target_link_options_gc_sections(executorch_jni)

Expand Down
11 changes: 11 additions & 0 deletions extension/android/jni/jni_noop.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* 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.
*/

// Empty translation unit so SHARED libraries that only need to whole-archive
// other static archives (e.g. libvulkan_executorch_backend.so) have at least
// one source file for CMake to invoke the linker on.
23 changes: 23 additions & 0 deletions extension/android/jni/version_script_shared_backends.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
{
global:
JNI_OnLoad;
Java_*;
# A backend built as its own .so (EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED /
# EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED) carries its own statically linked
# copy of executorch_core, and therefore its own backend registry. Its call
# to register_backend goes through the PLT, so exporting the registry entry
# points here lets the dynamic linker bind that call to this library's
# registry - the one the runtime actually reads from.
extern "C++" {
executorch::runtime*::register_backend*;
executorch::runtime*::get_backend_class*;
executorch::runtime*::get_backend_name*;
executorch::runtime*::get_num_registered_backends*;
};
# Same reason, for the thread pool the split XNNPACK backend runs on: bind
# its pthreadpool calls to the pool extension_threadpool configures here
# rather than to a second, unconfigured copy inside the backend .so.
pthreadpool_*;
local:
*;
};
6 changes: 5 additions & 1 deletion extension/llm/custom_ops/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,7 +67,11 @@ endif()

list(TRANSFORM _custom_ops__srcs PREPEND "${EXECUTORCH_ROOT}/")

if(NOT EXECUTORCH_BUILD_XNNPACK)
if(NOT EXECUTORCH_BUILD_XNNPACK OR EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED)
# custom_ops doesn't actually call into xnnpack_backend (no XNNPACK refs in
# its sources). The transitive PUBLIC link is what was previously dragging
# XNNCompiler/XNNExecutor into libexecutorch_jni.so via the WHOLE_ARCHIVE on
# custom_ops, which defeats EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED. Drop it.
list(APPEND custom_ops_libs extension_threadpool)
else()
list(APPEND custom_ops_libs extension_threadpool xnnpack_backend)
Expand Down
17 changes: 16 additions & 1 deletion scripts/build_android_library.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ build_android_native_library() {
fi

EXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN:-OFF}"
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED:-OFF}"
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED:-OFF}"
XNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2:-ON}"

cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
Expand All@@ -52,6 +54,8 @@ build_android_native_library() {
-DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
-DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
-DEXECUTORCH_BUILD_VULKAN="${EXECUTORCH_BUILD_VULKAN}" \
-DEXECUTORCH_BUILD_VULKAN_BACKEND_SHARED="${EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED}" \
-DEXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED="${EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED}" \
-DXNNPACK_ENABLE_ARM_SME2="${XNNPACK_ENABLE_ARM_SME2}" \
-DFLATCC_ALLOW_WERROR=OFF \
-DSUPPORT_REGEX_LOOKAHEAD=ON \
Expand All@@ -68,7 +72,18 @@ build_android_native_library() {
# Copy artifacts to ABI specific directory
local SO_STAGE_DIR="cmake-out-android-so/${ANDROID_ABI}"
mkdir -p ${SO_STAGE_DIR}
cp "${CMAKE_OUT}"/extension/android/*.so "${SO_STAGE_DIR}/libexecutorch.so"
cp "${CMAKE_OUT}"/extension/android/libexecutorch_jni.so "${SO_STAGE_DIR}/libexecutorch.so"

# Copy standalone Vulkan backend shared library if built. Used by React
# Native Executorch as an opt-in artifact when the app enables Vulkan.
if [ -f "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libvulkan_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Same for XNNPACK as a standalone shared library.
if [ -f "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so ]; then
cp "${CMAKE_OUT}"/extension/android/libxnnpack_executorch_backend.so "${SO_STAGE_DIR}/"
fi

# Copy QNN related so library
if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
Expand Down
21 changes: 21 additions & 0 deletions tools/cmake/preset/default.cmake
Original file line numberDiff line numberDiff line change
Expand Up@@ -171,6 +171,18 @@ define_overridable_option(
define_overridable_option(
EXECUTORCH_BUILD_VULKAN "Build the Vulkan backend" BOOL OFF
)
define_overridable_option(
EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED
"Build the XNNPACK backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED
"Build the Vulkan backend as its own shared library instead of linking it into the Android JNI library"
BOOL
OFF
)
define_overridable_option(
EXECUTORCH_BUILD_WEBGPU "Build the WebGPU backend" BOOL OFF
)
Expand DownExpand Up@@ -357,6 +369,15 @@ check_required_options_on(
IF_ON EXECUTORCH_BUILD_QNN REQUIRES EXECUTORCH_BUILD_EXTENSION_TENSOR
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED REQUIRES
EXECUTORCH_BUILD_XNNPACK
)

check_required_options_on(
IF_ON EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED REQUIRES EXECUTORCH_BUILD_VULKAN
)

check_required_options_on(
IF_ON EXECUTORCH_ENABLE_BUNDLE_IO REQUIRES EXECUTORCH_BUILD_DEVTOOLS
)
Expand Down
Loading