Skip to content

Commit 92c6312

Browse files
committed
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.
1 parent 3ed9155 commit 92c6312

File tree

21 files changed

+242
-290
lines changed

21 files changed

+242
-290
lines changed

lib/std/debug.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,12 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_
15301530
}
15311531

15321532
fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) noreturn {
1533+
// For backends that cannot handle the language features used by this segfault handler, we have a simpler one,
1534+
switch (builtin.zig_backend) {
1535+
.stage2_x86_64 => if (builtin.target.ofmt == .coff) @trap(),
1536+
else => {},
1537+
}
1538+
15331539
comptime assert(windows.CONTEXT != void);
15341540
nosuspend switch (panic_stage) {
15351541
0 => {

src/Air.zig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,17 @@ pub const Inst = struct {
849849
/// Uses the `vector_store_elem` field.
850850
vector_store_elem,
851851

852+
/// Compute a pointer to a threadlocal or dllimport `Nav`, meaning one of:
853+
///
854+
/// * `threadlocal var`
855+
/// * `extern threadlocal var` (or corresponding `@extern`)
856+
/// * `@extern` with `.is_dll_import = true`
857+
///
858+
/// Such pointers are runtime values, so cannot be represented with an InternPool index.
859+
///
860+
/// Uses the `ty_nav` field.
861+
tlv_dllimport_ptr,
862+
852863
/// Implements @cVaArg builtin.
853864
/// Uses the `ty_op` field.
854865
c_va_arg,
@@ -1150,6 +1161,10 @@ pub const Inst = struct {
11501161
// Index into a different array.
11511162
payload: u32,
11521163
},
1164+
ty_nav: struct {
1165+
ty: InternPool.Index,
1166+
nav: InternPool.Nav.Index,
1167+
},
11531168
inferred_alloc_comptime: InferredAllocComptime,
11541169
inferred_alloc: InferredAlloc,
11551170

@@ -1604,6 +1619,8 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
16041619
return Type.fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type);
16051620
},
16061621

1622+
.tlv_dllimport_ptr => return .fromInterned(datas[@intFromEnum(inst)].ty_nav.ty),
1623+
16071624
.work_item_id,
16081625
.work_group_size,
16091626
.work_group_id,
@@ -1876,6 +1893,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
18761893
.err_return_trace,
18771894
.addrspace_cast,
18781895
.save_err_return_trace_index,
1896+
.tlv_dllimport_ptr,
18791897
.work_item_id,
18801898
.work_group_size,
18811899
.work_group_id,

src/Air/types_resolved.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,10 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
311311
if (!checkRef(bin.rhs, zcu)) return false;
312312
},
313313

314+
.tlv_dllimport_ptr => {
315+
if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false;
316+
},
317+
314318
.select,
315319
.mul_add,
316320
=> {

src/InternPool.zig

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12036,30 +12036,6 @@ pub fn isVariable(ip: *const InternPool, val: Index) bool {
1203612036
return val.unwrap(ip).getTag(ip) == .variable;
1203712037
}
1203812038

12039-
pub fn getBackingNav(ip: *const InternPool, val: Index) Nav.Index.Optional {
12040-
var base = val;
12041-
while (true) {
12042-
const unwrapped_base = base.unwrap(ip);
12043-
const base_item = unwrapped_base.getItem(ip);
12044-
switch (base_item.tag) {
12045-
.ptr_nav => return @enumFromInt(unwrapped_base.getExtra(ip).view().items(.@"0")[
12046-
base_item.data + std.meta.fieldIndex(PtrNav, "nav").?
12047-
]),
12048-
inline .ptr_eu_payload,
12049-
.ptr_opt_payload,
12050-
.ptr_elem,
12051-
.ptr_field,
12052-
=> |tag| base = @enumFromInt(unwrapped_base.getExtra(ip).view().items(.@"0")[
12053-
base_item.data + std.meta.fieldIndex(tag.Payload(), "base").?
12054-
]),
12055-
.ptr_slice => base = @enumFromInt(unwrapped_base.getExtra(ip).view().items(.@"0")[
12056-
base_item.data + std.meta.fieldIndex(PtrSlice, "ptr").?
12057-
]),
12058-
else => return .none,
12059-
}
12060-
}
12061-
}
12062-
1206312039
pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.BaseAddr.Tag {
1206412040
var base = val;
1206512041
while (true) {

src/Liveness.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ pub fn categorizeOperand(
334334
.wasm_memory_size,
335335
.err_return_trace,
336336
.save_err_return_trace_index,
337+
.tlv_dllimport_ptr,
337338
.c_va_start,
338339
.work_item_id,
339340
.work_group_size,
@@ -960,6 +961,7 @@ fn analyzeInst(
960961
.wasm_memory_size,
961962
.err_return_trace,
962963
.save_err_return_trace_index,
964+
.tlv_dllimport_ptr,
963965
.c_va_start,
964966
.work_item_id,
965967
.work_group_size,

src/Liveness/Verify.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
6262
.wasm_memory_size,
6363
.err_return_trace,
6464
.save_err_return_trace_index,
65+
.tlv_dllimport_ptr,
6566
.c_va_start,
6667
.work_item_id,
6768
.work_group_size,

src/Sema.zig

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,10 +2223,7 @@ fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
22232223

22242224
if (inst.toInterned()) |ip_index| {
22252225
const val: Value = .fromInterned(ip_index);
2226-
22272226
assert(val.getVariable(zcu) == null);
2228-
if (val.isPtrRuntimeValue(zcu)) return null;
2229-
22302227
return val;
22312228
} else {
22322229
// Runtime-known value.
@@ -2295,31 +2292,12 @@ pub fn resolveFinalDeclValue(
22952292
air_ref: Air.Inst.Ref,
22962293
) CompileError!Value {
22972294
const zcu = sema.pt.zcu;
2298-
2299-
const val = try sema.resolveValue(air_ref) orelse {
2300-
const is_runtime_ptr = rt_ptr: {
2301-
const ip_index = air_ref.toInterned() orelse break :rt_ptr false;
2302-
const val: Value = .fromInterned(ip_index);
2303-
break :rt_ptr val.isPtrRuntimeValue(zcu);
2304-
};
2305-
2306-
switch (sema.failWithNeededComptime(block, src, .{ .simple = .container_var_init })) {
2307-
error.AnalysisFail => |e| {
2308-
if (sema.err != null and is_runtime_ptr) {
2309-
try sema.errNote(src, sema.err.?, "threadlocal and dll imported variables have runtime-known addresses", .{});
2310-
}
2311-
return e;
2312-
},
2313-
else => |e| return e,
2314-
}
2315-
};
2316-
2295+
const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init });
23172296
if (val.canMutateComptimeVarState(zcu)) {
23182297
const ip = &zcu.intern_pool;
23192298
const nav = ip.getNav(sema.owner.unwrap().nav_val);
23202299
return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val);
23212300
}
2322-
23232301
return val;
23242302
}
23252303

@@ -26506,6 +26484,7 @@ fn zirBuiltinExtern(
2650626484
const zcu = pt.zcu;
2650726485
const ip = &zcu.intern_pool;
2650826486
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
26487+
const src = block.nodeOffset(extra.node);
2650926488
const ty_src = block.builtinCallArgSrc(extra.node, 0);
2651026489
const options_src = block.builtinCallArgSrc(extra.node, 1);
2651126490

@@ -26560,17 +26539,15 @@ fn zirBuiltinExtern(
2656026539
},
2656126540
.owner_nav = undefined, // ignored by `getExtern`
2656226541
});
26563-
const extern_nav = ip.indexToKey(extern_val).@"extern".owner_nav;
2656426542

26565-
return Air.internedToRef((try pt.getCoerced(Value.fromInterned(try pt.intern(.{ .ptr = .{
26566-
.ty = switch (ip.indexToKey(ty.toIntern())) {
26567-
.ptr_type => ty.toIntern(),
26568-
.opt_type => |child_type| child_type,
26569-
else => unreachable,
26570-
},
26571-
.base_addr = .{ .nav = extern_nav },
26572-
.byte_offset = 0,
26573-
} })), ty)).toIntern());
26543+
const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav);
26544+
// We want to cast to `ty`, but that isn't necessarily an allowed coercion.
26545+
if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| {
26546+
const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty);
26547+
return Air.internedToRef(casted_ptr_val.toIntern());
26548+
} else {
26549+
return block.addBitCast(ty, uncasted_ptr);
26550+
}
2657426551
}
2657526552

