remove dynamic allocations, simplfying our allocation model
diff --git a/emscripten.py b/emscripten.py index 973aca0..ae65cbc 100755 --- a/emscripten.py +++ b/emscripten.py
@@ -605,7 +605,9 @@ basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT'] basic_float_vars = [] - if settings['SAFE_HEAP']: basic_vars += ['DYNAMICTOP'] + if settings['SAFE_HEAP']: + asm_setup += 'var DYNAMICTOP = STACK_MAX;\n' + basic_vars += ['DYNAMICTOP'] if metadata.get('preciseI64MathUsed'): basic_vars += ['cttz_i8']
diff --git a/src/deterministic.js b/src/deterministic.js index 0f9be2a..34c5103 100644 --- a/src/deterministic.js +++ b/src/deterministic.js
@@ -14,7 +14,7 @@ function hashMemory(id) { var ret = 0; - var len = Math.max(DYNAMICTOP, STATICTOP); + var len = TOTAL_MEMORY; // TODO: optimize for (var i = 0; i < len; i++) { ret = (ret*17 + HEAPU8[i])|0; }
diff --git a/src/jsifier.js b/src/jsifier.js index e31c7ed..8703af4 100644 --- a/src/jsifier.js +++ b/src/jsifier.js
@@ -387,8 +387,7 @@ print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n'); print('staticSealed = true; // seal the static portion of memory\n'); print('STACK_MAX = STACK_BASE + TOTAL_STACK;\n'); - print('DYNAMIC_BASE = DYNAMICTOP = Runtime.alignMemory(STACK_MAX);\n'); - if (ASSERTIONS) print('assert(DYNAMIC_BASE < TOTAL_MEMORY, "TOTAL_MEMORY not big enough for stack");\n'); + if (ASSERTIONS) print('assert(STACK_MAX < TOTAL_MEMORY, "TOTAL_MEMORY not big enough for stack");\n'); } if (SPLIT_MEMORY) { print('assert(STACK_MAX < SPLIT_MEMORY, "SPLIT_MEMORY size must be big enough so the entire static memory + stack can fit in one chunk, need " + STACK_MAX);\n'); @@ -429,7 +428,6 @@ assert(typeof dep == 'function'); var text = dep(); assert(text.indexOf('\n') < 0); - text = text.replace('ALLOC_STATIC', 'ALLOC_DYNAMIC'); print('/* PRE_ASM */ ' + text + '\n'); }); }
diff --git a/src/library.js b/src/library.js index af0a07c..80dc2d3 100644 --- a/src/library.js +++ b/src/library.js
@@ -435,16 +435,33 @@ // Implement a Linux-like 'memory area' for our 'process'. // Changes the size of the memory area by |bytes|; returns the // address of the previous top ('break') of the memory area - // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP + // We control the "dynamic" memory - above static and the top var self = _sbrk; if (!self.called) { - DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned + self.top = alignMemoryPage(STACK_MAX); // make sure we start out aligned self.called = true; - assert(Runtime.dynamicAlloc); - self.alloc = Runtime.dynamicAlloc; - Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') }; + self.alloc = function(bytes) { +#if USE_PTHREADS + if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.dynamicAlloc is not available in pthreads!'; // This is because each worker has its own copy of self.top, of which main thread is authoritative. +#endif + var ret = (self.top = ((self.top + bytes) & -15)); +#if SAFE_HEAP + if (asm) Runtime.setDynamicTop(self.top); +#endif + if (self.top >= TOTAL_MEMORY) { + var success = enlargeMemory(); // XXX TO HOW MUCH? + if (!success) { + self.top = ret; +#if SAFE_HEAP + if (asm) Runtime.setDynamicTop(self.top); +#endif + return 0; + } + } + return ret; + }; } - var ret = DYNAMICTOP; + var ret = self.top; if (bytes != 0) { var success = self.alloc(bytes); if (!success) return -1 >>> 0; // sbrk failure code
diff --git a/src/library_bootstrap_structInfo.js b/src/library_bootstrap_structInfo.js index a0808a7..205ecff 100644 --- a/src/library_bootstrap_structInfo.js +++ b/src/library_bootstrap_structInfo.js
@@ -13,25 +13,8 @@ } return ret; }, - sbrk: function(bytes) { - // Implement a Linux-like 'memory area' for our 'process'. - // Changes the size of the memory area by |bytes|; returns the - // address of the previous top ('break') of the memory area - // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP - var self = _sbrk; - if (!self.called) { - DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned - self.called = true; - assert(Runtime.dynamicAlloc); - self.alloc = Runtime.dynamicAlloc; - Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') }; - } - var ret = DYNAMICTOP; - if (bytes != 0) self.alloc(bytes); - return ret; // Previous break location. - }, malloc: function(x) { - return Runtime.dynamicAlloc(x); + return Runtime.stackAlloc(x); }, };
diff --git a/src/library_trace.js b/src/library_trace.js index 6899221..a3e7155 100644 --- a/src/library_trace.js +++ b/src/library_trace.js
@@ -261,8 +261,6 @@ 'stack_base': STACK_BASE, 'stack_top': STACKTOP, 'stack_max': STACK_MAX, - 'dynamic_base': DYNAMIC_BASE, - 'dynamic_top': DYNAMICTOP, 'total_memory': TOTAL_MEMORY }; var now = EmscriptenTrace.now();
diff --git a/src/memoryprofiler.js b/src/memoryprofiler.js index 118aa55..57ec78c 100644 --- a/src/memoryprofiler.js +++ b/src/memoryprofiler.js
@@ -302,17 +302,12 @@ html += '. STACK_MAX: ' + toHex(STACK_MAX, width) + '.'; html += '<br />STACK memory area used now (should be zero): ' + this.formatBytes(STACKTOP - STACK_BASE) + '.' + colorBar('#FFFF00') + ' STACK watermark highest seen usage (approximate lower-bound!): ' + this.formatBytes(this.stackTopWatermark - STACK_BASE); - html += '<br />' + colorBar('#70FF70') + 'DYNAMIC memory area size: ' + this.formatBytes(DYNAMICTOP-DYNAMIC_BASE); - html += '. DYNAMIC_BASE: ' + toHex(DYNAMIC_BASE, width); - html += '. DYNAMICTOP: ' + toHex(DYNAMICTOP, width) + '.'; - html += '<br />' + colorBar('#6699CC') + colorBar('#003366') + colorBar('#0000FF') + 'DYNAMIC memory area used: ' + this.formatBytes(this.totalMemoryAllocated) + ' (' + (this.totalMemoryAllocated * 100.0 / (TOTAL_MEMORY - DYNAMIC_BASE)).toFixed(2) + '% of all free memory)'; - var preloadedMemoryUsed = 0; for (i in this.preRunMallocs) preloadedMemoryUsed += this.preRunMallocs[i]|0; html += '<br />' + colorBar('#FF9900') + colorBar('#FFDD33') + 'Preloaded memory used, most likely memory reserved by files in the virtual filesystem : ' + this.formatBytes(preloadedMemoryUsed); html += '<br />OpenAL audio data: ' + this.formatBytes(this.countOpenALAudioDataSize()) + ' (outside HEAP)'; - html += '<br />' + colorBar('#FFFFFF') + 'Unallocated HEAP space: ' + this.formatBytes(TOTAL_MEMORY - DYNAMICTOP); + html += '<br />' + colorBar('#FFFFFF') + 'Unallocated HEAP space: ' + this.formatBytes(TOTAL_MEMORY - STACK_MAX); html += '<br /># of total malloc()s/free()s performed in app lifetime: ' + this.totalTimesMallocCalled + '/' + this.totalTimesFreeCalled + ' (delta: ' + (this.totalTimesMallocCalled-this.totalTimesFreeCalled) + ')'; // Background clear @@ -331,9 +326,6 @@ this.drawContext.fillStyle = "#FF0000"; this.fillLine(STACK_BASE, STACKTOP); - this.drawContext.fillStyle = "#70FF70"; - this.fillLine(DYNAMIC_BASE, DYNAMICTOP); - if (this.detailedHeapUsage) { this.printAllocsWithCyclingColors(["#6699CC", "#003366", "#0000FF"], this.allocatedPtrSizes); this.printAllocsWithCyclingColors(["#FF9900", "#FFDD33"], this.preRunMallocs);
diff --git a/src/preamble.js b/src/preamble.js index fe8bb74..fb7b294 100644 --- a/src/preamble.js +++ b/src/preamble.js
@@ -52,8 +52,7 @@ #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 (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP); - assert(DYNAMICTOP <= TOTAL_MEMORY); + assert(dest + bytes <= TOTAL_MEMORY); setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); } function SAFE_HEAP_STORE_D(dest, value, bytes) { @@ -63,8 +62,7 @@ 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 (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP); - assert(DYNAMICTOP <= TOTAL_MEMORY); + assert(dest + bytes <= TOTAL_MEMORY); var type = getSafeHeapType(bytes, isFloat); var ret = getValue(dest, type, 1); if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); @@ -351,12 +349,10 @@ 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 {{{ maybeExport('ALLOC_NORMAL') }}} {{{ maybeExport('ALLOC_STACK') }}} {{{ maybeExport('ALLOC_STATIC') }}} -{{{ maybeExport('ALLOC_DYNAMIC') }}} {{{ maybeExport('ALLOC_NONE') }}} // allocate(): This is for internal use. You can use it yourself as well, but the interface @@ -388,7 +384,7 @@ if (allocator == ALLOC_NONE) { ret = ptr; } else { - ret = [typeof _malloc === 'function' ? _malloc : Runtime.staticAlloc, Runtime.stackAlloc, Runtime.staticAlloc, Runtime.dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + ret = [typeof _malloc === 'function' ? _malloc : Runtime.staticAlloc, Runtime.stackAlloc, Runtime.staticAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); } if (zeroinit) { @@ -450,7 +446,7 @@ // Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready function getMemory(size) { if (!staticSealed) return Runtime.staticAlloc(size); - if ((typeof _sbrk !== 'undefined' && !_sbrk.called) || !runtimeInitialized) return Runtime.dynamicAlloc(size); + if (!runtimeInitialized) return Runtime.stackAlloc(size); // unfreeable, of course return _malloc(size); } {{{ maybeExport('getMemory') }}} @@ -886,7 +882,6 @@ var STATIC_BASE = 0, STATICTOP = 0, staticSealed = false; // static area var STACK_BASE = 0, STACKTOP = 0, STACK_MAX = 0; // stack area -var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk #if USE_PTHREADS if (ENVIRONMENT_IS_PTHREAD) { @@ -933,9 +928,8 @@ return false; // malloc will report failure #endif #else - // TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top. + // TOTAL_MEMORY is the current size of the actual array. #if ASSERTIONS - assert(DYNAMICTOP >= TOTAL_MEMORY); assert(TOTAL_MEMORY > 4); // So the loop below will not be infinite #endif @@ -948,9 +942,9 @@ var LIMIT = Math.pow(2, 31); // 2GB is a practical maximum, as we use signed ints as pointers // and JS engines seem unhappy to give us 2GB arrays currently - if (DYNAMICTOP >= LIMIT) return false; - while (TOTAL_MEMORY <= DYNAMICTOP) { // Simple heuristic. + var previous = TOTAL_MEMORY; + while (TOTAL_MEMORY <= previous) { // Simple heuristic. if (TOTAL_MEMORY < LIMIT/2) { TOTAL_MEMORY = alignMemoryPage(2*TOTAL_MEMORY); // double until 1GB } else {
diff --git a/src/runtime.js b/src/runtime.js index 7d2c669..31e6203 100644 --- a/src/runtime.js +++ b/src/runtime.js
@@ -49,26 +49,11 @@ // An allocation that cannot normally be free'd (except through sbrk, which once // called, takes control of STATICTOP) staticAlloc: function(size) { - if (ASSERTIONS) size = '(assert(!staticSealed),' + size + ')'; // static area must not be sealed #if USE_PTHREADS if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.staticAlloc is not available in pthreads!'; // This is because each worker has its own copy of STATICTOP, of which main thread is authoritative. #endif - var ret = RuntimeGenerator.alloc(size, 'STATIC'); - return ret; - }, - - // allocation on the top of memory, adjusted dynamically by sbrk - dynamicAlloc: function(size) { - if (ASSERTIONS) size = '(assert(DYNAMICTOP > 0),' + size + ')'; // dynamic area must be ready -#if USE_PTHREADS - if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.dynamicAlloc is not available in pthreads!'; // This is because each worker has its own copy of DYNAMICTOP, of which main thread is authoritative. -#endif - var ret = RuntimeGenerator.alloc(size, 'DYNAMIC'); - if (SAFE_HEAP) ret += '; if (asm) { Runtime.setDynamicTop(DYNAMICTOP); }'; - ret += '; if (DYNAMICTOP >= TOTAL_MEMORY) { var success = enlargeMemory(); if (!success) { DYNAMICTOP = ret; '; - if (SAFE_HEAP) ret += 'if (asm) { Runtime.setDynamicTop(DYNAMICTOP); }'; - ret += ' return 0; } }' - return ret; + // After we seal static memory + return 'STATICTOP; if (staticSealed) return Runtime.stackAlloc(' + size + '); STATICTOP = (STATICTOP + size)|0;STATICTOP = (((STATICTOP)+15)&-16)'; }, forceAlign: function(target, quantum) { @@ -421,7 +406,6 @@ Runtime.stackAlloc = unInline('stackAlloc', ['size']); Runtime.staticAlloc = unInline('staticAlloc', ['size']); -Runtime.dynamicAlloc = unInline('dynamicAlloc', ['size']); Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']); Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned']);
diff --git a/src/shell_sharedlib.js b/src/shell_sharedlib.js index 7e32d9e..27416b3 100644 --- a/src/shell_sharedlib.js +++ b/src/shell_sharedlib.js
@@ -10,6 +10,7 @@ var gb = 0; // Each module has its own stack + // XXX without dynamic allocations, we try to allocate these on the parent's stack. But if our stacks are the same size, that can't work! Need to specify smaller sizes for side module stacks. var STACKTOP = getMemory(TOTAL_STACK); assert(STACKTOP % 8 == 0); var STACK_MAX = STACKTOP + TOTAL_STACK;
diff --git a/system/lib/split_malloc.cpp b/system/lib/split_malloc.cpp index 8a697da..59e2e4d 100644 --- a/system/lib/split_malloc.cpp +++ b/system/lib/split_malloc.cpp
@@ -71,7 +71,7 @@ start = split_memory*index; } else { // small area in existing chunk 0 - start = EM_ASM_INT_V({ return (DYNAMICTOP+3)&-4; }); + start = EM_ASM_INT_V({ return (STACK_MAX+3)&-4; }); assert(start < split_memory); } int size = (split_memory*(index+1)) - start;