Skip to content

Commit 133cdf6

Browse files
committed
Remove deps_info system and and the running of llvm-nm on input file. NFC
This uses a new "stub object" construct to tell the linker (wasm-ld) not only about the existence of the JS library symbols but the native symbols on which they depend (a.k.a reverse dependencies). This allows us to completely remove deps_info.py in favor of just using normal `__deps` entries in the library files. It also means we no longer need to run `llvm-nm` on the linker inputs to discover the symbols they use. Depends on: https://reviews.llvm.org/D145308 Fixes: #18875
1 parent ef15cbb commit 133cdf6

23 files changed

+177
-539
lines changed

emcc.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
from tools.minimal_runtime_shell import generate_minimal_runtime_html
5151
import tools.line_endings
5252
from tools import feature_matrix
53-
from tools import deps_info
5453
from tools import js_manipulation
5554
from tools import wasm2c
5655
from tools import webassembly
@@ -1275,7 +1274,7 @@ def run(args):
12751274
process_libraries(state, [])
12761275
if len(input_files) != 1:
12771276
exit_with_error('--post-link requires a single input file')
1278-
phase_post_link(options, state, input_files[0][1], wasm_target, target)
1277+
phase_post_link(options, state, input_files[0][1], wasm_target, target, {})
12791278
return 0
12801279

12811280
## Compile source code to object files
@@ -1316,11 +1315,10 @@ def run(args):
13161315
js_info = get_js_sym_info()
13171316
if not settings.SIDE_MODULE:
13181317
js_syms = js_info['deps']
1319-
deps_info.append_deps_info(js_syms)
13201318
if settings.ASYNCIFY:
13211319
settings.ASYNCIFY_IMPORTS += ['env.' + x for x in js_info['asyncFuncs']]
13221320

1323-
phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs)
1321+
phase_calculate_system_libraries(state, linker_arguments, newargs)
13241322

13251323
phase_link(linker_arguments, wasm_target, js_syms)
13261324

@@ -1336,7 +1334,7 @@ def run(args):
13361334

13371335
# Perform post-link steps (unless we are running bare mode)
13381336
if options.oformat != OFormat.BARE:
1339-
phase_post_link(options, state, wasm_target, wasm_target, target)
1337+
phase_post_link(options, state, wasm_target, wasm_target, target, js_syms)
13401338

13411339
return 0
13421340

@@ -2082,11 +2080,6 @@ def phase_linker_setup(options, state, newargs):
20822080
settings.INCLUDE_FULL_LIBRARY = 1
20832081
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$loadDylibs']
20842082

2085-
# If we are including the entire JS library then we know for sure we will, by definition,
2086-
# require all the reverse dependencies.
2087-
if settings.INCLUDE_FULL_LIBRARY:
2088-
default_setting('REVERSE_DEPS', 'all')
2089-
20902083
if settings.MAIN_MODULE == 1 or settings.SIDE_MODULE == 1:
20912084
settings.LINKABLE = 1
20922085

@@ -3086,14 +3079,13 @@ def compile_source_file(i, input_file):
30863079

30873080

30883081
@ToolchainProfiler.profile_block('calculate system libraries')
3089-
def phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs):
3082+
def phase_calculate_system_libraries(state, linker_arguments, newargs):
30903083
extra_files_to_link = []
30913084
# Link in ports and system libraries, if necessary
30923085
if not settings.SIDE_MODULE:
30933086
# Ports are always linked into the main module, never the side module.
30943087
extra_files_to_link += ports.get_libs(settings)
3095-
all_linker_inputs = [f for _, f in sorted(linker_inputs)] + extra_files_to_link
3096-
extra_files_to_link += system_libs.calculate(all_linker_inputs, newargs, forced=state.forced_stdlibs)
3088+
extra_files_to_link += system_libs.calculate(newargs, forced=state.forced_stdlibs)
30973089
linker_arguments.extend(extra_files_to_link)
30983090

30993091

@@ -3112,7 +3104,7 @@ def phase_link(linker_arguments, wasm_target, js_syms):
31123104

