diff --git a/src/lib/liblittle_endian_heap.js b/src/lib/liblittle_endian_heap.js index d12432b8613d2..571cea68f462d 100644 --- a/src/lib/liblittle_endian_heap.js +++ b/src/lib/liblittle_endian_heap.js @@ -34,6 +34,82 @@ var LibraryLittleEndianHeap = { $LE_HEAP_LOAD_F64: (byteOffset) => HEAP_DATA_VIEW.getFloat64(byteOffset, true), + + $LE_ATOMICS_NATIVE_BYTE_ORDER__postset: ` +LE_ATOMICS_NATIVE_BYTE_ORDER = (new Int8Array(new Int16Array([1]).buffer)[0] === 1) + ? [ /* little endian */ + (x => x), + (x => x), + undefined, + (x => x), + ] + : [ /* big endian */ + (x => x), + (x => (((x & 0xff00) << 8) | ((x & 0xff) << 24)) >> 16), + undefined, + (x => ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8) | ((x & 0xff) << 24)), + ]; +function LE_HEAP_UPDATE() { + HEAPU16.unsigned = (x => x & 0xffff); + HEAPU32.unsigned = (x => x >>> 0); +} + `, + $LE_ATOMICS_NATIVE_BYTE_ORDER: [], + + $LE_ATOMICS_ADD: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.add(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_AND: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.and(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_COMPAREEXCHANGE: (heap, offset, expected, replacement) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.compareExchange(heap, offset, order(expected), order(replacement))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_EXCHANGE: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.exchange(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_ISLOCKFREE: (size) => Atomics.isLockFree(size), + $LE_ATOMICS_LOAD: (heap, offset) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.load(heap, offset)); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_NOTIFY: (heap, offset, count) => Atomics.notify(heap, offset, count), + $LE_ATOMICS_OR: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.or(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_STORE: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + Atomics.store(heap, offset, order(value)); + }, + $LE_ATOMICS_SUB: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.sub(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_WAIT: (heap, offset, value, timeout) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + return Atomics.wait(heap, offset, order(value), timeout); + }, + $LE_ATOMICS_WAITASYNC: (heap, offset, value, timeout) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + return Atomics.waitAsync(heap, offset, order(value), timeout); + }, + $LE_ATOMICS_XOR: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.xor(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, } addToLibrary(LibraryLittleEndianHeap); diff --git a/src/runtime_shared.js b/src/runtime_shared.js index 850e0859ae051..5743b83754799 100644 --- a/src/runtime_shared.js +++ b/src/runtime_shared.js @@ -66,9 +66,6 @@ var wasmOffsetConverter; function updateMemoryViews() { var b = wasmMemory.buffer; -#if SUPPORT_BIG_ENDIAN - {{{ maybeExportHeap('HEAP_DATA_VIEW') }}} HEAP_DATA_VIEW = new DataView(b); -#endif {{{ maybeExportHeap('HEAP8') }}}HEAP8 = new Int8Array(b); {{{ maybeExportHeap('HEAP16') }}}HEAP16 = new Int16Array(b); {{{ maybeExportHeap('HEAPU8') }}}HEAPU8 = new Uint8Array(b); @@ -81,6 +78,10 @@ function updateMemoryViews() { {{{ maybeExportHeap('HEAP64') }}}HEAP64 = new BigInt64Array(b); {{{ maybeExportHeap('HEAPU64') }}}HEAPU64 = new BigUint64Array(b); #endif +#if SUPPORT_BIG_ENDIAN + {{{ maybeExportHeap('HEAP_DATA_VIEW') }}} HEAP_DATA_VIEW = new DataView(b); + LE_HEAP_UPDATE(); +#endif } #if ENVIRONMENT_MAY_BE_NODE && MIN_NODE_VERSION < 160000 diff --git a/test/js_optimizer/LittleEndianHeap-output.js b/test/js_optimizer/LittleEndianHeap-output.js index af835e330646d..3e96e2d132197 100644 --- a/test/js_optimizer/LittleEndianHeap-output.js +++ b/test/js_optimizer/LittleEndianHeap-output.js @@ -33,3 +33,23 @@ LE_HEAP_STORE_F64(x * 8, a); HEAP[x]; HeAp[x]; + +LE_ATOMICS_ADD(heap, offset, value); + +LE_ATOMICS_AND(heap, offset, value); + +LE_ATOMICS_COMPAREEXCHANGE(heap, offset, expected, replacement); + +LE_ATOMICS_EXCHANGE(heap, offset, value); + +LE_ATOMICS_LOAD(heap, offset); + +LE_ATOMICS_OR(heap, offset, value); + +LE_ATOMICS_SUB(heap, offset, value); + +LE_ATOMICS_WAIT(heap, offset, value, timeout); + +LE_ATOMICS_WAITASYNC(heap, offset, value, timeout); + +LE_ATOMICS_XOR(heap, offset, value); diff --git a/test/js_optimizer/LittleEndianHeap.js b/test/js_optimizer/LittleEndianHeap.js index 8b12dea0a98af..94d038b570f04 100644 --- a/test/js_optimizer/LittleEndianHeap.js +++ b/test/js_optimizer/LittleEndianHeap.js @@ -16,3 +16,13 @@ a = HEAPF64[x]; // HEAPF64 HEAPF64[x] = a; HEAP[x]; // should not be changed HeAp[x]; +Atomics.add(heap, offset, value); +Atomics.and(heap, offset, value); +Atomics.compareExchange(heap, offset, expected, replacement); +Atomics.exchange(heap, offset, value); +Atomics.load(heap, offset); +Atomics.or(heap, offset, value); +Atomics.sub(heap, offset, value); +Atomics.wait(heap, offset, value, timeout); +Atomics.waitAsync(heap, offset, value, timeout); +Atomics.xor(heap, offset, value); diff --git a/tools/acorn-optimizer.mjs b/tools/acorn-optimizer.mjs index 028c7835f0c1f..241146233c0da 100755 --- a/tools/acorn-optimizer.mjs +++ b/tools/acorn-optimizer.mjs @@ -1106,10 +1106,21 @@ function littleEndianHeap(ast) { recursiveWalk(ast, { FunctionDeclaration: (node, c) => { // do not recurse into LE_HEAP_STORE, LE_HEAP_LOAD functions - if (!(node.id.type === 'Identifier' && node.id.name.startsWith('LE_HEAP'))) { + if ( + !( + node.id.type === 'Identifier' && + (node.id.name.startsWith('LE_HEAP') || node.id.name.startsWith('LE_ATOMICS_')) + ) + ) { c(node.body); } }, + VariableDeclarator: (node, c) => { + if (!(node.id.type === 'Identifier' && node.id.name.startsWith('LE_ATOMICS_'))) { + c(node.id); + if (node.init) c(node.init); + } + }, AssignmentExpression: (node, c) => { const target = node.left; const value = node.right; @@ -1160,6 +1171,27 @@ function littleEndianHeap(ast) { } } }, + CallExpression: (node, c) => { + if (node.arguments) { + for (var a of node.arguments) c(a); + } + if ( + // Atomics.X(args) -> LE_ATOMICS_X(args) + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && + node.callee.object.name === 'Atomics' && + node.callee.property.type === 'Identifier' && + !node.computed + ) { + makeCallExpression( + node, + 'LE_ATOMICS_' + node.callee.property.name.toUpperCase(), + node.arguments, + ); + } else { + c(node.callee); + } + }, MemberExpression: (node, c) => { c(node.property); if (!isHEAPAccess(node)) { @@ -1217,7 +1249,12 @@ function growableHeap(ast) { recursiveWalk(ast, { FunctionDeclaration(node, c) { // Do not recurse into to GROWABLE_HEAP_ helper functions themselves. - if (!(node.id.type === 'Identifier' && node.id.name.startsWith('GROWABLE_HEAP_'))) { + if ( + !( + node.id.type === 'Identifier' && + (node.id.name.startsWith('GROWABLE_HEAP_') || node.id.name === 'LE_HEAP_UPDATE') + ) + ) { c(node.body); } }, diff --git a/tools/link.py b/tools/link.py index ca8d64620f6e8..931039c9e8ea0 100644 --- a/tools/link.py +++ b/tools/link.py @@ -1122,7 +1122,21 @@ def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915 '$LE_HEAP_LOAD_U32', '$LE_HEAP_LOAD_I32', '$LE_HEAP_LOAD_F32', - '$LE_HEAP_LOAD_F64' + '$LE_HEAP_LOAD_F64', + '$LE_ATOMICS_NATIVE_BYTE_ORDER', + '$LE_ATOMICS_ADD', + '$LE_ATOMICS_AND', + '$LE_ATOMICS_COMPAREEXCHANGE', + '$LE_ATOMICS_EXCHANGE', + '$LE_ATOMICS_ISLOCKFREE', + '$LE_ATOMICS_LOAD', + '$LE_ATOMICS_NOTIFY', + '$LE_ATOMICS_OR', + '$LE_ATOMICS_STORE', + '$LE_ATOMICS_SUB', + '$LE_ATOMICS_WAIT', + '$LE_ATOMICS_WAITASYNC', + '$LE_ATOMICS_XOR', ] if settings.RUNTIME_DEBUG or settings.ASSERTIONS or settings.STACK_OVERFLOW_CHECK or settings.PTHREADS_PROFILING or settings.GL_ASSERTIONS: