Skip to content

Commit 0bfea47

Browse files
committed
Avoid creating JS symbols for symbols only used in dynamic linking
Symbols that are exported using EMSCRIPTEN_KEEPALIVE are supposed to be exported to the outside world (i.e. on the Module object) and also be available to call JS within the module. Symbols exports for the purposed of dynamic linking so not need to be exported on the Module and are added (at runtime) to `wasmImports` which acts as a kind of global symbol table for the program. In in the case of `-sMAIN_MODULE=1` we export *all* symbols from all libraries, and prior to this change it was not possible to distingish between all the exported generated because of `--export-dynamic`, and the exports generated due to `EMSCRIPTEN_KEEPALIVE`. This change allows us to differentiate by running `wasm-ld` twice: once without `--export-dynamic` (to get the smaller list of `EMSCRIPTEN_KEEPALIVE`) and then once with `--export-dynamic` to produce the actual wasm that we output. This takes the list of exports that we turn in to JS globals from 7993 to 28, massively reducing the overhead of `-sMAIN_MODULE=1`.
1 parent e3d7e5e commit 0bfea47

File tree

2 files changed

+41
-26
lines changed

2 files changed

+41
-26
lines changed

tools/emscripten.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -290,22 +290,22 @@ def trim_asm_const_body(body):
290290
return body
291291

292292

293-
def create_global_exports(metadata):
294-
global_exports = []
295-
for k, v in metadata.global_exports.items():
293+
def create_global_exports(global_exports):
294+
lines = []
295+
for k, v in global_exports.items():
296296
v = int(v)
297297
if settings.RELOCATABLE:
298298
v += settings.GLOBAL_BASE
299299
mangled = asmjs_mangle(k)
300300
if settings.MINIMAL_RUNTIME:
301-
global_exports.append("var %s = %s;" % (mangled, v))
301+
lines.append("var %s = %s;" % (mangled, v))
302302
else:
303-
global_exports.append("var %s = Module['%s'] = %s;" % (mangled, mangled, v))
303+
lines.append("var %s = Module['%s'] = %s;" % (mangled, mangled, v))
304304

305-
return '\n'.join(global_exports)
305+
return '\n'.join(lines)
306306

307307

308-
def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True):
308+
def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadata=None):
309309
# Overview:
310310
# * Run wasm-emscripten-finalize to extract metadata and modify the binary
311311
# to use emscripten's wasm<->JS ABI
@@ -329,12 +329,6 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True):
329329
if settings.RELOCATABLE and settings.MEMORY64 == 2:
330330
metadata.imports += ['__memory_base32']
331331

332-
if settings.ASYNCIFY == 1:
333-
metadata.function_exports['asyncify_start_unwind'] = webassembly.FuncType([webassembly.Type.I32], [])
334-
metadata.function_exports['asyncify_stop_unwind'] = webassembly.FuncType([], [])
335-
metadata.function_exports['asyncify_start_rewind'] = webassembly.FuncType([webassembly.Type.I32], [])
336-
metadata.function_exports['asyncify_stop_rewind'] = webassembly.FuncType([], [])
337-
338332
# If the binary has already been finalized the settings have already been
339333
# updated and we can skip updating them.
340334
if finalize:
@@ -444,18 +438,31 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True):
444438
'// === Body ===\n',
445439
'// === Body ===\n\n' + extra_code + '\n')
446440

441+
if base_metadata:
442+
function_exports = base_metadata.function_exports
443+
global_exports = base_metadata.global_exports
444+
else:
445+
function_exports = metadata.function_exports
446+
global_exports = metadata.global_exports
447+
448+
if settings.ASYNCIFY == 1:
449+
function_exports['asyncify_start_unwind'] = webassembly.FuncType([webassembly.Type.I32], [])
450+
function_exports['asyncify_stop_unwind'] = webassembly.FuncType([], [])
451+
function_exports['asyncify_start_rewind'] = webassembly.FuncType([webassembly.Type.I32], [])
452+
function_exports['asyncify_stop_rewind'] = webassembly.FuncType([], [])
453+
447454
with open(outfile_js, 'w', encoding='utf-8') as out:
448455
out.write(pre)
449456
pre = None
450457

451-
receiving = create_receiving(metadata.function_exports)
458+
receiving = create_receiving(function_exports)
452459

453460
if settings.MINIMAL_RUNTIME:
454461
if settings.DECLARE_ASM_MODULE_EXPORTS:
455-
post = compute_minimal_runtime_initializer_and_exports(post, metadata.function_exports, receiving)
462+
post = compute_minimal_runtime_initializer_and_exports(post, function_exports, receiving)
456463
receiving = ''
457464

458-
module = create_module(receiving, metadata, forwarded_json['librarySymbols'])
465+
module = create_module(receiving, metadata, global_exports, forwarded_json['librarySymbols'])
459466

460467
metadata.library_definitions = forwarded_json['libraryDefinitions']
461468

