Skip to content

Commit

Permalink
remove emterpreter YIELDLIST - it complicates the mental model and is…
Browse files Browse the repository at this point in the history
… not worth it for the rather slim amount of benefit it provides
  • Loading branch information
kripken committed Jun 10, 2015
1 parent 5d970d5 commit fe87592
Show file tree
Hide file tree
Showing 7 changed files with 14 additions and 59 deletions.
2 changes: 1 addition & 1 deletion emcc
Expand Up @@ -1562,7 +1562,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', json.dumps(shared.Settings.EMTERPRETIFY_BLACKLIST), json.dumps(shared.Settings.EMTERPRETIFY_WHITELIST), json.dumps(shared.Settings.EMTERPRETIFY_YIELDLIST), str(shared.Settings.SWAPPABLE_ASM_MODULE)]
args = [shared.PYTHON, shared.path_from_root('tools', 'emterpretify.py'), js_target, final + '.em.js', json.dumps(shared.Settings.EMTERPRETIFY_BLACKLIST), json.dumps(shared.Settings.EMTERPRETIFY_WHITELIST), '', str(shared.Settings.SWAPPABLE_ASM_MODULE)]
if shared.Settings.EMTERPRETIFY_ASYNC:
args += ['ASYNC=1']
if shared.Settings.EMTERPRETIFY_ADVISE:
Expand Down
2 changes: 1 addition & 1 deletion src/library_async.js
Expand Up @@ -222,7 +222,7 @@ mergeInto(LibraryManager.library, {
#if ASSERTIONS
abortDecorators.push(function(output, what) {
if (EmterpreterAsync.state !== 0) {
return output + '\nThis error happened during an emterpreter-async save or load of the stack. Was there non-emterpreted code on the stack during save (which is unallowed)? You may want to adjust EMTERPRETIFY_BLACKLIST, EMTERPRETIFY_WHITELIST, or EMTERPRETIFY_YIELDLIST (to consider certain functions ok to run during an emscripten_sleep_with_yield).\nThis is what the stack looked like when we tried to save it: ' + [EmterpreterAsync.state, EmterpreterAsync.saveStack];
return output + '\nThis error happened during an emterpreter-async save or load of the stack. Was there non-emterpreted code on the stack during save (which is unallowed)? You may want to adjust EMTERPRETIFY_BLACKLIST, EMTERPRETIFY_WHITELIST.\nThis is what the stack looked like when we tried to save it: ' + [EmterpreterAsync.state, EmterpreterAsync.saveStack];
}
return output;
});
Expand Down
11 changes: 0 additions & 11 deletions src/settings.js
Expand Up @@ -464,22 +464,11 @@ 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_YIELDLIST = []; // A list of functions that are allowed to run during while sleeping. Typically this is
// during emscripten_sleep_with_yield , but also you may need to add methods to this list
// for things like event handling (an SDL EventHandler will be called from the event, directly -
// if we do that later, you lose out on the whole point of an EventHandler, which is to let
// you react to key presses in order to launch fullscreen, etc.).
// Functions in the yield list do not trigger asserts checking on running during a sleep,
// in ASSERTIONS builds,
var EMTERPRETIFY_ASYNC = 0; // Allows sync code in the emterpreter, by saving the call stack, doing an async delay, and resuming it
var EMTERPRETIFY_ADVISE = 0; // Performs a static analysis to suggest which functions should be run in the emterpreter, as it
// appears they can be on the stack when a sync function is called in the EMTERPRETIFY_ASYNC option.
// After showing the suggested list, compilation will halt. You can apply the provided list as an
// emcc argument when compiling later.
// This will also advise on the YIELDLIST, if it contains at least one value (it then reports
// all things reachable from that function, as they may need to be in the YIELDLIST as well).
// Note that this depends on things like inlining. If you run this with different inlining than
// when you use the list, it might not work.

var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler
var BOOTSTRAPPING_STRUCT_INFO = 0; // whether we are in the generate struct_info bootstrap phase
Expand Down
4 changes: 2 additions & 2 deletions tests/test_browser.py
Expand Up @@ -621,7 +621,7 @@ def test_sdl_key(self):
]:
for emterps in [
[],
['-DTEST_SLEEP', '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'ASSERTIONS=1', '-s', 'EMTERPRETIFY_YIELDLIST=["_EventHandler"]', '-s', "SAFE_HEAP=1"]
['-DTEST_SLEEP', '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'ASSERTIONS=1', '-s', "SAFE_HEAP=1"]
]:
print delay, defines, emterps
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
Expand Down Expand Up @@ -2400,7 +2400,7 @@ def test_emterpreter_async_sleep2(self):
self.btest('emterpreter_async_sleep2.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Oz'])

def test_sdl_audio_beep_sleep(self):
self.btest('sdl_audio_beep_sleep.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Os', '-s', 'ASSERTIONS=1', '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-profiling', '-s', 'EMTERPRETIFY_YIELDLIST=["__Z14audio_callbackPvPhi", "__ZN6Beeper15generateSamplesIhEEvPT_i", "__ZN6Beeper15generateSamplesIsEEvPT_i"]', '-s', 'SAFE_HEAP=1'])
self.btest('sdl_audio_beep_sleep.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Os', '-s', 'ASSERTIONS=1', '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-profiling', '-s', 'SAFE_HEAP=1'])

