diff options
-rwxr-xr-x | em-config | 24 | ||||
-rw-r--r-- | src/library.js | 39 | ||||
-rw-r--r-- | src/library_gc.js | 165 | ||||
-rw-r--r-- | src/library_gl.js | 19 | ||||
-rw-r--r-- | src/library_sdl.js | 4 | ||||
-rw-r--r-- | src/modules.js | 2 | ||||
-rw-r--r-- | src/settings.js | 2 | ||||
-rw-r--r-- | system/include/gc.h | 50 | ||||
-rw-r--r-- | tests/browser_gc.cpp | 96 | ||||
-rwxr-xr-x | tests/runner.py | 141 | ||||
-rw-r--r-- | tests/sdl_image.c | 26 | ||||
-rw-r--r-- | tests/sdl_ogl_defaultMatrixMode.c | 176 | ||||
-rw-r--r-- | tools/js-optimizer.js | 152 | ||||
-rw-r--r-- | tools/shared.py | 1 | ||||
-rw-r--r-- | tools/test-js-optimizer-regs-output.js | 187 | ||||
-rw-r--r-- | tools/test-js-optimizer-regs.js | 128 |
16 files changed, 1086 insertions, 126 deletions
diff --git a/em-config b/em-config new file mode 100755 index 00000000..dee399ed --- /dev/null +++ b/em-config @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +''' +This is a helper tool which is designed to make it possible +for other apps to read emscripten's configuration variables +in a unified way. Usage: + + em-config VAR_NAME + +This tool prints the value of the variable to stdout if one +is found, or exits with 1 if the variable does not exist. +''' + +import os, sys, re +from tools import shared + +if len(sys.argv) != 2 or \ + not re.match(r"^[\w\W_][\w\W_\d]*$", sys.argv[1]) or \ + not (sys.argv[1] in dir(shared)): + print 'Usage: em-config VAR_NAME' + exit(1) + +print eval('shared.' + sys.argv[1]) + diff --git a/src/library.js b/src/library.js index 82ea327d..2472e701 100644 --- a/src/library.js +++ b/src/library.js @@ -90,18 +90,18 @@ LibraryManager.library = { #if FS_LOG var inputPath = path; function log() { - print('FS.analyzePath("' + inputPath + '", ' + - dontResolveLastLink + ', ' + - linksVisited + ') => {' + - 'isRoot: ' + ret.isRoot + ', ' + - 'exists: ' + ret.exists + ', ' + - 'error: ' + ret.error + ', ' + - 'name: "' + ret.name + '", ' + - 'path: "' + ret.path + '", ' + - 'object: ' + ret.object + ', ' + - 'parentExists: ' + ret.parentExists + ', ' + - 'parentPath: "' + ret.parentPath + '", ' + - 'parentObject: ' + ret.parentObject + '}'); + Module['print']('FS.analyzePath("' + inputPath + '", ' + + dontResolveLastLink + ', ' + + linksVisited + ') => {' + + 'isRoot: ' + ret.isRoot + ', ' + + 'exists: ' + ret.exists + ', ' + + 'error: ' + ret.error + ', ' + + 'name: "' + ret.name + '", ' + + 'path: "' + ret.path + '", ' + + 'object: ' + ret.object + ', ' + + 'parentExists: ' + ret.parentExists + ', ' + + 'parentPath: "' + ret.parentPath + '", ' + + 'parentObject: ' + ret.parentObject + '}'); } #endif path = FS.absolutePath(path); @@ -177,11 +177,11 @@ LibraryManager.library = { // Creates a file system record: file, link, device or folder. createObject: function(parent, name, properties, canRead, canWrite) { #if FS_LOG - print('FS.createObject("' + parent + '", ' + - '"' + name + '", ' + - JSON.stringify(properties) + ', ' + - canRead + ', ' + - canWrite + ')'); + Module['print']('FS.createObject("' + parent + '", ' + + '"' + name + '", ' + + JSON.stringify(properties) + ', ' + + canRead + ', ' + + canWrite + ')'); #endif if (!parent) parent = '/'; if (typeof parent === 'string') parent = FS.findObject(parent); @@ -349,7 +349,7 @@ LibraryManager.library = { typeof window.prompt == 'function') { // Browser. result = window.prompt('Input: '); - if (result === null) result = '\x00'; // cancel ==> EOF + if (result === null) result = String.fromCharCode(0); // cancel ==> EOF } else if (typeof readline == 'function') { // Command line. result = readline(); @@ -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..ccf6656d --- /dev/null +++ b/src/library_gc.js @@ -0,0 +1,165 @@ + +if (GC_SUPPORT) { + var LibraryGC = { + $GC__deps: ['sbrk'], + $GC: { + ALLOCATIONS_TO_GC: 1*1024*1024, + + sizes: {}, // if in this map, then a live allocated object. this is iterable + scannables: {}, + finalizers: {}, + finalizerArgs: {}, + + totalAllocations: 0, // bytes of all currently active objects + recentAllocations: 0, // bytes allocated since last gc. ignores free()s + + init: function() { + assert(!GC.initted); + GC.initted = true; +#if GENERATING_HTML + setInterval(function() { + GC.maybeCollect(); + }, 1000); +#else + // No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually +#endif + }, + + malloc: function(bytes, clear, scannable) { + if (!bytes) return 0; + var ptr; + if (clear) { + ptr = _calloc(1, bytes); + } else { + ptr = _malloc(bytes); + } + GC.scannables[ptr] = scannable; + 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 + var finalizer = GC.finalizers[ptr]; + if (finalizer) { + Runtime.getFuncWrapper(finalizer)(ptr, GC.finalizerArgs[ptr]); + GC.finalizers[ptr] = 0; + } + _free(ptr); + delete GC.sizes[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 GC.recentAllocations >= GC.ALLOCATIONS_TO_GC; // TODO: time, etc. + }, + + collect: function() { + GC.prep(); + GC.mark(); + GC.sweep(); + GC.recentAllocations = 0; + }, + + scan: function(start, end) { // scans a memory region and adds new reachable objects + for (var i = start; i < end; i += {{{ Runtime.getNativeTypeSize('void*') }}}) { + var ptr = {{{ makeGetValue('i', '0', 'void*') }}}; + if (GC.sizes[ptr] && !GC.reachable[ptr]) { + GC.reachable[ptr] = 1; + if (GC.scannables[ptr]) { + GC.reachableList.push(ptr); + } + } + } + }, + + prep: function() { // Clear reachables and scan for roots + GC.reachable = {}; // 1 if reachable. XXX + GC.reachableList = []; // each reachable is added once to this. XXX + // static data areas + var staticStart = STACK_MAX; + var staticEnd = _sbrk.DYNAMIC_START || STATICTOP; // after DYNAMIC_START, sbrk manages it (but it might not exist yet) + GC.scan(staticStart, staticEnd); + // TODO: scan stack and registers. Currently we assume we run from a timeout or such, so no stack/regs + // stack: STACK_ROOT to STACKTOP + // registers: call scanners + }, + + mark: function() { // mark all reachable from roots + for (var i = 0; i < GC.reachableList.length; i++) { // note that the list length changes as we push more + var ptr = GC.reachableList[i]; + GC.scan(ptr, ptr + GC.sizes[ptr]); + } + }, + + sweep: function() { // traverse all objects and free all unreachable + var freeList = []; + for (var ptr in GC.sizes) { + if (!GC.reachable[ptr]) { + freeList.push(parseInt(ptr)); + } + } + for (var i = 0; i < freeList.length; i++) { + GC.free(freeList[i]); + } + } + }, + + GC_INIT__deps: ['$GC'], + GC_INIT: function() { + GC.init(); + }, + + 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(); + }, + + GC_FORCE_COLLECT__deps: ['$GC'], + GC_FORCE_COLLECT: function() { + GC.collect(); + } + }; + + mergeInto(LibraryManager.library, LibraryGC); +} + diff --git a/src/library_gl.js b/src/library_gl.js index 8867a830..b5d65ee6 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1116,6 +1116,10 @@ var LibraryGL = { HEAPF32.set(GL.immediate.matrix['t' + GL.immediate.clientActiveTexture], params >> 2); } else if (pname == 0x0B66) { // GL_FOG_COLOR {{{ makeSetValue('params', '0', '0', 'float') }}}; + } else if (pname == 0x0B63) { // GL_FOG_START + {{{ makeSetValue('params', '0', '0', 'float') }}}; + } else if (pname == 0x0B64) { // GL_FOG_END + {{{ makeSetValue('params', '0', '0', 'float') }}}; } else { glGetFloatv(pname, params); } @@ -1513,13 +1517,6 @@ var LibraryGL = { Module.printErr('WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports'); GL.immediate.initted = true; - // No JSON notation for these objects, for closure w/js optimizer - this.matrix['m'] = null; // modelview - this.matrix['p'] = null; // projection - for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { - this.matrix['t' + i] = null; // texture - } - this.matrixStack['m'] = []; this.matrixStack['p'] = []; for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { @@ -1544,7 +1541,6 @@ var LibraryGL = { for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { GL.immediate.matrix['t' + i] = GL.immediate.matrix.lib.mat4.create(); } - GL.immediate.currentMatrix = GL.immediate.matrix.lib.mat4.create(); // Buffers for data this.tempData = new Float32Array(this.maxElements); @@ -1835,6 +1831,8 @@ var LibraryGL = { glPolygonMode: function(){}, // TODO + glAlphaFunc: function(){}, // TODO + // ClientState/gl*Pointer glEnableClientState: function(cap, disable) { @@ -1964,6 +1962,7 @@ var LibraryGL = { GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.ortho(left, right, bottom, top_, nearVal, farVal)); }, + glOrthof: 'glOrtho', glScaled: function(x, y, z) { GL.immediate.matrix.lib.mat4.scale(GL.immediate.matrix[GL.immediate.currentMatrix], [x, y, z]); @@ -2042,8 +2041,8 @@ var LibraryGL = { // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name [[0, 'shadeModel fogi fogfv getError* finish flush'], - [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation polygonOffset hint sampleCoverage isEnabled*'], - [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate'], + [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation polygonOffset sampleCoverage isEnabled*'], + [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint'], [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'], [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], [5, 'vertexAttrib4f'], diff --git a/src/library_sdl.js b/src/library_sdl.js index d6e205ee..472228c1 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -756,6 +756,10 @@ var LibrarySDL = { IMG_Load__deps: ['SDL_LockSurface'], IMG_Load: function(filename) { filename = FS.standardizePath(Pointer_stringify(filename)); + if (filename[0] == '/') { + // Convert the path to relative + filename = filename.substr(1); + } var raw = preloadedImages[filename]; if (!raw) { Runtime.warnOnce('Cannot find preloaded image ' + filename); 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..1910e943 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 = 1; // Enables GC, see gc.h (this does not add overhead, so it is on by default) + // 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..996bc9ec --- /dev/null +++ b/system/include/gc.h @@ -0,0 +1,50 @@ +/* + * Boehm-compatible GC API + */ + +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void __attribute__((used)) __GC_KEEPALIVE__() { + // Force inclusion of necessary dlmalloc functions + static int times = 1; + void *x = malloc(times); + free(x); + x = calloc(1, times); + free(x); + x = calloc(times, 1); + 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 *, void *), void *arg, + void *(*old_func)(void *, void *), void *old_arg); + +/* Non-Boehm additions */ + +/* Call this once per frame or such, it will collect if necessary */ +void GC_MAYBE_COLLECT(); + +/* Forces a GC. Mainly useful for testing, but call it if you know a good time to GC in your app. */ +void GC_FORCE_COLLECT(); + +#ifdef __cplusplus +} +#endif + diff --git a/tests/browser_gc.cpp b/tests/browser_gc.cpp new file mode 100644 index 00000000..75dea10a --- /dev/null +++ b/tests/browser_gc.cpp @@ -0,0 +1,96 @@ +#include <stdio.h> +#include <gc.h> +#include <assert.h> +#include <emscripten.h> + +void *global; + +int freed = 0; + +void finalizer(void *ptr, void *arg) { + printf("finalizing %d (global == %d)\n", (int)arg, ptr == global); + freed++; + if (ptr == global) global = 0; +} + +int stage = 0; +float start = 0; + +void waiter() { + if (stage == 0) { // wait for a while, see no GCing + assert(global); + if (emscripten_get_now() - start > 2100) { + GC_MALLOC(1024*1024*2); // allocate enough to trigger a GC + start = emscripten_get_now(); + stage = 1; + printf("stage 1\n"); + } + } else if (stage == 1) { + assert(global); + if (freed > 0) { + GC_FREE(global); + stage = 2; + start = emscripten_get_now(); + printf("stage 2\n"); + } + if (emscripten_get_now() - start > 2100) { + printf("fail, too much time passed (a)\n"); + return; + } + } else if (stage == 2) { + if (emscripten_get_now() - start > 2100) { // wait and see that no gc'ing happens yet + GC_MALLOC(1024*1024*2); // allocate enough to trigger a GC + stage = 3; + start = emscripten_get_now(); + printf("stage 3\n"); + } + } else if (stage == 3) { + assert(!global); + if (freed == 5) { + printf("Ok.\n"); + int result = 1; + REPORT_RESULT(); + return; + } + if (emscripten_get_now() - start > 2100) { + printf("fail, too much time passed (b)\n"); + return; + } + } + + emscripten_async_call(waiter, 100); +} + +int main() { + start = emscripten_get_now(); + + GC_INIT(); + + void *local, *local2, *local3, *local4; + + global = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); + local = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); + local2 = GC_MALLOC_ATOMIC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local2, finalizer, (void*)2, 0, 0); + local3 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer, (void*)3, 0, 0); + local4 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer, (void*)4, 0, 0); + + void **globalData = (void**)global; + globalData[0] = local; + globalData[1] = local2; + + void **localData = (void**)local; + localData[0] = local3; + + void **local2Data = (void**)local2; + local2Data[0] = local4; // actually ignored, because local2 is atomic, so 4 is freeable + + emscripten_async_call(waiter, 100); + + return 0; +} + diff --git a/tests/runner.py b/tests/runner.py index 126be5aa..15a851c6 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -6094,6 +6094,120 @@ def process(filename): ''' self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118') + def test_gc(self): + if self.emcc_args == None: return self.skip('needs ta2') + + Settings.GC_SUPPORT = 1 + + src = r''' + #include <stdio.h> + #include <gc.h> + #include <assert.h> + + void *global; + + void finalizer(void *ptr, void *arg) { + printf("finalizing %d (global == %d)\n", (int)arg, ptr == global); + } + + void finalizer2(void *ptr, void *arg) { + printf("finalizing2 %d (global == %d)\n", (int)arg, ptr == global); + } + + int main() { + GC_INIT(); + + void *local, *local2, *local3, *local4; + + // Hold on to global, drop locals + + global = GC_MALLOC(1024); // rooted since in a static allocation + GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); + printf("alloc %p\n", global); + + local = GC_MALLOC(1024); // not rooted since stack is not scanned + GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); + printf("alloc %p\n", local); + + assert((char*)local - (char*)global >= 1024 || (char*)global - (char*)local >= 1024); + + local2 = GC_MALLOC(1024); // no finalizer + printf("alloc %p\n", local2); + + local3 = GC_MALLOC(1024); // with finalizable2 + GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer2, (void*)2, 0, 0); + printf("alloc %p\n", local); + + local4 = GC_MALLOC(1024); // yet another + GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer2, (void*)3, 0, 0); + printf("alloc %p\n", local); + + printf("basic test\n"); + + GC_FORCE_COLLECT(); + + printf("*\n"); + + GC_FREE(global); // force free will actually work + + // scanning inside objects + + global = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); + local = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); + local2 = GC_MALLOC_ATOMIC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local2, finalizer, (void*)2, 0, 0); + local3 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer, (void*)3, 0, 0); + local4 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer, (void*)4, 0, 0); + + void **globalData = (void**)global; + globalData[0] = local; + globalData[1] = local2; + + void **localData = (void**)local; + localData[0] = local3; + + void **local2Data = (void**)local2; + local2Data[0] = local4; // actually ignored, because local2 is atomic, so 4 is freeable + + printf("object scan test test\n"); + + GC_FORCE_COLLECT(); + + printf("*\n"); + + GC_FREE(global); // force free will actually work + + printf("*\n"); + + GC_FORCE_COLLECT(); + + printf(".\n"); + + global = 0; + + return 0; + } + ''' + self.do_run(src, '''basic test +finalizing 1 (global == 0) +finalizing2 2 (global == 0) +finalizing2 3 (global == 0) +* +finalizing 0 (global == 1) +object scan test test +finalizing 4 (global == 0) +* +finalizing 0 (global == 1) +* +finalizing 1 (global == 0) +finalizing 2 (global == 0) +finalizing 3 (global == 0) +. +''') # Generate tests for everything def make_run(fullname, name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None): @@ -6764,6 +6878,23 @@ fscanfed: 10 - hello ''', output[0]) self.assertIdentical('texte\n', output[1]) + def test_emconfig(self): + output = Popen(['python', EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0] + assert output == LLVM_ROOT + "\n" + invalid = 'Usage: em-config VAR_NAME\n' + # Don't accept variables that do not exist + output = Popen(['python', EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE).communicate()[0] + assert output == invalid + # Don't accept no arguments + output = Popen(['python', EMCONFIG], stdout=PIPE, stderr=PIPE).communicate()[0] + assert output == invalid + # Don't accept more than one variable + output = Popen(['python', EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE).communicate()[0] + assert output == invalid + # Don't accept arbitrary python code + output = Popen(['python', EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE).communicate()[0] + assert output == invalid + elif 'browser' in str(sys.argv): # Browser tests. @@ -7199,6 +7330,13 @@ elif 'browser' in str(sys.argv): Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') + def test_sdl_ogl_defaultmatrixmode(self): + # SDL, OpenGL, textures, immediate mode. Closure for more coverage + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.reftest(path_from_root('tests', 'screenshot-gray-purple.png')) + Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') + def test_sdl_ogl_p(self): # Immediate mode with pointers shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) @@ -7300,6 +7438,9 @@ elif 'browser' in str(sys.argv): def test_emscripten_api(self): self.btest('emscripten_api_browser.cpp', '1') + def test_gc(self): + self.btest('browser_gc.cpp', '1') + def test_sdlglshader(self): self.btest('sdlglshader.c', reference='sdlglshader.png', args=['--closure', '1']) diff --git a/tests/sdl_image.c b/tests/sdl_image.c index d934f863..9d8c36f2 100644 --- a/tests/sdl_image.c +++ b/tests/sdl_image.c @@ -4,30 +4,40 @@ #include <assert.h> #include <emscripten.h> -int main() { - SDL_Init(SDL_INIT_VIDEO); - SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_SWSURFACE); - - SDL_Surface *image = IMG_Load("screenshot.jpg"); +int testImage(SDL_Surface* screen, const char* fileName) { + SDL_Surface *image = IMG_Load(fileName); if (!image) { printf("IMG_Load: %s\n", IMG_GetError()); - return 1; + return 0; } assert(image->format->BitsPerPixel == 32); assert(image->format->BytesPerPixel == 4); assert(image->pitch == 4*image->w); + int result = image->w; SDL_BlitSurface (image, NULL, screen, NULL); SDL_FreeSurface (image); - SDL_Flip(screen); + return result; +} + +int main() { + SDL_Init(SDL_INIT_VIDEO); + SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_SWSURFACE); + + int result = 0; + result = testImage(screen, "screenshot.jpg"); // relative path + assert(result != 0); + result |= testImage(screen, "/screenshot.jpg"); // absolute path + assert(result != 0); + + SDL_Flip(screen); printf("you should see an image.\n"); SDL_Quit(); - int result = image->w; REPORT_RESULT(); return 0; diff --git a/tests/sdl_ogl_defaultMatrixMode.c b/tests/sdl_ogl_defaultMatrixMode.c new file mode 100644 index 00000000..0da0a326 --- /dev/null +++ b/tests/sdl_ogl_defaultMatrixMode.c @@ -0,0 +1,176 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + // GL_MODELVIEW should be the default... + //glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1, 1 ); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 0 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 0 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 0 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 0 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 0 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 0 ); + glEnd(); + +#if !EMSCRIPTEN + glDisable(GL_TEXTURE_2D); +#endif + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 0 ); + glVertex3f( 300, 410, 0 ); + glVertex3f( 300, 480, 0 ); + glVertex3f( 10, 470, 0 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 0 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 0 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 0 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 0 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index d438094b..eda85799 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -119,6 +119,8 @@ load('utility.js'); var FUNCTION = set('defun', 'function'); var LOOP = set('do', 'while', 'for'); var LOOP_FLOW = set('break', 'continue'); +var ASSIGN_OR_ALTER = set('assign', 'unary-postfix', 'unary-prefix'); +var CONTROL_FLOW = set('do', 'while', 'for', 'if', 'switch'); var NULL_NODE = ['name', 'null']; var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]]; @@ -1141,48 +1143,106 @@ function loopOptimizer(ast) { } // Very simple 'registerization', coalescing of variables into a smaller number. -function registerize(ast, conservative) { +function registerize(ast) { traverseGeneratedFunctions(ast, function(fun) { - // Find the # of uses of each variable. The definition is considered a 'use' - // First, find all the var definitions - var varUses = {}; + // Replace all var definitions with assignments; we will add var definitions at the top after we registerize + // We also mark local variables - i.e., having a var definition + var localVars = {}; traverse(fun, function(node, type) { if (type == 'var') { - node[1].forEach(function(arg) { - var name = arg[0]; - if (!varUses[name]) { // may have multiple var definitions - varUses[name] = 0; + node[1].forEach(function(defined) { localVars[defined[0]] = 1 }); + var vars = node[1].filter(function(varr) { return varr[1] }); + if (vars.length > 1) { + var ret = ['stat', []]; + var curr = ret[1]; + for (var i = 0; i < vars.length-1; i++) { + curr[0] = 'seq'; + curr[1] = ['assign', true, ['name', vars[i][0]], vars[i][1]]; + if (i != vars.length-2) curr = curr[2] = []; } - varUses[name]++; - }); + curr[2] = ['assign', true, ['name', vars[vars.length-1][0]], vars[vars.length-1][1]]; + return ret; + } else if (vars.length == 1) { + return ['stat', ['assign', true, ['name', vars[0][0]], vars[0][1]]]; + } else { + return emptyNode(); + } } }); - // Find uses of those variables + vacuum(fun); + // Find the # of uses of each variable. + // While doing so, check if all a variable's uses are dominated in a simple + // way by a simple assign, if so, then we can assign its register to it + // just for its definition to its last use, and not to the entire toplevel loop, + // we call such variables "optimizable" + var varUses = {}; + var level = 1; + var levelDominations = {}; + var varLevels = {}; + var possibles = {}; + var unoptimizables = {}; traverse(fun, function(node, type) { if (type == 'name') { var name = node[1]; - if (varUses[name]) { + if (localVars[name]) { + if (!varUses[name]) varUses[name] = 0; varUses[name]++; + if (possibles[name] && !varLevels[name]) unoptimizables[name] = 1; // used outside of simple domination } + } else if (type == 'assign' && typeof node[1] != 'string') { + if (node[2] && node[2][0] == 'name') { + var name = node[2][1]; + // if local and not yet used, this might be optimizable if we dominate + // all other uses + if (localVars[name] && !varUses[name] && !varLevels[name]) { + possibles[name] = 1; + varLevels[name] = level; + if (!levelDominations[level]) levelDominations[level] = {}; + levelDominations[level][name] = 1; + } + } + } else if (type in CONTROL_FLOW) { + level++; + } + }, function(node, type) { + if (type in CONTROL_FLOW) { + // Invalidate all dominating on this level, further users make it unoptimizable + for (var name in levelDominations[level]) { + varLevels[name] = 0; + } + levelDominations[level] = null; + level--; } }); + var optimizables = {}; + for (var possible in possibles) { + if (!unoptimizables[possible]) optimizables[possible] = 1; + } // Go through the function's code, assigning 'registers'. // The only tricky bit is to keep variables locked on a register through loops, - // since they can potentially be returned to. We use a simple approach of - // locking the register to the topmost loop. + // since they can potentially be returned to. Optimizable variables lock onto + // loops that they enter, unoptimizable variables lock in a conservative way + // into the topmost loop. + // Note that we cannot lock onto a variable in a loop if it was used and free'd + // before! (then they could overwrite us in the early part of the loop). For now + // we just use a fresh register to make sure we avoid this, but it could be + // optimized to check for safe registers (free, and not used in this loop level). var varRegs = {}; // maps variables to the register they will use all their life var freeRegs = []; var nextReg = 1; var fullNames = {}; - var loopRegs = []; - var loops = 0; + var loopRegs = {}; // for each loop nesting level, the list of bound variables + var loops = 0; // 0 is toplevel, 1 is first loop, etc var saved = 0; + var activeOptimizables = {}; + var optimizableLoops = {}; function decUse(name) { if (!varUses[name]) return false; // no uses left, or not a relevant variable + if (optimizables[name]) activeOptimizables[name] = 1; var reg = varRegs[name]; if (!reg) { // acquire register - if (freeRegs.length > 0) { + if (optimizables[name] && freeRegs.length > 0) { reg = freeRegs.pop(); saved++; } else { @@ -1194,62 +1254,46 @@ function registerize(ast, conservative) { varUses[name]--; assert(varUses[name] >= 0); if (varUses[name] == 0) { - if (loops == 0) { + if (optimizables[name]) delete activeOptimizables[name]; + // If we are not in a loop, or we are optimizable and not bound to a loop + // (we might have been in one but left it), we can free the register now. + if (loops == 0 || (optimizables[name] && !optimizableLoops[name])) { // free register freeRegs.push(reg); } else { - loopRegs.push(reg); + // when the relevant loop is exited, we will free the register + var releventLoop = optimizables[name] ? (optimizableLoops[name] || 1) : 1; + if (!loopRegs[releventLoop]) loopRegs[releventLoop] = []; + loopRegs[releventLoop].push(reg); } } return true; } traverse(fun, function(node, type) { // XXX we rely on traversal order being the same as execution order here - if (type == 'var') { - node[1].forEach(function(arg) { - var name = arg[0]; - if (decUse(name)) { - arg[0] = fullNames[varRegs[name]]; - } - }); - } else if (type == 'name') { + if (type == 'name') { var name = node[1]; if (decUse(name)) { node[1] = fullNames[varRegs[name]]; } } else if (type in LOOP) { loops++; + // Active optimizables lock onto this loop, if not locked onto one that encloses this one + for (var name in activeOptimizables) { + if (!optimizableLoops[name]) { + optimizableLoops[name] = loops; + } + } } }, function(node, type) { if (type in LOOP) { - loops--; - if (loops == 0 && loopRegs.length > 0) { - freeRegs = freeRegs.concat(loopRegs); - loopRegs = []; - } - } - }); - // Remove all vars - traverse(fun, function(node, type) { - if (type == 'var') { - var vars = node[1].filter(function(varr) { return varr[1] }); - if (vars.length > 1) { - var ret = ['stat', []]; - var curr = ret[1]; - for (var i = 0; i < vars.length-1; i++) { - curr[0] = 'seq'; - curr[1] = ['assign', true, ['name', vars[i][0]], vars[i][1]]; - if (i != vars.length-2) curr = curr[2] = []; - } - curr[2] = ['assign', true, ['name', vars[vars.length-1][0]], vars[vars.length-1][1]]; - return ret; - } else if (vars.length == 1) { - return ['assign', true, ['name', vars[0][0]], vars[0][1]]; - } else { - return emptyNode(); + // Free registers that were locked to this loop + if (loopRegs[loops]) { + freeRegs = freeRegs.concat(loopRegs[loops]); + loopRegs[loops] = []; } + loops--; } }); - vacuum(fun); // Add vars at the beginning if (nextReg > 1) { var vars = []; @@ -1258,7 +1302,7 @@ function registerize(ast, conservative) { } getStatements(fun).unshift(['var', vars]); } - printErr(fun[1] + ': saved ' + saved + ' vars through registerization'); + printErr(fun[1] + ': saved ' + saved + ' / ' + (saved + nextReg - 1) + ' vars through registerization'); // not totally accurate }); } diff --git a/tools/shared.py b/tools/shared.py index c864f623..b8dc8221 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -125,6 +125,7 @@ EMAR = path_from_root('emar') EMLD = path_from_root('emld') EMRANLIB = path_from_root('emranlib') EMLIBTOOL = path_from_root('emlibtool') +EMCONFIG = path_from_root('em-config') EMMAKEN = path_from_root('tools', 'emmaken.py') AUTODEBUGGER = path_from_root('tools', 'autodebugger.py') BINDINGS_GENERATOR = path_from_root('tools', 'bindings_generator.py') diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js index f3322e11..16d675b8 100644 --- a/tools/test-js-optimizer-regs-output.js +++ b/tools/test-js-optimizer-regs-output.js @@ -1,8 +1,8 @@ function test() { - var r1, r2, r3; + var r1, r2; r1 = 0; f(r1); - r1++; + r1 += 1; r2 = r1 + 2; g(r1, r2); f(r1); @@ -18,62 +18,183 @@ function test() { r2 = 5; r1 = 12; gg(r2, r1 * 2); - r3 = 100; - gg(r3, 20); + r1 = 100; + gg(r1, 20); } - r3 = f(), r1 = 100, r1 = 1e3, r1 = 1e5; - f(r3()); + r1 = f(), r2 = 100, r2 = 1e3, r2 = 1e5; + f(r1()); } function primes() { - var r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14; + var r1, r2, r3, r4, r5, r6, r7; r1 = 2; r2 = 0; $_$2 : while (1) { r3 = r1 | 0; r4 = _sqrtf(r3); - r5 = 2; + r3 = 2; $_$4 : while (1) { - r6 = r5 | 0; - r7 = r6 < r4; - if (!r7) { - r8 = 1; + r5 = r3 | 0; + r6 = r5 < r4; + if (!r6) { + r7 = 1; break $_$4; } - r9 = (r1 | 0) % (r5 | 0); - r10 = (r9 | 0) == 0; - if (r10) { - r8 = 0; + r6 = (r1 | 0) % (r3 | 0); + r5 = (r6 | 0) == 0; + if (r5) { + r7 = 0; break $_$4; } - r11 = r5 + 1 | 0; - r5 = r11; + r5 = r3 + 1 | 0; + r3 = r5; } - r12 = r8 + r2 | 0; - r13 = r1 + 1 | 0; - r14 = (r12 | 0) < 1e5; - if (r14) { - r1 = r13; - r2 = r12; + r3 = r7 + r2 | 0; + r4 = r1 + 1 | 0; + r5 = (r3 | 0) < 1e5; + if (r5) { + r1 = r4; + r2 = r3; } else { break $_$2; } } - r12 = _printf(STRING_TABLE.__str | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r1, tempInt)); + r2 = _printf(STRING_TABLE.__str | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r1, tempInt)); return 1; return null; } function atomic() { var r1, r2, r3, r4; - r2 = STACKTOP; + r1 = STACKTOP; STACKTOP += 4; - r1 = r2 >> 2; - HEAP32[r1] = 10; - r3 = (tempValue = HEAP32[r1], HEAP32[r1] == 10 && (HEAP32[r1] = 7), tempValue); + r2 = r1 >> 2; + HEAP32[r2] = 10; + r3 = (tempValue = HEAP32[r2], HEAP32[r2] == 10 && (HEAP32[r2] = 7), tempValue); r4 = (r3 | 0) == 10 & 1; - r3 = HEAP32[r1]; - r1 = _printf(STRING_TABLE.__str | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = r3, HEAP32[tempInt + 4 >> 2] = r4, tempInt)); - STACKTOP = r2; + r3 = HEAP32[r2]; + r2 = _printf(STRING_TABLE.__str | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = r3, HEAP32[tempInt + 4 >> 2] = r4, tempInt)); + STACKTOP = r1; return 0; return null; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic"] +function fcntl_open() { + var r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17; + r1 = STACKTOP; + STACKTOP += 84; + r2 = r1; + r3 = r1 + 72; + r4 = r3 | 0; + for (r5 = STRING_TABLE.__ZZ4mainE16nonexistent_name | 0, r6 = r4, r7 = r5 + 12; r5 < r7; r5++, r6++) { + HEAP8[r6] = HEAP8[r5]; + } + r5 = (r2 + 8 | 0) >> 2; + r8 = r2 >> 2; + r9 = r3 + 9 | 0; + r10 = r3 + 10 | 0; + r3 = 0; + while (1) { + r11 = HEAP32[__ZZ4mainE5modes + (r3 << 2) >> 2]; + r12 = r11 | 512; + r13 = r3 + 97 & 255; + r14 = 0; + while (1) { + r15 = (r14 & 1 | 0) == 0 ? r11 : r12; + r16 = (r14 & 2 | 0) == 0 ? r15 : r15 | 2048; + r15 = (r14 & 4 | 0) == 0 ? r16 : r16 | 1024; + r16 = (r14 & 8 | 0) == 0 ? r15 : r15 | 8; + r15 = _printf(STRING_TABLE.__str | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = r3, HEAP32[tempInt + 4 >> 2] = r14, tempInt)); + r15 = _open(STRING_TABLE.__str2 | 0, r16, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = 511, tempInt)); + r17 = (r15 | 0) != -1 & 1; + r15 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r17, tempInt)); + r17 = ___errno(); + r15 = HEAP32[r17 >> 2]; + r17 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r15, tempInt)); + r15 = _stat(STRING_TABLE.__str2 | 0, r2); + r15 = HEAP32[r5] & -512; + r17 = _printf(STRING_TABLE.__str4 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r15, tempInt)); + for (r6 = r8, r7 = r6 + 18; r6 < r7; r6++) { + HEAP32[r6] = 0; + } + r15 = _putchar(10); + r15 = ___errno(); + HEAP32[r15 >> 2] = 0; + r15 = _printf(STRING_TABLE.__str6 | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = r3, HEAP32[tempInt + 4 >> 2] = r14, tempInt)); + r15 = _open(STRING_TABLE.__str7 | 0, r16, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = 511, tempInt)); + r17 = (r15 | 0) != -1 & 1; + r15 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r17, tempInt)); + r17 = ___errno(); + r15 = HEAP32[r17 >> 2]; + r17 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r15, tempInt)); + r15 = _stat(STRING_TABLE.__str7 | 0, r2); + r15 = HEAP32[r5] & -512; + r17 = _printf(STRING_TABLE.__str4 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r15, tempInt)); + for (r6 = r8, r7 = r6 + 18; r6 < r7; r6++) { + HEAP32[r6] = 0; + } + r15 = _putchar(10); + r15 = ___errno(); + HEAP32[r15 >> 2] = 0; + HEAP8[r9] = r13; + HEAP8[r10] = r14 + 97 & 255; + r15 = _printf(STRING_TABLE.__str8 | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = r3, HEAP32[tempInt + 4 >> 2] = r14, tempInt)); + r15 = _open(r4, r16, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = 511, tempInt)); + r17 = (r15 | 0) != -1 & 1; + r15 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r17, tempInt)); + r17 = ___errno(); + r15 = HEAP32[r17 >> 2]; + r17 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r15, tempInt)); + r15 = _stat(r4, r2); + r15 = HEAP32[r5] & -512; + r17 = _printf(STRING_TABLE.__str4 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r15, tempInt)); + for (r6 = r8, r7 = r6 + 18; r6 < r7; r6++) { + HEAP32[r6] = 0; + } + r16 = _putchar(10); + r16 = ___errno(); + HEAP32[r16 >> 2] = 0; + r16 = r14 + 1 | 0; + if ((r16 | 0) == 16) { + break; + } + r14 = r16; + } + r14 = r3 + 1 | 0; + if ((r14 | 0) == 3) { + break; + } + r3 = r14; + } + r3 = _puts(STRING_TABLE._str | 0); + r3 = _creat(STRING_TABLE.__str10 | 0, 511); + r6 = (r3 | 0) != -1 & 1; + r3 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r6, tempInt)); + r6 = ___errno(); + r3 = HEAP32[r6 >> 2]; + r6 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r3, tempInt)); + STACKTOP = r1; + return 0; + return null; +} +function ex() { + var r1, r2; + r1 = STACKTOP; + STACKTOP += 4; + r2 = r1; + r1 = _puts(STRING_TABLE._str17 | 0); + r1 = r2 | 0; + r2 = 0; + while (1) { + r1 = _printf(STRING_TABLE.__str15 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = r2, tempInt)); + ((function() { + try { + __THREW__ = false; + return __Z5magici(r2); + } catch (e) { + if (typeof e != "number") throw e; + if (ABORT) throw e; + __THREW__ = true; + return null; + } + }))(); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex"] diff --git a/tools/test-js-optimizer-regs.js b/tools/test-js-optimizer-regs.js index d54ce508..576e2f8c 100644 --- a/tools/test-js-optimizer-regs.js +++ b/tools/test-js-optimizer-regs.js @@ -1,7 +1,7 @@ function test() { var i = 0; f(i); - i++; + i+=1; var j = i + 2; g(i, j); f(i); @@ -79,4 +79,128 @@ function atomic() { return 0; return null; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic"] +function fcntl_open() { + var $1$s2; + var $st_mode$s2; + var __stackBase__ = STACKTOP; + STACKTOP += 84; + var $s = __stackBase__; + var $nonexistent_name = __stackBase__ + 72; + var $0 = $nonexistent_name | 0; + for (var $$src = STRING_TABLE.__ZZ4mainE16nonexistent_name | 0, $$dest = $0, $$stop = $$src + 12; $$src < $$stop; $$src++, $$dest++) { + HEAP8[$$dest] = HEAP8[$$src]; + } + var $st_mode$s2 = ($s + 8 | 0) >> 2; + var $1$s2 = $s >> 2; // critical variable, becomes r8 + var $arrayidx43 = $nonexistent_name + 9 | 0; + var $arrayidx46 = $nonexistent_name + 10 | 0; + var $i_04 = 0; + while (1) { + var $i_04; + var $2 = HEAP32[__ZZ4mainE5modes + ($i_04 << 2) >> 2]; + var $or = $2 | 512; + var $conv42 = $i_04 + 97 & 255; + var $j_03 = 0; + while (1) { + var $j_03; + var $flags_0 = ($j_03 & 1 | 0) == 0 ? $2 : $or; + var $flags_0_or7 = ($j_03 & 2 | 0) == 0 ? $flags_0 : $flags_0 | 2048; + var $flags_2 = ($j_03 & 4 | 0) == 0 ? $flags_0_or7 : $flags_0_or7 | 1024; + var $flags_2_or17 = ($j_03 & 8 | 0) == 0 ? $flags_2 : $flags_2 | 8; + var $call = _printf(STRING_TABLE.__str | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = $i_04, HEAP32[tempInt + 4 >> 2] = $j_03, tempInt)); + var $call19 = _open(STRING_TABLE.__str2 | 0, $flags_2_or17, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = 511, tempInt)); + var $conv = ($call19 | 0) != -1 & 1; + var $call21 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $conv, tempInt)); + var $call22 = ___errno(); + var $3 = HEAP32[$call22 >> 2]; + var $call23 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $3, tempInt)); + var $call24 = _stat(STRING_TABLE.__str2 | 0, $s); + var $and25 = HEAP32[$st_mode$s2] & -512; + var $call26 = _printf(STRING_TABLE.__str4 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $and25, tempInt)); + for (var $$dest = $1$s2, $$stop = $$dest + 18; $$dest < $$stop; $$dest++) { + HEAP32[$$dest] = 0; + } + var $putchar = _putchar(10); + var $call28 = ___errno(); + HEAP32[$call28 >> 2] = 0; + var $call29 = _printf(STRING_TABLE.__str6 | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = $i_04, HEAP32[tempInt + 4 >> 2] = $j_03, tempInt)); + var $call30 = _open(STRING_TABLE.__str7 | 0, $flags_2_or17, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = 511, tempInt)); + var $conv32 = ($call30 | 0) != -1 & 1; + var $call33 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $conv32, tempInt)); + var $call34 = ___errno(); + var $5 = HEAP32[$call34 >> 2]; + var $call35 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $5, tempInt)); + var $call36 = _stat(STRING_TABLE.__str7 | 0, $s); + var $and38 = HEAP32[$st_mode$s2] & -512; + var $call39 = _printf(STRING_TABLE.__str4 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $and38, tempInt)); + for (var $$dest = $1$s2, $$stop = $$dest + 18; $$dest < $$stop; $$dest++) { + HEAP32[$$dest] = 0; + } + var $putchar1 = _putchar(10); + var $call41 = ___errno(); + HEAP32[$call41 >> 2] = 0; + HEAP8[$arrayidx43] = $conv42; + HEAP8[$arrayidx46] = $j_03 + 97 & 255; + var $call47 = _printf(STRING_TABLE.__str8 | 0, (tempInt = STACKTOP, STACKTOP += 8, HEAP32[tempInt >> 2] = $i_04, HEAP32[tempInt + 4 >> 2] = $j_03, tempInt)); + var $call48 = _open($0, $flags_2_or17, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = 511, tempInt)); + var $conv50 = ($call48 | 0) != -1 & 1; + var $call51 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $conv50, tempInt)); + var $call52 = ___errno(); + var $7 = HEAP32[$call52 >> 2]; + var $call53 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $7, tempInt)); + var $call55 = _stat($0, $s); + var $and57 = HEAP32[$st_mode$s2] & -512; + var $call58 = _printf(STRING_TABLE.__str4 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $and57, tempInt)); + for (var $$dest = $1$s2, $$stop = $$dest + 18; $$dest < $$stop; $$dest++) { + HEAP32[$$dest] = 0; + } + var $putchar2 = _putchar(10); + var $call60 = ___errno(); + HEAP32[$call60 >> 2] = 0; + var $inc = $j_03 + 1 | 0; + if (($inc | 0) == 16) { + break; + } + var $j_03 = $inc; + } + var $inc62 = $i_04 + 1 | 0; + if (($inc62 | 0) == 3) { + break; + } + var $i_04 = $inc62; + } + var $puts = _puts(STRING_TABLE._str | 0); + var $call65 = _creat(STRING_TABLE.__str10 | 0, 511); + var $conv67 = ($call65 | 0) != -1 & 1; + var $call68 = _printf(STRING_TABLE.__str1 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $conv67, tempInt)); + var $call69 = ___errno(); + var $9 = HEAP32[$call69 >> 2]; + var $call70 = _printf(STRING_TABLE.__str3 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $9, tempInt)); + STACKTOP = __stackBase__; + return 0; + return null; +} +function ex() { + var __stackBase__ = STACKTOP; + STACKTOP += 4; + var $e1 = __stackBase__; + var $puts = _puts(STRING_TABLE._str17 | 0); + var $x41 = $e1 | 0; + var $i_04 = 0; + while (1) { + var $i_04; + var $call1 = _printf(STRING_TABLE.__str15 | 0, (tempInt = STACKTOP, STACKTOP += 4, HEAP32[tempInt >> 2] = $i_04, tempInt)); + ((function() { + try { + __THREW__ = false; + return __Z5magici($i_04); + } catch (e) { + if (typeof e != "number") throw e; + if (ABORT) throw e; + __THREW__ = true; + return null; + } + }))(); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex"] |