From 667298f5be78a8fb4bc126d20551b7c639b742b1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 18 Nov 2013 14:48:12 -0800 Subject: [PATCH 01/14] disable closure in asm2f.test_exceptions due to closure issue with 458ac87 --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 67e316e41a510..1803c926d6789 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2631,7 +2631,7 @@ def test_exceptions(self): Settings.EXCEPTION_DEBUG = 1 Settings.DISABLE_EXCEPTION_CATCHING = 0 - if '-O2' in self.emcc_args: + if '-O2' in self.emcc_args and self.run_name != 'asm2f': # XXX closure problem with asm2f self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage src = ''' From df11c6f1fd1636a355b83a1c48b3a890596e6a32 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 18 Nov 2013 17:43:11 -0800 Subject: [PATCH 02/14] version 1.7.8 --- tools/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/shared.py b/tools/shared.py index 0a6740b9d0680..6330b8a6839af 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -307,7 +307,7 @@ def find_temp_directory(): # 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.7.7' +EMSCRIPTEN_VERSION = '1.7.8' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT From ba54ecbc9321c49f119b7e013559cee1b8a8afb7 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Tue, 14 Jan 2014 00:26:57 +1100 Subject: [PATCH 03/14] Split name-minification into a separate pass from registerization. --- emcc | 2 +- tests/test_other.py | 2 +- tools/js-optimizer.js | 189 ++++++++++++++++++++++-------------------- tools/js_optimizer.py | 6 +- 4 files changed, 104 insertions(+), 95 deletions(-) diff --git a/emcc b/emcc index 3b27bc4044346..6ae8f2efa6767 100755 --- a/emcc +++ b/emcc @@ -1996,7 +1996,7 @@ try: js_optimizer_queue += ['registerize'] if opt_level > 0: - if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue) + if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue += ['minifyNames'] if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] if closure and shared.Settings.ASM_JS: diff --git a/tests/test_other.py b/tests/test_other.py index 00c4241871307..5e5dd7a644456 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1721,7 +1721,7 @@ def test_js_optimizer(self): (path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(), ['asm', 'registerize']), (path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(), - ['asm', 'registerize']), + ['asm', 'registerize', 'minifyLocals']), (path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(), ['asm', 'simplifyExpressions']), (path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(), diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 3a6d70bce54e0..7130faa44ccae 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1773,9 +1773,7 @@ function ensureMinifiedNames(n) { // make sure the nth index in minifiedNames ex } } -// Very simple 'registerization', coalescing of variables into a smaller number, -// as part of minification. Globals-level minification began in a previous pass, -// we receive extraInfo which tells us how to rename globals. (Only in asm.js.) +// Very simple 'registerization', coalescing of variables into a smaller number. // // We do not optimize when there are switches, so this pass only makes sense with // relooping. @@ -1811,6 +1809,7 @@ function registerize(ast) { // Replace all var definitions with assignments; we will add var definitions at the top after we registerize // We also mark local variables - i.e., having a var definition var localVars = {}; + var allVars = {}; var hasSwitch = false; // we cannot optimize variables if there is a switch, unless in asm mode traverse(fun, function(node, type) { if (type === 'var') { @@ -1823,74 +1822,25 @@ function registerize(ast) { } } else if (type === 'switch') { hasSwitch = true; + } else if (type === 'name') { + allVars[node[1]] = 1; } }); vacuum(fun); - if (extraInfo && extraInfo.globals) { - assert(asm); - var usedGlobals = {}; - var nextLocal = 0; - // Minify globals using the mapping we were given - traverse(fun, function(node, type) { - if (type === 'name') { - var name = node[1]; - var minified = extraInfo.globals[name]; - if (minified) { - assert(!localVars[name], name); // locals must not shadow globals, or else we don't know which is which - if (localVars[minified]) { - // trying to minify a global into a name used locally. rename all the locals - var newName = '$_newLocal_' + (nextLocal++); - assert(!localVars[newName]); - if (params[minified]) { - params[newName] = 1; - delete params[minified]; - } - localVars[newName] = 1; - delete localVars[minified]; - asmData.vars[newName] = asmData.vars[minified]; - delete asmData.vars[minified]; - asmData.params[newName] = asmData.params[minified]; - delete asmData.params[minified]; - traverse(fun, function(node, type) { - if (type === 'name' && node[1] === minified) { - node[1] = newName; - } - }); - if (fun[2]) { - for (var i = 0; i < fun[2].length; i++) { - if (fun[2][i] === minified) fun[2][i] = newName; - } - } - } - node[1] = minified; - usedGlobals[minified] = 1; - } - } - }); - if (fun[1] in extraInfo.globals) { // if fun was created by a previous optimization pass, it will not be here - fun[1] = extraInfo.globals[fun[1]]; - assert(fun[1]); - } - var nextRegName = 0; - } var regTypes = {}; function getNewRegName(num, name) { - if (!asm) return 'r' + num; - var type = asmData.vars[name]; - if (!extraInfo || !extraInfo.globals) { - var ret = (type ? 'd' : 'i') + num; + var ret; + if (!asm) { + ret = 'r' + num; + } else { + var type = asmData.vars[name]; + ret = (type ? 'd' : 'i') + num; regTypes[ret] = type; - return ret; } - // find the next free minified name that is not used by a global that shows up in this function - while (1) { - ensureMinifiedNames(nextRegName); - var ret = minifiedNames[nextRegName++]; - if (!usedGlobals[ret]) { - regTypes[ret] = type; - return ret; - } + if (ret in allVars) { + assert(ret in localVars, 'register shadows non-local name: ' + ret); } + return ret; } // Find the # of uses of each variable. // While doing so, check if all a variable's uses are dominated in a simple @@ -2111,33 +2061,6 @@ function registerize(ast) { } } denormalizeAsm(fun, finalAsmData); - if (extraInfo && extraInfo.globals) { - // minify in asm var definitions, that denormalizeAsm just generated - function minify(value) { - if (value && value[0] === 'call' && value[1][0] === 'name') { - var name = value[1][1]; - var minified = extraInfo.globals[name]; - if (minified) { - value[1][1] = minified; - } - } - } - var stats = fun[3]; - for (var i = 0; i < stats.length; i++) { - var line = stats[i]; - if (i >= fun[2].length && line[0] !== 'var') break; // when we pass the arg and var coercions, break - if (line[0] === 'stat') { - assert(line[1][0] === 'assign'); - minify(line[1][3]); - } else { - assert(line[0] === 'var'); - var pairs = line[1]; - for (var j = 0; j < pairs.length; j++) { - minify(pairs[j][1]); - } - } - } - } } }); } @@ -2900,6 +2823,91 @@ function minifyGlobals(ast) { suffix = '// EXTRA_INFO:' + JSON.stringify(minified); } + +function minifyLocals(ast) { + assert(asm) + assert(extraInfo && extraInfo.globals) + + traverseGeneratedFunctions(ast, function(fun, type) { + + // Analyse the asmjs to figure out local variable names, + // but operate on the original source tree so that we don't + // miss any global names in e.g. variable initializers. + var asmData = normalizeAsm(fun); denormalizeAsm(fun, asmData); + var newNames = {}; + var usedNames = {}; + + // Find all the globals that we need to minify using + // pre-assigned names. Don't actually minify them yet + // as that might interfere with local variable names. + function isLocalName(name) { + return name in asmData.vars || name in asmData.params; + } + traverse(fun, function(node, type) { + if (type === 'name') { + var name = node[1]; + if (!isLocalName(name)) { + var minified = extraInfo.globals[name]; + if (minified){ + newNames[name] = minified; + usedNames[minified] = 1; + } + } + } + }); + + // Traverse and minify all names. + // The first time we encounter a local name, we assign it a + // minified name that's not currently in use. Allocating on + // demand means they're processed in a predicatable order, + // which is very handy for testing/debugging purposes. + var nextMinifiedName = 0; + function getNextMinifiedName() { + var minified; + while (1) { + ensureMinifiedNames(nextMinifiedName); + minified = minifiedNames[nextMinifiedName++]; + if (!usedNames[minified] && !isLocalName(minified)) { + return minified; + } + } + } + if (fun[1] in extraInfo.globals) { + fun[1] = extraInfo.globals[fun[1]]; + assert(fun[1]); + } + if (fun[2]) { + for (var i=0; i= 0 - minify_globals = 'registerizeAndMinify' in passes and 'asm' in passes + minify_globals = 'minifyNames' in passes and 'asm' in passes if minify_globals: - passes = map(lambda p: p if p != 'registerizeAndMinify' else 'registerize', passes) + passes = map(lambda p: p if p != 'minifyNames' else 'minifyLocals', passes) start_asm = js.find(start_asm_marker) end_asm = js.rfind(end_asm_marker) assert (start_asm >= 0) == (end_asm >= 0) From 7dc8c2ff08e46c9d9a88ba44bf221a404eeb1e5e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 15 Jan 2014 17:02:28 -0800 Subject: [PATCH 04/14] undo unnecessary test disabling --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 6c48306965ad3..458e04fb4f62e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1208,7 +1208,7 @@ def test_exceptions(self): Settings.EXCEPTION_DEBUG = 1 Settings.DISABLE_EXCEPTION_CATCHING = 0 - if '-O2' in self.emcc_args and self.run_name != 'asm2f': # XXX closure problem with asm2f + if '-O2' in self.emcc_args: self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage src = ''' From c7cb560f9325adf022aff445e14d71d99ca80deb Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 15 Jan 2014 21:06:38 -0800 Subject: [PATCH 05/14] do not track eliminable variables from one switch statement to another, if they have any dependencies; fixes #2003 --- .../eliminator/asm-eliminator-test-output.js | 506 ++++++++++++++ tools/eliminator/asm-eliminator-test.js | 651 +++++++++++++++++- tools/js-optimizer.js | 17 +- 3 files changed, 1171 insertions(+), 3 deletions(-) diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index a344fc3555a64..7a8baef2aa4ba 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -304,4 +304,510 @@ function binary(x) { memset(f(x)) | 0; +dmemset(f(x)); } +function cute($this, $outImage) { + $this = $this | 0; + $outImage = $outImage | 0; + var $retval = 0, $outImage_addr = 0, $width = 0, $height = 0, $bit_depth = 0, $color_type = 0, $data = 0, $bpl = 0, $y = 0, $i = 0, $y76 = 0, $p = 0, $end = 0, $this1 = 0, $call = 0, $call7 = 0, $call8 = 0, $3 = 0, $call17 = 0, $10 = 0, $call32 = 0, $call33 = 0, $17$0 = 0, $call34 = 0, $add_ptr = 0, $32 = 0, $call42 = 0, $35 = 0, $call45 = 0, $41 = 0, $call51 = 0, $43 = 0, $call55 = 0, $call57 = 0, $49 = 0, $call72 = 0, $call75 = 0, label = 0, setjmpLabel = 0, setjmpTable = 0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 32 | 0; + label = 1; + setjmpLabel = 0; + setjmpTable = STACKTOP; + STACKTOP = STACKTOP + 168 | 0; + HEAP32[setjmpTable >> 2] = 0; + while (1) switch (label | 0) { + case 1: + $width = sp | 0; + $height = sp + 8 | 0; + $bit_depth = sp + 16 | 0; + $color_type = sp + 24 | 0; + $outImage_addr = $outImage; + $this1 = $this; + if ((HEAP32[($this1 + 32 | 0) >> 2] | 0 | 0) == 3) { + label = 2; + break; + } else { + label = 3; + break; + } + case 2: + $retval = 0; + label = 37; + break; + case 3: + if ((HEAP32[($this1 + 32 | 0) >> 2] | 0 | 0) == 0) { + label = 4; + break; + } else { + label = 6; + break; + } + case 4: + $call = invoke_ii(900, $this1 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + if ($call) { + label = 6; + break; + } else { + label = 5; + break; + } + case 5: + HEAP32[($this1 + 32 | 0) >> 2] = 3; + $retval = 0; + label = 37; + break; + case 6: + HEAP32[($this1 + 28 | 0) >> 2] = 0; + $call7 = invoke_iiii(30, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, 2638 | 0, 156 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $call8 = _saveSetjmp($call7 | 0 | 0, label, setjmpTable) | 0; + label = 38; + break; + case 38: + if (($call8 | 0) != 0) { + label = 7; + break; + } else { + label = 10; + break; + } + case 7: + invoke_viii(640, $this1 + 16 | 0 | 0, $this1 + 20 | 0 | 0, $this1 + 24 | 0 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $3 = HEAP32[($this1 + 28 | 0) >> 2] | 0; + if (($3 | 0) == 0) { + label = 9; + break; + } else { + label = 8; + break; + } + case 8: + invoke_vi(926, $3 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 9; + break; + case 9: + HEAP32[($this1 + 16 | 0) >> 2] = 0; + HEAP32[($this1 + 32 | 0) >> 2] = 3; + $retval = 0; + label = 37; + break; + case 10: + invoke_viiif(2, $outImage_addr | 0, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, HEAP32[($this1 + 20 | 0) >> 2] | 0 | 0, +(+HEAPF32[($this1 | 0) >> 2])); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $call17 = invoke_ii(832, $outImage_addr | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + if ($call17) { + label = 11; + break; + } else { + label = 14; + break; + } + case 11: + invoke_viii(640, $this1 + 16 | 0 | 0, $this1 + 20 | 0 | 0, $this1 + 24 | 0 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $10 = HEAP32[($this1 + 28 | 0) >> 2] | 0; + if (($10 | 0) == 0) { + label = 13; + break; + } else { + label = 12; + break; + } + case 12: + invoke_vi(926, $10 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 13; + break; + case 13: + HEAP32[($this1 + 16 | 0) >> 2] = 0; + HEAP32[($this1 + 32 | 0) >> 2] = 3; + $retval = 0; + label = 37; + break; + case 14: + invoke_iiiiiiiiii(2, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, HEAP32[($this1 + 20 | 0) >> 2] | 0 | 0, $width | 0, $height | 0, $bit_depth | 0, $color_type | 0, 0 | 0, 0 | 0, 0 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $call32 = invoke_ii(850, $outImage_addr | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $data = $call32; + $call33 = invoke_ii(284, $outImage_addr | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $bpl = $call33; + $17$0 = invoke_iii(860, HEAP32[$height >> 2] | 0 | 0, 4 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $call34 = invoke_ii(550, (tempRet0 ? -1 : $17$0) | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + HEAP32[($this1 + 28 | 0) >> 2] = $call34; + $y = 0; + label = 15; + break; + case 15: + if ($y >>> 0 < (HEAP32[$height >> 2] | 0) >>> 0) { + label = 16; + break; + } else { + label = 18; + break; + } + case 16: + $add_ptr = $data + (Math_imul($y, $bpl) | 0) | 0; + HEAP32[((HEAP32[($this1 + 28 | 0) >> 2] | 0) + ($y << 2) | 0) >> 2] = $add_ptr; + label = 17; + break; + case 17: + $y = $y + 1 | 0; + label = 15; + break; + case 18: + invoke_vii(858, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, HEAP32[($this1 + 28 | 0) >> 2] | 0 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $32 = $outImage_addr; + $call42 = invoke_iii(690, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, HEAP32[($this1 + 20 | 0) >> 2] | 0 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_vii(1890, $32 | 0, $call42 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $35 = $outImage_addr; + $call45 = invoke_iii(256, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, HEAP32[($this1 + 20 | 0) >> 2] | 0 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_vii(2126, $35 | 0, $call45 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + HEAP32[($this1 + 32 | 0) >> 2] = 2; + invoke_vii(36, HEAP32[($this1 + 16 | 0) >> 2] | 0 | 0, HEAP32[($this1 + 24 | 0) >> 2] | 0 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_vii(2752, $this1 | 0, HEAP32[($this1 + 24 | 0) >> 2] | 0 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $i = 0; + label = 19; + break; + case 19: + $41 = $i; + $call51 = invoke_ii(618, $this1 + 12 | 0 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + if (($41 | 0) < ($call51 - 1 | 0 | 0)) { + label = 20; + break; + } else { + label = 22; + break; + } + case 20: + $43 = $outImage_addr; + $call55 = invoke_iii(502, $this1 + 12 | 0 | 0, $i | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $call57 = invoke_iii(502, $this1 + 12 | 0 | 0, $i + 1 | 0 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_viii(550, $43 | 0, $call55 | 0, $call57 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 21; + break; + case 21: + $i = $i + 2 | 0; + label = 19; + break; + case 22: + invoke_viii(640, $this1 + 16 | 0 | 0, $this1 + 20 | 0 | 0, $this1 + 24 | 0 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $49 = HEAP32[($this1 + 28 | 0) >> 2] | 0; + if (($49 | 0) == 0) { + label = 24; + break; + } else { + label = 23; + break; + } + case 23: + invoke_vi(926, $49 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 24; + break; + case 24: + HEAP32[($this1 + 16 | 0) >> 2] = 0; + HEAP32[($this1 + 32 | 0) >> 2] = 0; + if ((HEAP32[$color_type >> 2] | 0 | 0) == 3) { + label = 25; + break; + } else { + label = 36; + break; + } + case 25: + $call72 = invoke_ii(926, $outImage_addr | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + if (($call72 | 0) == 3) { + label = 26; + break; + } else { + label = 36; + break; + } + case 26: + $call75 = invoke_ii(860, $outImage_addr | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $y76 = 0; + label = 27; + break; + case 27: + if (($y76 | 0) < (HEAP32[$height >> 2] | 0 | 0)) { + label = 28; + break; + } else { + label = 35; + break; + } + case 28: + $p = $data + (Math_imul($y76, $bpl) | 0) | 0; + $end = $p + (HEAP32[$width >> 2] | 0) | 0; + label = 29; + break; + case 29: + if ($p >>> 0 < $end >>> 0) { + label = 30; + break; + } else { + label = 33; + break; + } + case 30: + if (((HEAP8[$p] | 0) & 255 | 0) >= ($call75 | 0)) { + label = 31; + break; + } else { + label = 32; + break; + } + case 31: + HEAP8[$p] = 0; + label = 32; + break; + case 32: + $p = $p + 1 | 0; + label = 29; + break; + case 33: + label = 34; + break; + case 34: + $y76 = $y76 + 1 | 0; + label = 27; + break; + case 35: + label = 36; + break; + case 36: + $retval = 1; + label = 37; + break; + case 37: + STACKTOP = sp; + return $retval | 0; + case -1: + if ((setjmpLabel | 0) == 6) { + $call8 = threwValue; + label = 38; + } + __THREW__ = threwValue = 0; + break; + } + return 0; +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index 4b45e4d401018..ad1ed05eea753 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -378,5 +378,654 @@ function binary(x) { z = f(x); +dmemset(z); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf", "select2", "binary"] +function cute($this, $outImage) { + $this = $this | 0; + $outImage = $outImage | 0; + var $retval = 0, $this_addr = 0, $outImage_addr = 0, $width = 0, $height = 0, $bit_depth = 0, $color_type = 0, $data = 0, $bpl = 0, $y = 0, $i = 0, $color_table_size = 0, $y76 = 0, $p = 0, $end = 0, $this1 = 0, $state = 0, $0 = 0, $cmp = 0, $state2 = 0; + var $1 = 0, $cmp3 = 0, $call = 0, $state5 = 0, $row_pointers = 0, $png_ptr = 0, $2 = 0, $call7 = 0, $arraydecay = 0, $call8 = 0, $tobool = 0, $png_ptr10 = 0, $info_ptr = 0, $end_info = 0, $row_pointers11 = 0, $3 = 0, $isnull = 0, $4 = 0, $png_ptr12 = 0, $state13 = 0; + var $5 = 0, $png_ptr15 = 0, $6 = 0, $info_ptr16 = 0, $7 = 0, $gamma = 0, $8 = +0, $9 = 0, $call17 = 0, $png_ptr19 = 0, $info_ptr20 = 0, $end_info21 = 0, $row_pointers22 = 0, $10 = 0, $isnull23 = 0, $11 = 0, $png_ptr26 = 0, $state27 = 0, $png_ptr29 = 0, $12 = 0; + var $info_ptr30 = 0, $13 = 0, $call31 = 0, $14 = 0, $call32 = 0, $15 = 0, $call33 = 0, $16 = 0, $17$0 = 0, $17$1 = 0, $18 = 0, $19 = 0, $20 = 0, $call34 = 0, $21 = 0, $row_pointers35 = 0, $22 = 0, $23 = 0, $cmp36 = 0, $24 = 0; + var $25 = 0, $26 = 0, $mul = 0, $add_ptr = 0, $27 = 0, $row_pointers37 = 0, $28 = 0, $arrayidx = 0, $29 = 0, $inc = 0, $png_ptr38 = 0, $30 = 0, $row_pointers39 = 0, $31 = 0, $32 = 0, $png_ptr40 = 0, $33 = 0, $info_ptr41 = 0, $34 = 0, $call42 = 0; + var $35 = 0, $png_ptr43 = 0, $36 = 0, $info_ptr44 = 0, $37 = 0, $call45 = 0, $state46 = 0, $png_ptr47 = 0, $38 = 0, $end_info48 = 0, $39 = 0, $end_info49 = 0, $40 = 0, $41 = 0, $readTexts = 0, $42 = 0, $call51 = 0, $sub = 0, $cmp52 = 0, $43 = 0; + var $readTexts54 = 0, $44 = 0, $45 = 0, $call55 = 0, $readTexts56 = 0, $46 = 0, $47 = 0, $add = 0, $call57 = 0, $48 = 0, $add59 = 0, $png_ptr61 = 0, $info_ptr62 = 0, $end_info63 = 0, $row_pointers64 = 0, $49 = 0, $isnull65 = 0, $50 = 0, $png_ptr68 = 0, $state69 = 0; + var $51 = 0, $cmp70 = 0, $52 = 0, $call72 = 0, $cmp73 = 0, $53 = 0, $call75 = 0, $54 = 0, $55 = 0, $cmp78 = 0, $56 = 0, $57 = 0, $58 = 0, $mul80 = 0, $add_ptr81 = 0, $59 = 0, $60 = 0, $add_ptr82 = 0, $61 = 0, $62 = 0; + var $cmp83 = 0, $63 = 0, $64 = 0, $conv = 0, $65 = 0, $cmp84 = 0, $66 = 0, $67 = 0, $incdec_ptr = 0, $68 = 0, $inc88 = 0, $69 = 0, label = 0, setjmpLabel = 0, setjmpTable = 0; + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 32 | 0; + label = 1; + setjmpLabel = 0; + setjmpTable = STACKTOP; + STACKTOP = STACKTOP + 168 | 0; + HEAP32[setjmpTable >> 2] = 0; + while (1) switch (label | 0) { + case 1: + $width = sp | 0; + $height = sp + 8 | 0; + $bit_depth = sp + 16 | 0; + $color_type = sp + 24 | 0; + $this_addr = $this; + $outImage_addr = $outImage; + $this1 = $this_addr; + $state = $this1 + 32 | 0; + $0 = HEAP32[$state >> 2] | 0; + $cmp = ($0 | 0) == 3; + if ($cmp) { + label = 2; + break; + } else { + label = 3; + break; + } + case 2: + $retval = 0; + label = 37; + break; + case 3: + $state2 = $this1 + 32 | 0; + $1 = HEAP32[$state2 >> 2] | 0; + $cmp3 = ($1 | 0) == 0; + if ($cmp3) { + label = 4; + break; + } else { + label = 6; + break; + } + case 4: + $call = invoke_ii(900, $this1 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + if ($call) { + label = 6; + break; + } else { + label = 5; + break; + } + case 5: + $state5 = $this1 + 32 | 0; + HEAP32[$state5 >> 2] = 3; + $retval = 0; + label = 37; + break; + case 6: + $row_pointers = $this1 + 28 | 0; + HEAP32[$row_pointers >> 2] = 0; + $png_ptr = $this1 + 16 | 0; + $2 = HEAP32[$png_ptr >> 2] | 0; + $call7 = invoke_iiii(30, $2 | 0, 2638 | 0, 156 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $arraydecay = $call7 | 0; + $call8 = _saveSetjmp($arraydecay | 0, label, setjmpTable) | 0; + label = 38; + break; + case 38: + $tobool = ($call8 | 0) != 0; + if ($tobool) { + label = 7; + break; + } else { + label = 10; + break; + } + case 7: + $png_ptr10 = $this1 + 16 | 0; + $info_ptr = $this1 + 20 | 0; + $end_info = $this1 + 24 | 0; + invoke_viii(640, $png_ptr10 | 0, $info_ptr | 0, $end_info | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $row_pointers11 = $this1 + 28 | 0; + $3 = HEAP32[$row_pointers11 >> 2] | 0; + $isnull = ($3 | 0) == 0; + if ($isnull) { + label = 9; + break; + } else { + label = 8; + break; + } + case 8: + $4 = $3; + invoke_vi(926, $4 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 9; + break; + case 9: + $png_ptr12 = $this1 + 16 | 0; + HEAP32[$png_ptr12 >> 2] = 0; + $state13 = $this1 + 32 | 0; + HEAP32[$state13 >> 2] = 3; + $retval = 0; + label = 37; + break; + case 10: + $5 = $outImage_addr; + $png_ptr15 = $this1 + 16 | 0; + $6 = HEAP32[$png_ptr15 >> 2] | 0; + $info_ptr16 = $this1 + 20 | 0; + $7 = HEAP32[$info_ptr16 >> 2] | 0; + $gamma = $this1 | 0; + $8 = +HEAPF32[$gamma >> 2]; + invoke_viiif(2, $5 | 0, $6 | 0, $7 | 0, +$8); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $9 = $outImage_addr; + $call17 = invoke_ii(832, $9 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + if ($call17) { + label = 11; + break; + } else { + label = 14; + break; + } + case 11: + $png_ptr19 = $this1 + 16 | 0; + $info_ptr20 = $this1 + 20 | 0; + $end_info21 = $this1 + 24 | 0; + invoke_viii(640, $png_ptr19 | 0, $info_ptr20 | 0, $end_info21 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $row_pointers22 = $this1 + 28 | 0; + $10 = HEAP32[$row_pointers22 >> 2] | 0; + $isnull23 = ($10 | 0) == 0; + if ($isnull23) { + label = 13; + break; + } else { + label = 12; + break; + } + case 12: + $11 = $10; + invoke_vi(926, $11 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 13; + break; + case 13: + $png_ptr26 = $this1 + 16 | 0; + HEAP32[$png_ptr26 >> 2] = 0; + $state27 = $this1 + 32 | 0; + HEAP32[$state27 >> 2] = 3; + $retval = 0; + label = 37; + break; + case 14: + $png_ptr29 = $this1 + 16 | 0; + $12 = HEAP32[$png_ptr29 >> 2] | 0; + $info_ptr30 = $this1 + 20 | 0; + $13 = HEAP32[$info_ptr30 >> 2] | 0; + $call31 = invoke_iiiiiiiiii(2, $12 | 0, $13 | 0, $width | 0, $height | 0, $bit_depth | 0, $color_type | 0, 0 | 0, 0 | 0, 0 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $14 = $outImage_addr; + $call32 = invoke_ii(850, $14 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $data = $call32; + $15 = $outImage_addr; + $call33 = invoke_ii(284, $15 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $bpl = $call33; + $16 = HEAP32[$height >> 2] | 0; + $17$0 = invoke_iii(860, $16 | 0, 4 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $17$1 = tempRet0; + $18 = $17$1; + $19 = $17$0; + $20 = $18 ? -1 : $19; + $call34 = invoke_ii(550, $20 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $21 = $call34; + $row_pointers35 = $this1 + 28 | 0; + HEAP32[$row_pointers35 >> 2] = $21; + $y = 0; + label = 15; + break; + case 15: + $22 = $y; + $23 = HEAP32[$height >> 2] | 0; + $cmp36 = $22 >>> 0 < $23 >>> 0; + if ($cmp36) { + label = 16; + break; + } else { + label = 18; + break; + } + case 16: + $24 = $data; + $25 = $y; + $26 = $bpl; + $mul = Math_imul($25, $26) | 0; + $add_ptr = $24 + $mul | 0; + $27 = $y; + $row_pointers37 = $this1 + 28 | 0; + $28 = HEAP32[$row_pointers37 >> 2] | 0; + $arrayidx = $28 + ($27 << 2) | 0; + HEAP32[$arrayidx >> 2] = $add_ptr; + label = 17; + break; + case 17: + $29 = $y; + $inc = $29 + 1 | 0; + $y = $inc; + label = 15; + break; + case 18: + $png_ptr38 = $this1 + 16 | 0; + $30 = HEAP32[$png_ptr38 >> 2] | 0; + $row_pointers39 = $this1 + 28 | 0; + $31 = HEAP32[$row_pointers39 >> 2] | 0; + invoke_vii(858, $30 | 0, $31 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $32 = $outImage_addr; + $png_ptr40 = $this1 + 16 | 0; + $33 = HEAP32[$png_ptr40 >> 2] | 0; + $info_ptr41 = $this1 + 20 | 0; + $34 = HEAP32[$info_ptr41 >> 2] | 0; + $call42 = invoke_iii(690, $33 | 0, $34 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_vii(1890, $32 | 0, $call42 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $35 = $outImage_addr; + $png_ptr43 = $this1 + 16 | 0; + $36 = HEAP32[$png_ptr43 >> 2] | 0; + $info_ptr44 = $this1 + 20 | 0; + $37 = HEAP32[$info_ptr44 >> 2] | 0; + $call45 = invoke_iii(256, $36 | 0, $37 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_vii(2126, $35 | 0, $call45 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $state46 = $this1 + 32 | 0; + HEAP32[$state46 >> 2] = 2; + $png_ptr47 = $this1 + 16 | 0; + $38 = HEAP32[$png_ptr47 >> 2] | 0; + $end_info48 = $this1 + 24 | 0; + $39 = HEAP32[$end_info48 >> 2] | 0; + invoke_vii(36, $38 | 0, $39 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $end_info49 = $this1 + 24 | 0; + $40 = HEAP32[$end_info49 >> 2] | 0; + invoke_vii(2752, $this1 | 0, $40 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $i = 0; + label = 19; + break; + case 19: + $41 = $i; + $readTexts = $this1 + 12 | 0; + $42 = $readTexts; + $call51 = invoke_ii(618, $42 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $sub = $call51 - 1 | 0; + $cmp52 = ($41 | 0) < ($sub | 0); + if ($cmp52) { + label = 20; + break; + } else { + label = 22; + break; + } + case 20: + $43 = $outImage_addr; + $readTexts54 = $this1 + 12 | 0; + $44 = $readTexts54; + $45 = $i; + $call55 = invoke_iii(502, $44 | 0, $45 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $readTexts56 = $this1 + 12 | 0; + $46 = $readTexts56; + $47 = $i; + $add = $47 + 1 | 0; + $call57 = invoke_iii(502, $46 | 0, $add | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + invoke_viii(550, $43 | 0, $call55 | 0, $call57 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 21; + break; + case 21: + $48 = $i; + $add59 = $48 + 2 | 0; + $i = $add59; + label = 19; + break; + case 22: + $png_ptr61 = $this1 + 16 | 0; + $info_ptr62 = $this1 + 20 | 0; + $end_info63 = $this1 + 24 | 0; + invoke_viii(640, $png_ptr61 | 0, $info_ptr62 | 0, $end_info63 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $row_pointers64 = $this1 + 28 | 0; + $49 = HEAP32[$row_pointers64 >> 2] | 0; + $isnull65 = ($49 | 0) == 0; + if ($isnull65) { + label = 24; + break; + } else { + label = 23; + break; + } + case 23: + $50 = $49; + invoke_vi(926, $50 | 0); + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + label = 24; + break; + case 24: + $png_ptr68 = $this1 + 16 | 0; + HEAP32[$png_ptr68 >> 2] = 0; + $state69 = $this1 + 32 | 0; + HEAP32[$state69 >> 2] = 0; + $51 = HEAP32[$color_type >> 2] | 0; + $cmp70 = ($51 | 0) == 3; + if ($cmp70) { + label = 25; + break; + } else { + label = 36; + break; + } + case 25: + $52 = $outImage_addr; + $call72 = invoke_ii(926, $52 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $cmp73 = ($call72 | 0) == 3; + if ($cmp73) { + label = 26; + break; + } else { + label = 36; + break; + } + case 26: + $53 = $outImage_addr; + $call75 = invoke_ii(860, $53 | 0) | 0; + if ((__THREW__ | 0) != 0 & (threwValue | 0) != 0) { + setjmpLabel = _testSetjmp(HEAP32[__THREW__ >> 2] | 0, setjmpTable) | 0; + if ((setjmpLabel | 0) > 0) { + label = -1; + break; + } else return 0 | 0; + } + __THREW__ = threwValue = 0; + $color_table_size = $call75; + $y76 = 0; + label = 27; + break; + case 27: + $54 = $y76; + $55 = HEAP32[$height >> 2] | 0; + $cmp78 = ($54 | 0) < ($55 | 0); + if ($cmp78) { + label = 28; + break; + } else { + label = 35; + break; + } + case 28: + $56 = $data; + $57 = $y76; + $58 = $bpl; + $mul80 = Math_imul($57, $58) | 0; + $add_ptr81 = $56 + $mul80 | 0; + $p = $add_ptr81; + $59 = $p; + $60 = HEAP32[$width >> 2] | 0; + $add_ptr82 = $59 + $60 | 0; + $end = $add_ptr82; + label = 29; + break; + case 29: + $61 = $p; + $62 = $end; + $cmp83 = $61 >>> 0 < $62 >>> 0; + if ($cmp83) { + label = 30; + break; + } else { + label = 33; + break; + } + case 30: + $63 = $p; + $64 = HEAP8[$63] | 0; + $conv = $64 & 255; + $65 = $color_table_size; + $cmp84 = ($conv | 0) >= ($65 | 0); + if ($cmp84) { + label = 31; + break; + } else { + label = 32; + break; + } + case 31: + $66 = $p; + HEAP8[$66] = 0; + label = 32; + break; + case 32: + $67 = $p; + $incdec_ptr = $67 + 1 | 0; + $p = $incdec_ptr; + label = 29; + break; + case 33: + label = 34; + break; + case 34: + $68 = $y76; + $inc88 = $68 + 1 | 0; + $y76 = $inc88; + label = 27; + break; + case 35: + label = 36; + break; + case 36: + $retval = 1; + label = 37; + break; + case 37: + $69 = $retval; + STACKTOP = sp; + return $69 | 0; + case -1: + if ((setjmpLabel | 0) == 6) { + $call8 = threwValue; + label = 38; + } + __THREW__ = threwValue = 0; + break; + } + return 0; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf", "select2", "binary", "cute"] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 3a6d70bce54e0..fc9e3002009ea 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2322,7 +2322,7 @@ function eliminate(ast, memSafe) { var memoryInvalidated = false; var callsInvalidated = false; function track(name, value, defNode) { // add a potential that has just been defined to the tracked list, we hope to eliminate it - var usesGlobals = false, usesMemory = false, deps = {}, doesCall = false; + var usesGlobals = false, usesMemory = false, deps = {}, doesCall = false, hasDeps = false; var ignoreName = false; // one-time ignorings of names, as first op in sub and call traverse(value, function(node, type) { if (type === 'name') { @@ -2333,6 +2333,7 @@ function eliminate(ast, memSafe) { } if (!(name in potentials)) { // deps do not matter for potentials - they are defined once, so no complexity deps[name] = 1; + hasDeps = true; } } else { ignoreName = false; @@ -2354,6 +2355,7 @@ function eliminate(ast, memSafe) { usesMemory: usesMemory, defNode: defNode, deps: deps, + hasDeps: hasDeps, doesCall: doesCall }; globalsInvalidated = false; @@ -2426,7 +2428,7 @@ function eliminate(ast, memSafe) { function traverseInOrder(node, ignoreSub, ignoreName) { if (abort) return; //nesting++; // printErr-related - //printErr(spaces(2*(nesting+1)) + 'trav: ' + JSON.stringify(node).substr(0, 50) + ' : ' + keys(tracked) + ' : ' + [allowTracking, ignoreSub, ignoreName]); + //printErr(JSON.stringify(node).substr(0, 50) + ' : ' + keys(tracked) + ' : ' + [allowTracking, ignoreSub, ignoreName]); var type = node[0]; if (type === 'assign') { var target = node[2]; @@ -2602,6 +2604,8 @@ function eliminate(ast, memSafe) { traverseInOrder(node[3]); } else if (type === 'switch') { traverseInOrder(node[1]); + var originalTracked = {}; + for (var o in tracked) originalTracked[o] = 1; var cases = node[2]; for (var i = 0; i < cases.length; i++) { var c = cases[i]; @@ -2610,6 +2614,15 @@ function eliminate(ast, memSafe) { for (var j = 0; j < stats.length; j++) { traverseInOrder(stats[j]); } + // We cannot track from one switch case into another, undo all new trackings TODO: general framework here, use in if-else as well + for (var t in tracked) { + if (!(t in originalTracked)) { + var info = tracked[t]; + if (info.usesGlobals || info.usesMemory || info.hasDeps) { + delete tracked[t]; + } + } + } } } else { if (!(type in ABORTING_ELIMINATOR_SCAN_NODES)) { From 375761d4a5f514f7de05efb134037bd6a6923831 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Thu, 16 Jan 2014 16:19:22 +1100 Subject: [PATCH 06/14] Address review comments. --- tools/js-optimizer.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 7130faa44ccae..5f0e17a539aba 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1838,7 +1838,7 @@ function registerize(ast) { regTypes[ret] = type; } if (ret in allVars) { - assert(ret in localVars, 'register shadows non-local name: ' + ret); + assert(ret in localVars, 'register shadows non-local name'); } return ret; } @@ -2867,6 +2867,7 @@ function minifyLocals(ast) { while (1) { ensureMinifiedNames(nextMinifiedName); minified = minifiedNames[nextMinifiedName++]; + // TODO: we can probably remove !isLocalName here if (!usedNames[minified] && !isLocalName(minified)) { return minified; } @@ -2877,7 +2878,7 @@ function minifyLocals(ast) { assert(fun[1]); } if (fun[2]) { - for (var i=0; i Date: Wed, 15 Jan 2014 21:35:30 -0800 Subject: [PATCH 07/14] add todo --- tools/js-optimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 4b1463eb16e9a..9391ff8eb4c41 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2846,7 +2846,7 @@ function minifyLocals(ast) { // Analyse the asmjs to figure out local variable names, // but operate on the original source tree so that we don't // miss any global names in e.g. variable initializers. - var asmData = normalizeAsm(fun); denormalizeAsm(fun, asmData); + var asmData = normalizeAsm(fun); denormalizeAsm(fun, asmData); // TODO: we can avoid modifying at all here - we just need a list of local vars+params var newNames = {}; var usedNames = {}; From a3108271317e8242830113a2c87ceed1ec4ac02d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 11:25:22 -0800 Subject: [PATCH 08/14] enable a form of safe heap in asm, using js optimizer pass to ensure full coverage and support for fastcomp --- emcc | 6 ++- emscripten.py | 4 +- src/parseTools.js | 34 ++++++++------ src/preamble.js | 33 ++++++++++++++ src/runtime.js | 2 +- tests/test_core.py | 2 +- tools/js-optimizer.js | 102 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 165 insertions(+), 18 deletions(-) diff --git a/emcc b/emcc index 958b018cb3aba..4be24e206d691 100755 --- a/emcc +++ b/emcc @@ -1196,7 +1196,6 @@ try: shared.Settings.ASM_JS = 1 assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet' assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp' - assert shared.Settings.SAFE_HEAP == 0, 'safe heap not supported in fastcomp yet' assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet' assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp' assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers not supported in fastcomp' @@ -1230,6 +1229,9 @@ try: shared.Settings.CORRECT_OVERFLOWS = 1 assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode' + if shared.Settings.SAFE_HEAP and not js_opts: + logging.warning('asm.js+SAFE_HEAP requires js opts to be run (-O1 or above by default)') + if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: debug_level = 4 # must keep debug info to do line-by-line operations @@ -1991,6 +1993,8 @@ try: if DEBUG: save_intermediate('closure') if js_opts: + if shared.Settings.ASM_JS and shared.Settings.SAFE_HEAP: js_optimizer_queue += ['safeHeap'] + if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS: js_optimizer_queue += ['outline'] js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT diff --git a/emscripten.py b/emscripten.py index befad8d500602..aeace63d22573 100755 --- a/emscripten.py +++ b/emscripten.py @@ -455,7 +455,7 @@ def fix_item(item): basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') - if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR'] + if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE'] if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] if settings['ASSERTIONS']: basic_funcs += ['nullFunc'] @@ -956,7 +956,7 @@ def fix_item(item): basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') - if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR'] + if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE'] if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] if settings['ASSERTIONS']: basic_funcs += ['nullFunc'] diff --git a/src/parseTools.js b/src/parseTools.js index be0cbcab3f69a..036ccfc1a2e5f 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1327,18 +1327,22 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa var printType = type; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType[0] === '#') printType = printType.substr(1); - return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type); - } else { - var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']'; - if (ASM_JS && (phase == 'funcs' || forceAsm)) { - ret = asmCoercion(ret, type); - } - if (ASM_HEAP_LOG) { - ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret, - 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int')); + if (ASM_JS) { + if (!ignore) return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type); + // else fall through + } else { + return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type); } - return ret; } + var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']'; + if (ASM_JS && (phase == 'funcs' || forceAsm)) { + ret = asmCoercion(ret, type); + } + if (ASM_HEAP_LOG) { + ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret, + 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int')); + } + return ret; } function makeGetValueAsm(ptr, pos, type, unsigned) { @@ -1435,10 +1439,14 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, var printType = type; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType[0] === '#') printType = printType.substr(1); - return 'SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')'; - } else { - return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep); + if (ASM_JS) { + if (!ignore) return asmCoercion('SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type); + // else fall through + } else { + return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')'; + } } + return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep); } function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) { diff --git a/src/preamble.js b/src/preamble.js index ac6ee7b32365d..d70ef4b14309c 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -21,6 +21,7 @@ Module.print = Module.printErr = function(){}; #endif #if SAFE_HEAP +#if ASM_JS == 0 //======================================== // Debugging tools - Heap //======================================== @@ -166,6 +167,38 @@ function SAFE_HEAP_FILL_HISTORY(from, to, type) { } //========================================== +#else +// ASM_JS safe heap + +function getSafeHeapType(bytes, isFloat) { + switch (bytes) { + case 1: return 'i8'; + case 2: return 'i16'; + case 4: return isFloat ? 'float' : 'i32'; + case 8: return 'double'; + default: assert(0); + } +} + +function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { +#if SAFE_HEAP_LOG + Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]); +#endif + assert(dest > 0, 'segmentation fault'); + assert(dest % bytes === 0); + setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); +} + +function SAFE_HEAP_LOAD(dest, bytes, isFloat) { +#if SAFE_HEAP_LOG + Module.print('SAFE_HEAP load: ' + [dest, bytes, isFloat]); +#endif + assert(dest > 0, 'segmentation fault'); + assert(dest % bytes === 0); + return getValue(dest, getSafeHeapType(bytes, isFloat), 1); +} + +#endif #endif #if CHECK_HEAP_ALIGN diff --git a/src/runtime.js b/src/runtime.js index cd3afb4ba77bc..1fc9e026459a3 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -49,7 +49,7 @@ var RuntimeGenerator = { stackExit: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; var ret = ''; - if (SAFE_HEAP) { + if (SAFE_HEAP && !ASM_JS) { ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }'; } return ret += 'STACKTOP=sp'; diff --git a/tests/test_core.py b/tests/test_core.py index 458e04fb4f62e..ce77820955dda 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1486,7 +1486,7 @@ def test_polymorph(self): def test_segfault(self): if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') - if Settings.ASM_JS: return self.skip('asm does not support safe heap') + if os.environ.get('EMCC_FAST_COMPILER') == '1' and '-O2' not in self.emcc_args: return self.skip('todo in non-jsopts-enabled fastcomp') Settings.SAFE_HEAP = 1 diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 9391ff8eb4c41..fa59dbec632bd 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3888,6 +3888,104 @@ function outline(ast) { }); } +function safeHeap(ast) { + function fixPtr(ptr, heap) { + switch (heap) { + case 'HEAP8': case 'HEAPU8': break; + case 'HEAP16': case 'HEAPU16': { + if (ptr[0] === 'binary') { + assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 1); + ptr = ptr[2]; // skip the shift + } else { + ptr = ['binary', '*', ptr, ['num', 2]]; // was unshifted, convert to absolute address + } + break; + } + case 'HEAP32': case 'HEAPU32': { + if (ptr[0] === 'binary') { + assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 2); + ptr = ptr[2]; // skip the shift + } else { + ptr = ['binary', '*', ptr, ['num', 4]]; // was unshifted, convert to absolute address + } + break; + } + case 'HEAPF32': { + if (ptr[0] === 'binary') { + assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 2); + ptr = ptr[2]; // skip the shift + } else { + ptr = ['binary', '*', ptr, ['num', 4]]; // was unshifted, convert to absolute address + } + break; + } + case 'HEAPF64': { + if (ptr[0] === 'binary') { + assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 3); + ptr = ptr[2]; // skip the shift + } else { + ptr = ['binary', '*', ptr, ['num', 8]]; // was unshifted, convert to absolute address + } + break; + } + default: throw 'bad heap ' + heap; + } + ptr = ['binary', '|', ptr, ['num', 0]]; + return ptr; + } + traverseGenerated(ast, function(node, type) { + if (type === 'assign') { + if (node[1] === true && node[2][0] === 'sub') { + var heap = node[2][1][1]; + var ptr = fixPtr(node[2][2], heap); + var value = node[3]; + // SAFE_HEAP_STORE(ptr, value, bytes, isFloat) + switch (heap) { + case 'HEAP8': case 'HEAPU8': { + return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 1], ['num', '0']]]; + } + case 'HEAP16': case 'HEAPU16': { + return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 2], ['num', '0']]]; + } + case 'HEAP32': case 'HEAPU32': { + return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 4], ['num', '0']]]; + } + case 'HEAPF32': { + return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_FLOAT), ['num', 4], ['num', '1']]]; + } + case 'HEAPF64': { + return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_DOUBLE), ['num', 8], ['num', '1']]]; + } + default: throw 'bad heap ' + heap; + } + } + } else if (type === 'sub') { + var heap = node[1][1]; + if (heap[0] !== 'H') return; + var ptr = fixPtr(node[2], heap); + // SAFE_HEAP_LOAD(ptr, bytes, isFloat) + switch (heap) { + case 'HEAP8': case 'HEAPU8': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0']]], ASM_INT); + } + case 'HEAP16': case 'HEAPU16': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0']]], ASM_INT); + } + case 'HEAP32': case 'HEAPU32': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0']]], ASM_INT); + } + case 'HEAPF32': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1']]], ASM_FLOAT); + } + case 'HEAPF64': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1']]], ASM_DOUBLE); + } + default: throw 'bad heap ' + heap; + } + } + }); +} + // Last pass utilities // Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly) @@ -3978,6 +4076,7 @@ function asmLastOpts(ast) { var minifyWhitespace = false, printMetadata = true, asm = false, last = false; var passes = { + // passes dumpAst: dumpAst, dumpSrc: dumpSrc, unGlobalize: unGlobalize, @@ -3996,6 +4095,9 @@ var passes = { minifyLocals: minifyLocals, relocate: relocate, outline: outline, + safeHeap: safeHeap, + + // flags minifyWhitespace: function() { minifyWhitespace = true }, noPrintMetadata: function() { printMetadata = false }, asm: function() { asm = true }, From 9fed906bd03def3e21aba3c90bcbadd7b248b350 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 16 Jan 2014 20:21:24 +0100 Subject: [PATCH 09/14] It's 2014 --- AUTHORS | 1 + ChangeLog | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index d47b3199e5a4e..792fb5236edb0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -118,3 +118,4 @@ a license to everyone to use it as detailed in LICENSE.) * Alexandre Perrot * Emerson José Silveira da Costa * Jari Vetoniemi +* Sindre Sorhus diff --git a/ChangeLog b/ChangeLog index 8749f02f99c38..9d1d85d60c8cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,12 +12,12 @@ Current trunk code - To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see https://github.com/kripken/emscripten/compare/1.8.2...incoming -v1.8.2: 1/4/2013 +v1.8.2: 1/4/2014 ------------------ - Fixed glGetFramebufferAttachmentParameteriv and an issue with glGetXXX when the returned value was null. - Full list of changes: https://github.com/kripken/emscripten/compare/1.8.1...1.8.2 -v1.8.1: 1/3/2013 +v1.8.1: 1/3/2014 ------------------ - Added support for WebGL hardware instancing extension. - Improved fastcomp native LLVM backend support. From 41675a7501a7d6b07f1c4a8045209ec96e9507bd Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 13:17:37 -0800 Subject: [PATCH 10/14] strengthen test_alloca to check alignment --- tests/core/test_alloca.in | 13 +++++++++---- tests/test_core.py | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/core/test_alloca.in b/tests/core/test_alloca.in index bfad33248a785..d115880f2c838 100644 --- a/tests/core/test_alloca.in +++ b/tests/core/test_alloca.in @@ -1,9 +1,14 @@ #include #include +#include -int main() { - char *pc; - pc = (char *)alloca(5); - printf("z:%d*%d*\n", pc > 0, (int)pc); +int main(int argc, char **argv) { + char *pc, *pc2; + assert(argc == 1); + pc = (char *)alloca(4+argc); + assert(((int)pc) % 4 == 0); + pc2 = (char *)alloca(4+argc); + assert(((int)pc2) % 4 == 0); + printf("z:%d*%d*%d*\n", pc > 0, (int)pc, (int)pc2); return 0; } diff --git a/tests/test_core.py b/tests/test_core.py index ce77820955dda..59f2f9e9713eb 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1606,6 +1606,8 @@ def test_emptyclass(self): self.do_run_from_file(src, output) def test_alloca(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('non-ta2 may have unaligned allocas') + test_path = path_from_root('tests', 'core', 'test_alloca') src, output = (test_path + s for s in ('.in', '.out')) From 62de2bd37e003763733b3147ea32164a68e5962e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 14:12:25 -0800 Subject: [PATCH 11/14] don't override Module.print and printErr if the user specified them --- src/shell.js | 12 ++++++------ tests/test_other.py | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/shell.js b/src/shell.js index b41fbb51bb4f9..84844c85f2075 100644 --- a/src/shell.js +++ b/src/shell.js @@ -38,10 +38,10 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR 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 - Module['print'] = function print(x) { + if (!Module['print']) Module['print'] = function print(x) { process['stdout'].write(x + '\n'); }; - Module['printErr'] = function printErr(x) { + if (!Module['printErr']) Module['printErr'] = function printErr(x) { process['stderr'].write(x + '\n'); }; @@ -71,7 +71,7 @@ if (ENVIRONMENT_IS_NODE) { module['exports'] = Module; } else if (ENVIRONMENT_IS_SHELL) { - Module['print'] = print; + if (!Module['print']) Module['print'] = print; if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm if (typeof read != 'undefined') { @@ -107,16 +107,16 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { } if (typeof console !== 'undefined') { - Module['print'] = function print(x) { + if (!Module['print']) Module['print'] = function print(x) { console.log(x); }; - Module['printErr'] = function printErr(x) { + if (!Module['printErr']) Module['printErr'] = function printErr(x) { console.log(x); }; } else { // Probably a worker, and without console.log. We can do very little here... var TRY_USE_DUMP = false; - Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) { + if (!Module['print']) Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) { dump(x); }) : (function(x) { // self.postMessage(x); // enable this if you want stdout to be sent as messages diff --git a/tests/test_other.py b/tests/test_other.py index 5e5dd7a644456..b2fc4cf6545e5 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -2221,7 +2221,6 @@ def test_default_obj_ext(self): process.communicate() assert(os.path.isfile(outdir + 'hello_world.obj')) - def test_doublestart_bug(self): open('code.cpp', 'w').write(r''' #include @@ -2253,3 +2252,20 @@ def test_doublestart_bug(self): assert output.count('This should only appear once.') == 1, '\n'+output + def test_module_print(self): + open('code.cpp', 'w').write(r''' +#include +int main(void) { + printf("123456789\n"); + return 0; +} +''') + + open('pre.js', 'w').write(r''' +var Module = { print: function(x) { throw '<{(' + x + ')}>' } }; +''') + + Popen([PYTHON, EMCC, 'code.cpp', '--pre-js', 'pre.js']).communicate() + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True, engine=NODE_JS) + assert r'<{(123456789)}>' in output, output + From 72972e7102e8a7c94f576e841749d2e7c4b432d0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 14:31:47 -0800 Subject: [PATCH 12/14] add __h_errno; fixes #2013 --- src/library.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/library.js b/src/library.js index 69569601bc432..eda4a0b73b7f0 100644 --- a/src/library.js +++ b/src/library.js @@ -7344,6 +7344,12 @@ LibraryManager.library = { // netdb.h // ========================================================================== + __h_errno_state: 'allocate(1, "i32", ALLOC_STATIC)', + __h_errno_location__deps: ['__h_errno_state'], + __h_errno_location: function() { + return ___h_errno_state; + }, + // We can't actually resolve hostnames in the browser, so instead // we're generating fake IP addresses with lookup_name that we can // resolve later on with lookup_addr. @@ -7399,6 +7405,7 @@ LibraryManager.library = { gethostbyaddr: function (addr, addrlen, type) { if (type !== {{{ cDefine('AF_INET') }}}) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + // TODO: set h_errno return null; } addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr From 3d771ae92881d253aae30e80a400ba133de08d26 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 15:38:02 -0800 Subject: [PATCH 13/14] fix strnlen input of size, which should be unsigned --- src/library.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/library.js b/src/library.js index eda4a0b73b7f0..36a7cd260837f 100644 --- a/src/library.js +++ b/src/library.js @@ -3830,6 +3830,7 @@ LibraryManager.library = { }, strnlen: function(ptr, num) { + num = num >>> 0; for (var i = 0; i < num; i++) { if ({{{ makeGetValue('ptr', 0, 'i8') }}} == 0) return i; ptr++; From 5bb976ed71f1402f8f7853cc9a0c8ea4dd551615 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 14:42:22 -0800 Subject: [PATCH 14/14] add fnmatch; fixes #2002 --- emcc | 1 + system/lib/libc/musl/src/regex/fnmatch.c | 299 +++++++++++++++++++++++ system/lib/libcextra.symbols | 1 + tests/core/fnmatch.c | 79 ++++++ tests/core/fnmatch.out | 23 ++ tests/test_core.py | 5 + tools/shared.py | 2 +- 7 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 system/lib/libc/musl/src/regex/fnmatch.c create mode 100644 tests/core/fnmatch.c create mode 100644 tests/core/fnmatch.out diff --git a/emcc b/emcc index 4be24e206d691..c295a76e19647 100755 --- a/emcc +++ b/emcc @@ -1584,6 +1584,7 @@ try: 'wctomb.c', ]], ['regex', [ + 'fnmatch.c', 'regcomp.c', 'regerror.c', 'regexec.c', diff --git a/system/lib/libc/musl/src/regex/fnmatch.c b/system/lib/libc/musl/src/regex/fnmatch.c new file mode 100644 index 0000000000000..ffd3ea0d74fd2 --- /dev/null +++ b/system/lib/libc/musl/src/regex/fnmatch.c @@ -0,0 +1,299 @@ +/* + * An implementation of what I call the "Sea of Stars" algorithm for + * POSIX fnmatch(). The basic idea is that we factor the pattern into + * a head component (which we match first and can reject without ever + * measuring the length of the string), an optional tail component + * (which only exists if the pattern contains at least one star), and + * an optional "sea of stars", a set of star-separated components + * between the head and tail. After the head and tail matches have + * been removed from the input string, the components in the "sea of + * stars" are matched sequentially by searching for their first + * occurrence past the end of the previous match. + * + * - Rich Felker, April 2012 + */ + +#include +#include +#include +#include +#include + +#define END -1 +#define UNMATCHABLE -2 +#define BRACKET -3 +#define QUESTION -4 +#define STAR -5 + +static int str_next(const char *str, size_t n, size_t *step) +{ + if (!n) { + *step = 0; + return 0; + } + if (str[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, str, n); + if (k<0) { + *step = 1; + return -1; + } + *step = k; + return wc; + } + *step = 1; + return str[0]; +} + +static int pat_next(const char *pat, size_t m, size_t *step, int flags) +{ + int esc = 0; + if (!m || !*pat) { + *step = 0; + return END; + } + *step = 1; + if (pat[0]=='\\' && !(flags & FNM_NOESCAPE)) { + *step = 2; + pat++; + esc = 1; + goto escaped; + } + if (pat[0]=='[') { + size_t k = 1; + if (k= 128U) { + wchar_t wc; + int k = mbtowc(&wc, pat, m); + if (k<0) { + *step = 0; + return UNMATCHABLE; + } + *step = k + esc; + return wc; + } + return pat[0]; +} + +static int match_bracket(const char *p, int k) +{ + wchar_t wc; + int inv = 0; + p++; + if (*p=='^' || *p=='!') { + inv = 1; + p++; + } + if (*p==']') { + if (k==']') return !inv; + p++; + } else if (*p=='-') { + if (k=='-') return !inv; + p++; + } + wc = p[-1]; + for (; *p != ']'; p++) { + if (p[0]=='-' && p[1]!=']') { + wchar_t wc2; + int l = mbtowc(&wc2, p+1, 4); + if (l < 0) return 0; + if (wc<=wc2 && (unsigned)k-wc <= wc2-wc) return !inv; + p += l-1; + continue; + } + if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) { + const char *p0 = p+2; + int z = p[1]; + p+=3; + while (p[-1]!=z || p[0]!=']') p++; + if (z == ':' && p-1-p0 < 16) { + char buf[16]; + memcpy(buf, p0, p-1-p0); + buf[p-1-p0] = 0; + if (iswctype(k, wctype(buf))) return !inv; + } + continue; + } + if (*p < 128U) { + wc = (unsigned char)*p; + } else { + int l = mbtowc(&wc, p, 4); + if (l < 0) return 0; + p += l-1; + } + if (wc==k) return !inv; + } + return inv; +} + +static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n, int flags) +{ + const char *p, *ptail, *endpat; + const char *s, *stail, *endstr; + size_t pinc, sinc, tailcnt=0; + int c, k; + + if (flags & FNM_PERIOD) { + if (*str == '.' && *pat != '.') + return FNM_NOMATCH; + } + for (;;) { + switch ((c = pat_next(pat, m, &pinc, flags))) { + case UNMATCHABLE: + return FNM_NOMATCH; + case STAR: + pat++; + m--; + break; + default: + k = str_next(str, n, &sinc); + if (k <= 0) + return (c==END) ? 0 : FNM_NOMATCH; + str += sinc; + n -= sinc; + if (c == BRACKET) { + if (!match_bracket(pat, k)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c) { + return FNM_NOMATCH; + } + pat+=pinc; + m-=pinc; + continue; + } + break; + } + + /* Compute real pat length if it was initially unknown/-1 */ + m = strnlen(pat, m); + endpat = pat + m; + + /* Find the last * in pat and count chars needed after it */ + for (p=ptail=pat; pstr && tailcnt; tailcnt--) { + if (s[-1] < 128U) s--; + else while ((unsigned char)*--s-0x80U<0x40 && s>str); + } + if (tailcnt) return FNM_NOMATCH; + stail = s; + + /* Check that the pat and str tails match */ + p = ptail; + for (;;) { + c = pat_next(p, endpat-p, &pinc, flags); + p += pinc; + if ((k = str_next(s, endstr-s, &sinc)) <= 0) { + if (c != END) return FNM_NOMATCH; + break; + } + s += sinc; + if (c == BRACKET) { + if (!match_bracket(p-pinc, k)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c) { + return FNM_NOMATCH; + } + } + + /* We're all done with the tails now, so throw them out */ + endstr = stail; + endpat = ptail; + + /* Match pattern components until there are none left */ + while (pat 0) str += sinc; + else for (str++; str_next(str, endstr-str, &sinc)<0; str++); + } + + return 0; +} + +int fnmatch(const char *pat, const char *str, int flags) +{ + const char *s, *p; + size_t inc; + int c; + if (flags & FNM_PATHNAME) for (;;) { + for (s=str; *s && *s!='/'; s++); + for (p=pat; (c=pat_next(p, -1, &inc, flags))!=END && c!='/'; p+=inc); + if (*s && *p!=*s) return FNM_NOMATCH; + if (fnmatch_internal(pat, p-pat, str, s-str, flags)) + return FNM_NOMATCH; + if (!*s && c==END) return 0; + str = s+1; + pat = p+1; + } + return fnmatch_internal(pat, -1, str, -1, flags); +} diff --git a/system/lib/libcextra.symbols b/system/lib/libcextra.symbols index 6f1039f1dc7c9..54176b1da4c8f 100644 --- a/system/lib/libcextra.symbols +++ b/system/lib/libcextra.symbols @@ -187,3 +187,4 @@ T wmemmove T wmemset T wprintf + T fnmatch diff --git a/tests/core/fnmatch.c b/tests/core/fnmatch.c new file mode 100644 index 0000000000000..ebdb200909809 --- /dev/null +++ b/tests/core/fnmatch.c @@ -0,0 +1,79 @@ +// Begin test_fnmatch.cpp +#include +#include +#include +#include + +using namespace std; + +class TestCase { +public: + TestCase(const string& pattern, const string& testString, int flags, int expected) : + pattern(pattern), + testString(testString), + flags(flags), + expected(expected) + {} + string pattern; + string testString; + int flags; + int expected; +}; + +int main() +{ + vector testCases; + + testCases.push_back(TestCase("*","anything",0,0)); + testCases.push_back(TestCase("*.txt","readme.txt",0,0)); + testCases.push_back(TestCase("*.txt","readme.info",0,FNM_NOMATCH)); + testCases.push_back(TestCase("*.t?t","readme.txt",0,0)); + testCases.push_back(TestCase("*.t?t","readme.tot",0,0)); + testCases.push_back(TestCase("*.t?t","readme.txxt",0,FNM_NOMATCH)); + testCases.push_back(TestCase("[a-g]1","c1",0,0)); + testCases.push_back(TestCase("[a-g]1","i1",0,FNM_NOMATCH)); + testCases.push_back(TestCase("[!a-g]1","i1",0,0)); + testCases.push_back(TestCase("a\\*","anything",0,FNM_NOMATCH)); + testCases.push_back(TestCase("a\\*","a*",0,0)); + testCases.push_back(TestCase("a\\*","a*",FNM_NOESCAPE,FNM_NOMATCH)); + testCases.push_back(TestCase("a\\*","a\\*",FNM_NOESCAPE,0)); + testCases.push_back(TestCase("*readme","/etc/readme",0,0)); + testCases.push_back(TestCase("*readme","/etc/readme",FNM_PATHNAME,FNM_NOMATCH)); + testCases.push_back(TestCase("/*/readme","/etc/readme",FNM_PATHNAME,0)); + testCases.push_back(TestCase("*readme","/etc/.readme",0,0)); + testCases.push_back(TestCase("*readme",".readme",FNM_PERIOD,FNM_NOMATCH)); + testCases.push_back(TestCase("*.readme","/etc/.readme",FNM_PERIOD,0)); + testCases.push_back(TestCase("*.readme","/etc/.readme",FNM_PERIOD|FNM_PATHNAME,FNM_NOMATCH)); + testCases.push_back(TestCase("/*/.readme","/etc/.readme",FNM_PERIOD|FNM_PATHNAME,0)); + testCases.push_back(TestCase("ReAdME","readme",0,FNM_NOMATCH)); + + bool pass = true; + + for (vector::const_iterator it = testCases.begin(); it != testCases.end(); ++it) + { + int result = fnmatch(it->pattern.c_str(), it->testString.c_str(), it->flags); + if (result == it->expected) + cout << "Pass: "; + else + { + cout << "Fail: "; + pass = false; + } + + cout << "fnmatch(" << it->pattern << ", " << it->testString << ", " + << it->flags << ") returned " << result << ", expected " + << it->expected << endl; + } + + if (pass) + { + cout << "All tests passed." << endl; + return 0; + } + else + { + cout << "Some tests failed." << endl; + return 1; + } +} + diff --git a/tests/core/fnmatch.out b/tests/core/fnmatch.out new file mode 100644 index 0000000000000..303f7449b66e2 --- /dev/null +++ b/tests/core/fnmatch.out @@ -0,0 +1,23 @@ +Pass: fnmatch(*, anything, 0) returned 0, expected 0 +Pass: fnmatch(*.txt, readme.txt, 0) returned 0, expected 0 +Pass: fnmatch(*.txt, readme.info, 0) returned 1, expected 1 +Pass: fnmatch(*.t?t, readme.txt, 0) returned 0, expected 0 +Pass: fnmatch(*.t?t, readme.tot, 0) returned 0, expected 0 +Pass: fnmatch(*.t?t, readme.txxt, 0) returned 1, expected 1 +Pass: fnmatch([a-g]1, c1, 0) returned 0, expected 0 +Pass: fnmatch([a-g]1, i1, 0) returned 1, expected 1 +Pass: fnmatch([!a-g]1, i1, 0) returned 0, expected 0 +Pass: fnmatch(a\*, anything, 0) returned 1, expected 1 +Pass: fnmatch(a\*, a*, 0) returned 0, expected 0 +Pass: fnmatch(a\*, a*, 2) returned 1, expected 1 +Pass: fnmatch(a\*, a\*, 2) returned 0, expected 0 +Pass: fnmatch(*readme, /etc/readme, 0) returned 0, expected 0 +Pass: fnmatch(*readme, /etc/readme, 1) returned 1, expected 1 +Pass: fnmatch(/*/readme, /etc/readme, 1) returned 0, expected 0 +Pass: fnmatch(*readme, /etc/.readme, 0) returned 0, expected 0 +Pass: fnmatch(*readme, .readme, 4) returned 1, expected 1 +Pass: fnmatch(*.readme, /etc/.readme, 4) returned 0, expected 0 +Pass: fnmatch(*.readme, /etc/.readme, 5) returned 1, expected 1 +Pass: fnmatch(/*/.readme, /etc/.readme, 5) returned 0, expected 0 +Pass: fnmatch(ReAdME, readme, 0) returned 1, expected 1 +All tests passed. diff --git a/tests/test_core.py b/tests/test_core.py index 59f2f9e9713eb..a884ea7560c90 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3705,6 +3705,11 @@ def test_strstr(self): self.do_run_from_file(src, output) + def test_fnmatch(self): + test_path = path_from_root('tests', 'core', 'fnmatch') + src, output = (test_path + s for s in ('.c', '.out')) + self.do_run_from_file(src, output) + def test_sscanf(self): if self.emcc_args is None: return self.skip('needs emcc for libc') if not self.is_le32(): return self.skip('le32 needed for accurate math') diff --git a/tools/shared.py b/tools/shared.py index 1ee5cce86c410..bb50350df7b5b 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -345,7 +345,7 @@ def find_temp_directory(): # 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.9.2' +EMSCRIPTEN_VERSION = '1.9.3' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT + '|' + get_clang_version()