Make DEAD_FUNCTIONS work, add test
This commit is contained in:
Родитель
c83716cce4
Коммит
25999c92d3
8
emcc
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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче