public static EmscriptenDynamicLinker ConfigureEmscriptenLinkerFor(EmscriptenDynamicLinker e, string variation, bool enableManagedDebugger) { var linkflags = new Dictionary <string, string> { // Bee defaults to PRECISE_F32=2, which is not an interesting feature for Dots. In Dots asm.js builds, we don't // care about single-precision floats, but care more about code size. { "PRECISE_F32", "0" }, // No exceptions machinery needed, saves code size { "DISABLE_EXCEPTION_CATCHING", "1" }, //// No virtual filesystem needed, saves code size { "NO_FILESYSTEM", "1" }, // Make generated builds only ever executable from web, saves code size. // TODO: if/when we are generating a build for node.js test harness purposes, remove this line. { "ENVIRONMENT", "web" }, // In -Oz builds, Emscripten does compile time global initializer evaluation in hope that it can // optimize away some ctors that can be compile time executed. This does not really happen often, // and with MINIMAL_RUNTIME we have a better "super-constructor" approach that groups all ctors // together into one, and that saves more code size. Unfortunately grouping constructors is // not possible if EVAL_CTORS is used, so disable EVAL_CTORS to enable grouping. { "EVAL_CTORS", "0" }, // We don't want malloc() failures to trigger program exit and abort handling, but instead behave // like C runtimes do, and make malloc() return null. This saves code size and lets our code // handle oom failures. { "ABORTING_MALLOC", "0" }, // By default the musl C runtime used by Emscripten is POSIX errno aware. We do not care about // errno, so opt out from errno management to save a tiny bit of performance and code size. { "SUPPORT_ERRNO", "0" }, // Safari does not support WebAssembly.instantiateStreaming(), so revert to the older // WebAssembly.instantiate() API. This has the drawback that WebAssembly compilation will not // occur while downloading the .wasm file, but enables Safari compatibility. { "STREAMING_WASM_COMPILATION", "0" } }; if (enableManagedDebugger) { linkflags["PROXY_POSIX_SOCKETS"] = "1"; } if (e.Toolchain.Architecture is AsmJsArchitecture) { linkflags["LEGACY_VM_SUPPORT"] = "1"; e = e.WithSeparateAsm(true); } if (variation == "debug" || variation == "develop") { linkflags["ASSERTIONS"] = "2"; linkflags["DEMANGLE_SUPPORT"] = "1"; } else { linkflags["ASSERTIONS"] = "0"; linkflags["AGGRESSIVE_VARIABLE_ELIMINATION"] = "1"; linkflags["ELIMINATE_DUPLICATE_FUNCTIONS"] = "1"; } if (BuildSettingsContent != null && BuildSettingsContent.TryGetValue("emscriptenLinkSettings", out var emscriptenLinkSettingsToken) && emscriptenLinkSettingsToken is JObject emscriptenLinkSettings) { foreach (var setting in emscriptenLinkSettings) { linkflags[setting.Key] = setting.Value.Value <string>(); } } e = e.WithEmscriptenSettings(linkflags); e = e.WithNoExitRuntime(true); switch (variation) { case "debug": e = e.WithDebugLevel("3"); e = e.WithOptLevel("0"); e = e.WithLinkTimeOptLevel(0); e = e.WithEmitSymbolMap(true); break; case "develop": e = e.WithDebugLevel("2"); e = e.WithOptLevel("1"); e = e.WithLinkTimeOptLevel(0); e = e.WithEmitSymbolMap(false); break; case "release": e = e.WithDebugLevel("0"); e = e.WithOptLevel("z"); e = e.WithLinkTimeOptLevel(3); e = e.WithEmitSymbolMap(true); break; default: throw new ArgumentException(); } e = e.WithMinimalRuntime(EmscriptenMinimalRuntimeMode.EnableDangerouslyAggressive); // Bee is not yet aware of the new --closure-externs (and --closure-annotations) linker flags, so add them using the generic // escape hatch hook. e = e.WithCustomFlags_workaround(new[] { "--closure-externs", BuildProgram.BeeRoot.Combine("closure_externs.js").ToString().QuoteForProcessStart() }); // TODO: Remove this line once Bee fix is in to support SystemLibrary() objects on web builds. Then restore // the line Libraries.Add(c => c.ToolChain.Platform is WebGLPlatform, new SystemLibrary("GL")); at the top of this file e = e.WithCustomFlags_workaround(new[] { "-lGL" }); e = e.WithMemoryInitFile(e.Toolchain.Architecture is AsmJsArchitecture || RunInBackgroundWorker); if (RunInBackgroundWorker) { // Specify Emscripten -s USE_PTHREADS=1 at compile time, so that C++ code that is compiled will have // the __EMSCRIPTEN_PTHREADS__ preprocessor #define available to it to detect if code will be compiled // single- or multithreaded. e = e.WithCustomFlags_workaround(new[] { "-s USE_PTHREADS=1 " }); } // Enables async requests for web IO and Disabling IndexDB support as this is not fully implemented yet in emscripten // Using custom flags as there appears to be no standard way to set the option in bee and passing the flags to the linker settings // normally will cause bee to error e = e.WithCustomFlags_workaround(new[] { "-s FETCH=1 -s FETCH_SUPPORT_INDEXEDDB=0" }); return(e); }
public static EmscriptenDynamicLinker ConfigureEmscriptenLinkerFor(EmscriptenDynamicLinker e, string variation, bool enableManagedDebugger) { bool runInBackgroundWorker = RunInBackgroundWorker || enableManagedDebugger; bool enableMultithreading = EnableMultithreading || runInBackgroundWorker; var linkflags = new Dictionary <string, string> { // Bee defaults to PRECISE_F32=2, which is not an interesting feature for Dots. In Dots asm.js builds, we don't // care about single-precision floats, but care more about code size. { "PRECISE_F32", "0" }, // No virtual filesystem needed, saves code size { "NO_FILESYSTEM", "1" }, // Generate runtime code only for executing in web browser (and in a Web Worker if debugging) { "ENVIRONMENT", enableMultithreading ? "web,worker" : "web" }, // Proxy all posix sockets calls to a sockets thread when managed debugging is enabled { "PROXY_POSIX_SOCKETS", enableManagedDebugger ? "1" : "0" }, // No exceptions machinery needed when not debugging, saves code size { "DISABLE_EXCEPTION_CATCHING", enableManagedDebugger ? "0" : "1" }, // In -Oz builds, Emscripten does compile time global initializer evaluation in hope that it can // optimize away some ctors that can be compile time executed. This does not really happen often, // and with MINIMAL_RUNTIME we have a better "super-constructor" approach that groups all ctors // together into one, and that saves more code size. Unfortunately grouping constructors is // not possible if EVAL_CTORS is used, so disable EVAL_CTORS to enable grouping. { "EVAL_CTORS", "0" }, // By default the musl C runtime used by Emscripten is POSIX errno aware. We do not care about // errno, so opt out from errno management to save a tiny bit of performance and code size. { "SUPPORT_ERRNO", "0" }, // IndexedDB not currently working with Fetch when MINIMAL_RUNTIME is used. (We don't need // it anyways as long as our content sizes are nice and small. Revisit when we have 50MB+ // asset package files) { "FETCH_SUPPORT_INDEXEDDB", "0" }, // We only utilize asynchronous Fetches, so do not need a dedicated Fetch Worker for sync fetches // on the background. { "USE_FETCH_WORKER", "0" }, // Initial heap size is defined by TOTAL_MEMORY, but also enable the heap to grow at runtime with // ALLOW_MEMORY_GROWTH flag. { "ALLOW_MEMORY_GROWTH", "1" }, // Remove support for OES_texture_half_float and OES_texture_half_float_linear extensions if // they are broken. See https://bugs.webkit.org/show_bug.cgi?id=183321, // https://bugs.webkit.org/show_bug.cgi?id=169999, // https://stackoverflow.com/questions/54248633/cannot-create-half-float-oes-texture-from-uint16array-on-ipad { "GL_DISABLE_HALF_FLOAT_EXTENSION_IF_BROKEN", "1" }, // Use emmalloc_memalign to allocate VM GC memory with BDWGC so that the managed heap // uses the same allcoator as everything else. { "MALLOC", "emmalloc" }, }; if (enableMultithreading) { e = e.WithMultithreading_Linker(EmscriptenMultithreadingMode.Enabled); } if (runInBackgroundWorker) { // WebGL rendering will run in a background thread - proxy GL over to the main thread. linkflags["OFFSCREEN_FRAMEBUFFER"] = "1"; // Enable the main() thread to launch in a Web Worker instead of the main browser thread. linkflags["PROXY_TO_PTHREAD"] = "1"; } if (enableManagedDebugger) { e = e.WithCustomFlags_workaround(new[] { "-lwebsocket.js" }); } if (e.Toolchain.Architecture is AsmJsArchitecture) { // Target the oldest of browsers when building to JavaScript. linkflags["LEGACY_VM_SUPPORT"] = "1"; // In old fastcomp backend, we can separate the unreadable .asm.js content to its own .asm.js file. // In new LLVM backend, it is currently always separated if -s WASM=2 is set, or embedded inline // if -s WASM=0 is set, so this option does not apply there. if (!UseWasmBackend) { e = e.WithSeparateAsm(true); } } if (variation == "debug" || variation == "develop") { linkflags["ASSERTIONS"] = "1"; linkflags["DEMANGLE_SUPPORT"] = "1"; } else { linkflags["ASSERTIONS"] = "0"; linkflags["AGGRESSIVE_VARIABLE_ELIMINATION"] = "1"; if (!UseWasmBackend) // This optimization pass only exists for the old fastcomp backend. { linkflags["ELIMINATE_DUPLICATE_FUNCTIONS"] = "1"; } } e = e.WithEmscriptenSettings(linkflags); e = e.WithNoExitRuntime(true); switch (variation) { case "debug": e = e.WithDebugLevel("3"); // Preserve JS whitespace, function names, and LLVM debug info e = e.WithOptLevel("1"); // -O0 generates too much code from IL2CPP, must apply optimizations. e = e.WithLinkTimeOptLevel(0); e = e.WithEmitSymbolMap(false); // At -g3 no name minification occurs -> no symbols present e = e.WithCustomFlags_workaround(new[] { "-fno-inline" // Disable inlining in debug builds for easier stepping through code. }); break; case "develop": e = e.WithDebugLevel("2"); // Preserve JS whitespace and function names e = e.WithOptLevel("1"); e = e.WithLinkTimeOptLevel(0); e = e.WithEmitSymbolMap(false); // At -g2 no name minification occurs -> no symbols present break; case "release": e = e.WithDebugLevel("0"); e = e.WithOptLevel("z"); if (UseWasmBackend) { // Wasm backend uses the general LLVM/GCC -flto flag to enable LTO. e = e.WithCustomFlags_workaround(new[] { "-flto" }); } else { // Old fastcomp backend uses its own --llvm-lto <x> flag. e = e.WithLinkTimeOptLevel(3); } e = e.WithEmitSymbolMap(false); // TODO: re-enable this after Emscripten update break; default: throw new ArgumentException(); } e = e.WithMinimalRuntime(EmscriptenMinimalRuntimeMode.EnableDangerouslyAggressive); if (variation == "release" && EnableClosureCompiler) { // Inject -g1 (preserve whitespace) to Emscripten build so that error messages from Closure will be readable and not minified. // Closure will kill this whitespace as part of its minification. // TODO: Remove this the next time we update Emscripten (https://github.com/emscripten-core/emscripten/pull/12726) e = e.WithDebugLevel("1"); e = e.WithCustomFlags_workaround(new[] { "--closure-args", ("--platform native --externs " + BuildProgram.BeeRoot.Combine("closure_externs.js").ToString()).QuoteForProcessStart(), "--closure", "1", "-s", "CLOSURE_WARNINGS=warn" }); } // TODO: Remove this line once Bee fix is in to support SystemLibrary() objects on web builds. Then restore // the line Libraries.Add(c => c.ToolChain.Platform is WebGLPlatform, new SystemLibrary("GL")); at the top of this file e = e.WithCustomFlags_workaround(new[] { "-lGL" }); // Target the Emscripten Fetch API for network requests. e = e.WithCustomFlags_workaround(new[] { "-s FETCH=1" }); e = e.WithMemoryInitFile(e.Toolchain.Architecture is AsmJsArchitecture || (enableMultithreading && !UseWasmBackend)); if (UseWasmBackend && e.Toolchain.Architecture is AsmJsArchitecture) { // Work around Binaryen multithreading bug: using more than 1 core is slower than using a single core! // TODO: Remove this after Emscripten update, where the issue has been fixed. // BUG: this does not actually work. Emcc is not a child of the Bee build process. // Switch to use something else. // Environment.SetEnvironmentVariable("BINARYEN_CORES", "1"); } return(e); }