diff --git a/emcc.py b/emcc.py index 023b24e52b27d..3c688049c10eb 100755 --- a/emcc.py +++ b/emcc.py @@ -50,7 +50,6 @@ from tools.minimal_runtime_shell import generate_minimal_runtime_html import tools.line_endings from tools import feature_matrix -from tools import deps_info from tools import js_manipulation from tools import wasm2c from tools import webassembly @@ -1322,11 +1321,19 @@ def run(args): js_info = get_js_sym_info() if not settings.SIDE_MODULE: js_syms = js_info['deps'] - deps_info.append_deps_info(js_syms) + if not settings.USES_DYNAMIC_ALLOC: + # When USES_DYNAMIC_ALLOC=0 is set we don't export malloc/free, even + # if the JS library symbols depend on them. In this mode we instead + # fail at runtime if they are actaully called. + for value in js_syms.values(): + if 'malloc' in value: + value.remove('malloc') + if 'free' in value: + value.remove('free') if settings.ASYNCIFY: settings.ASYNCIFY_IMPORTS += ['env.' + x for x in js_info['asyncFuncs']] - phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs) + phase_calculate_system_libraries(state, linker_arguments, newargs) phase_link(linker_arguments, wasm_target, js_syms) @@ -2085,11 +2092,6 @@ def phase_linker_setup(options, state, newargs): settings.INCLUDE_FULL_LIBRARY = 1 settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$loadDylibs'] - # If we are including the entire JS library then we know for sure we will, by definition, - # require all the reverse dependencies. - if settings.INCLUDE_FULL_LIBRARY: - default_setting('REVERSE_DEPS', 'all') - if settings.MAIN_MODULE == 1 or settings.SIDE_MODULE == 1: settings.LINKABLE = 1 @@ -3079,14 +3081,13 @@ def compile_source_file(i, input_file): @ToolchainProfiler.profile_block('calculate system libraries') -def phase_calculate_system_libraries(state, linker_arguments, linker_inputs, newargs): +def phase_calculate_system_libraries(state, linker_arguments, newargs): extra_files_to_link = [] # Link in ports and system libraries, if necessary if not settings.SIDE_MODULE: # Ports are always linked into the main module, never the side module. extra_files_to_link += ports.get_libs(settings) - all_linker_inputs = [f for _, f in sorted(linker_inputs)] + extra_files_to_link - extra_files_to_link += system_libs.calculate(all_linker_inputs, newargs, forced=state.forced_stdlibs) + extra_files_to_link += system_libs.calculate(newargs, forced=state.forced_stdlibs) linker_arguments.extend(extra_files_to_link) diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst b/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst index eed087f3f90d5..b7096e81623af 100644 --- a/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst +++ b/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst @@ -596,9 +596,6 @@ See the `library_*.js`_ files for other examples. This is useful when all the implemented methods use a JavaScript singleton containing helper methods. See ``library_webgl.js`` for an example. - - If a JavaScript library depends on a compiled C library (like most - of *libc*), you must edit `src/deps_info.json`_. Search for - "deps_info" in `tools/system_libs.py`_. - The keys passed into `mergeInto` generate functions that are prefixed by ``_``. In other words ``my_func: function() {},`` becomes ``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. .. _library.js: https://github.com/emscripten-core/emscripten/blob/main/src/library.js .. _test_js_libraries: https://github.com/emscripten-core/emscripten/blob/1.29.12/tests/test_core.py#L5043 -.. _src/deps_info.json: https://github.com/emscripten-core/emscripten/blob/main/src/deps_info.json .. _tools/system_libs.py: https://github.com/emscripten-core/emscripten/blob/main/tools/system_libs.py .. _library_\*.js: https://github.com/emscripten-core/emscripten/tree/main/src .. _test_add_function in test/test_core.py: https://github.com/emscripten-core/emscripten/blob/1.29.12/tests/test_core.py#L6237 diff --git a/src/jsifier.js b/src/jsifier.js index a966e7083cc05..e94910cf77979 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -66,6 +66,25 @@ function isDefined(symName) { return false; } +function getTransitiveDeps(symbol) { + const transitiveDeps = new Set(); + const seen = new Set(); + const toVisit = [symbol]; + while (toVisit.length) { + const sym = toVisit.pop(); + if (!seen.has(sym)) { + let directDeps = LibraryManager.library[sym + '__deps'] || []; + directDeps = directDeps.filter((d) => typeof d === 'string'); + if (directDeps.length) { + directDeps.forEach(transitiveDeps.add, transitiveDeps); + toVisit.push(...directDeps); + } + seen.add(sym); + } + } + return Array.from(transitiveDeps); +} + function runJSify() { const libraryItems = []; const symbolDeps = {}; @@ -260,8 +279,8 @@ function ${name}(${args}) { if (symbolsOnly) { if (!isJsOnlySymbol(symbol) && LibraryManager.library.hasOwnProperty(symbol)) { - externalDeps = deps.filter((d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library) && typeof d === 'string'); - symbolDeps[symbol] = externalDeps; + var transtiveDeps = getTransitiveDeps(symbol); + symbolDeps[symbol] = transtiveDeps.filter((d) => !isJsOnlySymbol(d) && !(d in LibraryManager.library)); } return; } diff --git a/src/library.js b/src/library.js index df932af4e8a69..daa8d9233fd0f 100644 --- a/src/library.js +++ b/src/library.js @@ -567,7 +567,7 @@ mergeInto(LibraryManager.library, { // TODO: Initialize these to defaults on startup from system settings. // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) - _tzset_js__deps: ['$stringToNewUTF8'], + _tzset_js__deps: ['$stringToNewUTF8', 'malloc'], _tzset_js__internal: true, _tzset_js: function(timezone, daylight, tzname) { // TODO: Use (malleable) environment variables instead of system settings. @@ -1210,6 +1210,7 @@ mergeInto(LibraryManager.library, { // ========================================================================== #if SUPPORT_LONGJMP == 'emscripten' + _emscripten_throw_longjmp__deps: ['setThrew'], _emscripten_throw_longjmp: function() { #if EXCEPTION_STACK_TRACES throw new EmscriptenSjLj; @@ -1719,7 +1720,7 @@ mergeInto(LibraryManager.library, { return { family: family, addr: addr, port: port }; }, $writeSockaddr__docs: '/** @param {number=} addrlen */', - $writeSockaddr__deps: ['$Sockets', '$inetPton4', '$inetPton6', '$zeroMemory'], + $writeSockaddr__deps: ['$Sockets', '$inetPton4', '$inetPton6', '$zeroMemory', 'htons'], $writeSockaddr: function (sa, family, addr, port, addrlen) { switch (family) { case {{{ cDefs.AF_INET }}}: @@ -1856,7 +1857,7 @@ mergeInto(LibraryManager.library, { return 0; }, - getaddrinfo__deps: ['$Sockets', '$DNS', '$inetPton4', '$inetNtop4', '$inetPton6', '$inetNtop6', '$writeSockaddr'], + getaddrinfo__deps: ['$Sockets', '$DNS', '$inetPton4', '$inetNtop4', '$inetPton6', '$inetNtop6', '$writeSockaddr', 'malloc', 'htonl'], getaddrinfo__proxy: 'sync', getaddrinfo: function(node, service, hint, out) { // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL @@ -2621,7 +2622,7 @@ mergeInto(LibraryManager.library, { // using builtin_malloc to avoid LSan reporting these as leaks. emscripten_get_compiler_setting__noleakcheck: true, #if RETAIN_COMPILER_SETTINGS - emscripten_get_compiler_setting__deps: ['$stringToNewUTF8'], + emscripten_get_compiler_setting__deps: ['$stringToNewUTF8', 'malloc'], #endif emscripten_get_compiler_setting: function(name) { #if RETAIN_COMPILER_SETTINGS @@ -2803,7 +2804,7 @@ mergeInto(LibraryManager.library, { // Look up the function name from our stack frame cache with our PC representation. #if USE_OFFSET_CONVERTER - emscripten_pc_get_function__deps: ['$UNWIND_CACHE', 'free', '$stringToNewUTF8'], + emscripten_pc_get_function__deps: ['$UNWIND_CACHE', '$stringToNewUTF8', 'malloc', 'free'], // Don't treat allocation of _emscripten_pc_get_function.ret as a leak emscripten_pc_get_function__noleakcheck: true, #endif diff --git a/src/library_async.js b/src/library_async.js index 6df67bed9acb6..bc74064f0b749 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -488,7 +488,7 @@ mergeInto(LibraryManager.library, { }); }, - emscripten_wget_data__deps: ['$asyncLoad', 'malloc'], + emscripten_wget_data__deps: ['$asyncLoad', 'malloc', 'free'], emscripten_wget_data: function(url, pbuffer, pnum, perror) { return Asyncify.handleSleep((wakeUp) => { asyncLoad(UTF8ToString(url), (byteArray) => { diff --git a/src/library_exceptions.js b/src/library_exceptions.js index bb3281330a3ea..68de3ae850297 100644 --- a/src/library_exceptions.js +++ b/src/library_exceptions.js @@ -224,7 +224,7 @@ var LibraryExceptions = { return type; }, - __cxa_begin_catch__deps: ['$exceptionCaught', '$exception_addRef', '$uncaughtExceptionCount'], + __cxa_begin_catch__deps: ['$exceptionCaught', '$exception_addRef', '$uncaughtExceptionCount', '__cxa_can_catch', 'setTempRet0'], __cxa_begin_catch__sig: 'pp', __cxa_begin_catch: function(ptr) { var info = new ExceptionInfo(ptr); diff --git a/src/library_glew.js b/src/library_glew.js index 817cc2e0f09cf..fa93de980f9f7 100644 --- a/src/library_glew.js +++ b/src/library_glew.js @@ -112,6 +112,7 @@ var LibraryGLEW = { }, }, + glewInit__deps: ['malloc'], glewInit: function() { return 0; }, glewIsSupported: function(name) { diff --git a/src/library_glfw.js b/src/library_glfw.js index 944fc70474cb0..ede2a78b06c31 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -1121,7 +1121,7 @@ var LibraryGLFW = { /******************************************************************************* * GLFW FUNCTIONS ******************************************************************************/ - glfwInit__deps: ['emscripten_get_device_pixel_ratio'], + glfwInit__deps: ['emscripten_get_device_pixel_ratio', 'malloc', 'free'], glfwInit__sig: 'i', glfwInit: function() { if (GLFW.windows) return 1; // GL_TRUE @@ -1269,6 +1269,7 @@ var LibraryGLFW = { glfwPostEmptyEvent__sig: 'v', glfwPostEmptyEvent: function() {}, + glfwGetMonitors__deps: ['malloc'], glfwGetMonitors__sig: 'ii', glfwGetMonitors: function(count) { {{{ makeSetValue('count', '0', '1', 'i32') }}}; diff --git a/src/library_html5.js b/src/library_html5.js index 9aa77eb98fb2b..b8422a5bd8954 100644 --- a/src/library_html5.js +++ b/src/library_html5.js @@ -235,7 +235,7 @@ var LibraryHTML5 = { }, }, - $registerKeyEventCallback__deps: ['$JSEvents', '$findEventTarget', '$stringToUTF8'], + $registerKeyEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc', '$stringToUTF8'], $registerKeyEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -503,7 +503,7 @@ var LibraryHTML5 = { #endif }, - $registerMouseEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget'], + $registerMouseEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget', 'malloc'], $registerMouseEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -616,7 +616,7 @@ var LibraryHTML5 = { return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, - $registerWheelEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget'], + $registerWheelEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget', 'malloc'], $registerWheelEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -692,7 +692,7 @@ var LibraryHTML5 = { } }, - $registerUiEventCallback__deps: ['$JSEvents', '$findEventTarget'], + $registerUiEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc'], $registerUiEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -768,7 +768,7 @@ var LibraryHTML5 = { return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, - $registerFocusEventCallback__deps: ['$JSEvents', '$findEventTarget', '$stringToUTF8'], + $registerFocusEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc', '$stringToUTF8'], $registerFocusEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -911,7 +911,7 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateGamma, 'rr["gamma"]', 'double') }}}; }, - $registerDeviceMotionEventCallback__deps: ['$JSEvents', '$fillDeviceMotionEventData', '$findEventTarget'], + $registerDeviceMotionEventCallback__deps: ['$JSEvents', '$fillDeviceMotionEventData', '$findEventTarget', 'malloc'], $registerDeviceMotionEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -979,7 +979,7 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationAngle, 'orientation', 'i32') }}}; }, - $registerOrientationChangeEventCallback__deps: ['$JSEvents', '$fillOrientationChangeEventData', '$findEventTarget'], + $registerOrientationChangeEventCallback__deps: ['$JSEvents', '$fillOrientationChangeEventData', '$findEventTarget', 'malloc'], $registerOrientationChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -1099,7 +1099,7 @@ var LibraryHTML5 = { } }, - $registerFullscreenChangeEventCallback__deps: ['$JSEvents', '$fillFullscreenChangeEventData', '$findEventTarget'], + $registerFullscreenChangeEventCallback__deps: ['$JSEvents', '$fillFullscreenChangeEventData', '$findEventTarget', 'malloc'], $registerFullscreenChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -1677,7 +1677,7 @@ var LibraryHTML5 = { stringToUTF8(id, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.id }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); }, - $registerPointerlockChangeEventCallback__deps: ['$JSEvents', '$fillPointerlockChangeEventData', '$findEventTarget'], + $registerPointerlockChangeEventCallback__deps: ['$JSEvents', '$fillPointerlockChangeEventData', '$findEventTarget', 'malloc'], $registerPointerlockChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -1923,7 +1923,7 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}}; }, - $registerVisibilityChangeEventCallback__deps: ['$JSEvents', '$fillVisibilityChangeEventData', '$findEventTarget'], + $registerVisibilityChangeEventCallback__deps: ['$JSEvents', '$fillVisibilityChangeEventData', '$findEventTarget', 'malloc'], $registerVisibilityChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -1978,7 +1978,7 @@ var LibraryHTML5 = { return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, - $registerTouchEventCallback__deps: ['$JSEvents', '$findEventTarget', '$getBoundingClientRect'], + $registerTouchEventCallback__deps: ['$JSEvents', '$findEventTarget', '$getBoundingClientRect', 'malloc'], $registerTouchEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -2137,7 +2137,7 @@ var LibraryHTML5 = { stringToUTF8(e.mapping, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.mapping }}}, {{{ cDefs.EM_HTML5_MEDIUM_STRING_LEN_BYTES }}}); }, - $registerGamepadEventCallback__deps: ['$JSEvents', '$fillGamepadEventData', '$findEventTarget'], + $registerGamepadEventCallback__deps: ['$JSEvents', '$fillGamepadEventData', '$findEventTarget', 'malloc'], $registerGamepadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); @@ -2305,7 +2305,7 @@ var LibraryHTML5 = { }, emscripten_set_batterychargingchange_callback_on_thread__proxy: 'sync', - emscripten_set_batterychargingchange_callback_on_thread__deps: ['$registerBatteryEventCallback', '$battery', 'malloc'], + emscripten_set_batterychargingchange_callback_on_thread__deps: ['$registerBatteryEventCallback', '$battery'], emscripten_set_batterychargingchange_callback_on_thread: function(userData, callbackfunc, targetThread) { if (!battery()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; registerBatteryEventCallback(battery(), userData, true, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE }}}, "chargingchange", targetThread); @@ -2313,7 +2313,7 @@ var LibraryHTML5 = { }, emscripten_set_batterylevelchange_callback_on_thread__proxy: 'sync', - emscripten_set_batterylevelchange_callback_on_thread__deps: ['$registerBatteryEventCallback', '$battery', 'malloc'], + emscripten_set_batterylevelchange_callback_on_thread__deps: ['$registerBatteryEventCallback', '$battery'], emscripten_set_batterylevelchange_callback_on_thread: function(userData, callbackfunc, targetThread) { if (!battery()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; registerBatteryEventCallback(battery(), userData, true, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE }}}, "levelchange", targetThread); diff --git a/src/library_openal.js b/src/library_openal.js index 6d5ecf7d26db6..13f9d5b2338c8 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -3018,7 +3018,7 @@ var LibraryOpenAL = { }, alGetString__proxy: 'sync', - alGetString__deps: ['$stringToNewUTF8'], + alGetString__deps: ['$stringToNewUTF8', 'malloc'], alGetString: function(param) { if (AL.stringCache[param]) { return AL.stringCache[param]; diff --git a/src/library_syscall.js b/src/library_syscall.js index bc276d08c0a9f..61594a862d934 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -126,7 +126,7 @@ var SyscallsLibrary = { _mmap_js__deps: ['$SYSCALLS', #if FILESYSTEM && SYSCALLS_REQUIRE_FILESYSTEM - '$FS', + '$FS', 'emscripten_builtin_memalign', #endif ], _mmap_js: function(len, prot, flags, fd, off, allocated, addr) { diff --git a/src/parseTools.js b/src/parseTools.js index b8a16c34b5411..cfd65337d7509 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -922,7 +922,7 @@ function makeMalloc(source, param) { return `_malloc(${param})`; } // It should be impossible to call some functions without malloc being - // included, unless we have a deps_info.json bug. To let closure not error + // included, unless we have a missing __deps entry. To let closure not error // on `_malloc` not being present, they don't call malloc and instead abort // with an error at runtime. // TODO: A more comprehensive deps system could catch this at compile time. diff --git a/src/settings.js b/src/settings.js index e839bf2a78fd0..6907e6f9d1426 100644 --- a/src/settings.js +++ b/src/settings.js @@ -2028,22 +2028,6 @@ var IMPORTED_MEMORY = false; // [link] var SPLIT_MODULE = false; -// How to calculate reverse dependencies (dependencies from JS functions to -// native functions) prior to linking native code with wasm-ld. This option -// has three possible values: -// 'auto': (default) Inspect the object code passed to the linker (by running -// llvm-nm on all input) and use the map in deps_info.py to determine -// the set of additional dependencies. -// 'all' : Include the full set of possible reverse dependencies. -// 'none': No reverse dependences will be added by emscriopten. Any reverse -// dependencies will be assumed to be explicitly added to -// EXPORTED_FUNCTIONS and deps_info.py will be completely ignored. -// While 'auto' will produce a minimal set (so is good for code size), 'all' -// and 'none' will give faster link times, especially for very large projects -// (since they both avoid the running of llvm-nm on all linker inputs). -// [link] -var REVERSE_DEPS = 'auto'; - // For MAIN_MODULE builds, automatically load any dynamic library dependencies // on startup, before loading the main module. var AUTOLOAD_DYLIBS = true; @@ -2175,4 +2159,5 @@ var LEGACY_SETTINGS = [ ['LLD_REPORT_UNDEFINED', [1], 'Disabling is no longer supported'], ['MEM_INIT_METHOD', [0], 'No longer supported'], ['USE_PTHREADS', [0, 1], 'No longer needed. Use -pthread instead'], + ['REVERSE_DEPS', ['auto', 'all', 'none'], 'No longer needed'], ]; diff --git a/test/other/metadce/test_metadce_minimal_pthreads.exports b/test/other/metadce/test_metadce_minimal_pthreads.exports index 9c917676e4d2a..111be2f213161 100644 --- a/test/other/metadce/test_metadce_minimal_pthreads.exports +++ b/test/other/metadce/test_metadce_minimal_pthreads.exports @@ -4,6 +4,7 @@ C D E F +G p q r diff --git a/test/other/metadce/test_metadce_minimal_pthreads.funcs b/test/other/metadce/test_metadce_minimal_pthreads.funcs index 86cf4f2716c41..46d10e968aff8 100644 --- a/test/other/metadce/test_metadce_minimal_pthreads.funcs +++ b/test/other/metadce/test_metadce_minimal_pthreads.funcs @@ -45,6 +45,7 @@ $cancel_notification $dispose_chunk $dlfree $dlmalloc +$dlmemalign $do_dispatch_to_thread $em_queued_call_malloc $em_task_queue_cancel diff --git a/test/other/metadce/test_metadce_minimal_pthreads.jssize b/test/other/metadce/test_metadce_minimal_pthreads.jssize index 691c02c06b388..2a6673cc9848c 100644 --- a/test/other/metadce/test_metadce_minimal_pthreads.jssize +++ b/test/other/metadce/test_metadce_minimal_pthreads.jssize @@ -1 +1 @@ -15562 +15680 diff --git a/test/other/metadce/test_metadce_minimal_pthreads.size b/test/other/metadce/test_metadce_minimal_pthreads.size index 686ddd583747b..59501bf076992 100644 --- a/test/other/metadce/test_metadce_minimal_pthreads.size +++ b/test/other/metadce/test_metadce_minimal_pthreads.size @@ -1 +1 @@ -18965 +19448 diff --git a/test/test_core.py b/test/test_core.py index b9dbadf7ae087..60086f99acd20 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -9682,22 +9682,6 @@ def test_main_module_js_symbol(self): self.emcc_args += ['--js-library', test_file('core/test_main_module_js_symbol.js')] self.do_runf(test_file('core/test_main_module_js_symbol.c')) - def test_REVERSE_DEPS(self): - create_file('connect.c', '#include \nint main() { return (int)(long)&connect; }') - self.run_process([EMCC, 'connect.c']) - base_size = os.path.getsize('a.out.wasm') - - # 'auto' should work (its the default) - self.run_process([EMCC, 'connect.c', '-sREVERSE_DEPS=auto']) - - # 'all' should work too although it should produce a larger binary - self.run_process([EMCC, 'connect.c', '-sREVERSE_DEPS=all']) - self.assertGreater(os.path.getsize('a.out.wasm'), base_size) - - # 'none' should fail to link because the dependency on ntohs was not added. - err = self.expect_fail([EMCC, 'connect.c', '-sREVERSE_DEPS=none']) - self.assertContained('undefined symbol: ntohs', err) - def test_emscripten_async_call(self): self.set_setting('EXIT_RUNTIME') self.do_run_in_out_file_test(test_file('core/test_emscripten_async_call.c')) diff --git a/test/test_other.py b/test/test_other.py index fb88902dcd359..bae826fc00590 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -35,7 +35,7 @@ from common import compiler_for, EMBUILDER, requires_v8, requires_node, requires_wasm64 from common import requires_wasm_eh, crossplatform, with_both_sjlj from common import also_with_minimal_runtime, also_with_wasm_bigint, EMTEST_BUILD_VERBOSE, PYTHON -from tools import shared, building, utils, deps_info, response_file, cache +from tools import shared, building, utils, response_file, cache from tools.utils import read_file, write_file, delete_file, read_binary import common import jsrun @@ -6944,7 +6944,7 @@ def test_no_missing_symbols(self): # simple hello world should not show any miss }); ''') err = self.expect_fail([EMCC, 'test.c', '--js-library', 'library_foo_missing.js']) - self.assertContained('wasm-ld: error: symbol exported via --export not found: nonexistingvariable', err) + self.assertContained('undefined symbol: nonexistingvariable. Required by my_js', err) # and also for missing C code, of course (without the --js-library, it's just a missing C method) err = self.expect_fail([EMCC, 'test.c']) @@ -7537,12 +7537,6 @@ def test(contents, expected, args=[], assert_returncode=0): # noqa # test backwards compatibility test("Module['print']('print'); Module['printErr']('err'); ", 'print\nerr', ['-sEXPORTED_RUNTIME_METHODS=print,printErr']) - def test_warn_unexported_main(self): - WARNING = 'main() is in the input files, but "_main" is not in EXPORTED_FUNCTIONS, which means it may be eliminated as dead code. Export it if you want main() to run.' - - proc = self.run_process([EMCC, test_file('hello_world.c'), '-sEXPORTED_FUNCTIONS=[]'], stderr=PIPE) - self.assertContained(WARNING, proc.stderr) - def test_source_file_with_fixed_language_mode(self): create_file('src_tmp_fixed_lang', ''' #include @@ -10967,7 +10961,7 @@ def test_empty_output_extension(self): self.assertContained('hello, world!', self.run_js('hello')) def test_backwards_deps_in_archive(self): - # Test that JS dependencies from deps_info.json work for code linked via + # Test that JS dependencies on native code work for code linked via # static archives using -l self.run_process([EMCC, '-c', test_file('sockets/test_gethostbyname.c'), '-o', 'a.o']) self.run_process([LLVM_AR, 'cr', 'liba.a', 'a.o']) @@ -11369,7 +11363,7 @@ def test_linker_version(self): # depended on the missing function. def test_chained_js_error_diagnostics(self): err = self.expect_fail([EMCC, test_file('test_chained_js_error_diagnostics.c'), '--js-library', test_file('test_chained_js_error_diagnostics.js')]) - self.assertContained("error: undefined symbol: nonexistent_function (referenced by bar__deps: ['nonexistent_function'], referenced by foo__deps: ['bar'], referenced by top-level compiled C/C++ code)", err) + self.assertContained('emscripten_js_symbols.so: undefined symbol: nonexistent_function. Required by foo', err) # Test without chaining. In this case we don't include the JS library at # all resulting in `foo` being undefined in the native code. @@ -11901,97 +11895,6 @@ def test_relocatable_limited_exports(self): self.assertIn('sendmsg', exports_linkable) self.assertNotIn('sendmsg', exports) - @is_slow_test - def test_deps_info(self): - # Verify that for each symbol listed in deps_info all the reverse - # dependencies are indeed valid. - # To do this we compile a tiny test program that depends on the address - # of each function. Once compiled the resulting JavaScript code should - # contain a reference to each of the dependencies. - - # When debugging set this value to the function that you want to start - # with. All symbols prior will be skipped over. - start_at = None - assert not start_at or start_at in deps_info.get_deps_info() - for function, deps in deps_info.get_deps_info().items(): - if start_at: - if function == start_at: - start_at = None - else: - print(f'skipping {function}') - continue - create_file(function + '.c', ''' - void %s(); - int main() { - return (int)(long)&%s; - } - ''' % (function, function)) - # Compile with -O2 so we get JSDCE run to remove any false positives. This - # also makes the string quotes consistent which makes the test below simpler. - # Including -sREVERSE_DEPS=auto explicitly (even though its the default) to - # be clear this is what we are testing (and in case the default ever changes). - cmd = [EMCC, function + '.c', '-O2', '--minify=0', '--profiling-funcs', '-Wno-incompatible-library-redeclaration', '-sREVERSE_DEPS=auto'] - print(f'compiling test program for: {function}') - if 'emscripten_get_compiler_setting' in function: - cmd.append('-sRETAIN_COMPILER_SETTINGS') - if 'emscripten_pc_get_function' in function: - cmd.append('-sUSE_OFFSET_CONVERTER') - if 'embind' in function: - cmd.append('-lembind') - if 'websocket' in function: - cmd += ['-sPROXY_POSIX_SOCKETS', '-lwebsocket.js'] - if function == 'Mix_LoadWAV_RW': - cmd += ['-sUSE_SDL=2'] - if 'thread' in function: - cmd.append('-pthread') - if 'glGetStringi' in function: - cmd.append('-sUSE_WEBGL2') - if 'glMapBufferRange' in function: - cmd.append('-sFULL_ES3') - if function == 'wgpuDeviceCreateBuffer': - cmd.append('-sUSE_WEBGPU') - # dladdr dlsym etc.. - if function.startswith('dl'): - cmd.append('-sMAIN_MODULE=2') - if function.startswith('emscripten_idb') or function.startswith('emscripten_wget_'): - cmd.append('-sASYNCIFY') - if function.startswith('emscripten_webgl_') or 'offscreencanvas' in function: - cmd.append('-sOFFSCREENCANVAS_SUPPORT') - cmd.append('-pthread') - if function.startswith('wgpu'): - cmd.append('-sUSE_WEBGPU') - if function.startswith('__cxa_'): - cmd.append('-fexceptions') - if function.startswith('glfwGetMonitors'): - cmd.append('-sUSE_GLFW=3') - if 'mmap' in function: - cmd.append('-sFORCE_FILESYSTEM') - # In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function - # calls that exist in the same function with setjmp are converted to some - # code sequence that includes emscripten_longjmp. emscripten_longjmp is - # included in deps_info.py because in non-LTO builds setjmp does not exist - # anymore in the object files. So the mere indirect reference of setjmp or - # emscripten_longjmp does not generate calls to its dependencies specified - # in deps_info.py. Also Emscripten EH has a known restriction that setjmp - # cannot be called or referenced indirectly anyway. - if function in ['emscripten_longjmp', 'setjmp']: - continue - print(shared.shlex_join(cmd)) - self.run_process(cmd) - js = read_file('a.out.js') - for dep in deps: - direct = '_' + dep + '(' - via_module = '"_%s"](' % dep - assignment = ' = _' + dep - print(f' checking for: {dep}') - if direct not in js and via_module not in js and assignment not in js: - self.fail(f'use of declared dependency {dep} not found in JS output for {function}') - - # Check that the dependency not a JS library function. Dependencies on JS functions - # do not need entries in deps_info.py. - if f'function _{dep}(' in js: - self.fail(f'dependency {dep} in deps_info.py looks like a JS function (but should be native)') - @requires_v8 def test_shell_Oz(self): # regression test for -Oz working on non-web, non-node environments that diff --git a/tools/building.py b/tools/building.py index 9125335950d0f..ca267d3dd8938 100644 --- a/tools/building.py +++ b/tools/building.py @@ -40,8 +40,6 @@ binaryen_checked = False EXPECTED_BINARYEN_VERSION = 112 -# cache results of nm - it can be slow to run -nm_cache = {} _is_ar_cache: Dict[str, bool] = {} # the exports the user requested user_requested_exports: Set[str] = set() @@ -84,34 +82,6 @@ def get_building_env(): return env -@ToolchainProfiler.profile() -def llvm_nm_multiple(files): - """Runs llvm-nm for the given list of files. - - The results are populated in nm_cache, and also returned as an array with - order corresponding with the input files. - If a given file cannot be processed, None will be present in its place. - """ - if len(files) == 0: - return [] - # Run llvm-nm on files that we haven't cached yet - cmd = [LLVM_NM, '--print-file-name'] + [f for f in files if f not in nm_cache] - cmd = get_command_with_possible_response_file(cmd) - results = run_process(cmd, stdout=PIPE, stderr=PIPE, check=False) - - # If one or more of the input files cannot be processed, llvm-nm will return - # a non-zero error code, but it will still process and print out all the - # other files in order. So even if process return code is non zero, we should - # always look at what we got to stdout. - if results.returncode != 0: - logger.debug(f'Subcommand {" ".join(cmd)} failed with return code {results.returncode}! (An input file was corrupt?)') - - for key, value in parse_llvm_nm_symbols(results.stdout).items(): - nm_cache[key] = value - - return [nm_cache[f] if f in nm_cache else {'defs': set(), 'undefs': set(), 'parse_error': True} for f in files] - - def llvm_backend_args(): # disable slow and relatively unimportant optimization passes args = ['-combiner-global-alias-analysis=false'] @@ -146,13 +116,36 @@ def link_to_object(args, target): link_lld(args + ['--relocatable'], target) +def side_module_js_library_reverse_deps(external_symbols): + deps = set() + for sym in settings.SIDE_MODULE_IMPORTS: + sym = demangle_c_symbol_name(sym) + if sym in external_symbols: + deps = deps.union(external_symbols[sym]) + return sorted(list(deps)) + + def lld_flags_for_executable(external_symbols): cmd = [] - if settings.ERROR_ON_UNDEFINED_SYMBOLS: - undefs = shared.get_temp_files().get('.undefined').name - utils.write_file(undefs, '\n'.join(external_symbols)) - cmd.append('--allow-undefined-file=%s' % undefs) - else: + if external_symbols: + if settings.INCLUDE_FULL_LIBRARY: + all_deps = set() + for deps in external_symbols.values(): + for dep in deps: + if dep not in all_deps: + cmd.append('--export-if-defined=' + dep) + all_deps.add(dep) + stubfile = shared.get_temp_files().get('libemscripten_js_symbols.so').name + stubs = ['#STUB'] + for name, deps in external_symbols.items(): + if settings.ERROR_ON_UNDEFINED_SYMBOLS: + stubs.append('%s: %s' % (name, ','.join(deps))) + else: + stubs.append(name) + utils.write_file(stubfile, '\n'.join(stubs)) + cmd.append(stubfile) + + if not settings.ERROR_ON_UNDEFINED_SYMBOLS: cmd.append('--import-undefined') if settings.IMPORTED_MEMORY: @@ -178,6 +171,8 @@ def lld_flags_for_executable(external_symbols): c_exports += settings.EXPORT_IF_DEFINED # Filter out symbols external/JS symbols c_exports = [e for e in c_exports if e not in external_symbols] + if settings.MAIN_MODULE: + c_exports += side_module_js_library_reverse_deps(external_symbols) for export in c_exports: cmd.append('--export-if-defined=' + export) @@ -288,58 +283,6 @@ def get_command_with_possible_response_file(cmd): return new_cmd -# Parses the output of llvm-nm and returns a dictionary of symbols for each file in the output. -# This function can be called either for a single file output listing ("llvm-nm a.o", or for -# multiple files listing ("llvm-nm a.o b.o"). -def parse_llvm_nm_symbols(output): - # If response files are used in a call to llvm-nm, the output printed by llvm-nm has a - # quirk that it will contain double backslashes as directory separators on Windows. (it will - # not remove the escape characters when printing) - # Therefore canonicalize the output to single backslashes. - output = output.replace('\\\\', '\\') - - # a dictionary from 'filename' -> { 'defs': set(), 'undefs': set(), 'commons': set() } - symbols = {} - - for line in output.split('\n'): - # Line format: "[archive filename:]object filename: address status name" - entry_pos = line.rfind(': ') - if entry_pos < 0: - continue - - filename_end = line.rfind(':', 0, entry_pos) - # Disambiguate between - # C:\bar.o: T main - # and - # /foo.a:bar.o: T main - # (both of these have same number of ':'s, but in the first, the file name on disk is "C:\bar.o", in second, it is "/foo.a") - if filename_end < 0 or line[filename_end + 1] in '/\\': - filename_end = entry_pos - - filename = line[:filename_end] - if entry_pos + 13 >= len(line): - exit_with_error('error parsing output of llvm-nm: `%s`\nIf the symbol name here contains a colon, and starts with __invoke_, then the object file was likely built with an old version of llvm (please rebuild it).' % line) - - # Skip address, which is always fixed-length 8 chars (plus 2 leading chars `: ` and one trailing space) - status = line[entry_pos + 11] - symbol = line[entry_pos + 13:] - - if filename not in symbols: - record = symbols.setdefault(filename, { - 'defs': set(), - 'undefs': set(), - 'commons': set(), - 'parse_error': False - }) - if status == 'U': - record['undefs'] |= {symbol} - elif status == 'C': - record['commons'] |= {symbol} - elif status == status.upper(): - record['defs'] |= {symbol} - return symbols - - def emar(action, output_filename, filenames, stdout=None, stderr=None, env=None): utils.delete_file(output_filename) cmd = [EMAR, action, output_filename] + filenames diff --git a/tools/deps_info.py b/tools/deps_info.py deleted file mode 100644 index 4d5f9a557019b..0000000000000 --- a/tools/deps_info.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. - -# deps_info is a mechanism that lets JS code depend on C functions. This -# needs special help because of how linking works: -# -# 1. Receive some input files (.o, .c, etc.) from the user. -# 2. Link them with system libraries. -# 3. Whatever symbols are still unresolved, look in JS libraries for them. -# -# This makes C->JS calls work in a natural way: if compiled code depends on -# a function foo() that is implemented in a JS library, it will be unresolved -# after stage 2, and therefore linked in at stage 3. The problem is the other -# direction: if a JS library function decides it needs some function from say -# libc, then at stage 3 it is too late to link in more libc code. That's -# where deps_info comes in. -# -# Specifically, before stage 2 (linking with system libraries) we look at what -# symbols are required by the input files. Imagine that js_func in a JS -# library depends on libc_func in libc. Then if deps_info tells us -# -# "js_func": ["libc_func"] -# -# then if we see js_func is required (undefined) before stage 2, then we add -# a requirement to link in libc_func when linking in system libraries. All -# we do with deps_info is see if any of the keys are among the -# undefined symbols before stage 2, and if they are, add their values to the -# things we need to link in. -# -# This usually works the way you want, but note that it happens *before* stage -# 2 and not after it. That is, we look for js_func before linking in system -# libraries. If you have a situation where -# -# user_code => other_libc_func => js_func => libc_func -# -# then the deps_info entry must contain -# -# "other_libc_func": ["libc_func"] -# -# because that is what we see before stage 2: all we see is that -# other_libc_func is going to be linked in, and we don't know yet that it -# will end up calling js_func. But the presence of a call to other_libc_func -# indicates that we will need libc_func linked in as well, so that is what the -# deps_info entry should contain. -# -# TODO: Move all __deps from src/library*.js to deps_info, and use that single source of info -# both here and in the JS compiler. - -from tools.settings import settings - -_deps_info = { - 'alarm': ['_emscripten_timeout'], - 'sched_yield': ['_emscripten_timeout'], - 'setitimer': ['_emscripten_timeout'], - 'SDL_GL_GetProcAddress': ['malloc'], - 'SDL_LockSurface': ['malloc', 'free'], - 'SDL_OpenAudio': ['malloc', 'free'], - 'SDL_PushEvent': ['malloc', 'free'], - '_embind_register_class': ['free'], - '_embind_register_enum_value': ['free'], - '_embind_register_function': ['free'], - '_embind_register_std_string': ['free'], - '_embind_register_std_wstring': ['free'], - 'alGetString': ['malloc'], - 'alcGetString': ['malloc'], - 'bind': ['ntohs', 'htons'], - 'connect': ['ntohs', 'htons'], - 'ctime': ['malloc'], - 'gmtime': ['malloc'], - 'ctime_r': ['malloc'], - 'dladdr': ['malloc'], - 'dlopen': ['__dl_seterr', 'malloc'], - 'dlsym': ['__dl_seterr'], - 'eglGetProcAddress': ['malloc'], - 'eglQueryString': ['malloc'], - 'emscripten_GetProcAddress': ['malloc'], - 'emscripten_SDL_SetEventHandler': ['malloc', 'free'], - 'emscripten_alcGetStringiSOFT': ['malloc'], - 'emscripten_async_wget2_data': ['malloc', 'free'], - 'emscripten_async_wget_data': ['malloc', 'free'], - 'emscripten_create_worker': ['malloc', 'free'], - 'emscripten_get_compiler_setting': ['malloc'], - 'emscripten_get_window_title': ['malloc'], - 'emscripten_idb_async_load': ['malloc', 'free'], - 'emscripten_idb_load': ['malloc', 'free'], - 'emscripten_init_websocket_to_posix_socket_bridge': ['malloc', 'free'], - # This list is the same as setjmp's dependencies. In non-LTO builds, setjmp - # does not exist in the object files; it is converted into a code sequence - # that includes several functions, one of which is emscripten_longjmp. This is - # a trick to include these dependencies for setjmp even when setjmp does not - # exist. Refer to setjmp's entry for more details. - 'emscripten_longjmp': ['malloc', 'free', 'saveSetjmp', 'setThrew'], - 'emscripten_pc_get_file': ['malloc', 'free'], - 'emscripten_pc_get_function': ['malloc', 'free'], - 'emscripten_run_script_string': ['malloc', 'free'], - 'emscripten_set_blur_callback_on_thread': ['malloc'], - 'emscripten_set_click_callback_on_thread': ['malloc'], - 'emscripten_set_dblclick_callback_on_thread': ['malloc'], - 'emscripten_set_devicemotion_callback_on_thread': ['malloc'], - 'emscripten_set_deviceorientation_callback_on_thread': ['malloc'], - 'emscripten_set_focus_callback_on_thread': ['malloc'], - 'emscripten_set_focusin_callback_on_thread': ['malloc'], - 'emscripten_set_focusout_callback_on_thread': ['malloc'], - 'emscripten_set_fullscreenchange_callback_on_thread': ['malloc'], - 'emscripten_set_gamepadconnected_callback_on_thread': ['malloc'], - 'emscripten_set_gamepaddisconnected_callback_on_thread': ['malloc'], - 'emscripten_set_keydown_callback_on_thread': ['malloc'], - 'emscripten_set_keypress_callback_on_thread': ['malloc'], - 'emscripten_set_keyup_callback_on_thread': ['malloc'], - 'emscripten_set_mousedown_callback_on_thread': ['malloc'], - 'emscripten_set_mouseenter_callback_on_thread': ['malloc'], - 'emscripten_set_mouseleave_callback_on_thread': ['malloc'], - 'emscripten_set_mousemove_callback_on_thread': ['malloc'], - 'emscripten_set_mouseout_callback_on_thread': ['malloc'], - 'emscripten_set_mouseover_callback_on_thread': ['malloc'], - 'emscripten_set_mouseup_callback_on_thread': ['malloc'], - 'emscripten_set_orientationchange_callback_on_thread': ['malloc'], - 'emscripten_set_pointerlockchange_callback_on_thread': ['malloc'], - 'emscripten_set_resize_callback_on_thread': ['malloc'], - 'emscripten_set_scroll_callback_on_thread': ['malloc'], - 'emscripten_set_touchcancel_callback_on_thread': ['malloc'], - 'emscripten_set_touchend_callback_on_thread': ['malloc'], - 'emscripten_set_touchmove_callback_on_thread': ['malloc'], - 'emscripten_set_touchstart_callback_on_thread': ['malloc'], - 'emscripten_set_visibilitychange_callback_on_thread': ['malloc'], - 'emscripten_set_wheel_callback_on_thread': ['malloc'], - 'emscripten_thread_sleep': ['_emscripten_timeout'], - 'emscripten_webgl_get_parameter_utf8': ['malloc'], - 'emscripten_webgl_get_program_info_log_utf8': ['malloc'], - 'emscripten_webgl_get_shader_info_log_utf8': ['malloc'], - 'emscripten_webgl_get_shader_source_utf8': ['malloc'], - 'emscripten_webgl_get_supported_extensions': ['malloc'], - 'emscripten_websocket_set_onclose_callback_on_thread': ['malloc'], - 'emscripten_websocket_set_onerror_callback_on_thread': ['malloc'], - 'emscripten_websocket_set_onmessage_callback_on_thread': ['malloc', 'free'], - 'emscripten_websocket_set_onopen_callback_on_thread': ['malloc'], - 'emscripten_wget_data': ['malloc', 'free'], - 'getaddrinfo': ['malloc', 'htonl', 'htons', 'ntohs'], - 'gethostbyaddr': ['malloc', 'htons'], - 'gethostbyname': ['malloc', 'htons'], - 'gethostbyname_r': ['malloc', 'free', 'htons', 'memcpy'], - 'getnameinfo': ['htons', 'ntohs'], - 'getpeername': ['htons'], - 'getsockname': ['htons'], - 'glGetString': ['malloc'], - 'glGetStringi': ['malloc'], - 'glMapBufferRange': ['malloc'], - 'glewInit': ['malloc'], - 'glfwGetProcAddress': ['malloc'], - 'glfwInit': ['malloc', 'free'], - 'glfwSleep': ['sleep'], - 'glfwGetMonitors': ['malloc'], - 'localtime': ['malloc'], - 'localtime_r': ['malloc'], - 'mktime': ['malloc'], - 'recv': ['htons'], - 'recvmsg': ['htons'], - 'accept': ['htons'], - 'recvfrom': ['htons'], - 'send': ['ntohs', 'htons'], - 'sendmsg': ['ntohs', 'htons'], - # In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function calls - # that exist in the same function with setjmp are converted to some code - # sequence that includes invokes, malloc, free, saveSetjmp, and - # emscripten_longjmp. setThrew is called from invokes, but there's no way to - # directly include invokes in deps_info.py, so we list it as a setjmp's - # dependency. - 'setjmp': ['malloc', 'free', 'saveSetjmp', 'setThrew'], - 'syslog': ['malloc', 'ntohs', 'htons'], - 'vsyslog': ['malloc', 'ntohs', 'htons'], - 'timegm': ['malloc'], - 'tzset': ['malloc'], - 'emscripten_glGetString': ['malloc'], -} - - -def append_deps_info(js_symbol_deps): - for key, value in js_symbol_deps.items(): - if value: - _deps_info.setdefault(key, []) - _deps_info[key] += value - - -def get_deps_info(): - if not settings.WASM_EXCEPTIONS and settings.LINK_AS_CXX: - _deps_info['__cxa_begin_catch'] = ['__cxa_is_pointer_type', '__cxa_free_exception'] - _deps_info['__cxa_throw'] = ['__cxa_is_pointer_type'] - _deps_info['__cxa_find_matching_catch'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_1'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_2'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_3'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_4'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_5'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_6'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_7'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_8'] = ['__cxa_can_catch', 'setTempRet0'] - _deps_info['__cxa_find_matching_catch_9'] = ['__cxa_can_catch', 'setTempRet0'] - if settings.PTHREADS and settings.OFFSCREENCANVAS_SUPPORT: - _deps_info['pthread_create'] = ['malloc'] - if settings.FILESYSTEM and settings.SYSCALLS_REQUIRE_FILESYSTEM: - _deps_info['mmap'] = ['emscripten_builtin_memalign'] - if settings.PTHREADS: - _deps_info['glutCreateWindow'] = ['malloc'] - _deps_info['emscripten_webgl_create_context'] = ['malloc'] - _deps_info['emscripten_webgl_destroy_context'] = ['free'] - if settings.OFFSCREEN_FRAMEBUFFER: - # When OFFSCREEN_FRAMEBUFFER is defined these functions are defined in native code, - # otherwise they are defined in src/library_html5_webgl.js. - _deps_info['emscripten_webgl_destroy_context'] += ['emscripten_webgl_make_context_current', 'emscripten_webgl_get_current_context'] - - return _deps_info diff --git a/tools/shared.py b/tools/shared.py index b1780587347de..fb9cb90ace857 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -418,7 +418,7 @@ def perform_sanity_checks(): check_node() with ToolchainProfiler.profile_block('sanity LLVM'): - for cmd in [CLANG_CC, LLVM_AR, LLVM_NM]: + for cmd in [CLANG_CC, LLVM_AR]: if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'): # .exe extension required for Windows exit_with_error('Cannot find %s, check the paths in %s', cmd, config.EM_CONFIG) diff --git a/tools/system_libs.py b/tools/system_libs.py index 7bebfb22b6bbc..3574d81cb818f 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -15,10 +15,8 @@ from typing import List, Optional from . import shared, building, utils -from . import deps_info from . import diagnostics from . import cache -from tools.shared import demangle_c_symbol_name from tools.settings import settings from tools.utils import read_file @@ -954,6 +952,7 @@ def get_libcall_files(self): other_files = files_in_path( path='system/lib/libc', filenames=['emscripten_memcpy.c', 'emscripten_memset.c', + 'emscripten_get_heap_size.c', 'emscripten_scan_stack.c', 'emscripten_memmove.c']) # Calls to iprintf can be generated during codegen. Ideally we wouldn't @@ -1424,6 +1423,7 @@ class libcxxabi(NoExceptLibrary, MTLibrary, DebugLibrary): '-std=c++20', ] includes = ['system/lib/libcxx/src'] + force_object_files = True def __init__(self, **kwargs): super().__init__(**kwargs) @@ -2004,53 +2004,6 @@ def warn_on_unexported_main(symbolses): return -def handle_reverse_deps(input_files): - if settings.REVERSE_DEPS == 'none' or settings.SIDE_MODULE: - return - elif settings.REVERSE_DEPS == 'all': - # When not optimizing we add all possible reverse dependencies rather - # than scanning the input files - for symbols in deps_info.get_deps_info().values(): - for symbol in symbols: - settings.REQUIRED_EXPORTS.append(symbol) - return - - if settings.REVERSE_DEPS != 'auto': - shared.exit_with_error(f'invalid values for REVERSE_DEPS: {settings.REVERSE_DEPS}') - - added = set() - - def add_reverse_deps(need): - more = False - for ident, deps in deps_info.get_deps_info().items(): - if ident in need['undefs'] and ident not in added: - added.add(ident) - more = True - for dep in deps: - need['undefs'].add(dep) - logger.debug('adding dependency on %s due to deps-info on %s' % (dep, ident)) - settings.REQUIRED_EXPORTS.append(dep) - if more: - add_reverse_deps(need) # recurse to get deps of deps - - # Scan symbols - symbolses = building.llvm_nm_multiple([os.path.abspath(t) for t in input_files]) - - warn_on_unexported_main(symbolses) - - if len(symbolses) == 0: - symbolses.append({'defs': set(), 'undefs': set()}) - - # depend on exported functions - for export in settings.EXPORTED_FUNCTIONS + settings.SIDE_MODULE_IMPORTS: - if settings.VERBOSE: - logger.debug('adding dependency on export %s' % export) - symbolses[0]['undefs'].add(demangle_c_symbol_name(export)) - - for symbols in symbolses: - add_reverse_deps(symbols) - - def get_libs_to_link(args, forced, only_forced): libs_to_link = [] @@ -2188,19 +2141,15 @@ def add_sanitizer_libs(): return libs_to_link -def calculate(input_files, args, forced): +def calculate(args, forced): # Setting this will only use the forced libs in EMCC_FORCE_STDLIBS. This avoids spending time checking # for unresolved symbols in your project files, which can speed up linking, but if you do not have - # the proper list of actually needed libraries, errors can occur. See below for how we must - # export all the symbols in deps_info when using this option. + # the proper list of actually needed libraries, errors can occur. only_forced = os.environ.get('EMCC_ONLY_FORCED_STDLIBS') if only_forced: # One of the purposes EMCC_ONLY_FORCED_STDLIBS was to skip the scanning # of the input files for reverse dependencies. - diagnostics.warning('deprecated', 'EMCC_ONLY_FORCED_STDLIBS is deprecated. Use `-nostdlib` and/or `-sREVERSE_DEPS=none` depending on the desired result') - settings.REVERSE_DEPS = 'all' - - handle_reverse_deps(input_files) + diagnostics.warning('deprecated', 'EMCC_ONLY_FORCED_STDLIBS is deprecated. Use `-nostdlib` to avoid linking standard libraries') libs_to_link = get_libs_to_link(args, forced, only_forced)