Skip to content

Commit

Permalink
expression optimizer in eliminator
Browse files Browse the repository at this point in the history
  • Loading branch information
kripken committed Nov 8, 2011
1 parent 6fe0acc commit bcccdc9
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 12 deletions.
3 changes: 3 additions & 0 deletions tools/eliminator/eliminator-test-output.js
Expand Up @@ -105,3 +105,6 @@ var anon = (function(x) {
var $8 = $4 + 12;
HEAP[$8] = $7;
});
function r($0) {
HEAP[$0 + 7] = 107;
}
5 changes: 4 additions & 1 deletion tools/eliminator/eliminator-test.js
Expand Up @@ -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"]

59 changes: 48 additions & 11 deletions tools/eliminator/eliminator.coffee
Expand Up @@ -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
Expand Down Expand Up @@ -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]

Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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 = ->
Expand All @@ -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.
Expand Down

0 comments on commit bcccdc9

Please sign in to comment.