Browse files

expression optimizer in eliminator

  • Loading branch information...
1 parent 6fe0acc commit bcccdc948dc02db5a5b17ef722ddf3f688a8cc9e @kripken committed Nov 7, 2011
View
3 tools/eliminator/eliminator-test-output.js
@@ -105,3 +105,6 @@ var anon = (function(x) {
var $8 = $4 + 12;
HEAP[$8] = $7;
});
+function r($0) {
+ HEAP[$0 + 7] = 107;
+}
View
5 tools/eliminator/eliminator-test.js
@@ -105,5 +105,8 @@ var anon = function(x) {
var $8 = $4 + 12;
HEAP[$8] = $7;
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py"]
+function r($0) {
+ HEAP[$0 + 5 + 2] = 99+5+2+1;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r"]
View
59 tools/eliminator/eliminator.coffee
@@ -23,6 +23,8 @@ fs = require 'fs'
# Functions which have been generated by Emscripten. We optimize only those.
generatedFunctions = []
GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS:'
+isGenerated = (ident) ->
+ ident in generatedFunctions
# Maximum number of uses to consider a variable not worth eliminating.
MAX_USES = 3
@@ -83,8 +85,6 @@ traverse = (node, callback) ->
# function/defun node and call run() to apply the optimization (in-place).
class Eliminator
constructor: (func) ->
- @ident = func[1]
-
# The statements of the function to analyze.
@body = func[3]
@@ -112,9 +112,6 @@ class Eliminator
# Runs the eliminator on a given function body updating the AST in-place.
# @returns: The number of variables eliminated, or undefined if skipped.
run: ->
- # Our optimization does not account for closures.
- if not @isGenerated() then return undefined
-
@calculateBasicVarStats()
@analyzeInitialValues()
@calculateTransitiveDependencies()
@@ -133,10 +130,6 @@ class Eliminator
return eliminated
- # Determines if a function is Emscripten-generated.
- isGenerated: ->
- return @ident in generatedFunctions
-
# Runs the basic variable scan pass. Fills the following member variables:
# isLocal
# isSingleDef
@@ -330,6 +323,44 @@ class Eliminator
return undefined
+# A class for optimizing expressions. We know that it is legitimate to collapse
+# 5+7 in the generated code, as it will always be numerical, for example.
+class ExpressionOptimizer
+ constructor: (node) ->
+ @node = node
+
+ run: ->
+ traverse @node, (node, type) ->
+ if type is 'binary' and node[1] == '+'
+ names = []
+ num = 0
+ has_num = false
+ fail = false
+ traverse node, (subNode, subType) ->
+ if subType is 'binary'
+ if subNode[1] isnt '+'
+ fail = true
+ return false
+ return undefined
+ else if subType is 'name'
+ names.push subNode[1]
+ return undefined
+ else if subType is 'num'
+ num += subNode[1]
+ has_num = true
+ return undefined
+ else
+ fail = true
+ return false
+ if not fail and has_num
+ ret = ['num', num]
+ for name in names
+ ret = ['binary', '+', ['name', name], ret]
+ return ret
+ return undefined
+
+ return undefined
+
# The main entry point. Reads JavaScript from stdin, runs the eliminator on each
# function, then writes the optimized result to stdout.
main = ->
@@ -343,15 +374,21 @@ main = ->
ast = uglify.parser.parse src
- # Run the eliminator on all functions.
+ # Run on all functions.
traverse ast, (node, type) ->
- if type in ['defun', 'function']
+ if type in ['defun', 'function'] and isGenerated node[1]
+
+ # Run the eliminator
process.stderr.write (node[1] || '(anonymous)') + '\n'
eliminated = new Eliminator(node).run()
if eliminated?
process.stderr.write " Eliminated #{eliminated} vars.\n"
+
+ # Run the expression optimizer
+ new ExpressionOptimizer(node[3]).run()
else
process.stderr.write ' Skipped.\n'
+
return undefined
# Write out the optimized code.

0 comments on commit bcccdc9

Please sign in to comment.