Skip to content

Commit

Permalink
Remove fastcomp-only EMULATED_FUNCTION_POINTERS setting (#11864)
Browse files Browse the repository at this point in the history
See #11860
  • Loading branch information
kripken authored Aug 11, 2020
1 parent 2b6e3af commit c0470c7
Show file tree
Hide file tree
Showing 14 changed files with 46 additions and 1,110 deletions.
7 changes: 1 addition & 6 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1564,8 +1564,6 @@ def check(input_file):
exit_with_error('Cannot set GLOBAL_BASE when building SIDE_MODULE')

if shared.Settings.RELOCATABLE:
if 'EMULATED_FUNCTION_POINTERS' not in settings_key_changes and not shared.Settings.WASM_BACKEND:
shared.Settings.EMULATED_FUNCTION_POINTERS = 2 # by default, use optimized function pointer emulation
shared.Settings.ERROR_ON_UNDEFINED_SYMBOLS = 0
shared.Settings.WARN_ON_UNDEFINED_SYMBOLS = 0

Expand Down Expand Up @@ -1975,9 +1973,6 @@ def include_and_export(name):
if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
# emulated function pointer casts is emulated in fastcomp wasm using a binaryen pass
options.binaryen_passes += ['--fpcast-emu']
# we also need emulated function pointers for that, as we need a single flat
# table, as is standard in wasm, and not asm.js split ones.
shared.Settings.EMULATED_FUNCTION_POINTERS = 1

if shared.Settings.WASM2JS:
if not shared.Settings.WASM_BACKEND:
Expand Down Expand Up @@ -3194,7 +3189,7 @@ def do_binaryen(target, asm_target, options, memfile, wasm_binary_target,
cmd += ['--mem-base=' + str(shared.Settings.GLOBAL_BASE)]
# various options imply that the imported table may not be the exact size as
# the wasm module's own table segments
if shared.Settings.RELOCATABLE or shared.Settings.RESERVED_FUNCTION_POINTERS > 0 or shared.Settings.EMULATED_FUNCTION_POINTERS:
if shared.Settings.RELOCATABLE or shared.Settings.RESERVED_FUNCTION_POINTERS > 0:
cmd += ['--table-max=-1']
if shared.Settings.SIDE_MODULE:
cmd += ['--mem-max=-1']
Expand Down
123 changes: 5 additions & 118 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,14 +414,6 @@ def define_asmjs_import_names(imports):

function_tables_impls = make_function_tables_impls(function_table_data)
final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs
if shared.Settings.EMULATED_FUNCTION_POINTERS:
final_function_tables = (
final_function_tables
.replace("asm['", '')
.replace("']", '')
.replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_')
.replace('var dynCall_', '//')
)

if DEBUG:
logger.debug('asm text sizes' + str([
Expand Down Expand Up @@ -580,8 +572,6 @@ def create_backend_cmd(infile, temp_js):
args += ['-emscripten-assertions=%d' % shared.Settings.ASSERTIONS]
if shared.Settings.ALIASING_FUNCTION_POINTERS == 0:
args += ['-emscripten-no-aliasing-function-pointers']
if shared.Settings.EMULATED_FUNCTION_POINTERS:
args += ['-emscripten-emulated-function-pointers']
if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
args += ['-emscripten-emulate-function-pointer-casts']
if shared.Settings.RELOCATABLE:
Expand Down Expand Up @@ -1119,17 +1109,6 @@ def make_bad(target=None):
start = raw.index('[')
end = raw.rindex(']')
body = raw[start + 1:end].split(',')
if shared.Settings.EMULATED_FUNCTION_POINTERS:
def receive(item):
if item == '0':
return item
if item not in all_implemented:
# this is not implemented; it would normally be wrapped, but with emulation, we just use it directly outside
return item
in_table.add(item)
return "asm['" + item + "']"

body = [receive(b) for b in body]
for j in range(shared.Settings.RESERVED_FUNCTION_POINTERS):
curr = 'jsCall_%s_%s' % (sig, j)
body[1 + j] = curr
Expand All @@ -1144,9 +1123,6 @@ def fix_item(item):
# emulate all non-null pointer calls, if asked to
if j > 0 and shared.Settings.EMULATE_FUNCTION_POINTER_CASTS and not shared.Settings.WASM and j in function_pointer_targets:
proper_sig, proper_target = function_pointer_targets[j]
if shared.Settings.EMULATED_FUNCTION_POINTERS:
if proper_target in all_implemented:
proper_target = "asm['" + proper_target + "']"

def make_emulated_param(i):
if i >= len(sig):
Expand Down Expand Up @@ -1182,7 +1158,7 @@ def make_emulated_param(i):
# when emulating function pointers, we don't need wrappers
# but if relocating, then we also have the copies in-module, and do
# in wasm we never need wrappers though
if clean_item not in implemented_functions and not (shared.Settings.EMULATED_FUNCTION_POINTERS and not shared.Settings.RELOCATABLE) and not shared.Settings.WASM:
if clean_item not in implemented_functions and shared.Settings.RELOCATABLE and not shared.Settings.WASM:
# this is imported into asm, we must wrap it
call_ident = clean_item
if call_ident in metadata['redirects']:
Expand Down Expand Up @@ -1223,38 +1199,22 @@ def math_fix(g):
return g if not g.startswith('Math_') else g.split('_')[1]


# asm.js function tables have one table in each linked asm.js module, so we
# can't just dynCall into them - ftCall exists for that purpose. In wasm,
# even linked modules share the table, so it's all fine.
def asm_js_emulated_function_pointers():
return shared.Settings.EMULATED_FUNCTION_POINTERS and not shared.Settings.WASM


def make_function_tables_impls(function_table_data):
function_tables_impls = []
for sig, table in function_table_data.items():
args = ','.join(['a' + str(i) for i in range(1, len(sig))])
arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i]) + ';' for i in range(1, len(sig))])
coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i]) for i in range(1, len(sig))])
sig_mask = str(table.count(','))
if not (shared.Settings.WASM and shared.Settings.EMULATED_FUNCTION_POINTERS):
ret = 'FUNCTION_TABLE_%s[index&%s](%s)' % (sig, sig_mask, coerced_args)
else:
# for wasm with emulated function pointers, emit an mft_SIG(..) call, we avoid asm.js function tables there.
ret = 'mftCall_%s(index%s%s)' % (sig, ',' if len(sig) > 1 else '', coerced_args)
ret = 'FUNCTION_TABLE_%s[index&%s](%s)' % (sig, sig_mask, coerced_args)
ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion(ret, sig[0])
if not asm_js_emulated_function_pointers():
function_tables_impls.append('''
function_tables_impls.append('''
function dynCall_%s(index%s%s) {
index = index|0;
%s
%s;
}
''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret))
else:
function_tables_impls.append('''
var dynCall_%s = ftCall_%s;
''' % (sig, sig))

