summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rwxr-xr-xemscripten.py3
-rw-r--r--src/analyzer.js12
-rw-r--r--src/headless.js14
-rw-r--r--src/library.js45
-rw-r--r--src/library_sdl.js116
-rw-r--r--src/modules.js1
-rw-r--r--src/parseTools.js2
-rw-r--r--src/relooper/Relooper.cpp95
-rw-r--r--src/relooper/Relooper.h1
-rw-r--r--src/relooper/test.cpp27
-rw-r--r--src/relooper/test.txt17
-rw-r--r--src/relooper/test3.txt4
-rw-r--r--src/relooper/test_inf.txt648
-rw-r--r--src/settings.js3
-rw-r--r--src/shell.js4
-rw-r--r--system/include/sys/socket.h2
-rwxr-xr-xtests/runner.py65
-rw-r--r--tests/sdl_key.c43
-rw-r--r--tools/eliminator/asm-eliminator-test-output.js147
-rw-r--r--tools/eliminator/asm-eliminator-test.js162
-rw-r--r--tools/file_packager.py2
-rw-r--r--tools/js-optimizer.js383
-rw-r--r--tools/shared.py2
-rw-r--r--tools/test-js-optimizer-asm-last-output.js11
-rw-r--r--tools/test-js-optimizer-asm-last.js23
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js54
-rw-r--r--tools/test-js-optimizer-asm-pre.js58
28 files changed, 1452 insertions, 495 deletions
diff --git a/AUTHORS b/AUTHORS
index 7196711d..9e9d5748 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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"]