31133105

31143106
@ToolchainProfiler.profile_block('post_link')
3115-
def phase_post_link(options, state, in_wasm, wasm_target, target):
3107+
def phase_post_link(options, state, in_wasm, wasm_target, target, js_syms):
31163108
global final_js
31173109

31183110
target_basename = unsuffixed_basename(target)
@@ -3134,7 +3126,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target):
31343126
else:
31353127
memfile = shared.replace_or_append_suffix(target, '.mem')
31363128

3137-
phase_emscript(options, in_wasm, wasm_target, memfile)
3129+
phase_emscript(options, in_wasm, wasm_target, memfile, js_syms)
31383130

31393131
if options.js_transform:
31403132
phase_source_transforms(options)
@@ -3152,7 +3144,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target):
31523144

31533145

31543146
@ToolchainProfiler.profile_block('emscript')
3155-
def phase_emscript(options, in_wasm, wasm_target, memfile):
3147+
def phase_emscript(options, in_wasm, wasm_target, memfile, js_syms):
31563148
# Emscripten
31573149
logger.debug('emscript')
31583150

@@ -3161,7 +3153,7 @@ def phase_emscript(options, in_wasm, wasm_target, memfile):
31613153
# _read in shell.js depends on intArrayToString when SUPPORT_BASE64_EMBEDDING is set
31623154
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('$intArrayToString')
31633155

3164-
emscripten.run(in_wasm, wasm_target, final_js, memfile)
3156+
emscripten.run(in_wasm, wasm_target, final_js, memfile, js_syms)
31653157
save_intermediate('original')
31663158

31673159

emscripten.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def create_named_globals(metadata):
296296
return '\n'.join(named_globals)
297297

298298

299-
def emscript(in_wasm, out_wasm, outfile_js, memfile):
299+
def emscript(in_wasm, out_wasm, outfile_js, memfile, js_syms):
300300
# Overview:
301301
# * Run wasm-emscripten-finalize to extract metadata and modify the binary
302302
# to use emscripten's wasm<->JS ABI
@@ -309,7 +309,7 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
309309
# set file locations, so that JS glue can find what it needs
310310
settings.WASM_BINARY_FILE = js_manipulation.escape_for_js_string(os.path.basename(out_wasm))
311311

312-
metadata = finalize_wasm(in_wasm, out_wasm, memfile)
312+
metadata = finalize_wasm(in_wasm, out_wasm, memfile, js_syms)
313313

314314
if settings.RELOCATABLE and settings.MEMORY64 == 2:
315315
metadata.imports += ['__memory_base32']
@@ -461,7 +461,7 @@ def get_metadata(infile, outfile, modify_wasm, args):
461461
return metadata
462462

463463

464-
def finalize_wasm(infile, outfile, memfile):
464+
def finalize_wasm(infile, outfile, memfile, js_syms):
465465
building.save_intermediate(infile, 'base.wasm')
466466
args = []
467467

@@ -536,13 +536,16 @@ def finalize_wasm(infile, outfile, memfile):
536536

537537
expected_exports = set(settings.EXPORTED_FUNCTIONS)
538538
expected_exports.update(asmjs_mangle(s) for s in settings.REQUIRED_EXPORTS)
539+
for deps in js_syms.values():
540+
expected_exports.update(asmjs_mangle(s) for s in deps)
539541

540542
# Calculate the subset of exports that were explicitly marked with llvm.used.
541543
# These are any exports that were not requested on the command line and are
542544
# not known auto-generated system functions.
543545
unexpected_exports = [e for e in metadata.exports if treat_as_user_function(e)]
544546
unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports]
545547
unexpected_exports = [e for e in unexpected_exports if e not in expected_exports]
548+
546549
building.user_requested_exports.update(unexpected_exports)
547550
settings.EXPORTED_FUNCTIONS.extend(unexpected_exports)
548551

@@ -922,5 +925,5 @@ def normalize_line_endings(text):
922925
return text
923926

924927