ffi_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], ffi_arg=True) for i in range(1, len(sig))])
for i in range(shared.Settings.RESERVED_FUNCTION_POINTERS):
Expand All @@ -1270,33 +1230,7 @@ def make_function_tables_impls(function_table_data):


def create_mftCall_funcs(function_table_data):
if not asm_js_emulated_function_pointers():
return []
if shared.Settings.WASM or not shared.Settings.RELOCATABLE:
return []

mftCall_funcs = []
# in wasm, emulated function pointers are just simple table calls
for sig, table in function_table_data.items():
return_type, sig_args = sig[0], sig[1:]
num_args = len(sig_args)
params = ','.join(['ptr'] + ['p%d' % i for i in range(num_args)])
coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i')] + [shared.JS.make_coercion('p%d' % i, unfloat(sig_args[i])) for i in range(num_args)])
coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (i, shared.JS.make_coercion('p%d' % i, unfloat(sig_args[i]))) for i in range(num_args)]) + ';'
mini_coerced_params = ','.join([shared.JS.make_coercion('p%d' % i, sig_args[i]) for i in range(num_args)])
maybe_return = '' if return_type == 'v' else 'return'
final_return = maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', unfloat(return_type)) + ';'
if shared.Settings.EMULATED_FUNCTION_POINTERS == 1:
body = final_return
else:
sig_mask = str(table.count(','))
body = ('if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + ' + sig_mask + ' | 0))) { ' + maybe_return + ' ' +
shared.JS.make_coercion(
'FUNCTION_TABLE_' + sig + '[(ptr-fb)&' + sig_mask + '](' +
mini_coerced_params + ')', return_type, ffi_arg=True
) + '; ' + ('return;' if return_type == 'v' else '') + ' }' + final_return)
mftCall_funcs.append(make_func('mftCall_' + sig, body, params, coercions) + '\n')
return mftCall_funcs
return []


