Skip to content

Commit 6c33e10

Browse files
committed
shard: fix COFF anon dups, module-asm routing, cross-shard @export aliases; IES yield
- llvm/Builder.zig + ir.zig: add COMDAT support (MODULE_CODE_COMDAT records, Variable.comdat field, addComdat). Required for COFF — without comdat any, linkonce_odr emits as a strong def per shard and lld-link rejects ~350 duplicate __anon_* symbols. - codegen/llvm.zig resolveGlobalUav/updateExportedValue: setComdat(.any) on COFF for sharded linkonce_odr uavs. - Zcu.navShard: switch from fqn-hash to file-hash via File.computeShard; add analUnitShard mapping comptime/nav/func units to their file's shard. - codegen/llvm.zig genModuleLevelAssembly: route each global asm block to its source file's shard so .set aliases resolve against same-module defs. - codegen/llvm.zig PartitionSet.updateExports: broadcast to all shards; Object.updateExports collapses non-owner extern globals onto one canonical decl so InstCombine cannot fold &a==&b to false pre-link. - Zcu.isClaimedByOther + Sema.resolveInferredErrorSet: when the IES func is claimed by another thread, set tls_retry_loop and yield (cap 8) instead of parking in claimOrWait. Reuses existing requeue path.
1 parent a59bd0a commit 6c33e10

File tree

5 files changed

+232
-20
lines changed

5 files changed

+232
-20
lines changed

lib/std/zig/llvm/Builder.zig

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ next_unique_global_id: std.AutoHashMapUnmanaged(StrtabString, u32),
4242
aliases: std.ArrayListUnmanaged(Alias),
4343
variables: std.ArrayListUnmanaged(Variable),
4444
functions: std.ArrayListUnmanaged(Function),
45+
comdats: std.ArrayListUnmanaged(Comdat),
4546

4647
strtab_string_map: std.AutoArrayHashMapUnmanaged(void, void),
4748
strtab_string_indices: std.ArrayListUnmanaged(u32),
@@ -2513,12 +2514,37 @@ pub const Alias = struct {
25132514
};
25142515
};
25152516

