aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-12-06 13:27:43 -0800
committerAlon Zakai <alonzakai@gmail.com>2011-12-06 13:27:43 -0800
commitbea3d23dc5316222d31127299ea1896223075ac4 (patch)
treef7eaa5442d162cd3ca2575e6a46c06a0fd7a309c
parent0f3f5cc6934a45c78ce7bfb2bdab15bb8756edf7 (diff)
initial work on memory growth
-rw-r--r--src/library.js4
-rw-r--r--src/preamble.js35
-rw-r--r--src/runtime.js6
-rw-r--r--src/settings.js7
-rw-r--r--system/include/emscripten.h1
-rw-r--r--tests/runner.py45
-rw-r--r--tools/js-optimizer.js5
7 files changed, 94 insertions, 9 deletions
diff --git a/src/library.js b/src/library.js
index 1d296981..a4278b9f 100644
--- a/src/library.js
+++ b/src/library.js
@@ -5493,8 +5493,8 @@ LibraryManager.library = {
eval(Pointer_stringify(ptr));
},
- _Z21emscripten_run_scriptPKc: function(ptr) {
- eval(Pointer_stringify(ptr));
+ emscripten_run_script_int: function(ptr) {
+ return eval(Pointer_stringify(ptr));
},
$Profiling: {
diff --git a/src/preamble.js b/src/preamble.js
index 3fcdf1f8..7fd51733 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -532,6 +532,41 @@ var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32;
var STACK_ROOT, STACKTOP, STACK_MAX;
var STATICTOP;
+#if USE_TYPED_ARRAYS
+var LAST_STATICTOP;
+function enlargeMemory() {
+ // LAST_STATICTOP is the previous top, TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top.
+#if ASSERTIONS
+ assert(STATICTOP >= TOTAL_MEMORY && LAST_STATICTOP < TOTAL_MEMORY);
+ assert(TOTAL_MEMORY > 4); // So the loop below will not be infinite
+#endif
+ while (TOTAL_MEMORY <= STATICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it
+ TOTAL_MEMORY = alignMemoryPage(TOTAL_MEMORY*1.25);
+ }
+#if USE_TYPED_ARRAYS == 1
+ var oldIHEAP = IHEAP;
+ HEAP = IHEAP = new Int32Array(TOTAL_MEMORY);
+ IHEAP.set(oldIHEAP);
+#if USE_FHEAP
+ var oldFHEAP = FHEAP;
+ FHEAP = new Float64Array(TOTAL_MEMORY);
+ FHEAP.set(oldFHEAP);
+#endif
+#endif
+#if USE_TYPED_ARRAYS == 2
+ var oldHEAP8 = HEAP8;
+ var buffer = new ArrayBuffer(TOTAL_MEMORY);
+ 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);
+ HEAP8.set(oldHEAP8);
+#endif
+}
+#endif
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}};
var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
diff --git a/src/runtime.js b/src/runtime.js
index a6261c74..e0eb7404 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -56,7 +56,11 @@ var RuntimeGenerator = {
// An allocation that cannot be free'd
staticAlloc: function(size) {
- return RuntimeGenerator.alloc(size, 'STATIC', INIT_HEAP);
+ var ret = '';
+ if (USE_TYPED_ARRAYS) ret += 'LAST_STATICTOP = STATICTOP;'
+ ret += RuntimeGenerator.alloc(size, 'STATIC', INIT_HEAP);
+ if (USE_TYPED_ARRAYS) ret += 'if (STATICTOP >= TOTAL_MEMORY) enlargeMemory();'
+ return ret;
},
alignMemory: function(target, quantum) {
diff --git a/src/settings.js b/src/settings.js
index 0964d4bb..4dc94f4a 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -33,10 +33,9 @@ var INIT_STACK = 1; // Whether to initialize memory on the stack to 0.
var INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to 0.
var FAST_MEMORY = 2*1024*1024; // The amount of memory to initialize to 0. This ensures it will be
// in a flat array. This only matters in non-typed array builds.
-var TOTAL_MEMORY = 50*1024*1024; // The total amount of memory to use. This mainly matters in
- // typed array builds - accessing memory about this value will
- // return undefined values and lead to serious problems, and there
- // is currently no warning about that!
+var TOTAL_MEMORY = 50*1024*1024; // The total amount of memory to use. Using more memory than this will
+ // cause us to expand the heap, which can be costly with typed arrays:
+ // we need to copy the old heap into a new one in that case.
// Code embetterments
var MICRO_OPTS = 0; // Various micro-optimizations, like nativizing variables
diff --git a/system/include/emscripten.h b/system/include/emscripten.h
index 482f7ef3..7b135c5a 100644
--- a/system/include/emscripten.h
+++ b/system/include/emscripten.h
@@ -16,6 +16,7 @@ extern "C" {
* eval() the given script.
*/
extern void emscripten_run_script(const char *script);
+extern int emscripten_run_script_int(const char *script);
/*
* This macro-looking function will cause Emscripten to
diff --git a/tests/runner.py b/tests/runner.py
index 04913a11..aa1d5922 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -1579,13 +1579,14 @@ if 'benchmark' not in str(sys.argv):
def test_emscripten_api(self):
#if Settings.MICRO_OPTS or Settings.RELOOP or Building.LLVM_OPTS: return self.skip('FIXME')
- src = '''
+ src = r'''
#include <stdio.h>
#include "emscripten.h"
int main() {
// EMSCRIPTEN_COMMENT("hello from the source");
emscripten_run_script("print('hello world' + '!')");
+ printf("*%d*\n", emscripten_run_script_int("5*20"));
return 0;
}
'''
@@ -1594,7 +1595,47 @@ if 'benchmark' not in str(sys.argv):
src = open(filename, 'r').read()
# TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src
- self.do_run(src, 'hello world!', post_build=check)
+ self.do_run(src, 'hello world!\n*100*', post_build=check)
+
+ def test_memorygrowth(self):
+ # With typed arrays in particular, it is dangerous to use more memory than TOTAL_MEMORY,
+ # since we then need to enlarge the heap(s).
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <assert.h>
+ #include "emscripten.h"
+
+ int main()
+ {
+ char *buf1 = (char*)malloc(100);
+ char *data1 = "hello";
+ memcpy(buf1, data1, strlen(data1)+1);
+
+ float *buf2 = (float*)malloc(100);
+ float pie = 4.955;
+ memcpy(buf2, &pie, sizeof(float));
+
+ printf("*pre: %s,%.3f*\n", buf1, buf2[0]);
+
+ int totalMemory = emscripten_run_script_int("TOTAL_MEMORY");
+ char *buf3 = (char*)malloc(totalMemory*2);
+ char *buf4 = (char*)malloc(100);
+ float *buf5 = (float*)malloc(100);
+ //printf("totalMemory: %d bufs: %d,%d,%d,%d,%d\n", totalMemory, buf1, buf2, buf3, buf4, buf5);
+ assert((int)buf4 > (int)totalMemory && (int)buf5 > (int)totalMemory);
+
+ printf("*%s,%.3f*\n", buf1, buf2[0]); // the old heap data should still be there
+
+ memcpy(buf4, buf1, strlen(data1)+1);
+ memcpy(buf5, buf2, sizeof(float));
+ printf("*%s,%.3f*\n", buf4, buf5[0]); // and the new heap space should work too
+
+ return 0;
+ }
+ '''
+ self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*')
def test_ssr(self): # struct self-ref
src = '''
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index e889c7a3..d22de39c 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -193,6 +193,11 @@ function unGlobalize(ast) {
// - and in library/shell code too! - we should never rely on
// undefined being assigned. So we can simply remove those assignments.
//
+// Note: An inlined function that kept a large value referenced, may
+// keep that references when inlined, if we remove the setting to
+// undefined. This is not dangerous in compiled code, but might be
+// in supporting code (for example, holding on to the HEAP when copying).
+//
// This pass assumes that unGlobalize has been run, so undefined
// is now explicit.
function removeAssignsToUndefined(ast) {