From f45f9649e3fc2aa2b6a76476f2467f02ffc7d461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20=C3=85stholm?= Date: Sun, 23 Mar 2025 13:12:45 +0100 Subject: [PATCH] Lower `@returnAddress` to a constant 0 in Emscripten release builds Emscripten currently implements `emscripten_return_address()` by calling out into JavaScript and parsing a stack trace, which introduces significant overhead that we would prefer to avoid in release builds. This is especially problematic for allocators because the generic parts of `std.mem.Allocator` make frequent use of `@returnAddress`, even though very few allocator implementations even observe the return address, which makes allocators nigh unusable for performance-critical applications like games if the compiler is unable to devirtualize the allocator calls. --- lib/std/debug.zig | 4 +++- src/codegen/llvm.zig | 2 +- src/target.zig | 9 +++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 77b619a056..929ab69bf0 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -183,9 +183,11 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { // `@returnAddress()` in LLVM 10 gives // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address". + // On Emscripten, Zig only supports `@returnAddress()` in debug builds + // because Emscripten's implementation is very slow. .wasm32, .wasm64, - => native_os == .emscripten, + => native_os == .emscripten and builtin.mode == .Debug, // `@returnAddress()` is unsupported in LLVM 13. .bpfel, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 167e0d1607..f4e4c1cdd6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9525,7 +9525,7 @@ pub const FuncGen = struct { _ = inst; const o = self.ng.object; const llvm_usize = try o.lowerType(Type.usize); - if (!target_util.supportsReturnAddress(o.pt.zcu.getTarget())) { + if (!target_util.supportsReturnAddress(o.pt.zcu.getTarget(), self.ng.ownerModule().optimize_mode)) { // https://github.com/ziglang/zig/issues/11946 return o.builder.intValue(llvm_usize, 0); } diff --git a/src/target.zig b/src/target.zig index 76eec4fa6e..5c8f7895b5 100644 --- a/src/target.zig +++ b/src/target.zig @@ -248,9 +248,14 @@ pub fn libcProvidesStackProtector(target: std.Target) bool { return !target.isMinGW() and target.os.tag != .wasi and !target.cpu.arch.isSpirV(); } -pub fn supportsReturnAddress(target: std.Target) bool { +/// Returns true if `@returnAddress()` is supported by the target and has a +/// reasonably performant implementation for the requested optimization mode. +pub fn supportsReturnAddress(target: std.Target, optimize: std.builtin.OptimizeMode) bool { return switch (target.cpu.arch) { - .wasm32, .wasm64 => target.os.tag == .emscripten, + // Emscripten currently implements `emscripten_return_address()` by calling + // out into JavaScript and parsing a stack trace, which introduces significant + // overhead that we would prefer to avoid in release builds. + .wasm32, .wasm64 => target.os.tag == .emscripten and optimize == .Debug, .bpfel, .bpfeb => false, .spirv, .spirv32, .spirv64 => false, else => true,