@@ -638,8 +645,7 @@ def create_tsd(metadata, embind_tsd):
638645
out += create_tsd_exported_runtime_methods(metadata)
639646
# Manually generate defintions for any Wasm function exports.
640647
out += 'interface WasmModule {\n'
641-
function_exports = metadata.function_exports
642-
for name, types in function_exports.items():
648+
for name, types in metadata.function_exports.items():
643649
mangled = asmjs_mangle(name)
644650
should_export = settings.EXPORT_KEEPALIVE and mangled in settings.EXPORTED_FUNCTIONS
645651
if not should_export:
@@ -950,8 +956,8 @@ def create_receiving(function_exports):
950956
return '\n'.join(receiving) + '\n'
951957

952958

953-
def create_module(receiving, metadata, library_symbols):
954-
receiving += create_global_exports(metadata)
959+
def create_module(receiving, metadata, global_exports, library_symbols):
960+
receiving += create_global_exports(global_exports)
955961
module = []
956962

957963
sending = create_sending(metadata, library_symbols)

tools/link.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from . import system_libs
3333
from . import utils
3434
from . import webassembly
35+
from . import extract_metadata
3536
from .utils import read_file, read_binary, write_file, delete_file
3637
from .utils import removeprefix, exit_with_error
3738
from .shared import in_temp, safe_copy, do_replace, OFormat
@@ -1847,11 +1848,19 @@ def phase_link(linker_arguments, wasm_target, js_syms):
18471848
settings.REQUIRED_EXPORTS = dedup_list(settings.REQUIRED_EXPORTS)
18481849
settings.EXPORT_IF_DEFINED = dedup_list(settings.EXPORT_IF_DEFINED)
18491850

1851+
rtn = None
1852+
if settings.MAIN_MODULE == 1 and settings.LINKABLE:
1853+
settings.LINKABLE = False
1854+
building.link_lld(linker_arguments, wasm_target, external_symbols=js_syms)
1855+
settings.LINKABLE = True
1856+
rtn = extract_metadata.extract_metadata(wasm_target)
1857+
18501858
building.link_lld(linker_arguments, wasm_target, external_symbols=js_syms)
1859+
return rtn
18511860

18521861

18531862
@ToolchainProfiler.profile_block('post link')
1854-
def phase_post_link(options, state, in_wasm, wasm_target, target, js_syms):
1863+
def phase_post_link(options, state, in_wasm, wasm_target, target, js_syms, base_metadata=None):
18551864
global final_js
18561865

18571866
target_basename = unsuffixed_basename(target)
@@ -1868,7 +1877,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target, js_syms):
18681877

18691878
settings.TARGET_JS_NAME = os.path.basename(state.js_target)
18701879

1871-
metadata = phase_emscript(in_wasm, wasm_target, js_syms)
1880+
metadata = phase_emscript(in_wasm, wasm_target, js_syms, base_metadata)
18721881

18731882
if settings.EMBIND_AOT:
18741883
phase_embind_aot(wasm_target, js_syms)
@@ -1887,7 +1896,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target, js_syms):
18871896

18881897

18891898
@ToolchainProfiler.profile_block('emscript')
1890-
def phase_emscript(in_wasm, wasm_target, js_syms):
1899+
def phase_emscript(in_wasm, wasm_target, js_syms, base_metadata):
18911900
# Emscripten
18921901
logger.debug('emscript')
18931902

@@ -1898,7 +1907,7 @@ def phase_emscript(in_wasm, wasm_target, js_syms):
18981907
if shared.SKIP_SUBPROCS:
18991908
return
19001909

1901-
metadata = emscripten.emscript(in_wasm, wasm_target, final_js, js_syms)
1910+
metadata = emscripten.emscript(in_wasm, wasm_target, final_js, js_syms, base_metadata=base_metadata)
19021911
save_intermediate('original')
19031912
return metadata
19041913

@@ -2992,7 +3001,7 @@ def add_js_deps(sym):
29923001
settings.ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS = settings.ASYNCIFY_IMPORTS[:]
29933002
settings.ASYNCIFY_IMPORTS += ['*.' + x for x in js_info['asyncFuncs']]
29943003

2995-
phase_link(linker_arguments, wasm_target, js_syms)
3004+
base_metadata = phase_link(linker_arguments, wasm_target, js_syms)
29963005

29973006
# Special handling for when the user passed '-Wl,--version'. In this case the linker
29983007
# does not create the output file, but just prints its version and exits with 0.
@@ -3006,6 +3015,6 @@ def add_js_deps(sym):
30063015

30073016
# Perform post-link steps (unless we are running bare mode)
30083017
if options.oformat != OFormat.BARE:
3009-
phase_post_link(options, state, wasm_target, wasm_target, target, js_syms)
3018+
phase_post_link(options, state, wasm_target, wasm_target, target, js_syms, base_metadata)
30103019

30113020
return 0

0 commit comments

Comments
 (0)