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 VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.20261019.0
1.20261020.0
39 changes: 25 additions & 14 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -372,19 +372,28 @@ if(EMSCRIPTEN)
# Note #3: for linking, we use -O2 (rather than -O3) since our compressed .wasm file is then smaller with no loss of
# performance (and it links faster), -flto to use the LTO version of Emscripten's system libraries and to
# optimise LLVM, which we build as LLVM bitcode (see src/3rdparty/LLVM/CMakeLists.txt; our other object
# files are not LTO ones), -pthread to enable native threading support, -sASSERTIONS=0 to disable
# assertions (they are not needed in production), -sCROSS_ORIGIN=1 to support our .js file being loaded
# from another origin than that of the Web page (see Note #5), -sEXPORT_ES6=1 to export ES6 modules,
# -sEXPORTED_FUNCTIONS to export the functions we need (i.e. those needed by the WebAssembly modules that
# we generate for our models, see initialiseWorkerWasmJS()), -sEXPORTED_RUNTIME_METHODS to export the
# runtime methods we need, -sINITIAL_MEMORY=2GB to have enough memory for "big" models, -sMODULARIZE=1 to
# generate a module factory function, -sSTACK_SIZE=8MB to reserve a reasonable amount of memory for the
# stack of each thread (which is needed for "big" models), --bind to generate the necessary Embind
# bindings, and --emit-tsd to generate a TypeScript type definitions file.
# Note #4: for linking, we used to have -sALLOW_MEMORY_GROWTH=1, -sINITIAL_MEMORY=256MB, and -sMAXIMUM_MEMORY=2GB,
# but we now support threading, which means that our memory is shared, and to allow shared memory to grow
# would make JavaScript's accesses to it (e.g., through HEAPU8) slower. So, now, we use
# -sINITIAL_MEMORY=2GB and don't allow memory growth.
# files are not LTO ones), -pthread to enable native threading support, -Wno-pthreads-mem-growth to silence
# Emscripten's warning about threading and memory growth (see Note #4), -sALLOW_MEMORY_GROWTH=1 to let our
# memory grow on demand (see Note #4), -sASSERTIONS=0 to disable assertions (they are not needed in
# production), -sCROSS_ORIGIN=1 to support our .js file being loaded from another origin than that of the
# Web page (see Note #5), -sEXPORT_ES6=1 to export ES6 modules, -sEXPORTED_FUNCTIONS to export the
# functions we need (i.e. those needed by the WebAssembly modules that we generate for our models, see
# initialiseWorkerWasmJS()), -sEXPORTED_RUNTIME_METHODS to export the runtime methods we need,
# -sMAXIMUM_MEMORY=2GB to let our memory grow up to 2GB (see Note #4), -sMODULARIZE=1 to generate a module
# factory function, -sSTACK_SIZE=8MB to reserve a reasonable amount of memory for the stack of each thread
# (which is needed for "big" models), --bind to generate the necessary Embind bindings, and --emit-tsd to
# generate a TypeScript type definitions file.
# Note #4: our memory starts small (16MB, which is Emscripten's default when, like here, our memory is imported) and
# grows on demand, up to 2GB. Since we support threading, our memory is shared, which means that another
# thread may grow it, so Emscripten precedes each of our JavaScript code's accesses to it (e.g., through
# HEAPU8 in an EM_ASM() block) with a check of whether it has grown. That check makes those accesses slower
# (about 15ns each), hence Emscripten's warning, but it only affects calls that cross the
# JavaScript/WebAssembly boundary (e.g., a call that passes or returns a string), not our WebAssembly code
# (e.g., the simulation of a model). We don't use -sGROWABLE_ARRAYBUFFERS=1|2, which would replace that
# check with a growable SharedArrayBuffer since our results (see toFloat64ArrayJS()) would then be views
# over a growable SharedArrayBuffer, which some Web APIs reject (e.g., structuredClone()). We also don't
# use -sMAXIMUM_MEMORY=4GB since our EM_JS() functions would then get a pointer of 2GB or more as a
# negative number and Embind calls would be slower (since pointers would need to be made unsigned).
# Note #5: a Web browser only creates a worker (and therefore a thread) from a script that has the same origin as
# the Web page, so without -sCROSS_ORIGIN=1, a thread couldn't be created if our .js file was loaded from
# another origin (e.g., a CDN), i.e. startRun() would throw an error (Chrome) or never complete (Firefox).
Expand All @@ -404,14 +413,16 @@ if(EMSCRIPTEN)
-O2
-flto
-pthread
-sALLOW_MEMORY_GROWTH=1
-sASSERTIONS=0
-sCROSS_ORIGIN=1
-sEXPORT_ES6=1
-sEXPORTED_FUNCTIONS=[_free,_malloc,_memset,_nlaSolverAddress,_wasmNlaSolve,_pow,_sqrt,_fabs,_exp,_exp2,_log,_log10,_ceil,_floor,_fmin,_fmax,_fmod,_sin,_cos,_tan,_sinh,_cosh,_tanh,_asin,_acos,_atan,_asinh,_acosh,_atanh]
-sEXPORTED_RUNTIME_METHODS=HEAPU8
-sINITIAL_MEMORY=2GB
-sMAXIMUM_MEMORY=2GB
-sMODULARIZE=1
-sSTACK_SIZE=8MB
-Wno-pthreads-mem-growth
--bind
--emit-tsd ${DTS_FILE})