def test_mainloop_reschedule(self):
self.btest('mainloop_reschedule.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Os'])
Expand Down
7 changes: 0 additions & 7 deletions tests/test_other.py
Expand Up @@ -4418,13 +4418,6 @@ def test_emterpreter_advise(self):

out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise_funcptr.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1'], stdout=PIPE).communicate()
self.assertContained('-s EMTERPRETIFY_WHITELIST=\'["__Z4posti", "__Z5post2i", "__Z6middlev", "__Z7sleeperv", "__Z8recurserv", "_main"]\'', out)
self.assertNotContained('EMTERPRETIFY_YIELDLIST', out);

out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise_funcptr.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1', '-s', 'EMTERPRETIFY_YIELDLIST=["__Z6middlev"]'], stdout=PIPE).communicate()
self.assertContained('-s EMTERPRETIFY_YIELDLIST=\'["__Z6middlev", "__Z7siblingii", "__Z7sleeperv", "__Z8recurserv", "_printf"]\'', out)

out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'emterpreter_advise_funcptr.cpp'), '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'EMTERPRETIFY_ADVISE=1', '-s', 'EMTERPRETIFY_YIELDLIST=["__Z3pref"]'], stdout=PIPE).communicate()
self.assertContained('-s EMTERPRETIFY_YIELDLIST=\'["__Z3pref", "__Z7siblingii", "_printf"]\'', out)

def test_link_with_a_static(self):
for args in [[], ['-O2']]:
Expand Down
29 changes: 1 addition & 28 deletions tools/emterpretify.py
Expand Up @@ -57,7 +57,6 @@ def handle_arg(arg):

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 = []
YIELDLIST = ['stackSave', 'stackRestore', 'stackAlloc', 'setThrew', '_memset'] # functions which are ok to run while doing a sleep_with_yield.

SYNC_FUNCS = set(['_emscripten_sleep', '_emscripten_sleep_with_yield', '_emscripten_wget_data', '_emscripten_idb_load', '_emscripten_idb_store', '_emscripten_idb_delete'])

Expand Down Expand Up @@ -703,8 +702,6 @@ def process(code):
infile = sys.argv[1]
outfile = sys.argv[2]

original_yieldlist = YIELDLIST

extra_blacklist = []
if len(sys.argv) >= 4:
temp = sys.argv[3]
Expand All @@ -723,13 +720,6 @@ def process(code):
WHITELIST = 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()
YIELDLIST = YIELDLIST + json.loads(temp)

if len(sys.argv) >= 7:
SWAPPABLE = int(sys.argv[6])

Expand Down Expand Up @@ -774,23 +764,6 @@ def process(code):
print "Suggested list of functions to run in the emterpreter:"
print " -s EMTERPRETIFY_WHITELIST='" + str(sorted(list(advised))).replace("'", '"') + "'"
print "(%d%% out of %d functions)" % (int((100.0*len(advised))/len(can_call)), len(can_call))
if len(YIELDLIST) > len(original_yieldlist):
# advise on the yield list as well. Anything a yield function can reach, likely needs to also be a yield function
YIELD_IGNORE = set(['abort'])
to_check = list(YIELDLIST)
advised = set([str(f) for f in YIELDLIST])
while len(to_check) > 0:
curr = to_check.pop()
if curr not in can_call: continue
for next in can_call[curr]:
if next not in advised:
advised.add(str(next))
to_check.append(next)
advised = [next for next in advised if not is_dyn_call(next) and not is_function_table(next) and not next in original_yieldlist and next not in SYNC_FUNCS and next not in YIELD_IGNORE and next[0] == '_']
print
print "Suggested list of yield functions for the emterpreter:"
print " -s EMTERPRETIFY_YIELDLIST='" + str(sorted(list(advised))).replace("'", '"') + "'"
print "(%d%% out of %d functions)" % (int((100.0*len(advised))/len(can_call)), len(can_call))
sys.exit(0)

