diff options
-rw-r--r-- | AUTHORS | 3 | ||||
-rwxr-xr-x | emscripten.py | 3 | ||||
-rw-r--r-- | src/analyzer.js | 12 | ||||
-rw-r--r-- | src/headless.js | 14 | ||||
-rw-r--r-- | src/library.js | 45 | ||||
-rw-r--r-- | src/library_sdl.js | 116 | ||||
-rw-r--r-- | src/modules.js | 1 | ||||
-rw-r--r-- | src/parseTools.js | 2 | ||||
-rw-r--r-- | src/relooper/Relooper.cpp | 95 | ||||
-rw-r--r-- | src/relooper/Relooper.h | 1 | ||||
-rw-r--r-- | src/relooper/test.cpp | 27 | ||||
-rw-r--r-- | src/relooper/test.txt | 17 | ||||
-rw-r--r-- | src/relooper/test3.txt | 4 | ||||
-rw-r--r-- | src/relooper/test_inf.txt | 648 | ||||
-rw-r--r-- | src/settings.js | 3 | ||||
-rw-r--r-- | src/shell.js | 4 | ||||
-rw-r--r-- | system/include/sys/socket.h | 2 | ||||
-rwxr-xr-x | tests/runner.py | 65 | ||||
-rw-r--r-- | tests/sdl_key.c | 43 | ||||
-rw-r--r-- | tools/eliminator/asm-eliminator-test-output.js | 147 | ||||
-rw-r--r-- | tools/eliminator/asm-eliminator-test.js | 162 | ||||
-rw-r--r-- | tools/file_packager.py | 2 | ||||
-rw-r--r-- | tools/js-optimizer.js | 383 | ||||
-rw-r--r-- | tools/shared.py | 2 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-last-output.js | 11 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-last.js | 23 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre-output.js | 54 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre.js | 58 |
28 files changed, 1452 insertions, 495 deletions
@@ -82,4 +82,7 @@ a license to everyone to use it as detailed in LICENSE.) * Onno Jongbloed <hey@onnoj.net> * Jez Ng <me@jezng.com> * Marc Feeley <mfeeley@mozilla.com> (copyright owned by Mozilla Foundation) +* Ludovic Perrine <jazzzz@gmail.com> + + diff --git a/emscripten.py b/emscripten.py index 248d0ce4..d8312855 100755 --- a/emscripten.py +++ b/emscripten.py @@ -127,6 +127,9 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, if DEBUG and len(meta) > 1024*1024: print >> sys.stderr, 'emscript warning: large amounts of metadata, will slow things down' if DEBUG: print >> sys.stderr, ' emscript: split took %s seconds' % (time.time() - t) + if len(funcs) == 0: + raise RuntimeError('No functions to process. Make sure you prevented LLVM from eliminating them as dead (use EXPORTED_FUNCTIONS if necessary, see the FAQ)') + #if DEBUG: # print >> sys.stderr, '========= pre ================\n' # print >> sys.stderr, ''.join(pre) diff --git a/src/analyzer.js b/src/analyzer.js index 2cc46ab6..de9a7940 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -20,7 +20,7 @@ var BRANCH_INVOKE = set('branch', 'invoke'); var LABEL_ENDERS = set('branch', 'return', 'switch'); var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic'); var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam'); -var I64_DOUBLE_FLIP = { i64: 'double', double: 'i64' }; +var SHADOW_FLIP = { i64: 'double', double: 'i64' }; //, i32: 'float', float: 'i32' }; // Analyzer @@ -124,13 +124,13 @@ function analyzer(data, sidePass) { var lines = label.lines; for (var i = 0; i < lines.length; i++) { var line = lines[i]; - if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP) { + if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP) { has = true; } } }); if (!has) return; - // there are i64<-->double bitcasts, create shadows for everything + // there are integer<->floating-point bitcasts, create shadows for everything var shadowed = {}; func.labels.forEach(function(label) { var lines = label.lines; @@ -138,11 +138,11 @@ function analyzer(data, sidePass) { while (i < lines.length) { var lines = label.lines; var line = lines[i]; - if (line.intertype == 'load' && line.type in I64_DOUBLE_FLIP) { + if (line.intertype == 'load' && line.type in SHADOW_FLIP) { if (line.pointer.intertype != 'value') { i++; continue } // TODO shadowed[line.assignTo] = 1; var shadow = line.assignTo + '$$SHADOW'; - var flip = I64_DOUBLE_FLIP[line.type]; + var flip = SHADOW_FLIP[line.type]; lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase tokens: null, indent: 2, @@ -171,7 +171,7 @@ function analyzer(data, sidePass) { var lines = label.lines; for (var i = 0; i < lines.length; i++) { var line = lines[i]; - if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP && line.ident in shadowed) { + if (line.intertype == 'bitcast' && line.type in SHADOW_FLIP && line.ident in shadowed) { var shadow = line.ident + '$$SHADOW'; line.params[0].ident = shadow; line.params[0].type = line.type; diff --git a/src/headless.js b/src/headless.js index d81fb5a3..097a42f7 100644 --- a/src/headless.js +++ b/src/headless.js @@ -4,6 +4,20 @@ // TODO: sync from bananabread headless.js var window = { + eventListeners: {}, + addEventListener: function(id, func) { + var listeners = this.eventListeners[id]; + if (!listeners) { + listeners = this.eventListeners[id] = []; + } + listeners.push(func); + }, + callEventListeners: function(id) { + var listeners = this.eventListeners[id]; + if (listeners) { + listeners.forEach(function(listener) { listener() }); + } + }, location: { toString: function() { return '%s'; diff --git a/src/library.js b/src/library.js index ab27ed35..f958a436 100644 --- a/src/library.js +++ b/src/library.js @@ -2621,7 +2621,7 @@ LibraryManager.library = { var curr = 0; var buffer = []; // Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later - if (type == 'f' || type == 'e' || type == 'g' || + if (type == 'f' || type == 'e' || type == 'g' || type == 'F' || type == 'E' || type == 'G') { var last = 0; next = get(); @@ -3965,8 +3965,8 @@ LibraryManager.library = { {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { str += 2; } - } - } + } + } if (!finalBase) finalBase = 10; // Get digits. @@ -4020,7 +4020,7 @@ LibraryManager.library = { var isNegative = false; // Skip space. while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; - + // Check for a plus/minus sign. if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) { str++; @@ -4049,8 +4049,8 @@ LibraryManager.library = { {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { str += 2; } - } - } + } + } if (!finalBase) finalBase = 10; start = str; @@ -5732,7 +5732,8 @@ LibraryManager.library = { llround: 'round', llroundf: 'round', rint: function(x) { - return (x > 0) ? -Math.round(-x) : Math.round(x); + if (Math.abs(x % 1) !== 0.5) return Math.round(x); + return x + x % 2 + ((x < 0) ? 1 : -1); }, rintf: 'rint', lrint: 'rint', @@ -6353,7 +6354,8 @@ LibraryManager.library = { // Note that we need to emulate functions that use setjmp, and also to create // a new label we can return to. Emulation make such functions slower, this // can be alleviated by making a new function containing just the setjmp - // related functionality so the slowdown is more limited. + // related functionality so the slowdown is more limited - you may need + // to prevent inlining to keep this isolated, try __attribute__((noinline)) // ========================================================================== saveSetjmp__asm: true, @@ -6373,11 +6375,11 @@ LibraryManager.library = { setjmpId = (setjmpId+1)|0; {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; while ((i|0) < {{{ 2*MAX_SETJMPS }}}) { - if ({{{ makeGetValueAsm('table', 'i*4', 'i32') }}} == 0) { - {{{ makeSetValueAsm('table', 'i*4', 'setjmpId', 'i32') }}}; - {{{ makeSetValueAsm('table', 'i*4+4', 'label', 'i32') }}}; + if ({{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}} == 0) { + {{{ makeSetValueAsm('table', '(i<<2)', 'setjmpId', 'i32') }}}; + {{{ makeSetValueAsm('table', '(i<<2)+4', 'label', 'i32') }}}; // prepare next slot - {{{ makeSetValueAsm('table', 'i*4+8', '0', 'i32') }}}; + {{{ makeSetValueAsm('table', '(i<<2)+8', '0', 'i32') }}}; return 0; } i = (i+2)|0; @@ -6394,10 +6396,10 @@ LibraryManager.library = { table = table|0; var i = 0, curr = 0; while ((i|0) < {{{ MAX_SETJMPS }}}) { - curr = {{{ makeGetValueAsm('table', 'i*4', 'i32') }}}; + curr = {{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}}; if ((curr|0) == 0) break; if ((curr|0) == (id|0)) { - return {{{ makeGetValueAsm('table', 'i*4+4', 'i32') }}}; + return {{{ makeGetValueAsm('table', '(i<<2)+4', 'i32') }}}; } i = (i+2)|0; } @@ -7330,6 +7332,7 @@ LibraryManager.library = { */ socket__deps: ['$Sockets'], socket: function(family, type, protocol) { + var INCOMING_QUEUE_LENGTH = 64; var fd = FS.createFileHandle({ addr: null, port: null, @@ -7337,7 +7340,7 @@ LibraryManager.library = { header: new Uint16Array(2), bound: false, socket: true - }; + }); assert(fd < 64); // select() assumes socket fd values are in 0..63 var stream = type == {{{ cDefine('SOCK_STREAM') }}}; if (protocol) { @@ -7423,8 +7426,6 @@ LibraryManager.library = { Sockets.peer = peer; } - var INCOMING_QUEUE_LENGTH = 64; - function CircularBuffer(max_length) { var buffer = new Array(++ max_length); var head = 0; @@ -7668,8 +7669,8 @@ LibraryManager.library = { nfds = Math.min(64, nfds); // fd sets have 64 bits for (var fd = 0; fd < nfds; fd++) { - var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh; - if (int & mask) { + var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh; + if (int_ & mask) { // index is in the set, check if it is ready for read var info = FS.streams[fd]; if (info && can(info)) { @@ -7700,7 +7701,7 @@ LibraryManager.library = { if (protocol) { assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp } - var fd = FS.createFileHandle({ + var fd = FS.createFileHandle({ connected: false, stream: stream, socket: true @@ -8057,8 +8058,8 @@ LibraryManager.library = { nfds = Math.min(64, nfds); // fd sets have 64 bits for (var fd = 0; fd < nfds; fd++) { - var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh; - if (int & mask) { + var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh; + if (int_ & mask) { // index is in the set, check if it is ready for read var info = FS.streams[fd]; if (info && can(info)) { diff --git a/src/library_sdl.js b/src/library_sdl.js index 4f871f9d..a131f424 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -47,6 +47,7 @@ var LibrarySDL = { startTime: null, buttonState: 0, + modState: 0, DOMButtons: [0, 0, 0], DOMEventToSDLEvent: {}, @@ -373,7 +374,8 @@ var LibrarySDL = { if (event['movementX'] == 0 && event['movementY'] == 0) { // ignore a mousemove event if it doesn't contain any movement info // (without pointer lock, we infer movement from pageX/pageY, so this check is unnecessary) - return false; + event.preventDefault(); + return; } } // fall through @@ -396,15 +398,20 @@ var LibrarySDL = { } else if (event.type == 'mousedown') { SDL.DOMButtons[event.button] = 1; } else if (event.type == 'mouseup') { - if (!SDL.DOMButtons[event.button]) return false; // ignore extra ups, can happen if we leave the canvas while pressing down, then return, - // since we add a mouseup in that case + // ignore extra ups, can happen if we leave the canvas while pressing down, then return, + // since we add a mouseup in that case + if (!SDL.DOMButtons[event.button]) { + event.preventDefault(); + return; + } + SDL.DOMButtons[event.button] = 0; } if (event.type == 'keypress' && !SDL.textInput) { break; } - + SDL.events.push(event); break; case 'mouseout': @@ -438,7 +445,7 @@ var LibrarySDL = { // Force-run a main event loop, since otherwise this event will never be caught! Browser.mainLoop.runner(); } - return true; + return; case 'resize': SDL.events.push(event); break; @@ -447,7 +454,11 @@ var LibrarySDL = { Module.printErr('SDL event queue full, dropping events'); SDL.events = SDL.events.slice(0, 10000); } - return false; + // manually triggered resize event doesn't have a preventDefault member + if (event.preventDefault) { + event.preventDefault(); + } + return; }, makeCEvent: function(event, ptr) { @@ -473,15 +484,6 @@ var LibrarySDL = { } else { scan = SDL.scanCodes[key] || key; } - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} - //{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.which', '1', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO - - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', '0', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'key', 'i32') }}} var code = SDL.keyCodes[event.keyCode] || event.keyCode; {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}}; @@ -491,6 +493,19 @@ var LibrarySDL = { delete SDL.keyboardMap[code]; } + // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED + SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL + ({{{ makeGetValue('SDL.keyboardState', '1249', 'i8') }}} ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT + ({{{ makeGetValue('SDL.keyboardState', '1250', 'i8') }}} ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT + + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}} + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}} + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}} + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', 'SDL.modState', 'i32') }}} + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'key', 'i32') }}} + break; } case 'keypress': { @@ -614,13 +629,13 @@ var LibrarySDL = { SDL.startTime = Date.now(); // capture all key events. we just keep down and up, but also capture press to prevent default actions if (!Module['doNotCaptureKeyboard']) { - document.onkeydown = SDL.receiveEvent; - document.onkeyup = SDL.receiveEvent; - document.onkeypress = SDL.receiveEvent; - document.onblur = SDL.receiveEvent; + document.addEventListener("keydown", SDL.receiveEvent); + document.addEventListener("keyup", SDL.receiveEvent); + document.addEventListener("keypress", SDL.receiveEvent); + document.addEventListener("blur", SDL.receiveEvent); document.addEventListener("visibilitychange", SDL.receiveEvent); } - window.onunload = SDL.receiveEvent; + window.addEventListener("unload", SDL.receiveEvent); SDL.keyboardState = _malloc(0x10000); // Our SDL needs 512, but 64K is safe for older SDLs _memset(SDL.keyboardState, 0, 0x10000); // Initialize this structure carefully for closure @@ -868,7 +883,7 @@ var LibrarySDL = { }, SDL_Delay: function(delay) { - throw 'SDL_Delay called! Potential infinite loop, quitting. ' + new Error().stack; + abort('SDL_Delay called! Potential infinite loop, quitting.'); }, SDL_WM_SetCaption: function(title, icon) { @@ -888,12 +903,16 @@ var LibrarySDL = { SDL_GetKeyState: function() { return _SDL_GetKeyboardState(); }, + + SDL_GetKeyName: function(key) { + if (!SDL.keyName) { + SDL.keyName = allocate(intArrayFromString('unknown key'), 'i8', ALLOC_NORMAL); + } + return SDL.keyName; + }, SDL_GetModState: function() { - // TODO: numlock, capslock, etc. - return (SDL.keyboardState[16] ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT - (SDL.keyboardState[17] ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL - (SDL.keyboardState[18] ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT + return SDL.modState; }, SDL_GetMouseState: function(x, y) { @@ -936,7 +955,10 @@ var LibrarySDL = { }, SDL_GetError: function() { - return allocate(intArrayFromString("unknown SDL-emscripten error"), 'i8'); + if (!SDL.errorMessage) { + SDL.errorMessage = allocate(intArrayFromString("unknown SDL-emscripten error"), 'i8', ALLOC_NORMAL); + } + return SDL.errorMessage; }, SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) { @@ -1100,6 +1122,20 @@ var LibrarySDL = { return (a&0xff)+((b&0xff)<<8)+((g&0xff)<<16)+((r&0xff)<<24) }, + SDL_GetAppState: function() { + var state = 0; + + if (Browser.pointerLock) { + state |= 0x01; // SDL_APPMOUSEFOCUS + } + if (document.hasFocus()) { + state |= 0x02; // SDL_APPINPUTFOCUS + } + state |= 0x04; // SDL_APPACTIVE + + return state; + }, + SDL_WM_GrabInput: function() {}, SDL_WM_ToggleFullScreen: function(surf) { @@ -1160,9 +1196,6 @@ var LibrarySDL = { SDL_OpenAudio: function(desired, obtained) { SDL.allocateChannels(32); - // FIXME: Assumes 16-bit audio - assert(obtained === 0, 'Cannot return obtained SDL audio params'); - SDL.audio = { freq: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.freq', 'i32', 0, 1) }}}, format: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.format', 'i16', 0, 1) }}}, @@ -1174,6 +1207,16 @@ var LibrarySDL = { timer: null }; + if (obtained) { + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.freq', 'SDL.audio.freq', 'i32') }}}; // no good way for us to know if the browser can really handle this + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.format', 33040, 'i16') }}}; // float, signed, 16-bit + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.channels', 'SDL.audio.channels', 'i8') }}}; + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.silence', makeGetValue('desired', 'SDL.structs.AudioSpec.silence', 'i8', 0, 1), 'i8') }}}; // unclear if browsers can provide this + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.samples', 'SDL.audio.samples', 'i16') }}}; + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.callback', 'SDL.audio.callback', '*') }}}; + {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.userdata', 'SDL.audio.userdata', '*') }}}; + } + var totalSamples = SDL.audio.samples*SDL.audio.channels; SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio SDL.audio.buffer = _malloc(SDL.audio.bufferSize); @@ -1745,6 +1788,10 @@ var LibrarySDL = { return -1; }, + SDL_SetGammaRamp: function (redTable, greenTable, blueTable) { + return -1; + }, + // Misc SDL_InitSubSystem: function(flags) { return 0 }, @@ -1782,7 +1829,7 @@ var LibrarySDL = { SDL_FreeRW: function() { throw 'SDL_FreeRW: TODO' }, SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' }, SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' }, - SDL_WM_ToggleFullScreen: function() { throw 'SDL_WM_ToggleFullScreen: TODO' }, + SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' }, Mix_SetPostMix: function() { throw 'Mix_SetPostMix: TODO' }, Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' }, @@ -1792,6 +1839,15 @@ var LibrarySDL = { Mix_Linked_Version: function() { throw 'Mix_Linked_Version: TODO' }, SDL_CreateRGBSurfaceFrom: function() { throw 'SDL_CreateRGBSurfaceFrom: TODO' }, SDL_SaveBMP_RW: function() { throw 'SDL_SaveBMP_RW: TODO' }, + + SDL_HasRDTSC: function() { return 0; }, + SDL_HasMMX: function() { return 0; }, + SDL_HasMMXExt: function() { return 0; }, + SDL_Has3DNow: function() { return 0; }, + SDL_Has3DNowExt: function() { return 0; }, + SDL_HasSSE: function() { return 0; }, + SDL_HasSSE2: function() { return 0; }, + SDL_HasAltiVec: function() { return 0; } }; autoAddDeps(LibrarySDL, '$SDL'); diff --git a/src/modules.js b/src/modules.js index e4093078..b13ab3c5 100644 --- a/src/modules.js +++ b/src/modules.js @@ -341,6 +341,7 @@ var Functions = { call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32'); } call += ')'; + if (curr == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things'); tables.pre += 'function ' + curr + '__wrapper(' + args + ') { ' + arg_coercions + ' ; ' + retPre + call + retPost + ' }\n'; wrapped[curr] = 1; } diff --git a/src/parseTools.js b/src/parseTools.js index 3949491e..687faaa8 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -2204,7 +2204,7 @@ function processMathop(item) { case 'sub': return handleOverflow(getFastValue(idents[0], '-', idents[1], item.type), bits); case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, op[0] === 's'); case 'mul': return getFastValue(idents[0], '*', idents[1], item.type); // overflow handling is already done in getFastValue for '*' - case 'urem': case 'srem': return getFastValue(idents[0], '%', idents[1], item.type); + case 'urem': case 'srem': return makeRounding(getFastValue(idents[0], '%', idents[1], item.type), bits, op[0] === 's'); case 'or': { if (bits > 32) { assert(bits === 64, 'Too many bits for or: ' + bits); diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index 8c72b0a6..8a6e18b8 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -70,7 +70,7 @@ int Indenter::CurrIndent = 0; // Branch -Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL), Labeled(false) { +Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL), Labeled(true) { Condition = ConditionInit ? strdup(ConditionInit) : NULL; Code = CodeInit ? strdup(CodeInit) : NULL; } @@ -522,14 +522,60 @@ void Relooper::Calculate(Block *Entry) { } } +#if 0 + // We can avoid multiple next entries by hoisting them into the loop. + if (NextEntries.size() > 1) { + BlockBlockSetMap IndependentGroups; + FindIndependentGroups(NextEntries, IndependentGroups, &InnerBlocks); + + while (IndependentGroups.size() > 0 && NextEntries.size() > 1) { + Block *Min = NULL; + int MinSize = 0; + for (BlockBlockSetMap::iterator iter = IndependentGroups.begin(); iter != IndependentGroups.end(); iter++) { + Block *Entry = iter->first; + BlockSet &Blocks = iter->second; + if (!Min || Blocks.size() < MinSize) { // TODO: code size, not # of blocks + Min = Entry; + MinSize = Blocks.size(); + } + } + // check how many new entries this would cause + BlockSet &Hoisted = IndependentGroups[Min]; + bool abort = false; + for (BlockSet::iterator iter = Hoisted.begin(); iter != Hoisted.end() && !abort; iter++) { + Block *Curr = *iter; + for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) { + Block *Target = iter->first; + if (Hoisted.find(Target) == Hoisted.end() && NextEntries.find(Target) == NextEntries.end()) { + // abort this hoisting + abort = true; + break; + } + } + } + if (abort) { + IndependentGroups.erase(Min); + continue; + } + // hoist this entry + PrintDebug("hoisting %d into loop\n", Min->Id); + NextEntries.erase(Min); + for (BlockSet::iterator iter = Hoisted.begin(); iter != Hoisted.end(); iter++) { + Block *Curr = *iter; + InnerBlocks.insert(Curr); + Blocks.erase(Curr); + } + IndependentGroups.erase(Min); + } + } +#endif + PrintDebug("creating loop block:\n"); DebugDump(InnerBlocks, " inner blocks:"); DebugDump(Entries, " inner entries:"); DebugDump(Blocks, " outer blocks:"); DebugDump(NextEntries, " outer entries:"); - // TODO: Optionally hoist additional blocks into the loop - LoopShape *Loop = new LoopShape(); Notice(Loop); @@ -551,7 +597,8 @@ void Relooper::Calculate(Block *Entry) { // For each entry, find the independent group reachable by it. The independent group is // the entry itself, plus all the blocks it can reach that cannot be directly reached by another entry. Note that we // ignore directly reaching the entry itself by another entry. - void FindIndependentGroups(BlockSet &Blocks, BlockSet &Entries, BlockBlockSetMap& IndependentGroups) { + // @param Ignore - previous blocks that are irrelevant + void FindIndependentGroups(BlockSet &Entries, BlockBlockSetMap& IndependentGroups, BlockSet *Ignore=NULL) { typedef std::map<Block*, Block*> BlockBlockMap; struct HelperClass { @@ -639,6 +686,7 @@ void Relooper::Calculate(Block *Entry) { Block *Child = *iter; for (BlockSet::iterator iter = Child->BranchesIn.begin(); iter != Child->BranchesIn.end(); iter++) { Block *Parent = *iter; + if (Ignore && Ignore->find(Parent) != Ignore->end()) continue; if (Helper.Ownership[Parent] != Helper.Ownership[Child]) { ToInvalidate.push_back(Child); } @@ -756,7 +804,7 @@ void Relooper::Calculate(Block *Entry) { // independent blocks from an entry/ies. It is important to remove through // multiples as opposed to looping since the former is more performant. BlockBlockSetMap IndependentGroups; - FindIndependentGroups(Blocks, *Entries, IndependentGroups); + FindIndependentGroups(*Entries, IndependentGroups); PrintDebug("Independent groups: %d\n", IndependentGroups.size()); @@ -903,10 +951,28 @@ void Relooper::Calculate(Block *Entry) { }); } + void FindNaturals(Shape *Root, Shape *Otherwise=NULL) { + if (Root->Next) { + Root->Natural = Root->Next; + FindNaturals(Root->Next, Otherwise); + } else { + Root->Natural = Otherwise; + } + + SHAPE_SWITCH(Root, { + }, { + for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) { + FindNaturals(iter->second, Root->Natural); + } + }, { + FindNaturals(Loop->Inner, Loop->Inner); + }); + } + // Remove unneeded breaks and continues. // A flow operation is trivially unneeded if the shape we naturally get to by normal code // execution is the same as the flow forces us to. - void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL) { + void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL) { BlockSet NaturalBlocks; FollowNaturalFlow(Natural, NaturalBlocks); Shape *Next = Root; @@ -928,16 +994,22 @@ void Relooper::Calculate(Block *Entry) { if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) { Multiple->NeedLoop--; } + } else if (Details->Type == Branch::Break && LastLoop && LastLoop->Natural == Details->Ancestor->Natural) { + // it is important to simplify breaks, as simpler breaks enable other optimizations + Details->Labeled = false; + if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) { + Multiple->NeedLoop--; + } } } } }, { for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) { - RemoveUnneededFlows(iter->second, Multiple->Next); + RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->NeedLoop ? NULL : LastLoop); } Next = Multiple->Next; }, { - RemoveUnneededFlows(Loop->Inner, Loop->Inner); + RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop); Next = Loop->Next; }); } @@ -968,7 +1040,7 @@ void Relooper::Calculate(Block *Entry) { Branch *Details = iter->second; if (Details->Type != Branch::Direct) { assert(LoopStack.size() > 0); - if (Details->Ancestor != LoopStack.top()) { + if (Details->Ancestor != LoopStack.top() && Details->Labeled) { LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); Labeled->Labeled = true; Details->Labeled = true; @@ -1006,6 +1078,7 @@ void Relooper::Calculate(Block *Entry) { } void Process(Shape *Root) { + FindNaturals(Root); RemoveUnneededFlows(Root); FindLabeledLoops(Root); } @@ -1053,6 +1126,10 @@ void Debugging::Dump(BlockSet &Blocks, const char *prefix) { void Debugging::Dump(Shape *S, const char *prefix) { if (prefix) printf("%s ", prefix); + if (!S) { + printf(" (null)\n"); + return; + } printf(" %d ", S->Id); SHAPE_SWITCH(S, { printf("<< Simple with block %d\n", Simple->Inner->Id); diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h index 34b6db08..fe56a133 100644 --- a/src/relooper/Relooper.h +++ b/src/relooper/Relooper.h @@ -101,6 +101,7 @@ class LoopShape; struct Shape { int Id; // A unique identifier. Used to identify loops, labels are Lx where x is the Id. Shape *Next; // The shape that will appear in the code right after this one + Shape *Natural; // The shape that control flow gets to naturally (if there is Next, then this is Next) enum ShapeType { Simple, diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp index 7da990b5..b2d500d7 100644 --- a/src/relooper/test.cpp +++ b/src/relooper/test.cpp @@ -231,5 +231,32 @@ int main() { puts(buffer); } + + if (1) { + Relooper::SetOutputBuffer(buffer, sizeof(buffer)); + + printf("\n\n-- conditional loop --\n\n"); + + Block *b_a = new Block("// block A\n"); + Block *b_b = new Block("// block B\n"); + Block *b_c = new Block("// block C\n"); + + b_a->AddBranchTo(b_b, "shouldLoop()"); + b_a->AddBranchTo(b_c, NULL); + + b_b->AddBranchTo(b_b, "moarLoop()"); + b_b->AddBranchTo(b_c, NULL); + + Relooper r; + r.AddBlock(b_a); + r.AddBlock(b_b); + r.AddBlock(b_c); + + r.Calculate(b_a); + printf("\n\n"); + r.Render(); + + puts(buffer); + } } diff --git a/src/relooper/test.txt b/src/relooper/test.txt index d657c6af..b3535592 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -136,3 +136,20 @@ while(1) { // block F } + + +-- conditional loop -- + + + +// block A +if (shouldLoop()) { + while(1) { + // block B + if (!(moarLoop())) { + break; + } + } +} +// block C + diff --git a/src/relooper/test3.txt b/src/relooper/test3.txt index 696542ef..6049ee48 100644 --- a/src/relooper/test3.txt +++ b/src/relooper/test3.txt @@ -9,7 +9,7 @@ do { } } while(0); LBB3 -L5: do { +do { if (LBB3 -> LBB4) { LBB4 if (!(LBB4 -> LBB5)) { @@ -18,7 +18,7 @@ L5: do { while(1) { LBB5 if (LBB5 -> LBB6) { - break L5; + break; } } } diff --git a/src/relooper/test_inf.txt b/src/relooper/test_inf.txt index 379d2083..1dff59bb 100644 --- a/src/relooper/test_inf.txt +++ b/src/relooper/test_inf.txt @@ -5,35 +5,33 @@ if (uint(i4) >= uint(i5)) { code 1 } code 3 -L5: do { - if (!(i2 == 0)) { - code 4 - while(1) { - code 5 - if (uint(i6) >= uint(i7)) { - code 7 - } else { - code 6 - } - code 8 - if (uint(i6) >= uint(i7)) { - code 10 - } else { - code 9 - } - code 11 - if (uint(i5) >= uint(i6)) { - code 13 - } else { - code 12 - } - code 14 - if (!(i2 != 0)) { - break L5; - } +if (!(i2 == 0)) { + code 4 + while(1) { + code 5 + if (uint(i6) >= uint(i7)) { + code 7 + } else { + code 6 + } + code 8 + if (uint(i6) >= uint(i7)) { + code 10 + } else { + code 9 + } + code 11 + if (uint(i5) >= uint(i6)) { + code 13 + } else { + code 12 + } + code 14 + if (!(i2 != 0)) { + break; } } -} while(0); +} code 15 if (uint(i4) >= uint(i5)) { code 17 @@ -41,179 +39,177 @@ if (uint(i4) >= uint(i5)) { code 16 } code 18 -L26: do { - if (!(i2 == 0)) { - code 19 - while(1) { - code 20 - if (uint(i5) >= uint(i6)) { - code 22 - } else { - code 21 - } - code 23 - if (uint(i5) >= uint(i6)) { - code 25 - } else { - code 24 - } - code 26 - if (uint(i5) >= uint(i6)) { - code 28 - } else { - code 27 - } - code 29 - if (uint(i5) >= uint(i6)) { - code 31 - } else { - code 30 - } - code 32 - if (uint(i5) >= uint(i6)) { - code 34 - } else { - code 33 - } - code 35 - if (uint(i5) >= uint(i6)) { - code 37 - } else { - code 36 - } - code 38 - if (uint(i5) >= uint(i6)) { - code 40 - } else { - code 39 - } - code 41 - if (uint(i5) >= uint(i6)) { - code 43 - } else { - code 42 - } - code 44 - if (uint(i5) >= uint(i6)) { - code 46 - } else { - code 45 - } - code 47 - if (uint(i5) >= uint(i6)) { - code 49 - } else { - code 48 - } - code 50 - if (uint(i5) >= uint(i6)) { - code 52 - } else { - code 51 - } - code 53 - if (uint(i5) >= uint(i6)) { - code 55 - } else { - code 54 - } - code 56 - if (uint(i5) >= uint(i6)) { - code 58 - } else { - code 57 - } - code 59 - if (uint(i5) >= uint(i6)) { - code 61 - } else { - code 60 - } - code 62 - if (uint(i5) >= uint(i6)) { - code 64 - } else { - code 63 - } - code 65 - if (uint(i5) >= uint(i6)) { - code 67 - } else { - code 66 - } - code 68 - if (uint(i5) >= uint(i6)) { - code 70 - } else { - code 69 - } - code 71 - if (uint(i5) >= uint(i6)) { - code 73 - } else { - code 72 - } - code 74 - if (uint(i5) >= uint(i6)) { - code 76 - } else { - code 75 - } - code 77 - if (uint(i5) >= uint(i6)) { - code 79 - } else { - code 78 - } - code 80 - if (uint(i5) >= uint(i6)) { - code 82 - } else { - code 81 - } - code 83 - if (uint(i5) >= uint(i6)) { - code 85 - } else { - code 84 - } - code 86 - if (uint(i5) >= uint(i6)) { - code 88 - } else { - code 87 - } - code 89 - if (uint(i5) >= uint(i6)) { - code 91 - } else { - code 90 - } - code 92 - if (uint(i5) >= uint(i6)) { - code 94 - } else { - code 93 - } - code 95 - if (uint(i5) >= uint(i6)) { - code 97 - } else { - code 96 - } - code 98 - if (uint(i5) >= uint(i6)) { - code 100 - } else { - code 99 - } - code 101 - if (!(i2 != 0)) { - break L26; - } +if (!(i2 == 0)) { + code 19 + while(1) { + code 20 + if (uint(i5) >= uint(i6)) { + code 22 + } else { + code 21 + } + code 23 + if (uint(i5) >= uint(i6)) { + code 25 + } else { + code 24 + } + code 26 + if (uint(i5) >= uint(i6)) { + code 28 + } else { + code 27 + } + code 29 + if (uint(i5) >= uint(i6)) { + code 31 + } else { + code 30 + } + code 32 + if (uint(i5) >= uint(i6)) { + code 34 + } else { + code 33 + } + code 35 + if (uint(i5) >= uint(i6)) { + code 37 + } else { + code 36 + } + code 38 + if (uint(i5) >= uint(i6)) { + code 40 + } else { + code 39 + } + code 41 + if (uint(i5) >= uint(i6)) { + code 43 + } else { + code 42 + } + code 44 + if (uint(i5) >= uint(i6)) { + code 46 + } else { + code 45 + } + code 47 + if (uint(i5) >= uint(i6)) { + code 49 + } else { + code 48 + } + code 50 + if (uint(i5) >= uint(i6)) { + code 52 + } else { + code 51 + } + code 53 + if (uint(i5) >= uint(i6)) { + code 55 + } else { + code 54 + } + code 56 + if (uint(i5) >= uint(i6)) { + code 58 + } else { + code 57 + } + code 59 + if (uint(i5) >= uint(i6)) { + code 61 + } else { + code 60 + } + code 62 + if (uint(i5) >= uint(i6)) { + code 64 + } else { + code 63 + } + code 65 + if (uint(i5) >= uint(i6)) { + code 67 + } else { + code 66 + } + code 68 + if (uint(i5) >= uint(i6)) { + code 70 + } else { + code 69 + } + code 71 + if (uint(i5) >= uint(i6)) { + code 73 + } else { + code 72 + } + code 74 + if (uint(i5) >= uint(i6)) { + code 76 + } else { + code 75 + } + code 77 + if (uint(i5) >= uint(i6)) { + code 79 + } else { + code 78 + } + code 80 + if (uint(i5) >= uint(i6)) { + code 82 + } else { + code 81 + } + code 83 + if (uint(i5) >= uint(i6)) { + code 85 + } else { + code 84 + } + code 86 + if (uint(i5) >= uint(i6)) { + code 88 + } else { + code 87 + } + code 89 + if (uint(i5) >= uint(i6)) { + code 91 + } else { + code 90 + } + code 92 + if (uint(i5) >= uint(i6)) { + code 94 + } else { + code 93 + } + code 95 + if (uint(i5) >= uint(i6)) { + code 97 + } else { + code 96 + } + code 98 + if (uint(i5) >= uint(i6)) { + code 100 + } else { + code 99 + } + code 101 + if (!(i2 != 0)) { + break; } } -} while(0); +} code 102 if (uint(i4) >= uint(i5)) { code 104 @@ -221,137 +217,135 @@ if (uint(i4) >= uint(i5)) { code 103 } code 105 -L143: do { - if (!(i2 == 0)) { - code 106 - while(1) { - code 107 - if (uint(i5) >= uint(i6)) { - code 109 - } else { - code 108 - } - code 110 - if (uint(i5) >= uint(i6)) { - code 112 - } else { - code 111 - } - code 113 - if (uint(i5) >= uint(i6)) { - code 115 - } else { - code 114 - } - code 116 - if (uint(i5) >= uint(i6)) { - code 118 - } else { - code 117 - } - code 119 - if (uint(i5) >= uint(i6)) { - code 121 - } else { - code 120 - } - code 122 - if (uint(i5) >= uint(i6)) { - code 124 - } else { - code 123 - } - code 125 - if (uint(i5) >= uint(i6)) { - code 127 - } else { - code 126 - } - code 128 - if (uint(i5) >= uint(i6)) { - code 130 - } else { - code 129 - } - code 131 - if (uint(i5) >= uint(i6)) { - code 133 - } else { - code 132 - } - code 134 - if (uint(i5) >= uint(i6)) { - code 136 - } else { - code 135 - } - code 137 - if (uint(i5) >= uint(i6)) { - code 139 - } else { - code 138 - } - code 140 - if (uint(i5) >= uint(i6)) { - code 142 - } else { - code 141 - } - code 143 - if (uint(i5) >= uint(i6)) { - code 145 - } else { - code 144 - } - code 146 - if (uint(i5) >= uint(i6)) { - code 148 - } else { - code 147 - } - code 149 - if (uint(i5) >= uint(i6)) { - code 151 - } else { - code 150 - } - code 152 - if (uint(i5) >= uint(i6)) { - code 154 - } else { - code 153 - } - code 155 - if (uint(i5) >= uint(i6)) { - code 157 - } else { - code 156 - } - code 158 - if (uint(i5) >= uint(i6)) { - code 160 - } else { - code 159 - } - code 161 - if (uint(i5) >= uint(i6)) { - code 163 - } else { - code 162 - } - code 164 - if (uint(i5) >= uint(i6)) { - code 166 - } else { - code 165 - } - code 167 - if (!(i2 != 0)) { - break L143; - } +if (!(i2 == 0)) { + code 106 + while(1) { + code 107 + if (uint(i5) >= uint(i6)) { + code 109 + } else { + code 108 + } + code 110 + if (uint(i5) >= uint(i6)) { + code 112 + } else { + code 111 + } + code 113 + if (uint(i5) >= uint(i6)) { + code 115 + } else { + code 114 + } + code 116 + if (uint(i5) >= uint(i6)) { + code 118 + } else { + code 117 + } + code 119 + if (uint(i5) >= uint(i6)) { + code 121 + } else { + code 120 + } + code 122 + if (uint(i5) >= uint(i6)) { + code 124 + } else { + code 123 + } + code 125 + if (uint(i5) >= uint(i6)) { + code 127 + } else { + code 126 + } + code 128 + if (uint(i5) >= uint(i6)) { + code 130 + } else { + code 129 + } + code 131 + if (uint(i5) >= uint(i6)) { + code 133 + } else { + code 132 + } + code 134 + if (uint(i5) >= uint(i6)) { + code 136 + } else { + code 135 + } + code 137 + if (uint(i5) >= uint(i6)) { + code 139 + } else { + code 138 + } + code 140 + if (uint(i5) >= uint(i6)) { + code 142 + } else { + code 141 + } + code 143 + if (uint(i5) >= uint(i6)) { + code 145 + } else { + code 144 + } + code 146 + if (uint(i5) >= uint(i6)) { + code 148 + } else { + code 147 + } + code 149 + if (uint(i5) >= uint(i6)) { + code 151 + } else { + code 150 + } + code 152 + if (uint(i5) >= uint(i6)) { + code 154 + } else { + code 153 + } + code 155 + if (uint(i5) >= uint(i6)) { + code 157 + } else { + code 156 + } + code 158 + if (uint(i5) >= uint(i6)) { + code 160 + } else { + code 159 + } + code 161 + if (uint(i5) >= uint(i6)) { + code 163 + } else { + code 162 + } + code 164 + if (uint(i5) >= uint(i6)) { + code 166 + } else { + code 165 + } + code 167 + if (!(i2 != 0)) { + break; } } -} while(0); +} code 168 if (uint(i4) >= uint(i5)) { code 170 diff --git a/src/settings.js b/src/settings.js index 7e607996..3a91b488 100644 --- a/src/settings.js +++ b/src/settings.js @@ -245,7 +245,8 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc']; // through LLVM dead code elimination, and also made accessible outside of // the generated code even after running closure compiler (on "Module"). // Note the necessary prefix of "_". -var EXPORT_ALL = 0; // If true, we export all the symbols +var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can + // still eliminate functions as dead. This just exports them on the Module object. var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This // is necessary to use the bindings generator with asm.js diff --git a/src/shell.js b/src/shell.js index 20db25a7..873bcc65 100644 --- a/src/shell.js +++ b/src/shell.js @@ -12,6 +12,10 @@ var ENVIRONMENT_IS_WEB = typeof window === 'object'; var ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; +if (typeof module === "object") { + module.exports = Module; +} + if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work // Note that we pollute the global namespace here, otherwise we break in node diff --git a/system/include/sys/socket.h b/system/include/sys/socket.h index 56a37375..b83ce89a 100644 --- a/system/include/sys/socket.h +++ b/system/include/sys/socket.h @@ -44,7 +44,7 @@ typedef unsigned int sa_family_t; struct sockaddr { sa_family_t sa_family; - char sa_data[]; + char sa_data[16]; }; struct sockaddr_storage { diff --git a/tests/runner.py b/tests/runner.py index 0f8883c8..5e101024 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -126,15 +126,6 @@ class RunnerCore(unittest.TestCase): def in_dir(self, *pathelems): return os.path.join(self.get_dir(), *pathelems) - def get_shared_library_name(self, linux_name): - if platform.system() == 'Linux': - return linux_name - elif platform.system() == 'Darwin': - return linux_name.replace('.so', '') + '.dylib' - else: - print >> sys.stderr, 'get_shared_library_name needs to be implemented on %s' % platform.system() - return linux_name - def get_stdout_path(self): return os.path.join(self.get_dir(), 'stdout') @@ -1957,6 +1948,39 @@ Succeeded! 1.000000=1.000000*2^0 -1.000000=-1.000000*2^0''') + def test_rounding(self): + src = ''' + #include <stdio.h> + #include <math.h> + + int main() + { + printf("%.1f ", round(1.4)); + printf("%.1f ", round(1.6)); + printf("%.1f ", round(-1.4)); + printf("%.1f ", round(-1.6)); + + printf("%.1f ", round(1.5)); + printf("%.1f ", round(2.5)); + printf("%.1f ", round(-1.5)); + printf("%.1f ", round(-2.5)); + + printf("%ld ", lrint(1.4)); + printf("%ld ", lrint(1.6)); + printf("%ld ", lrint(-1.4)); + printf("%ld ", lrint(-1.6)); + + printf("%ld ", lrint(1.5)); + printf("%ld ", lrint(2.5)); + printf("%ld ", lrint(-1.5)); + printf("%ld ", lrint(-2.5)); + + return 0; + } + ''' + self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 " + "1 2 -1 -2 2 2 -2 -2") + def test_getgep(self): # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP src = ''' @@ -6515,7 +6539,7 @@ Pass: 0.000012 0.000012''') int main(){ unsigned int a; float e, f, g; - sscanf("a 1.1 1.1 1.1", "%x %E %F %G", &a, &e, &f, &g); + sscanf("a 1.1 1.1 1.1", "%X %E %F %G", &a, &e, &f, &g); printf("%d %.1F %.1F %.1F\n", a, e, f, g); } ''' @@ -8389,7 +8413,7 @@ def process(filename): [os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/index.c.o'.split('/')), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/convert.c.o'.split('/')), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/__/common/color.c.o'.split('/')), - os.path.join('bin', self.get_shared_library_name('libopenjpeg.so.1.4.0'))], + os.path.join('bin', 'libopenjpeg.so.1.4.0')], configure=['cmake', '.'], #configure_args=['--enable-tiff=no', '--enable-jp3d=no', '--enable-png=no'], make_args=[]) # no -j 2, since parallel builds can fail @@ -12088,23 +12112,26 @@ elif 'browser' in str(sys.argv): setTimeout(doOne, 1000/60); } - function simulateKeyEvent(c) { + function keydown(c) { var event = document.createEvent("KeyboardEvent"); event.initKeyEvent("keydown", true, true, window, 0, 0, 0, 0, c, c); document.dispatchEvent(event); - var event2 = document.createEvent("KeyboardEvent"); - event2.initKeyEvent("keyup", true, true, window, + } + + function keyup(c) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keyup", true, true, window, 0, 0, 0, 0, c, c); - document.dispatchEvent(event2); + document.dispatchEvent(event); } ''') open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read())) Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() - self.run_browser('page.html', '', '/report_result?510510') + self.run_browser('page.html', '', '/report_result?223092870') def test_sdl_text(self): open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' @@ -13113,6 +13140,9 @@ elif 'benchmark' in str(sys.argv): class benchmark(RunnerCore): def print_stats(self, times, native_times, last=False, reps=TEST_REPS): + if reps == 0: + print '(no reps)' + return mean = sum(times)/len(times) squared_times = map(lambda x: x*x, times) mean_of_squared = sum(squared_times)/len(times) @@ -13501,6 +13531,7 @@ elif 'benchmark' in str(sys.argv): self.lua('scimark', '[small problem sizes]', output_parser=output_parser) def test_zzz_lua_binarytrees(self): + # js version: ['binarytrees.lua', {0: 0, 1: 9.5, 2: 11.99, 3: 12.85, 4: 14.72, 5: 15.82}[arguments[0]]] def args_processor(args): arg = int(DEFAULT_ARG) if arg == 0: @@ -13953,7 +13984,7 @@ fi assert os.path.exists(RELOOPER) == (i >= 2), 'have relooper on O2: ' + output src = open('a.out.js').read() main = src.split('function _main()')[1].split('\n}\n')[0] - assert ('while (1) {' in main or 'while(1){' in main) == (i >= 2), 'reloop code on O2: ' + main + assert ('while (1) {' in main or 'while(1){' in main or '} while ($' in main or '}while($' in main) == (i >= 2), 'reloop code on O2: ' + main assert ('switch' not in main) == (i >= 2), 'reloop code on O2: ' + main def test_jcache(self): diff --git a/tests/sdl_key.c b/tests/sdl_key.c index 19b0a3d6..7a304fc1 100644 --- a/tests/sdl_key.c +++ b/tests/sdl_key.c @@ -12,16 +12,30 @@ void one() { case SDL_KEYDOWN: break; case SDL_KEYUP: + // don't handle the modifier key events + if (event.key.keysym.sym == SDLK_LCTRL || + event.key.keysym.sym == SDLK_LSHIFT || + event.key.keysym.sym == SDLK_LALT) { + return; + } + if ((event.key.keysym.mod & KMOD_LCTRL) || (event.key.keysym.mod & KMOD_RCTRL)) { + result *= 2; + } + if ((event.key.keysym.mod & KMOD_LSHIFT) || (event.key.keysym.mod & KMOD_RSHIFT)) { + result *= 3; + } + if ((event.key.keysym.mod & KMOD_LALT) || (event.key.keysym.mod & KMOD_RALT)) { + result *= 5; + } switch (event.key.keysym.sym) { - case SDLK_RIGHT: printf("right\n"); result *= 2; break; - case SDLK_LEFT: printf("left\n"); result *= 3; break; - case SDLK_DOWN: printf("down\n"); result *= 5; break; - case SDLK_UP: printf("up\n"); result *= 7; break; - case SDLK_SPACE: printf("space\n"); result *= 11; break; - case SDLK_a: printf("a\n"); result *= 13; break; + case SDLK_RIGHT: printf("right\n"); result *= 7; break; + case SDLK_LEFT: printf("left\n"); result *= 11; break; + case SDLK_DOWN: printf("down\n"); result *= 13; break; + case SDLK_UP: printf("up\n"); result *= 17; break; + case SDLK_a: printf("a\n"); result *= 19; break; default: { if (event.key.keysym.scancode == SDL_SCANCODE_B) { - printf("b scancode\n"); result *= 17; break; + printf("b scancode\n"); result *= 23; break; } printf("unknown key: sym %d scancode %d\n", event.key.keysym.sym, event.key.keysym.scancode); REPORT_RESULT(); @@ -40,14 +54,13 @@ int main(int argc, char **argv) { SDL_Init(SDL_INIT_VIDEO); SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE); - emscripten_run_script("simulateKeyEvent(38)"); // up - emscripten_run_script("simulateKeyEvent(40)"); // down - emscripten_run_script("simulateKeyEvent(37)"); // left - emscripten_run_script("simulateKeyEvent(39)"); // right - emscripten_run_script("simulateKeyEvent(32)"); // space - emscripten_run_script("simulateKeyEvent(65)"); // a - emscripten_run_script("simulateKeyEvent(66)"); // b - emscripten_run_script("simulateKeyEvent(100)"); // trigger the end + emscripten_run_script("keydown(1250);keydown(38);keyup(38);keyup(1250);"); // alt, up + emscripten_run_script("keydown(1248);keydown(1249);keydown(40);keyup(40);keyup(1249);keyup(1248);"); // ctrl, shift, down + emscripten_run_script("keydown(37);keyup(37);"); // left + emscripten_run_script("keydown(39);keyup(39);"); // right + emscripten_run_script("keydown(65);keyup(65);"); // a + emscripten_run_script("keydown(66);keyup(66);"); // b + emscripten_run_script("keydown(100);keyup(100);"); // trigger the end if (argc == 1337) one(); // keep it alive diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index 41a2c2b6..2ce77b78 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -4961,4 +4961,151 @@ function _java_nio_charset_Charset_forNameInternal___java_lang_String($n1) { break; } } +function looop2() { + var i = 0; + while (1) { + do_it(); + i = i + 1 | 0; + if (condition(i)) { + break; + } + } +} +function looop3() { + var i = 0; + while (1) { + do_it(); + i = i + 1 | 0; + if (!condition(i)) { + break; + } + } +} +function looop4() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = i + 1 | 0; + f(i, helper); + if (condition()) { + i = helper; + } else { + break; + } + } +} +function looop4b() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = i + 1 | 0; + g(helper); + if (condition(i)) { + i = helper; + } else { + break; + } + } +} +function looop5() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = i + 1 | 0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } + moar(i); +} +function looop6() { + var i = 0; + while (1) { + do_it(); + i = i + 1 | 0; + if (!condition(i)) { + break; + } + } + moar(i); +} +function looop7() { + var $old_0_i107_i = 0, $current_0_i108_i = 0, $696 = 0; + $old_0_i107_i = $draw_left_i; + while (1) { + $current_0_i108_i = HEAP32[$old_0_i107_i >> 2] | 0; + if (($current_0_i108_i | 0) == 0) { + break; + } + $696 = $current_0_i108_i + 4 | 0; + if (($current_0_i108_i | 0) == ($P_3207_i | 0)) { + break; + } else { + $old_0_i107_i = $696; + } + } + HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; + while (1) {} +} +function multiloop($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $41 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $p_0 = $p_0 - 2 | 0; + $41 = HEAPU16[$p_0 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$p_0 >> 1] = $_off0; + $n_0 = $n_0 - 1 | 0; + if (($n_0 | 0) == 0) { + break; + } + } +} +function multiloop2($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $39 = $p_0 - 2 | 0; + $41 = HEAPU16[$39 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$39 >> 1] = $p_0; + $46 = $n_0 - 1 | 0; + if (($46 | 0) == 0) { + break; + } else { + $n_0 = $46; + $p_0 = $39; + } + } +} +function tempDouble2($46, $14, $28, $42, $20, $32, $45) { + $46 = $46 | 0; + $14 = $14 | 0; + $28 = $28 | 0; + $42 = $42 | 0; + $20 = $20 | 0; + $32 = $32 | 0; + $45 = $45 | 0; + var $_sroa_06_0_insert_insert$1 = 0; + $_sroa_06_0_insert_insert$1 = (HEAPF32[tempDoublePtr >> 2] = ($20 < $32 ? $20 : $32) - $42, HEAP32[tempDoublePtr >> 2] | 0) | 0; + HEAP32[$45 >> 2] = 0 | (HEAPF32[tempDoublePtr >> 2] = ($14 < $28 ? $14 : $28) - $42, HEAP32[tempDoublePtr >> 2] | 0); + HEAP32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1; + HEAP32[$45 + 8 >> 2] = $_sroa_06_0_insert_insert$1; +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index 9b9cd3f7..f45f082b 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -6678,5 +6678,165 @@ function _java_nio_charset_Charset_forNameInternal___java_lang_String($n1) { __THREW__ = threwValue = 0; break; } -}// 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"] +} +function looop2() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + break; + } else { + i = helper; + } + } +} +function looop3() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } +} +function looop4() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + f(i, helper); // i is used, cannot optimize! + if (condition()) { + i = helper; + } else { + break; + } + } +} +function looop4b() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + g(helper); + if (condition(i)) { // i is used, cannot optimize! + i = helper; + } else { + break; + } + } +} +function looop5() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } + moar(i); // i is still needed, cannot optimize! +} +function looop6() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } + moar(helper); // this is cool +} +function looop7() { + var $old_0_i107_i = 0, $current_0_i108_i = 0, $696 = 0; + $old_0_i107_i = $draw_left_i; + while (1) { + $current_0_i108_i = HEAP32[$old_0_i107_i >> 2] | 0; + if (($current_0_i108_i | 0) == 0) { + break; + } + $696 = $current_0_i108_i + 4 | 0; + if (($current_0_i108_i | 0) == ($P_3207_i | 0)) { + break; + } else { + $old_0_i107_i = $696; + } + } + HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; + // empty loop + while (1) { + } +} +function multiloop($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $39 = $p_0 - 2 | 0; + $41 = HEAPU16[$39 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$39 >> 1] = $_off0; + $46 = $n_0 - 1 | 0; + if (($46 | 0) == 0) { + break; + } else { + $n_0 = $46; + $p_0 = $39; + } + } +} +function multiloop2($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $39 = $p_0 - 2 | 0; + $41 = HEAPU16[$39 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$39 >> 1] = $p_0; // cannot optimize one, so none + $46 = $n_0 - 1 | 0; + if (($46 | 0) == 0) { + break; + } else { + $n_0 = $46; + $p_0 = $39; + } + } +} +function tempDouble2($46, $14, $28, $42, $20, $32, $45) { + $46 = $46 | 0; + $14 = $14 | 0; + $28 = $28 | 0; + $42 = $42 | 0; + $20 = $20 | 0; + $32 = $32 | 0; + $45 = $45 | 0; + var $46 = 0, $_sroa_06_0_insert_insert$1 = 0; + $46 = (HEAPF32[tempDoublePtr >> 2] = ($14 < $28 ? $14 : $28) - $42, HEAP32[tempDoublePtr >> 2] | 0); + $_sroa_06_0_insert_insert$1 = (HEAPF32[tempDoublePtr >> 2] = ($20 < $32 ? $20 : $32) - $42, HEAP32[tempDoublePtr >> 2] | 0) | 0; + HEAP32[$45 >> 2] = 0 | $46; + 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", "multiloop", "multiloop2", "tempDouble2"] diff --git a/tools/file_packager.py b/tools/file_packager.py index 6a8390ad..1443d165 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -240,7 +240,7 @@ if crunch: #if not os.path.exists(os.path.basename(crunch_name)): # print >> sys.stderr, 'Failed to crunch, perhaps a weird dxt format? Looking for a source PNG for the DDS' # Popen([CRUNCH, '-file', unsuffixed(file_['srcpath']) + '.png', '-quality', crunch] + format, stdout=sys.stderr).communicate() - assert os.path.exists(os.path.basename(src_crunch_name)), 'crunch failed to generate output' + assert os.path.exists(src_crunch_name), 'crunch failed to generate output' # prepend the dds header crunched = open(src_crunch_name, 'rb').read() c = open(src_crunch_name, 'wb') diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 09791150..07317e0a 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -402,10 +402,12 @@ function removeUnneededLabelSettings(ast) { // Various expression simplifications. Pre run before closure (where we still have metadata), Post run after. +var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^'); + function simplifyExpressionsPre(ast) { // Look for (x&A)<<B>>B and replace it with X&A if possible. function simplifySignExtends(ast) { - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'binary' && node[1] == '>>' && node[3][0] == 'num' && node[2][0] == 'binary' && node[2][1] == '<<' && node[2][3][0] == 'num' && node[3][1] == node[2][3][1]) { var innerNode = node[2][2]; @@ -427,13 +429,12 @@ function simplifyExpressionsPre(ast) { // 'useful' mathops already |0 anyhow. function simplifyBitops(ast) { - var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^'); var SAFE_BINARY_OPS = set('+', '-', '*'); // division is unsafe as it creates non-ints in JS; mod is unsafe as signs matter so we can't remove |0's var ZERO = ['num', 0]; var rerun = true; while (rerun) { rerun = false; - traverseGenerated(ast, function process(node, type, stack) { + traverse(ast, function process(node, type, stack) { if (type == 'binary' && node[1] == '|') { if (node[2][0] == 'num' && node[3][0] == 'num') { return ['num', node[2][1] | node[3][1]]; @@ -479,8 +480,12 @@ function simplifyExpressionsPre(ast) { return true; } - traverseGenerated(ast, function(node, type) { - if (type == 'binary' && node[1] == '&' && node[3][0] == 'num') { + var hasTempDoublePtr = false; + + traverse(ast, function(node, type) { + if (type == 'name') { + if (node[1] == 'tempDoublePtr') hasTempDoublePtr = true; + } else if (type == 'binary' && node[1] == '&' && node[3][0] == 'num') { if (node[2][0] == 'num') return ['num', node[2][1] & node[3][1]]; var input = node[2]; var amount = node[3][1]; @@ -522,12 +527,133 @@ function simplifyExpressionsPre(ast) { return node; } } + } else if (type == 'assign') { + // optimizations for assigning into HEAP32 specifically + if (node[1] === true && node[2][0] == 'sub' && node[2][1][0] == 'name' && node[2][1][1] == 'HEAP32') { + // HEAP32[..] = x | 0 does not need the | 0 (unless it is a mandatory |0 of a call) + if (node[3][0] == 'binary' && node[3][1] == '|') { + if (node[3][2][0] == 'num' && node[3][2][1] == 0 && node[3][3][0] != 'call') { + node[3] = node[3][3]; + } else if (node[3][3][0] == 'num' && node[3][3][1] == 0 && node[3][2][0] != 'call') { + node[3] = node[3][2]; + } + } + } + var value = node[3]; + if (value[0] == 'binary' && value[1] == '|') { + // canonicalize order of |0 to end + if (value[2][0] == 'num' && value[2][1] == 0) { + var temp = value[2]; + value[2] = value[3]; + value[3] = temp; + } + // if a seq ends in an |0, remove an external |0 + // note that it is only safe to do this in assigns, like we are doing here (return (x, y|0); is not valid) + if (value[2][0] == 'seq' && value[2][2][0] == 'binary' && value[2][2][1] in USEFUL_BINARY_OPS) { + node[3] = value[2]; + } + } } }); if (asm) { + if (hasTempDoublePtr) { + traverse(ast, function(node, type) { + if (type == 'assign') { + if (node[1] === true && node[2][0] == 'sub' && node[2][1][0] == 'name' && node[2][1][1] == 'HEAP32') { + // remove bitcasts that are now obviously pointless, e.g. + // HEAP32[$45 >> 2] = HEAPF32[tempDoublePtr >> 2] = ($14 < $28 ? $14 : $28) - $42, HEAP32[tempDoublePtr >> 2] | 0; + var value = node[3]; + if (value[0] == 'seq' && value[1][0] == 'assign' && value[1][2][0] == 'sub' && value[1][2][1][0] == 'name' && value[1][2][1][1] == 'HEAPF32' && + value[1][2][2][0] == 'binary' && value[1][2][2][2][0] == 'name' && value[1][2][2][2][1] == 'tempDoublePtr') { + // transform to HEAPF32[$45 >> 2] = ($14 < $28 ? $14 : $28) - $42; + node[2][1][1] = 'HEAPF32'; + node[3] = value[1][3]; + } + } + } else if (type == 'seq') { + // (HEAP32[tempDoublePtr >> 2] = HEAP32[$37 >> 2], +HEAPF32[tempDoublePtr >> 2]) + // ==> + // +HEAPF32[$37 >> 2] + if (node[0] == 'seq' && node[1][0] == 'assign' && node[1][2][0] == 'sub' && node[1][2][1][0] == 'name' && + (node[1][2][1][1] == 'HEAP32' || node[1][2][1][1] == 'HEAPF32') && + node[1][2][2][0] == 'binary' && node[1][2][2][2][0] == 'name' && node[1][2][2][2][1] == 'tempDoublePtr' && + node[1][3][0] == 'sub' && node[1][3][1][0] == 'name' && (node[1][3][1][1] == 'HEAP32' || node[1][3][1][1] == 'HEAPF32')) { + if (node[1][2][1][1] == 'HEAP32') { + node[1][3][1][1] = 'HEAPF32'; + return ['unary-prefix', '+', node[1][3]]; + } else { + node[1][3][1][1] = 'HEAP32'; + return ['binary', '|', node[1][3], ['num', 0]]; + } + } + } + }); + + // finally, wipe out remaining ones by finding cases where all assignments to X are bitcasts, and all uses are writes to + // the other heap type, then eliminate the bitcast + var bitcastVars = {}; + traverse(ast, function(node, type) { + if (type == 'assign' && node[1] === true && node[2][0] == 'name') { + var value = node[3]; + if (value[0] == 'seq' && value[1][0] == 'assign' && value[1][2][0] == 'sub' && value[1][2][1][0] == 'name' && + (value[1][2][1][1] == 'HEAP32' || value[1][2][1][1] == 'HEAPF32') && + value[1][2][2][0] == 'binary' && value[1][2][2][2][0] == 'name' && value[1][2][2][2][1] == 'tempDoublePtr') { + var name = node[2][1]; + if (!bitcastVars[name]) bitcastVars[name] = { + define_HEAP32: 0, define_HEAPF32: 0, use_HEAP32: 0, use_HEAPF32: 0, bad: false, namings: 0, defines: [], uses: [] + }; + bitcastVars[name]['define_' + value[1][2][1][1]]++; + bitcastVars[name].defines.push(node); + } + } + }); + traverse(ast, function(node, type) { + if (type == 'name' && bitcastVars[node[1]]) { + bitcastVars[node[1]].namings++; + } else if (type == 'assign' && node[1] === true) { + var value = node[3]; + if (value[0] == 'name') { + var name = value[1]; + if (bitcastVars[name]) { + var target = node[2]; + if (target[0] == 'sub' && target[1][0] == 'name' && (target[1][1] == 'HEAP32' || target[1][1] == 'HEAPF32')) { + bitcastVars[name]['use_' + target[1][1]]++; + bitcastVars[name].uses.push(node); + } + } + } + } + }); + var asmData = normalizeAsm(ast); + for (var v in bitcastVars) { + var info = bitcastVars[v]; + // good variables define only one type, use only one type, have definitions and uses, and define as a different type than they use + if (info.define_HEAP32*info.define_HEAPF32 == 0 && info.use_HEAP32*info.use_HEAPF32 == 0 && + info.define_HEAP32+info.define_HEAPF32 > 0 && info.use_HEAP32+info.use_HEAPF32 > 0 && + info.define_HEAP32*info.use_HEAP32 == 0 && info.define_HEAPF32*info.use_HEAPF32 == 0 && + v in asmData.vars && info.namings == info.define_HEAP32+info.define_HEAPF32+info.use_HEAP32+info.use_HEAPF32) { + var correct = info.use_HEAP32 ? 'HEAPF32' : 'HEAP32'; + info.defines.forEach(function(define) { + define[3] = define[3][1][3]; + if (correct == 'HEAP32') { + define[3] = ['binary', '|', define[3], ['num', 0]]; + } else { + define[3] = ['unary-prefix', '+', define[3]]; + } + // do we want a simplifybitops on the new values here? + }); + info.uses.forEach(function(use) { + use[2][1][1] = correct; + }); + asmData.vars[v] = 1 - asmData.vars[v]; + } + } + denormalizeAsm(ast, asmData); + } + // optimize num >> num, in asm we need this here since we do not run optimizeShifts - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'binary' && node[1] == '>>' && node[2][0] == 'num' && node[3][0] == 'num') { node[0] = 'num'; node[1] = node[2][1] >> node[3][1]; @@ -543,7 +669,7 @@ function simplifyExpressionsPre(ast) { var rerun = true; while (rerun) { rerun = false; - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'binary' && node[1] == '+') { if (node[2][0] == 'num' && node[3][0] == 'num') { rerun = true; @@ -566,7 +692,7 @@ function simplifyExpressionsPre(ast) { // if (x == 0) can be if (!x), etc. function simplifyZeroComp(ast) { - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { var binary; if (type == 'if' && (binary = node[1])[0] == 'binary') { if ((binary[1] == '!=' || binary[1] == '!==') && binary[3][0] == 'num' && binary[3][1] == 0) { @@ -580,40 +706,40 @@ function simplifyExpressionsPre(ast) { }); } - function asmOpts(ast) { + function asmOpts(fun) { // 1. Add final returns when necessary // 2. Remove unneeded coercions on function calls that have no targets (eliminator removed it) - traverseGeneratedFunctions(ast, function(fun) { - var returnType = null; - traverse(fun, function(node, type) { - if (type == 'return' && node[1]) { - returnType = detectAsmCoercion(node[1]); - } else if (type == 'stat') { - var inner = node[1]; - if ((inner[0] == 'binary' && inner[1] in ASSOCIATIVE_BINARIES && inner[2][0] == 'call' && inner[3][0] == 'num') || - (inner[0] == 'unary-prefix' && inner[1] == '+' && inner[2][0] == 'call')) { - node[1] = inner[2]; - } - } - }); - // Add a final return if one is missing. - if (returnType !== null) { - var stats = getStatements(fun); - var last = stats[stats.length-1]; - if (last[0] != 'return') { - var returnValue = ['num', 0]; - if (returnType == ASM_DOUBLE) returnValue = ['unary-prefix', '+', returnValue]; - stats.push(['return', returnValue]); + var returnType = null; + traverse(fun, function(node, type) { + if (type == 'return' && node[1]) { + returnType = detectAsmCoercion(node[1]); + } else if (type == 'stat') { + var inner = node[1]; + if ((inner[0] == 'binary' && inner[1] in ASSOCIATIVE_BINARIES && inner[2][0] == 'call' && inner[3][0] == 'num') || + (inner[0] == 'unary-prefix' && inner[1] == '+' && inner[2][0] == 'call')) { + node[1] = inner[2]; } } }); + // Add a final return if one is missing. + if (returnType !== null) { + var stats = getStatements(fun); + var last = stats[stats.length-1]; + if (last[0] != 'return') { + var returnValue = ['num', 0]; + if (returnType == ASM_DOUBLE) returnValue = ['unary-prefix', '+', returnValue]; + stats.push(['return', returnValue]); + } + } } - simplifySignExtends(ast); - simplifyBitops(ast); - joinAdditions(ast); - // simplifyZeroComp(ast); TODO: investigate performance - if (asm) asmOpts(ast); + traverseGeneratedFunctions(ast, function(func) { + simplifySignExtends(func); + simplifyBitops(func); + joinAdditions(func); + // simplifyZeroComp(func); TODO: investigate performance + if (asm) asmOpts(func); + }); } // In typed arrays mode 2, we can have @@ -950,24 +1076,28 @@ function optimizeShiftsAggressive(ast) { // we then get // if (!(x < 5)) // or such. Simplifying these saves space and time. -function simplifyNotComps(ast) { - traverse(ast, function(node, type) { - if (type == 'unary-prefix' && node[1] == '!' && node[2][0] == 'binary') { - if (node[2][1] == '<') { - return ['binary', '>=', node[2][2], node[2][3]]; - } else if (node[2][1] == '>') { - return ['binary', '<=', node[2][2], node[2][3]]; - } else if (node[2][1] == '==') { - return ['binary', '!=', node[2][2], node[2][3]]; - } else if (node[2][1] == '!=') { - return ['binary', '==', node[2][2], node[2][3]]; - } else if (node[2][1] == '===') { - return ['binary', '!==', node[2][2], node[2][3]]; - } else if (node[2][1] == '!==') { - return ['binary', '===', node[2][2], node[2][3]]; - } +function simplifyNotCompsDirect(node) { + if (node[0] == 'unary-prefix' && node[1] == '!') { + if (node[2][0] == 'binary') { + switch(node[2][1]) { + case '<': return ['binary', '>=', node[2][2], node[2][3]]; + case '>': return ['binary', '<=', node[2][2], node[2][3]]; + case '<=': return ['binary', '>', node[2][2], node[2][3]]; + case '>=': return ['binary', '<', node[2][2], node[2][3]]; + case '==': return ['binary', '!=', node[2][2], node[2][3]]; + case '!=': return ['binary', '==', node[2][2], node[2][3]]; + case '===': return ['binary', '!==', node[2][2], node[2][3]]; + case '!==': return ['binary', '===', node[2][2], node[2][3]]; + } + } else if (node[2][0] == 'unary-prefix' && node[2][1] == '!') { + return node[2][2]; } - }); + } + return node; +} + +function simplifyNotComps(ast) { + traverse(ast, simplifyNotCompsDirect); } function simplifyExpressionsPost(ast) { @@ -1216,8 +1346,7 @@ function hoistMultiples(ast) { var temp = node[3]; node[3] = node[2]; node[2] = temp; - node[1] = ['unary-prefix', '!', node[1]]; - simplifyNotComps(node[1]); // bake the ! into the expression + node[1] = simplifyNotCompsDirect(['unary-prefix', '!', node[1]]); stat1 = node[2][1]; stat2 = node[3][1]; } @@ -1844,6 +1973,13 @@ var NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS = set('name', 'num', 'string', 'binar var IGNORABLE_ELIMINATOR_SCAN_NODES = set('num', 'toplevel', 'string', 'break', 'continue', 'dot'); // dot can only be STRING_TABLE.* var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', 'for', 'while', 'array', 'throw'); // we could handle some of these, TODO, but nontrivial (e.g. for while, the condition is hit multiple times after the body) +function isTempDoublePtrAccess(node) { // these are used in bitcasts; they are not really affecting memory, and should cause no invalidation + assert(node[0] == 'sub'); + return (node[2][0] == 'name' && node[2][1] == 'tempDoublePtr') || + (node[2][0] == 'binary' && ((node[2][2][0] == 'name' && node[2][2][1] == 'tempDoublePtr') || + (node[2][3][0] == 'name' && node[2][3][1] == 'tempDoublePtr'))); +} + function eliminate(ast, memSafe) { // Find variables that have a single use, and if they can be eliminated, do so traverseGeneratedFunctions(ast, function(func, type) { @@ -1853,6 +1989,7 @@ function eliminate(ast, memSafe) { // First, find the potentially eliminatable functions: that have one definition and one use var definitions = {}; var uses = {}; + var namings = {}; var values = {}; var locals = {}; var varsToRemove = {}; // variables being removed, that we can eliminate all 'var x;' of (this refers to 'var' nodes we should remove) @@ -1895,6 +2032,8 @@ function eliminate(ast, memSafe) { if (!values[name]) values[name] = node[3]; if (node[1] === true) { // not +=, -= etc., just = uses[name]--; // because the name node will show up by itself in the previous case + if (!namings[name]) namings[name] = 0; + namings[name]++; // offset it here, this tracks the total times we are named } } } else if (type == 'switch') { @@ -1902,6 +2041,10 @@ function eliminate(ast, memSafe) { } }); + for (var used in uses) { + namings[used] = (namings[used] || 0) + uses[used]; + } + // we cannot eliminate variables if there is a switch if (hasSwitch && !asm) return; @@ -1971,7 +2114,7 @@ function eliminate(ast, memSafe) { //printErr('locals: ' + JSON.stringify(locals)); //printErr('varsToRemove: ' + JSON.stringify(varsToRemove)); //printErr('varsToTryToRemove: ' + JSON.stringify(varsToTryToRemove)); - definitions = values = null; + values = null; //printErr('potentials: ' + JSON.stringify(potentials)); // We can now proceed through the function. In each list of statements, we try to eliminate var tracked = {}; @@ -2121,7 +2264,7 @@ function eliminate(ast, memSafe) { if (allowTracking) track(name, node[3], node); } } else if (target[0] == 'sub') { - if (!memoryInvalidated) { + if (!isTempDoublePtrAccess(target) && !memoryInvalidated) { invalidateMemory(); memoryInvalidated = true; } @@ -2129,7 +2272,8 @@ function eliminate(ast, memSafe) { } else if (type == 'sub') { traverseInOrder(node[1], false, !memSafe); // evaluate inner traverseInOrder(node[2]); // evaluate outer - if (!ignoreSub) { // ignoreSub means we are a write (happening later), not a read + // ignoreSub means we are a write (happening later), not a read + if (!ignoreSub && !isTempDoublePtrAccess(node)) { // do the memory access if (!callsInvalidated) { invalidateCalls(); @@ -2326,9 +2470,11 @@ function eliminate(ast, memSafe) { //printErr('delete StatBlock'); }); - // clean up vars - //printErr('cleaning up ' + JSON.stringify(varsToRemove)); + var seenUses = {}, helperReplacements = {}; // for looper-helper optimization + + // clean up vars, and loop variable elimination traverse(func, function(node, type) { + // pre if (type === 'var') { node[1] = node[1].filter(function(pair) { return !varsToRemove[pair[0]] }); if (node[1].length == 0) { @@ -2337,6 +2483,103 @@ function eliminate(ast, memSafe) { node[1] = []; } } + }, function(node, type) { + // post + if (type == 'name') { + var name = node[1]; + if (name in helperReplacements) { + node[1] = helperReplacements[name]; + return; // no need to track this anymore, we can't loop-optimize more than once + } + // track how many uses we saw. we need to know when a variable is no longer used (hence we run this in the post) + if (!(name in seenUses)) { + seenUses[name] = 1; + } else { + seenUses[name]++; + } + } else if (type == 'while') { + // try to remove loop helper variables specifically + var stats = node[2][1]; + var last = stats[stats.length-1]; + if (last && last[0] == 'if' && last[2][0] == 'block' && last[3] && last[3][0] == 'block') { + var ifTrue = last[2]; + var ifFalse = last[3]; + var flip = false; + if (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') { + var assigns = ifFalse[1]; + var loopers = [], helpers = []; + for (var i = 0; i < assigns.length; i++) { + if (assigns[i][0] == 'stat' && assigns[i][1][0] == 'assign') { + var assign = assigns[i][1]; + if (assign[1] === true && assign[2][0] == 'name' && assign[3][0] == 'name') { + var looper = assign[2][1]; + var helper = assign[3][1]; + if (definitions[helper] == 1 && seenUses[looper] == namings[looper] && + !helperReplacements[helper] && !helperReplacements[looper]) { + loopers.push(looper); + helpers.push(helper); + } + } + } + } + if (loopers.length < assigns.length) return; // TODO: handle the case where can can just eliminate one. (we can't optimize the break, but we can remove the var at least) + for (var l = 0; l < loopers.length; l++) { + var looper = loopers[l]; + var helper = helpers[l]; + // the remaining issue is whether loopers are used after the assignment to helper and before the last line (where we assign to it) + var found = -1; + for (var i = stats.length-2; i >= 0; i--) { + var curr = stats[i]; + if (curr[0] == 'stat' && curr[1][0] == 'assign') { + var currAssign = curr[1]; + if (currAssign[1] === true && currAssign[2][0] == 'name') { + var to = currAssign[2][1]; + if (to == helper) { + found = i; + break; + } + } + } + } + if (found < 0) return; + var looperUsed = false; + for (var i = found+1; i < stats.length && !looperUsed; i++) { + var curr = i < stats.length-1 ? stats[i] : last[1]; // on the last line, just look in the condition + traverse(curr, function(node, type) { + if (type == 'name' && node[1] == looper) { + looperUsed = true; + return true; + } + }); + } + if (looperUsed) return; + } + // hurrah! this is safe to do + for (var l = 0; l < loopers.length; l++) { + var looper = loopers[l]; + var helper = helpers[l]; + varsToRemove[helper] = 2; + traverse(node, function(node, type) { // replace all appearances of helper with looper + if (type == 'name' && node[1] == helper) node[1] = looper; + }); + helperReplacements[helper] = looper; // replace all future appearances of helper with looper + helperReplacements[looper] = looper; // avoid any further attempts to optimize looper in this manner (seenUses is wrong anyhow, too) + } + // simplify the if. we remove the if branch, leaving only the else + if (flip) { + last[1] = simplifyNotCompsDirect(['unary-prefix', '!', last[1]]); + last[2] = last[3]; + } + last.pop(); + } + } + } }); if (asm) { @@ -2455,6 +2698,27 @@ function fixDotZero(js) { }); } +function asmLoopOptimizer(ast) { + traverseGeneratedFunctions(ast, function(fun) { + // This is at the end of the pipeline, we can assume all other optimizations are done, and we modify loops + // into shapes that might confuse other passes + traverse(fun, function(node, type) { + if (type == 'while' && node[1][0] == 'num' && node[1][1] == 1 && node[2][0] == 'block') { + // while (1) { .. if (..) { break } } ==> do { .. } while(..) + var stats = node[2][1]; + var last = stats[stats.length-1]; + if (last && last[0] == 'if' && !last[3] && last[2][0] == 'block' && last[2][1][0][0] == 'break' && !last[2][1][0][1]) { + var conditionToBreak = last[1]; + stats.pop(); + node[0] = 'do'; + node[1] = simplifyNotCompsDirect(['unary-prefix', '!', conditionToBreak]); + return node; + } + } + }); + }); +} + // Passes table var compress = false, printMetadata = true, asm = false, last = false; @@ -2498,6 +2762,7 @@ arguments_.slice(1).forEach(function(arg) { passes[arg](ast); }); if (asm && last) { + asmLoopOptimizer(ast); prepDotZero(ast); } var js = astToSrc(ast, compress), old; diff --git a/tools/shared.py b/tools/shared.py index 799a5bbe..8a172d9c 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -295,7 +295,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.4.7' +EMSCRIPTEN_VERSION = '1.4.9' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js index c10cc6b0..1dacd0cd 100644 --- a/tools/test-js-optimizer-asm-last-output.js +++ b/tools/test-js-optimizer-asm-last-output.js @@ -32,4 +32,15 @@ function finall(x) { a = -0xde0b6b000000000; return 12.0e10; } +function looop() { + do { + do_it(); + } while (!condition()); + do { + do_it(); + } while (a <= b); + do { + do_it(); + } while (x()); +} diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js index 6e97b687..e83822f8 100644 --- a/tools/test-js-optimizer-asm-last.js +++ b/tools/test-js-optimizer-asm-last.js @@ -32,4 +32,25 @@ function finall(x) { a = -0xde0b6b000000000; return +12e10; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall"] +function looop() { + while (1) { + do_it(); + if (condition()) { + break; + } + } + while (1) { + do_it(); + if (a > b) { + break; + } + } + while (1) { + do_it(); + if (!x()) { + break; + } + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall", "looop"] + diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js index ab953e5d..25d521ab 100644 --- a/tools/test-js-optimizer-asm-pre-output.js +++ b/tools/test-js-optimizer-asm-pre-output.js @@ -8,6 +8,7 @@ function a() { HEAP[1024] = 5; whee(12, 13); whee(12, 13); + f((g = t(), g + g | 0) | 0); } function b($this, $__n) { $this = $this | 0; @@ -107,4 +108,57 @@ function sign_extension_simplification() { print(5); } } +function tempDoublePtr($45, $14, $28, $42) { + $45 = $45 | 0; + $14 = $14 | 0; + $28 = $28 | 0; + $42 = $42 | 0; + var unelim = +0, bad = 0, unelim2 = +0; + unelim = +(127.5 * +$14); + HEAPF32[$45 >> 2] = ($14 < $28 ? $14 : $28) - $42; + HEAP32[$world + 102916 >> 2] = _malloc(192) | 0; + f(+HEAPF32[$45 >> 2]); + g(HEAP32[$14 >> 2] | 0); + $42 = +HEAPF32[$42 >> 2]; + ch($42); + HEAPF32[$45 >> 2] = unelim; + moar(); + bad = (HEAPF32[tempDoublePtr >> 2] = 127.5 * +$14, HEAP32[tempDoublePtr >> 2] | 0); + func(); + HEAP32[4] = bad; + HEAP32[5] = bad + 1; + moar(); + unelim2 = 127 + $14 | 0; + func(); + HEAP32[4] = unelim2; +} +function boxx($this, $aabb, $xf, $childIndex) { + $this = $this | 0; + $aabb = $aabb | 0; + $xf = $xf | 0; + $childIndex = $childIndex | 0; + var $2 = +0, $4 = +0, $7 = +0, $9 = +0, $13 = +0, $14 = +0, $19 = +0, $20 = +0, $22 = +0, $25 = +0, $28 = +0, $32 = +0, $42 = +0, $45 = 0, $_sroa_06_0_insert_insert$1 = +0, $51 = 0, $_sroa_0_0_insert_insert$1 = +0; + $2 = +HEAPF32[$xf + 12 >> 2]; + $4 = +HEAPF32[$this + 12 >> 2]; + $7 = +HEAPF32[$xf + 8 >> 2]; + $9 = +HEAPF32[$this + 16 >> 2]; + $13 = +HEAPF32[$xf >> 2]; + $14 = $13 + ($2 * $4 - $7 * $9); + $19 = +HEAPF32[$xf + 4 >> 2]; + $20 = $4 * $7 + $2 * $9 + $19; + $22 = +HEAPF32[$this + 20 >> 2]; + $25 = +HEAPF32[$this + 24 >> 2]; + $28 = $13 + ($2 * $22 - $7 * $25); + $32 = $19 + ($7 * $22 + $2 * $25); + $42 = +HEAPF32[$this + 8 >> 2]; + $45 = $aabb; + $_sroa_06_0_insert_insert$1 = +(($20 < $32 ? $20 : $32) - $42); + HEAPF32[$45 >> 2] = ($14 < $28 ? $14 : $28) - $42; + HEAPF32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1; + $51 = $aabb + 8 | 0; + $_sroa_0_0_insert_insert$1 = +($42 + ($20 > $32 ? $20 : $32)); + HEAPF32[$51 >> 2] = $42 + ($14 > $28 ? $14 : $28); + HEAPF32[$51 + 4 >> 2] = $_sroa_0_0_insert_insert$1; + return; +} diff --git a/tools/test-js-optimizer-asm-pre.js b/tools/test-js-optimizer-asm-pre.js index 264587d2..38487d2f 100644 --- a/tools/test-js-optimizer-asm-pre.js +++ b/tools/test-js-optimizer-asm-pre.js @@ -8,6 +8,7 @@ function a() { HEAP[(4096 & 8191) >> 2] = 5; whee(12, 13) | 0; +whee(12, 13); + f((g = t(), (g+g)|0)|0); } function b($this, $__n) { $this = $this | 0; @@ -109,4 +110,59 @@ function sign_extension_simplification() { print(5); } } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "rett", "ret2t", "retf", "i32_8"] +function tempDoublePtr($45, $14, $28, $42) { + $45 = $45 | 0; + $14 = $14 | 0; + $28 = $28 | 0; + $42 = $42 | 0; + var unelim = 0; // only used as assign to int heap, so can avoid bitcast in definition + var bad = 0; + var unelim2 = 0; // opposite types + unelim = (HEAPF32[tempDoublePtr >> 2] = 127.5 * +$14, HEAP32[tempDoublePtr >> 2] | 0); + HEAP32[$45 >> 2] = 0 | (HEAPF32[tempDoublePtr >> 2] = ($14 < $28 ? $14 : $28) - $42, HEAP32[tempDoublePtr >> 2] | 0); + HEAP32[$world + 102916 >> 2] = _malloc(192) | 0; + f((HEAP32[tempDoublePtr >> 2] = HEAP32[$45 >> 2], +HEAPF32[tempDoublePtr >> 2])); + g((HEAPF32[tempDoublePtr >> 2] = HEAPF32[$14 >> 2], HEAP32[tempDoublePtr >> 2] | 0)); + $42 = (HEAP32[tempDoublePtr >> 2] = HEAP32[$42 >> 2] | 0, +HEAPF32[tempDoublePtr >> 2]); + ch($42); + HEAP32[$45 >> 2] = unelim; + moar(); + bad = (HEAPF32[tempDoublePtr >> 2] = 127.5 * +$14, HEAP32[tempDoublePtr >> 2] | 0); + func(); + HEAP32[4] = bad; + HEAP32[5] = (bad + 1) | 0; + moar(); + unelim2 = (HEAP32[tempDoublePtr >> 2] = 127 + $14, +HEAPF32[tempDoublePtr >> 2]); + func(); + HEAPF32[4] = unelim2; +} +function boxx($this, $aabb, $xf, $childIndex) { + $this = $this | 0; + $aabb = $aabb | 0; + $xf = $xf | 0; + $childIndex = $childIndex | 0; + var $2 = +0, $4 = +0, $7 = +0, $9 = +0, $13 = +0, $14 = +0, $19 = +0, $20 = +0, $22 = +0, $25 = +0, $28 = +0, $32 = +0, $42 = +0, $45 = 0, $_sroa_06_0_insert_insert$1 = 0, $51 = 0, $_sroa_0_0_insert_insert$1 = 0; + $2 = +HEAPF32[$xf + 12 >> 2]; + $4 = +HEAPF32[$this + 12 >> 2]; + $7 = +HEAPF32[$xf + 8 >> 2]; + $9 = +HEAPF32[$this + 16 >> 2]; + $13 = +HEAPF32[$xf >> 2]; + $14 = $13 + ($2 * $4 - $7 * $9); + $19 = +HEAPF32[$xf + 4 >> 2]; + $20 = $4 * $7 + $2 * $9 + $19; + $22 = +HEAPF32[$this + 20 >> 2]; + $25 = +HEAPF32[$this + 24 >> 2]; + $28 = $13 + ($2 * $22 - $7 * $25); + $32 = $19 + ($7 * $22 + $2 * $25); + $42 = +HEAPF32[$this + 8 >> 2]; + $45 = $aabb; + $_sroa_06_0_insert_insert$1 = (HEAPF32[tempDoublePtr >> 2] = ($20 < $32 ? $20 : $32) - $42, HEAP32[tempDoublePtr >> 2] | 0) | 0; + HEAPF32[$45 >> 2] = ($14 < $28 ? $14 : $28) - $42; + HEAP32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1; + $51 = $aabb + 8 | 0; + $_sroa_0_0_insert_insert$1 = (HEAPF32[tempDoublePtr >> 2] = $42 + ($20 > $32 ? $20 : $32), HEAP32[tempDoublePtr >> 2] | 0) | 0; + HEAPF32[$51 >> 2] = $42 + ($14 > $28 ? $14 : $28); + HEAP32[$51 + 4 >> 2] = $_sroa_0_0_insert_insert$1; + return; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "rett", "ret2t", "retf", "i32_8", "tempDoublePtr", "boxx"] |