Skip to content
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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -385,4 +385,5 @@ a license to everyone to use it as detailed in LICENSE.)
* Florian Stellbrink <florian@stellbr.ink>
* Shane Peelar <lookatyouhacker@gmail.com>
* Alessandro Pignotti <alessandro@leaningtech.com>
* Zheng Tao Lee <zhengtao.lee@autodesk.com> (copyright owned by Autodesk, Inc.)
* Martina Kraus <kraus.martina.m@googlemail.com>
70 changes: 32 additions & 38 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,15 +1164,27 @@ 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(','))
ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&%s](%s)' % (sig, sig_mask, coerced_args), sig[0])
if not shared.Settings.EMULATED_FUNCTION_POINTERS:
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 = ('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 dynCall_%s(index%s%s) {
index = index|0;
Expand All @@ -1199,7 +1211,7 @@ def make_function_tables_impls(function_table_data):


def create_mftCall_funcs(function_table_data):
if not shared.Settings.EMULATED_FUNCTION_POINTERS:
if not asm_js_emulated_function_pointers():
return []
if shared.Settings.WASM or not shared.Settings.RELOCATABLE:
return []
Expand Down Expand Up @@ -1488,40 +1500,24 @@ 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'
if shared.Settings.EMULATED_FUNCTION_POINTERS:
args = ['a%d' % i for i in range(len(sig) - 1)]
full_args = ['x'] + args
if shared.Settings.WASM:
if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
# emulated function pointers in wasm use an internal i64-based ABI with a fixed number of arguments. we can't
# call into it directly because it returns an i64, which is an error for the VM. instead, we use dynCalls
dyn_call = "Module['asm']['dynCall_" + sig + "']"
asm_setup += '''
function ftCall_%s(%s) {
return %s(%s);
}
''' % (sig, ', '.join(full_args), dyn_call, ', '.join(full_args))
# and we are done with this signature, continue
continue
else:
# otherwise, wasm emulated function pointers *without* emulated casts can just all
# into the table
table_access = "wasmTable"
table_read = table_access + '.get(x)'
else:
# 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))
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 @@ -1545,10 +1541,8 @@ 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 shared.Settings.EMULATED_FUNCTION_POINTERS:
# in wasm, emulated function pointers are just simple table calls
if not shared.Settings.WASM:
basic_funcs.append('ftCall_%s' % sig)
if asm_js_emulated_function_pointers():
basic_funcs.append('ftCall_%s' % sig)
return basic_funcs


Expand Down Expand Up @@ -1615,7 +1609,7 @@ def create_asm_runtime_funcs():


def function_tables(function_table_data):
if not shared.Settings.EMULATED_FUNCTION_POINTERS:
if not asm_js_emulated_function_pointers():
return ['dynCall_' + table for table in function_table_data]
else:
return []
Expand Down
9 changes: 6 additions & 3 deletions src/parseTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1455,10 +1455,13 @@ function asmFFICoercion(value, type) {
}

function makeDynCall(sig) {
if (!EMULATED_FUNCTION_POINTERS) {
return 'dynCall_' + sig;
} else {
// 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;
}
}

Expand Down
36 changes: 36 additions & 0 deletions tests/other/noffi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2019 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdexcept>
#include <cstdint>
#include <dlfcn.h>

using namespace std;

uint64_t getbigint(){
int ran = rand() % 100;// v1 in the range 0 to 99
++ran;
if(ran > -1)
throw new std::runtime_error("error!!");

return 1152921504606846975 + ran;
}
int main()
{
float safeY = 0.0f;
uint64_t mybig = 0;

try{
mybig = getbigint();
}
catch(std::runtime_error){};

return 0;
}
32 changes: 30 additions & 2 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -7979,7 +7979,7 @@ def test_binaryen_metadce_cxx(self):
self.run_metadce_tests(path_from_root('tests', 'hello_libcxx.cpp'), [
(['-O2'], 34, ['abort'], ['waka'], 196709, 28, 37, 660), # noqa
(['-O2', '-s', 'EMULATED_FUNCTION_POINTERS=1'],
34, ['abort'], ['waka'], 196709, 28, 18, 621), # noqa
34, ['abort'], ['waka'], 196709, 28, 38, 642), # noqa
]) # noqa

def test_binaryen_metadce_hello(self):
Expand Down Expand Up @@ -8008,7 +8008,7 @@ def test_binaryen_metadce_hello(self):
0, [], [], 8, 0, 0, 0), # noqa; totally empty!
# we don't metadce with linkable code! other modules may want stuff
(['-O3', '-s', 'MAIN_MODULE=1'],
1533, [], [], 226057, 28, 85, None), # noqa; don't compare the # of functions in a main module, which changes a lot
1533, [], [], 226403, 28, 93, None), # noqa; don't compare the # of functions in a main module, which changes a lot
]) # noqa

# ensures runtime exports work, even with metadce
Expand Down Expand Up @@ -8070,6 +8070,34 @@ def test_legalize_js_ffi(self):
assert not e_f32_f64, 'f32 converted to f64 in exports'
assert e_i64_i64, 'i64 converted to i64 in exports'

def test_no_legalize_js_ffi(self):
# test minimal JS FFI legalization for invoke and dyncalls
if self.is_wasm_backend():
self.skipTest('not testing legalize with main module and wasm backend')
wasm_dis = os.path.join(Building.get_binaryen_bin(), 'wasm-dis')
for (args, js_ffi) in [
(['-s', 'LEGALIZE_JS_FFI=0', '-s', 'MAIN_MODULE=2', '-O3', '-s', 'DISABLE_EXCEPTION_CATCHING=0'], False),
]:
print(args)
try_delete('a.out.wasm')
try_delete('a.out.wast')
with env_modify({'EMCC_FORCE_STDLIBS': 'libc++'}):
cmd = [PYTHON, EMCC, path_from_root('tests', 'other', 'noffi.cpp'), '-g', '-o', 'a.out.js'] + args
print(' '.join(cmd))
run_process(cmd)
run_process([wasm_dis, 'a.out.wasm', '-o', 'a.out.wast'])
text = open('a.out.wast').read()
# remove internal comments and extra whitespace
text = re.sub(r'\(;[^;]+;\)', '', text)
text = re.sub(r'\$var\$*.', '', text)
text = re.sub(r'param \$\d+', 'param ', text)
text = re.sub(r' +', ' ', text)
# print("text: %s" % text)
i_legalimport_i64 = re.search('\(import.*\$legalimport\$invoke_j.*', text)
e_legalstub_i32 = re.search('\(func.*\$legalstub\$dyn.*\(type \$\d+\).*\(result i32\)', text)
assert i_legalimport_i64, 'legal import not generated for invoke call'
assert e_legalstub_i32, 'legal stub not generated for dyncall'

def test_sysconf_phys_pages(self):
for args, expected in [
([], 1024),
Expand Down