Skip to content

Commit

Permalink
Add a native and non-native pass to remove dead functions
Browse files Browse the repository at this point in the history
  • Loading branch information
aidanhs committed Apr 3, 2015
1 parent abbc840 commit d37586b
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 41 deletions.
7 changes: 7 additions & 0 deletions emcc
Expand Up @@ -1491,6 +1491,13 @@ try:
js_optimizer_queue = []
js_optimizer_extra_info = {}

# Regardless of what -OX mode we're in, dead functions should be removed. This pass
# should be considered first so other optimisations aren't taken along for the ride.
if shared.Settings.DEAD_FUNCTIONS:
js_optimizer_queue += ['eliminateDeadFuncs']
js_optimizer_extra_info['dead_functions'] = shared.Settings.DEAD_FUNCTIONS
flush_js_optimizer_queue()

if opt_level >= 1 and js_opts:
logging.debug('running js post-opts')

Expand Down
38 changes: 0 additions & 38 deletions emscripten.py
Expand Up @@ -814,44 +814,6 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
#if DEBUG: print >> sys.stderr, "META", metadata
#if DEBUG: print >> sys.stderr, "meminit", mem_init

# Strip functions to exclude from LLVM output
# This must be done as soon as possible, before we build function tables
# based on the functions in the js
rmfuncs = set(settings['DEAD_FUNCTIONS'])
if len(rmfuncs) > 0:
if DEBUG: logging.debug('Checking for dead functions')
funclines = funcs.split('\n')
new_funclines = []
infunc = False
shouldrm = False
for i, line in enumerate(funclines):
# The only lines we don't add are the *body* of the removed function -
# they get replaced by a comment, an abort and (if appropriate) a
# return value.
if shouldrm and not line.startswith('}'):
continue
new_funclines.append(line)
if line.startswith('function '):
assert not infunc
infunc = True
funcname = line[len('function '):line.index('(')]
if funcname in rmfuncs:
if DEBUG: logging.debug(' Stripping ' + funcname)
shouldrm = True
# Add parameter type hints to comply with asm.js
num_params = line.count('$')
new_funclines.extend(funclines[i+1:i+1+num_params])
# New body of function
new_funclines.append('/* Dead function %s stripped */' % (funcname,))
new_funclines.append('_abort();')
elif line.startswith('}'):
assert infunc
# If the original function had a return, make a return
if shouldrm and funclines[i-1].strip().startswith('return '):
new_funclines.insert(-1, 'return 0;')
infunc = shouldrm = False
funcs = '\n'.join(new_funclines)

# if emulating pointer casts, force all tables to the size of the largest
if settings['EMULATE_FUNCTION_POINTER_CASTS']:
max_size = 0
Expand Down
3 changes: 1 addition & 2 deletions tests/test_core.py
Expand Up @@ -6140,13 +6140,12 @@ def test(expected, args=[], no_build=False):
# Sanity check that it works and the dead function is emitted
js = test('*1*', ['x'])
test('*2*', no_build=True)
if has_comments: assert 'function _unused($' in js
if self.run_name in ['default', 'asm1', 'asm2g']: assert 'function _unused($' in js

# Kill off the dead function, still works and is emitted as a stub
Settings.DEAD_FUNCTIONS = ['_unused']

js = test('*2*')
if has_comments: assert 'Dead function _unused stripped' in js # stub comment

# Run the same code with argc that uses the dead function, see abort
test('abort() at Error', args=['x'], no_build=True)
Expand Down
21 changes: 21 additions & 0 deletions tools/js-optimizer.js
Expand Up @@ -7911,6 +7911,26 @@ function asmLastOpts(ast) {
});
}

