authorgravatar for mick@sayson.comMick Sayson <mick@sayson.com> 2025-12-15 17:07:34-08:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-12-17 15:33:10+01:00
logfc78a61c4c6dd2a8a1047b5e5b62037e82b0dca0
treebdf9ea1d5b3fe99449c0acffbebc9f5580927dbd
parentb9eefe17af2c30855f229f5214679d3766966b85

Prevent register clobbering on x86_64 threadlocal access

On the x86_64 self hosted backend, thread locals are accessed through __tls_get_addr on PIC. Usually this goes through a fast path which does not lose any registers, however in some cases (notably any dlopened library on my machine) this can take a slow path which calls out to C ABI functions Catch this case and backup registers as necessary Fix a few other ones while we're here. Credit to mlugg Fixes #30183

1 files changed, 32 insertions(+), 4 deletions(-)

src/codegen/x86_64/CodeGen.zig+32-4
...@@ -173048,11 +173048,39 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -173048,11 +173048,39 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
173048 const ty_nav = air_datas[@intFromEnum(inst)].ty_nav;173048 const ty_nav = air_datas[@intFromEnum(inst)].ty_nav;
173049 const nav = ip.getNav(ty_nav.nav);173049 const nav = ip.getNav(ty_nav.nav);
173050 const is_threadlocal = zcu.comp.config.any_non_single_threaded and nav.isThreadlocal(ip);173050 const is_threadlocal = zcu.comp.config.any_non_single_threaded and nav.isThreadlocal(ip);
173051 if (is_threadlocal) if (cg.target.ofmt == .coff or cg.mod.pic) {173051
173052 try cg.spillRegisters(&.{ .rdi, .rax });173052 if (is_threadlocal) switch (cg.target.ofmt) {
173053 } else {173053 .elf => if (cg.mod.pic) {
173054 try cg.spillRegisters(&.{.rax});173054 // LD model: `__tls_get_addr` uses the standard ABI
173055 try cg.spillEflagsIfOccupied();
173056 try cg.spillRegisters(abi.getCallerPreservedRegs(.x86_64_sysv));
173057 } else {
173058 // LE model: manual lowering uses these two registers
173059 try cg.spillRegisters(&.{ .rdi, .rax });
173060 },
173061
173062 .coff => {
173063 // manual lowering uses these registers
173064 try cg.spillRegisters(&.{ .rdi, .rax });
173065 },
173066
173067 .macho => switch (cg.target.cpu.arch) {
173068 .x86 => {
173069 // `tlv_get_addr` returns in eax, clobbers ecx, preserves other GPRs
173070 try cg.spillEflagsIfOccupied();
173071 try cg.spillRegisters(&.{ .rax, .rcx });
173072 },
173073 .x86_64 => {
173074 // `tlv_get_addr` returns in rax, preserves other GPRs
173075 try cg.spillEflagsIfOccupied();
173076 try cg.spillRegisters(&.{.rax});
173077 },
173078 else => unreachable,
173079 },
173080
173081 else => unreachable,
173055 };173082 };
173083
173056 var res = try cg.tempInit(.fromInterned(ty_nav.ty), .{ .lea_nav = ty_nav.nav });173084 var res = try cg.tempInit(.fromInterned(ty_nav.ty), .{ .lea_nav = ty_nav.nav });
173057 if (is_threadlocal) while (try res.toRegClass(true, .general_purpose, cg)) {};173085 if (is_threadlocal) while (try res.toRegClass(true, .general_purpose, cg)) {};
173058 try res.finish(inst, &.{}, &.{}, cg);173086 try res.finish(inst, &.{}, &.{}, cg);