aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-12-15 18:39:03 -0800
committerAlon Zakai <alonzakai@gmail.com>2011-12-15 18:39:03 -0800
commit693dcd6d303554f8e5bf0278b2186d6acbee72b6 (patch)
tree02a53601c970d7a81f47a256646f1ac5f80b7c83
parent8b1ee6dde83f6b4a10978b36a18c628dfa0a0ca8 (diff)
js optimizer pass to simplify certain bit ops
-rwxr-xr-xemcc12
-rw-r--r--tests/runner.py7
-rw-r--r--tools/js-optimizer.js42
-rw-r--r--tools/test-js-optimizer-output.js7
-rw-r--r--tools/test-js-optimizer.js5
5 files changed, 60 insertions, 13 deletions
diff --git a/emcc b/emcc
index 3e00d9ec..d059a4b0 100755
--- a/emcc
+++ b/emcc
@@ -219,8 +219,9 @@ if header: # header or such
if TEMP_DIR:
temp_dir = TEMP_DIR
- if not os.path.exists(temp_dir):
- os.makedirs(temp_dir)
+ if os.path.exists(temp_dir):
+ shutil.rmtree(temp_dir) # clear it
+ os.makedirs(temp_dir)
else:
temp_dir = tempfile.mkdtemp()
@@ -386,14 +387,17 @@ try:
# eliminator
final = shared.Building.eliminator(final)
+ # js optimizer pre-pass
+ final = shared.Building.js_optimizer(final, 'simplifyExpressionsPre')
+
if closure:
if DEBUG: print >> sys.stderr, 'emcc: running closure'
final = shared.Building.closure_compiler(final)
if opt_level >= 1:
- # js optimizer
+ # js optimizer post-pass
if DEBUG: print >> sys.stderr, 'emcc: running post-closure post-opts'
- final = shared.Building.js_optimizer(final, 'simplifyExpressions')
+ final = shared.Building.js_optimizer(final, 'simplifyExpressionsPost')
# If we were asked to also generate HTML, do that
if final_suffix == 'html':
diff --git a/tests/runner.py b/tests/runner.py
index 5812c36e..68562215 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -4953,7 +4953,6 @@ Options that are modified or new in %s include:
# Verify optimization level etc. in the generated code
# XXX these are quite sensitive, and will need updating when code generation changes
generated = open('something.js').read() # TODO: parse out the _main function itself, not support code, if the tests below need that some day
- assert ('|0)/2)|0)' in generated or '| 0) / 2 | 0)' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2'
assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default'
assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default'
assert ': while(' not in generated, 'when relooping we also js-optimize, so there should be no labelled whiles'
@@ -4961,6 +4960,8 @@ Options that are modified or new in %s include:
assert 'Module._main = ' in generated, 'closure compiler should have been run'
else:
# closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
+ assert 'Module._main = ' not in generated, 'closure compiler should not have been run'
+ # XXX find a way to test this: assert ('& 255' in generated or '&255' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2'
assert ('(__label__)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2'
assert ('assert(STACKTOP < STACK_MAX)' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
assert 'var $i;' in generated, 'micro opts should always be on'
@@ -5081,7 +5082,7 @@ Options that are modified or new in %s include:
def test_js_optimizer(self):
input = open(path_from_root('tools', 'test-js-optimizer.js')).read()
expected = open(path_from_root('tools', 'test-js-optimizer-output.js')).read()
- output = Popen([NODE_JS, JS_OPTIMIZER, 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressions', 'loopOptimizer'],
+ output = Popen([NODE_JS, JS_OPTIMIZER, 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost', 'loopOptimizer'],
stdin=PIPE, stdout=PIPE).communicate(input)[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
@@ -5122,8 +5123,6 @@ else:
print 'Benchmarking JS engine:', JS_ENGINE
Building.COMPILER_TEST_OPTS = []
- # TODO: Use other js optimizer options, like remove assigns to undefined (seems to slow us down more than speed us up)
- POST_OPTIMIZATIONS = [['js-optimizer', 'loopOptimizer'], 'eliminator', 'closure', ['js-optimizer', 'simplifyExpressions']]
TEST_REPS = 10
TOTAL_TESTS = 7
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d22de39c..010739d3 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -260,7 +260,44 @@ function removeUnneededLabelSettings(ast) {
}
// Various expression simplifications
-function simplifyExpressions(ast) {
+function simplifyExpressionsPre(ast) {
+ // When there is a bunch of math like (((8+5)|0)+12)|0, only the external |0 is needed, one correction is enough.
+ // At each node, ((X|0)+Y)|0 can be transformed into (X+Y): The inner corrections are not needed
+ // TODO: Is the same is true for 0xff, 0xffff?
+
+ function simplifyBitops(ast) {
+ var SAFE_BINARY_OPS = set('+', '-', '*', '/', '%', '|');
+ var ZERO = ['num', 0];
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+ traverseGenerated(ast, function(node, type, stack) {
+ if (type == 'binary' && node[1] == '|' && (jsonCompare(node[2], ZERO) || jsonCompare(node[3], ZERO))) {
+ stack.push(1); // From here on up, no need for this kind of correction, it's done at the top
+
+ // We might be able to remove this correction
+ for (var i = stack.length-2; i >= 0; i--) {
+ if (stack[i] == 1) {
+ // Great, we can eliminate
+ rerun = true;
+ return jsonCompare(node[2], ZERO) ? node[3] : node[2];
+ } else if (stack[i] == -1) {
+ break; // Too bad, we can't
+ }
+ }
+ } else if ((type == 'binary' && node[1] in SAFE_BINARY_OPS) || type == 'num' || type == 'name') {
+ stack.push(0); // This node is safe in that it does not interfere with this optimization
+ } else {
+ stack.push(-1); // This node is dangerous! Give up if you see this before you see '1'
+ }
+ }, null, []);
+ }
+ }
+
+ simplifyBitops(ast);
+}
+
+function simplifyExpressionsPost(ast) {
// We often have branchings that are simplified so one end vanishes, and
// we then get
// if (!(x < 5))
@@ -373,7 +410,8 @@ var passes = {
unGlobalize: unGlobalize,
removeAssignsToUndefined: removeAssignsToUndefined,
//removeUnneededLabelSettings: removeUnneededLabelSettings,
- simplifyExpressions: simplifyExpressions,
+ simplifyExpressionsPre: simplifyExpressionsPre,
+ simplifyExpressionsPost: simplifyExpressionsPost,
loopOptimizer: loopOptimizer
};
diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js
index fcd2380d..95426478 100644
--- a/tools/test-js-optimizer-output.js
+++ b/tools/test-js-optimizer-output.js
@@ -79,6 +79,9 @@ function ignoreLoopy() {
}
}
function bits() {
- print((($s & 65535) + ((($f & 65535) << 16 >> 16) * (($f & 65535) << 16 >> 16) | 0 | 0) % 256 | 0) & 65535);
+ print((($s & 65535) + (($f & 65535) << 16 >> 16) * (($f & 65535) << 16 >> 16) % 256 | 0) & 65535);
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits"]
+function maths() {
+ __ZN6b2Vec2C1Ev($this1 + 20 + 8 + 8 + 8 + 8 + 8 + 8 + 8 | 0);
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths"]
diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js
index e06ebd29..c59454e6 100644
--- a/tools/test-js-optimizer.js
+++ b/tools/test-js-optimizer.js
@@ -83,5 +83,8 @@ function ignoreLoopy() {
function bits() { // TODO: optimize this!
print((($s & 65535) + ((($f & 65535) << 16 >> 16) * (($f & 65535) << 16 >> 16) | 0 | 0) % 256 | 0) & 65535);
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits"]
+function maths() {
+ __ZN6b2Vec2C1Ev(((((((($this1 + 20 | 0 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0);
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths"]