From 92c63126e8a58235625cceaf0ea9e3d4eacfe513 Mon Sep 17 00:00:00 2001 From: mlugg Date: Mon, 26 May 2025 22:59:01 +0100 Subject: [PATCH] compiler: tlv pointers are not comptime-known Pointers to thread-local variables do not have their addresses known until runtime, so it is nonsensical for them to be comptime-known. There was logic in the compiler which was essentially attempting to treat them as not being comptime-known despite the pointer being an interned value. This was a bit of a mess, the check was frequent enough to actually show up in compiler profiles, and it was very awkward for backends to deal with, because they had to grapple with the fact that a "constant" they were lowering might actually require runtime operations. So, instead, do not consider these pointers to be comptime-known in *any* way. Never intern such a pointer; instead, when the address of a threadlocal is taken, emit an AIR instruction which computes the pointer at runtime. This avoids lots of special handling for TLVs across basically all codegen backends; of all somewhat-functional backends, the only one which wasn't improved by this change was the LLVM backend, because LLVM pretends this complexity around threadlocals doesn't exist. This change simplifies Sema and codegen, avoids a potential source of bugs, and potentially improves Sema performance very slightly by avoiding a non-trivial check on a hot path. --- lib/std/debug.zig | 6 + src/Air.zig | 18 ++ src/Air/types_resolved.zig | 4 + src/InternPool.zig | 24 -- src/Liveness.zig | 2 + src/Liveness/Verify.zig | 1 + src/Sema.zig | 71 +++--- src/Value.zig | 15 -- src/arch/aarch64/CodeGen.zig | 3 +- src/arch/arm/CodeGen.zig | 3 +- src/arch/riscv64/CodeGen.zig | 91 ++++---- src/arch/sparc64/CodeGen.zig | 3 +- src/arch/wasm/CodeGen.zig | 15 ++ src/arch/x86_64/CodeGen.zig | 217 ++++++------------ src/codegen.zig | 18 +- src/codegen/c.zig | 13 ++ src/codegen/llvm.zig | 9 + src/print_air.zig | 8 + test/behavior/threadlocal.zig | 4 + ...ress_of_threadlocal_not_comptime_known.zig | 3 - .../builtin_extern_in_comptime_scope.zig | 4 - 21 files changed, 242 insertions(+), 290 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 91ff367c2d8953b83e5cc96e6ed26688a386e546..7b55a05db59c1256a01c8fa07b4c88b8a35f0516 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1530,6 +1530,12 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_ } fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) noreturn { + // For backends that cannot handle the language features used by this segfault handler, we have a simpler one, + switch (builtin.zig_backend) { + .stage2_x86_64 => if (builtin.target.ofmt == .coff) @trap(), + else => {}, + } + comptime assert(windows.CONTEXT != void); nosuspend switch (panic_stage) { 0 => { diff --git a/src/Air.zig b/src/Air.zig index 86fc948fd83ef6a0a1ed59071e37abd984427561..6109e93aeee9559b62c326a51df5df4c62cf6aec 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -849,6 +849,17 @@ pub const Inst = struct { /// Uses the `vector_store_elem` field. vector_store_elem, + /// Compute a pointer to a threadlocal or dllimport `Nav`, meaning one of: + /// + /// * `threadlocal var` + /// * `extern threadlocal var` (or corresponding `@extern`) + /// * `@extern` with `.is_dll_import = true` + /// + /// Such pointers are runtime values, so cannot be represented with an InternPool index. + /// + /// Uses the `ty_nav` field. + tlv_dllimport_ptr, + /// Implements @cVaArg builtin. /// Uses the `ty_op` field. c_va_arg, @@ -1150,6 +1161,10 @@ pub const Inst = struct { // Index into a different array. payload: u32, }, + ty_nav: struct { + ty: InternPool.Index, + nav: InternPool.Nav.Index, + }, inferred_alloc_comptime: InferredAllocComptime, inferred_alloc: InferredAlloc, @@ -1604,6 +1619,8 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) return Type.fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type); }, + .tlv_dllimport_ptr => return .fromInterned(datas[@intFromEnum(inst)].ty_nav.ty), + .work_item_id, .work_group_size, .work_group_id, @@ -1876,6 +1893,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .err_return_trace, .addrspace_cast, .save_err_return_trace_index, + .tlv_dllimport_ptr, .work_item_id, .work_group_size, .work_group_id, diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig index bb3cc4c19fcb2220705a97c08d15d8a15855471f..fcc2667a29d6c8b724405a8b426e5d2b958c17f4 100644 --- a/src/Air/types_resolved.zig +++ b/src/Air/types_resolved.zig @@ -311,6 +311,10 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { if (!checkRef(bin.rhs, zcu)) return false; }, + .tlv_dllimport_ptr => { + if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false; + }, + .select, .mul_add, => { diff --git a/src/InternPool.zig b/src/InternPool.zig index f72244a92999cf97f9d300f74a15071ea1600021..7498f51fa4b2de09de5527eceb2bff583432ac8a 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -12036,30 +12036,6 @@ pub fn isVariable(ip: *const InternPool, val: Index) bool { return val.unwrap(ip).getTag(ip) == .variable; } -pub fn getBackingNav(ip: *const InternPool, val: Index) Nav.Index.Optional { - var base = val; - while (true) { - const unwrapped_base = base.unwrap(ip); - const base_item = unwrapped_base.getItem(ip); - switch (base_item.tag) { - .ptr_nav => return @enumFromInt(unwrapped_base.getExtra(ip).view().items(.@"0")[ - base_item.data + std.meta.fieldIndex(PtrNav, "nav").? - ]), - inline .ptr_eu_payload, - .ptr_opt_payload, - .ptr_elem, - .ptr_field, - => |tag| base = @enumFromInt(unwrapped_base.getExtra(ip).view().items(.@"0")[ - base_item.data + std.meta.fieldIndex(tag.Payload(), "base").? - ]), - .ptr_slice => base = @enumFromInt(unwrapped_base.getExtra(ip).view().items(.@"0")[ - base_item.data + std.meta.fieldIndex(PtrSlice, "ptr").? - ]), - else => return .none, - } - } -} - pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.BaseAddr.Tag { var base = val; while (true) { diff --git a/src/Liveness.zig b/src/Liveness.zig index 87b63d93d51b7921eac4be8d97af8635cef0c108..9c47e12679963676f575a66e31c360d43ee7e762 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -334,6 +334,7 @@ pub fn categorizeOperand( .wasm_memory_size, .err_return_trace, .save_err_return_trace_index, + .tlv_dllimport_ptr, .c_va_start, .work_item_id, .work_group_size, @@ -960,6 +961,7 @@ fn analyzeInst( .wasm_memory_size, .err_return_trace, .save_err_return_trace_index, + .tlv_dllimport_ptr, .c_va_start, .work_item_id, .work_group_size, diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index 449dbb63fb44b13bfdfd83dc0cf180772c732996..1d61145b3c542646a6707612be9d058578987622 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -62,6 +62,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .wasm_memory_size, .err_return_trace, .save_err_return_trace_index, + .tlv_dllimport_ptr, .c_va_start, .work_item_id, .work_group_size, diff --git a/src/Sema.zig b/src/Sema.zig index e7f47a1791bfbf65d3eef77c1cc6aaa4ca109953..46dc8cf857d5b572a519f5a514dda5bca7d9a9bc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2223,10 +2223,7 @@ fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { if (inst.toInterned()) |ip_index| { const val: Value = .fromInterned(ip_index); - assert(val.getVariable(zcu) == null); - if (val.isPtrRuntimeValue(zcu)) return null; - return val; } else { // Runtime-known value. @@ -2295,31 +2292,12 @@ pub fn resolveFinalDeclValue( air_ref: Air.Inst.Ref, ) CompileError!Value { const zcu = sema.pt.zcu; - - const val = try sema.resolveValue(air_ref) orelse { - const is_runtime_ptr = rt_ptr: { - const ip_index = air_ref.toInterned() orelse break :rt_ptr false; - const val: Value = .fromInterned(ip_index); - break :rt_ptr val.isPtrRuntimeValue(zcu); - }; - - switch (sema.failWithNeededComptime(block, src, .{ .simple = .container_var_init })) { - error.AnalysisFail => |e| { - if (sema.err != null and is_runtime_ptr) { - try sema.errNote(src, sema.err.?, "threadlocal and dll imported variables have runtime-known addresses", .{}); - } - return e; - }, - else => |e| return e, - } - }; - + const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init }); if (val.canMutateComptimeVarState(zcu)) { const ip = &zcu.intern_pool; const nav = ip.getNav(sema.owner.unwrap().nav_val); return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val); } - return val; } @@ -26506,6 +26484,7 @@ fn zirBuiltinExtern( const zcu = pt.zcu; const ip = &zcu.intern_pool; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; + const src = block.nodeOffset(extra.node); const ty_src = block.builtinCallArgSrc(extra.node, 0); const options_src = block.builtinCallArgSrc(extra.node, 1); @@ -26560,17 +26539,15 @@ fn zirBuiltinExtern( }, .owner_nav = undefined, // ignored by `getExtern` }); - const extern_nav = ip.indexToKey(extern_val).@"extern".owner_nav; - return Air.internedToRef((try pt.getCoerced(Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = switch (ip.indexToKey(ty.toIntern())) { - .ptr_type => ty.toIntern(), - .opt_type => |child_type| child_type, - else => unreachable, - }, - .base_addr = .{ .nav = extern_nav }, - .byte_offset = 0, - } })), ty)).toIntern()); + const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav); + // We want to cast to `ty`, but that isn't necessarily an allowed coercion. + if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| { + const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty); + return Air.internedToRef(casted_ptr_val.toIntern()); + } else { + return block.addBitCast(ty, uncasted_ptr); + } } fn zirWorkItem( @@ -32037,7 +32014,20 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde break :nav orig_nav_index; }; - const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_index).status) { + const nav_status = ip.getNav(nav_index).status; + + const is_tlv_or_dllimport = switch (nav_status) { + .unresolved => unreachable, + // dllimports go straight to `fully_resolved`; the only option is threadlocal + .type_resolved => |r| r.is_threadlocal, + .fully_resolved => |r| switch (ip.indexToKey(r.val)) { + .@"extern" => |e| e.is_threadlocal or e.is_dll_import, + .variable => |v| v.is_threadlocal, + else => false, + }, + }; + + const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) { .unresolved => unreachable, .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) }, @@ -32050,9 +32040,22 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde .address_space = @"addrspace", }, }); + + if (is_tlv_or_dllimport) { + // This pointer is runtime-known; we need to emit an AIR instruction to create it. + return block.addInst(.{ + .tag = .tlv_dllimport_ptr, + .data = .{ .ty_nav = .{ + .ty = ptr_ty.toIntern(), + .nav = nav_index, + } }, + }); + } + if (is_ref) { try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index); } + return Air.internedToRef((try pt.intern(.{ .ptr = .{ .ty = ptr_ty.toIntern(), .base_addr = .{ .nav = nav_index }, diff --git a/src/Value.zig b/src/Value.zig index 6d041bc5d2d599937b86f20520877f09b527a1c2..55b047f7caaed76961b4332c849a414cd81fb4f1 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -1325,21 +1325,6 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool { }; } -pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool { - const ip = &zcu.intern_pool; - const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false; - const nav_val = switch (ip.getNav(nav).status) { - .unresolved => unreachable, - .type_resolved => |r| return r.is_threadlocal, - .fully_resolved => |r| r.val, - }; - return switch (ip.indexToKey(nav_val)) { - .@"extern" => |e| e.is_threadlocal or e.is_dll_import, - .variable => |v| v.is_threadlocal, - else => false, - }; -} - // Asserts that the provided start/end are in-bounds. pub fn sliceArray( val: Value, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 741199159fe1a568106881d6211ac039e23326f0..5c5ccfa6ae37c2c1e1c19e5b5dbe2ebcb34c1a94 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -876,6 +876,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), + .tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), @@ -6168,7 +6169,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .memory => |addr| .{ .memory = addr }, .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } }, .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, - .load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO + .load_symbol, .lea_symbol, .lea_direct => unreachable, // TODO }, .fail => |msg| return self.failMsg(msg), }; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 66c9a5ba09d3440f61f7e40d6806528277301ef6..c665e89a183b55d0eef5ffa42def529a4df8de82 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -865,6 +865,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), + .tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), @@ -6135,7 +6136,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO .immediate => |imm| .{ .immediate = @truncate(imm) }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 226781619548110db2535e4a86302465b1f69ac2..0d5a9103db4f978965381838f1f3e494a7ba4a4a 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -162,12 +162,8 @@ const MCValue = union(enum) { immediate: u64, /// The value doesn't exist in memory yet. load_symbol: SymbolOffset, - /// A TLV value. - load_tlv: u32, /// The address of the memory location not-yet-allocated by the linker. lea_symbol: SymbolOffset, - /// The address of a TLV value. - lea_tlv: u32, /// The value is in a target-specific register. register: Register, /// The value is split across two registers @@ -224,7 +220,6 @@ const MCValue = union(enum) { .lea_frame, .undef, .lea_symbol, - .lea_tlv, .air_ref, .reserved_frame, => false, @@ -233,7 +228,6 @@ const MCValue = union(enum) { .register_pair, .register_offset, .load_symbol, - .load_tlv, .indirect, => true, @@ -254,12 +248,10 @@ const MCValue = union(enum) { .undef, .air_ref, .lea_symbol, - .lea_tlv, .reserved_frame, => unreachable, // not in memory .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, - .load_tlv => |sym| .{ .lea_tlv = sym }, .memory => |addr| .{ .immediate = addr }, .load_frame => |off| .{ .lea_frame = off }, .indirect => |reg_off| switch (reg_off.off) { @@ -281,7 +273,6 @@ const MCValue = union(enum) { .register_pair, .load_frame, .load_symbol, - .load_tlv, .reserved_frame, => unreachable, // not a pointer @@ -290,7 +281,6 @@ const MCValue = union(enum) { .register_offset => |reg_off| .{ .indirect = reg_off }, .lea_frame => |off| .{ .load_frame = off }, .lea_symbol => |sym_off| .{ .load_symbol = sym_off }, - .lea_tlv => |sym| .{ .load_tlv = sym }, }; } @@ -308,8 +298,6 @@ const MCValue = union(enum) { .indirect, .load_symbol, .lea_symbol, - .lea_tlv, - .load_tlv, => switch (off) { 0 => mcv, else => unreachable, @@ -367,8 +355,6 @@ const InstTracking = struct { .memory, .load_frame, .lea_frame, - .load_tlv, - .lea_tlv, .load_symbol, .lea_symbol, => result, @@ -424,8 +410,6 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, - .load_tlv, - .lea_tlv, => inst_tracking.long, .dead, .register, @@ -454,8 +438,6 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, - .load_tlv, - .lea_tlv, => assert(std.meta.eql(inst_tracking.long, target.long)), .load_frame, .reserved_frame, @@ -1665,6 +1647,8 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .wrap_errunion_payload => try func.airWrapErrUnionPayload(inst), .wrap_errunion_err => try func.airWrapErrUnionErr(inst), + .tlv_dllimport_ptr => try func.airTlvDllimportPtr(inst), + .add_optimized, .sub_optimized, .mul_optimized, @@ -3620,6 +3604,50 @@ fn airWrapErrUnionErr(func: *Func, inst: Air.Inst.Index) !void { return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airTlvDllimportPtr(func: *Func, inst: Air.Inst.Index) !void { + const zcu = func.pt.zcu; + const ip = &zcu.intern_pool; + const ty_nav = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; + const ptr_ty: Type = .fromInterned(ty_nav.ty); + + const nav = ip.getNav(ty_nav.nav); + const tlv_sym_index = if (func.bin_file.cast(.elf)) |elf_file| sym: { + const zo = elf_file.zigObjectPtr().?; + if (nav.getExtern(ip)) |e| { + const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); + zo.symbol(sym).flags.is_extern_ptr = true; + break :sym sym; + } + break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); + } else return func.fail("TODO tlv_dllimport_ptr on {}", .{func.bin_file.tag}); + + const dest_mcv = try func.allocRegOrMem(ptr_ty, inst, true); + if (dest_mcv.isRegister()) { + _ = try func.addInst(.{ + .tag = .pseudo_load_tlv, + .data = .{ .reloc = .{ + .register = dest_mcv.getReg().?, + .atom_index = try func.owner.getSymbolIndex(func), + .sym_index = tlv_sym_index, + } }, + }); + } else { + const tmp_reg, const tmp_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(tmp_lock); + _ = try func.addInst(.{ + .tag = .pseudo_load_tlv, + .data = .{ .reloc = .{ + .register = tmp_reg, + .atom_index = try func.owner.getSymbolIndex(func), + .sym_index = tlv_sym_index, + } }, + }); + try func.genCopy(ptr_ty, dest_mcv, .{ .register = tmp_reg }); + } + + return func.finishAir(inst, dest_mcv, .{ .none, .none, .none }); +} + fn airTry(func: *Func, inst: Air.Inst.Index) !void { const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = func.air.extraData(Air.Try, pl_op.payload); @@ -4494,14 +4522,12 @@ fn load(func: *Func, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro .register_offset, .lea_frame, .lea_symbol, - .lea_tlv, => try func.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), .memory, .indirect, .load_symbol, .load_frame, - .load_tlv, => { const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg); @@ -4548,14 +4574,12 @@ fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type) !void { .register_offset, .lea_symbol, .lea_frame, - .lea_tlv, => try func.genCopy(src_ty, ptr_mcv.deref(), src_mcv), .memory, .indirect, .load_symbol, .load_frame, - .load_tlv, => { const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg); @@ -6544,7 +6568,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { ty, src_mcv, ), - .load_symbol, .load_tlv => { + .load_symbol => { const addr_reg, const addr_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(addr_lock); @@ -7072,25 +7096,6 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! try func.genSetReg(ty, addr_reg, src_mcv.address()); try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); }, - .lea_tlv => |sym| { - const atom_index = try func.owner.getSymbolIndex(func); - - _ = try func.addInst(.{ - .tag = .pseudo_load_tlv, - .data = .{ .reloc = .{ - .register = reg, - .atom_index = atom_index, - .sym_index = sym, - } }, - }); - }, - .load_tlv => { - const addr_reg, const addr_lock = try func.allocReg(.int); - defer func.register_manager.unlockReg(addr_lock); - - try func.genSetReg(ty, addr_reg, src_mcv.address()); - try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); - }, .air_ref => |ref| try func.genSetReg(ty, reg, try func.resolveInst(ref)), else => return func.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), } @@ -7256,7 +7261,6 @@ fn genSetMem( return func.genSetMem(base, disp, ty, .{ .register = reg }); }, .air_ref => |src_ref| try func.genSetMem(base, disp, ty, try func.resolveInst(src_ref)), - else => return func.fail("TODO: genSetMem {s}", .{@tagName(src_mcv)}), } } @@ -8190,7 +8194,6 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { .undef => unreachable, .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } }, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, - .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, .load_got, .load_direct, .lea_direct => { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 33dc2d5e40f96a86b0d8666b7cc6db48674f37bd..4668d2acd4eb6a5995d6000a6d33a8a95f022c4d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -719,6 +719,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_named_enum_value => @panic("TODO implement is_named_enum_value"), .error_set_has_value => @panic("TODO implement error_set_has_value"), .vector_store_elem => @panic("TODO implement vector_store_elem"), + .tlv_dllimport_ptr => @panic("TODO implement tlv_dllimport_ptr"), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), @@ -4088,7 +4089,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 56bbb4697408fc9b28d3fe1f16be68026fb9106c..c3be73bbca07725e5ab78b12552bd600fa9badee 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2050,6 +2050,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .error_set_has_value => cg.airErrorSetHasValue(inst), .frame_addr => cg.airFrameAddress(inst), + .tlv_dllimport_ptr => cg.airTlvDllimportPtr(inst), + .assembly, .is_err_ptr, .is_non_err_ptr, @@ -7551,6 +7553,19 @@ fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { return cg.finishAir(inst, .stack, &.{}); } +fn airTlvDllimportPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const ty_nav = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; + const mod = cg.pt.zcu.navFileScope(cg.owner_nav).mod.?; + if (mod.single_threaded) { + const result: WValue = .{ .nav_ref = .{ + .nav_index = ty_nav.nav, + .offset = 0, + } }; + return cg.finishAir(inst, result, &.{}); + } + return cg.fail("TODO: thread-local variables", .{}); +} + fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type { const zcu = cg.pt.zcu; return cg.air.typeOf(inst, &zcu.intern_pool); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 36c102cb5da3a8a611d46745a4f68f4d21893bd4..93bf26241d3efc9e7f9d3fcad7022af34d38b245 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -204,12 +204,6 @@ pub const MCValue = union(enum) { /// The value is a pointer to a value referenced indirectly via GOT. /// Payload is a symbol index. lea_got: u32, - /// The value is a threadlocal variable. - /// Payload is a symbol index. - load_tlv: u32, - /// The value is a pointer to a threadlocal variable. - /// Payload is a symbol index. - lea_tlv: u32, /// The value stored at an offset from a frame index /// Payload is a frame address. load_frame: bits.FrameAddr, @@ -238,7 +232,6 @@ pub const MCValue = union(enum) { .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -252,7 +245,6 @@ pub const MCValue = union(enum) { .load_symbol, .load_got, .load_direct, - .load_tlv, .indirect, => true, .load_frame => |frame_addr| !frame_addr.index.isNamed(), @@ -355,7 +347,6 @@ pub const MCValue = union(enum) { .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -368,7 +359,6 @@ pub const MCValue = union(enum) { }, .load_direct => |sym_index| .{ .lea_direct = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index }, - .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .load_frame => |frame_addr| .{ .lea_frame = frame_addr }, .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, }; @@ -390,7 +380,6 @@ pub const MCValue = union(enum) { .indirect, .load_direct, .load_got, - .load_tlv, .load_frame, .load_symbol, .elementwise_regs_then_frame, @@ -402,7 +391,6 @@ pub const MCValue = union(enum) { .register_offset => |reg_off| .{ .indirect = reg_off }, .lea_direct => |sym_index| .{ .load_direct = sym_index }, .lea_got => |sym_index| .{ .load_got = sym_index }, - .lea_tlv => |sym_index| .{ .load_tlv = sym_index }, .lea_frame => |frame_addr| .{ .load_frame = frame_addr }, .lea_symbol => |sym_index| .{ .load_symbol = sym_index }, }; @@ -430,8 +418,6 @@ pub const MCValue = union(enum) { .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .load_frame, .load_symbol, .lea_symbol, @@ -469,8 +455,6 @@ pub const MCValue = union(enum) { .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -555,8 +539,6 @@ pub const MCValue = union(enum) { .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), .load_got => |pl| try writer.print("[got:{d}]", .{pl}), .lea_got => |pl| try writer.print("got:{d}", .{pl}), - .load_tlv => |pl| try writer.print("[tlv:{d}]", .{pl}), - .lea_tlv => |pl| try writer.print("tlv:{d}", .{pl}), .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }), .elementwise_regs_then_frame => |pl| try writer.print("elementwise:{d}:[{} + 0x{x}]", .{ pl.regs, pl.frame_index, pl.frame_off, @@ -585,8 +567,6 @@ const InstTracking = struct { .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .load_frame, .lea_frame, .load_symbol, @@ -688,8 +668,6 @@ const InstTracking = struct { .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .lea_frame, .load_symbol, .lea_symbol, @@ -120945,6 +120923,47 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }; for (ops) |op| try op.die(cg); }, + .tlv_dllimport_ptr => switch (cg.bin_file.tag) { + .elf, .macho => { + const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; + + const nav = ip.getNav(ty_nav.nav); + const tlv_sym_index = sym: { + if (cg.bin_file.cast(.elf)) |elf_file| { + const zo = elf_file.zigObjectPtr().?; + if (nav.getExtern(ip)) |e| { + const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); + zo.symbol(sym).flags.is_extern_ptr = true; + break :sym sym; + } + break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); + } + if (cg.bin_file.cast(.macho)) |macho_file| { + const zo = macho_file.getZigObject().?; + if (nav.getExtern(ip)) |e| { + const sym = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); + zo.symbols.items[sym].flags.is_extern_ptr = true; + break :sym sym; + } + break :sym try zo.getOrCreateMetadataForNav(macho_file, ty_nav.nav); + } + unreachable; + }; + + if (cg.mod.pic) { + try cg.spillRegisters(&.{ .rdi, .rax }); + } else { + try cg.spillRegisters(&.{.rax}); + } + + var slot = try cg.tempInit(.usize, .{ .lea_symbol = .{ + .sym_index = tlv_sym_index, + } }); + while (try slot.toRegClass(true, .general_purpose, cg)) {} + try slot.finish(inst, &.{}, &.{}, cg); + }, + else => return cg.fail("TODO implement tlv/dllimport on {}", .{cg.bin_file.tag}), + }, .c_va_arg => try cg.airVaArg(inst), .c_va_copy => try cg.airVaCopy(inst), .c_va_end => try cg.airVaEnd(inst), @@ -124664,7 +124683,7 @@ fn airArrayElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { }.to64(), ), }, - .memory, .load_symbol, .load_direct, .load_got, .load_tlv => switch (index_mcv) { + .memory, .load_symbol, .load_direct, .load_got => switch (index_mcv) { .immediate => |index_imm| try self.asmMemoryImmediate( .{ ._, .bt }, .{ @@ -124729,9 +124748,8 @@ fn airArrayElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { .load_symbol, .load_direct, .load_got, - .load_tlv, => try self.genSetReg(addr_reg, .usize, array_mcv.address(), .{}), - .lea_symbol, .lea_direct, .lea_tlv => unreachable, + .lea_symbol, .lea_direct => unreachable, else => return self.fail("TODO airArrayElemVal_val for {s} of {}", .{ @tagName(array_mcv), array_ty.fmt(pt), }), @@ -126611,7 +126629,6 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref(), .{}), .memory, @@ -126619,7 +126636,6 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE .load_symbol, .load_direct, .load_got, - .load_tlv, .load_frame, => { const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); @@ -126831,7 +126847,6 @@ fn store( .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv, opts), .memory, @@ -126839,7 +126854,6 @@ fn store( .load_symbol, .load_direct, .load_got, - .load_tlv, .load_frame, => { const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); @@ -127309,7 +127323,6 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -127317,7 +127330,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: => unreachable, // unmodifiable destination .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented - .memory, .load_symbol, .load_got, .load_direct, .load_tlv => { + .memory, .load_symbol, .load_got, .load_direct => { const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -128922,8 +128935,6 @@ fn genBinOp( .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .lea_frame, => true, .memory => |addr| std.math.cast(i32, @as(i64, @bitCast(addr))) == null, @@ -128983,8 +128994,6 @@ fn genBinOp( .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -130167,7 +130176,6 @@ fn genBinOpMir( .register_mask, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .lea_symbol, .elementwise_regs_then_frame, @@ -130265,8 +130273,6 @@ fn genBinOpMir( .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .load_frame, .lea_frame, => { @@ -130304,7 +130310,6 @@ fn genBinOpMir( .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, => { assert(off == 0); @@ -130320,7 +130325,6 @@ fn genBinOpMir( .load_symbol, .load_direct, .load_got, - .load_tlv, => { const ptr_ty = try pt.singleConstPtrType(ty); const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address()); @@ -130340,13 +130344,13 @@ fn genBinOpMir( } } }, - .memory, .indirect, .load_symbol, .load_got, .load_direct, .load_tlv, .load_frame => { + .memory, .indirect, .load_symbol, .load_got, .load_direct, .load_frame => { const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; const limb_abi_size: u32 = @min(abi_size, 8); const dst_info: OpInfo = switch (dst_mcv) { else => unreachable, - .memory, .load_symbol, .load_got, .load_direct, .load_tlv => dst: { + .memory, .load_symbol, .load_got, .load_direct => dst: { const dst_addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); @@ -130384,17 +130388,16 @@ fn genBinOpMir( .indirect, .lea_direct, .lea_got, - .lea_tlv, .load_frame, .lea_frame, .lea_symbol, => null, - .memory, .load_symbol, .load_got, .load_direct, .load_tlv => src: { + .memory, .load_symbol, .load_got, .load_direct => src: { switch (resolved_src_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr))) != null and std.math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null) break :src null, - .load_symbol, .load_got, .load_direct, .load_tlv => {}, + .load_symbol, .load_got, .load_direct => {}, else => unreachable, } @@ -130437,7 +130440,6 @@ fn genBinOpMir( .load_symbol, .load_got, .load_direct, - .load_tlv, => .{ .base = .{ .reg = dst_info.?.addr_reg }, .mod = .{ .rm = .{ @@ -130533,8 +130535,6 @@ fn genBinOpMir( .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .load_frame, .lea_frame, => { @@ -130549,7 +130549,6 @@ fn genBinOpMir( .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, => switch (limb_i) { 0 => resolved_src_mcv, @@ -130601,7 +130600,6 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -130666,8 +130664,6 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .lea_frame, => { const src_reg = try self.copyToTmpRegister(dst_ty, resolved_src_mcv); @@ -130723,7 +130719,7 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv } }, .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented - .memory, .indirect, .load_symbol, .load_direct, .load_got, .load_tlv, .load_frame => { + .memory, .indirect, .load_symbol, .load_direct, .load_got, .load_frame => { const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); const tmp_mcv = MCValue{ .register = tmp_reg }; const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); @@ -130899,7 +130895,7 @@ fn genLocalDebugInfo( .disp = sym_off.off, } }, }), - .lea_direct, .lea_got, .lea_tlv => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{ + .lea_direct, .lea_got => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{ .base = .{ .reloc = sym_index }, .mod = .{ .rm = .{ .size = .qword } }, }), @@ -131548,7 +131544,6 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .indirect, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .lea_symbol, .elementwise_regs_then_frame, @@ -131556,7 +131551,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .air_ref, => unreachable, .register, .register_pair, .register_triple, .register_quadruple, .load_frame => null, - .memory, .load_symbol, .load_got, .load_direct, .load_tlv => dst: { + .memory, .load_symbol, .load_got, .load_direct => dst: { switch (resolved_dst_mcv) { .memory => |addr| if (std.math.cast( i32, @@ -131565,7 +131560,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :dst null, - .load_symbol, .load_got, .load_direct, .load_tlv => {}, + .load_symbol, .load_got, .load_direct => {}, else => unreachable, } @@ -131605,14 +131600,13 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .lea_symbol, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, .air_ref, => unreachable, .register_pair, .register_triple, .register_quadruple, .load_frame => null, - .memory, .load_symbol, .load_got, .load_direct, .load_tlv => src: { + .memory, .load_symbol, .load_got, .load_direct => src: { switch (resolved_src_mcv) { .memory => |addr| if (std.math.cast( i32, @@ -131621,7 +131615,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :src null, - .load_symbol, .load_got, .load_direct, .load_tlv => {}, + .load_symbol, .load_got, .load_direct => {}, else => unreachable, } @@ -132011,7 +132005,6 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) .register_mask, .lea_direct, .lea_got, - .lea_tlv, .lea_symbol, .elementwise_regs_then_frame, .reserved_frame, @@ -132063,7 +132056,6 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) .load_symbol, .load_got, .load_direct, - .load_tlv, => { const addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); @@ -133105,7 +133097,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| break :arg input_mcv, .indirect, .load_frame => break :arg input_mcv, - .load_symbol, .load_direct, .load_got, .load_tlv => {}, + .load_symbol, .load_direct, .load_got => {}, else => { const temp_mcv = try self.allocTempRegOrMem(ty, false); try self.genCopy(ty, temp_mcv, input_mcv, .{}); @@ -134075,7 +134067,6 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C .register_mask, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, .lea_symbol, .elementwise_regs_then_frame, @@ -134159,7 +134150,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C } return; }, - .load_symbol, .load_direct, .load_got, .load_tlv => { + .load_symbol, .load_direct, .load_got => { const src_addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); @@ -134192,7 +134183,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C .undef => if (opts.safety and part_i > 0) .{ .register = dst_regs[0] } else .undef, dst_tag => |src_regs| .{ .register = src_regs[part_i] }, .memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(), - .load_symbol, .load_direct, .load_got, .load_tlv => .{ .indirect = .{ + .load_symbol, .load_direct, .load_got => .{ .indirect = .{ .reg = src_info.?.addr_reg, .off = part_disp, } }, @@ -134213,11 +134204,11 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C src_mcv, opts, ), - .memory, .load_symbol, .load_direct, .load_got, .load_tlv => { + .memory, .load_symbol, .load_direct, .load_got => { switch (dst_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv, opts), - .load_symbol, .load_direct, .load_got, .load_tlv => {}, + .load_symbol, .load_direct, .load_got => {}, else => unreachable, } @@ -134639,7 +134630,7 @@ fn genSetReg( if (src_reg_mask.info.inverted) try self.asmRegister(.{ ._, .not }, registerAlias(bits_reg, abi_size)); try self.genSetReg(dst_reg, ty, .{ .register = bits_reg }, .{}); }, - .memory, .load_symbol, .load_direct, .load_got, .load_tlv => { + .memory, .load_symbol, .load_direct, .load_got => { switch (src_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return (try self.moveStrategy( @@ -134683,7 +134674,7 @@ fn genSetReg( .segment, .mmx, .ip, .cr, .dr => unreachable, .x87, .sse => {}, }, - .load_got, .load_tlv => {}, + .load_got => {}, else => unreachable, } @@ -134734,7 +134725,6 @@ fn genSetReg( .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), } }, }), - .lea_tlv => unreachable, // TODO: remove this .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref), opts), } } @@ -134952,8 +134942,6 @@ fn genSetMem( .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .load_frame, .lea_frame, .load_symbol, @@ -138121,11 +138109,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size); const elem_bit_off = elem_off % elem_abi_bits; const elem_mcv = try self.resolveInst(elem); - const mat_elem_mcv = switch (elem_mcv) { - .load_tlv => |sym_index| MCValue{ .lea_tlv = sym_index }, - else => elem_mcv, - }; - const elem_lock = switch (mat_elem_mcv) { + const elem_lock = switch (elem_mcv) { .register => |reg| self.register_manager.lockReg(reg), .immediate => |imm| lock: { if (imm == 0) continue; @@ -138137,7 +138121,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { const elem_extra_bits = self.regExtraBits(elem_ty); { - const temp_reg = try self.copyToTmpRegister(elem_ty, mat_elem_mcv); + const temp_reg = try self.copyToTmpRegister(elem_ty, elem_mcv); const temp_alias = registerAlias(temp_reg, elem_abi_size); const temp_lock = self.register_manager.lockRegAssumeUnused(temp_reg); defer self.register_manager.unlockReg(temp_lock); @@ -138160,7 +138144,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { ); } if (elem_bit_off > elem_extra_bits) { - const temp_reg = try self.copyToTmpRegister(elem_ty, mat_elem_mcv); + const temp_reg = try self.copyToTmpRegister(elem_ty, elem_mcv); const temp_alias = registerAlias(temp_reg, elem_abi_size); const temp_lock = self.register_manager.lockRegAssumeUnused(temp_reg); defer self.register_manager.unlockReg(temp_lock); @@ -138192,11 +138176,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { const elem_ty = result_ty.fieldType(elem_i, zcu); const elem_off: i32 = @intCast(result_ty.structFieldOffset(elem_i, zcu)); const elem_mcv = try self.resolveInst(elem); - const mat_elem_mcv = switch (elem_mcv) { - .load_tlv => |sym_index| MCValue{ .lea_tlv = sym_index }, - else => elem_mcv, - }; - try self.genSetMem(.{ .frame = frame_index }, elem_off, elem_ty, mat_elem_mcv, .{}); + try self.genSetMem(.{ .frame = frame_index }, elem_off, elem_ty, elem_mcv, .{}); } break :result .{ .load_frame = .{ .index = frame_index } }; }, @@ -138239,16 +138219,12 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { for (elements, 0..) |elem, elem_i| { const elem_mcv = try self.resolveInst(elem); - const mat_elem_mcv = switch (elem_mcv) { - .load_tlv => |sym_index| MCValue{ .lea_tlv = sym_index }, - else => elem_mcv, - }; const elem_off: i32 = @intCast(elem_size * elem_i); try self.genSetMem( .{ .frame = frame_index }, elem_off, elem_ty, - mat_elem_mcv, + elem_mcv, .{}, ); } @@ -138744,32 +138720,7 @@ fn resolveInst(self: *CodeGen, ref: Air.Inst.Ref) InnerError!MCValue { const mcv: MCValue = if (ref.toIndex()) |inst| mcv: { break :mcv self.inst_tracking.getPtr(inst).?.short; } else mcv: { - const const_mcv = try self.genTypedValue(.fromInterned(ref.toInterned().?)); - switch (const_mcv) { - .lea_tlv => |tlv_sym| switch (self.bin_file.tag) { - .elf, .macho => { - if (self.mod.pic) { - try self.spillRegisters(&.{ .rdi, .rax }); - } else { - try self.spillRegisters(&.{.rax}); - } - const frame_index = try self.allocFrameIndex(.init(.{ - .size = 8, - .alignment = .@"8", - })); - try self.genSetMem( - .{ .frame = frame_index }, - 0, - .usize, - .{ .lea_symbol = .{ .sym_index = tlv_sym } }, - .{}, - ); - break :mcv .{ .load_frame = .{ .index = frame_index } }; - }, - else => break :mcv const_mcv, - }, - else => break :mcv const_mcv, - } + break :mcv try self.genTypedValue(.fromInterned(ref.toInterned().?)); }; switch (mcv) { @@ -138819,7 +138770,6 @@ fn genResult(self: *CodeGen, res: codegen.GenResult) InnerError!MCValue { .load_direct => |sym_index| .{ .load_direct = sym_index }, .lea_direct => |sym_index| .{ .lea_direct = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index }, - .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, }, .fail => |msg| return self.failMsg(msg), }; @@ -139686,8 +139636,6 @@ const Temp = struct { .lea_direct, .load_got, .lea_got, - .load_tlv, - .lea_tlv, .lea_frame, .elementwise_regs_then_frame, .reserved_frame, @@ -140133,7 +140081,6 @@ const Temp = struct { .register_offset, .lea_direct, .lea_got, - .lea_tlv, .lea_frame, => return false, .memory, @@ -140141,7 +140088,6 @@ const Temp = struct { .load_symbol, .load_direct, .load_got, - .load_tlv, .load_frame, => return temp.toRegClass(true, .general_purpose, cg), .lea_symbol => |sym_off| { @@ -145048,34 +144994,7 @@ fn tempFromOperand(cg: *CodeGen, op_ref: Air.Inst.Ref, op_dies: bool) InnerError if (op_ref.toIndex()) |op_inst| return .{ .index = op_inst }; const val = op_ref.toInterned().?; - return cg.tempInit(.fromInterned(ip.typeOf(val)), init: { - const const_mcv = try cg.genTypedValue(.fromInterned(val)); - switch (const_mcv) { - .lea_tlv => |tlv_sym| switch (cg.bin_file.tag) { - .elf, .macho => { - if (cg.mod.pic) { - try cg.spillRegisters(&.{ .rdi, .rax }); - } else { - try cg.spillRegisters(&.{.rax}); - } - const frame_index = try cg.allocFrameIndex(.init(.{ - .size = 8, - .alignment = .@"8", - })); - try cg.genSetMem( - .{ .frame = frame_index }, - 0, - .usize, - .{ .lea_symbol = .{ .sym_index = tlv_sym } }, - .{}, - ); - break :init .{ .load_frame = .{ .index = frame_index } }; - }, - else => break :init const_mcv, - }, - else => break :init const_mcv, - } - }); + return cg.tempInit(.fromInterned(ip.typeOf(val)), try cg.genTypedValue(.fromInterned(val))); } fn tempsFromOperandsInner( diff --git a/src/codegen.zig b/src/codegen.zig index c3be335acc4e7473d73deb6ea12af1959570be7e..6577667c7c38d7e99efb129bb51f97dbe6fe42b5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -818,10 +818,6 @@ pub const GenResult = union(enum) { /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets /// such as ARM, the immediate will never exceed 32-bits. immediate: u64, - /// Threadlocal variable with address deferred until the linker allocates - /// everything in virtual memory. - /// Payload is a symbol index. - load_tlv: u32, /// Decl with address deferred until the linker allocates everything in virtual memory. /// Payload is a symbol index. load_direct: u32, @@ -883,13 +879,13 @@ fn genNavRef( } const nav = ip.getNav(nav_index); + assert(!nav.isThreadlocal(ip)); - const is_extern, const lib_name, const is_threadlocal = if (nav.getExtern(ip)) |e| - .{ true, e.lib_name, e.is_threadlocal } + const is_extern, const lib_name = if (nav.getExtern(ip)) |e| + .{ true, e.lib_name } else - .{ false, .none, nav.isThreadlocal(ip) }; + .{ false, .none }; - const single_threaded = zcu.navFileScope(nav_index).mod.?.single_threaded; const name = nav.name; if (lf.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; @@ -899,9 +895,6 @@ fn genNavRef( return .{ .mcv = .{ .lea_symbol = sym_index } }; } const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); - if (!single_threaded and is_threadlocal) { - return .{ .mcv = .{ .load_tlv = sym_index } }; - } return .{ .mcv = .{ .lea_symbol = sym_index } }; } else if (lf.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; @@ -912,9 +905,6 @@ fn genNavRef( } const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); const sym = zo.symbols.items[sym_index]; - if (!single_threaded and is_threadlocal) { - return .{ .mcv = .{ .load_tlv = sym.nlist_idx } }; - } return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } }; } else if (lf.cast(.coff)) |coff_file| { if (is_extern) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 891b84457ecb0e221d33faa2a70209755ab91a4c..075d9ba303c569bdbcf18cc77449b5253a9d4034 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3453,6 +3453,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), + .tlv_dllimport_ptr => try airTlvDllimportPtr(f, inst), + .c_va_start => try airCVaStart(f, inst), .c_va_arg => try airCVaArg(f, inst), .c_va_end => try airCVaEnd(f, inst), @@ -7617,6 +7619,17 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airTlvDllimportPtr(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; + const writer = f.object.writer(); + const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.object.dg.renderNav(writer, ty_nav.nav, .Other); + try writer.writeAll(";\n"); + return local; +} + fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e6926e8468a71f99286eb09dc0be020a7dd06260..dc1a8edf56cd69dabeea97ea8d28fb95b6de82d8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5015,6 +5015,8 @@ pub const FuncGen = struct { .vector_store_elem => try self.airVectorStoreElem(inst), + .tlv_dllimport_ptr => try self.airTlvDllimportPtr(inst), + .inferred_alloc, .inferred_alloc_comptime => unreachable, .dbg_stmt => try self.airDbgStmt(inst), @@ -8112,6 +8114,13 @@ pub const FuncGen = struct { return .none; } + fn airTlvDllimportPtr(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + const o = fg.ng.object; + const ty_nav = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; + const llvm_ptr_const = try o.lowerNavRefValue(ty_nav.nav); + return llvm_ptr_const.toValue(); + } + fn airMin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.ng.object; const zcu = o.pt.zcu; diff --git a/src/print_air.zig b/src/print_air.zig index 3a5963f7c4c341f14c65d291299d2cb4ecd32fac..3436d915aa417be23749640c1ff0b4c57be55c72 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -320,6 +320,7 @@ const Writer = struct { .reduce, .reduce_optimized => try w.writeReduce(s, inst), .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst), .vector_store_elem => try w.writeVectorStoreElem(s, inst), + .tlv_dllimport_ptr => try w.writeTlvDllimportPtr(s, inst), .work_item_id, .work_group_size, @@ -552,6 +553,13 @@ const Writer = struct { try w.writeOperand(s, inst, 2, extra.rhs); } + fn writeTlvDllimportPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ip = &w.pt.zcu.intern_pool; + const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; + try w.writeType(s, .fromInterned(ty_nav.ty)); + try s.print(", '{}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); + } + fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const atomic_load = w.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; diff --git a/test/behavior/threadlocal.zig b/test/behavior/threadlocal.zig index 9c3904644b35e729b49982f2d535ac1a810ccc53..2d87048653464713c516243beb9e2d4bcf99cac0 100644 --- a/test/behavior/threadlocal.zig +++ b/test/behavior/threadlocal.zig @@ -7,6 +7,8 @@ test "thread local variable" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) { // Fails due to register hazards. @@ -26,6 +28,7 @@ test "pointer to thread local array" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest; // TODO const s = "Hello world"; @memcpy(buffer[0..s.len], s); @@ -40,6 +43,7 @@ test "reference a global threadlocal variable" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest; // TODO _ = nrfx_uart_rx(&g_uart0); } diff --git a/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig b/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig index 904a31cddb5a14f1f50dab6324678a1953d3c89a..309d055280c92cf5d003f409c0f344f40492dbe0 100644 --- a/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig +++ b/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig @@ -6,9 +6,6 @@ pub export fn entry() void { } // error -// backend=stage2 -// target=native // // :2:36: error: unable to resolve comptime value // :2:36: note: initializer of container-level variable must be comptime-known -// :2:36: note: threadlocal and dll imported variables have runtime-known addresses diff --git a/test/cases/compile_errors/builtin_extern_in_comptime_scope.zig b/test/cases/compile_errors/builtin_extern_in_comptime_scope.zig index 66951a03a009a28e94f7c1e255e1074848c1f5a9..cf19075fa2439c353288e61c3c024542919060df 100644 --- a/test/cases/compile_errors/builtin_extern_in_comptime_scope.zig +++ b/test/cases/compile_errors/builtin_extern_in_comptime_scope.zig @@ -7,12 +7,8 @@ pub export fn entry2() void { _ = foo_dll; } // error -// backend=stage2 -// target=native // // :1:16: error: unable to resolve comptime value // :1:16: note: initializer of container-level variable must be comptime-known -// :1:16: note: threadlocal and dll imported variables have runtime-known addresses // :2:17: error: unable to resolve comptime value // :2:17: note: initializer of container-level variable must be comptime-known -// :2:17: note: threadlocal and dll imported variables have runtime-known addresses -- 2.54.0