From 7d5be1566c822f1da0aca17c1869e74f547fb6a6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Jan 2015 17:56:11 -0800 Subject: [PATCH] add a whitelist option for the emterpreter; #3129 --- emcc | 4 ++-- src/settings.js | 2 ++ tests/test_other.py | 23 +++++++++++++++++++++++ tools/emterpretify.py | 14 +++++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/emcc b/emcc index 9c41112b3..fd9c2b3e3 100755 --- a/emcc +++ b/emcc @@ -70,7 +70,7 @@ LIB_PREFIXES = ('', 'lib') JS_CONTAINING_SUFFIXES = ('js', 'html') -DEFERRED_REPONSE_FILES = ('EMTERPRETIFY_BLACKLIST',) +DEFERRED_REPONSE_FILES = ('EMTERPRETIFY_BLACKLIST', 'EMTERPRETIFY_WHITELIST') # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt # levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get @@ -1555,7 +1555,7 @@ try: try: # move temp js to final position, alongside its mem init file shutil.move(final, js_target) - args = [shared.PYTHON, shared.path_from_root('tools', 'emterpretify.py'), js_target, final + '.em.js', memfile, json.dumps(shared.Settings.EMTERPRETIFY_BLACKLIST)] + args = [shared.PYTHON, shared.path_from_root('tools', 'emterpretify.py'), js_target, final + '.em.js', memfile, json.dumps(shared.Settings.EMTERPRETIFY_BLACKLIST), json.dumps(shared.Settings.EMTERPRETIFY_WHITELIST)] if shared.Settings.EMTERPRETIFY_ASYNC: args += ['ASYNC=1'] if profiling: diff --git a/src/settings.js b/src/settings.js index b1c59c0bb..e11225843 100644 --- a/src/settings.js +++ b/src/settings.js @@ -576,6 +576,8 @@ var NO_DYNAMIC_EXECUTION = 0; // When enabled, we do not emit eval() and new Fun var EMTERPRETIFY = 0; // Runs tools/emterpretify on the compiler output var EMTERPRETIFY_BLACKLIST = []; // Functions to not emterpret, that is, to run normally at full speed +var EMTERPRETIFY_WHITELIST = []; // If this contains any functions, then only the functions in this list + // are emterpreted (as if all the rest are blacklisted; this overrides the BLACKLIST) var EMTERPRETIFY_ASYNC = 0; // Allows sync code in the emterpreter, by saving the call stack, doing an async delay, and resuming it var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler diff --git a/tests/test_other.py b/tests/test_other.py index e8dcc4131..23e972730 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4378,6 +4378,29 @@ pass: error == ENOTDIR assert 'emterpret' not in get_func(src, '_main'), 'main is NOT emterpreted, it was blacklisted' assert 'emterpret' not in get_func(src, '_atoi'), 'atoi is NOT emterpreted either' + print 'whitelisting' + + do_emcc_test('fannkuch.cpp', ['5'], 'Pfannkuchen(5) = 7.', ['-s', 'EMTERPRETIFY_WHITELIST=[]']) + src = open('a.out.js').read() + assert 'emterpret' in get_func(src, '_main'), 'main is emterpreted' + assert 'function _atoi(' not in src, 'atoi is emterpreted and does not even have a trampoline, since only other emterpreted can reach it' + + do_emcc_test('fannkuch.cpp', ['5'], 'Pfannkuchen(5) = 7.', ['-s', 'EMTERPRETIFY_WHITELIST=["_main"]']) + src = open('a.out.js').read() + assert 'emterpret' in get_func(src, '_main') + assert 'emterpret' not in get_func(src, '_atoi'), 'atoi is not in whitelist, so it is not emterpreted' + + do_emcc_test('fannkuch.cpp', ['5'], 'Pfannkuchen(5) = 7.', ['-s', 'EMTERPRETIFY_WHITELIST=["_main", "_atoi"]']) + src = open('a.out.js').read() + assert 'emterpret' in get_func(src, '_main') + assert 'function _atoi(' not in src, 'atoi is emterpreted and does not even have a trampoline, since only other emterpreted can reach it' + + open('whitelist.txt', 'w').write('["_main"]') + do_emcc_test('fannkuch.cpp', ['5'], 'Pfannkuchen(5) = 7.', ['-s', 'EMTERPRETIFY_WHITELIST=@whitelist.txt']) + src = open('a.out.js').read() + assert 'emterpret' in get_func(src, '_main') + assert 'emterpret' not in get_func(src, '_atoi'), 'atoi is not in whitelist, so it is not emterpreted' + do_test(r''' #include diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 222e8c197..25650f45c 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -41,6 +41,7 @@ sys.argv = filter(handle_arg, sys.argv) # consts BLACKLIST = set(['_malloc', '_free', '_memcpy', '_memmove', '_memset', 'copyTempDouble', 'copyTempFloat', '_strlen', 'stackAlloc', 'setThrew', 'stackRestore', 'setTempRet0', 'getTempRet0', 'stackSave', 'runPostSets', '_emscripten_autodebug_double', '_emscripten_autodebug_float', '_emscripten_autodebug_i8', '_emscripten_autodebug_i16', '_emscripten_autodebug_i32', '_emscripten_autodebug_i64', '_strncpy', '_strcpy', '_strcat', '_saveSetjmp', '_testSetjmp', '_emscripten_replace_memory', '_bitshift64Shl', '_bitshift64Ashr', '_bitshift64Lshr', 'setAsyncState', 'emtStackSave']) +WHITELIST = [] OPCODES = [ # l, lx, ly etc - one of 256 locals 'SET', # [lx, ly, 0] lx = ly (int or float, not double) @@ -645,6 +646,13 @@ if __name__ == '__main__': assert temp[1] == '@' temp = open(temp[2:-1]).read() extra_blacklist = json.loads(temp) + if len(sys.argv) >= 6: + temp = sys.argv[5] + if temp[0] == '"': + # response file + assert temp[1] == '@' + temp = open(temp[2:-1]).read() + WHITELIST = json.loads(temp) BLACKLIST = set(list(BLACKLIST) + extra_blacklist) @@ -655,11 +663,15 @@ if __name__ == '__main__': asm = asm_module.AsmModule(infile) - # sanity check on blacklist + # process blacklist for func in extra_blacklist: assert func in asm.funcs, 'requested blacklist of %s but it does not exist' % func + if len(WHITELIST) > 0: + # we are using a whitelist: fill the blacklist with everything not whitelisted + BLACKLIST = set([func for func in asm.funcs if func not in WHITELIST]) + # decide which functions will be emterpreted, and find which are externally reachable (from outside other emterpreted code; those will need trampolines) emterpreted_funcs = set([func for func in asm.funcs if func not in BLACKLIST and not func.startswith('dynCall_')])