diff --git a/emcc.py b/emcc.py index 15c56a522349..b1c4c379ee05 100755 --- a/emcc.py +++ b/emcc.py @@ -1305,7 +1305,10 @@ def check(input_file): shared.Settings.ASMJS_CODE_FILE = shared.JS.escape_for_js_string(os.path.basename(asm_target)) shared.Settings.ASM_JS = 2 # when targeting wasm, we use a wasm Memory, but that is not compatible with asm.js opts - shared.Settings.GLOBAL_BASE = 1024 # leave some room for mapping global vars + # for asm2wasm, a higher global base is useful for optimizing load/store offsets + # (and historically for mapping asm.js globals) + # TODO: for the wasm backend, we don't need this? + shared.Settings.GLOBAL_BASE = 1024 if shared.Settings.ELIMINATE_DUPLICATE_FUNCTIONS: logger.warning('for wasm there is no need to set ELIMINATE_DUPLICATE_FUNCTIONS, the binaryen optimizer does it automatically') shared.Settings.ELIMINATE_DUPLICATE_FUNCTIONS = 0 diff --git a/emscripten.py b/emscripten.py index 3b94b2e7a966..5e8d4be21fb1 100644 --- a/emscripten.py +++ b/emscripten.py @@ -527,6 +527,15 @@ def is_int(x): return False +def align_memory(addr): + return (addr + 15) & -16 + + +def align_static_bump(metadata): + metadata['staticBump'] = align_memory(metadata['staticBump']) + return metadata['staticBump'] + + def update_settings_glue(metadata): if shared.Settings.CYBERDWARF: shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append("cyberdwarf_Debugger") @@ -581,6 +590,14 @@ def read_proxied_function_signatures(asmConsts): if not shared.Settings.WASM_BACKEND: shared.Settings.PROXIED_FUNCTION_SIGNATURES = read_proxied_function_signatures(metadata['asmConsts']) + shared.Settings.STATIC_BUMP = align_static_bump(metadata) + + +def apply_forwarded_data(forwarded_data): + forwarded_json = json.loads(forwarded_data) + # Be aware of JS static allocations + shared.Settings.STATIC_BUMP = forwarded_json['STATIC_BUMP'] + def compile_settings(compiler_engine, libraries, temp_files): # Save settings to a file to work around v8 issue 1579 @@ -596,18 +613,53 @@ def compile_settings(compiler_engine, libraries, temp_files): cwd=path_from_root('src'), env=env) assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?' glue, forwarded_data = out.split('//FORWARDED_DATA:') + + apply_forwarded_data(forwarded_data) + return glue, forwarded_data +def apply_memory(pre, metadata): + # Apply the statically-at-compile-time computed memory locations. + # Note: if RELOCATABLE, then only relative sizes can be computed, and we don't + # actually write out any absolute memory locations ({{{ STACK_BASE }}} + # does not exist, etc.) + + # Memory layout: + # * first the static globals + global_start = shared.Settings.GLOBAL_BASE + static_bump = shared.Settings.STATIC_BUMP + # * then the stack (up on fastcomp, down on upstream) + stack_low = align_memory(global_start + static_bump) + stack_high = align_memory(stack_low + shared.Settings.TOTAL_STACK) + if shared.Settings.WASM_BACKEND: + stack_start = stack_high + stack_max = stack_low + else: + stack_start = stack_low + stack_max = stack_high + # * then dynamic memory begins + dynamic_start = align_memory(stack_high) + + # Write it all out + pre = pre.replace('{{{ STATIC_BUMP }}}', str(static_bump)) + pre = pre.replace('{{{ STACK_BASE }}}', str(stack_start)) + pre = pre.replace('{{{ STACK_MAX }}}', str(stack_max)) + pre = pre.replace('{{{ DYNAMIC_BASE }}}', str(dynamic_start)) + + logger.debug('global_start: %d stack_start: %d, stack_max: %d, dynamic_start: %d, static bump: %d', global_start, stack_start, stack_max, dynamic_start, static_bump) + + return pre + + def memory_and_global_initializers(pre, metadata, mem_init): global_initializers = ', '.join('{ func: function() { %s() } }' % i for i in metadata['initializers']) if shared.Settings.SIMD == 1: pre = open(path_from_root(os.path.join('src', 'ecmascript_simd.js'))).read() + '\n\n' + pre - staticbump = metadata['staticBump'] - while staticbump % 16 != 0: - staticbump += 1 + staticbump = shared.Settings.STATIC_BUMP + pthread = '' if shared.Settings.USE_PTHREADS: pthread = 'if (!ENVIRONMENT_IS_PTHREAD)' @@ -621,8 +673,8 @@ def memory_and_global_initializers(pre, metadata, mem_init): if shared.Settings.SIDE_MODULE: pre = pre.replace('GLOBAL_BASE', 'gb') - if shared.Settings.SIDE_MODULE or shared.Settings.WASM: - pre = pre.replace('{{{ STATIC_BUMP }}}', str(staticbump)) + + pre = apply_memory(pre, metadata) return pre @@ -1887,16 +1939,15 @@ def emscript_wasm_backend(infile, outfile, memfile, libraries, compiler_engine, global_initializers = ', '.join('{ func: function() { %s() } }' % i for i in metadata['initializers']) - staticbump = metadata['staticBump'] - while staticbump % 16 != 0: - staticbump += 1 + staticbump = shared.Settings.STATIC_BUMP + pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + %d; /* global initializers */ %s __ATINIT__.push(%s); ''' % (staticbump, 'if (!ENVIRONMENT_IS_PTHREAD)' if shared.Settings.USE_PTHREADS else '', global_initializers)) - pre = pre.replace('{{{ STATIC_BUMP }}}', str(staticbump)) + pre = apply_memory(pre, metadata) # merge forwarded data shared.Settings.EXPORTED_FUNCTIONS = forwarded_json['EXPORTED_FUNCTIONS'] diff --git a/site/source/docs/api_reference/preamble.js.rst b/site/source/docs/api_reference/preamble.js.rst index bf2c3c67887f..f097913c0f98 100644 --- a/site/source/docs/api_reference/preamble.js.rst +++ b/site/source/docs/api_reference/preamble.js.rst @@ -405,7 +405,7 @@ The :ref:`emscripten-memory-model` uses a typed array buffer (``ArrayBuffer``) t .. COMMENT (not rendered) : The following methods are explicitly not part of the public API and not documented. Note that in some case referred to by function name, other cases by Module assignment. function allocate(slab, types, allocator, ptr) — Internal and use is discouraged. Documentation can remain in source code but not here. - associated contants ALLOC_NORMAL, ALLOC_STACK, ALLOC_STATIC, ALLOC_DYNAMIC, ALLOC_NONE + associated contants ALLOC_NORMAL, ALLOC_STACK, ALLOC_DYNAMIC, ALLOC_NONE function addOnPreRun function addOnInit @@ -414,7 +414,6 @@ The :ref:`emscripten-memory-model` uses a typed array buffer (``ArrayBuffer``) t function addOnPostRun Module['ALLOC_NORMAL'] = ALLOC_NORMAL; Module['ALLOC_STACK'] = ALLOC_STACK; - Module['ALLOC_STATIC'] = ALLOC_STATIC; Module['ALLOC_DYNAMIC'] = ALLOC_DYNAMIC; Module['ALLOC_NONE'] = ALLOC_NONE; Module['HEAP'] = HEAP; diff --git a/src/deterministic.js b/src/deterministic.js index d12d9bfce184..07dd7c7dc4ea 100644 --- a/src/deterministic.js +++ b/src/deterministic.js @@ -19,7 +19,7 @@ Module['thisProgram'] = 'thisProgram'; // for consistency between different buil function hashMemory(id) { var ret = 0; - var len = Math.max(HEAP32[DYNAMICTOP_PTR>>2], STATICTOP); + var len = HEAP32[DYNAMICTOP_PTR>>2]; for (var i = 0; i < len; i++) { ret = (ret*17 + HEAPU8[i])|0; } diff --git a/src/jsifier.js b/src/jsifier.js index 058a112a427a..b9cd0a7f2bfb 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -431,7 +431,6 @@ function JSify(data, functionsOnly) { // Globals are done, here is the rest of static memory if (!SIDE_MODULE) { print('STATIC_BASE = GLOBAL_BASE;\n'); - print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); } else { print('gb = alignMemory(getMemory({{{ STATIC_BUMP }}} + ' + MAX_GLOBAL_ALIGN + '), ' + MAX_GLOBAL_ALIGN + ' || 1);\n'); // The static area consists of explicitly initialized data, followed by zero-initialized data. @@ -440,8 +439,9 @@ function JSify(data, functionsOnly) { // here, we just zero the whole thing, which is suboptimal, but should at least resolve bugs // from uninitialized memory. print('for (var i = gb; i < gb + {{{ STATIC_BUMP }}}; ++i) HEAP8[i] = 0;\n'); - print('// STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); // comment as metadata only } + // emit "metadata" in a comment. FIXME make this nicer + print('// STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); if (WASM) { // export static base and bump, needed for linking in wasm binary's memory, dynamic linking, etc. print('var STATIC_BUMP = {{{ STATIC_BUMP }}};'); @@ -487,7 +487,7 @@ function JSify(data, functionsOnly) { if (!SIDE_MODULE) { if (USE_PTHREADS) { print('var tempDoublePtr;'); - print('if (!ENVIRONMENT_IS_PTHREAD) tempDoublePtr = alignMemory(staticAlloc(12), 8);'); + print('if (!ENVIRONMENT_IS_PTHREAD) tempDoublePtr = ' + makeStaticAlloc(12) + ';'); } else { print('var tempDoublePtr = ' + makeStaticAlloc(8) + ''); } @@ -542,19 +542,6 @@ function JSify(data, functionsOnly) { for(i in proxiedFunctionInvokers) print(proxiedFunctionInvokers[i]+'\n'); print('if (!ENVIRONMENT_IS_PTHREAD) {\n // Only main thread initializes these, pthreads copy them over at thread worker init time (in worker.js)'); } - print('DYNAMICTOP_PTR = staticAlloc(4);\n'); - print('STACK_BASE = STACKTOP = alignMemory(STATICTOP);\n'); - if (STACK_START > 0) print('if (STACKTOP < ' + STACK_START + ') STACK_BASE = STACKTOP = alignMemory(' + STACK_START + ');\n'); - print('STACK_MAX = STACK_BASE + TOTAL_STACK;\n'); - print('DYNAMIC_BASE = alignMemory(STACK_MAX);\n'); - if (WASM_BACKEND) { - // wasm backend stack goes down - print('STACKTOP = STACK_BASE + TOTAL_STACK;'); - print('STACK_MAX = STACK_BASE;'); - } - print('HEAP32[DYNAMICTOP_PTR>>2] = DYNAMIC_BASE;\n'); - print('staticSealed = true; // seal the static portion of memory\n'); - if (ASSERTIONS) print('assert(DYNAMIC_BASE < TOTAL_MEMORY, "TOTAL_MEMORY not big enough for stack");\n'); if (USE_PTHREADS) print('}\n'); } diff --git a/src/library.js b/src/library.js index bc9f5868224b..91bd48f23e71 100644 --- a/src/library.js +++ b/src/library.js @@ -19,16 +19,17 @@ // object. For convenience, the short name appears here. Note that if you add a // new function with an '_', it will not be found. -// Memory allocated during startup, in postsets, should only be ALLOC_STATIC +// Memory allocated during startup, in postsets, should only be static +// (using makeStaticAlloc) LibraryManager.library = { // keep this low in memory, because we flatten arrays with them in them #if USE_PTHREADS - stdin: '; if (ENVIRONMENT_IS_PTHREAD) _stdin = PthreadWorkerInit._stdin; else PthreadWorkerInit._stdin = _stdin = staticAlloc(4)', - stdout: '; if (ENVIRONMENT_IS_PTHREAD) _stdout = PthreadWorkerInit._stdout; else PthreadWorkerInit._stdout = _stdout = staticAlloc(4)', - stderr: '; if (ENVIRONMENT_IS_PTHREAD) _stderr = PthreadWorkerInit._stderr; else PthreadWorkerInit._stderr = _stderr = staticAlloc(4)', - _impure_ptr: '; if (ENVIRONMENT_IS_PTHREAD) __impure_ptr = PthreadWorkerInit.__impure_ptr; else PthreadWorkerInit.__impure_ptr __impure_ptr = staticAlloc(4)', - __dso_handle: '; if (ENVIRONMENT_IS_PTHREAD) ___dso_handle = PthreadWorkerInit.___dso_handle; else PthreadWorkerInit.___dso_handle = ___dso_handle = staticAlloc(4)', + stdin: '; if (ENVIRONMENT_IS_PTHREAD) _stdin = PthreadWorkerInit._stdin; else PthreadWorkerInit._stdin = _stdin = {{{ makeStaticAlloc(4) }}}', + stdout: '; if (ENVIRONMENT_IS_PTHREAD) _stdout = PthreadWorkerInit._stdout; else PthreadWorkerInit._stdout = _stdout = {{{ makeStaticAlloc(4) }}}', + stderr: '; if (ENVIRONMENT_IS_PTHREAD) _stderr = PthreadWorkerInit._stderr; else PthreadWorkerInit._stderr = _stderr = {{{ makeStaticAlloc(4) }}}', + _impure_ptr: '; if (ENVIRONMENT_IS_PTHREAD) __impure_ptr = PthreadWorkerInit.__impure_ptr; else PthreadWorkerInit.__impure_ptr __impure_ptr = {{{ makeStaticAlloc(4) }}}', + __dso_handle: '; if (ENVIRONMENT_IS_PTHREAD) ___dso_handle = PthreadWorkerInit.___dso_handle; else PthreadWorkerInit.___dso_handle = ___dso_handle = {{{ makeStaticAlloc(4) }}}', #else stdin: '{{{ makeStaticAlloc(1) }}}', stdout: '{{{ makeStaticAlloc(1) }}}', @@ -1962,13 +1963,13 @@ LibraryManager.library = { // Statically allocated time struct. #if USE_PTHREADS - __tm_current: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_current = PthreadWorkerInit.___tm_current; else PthreadWorkerInit.___tm_current = ___tm_current = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', - __tm_timezone: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_timezone = PthreadWorkerInit.___tm_timezone; else PthreadWorkerInit.___tm_timezone = ___tm_timezone = allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', - __tm_formatted: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_formatted = PthreadWorkerInit.___tm_formatted; else PthreadWorkerInit.___tm_formatted = ___tm_formatted = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', + __tm_current: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_current = PthreadWorkerInit.___tm_current; else PthreadWorkerInit.___tm_current = ___tm_current = {{{ makeStaticAlloc(C_STRUCTS.tm.__size__) }}}', + __tm_timezone: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_timezone = PthreadWorkerInit.___tm_timezone; else PthreadWorkerInit.___tm_timezone = ___tm_timezone = {{{ makeStaticString("GMT") }}}', + __tm_formatted: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_formatted = PthreadWorkerInit.___tm_formatted; else PthreadWorkerInit.___tm_formatted = ___tm_formatted = {{{ makeStaticAlloc(C_STRUCTS.tm.__size__) }}}', #else __tm_current: '{{{ makeStaticAlloc(C_STRUCTS.tm.__size__) }}}', // Statically allocated copy of the string "GMT" for gmtime() to point to - __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', + __tm_timezone: '{{{ makeStaticString("GMT") }}}', // Statically allocated time strings. __tm_formatted: '{{{ makeStaticAlloc(C_STRUCTS.tm.__size__) }}}', #endif @@ -3297,13 +3298,13 @@ LibraryManager.library = { // ========================================================================== #if USE_PTHREADS - in6addr_any: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_any = PthreadWorkerInit._in6addr_any; else PthreadWorkerInit._in6addr_any = _in6addr_any = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', - in6addr_loopback: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_loopback = PthreadWorkerInit._in6addr_loopback; else PthreadWorkerInit._in6addr_loopback = _in6addr_loopback = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', + in6addr_any: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_any = PthreadWorkerInit._in6addr_any; else PthreadWorkerInit._in6addr_any = _in6addr_any = {{{ makeStaticAlloc(16) }}}', + in6addr_loopback: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_loopback = PthreadWorkerInit._in6addr_loopback; else PthreadWorkerInit._in6addr_loopback = _in6addr_loopback = {{{ makeStaticAlloc(16) }}}', #else in6addr_any: - 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', + '{{{ makeStaticAlloc(16) }}}', in6addr_loopback: - 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', + '{{{ makeStaticAlloc(16) }}}', #endif // ========================================================================== @@ -4818,3 +4819,4 @@ function autoAddDeps(object, name) { } } } + diff --git a/src/library_fetch.js b/src/library_fetch.js index b74659405dd6..a30e76711772 100644 --- a/src/library_fetch.js +++ b/src/library_fetch.js @@ -8,10 +8,10 @@ var LibraryFetch = { #if USE_PTHREADS $Fetch__postset: 'if (!ENVIRONMENT_IS_PTHREAD) Fetch.staticInit();', - fetch_work_queue: '; if (ENVIRONMENT_IS_PTHREAD) _fetch_work_queue = PthreadWorkerInit._fetch_work_queue; else PthreadWorkerInit._fetch_work_queue = _fetch_work_queue = staticAlloc(12)', + fetch_work_queue: '; if (ENVIRONMENT_IS_PTHREAD) _fetch_work_queue = PthreadWorkerInit._fetch_work_queue; else PthreadWorkerInit._fetch_work_queue = _fetch_work_queue = {{{ makeStaticAlloc(12) }}}', #else $Fetch__postset: 'Fetch.staticInit();', - fetch_work_queue: 'staticAlloc(12)', + fetch_work_queue: '{{{ makeStaticAlloc(12) }}}', #endif $Fetch: Fetch, _emscripten_get_fetch_work_queue__deps: ['fetch_work_queue'], diff --git a/src/library_pthread.js b/src/library_pthread.js index 0f173fe53090..7d7189adcefb 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -21,7 +21,7 @@ var LibraryPThread = { // mainThreadBlock: undefined, initMainThreadBlock: function() { if (ENVIRONMENT_IS_PTHREAD) return undefined; - PThread.mainThreadBlock = allocate({{{ C_STRUCTS.pthread.__size__ }}}, "i32*", ALLOC_STATIC); + PThread.mainThreadBlock = {{{ makeStaticAlloc(C_STRUCTS.pthread.__size__) }}}; for (var i = 0; i < {{{ C_STRUCTS.pthread.__size__ }}}/4; ++i) HEAPU32[PThread.mainThreadBlock/4+i] = 0; @@ -34,7 +34,7 @@ var LibraryPThread = { {{{ makeSetValue('headPtr', 0, 'headPtr', 'i32') }}}; // Allocate memory for thread-local storage. - var tlsMemory = allocate({{{ cDefine('PTHREAD_KEYS_MAX') }}} * 4, "i32*", ALLOC_STATIC); + var tlsMemory = {{{ makeStaticAlloc(cDefine('PTHREAD_KEYS_MAX') * 4) }}}; for (var i = 0; i < {{{ cDefine('PTHREAD_KEYS_MAX') }}}; ++i) HEAPU32[tlsMemory/4+i] = 0; Atomics.store(HEAPU32, (PThread.mainThreadBlock + {{{ C_STRUCTS.pthread.tsd }}} ) >> 2, tlsMemory); // Init thread-local-storage memory array. Atomics.store(HEAPU32, (PThread.mainThreadBlock + {{{ C_STRUCTS.pthread.tid }}} ) >> 2, PThread.mainThreadBlock); // Main thread ID. @@ -54,7 +54,7 @@ var LibraryPThread = { #if PTHREADS_PROFILING createProfilerBlock: function(pthreadPtr) { - var profilerBlock = (pthreadPtr == PThread.mainThreadBlock) ? allocate({{{ C_STRUCTS.thread_profiler_block.__size__ }}}, "i32*", ALLOC_STATIC) : _malloc({{{ C_STRUCTS.thread_profiler_block.__size__ }}}); + var profilerBlock = (pthreadPtr == PThread.mainThreadBlock) ? {{{ makeStaticAlloc(C_STRUCTS.thread_profiler_block.__size__) }}} : _malloc({{{ C_STRUCTS.thread_profiler_block.__size__ }}}); Atomics.store(HEAPU32, (pthreadPtr + {{{ C_STRUCTS.pthread.profilerBlock }}} ) >> 2, profilerBlock); // Zero fill contents at startup. @@ -400,7 +400,6 @@ var LibraryPThread = { #endif tempDoublePtr: tempDoublePtr, TOTAL_MEMORY: TOTAL_MEMORY, - STATICTOP: STATICTOP, DYNAMIC_BASE: DYNAMIC_BASE, DYNAMICTOP_PTR: DYNAMICTOP_PTR, PthreadWorkerInit: PthreadWorkerInit @@ -529,7 +528,7 @@ var LibraryPThread = { }, _num_logical_cores__deps: ['emscripten_force_num_logical_cores'], - _num_logical_cores: '; if (ENVIRONMENT_IS_PTHREAD) __num_logical_cores = PthreadWorkerInit.__num_logical_cores; else { PthreadWorkerInit.__num_logical_cores = __num_logical_cores = allocate(1, "i32*", ALLOC_STATIC); HEAPU32[__num_logical_cores>>2] = navigator["hardwareConcurrency"] || ' + {{{ PTHREAD_HINT_NUM_CORES }}} + '; }', + _num_logical_cores: '; if (ENVIRONMENT_IS_PTHREAD) __num_logical_cores = PthreadWorkerInit.__num_logical_cores; else { PthreadWorkerInit.__num_logical_cores = __num_logical_cores = {{{ makeStaticAlloc(4) }}}; HEAPU32[__num_logical_cores>>2] = navigator["hardwareConcurrency"] || ' + {{{ PTHREAD_HINT_NUM_CORES }}} + '; }', emscripten_has_threading_support: function() { return typeof SharedArrayBuffer !== 'undefined'; @@ -1031,7 +1030,7 @@ var LibraryPThread = { }, // Stores the memory address that the main thread is waiting on, if any. - _main_thread_futex_wait_address: '; if (ENVIRONMENT_IS_PTHREAD) __main_thread_futex_wait_address = PthreadWorkerInit.__main_thread_futex_wait_address; else PthreadWorkerInit.__main_thread_futex_wait_address = __main_thread_futex_wait_address = allocate(1, "i32*", ALLOC_STATIC)', + _main_thread_futex_wait_address: '; if (ENVIRONMENT_IS_PTHREAD) __main_thread_futex_wait_address = PthreadWorkerInit.__main_thread_futex_wait_address; else PthreadWorkerInit.__main_thread_futex_wait_address = __main_thread_futex_wait_address = {{{ makeStaticAlloc(4) }}}', // Returns 0 on success, or one of the values -ETIMEDOUT, -EWOULDBLOCK or -EINVAL on error. emscripten_futex_wait__deps: ['_main_thread_futex_wait_address', 'emscripten_main_thread_process_queued_calls'], diff --git a/src/library_trace.js b/src/library_trace.js index 49dead959c3f..0e11375fb008 100644 --- a/src/library_trace.js +++ b/src/library_trace.js @@ -262,7 +262,6 @@ var LibraryTracing = { if (EmscriptenTrace.postEnabled) { var memory_layout = { 'static_base': STATIC_BASE, - 'static_top': STATICTOP, 'stack_base': STACK_BASE, 'stack_top': STACKTOP, 'stack_max': STACK_MAX, diff --git a/src/memoryprofiler.js b/src/memoryprofiler.js index ef7d8e3815c9..1f27df64e29a 100644 --- a/src/memoryprofiler.js +++ b/src/memoryprofiler.js @@ -308,9 +308,8 @@ var emscriptenMemoryProfiler = { var width = (nBits(TOTAL_MEMORY) + 3) / 4; // Pointer 'word width' var html = 'Total HEAP size: ' + this.formatBytes(TOTAL_MEMORY) + '.'; - html += '
' + colorBar('#202020') + 'STATIC memory area size: ' + this.formatBytes(STATICTOP - STATIC_BASE); + html += '
' + colorBar('#202020') + 'STATIC memory area size: ' + this.formatBytes(STACK_BASE - STATIC_BASE); html += '. STATIC_BASE: ' + toHex(STATIC_BASE, width); - html += '. STATICTOP: ' + toHex(STATICTOP, width) + '.'; html += '
' + colorBar('#FF8080') + 'STACK memory area size: ' + this.formatBytes(STACK_MAX - STACK_BASE); html += '. STACK_BASE: ' + toHex(STACK_BASE, width); @@ -336,9 +335,6 @@ var emscriptenMemoryProfiler = { this.drawContext.fillStyle = "#FFFFFF"; this.drawContext.fillRect(0, 0, this.canvas.width, this.canvas.height); - this.drawContext.fillStyle = "#202020"; - this.fillLine(STATIC_BASE, STATICTOP); - this.drawContext.fillStyle = "#FF8080"; this.fillLine(STACK_BASE, STACK_MAX); diff --git a/src/modules.js b/src/modules.js index 2777e0a4a882..b592fa1cab04 100644 --- a/src/modules.js +++ b/src/modules.js @@ -221,6 +221,7 @@ var LibraryManager = { target = lib[target]; } if (lib[target + '__asm']) continue; // This is an alias of an asm library function. Also needs to be fully optimized. + if (!isNaN(target)) continue; // This is a number, and so cannot be an alias target. if (typeof lib[target] === 'undefined' || typeof lib[target] === 'function') { lib[x] = new Function('return _' + target + '.apply(null, arguments)'); if (!lib[x + '__deps']) lib[x + '__deps'] = []; @@ -404,7 +405,6 @@ function exportRuntime() { 'FS_createDevice', 'FS_unlink', 'GL', - 'staticAlloc', 'dynamicAlloc', 'warnOnce', 'loadDynamicLibrary', @@ -445,7 +445,6 @@ function exportRuntime() { var runtimeNumbers = [ 'ALLOC_NORMAL', 'ALLOC_STACK', - 'ALLOC_STATIC', 'ALLOC_DYNAMIC', 'ALLOC_NONE', ]; @@ -469,7 +468,8 @@ var PassManager = { serialize: function() { print('\n//FORWARDED_DATA:' + JSON.stringify({ Functions: Functions, - EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS + EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS, + STATIC_BUMP: STATIC_BUMP // updated with info from JS })); }, load: function(json) { diff --git a/src/parseTools.js b/src/parseTools.js index d660638a6b00..13af6086cc99 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1391,6 +1391,31 @@ function charCode(char) { return char.charCodeAt(0); } +// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. +function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) { + ++len; + } else if (u <= 0x7FF) { + len += 2; + } else if (u <= 0xFFFF) { + len += 3; + } else if (u <= 0x1FFFFF) { + len += 4; + } else if (u <= 0x3FFFFFF) { + len += 5; + } else { + len += 6; + } + } + return len; +} + function getTypeFromHeap(suffix) { switch (suffix) { case '8': return 'i8'; @@ -1441,8 +1466,32 @@ function makeEval(code) { } function makeStaticAlloc(size) { - size = (size + (STACK_ALIGN-1)) & -STACK_ALIGN; - return 'STATICTOP; STATICTOP += ' + size + ';'; + size = alignMemory(size); + var ret = alignMemory(GLOBAL_BASE + STATIC_BUMP); + STATIC_BUMP = ret + size - GLOBAL_BASE; + return ret; +} + +function makeStaticString(string) { + var len = lengthBytesUTF8(string) + 1; + var ptr = makeStaticAlloc(len); + return '(stringToUTF8("' + string + '", ' + ptr + ', ' + len + '), ' + ptr + ')'; +} + +// We emit the dynamic and stack bases as strings that need to be further +// preprocessed, since during JS compiler time here we are still computing +// static allocations as we go. + +function getStackBase() { + return '{{{ STACK_BASE }}}'; +} + +function getStackMax() { + return '{{{ STACK_MAX }}}'; +} + +function getDynamicBase() { + return '{{{ DYNAMIC_BASE }}}'; } function makeRetainedCompilerSettings() { diff --git a/src/preamble.js b/src/preamble.js index c7094f9d0fce..fd9ab0a8d1a8 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -34,13 +34,9 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { #endif if (dest <= 0) abort('segmentation fault storing ' + bytes + ' bytes to address ' + dest); if (dest % bytes !== 0) abort('alignment error storing to address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes); - if (staticSealed) { - if (dest + bytes > HEAP32[DYNAMICTOP_PTR>>2]) abort('segmentation fault, exceeded the top of the available dynamic heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + HEAP32[DYNAMICTOP_PTR>>2]); - assert(DYNAMICTOP_PTR); - assert(HEAP32[DYNAMICTOP_PTR>>2] <= TOTAL_MEMORY); - } else { - if (dest + bytes > STATICTOP) abort('segmentation fault, exceeded the top of the available static heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP); - } + if (dest + bytes > HEAP32[DYNAMICTOP_PTR>>2]) abort('segmentation fault, exceeded the top of the available dynamic heap when storing ' + bytes + ' bytes to address ' + dest + '. DYNAMICTOP=' + HEAP32[DYNAMICTOP_PTR>>2]); + assert(DYNAMICTOP_PTR); + assert(HEAP32[DYNAMICTOP_PTR>>2] <= TOTAL_MEMORY); setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); } function SAFE_HEAP_STORE_D(dest, value, bytes) { @@ -50,13 +46,9 @@ function SAFE_HEAP_STORE_D(dest, value, bytes) { function SAFE_HEAP_LOAD(dest, bytes, unsigned, isFloat) { if (dest <= 0) abort('segmentation fault loading ' + bytes + ' bytes from address ' + dest); if (dest % bytes !== 0) abort('alignment error loading from address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes); - if (staticSealed) { - if (dest + bytes > HEAP32[DYNAMICTOP_PTR>>2]) abort('segmentation fault, exceeded the top of the available dynamic heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + HEAP32[DYNAMICTOP_PTR>>2]); - assert(DYNAMICTOP_PTR); - assert(HEAP32[DYNAMICTOP_PTR>>2] <= TOTAL_MEMORY); - } else { - if (dest + bytes > STATICTOP) abort('segmentation fault, exceeded the top of the available static heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP); - } + if (dest + bytes > HEAP32[DYNAMICTOP_PTR>>2]) abort('segmentation fault, exceeded the top of the available dynamic heap when loading ' + bytes + ' bytes from address ' + dest + '. DYNAMICTOP=' + HEAP32[DYNAMICTOP_PTR>>2]); + assert(DYNAMICTOP_PTR); + assert(HEAP32[DYNAMICTOP_PTR>>2] <= TOTAL_MEMORY); var type = getSafeHeapType(bytes, isFloat); var ret = getValue(dest, type, 1); if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); @@ -286,9 +278,8 @@ function getValue(ptr, type, noSafe) { var ALLOC_NORMAL = 0; // Tries to use _malloc() var ALLOC_STACK = 1; // Lives for the duration of the current function call -var ALLOC_STATIC = 2; // Cannot be freed -var ALLOC_DYNAMIC = 3; // Cannot be freed except through sbrk -var ALLOC_NONE = 4; // Do not allocate +var ALLOC_DYNAMIC = 2; // Cannot be freed except through sbrk +var ALLOC_NONE = 3; // Do not allocate // allocate(): This is for internal use. You can use it yourself as well, but the interface // is a little tricky (see docs right below). The reason is that it is optimized @@ -323,7 +314,7 @@ function allocate(slab, types, allocator, ptr) { if (allocator == ALLOC_NONE) { ret = ptr; } else { - ret = [typeof _malloc === 'function' ? _malloc : staticAlloc, stackAlloc, staticAlloc, dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + ret = [_malloc, stackAlloc, dynamicAlloc][allocator](Math.max(size, singleType ? 1 : types.length)); } if (zeroinit) { @@ -380,7 +371,6 @@ function allocate(slab, types, allocator, ptr) { // Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready function getMemory(size) { - if (!staticSealed) return staticAlloc(size); if (!runtimeInitialized) return dynamicAlloc(size); return _malloc(size); } @@ -599,30 +589,7 @@ function stringToUTF8(str, outPtr, maxBytesToWrite) { } // Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. - -function lengthBytesUTF8(str) { - var len = 0; - for (var i = 0; i < str.length; ++i) { - // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. - // See http://unicode.org/faq/utf_bom.html#utf16-3 - var u = str.charCodeAt(i); // possibly a lead surrogate - if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); - if (u <= 0x7F) { - ++len; - } else if (u <= 0x7FF) { - len += 2; - } else if (u <= 0xFFFF) { - len += 3; - } else if (u <= 0x1FFFFF) { - len += 4; - } else if (u <= 0x3FFFFFF) { - len += 5; - } else { - len += 6; - } - } - return len; -} +{{{ lengthBytesUTF8 }}} // Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns // a copy of that string as a Javascript String object. @@ -929,22 +896,28 @@ function updateGlobalBufferViews() { Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer); } -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/worker.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; + +var STATIC_BASE = {{{ GLOBAL_BASE }}}, + STACK_BASE = {{{ getStackBase() }}}, + STACKTOP = STACK_BASE, + STACK_MAX = {{{ getStackMax() }}}, + DYNAMIC_BASE = {{{ getDynamicBase() }}}, + DYNAMICTOP_PTR = {{{ makeStaticAlloc(4) }}}; + +#if ASSERTIONS +assert(STACK_BASE % 16 === 0, 'stack must start aligned'); +assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned'); +#endif + #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 @@ -1121,7 +1094,11 @@ try { } #endif -var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}}; +var TOTAL_STACK = {{{ TOTAL_STACK }}}; +#if ASSERTIONS +if (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime') +#endif + 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 + ')'); @@ -1262,6 +1239,14 @@ updateGlobalBufferViews(); #endif // USE_PTHREADS +#if USE_PTHREADS +if (!ENVIRONMENT_IS_PTHREAD) { // Pthreads have already initialized these variables in src/worker.js, where they were passed to the thread worker at startup time +#endif +HEAP32[DYNAMICTOP_PTR>>2] = DYNAMIC_BASE; +#if USE_PTHREADS +} +#endif + function getTotalMemory() { return TOTAL_MEMORY; } diff --git a/src/settings.js b/src/settings.js index 5ba223c37bb9..0399ae06b2e1 100644 --- a/src/settings.js +++ b/src/settings.js @@ -141,9 +141,6 @@ var GLOBAL_BASE = -1; // allocations, by forcing the stack to start in the same place their // memory usage patterns would be the same. -// Code embetterments -var STACK_START = -1; - // How to load and store 64-bit doubles. A potential risk is that doubles may // be only 32-bit aligned. Forcing 64-bit alignment in Clang itself should be // able to solve that, or as a workaround in DOUBLE_MODE 1 we will carefully @@ -1136,12 +1133,6 @@ var SDL2_IMAGE_FORMATS = []; // legalizer var DEBUG_TAGS_SHOWING = []; -// Internal: tracks the list of EM_ASM signatures that are proxied between threads. -var PROXIED_FUNCTION_SIGNATURES = []; - -// For internal use only -var ORIGINAL_EXPORTED_FUNCTIONS = []; - // The list of defines (C_DEFINES) was moved into struct_info.json in the same // directory. That file is automatically parsed by tools/gen_struct_info.py. // If you modify the headers, just clear your cache and emscripten libc should @@ -1292,39 +1283,56 @@ var ASMFS = 0; // then you can safely ignore this warning. var SINGLE_FILE = 0; -// For internal use only (name of the file containing wasm text, if relevant). +// if set to 1, then generated WASM files will contain a custom +// "emscripten_metadata" section that contains information necessary +// to execute the file without the accompanying JS file. +var EMIT_EMSCRIPTEN_METADATA = 0; + + +// Internal use only, from here + +// tracks the list of EM_ASM signatures that are proxied between threads. +var PROXIED_FUNCTION_SIGNATURES = []; + +var ORIGINAL_EXPORTED_FUNCTIONS = []; + +// name of the file containing wasm text, if relevant var WASM_TEXT_FILE = ''; -// For internal use only (name of the file containing wasm binary, if relevant). +// name of the file containing wasm binary, if relevant var WASM_BINARY_FILE = ''; -// For internal use only (name of the file containing asm.js, if relevant). +// name of the file containing asm.js code, if relevant var ASMJS_CODE_FILE = ''; -// For internal use only (name of the file containing the pthread *.worker.js, if relevant). +// name of the file containing the pthread *.worker.js, if relevant var PTHREAD_WORKER_FILE = ''; // Base URL the source mapfile, if relevant var SOURCE_MAP_BASE = ''; -// for internal use only var MEM_INIT_IN_WASM = 0; // If set to 1, src/base64Utils.js will be included in the bundle. // This is set internally when needed (SINGLE_FILE) var SUPPORT_BASE64_EMBEDDING = 0; -// For internal use only, the possible environments the code may run in. +// the possible environments the code may run in. var ENVIRONMENT_MAY_BE_WEB = 1; var ENVIRONMENT_MAY_BE_WORKER = 1; var ENVIRONMENT_MAY_BE_NODE = 1; var ENVIRONMENT_MAY_BE_SHELL = 1; -// Internal: passes information to emscripten.py about whether to minify +// passes information to emscripten.py about whether to minify // JS -> asm.js import names. Controlled by optimization level, enabled // at -O1 and higher, but disabled at -g2 and higher. var MINIFY_ASMJS_IMPORT_NAMES = 0; +// the total static allocation, that is, how much to bump the start of memory +// for static globals. received from the backend, and possibly increased due +// to JS static allocations +var STATIC_BUMP = -1; + // if set to 1, then generated WASM files will contain a custom // "emscripten_metadata" section that contains information necessary // to execute the file without the accompanying JS file. diff --git a/src/support.js b/src/support.js index ed3363cb27aa..e10ba72a1c50 100644 --- a/src/support.js +++ b/src/support.js @@ -13,19 +13,11 @@ var STACK_ALIGN = {{{ STACK_ALIGN }}}; stackSave = stackRestore = stackAlloc = function() { abort('cannot use the stack before compiled code is ready to run, and has provided stack access'); }; -#endif function staticAlloc(size) { -#if ASSERTIONS - assert(!staticSealed); -#endif - var ret = STATICTOP; - STATICTOP = (STATICTOP + size + 15) & -16; -#if ASSERTIONS - assert(STATICTOP < TOTAL_MEMORY, 'not enough memory for static allocation - increase TOTAL_MEMORY'); -#endif - return ret; + abort('staticAlloc is no longer available at runtime; instead, perform static allocations at compile time (using makeStaticAlloc)'); } +#endif function dynamicAlloc(size) { #if ASSERTIONS diff --git a/src/worker.js b/src/worker.js index e4219d3b10d5..91a344f07ec4 100644 --- a/src/worker.js +++ b/src/worker.js @@ -22,8 +22,6 @@ var STACK_MAX = 0; var buffer; // All pthreads share the same Emscripten HEAP as SharedArrayBuffer with the main execution thread. var DYNAMICTOP_PTR = 0; var TOTAL_MEMORY = 0; -var STATICTOP = 0; -var staticSealed = true; // When threads are being initialized, the static memory area has been already sealed a long time ago. var DYNAMIC_BASE = 0; var ENVIRONMENT_IS_PTHREAD = true; @@ -85,7 +83,6 @@ this.onmessage = function(e) { // Initialize the global "process"-wide fields: Module['TOTAL_MEMORY'] = TOTAL_MEMORY = e.data.TOTAL_MEMORY; - STATICTOP = e.data.STATICTOP; DYNAMIC_BASE = e.data.DYNAMIC_BASE; DYNAMICTOP_PTR = e.data.DYNAMICTOP_PTR; diff --git a/tests/core/legacy_exported_runtime_numbers.txt b/tests/core/legacy_exported_runtime_numbers.txt index a12955e26f70..fd524e7bf08c 100644 --- a/tests/core/legacy_exported_runtime_numbers.txt +++ b/tests/core/legacy_exported_runtime_numbers.txt @@ -1 +1 @@ -|3| +|2| diff --git a/tests/test_core.py b/tests/test_core.py index 2a167288582f..4c53b942e747 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3790,7 +3790,7 @@ def test_dylink_jslib(self): def test_dylink_global_var_jslib(self): create_test_file('lib.js', r''' mergeInto(LibraryManager.library, { - jslib_x: 'allocate(1, "i32*", ALLOC_STATIC)', + jslib_x: '{{{ makeStaticAlloc(4) }}}', jslib_x__postset: 'HEAP32[_jslib_x>>2] = 148;', }); ''') @@ -7818,7 +7818,6 @@ def test_memprof_requirements(self): check_memprof_requirements: function() { if (typeof TOTAL_MEMORY === 'number' && typeof STATIC_BASE === 'number' && - typeof STATICTOP === 'number' && typeof STACK_BASE === 'number' && typeof STACK_MAX === 'number' && typeof STACKTOP === 'number' && @@ -7851,8 +7850,6 @@ def test_fs_dict(self): @no_wasm_backend("wasm backend has no support for fastcomp's -emscripten-assertions flag") def test_stack_overflow_check(self): args = self.emcc_args + ['-s', 'TOTAL_STACK=1048576'] - self.emcc_args = args + ['-s', 'STACK_OVERFLOW_CHECK=1', '-s', 'ASSERTIONS=0'] - self.do_run(open(path_from_root('tests', 'stack_overflow.cpp')).read(), 'Stack overflow! Stack cookie has been overwritten' if not self.get_setting('SAFE_HEAP') else 'segmentation fault') self.emcc_args = args + ['-s', 'STACK_OVERFLOW_CHECK=2', '-s', 'ASSERTIONS=0'] self.do_run(open(path_from_root('tests', 'stack_overflow.cpp')).read(), 'Stack overflow! Attempted to allocate') diff --git a/tests/zlib/gzwrite.c b/tests/zlib/gzwrite.c index e8defc6887a1..ed2074972ef5 100644 --- a/tests/zlib/gzwrite.c +++ b/tests/zlib/gzwrite.c @@ -321,7 +321,7 @@ int ZEXPORTVA gzprintf (gzFile file, const char *format, ...) for (len = 0; len < size; len++) if (state->in[len] == 0) break; # else - len = vsprintf(state->in, format, va); + len = vsprintf((char*)state->in, format, va); va_end(va); # endif #else