From aa5170bc45b479e3b8c5b7506a56934f26657845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 20 Jan 2021 16:27:53 +0200 Subject: [PATCH] Revise the API. --- emcc.py | 2 +- emscripten.py | 10 +- src/library_dyncall.js | 119 +++++++++----------- tests/bench_dyncall.c | 242 ++++++++++++++++++++++++++++++++--------- tests/test_core.py | 4 + tests/test_dyncalls.c | 38 ++----- tests/test_dyncalls.js | 162 +++++++++++---------------- 7 files changed, 323 insertions(+), 254 deletions(-) diff --git a/emcc.py b/emcc.py index 0ffd3ff7eba26..6c445f9e1820c 100755 --- a/emcc.py +++ b/emcc.py @@ -1283,7 +1283,7 @@ def default_setting(name, new_default): exit_with_error('Invalid option -s CLOSURE_WARNINGS=%s specified! Allowed values are "quiet", "warn" or "error".' % shared.Settings.CLOSURE_WARNINGS) # Calling function pointers from JS libraries is default runtime functionality, so always include the functionality. (to be DCEd if not used) - shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$dynCall', '$bindDynCall', '$bindDynCallArray'] + shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$dynCall'] if shared.Settings.MAIN_MODULE: assert not shared.Settings.SIDE_MODULE diff --git a/emscripten.py b/emscripten.py index 262a94db8d1e4..1fc10a512b60c 100644 --- a/emscripten.py +++ b/emscripten.py @@ -49,7 +49,9 @@ def make_wasm_table_static_dyncaller(func): i += 1 ptr_args = ('ptr, ' + args) if len(args) > 0 else 'ptr' - return 'function ' + func + '(' + ptr_args + ') { ' + ret + 'wasmTable.get(ptr)(' + args + '); }' + dyncall = ('dyncalls["' + sig + '"]') if shared.Settings.MINIMAL_RUNTIME else ('Module["' + func + '"]') + wasmTableGet = 'wasmTableGet' if shared.Settings.SHRINK_LEVEL == 0 else 'wasmTable.get' + return 'function ' + func + '(' + ptr_args + ') { ' + ret + dyncall + '(' + ptr_args + '); }\n' def compute_minimal_runtime_initializer_and_exports(post, exports, receiving): @@ -62,7 +64,7 @@ def compute_minimal_runtime_initializer_and_exports(post, exports, receiving): static_dyncall_sig_functions = '' - if shared.Settings.USE_LEGACY_DYNCALLS: + if shared.Settings.USE_LEGACY_DYNCALLS or not shared.Settings.WASM_BIGINT: if len([x for x in exports_that_are_not_initializers if x.startswith('dynCall_')]) > 0: exports_that_are_not_initializers += ['dynCalls = {}'] else: @@ -726,7 +728,7 @@ def create_receiving(exports): return '' exports_that_are_not_initializers = [x for x in exports if x != WASM_INIT_FUNC] - if not shared.Settings.USE_LEGACY_DYNCALLS: + if not shared.Settings.USE_LEGACY_DYNCALLS and shared.Settings.WASM_BIGINT: exports_that_are_not_initializers = [x for x in exports_that_are_not_initializers if not x.startswith('dynCall_')] receiving = [] @@ -743,7 +745,7 @@ def create_receiving(exports): # _main = asm["_main"]; for s in exports_that_are_not_initializers: mangled = asmjs_mangle(s) - dynCallAssignment = ('dynCalls["' + s.replace('dynCall_', '') + '"] = ') if shared.Settings.USE_LEGACY_DYNCALLS and mangled.startswith('dynCall_') else '' + dynCallAssignment = ('dynCalls["' + s.replace('dynCall_', '') + '"] = ') if shared.Settings.USE_LEGACY_DYNCALLS or not shared.Settings.WASM_BIGINT and mangled.startswith('dynCall_') else '' receiving += [dynCallAssignment + mangled + ' = asm["' + s + '"];'] else: if shared.Settings.MINIMAL_RUNTIME: diff --git a/src/library_dyncall.js b/src/library_dyncall.js index 087d3d28c374e..4889b8d103f0d 100644 --- a/src/library_dyncall.js +++ b/src/library_dyncall.js @@ -1,78 +1,67 @@ mergeInto(LibraryManager.library, { - $bindDynCall: function(funcPtr) { + {{{ (function() { global.wbind = function() { return SHRINK_LEVEL == 0 ? 'wbind' : 'wasmTable.get'; }; return null; })(); }}} + {{{ (function() { global.getDynCaller = function(sig) { return MINIMAL_RUNTIME ? `dynCalls[${sig}]` : `Module["dynCall_${sig}]`; }; return null; })(); }}} + +#if SHRINK_LEVEL == 0 + // A mirror copy of contents of wasmTable in JS side, to avoid relatively + // slow wasmTable.get() call. Only used when not compiling with -Os or -Oz. + _wasmTableMirror: [], + + $wbind__deps: ['_wasmTableMirror'], + $wbind: function(funcPtr) { + var func = __wasmTableMirror[funcPtr]; + if (!func) { + if (funcPtr >= __wasmTableMirror.length) __wasmTableMirror.length = funcPtr + 1; + __wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); + } + return func; + }, + + $dynCall__deps: ['$wbind'], + $bindDynCall__deps: ['$wbind'], + $wbindArray__deps: ['$wbind'], +#else + $wbind: function(funcPtr) { + // In -Os and -Oz builds, do not implement a JS side wasm table mirror for small + // code size, but directly access wasmTable, which is a bit slower. return wasmTable.get(funcPtr); }, +#endif - $bindDynCallArray: function(funcPtr) { - var func = wasmTable.get(funcPtr); - return func.length ? function(args) { - return func.apply(null, args); - } : function() { return func(); } + // A helper that binds a wasm function into a form that can be called by passing all + // the parameters in an array, e.g. wbindArray(func)([param1, param2, ..., paramN]). + $wbindArray: function(funcPtr) { + var func = {{{wbind()}}}(funcPtr); + return func.length + ? function(args) { return func.apply(null, args); } + : function() { return func(); } }, -#if USE_LEGACY_DYNCALLS || !WASM_BIGINT - $dynCallLegacy: function(sig, ptr, args) { -#if ASSERTIONS - assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); - if (args && args.length) { - // j (64-bit integer) must be passed in as two numbers [low 32, high 32]. - assert(args.length === sig.substring(1).replace(/j/g, '--').length); - } else { - assert(sig.length == 1); - } -#endif - if (args && args.length) { - return Module['dynCall_' + sig].apply(null, [ptr].concat(args)); - } - return Module['dynCall_' + sig].call(null, ptr); + // A helper that returns a function that can be used to invoke function pointers, i.e. + // getDynCaller('vi')(funcPtr, myInt); + $getDynCaller: function(sig, funcPtr) { + return {{{getDynCaller('sig')}}}; }, - // Used in library code to get JS function from wasm function pointer. - // All callers should use direct table access where possible and only fall - // back to this function if needed. - $getDynCaller__deps: ['$dynCall'], - $getDynCaller: function(sig, ptr) { -#if !USE_LEGACY_DYNCALLS - assert(sig.indexOf('j') >= 0, 'getDynCaller should only be called with i64 sigs') -#endif - var argCache = []; - return function() { - argCache.length = arguments.length; - for (var i = 0; i < arguments.length; i++) { - argCache[i] = arguments[i]; - } - return dynCall(sig, ptr, argCache); - }; + $bindDynCall: function(sig, funcPtr) { + // For int64 signatures, use the dynCall_sig dispatch mechanism. + if (sig.includes('j')) return function(args) { + return {{{getDynCaller('sig')}}}.apply(null, [funcPtr].concat(args)); + } + // For non-int64 signatures, invoke via the wasm table. + var func = {{{wbind()}}}(funcPtr); + return func.length + ? function(args) { return func.apply(null, args); } + : function() { return func(); } }, -#endif -// $dynCall__deps: ['$dynCallLegacy'], - $dynCall: function(sig, ptr, args) { -#if USE_LEGACY_DYNCALLS -#if MINIMAL_RUNTIME - var func = dynCalls[sig]; -#else - var func = Module['dynCall_'+sig]; -#endif - return args ? func.apply(null, [ptr].concat(args)) : func(ptr); -#else -#if !WASM_BIGINT - // Without WASM_BIGINT support we cannot directly call function with i64 as - // part of thier signature, so we rely the dynCall functions generated by - // wasm-emscripten-finalize - if (sig.indexOf('j') != -1) { -#if MINIMAL_RUNTIME - var func = dynCalls[sig]; -#else - var func = Module['dynCall_'+sig]; -#endif - return args ? func.apply(null, [ptr].concat(args)) : func(ptr); + $dynCall: function(sig, funcPtr, args) { + // For int64 signatures, use the dynCall_sig dispatch mechanism. + if (sig.includes('j')) { + return {{{getDynCaller('sig')}}}.apply(null, [funcPtr].concat(args)); } -#endif -#if ASSERTIONS - assert(wasmTable.get(ptr), 'missing table entry in dynCall: ' + ptr); -#endif - return wasmTable.get(ptr).apply(null, args) -#endif + + // For non-int64 signatures, invoke via the wasm table. + return {{{wbind()}}}(funcPtr).apply(null, args); } }); diff --git a/tests/bench_dyncall.c b/tests/bench_dyncall.c index 597ab59f3d878..03c6163ae3af6 100644 --- a/tests/bench_dyncall.c +++ b/tests/bench_dyncall.c @@ -1,10 +1,11 @@ #include +#include void v() {} -int iffddii(float f, float g, double d, double e, int i, int j) { return 1;} +int iffddjj(float f, float g, double d, double e, int64_t i, int64_t j) { return 1;} typedef void (*vptr)(); -typedef int (*iffddiiptr)(float f, float g, double d, double e, int i, int j); +typedef int (*iffddjjptr)(float f, float g, double d, double e, int64_t i, int64_t j); EM_JS(double, bench_static_direct_call_v, (vptr func), { testStarted('bench_static_direct_call_v'); @@ -36,7 +37,7 @@ EM_JS(double, bench_static_bound_call_v, (vptr func), { testStarted('bench_static_bound_call_v'); var numRuns = 11; var results = []; - var boundFunc = bindDynCall(func); + var boundFunc = bindDynCall('v', func); var run = setInterval(function() { var t0 = tick(); for(var i = 0; i < 100000; ++i) { @@ -89,7 +90,7 @@ EM_JS(double, bench_dynamic_bound_call_v, (vptr func), { testStarted('bench_dynamic_bound_call_v'); var numRuns = 11; var results = []; - var boundFunc = bindDynCallArray(func); + var boundFunc = bindDynCallArray('v', func); var run = setInterval(function() { var t0 = tick(); for(var i = 0; i < 100000; ++i) { @@ -112,112 +113,245 @@ EM_JS(double, bench_dynamic_bound_call_v, (vptr func), { }, 1); }); -EM_JS(double, bench_static_direct_call_iffddii, (iffddiiptr func), { - testStarted('bench_static_direct_call_iffddii'); +EM_JS(double, bench_static_direct_call_iffddjj, (iffddjjptr func), { + testStarted('bench_static_direct_call_iffddjj'); var numRuns = 11; var results = []; var run = setInterval(function() { var t0 = tick(); for(var i = 0; i < 100000; ++i) { - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); - dynCall_iffddii(func, 42.5, 16.5, 13.3, -12.1, 2, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + dynCall_iffddjj(func, 42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); } var t1 = tick(); if (numRuns-- >= 0) { results.push(t1 - t0); } else { clearInterval(run); - testFinished('bench_static_direct_call_iffddii', results); + testFinished('bench_static_direct_call_iffddjj', results); } }, 1); }); -EM_JS(double, bench_static_bound_call_iffddii, (iffddiiptr func), { - testStarted('bench_static_bound_call_iffddii'); +EM_JS(double, bench_static_bound_call_iffddjj, (iffddjjptr func), { + testStarted('bench_static_bound_call_iffddjj'); var numRuns = 11; var results = []; - var boundFunc = bindDynCall(func); + var boundFunc = bindDynCall('iffddjj', func); var run = setInterval(function() { var t0 = tick(); for(var i = 0; i < 100000; ++i) { - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); - boundFunc(42.5, 16.5, 13.3, -12.1, 2, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); } var t1 = tick(); if (numRuns-- >= 0) { results.push(t1 - t0); } else { clearInterval(run); - testFinished('bench_static_bound_call_iffddii', results); + testFinished('bench_static_bound_call_iffddjj', results); } }, 1); }); -EM_JS(double, bench_dynamic_direct_call_iffddii, (iffddiiptr func), { - testStarted('bench_dynamic_direct_call_iffddii'); +EM_JS(double, bench_dynamic_direct_call_iffddjj, (iffddjjptr func), { + testStarted('bench_dynamic_direct_call_iffddjj'); var numRuns = 11; var results = []; var run = setInterval(function() { var t0 = tick(); for(var i = 0; i < 100000; ++i) { - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); - dynCall('iffddii', func, [42.5, 16.5, 13.3, -12.1, 2, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + dynCall('iffddjj', func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); } var t1 = tick(); if (numRuns-- >= 0) { results.push(t1 - t0); } else { clearInterval(run); - testFinished('bench_dynamic_direct_call_iffddii', results); + testFinished('bench_dynamic_direct_call_iffddjj', results); } }, 1); }); -EM_JS(double, bench_dynamic_bound_call_iffddii, (iffddiiptr func), { - testStarted('bench_dynamic_bound_call_iffddii'); +EM_JS(double, bench_dynamic_bound_call_iffddjj, (iffddjjptr func), { + testStarted('bench_dynamic_bound_call_iffddjj'); var numRuns = 11; var results = []; - var boundFunc = bindDynCallArray(func); + var boundFunc = bindDynCallArray('iffddjj', func); var run = setInterval(function() { var t0 = tick(); for(var i = 0; i < 100000; ++i) { - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); - boundFunc([42.5, 16.5, 13.3, -12.1, 2, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); } var t1 = tick(); if (numRuns-- >= 0) { results.push(t1 - t0); } else { clearInterval(run); - testFinished('bench_dynamic_bound_call_iffddii', results); + testFinished('bench_dynamic_bound_call_iffddjj', results); } }, 1); }); + + + + + + + + + + + + + + + + + + + + + + + +EM_JS(double, bench_wbind_static_direct_call_iffddjj, (iffddjjptr func), { + testStarted('bench_wbind_static_direct_call_iffddjj'); + var numRuns = 11; + var results = []; + var run = setInterval(function() { + var t0 = tick(); + for(var i = 0; i < 100000; ++i) { + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + wbind(func)(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + } + var t1 = tick(); + if (numRuns-- >= 0) { + results.push(t1 - t0); + } else { + clearInterval(run); + testFinished('bench_wbind_static_direct_call_iffddjj', results); + } + }, 1); +}); + +EM_JS(double, bench_wbind_static_bound_call_iffddjj, (iffddjjptr func), { + testStarted('bench_wbind_static_bound_call_iffddjj'); + var numRuns = 11; + var results = []; + var boundFunc = wbind(func); + var run = setInterval(function() { + var t0 = tick(); + for(var i = 0; i < 100000; ++i) { + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + boundFunc(42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3); + } + var t1 = tick(); + if (numRuns-- >= 0) { + results.push(t1 - t0); + } else { + clearInterval(run); + testFinished('bench_wbind_static_bound_call_iffddjj', results); + } + }, 1); +}); + +EM_JS(double, bench_wbind_dynamic_direct_call_iffddjj, (iffddjjptr func), { + testStarted('bench_wbind_dynamic_direct_call_iffddjj'); + var numRuns = 11; + var results = []; + var run = setInterval(function() { + var t0 = tick(); + for(var i = 0; i < 100000; ++i) { + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + wbindArray(func, [42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + } + var t1 = tick(); + if (numRuns-- >= 0) { + results.push(t1 - t0); + } else { + clearInterval(run); + testFinished('bench_wbind_dynamic_direct_call_iffddjj', results); + } + }, 1); +}); + +EM_JS(double, bench_wbind_dynamic_bound_call_iffddjj, (iffddjjptr func), { + testStarted('bench_wbind_dynamic_bound_call_iffddjj'); + var numRuns = 11; + var results = []; + var boundFunc = wbindArray(func); + var run = setInterval(function() { + var t0 = tick(); + for(var i = 0; i < 100000; ++i) { + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + boundFunc([42.5, 16.5, 13.3, -12.1, 2, 2, 3, 3]); + } + var t1 = tick(); + if (numRuns-- >= 0) { + results.push(t1 - t0); + } else { + clearInterval(run); + testFinished('bench_wbind_dynamic_bound_call_iffddjj', results); + } + }, 1); +}); + + + + + int main() { EM_ASM({ var g = ENVIRONMENT_IS_NODE ? global : window; @@ -240,8 +374,8 @@ int main() { bench_static_direct_call_v(v); bench_static_bound_call_v(v); - bench_dynamic_direct_call_iffddii(iffddii); - bench_dynamic_bound_call_iffddii(iffddii); - bench_static_direct_call_iffddii(iffddii); - bench_static_bound_call_iffddii(iffddii); + bench_dynamic_direct_call_iffddjj(iffddjj); + bench_dynamic_bound_call_iffddjj(iffddjj); + bench_static_direct_call_iffddjj(iffddjj); + bench_static_bound_call_iffddjj(iffddjj); } diff --git a/tests/test_core.py b/tests/test_core.py index 2eca4a3340086..88ae8139e3e41 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8270,6 +8270,10 @@ def test_gl_main_module(self): self.set_setting('MAIN_MODULE') self.do_runf(path_from_root('tests', 'core', 'test_gl_get_proc_address.c')) + def test_dyncalls(self): + self.emcc_args += ['--js-library', path_from_root('tests', 'test_dyncalls.js')] + self.do_run_in_out_file_test('tests', 'test_dyncalls.c') + # Generate tests for everything def make_run(name, emcc_args, settings=None, env=None): diff --git a/tests/test_dyncalls.c b/tests/test_dyncalls.c index 2b45123688b26..fb8de6060a93d 100644 --- a/tests/test_dyncalls.c +++ b/tests/test_dyncalls.c @@ -1,44 +1,24 @@ #include #include +#include -void v() -{ - EM_ASM(console.log('v')); -} - -float f() -{ - EM_ASM(console.log('f')); - return 16.5; -} - -void vii(int i, int j) +void vijdf(int i, int64_t j, double d, float f) { - EM_ASM(console.log('vii: i='+$0+',j='+$1), i, j); + EM_ASM(console.log('vijdf: i='+$0+',jlo='+$1+',jhi='+$2+',d='+$3+',f='+$4), i, (uint32_t)j, (uint32_t)(((uint64_t)j) >> 32), d, f); } -double dfi(float f, int i) +int iii(int i, int j) { - EM_ASM(console.log('dfi: f='+$0+',i='+$1), f, i); - return 42.5; + EM_ASM(console.log('iii: i='+$0+',j='+$1), i, j); + return 42; } -void vijdf(int i, int64_t j, double d, float f) -{ - EM_ASM(console.log('vijdf: i='+$0+',j='+$1+',d='+$2+',f='+$3), i, j, d, f); -} -void test_dyncalls_v(void(*)()); -void test_dyncalls_f(float(*)()); -void test_dyncalls_vii(void(*)(int, int)); -void test_dyncalls_dfi(double(*)(float, int)); void test_dyncalls_vijdf(void(*)(int, int64_t, double, float)); +void test_dyncalls_iii(int(*)(int, int)); int main() { - test_dyncalls_v(&v); - test_dyncalls_f(&f); - test_dyncalls_vii(&vii); - test_dyncalls_dfi(&dfi); -// test_dyncalls_vijdf(&vijdf); + test_dyncalls_vijdf(&vijdf); + test_dyncalls_iii(&iii); } diff --git a/tests/test_dyncalls.js b/tests/test_dyncalls.js index b10309fb948ba..6c0ae3fa91992 100644 --- a/tests/test_dyncalls.js +++ b/tests/test_dyncalls.js @@ -1,106 +1,66 @@ mergeInto(LibraryManager.library, { - test_dyncalls_v: function(funcPtr) { - // 1. Directly access a function pointer via a static signature once - // (this is the fastest way to call a function pointer when one needs to only call it once, should always be preferred) - dynCall_v(funcPtr); - - // 2. Bind a function pointer via a static signature to call it multiple times. - // (this is the fastest way to call a function pointer when one needs to call it multiple times, should always be preferred) - var func = bindDynCall(funcPtr); - func(); - func(); - - // 3. Directly access a function pointer via a dynamic signature once - // (this form should be used only when one has a dynamic signature dispatch problem and the function is to be called only once) - dynCall('v', funcPtr); - - // 4. Bind a function pointer via a dynamic signature to call it multiple times. - // (this form should be used only when one has a dynamic signature dispatch problem and the function will be called multiple times) - var func = bindDynCallArray(funcPtr); - func(); - func(); - }, - - test_dyncalls_f: function(funcPtr) { - // 1. Access static signature once - var ret = dynCall_f(funcPtr); - console.log('f returns ' + ret); - - // 2. Bind a static signature for multiple calls - var func = bindDynCall(funcPtr); - ret = func(); - console.log('f returns ' + ret); - ret = func(); - console.log('f returns ' + ret); - - // 3. Access dynamic signature once - ret = dynCall('f', funcPtr); - console.log('f returns ' + ret); - - // 4. Bind a dynamic signature for multiple calls - var func = bindDynCallArray(funcPtr); - ret = func(); - ret = func(); - console.log('f returns ' + ret); - }, - - test_dyncalls_vii: function(funcPtr) { - // 1. Access static signature once - dynCall_vii(funcPtr, 1, 2); - - // 2. Bind a static signature for multiple calls - var func = bindDynCall(funcPtr); - func(2, 3); - func(3, 4); - - // 3. Access dynamic signature once - dynCall('vii', funcPtr, [4, 5]); - - // 4. Bind a dynamic signature for multiple calls - var func = bindDynCallArray(funcPtr); - func([5, 6]); - func([6, 7]); - }, - - test_dyncalls_dfi: function(funcPtr) { - // 1. Access static signature once - var ret = dynCall_dfi(funcPtr, 1, 2); - console.log('dfi returns ' + ret); - - // 2. Bind a static signature for multiple calls - var func = bindDynCall(funcPtr); - ret = func(2, 3); - console.log('dfi returns ' + ret); - ret = func(3, 4); - console.log('dfi returns ' + ret); - - // 3. Access dynamic signature once - ret = dynCall('vii', funcPtr, [4, 5]); - console.log('dfi returns ' + ret); - - // 4. Bind a dynamic signature for multiple calls - var func = bindDynCallArray(funcPtr); - ret = func([5, 6]); - console.log('dfi returns ' + ret); - ret = func([6, 7]); - console.log('dfi returns ' + ret); - }, - + test_dyncalls_vijdf__deps: ['$getDynCaller', '$bindDynCall', '$wbind', '$wbindArray'], test_dyncalls_vijdf: function(funcPtr) { - // 1. Access static signature once - dynCall_vijdf(funcPtr, 1, /*lo=*/2, /*hi=*/0, 3, 4); - - // 2. Bind a static signature for multiple calls - var func = bindDynCall(funcPtr); - func(2, /*lo=*/3, /*hi=*/0, 4, 5); - func(3, /*lo=*/4, /*hi=*/0, 5, 6); - - // 3. Access dynamic signature once - dynCall('vijdf', funcPtr, [4, /*lo=*/5, /*hi=*/0, 6, 7]); +#if WASM_BIGINT != 2 + // 1. Directly access a function pointer via a static signature (32-bit ABI) + // (this is the fastest way to call a function pointer when the signature is statically known in WASM_BIGINT==0 builds) + dynCall_vijdf(funcPtr, 1, /*lo=*/2, /*hi=*/3, 4, 5); // Available only in WASM_BIGINT != 2 builds + + // 2. Access a function pointer using the convenience/legacy 'dynCall' function (32-bit ABI) + // (this form should never be used, it is suboptimal for performance, but provided for legacy compatibility) + dynCall('vijdf', funcPtr, [3, /*lo=*/4, /*hi=*/5, 6, 7]); // Available only in WASM_BIGINT != 2 builds + + // 3. Obtain a dynamic function caller to a given signature and call it with .apply() (32-bit ABI) + // (this form should be used when dealing with a dynamic input signature problem with varying length of function args, and funcPtr + args are fused together in one array) + getDynCaller('vijdf').apply(null, [funcPtr, 4, /*lo=*/5, /*hi=*/6, 7, 8]); // Available only in WASM_BIGINT != 2 builds + + // 4. Obtain a function wrapper to given function pointer and call it by submitting args in an array (32-bit ABI) + // (this form should be used when dealing with a dynamic input signature problem with varying length of function args, but funcPtr and args params are dealt with separately) + bindDynCall('vijdf', funcPtr)([4, /*lo=*/5, /*hi=*/6, 7, 8]); // Available only in WASM_BIGINT != 2 builds +#endif + +#if WASM_BIGINT + // 5. Directly access a function pointer via a static signature (64-bit ABI) + // (this is the fastest way to call a function pointer when the signature is statically known in WASM_BIGINT>0 builds) + wbind(funcPtr)(2, BigInt(3) | (BigInt(4) << BigInt(32)), 5, 6); // Available in all builds, but in WASM_BIGINT==0 builds cannot be used to call int64 signatures + + // 6. Obtain an array form access to the specified signature. (64-bit ABI) + // (this form should be used when dealing with a dynamic input signature problem with varying length of function args) + wbindArray(funcPtr)([5, BigInt(6) | (BigInt(7) << BigInt(32)), 8, 9]); // Available in all builds, but in WASM_BIGINT==0 builds cannot be used to call int64 signatures +#endif + }, - // 4. Bind a dynamic signature for multiple calls - var func = bindDynCallArray(funcPtr); - func([5, /*lo=*/6, /*hi=*/0, 7, 8]); - func([6, /*lo=*/7, /*hi=*/0, 8, 9]); + test_dyncalls_iii: function(funcPtr) { +#if WASM_BIGINT != 2 + // 1. Directly access a function pointer via a static signature (32-bit ABI) + // (this is the fastest way to call a function pointer when the signature is statically known in WASM_BIGINT==0 builds) + var ret = dynCall_iii(funcPtr, 1, 2); // Available only in WASM_BIGINT != 2 builds + console.log('iii returned ' + ret); + + // 2. Access a function pointer using the convenience/legacy 'dynCall' function (32-bit ABI) + // (this form should never be used, it is suboptimal for performance, but provided for legacy compatibility) + var ret = dynCall('iii', funcPtr, [3, 4]); // Available only in WASM_BIGINT != 2 builds + console.log('iii returned ' + ret); + + // 3. Obtain a dynamic function caller to a given signature and call it with .apply() (32-bit ABI) + // (this form should be used when dealing with a dynamic input signature problem with varying length of function args, and funcPtr + args are fused together in one array) + var ret = getDynCaller('iii').apply(null, [funcPtr, 4, 5]); // Available only in WASM_BIGINT != 2 builds + console.log('iii returned ' + ret); + + // 4. Obtain a function wrapper to given function pointer and call it by submitting args in an array (32-bit ABI) + // (this form should be used when dealing with a dynamic input signature problem with varying length of function args, but funcPtr and args params are dealt with separately) + var ret = bindDynCall('iii', funcPtr)([5, 6]); // Available only in WASM_BIGINT != 2 builds + console.log('iii returned ' + ret); +#endif + + // 5. Directly access a function pointer via a static signature (64-bit ABI) + // (this is the fastest way to call a function pointer when the signature is statically known in WASM_BIGINT>0 builds) + var ret = wbind(funcPtr)(2, 3); // Available in all builds, but in WASM_BIGINT==0 builds cannot be used to call int64 signatures + console.log('iii returned ' + ret); + + // 6. Obtain an array form access to the specified signature. (64-bit ABI) + // (this form should be used when dealing with a dynamic input signature problem with varying length of function args) + var ret = wbindArray(funcPtr)([6, 7]); // Available in all builds, but in WASM_BIGINT==0 builds cannot be used to call int64 signatures + console.log('iii returned ' + ret); } });