def get_function_pointer_error(sig, function_table_sigs):
Expand Down Expand Up @@ -1416,10 +1350,6 @@ def check(extern):
asm_setup += create_invoke_wrappers(invoke_function_names)
asm_setup += setup_function_pointers(function_table_sigs)

if shared.Settings.EMULATED_FUNCTION_POINTERS:
function_tables_impls = make_function_tables_impls(function_table_data)
asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n'

return asm_setup


Expand All @@ -1428,24 +1358,6 @@ def setup_function_pointers(function_table_sigs):
for sig in function_table_sigs:
if shared.Settings.RESERVED_FUNCTION_POINTERS:
asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n'
# nothing special to do here for wasm, we just use dynCalls
if not shared.Settings.WASM:
if shared.Settings.EMULATED_FUNCTION_POINTERS:
args = ['a%d' % i for i in range(len(sig) - 1)]
full_args = ['x'] + args
table_access = 'FUNCTION_TABLE_' + sig
if shared.Settings.SIDE_MODULE:
table_access = 'parentModule["' + table_access + '"]' # side module tables were merged into the parent, we need to access the global one
table_read = table_access + '[x]'
prelude = ''
if shared.Settings.ASSERTIONS:
prelude = '''
if (x < 0 || x >= %s.length) { err("Function table mask error (out of range)"); %s ; abort(x) }''' % (table_access, get_function_pointer_error(sig, function_table_sigs))
asm_setup += '''
function ftCall_%s(%s) {%s
return %s(%s);
}
''' % (sig, ', '.join(full_args), prelude, table_read, ', '.join(args))
return asm_setup


Expand All @@ -1471,8 +1383,6 @@ def create_basic_funcs(function_table_sigs, invoke_function_names):
for sig in function_table_sigs:
if shared.Settings.RESERVED_FUNCTION_POINTERS:
basic_funcs.append('jsCall_%s' % sig)
if asm_js_emulated_function_pointers():
basic_funcs.append('ftCall_%s' % sig)
return basic_funcs


Expand All @@ -1493,12 +1403,6 @@ def create_basic_vars(exported_implemented_functions, forwarded_json, metadata):