925-
def run(in_wasm, out_wasm, outfile_js, memfile):
926-
emscript(in_wasm, out_wasm, outfile_js, memfile)
928+
def run(in_wasm, out_wasm, outfile_js, memfile, js_syms):
929+
emscript(in_wasm, out_wasm, outfile_js, memfile, js_syms)

site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,6 @@ See the `library_*.js`_ files for other examples.
596596
This is useful when all the implemented methods use a JavaScript
597597
singleton containing helper methods. See ``library_webgl.js`` for
598598
an example.
599-
- If a JavaScript library depends on a compiled C library (like most
600-
of *libc*), you must edit `src/deps_info.json`_. Search for
601-
"deps_info" in `tools/system_libs.py`_.
602599
- The keys passed into `mergeInto` generate functions that are prefixed
603600
by ``_``. In other words ``my_func: function() {},`` becomes
604601
``function _my_func() {}``, as all C methods in emscripten have a ``_`` prefix. Keys starting with ``$`` have the ``$``
@@ -810,7 +807,6 @@ you can give it a try. See `Emnapi documentation`_ for more details.
810807

811808
.. _library.js: https://github.com/emscripten-core/emscripten/blob/main/src/library.js
812809
.. _test_js_libraries: https://github.com/emscripten-core/emscripten/blob/1.29.12/tests/test_core.py#L5043
813-
.. _src/deps_info.json: https://github.com/emscripten-core/emscripten/blob/main/src/deps_info.json
814810
.. _tools/system_libs.py: https://github.com/emscripten-core/emscripten/blob/main/tools/system_libs.py
815811
.. _library_\*.js: https://github.com/emscripten-core/emscripten/tree/main/src
816812
.. _test_add_function in test/test_core.py: https://github.com/emscripten-core/emscripten/blob/1.29.12/tests/test_core.py#L6237

src/jsifier.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,26 @@ function isDefined(symName) {
6666
return false;
6767
}
6868