2657626553
fn zirWorkItem(
@@ -32037,7 +32014,20 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde
3203732014
break :nav orig_nav_index;
3203832015
};
3203932016

32040-
const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_index).status) {
32017+
const nav_status = ip.getNav(nav_index).status;
32018+
32019+
const is_tlv_or_dllimport = switch (nav_status) {
32020+
.unresolved => unreachable,
32021+
// dllimports go straight to `fully_resolved`; the only option is threadlocal
32022+
.type_resolved => |r| r.is_threadlocal,
32023+
.fully_resolved => |r| switch (ip.indexToKey(r.val)) {
32024+
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
32025+
.variable => |v| v.is_threadlocal,
32026+
else => false,
32027+
},
32028+
};
32029+
32030+
const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) {
3204132031
.unresolved => unreachable,
3204232032
.type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
3204332033
.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
3205032040
.address_space = @"addrspace",
3205132041
},
3205232042
});
32043+
32044+
if (is_tlv_or_dllimport) {
32045+
// This pointer is runtime-known; we need to emit an AIR instruction to create it.
32046+
return block.addInst(.{
32047+
.tag = .tlv_dllimport_ptr,
32048+
.data = .{ .ty_nav = .{
32049+
.ty = ptr_ty.toIntern(),
32050+
.nav = nav_index,
32051+
} },
32052+
});
32053+
}
32054+
3205332055
if (is_ref) {
3205432056
try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index);
3205532057
}
32058+
3205632059
return Air.internedToRef((try pt.intern(.{ .ptr = .{
3205732060
.ty = ptr_ty.toIntern(),
3205832061
.base_addr = .{ .nav = nav_index },

src/Value.zig

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,21 +1325,6 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
13251325
};
13261326
}
13271327

1328-
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
1329-
const ip = &zcu.intern_pool;
1330-
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
1331-
const nav_val = switch (ip.getNav(nav).status) {
1332-
.unresolved => unreachable,
1333-
.type_resolved => |r| return r.is_threadlocal,
1334-
.fully_resolved => |r| r.val,
1335-
};
1336-
return switch (ip.indexToKey(nav_val)) {
1337-
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
1338-
.variable => |v| v.is_threadlocal,
1339-
else => false,
1340-
};
1341-
}
1342-
13431328
// Asserts that the provided start/end are in-bounds.
13441329
pub fn sliceArray(
13451330
val: Value,

src/arch/aarch64/CodeGen.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
876876
.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
877877
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
878878
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
879+
.tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}),
879880

880881
.c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
881882
.c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
@@ -6168,7 +6169,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
61686169
.memory => |addr| .{ .memory = addr },
61696170
.load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
61706171
.load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
6171-
.load_symbol, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO
6172+
.load_symbol, .lea_symbol, .lea_direct => unreachable, // TODO
61726173
},
61736174
.fail => |msg| return self.failMsg(msg),
61746175
};

src/arch/arm/CodeGen.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
865865
.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
866866
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
867867
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
868+
.tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}),
868869

869870
.c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
870871
.c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
@@ -6135,7 +6136,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
61356136
.mcv => |mcv| switch (mcv) {
61366137
.none => .none,
61376138
.undef => .undef,
6138-
.load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol, .lea_direct => unreachable, // TODO
6139+
.load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO
61396140
.immediate => |imm| .{ .immediate = @truncate(imm) },
61406141
.memory => |addr| .{ .memory = addr },
61416142
},

0 commit comments

Comments
 (0)