Skip to content

Commit

Permalink
fix: Crash during PThread initialization on big-endian machine
Browse files Browse the repository at this point in the history
  • Loading branch information
slavek-kucera committed Feb 20, 2025
1 parent 4204bc3 commit 6d93671
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 2 deletions.
55 changes: 55 additions & 0 deletions src/lib/liblittle_endian_heap.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,61 @@ var LibraryLittleEndianHeap = {

$LE_HEAP_LOAD_F64: (byteOffset) =>
HEAP_DATA_VIEW.getFloat64(byteOffset, true),

$LE_ATOMICS_ADD: (heap, offset, value) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = Atomics.add(heap, offset, order(value));
return heap.unsigned ? heap.unsigned(res) : res;
},
$LE_ATOMICS_AND: (heap, offset, value) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = Atomics.and(heap, offset, order(value));
return heap.unsigned ? heap.unsigned(res) : res;
},
$LE_ATOMICS_COMPAREEXCHANGE: (heap, offset, expected, replacement) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = Atomics.compareExchange(heap, offset, order(expected), order(replacement));
return heap.unsigned ? heap.unsigned(res) : res;
},
$LE_ATOMICS_EXCHANGE: (heap, offset, value) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = 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 = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = 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 = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = Atomics.or(heap, offset, order(value));
return heap.unsigned ? heap.unsigned(res) : res;
},
$LE_ATOMICS_STORE: (heap, offset, value) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
Atomics.store(heap, offset, order(value));
},
$LE_ATOMICS_SUB: (heap, offset, value) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = Atomics.sub(heap, offset, order(value));
return heap.unsigned ? heap.unsigned(res) : res;
},
$LE_ATOMICS_WAIT: (heap, offset, value, timeout) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
return Atomics.wait(heap, offset, order(value), timeout);
},
$LE_ATOMICS_WAITASYNC: (heap, offset, value, timeout) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
return Atomics.waitAsync(heap, offset, order(value), timeout);
},
$LE_ATOMICS_XOR: (heap, offset, value) => {
const order = nativeByteOrder[heap.BYTES_PER_ELEMENT - 1];
const res = Atomics.xor(heap, offset, order(value));
return heap.unsigned ? heap.unsigned(res) : res;
},
}

addToLibrary(LibraryLittleEndianHeap);
56 changes: 56 additions & 0 deletions src/runtime_shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,64 @@ function updateMemoryViews() {
{{{ maybeExportHeap('HEAP64') }}}HEAP64 = new BigInt64Array(b);
{{{ maybeExportHeap('HEAPU64') }}}HEAPU64 = new BigUint64Array(b);
#endif

#if SUPPORT_BIG_ENDIAN
HEAPU16.unsigned = (x => x >>> 0);
HEAPU32.unsigned = (x => x >>> 0);
#if WASM_BIGINT
HEAPU64.unsigned = (x => x >= 0 ? x : BigInt(2**64) + x);
#endif
#endif
}

#if SUPPORT_BIG_ENDIAN
var nativeByteOrder = (() => {
var h16 = new Int16Array(1);
var h8 = new Int8Array(h16.buffer);
h16[0] = 42;
return h8[0] === 42 && h8[1] === 0; // little endian ordering
})()
? [ /* little endian */
(x => x),
(x => x),
undefined,
(x => x),
#if WASM_BIGINT
undefined,
undefined,
undefined,
(x => x),
#endif
]
: [ /* big endian */
(x => x),
(x => ((x >> 8) & 0xff) | ((x & 0xff) << 8)),
undefined,
(x => ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8) | ((x & 0xff) << 24)),
#if WASM_BIGINT
undefined,
undefined,
undefined,
(x => {
x = x >= 0 ? x : BigInt(2**64) + x;
var res = ((x >> BigInt(56)) & BigInt(0x000000ff))
| ((x >> BigInt(40)) & BigInt(0x0000ff00))
| ((x >> BigInt(24)) & BigInt(0x00ff0000))
| ((x >> BigInt(8)) & BigInt(0xff000000))
| ((x & BigInt(0xff000000)) << BigInt(8) )
| ((x & BigInt(0x00ff0000)) << BigInt(24))
| ((x & BigInt(0x0000ff00)) << BigInt(40))
| ((x & BigInt(0x000000ff)) << BigInt(56));
if ((x & BigInt(0x80)) == 0)
return res;
else
return res - BigInt(2**64);
}
),
#endif
];
#endif

#if ENVIRONMENT_MAY_BE_NODE && MIN_NODE_VERSION < 160000
// The performance global was added to node in v16.0.0:
// https://nodejs.org/api/globals.html#performance
Expand Down
20 changes: 20 additions & 0 deletions test/js_optimizer/LittleEndianHeap-output.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
10 changes: 10 additions & 0 deletions test/js_optimizer/LittleEndianHeap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
34 changes: 33 additions & 1 deletion tools/acorn-optimizer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down
15 changes: 14 additions & 1 deletion tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,20 @@ 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_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:
Expand Down

0 comments on commit 6d93671

Please sign in to comment.