commit f45f9649e3fc2aa2b6a76476f2467f02ffc7d461 (tree)
parent 9f235a105b29c6f1c0c773f66c40e7ee655da559
Author: Carl Ã…stholm <carl@astholm.se>
Date: Sun, 23 Mar 2025 13:12:45 +0100
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.
Diffstat:
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git 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
@@ -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
@@ -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,