authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2025-03-23 13:12:45+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-03-23 17:13:19-04:00
logf45f9649e3fc2aa2b6a76476f2467f02ffc7d461
tree4d10ac814ef9d7d7e9f06fffac796217336bfbc5
parent9f235a105b29c6f1c0c773f66c40e7ee655da559

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.

3 files changed, 11 insertions(+), 4 deletions(-)

lib/std/debug.zig+3-1
...@@ -183,9 +183,11 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) {...@@ -183,9 +183,11 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) {
183183
184 // `@returnAddress()` in LLVM 10 gives184 // `@returnAddress()` in LLVM 10 gives
185 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address".185 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address".
186 // On Emscripten, Zig only supports `@returnAddress()` in debug builds
187 // because Emscripten's implementation is very slow.
186 .wasm32,188 .wasm32,
187 .wasm64,189 .wasm64,
188 => native_os == .emscripten,190 => native_os == .emscripten and builtin.mode == .Debug,
189191
190 // `@returnAddress()` is unsupported in LLVM 13.192 // `@returnAddress()` is unsupported in LLVM 13.
191 .bpfel,193 .bpfel,
src/codegen/llvm.zig+1-1
...@@ -9525,7 +9525,7 @@ pub const FuncGen = struct {...@@ -9525,7 +9525,7 @@ pub const FuncGen = struct {
9525 _ = inst;9525 _ = inst;
9526 const o = self.ng.object;9526 const o = self.ng.object;
9527 const llvm_usize = try o.lowerType(Type.usize);9527 const llvm_usize = try o.lowerType(Type.usize);
9528 if (!target_util.supportsReturnAddress(o.pt.zcu.getTarget())) {9528 if (!target_util.supportsReturnAddress(o.pt.zcu.getTarget(), self.ng.ownerModule().optimize_mode)) {
9529 // https://github.com/ziglang/zig/issues/119469529 // https://github.com/ziglang/zig/issues/11946
9530 return o.builder.intValue(llvm_usize, 0);9530 return o.builder.intValue(llvm_usize, 0);
9531 }9531 }
src/target.zig+7-2
...@@ -248,9 +248,14 @@ pub fn libcProvidesStackProtector(target: std.Target) bool {...@@ -248,9 +248,14 @@ pub fn libcProvidesStackProtector(target: std.Target) bool {
248 return !target.isMinGW() and target.os.tag != .wasi and !target.cpu.arch.isSpirV();248 return !target.isMinGW() and target.os.tag != .wasi and !target.cpu.arch.isSpirV();
249}249}
250250
251pub fn supportsReturnAddress(target: std.Target) bool {251/// Returns true if `@returnAddress()` is supported by the target and has a
252/// reasonably performant implementation for the requested optimization mode.
253pub fn supportsReturnAddress(target: std.Target, optimize: std.builtin.OptimizeMode) bool {
252 return switch (target.cpu.arch) {254 return switch (target.cpu.arch) {
253 .wasm32, .wasm64 => target.os.tag == .emscripten,255 // Emscripten currently implements `emscripten_return_address()` by calling
256 // out into JavaScript and parsing a stack trace, which introduces significant
257 // overhead that we would prefer to avoid in release builds.
258 .wasm32, .wasm64 => target.os.tag == .emscripten and optimize == .Debug,
254 .bpfel, .bpfeb => false,259 .bpfel, .bpfeb => false,
255 .spirv, .spirv32, .spirv64 => false,260 .spirv, .spirv32, .spirv64 => false,
256 else => true,261 else => true,