Skip to content

Commit 6316461

Browse files
authored
Unify legacy dyncall mechanisms (#24371)
With this change we use the MINIMAL_RUNTIME technique in all cases. That is, dyncall functions are stored in a global `dynCalls` object that maps signatures to functions. Previously we were relying on the `dynCall_xx` helpers existing on the `Module` object. This allows `-sMODULARIZE=instance` to work with `-sDYNCALL` and by extension with `-sASYNCIFY=1`.
1 parent 16f880a commit 6316461

File tree

5 files changed

+37
-47
lines changed

5 files changed

+37
-47
lines changed

site/source/docs/compiling/Modularized-Output.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ fix in future releses. Current limitations include:
126126
* :ref:`abort_on_wasm_exceptions` is not supported (requires wrapping wasm
127127
exports).
128128

129-
* :ref:`dyncalls` is not supported (depends on the ``Module`` global)
130-
131-
* :ref:`asyncify` is not supported (depends on :ref:`dyncalls`)
132-
133129
* :ref:`asyncify_lazy_load_code` is not supported (depends on ``wasmExports``
134130
global)
135131

@@ -172,6 +168,10 @@ Some additional limitations are:
172168

173169
- ``-pthread`` / :ref:`wasm_workers` are not yet supported.
174170

171+
* :ref:`dyncalls` is not supported (depends on the ``Module`` global)
172+
173+
* :ref:`asyncify` is not supported (depends on :ref:`dyncalls`)
174+
175175
- Setting :ref:`wasm` to ``0`` is not supported.
176176

177177
- Setting :ref:`wasm_async_compilation` to ``0`` is not supported.

src/lib/libcore.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,25 +1729,13 @@ addToLibrary({
17291729
},
17301730

17311731
#if DYNCALLS || !WASM_BIGINT
1732-
#if MINIMAL_RUNTIME
1733-
$dynCalls: '{}',
1734-
#endif
1735-
$dynCallLegacy__deps: [
1736-
#if MINIMAL_RUNTIME
1737-
'$dynCalls',
1738-
#endif
1739-
#if MODULARIZE == 'instance'
1740-
() => error('dynCallLegacy is not yet compatible with MODULARIZE=instance'),
1741-
#endif
1742-
],
1732+
$dynCalls__internal: true,
1733+
$dynCalls: {},
1734+
$dynCallLegacy__deps: ['$dynCalls'],
17431735
$dynCallLegacy: (sig, ptr, args) => {
17441736
sig = sig.replace(/p/g, {{{ MEMORY64 ? "'j'" : "'i'" }}})
17451737
#if ASSERTIONS
1746-
#if MINIMAL_RUNTIME
17471738
assert(sig in dynCalls, `bad function pointer type - sig is not in dynCalls: '${sig}'`);
1748-
#else
1749-
assert(('dynCall_' + sig) in Module, `bad function pointer type - dynCall function not found for sig '${sig}'`);
1750-
#endif
17511739
if (args?.length) {
17521740
#if WASM_BIGINT
17531741
// j (64-bit integer) is fine, and is implemented as a BigInt. Without
@@ -1762,11 +1750,7 @@ addToLibrary({
17621750
assert(sig.length == 1);
17631751
}
17641752
#endif
1765-
#if MINIMAL_RUNTIME
17661753
var f = dynCalls[sig];
1767-
#else
1768-
var f = Module['dynCall_' + sig];
1769-
#endif
17701754
return f(ptr, ...args);
17711755
},
17721756
#if DYNCALLS

test/test_core.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,6 @@ def metafunc(self, jspi, *args, **kwargs):
212212
self.require_jspi()
213213
else:
214214
self.set_setting('ASYNCIFY')
215-
if self.get_setting('MODULARIZE') == 'instance':
216-
self.skipTest('MODULARIZE=instance is not compatible with ASYNCIFY=1')
217215
f(self, *args, **kwargs)
218216

219217
parameterize(metafunc, {'': (False,),
@@ -233,8 +231,6 @@ def metafunc(self, asyncify, *args, **kwargs):
233231
self.require_jspi()
234232
elif asyncify == 1:
235233
self.set_setting('ASYNCIFY')
236-
if self.get_setting('MODULARIZE') == 'instance':
237-
self.skipTest('MODULARIZE=instance is not compatible with ASYNCIFY=1')
238234
else:
239235
assert asyncify == 0
240236
f(self, *args, **kwargs)
@@ -1885,7 +1881,7 @@ def test_emscripten_get_compiler_setting(self):
18851881
self.set_setting('RETAIN_COMPILER_SETTINGS')
18861882
self.do_runf(src, read_file(output).replace('waka', utils.EMSCRIPTEN_VERSION))
18871883

1888-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
1884+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
18891885
def test_emscripten_has_asyncify(self):
18901886
src = r'''
18911887
#include <stdio.h>
@@ -7024,7 +7020,7 @@ def test_EXPORTED_RUNTIME_METHODS(self):
70247020
self.do_core_test('EXPORTED_RUNTIME_METHODS.c')
70257021

70267022
@also_with_minimal_runtime
7027-
@no_modularize_instance('uses dynCallLegacy')
7023+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with DYNCALLS')
70287024
def test_dyncall_specific(self):
70297025
if self.get_setting('WASM_BIGINT') != 0 and not self.is_wasm2js():
70307026
# define DYNCALLS because this test does test calling them directly, and
@@ -7051,8 +7047,8 @@ def test_dyncall_specific(self):
70517047
'legacy': (['-sDYNCALLS'],),
70527048
})
70537049
def test_dyncall_pointers(self, args):
7054-
if args:
7055-
self.skipTest('dynCallLegacy is not yet compatible with WASM_ESM_INTEGRATION')
7050+
if args and self.get_setting('WASM_ESM_INTEGRATION'):
7051+
self.skipTest('WASM_ESM_INTEGRATION is not compatible with DYNCALLS')
70567052
self.do_core_test('test_dyncall_pointers.c', emcc_args=args)
70577053

70587054
@also_with_wasm_bigint
@@ -8068,7 +8064,7 @@ def test_vswprintf_utf8(self):
80688064

80698065
# Test async sleeps in the presence of invoke_* calls, which can happen with
80708066
# longjmp or exceptions.
8071-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8067+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
80728068
def test_asyncify_longjmp(self):
80738069
self.set_setting('ASYNCIFY')
80748070
self.set_setting('STRICT')
@@ -8128,7 +8124,7 @@ def test_async_loop(self):
81288124
self.do_runf('main.c', 'hello 0\nhello 1\nhello 2\nhello 3\nhello 4\n')
81298125

81308126
@requires_v8
8131-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8127+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
81328128
def test_async_hello_v8(self):
81338129
self.test_async_hello()
81348130

@@ -8233,7 +8229,7 @@ def test_async_ccall_promise(self, exit_runtime):
82338229
self.emcc_args += ['--pre-js', 'pre.js']
82348230
self.do_runf('main.c', 'stringf: first\nsecond\n6.4')
82358231

8236-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8232+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
82378233
def test_fibers_asyncify(self):
82388234
self.set_setting('ASYNCIFY')
82398235
self.maybe_closure()
@@ -8244,7 +8240,7 @@ def test_asyncify_unused(self):
82448240
# test a program not using asyncify, but the pref is set
82458241
self.do_core_test('test_hello_world.c')
82468242

8247-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8243+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
82488244
@parameterized({
82498245
'normal': ([], True),
82508246
'removelist_a': (['-sASYNCIFY_REMOVE=["foo(int, double)"]'], False),
@@ -8292,7 +8288,7 @@ def test_asyncify_lists(self, args, should_pass, response=None):
82928288
# virt() manually, rather than have them inferred automatically.
82938289
'add_no_prop': (['-sASYNCIFY_IGNORE_INDIRECT', '-sASYNCIFY_ADD=["__original_main","main","virt()"]', '-sASYNCIFY_PROPAGATE_ADD=0'], True),
82948290
})
8295-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8291+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
82968292
def test_asyncify_indirect_lists(self, args, should_pass):
82978293
self.set_setting('ASYNCIFY')
82988294
self.emcc_args += args
@@ -8310,7 +8306,7 @@ def test_asyncify_indirect_lists(self, args, should_pass):
83108306
raise
83118307

83128308
@with_dylink_reversed
8313-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8309+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
83148310
def test_asyncify_side_module(self):
83158311
self.set_setting('ASYNCIFY')
83168312
self.set_setting('ASYNCIFY_IMPORTS', ['my_sleep'])
@@ -8340,12 +8336,12 @@ def test_asyncify_side_module(self):
83408336
''', 'before sleep\n42\n42\nafter sleep\n', header='void my_sleep(int);', force_c=True)
83418337

83428338
@no_asan('asyncify stack operations confuse asan')
8343-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8339+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
83448340
def test_emscripten_scan_registers(self):
83458341
self.set_setting('ASYNCIFY')
83468342
self.do_core_test('test_emscripten_scan_registers.cpp')
83478343

8348-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8344+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
83498345
def test_asyncify_assertions(self):
83508346
self.set_setting('ASYNCIFY')
83518347
self.set_setting('ASYNCIFY_IMPORTS', ['suspend'])
@@ -8354,7 +8350,7 @@ def test_asyncify_assertions(self):
83548350

83558351
@no_lsan('leaks asyncify stack during exit')
83568352
@no_asan('leaks asyncify stack during exit')
8357-
@no_modularize_instance('MODULARIZE=instance is not compatible with ASYNCIFY=1')
8353+
@no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1')
83588354
def test_asyncify_during_exit(self):
83598355
self.set_setting('ASYNCIFY')
83608356
self.set_setting('ASSERTIONS')

tools/emscripten.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,15 @@ def can_use_await():
907907
return settings.MODULARIZE
908908

909909

910+
def make_dyncall_assignment(sym):
911+
if not sym.startswith('dynCall_'):
912+
return ''
913+
if not settings.DYNCALLS or '$dynCall' not in settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE:
914+
return ''
915+
sig = sym.replace('dynCall_', '')
916+
return f"dynCalls['{sig}'] = "
917+
918+
910919
def make_export_wrappers(function_exports):
911920
assert not settings.MINIMAL_RUNTIME
912921

@@ -946,6 +955,8 @@ def install_wrapper(sym):
946955
exported = "Module['%s'] = " % mangled
947956
wrapper += exported
948957

958+
wrapper += make_dyncall_assignment(name)
959+
949960
if settings.ASSERTIONS and install_wrapper(name):
950961
# With assertions enabled we create a wrapper that are calls get routed through, for
951962
# the lifetime of the program.
@@ -995,17 +1006,16 @@ def create_receiving(function_exports, tag_exports):
9951006
# var _main;
9961007
# function assignWasmExports(wasmExport) {
9971008
# _main = wasmExports["_main"];
998-
generate_dyncall_assignment = settings.DYNCALLS and '$dynCall' in settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE
9991009
exports = [x for x in function_exports if x != building.WASM_CALL_CTORS]
10001010
receiving.append('function assignWasmExports(wasmExports) {')
10011011
for s in exports:
10021012
mangled = asmjs_mangle(s)
1003-
dynCallAssignment = ('dynCalls["' + s.replace('dynCall_', '') + '"] = ') if generate_dyncall_assignment and mangled.startswith('dynCall_') else ''
1013+
dyncall_assignment = make_dyncall_assignment(s)
10041014
should_export = settings.EXPORT_ALL or (settings.EXPORT_KEEPALIVE and mangled in settings.EXPORTED_FUNCTIONS)
10051015
export_assignment = ''
10061016
if settings.MODULARIZE and should_export:
10071017
export_assignment = f"Module['{mangled}'] = "
1008-
receiving.append(f" {export_assignment}{dynCallAssignment}{mangled} = wasmExports['{s}'];")
1018+
receiving.append(f" {export_assignment}{dyncall_assignment}{mangled} = wasmExports['{s}'];")
10091019
receiving.append('}')
10101020
sep = ',\n '
10111021
mangled = [asmjs_mangle(s) for s in exports]

tools/link.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,10 @@ def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915
800800
exit_with_error('WASM_ESM_INTEGRATION requires MODULARIZE=instance')
801801
if settings.RELOCATABLE:
802802
exit_with_error('WASM_ESM_INTEGRATION is not compatible with dynamic linking')
803+
if settings.ASYNCIFY == 1:
804+
exit_with_error('WASM_ESM_INTEGRATION is not compatible with -sASYNCIFY=1')
805+
if settings.DYNCALLS:
806+
exit_with_error('WASM_ESM_INTEGRATION is not compatible with DYNCALLS')
803807
if settings.WASM_WORKERS or settings.PTHREADS:
804808
exit_with_error('WASM_ESM_INTEGRATION is not compatible with multi-threading')
805809
if settings.USE_OFFSET_CONVERTER:
@@ -832,10 +836,6 @@ def limit_incoming_module_api():
832836
exit_with_error('MODULARIZE=instance requires EXPORT_ES6')
833837
if settings.ABORT_ON_WASM_EXCEPTIONS:
834838
exit_with_error('MODULARIZE=instance is not compatible with ABORT_ON_WASM_EXCEPTIONS')
835-
if settings.ASYNCIFY == 1:
836-
exit_with_error('MODULARIZE=instance is not compatible with -sASYNCIFY=1')
837-
if settings.DYNCALLS:
838-
exit_with_error('MODULARIZE=instance is not compatible with -sDYNCALLS')
839839
if settings.ASYNCIFY_LAZY_LOAD_CODE:
840840
exit_with_error('MODULARIZE=instance is not compatible with -sASYNCIFY_LAZY_LOAD_CODE')
841841
if settings.MINIMAL_RUNTIME:

0 commit comments

Comments
 (0)