Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
[submodule "cmake/external/emsdk"]
path = cmake/external/emsdk
url = https://github.com/emscripten-core/emsdk.git
branch = 4.0.8
branch = 4.0.11
1 change: 1 addition & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ else()
check_cxx_compiler_flag(-Wcast-function-type HAS_CAST_FUNCTION_TYPE)
check_cxx_compiler_flag(-Wcatch-value HAS_CATCH_VALUE)
check_cxx_compiler_flag(-Wclass-memaccess HAS_CLASS_MEMACCESS)
check_cxx_compiler_flag(-Wcharacter-conversion HAS_CHARACTER_CONVERSION)
check_cxx_compiler_flag(-Wdangling-reference HAS_DANGLING_REFERENCE)
check_cxx_compiler_flag(-Wdeprecated-anon-enum-enum-conversion HAS_DEPRECATED_ANON_ENUM_ENUM_CONVERSION)
check_cxx_compiler_flag(-Wdeprecated-builtins HAS_DEPRECATED_BUILTINS)
Expand Down
6 changes: 6 additions & 0 deletions cmake/onnxruntime_unittests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ function(AddTest)
if (${HAS_NOERROR})
target_compile_options(${_UT_TARGET} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-Wno-error=uninitialized>")
endif()
if (${HAS_CHARACTER_CONVERSION})
target_compile_options(${_UT_TARGET} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-Wno-error=character-conversion>")
endif()
endif()

set(TEST_ARGS ${_UT_TEST_ARGS})
Expand Down Expand Up @@ -789,6 +792,9 @@ if(MSVC)
"$<$<NOT:$<COMPILE_LANGUAGE:CUDA>>:/wd6326>")
else()
target_include_directories(onnxruntime_test_utils PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${ONNXRUNTIME_ROOT})
if (HAS_CHARACTER_CONVERSION)
target_compile_options(onnxruntime_test_utils PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-Wno-error=character-conversion>")
endif()
endif()
if (onnxruntime_USE_NCCL)
target_include_directories(onnxruntime_test_utils PRIVATE ${NCCL_INCLUDE_DIRS})
Expand Down
9 changes: 4 additions & 5 deletions cmake/onnxruntime_webassembly.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ else()
"${ONNXRUNTIME_ROOT}/wasm/api.cc"
"${ONNXRUNTIME_ROOT}/core/session/onnxruntime_c_api.cc"
)
set (WASM_API_EXCEPTION_CATCHING "-s DISABLE_EXCEPTION_CATCHING=0")
message(STATUS "onnxruntime_ENABLE_WEBASSEMBLY_EXCEPTION_CATCHING_ON_API set")
set_source_files_properties(${onnxruntime_webassembly_src_exc} PROPERTIES COMPILE_FLAGS ${WASM_API_EXCEPTION_CATCHING})
set_source_files_properties(${onnxruntime_webassembly_src_exc} PROPERTIES COMPILE_FLAGS "-sDISABLE_EXCEPTION_CATCHING=0")
target_link_options(onnxruntime_webassembly PRIVATE "SHELL:-s DISABLE_EXCEPTION_CATCHING=0")
endif()

target_link_libraries(onnxruntime_webassembly PRIVATE
Expand Down Expand Up @@ -241,11 +241,10 @@ else()
"SHELL:-s FILESYSTEM=0"
"SHELL:-s INCOMING_MODULE_JS_API=[locateFile,instantiateWasm,wasmBinary]"
"SHELL:-s WASM_BIGINT=1"
${WASM_API_EXCEPTION_CATCHING}
--no-entry
"SHELL:--pre-js \"${ONNXRUNTIME_ROOT}/wasm/pre.js\""
)

if (onnxruntime_USE_JSEP)
# NOTE: "-s ASYNCIFY=1" is required for JSEP to work with WebGPU
# This flag allows async functions to be called from sync functions, in the cost of binary size and
Expand All @@ -256,7 +255,7 @@ else()
"SHELL:--pre-js \"${ONNXRUNTIME_ROOT}/wasm/pre-jsep.js\""
)
list(APPEND onnxruntime_webassembly_script_deps "${ONNXRUNTIME_ROOT}/wasm/pre-jsep.js")

endif()