69+
function getTransitiveDeps(symbol) {
70+
// TODO(sbc): Use some kind of cache to avoid quadratic behaviour here.
71+
const transitiveDeps = new Set();
72+
const seen = new Set();
73+
const toVisit = [symbol];
74+
while (toVisit.length) {
75+
const sym = toVisit.pop();
76+
if (!seen.has(sym)) {
77+
let directDeps = LibraryManager.library[sym + '__deps'] || [];
78+
directDeps = directDeps.filter((d) => typeof d === 'string');
79+
if (directDeps.length) {
80+
directDeps.forEach(transitiveDeps.add, transitiveDeps);
81+
toVisit.push(...directDeps);
82+
}
83+
seen.add(sym);
84+
}
85+
}
86+
return Array.from(transitiveDeps);
87+
}
88+
6989
function runJSify() {
7090
const libraryItems = [];
7191
const symbolDeps = {};
@@ -221,7 +241,7 @@ function ${name}(${args}) {
221241
// the number specifies the number of arguments. In Emscripten, route all
222242
// these to a single function '__cxa_find_matching_catch' that variadically
223243
// processes all of these functions using JS 'arguments' object.
224-
if (symbol.startsWith('__cxa_find_matching_catch_')) {
244+
if (!WASM_EXCEPTIONS && symbol.startsWith('__cxa_find_matching_catch_')) {
225245
if (DISABLE_EXCEPTION_THROWING) {
226246
error('DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but exception catching code appears. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-except (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.');
227247
return;
@@ -260,8 +280,14 @@ function ${name}(${args}) {
260280

261281
if (symbolsOnly) {
262282
if (!isJsOnlySymbol(symbol) && LibraryManager.library.hasOwnProperty(symbol)) {
263-
externalDeps = deps.filter((d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library) && typeof d === 'string');
264-
symbolDeps[symbol] = externalDeps;
283+
var value = LibraryManager.library[symbol];
284+
var resolvedSymbol = symbol;
285+
// Resolve aliases before looking up deps
286+
if (typeof value == 'string' && value[0] != '=' && LibraryManager.library.hasOwnProperty(value)) {
287+
resolvedSymbol = value;
288+
}
289+
var transtiveDeps = getTransitiveDeps(resolvedSymbol);
290+
symbolDeps[symbol] = transtiveDeps.filter((d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library));
265291
}
266292
return;
267293
}
@@ -565,6 +591,7 @@ function ${name}(${args}) {
565591
}
566592

567593
if (symbolsOnly) {
594+
if (abortExecution) throw Error('Aborting compilation due to previous errors');
568595
print(JSON.stringify({
569596
deps: symbolDeps,
570597
asyncFuncs

src/library.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,13 @@ mergeInto(LibraryManager.library, {
12121212
// ==========================================================================
12131213

12141214
#if SUPPORT_LONGJMP == 'emscripten'
1215+
// In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function
1216+
// calls that exist in the same function with setjmp are converted to a code
1217+
// sequence that includes invokes, malloc, free, saveSetjmp, and
1218+
// emscripten_longjmp. setThrew is called from invokes, but we don't have
1219+
// any way to express that dependency so we use emscripten_throw_longjmp as
1220+
// a proxy and declare the dependency here.
1221+
_emscripten_throw_longjmp__deps: ['setThrew'],
12151222
_emscripten_throw_longjmp: function() {
12161223
#if EXCEPTION_STACK_TRACES
12171224
throw new EmscriptenSjLj;
@@ -1721,7 +1728,7 @@ mergeInto(LibraryManager.library, {
17211728
return { family: family, addr: addr, port: port };
17221729
},
17231730
$writeSockaddr__docs: '/** @param {number=} addrlen */',
1724-
$writeSockaddr__deps: ['$Sockets', '$inetPton4', '$inetPton6', '$zeroMemory'],
1731+
$writeSockaddr__deps: ['$Sockets', '$inetPton4', '$inetPton6', '$zeroMemory', 'htons'],
17251732
$writeSockaddr: function (sa, family, addr, port, addrlen) {
17261733
switch (family) {
17271734
case {{{ cDefs.AF_INET }}}:
@@ -1858,7 +1865,7 @@ mergeInto(LibraryManager.library, {
18581865
return 0;
18591866
},
18601867

1861-
getaddrinfo__deps: ['$Sockets', '$DNS', '$inetPton4', '$inetNtop4', '$inetPton6', '$inetNtop6', '$writeSockaddr'],
1868+
getaddrinfo__deps: ['$Sockets', '$DNS', '$inetPton4', '$inetNtop4', '$inetPton6', '$inetNtop6', '$writeSockaddr', 'malloc', 'htonl'],
18621869
getaddrinfo__proxy: 'sync',
18631870
getaddrinfo: function(node, service, hint, out) {
18641871
// Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL

src/library_async.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ mergeInto(LibraryManager.library, {
2222
#if ASYNCIFY
2323
$Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback', '$sigToWasmTypes',
2424
#if !MINIMAL_RUNTIME
25-
'$runtimeKeepalivePush', '$runtimeKeepalivePop'
25+
'$runtimeKeepalivePush', '$runtimeKeepalivePop',
26+
#endif
27+
#if ASYNCIFY == 1
28+
// Needed by allocateData and handleSleep respectively
29+
'malloc', 'free',
2630
#endif
2731
],
2832

src/library_browser.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,7 @@ var LibraryBrowser = {
12121212

12131213
// To avoid creating worker parent->child chains, always proxies to execute on the main thread.
12141214
emscripten_create_worker__proxy: 'sync',
1215+
emscripten_create_worker__deps: ['$UTF8ToString', 'malloc', 'free'],
12151216
emscripten_create_worker: function(url) {
12161217
url = UTF8ToString(url);
12171218
var id = Browser.workers.length;
@@ -1253,6 +1254,7 @@ var LibraryBrowser = {
12531254
return id;
12541255
},
12551256

1257+
emscripten_destroy_worker__deps: ['free'],
12561258
emscripten_destroy_worker__proxy: 'sync',
12571259
emscripten_destroy_worker: function(id) {
12581260
var info = Browser.workers[id];

src/library_exceptions.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ var LibraryExceptions = {
2323
//
2424
// excPtr - Thrown object pointer to wrap. Metadata pointer is calculated from it.
2525
$ExceptionInfo__docs: '/** @constructor */',
26+
$ExceptionInfo__deps: [
27+
'__cxa_is_pointer_type',
2628
#if EXCEPTION_DEBUG
27-
$ExceptionInfo__deps: ['$ptrToString'],
29+
'$ptrToString'
2830
#endif
31+
],
2932
$ExceptionInfo: function(excPtr) {
3033
this.excPtr = excPtr;
3134
this.ptr = excPtr - {{{ C_STRUCTS.__cxa_exception.__size__ }}};
@@ -238,7 +241,6 @@ var LibraryExceptions = {
238241
// functionality boils down to picking a suitable 'catch' block.
239242
// We'll do that here, instead, to keep things simpler.
240243
__cxa_find_matching_catch__deps: ['$exceptionLast', '$ExceptionInfo', '__resumeException', '__cxa_can_catch', 'setTempRet0'],
241-
//__cxa_find_matching_catch__sig: 'p',
242244
__cxa_find_matching_catch: function() {
243245
var thrown =
244246
#if EXCEPTION_STACK_TRACES
@@ -408,6 +410,7 @@ var LibraryExceptions = {
408410
#endif
409411
};
410412

413+
#if !WASM_EXCEPTIONS
411414
// In LLVM, exceptions generate a set of functions of form
412415
// __cxa_find_matching_catch_1(), __cxa_find_matching_catch_2(), etc. where the
413416
// number specifies the number of arguments. In Emscripten, route all these to
@@ -417,4 +420,12 @@ addCxaCatch = function(n) {
417420
LibraryManager.library['__cxa_find_matching_catch_' + n] = '__cxa_find_matching_catch';
418421
};
419422

423+
// Add the first 10 catch handlers premptively. Others get added on demand in
424+
// jsifier. This is done here primarily so that these symbols end up with the
425+
// correct deps in the stub library that we pass to wasm-ld.
426+
for (let i = 1; i < 10; i++) {
427+
addCxaCatch(i)
428+
}
429+
#endif
430+
420431
mergeInto(LibraryManager.library, LibraryExceptions);

src/library_exceptions_stub.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var LibraryExceptions = {};
2424
#if !INCLUDE_FULL_LIBRARY
2525
// This method of link-time error genertation is not compatible with INCLUDE_FULL_LIBRARY
2626
LibraryExceptions[name + '__deps'] = [function() {
27-
error('DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but such support is required by symbol ' + name + '. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-except (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.');
27+
error(`DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but such support is required by symbol '${name}'. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-except (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.`);
2828
}];
2929
#endif
3030
});

src/library_glew.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121

2222
var LibraryGLEW = {
23-
$GLEW__deps: ['glGetString', '$stringToNewUTF8'],
23+
$GLEW__deps: ['glGetString', '$stringToNewUTF8', '$UTF8ToString'],
2424
$GLEW: {
2525
isLinaroFork: 1,
2626
extensions: null,

src/library_glfw.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ var LibraryGLFW = {
11231123
/*******************************************************************************
11241124
* GLFW FUNCTIONS
11251125
******************************************************************************/
1126-
glfwInit__deps: ['emscripten_get_device_pixel_ratio'],
1126+
glfwInit__deps: ['emscripten_get_device_pixel_ratio', 'malloc', 'free'],
11271127
glfwInit__sig: 'i',
11281128
glfwInit: function() {
11291129
if (GLFW.windows) return 1; // GL_TRUE
@@ -1271,6 +1271,7 @@ var LibraryGLFW = {
12711271
glfwPostEmptyEvent__sig: 'v',
12721272
glfwPostEmptyEvent: function() {},
12731273

1274+
glfwGetMonitors__deps: ['malloc'],
12741275
glfwGetMonitors__sig: 'ii',
12751276
glfwGetMonitors__deps: ['malloc'],
12761277
glfwGetMonitors: function(count) {

0 commit comments

Comments
 (0)