Make DEAD_FUNCTIONS work, add test

This commit is contained in:
Aidan Hobson Sayers 2015-03-07 15:29:59 +00:00
Родитель c83716cce4
Коммит 25999c92d3
5 изменённых файлов: 83 добавлений и 1 удалений

8
emcc
Просмотреть файл

@ -1070,6 +1070,10 @@ try:
memory_init_file = True
assert closure is not 2, 'EMTERPRETIFY requires valid asm.js, and is incompatible with closure 2 which disables that'
if shared.Settings.DEAD_FUNCTIONS and not js_opts:
js_opts = True
logging.warning('enabling js opts for DEAD_FUNCTIONS')
if proxy_to_worker:
shared.Settings.PROXY_TO_WORKER = 1
@ -1491,6 +1495,10 @@ try:
js_optimizer_queue = []
js_optimizer_extra_info = {}
if shared.Settings.DEAD_FUNCTIONS:
js_optimizer_queue += ['eliminateDeadFuncs']
js_optimizer_extra_info['dead_functions'] = shared.Settings.DEAD_FUNCTIONS
if opt_level >= 1 and js_opts:
logging.debug('running js post-opts')

Просмотреть файл

@ -6125,6 +6125,38 @@ def process(filename):
self.emcc_args += ['--closure', '1']
self.do_run_from_file(src, output, post_build=post)
def test_dead_functions(self):
src = r'''
#include <stdio.h>
extern "C" {
__attribute__((noinline)) int unused(int x) {
return x;
}
}
int main(int argc, char **argv) {
printf("*%d*\n", argc > 1 ? unused(1) : 2);
return 0;
}
'''
def test(expected, args=[], no_build=False):
self.do_run(src, expected, args=args, no_build=no_build)
return open(self.in_dir('src.cpp.o.js')).read()
# Sanity check that it works and the dead function is emitted
js = test('*1*', ['x'])
test('*2*', no_build=True)
if self.run_name in ['default', 'asm1', 'asm2g']: assert 'function _unused($' in js
# Kill off the dead function, and check a code path using it aborts
Settings.DEAD_FUNCTIONS = ['_unused']
test('*2*')
test('abort(-1) at Error', args=['x'], no_build=True)
# Kill off a library function, check code aborts
Settings.DEAD_FUNCTIONS = ['_printf']
test('abort(-1) at Error')
test('abort(-1) at Error', args=['x'], no_build=True)
def test_pgo(self):
if Settings.ASM_JS: return self.skip('PGO does not work in asm mode')

Просмотреть файл

@ -7916,6 +7916,23 @@ 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 deadFunctions = set(extraInfo.dead_functions);
traverseGeneratedFunctions(ast, function (fun, type) {
if (!(fun[1] in deadFunctions)) {
return;
}
var asmData = normalizeAsm(fun);
fun[3] = [['stat', ['call', ['name', 'abort'], [['num', -1]]]]];
asmData.vars = {};
denormalizeAsm(fun, asmData);
});
}
// Passes table
var minifyWhitespace = false, printMetadata = true, asm = false, asmPreciseF32 = false, emitJSON = false, last = false;
@ -7936,6 +7953,7 @@ var passes = {
loopOptimizer: loopOptimizer,
registerize: registerize,
registerizeHarder: registerizeHarder,
eliminateDeadFuncs: eliminateDeadFuncs,
eliminate: eliminate,
eliminateMemSafe: eliminateMemSafe,
aggressiveVariableElimination: aggressiveVariableElimination,

Просмотреть файл

@ -9,7 +9,7 @@ __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
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')

Просмотреть файл

@ -3869,6 +3869,29 @@ 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));
StringSet deadFunctions;
for (size_t i = 0; i < extraInfo[DEAD_FUNCTIONS]->size(); i++) {
deadFunctions.insert(extraInfo[DEAD_FUNCTIONS][i]->getIString());
}
traverseFunctions(ast, [&](Ref fun) {
if (!deadFunctions.has(fun[1].get()->getIString())) {
return;
}
AsmData asmData(fun);
fun[3]->setSize(1);
fun[3][0] = make1(STAT, make2(CALL, makeName(ABORT), &(makeArray())->push_back(makeNum(-1))));
asmData.vars.clear();
asmData.denormalize();
});
}
//==================
// Main
//==================
@ -3925,6 +3948,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);