diff --git a/emscripten.py b/emscripten.py index 4ab212550d9d8..f407789936834 100755 --- a/emscripten.py +++ b/emscripten.py @@ -522,6 +522,8 @@ def update_settings_glue(settings, metadata): settings['JSCALL_START_INDEX'] = start_index settings['JSCALL_SIG_ORDER'] = sig2order + shared.Building.global_ctors = metadata['initializers'] + def compile_settings(compiler_engine, settings, libraries, temp_files): # Save settings to a file to work around v8 issue 1579 @@ -1157,7 +1159,77 @@ def table_size(table): return table_contents.count(',') + 1 table_total_size = sum(map(table_size, list(function_table_data.values()))) - asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size + if not settings['CUSTOM_CORE_JS']: + asm_setup += "\nModule['wasmTableSize'] = %d;\n" % table_total_size + else: + asm_setup += ''' +var setupInfo = setup({ + memorySize: %d / WASM_PAGE_SIZE, + tableSize: %d, + staticStart: %d, + staticSize: STATIC_BUMP, + stackSize: %d +}); +DYNAMICTOP_PTR = setupInfo.sbrkPtr; +DYNAMICTOP = setupInfo.sbrkStart; +STACKTOP = setupInfo.stackStart; +STACK_MAX = STACKTOP + %d; +buffer = setupInfo.memory.buffer; +HEAP8 = new Int8Array(buffer); +HEAP16 = new Int16Array(buffer); +HEAP32 = new Int32Array(buffer); +HEAPU8 = new Uint8Array(buffer); +HEAPU16 = new Uint16Array(buffer); +HEAPU32 = new Uint32Array(buffer); +HEAPF32 = new Float32Array(buffer); +HEAPF64 = new Float64Array(buffer); + +// XXX fix these +var ABORT, EXITSTATUS; +var DYNAMICTOP; +// XXX fix all the above + +// XXX - should we disallow --post-js'es? checked late, +// so would error on things that emcc adds a post for. + +Module['asm'] = function(global, env, buffer) { + env['memory'] = setupInfo.memory; + env['table'] = setupInfo.table; + env['memoryBase'] = 0; + env['tableBase'] = 0; + var info = { + 'asm2wasm': { + "f64-rem": function(x, y) { + return x %% y; + }, + "debugger": function() { + debugger; + } + }, + 'env': env, + 'global': { // XXX + 'Infinity': Infinity, + 'NaN': NaN + }, + 'global.Math': Math + }; + start(info, function(exports) { + [%s].forEach(function(ctor) { + exports[ctor](); + }); + __ATINIT__.concat(__ATMAIN__).forEach(function(ctor) { + if (typeof ctor === 'function') { // XXX FIXME + ctor(); + } + }); + }); +}; +''' % (settings['TOTAL_MEMORY'], + table_total_size, + settings['GLOBAL_BASE'], + settings['TOTAL_STACK'], + settings['TOTAL_STACK'], + ', '.join(['"' + name + '"' for name in metadata['initializers']])) if not settings['EMULATED_FUNCTION_POINTERS']: asm_setup += "\nModule['wasmMaxTableSize'] = %d;\n" % table_total_size if settings['RELOCATABLE']: @@ -1251,7 +1323,7 @@ def create_basic_funcs(function_table_sigs, settings): def create_basic_vars(exported_implemented_functions, forwarded_json, metadata, settings): - basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr', 'ABORT'] + basic_vars = ['DYNAMICTOP_PTR', 'tempDoublePtr'] if not (settings['WASM'] and settings['SIDE_MODULE']): basic_vars += ['STACKTOP', 'STACK_MAX'] if settings['RELOCATABLE']: @@ -1327,6 +1399,8 @@ def create_the_global(metadata, settings): def create_receiving(function_table_data, function_tables_defs, exported_implemented_functions, settings): + if settings['CUSTOM_CORE_JS']: + return '' receiving = '' if not settings['ASSERTIONS']: runtime_assertions = '' @@ -1982,7 +2056,7 @@ def create_sending_wasm(invoke_funcs, jscall_sigs, forwarded_json, metadata, if settings['SAFE_HEAP']: basic_funcs += ['segfault', 'alignfault'] - basic_vars = ['STACKTOP', 'STACK_MAX', 'DYNAMICTOP_PTR', 'ABORT'] + basic_vars = ['STACKTOP', 'STACK_MAX', 'DYNAMICTOP_PTR'] if not settings['RELOCATABLE']: global_vars = metadata['externs'] @@ -2002,6 +2076,8 @@ def math_fix(g): def create_receiving_wasm(exported_implemented_functions, settings): + if settings['CUSTOM_CORE_JS']: + return '' receiving = '' if settings['ASSERTIONS']: # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are diff --git a/src/corejs/minimal-multi.js b/src/corejs/minimal-multi.js new file mode 100644 index 0000000000000..870ea7b19b0fd --- /dev/null +++ b/src/corejs/minimal-multi.js @@ -0,0 +1,105 @@ + +// Minimal core with +// * Web, Node.js, SpiderMonkey support for loading wasm +// * console.log for all output +// * Support for main()'s argc/argv + +// Environment setup + +var out, err; +out = err = function(x) { + console.log(x); +}; + +// Set up memory and table + +// Some extra static memory for main() argv strings +var extraStatic, extraStaticSize = 1024; + +function setup(info) { + var memory = new WebAssembly.Memory({ initial: info.memorySize, maximum: info.memorySize }); + var table = new WebAssembly.Table({ initial: info.tableSize, maximum: info.tableSize, element: 'anyfunc' }); + var staticEnd = info.staticStart + info.staticSize; + extraStatic = staticEnd; + var stackStart = extraStatic + extraStaticSize; + var stackMax = stackStart + info.stackSize; + var sbrkStart = stackMax; + var sbrkPtr = 16; + (new Int32Array(memory.buffer))[sbrkPtr >> 2] = sbrkStart; + return { + memory: memory, + table: table, + stackStart: stackStart, + sbrkStart: sbrkStart, + sbrkPtr: sbrkPtr, + }; +} + +// Compile and run + +function start(imports, onload) { + // todo main's argc/argv + function postInstantiate(instance, args) { + var exports = instance['exports']; + onload(exports); + // allocate main() argc/argv + var extraStaticMax = extraStatic + extraStaticSize; + function extraAlloc(size) { + var ret = extraStatic; + extraStatic += size; + assert(extraStatic <= extraStaticMax); + return ret; + } + function writeCString(ptr, string) { + for (var j = 0; j < string.length; j++) { + HEAPU8[ptr + j] = string.charCodeAt(j); + } + HEAPU8[ptr + string.length] = 0; + } + function allocCString(string) { + var ptr = extraAlloc(string.length + 1); + writeCString(ptr, string); + return ptr; + } + var argc = args.length + 1; + var argv = extraAlloc(argc * 4); + HEAP32[argv >> 2] = allocCString('program'); + for (var i = 0; i < args.length; i++) { + HEAP32[(argv >> 2) + 1 + i] = allocCString(args[i]); + } + exports['_main'](argc, argv); + } + var filename = '{{{ WASM_BINARY_FILE }}}'; + if (typeof fetch === 'function') { + // Web + fetch(filename, { credentials: 'same-origin' }) + .then(function(response) { + return response.arrayBuffer(); + }) + .then(function(arrayBuffer) { + return new Uint8Array(arrayBuffer); + }) + .then(function(binary) { + return WebAssembly.instantiate(binary, imports); + }) + .then(function(pair) { + postInstantiate(pair['instance']); + }); + } else { + var data, args; + if (typeof require === 'function') { + // node.js + data = require('fs')['readFileSync'](filename); + args = process['argv'].slice(2); + } else if (typeof read === 'function') { + // SpiderMonkey shell + data = read(filename, 'binary'); + args = scriptArgs; + } else { + throw Error('where am i'); + } + var instance = new WebAssembly.Instance(new WebAssembly.Module(data), imports); + postInstantiate(instance, args); + } +} + diff --git a/src/corejs/minimal-web.js b/src/corejs/minimal-web.js new file mode 100644 index 0000000000000..5ff43f04f62f7 --- /dev/null +++ b/src/corejs/minimal-web.js @@ -0,0 +1,51 @@ + +// Minimal web-only core + +// Environment setup + +var out, err; +out = err = function(x) { + console.log(x); +}; + +// Set up memory and table + +function setup(info) { + var memory = new WebAssembly.Memory({ initial: info.memorySize, maximum: info.memorySize }); + var table = new WebAssembly.Table({ initial: info.tableSize, maximum: info.tableSize, element: 'anyfunc' }); + var staticEnd = info.staticStart + info.staticSize; + var stackStart = staticEnd; + var stackMax = stackStart + info.stackSize; + var sbrkStart = stackMax; + var sbrkPtr = 16; + (new Int32Array(memory.buffer))[sbrkPtr >> 2] = sbrkStart; + return { + memory: memory, + table: table, + stackStart: stackStart, + sbrkStart: sbrkStart, + sbrkPtr: sbrkPtr, + }; +} + +// Compile and run + +function start(imports, onload) { + fetch('{{{ WASM_BINARY_FILE }}}', { credentials: 'same-origin' }) + .then(function(response) { + return response.arrayBuffer(); + }) + .then(function(arrayBuffer) { + return new Uint8Array(arrayBuffer); + }) + .then(function(binary) { + return WebAssembly.instantiate(binary, imports); + }) + .then(function(pair) { + var instance = pair['instance']; + var exports = instance['exports']; + onload(exports); + exports['_main'](); + }); +} + diff --git a/src/jsifier.js b/src/jsifier.js index 8c69971df13c2..ae1917a9492e0 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -70,6 +70,7 @@ function JSify(data, functionsOnly) { var shellParts = read(shellFile).split('{{BODY}}'); print(processMacros(preprocess(shellParts[0], shellFile))); + if (CUSTOM_CORE_JS) print(processMacros(preprocess(read(CUSTOM_CORE_JS)))); var pre; if (BUILD_AS_SHARED_LIB || SIDE_MODULE) { pre = processMacros(preprocess(read('preamble_sharedlib.js'), 'preamble_sharedlib.js')); @@ -526,7 +527,7 @@ function JSify(data, functionsOnly) { legalizedI64s = legalizedI64sDefault; - if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) { + if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE && !CUSTOM_CORE_JS) { if (USE_PTHREADS) { print('\n // proxiedFunctionTable specifies the list of functions that can be called either synchronously or asynchronously from other threads in postMessage()d or internally queued events. This way a pthread in a Worker can synchronously access e.g. the DOM on the main thread.') print('\nvar proxiedFunctionTable = [' + proxiedFunctionTable.join() + '];\n'); @@ -589,12 +590,12 @@ function JSify(data, functionsOnly) { print(read('deterministic.js')); } - var postFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'postamble_sharedlib.js' : 'postamble.js'; - var postParts = processMacros(preprocess(read(postFile), postFile)).split('{{GLOBAL_VARS}}'); - print(postParts[0]); - - print(postParts[1]); - + if (!CUSTOM_CORE_JS) { + var postFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'postamble_sharedlib.js' : 'postamble.js'; + var postParts = processMacros(preprocess(read(postFile), postFile)).split('{{GLOBAL_VARS}}'); + print(postParts[0]); + print(postParts[1]); + } var shellParts = read(shellFile).split('{{BODY}}'); print(processMacros(preprocess(shellParts[1], shellFile))); // Print out some useful metadata diff --git a/src/modules.js b/src/modules.js index 407172bc000f1..b6430443bae00 100644 --- a/src/modules.js +++ b/src/modules.js @@ -408,6 +408,7 @@ function exportRuntime() { 'establishStackSpace', 'print', 'printErr', + 'abort', ]; if (SUPPORT_BASE64_EMBEDDING) { runtimeElements.push('intArrayFromBase64'); diff --git a/src/postamble.js b/src/postamble.js index f81ef5fc67098..016adfbdfdcb6 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -403,42 +403,6 @@ function exit(status, implicit) { Module['quit'](status, new ExitStatus(status)); } -var abortDecorators = []; - -function abort(what) { - if (Module['onAbort']) { - Module['onAbort'](what); - } - -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) console.error('Pthread aborting at ' + new Error().stack); -#endif - if (what !== undefined) { - out(what); - err(what); - what = JSON.stringify(what) - } else { - what = ''; - } - - ABORT = true; - EXITSTATUS = 1; - -#if ASSERTIONS == 0 - throw 'abort(' + what + '). Build with -s ASSERTIONS=1 for more info.'; -#else - var extra = ''; - var output = 'abort(' + what + ') at ' + stackTrace() + extra; - if (abortDecorators) { - abortDecorators.forEach(function(decorator) { - output = decorator(output, what); - }); - } - throw output; -#endif // ASSERTIONS -} -Module['abort'] = abort; - // {{PRE_RUN_ADDITIONS}} if (Module['preInit']) { diff --git a/src/preamble.js b/src/preamble.js index afb3658035774..5bf15b91e553d 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -88,13 +88,6 @@ function ftfault() { } #endif -//======================================== -// Runtime essentials -//======================================== - -var ABORT = 0; // whether we are quitting the application. no code should run after this. set in exit() and abort() -var EXITSTATUS = 0; - /** @type {function(*, string=)} */ function assert(condition, text) { if (!condition) { @@ -843,13 +836,6 @@ function stackTrace() { return demangleAll(js); } -// Memory management - -var PAGE_SIZE = 16384; -var WASM_PAGE_SIZE = 65536; -var ASMJS_PAGE_SIZE = 16777216; -var MIN_TOTAL_MEMORY = 16777216; - function alignUp(x, multiple) { if (x % multiple > 0) { x += multiple - (x % multiple); @@ -857,7 +843,7 @@ function alignUp(x, multiple) { return x; } -var HEAP, +var /** @type {ArrayBuffer} */ buffer, /** @type {Int8Array} */ @@ -896,24 +882,6 @@ var STATIC_BASE, STATICTOP, staticSealed; // static area var STACK_BASE, STACKTOP, STACK_MAX; // stack area var DYNAMIC_BASE, DYNAMICTOP_PTR; // dynamic area handled by sbrk -#if USE_PTHREADS -if (!ENVIRONMENT_IS_PTHREAD) { // Pthreads have already initialized these variables in src/pthread-main.js, where they were passed to the thread worker at startup time -#endif - STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0; - staticSealed = false; -#if USE_PTHREADS -} -#endif - -#if USE_PTHREADS -if (ENVIRONMENT_IS_PTHREAD) { - staticSealed = true; // The static memory area has been initialized already in the main thread, pthreads skip this. -#if SEPARATE_ASM != 0 - importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js -#endif -} -#endif - #if STACK_OVERFLOW_CHECK // Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. function writeStackCookie() { @@ -1055,6 +1023,7 @@ function enlargeMemory() { #endif // USE_PTHREADS } +#if WASM == 0 #if ALLOW_MEMORY_GROWTH var byteLength; try { @@ -1063,11 +1032,8 @@ try { } catch(e) { // can fail on older node/v8 byteLength = function(buffer) { return buffer.byteLength; }; } -#endif - -var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}}; -var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}}; -if (TOTAL_MEMORY < TOTAL_STACK) err('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); +#endif // ALLOW_MEMORY_GROWTH +#endif // WASM == 0 // Initialize the runtime's memory #if ASSERTIONS @@ -1094,9 +1060,196 @@ if (typeof SharedArrayBuffer === 'undefined' || typeof Atomics === 'undefined') xhr.send(); setTimeout(function() { window.close() }, 2000); } +#endif // USE_PTHREADS == 1 +#endif // IN_TEST_HARNESS + +function getTotalMemory() { + return TOTAL_MEMORY; +} + +// Deprecated: This function should not be called because it is unsafe and does not provide +// a maximum length limit of how many bytes it is allowed to write. Prefer calling the +// function stringToUTF8Array() instead, which takes in a maximum length that can be used +// to be secure from out of bounds writes. +/** @deprecated */ +function writeStringToMemory(string, buffer, dontAddNull) { + warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8Array always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. +} + +function writeArrayToMemory(array, buffer) { +#if ASSERTIONS + assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') +#endif + HEAP8.set(array, buffer); +} + +function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { +#if ASSERTIONS + assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); +#endif + {{{ makeSetValue('buffer++', 0, 'str.charCodeAt(i)', 'i8') }}}; + } + // Null-terminate the pointer to the HEAP. + if (!dontAddNull) {{{ makeSetValue('buffer', 0, 0, 'i8') }}}; +} + +{{{ unSign }}} +{{{ reSign }}} + +#if LEGACY_VM_SUPPORT +// check for imul support, and also for correctness ( https://bugs.webkit.org/show_bug.cgi?id=126345 ) +if (!Math['imul'] || Math['imul'](0xffffffff, 5) !== -5) Math['imul'] = function imul(a, b) { + var ah = a >>> 16; + var al = a & 0xffff; + var bh = b >>> 16; + var bl = b & 0xffff; + return (al*bl + ((ah*bl + al*bh) << 16))|0; +}; +Math.imul = Math['imul']; + +#if PRECISE_F32 +#if PRECISE_F32 == 1 +if (!Math['fround']) { + var froundBuffer = new Float32Array(1); + Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] }; +} +#else // 2 +if (!Math['fround']) Math['fround'] = function(x) { return x }; +#endif +Math.fround = Math['fround']; +#else +#if SIMD +if (!Math['fround']) Math['fround'] = function(x) { return x }; +#endif +#endif + +if (!Math['clz32']) Math['clz32'] = function(x) { + x = x >>> 0; + for (var i = 0; i < 32; i++) { + if (x & (1 << (31 - i))) return i; + } + return 32; +}; +Math.clz32 = Math['clz32'] + +if (!Math['trunc']) Math['trunc'] = function(x) { + return x < 0 ? Math.ceil(x) : Math.floor(x); +}; +Math.trunc = Math['trunc']; +#else // LEGACY_VM_SUPPORT +#if ASSERTIONS +assert(Math['imul'] && Math['fround'] && Math['clz32'] && Math['trunc'], 'this is a legacy browser, build with LEGACY_VM_SUPPORT'); +#endif +#endif // LEGACY_VM_SUPPORT + +var Math_abs = Math.abs; +var Math_cos = Math.cos; +var Math_sin = Math.sin; +var Math_tan = Math.tan; +var Math_acos = Math.acos; +var Math_asin = Math.asin; +var Math_atan = Math.atan; +var Math_atan2 = Math.atan2; +var Math_exp = Math.exp; +var Math_log = Math.log; +var Math_sqrt = Math.sqrt; +var Math_ceil = Math.ceil; +var Math_floor = Math.floor; +var Math_pow = Math.pow; +var Math_imul = Math.imul; +var Math_fround = Math.fround; +var Math_round = Math.round; +var Math_min = Math.min; +var Math_max = Math.max; +var Math_clz32 = Math.clz32; +var Math_trunc = Math.trunc; + +var abortDecorators = []; + +function abort(what) { + if (Module['onAbort']) { + Module['onAbort'](what); + } + +#if USE_PTHREADS + if (ENVIRONMENT_IS_PTHREAD) console.error('Pthread aborting at ' + new Error().stack); +#endif + if (what !== undefined) { + out(what); + err(what); + what = JSON.stringify(what) + } else { + what = ''; + } + + ABORT = true; + EXITSTATUS = 1; + +#if ASSERTIONS == 0 + throw 'abort(' + what + '). Build with -s ASSERTIONS=1 for more info.'; +#else + var extra = ''; + var output = 'abort(' + what + ') at ' + stackTrace() + extra; + if (abortDecorators) { + abortDecorators.forEach(function(decorator) { + output = decorator(output, what); + }); + } + throw output; +#endif // ASSERTIONS +} + +var WASM_PAGE_SIZE = 65536; + +#include "URIUtils.js" + +// === Body === + +#if !CUSTOM_CORE_JS + +var ABORT = 0; // whether we are quitting the application. no code should run after this. set in exit() and abort() +var EXITSTATUS = 0; + +// Memory management + +var PAGE_SIZE = 16384; +var ASMJS_PAGE_SIZE = 16777216; +var MIN_TOTAL_MEMORY = 16777216; + +#if USE_PTHREADS +if (!ENVIRONMENT_IS_PTHREAD) { // Pthreads have already initialized these variables in src/pthread-main.js, where they were passed to the thread worker at startup time +#endif + STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0; + staticSealed = false; +#if USE_PTHREADS +} +#endif + +#if USE_PTHREADS +if (ENVIRONMENT_IS_PTHREAD) { + staticSealed = true; // The static memory area has been initialized already in the main thread, pthreads skip this. +#if SEPARATE_ASM != 0 + importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js #endif +} #endif +var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}}; +var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}}; +if (TOTAL_MEMORY < TOTAL_STACK) err('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); + #if USE_PTHREADS #if !WASM if (typeof SharedArrayBuffer !== 'undefined') { @@ -1485,10 +1638,6 @@ function setF64(ptr, value) { #endif // USE_PTHREADS -function getTotalMemory() { - return TOTAL_MEMORY; -} - // Endianness check (note: assumes compiler arch was little-endian) #if SAFE_SPLIT_MEMORY == 0 #if STACK_OVERFLOW_CHECK @@ -1629,115 +1778,6 @@ function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb); } -// Deprecated: This function should not be called because it is unsafe and does not provide -// a maximum length limit of how many bytes it is allowed to write. Prefer calling the -// function stringToUTF8Array() instead, which takes in a maximum length that can be used -// to be secure from out of bounds writes. -/** @deprecated */ -function writeStringToMemory(string, buffer, dontAddNull) { - warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); - - var /** @type {number} */ lastChar, /** @type {number} */ end; - if (dontAddNull) { - // stringToUTF8Array always appends null. If we don't want to do that, remember the - // character that existed at the location where the null will be placed, and restore - // that after the write (below). - end = buffer + lengthBytesUTF8(string); - lastChar = HEAP8[end]; - } - stringToUTF8(string, buffer, Infinity); - if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. -} - -function writeArrayToMemory(array, buffer) { -#if ASSERTIONS - assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') -#endif - HEAP8.set(array, buffer); -} - -function writeAsciiToMemory(str, buffer, dontAddNull) { - for (var i = 0; i < str.length; ++i) { -#if ASSERTIONS - assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); -#endif - {{{ makeSetValue('buffer++', 0, 'str.charCodeAt(i)', 'i8') }}}; - } - // Null-terminate the pointer to the HEAP. - if (!dontAddNull) {{{ makeSetValue('buffer', 0, 0, 'i8') }}}; -} - -{{{ unSign }}} -{{{ reSign }}} - -#if LEGACY_VM_SUPPORT -// check for imul support, and also for correctness ( https://bugs.webkit.org/show_bug.cgi?id=126345 ) -if (!Math['imul'] || Math['imul'](0xffffffff, 5) !== -5) Math['imul'] = function imul(a, b) { - var ah = a >>> 16; - var al = a & 0xffff; - var bh = b >>> 16; - var bl = b & 0xffff; - return (al*bl + ((ah*bl + al*bh) << 16))|0; -}; -Math.imul = Math['imul']; - -#if PRECISE_F32 -#if PRECISE_F32 == 1 -if (!Math['fround']) { - var froundBuffer = new Float32Array(1); - Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] }; -} -#else // 2 -if (!Math['fround']) Math['fround'] = function(x) { return x }; -#endif -Math.fround = Math['fround']; -#else -#if SIMD -if (!Math['fround']) Math['fround'] = function(x) { return x }; -#endif -#endif - -if (!Math['clz32']) Math['clz32'] = function(x) { - x = x >>> 0; - for (var i = 0; i < 32; i++) { - if (x & (1 << (31 - i))) return i; - } - return 32; -}; -Math.clz32 = Math['clz32'] - -if (!Math['trunc']) Math['trunc'] = function(x) { - return x < 0 ? Math.ceil(x) : Math.floor(x); -}; -Math.trunc = Math['trunc']; -#else // LEGACY_VM_SUPPORT -#if ASSERTIONS -assert(Math['imul'] && Math['fround'] && Math['clz32'] && Math['trunc'], 'this is a legacy browser, build with LEGACY_VM_SUPPORT'); -#endif -#endif // LEGACY_VM_SUPPORT - -var Math_abs = Math.abs; -var Math_cos = Math.cos; -var Math_sin = Math.sin; -var Math_tan = Math.tan; -var Math_acos = Math.acos; -var Math_asin = Math.asin; -var Math_atan = Math.atan; -var Math_atan2 = Math.atan2; -var Math_exp = Math.exp; -var Math_log = Math.log; -var Math_sqrt = Math.sqrt; -var Math_ceil = Math.ceil; -var Math_floor = Math.floor; -var Math_pow = Math.pow; -var Math_imul = Math.imul; -var Math_fround = Math.fround; -var Math_round = Math.round; -var Math_min = Math.min; -var Math_max = Math.max; -var Math_clz32 = Math.clz32; -var Math_trunc = Math.trunc; - // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and // decrement it. Incrementing must happen in a place like @@ -1985,8 +2025,6 @@ Module['FS_createPreloadedFile'] = FS.createPreloadedFile; var cyberDWARFFile = '{{{ BUNDLED_CD_DEBUG_FILE }}}'; #endif -#include "URIUtils.js" - #if WASM function integrateWasmJS() { // wasm.js has several methods for creating the compiled code module here: @@ -2485,6 +2523,7 @@ function integrateWasmJS() { } integrateWasmJS(); -#endif +#endif // WASM + +#endif // CUSTOM_CORE_JS -// === Body === diff --git a/src/settings.js b/src/settings.js index 84dfe03432c6b..469af250805a1 100644 --- a/src/settings.js +++ b/src/settings.js @@ -483,6 +483,25 @@ var INCLUDE_FULL_LIBRARY = 0; // Include all JS library functions instead of the // need to use those in the main file too to pull in malloc // for use by the module. +var CUSTOM_CORE_JS = ''; // Setting this will include the contents of that .js file instead + // of shell.js, and will prevent emitting the setup/startup code + // in preamble.js and postamble.js. The custom core JS should + // instead set up the environment itself, and implement the + // functionality you want, which should include + // setup() - called early, to set up memory etc. + // startup(env) - called after the JS library is set up, with + // the wasm imports as a parameter. This + // function should handle creating and running + // the wasm. XXX or define Module['asm'] + // ISSUES: + // * test_llvm_intrinsics etc. - calling from a JS library into + // asm library function does not work, as the asm library export + // / receiving code is not emitted. + // * EMSCRIPTEN_KEEPALIVE auto exported is not emitted, the user + // must receive it from the wasm module. + // TODO: add a JS ctor that the start() receives which receives + // the necessary exports for internal use. simplifies metadce also. + var SHELL_FILE = 0; // set this to a string to override the shell file used var RELOCATABLE = 0; // If set to 1, we emit relocatable code from the LLVM backend; both diff --git a/src/shell.js b/src/shell.js index 66bd75be99fad..a4e0427d27eb9 100644 --- a/src/shell.js +++ b/src/shell.js @@ -21,6 +21,7 @@ var Module = typeof {{{ EXPORT_NAME }}} !== 'undefined' ? {{{ EXPORT_NAME }}} : #endif // USE_CLOSURE_COMPILER #endif // SIDE_MODULE +#if !CUSTOM_CORE_JS // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) // {{PRE_JSES}} @@ -312,6 +313,27 @@ for (key in moduleOverrides) { // reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. moduleOverrides = undefined; +#else // !CUSTOM_CORE_JS +// XXX FIXME all these +var __ATINIT__ = []; +var __ATMAIN__ = []; +var __ATEXIT__ = []; +var ENVIRONMENT_IS_NODE = 0; +var ENVIRONMENT_IS_WORKER = 0; +var TOTAL_MEMORY = {{{ TOTAL_MEMORY }}}; +var __GLOBAL__I_000101 = 0; +var __GLOBAL__sub_I_iostream_cpp = 0; +var __ZSt18uncaught_exceptionv = 0; +var _free = 0; +var _malloc = 0; +var addRunDependency = 0; +var getUniqueRunDependency = 0; +var removeRunDependency = 0; +var stackRestore = 0; +var stackSave = 0; +// XXX FIXME all the above +#endif //!CUSTOM_CORE_JS + {{BODY}} // {{MODULE_ADDITIONS}} diff --git a/tests/runner.py b/tests/runner.py index 04cfc7088ee6a..8711e33ebd695 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -150,6 +150,7 @@ def get_browser(): 'binaryenz', 'asmi', 'asm2i', + 'cjs', ] test_index = 0 diff --git a/tests/test_core.py b/tests/test_core.py index ecc4002663e21..e1ff9d288eaf9 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7803,4 +7803,6 @@ def setUp(self): asmi = make_run('asmi', compiler=CLANG, emcc_args=['-s', 'EMTERPRETIFY=1', '-s', 'WASM=0']) asm2i = make_run('asm2i', compiler=CLANG, emcc_args=['-O2', '-s', 'EMTERPRETIFY=1', '-s', 'WASM=0']) +cjs = make_run('cjs', compiler=CLANG, emcc_args=['-g', '-s', 'CUSTOM_CORE_JS="%s"' % path_from_root('src', 'corejs', 'minimal-multi.js')]) + del T # T is just a shape for the specific subclasses, we don't test it itself diff --git a/tests/test_other.py b/tests/test_other.py index d9a012242d400..a6e0e76828bfd 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -8017,9 +8017,9 @@ def test(filename, expectations): print('test on hello world') test(path_from_root('tests', 'hello_world.cpp'), [ - ([], 24, ['abort', 'tempDoublePtr'], ['waka'], 46505, 25, 19), - (['-O1'], 19, ['abort', 'tempDoublePtr'], ['waka'], 12630, 16, 17), - (['-O2'], 19, ['abort', 'tempDoublePtr'], ['waka'], 12616, 16, 17), + ([], 23, ['abort', 'tempDoublePtr'], ['waka'], 46505, 24, 19), + (['-O1'], 18, ['abort', 'tempDoublePtr'], ['waka'], 12630, 16, 17), + (['-O2'], 18, ['abort', 'tempDoublePtr'], ['waka'], 12616, 16, 17), (['-O3'], 7, ['abort'], ['tempDoublePtr', 'waka'], 2818, 10, 2), # in -O3, -Os and -Oz we metadce (['-Os'], 7, ['abort'], ['tempDoublePtr', 'waka'], 2771, 10, 2), (['-Oz'], 7, ['abort'], ['tempDoublePtr', 'waka'], 2765, 10, 2), @@ -8028,7 +8028,7 @@ def test(filename, expectations): 0, [], ['tempDoublePtr', 'waka'], 8, 0, 0), # totally empty! # but we don't metadce with linkable code! other modules may want it (['-O3', '-s', 'MAIN_MODULE=1'], - 1534, ['invoke_i'], ['waka'], 496958, 163, 2560), + 1533, ['invoke_i'], ['waka'], 496958, 163, 2560), ]) print('test on a minimal pure computational thing') @@ -8041,9 +8041,9 @@ def test(filename, expectations): } ''') test('minimal.c', [ - ([], 24, ['abort', 'tempDoublePtr'], ['waka'], 22712, 25, 18), - (['-O1'], 12, ['abort', 'tempDoublePtr'], ['waka'], 10450, 9, 15), - (['-O2'], 12, ['abort', 'tempDoublePtr'], ['waka'], 10440, 9, 15), + ([], 23, ['abort', 'tempDoublePtr'], ['waka'], 22712, 24, 18), + (['-O1'], 11, ['abort', 'tempDoublePtr'], ['waka'], 10450, 9, 15), + (['-O2'], 11, ['abort', 'tempDoublePtr'], ['waka'], 10440, 9, 15), # in -O3, -Os and -Oz we metadce, and they shrink it down to the minimal output we want (['-O3'], 0, [], ['tempDoublePtr', 'waka'], 58, 0, 1), (['-Os'], 0, [], ['tempDoublePtr', 'waka'], 58, 0, 1), diff --git a/tools/shared.py b/tools/shared.py index b2e9280e9b15a..908fe1763eef8 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -2322,14 +2322,33 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info): txt = Building.js_optimizer_no_asmjs(js_file, ['emitDCEGraph', 'noEmitAst'], return_output=True) graph = json.loads(txt) # ensure that functions expected to be exported to the outside are roots + exports = set() for item in graph: if 'export' in item: export = item['export'] + exports.add(item['name']) # wasm backend's exports are prefixed differently inside the wasm if Settings.WASM_BACKEND: export = '_' + export if export in Building.user_requested_exports or Settings.EXPORT_ALL: item['root'] = True + if Settings.CUSTOM_CORE_JS: + # In custom core JS we must handle things more manually, as we can't + # assume the JS contains the standard JS to receive wasm exports + # (the custom code might write it differently). + # Specifically, we need to have rooted exports for all user-requested + # exports, plus global ctors, as all those are absolutely mandatory + # to be kept alive through dce. (If users want more, they can add + # to EXPORTED_FUNCTIONS.) + needed = Building.user_requested_exports + Building.global_ctors + for export in needed: + if export not in exports: + graph.append({ + 'export': export, + 'name': 'emcc$export$' + export, + 'root': True, + 'reaches': [] + }) if Settings.WASM_BACKEND: # wasm backend's imports are prefixed differently inside the wasm for item in graph: @@ -2361,6 +2380,9 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info): # the exports the user requested user_requested_exports = [] + # the global constructor functions (to be called before main) + global_ctors = [] + _is_ar_cache = {} @staticmethod def is_ar(filename):