Skip to content

Commit fd743d1

Browse files
authored
Fix many remaining bugs with >2gb address space (#20099)
1 parent ac85c42 commit fd743d1

16 files changed

+137
-92
lines changed

.circleci/config.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,9 +618,8 @@ jobs:
618618
title: "wasm64"
619619
test_targets: "
620620
wasm64
621-
core_2gb.test_em_asm
622-
core_2gb.test_embind_unsigned_bigint
623-
core_2gb.test_embind_no_rtti
621+
core_2gb.test_*em_asm*
622+
core_2gb.test_*embind*
624623
wasm64l.test_bigswitch
625624
other.test_memory64_proxies
626625
other.test_failing_growth_wasm64"

emcc.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,8 +1781,11 @@ def set_max_memory():
17811781
diagnostics.warning('unused-command-line-argument', 'MAXIMUM_MEMORY is only meaningful with ALLOW_MEMORY_GROWTH')
17821782
settings.MAXIMUM_MEMORY = settings.INITIAL_MEMORY
17831783

1784-
# INITIAL_MEMORY sets a lower bound for MAXIMUM_MEMORY
17851784
if 'MAXIMUM_MEMORY' not in user_settings:
1785+
if settings.ALLOW_MEMORY_GROWTH and settings.INITIAL_MEMORY > 2 * 1024 * 1024 * 1024:
1786+
settings.MAXIMUM_MEMORY = 4 * 1024 * 1024 * 1024
1787+
1788+
# INITIAL_MEMORY sets a lower bound for MAXIMUM_MEMORY
17861789
if settings.INITIAL_MEMORY > settings.MAXIMUM_MEMORY:
17871790
settings.MAXIMUM_MEMORY = settings.INITIAL_MEMORY
17881791

@@ -2875,10 +2878,12 @@ def get_full_import_name(name):
28752878
if settings.WASM2JS:
28762879
if settings.GENERATE_SOURCE_MAP:
28772880
exit_with_error('wasm2js does not support source maps yet (debug in wasm for now)')
2878-
if settings.WASM_BIGINT:
2879-
exit_with_error('wasm2js does not support WASM_BIGINT')
28802881
if settings.MEMORY64:
28812882
exit_with_error('wasm2js does not support MEMORY64')
2883+
if settings.WASM_BIGINT:
2884+
exit_with_error('wasm2js does not support WASM_BIGINT')
2885+
if settings.CAN_ADDRESS_2GB:
2886+
exit_with_error('wasm2js does not support >2gb address space')
28822887

28832888
if settings.NODE_CODE_CACHING:
28842889
if settings.WASM_ASYNC_COMPILATION:

src/jsifier.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ function runJSify() {
160160
args = newArgs.join(',');
161161
}
162162

163-
if ((sig[0] == 'j' && i53abi) || (sig[0] == 'p' && WASM_BIGINT)) {
163+
if ((sig[0] == 'j' && i53abi) || (sig[0] == 'p' && MEMORY64)) {
164164
// For functions that where we need to mutate the return value, we
165165
// also need to wrap the body in an inner function.
166166
if (oneliner) {

src/library.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,34 +2872,31 @@ addToLibrary({
28722872
while (ch = HEAPU8[sigPtr++]) {
28732873
#if ASSERTIONS
28742874
var chr = String.fromCharCode(ch);
2875-
var validChars = ['d', 'f', 'i'];
2875+
var validChars = ['d', 'f', 'i', 'p'];
28762876
#if WASM_BIGINT
28772877
// In WASM_BIGINT mode we support passing i64 values as bigint.
28782878
validChars.push('j');
2879-
#endif
2880-
#if MEMORY64
2881-
// In MEMORY64 mode we also support passing i64 pointer types which
2882-
// get automatically converted to int53/Double.
2883-
validChars.push('p');
28842879
#endif
28852880
assert(validChars.includes(chr), `Invalid character ${ch}("${chr}") in readEmAsmArgs! Use only [${validChars}], and do not specify "v" for void return argument.`);
28862881
#endif
28872882
// Floats are always passed as doubles, so all types except for 'i'
28882883
// are 8 bytes and require alignment.
2889-
buf += (ch != {{{ charCode('i') }}}) && buf % 8 ? 4 : 0;
2890-
readEmAsmArgsArray.push(
2891-
#if MEMORY64
2892-
// Special case for pointers under wasm64 which we read as int53 Numbers.
2893-
ch == {{{ charCode('p') }}} ? {{{ makeGetValue('buf', 0, '*') }}} :
2884+
var wide = (ch != {{{ charCode('i') }}});
2885+
#if !MEMORY64
2886+
wide &= (ch != {{{ charCode('p') }}});
28942887
#endif
2888+
buf += wide && (buf % 8) ? 4 : 0;
2889+
readEmAsmArgsArray.push(
2890+
// Special case for pointers under wasm64 or CAN_ADDRESS_2GB mode.
2891+
ch == {{{ charCode('p') }}} ? {{{ makeGetValue('buf', 0, '*') }}} :
28952892
#if WASM_BIGINT
2896-
ch == {{{ charCode('j') }}} ? {{{ makeGetValue('buf', 0, 'i64') }}} :
2893+
ch == {{{ charCode('j') }}} ? {{{ makeGetValue('buf', 0, 'i64') }}} :
28972894
#endif
28982895
ch == {{{ charCode('i') }}} ?
28992896
{{{ makeGetValue('buf', 0, 'i32') }}} :
29002897
{{{ makeGetValue('buf', 0, 'double') }}}
29012898
);
2902-
buf += ch == {{{ charCode('i') }}} ? 4 : 8;
2899+
buf += wide ? 8 : 4;
29032900
}
29042901
return readEmAsmArgsArray;
29052902
},

src/library_int53.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ addToLibrary({
2424
{{{ makeSetValue('ptr', 4, '(num - lower)/4294967296', 'u32') }}};
2525
#if ASSERTIONS
2626
var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr);
27-
if (deserialized != num) warnOnce(`writeI53ToI64() out of range: serialized JS Number ${num} to Wasm heap as bytes lo=${ptrToString(HEAPU32[ptr/4])}, hi=${ptrToString(HEAPU32[ptr/4+1])}, which deserializes back to ${deserialized} instead!`);
27+
var offset = {{{ getHeapOffset('ptr', 'u32') }}};
28+
if (deserialized != num) warnOnce(`writeI53ToI64() out of range: serialized JS Number ${num} to Wasm heap as bytes lo=${ptrToString(HEAPU32[offset])}, hi=${ptrToString(HEAPU32[offset+1])}, which deserializes back to ${deserialized} instead!`);
2829
#endif
2930
},
3031

@@ -48,9 +49,9 @@ addToLibrary({
4849
$writeI53ToI64Signaling: (ptr, num) => {
4950
if (num > 0x7FFFFFFFFFFFFFFF || num < -0x8000000000000000) {
5051
#if ASSERTIONS
51-
throw 'RangeError in writeI53ToI64Signaling(): input value ' + num + ' is out of range of int64';
52+
throw `RangeError in writeI53ToI64Signaling(): input value ${num} is out of range of int64`;
5253
#else
53-
throw 'RangeError:' + num;
54+
throw `RangeError: ${num}`;
5455
#endif
5556
}
5657
writeI53ToI64(ptr, num);
@@ -76,9 +77,9 @@ addToLibrary({
7677
$writeI53ToU64Signaling: (ptr, num) => {
7778
if (num < 0 || num > 0xFFFFFFFFFFFFFFFF) {
7879
#if ASSERTIONS
79-
throw 'RangeError in writeI53ToU64Signaling(): input value ' + num + ' is out of range of uint64';
80+
throw `RangeError in writeI53ToU64Signaling(): input value ${num} is out of range of uint64`;
8081
#else
81-
throw 'RangeError:'+num;
82+
throw `RangeError: ${num}`;
8283
#endif
8384
}
8485
writeI53ToI64(ptr, num);

src/library_pthread.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ var LibraryPThread = {
163163

164164
threadStatusAsString(pthreadPtr) {
165165
var profilerBlock = {{{ makeGetValue('pthreadPtr', C_STRUCTS.pthread.profilerBlock, POINTER_TYPE) }}};
166-
var status = (profilerBlock == 0) ? 0 : Atomics.load(HEAPU32, (profilerBlock + {{{ C_STRUCTS.thread_profiler_block.threadStatus }}} ) >> 2);
166+
var status = (profilerBlock == 0) ? 0 : Atomics.load(HEAPU32, {{{ getHeapOffset('profilerBlock + ' + C_STRUCTS.thread_profiler_block.threadStatus, 'i32') }}});
167167
return PThread.threadStatusToString(status);
168168
},
169169
#endif
@@ -972,7 +972,7 @@ var LibraryPThread = {
972972
// values we serialize for proxying. TODO: pack this?
973973
var serializedNumCallArgs = numCallArgs {{{ WASM_BIGINT ? "* 2" : "" }}};
974974
var args = stackAlloc(serializedNumCallArgs * 8);
975-
var b = args >> 3;
975+
var b = {{{ getHeapOffset('args', 'i64') }}};
976976
for (var i = 0; i < numCallArgs; i++) {
977977
var arg = outerArgs[2 + i];
978978
#if WASM_BIGINT
@@ -1008,7 +1008,7 @@ var LibraryPThread = {
10081008
numCallArgs /= 2;
10091009
#endif
10101010
proxiedJSCallArgs.length = numCallArgs;
1011-
var b = args >> 3;
1011+
var b = {{{ getHeapOffset('args', 'i64') }}};
10121012
for (var i = 0; i < numCallArgs; i++) {
10131013
#if WASM_BIGINT
10141014
if (HEAP64[b + 2*i]) {
@@ -1233,13 +1233,13 @@ var LibraryPThread = {
12331233
_emscripten_thread_mailbox_await: (pthread_ptr) => {
12341234
if (typeof Atomics.waitAsync === 'function') {
12351235
// TODO: How to make this work with wasm64?
1236-
var wait = Atomics.waitAsync(HEAP32, pthread_ptr >> 2, pthread_ptr);
1236+
var wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('pthread_ptr', 'i32') }}}, pthread_ptr);
12371237
#if ASSERTIONS
12381238
assert(wait.async);
12391239
#endif
12401240
wait.value.then(checkMailbox);
12411241
var waitingAsync = pthread_ptr + {{{ C_STRUCTS.pthread.waiting_async }}};
1242-
Atomics.store(HEAP32, waitingAsync >> 2, 1);
1242+
Atomics.store(HEAP32, {{{ getHeapOffset('waitingAsync', 'i32') }}}, 1);
12431243
}
12441244
},
12451245

src/library_wasm_worker.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,15 +294,15 @@ Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => {
294294

295295
emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$jstoi_q'],
296296
emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => {
297-
let wait = Atomics.waitAsync(HEAP32, addr >> 2, val, maxWaitMilliseconds);
297+
let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
298298
if (!wait.async) return atomicWaitStates.indexOf(wait.value);
299299
// Increment waitAsync generation counter, account for wraparound in case
300300
// application does huge amounts of waitAsyncs per second (not sure if
301301
// possible?)
302302
// Valid counterrange: 0...2^31-1
303303
let counter = liveAtomicWaitAsyncCounter;
304304
liveAtomicWaitAsyncCounter = Math.max(0, (liveAtomicWaitAsyncCounter+1)|0);
305-
liveAtomicWaitAsyncs[counter] = addr >> 2;
305+
liveAtomicWaitAsyncs[counter] = addr;
306306
wait.value.then((value) => {
307307
if (liveAtomicWaitAsyncs[counter]) {
308308
delete liveAtomicWaitAsyncs[counter];
@@ -323,13 +323,14 @@ Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => {
323323
warnOnce(`Attempted to call emscripten_atomic_cancel_wait_async() with an invalid wait token value ${waitToken}`);
324324
}
325325
#endif
326-
if (liveAtomicWaitAsyncs[waitToken]) {
326+
var address = liveAtomicWaitAsyncs[waitToken];
327+
if (address) {
327328
// Notify the waitAsync waiters on the memory location, so that JavaScript
328329
// garbage collection can occur.
329330
// See https://github.com/WebAssembly/threads/issues/176
330331
// This has the unfortunate effect of causing spurious wakeup of all other
331332
// waiters at the address (which causes a small performance loss).
332-
Atomics.notify(HEAP32, liveAtomicWaitAsyncs[waitToken]);
333+
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
333334
delete liveAtomicWaitAsyncs[waitToken];
334335
return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}};
335336
}
@@ -341,19 +342,18 @@ Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => {
341342
emscripten_atomic_cancel_all_wait_asyncs: () => {
342343
let waitAsyncs = Object.values(liveAtomicWaitAsyncs);
343344
waitAsyncs.forEach((address) => {
344-
Atomics.notify(HEAP32, address);
345+
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
345346
});
346347
liveAtomicWaitAsyncs = {};
347348
return waitAsyncs.length;
348349
},
349350

350351
emscripten_atomic_cancel_all_wait_asyncs_at_address__deps: ['$liveAtomicWaitAsyncs'],
351352
emscripten_atomic_cancel_all_wait_asyncs_at_address: (address) => {
352-
address >>= 2;
353353
let numCancelled = 0;
354354
Object.keys(liveAtomicWaitAsyncs).forEach((waitToken) => {
355355
if (liveAtomicWaitAsyncs[waitToken] == address) {
356-
Atomics.notify(HEAP32, address);
356+
Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}});
357357
delete liveAtomicWaitAsyncs[waitToken];
358358
numCancelled++;
359359
}

system/include/emscripten/em_asm.h

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,33 +69,27 @@ void emscripten_asm_const_async_on_main_thread(
6969

7070
// We can use the generic selection C11 feature (that clang supports pre-C11
7171
// as an extension) to emulate function overloading in C.
72-
// All other types, including *all* pointer types go through the default case.
73-
// This means we need to use a different default type for `__wasm64__` where
74-
// pointers are 64-bits wide, and we also need to include more non-default
75-
// cases, for example, we don't want `short` to end up using the wider default.
76-
#if __wasm64__
72+
// All other types, including *all* pointer types go through the default case
73+
#ifdef __wasm64__
74+
#define LONG_CODE 'j'
75+
#else
76+
#define LONG_CODE 'i'
77+
#endif
7778
#define _EM_ASM_SIG_CHAR(x) _Generic((x), \
7879
float: 'f', \
7980
double: 'd', \
8081
char: 'i', \
8182
unsigned char: 'i', \
8283
unsigned short: 'i', \
8384
unsigned int: 'i', \
84-
unsigned long: 'j', \
85+
unsigned long: LONG_CODE, \
8586
unsigned long long: 'j', \
8687
signed char: 'i', \
8788
signed short: 'i', \
8889
signed int: 'i', \
89-
signed long: 'j', \
90+
signed long: LONG_CODE, \
9091
signed long long: 'j', \
9192
default: 'p')
92-
#else
93-
#define _EM_ASM_SIG_CHAR(x) _Generic((x), \
94-
float: 'f', \
95-
double: 'd', \
96-
long long: 'j', \
97-
default: 'i')
98-
#endif
9993

10094
// This indirection is needed to allow us to concatenate computed results, e.g.
10195
// #define BAR(N) _EM_ASM_CONCATENATE(FOO_, N)
@@ -170,11 +164,7 @@ template<> struct __em_asm_sig<bool> { static const char value = 'i'; };
170164
template<> struct __em_asm_sig<wchar_t> { static const char value = 'i'; };
171165
template<> struct __em_asm_sig<long long> { static const char value = 'j'; };
172166
template<> struct __em_asm_sig<unsigned long long> { static const char value = 'j'; };
173-
#if __wasm64__
174167
template<typename T> struct __em_asm_sig<T*> { static const char value = 'p'; };
175-
#else
176-
template<typename T> struct __em_asm_sig<T*> { static const char value = 'i'; };
177-
#endif
178168

179169
// Explicit support for enums, they're passed as int via variadic arguments.
180170
template<bool> struct __em_asm_if { };

system/lib/emmalloc.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ static bool claim_more_memory(size_t numBytes)
484484
return false;
485485
}
486486
#ifdef EMMALLOC_VERBOSE
487-
MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory: claimed 0x' + ($0>>>0).toString(16) + ' - 0x' + ($1>>>0).toString(16) + ' (' + ($2>>>0) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
487+
MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory: claimed ' + ptrToString($0) + ' - ' + ptrToString($1) + ' (' + ($2>>>0) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
488488
#endif
489489
assert(HAS_ALIGNMENT(startPtr, alignof(size_t)));
490490
uint8_t *endPtr = startPtr + numBytes;
@@ -634,7 +634,7 @@ static void *attempt_allocate(Region *freeRegion, size_t alignment, size_t size)
634634
#endif
635635

636636
#ifdef EMMALLOC_VERBOSE
637-
MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_allocate - succeeded allocating memory, region ptr=0x' + ($0>>>0).toString(16) + ', align=' + $1 + ', payload size=' + ($2>>>0) + ' bytes)'), freeRegion, alignment, size);
637+
MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_allocate - succeeded allocating memory, region ptr=' + ptrToString($0) + ', align=' + $1 + ', payload size=' + ($2>>>0) + ' bytes)'), freeRegion, alignment, size);
638638
#endif
639639

640640
return (uint8_t*)freeRegion + sizeof(size_t);
@@ -661,6 +661,10 @@ static size_t validate_alloc_size(size_t size)
661661
return validatedSize;
662662
}
663663

664+
#ifdef EMMALLOC_VERBOSE
665+
EM_JS_DEPS(deps, "$ptrToString");
666+
#endif
667+
664668
static void *allocate_memory(size_t alignment, size_t size)
665669
{
666670
ASSERT_MALLOC_IS_ACQUIRED();

test/common.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,12 @@ def metafunc(self, with_wasm64):
339339
def can_do_standalone(self, impure=False):
340340
# Pure standalone engines don't support MEMORY64 yet. Even with MEMORY64=2 (lowered)
341341
# the WASI APIs that take pointer values don't have 64-bit variants yet.
342-
if self.get_setting('MEMORY64') and not impure:
343-
return False
342+
if not impure:
343+
if self.get_setting('MEMORY64'):
344+
return False
345+
# This is way to detect the core_2gb test mode in test_core.py
346+
if self.get_setting('INITIAL_MEMORY') == '2200mb':
347+
return False
344348
return self.is_wasm() and \
345349
self.get_setting('STACK_OVERFLOW_CHECK', 0) < 2 and \
346350
not self.get_setting('MINIMAL_RUNTIME') and \
@@ -1399,7 +1403,8 @@ def _test_dylink_dso_needed(self, do_run):
13991403
self.clear_setting('SIDE_MODULE')
14001404

14011405
# XXX in wasm each lib load currently takes 5MB; default INITIAL_MEMORY=16MB is thus not enough
1402-
self.set_setting('INITIAL_MEMORY', '32mb')
1406+
if not self.has_changed_setting('INITIAL_MEMORY'):
1407+
self.set_setting('INITIAL_MEMORY', '32mb')
14031408

14041409
so = '.wasm' if self.is_wasm() else '.js'
14051410

0 commit comments

Comments
 (0)