diff --git a/.circleci/config.yml b/.circleci/config.yml index 73d44bd5bab92..27c4fe69a9c19 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -616,15 +616,28 @@ jobs: title: "wasm64" test_targets: " wasm64 - wasm64_4gb.test_hello_world - wasm64_4gb.test_em_asm - wasm64_4gb.test_async_main - wasm64_4gb.*embind* core_2gb.test_em_asm wasm64l.test_bigswitch other.test_memory64_proxies other.test_failing_growth_wasm64" - upload-test-results + test-wasm64_4gb: + environment: + EMTEST_SKIP_NODE_CANARY: "1" + # We don't use `bionic` here since its tool old to run recent node versions: + # `/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found` + executor: linux-python + steps: + - prepare-for-tests + # The linux-python image uses /home/circleci rather than /root and jsvu + # hardcodes /root into its launcher scripts do we need to reinstall v8. + - run: rm -rf $HOME/.jsvu + - install-v8 + - install-node-canary + - run-tests: + title: "wasm64_4gb" + test_targets: "wasm64_4gb" + - upload-test-results test-jsc: executor: linux-python steps: @@ -876,6 +889,9 @@ workflows: - test-wasm64: requires: - build-linux + - test-wasm64_4gb: + requires: + - build-linux - test-wasm64l: requires: - build-linux diff --git a/emcc.py b/emcc.py index 8866def6297bf..badae0eed9e66 100755 --- a/emcc.py +++ b/emcc.py @@ -1783,7 +1783,10 @@ def set_max_memory(): # INITIAL_MEMORY sets a lower bound for MAXIMUM_MEMORY if 'MAXIMUM_MEMORY' not in user_settings: - if settings.INITIAL_MEMORY > settings.MAXIMUM_MEMORY: + if settings.ALLOW_MEMORY_GROWTH and settings.MEMORY64: + # 16gb is the current limit for wasm-ld + settings.MAXIMUM_MEMORY = 16 * 1024 * 1024 * 1024 + elif settings.INITIAL_MEMORY > settings.MAXIMUM_MEMORY: settings.MAXIMUM_MEMORY = settings.INITIAL_MEMORY if settings.MAXIMUM_MEMORY < settings.INITIAL_MEMORY: diff --git a/src/library.js b/src/library.js index 0ec39155fc68d..b33d2ea65d7ff 100644 --- a/src/library.js +++ b/src/library.js @@ -159,9 +159,9 @@ addToLibrary({ // it. Returns 1 on success, 0 on error. $growMemory: (size) => { var b = wasmMemory.buffer; - var pages = (size - b.byteLength + 65535) >>> 16; + var pages = (size - b.byteLength + {{{ WASM_PAGE_SIZE - 1 }}}) / {{{ WASM_PAGE_SIZE }}}; #if RUNTIME_DEBUG - dbg(`emscripten_resize_heap: ${size} (+${size - b.byteLength} bytes / ${pages} pages)`); + dbg(`growMemory: ${size} (+${size - b.byteLength} bytes / ${pages} pages)`); #endif #if MEMORYPROFILER var oldHeapSize = b.byteLength; @@ -249,7 +249,7 @@ addToLibrary({ var maxHeapSize = getHeapMax(); if (requestedSize > maxHeapSize) { #if ASSERTIONS - err(`Cannot enlarge memory, asked to go up to ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); + err(`Cannot enlarge memory, requested ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); #endif #if ABORTING_MALLOC abortOnCannotGrowMemory(requestedSize); @@ -693,7 +693,7 @@ addToLibrary({ // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html - var tm_zone = {{{ makeGetValue('tm', C_STRUCTS.tm.tm_zone, 'i32') }}}; + var tm_zone = {{{ makeGetValue('tm', C_STRUCTS.tm.tm_zone, '*') }}}; var date = { tm_sec: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}}, diff --git a/src/library_fs_shared.js b/src/library_fs_shared.js index e5cc06b0b0ed6..a8eab34cb8ddb 100644 --- a/src/library_fs_shared.js +++ b/src/library_fs_shared.js @@ -133,7 +133,7 @@ addToLibrary({ var fd = process.stdin.fd; try { - bytesRead = fs.readSync(fd, buf, 0, BUFSIZE, -1); + bytesRead = fs.readSync(fd, buf, { position: -1 }); } catch(e) { // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes, // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0. diff --git a/src/library_int53.js b/src/library_int53.js index be69b841084e1..20bc5033a0530 100644 --- a/src/library_int53.js +++ b/src/library_int53.js @@ -19,30 +19,32 @@ addToLibrary({ // function $writeI53ToU64(): the implementation would be identical, and it is up to the // C/C++ side code to interpret the resulting number as signed or unsigned as is desirable. $writeI53ToI64: (ptr, num) => { - HEAPU32[ptr>>2] = num; - HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; + {{{ makeSetValue('ptr', 0, 'num', 'u32') }}}; + var lower = {{{ makeGetValue('ptr', 0, 'u32') }}}; + {{{ makeSetValue('ptr', 4, '(num - lower)/4294967296', 'u32') }}}; #if ASSERTIONS var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr); - if (deserialized != num) warnOnce('writeI53ToI64() out of range: serialized JS Number ' + num + ' to Wasm heap as bytes lo=' + ptrToString(HEAPU32[ptr>>2]) + ', hi=' + ptrToString(HEAPU32[ptr+4>>2]) + ', which deserializes back to ' + deserialized + ' instead!'); + 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!`); #endif }, // Same as writeI53ToI64, but if the double precision number does not fit within the // 64-bit number, the number is clamped to range [-2^63, 2^63-1]. + $writeI53ToI64Clamped__deps: ['$writeI53ToI64'], $writeI53ToI64Clamped: (ptr, num) => { if (num > 0x7FFFFFFFFFFFFFFF) { - HEAPU32[ptr>>2] = 0xFFFFFFFF; - HEAPU32[ptr+4>>2] = 0x7FFFFFFF; + {{{ makeSetValue('ptr', 0, 0xFFFFFFFF, 'u32') }}}; + {{{ makeSetValue('ptr', 4, 0x7FFFFFFF, 'u32') }}}; } else if (num < -0x8000000000000000) { - HEAPU32[ptr>>2] = 0; - HEAPU32[ptr+4>>2] = 0x80000000; + {{{ makeSetValue('ptr', 0, 0, 'u32') }}}; + {{{ makeSetValue('ptr', 4, 0x80000000, 'u32') }}}; } else { - HEAPU32[ptr>>2] = num; - HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; + writeI53ToI64(ptr, num); } }, // Like writeI53ToI64, but throws if the passed number is out of range of int64. + $writeI53ToI64__deps: ['$writeI53ToI64'], $writeI53ToI64Signaling: (ptr, num) => { if (num > 0x7FFFFFFFFFFFFFFF || num < -0x8000000000000000) { #if ASSERTIONS @@ -51,22 +53,26 @@ addToLibrary({ throw 'RangeError:' + num; #endif } - HEAPU32[ptr>>2] = num; - HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; + writeI53ToI64(ptr, num); }, // Uint64 variant of writeI53ToI64Clamped. Writes the Number to a Uint64 variable on // the heap, clamping out of range values to range [0, 2^64-1]. + $writeI53ToU64Clamped__deps: ['$writeI53ToI64'], $writeI53ToU64Clamped: (ptr, num) => { - if (num > 0xFFFFFFFFFFFFFFFF) HEAPU32[ptr>>2] = HEAPU32[ptr+4>>2] = 0xFFFFFFFF; - else if (num < 0) HEAPU32[ptr>>2] = HEAPU32[ptr+4>>2] = 0; - else { - HEAPU32[ptr>>2] = num; - HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; + if (num > 0xFFFFFFFFFFFFFFFF) { + {{{ makeSetValue('ptr', 0, 0xFFFFFFFF, 'u32') }}}; + {{{ makeSetValue('ptr', 4, 0xFFFFFFFF, 'u32') }}}; + } else if (num < 0) { + {{{ makeSetValue('ptr', 0, 0, 'u32') }}}; + {{{ makeSetValue('ptr', 4, 0, 'u32') }}}; + } else { + writeI53ToI64(ptr, num); } }, // Like writeI53ToI64, but throws if the passed number is out of range of uint64. + $writeI53ToU64Signaling__deps: ['$writeI53ToI64'], $writeI53ToU64Signaling: (ptr, num) => { if (num < 0 || num > 0xFFFFFFFFFFFFFFFF) { #if ASSERTIONS @@ -75,22 +81,21 @@ addToLibrary({ throw 'RangeError:'+num; #endif } - HEAPU32[ptr>>2] = num; - HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; + writeI53ToI64(ptr, num); }, // Reads a 64-bit signed integer from the WebAssembly heap and // converts it to a JavaScript Number, which can represent 53 integer bits precisely. // TODO: Add $readI53FromI64Signaling() variant. $readI53FromI64: (ptr) => { - return HEAPU32[ptr>>2] + HEAP32[ptr+4>>2] * 4294967296; + return {{{ makeGetValue('ptr', 0, 'u32') }}} + {{{ makeGetValue('ptr', 4, 'i32') }}} * 4294967296; }, // Reads a 64-bit unsigned integer from the WebAssembly heap and // converts it to a JavaScript Number, which can represent 53 integer bits precisely. // TODO: Add $readI53FromU64Signaling() variant. $readI53FromU64: (ptr) => { - return HEAPU32[ptr>>2] + HEAPU32[ptr+4>>2] * 4294967296; + return {{{ makeGetValue('ptr', 0, 'u32') }}} + {{{ makeGetValue('ptr', 4, 'u32') }}} * 4294967296; }, // Converts the given signed 32-bit low-high pair to a JavaScript Number that diff --git a/src/library_math.js b/src/library_math.js index 6c8a63099aed1..c971124f4db68 100644 --- a/src/library_math.js +++ b/src/library_math.js @@ -23,7 +23,9 @@ addToLibrary({ emscripten_math_cosh: (x) => Math.cosh(x), emscripten_math_hypot: (count, varargs) => { var args = []; - for (var i = 0; i < count; ++i) args.push(HEAPF64[(varargs>>3) + i]); + for (var i = 0; i < count; ++i) { + args.push({{{ makeGetValue('varargs', `i * ${getNativeTypeSize('double')}`, 'double') }}}); + } return Math.hypot.apply(null, args); }, emscripten_math_sin: (x) => Math.sin(x), diff --git a/src/library_nodefs.js b/src/library_nodefs.js index aec4edb789ce5..43d5d8e1a9f74 100644 --- a/src/library_nodefs.js +++ b/src/library_nodefs.js @@ -269,17 +269,18 @@ addToLibrary({ } }, read(stream, buffer, offset, length, position) { + //err(`nodefs read: offset=${offset} length=${length} position=${position}`); // Node.js < 6 compatibility: node errors on 0 length reads if (length === 0) return 0; try { - return fs.readSync(stream.nfd, Buffer.from(buffer.buffer), offset, length, position); + return fs.readSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), { position: position }); } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); } }, write(stream, buffer, offset, length, position) { try { - return fs.writeSync(stream.nfd, Buffer.from(buffer.buffer), offset, length, position); + return fs.writeSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), { position: position }); } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); } diff --git a/src/library_noderawfs.js b/src/library_noderawfs.js index 2f73095cf3307..699ed8bdf78ad 100644 --- a/src/library_noderawfs.js +++ b/src/library_noderawfs.js @@ -148,7 +148,7 @@ addToLibrary({ } var seeking = typeof position != 'undefined'; if (!seeking && stream.seekable) position = stream.position; - var bytesRead = fs.readSync(stream.nfd, Buffer.from(buffer.buffer), offset, length, position); + var bytesRead = fs.readSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), { position: position }); // update position marker when non-seeking if (!seeking) stream.position += bytesRead; return bytesRead; @@ -164,7 +164,7 @@ addToLibrary({ } var seeking = typeof position != 'undefined'; if (!seeking && stream.seekable) position = stream.position; - var bytesWritten = fs.writeSync(stream.nfd, Buffer.from(buffer.buffer), offset, length, position); + var bytesWritten = fs.writeSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), { position: position }); // update position marker when non-seeking if (!seeking) stream.position += bytesWritten; return bytesWritten; diff --git a/src/library_pipefs.js b/src/library_pipefs.js index 8b988ffbe8e51..acede0dc12095 100644 --- a/src/library_pipefs.js +++ b/src/library_pipefs.js @@ -92,10 +92,12 @@ addToLibrary({ currentLength += bucket.offset - bucket.roffset; } +#if !MEMORY64 #if PTHREADS assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer)); #else assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer)); +#endif #endif var data = buffer.subarray(offset, offset + length); @@ -149,10 +151,12 @@ addToLibrary({ write(stream, buffer, offset, length, position /* ignored */) { var pipe = stream.node.pipe; +#if !MEMORY64 #if PTHREADS assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer)); #else assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer)); +#endif #endif var data = buffer.subarray(offset, offset + length); diff --git a/src/library_syscall.js b/src/library_syscall.js index 9494c3b2074d7..23b668b4b7a5a 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -93,13 +93,30 @@ var SyscallsLibrary = { #if ASSERTIONS assert(SYSCALLS.varargs != undefined); #endif + var ret = {{{ makeGetValue('SYSCALLS.varargs', 0, 'i32') }}}; SYSCALLS.varargs += 4; - var ret = {{{ makeGetValue('SYSCALLS.varargs', '-4', 'i32') }}}; #if SYSCALL_DEBUG dbg(` (raw: "${ret}")`); #endif return ret; }, + +#if MEMORY64 + getp() { +#if ASSERTIONS + assert(SYSCALLS.varargs != undefined); +#endif + var ret = {{{ makeGetValue('SYSCALLS.varargs', 0, '*') }}}; + SYSCALLS.varargs += {{{ POINTER_SIZE }}}; +#if SYSCALL_DEBUG + dbg(` (raw: "${ret}")`); +#endif + return ret; + }, +#else + getp() { return SYSCALLS.get() }, +#endif + getStr(ptr) { var ret = UTF8ToString(ptr); #if SYSCALL_DEBUG @@ -216,7 +233,7 @@ var SyscallsLibrary = { if (!stream.tty) return -{{{ cDefs.ENOTTY }}}; if (stream.tty.ops.ioctl_tcgets) { var termios = stream.tty.ops.ioctl_tcgets(stream); - var argp = SYSCALLS.get(); + var argp = SYSCALLS.getp(); {{{ makeSetValue('argp', C_STRUCTS.termios.c_iflag, 'termios.c_iflag || 0', 'i32') }}}; {{{ makeSetValue('argp', C_STRUCTS.termios.c_oflag, 'termios.c_oflag || 0', 'i32') }}}; {{{ makeSetValue('argp', C_STRUCTS.termios.c_cflag, 'termios.c_cflag || 0', 'i32') }}}; @@ -242,7 +259,7 @@ var SyscallsLibrary = { case {{{ cDefs.TCSETSF }}}: { if (!stream.tty) return -{{{ cDefs.ENOTTY }}}; if (stream.tty.ops.ioctl_tcsets) { - var argp = SYSCALLS.get(); + var argp = SYSCALLS.getp(); var c_iflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_iflag, 'i32') }}}; var c_oflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_oflag, 'i32') }}}; var c_cflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_cflag, 'i32') }}}; @@ -257,7 +274,7 @@ var SyscallsLibrary = { } case {{{ cDefs.TIOCGPGRP }}}: { if (!stream.tty) return -{{{ cDefs.ENOTTY }}}; - var argp = SYSCALLS.get(); + var argp = SYSCALLS.getp(); {{{ makeSetValue('argp', 0, 0, 'i32') }}}; return 0; } @@ -266,7 +283,7 @@ var SyscallsLibrary = { return -{{{ cDefs.EINVAL }}}; // not supported } case {{{ cDefs.FIONREAD }}}: { - var argp = SYSCALLS.get(); + var argp = SYSCALLS.getp(); return FS.ioctl(stream, op, argp); } case {{{ cDefs.TIOCGWINSZ }}}: { @@ -275,7 +292,7 @@ var SyscallsLibrary = { if (!stream.tty) return -{{{ cDefs.ENOTTY }}}; if (stream.tty.ops.ioctl_tiocgwinsz) { var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); - var argp = SYSCALLS.get(); + var argp = SYSCALLS.getp(); {{{ makeSetValue('argp', 0, 'winsize[0]', 'i16') }}}; {{{ makeSetValue('argp', 2, 'winsize[1]', 'i16') }}}; } @@ -770,7 +787,7 @@ var SyscallsLibrary = { return 0; } case {{{ cDefs.F_GETLK }}}: { - var arg = SYSCALLS.get(); + var arg = SYSCALLS.getp(); var offset = {{{ C_STRUCTS.flock.l_type }}}; // We're always unlocked. {{{ makeSetValue('arg', 'offset', cDefs.F_UNLCK, 'i16') }}}; diff --git a/src/library_wasmfs_node.js b/src/library_wasmfs_node.js index 791ad04fd8764..9eac3c61f35a8 100644 --- a/src/library_wasmfs_node.js +++ b/src/library_wasmfs_node.js @@ -187,7 +187,7 @@ addToLibrary({ try { // TODO: Cache open file descriptors to guarantee that opened files will // still exist when we try to access them. - let nread = fs.readSync(fd, HEAPU8, buf_p, len, pos); + let nread = fs.readSync(fd, new Int8Array(HEAPU8.buffer, buf_p, len), { position: pos }); {{{ makeSetValue('nread_p', 0, 'nread', 'i32') }}}; } catch (e) { if (!e.code) throw e; @@ -201,7 +201,7 @@ addToLibrary({ try { // TODO: Cache open file descriptors to guarantee that opened files will // still exist when we try to access them. - let nwritten = fs.writeSync(fd, HEAPU8, buf_p, len, pos); + let nwritten = fs.writeSync(fd, new Int8Array(HEAPU8.buffer, buf_p, len), { position: pos }); {{{ makeSetValue('nwritten_p', 0, 'nwritten', 'i32') }}}; } catch (e) { if (!e.code) throw e; diff --git a/src/preamble_minimal.js b/src/preamble_minimal.js index a4a34988dd423..d09991a2dd694 100644 --- a/src/preamble_minimal.js +++ b/src/preamble_minimal.js @@ -77,11 +77,15 @@ function updateMemoryViews() { #if SUPPORT_BIG_ENDIAN {{{ maybeExport('HEAP_DATA_VIEW') }}} HEAP_DATA_VIEW = new DataView(b); #endif +#if MEMORY64 && MAXIMUM_MEMORY > FOUR_GB +#include "runtime_view_proxy.js" +#else {{{ maybeExport('HEAP8') }}} HEAP8 = new Int8Array(b); {{{ maybeExport('HEAP16') }}} HEAP16 = new Int16Array(b); - {{{ maybeExport('HEAP32') }}} HEAP32 = new Int32Array(b); {{{ maybeExport('HEAPU8') }}} HEAPU8 = new Uint8Array(b); {{{ maybeExport('HEAPU16') }}} HEAPU16 = new Uint16Array(b); +#endif + {{{ maybeExport('HEAP32') }}} HEAP32 = new Int32Array(b); {{{ maybeExportIfAudioWorklet('HEAPU32') }}} HEAPU32 = new Uint32Array(b); {{{ maybeExportIfAudioWorklet('HEAPF32') }}} HEAPF32 = new Float32Array(b); {{{ maybeExport('HEAPF64') }}} HEAPF64 = new Float64Array(b); diff --git a/src/runtime_view_proxy.js b/src/runtime_view_proxy.js index 0f97f27ae1801..2e9ff62dc439f 100644 --- a/src/runtime_view_proxy.js +++ b/src/runtime_view_proxy.js @@ -42,7 +42,7 @@ function getHeapBlock(type, offset, length) { } } -function createProxyHandler(type, heapBlocks) { +function createProxyHandler(type, heapBlocks, byteLength) { let firstHeapBlock = heapBlocks[0] let bpe = firstHeapBlock.BYTES_PER_ELEMENT @@ -59,9 +59,8 @@ function createProxyHandler(type, heapBlocks) { let sourceArray = getHeapBlock(type, start * bpe, len) targetArray.set(sourceArray) return heapBlocks[0] - } else { - return heapBlocks[0].copyWithin(target, start, end) } + return heapBlocks[0].copyWithin(target, start, end) } function setOverridden(array, offset) { @@ -78,9 +77,8 @@ function createProxyHandler(type, heapBlocks) { let [startReal, endReal] = getRealStartAndEnd(start, end) if (startReal >= maxArraySize || endReal >= maxArraySize) { return getHeapBlock(type, startReal, endReal - startReal) - } else { - return firstHeapBlock.subarray(start, end) } + return firstHeapBlock.subarray(start, end) } function fill(value, start, end) { @@ -89,18 +87,16 @@ function createProxyHandler(type, heapBlocks) { let hb = getHeapBlock(type, startReal, endReal - startReal) hb.fill(value, 0, end - start) return firstHeapBlock - } else { - return firstHeapBlock.fill(value, start, end) } + return firstHeapBlock.fill(value, start, end) } function slice(start, end) { let [startReal, endReal] = getRealStartAndEnd(start, end) if (startReal >= maxArraySize || endReal >= maxArraySize) { let hb = getHeapBlock(type, startReal, endReal - startReal) - return hb.slice(start, end) - } else { - return firstHeapBlock.slice(start, end) + return hb.slice(0, end - start) } + return firstHeapBlock.slice(start, end) } return { @@ -110,27 +106,14 @@ function createProxyHandler(type, heapBlocks) { let blockNumber = Math.floor(memoryOffset / maxArraySize) return heapBlocks[blockNumber][property - blockNumber * maxArraySize] } - - if (property === 'copyWithin') { - return copyWithin - } - - if (property === 'set') { - return setOverridden - } - - if (property === 'subarray') { - return subarray - } - - if (property === 'fill') { - return fill + switch (property) { + case 'copyWithin': return copyWithin; + case 'set': return setOverridden; + case 'subarray': return subarray; + case 'fill': return fill; + case 'slice': return slice; + case 'length': return byteLength; } - - if (property === 'slice') { - return slice - } - return firstHeapBlock[property] }, set(target, property, value) { @@ -154,7 +137,7 @@ function createMemoryProxy(type) { for (let i = 0; i < numberOfBlocks; i++) { heapBlocks.push(getHeapBlock(type, i * maxArraySize * bpe)) } - return new Proxy(heapBlocks[0], createProxyHandler(type, heapBlocks)); + return new Proxy(heapBlocks[0], createProxyHandler(type, heapBlocks, b.byteLength)); } if (b.byteLength > maxArraySize) { diff --git a/test/common.py b/test/common.py index 2d2776c07917d..0a5c2a79b9101 100644 --- a/test/common.py +++ b/test/common.py @@ -575,6 +575,10 @@ def check_dylink(self): self.skipTest('no dynamic linking support in wasm2js yet') if '-fsanitize=undefined' in self.emcc_args: self.skipTest('no dynamic linking support in UBSan yet') + # Dynamic linking requires IMPORTED_MEMORY which depends on the JS API + # for creating 64-bit memories. + if self.get_setting('GLOBAL_BASE') == '4gb': + self.skipTest('no support for IMPORTED_MEMORY over 4gb yet') def require_v8(self): if not config.V8_ENGINE or config.V8_ENGINE not in config.JS_ENGINES: diff --git a/test/core/test_int53.c b/test/core/test_int53.c index e72c04bc5f7a1..c99c29a0db913 100644 --- a/test/core/test_int53.c +++ b/test/core/test_int53.c @@ -58,8 +58,8 @@ void writeI53ToI64Signaling_double(int64_t *heapAddress, double num) { EM_ASM(try { writeI53ToI64Signaling($0, $1) } catch(e) { - HEAPU32[$0>>2] = 0x01020304; - HEAPU32[$0+4>>2] = 0x01020304; + HEAPU32[($0) / 4] = 0x01020304; + HEAPU32[($0+4) / 4] = 0x01020304; }, heapAddress, num); #endif } @@ -88,8 +88,8 @@ void writeI53ToU64Signaling_double(uint64_t *heapAddress, double num) { EM_ASM(try { writeI53ToU64Signaling($0, $1) } catch(e) { - HEAPU32[$0>>2] = 0x01020304; - HEAPU32[$0+4>>2] = 0x01020304; + HEAPU32[$0 / 4] = 0x01020304; + HEAPU32[($0+4) / 4] = 0x01020304; } , heapAddress, num); #endif } @@ -263,7 +263,7 @@ int main() { printf(" writeI53ToI64Clamped: 0x%llx (%lld): error difference: %g\n", u, u, u - num); writeI53ToI64Signaling_double(&u, num); if (u == 0x0102030401020304) - printf(" writeI53ToU64Signaling: (RangeError)\n"); + printf(" writeI53ToI64Signaling: (RangeError)\n"); else printf(" writeI53ToI64Signaling: 0x%llx (%lld): error difference: %g\n", u, u, u - num); writeI53ToU64Clamped_double((uint64_t*)&u, num); diff --git a/test/core/test_int53.out b/test/core/test_int53.out index 1e313fb127037..b0ac5b411596c 100644 --- a/test/core/test_int53.out +++ b/test/core/test_int53.out @@ -667,28 +667,28 @@ Testing otherDoubles: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000: writeI53ToI64: 0x0 (0): error difference: -1.79769e+308 writeI53ToI64Clamped: 0x7fffffffffffffff (9223372036854775807): error difference: -1.79769e+308 - writeI53ToU64Signaling: (RangeError) + writeI53ToI64Signaling: (RangeError) writeI53ToU64Clamped: 0xffffffffffffffff (-1): error difference: -1.79769e+308 writeI53ToU64Signaling: (RangeError) -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000: writeI53ToI64: 0x0 (0): error difference: 1.79769e+308 writeI53ToI64Clamped: 0x8000000000000000 (-9223372036854775808): error difference: 1.79769e+308 - writeI53ToU64Signaling: (RangeError) + writeI53ToI64Signaling: (RangeError) writeI53ToU64Clamped: 0x0 (0): error difference: 1.79769e+308 writeI53ToU64Signaling: (RangeError) inf: writeI53ToI64: 0x0 (0): error difference: -inf writeI53ToI64Clamped: 0x7fffffffffffffff (9223372036854775807): error difference: -inf - writeI53ToU64Signaling: (RangeError) + writeI53ToI64Signaling: (RangeError) writeI53ToU64Clamped: 0xffffffffffffffff (-1): error difference: -inf writeI53ToU64Signaling: (RangeError) -inf: writeI53ToI64: 0x0 (0): error difference: inf writeI53ToI64Clamped: 0x8000000000000000 (-9223372036854775808): error difference: inf - writeI53ToU64Signaling: (RangeError) + writeI53ToI64Signaling: (RangeError) writeI53ToU64Clamped: 0x0 (0): error difference: inf writeI53ToU64Signaling: (RangeError) diff --git a/test/stdio/test_fgetc_ungetc.c b/test/stdio/test_fgetc_ungetc.c index ff2d6678e635f..4d1e0e5a2dbf9 100644 --- a/test/stdio/test_fgetc_ungetc.c +++ b/test/stdio/test_fgetc_ungetc.c @@ -58,6 +58,7 @@ void test() { int r = fread(buffer, sizeof(char), sizeof(buffer), file); assert(r == 3); buffer[3] = 0; + printf("buffer: %s\n", buffer); assert(!strcmp(buffer, "bcd")); // rewind and fseek should reset anything that's been diff --git a/test/test_core.py b/test/test_core.py index d2fde2ab58ffd..423fe1c3a739b 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -251,6 +251,21 @@ def decorated(self, *args, **kwargs): return decorator +def no_4gb(note): + assert not callable(note) + + def decorator(f): + assert callable(f) + + @wraps(f) + def decorated(self, *args, **kwargs): + if self.get_setting('INITIAL_MEMORY') == '4200mb': + self.skipTest(note) + f(self, *args, **kwargs) + return decorated + return decorator + + def no_ubsan(note): assert not callable(note) @@ -896,6 +911,7 @@ def test_stack_placement(self): @no_sanitize('sanitizers do not yet support dynamic linking') @no_wasm2js('MAIN_MODULE support') + @needs_dylink def test_stack_placement_pic(self): self.set_setting('STACK_SIZE', 1024) self.set_setting('MAIN_MODULE') @@ -978,6 +994,7 @@ def test_emmalloc_usable_size(self, *args): @no_asan('ASan does not support custom memory allocators') @no_lsan('LSan does not support custom memory allocators') @no_ubsan('UBSan changes memory consumption') + @no_4gb('uses INITIAL_MEMORY') def test_emmalloc_memory_statistics(self): if self.is_wasm64(): out_suffix = '64' @@ -2257,6 +2274,7 @@ def test_runtime_stacksave(self): self.do_runf(test_file('core/test_runtime_stacksave.c'), 'success') # Tests that -sMINIMAL_RUNTIME builds can utilize -sALLOW_MEMORY_GROWTH option. + @no_4gb('memory growth issues') def test_minimal_runtime_memorygrowth(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') @@ -2268,6 +2286,7 @@ def test_minimal_runtime_memorygrowth(self): self.set_setting('ALLOW_MEMORY_GROWTH') self.do_runf(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') + @no_4gb('memory growth issues') def test_memorygrowth(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') @@ -2302,6 +2321,7 @@ def test_memorygrowth(self): self.emcc_args += ['--tracing'] self.do_runf(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') + @no_4gb('memory growth issues') def test_memorygrowth_2(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') @@ -2333,6 +2353,7 @@ def test_memorygrowth_3(self): self.do_core_test('test_memorygrowth_3.c') @also_with_standalone_wasm(impure=True) + @no_4gb('depends on INITIAL_MEMORY') def test_memorygrowth_MAXIMUM_MEMORY(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') @@ -2343,6 +2364,7 @@ def test_memorygrowth_MAXIMUM_MEMORY(self): self.emcc_args += ['-sALLOW_MEMORY_GROWTH', '-sINITIAL_MEMORY=64Mb', '-sMAXIMUM_MEMORY=100Mb'] self.do_core_test('test_memorygrowth_wasm_mem_max.c') + @no_4gb('depends on INITIAL_MEMORY') def test_memorygrowth_linear_step(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') @@ -2354,6 +2376,7 @@ def test_memorygrowth_linear_step(self): self.do_core_test('test_memorygrowth_memory_growth_step.c') @no_ubsan('UBSan seems to effect the precise memory usage') + @no_4gb('depends on specifc memory layout') def test_memorygrowth_geometric_step(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') @@ -2377,6 +2400,7 @@ def test_memorygrowth_3_force_fail_reallocBuffer(self): }) @no_asan('requires more memory when growing') @no_lsan('requires more memory when growing') + @no_4gb('depends on INITIAL_MEMORY') def test_aborting_new(self, args): # test that C++ new properly errors if we fail to malloc when growth is # enabled, with or without growth @@ -2386,6 +2410,7 @@ def test_aborting_new(self, args): @no_wasm2js('no WebAssembly.Memory()') @no_asan('ASan alters the memory size') @no_lsan('LSan alters the memory size') + @no_4gb('uses IMPORTED_MEMORY') def test_module_wasm_memory(self): self.emcc_args += ['--pre-js', test_file('core/test_module_wasm_memory.js')] self.set_setting('IMPORTED_MEMORY') @@ -6169,6 +6194,12 @@ def test_unistd_sysconf_phys_pages(self): filename = test_file('unistd/sysconf_phys_pages.c') if self.get_setting('ALLOW_MEMORY_GROWTH'): expected = (2 * 1024 * 1024 * 1024) // webassembly.WASM_PAGE_SIZE + elif self.has_changed_setting('INITIAL_MEMORY'): + if self.get_setting('INITIAL_MEMORY') == '4200mb': + expected = (4200 * 1024 * 1024) // webassembly.WASM_PAGE_SIZE + else: + assert self.get_setting('INITIAL_MEMORY') == '2200mb' + expected = (2200 * 1024 * 1024) // webassembly.WASM_PAGE_SIZE else: expected = 16 * 1024 * 1024 // webassembly.WASM_PAGE_SIZE self.do_runf(filename, str(expected) + ', errno: 0') @@ -6516,6 +6547,7 @@ def test_whets(self): @requires_v8 @no_asan('depends on the specifics of memory size, which for asan we are forced to increase') @no_lsan('depends on the specifics of memory size, which for lsan we are forced to increase') + @no_4gb('uses INITIAL_MEMORY') def test_dlmalloc_inline(self): # needed with typed arrays self.set_setting('INITIAL_MEMORY', '128mb') @@ -6529,6 +6561,7 @@ def test_dlmalloc_inline(self): @no_asan('depends on the specifics of memory size, which for asan we are forced to increase') @no_lsan('depends on the specifics of memory size, which for lsan we are forced to increase') @no_wasmfs('wasmfs does some malloc/free during startup, fragmenting the heap, leading to differences later') + @no_4gb('uses INITIAL_MEMORY') def test_dlmalloc(self): # needed with typed arrays self.set_setting('INITIAL_MEMORY', '128mb') @@ -6561,6 +6594,7 @@ def test_dlmalloc(self): # Tests that a large allocation should gracefully fail @no_asan('the memory size limit here is too small for asan') @no_lsan('the memory size limit here is too small for lsan') + @no_4gb('uses INITIAL_MEMORY') def test_dlmalloc_large(self): self.emcc_args += ['-sABORTING_MALLOC=0', '-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=128MB'] self.do_runf(test_file('dlmalloc_test_large.c'), '0 0 0 1') @@ -6617,7 +6651,7 @@ def test_fakestat(self): @also_with_standalone_wasm() def test_mmap_anon(self): # ASan needs more memory, but that is set up separately - if '-fsanitize=address' not in self.emcc_args: + if '-fsanitize=address' not in self.emcc_args and not self.has_changed_setting('INITIAL_MEMORY'): self.set_setting('INITIAL_MEMORY', '128mb') self.do_core_test('test_mmap_anon.c') @@ -8398,12 +8432,14 @@ def test_asyncify_during_exit(self): @no_lsan('undefined symbol __global_base') @no_wasm2js('dynamic linking support in wasm2js') @with_asyncify_and_jspi + @needs_dylink def test_asyncify_main_module(self): self.set_setting('MAIN_MODULE', 2) self.do_core_test('test_hello_world.c') # Test that pthread_join works correctly with asyncify. @requires_node_canary + @node_pthreads def test_pthread_join_and_asyncify(self): # TODO Test with ASYNCIFY=1 https://github.com/emscripten-core/emscripten/issues/17552 self.require_jspi() @@ -8418,6 +8454,7 @@ def test_pthread_join_and_asyncify(self): 'conditional': (True,), 'unconditional': (False,), }) + @no_4gb('uses imported memory') def test_emscripten_lazy_load_code(self, conditional): if self.get_setting('STACK_OVERFLOW_CHECK'): self.skipTest('https://github.com/emscripten-core/emscripten/issues/16828') @@ -8916,6 +8953,8 @@ def test_ubsan_full_left_shift(self, args): }) @no_wasm2js('TODO: sanitizers in wasm2js') def test_ubsan_full_null_ref(self, args): + if '-sMAIN_MODULE=2' in args: + self.check_dylink() if is_sanitizing(self.emcc_args): self.skipTest('test is specific to null sanitizer') self.emcc_args += args diff --git a/third_party/closure-compiler/node-externs/fs.js b/third_party/closure-compiler/node-externs/fs.js index b9c417ef6b53d..4e7e95831fc7b 100644 --- a/third_party/closure-compiler/node-externs/fs.js +++ b/third_party/closure-compiler/node-externs/fs.js @@ -373,12 +373,10 @@ fs.write = function(fd, buffer, offset, length, position, callback) {}; /** * @param {*} fd * @param {*} buffer - * @param {number=} offset - * @param {number=} length - * @param {number=} position + * @param {Object.=} options * @return {number} */ -fs.writeSync = function(fd, buffer, offset, length, position) {}; +fs.writeSync = function(fd, buffer, options) {}; /** * @param {*} fd @@ -393,13 +391,11 @@ fs.read = function(fd, buffer, offset, length, position, callback) {}; /** * @param {*} fd * @param {*} buffer - * @param {number} offset - * @param {number} length - * @param {number} position + * @param {Object.=} options * @return {number} * @nosideeffects */ -fs.readSync = function(fd, buffer, offset, length, position) {}; +fs.readSync = function(fd, buffer, options) {}; /** * @param {string} filename