From 66b39d6eccbf309a47a18f4d36e3c00dfd5584cb Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 16 May 2022 13:11:18 -0700 Subject: [PATCH] [wasm64] Fix wasm64 + MINIMAL_RUNTIME Move the instrumentWasmExportsForMemory64 helper out into its own file so we have be used under MINIMAL_RUNTIME too. In the long run this function should be replaced with something more data driven but for now this works. Split out from #16922 --- .circleci/config.yml | 2 +- embuilder.py | 1 + src/postamble_minimal.js | 4 +++ src/preamble.js | 56 +++------------------------------ src/preamble_minimal.js | 4 +++ src/runtime_wasm64.js | 67 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 81 insertions(+), 53 deletions(-) create mode 100644 src/runtime_wasm64.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 0a11096d3ef8e..4b148ad815fc0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -381,7 +381,7 @@ jobs: executor: bionic steps: - run-tests-linux: - test_targets: "wasm64.test_hello_world wasm64.test_ccall wasm64l.test_hello_world wasm64l.test_mmap wasm64l.test_unistd_* skip:wasm64l.test_unistd_sysconf wasm64l.test_mmap_file wasm64l.test_ccall wasm64l.test_signals wasm64l.test_emscripten_get_compiler_setting wasm64l.test_float_builtins wasm64l.test_getopt wasm64l.test_em_asm*" + test_targets: "wasm64.test_hello_world wasm64.test_ccall wasm64l.test_hello_world wasm64l.test_mmap wasm64l.test_unistd_* skip:wasm64l.test_unistd_sysconf wasm64l.test_mmap_file wasm64l.test_ccall wasm64l.test_signals wasm64l.test_emscripten_get_compiler_setting wasm64l.test_float_builtins wasm64l.test_getopt wasm64l.test_em_asm* wasm64l.test_minimal_runtime_utf8_invalid" test-other: executor: bionic steps: diff --git a/embuilder.py b/embuilder.py index fdb0b3e6e6053..4512beaddebd9 100755 --- a/embuilder.py +++ b/embuilder.py @@ -40,6 +40,7 @@ 'libc++-noexcept', 'libal', 'libdlmalloc', + 'libdlmalloc-noerrno', 'libdlmalloc-debug', 'libemmalloc', 'libemmalloc-debug', diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index 1f2ad681f5bdb..6b28105254e43 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -199,6 +199,10 @@ WebAssembly.instantiate(Module['wasm'], imports).then(function(output) { asm = output.instance.exports; #endif +#if MEMORY64 + asm = instrumentWasmExportsForMemory64(asm); +#endif + #if USE_OFFSET_CONVERTER #if USE_PTHREADS if (!ENVIRONMENT_IS_PTHREAD) diff --git a/src/preamble.js b/src/preamble.js index 67b957b42d972..6f6ce43d6e289 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -50,6 +50,10 @@ if (typeof WebAssembly != 'object') { #include "runtime_asan.js" #endif +#if MEMORY64 +#include "runtime_wasm64.js" +#endif + // Wasm globals var wasmMemory; @@ -772,58 +776,6 @@ function instrumentWasmTableWithAbort() { } #endif -#if MEMORY64 -// In memory64 mode wasm pointers are 64-bit. To support that in JS we must use -// BigInts. For now we keep JS as much the same as it always was, that is, -// stackAlloc() receives and returns a Number from the JS point of view - -// we translate BigInts automatically for that. -// TODO: support minified export names, so we can turn MINIFY_WASM_IMPORTS_AND_EXPORTS -// back on for MEMORY64. -function instrumentWasmExportsForMemory64(exports) { - var instExports = {}; - for (var name in exports) { - (function(name) { - var original = exports[name]; - var replacement = original; - if (name === 'stackAlloc' || name === 'malloc' || name === 'emscripten_builtin_malloc') { - // get one i64, return an i64 - replacement = (x) => { - var r = Number(original(BigInt(x))); - return r; - }; - } else if (name === 'free') { - // get one i64 - replacement = (x) => { - original(BigInt(x)); - }; - } else if (name === 'emscripten_stack_get_end' || - name === 'emscripten_stack_get_base' || - name === 'emscripten_stack_get_current') { - // return an i64 - replacement = () => { - var r = Number(original()); - return r; - }; - } else if (name === 'emscripten_builtin_memalign') { - // get two i64, return an i64 - replacement = (x, y) => { - var r = Number(original(BigInt(x), BigInt(y))); - return r; - }; - } else if (name === 'main') { - // get a i64 as second arg - replacement = (x, y) => { - var r = original(x, BigInt(y)); - return r; - }; - } - instExports[name] = replacement; - })(name); - } - return instExports; -} -#endif MEMORY64 - var wasmBinaryFile; #if EXPORT_ES6 && USE_ES6_IMPORT_META && !SINGLE_FILE if (Module['locateFile']) { diff --git a/src/preamble_minimal.js b/src/preamble_minimal.js index da2744d7a93cd..26976a16f5114 100644 --- a/src/preamble_minimal.js +++ b/src/preamble_minimal.js @@ -12,6 +12,10 @@ #include "runtime_asan.js" #endif +#if MEMORY64 +#include "runtime_wasm64.js" +#endif + #if ASSERTIONS || SAFE_HEAP /** @type {function(*, string=)} */ function assert(condition, text) { diff --git a/src/runtime_wasm64.js b/src/runtime_wasm64.js new file mode 100644 index 0000000000000..51a205c94cd8e --- /dev/null +++ b/src/runtime_wasm64.js @@ -0,0 +1,67 @@ +/** + * @license + * Copyright 2022 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +#if !MEMORY64 +#error "should only be inclded in MEMORY64 mode" +#endif + +// In memory64 mode wasm pointers are 64-bit. In JS these show up as BigInts. +// For now, we keep JS as much the same as it always was, that is, stackAlloc() +// receives and returns a Number from the JS point of view - we translate +// BigInts automatically for that. +// TODO: support minified export names, so we can turn +// MINIFY_WASM_IMPORTS_AND_EXPORTS back on for MEMORY64. +// TODO: Remove this hacky mechanism and replace with something more like the +// `__sig` attributes we have in JS library code. +function instrumentWasmExportsForMemory64(exports) { + var instExports = {}; + for (var name in exports) { + (function(name) { + var original = exports[name]; + var replacement = original; + if (['stackAlloc', 'emscripten_builtin_malloc', 'malloc', '__getTypeName'].includes(name)) { + // get one i64, return an i64. + replacement = (x) => { + var r = Number(original(BigInt(x))); + return r; + }; + } else if (['setThrew', 'free', 'stackRestore', '__cxa_is_pointer_type'].includes(name)) { + // get one i64 + replacement = (x) => { + original(BigInt(x)); + }; + } else if (['stackSave', 'emscripten_stack_get_end', + 'emscripten_stack_get_base', 'pthread_self', + 'emscripten_stack_get_current', + '__errno_location'].includes(name)) { + // return an i64 + replacement = () => { + var r = Number(original()); + return r; + }; + } else if (name === 'emscripten_builtin_memalign') { + // get two i64, return an i64 + replacement = (x, y) => { + var r = Number(original(BigInt(x), BigInt(y))); + return r; + }; + } else if (name === 'main') { + // Special case for main. Use `function` here rather than arrow + // function to avoid implicit `strict`. + replacement = function(x, y) { + // Pass an extra 0 in case its a 3-argument form of main. Sadly we + // can't just omit that argument like we can for wasm32 because the + // missing third argument will generate: + // `TypeError: Cannot convert undefined to a BigInt`. + // See https://github.com/WebAssembly/JS-BigInt-integration/issues/12 + return original(x, BigInt(y ? y : 0), BigInt(0)); + }; + } + instExports[name] = replacement; + })(name); + } + return instExports; +}