diff --git a/.circleci/config.yml b/.circleci/config.yml index 6a5090aa04243..5830c4f8bb69e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -867,7 +867,10 @@ jobs: - install-emsdk - run-tests-firefox: title: "browser64" - test_targets: "browser64.test_sdl_image" + test_targets: " + browser64.test_sdl_image + browser64.test_dylink_many + " # TODO(sbc): Re-enable once we figure out why the emrun tests are # locking up. #test-browser-chrome-emrun: diff --git a/src/library.js b/src/library.js index 6d2607880de94..09b4f4fb607d1 100644 --- a/src/library.js +++ b/src/library.js @@ -1966,25 +1966,26 @@ addToLibrary({ $setWasmTableEntry__internal: true, $setWasmTableEntry__deps: ['$wasmTableMirror', '$wasmTable'], $setWasmTableEntry: (idx, func) => { - wasmTable.set(idx, func); + wasmTable.set({{{ toIndexType('idx') }}}, func); // With ABORT_ON_WASM_EXCEPTIONS wasmTable.get is overridden to return wrapped // functions so we need to call it here to retrieve the potential wrapper correctly // instead of just storing 'func' directly into wasmTableMirror - wasmTableMirror[idx] = wasmTable.get(idx); + wasmTableMirror[idx] = wasmTable.get({{{ toIndexType('idx') }}}); }, $getWasmTableEntry__internal: true, $getWasmTableEntry__deps: ['$wasmTableMirror', '$wasmTable'], $getWasmTableEntry: (funcPtr) => { #if MEMORY64 - // Function pointers are 64-bit, but wasmTable.get() requires a Number. + // Function pointers should show up as numbers, even under wasm64, but + // we still have some places where bigint values can flow here. // https://github.com/emscripten-core/emscripten/issues/18200 funcPtr = Number(funcPtr); #endif var func = wasmTableMirror[funcPtr]; if (!func) { if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1; - wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); + wasmTableMirror[funcPtr] = func = wasmTable.get({{{ toIndexType('funcPtr') }}}); #if ASYNCIFY == 2 if (Asyncify.isAsyncExport(func)) { wasmTableMirror[funcPtr] = func = Asyncify.makeAsyncFunction(func); @@ -1992,7 +1993,7 @@ addToLibrary({ #endif } #if ASSERTIONS && ASYNCIFY != 2 // With JSPI the function stored in the table will be a wrapper. - assert(wasmTable.get(funcPtr) == func, 'JavaScript-side Wasm function table mirror is out of date!'); + assert(wasmTable.get({{{ toIndexType('funcPtr') }}}) == func, 'JavaScript-side Wasm function table mirror is out of date!'); #endif return func; }, @@ -2000,7 +2001,7 @@ addToLibrary({ #else $setWasmTableEntry__deps: ['$wasmTable'], - $setWasmTableEntry: (idx, func) => wasmTable.set(idx, func), + $setWasmTableEntry: (idx, func) => wasmTable.set({{{ toIndexType('idx') }}}, func), $getWasmTableEntry__deps: ['$wasmTable'], $getWasmTableEntry: (funcPtr) => { @@ -2391,9 +2392,9 @@ addToLibrary({ #if RELOCATABLE // In RELOCATABLE mode we create the table in JS. $wasmTable: `=new WebAssembly.Table({ - 'initial': {{{ INITIAL_TABLE }}}, + 'initial': {{{ toIndexType(INITIAL_TABLE) }}}, #if !ALLOW_TABLE_GROWTH - 'maximum': {{{ INITIAL_TABLE }}}, + 'maximum': {{{ toIndexType(INITIAL_TABLE) }}}, #endif #if MEMORY64 == 1 'index': 'i64', diff --git a/src/library_addfunction.js b/src/library_addfunction.js index e9fa8f2adb9f6..fc9f91f11d00c 100644 --- a/src/library_addfunction.js +++ b/src/library_addfunction.js @@ -151,14 +151,14 @@ addToLibrary({ } // Grow the table try { - wasmTable.grow(1); + wasmTable.grow({{{ toIndexType('1') }}}); } catch (err) { if (!(err instanceof RangeError)) { throw err; } throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; } - return wasmTable.length - 1; + return {{{ from64Expr('wasmTable.length') }}} - 1; }, $updateTableMap__deps: ['$getWasmTableEntry'], @@ -179,7 +179,7 @@ addToLibrary({ // First, create the map if this is the first use. if (!functionsInTableMap) { functionsInTableMap = new WeakMap(); - updateTableMap(0, wasmTable.length); + updateTableMap(0, {{{ from64Expr('wasmTable.length') }}}); } return functionsInTableMap.get(func) || 0; }, diff --git a/src/library_dylink.js b/src/library_dylink.js index 648f0185d8300..b9402d1d7288e 100644 --- a/src/library_dylink.js +++ b/src/library_dylink.js @@ -581,8 +581,10 @@ var LibraryDylink = { #if DYLINK_DEBUG $dumpTable__deps: ['$wasmTable'], $dumpTable: () => { - for (var i = 0; i < wasmTable.length; i++) + var len = wasmTable.length; + for (var i = {{{ toIndexType(0) }}} ; i < len; i++) { dbg(`table: ${i} : ${wasmTable.get(i)}`); + } }, #endif @@ -641,7 +643,7 @@ var LibraryDylink = { tableBase = {{{ makeGetValue('handle', C_STRUCTS.dso.table_addr, '*') }}}; } - var tableGrowthNeeded = tableBase + metadata.tableSize - wasmTable.length; + var tableGrowthNeeded = tableBase + metadata.tableSize - {{{ from64Expr('wasmTable.length') }}}; if (tableGrowthNeeded > 0) { #if DYLINK_DEBUG dbg("loadModule: growing table: " + tableGrowthNeeded); diff --git a/src/parseTools.mjs b/src/parseTools.mjs index 59c99ea68dd4e..0a9fb43b14b2e 100644 --- a/src/parseTools.mjs +++ b/src/parseTools.mjs @@ -963,6 +963,11 @@ function from64Expr(x, assign = true) { return `Number(${x})`; } +function toIndexType(x) { + if (MEMORY64 != 1) return x; + return `toIndexType(${x})`; +} + function to64(x) { if (!MEMORY64) return x; return `BigInt(${x})`; @@ -1153,4 +1158,5 @@ addToCompileTimeContext({ splitI64, storeException, to64, + toIndexType, }); diff --git a/src/runtime_init_memory.js b/src/runtime_init_memory.js index 4344a5cdb73e3..b6b050e467a08 100644 --- a/src/runtime_init_memory.js +++ b/src/runtime_init_memory.js @@ -27,16 +27,16 @@ if (!ENVIRONMENT_IS_PTHREAD) { assert(INITIAL_MEMORY >= {{{STACK_SIZE}}}, 'INITIAL_MEMORY should be larger than STACK_SIZE, was ' + INITIAL_MEMORY + '! (STACK_SIZE=' + {{{STACK_SIZE}}} + ')'); #endif wasmMemory = new WebAssembly.Memory({ - 'initial': INITIAL_MEMORY / {{{ WASM_PAGE_SIZE }}}, + 'initial': {{{ toIndexType(`INITIAL_MEMORY / ${WASM_PAGE_SIZE}`) }}}, #if ALLOW_MEMORY_GROWTH // In theory we should not need to emit the maximum if we want "unlimited" // or 4GB of memory, but VMs error on that atm, see // https://github.com/emscripten-core/emscripten/issues/14130 // And in the pthreads case we definitely need to emit a maximum. So // always emit one. - 'maximum': {{{ MAXIMUM_MEMORY }}} / {{{ WASM_PAGE_SIZE }}}, + 'maximum': {{{ toIndexType(MAXIMUM_MEMORY / WASM_PAGE_SIZE) }}}, #else - 'maximum': INITIAL_MEMORY / {{{ WASM_PAGE_SIZE }}}, + 'maximum': {{{ toIndexType(`INITIAL_MEMORY / ${WASM_PAGE_SIZE}`) }}}, #endif // ALLOW_MEMORY_GROWTH #if SHARED_MEMORY 'shared': true, diff --git a/src/runtime_shared.js b/src/runtime_shared.js index 4d8f42053ebac..715211a67c6f4 100644 --- a/src/runtime_shared.js +++ b/src/runtime_shared.js @@ -46,3 +46,19 @@ function updateMemoryViews() { {{{ maybeExportHeap('HEAPU64') }}}HEAPU64 = new BigUint64Array(b); #endif } + +#if MEMORY64 == 1 +var toIndexType = (function() { + // Probe for support of bigint bounds with memory64. + // TODO(sbc): Remove this once all browsers start requiring bigint here. + // See https://github.com/WebAssembly/memory64/issues/68 + var bigintMemoryBounds = 1; + try { + /** @suppress {checkTypes} */ + new WebAssembly.Memory({'initial': 1n, 'index': 'i64'}); + } catch (e) { + bigintMemoryBounds = 0; + } + return (i) => bigintMemoryBounds ? BigInt(i) : i; +})(); +#endif diff --git a/test/no_this_in_dyncall.js b/test/no_this_in_dyncall.js index 40131a468e718..b3ed7c9c02858 100644 --- a/test/no_this_in_dyncall.js +++ b/test/no_this_in_dyncall.js @@ -1,10 +1,10 @@ addToLibrary({ - $classLike: { - fnptr: 0, + $classLike: { + fnptr: 0, call: function(val) { {{{ makeDynCall('vp', 'this.fnptr') }}}(val); } - }, + }, test__deps: ['$classLike'], test: function(fnptr, val) { diff --git a/tools/unsafe_optimizations.mjs b/tools/unsafe_optimizations.mjs index 5352d1877eede..134adc073e3bb 100755 --- a/tools/unsafe_optimizations.mjs +++ b/tools/unsafe_optimizations.mjs @@ -73,7 +73,13 @@ function optPassRemoveRedundantOperatorNews(ast) { // in emscripten with real side effects. For example, see // loadWasmModuleToWorker which returns a `new Promise` that is never // referenced (a least in some builds). - if (n.expression.callee.name !== 'Promise') { + // + // Another exception is made for `new WebAssembly.*` since we create and + // unused `WebAssembly.Memory` when probing for wasm64 fatures. + if ( + n.expression.callee.name !== 'Promise' && + n.expression.callee.object?.name !== 'WebAssembly' + ) { nodeArray.splice(i--, 1); } } @@ -217,7 +223,7 @@ function runOnJsText(js, pretty = false) { const output = terserAst.print_to_string({ wrap_func_args: false, beautify: pretty, - indent_level: pretty ? 1 : 0, + indent_level: pretty ? 2 : 0, }); return output;