diff --git a/src/library.js b/src/library.js index fbcd89e60e99d..cda307c0775b5 100644 --- a/src/library.js +++ b/src/library.js @@ -91,6 +91,7 @@ LibraryManager.library = { utime__proxy: 'sync', utime__sig: 'iii', utime: function(path, times) { + {{{ from64('times') }}}; // int utime(const char *path, const struct utimbuf *times); // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/utime.h.html var time; @@ -458,6 +459,7 @@ LibraryManager.library = { time__sig: 'ii', time: function(ptr) { + {{{ from64('ptr') }}}; var ret = (Date.now()/1000)|0; if (ptr) { {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; @@ -3102,6 +3104,7 @@ LibraryManager.library = { $readAsmConstArgsArray: '=[]', $readAsmConstArgs__deps: ['$readAsmConstArgsArray'], $readAsmConstArgs: function(sigPtr, buf) { + {{{ from64(['sigPtr', 'buf']) }}}; #if ASSERTIONS // Nobody should have mutated _readAsmConstArgsArray underneath us to be something else than an array. assert(Array.isArray(readAsmConstArgsArray)); @@ -3646,20 +3649,23 @@ LibraryManager.library = { // mode are created here and imported by the module. // Mark with `__import` so these are usable from native code. This is needed // because, by default, only functions can be be imported. - __stack_pointer: "new WebAssembly.Global({'value': 'i32', 'mutable': true}, {{{ STACK_BASE }}})", + __stack_pointer: "new WebAssembly.Global({'value': '{{{ POINTER_TYPE }}}', 'mutable': true}, {{{ to64(STACK_BASE) }}})", __stack_pointer__import: true, // tell the memory segments where to place themselves - __memory_base: '{{{ GLOBAL_BASE }}}', + __memory_base: "new WebAssembly.Global({'value': '{{{ POINTER_TYPE }}}', 'mutable': false}, {{{ to64(GLOBAL_BASE) }}})", __memory_base__import: true, // the wasm backend reserves slot 0 for the NULL function pointer - __table_base: 1, + __table_base: "new WebAssembly.Global({'value': '{{{ POINTER_TYPE }}}', 'mutable': false}, {{{ to64(1) }}})", __table_base__import: true, +#if MEMORY64 + __table_base32: 1, +#endif // To support such allocations during startup, track them on __heap_base and // then when the main module is loaded it reads that value and uses it to // initialize sbrk (the main module is relocatable itself, and so it does not // have __heap_base hardcoded into it - it receives it from JS as an extern // global, basically). - __heap_base: '{{{ HEAP_BASE }}}', + __heap_base: '{{{ to64(HEAP_BASE) }}}', __heap_base__import: true, #endif }; diff --git a/src/library_formatString.js b/src/library_formatString.js index 77bd3c40ce0f3..c9415c15dde7f 100644 --- a/src/library_formatString.js +++ b/src/library_formatString.js @@ -52,6 +52,7 @@ mergeInto(LibraryManager.library, { #endif ], $formatString: function(format, varargs) { + {{{ from64(['format', 'varargs']) }}}; #if ASSERTIONS assert((varargs & 3) === 0); #endif @@ -470,6 +471,7 @@ mergeInto(LibraryManager.library, { // printf/puts/strlen implementations for when musl is not pulled in - very // partial. useful for tests, and when bootstrapping structInfo strlen: function(ptr) { + {{{ from64('ptr') }}}; var end = ptr; while (HEAPU8[end]) ++end; return end - ptr; diff --git a/src/parseTools.js b/src/parseTools.js index 8479895e5d389..35e8ad0f3fc09 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -197,10 +197,20 @@ function isStructPointerType(type) { return !Compiletime.isNumberType(type) && type[0] == '%'; } +const POINTER_SIZE = MEMORY64 ? 8 : 4; +const POINTER_BITS = POINTER_SIZE * 8; +const POINTER_TYPE = 'i' + POINTER_BITS; + +const SIZE_TYPE = POINTER_TYPE; + function isPointerType(type) { return type[type.length - 1] == '*'; } +function sizeT(x) { + return MEMORY64 ? `BigInt(${x})` : x; +} + function isArrayType(type) { return /^\[\d+\ x\ (.*)\]/.test(type); } @@ -235,7 +245,7 @@ function isIntImplemented(type) { // Note: works for iX types and structure types, not pointers (even though they are implemented as ints) function getBits(type, allowPointers) { - if (allowPointers && isPointerType(type)) return 32; + if (allowPointers && isPointerType(type)) return POINTER_SIZE; if (!type) return 0; if (type[0] == 'i') { const left = type.substr(1); @@ -480,7 +490,7 @@ function checkSafeHeap() { } function getHeapOffset(offset, type) { - if (type == 'i64') { + if (!WASM_BIGINT && Runtime.getNativeFieldSize(type) > 4 && type == 'i64') { // we emulate 64-bit integer values as 32 in asmjs-unknown-emscripten, but not double type = 'i32'; } @@ -623,7 +633,16 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa return asmCoercion('SAFE_HEAP_LOAD' + ((type in Compiletime.FLOAT_TYPES) ? '_D' : '') + '(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + (!!unsigned + 0) + ')', type, unsigned ? 'u' : undefined); } } - return getHeapForType(type, unsigned) + '[' + getHeapOffset(offset, type) + ']'; + + const slab = getHeapForType(type, unsigned); + let ret = slab + '[' + getHeapOffset(offset, type) + ']'; + if (slab.substr(slab.length - 2) == '64') { + ret = `Number(${ret})`; + } + if (forceAsm) { + ret = asmCoercion(ret, type); + } + return ret; } /** @@ -665,7 +684,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, return '(' + makeSetTempDouble(0, 'double', value) + ',' + makeSetValue(ptr, pos, makeGetTempDouble(0, 'i32'), 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' + makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), makeGetTempDouble(1, 'i32'), 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')'; - } else if (type == 'i64') { + } else if (!WASM_BIGINT && type == 'i64') { return '(tempI64 = [' + splitI64(value) + '],' + makeSetValue(ptr, pos, 'tempI64[0]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ',' + makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempI64[1]', 'i32', noNeedFirst, ignore, align, noSafe, ',') + ')'; @@ -705,7 +724,12 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, return 'SAFE_HEAP_STORE' + ((type in Compiletime.FLOAT_TYPES) ? '_D' : '') + '(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ')'; } } - return getHeapForType(type) + '[' + getHeapOffset(offset, type) + '] = ' + value; + + const slab = getHeapForType(type); + if (slab == 'HEAPU64' || slab == 'HEAP64') { + value = `BigInt(${value})`; + } + return slab + '[' + getHeapOffset(offset, type) + '] = ' + value; } const UNROLL_LOOP_MAX = 8; @@ -862,13 +886,15 @@ function getFastValue(a, op, b, type) { function calcFastOffset(ptr, pos, noNeedFirst) { assert(!noNeedFirst); + if (typeof ptr == 'bigint') ptr = Number(ptr); + if (typeof pos == 'bigint') pos = Number(pos); return getFastValue(ptr, '+', pos, 'i32'); } function getHeapForType(type, unsigned) { assert(type); if (isPointerType(type)) { - type = 'i32'; // Hardcoded 32-bit + type = POINTER_TYPE; } switch (type) { case 'i1': @@ -876,9 +902,13 @@ function getHeapForType(type, unsigned) { return unsigned ? 'HEAPU8' : 'HEAP8'; case 'i16': return unsigned ? 'HEAPU16' : 'HEAP16'; + case 'i64': + if (WASM_BIGINT) { + return unsigned ? 'HEAPU64' : 'HEAP64'; + } + // fall through case '<4 x i32>': case 'i32': - case 'i64': return unsigned ? 'HEAPU32' : 'HEAP32'; case 'double': return 'HEAPF64'; @@ -908,10 +938,7 @@ function makeThrow(what) { } function makeSignOp(value, type, op, force, ignore) { - if (type == 'i64') { - return value; // these are always assumed to be two 32-bit unsigneds. - } - if (isPointerType(type)) type = 'i32'; // Pointers are treated as 32-bit ints + if (isPointerType(type)) type = POINTER_TYPE; if (!value) return value; let bits; let full; @@ -1292,6 +1319,26 @@ function sendI64Argument(low, high) { return low + ', ' + high; } +// Any function called from wasm64 may have bigint args, this function takes +// a list of variable names to convert to number. +function from64(x) { + if (!MEMORY64) { + return ''; + } + if (Array.isArray(x)) { + let ret = ''; + for (e of x) ret += from64(e); + return ret; + } else { + return `${x} = Number(${x});`; + } +} + +function to64(x) { + if (!MEMORY64) return x; + return `BigInt(${x})`; +} + // Add assertions to catch common errors when using the Promise object we // create on Module.ready() and return from MODULARIZE Module() invocations. function addReadyPromiseAssertions(promise) { diff --git a/src/postamble.js b/src/postamble.js index 15f87d8883fca..0fdd070f49db6 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -215,7 +215,7 @@ function stackCheckInit() { // here. // TODO(sbc): Move writeStackCookie to native to to avoid this. #if RELOCATABLE - _emscripten_stack_set_limits({{{ STACK_BASE }}}, {{{ STACK_MAX }}}); + _emscripten_stack_set_limits({{{ to64(STACK_BASE) }}}, {{{ to64(STACK_MAX) }}}); #else _emscripten_stack_init(); #endif diff --git a/src/runtime_strings.js b/src/runtime_strings.js index 63d599e1bd70c..396d731413186 100644 --- a/src/runtime_strings.js +++ b/src/runtime_strings.js @@ -128,6 +128,7 @@ function UTF8ArrayToString(heap, idx, maxBytesToRead) { * @return {string} */ function UTF8ToString(ptr, maxBytesToRead) { + {{{ from64('ptr') }}}; #if CAN_ADDRESS_2GB ptr >>>= 0; #endif diff --git a/src/settings.js b/src/settings.js index af7aee12731a2..172cb6f6bba3d 100644 --- a/src/settings.js +++ b/src/settings.js @@ -237,6 +237,7 @@ var MEMORY_GROWTH_LINEAR_STEP = -1; // the full end-to-end wasm64 mode, and 2 is wasm64 for clang/lld but lowered to // wasm32 in Binaryen (such that it can run on wasm32 engines, while internally // using i64 pointers). +// Assumes WASM_BIGINT. // [compile+link] var MEMORY64 = 0;