diff --git a/.github/workflows/linux-wasm-ci-build-and-test-workflow.yml b/.github/workflows/linux-wasm-ci-build-and-test-workflow.yml index e08ce13c6dd33..b996fefe0f9fb 100644 --- a/.github/workflows/linux-wasm-ci-build-and-test-workflow.yml +++ b/.github/workflows/linux-wasm-ci-build-and-test-workflow.yml @@ -95,7 +95,7 @@ jobs: --use_jsep \ --use_webnn \ --target onnxruntime_webassembly \ - ${{ inputs.build_config == 'Release' && '--enable_wasm_api_exception_catching' || '' }} \ + --enable_wasm_eh \ --skip_tests working-directory: ${{ github.workspace }} @@ -108,7 +108,7 @@ jobs: --use_webgpu \ --use_webnn \ --target onnxruntime_webassembly \ - ${{ inputs.build_config == 'Release' && '--enable_wasm_api_exception_catching' || '' }} \ + --enable_wasm_eh \ --skip_tests working-directory: ${{ github.workspace }} @@ -121,6 +121,7 @@ jobs: --use_webgpu \ --use_webnn \ --enable_wasm_jspi \ + --enable_wasm_eh \ --target onnxruntime_webassembly \ --skip_tests working-directory: ${{ github.workspace }} diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index f2009c8575b51..4db0e367476e4 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -202,6 +202,7 @@ option(onnxruntime_ENABLE_WEBASSEMBLY_THREADS "Enable this option to create WebA option(onnxruntime_ENABLE_WEBASSEMBLY_EXCEPTION_CATCHING "Enable this option to turn on exception catching" OFF) option(onnxruntime_ENABLE_WEBASSEMBLY_API_EXCEPTION_CATCHING "Enable this option to turn on api exception catching" OFF) option(onnxruntime_ENABLE_WEBASSEMBLY_EXCEPTION_THROWING "Enable this option to turn on exception throwing even if the build disabled exceptions support" OFF) +option(onnxruntime_ENABLE_WEBASSEMBLY_NATIVE_EH "Enable native WebAssembly exception handling (-fwasm-exceptions) instead of legacy JavaScript-based exception support" OFF) option(onnxruntime_WEBASSEMBLY_RUN_TESTS_IN_BROWSER "Enable this option to run tests in browser instead of Node.js" OFF) option(onnxruntime_ENABLE_WEBASSEMBLY_DEBUG_INFO "Enable this option to turn on DWARF format debug info" OFF) option(onnxruntime_ENABLE_WEBASSEMBLY_PROFILING "Enable this option to turn on WebAssembly profiling and preserve function names" OFF) diff --git a/cmake/adjust_global_compile_flags.cmake b/cmake/adjust_global_compile_flags.cmake index c20c504564181..1f8a3d4e378f3 100644 --- a/cmake/adjust_global_compile_flags.cmake +++ b/cmake/adjust_global_compile_flags.cmake @@ -46,9 +46,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten") endif() # Enable WebAssembly exception catching. - if (onnxruntime_ENABLE_WEBASSEMBLY_JSPI) - string(APPEND CMAKE_C_FLAGS " -fwasm-exceptions -s WASM_LEGACY_EXCEPTIONS=0") - string(APPEND CMAKE_CXX_FLAGS " -fwasm-exceptions -s WASM_LEGACY_EXCEPTIONS=0") + if (onnxruntime_ENABLE_WEBASSEMBLY_NATIVE_EH) + string(APPEND CMAKE_C_FLAGS " -fwasm-exceptions") + string(APPEND CMAKE_CXX_FLAGS " -fwasm-exceptions") elseif (onnxruntime_ENABLE_WEBASSEMBLY_EXCEPTION_CATCHING) string(APPEND CMAKE_C_FLAGS " -s DISABLE_EXCEPTION_CATCHING=0") string(APPEND CMAKE_CXX_FLAGS " -s DISABLE_EXCEPTION_CATCHING=0") diff --git a/cmake/onnxruntime_webassembly.cmake b/cmake/onnxruntime_webassembly.cmake index fa93113c76160..7d1ee0562a8e2 100644 --- a/cmake/onnxruntime_webassembly.cmake +++ b/cmake/onnxruntime_webassembly.cmake @@ -329,7 +329,7 @@ else() endif() endif() - if (NOT onnxruntime_ENABLE_WEBASSEMBLY_JSPI) + if (NOT onnxruntime_ENABLE_WEBASSEMBLY_NATIVE_EH) # Set link flag to enable exceptions support, this will override default disabling exception throwing behavior when disable exceptions. target_link_options(onnxruntime_webassembly PRIVATE "SHELL:-s DISABLE_EXCEPTION_THROWING=0" diff --git a/js/build_jsep.bat b/js/build_jsep.bat index ace96e978d934..239ed2f586ccb 100644 --- a/js/build_jsep.bat +++ b/js/build_jsep.bat @@ -22,7 +22,7 @@ if ["%~1"]==["d"] ( ) if ["%~1"]==["r"] ( set CONFIG=Release - set CONFIG_EXTRA_FLAG=--enable_wasm_api_exception_catching --disable_rtti --enable_wasm_profiling + set CONFIG_EXTRA_FLAG=--disable_rtti --enable_wasm_profiling goto :arg2 ) echo Invalid configuration "%~1", must be "d"(Debug) or "r"(Release) @@ -64,7 +64,7 @@ popd set PATH=C:\Program Files\Git\usr\bin;%PATH% -call %ROOT%build.bat --config %CONFIG% %CONFIG_EXTRA_FLAG% --skip_submodule_sync --build_wasm --skip_tests^ +call %ROOT%build.bat --config %CONFIG% %CONFIG_EXTRA_FLAG% --skip_submodule_sync --build_wasm --skip_tests --enable_wasm_eh^ --enable_wasm_simd --enable_wasm_threads --use_jsep --use_webnn --target onnxruntime_webassembly --build_dir %BUILD_DIR% IF NOT "%ERRORLEVEL%" == "0" ( diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py index a0712af35e455..5dfef4f746df4 100644 --- a/tools/ci_build/build.py +++ b/tools/ci_build/build.py @@ -501,6 +501,7 @@ def generate_build_tree( + ("ON" if args.enable_wasm_api_exception_catching else "OFF"), "-Donnxruntime_ENABLE_WEBASSEMBLY_EXCEPTION_THROWING=" + ("ON" if args.enable_wasm_exception_throwing_override else "OFF"), + "-Donnxruntime_ENABLE_WEBASSEMBLY_NATIVE_EH=" + ("ON" if args.enable_wasm_eh else "OFF"), "-Donnxruntime_WEBASSEMBLY_RUN_TESTS_IN_BROWSER=" + ("ON" if args.wasm_run_tests_in_browser else "OFF"), "-Donnxruntime_ENABLE_WEBASSEMBLY_JSPI=" + ("ON" if args.enable_wasm_jspi else "OFF"), "-Donnxruntime_ENABLE_WEBASSEMBLY_THREADS=" + ("ON" if args.enable_wasm_threads else "OFF"), @@ -619,7 +620,7 @@ def generate_build_tree( build_dir, configs, emscripten_root_path, - args.enable_wasm_jspi, + args.enable_wasm_eh, not args.disable_rtti, not args.disable_wasm_exception_catching, args.minimal_build is not None, diff --git a/tools/ci_build/build_args.py b/tools/ci_build/build_args.py index f32666f65cc38..0518d84f9bb8e 100644 --- a/tools/ci_build/build_args.py +++ b/tools/ci_build/build_args.py @@ -390,6 +390,11 @@ def add_webassembly_args(parser: argparse.ArgumentParser) -> None: action="store_true", help="Override default behavior to allow throwing exceptions even when catching is generally disabled.", ) + parser.add_argument( + "--enable_wasm_eh", + action="store_true", + help="Use native WebAssembly exception handling (-fwasm-exceptions) instead of legacy JavaScript-based exception support.", + ) parser.add_argument("--wasm_run_tests_in_browser", action="store_true", help="Run WASM tests in a browser.") parser.add_argument( "--enable_wasm_profiling", action="store_true", help="Enable WASM profiling and preserve function names." @@ -935,7 +940,30 @@ def convert_arg_line_to_args(self, arg_line: str) -> list[str]: # Use list[str] if args.android_ndk_path: args.android_ndk_path = os.path.normpath(args.android_ndk_path) + if args.enable_wasm_jspi and not args.enable_wasm_eh: + raise Exception( + "'--enable_wasm_jspi' requires '--enable_wasm_eh' to be specified. " + "Currently we only support JSPI builds with native exception handling enabled." + ) + # Handle WASM exception logic + if args.enable_wasm_eh: + # Native Wasm EH is incompatible with legacy exception flags + if args.disable_wasm_exception_catching: + raise Exception( + "'--enable_wasm_eh' cannot be used with '--disable_wasm_exception_catching'. " + "Native Wasm EH replaces the legacy exception catching mechanism." + ) + if args.enable_wasm_api_exception_catching: + raise Exception( + "'--enable_wasm_eh' cannot be used with '--enable_wasm_api_exception_catching'. " + "Native Wasm EH replaces the legacy API-level exception catching." + ) + if args.enable_wasm_exception_throwing_override: + raise Exception( + "'--enable_wasm_eh' cannot be used with '--enable_wasm_exception_throwing_override'. " + "Native Wasm EH handles exception throwing natively." + ) if args.enable_wasm_api_exception_catching: args.disable_wasm_exception_catching = True # Catching at API level implies disabling broader catching if not args.disable_wasm_exception_catching or args.enable_wasm_api_exception_catching: diff --git a/tools/ci_build/github/azure-pipelines/templates/linux-wasm-ci.yml b/tools/ci_build/github/azure-pipelines/templates/linux-wasm-ci.yml index ef30a49b0fb83..d9c56986b8c62 100644 --- a/tools/ci_build/github/azure-pipelines/templates/linux-wasm-ci.yml +++ b/tools/ci_build/github/azure-pipelines/templates/linux-wasm-ci.yml @@ -53,7 +53,7 @@ jobs: buildArch: x64 CommonBuildArgs: '--parallel --config ${{ parameters.BuildConfig }} --skip_submodule_sync --build_wasm --enable_wasm_simd --enable_wasm_threads ${{ parameters.ExtraBuildArgs }}' ${{ if eq(parameters.BuildConfig, 'Release') }}: - # Add '--enable_wasm_api_exception_catching' only for non-JSPI Release build + # Add '--enable_wasm_api_exception_catching' only for basic Release build ExceptionHandlingBuildArgs: '--enable_wasm_api_exception_catching' ${{ else }}: ExceptionHandlingBuildArgs: '' @@ -128,7 +128,7 @@ jobs: ${{ else }}: AdditionalKey: wasm_inferencing_jsep | ${{ parameters.BuildConfig }} CacheDir: $(ORT_CACHE_DIR)/wasm_inferencing_jsep - Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_jsep --use_jsep --use_webnn --target onnxruntime_webassembly $(ExceptionHandlingBuildArgs) --skip_tests' + Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_jsep --use_jsep --use_webnn --target onnxruntime_webassembly --enable_wasm_eh --skip_tests' DisplayName: 'Build (simd + threads + JSEP)' WithCache: ${{ parameters.WithCache }} @@ -141,7 +141,7 @@ jobs: ${{ else }}: AdditionalKey: wasm_inferencing_webgpu | ${{ parameters.BuildConfig }} CacheDir: $(ORT_CACHE_DIR)/wasm_inferencing_webgpu - Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_webgpu --use_webgpu --use_webnn --target onnxruntime_webassembly $(ExceptionHandlingBuildArgs) --skip_tests' + Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_webgpu --use_webgpu --use_webnn --target onnxruntime_webassembly --enable_wasm_eh --skip_tests' DisplayName: 'Build (simd + threads + WebGPU experimental)' WithCache: ${{ parameters.WithCache }} @@ -154,7 +154,7 @@ jobs: ${{ else }}: AdditionalKey: wasm_inferencing_webgpu_jspi | ${{ parameters.BuildConfig }} CacheDir: $(ORT_CACHE_DIR)/wasm_inferencing_webgpu_jspi - Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_webgpu_jspi --use_webgpu --use_webnn --enable_wasm_jspi --target onnxruntime_webassembly --skip_tests' + Arguments: '$(CommonBuildArgs) --build_dir $(Build.BinariesDirectory)/wasm_inferencing_webgpu_jspi --use_webgpu --use_webnn --enable_wasm_jspi --enable_wasm_eh --target onnxruntime_webassembly --skip_tests' DisplayName: 'Build (simd + threads + WebGPU experimental, JSPI)' WithCache: ${{ parameters.WithCache }} diff --git a/tools/python/util/vcpkg_helpers.py b/tools/python/util/vcpkg_helpers.py index 34477dc38e38d..d38dfdf1e102c 100644 --- a/tools/python/util/vcpkg_helpers.py +++ b/tools/python/util/vcpkg_helpers.py @@ -476,7 +476,7 @@ def generate_vcpkg_triplets_for_emscripten( configs: set[str], emscripten_root: str, # Parameters defining the specific build configuration - enable_jspi: bool, + enable_wasm_eh: bool, # Controls native Wasm EH (-fwasm-exceptions) enable_rtti: bool, enable_wasm_exception_catching: bool, # Controls -sDISABLE_EXCEPTION_CATCHING=... enable_minimal_onnx_build: bool, # Controls ONNX port setting AND C++ exceptions (-fno-exceptions) @@ -493,21 +493,21 @@ def generate_vcpkg_triplets_for_emscripten( - If enable_minimal_onnx_build=False, C++ exceptions are assumed enabled (-fexceptions). This supports 4 main effective EH scenarios depending on the combination of - 'enable_minimal_onnx_build', 'enable_jspi' and 'enable_wasm_exception_catching': + 'enable_minimal_onnx_build', 'enable_wasm_eh' and 'enable_wasm_exception_catching': 1. No EH (-fno-exceptions, -sDISABLE_EXCEPTION_CATCHING=1): Set enable_minimal_onnx_build=True, enable_wasm_exception_catching=False 2. Full EH (-fexceptions, -sDISABLE_EXCEPTION_CATCHING=0): Set enable_minimal_onnx_build=False, enable_wasm_exception_catching=True 3. Throw Only EH (-fexceptions, -sDISABLE_EXCEPTION_CATCHING=1): Set enable_minimal_onnx_build=False, enable_wasm_exception_catching=False - 4. Use the new Wasm EH (-fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0): - Set enable_minimal_onnx_build=False, enable_jspi=True + 4. The new Wasm native EH (-fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0): + Set enable_minimal_onnx_build=False, enable_wasm_eh=True Args: build_dir (str): The directory to save the generated triplet files. emscripten_root (str): The root path of Emscripten. - enable_jspi (bool): Flag indicating if JSPI is enabled. If JSPI is enabled, the new - Wasm EH will be used and enable_wasm_exception_catching is ignored. + enable_wasm_eh (bool): Flag indicating if native Wasm EH should be used. + If True, uses -fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0. enable_rtti (bool): Flag indicating if RTTI is enabled for dependencies. enable_wasm_exception_catching (bool): Flag indicating if the Emscripten runtime exception catching mechanism should be enabled @@ -525,11 +525,12 @@ def generate_vcpkg_triplets_for_emscripten( # Derive C++ exception enablement from the minimal build flag cpp_exceptions_enabled = not enable_minimal_onnx_build - # When JSPI is enabled, use the new Wasm EH - if enable_jspi: + # When native Wasm EH is enabled, ensure minimal build is not used + if enable_wasm_eh: if enable_minimal_onnx_build: - # TODO: support minimal build with JSPI if needed - raise ValueError("Currently minimal build cannot be used with JSPI.") + raise ValueError( + "Using minimal build means C++ exceptions are disabled, which is incompatible with enabling native Wasm EH." + ) for target_abi in ["wasm32", "wasm64"]: os_name = "emscripten" @@ -574,8 +575,8 @@ def generate_vcpkg_triplets_for_emscripten( # Wasm Exception Catching Runtime (-s flag, apply to Base and Linker flags) exception_catching_flag = "" - if enable_jspi: - exception_catching_flag = "-fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0" + if enable_wasm_eh: + exception_catching_flag = "-fwasm-exceptions" elif enable_wasm_exception_catching: exception_catching_flag = "-sDISABLE_EXCEPTION_CATCHING=0" else: