aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library.js3
-rw-r--r--src/library_gc.js115
-rw-r--r--src/modules.js2
-rw-r--r--src/settings.js2
-rw-r--r--system/include/gc.h41
5 files changed, 162 insertions, 1 deletions
diff --git a/src/library.js b/src/library.js
index 82ea327d..9e56d42e 100644
--- a/src/library.js
+++ b/src/library.js
@@ -2229,6 +2229,9 @@ LibraryManager.library = {
if (!self.called) {
STATICTOP = alignMemoryPage(STATICTOP); // make sure we start out aligned
self.called = true;
+#if GC_SUPPORT
+ _sbrk.DYNAMIC_START = STATICTOP;
+#endif
}
var ret = STATICTOP;
if (bytes != 0) Runtime.staticAlloc(bytes);
diff --git a/src/library_gc.js b/src/library_gc.js
new file mode 100644
index 00000000..3224d742
--- /dev/null
+++ b/src/library_gc.js
@@ -0,0 +1,115 @@
+
+if (GC_SUPPORT) {
+ var LibraryGC = {
+ $GC: {
+ sizes: {},
+ scannables: {}, // iterable
+ finalizers: {},
+ finalizerArgs: {},
+
+ totalAllocations: 0, // bytes of all currently active objects
+ recentAllocations: 0, // bytes allocated since last gc. ignores free()s
+
+ malloc: function(bytes, clear, scannable) {
+ var ptr;
+ if (clear) {
+ ptr = _calloc(bytes);
+ } else {
+ ptr = _malloc(bytes);
+ }
+ if (scannable) {
+ GC.scannables[ptr] = 1;
+ }
+ GC.sizes[ptr] = bytes;
+ GC.totalAllocations += bytes;
+ GC.recentAllocations += bytes;
+ return ptr;
+ },
+
+ free: function(ptr) { // does not check if anything refers to it, this is a forced free
+ if (GC.scannables[ptr]) delete GC.scannables[ptr];
+ var finalizer = GC.finalizers[ptr];
+ if (finalizer) {
+ Runtime.getFuncWrapper(finalizer)(GC.finalizerArgs[ptr]);
+ GC.finalizers[ptr] = 0;
+ }
+ _free(ptr);
+ GC.totalAllocations -= GC.sizes[ptr];
+ },
+
+ registerFinalizer: function(ptr, func, arg, oldFunc, oldArg) {
+ var finalizer = GC.finalizers[ptr];
+ if (finalizer) {
+ if (oldFunc) {
+ {{{ makeSetValue('oldFunc', '0', 'finalizer', 'i32') }}};
+ }
+ if (oldArg) {
+ {{{ makeSetValue('oldArg', '0', 'GC.finalizerArgs[ptr]', 'i32') }}};
+ }
+ }
+ GC.finalizers[ptr] = func;
+ GC.finalizerArgs[ptr] = arg;
+ },
+
+ maybeCollect: function() {
+ if (GC.needCollect()) GC.collect();
+ },
+
+ needCollect: function() {
+ return true; // TODO: heuristics, # of allocations, time, etc.
+ },
+
+ collect: function() {
+ GC.prep();
+ GC.mark();
+ GC.sweep();
+ },
+
+ prep: function() { // Clear reachables and scan for roots
+ GC.reachable = {}; // XXX
+ // static data areas: STACK_MAX to sbrk.DYNAMIC_START (after that, sbrk manages it
+ // for dlmalloc). Note that DYNAMIC_START may not exist yet,
+ // then use STATICTOP.
+ // stack: STACK_ROOT to STACKTOP
+ // registers: call scanners
+ },
+
+ mark: function() { // mark all reachable from roots
+ },
+
+ sweep: function() { // traverse all objects and free all unreachable
+ }
+ },
+
+ GC_INIT__deps: ['$GC'],
+ GC_INIT: function(){},
+
+ GC_MALLOC__deps: ['$GC'],
+ GC_MALLOC: function(bytes) {
+ return GC.malloc(bytes, true, true);
+ },
+
+ GC_MALLOC_ATOMIC__deps: ['$GC'],
+ GC_MALLOC_ATOMIC: function(bytes) {
+ return GC.malloc(bytes, false, false);
+ },
+
+ GC_FREE__deps: ['$GC'],
+ GC_FREE: function(ptr) {
+ GC.free(ptr);
+ },
+
+ GC_REGISTER_FINALIZER_NO_ORDER__deps: ['$GC'],
+ GC_REGISTER_FINALIZER_NO_ORDER: function(ptr, func, arg, old_func, old_arg) {
+ GC.registerFinalizer(ptr, func, arg, old_func, old_arg);
+ },
+
+ GC_MAYBE_COLLECT__deps: ['$GC'],
+ GC_MAYBE_COLLECT: function() {
+ GC.maybeCollect();
+ }
+ };
+
+ mergeInto(LibraryManager.library, LibraryGC);
+}
+
diff --git a/src/modules.js b/src/modules.js
index a6aaa99a..0f3b483b 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -259,7 +259,7 @@ var LibraryManager = {
load: function() {
assert(!this.library);
- var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js'].concat(additionalLibraries);
+ var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js'].concat(additionalLibraries);
for (var i = 0; i < libraries.length; i++) {
eval(processMacros(preprocess(read(libraries[i]))));
}
diff --git a/src/settings.js b/src/settings.js
index 881f7b17..2ca82238 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -216,6 +216,8 @@ var FAKE_X86_FP80 = 1; // Replaces x86_fp80 with double. This loses precision. I
// if you can, to get the original source code to build without x86_fp80
// (which is nonportable anyhow).
+var GC_SUPPORT = 0; // Enables GC, see gc.h
+
// Compiler debugging options
var DEBUG_TAGS_SHOWING = [];
// Some useful items:
diff --git a/system/include/gc.h b/system/include/gc.h
new file mode 100644
index 00000000..a4f7afbb
--- /dev/null
+++ b/system/include/gc.h
@@ -0,0 +1,41 @@
+/*
+ * Boehm-compatible GC API
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __attribute__((used)) __GC_KEEPALIVE__() {
+ // Force inclusion of necessary dlmalloc functions
+ static times = 1;
+ void *x = malloc(times);
+ free(x);
+ x = calloc(times);
+ free(x);
+ times++;
+}
+
+/* Initialize. */
+void GC_INIT();
+
+/* Allocate memory. Cleared to 0 to erase all pointers. */
+void *GC_MALLOC(int bytes);
+
+/* Allocate memory for an object that the user promises will not contain pointers. */
+void *GC_MALLOC_ATOMIC(int bytes);
+
+/* Explicitly deallocate an object. Dangerous as it forces a free and does not check if the object is reffed. */
+void GC_FREE(void *ptr);
+
+/* Register a finalizer. func(ptr, arg) will be called. The old values are saved in old_func, old_arg */
+void GC_REGISTER_FINALIZER_NO_ORDER((void*)ptr, void (*func)(), void *arg,
+ void *(*old_func)(), void *old_arg);
+
+/* Non-Boehm addition. Call this once per frame or such, it will collect if necessary */
+void GC_MAYBE_COLLECT();
+
+#ifdef __cplusplus
+}
+#endif
+