def create_exports(exported_implemented_functions, in_table, function_table_data, metadata):
all_exported = exported_implemented_functions + function_tables(function_table_data)
# In asm.js + emulated function pointers, export all the table because we use
# JS to add the asm.js module's functions to the table (which is external
# in this mode). In wasm, we don't need that since wasm modules can
# directly add functions to the imported Table.
if not shared.Settings.WASM and shared.Settings.EMULATED_FUNCTION_POINTERS:
all_exported += in_table
exports = []
for export in sorted(set(all_exported)):
exports.append(quote(export) + ": " + export)
Expand All @@ -1518,10 +1422,7 @@ def create_exports(exported_implemented_functions, in_table, function_table_data


def function_tables(function_table_data):
if not asm_js_emulated_function_pointers():
return ['dynCall_' + table for table in function_table_data]
else:
return []
return ['dynCall_' + table for table in function_table_data]


def create_the_global(metadata):
Expand Down Expand Up @@ -1577,20 +1478,6 @@ def create_receiving(function_table_data, function_tables_defs, exported_impleme
table = table.replace('var ' + tableName, 'var ' + tableName + ' = Module["' + tableName + '"]')
receiving += table + '\n'

if shared.Settings.EMULATED_FUNCTION_POINTERS:
# in asm.js emulated function tables, emit the table on the outside, where
# JS can manage it (for wasm, a native wasm Table is used directly, and we
# don't need this)
if not shared.Settings.WASM:
receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '')
# wasm still needs definitions for dyncalls on the outside, for JS
receiving += '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in function_table_data])
if not shared.Settings.WASM:
for sig in function_table_data.keys():
name = 'FUNCTION_TABLE_' + sig
fullname = name if not shared.Settings.SIDE_MODULE else ('SIDE_' + name)
receiving += 'Module["' + name + '"] = ' + fullname + ';\n'

return receiving


Expand Down
9 changes: 1 addition & 8 deletions src/parseTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1466,14 +1466,7 @@ function asmFFICoercion(value, type) {
}

function makeDynCall(sig) {
// asm.js function tables have one table in each linked asm.js module, so we
// can't just dynCall into them - ftCall exists for that purpose. In wasm,
// even linked modules share the table, so it's all fine.
if (EMULATED_FUNCTION_POINTERS && !WASM) {
return 'ftCall_' + sig;
} else {
return 'dynCall_' + sig;
}
return 'dynCall_' + sig;
}

function heapAndOffset(heap, ptr) { // given HEAP8, ptr , we return splitChunk, relptr
Expand Down
37 changes: 2 additions & 35 deletions src/runtime_functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* SPDX-License-Identifier: MIT
*/

#if !WASM_BACKEND && EMULATED_FUNCTION_POINTERS == 0
#if !WASM_BACKEND
var jsCallStartIndex = 1;
var functionPointers = new Array({{{ RESERVED_FUNCTION_POINTERS }}});
#endif // !WASM_BACKEND && EMULATED_FUNCTION_POINTERS == 0
#endif // !WASM_BACKEND

#if WASM
// Wraps a JS function as a wasm function with a given signature.
Expand Down Expand Up @@ -197,7 +197,6 @@ function addFunction(func, sig) {
return addFunctionWasm(func, sig);
#else

#if EMULATED_FUNCTION_POINTERS == 0
var base = 0;
for (var i = base; i < base + {{{ RESERVED_FUNCTION_POINTERS }}}; i++) {
if (!functionPointers[i]) {
Expand All @@ -207,45 +206,13 @@ function addFunction(func, sig) {
}
throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.';

#else // EMULATED_FUNCTION_POINTERS == 0

#if WASM
return addFunctionWasm(func, sig);
#else
alignFunctionTables(); // TODO: we should rely on this being an invariant
var tables = getFunctionTables();
var ret = -1;
for (var signature in tables) {
var table = tables[signature];
if (ret < 0) ret = table.length;
else assert(ret === table.length);
table.push(func);
}
return ret;
#endif // WASM

#endif // EMULATED_FUNCTION_POINTERS == 0
#endif // WASM_BACKEND
}

function removeFunction(index) {
#if WASM_BACKEND
removeFunctionWasm(index);
#else

#if EMULATED_FUNCTION_POINTERS == 0
functionPointers[index-jsCallStartIndex] = null;
#else
#if WASM
removeFunctionWasm(index);
#else
alignFunctionTables(); // XXX we should rely on this being an invariant
var tables = getFunctionTables();
for (var sig in tables) {
tables[sig][index] = null;
}
#endif // WASM

#endif // EMULATE_FUNCTION_POINTER_CASTS == 0
#endif // WASM_BACKEND
}
24 changes: 1 addition & 23 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,7 @@ var SAFE_HEAP = 0;
var SAFE_HEAP_LOG = 0;

// In asm.js mode, we cannot simply add function pointers to function tables, so
// we reserve some slots for them. An alternative to this is to use
// EMULATED_FUNCTION_POINTERS, in which case we don't need to reserve.
// we reserve some slots for them.
// [fastcomp-only]
var RESERVED_FUNCTION_POINTERS = 0;

Expand All @@ -324,27 +323,6 @@ var RESERVED_FUNCTION_POINTERS = 0;
// [fastcomp-only]
var ALIASING_FUNCTION_POINTERS = 0;

// asm.js: By default we implement function pointers using asm.js 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.
// wasm:
// By default we use a wasm Table for function pointers, which is fast and
// efficient. When enabling emulation, we also use the Table *outside* the wasm
// module, exactly as when emulating in asm.js, just replacing the plain JS
// array with a Table.
// [fastcomp-only]
var EMULATED_FUNCTION_POINTERS = 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. It also forces ALIASING_FUNCTION_POINTERS to 0. Aside from making
Expand Down
Loading

0 comments on commit c0470c7

Please sign in to comment.