// Contrary to the name this does not eliminate actual dead functions, only
// those marked as such with DEAD_FUNCTIONS
function eliminateDeadFuncs(ast) {
assert(asm);
assert(extraInfo && extraInfo.dead_functions);
var dead_functions = extraInfo.dead_functions;
traverseGeneratedFunctions(ast, function (fun, type) {
for (var i = 0; i < dead_functions.length; i++) {
if (dead_functions[i] === fun[1]) {
break;
}
return;
}
var asmData = normalizeAsm(fun);
fun[3] = [['stat', ['call', ['name', 'abort'], []]]];
asmData.vars = {};
denormalizeAsm(fun, asmData);
});
}

// Passes table

var minifyWhitespace = false, printMetadata = true, asm = false, asmPreciseF32 = false, emitJSON = false, last = false;
Expand All @@ -7931,6 +7951,7 @@ var passes = {
loopOptimizer: loopOptimizer,
registerize: registerize,
registerizeHarder: registerizeHarder,
eliminateDeadFuncs: eliminateDeadFuncs,
eliminate: eliminate,
eliminateMemSafe: eliminateMemSafe,
aggressiveVariableElimination: aggressiveVariableElimination,
Expand Down
2 changes: 1 addition & 1 deletion tools/js_optimizer.py
Expand Up @@ -9,7 +9,7 @@
def path_from_root(*pathelems):
return os.path.join(__rootpath__, *pathelems)

NATIVE_PASSES = set(['asm', 'asmPreciseF32', 'receiveJSON', 'emitJSON', 'eliminate', 'eliminateMemSafe', 'simplifyExpressions', 'simplifyIfs', 'optimizeFrounds', 'registerize', 'registerizeHarder', 'minifyNames', 'minifyLocals', 'minifyWhitespace', 'cleanup', 'asmLastOpts', 'last', 'noop', 'closure'])
NATIVE_PASSES = set(['asm', 'asmPreciseF32', 'receiveJSON', 'emitJSON', 'eliminateDeadFuncs', 'eliminate', 'eliminateMemSafe', 'simplifyExpressions', 'simplifyIfs', 'optimizeFrounds', 'registerize', 'registerizeHarder', 'minifyNames', 'minifyLocals', 'minifyWhitespace', 'cleanup', 'asmLastOpts', 'last', 'noop', 'closure'])

JS_OPTIMIZER = path_from_root('tools', 'js-optimizer.js')

Expand Down
26 changes: 26 additions & 0 deletions tools/optimizer/optimizer.cpp
Expand Up @@ -3869,6 +3869,31 @@ void asmLastOpts(Ref ast) {
});
}

// Contrary to the name this does not eliminate actual dead functions, only
// those marked as such with DEAD_FUNCTIONS
void eliminateDeadFuncs(Ref ast) {
assert(!!extraInfo);
IString DEAD_FUNCTIONS("dead_functions");
IString ABORT("abort");
assert(extraInfo->has(DEAD_FUNCTIONS));
Ref dead_functions = extraInfo[DEAD_FUNCTIONS];
traverseFunctions(ast, [&](Ref fun) {
for (size_t i = 0; i < dead_functions->size(); i++) {
if (dead_functions[i] == fun[1].get()->getCString()) {
break;
}
return;
}
AsmData asmData(fun);
fun[3]->setSize(1);
fun[3][0] = make1(STAT, make2(CALL, makeName(ABORT), makeArray()));
while (asmData.vars.size() > 0) {
asmData.deleteVar(asmData.vars[0]);
}
asmData.denormalize();
});
}

//==================
// Main
//==================
Expand Down Expand Up @@ -3925,6 +3950,7 @@ int main(int argc, char **argv) {
if (str == "asm") {} // the default for us
else if (str == "asmPreciseF32") {}
else if (str == "receiveJSON" || str == "emitJSON") {}
else if (str == "eliminateDeadFuncs") eliminateDeadFuncs(doc);
else if (str == "eliminate") eliminate(doc);
else if (str == "eliminateMemSafe") eliminateMemSafe(doc);
else if (str == "simplifyExpressions") simplifyExpressions(doc);
Expand Down

0 comments on commit d37586b

Please sign in to comment.