BLACKLIST = set(list(BLACKLIST) + extra_blacklist)
Expand Down Expand Up @@ -848,7 +821,7 @@ def process(code):
external_emterpreted_funcs = filter(lambda func: func in tabled_funcs or func in exported_funcs or func in reachable_funcs, emterpreted_funcs)

# process functions, generating bytecode
shared.Building.js_optimizer(infile, ['emterpretify'], extra_info={ 'emterpretedFuncs': list(emterpreted_funcs), 'externalEmterpretedFuncs': list(external_emterpreted_funcs), 'opcodes': OPCODES, 'ropcodes': ROPCODES, 'ASYNC': ASYNC, 'PROFILING': PROFILING, 'ASSERTIONS': ASSERTIONS, 'yieldFuncs': YIELDLIST }, output_filename=temp, just_concat=True)
shared.Building.js_optimizer(infile, ['emterpretify'], extra_info={ 'emterpretedFuncs': list(emterpreted_funcs), 'externalEmterpretedFuncs': list(external_emterpreted_funcs), 'opcodes': OPCODES, 'ropcodes': ROPCODES, 'ASYNC': ASYNC, 'PROFILING': PROFILING, 'ASSERTIONS': ASSERTIONS }, output_filename=temp, just_concat=True)

# load the module and modify it
asm = asm_module.AsmModule(temp)
Expand Down
18 changes: 9 additions & 9 deletions tools/js-optimizer.js
Expand Up @@ -5754,7 +5754,6 @@ function emterpretify(ast) {
var ASYNC = extraInfo.ASYNC;
var PROFILING = extraInfo.PROFILING;
var ASSERTIONS = extraInfo.ASSERTIONS;
var yieldFuncs = set(extraInfo.yieldFuncs);

var RELATIVE_BRANCHES = set('BR', 'BRT', 'BRF');
var ABSOLUTE_BRANCHES = set('BRA', 'BRTA', 'BRFA');
Expand Down Expand Up @@ -5782,6 +5781,14 @@ function emterpretify(ast) {
return Array.prototype.slice.call(tempUint8, 0, 8);
}

var OK_TO_CALL_WHILE_ASYNC = set('stackSave', 'stackRestore', 'stackAlloc', 'setThrew', '_memset'); // functions which are ok to run while async, even if not emterpreted
function okToCallWhileAsync(name) {
// dynCall *can* be on the stack, they are just bridges; what matters is where they go
if (/^dynCall_/.test(name)) return true;
if (name in OK_TO_CALL_WHILE_ASYNC) return true;
return false;
}

function verifyCode(code, stat) {
if (code.length % 4 !== 0) assert(0, JSON.stringify(code));
var len = code.length;
Expand Down Expand Up @@ -7037,12 +7044,9 @@ function emterpretify(ast) {

if (ignore) {
// we are not emterpreting this function
if (ASYNC && ASSERTIONS && !/^dynCall_/.test(func[1]) && !(func[1] in yieldFuncs)) {
if (ASYNC && ASSERTIONS && !okToCallWhileAsync(func[1])) {
// we need to be careful to never enter non-emterpreted code while doing an async save/restore,
// which is what happens if non-emterpreted code is on the stack while we attempt to save.
// note that we special-case dynCall, which *can* be on the stack, they are just bridges; what
// matters is where they go

// add asserts right after each call
var stack = [];
traverse(func, function(node, type) {
Expand Down Expand Up @@ -7251,10 +7255,6 @@ function emterpretify(ast) {
});
if (ASYNC) {
argStats.push(['if', srcToExp('(asyncState|0) == 1'), srcToStat('asyncState = 3;')]); // we know we are during a sleep, mark the state
if (ASSERTIONS && !(func[1] in yieldFuncs)) {
argStats.push(['if', srcToExp('((asyncState|0) == 1) | ((asyncState|0) == 3)'), srcToStat('abort(-12) | 0')]); // if *not* a yield func, we should never get here (trampoline entry)
// while sleeping (3, or 1 which has not yet been turned into a 3)
}
argStats = [['if', srcToExp('(asyncState|0) != 2'), ['block', argStats]]]; // 2 means restore, so do not trample the stack
}
func[3] = func[3].concat(argStats);
Expand Down

0 comments on commit fe87592

Please sign in to comment.