diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 3a70144407..a85ca21016 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3982,6 +3982,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .got => .load_memory_ptr_got, .direct => .load_memory_ptr_direct, .import => unreachable, + .tlv => @panic("TODO TLV support"), }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -5510,6 +5511,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .got => .load_memory_ptr_got, .direct => .load_memory_ptr_direct, .import => unreachable, + .tlv => @panic("TODO TLV support"), }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -5630,6 +5632,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .got => .load_memory_got, .direct => .load_memory_direct, .import => .load_memory_import, + .tlv => @panic("TODO TLV support"), }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { @@ -5830,6 +5833,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I .got => .load_memory_ptr_got, .direct => .load_memory_ptr_direct, .import => unreachable, + .tlv => @panic("TODO TLV support"), }; const atom_index = switch (self.bin_file.tag) { .macho => blk: { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 491144283c..16c85c622e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -736,6 +736,7 @@ fn asmMovLinker(self: *Self, reg: Register, atom_index: u32, linker_load: codege .got => .got_reloc, .direct => .direct_reloc, .import => .import_reloc, + .tlv => .tlv_reloc, }; _ = try self.addInst(.{ .tag = .mov_linker, @@ -753,6 +754,7 @@ fn asmLeaLinker(self: *Self, reg: Register, atom_index: u32, linker_load: codege .got => .got_reloc, .direct => .direct_reloc, .import => .import_reloc, + .tlv => .tlv_reloc, }; _ = try self.addInst(.{ .tag = .lea_linker, @@ -765,6 +767,15 @@ fn asmLeaLinker(self: *Self, reg: Register, atom_index: u32, linker_load: codege }); } +fn genTlvPtr(self: *Self, reg: Register, atom_index: u32, linker_load: codegen.LinkerLoad) !void { + assert(linker_load.type == .tlv); + if (self.bin_file.cast(link.File.MachO)) |_| { + try self.asmMovLinker(.rdi, atom_index, linker_load); + try self.asmMemory(.call, Memory.sib(.qword, .{ .base = .rdi })); + try self.genSetReg(Type.usize, reg, .{ .register = .rax }); + } else return self.fail("TODO emit ptr-to-TLV sequence on {s}", .{@tagName(self.bin_file.tag)}); +} + fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -2931,6 +2942,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => return self.fail("TODO implement array_elem_val when array is {}", .{array}), @@ -3805,6 +3817,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -3866,6 +3879,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (load_struct.type) { .import => unreachable, .got, .direct => try self.asmMovLinker(addr_reg, atom_index, load_struct), + .tlv => { + try self.genTlvPtr(addr_reg, atom_index, load_struct); + // Load the pointer, which is stored in memory + try self.asmRegisterMemory(.mov, addr_reg, Memory.sib(.qword, .{ .base = addr_reg })); + }, } }, else => unreachable, @@ -4221,6 +4239,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -4896,6 +4915,7 @@ fn genBinOp( .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -5042,6 +5062,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s .import => unreachable, .got => try self.asmMovLinker(dst_addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(dst_addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(dst_addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -5087,6 +5108,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s .import => unreachable, .got => try self.asmMovLinker(src_addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(src_addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(src_addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -6124,6 +6146,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -7081,6 +7104,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -7308,6 +7332,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .import => unreachable, .got => try self.asmMovLinker(addr_reg, atom_index, load_struct), .direct => try self.asmLeaLinker(addr_reg, atom_index, load_struct), + .tlv => try self.genTlvPtr(addr_reg, atom_index, load_struct), } }, else => unreachable, @@ -7439,6 +7464,11 @@ fn genInlineMemcpy( switch (load_struct.type) { .import => unreachable, .got, .direct => try self.asmMovLinker(.rdi, atom_index, load_struct), + .tlv => { + try self.genTlvPtr(.rdi, atom_index, load_struct); + // Load the pointer, which is stored in memory + try self.asmRegisterMemory(.mov, .rdi, Memory.sib(.qword, .{ .base = .rdi })); + }, } }, .stack_offset, .ptr_stack_offset => |off| { @@ -7481,6 +7511,11 @@ fn genInlineMemcpy( switch (load_struct.type) { .import => unreachable, .got, .direct => try self.asmMovLinker(.rsi, atom_index, load_struct), + .tlv => { + try self.genTlvPtr(.rsi, atom_index, load_struct); + // Load the pointer, which is stored in memory + try self.asmRegisterMemory(.mov, .rsi, Memory.sib(.qword, .{ .base = .rsi })); + }, } }, .stack_offset, .ptr_stack_offset => |off| { @@ -7546,6 +7581,11 @@ fn genInlineMemset( switch (load_struct.type) { .import => unreachable, .got, .direct => try self.asmMovLinker(.rdi, atom_index, load_struct), + .tlv => { + try self.genTlvPtr(.rdi, atom_index, load_struct); + // Load the pointer, which is stored in memory + try self.asmRegisterMemory(.mov, .rdi, Memory.sib(.qword, .{ .base = .rdi })); + }, } }, .stack_offset, .ptr_stack_offset => |off| { @@ -7735,7 +7775,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (ty.zigTypeTag()) { .Float => { const base_reg = (try self.register_manager.allocReg(null, gp)).to64(); - try self.asmLeaLinker(base_reg, atom_index, load_struct); + switch (load_struct.type) { + .tlv => try self.genTlvPtr(base_reg, atom_index, load_struct), + else => try self.asmLeaLinker(base_reg, atom_index, load_struct), + } if (intrinsicsAllowed(self.target.*, ty)) { return self.asmRegisterMemory( @@ -7753,7 +7796,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); }, - else => try self.asmMovLinker(registerAlias(reg, abi_size), atom_index, load_struct), + else => switch (load_struct.type) { + .tlv => { + try self.genTlvPtr(reg.to64(), atom_index, load_struct); + try self.asmRegisterMemory( + .mov, + registerAlias(reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }), + ); + }, + else => try self.asmMovLinker(registerAlias(reg, abi_size), atom_index, load_struct), + }, } }, .stack_offset => |off| { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 16fb8a0183..a81840be47 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -76,6 +76,7 @@ pub fn emitMir(emit: *Emit) Error!void { .type = switch (inst.ops) { .got_reloc => .got, .direct_reloc => .signed, + .tlv_reloc => .tlv, else => unreachable, }, .target = .{ .sym_index = metadata.sym_index, .file = null }, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 835e9bfdcb..8772b835dd 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -404,6 +404,9 @@ pub const Inst = struct { /// Linker relocation - imports table indirection (binding). /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. import_reloc, + /// Linker relocation - threadlocal variable via GOT indirection. + /// Uses `payload` payload with extra data of type `LeaRegisterReloc`. + tlv_reloc, }; pub const Data = union { diff --git a/src/codegen.zig b/src/codegen.zig index 4156dd853b..e00141db97 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -912,11 +912,13 @@ fn lowerDeclRef( /// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc) /// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc) /// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc) +/// * tlv - the value is a threadlocal variable referenced indirectly via GOT (the linker emits a got-type reloc) pub const LinkerLoad = struct { type: enum { got, direct, import, + tlv, }, sym_index: u32, }; @@ -991,6 +993,8 @@ fn genDeclRef( module.markDeclAlive(decl); + const is_threadlocal = tv.val.isPtrToThreadLocal(module) and !bin_file.options.single_threaded; + if (bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); const atom = elf_file.getAtom(atom_index); @@ -999,7 +1003,7 @@ fn genDeclRef( const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; return GenResult.mcv(.{ .linker_load = .{ - .type = .got, + .type = if (is_threadlocal) .tlv else .got, .sym_index = sym_index, } }); } else if (bin_file.cast(link.File.Coff)) |coff_file| {