Skip to content

Remove fastcomp-only EMULATED_FUNCTION_POINTERS setting #11864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1569,8 +1569,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 @@ -1982,9 +1980,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 @@ -3208,7 +3203,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 @@ -418,14 +418,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 @@ -586,8 +578,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 @@ -1125,17 +1115,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 @@ -1150,9 +1129,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 @@ -1188,7 +1164,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 @@ -1229,38 +1205,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 @@ -1276,33 +1236,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 @@ -1428,10 +1362,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 @@ -1440,24 +1370,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 @@ -1483,8 +1395,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 @@ -1505,12 +1415,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 @@ -1530,10 +1434,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 @@ -1589,20 +1490,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 @@ -336,8 +336,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 @@ -347,27 +346,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