diff options
-rw-r--r-- | src/library.js | 3 | ||||
-rw-r--r-- | src/library_gc.js | 115 | ||||
-rw-r--r-- | src/modules.js | 2 | ||||
-rw-r--r-- | src/settings.js | 2 | ||||
-rw-r--r-- | system/include/gc.h | 41 |
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 + |