diff options
-rwxr-xr-x | emcc | 5 | ||||
-rw-r--r-- | src/jsifier.js | 10 | ||||
-rw-r--r-- | src/library_browser.js | 31 | ||||
-rw-r--r-- | src/library_glut.js | 37 | ||||
-rw-r--r-- | src/library_sdl.js | 24 | ||||
-rw-r--r-- | tests/cases/returnfp.ll | 23 | ||||
-rwxr-xr-x | tests/runner.py | 9 | ||||
-rw-r--r-- | tests/sdl_audio_mix_channels.c | 59 | ||||
-rw-r--r-- | tools/eliminator/asm-eliminator-test-output.js | 5 | ||||
-rw-r--r-- | tools/eliminator/asm-eliminator-test.js | 8 | ||||
-rw-r--r-- | tools/js-optimizer.js | 98 | ||||
-rw-r--r-- | tools/js_optimizer.py | 12 | ||||
-rw-r--r-- | tools/shared.py | 23 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-regs-min.js | 2 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-relocate-output.js | 9 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-relocate.js | 12 |
16 files changed, 293 insertions, 74 deletions
@@ -844,6 +844,7 @@ try: DEBUG = 1 shared.set_logging() logging.debug('invocation: ' + ' '.join(sys.argv)) + shared.apply_configuration() # reset config to pick up change newargs[i] = '' elif newargs[i].startswith('--shell-file'): check_bad_eq(newargs[i]) @@ -1076,7 +1077,7 @@ try: output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') temp_files.append(output_file) args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file] - logging.debug("running:" + call + ' '.join(args)) + logging.debug("running:" + call + ' ' + ' '.join(args)) execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that) if not os.path.exists(output_file): logging.error('compiler frontend failed to generate LLVM bitcode, halting') @@ -1574,7 +1575,7 @@ try: js_optimizer_queue += ['registerize'] if opt_level > 0: - if debug_level < 2 and shared.Settings.ASM_JS and shared.Settings.RELOOP: js_optimizer_queue += ['minifyGlobals'] + if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue) if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] if closure and shared.Settings.ASM_JS: diff --git a/src/jsifier.js b/src/jsifier.js index a432b3e0..d6cd188c 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1182,7 +1182,7 @@ function JSify(data, functionsOnly, givenFunctions) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled); + var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone); var ret; @@ -1339,7 +1339,7 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; }); - function makeFunctionCall(ident, params, funcData, type, forceByPointer) { + function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); @@ -1467,8 +1467,8 @@ function JSify(data, functionsOnly, givenFunctions) { } } - var returnType; - if (byPointer || ASM_JS) { + var returnType = 'void'; + if ((byPointer || ASM_JS) && hasReturn) { returnType = getReturnType(type); } @@ -1513,7 +1513,7 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) }); makeFuncLineActor('call', function(item) { if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';'; - return makeFunctionCall(item.ident, item.params, item.funcData, item.type) + (item.standalone ? ';' : ''); + return makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : ''); }); makeFuncLineActor('unreachable', function(item) { diff --git a/src/library_browser.js b/src/library_browser.js index 925b64e2..d007d9a7 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -426,8 +426,17 @@ mergeInto(LibraryManager.library, { Browser.mouseMovementX = Browser.getMovementX(event); Browser.mouseMovementY = Browser.getMovementY(event); } - Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; - Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; + + // check if SDL is available + if (typeof SDL != "undefined") { + Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; + Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; + } else { + // just add the mouse delta to the current absolut mouse position + // FIXME: ideally this should be clamped against the canvas size and zero + Browser.mouseX += Browser.mouseMovementX; + Browser.mouseY += Browser.mouseMovementY; + } } else { // Otherwise, calculate the movement based on the changes // in the coordinates. @@ -504,9 +513,12 @@ mergeInto(LibraryManager.library, { this.windowedHeight = canvas.height; canvas.width = screen.width; canvas.height = screen.height; - var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}}; - flags = flags | 0x00800000; // set SDL_FULLSCREEN flag - {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}}; + flags = flags | 0x00800000; // set SDL_FULLSCREEN flag + {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} + } Browser.updateResizeListeners(); }, @@ -514,9 +526,12 @@ mergeInto(LibraryManager.library, { var canvas = Module['canvas']; canvas.width = this.windowedWidth; canvas.height = this.windowedHeight; - var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}}; - flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag - {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}}; + flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag + {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} + } Browser.updateResizeListeners(); } diff --git a/src/library_glut.js b/src/library_glut.js index 38cfe55b..36d47787 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -117,9 +117,9 @@ var LibraryGLUT = { if (48 <= keycode && keycode <= 57) return keycode; // numeric TODO handle shift? if (65 <= keycode && keycode <= 90) - return event['shiftKey'] ? keycode : keycode + 32; + return event['shiftKey'] ? keycode : keycode + 32; if (106 <= keycode && keycode <= 111) - return keycode - 106 + 42; // *,+-./ TODO handle shift? + return keycode - 106 + 42; // *,+-./ TODO handle shift? switch (keycode) { case 27: // escape @@ -227,7 +227,7 @@ var LibraryGLUT = { } else { width = GLUT.windowWidth; height = GLUT.windowHeight; - // TODO set position + // TODO set position document.removeEventListener('fullscreenchange', GLUT.onFullScreenEventChange, true); document.removeEventListener('mozfullscreenchange', GLUT.onFullScreenEventChange, true); document.removeEventListener('webkitfullscreenchange', GLUT.onFullScreenEventChange, true); @@ -255,13 +255,14 @@ var LibraryGLUT = { document['cancelFullScreen'] || document['mozCancelFullScreen'] || document['webkitCancelFullScreen'] || - (function() {}); + (function() {}); CFS.apply(document, []); } }, glutGetModifiers: function() { return GLUT.modifiers; }, + glutInit__deps: ['$Browser'], glutInit: function(argcp, argv) { // Ignore arguments GLUT.initTime = Date.now(); @@ -271,6 +272,12 @@ var LibraryGLUT = { window.addEventListener("mousemove", GLUT.onMousemove, true); window.addEventListener("mousedown", GLUT.onMouseButtonDown, true); window.addEventListener("mouseup", GLUT.onMouseButtonUp, true); + + Browser.resizeListeners.push(function(width, height) { + if (GLUT.reshapeFunc) { + Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]); + } + }); __ATEXIT__.push({ func: function() { window.removeEventListener("keydown", GLUT.onKeydown, true); @@ -294,21 +301,25 @@ var LibraryGLUT = { glutGet: function(type) { switch (type) { case 100: /* GLUT_WINDOW_X */ - return 0; /* TODO */ + return 0; /* TODO */ case 101: /* GLUT_WINDOW_Y */ - return 0; /* TODO */ + return 0; /* TODO */ case 102: /* GLUT_WINDOW_WIDTH */ - return Module['canvas'].width; + return Module['canvas'].width; case 103: /* GLUT_WINDOW_HEIGHT */ - return Module['canvas'].height; + return Module['canvas'].height; + case 200: /* GLUT_SCREEN_WIDTH */ + return Module['canvas'].width; + case 201: /* GLUT_SCREEN_HEIGHT */ + return Module['canvas'].height; case 500: /* GLUT_INIT_WINDOW_X */ - return 0; /* TODO */ + return 0; /* TODO */ case 501: /* GLUT_INIT_WINDOW_Y */ - return 0; /* TODO */ + return 0; /* TODO */ case 502: /* GLUT_INIT_WINDOW_WIDTH */ - return GLUT.initWindowWidth; + return GLUT.initWindowWidth; case 503: /* GLUT_INIT_WINDOW_HEIGHT */ - return GLUT.initWindowHeight; + return GLUT.initWindowHeight; case 700: /* GLUT_ELAPSED_TIME */ var now = Date.now(); return now - GLUT.initTime; @@ -386,10 +397,8 @@ var LibraryGLUT = { glutReshapeWindow__deps: ['$GLUT', 'glutPostRedisplay'], glutReshapeWindow: function(width, height) { GLUT.cancelFullScreen(); - // console.log("glutReshapeWindow: " + width + ", " + height); Browser.setCanvasSize(width, height); if (GLUT.reshapeFunc) { - // console.log("GLUT.reshapeFunc: " + width + ", " + height); Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]); } _glutPostRedisplay(); diff --git a/src/library_sdl.js b/src/library_sdl.js index 356c9746..176a2fae 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -1385,26 +1385,28 @@ var LibrarySDL = { // If the user asks us to allocate a channel automatically, get the first // free one. if (channel == -1) { - channel = SDL.channelMinimumNumber; for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) { if (!SDL.channels[i].audio) { channel = i; break; } } + if (channel == -1) { + Module.printErr('All ' + SDL.numChannels + ' channels in use!'); + return -1; + } } - // We clone the audio node to utilize the preloaded audio buffer, since // the browser has already preloaded the audio file. var channelInfo = SDL.channels[channel]; channelInfo.audio = audio = audio.cloneNode(true); audio.numChannels = info.audio.numChannels; audio.frequency = info.audio.frequency; - // TODO: handle N loops. Behavior matches Mix_PlayMusic audio.loop = loops != 0; - if (SDL.channelFinished) { - audio['onended'] = function() { // TODO: cache these + audio['onended'] = function() { // TODO: cache these + channelInfo.audio = null; + if (SDL.channelFinished) { Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); } } @@ -1461,7 +1463,6 @@ var LibrarySDL = { audio.play(); } audio.volume = channelInfo.volume; - audio.paused = false; return channel; }, Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing @@ -1475,6 +1476,8 @@ var LibrarySDL = { if (info.audio) { info.audio.pause(); info.audio = null; + } else { + Module.printErr('No Audio for channel: ' + channel); } if (SDL.channelFinished) { Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); @@ -1512,6 +1515,12 @@ var LibrarySDL = { } audio.volume = SDL.music.volume; audio['onended'] = _Mix_HaltMusic; // will send callback + if (SDL.music.audio) { + if (!SDL.music.audio.paused) { + Module.printErr('Music is already playing. ' + SDL.music.source); + } + SDL.music.audio.pause(); + } SDL.music.audio = audio; return 0; }, @@ -1577,7 +1586,8 @@ var LibrarySDL = { var info = SDL.channels[channel]; if (info && info.audio) { info.audio.pause(); - info.audio.paused = true; + } else { + Module.printErr('Mix_Pause: no sound found for channel: ' + channel); } }, diff --git a/tests/cases/returnfp.ll b/tests/cases/returnfp.ll new file mode 100644 index 00000000..974459e5 --- /dev/null +++ b/tests/cases/returnfp.ll @@ -0,0 +1,23 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +define internal void @__xmlRaiseError(void ()* %schannel) nounwind { + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + ret void +} + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + call void (void ()*)* @__xmlRaiseError(void ()* null) + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) + diff --git a/tests/runner.py b/tests/runner.py index f0f77bc9..c55c96fd 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -11340,6 +11340,8 @@ f.close() ['asm', 'simplifyExpressionsPre']), (path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(), ['asm', 'last']), + (path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(), + ['asm', 'relocate']), ]: print input output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0] @@ -12547,6 +12549,13 @@ Press any key to continue.''' Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() self.run_browser('page.html', '', '/report_result?1') + def test_sdl_audio_mix_channels(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) + open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read())) + + Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate() + self.run_browser('page.html', '', '/report_result?1') + def test_sdl_audio_mix(self): shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg')) diff --git a/tests/sdl_audio_mix_channels.c b/tests/sdl_audio_mix_channels.c new file mode 100644 index 00000000..dd91d594 --- /dev/null +++ b/tests/sdl_audio_mix_channels.c @@ -0,0 +1,59 @@ +#include <stdio.h> +#include <SDL/SDL.h> +#include <SDL/SDL_mixer.h> +#include <assert.h> +#include <emscripten.h> + +static Mix_Chunk *sound = NULL; +static Mix_Chunk *noiseLoop = NULL; +static Mix_Music *music = NULL; + +static int soundChannel = 0; +static int noiseLoopChannel = 0; + +static const int kNumChannels = 40; + +static int loadAndPlay() +{ + return Mix_PlayChannel(-1, sound, -1); +} + +int main(int argc, char **argv) { + SDL_Init(SDL_INIT_AUDIO); + Mix_Init(MIX_INIT_OGG); + + int ret = Mix_OpenAudio(0, 0, 0, 0); // we ignore all these.. + assert(ret == 0); + + Mix_AllocateChannels(kNumChannels); + + sound = Mix_LoadWAV("sound.ogg"); + + // allocate all the channels + for ( int i = 0; i < kNumChannels; i++ ) + { + assert(loadAndPlay() != -1); + } + + // This point, we should have exhausted our channels + + + + + int lastChannel = loadAndPlay(); + +#if EMSCRIPTEN + int result = (lastChannel == -1); + REPORT_RESULT(); +#endif + + assert(lastChannel == -1); + + // force a quit + while(Mix_Init(0)) + Mix_Quit(); + Mix_CloseAudio(); + + return 0; +} + diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index b28fbd4a..5cc6238e 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -5121,4 +5121,9 @@ function tempDouble2($46, $14, $28, $42, $20, $32, $45) { HEAP32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1; HEAP32[$45 + 8 >> 2] = $_sroa_06_0_insert_insert$1; } +function watIf() { + while (1) { + if ($cmp38) {} else {} + } +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index f90c3557..b33c6040 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -6851,5 +6851,11 @@ function tempDouble2($46, $14, $28, $42, $20, $32, $45) { HEAP32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1; HEAP32[$45 + 8 >> 2] = $_sroa_06_0_insert_insert$1; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2"] +function watIf() { + while (1) { + if ($cmp38) {} else { + } + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf"] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 532dc7fc..c772f667 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -144,7 +144,7 @@ var FALSE_NODE = ['unary-prefix', '!', ['num', 1]]; var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS'; var generatedFunctions = false; // whether we have received only generated functions -var minifierInfo = null; +var extraInfo = null; function srcToAst(src) { return uglify.parser.parse(src, false, debug); @@ -1638,7 +1638,7 @@ function denormalizeAsm(func, data) { // Very simple 'registerization', coalescing of variables into a smaller number, // as part of minification. Globals-level minification began in a previous pass, -// we receive minifierInfo which tells us how to rename globals. (Only in asm.js.) +// we receive extraInfo which tells us how to rename globals. (Only in asm.js.) // // We do not optimize when there are switches, so this pass only makes sense with // relooping. @@ -1680,7 +1680,7 @@ function registerize(ast) { } }); vacuum(fun); - if (minifierInfo) { + if (extraInfo) { assert(asm); var usedGlobals = {}; var nextLocal = 0; @@ -1688,7 +1688,7 @@ function registerize(ast) { traverse(fun, function(node, type) { if (type === 'name') { var name = node[1]; - var minified = minifierInfo.globals[name]; + var minified = extraInfo.globals[name]; if (minified) { assert(!localVars[name], name); // locals must not shadow globals, or else we don't know which is which if (localVars[minified]) { @@ -1721,8 +1721,8 @@ function registerize(ast) { } } }); - assert(fun[1] in minifierInfo.globals, fun[1]); - fun[1] = minifierInfo.globals[fun[1]]; + assert(fun[1] in extraInfo.globals, fun[1]); + fun[1] = extraInfo.globals[fun[1]]; assert(fun[1]); var nextRegName = 0; } @@ -1730,14 +1730,14 @@ function registerize(ast) { function getNewRegName(num, name) { if (!asm) return 'r' + num; var type = asmData.vars[name]; - if (!minifierInfo) { + if (!extraInfo) { var ret = (type ? 'd' : 'i') + num; regTypes[ret] = type; return ret; } // find the next free minified name that is not used by a global that shows up in this function - while (nextRegName < minifierInfo.names.length) { - var ret = minifierInfo.names[nextRegName++]; + while (nextRegName < extraInfo.names.length) { + var ret = extraInfo.names[nextRegName++]; if (!usedGlobals[ret]) { regTypes[ret] = type; return ret; @@ -2540,13 +2540,13 @@ function eliminate(ast, memSafe) { var ifTrue = last[2]; var ifFalse = last[3]; var flip = false; - if (ifFalse[1][0][0] === 'break') { // canonicalize break in the if + if (ifFalse[1][0] && ifFalse[1][0][0] === 'break') { // canonicalize break in the if var temp = ifFalse; ifFalse = ifTrue; ifTrue = temp; flip = true; } - if (ifTrue[1][0][0] === 'break') { + if (ifTrue[1][0] && ifTrue[1][0][0] === 'break') { var assigns = ifFalse[1]; var loopers = [], helpers = []; for (var i = 0; i < assigns.length; i++) { @@ -2696,16 +2696,16 @@ function minifyGlobals(ast) { var vars = node[1]; for (var i = 0; i < vars.length; i++) { var name = vars[i][0]; - assert(next < minifierInfo.names.length); - vars[i][0] = minified[name] = minifierInfo.names[next++]; + assert(next < extraInfo.names.length); + vars[i][0] = minified[name] = extraInfo.names[next++]; } } }); // add all globals in function chunks, i.e. not here but passed to us - for (var i = 0; i < minifierInfo.globals.length; i++) { - name = minifierInfo.globals[i]; - assert(next < minifierInfo.names.length); - minified[name] = minifierInfo.names[next++]; + for (var i = 0; i < extraInfo.globals.length; i++) { + name = extraInfo.globals[i]; + assert(next < extraInfo.names.length); + minified[name] = extraInfo.names[next++]; } // apply minification traverse(ast, function(node, type) { @@ -2716,9 +2716,64 @@ function minifyGlobals(ast) { } } }); - suffix = '// MINIFY_INFO:' + JSON.stringify(minified); + suffix = '// EXTRA_INFO:' + JSON.stringify(minified); } +// Relocation pass for a shared module (for the functions part of the module) +// +// 1. Replace function names with alternate names as defined (to avoid colliding with +// names in the main module we are being linked to) +// 2. Hardcode function table offsets from F_BASE+x to const+x if x is a variable, or +// the constant sum of the base + offset +// 3. Hardcode heap offsets from H_BASE as well +function relocate(ast) { + assert(asm); // we also assume we are normalized + + var replacements = extraInfo.replacements; + var fBase = extraInfo.fBase; + var hBase = extraInfo.hBase; + + traverse(ast, function(node, type) { + switch(type) { + case 'name': case 'defun': { + var rep = replacements[node[1]]; + if (rep) node[1] = rep; + break; + } + case 'binary': { + if (node[1] == '+' && node[2][0] == 'name') { + var base = null; + if (node[2][1] == 'F_BASE') { + base = fBase; + } else if (node[2][1] == 'H_BASE') { + base = hBase; + } + if (base) { + var other = node[3]; + if (other[0] == 'num') { + other[1] += base; + return other; + } else { + node[2] = ['num', base]; + } + } + } + break; + } + case 'var': { + var vars = node[1]; + for (var i = 0; i < vars.length; i++) { + var name = vars[i][0]; + assert(!(name in replacements)); // cannot shadow functions we are replacing TODO: fix that + } + break; + } + } + }); +} + +// Last pass utilities + // Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly) function prepDotZero(ast) { traverse(ast, function(node, type) { @@ -2783,6 +2838,7 @@ var passes = { eliminate: eliminate, eliminateMemSafe: eliminateMemSafe, minifyGlobals: minifyGlobals, + relocate: relocate, minifyWhitespace: function() { minifyWhitespace = true }, noPrintMetadata: function() { printMetadata = false }, asm: function() { asm = true }, @@ -2805,9 +2861,9 @@ var src = read(arguments_[0]); var ast = srcToAst(src); //printErr(JSON.stringify(ast)); throw 1; generatedFunctions = src.indexOf(GENERATED_FUNCTIONS_MARKER) >= 0; -var minifierInfoStart = src.indexOf('// MINIFY_INFO:') -if (minifierInfoStart > 0) minifierInfo = JSON.parse(src.substr(minifierInfoStart + 15)); -//printErr(JSON.stringify(minifierInfo)); +var extraInfoStart = src.indexOf('// EXTRA_INFO:') +if (extraInfoStart > 0) extraInfo = JSON.parse(src.substr(extraInfoStart + 14)); +//printErr(JSON.stringify(extraInfo)); arguments_.slice(1).forEach(function(arg) { diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 256c03cf..905ae835 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -36,7 +36,7 @@ class Minifier: MAX_NAMES = 80000 INVALID_2 = set(['do', 'if', 'in']) - INVALID_3 = set(['for', 'new', 'try', 'var', 'env']) + INVALID_3 = set(['for', 'new', 'try', 'var', 'env', 'let']) self.names = [] init_possibles = string.ascii_letters + '_$' @@ -73,7 +73,7 @@ class Minifier: f = open(temp_file, 'w') f.write(shell) f.write('\n') - f.write('// MINIFY_INFO:' + self.serialize()) + f.write('// EXTRA_INFO:' + self.serialize()) f.close() output = subprocess.Popen(self.js_engine + @@ -84,7 +84,7 @@ class Minifier: assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output #print >> sys.stderr, "minified SHELL 3333333333333333", output, "\n44444444444444444444" - code, metadata = output.split('// MINIFY_INFO:') + code, metadata = output.split('// EXTRA_INFO:') self.globs = json.loads(metadata) return code.replace('13371337', '0.0') @@ -135,9 +135,9 @@ def run_on_js(filename, passes, js_engine, jcache, source_map=False): end_funcs = js.rfind(end_funcs_marker) #assert (start_funcs >= 0) == (end_funcs >= 0) == (not not suffix) - minify_globals = 'minifyGlobals' in passes and 'registerize' in passes and 'asm' in passes + minify_globals = 'registerizeAndMinify' in passes and 'asm' in passes if minify_globals: - passes = filter(lambda p: p != 'minifyGlobals', passes) # we will run it manually + passes = map(lambda p: p if p != 'registerizeAndMinify' else 'registerize', passes) start_asm_marker = '// EMSCRIPTEN_START_ASM\n' end_asm_marker = '// EMSCRIPTEN_END_ASM\n' start_asm = js.find(start_asm_marker) @@ -250,7 +250,7 @@ EMSCRIPTEN_FUNCS(); f.write(suffix_marker) if minify_globals: f.write('\n') - f.write('// MINIFY_INFO:' + minify_info) + f.write('// EXTRA_INFO:' + minify_info) f.close() return temp_file filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))] diff --git a/tools/shared.py b/tools/shared.py index 64ffe29f..2a754d27 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -420,7 +420,7 @@ FILE_PACKAGER = path_from_root('tools', 'file_packager.py') # Temp dir. Create a random one, unless EMCC_DEBUG is set, in which case use TEMP_DIR/emscripten_temp class Configuration: - def __init__(self, environ): + def __init__(self, environ=os.environ): self.DEBUG = environ.get('EMCC_DEBUG') if self.DEBUG == "0": self.DEBUG = None @@ -448,11 +448,14 @@ class Configuration: tmp=self.TEMP_DIR if not self.DEBUG else self.EMSCRIPTEN_TEMP_DIR, save_debug_files=os.environ.get('EMCC_DEBUG_SAVE')) -configuration = Configuration(environ=os.environ) -DEBUG = configuration.DEBUG -EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR -DEBUG_CACHE = configuration.DEBUG_CACHE -CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR +def apply_configuration(): + global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR + configuration = Configuration() + DEBUG = configuration.DEBUG + EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR + DEBUG_CACHE = configuration.DEBUG_CACHE + CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR +apply_configuration() logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s') def set_logging(): @@ -462,9 +465,11 @@ set_logging() if not EMSCRIPTEN_TEMP_DIR: EMSCRIPTEN_TEMP_DIR = tempfile.mkdtemp(prefix='emscripten_temp_', dir=configuration.TEMP_DIR) - def clean_temp(): - try_delete(EMSCRIPTEN_TEMP_DIR) - atexit.register(clean_temp) + def prepare_to_clean_temp(d): + def clean_temp(): + try_delete(d) + atexit.register(clean_temp) + prepare_to_clean_temp(EMSCRIPTEN_TEMP_DIR) # this global var might change later # EM_CONFIG stuff diff --git a/tools/test-js-optimizer-asm-regs-min.js b/tools/test-js-optimizer-asm-regs-min.js index f47dbff3..a5b9427e 100644 --- a/tools/test-js-optimizer-asm-regs-min.js +++ b/tools/test-js-optimizer-asm-regs-min.js @@ -34,4 +34,4 @@ function collideLocal(i1) { bGlobal(i1); } // EMSCRIPTEN_GENERATED_FUNCTIONS -// MINIFY_INFO: { "names": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "i1", "cl"], "globals": { "aGlobal": "a", "bGlobal": "i1", "collideLocal": "cl" } } +// EXTRA_INFO: { "names": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "i1", "cl"], "globals": { "aGlobal": "a", "bGlobal": "i1", "collideLocal": "cl" } } diff --git a/tools/test-js-optimizer-asm-relocate-output.js b/tools/test-js-optimizer-asm-relocate-output.js new file mode 100644 index 00000000..6a197e81 --- /dev/null +++ b/tools/test-js-optimizer-asm-relocate-output.js @@ -0,0 +1,9 @@ +function leaveMeAlone(c) {} +function fixed(a, b) {} +function a(x, y) { + fixed(34, 12); + fixed(34 | 0, 12 | 0); + leaveMeAlone(10 + x, 33 + y); + leaveMeAlone(10 + x | 0, 33 + y | 0); +} + diff --git a/tools/test-js-optimizer-asm-relocate.js b/tools/test-js-optimizer-asm-relocate.js new file mode 100644 index 00000000..a45bc2f0 --- /dev/null +++ b/tools/test-js-optimizer-asm-relocate.js @@ -0,0 +1,12 @@ +function leaveMeAlone(c) { +} +function replaceMe(a, b) { +} +function a(x, y) { + replaceMe(H_BASE + 1, F_BASE + 2); + replaceMe(H_BASE + 1 | 0, F_BASE + 2 | 0); + leaveMeAlone(F_BASE + x, H_BASE + y); + leaveMeAlone(F_BASE + x | 0, H_BASE + y | 0); +} +// EMSCRIPTEN_GENERATED_FUNCTIONS +// EXTRA_INFO: { "replacements": { "replaceMe": "fixed" }, "hBase": 33, "fBase": 10 } |