2517+
pub const Comdat = struct {
2518+
name: StrtabString,
2519+
kind: SelectionKind,
2520+
2521+
/// Matches LLVM's bitc::ComdatSelectionKindCodes.
2522+
pub const SelectionKind = enum(u3) {
2523+
any = 1,
2524+
exactmatch = 2,
2525+
largest = 3,
2526+
nodeduplicate = 4,
2527+
samesize = 5,
2528+
};
2529+
2530+
pub const Index = enum(u32) {
2531+
/// Stored 1-based to match the bitcode encoding (0 = no comdat).
2532+
none = 0,
2533+
_,
2534+
2535+
pub fn ptrConst(self: Index, builder: *const Builder) *const Comdat {
2536+
return &builder.comdats.items[@intFromEnum(self) - 1];
2537+
}
2538+
};
2539+
};
2540+
25162541
pub const Variable = struct {
25172542
global: Global.Index,
25182543
thread_local: ThreadLocal = .default,
25192544
mutability: Mutability = .global,
25202545
init: Constant = .no_init,
25212546
section: String = .none,
2547+
comdat: Comdat.Index = .none,
25222548
alignment: Alignment = .default,
25232549

25242550
pub const Index = enum(u32) {
@@ -2595,6 +2621,10 @@ pub const Variable = struct {
25952621
self.ptr(builder).section = section;
25962622
}
25972623

2624+
pub fn setComdat(self: Index, comdat: Comdat.Index, builder: *Builder) void {
2625+
self.ptr(builder).comdat = comdat;
2626+
}
2627+
25982628
pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void {
25992629
self.ptr(builder).alignment = alignment;
26002630
}
@@ -8603,6 +8633,7 @@ pub fn init(options: Options) Allocator.Error!Builder {
86038633
.aliases = .{},
86048634
.variables = .{},
86058635
.functions = .{},
8636+
.comdats = .{},
86068637

86078638
.strtab_string_map = .{},
86088639
.strtab_string_indices = .{},
@@ -8752,6 +8783,7 @@ pub fn deinit(self: *Builder) void {
87528783
self.variables.deinit(self.gpa);
87538784
for (self.functions.items) |*function| function.deinit(self.gpa);
87548785
self.functions.deinit(self.gpa);
8786+
self.comdats.deinit(self.gpa);
87558787

87568788
self.strtab_string_map.deinit(self.gpa);
87578789
self.strtab_string_indices.deinit(self.gpa);
@@ -9007,6 +9039,16 @@ pub fn addAliasAssumeCapacity(
90079039
return alias_index;
90089040
}
90099041

9042+
pub fn addComdat(
9043+
self: *Builder,
9044+
name: StrtabString,
9045+
kind: Comdat.SelectionKind,
9046+
) Allocator.Error!Comdat.Index {
9047+
assert(!name.isAnon());
9048+
try self.comdats.append(self.gpa, .{ .name = name, .kind = kind });
9049+
return @enumFromInt(self.comdats.items.len);
9050+
}
9051+
90109052
pub fn addVariable(
90119053
self: *Builder,
90129054
name: StrtabString,
@@ -9564,6 +9606,14 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void
95649606
, .{ id.fmt(self), ty.fmt(self, .default) });
95659607
}
95669608

9609+
if (self.comdats.items.len > 0) {
9610+
if (need_newline) try w.writeByte('\n') else need_newline = true;
9611+
for (self.comdats.items) |comdat| try w.print(
9612+
\\${f} = comdat {s}
9613+
\\
9614+
, .{ comdat.name.fmt(self, .quote_unless_valid_identifier), @tagName(comdat.kind) });
9615+
}
9616+
95679617
if (self.variables.items.len > 0) {
95689618
if (need_newline) try w.writeByte('\n') else need_newline = true;
95699619
for (self.variables.items) |variable| {
@@ -9572,7 +9622,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void
95729622
metadata_formatter.need_comma = true;
95739623
defer metadata_formatter.need_comma = undefined;
95749624
try w.print(
9575-
\\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f}
9625+
\\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{s}{f}{f}
95769626
\\
95779627
, .{
95789628
variable.global.fmt(self),
@@ -9589,6 +9639,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void
95899639
@tagName(variable.mutability),
95909640
global.type.fmt(self, .percent),
95919641
variable.init.fmt(self, .{ .space = true }),
9642+
if (variable.comdat != .none) ", comdat" else "",
95929643
variable.alignment.fmt(", "),
95939644
try metadata_formatter.fmt("!dbg ", global.dbg, null),
95949645
});
@@ -13663,6 +13714,18 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco
1366313714
defer section_map.deinit(self.gpa);
1366413715
try section_map.ensureUnusedCapacity(self.gpa, globals.count());
1366513716

13717+
// COMDAT records must precede any global that references them by index.
13718+
for (self.comdats.items) |comdat| {
13719+
const name_index = comdat.name.toIndex().?;
13720+
const offset = self.strtab_string_indices.items[name_index];
13721+
const size = self.strtab_string_indices.items[name_index + 1] - offset;
13722+
try module_block.writeAbbrev(Module.Comdat{
13723+
.strtab_offset = offset,
13724+
.strtab_size = size,
13725+
.selection_kind = comdat.kind,
13726+
});
13727+
}
13728+
1366613729
for (self.variables.items) |variable| {
1366713730
if (variable.global.getReplacement(self) != .none) continue;
1366813731

@@ -13706,6 +13769,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco
1370613769
.unnamed_addr = global.unnamed_addr,
1370713770
.externally_initialized = global.externally_initialized,
1370813771
.dllstorageclass = global.dll_storage_class,
13772+
.comdat = @intFromEnum(variable.comdat),
1370913773
.preemption = global.preemption,
1371013774
});
1371113775
}

lib/std/zig/llvm/ir.zig

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ pub const Module = struct {
193193
Variable,
194194
Function,
195195
Alias,
196+
Comdat,
196197
};
197198

198199
pub const Version = struct {
@@ -211,6 +212,18 @@ pub const Module = struct {
211212
string: []const u8,
212213
};
213214

215+
pub const Comdat = struct {
216+
pub const ops = [_]AbbrevOp{
217+
.{ .literal = 12 }, // MODULE_CODE_COMDAT
218+
.{ .vbr = 16 }, // strtab_offset
219+
.{ .vbr = 16 }, // strtab_size
220+
.{ .fixed = @bitSizeOf(Builder.Comdat.SelectionKind) },
221+
};
222+
strtab_offset: usize,
223+
strtab_size: usize,
224+
selection_kind: Builder.Comdat.SelectionKind,
225+
};
226+
214227
pub const Variable = struct {
215228
const AddrSpaceAndIsConst = packed struct {
216229
is_const: bool,
@@ -233,7 +246,7 @@ pub const Module = struct {
233246
.{ .fixed = @bitSizeOf(Builder.UnnamedAddr) },
234247
.{ .fixed = @bitSizeOf(Builder.ExternallyInitialized) },
235248
.{ .fixed = @bitSizeOf(Builder.DllStorageClass) },
236-
.{ .literal = 0 }, // comdat
249+
.{ .vbr = 16 }, // comdat
237250
.{ .literal = 0 }, // attributes
238251
.{ .fixed = @bitSizeOf(Builder.Preemption) },
239252
};
@@ -250,6 +263,7 @@ pub const Module = struct {
250263
unnamed_addr: Builder.UnnamedAddr,
251264
externally_initialized: Builder.ExternallyInitialized,
252265
dllstorageclass: Builder.DllStorageClass,
266+
comdat: u32,
253267
preemption: Builder.Preemption,
254268
};
255269

src/Sema.zig

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35280,7 +35280,30 @@ fn resolveInferredErrorSet(
3528035280
// In this case we are dealing with the actual InferredErrorSet object that
3528135281
// corresponds to the function, not one created to track an inline/comptime call.
3528235282
const orig_func_index = ip.unwrapCoercedFunc(func_index);
35283-
try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index }));
35283+
const ies_unit: AnalUnit = .wrap(.{ .func = orig_func_index });
35284+
if (zcu.parallel_sema and zcu.isClaimedByOther(ies_unit)) {
35285+
// The IES owner's body is being analysed by another worker right
35286+
// now. `ensureFuncBodyUpToDate` would park this thread on
35287+
// `sema_claim_cond` until that worker finishes — under chained
35288+
// IES dependencies that idles a core for the bulk of Sema.
35289+
// Instead, yield: re-queue `sema.owner` and let this thread
35290+
// pull the next job. Cap retries low because each one re-runs
35291+
// the caller's body from scratch; once the cap is hit, fall
35292+
// through to the blocking wait.
35293+
zcu.sema_retry_mutex.lock();
35294+
const tries: u8 = blk: {
35295+
const gop = zcu.sema_retry_counts.getOrPut(zcu.gpa, ies_unit) catch break :blk 255;
35296+
if (!gop.found_existing) gop.value_ptr.* = 0;
35297+
gop.value_ptr.* +|= 1;
35298+
break :blk gop.value_ptr.*;
35299+
};
35300+
zcu.sema_retry_mutex.unlock();
35301+
if (tries < 8) {
35302+
Zcu.tls_retry_loop = sema.owner;
35303+
return error.AnalysisFail;
35304+
}
35305+
}
35306+
try sema.addReferenceEntry(block, src, ies_unit);
3528435307
try pt.ensureFuncBodyUpToDate(orig_func_index);
3528535308
}
3528635309

src/Zcu.zig

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3742,6 +3742,19 @@ pub fn releaseClaim(zcu: *Zcu, unit: AnalUnit) void {
37423742
zcu.sema_claim_cond.broadcast();
37433743
}
37443744

3745+
/// Returns true if `unit` is currently claimed for analysis by a thread other
3746+
/// than the caller. Used by `Sema.resolveInferredErrorSet` to yield-and-requeue
3747+
/// instead of parking on `sema_claim_cond` when a dependency IES is already in
3748+
/// progress on another worker.
3749+
pub fn isClaimedByOther(zcu: *Zcu, unit: AnalUnit) bool {
3750+
if (!zcu.parallel_sema) return false;
3751+
const me = std.Thread.getCurrentId();
3752+
zcu.unit_claims_mutex.lock();
3753+
defer zcu.unit_claims_mutex.unlock();
3754+
const owner = zcu.unit_claims.get(unit) orelse return false;
3755+
return owner != me;
3756+
}
3757+
37453758
/// Under parallel Sema, `analysis_in_progress` is per-OS-thread (lock-free).
37463759
threadlocal var tls_aip: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty;
37473760
/// Set by `ensureNavResolved` when a dependency loop is detected under
@@ -4650,10 +4663,27 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
46504663
}
46514664

46524665
pub fn navShard(zcu: *Zcu, nav: InternPool.Nav.Index, n: u32) u32 {
4666+
if (n <= 1) return 0;
4667+
return zcu.navFileScope(nav).computeShard(n);
4668+
}
4669+
4670+
/// Returns the LLVM codegen shard that owns `unit`. Module-level assembly is
4671+
/// keyed by `AnalUnit`; routing each asm string to the same shard as the
4672+
/// same-file navs it references (e.g. `.set` alias targets) lets the integrated
4673+
/// assembler resolve those symbols and avoids emitting any string twice.
4674+
pub fn analUnitShard(zcu: *Zcu, unit: AnalUnit, n: u32) u32 {
46534675
if (n <= 1) return 0;
46544676
const ip = &zcu.intern_pool;
4655-
const fqn = ip.getNav(nav).fqn.toSlice(ip);
4656-
return @intCast(std.hash.Wyhash.hash(0, fqn) % n);
4677+
return switch (unit.unwrap()) {
4678+
.@"comptime" => |cu_id| s: {
4679+
const cu = ip.getComptimeUnit(cu_id);
4680+
const resolved = cu.zir_index.resolveFull(ip) orelse break :s 0;
4681+
break :s zcu.fileByIndex(resolved.file).computeShard(n);
4682+
},
4683+
.nav_val, .nav_ty => |nav| zcu.navShard(nav, n),
4684+
.func => |func| zcu.navShard(zcu.funcInfo(func).owner_nav, n),
4685+
.type, .memoized_state => 0,
4686+
};
46574687
}
46584688

46594689
pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(FormatAnalUnit, formatAnalUnit) {

0 commit comments

Comments
 (0)