clarify the extra optimization for function pointers that we do in shared modules by default, by creating an EMULATED_FUNCTION_POINTERS=2 setting, in which we optimize, and 1 does not, and still allows for full flexibility in manipulating the function table from JS

This commit is contained in:
Alon Zakai 2015-08-18 16:01:22 -07:00
Родитель 55b5f3c60d
Коммит b9c24785c3
5 изменённых файлов: 69 добавлений и 3 удалений

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

@ -953,7 +953,8 @@ try:
if shared.Settings.RELOCATABLE:
assert shared.Settings.GLOBAL_BASE < 1
shared.Settings.EMULATED_FUNCTION_POINTERS = 1
if shared.Settings.EMULATED_FUNCTION_POINTERS == 0:
shared.Settings.EMULATED_FUNCTION_POINTERS = 2 # by default, use optimized function pointer emulation
shared.Settings.ERROR_ON_UNDEFINED_SYMBOLS = shared.Settings.WARN_ON_UNDEFINED_SYMBOLS = 0
if not shared.Settings.SIDE_MODULE:
shared.Settings.EXPORT_ALL = 1

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

@ -617,7 +617,11 @@ function ftCall_%s(%s) {%s
coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';'
mini_coerced_params = make_coerced_params(sig)
maybe_return = '' if sig[0] == 'v' else 'return'
funcs_js.append(make_func('mftCall_' + sig, 'if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + mini_coerced_params + ')', sig[0], settings) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', sig[0], settings) + ';', params, coercions) + '\n')
if settings['EMULATED_FUNCTION_POINTERS'] == 1:
body = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', sig[0], settings) + ';'
else:
body = 'if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + mini_coerced_params + ')', sig[0], settings) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', sig[0], settings) + ';'
funcs_js.append(make_func('mftCall_' + sig, body, params, coercions) + '\n')
def quote(prop):
if settings['USE_CLOSURE_COMPILER'] == 2:

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

@ -176,6 +176,16 @@ var EMULATED_FUNCTION_POINTERS = 0; // By default we implement function pointers
// function tables, which is very fast. With this option,
// we implement them more flexibly by emulating them: we
// call out into JS, which handles the function tables.
// 1: Full emulation. This means you can modify the
// table in JS fully dynamically, not just add to
// the end.
// 2: Optimized emulation. Assumes once something is
// added to the table, it will not change. This allows
// dynamic linking while keeping performance fast,
// as we can do a fast call into the internal table
// if the fp is in the right range. Shared modules
// (MAIN_MODULE, SIDE_MODULE) do this by default.
// This requires RELOCATABLE to be set.
var EMULATE_FUNCTION_POINTER_CASTS = 0; // Allows function pointers to be cast, wraps each
// call of an incorrect type with a runtime correction.
// This adds overhead and should not be used normally.

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

@ -465,7 +465,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
# extra coverages
for emulate_casts in [0, 1]:
for emulate_fps in [0, 1]:
for emulate_fps in [0, 1, 2]:
print emulate_casts, emulate_fps
Settings.EMULATE_FUNCTION_POINTER_CASTS = emulate_casts
Settings.EMULATED_FUNCTION_POINTERS = emulate_fps

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

@ -4933,6 +4933,57 @@ int main(int argc, char** argv) {
test(['-O' + str(opts)], 'no visible function tables')
test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=1'], 'function table: ')
def test_emulated_function_pointers_2(self):
src = r'''
#include <emscripten.h>
typedef void (*fp)();
void one() { EM_ASM( Module.print('one') ); }
void two() { EM_ASM( Module.print('two') ); }
void test() {
volatile fp f = one;
f();
f = two;
f();
}
int main(int argc, char **argv) {
test();
// swap them!
EM_ASM_INT({
var one = $0;
var two = $1;
if (typeof FUNCTION_TABLE_v === 'undefined') {
Module.print('no');
return;
}
var temp = FUNCTION_TABLE_v[one];
FUNCTION_TABLE_v[one] = FUNCTION_TABLE_v[two];
FUNCTION_TABLE_v[two] = temp;
}, (int)&one, (int)&two);
test();
return 0;
}
'''
open('src.c', 'w').write(src)
flipped = 'one\ntwo\ntwo\none\n'
unchanged = 'one\ntwo\none\ntwo\n'
no_table = 'one\ntwo\nno\none\ntwo\n'
def test(args, expected):
print args, expected.replace('\n', ' ')
Popen([PYTHON, EMCC, 'src.c'] + args).communicate()
self.assertContained(expected, run_js(self.in_dir('a.out.js')))
for opts in [0, 1, 2]:
test(['-O' + str(opts)], no_table)
test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=1'], flipped)
test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=2'], flipped)
test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=1', '-s', 'RELOCATABLE=1'], flipped)
test(['-O' + str(opts), '-s', 'EMULATED_FUNCTION_POINTERS=2', '-s', 'RELOCATABLE=1'], unchanged) # with both of those, we optimize and you cannot flip them
test(['-O' + str(opts), '-s', 'MAIN_MODULE=1'], unchanged) # default for modules is optimized
test(['-O' + str(opts), '-s', 'MAIN_MODULE=1', '-s', 'EMULATED_FUNCTION_POINTERS=2'], unchanged)
test(['-O' + str(opts), '-s', 'MAIN_MODULE=1', '-s', 'EMULATED_FUNCTION_POINTERS=1'], flipped) # but you can disable that
def test_file_packager_eval(self):
BAD = 'Module = eval('
src = path_from_root('tests', 'hello_world.c')