Skip to content

Commit cf6c237

Browse files
committed
Use direct table access over dynCall where possible
This change starts the transition away from dynCall and towards direct use of the wasm table. In particular this change replaces the use of dynCall in invoke_xx functions where possible. dynCall is still needed for functions with i64 in thier signature when WASM_BIGINT is not enabled. When WASM_BIGING is enabled this change removes all use of dynCall by invoke_xx functions.
1 parent ed2e2dc commit cf6c237

18 files changed

+94
-56
lines changed

src/library_browser.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,15 +1181,9 @@ var LibraryBrowser = {
11811181
Browser.mainLoop.arg = arg;
11821182

11831183
var browserIterationFunc;
1184-
if (typeof arg !== 'undefined') {
1185-
browserIterationFunc = function() {
1186-
Module['dynCall_vi'](func, arg);
1187-
};
1188-
} else {
1189-
browserIterationFunc = function() {
1190-
Module['dynCall_v'](func);
1191-
};
1192-
}
1184+
browserIterationFunc = function() {
1185+
tableCall(func, arg);
1186+
};
11931187

11941188
#if USE_CLOSURE_COMPILER
11951189
// Closure compiler bug(?): Closure does not see that the assignment

src/library_exceptions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ var LibraryExceptions = {
194194
var destructor = info.get_destructor();
195195
if (destructor) {
196196
// In Wasm, destructors return 'this' as in ARM
197-
Module['dynCall_ii'](destructor, info.excPtr);
197+
tableCall(destructor, info.excPtr);
198198
}
199199
___cxa_free_exception(info.excPtr);
200200
#if EXCEPTION_DEBUG

src/library_sdl.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ var LibrarySDL = {
873873
if (!SDL.eventHandler) return;
874874

875875
while (SDL.pollEvent(SDL.eventHandlerTemp)) {
876-
Module['dynCall_iii'](SDL.eventHandler, SDL.eventHandlerContext, SDL.eventHandlerTemp);
876+
tableCall(SDL.eventHandler, SDL.eventHandlerContext, SDL.eventHandlerTemp);
877877
}
878878
},
879879

src/library_webgpu.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ var LibraryWebGPU = {
10181018
if (ev.error instanceof GPUValidationError) type = Validation;
10191019
else if (ev.error instanceof GPUOutOfMemoryError) type = OutOfMemory;
10201020
var messagePtr = allocateUTF8(ev.error.message);
1021-
dynCall_viii(callback, type, messagePtr, userdata);
1021+
tableCall(callback, type, messagePtr, userdata);
10221022
_free(messagePtr);
10231023
};
10241024
},
@@ -1031,9 +1031,9 @@ var LibraryWebGPU = {
10311031
var completionValue = {{{ gpu.makeU64ToNumber('completionValue_low', 'completionValue_high') }}};
10321032

10331033
fence.onCompletion(completionValue).then(function() {
1034-
dynCall_vii(callback, 0 /* WEBGPU_FENCE_COMPLETION_STATUS_SUCCESS */, userdata);
1034+
tableCall(callback, 0 /* WEBGPU_FENCE_COMPLETION_STATUS_SUCCESS */, userdata);
10351035
}, function() {
1036-
dynCall_vii(callback, 1 /* WEBGPU_FENCE_COMPLETION_STATUS_ERROR */, userdata);
1036+
tableCall(callback, 1 /* WEBGPU_FENCE_COMPLETION_STATUS_ERROR */, userdata);
10371037
});
10381038
},
10391039

@@ -1374,10 +1374,10 @@ var LibraryWebGPU = {
13741374
// `callback` takes (WGPUBufferMapAsyncStatus status, void * userdata)
13751375

13761376
buffer["mapAsync"](mode, offset, size).then(function() {
1377-
dynCall_vii(callback, 0 /* WGPUBufferMapAsyncStatus_Success */, userdata);
1377+
tableCall(callback, 0 /* WGPUBufferMapAsyncStatus_Success */, userdata);
13781378
}, function() {
13791379
// TODO(kainino0x): Figure out how to pick other error status values.
1380-
dynCall_vii(callback, 1 /* WGPUBufferMapAsyncStatus_Error */, userdata);
1380+
tableCall(callback, 1 /* WGPUBufferMapAsyncStatus_Error */, userdata);
13811381
});
13821382
},
13831383

src/preamble.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ if (typeof WebAssembly !== 'object') {
4444

4545
var wasmMemory;
4646

47-
// In fastcomp asm.js, we don't need a wasm Table at all.
48-
// In the wasm backend, we polyfill the WebAssembly object,
49-
// so this creates a (non-native-wasm) table for us.
50-
#include "runtime_init_table.js"
47+
#include "runtime_table.js"
5148

5249
#if USE_PTHREADS
5350
// For sending to workers.
@@ -428,11 +425,7 @@ function callRuntimeCallbacks(callbacks) {
428425
}
429426
var func = callback.func;
430427
if (typeof func === 'number') {
431-
if (callback.arg === undefined) {
432-
Module['dynCall_v'](func);
433-
} else {
434-
Module['dynCall_vi'](func, callback.arg);
435-
}
428+
tableCall(func, callback.arg);
436429
} else {
437430
func(callback.arg === undefined ? null : callback.arg);
438431
}
@@ -902,6 +895,7 @@ function createWasm() {
902895
// then exported.
903896
// TODO: do not create a Memory earlier in JS
904897
wasmMemory = exports['memory'];
898+
wasmTable = exports['__indirect_function_table'];
905899
updateGlobalBufferAndViews(wasmMemory.buffer);
906900
#if ASSERTIONS
907901
writeStackCookie();

src/preamble_minimal.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ assert(buffer instanceof SharedArrayBuffer, 'requested a shared WebAssembly.Memo
102102
#endif
103103
#endif
104104

105-
#include "runtime_init_table.js"
105+
#include "runtime_table.js"
106106

107107
#else
108108

@@ -211,11 +211,7 @@ function callRuntimeCallbacks(callbacks) {
211211
}
212212
var func = callback.func;
213213
if (typeof func === 'number') {
214-
if (callback.arg === undefined) {
215-
dynCall_v(func);
216-
} else {
217-
dynCall_vi(func, callback.arg);
218-
}
214+
tableCall(func, callback.arg);
219215
} else {
220216
func(callback.arg === undefined ? null : callback.arg);
221217
}

src/runtime_init_table.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/runtime_table.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright 2017 The Emscripten Authors
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#if STANDALONE_WASM // standalone wasm creates the table in the wasm
8+
var wasmTable;
9+
#else
10+
var wasmTable = new WebAssembly.Table({
11+
'initial': {{{ getQuoted('WASM_TABLE_SIZE') }}},
12+
#if !ALLOW_TABLE_GROWTH
13+
'maximum': {{{ getQuoted('WASM_TABLE_SIZE') }}},
14+
#endif
15+
'element': 'anyfunc'
16+
});
17+
#endif
18+
19+
// TODO(sbc): Avoid this gross hack using `rest` ES6 operator once we
20+
// enable ES6: https://github.com/emscripten-core/emscripten/issues/11984
21+
// function tableCall(ptr, ...args) {
22+
// return wasmTable.get(ptr).apply(null, args);
23+
// }
24+
var tableCallArgs = [];
25+
function tableCall(ptr) {
26+
tableCallArgs.length = arguments.length - 1;
27+
for (var i = 1; i < arguments.length; i++) {
28+
tableCallArgs[i - 1] = arguments[i];
29+
}
30+
return wasmTable.get(ptr).apply(null, tableCallArgs);
31+
}

src/support.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ function createInvokeFunction(sig) {
301301
return function() {
302302
var sp = stackSave();
303303
try {
304-
return Module['dynCall_' + sig].apply(null, arguments);
304+
return dynCall(sig, arguments[0], Array.prototype.slice.call(arguments, 1));
305305
} catch(e) {
306306
stackRestore(sp);
307307
if (e !== e+0 && e !== 'longjmp') throw e;
@@ -602,24 +602,32 @@ function makeBigInt(low, high, unsigned) {
602602

603603
/** @param {Array=} args */
604604
function dynCall(sig, ptr, args) {
605-
if (args && args.length) {
606605
#if ASSERTIONS
606+
if (args && args.length) {
607607
// j (64-bit integer) must be passed in as two numbers [low 32, high 32].
608608
assert(args.length === sig.substring(1).replace(/j/g, '--').length);
609-
#endif
610-
#if ASSERTIONS
611-
assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
612-
#endif
613-
return Module['dynCall_' + sig].apply(null, [ptr].concat(args));
614609
} else {
615-
#if ASSERTIONS
616610
assert(sig.length == 1);
611+
}
617612
#endif
613+
614+
#if !WASM_BIGINT
615+
if (sig.indexOf('j') != -1) {
618616
#if ASSERTIONS
619-
assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
617+
assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\'');
620618
#endif
621-
return Module['dynCall_' + sig].call(null, ptr);
619+
if (args && args.length) {
620+
return Module['dynCall_' + sig].apply(null, [ptr].concat(args));
621+
} else {
622+
return Module['dynCall_' + sig].call(null, ptr);
623+
}
622624
}
625+
#endif
626+
627+
// TODO(sbc): Use ...args once ES6 is allowed:
628+
// tableCall(ptr, ...args);
629+
// https://github.com/emscripten-core/emscripten/issues/11984
630+
return tableCall.apply(null, [ptr].concat(args));
623631
}
624632

625633
var tempRet0 = 0;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
__indirect_function_table
12
_start
23
memory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
__indirect_function_table
12
_start
23
memory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
__indirect_function_table
12
_start
23
memory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
__indirect_function_table
12
_start
23
memory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
__indirect_function_table
12
_start
23
memory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
__indirect_function_table
12
_initialize
23
foo
34
memory

tests/test_other.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7168,7 +7168,7 @@ def test_metadce_mem(self, filename, *args):
71687168
[], [], 128), # noqa
71697169
# argc/argv support code etc. is in the wasm
71707170
'O3_standalone': ('libcxxabi_message.cpp', ['-O3', '-s', 'STANDALONE_WASM'],
7171-
[], [], 198), # noqa
7171+
[], [], 242), # noqa
71727172
})
71737173
def test_metadce_libcxxabi_message(self, filename, *args):
71747174
self.run_metadce_test(filename, *args)

tools/building.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ def lld_flags_for_executable(external_symbol_list):
484484
if not Settings.STANDALONE_WASM:
485485
cmd.append('--import-memory')
486486
cmd.append('--import-table')
487+
else:
488+
cmd.append('--export-table')
487489

488490
if Settings.USE_PTHREADS:
489491
cmd.append('--shared-memory')
@@ -1136,7 +1138,10 @@ def add_to_path(dirname):
11361138
logger.error(proc.stderr) # print list of errors (possibly long wall of text if input was minified)
11371139

11381140
# Exit and print final hint to get clearer output
1139-
exit_with_error('closure compiler failed (rc: %d.%s)', proc.returncode, '' if pretty else ' the error message may be clearer with -g1 and EMCC_DEBUG=2 set')
1141+
msg = 'closure compiler failed (rc: %d): %s' % (proc.returncode, shared.shlex_join(cmd))
1142+
if not pretty:
1143+
msg += ' the error message may be clearer with -g1 and EMCC_DEBUG=2 set'
1144+
exit_with_error(msg)
11401145

11411146
if len(proc.stderr.strip()) > 0 and Settings.CLOSURE_WARNINGS != 'quiet':
11421147
# print list of warnings (possibly long wall of text if input was minified)
@@ -1227,6 +1232,12 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info):
12271232
'reaches': [],
12281233
'root': True
12291234
})
1235+
graph.append({
1236+
'export': '__indirect_function_table',
1237+
'name': 'emcc$export$__indirect_function_table',
1238+
'reaches': [],
1239+
'root': True
1240+
})
12301241
# fix wasi imports TODO: support wasm stable with an option?
12311242
WASI_IMPORTS = set([
12321243
'environ_get',

tools/shared.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,16 +1221,22 @@ def make_dynCall(sig):
12211221
def make_invoke(sig, named=True):
12221222
legal_sig = JS.legalize_sig(sig) # TODO: do this in extcall, jscall?
12231223
args = ','.join(['a' + str(i) for i in range(1, len(legal_sig))])
1224-
args = 'index' + (',' if args else '') + args
12251224
ret = 'return ' if sig[0] != 'v' else ''
1226-
body = '%s%s(%s);' % (ret, JS.make_dynCall(sig), args)
1225+
args = 'index' + (',' if args else '') + args
1226+
# wasm2c doesn't yet support for the new `tableCall`
1227+
if Settings.WASM2C or not JS.is_legal_sig(sig):
1228+
body = '%s%s(%s);' % (ret, JS.make_dynCall(sig), args)
1229+
else:
1230+
body = '%stableCall(%s);' % (ret, args)
12271231
# C++ exceptions are numbers, and longjmp is a string 'longjmp'
12281232
if Settings.SUPPORT_LONGJMP:
12291233
rethrow = "if (e !== e+0 && e !== 'longjmp') throw e;"
12301234
else:
12311235
rethrow = "if (e !== e+0) throw e;"
12321236

1233-
ret = '''function%s(%s) {
1237+
name = (' invoke_' + sig) if named else ''
1238+
ret = '''\
1239+
function%s(%s) {
12341240
var sp = stackSave();
12351241
try {
12361242
%s
@@ -1239,7 +1245,8 @@ def make_invoke(sig, named=True):
12391245
%s
12401246
_setThrew(1, 0);
12411247
}
1242-
}''' % ((' invoke_' + sig) if named else '', args, body, rethrow)
1248+
}''' % (name, args, body, rethrow)
1249+
12431250
return ret
12441251

12451252
@staticmethod

0 commit comments

Comments
 (0)