if (onnxruntime_USE_WEBGPU)
Expand Down
70 changes: 4 additions & 66 deletions onnxruntime/wasm/pre-async.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,78 +15,20 @@ let initAsyncImpl = () => {
// It removes some overhead in cwarp() and ccall() that we don't need.
//
// Currently in ASYNCIFY build, we only use this for the following functions:
// - OrtAppendExecutionProvider()
// - OrtCreateSession()
// - OrtRun()
// - OrtRunWithBinding()
// - OrtBindInput()
//
// Note: about parameters "getFunc" and "setFunc":
// - Emscripten has different behaviors for Debug and Release builds for generating exported function wrapper.
// We need to wrap these functions with an async wrapper so that they can be called in an async context.
//
// - In Debug build, it will generate a wrapper function for each exported function. For example, it generates a
// wrapper for OrtRun() like this (minified):
// ```
// var _OrtRun = Module["_OrtRun"] = createExportWrapper("OrtRun");
// ```
//
// - In Release build, it will generate a lazy loading wrapper for each exported function. For example, it generates
// a wrapper for OrtRun() like this (minified):
// ```
// d._OrtRun = (a, b, c, e, f, h, l, q) => (d._OrtRun = J.ka)(a, b, c, e, f, h, l, q);
// ```
//
// The behavior of these two wrappers are different. The debug build will assign `Module["_OrtRun"]` only once
// because `createExportWrapper()` does not reset `Module["_OrtRun"]` inside. The release build, however, will
// reset d._OrtRun to J.ka when the first time it is called.
//
// The difference is important because we need to design the async wrapper in a way that it can handle both cases.
//
// Now, let's look at how the async wrapper is designed to work for both cases:
//
// - Debug build:
// 1. When Web assembly is being loaded, `Module["_OrtRun"]` is assigned to `createExportWrapper("OrtRun")`.
// 2. When the first time `Module["initAsync"]` is called, `Module["_OrtRun"]` is re-assigned to a new async
// wrapper function.
// Value of `Module["_OrtRun"]` will not be changed again.
//
// - Release build:
// 1. When Web assembly is being loaded, `Module["_OrtRun"]` is assigned to a lazy loading wrapper function.
// 2. When the first time `Module["initAsync"]` is called, `Module["_OrtRun"]` is re-assigned to a new async
// wrapper function.
// 3. When the first time `Module["_OrtRun"]` is called, the async wrapper will be called. It will call into this
// function:
// ```
// (a, b, c, e, f, h, l, q) => (d._OrtRun = J.ka)(a, b, c, e, f, h, l, q);
// ```
// This function will assign d._OrtRun (ie. the minimized `Module["_OrtRun"]`) to the real function (J.ka).
// 4. Since d._OrtRun is re-assigned, we need to update the async wrapper to re-assign its stored
// function to the updated value (J.ka), and re-assign the value of `d._OrtRun` back to the async wrapper.
// Value of `Module["_OrtRun"]` will not be changed again.
//
// The value of `Module["_OrtRun"]` will need to be assigned for 2 times for debug build and 4 times for release
// build.
//
// This is why we need this `getFunc` and `setFunc` parameters. They are used to get the current value of an
// exported function and set the new value of an exported function.
//
const wrapAsync = (func, getFunc, setFunc) => {
const wrapAsync = (func) => {
return (...args) => {
// cache the async data before calling the function.
const previousAsync = Asyncify.currData;

const previousFunc = getFunc?.();
const ret = func(...args);
const newFunc = getFunc?.();
if (previousFunc !== newFunc) {
// The exported function has been updated.
// Set the sync function reference to the new function.
func = newFunc;
// Set the exported function back to the async wrapper.
setFunc(previousFunc);
// Remove getFunc and setFunc. They are no longer needed.
setFunc = null;
getFunc = null;
}

// If the async data has been changed, it means that the function started an async operation.
if (Asyncify.currData != previousAsync) {
Expand All @@ -101,11 +43,7 @@ let initAsyncImpl = () => {
// replace the original functions with asyncified versions
const wrapAsyncAPIs = (funcNames) => {
for (const funcName of funcNames) {
Module[funcName] = wrapAsync(
Module[funcName],
() => Module[funcName],
(v) => (Module[funcName] = v)
);
Module[funcName] = wrapAsync(Module[funcName]);
}
};

Expand Down
2 changes: 1 addition & 1 deletion tools/ci_build/build_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def add_webassembly_args(parser: argparse.ArgumentParser) -> None:
"""Adds arguments for WebAssembly (WASM) platform builds."""
parser.add_argument("--build_wasm", action="store_true", help="Build for WebAssembly.")
parser.add_argument("--build_wasm_static_lib", action="store_true", help="Build WebAssembly static library.")
parser.add_argument("--emsdk_version", default="4.0.8", help="Specify version of emsdk.")
parser.add_argument("--emsdk_version", default="4.0.11", help="Specify version of emsdk.")
parser.add_argument("--enable_wasm_simd", action="store_true", help="Enable WebAssembly SIMD.")
parser.add_argument("--enable_wasm_relaxed_simd", action="store_true", help="Enable WebAssembly Relaxed SIMD.")
parser.add_argument("--enable_wasm_threads", action="store_true", help="Enable WebAssembly multi-threading.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ jobs:
- script: |
set -ex
cd '$(Build.SourcesDirectory)/cmake/external/emsdk'
./emsdk install 4.0.8 ccache-git-emscripten-64bit
./emsdk activate 4.0.8 ccache-git-emscripten-64bit
./emsdk install 4.0.11 ccache-git-emscripten-64bit
./emsdk activate 4.0.11 ccache-git-emscripten-64bit
displayName: 'emsdk install and activate ccache for emscripten'
- ${{if eq(parameters.WithCache, false)}}:
- script: |
set -ex
cd '$(Build.SourcesDirectory)/cmake/external/emsdk'
./emsdk install 4.0.8
./emsdk activate 4.0.8
./emsdk install 4.0.11
./emsdk activate 4.0.11
displayName: 'emsdk install and activate ccache for emscripten'

- template: build-linux-wasm-step.yml
Expand Down
Loading