Expand Down
4 changes: 2 additions & 2 deletions src/bindings/javascript/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ void fileApi()
}

// Note: we avoid using emscripten::typed_memory_view() as an intermediate step since it creates a
// JavaScript's TypedArray view directly backed by wasmMemory.buffer. This is fragile and would break
// if ALLOW_MEMORY_GROWTH was to be enabled. By performing the copy entirely within a single EM_ASM()
// JavaScript's TypedArray view directly backed by wasmMemory.buffer. This is fragile since our memory
// can grow (see src/CMakeLists.txt). By performing the copy entirely within a single EM_ASM()
// block, HEAPU8 is fetched and used atomically.

return emscripten::val::take_ownership(static_cast<emscripten::EM_VAL>(EM_ASM_PTR({
Expand Down
2 changes: 1 addition & 1 deletion src/misc/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ std::string patchWasmSharedMemory(UnsignedChars &pWasmModule)
// Memory import: patch its flags to mark it as shared and give it a maximum size, if it doesn't
// already have one. That maximum size must be at least that of the memory of our main WebAssembly
// module (i.e. the memory that we import), which we retrieve at runtime rather than hard-code it
// since it depends on -sINITIAL_MEMORY, etc. (see src/CMakeLists.txt).
// since it depends on -sMAXIMUM_MEMORY, etc. (see src/CMakeLists.txt).
// Note: LLVM currently emits a 32-bit, non-shared memory import without a maximum size (i.e. flags
// 0x00), but we handle a memory import with a maximum size (i.e. flags 0x01) and/or that is
// already shared (i.e. flags 0x03) in case a future version of LLVM does so. Any other
Expand Down
6 changes: 3 additions & 3 deletions src/sed/sedinstancetask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ EM_JS(intptr_t, toFloat64ArrayJS, (const void* data, size_t size), {
}

return Emval.toHandle(new Float64Array(HEAPU8.subarray(data, data + 8 * size).buffer, data, size));
// Note: we use HEAPU8.subarray() to create a view over the WASM heap's Float64Array buffer because it is safer than
// accessing HEAPU8.buffer directly in case the WASM heap was ever to grow (we don't allow this to happen, but
// it is still safer to use HEAPU8.subarray()) since it creates a view with the correct byte offset.
// Note: our memory can grow (see src/CMakeLists.txt), but HEAPU8 is always up to date when we access it and our
// memory is shared, i.e. backed by a SharedArrayBuffer, which never gets detached when our memory grows. So,
// our view remains valid even after our memory has grown (as long as our results don't get reallocated).
}); // clang-format on

static emscripten::val toFloat64Array(std::span<const double> data)
Expand Down
Loading