╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Zcu.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
4949 continue; 4950 }; 4951 while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1; 4952 break :depth depth; 4953 }; 4954 for (units.items[1..], 1..) |unit, index| { 4955 var depth: u32 = 0; 4956 var opt_ref = all_references.get(unit).?; 4957 while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1; 4958 if (depth < start_depth) { 4959 start_index = index; 4960 start_depth = depth; 4961 } 4962 } 4963 4964 // Collect a reference trace for the start of the loop. 4965 var ref_trace: std.ArrayList(std.zig.ErrorBundle.ReferenceTrace) = .empty; 4966 defer ref_trace.deinit(gpa); 4967 const frame_limit = zcu.comp.reference_trace orelse 0; 4968 try zcu.populateReferenceTrace(units.items[start_index], frame_limit, eb, &ref_trace); 4969 4970 if (units.items.len == 1) { 4971 // Don't do a complicated message with multiple notes, just do a single error message. 4972 assert(start_index == 0); 4973 const root_msg = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) { 4974 error.AlreadyReported => return, // give up on the dep loop error 4975 error.OutOfMemory => |e| return e, 4976 }; 4977 try eb.root_list.append(eb.gpa, root_msg); 4978 continue; 4979 } 4980 4981 // Collect all notes first so we don't leave an incomplete root error message on `error.AlreadyReported`. 4982 const note_buf = try gpa.alloc(std.zig.ErrorBundle.MessageIndex, units.items.len + 1); 4983 defer gpa.free(note_buf); 4984 note_buf[0] = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) { 4985 error.AlreadyReported => return, // give up on the dep loop error 4986 error.OutOfMemory => |e| return e, 4987 }; 4988 for (units.items[start_index + 1 ..], note_buf[1 .. units.items.len - start_index]) |unit, *note| { 4989 note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) { 4990 error.AlreadyReported => return, // give up on the dep loop error 4991 error.OutOfMemory => |e| return e, 4992 }; 4993 } 4994 for (units.items[0..start_index], note_buf[units.items.len - start_index .. units.items.len]) |unit, *note| { 4995 note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) { 4996 error.AlreadyReported => return, // give up on the dep loop error 4997 error.OutOfMemory => |e| return e, 4998 }; 4999 } 5000 note_buf[units.items.len] = try eb.addErrorMessage(.{ 5001 .msg = try eb.addString("eliminate any one of these dependencies to break the loop"), 5002 .src_loc = .none, 5003 }); 5004 5005 try eb.addRootErrorMessage(.{ 5006 .msg = try eb.printString("dependency loop with length {d}", .{units.items.len}), 5007 .src_loc = .none, 5008 .notes_len = @intCast(units.items.len + 1), 5009 }); 5010 const notes_start = try eb.reserveNotes(@intCast(units.items.len + 1)); 5011 const notes: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]); 5012 @memcpy(notes, note_buf); 5013 } 5014 } 5015 fn addDependencyLoopErrorLine( 5016 zcu: *Zcu, 5017 eb: *std.zig.ErrorBundle.Wip, 5018 source_unit: AnalUnit, 5019 ref_trace: []const std.zig.ErrorBundle.ReferenceTrace, 5020 ) (Allocator.Error || error{AlreadyReported})!std.zig.ErrorBundle.MessageIndex { 5021 const ip = &zcu.intern_pool; 5022 const comp = zcu.comp; 5023 5024 const fmt_source: std.fmt.Alt(FormatAnalUnit, formatDependencyLoopSourceUnit) = .{ .data = .{ 5025 .unit = source_unit, 5026 .zcu = zcu, 5027 } }; 5028 5029 const dep_node = zcu.dependency_loop_nodes.get(source_unit).?; 5030 5031 const msg: std.zig.ErrorBundle.String = if (dep_node.unit == source_unit) switch (source_unit.unwrap()) { 5032 .@"comptime" => unreachable, // cannot be involved in a dependency loop 5033 .nav_ty, .nav_val => try eb.printString("{f} depends on itself here", .{fmt_source}), 5034 .memoized_state => unreachable, // memoized_state definitely does not *directly* depend on itself 5035 .func => try eb.printString("{f} uses its own inferred error set here", .{fmt_source}), 5036 .type_layout => try eb.printString("{f} depends on itself {s}", .{ 5037 fmt_source, 5038 dep_node.reason.type_layout_reason.msg(), 5039 }), 5040 .struct_defaults => |ty| try eb.printString( 5041 "default field values of '{f}' depend on themselves for initialization here", 5042 .{Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip)}, 5043 ), 5044 } else switch (dep_node.unit.unwrap()) { 5045 .@"comptime" => unreachable, // cannot be involved in a dependency loop 5046 .nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{ 5047 fmt_source, ip.getNav(nav).fqn.fmt(ip), 5048 }), 5049 .nav_ty => |nav| try eb.printString("{f} uses type of declaration '{f}' here", .{ 5050 fmt_source, ip.getNav(nav).fqn.fmt(ip), 5051 }), 5052 .memoized_state => |stage| switch (stage) { 5053 .panic => try eb.printString("{f} requires panic handler for call here", .{fmt_source}), 5054 else => try eb.printString("{f} requires 'std.lang' declarations here", .{fmt_source}), 5055 }, 5056 .func => |func| try eb.printString("{f} uses inferred error set of function '{f}' here", .{ 5057 fmt_source, ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), 5058 }), 5059 .type_layout => |ty| try eb.printString("{f} depends on type '{f}' {s}", .{ 5060 fmt_source, 5061 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 5062 dep_node.reason.type_layout_reason.msg(), 5063 }), 5064 .struct_defaults => |ty| try eb.printString( 5065 "{f} uses default field values of '{f}' here", 5066 .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip) }, 5067 ), 5068 }; 5069 5070 const src_loc = dep_node.reason.src.upgrade(zcu); 5071 const source = src_loc.file_scope.getSource(zcu) catch |err| { 5072 try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err); 5073 return error.AlreadyReported; 5074 }; 5075 const span = src_loc.span(zcu) catch |err| { 5076 try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err); 5077 return error.AlreadyReported; 5078 }; 5079 const loc = std.zig.findLineColumn(source, span.main); 5080 const eb_src = try eb.addSourceLocation(.{ 5081 .src_path = try eb.printString("{f}", .{src_loc.file_scope.path.fmt(comp)}), 5082 .span_start = span.start, 5083 .span_main = span.main, 5084 .span_end = span.end, 5085 .line = @intCast(loc.line), 5086 .column = @intCast(loc.column), 5087 .source_line = try eb.addString(loc.source_line), 5088 .reference_trace_len = @intCast(ref_trace.len), 5089 }); 5090 for (ref_trace) |rt| try eb.addReferenceTrace(rt); 5091 return eb.addErrorMessage(.{ 5092 .msg = msg, 5093 .src_loc = eb_src, 5094 }); 5095 } 5096 fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer.Error!void { 5097 const zcu = data.zcu; 5098 const ip = &zcu.intern_pool; 5099 switch (data.unit.unwrap()) { 5100 .@"comptime" => unreachable, // cannot be involved in a dependency loop 5101 .nav_val => |nav| try w.print("value of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}), 5102 .nav_ty => |nav| try w.print("type of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}), 5103 .memoized_state => |stage| switch (stage) { 5104 .panic => try w.writeAll("panic handler"), 5105 else => try w.writeAll("'std.lang' declarations"), 5106 }, 5107 .type_layout => |ty| try w.print("type '{f}'", .{ 5108 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 5109 }), 5110 .struct_defaults => |ty| try w.print("default field value of '{f}'", .{ 5111 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 5112 }), 5113 .func => |func| try w.print("function '{f}'", .{ 5114 ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), 5115 }), 5116 } 5117 } 5118 5119 pub fn populateReferenceTrace( 5120 zcu: *Zcu, 5121 root: AnalUnit, 5122 frame_limit: u32, 5123 eb: *std.zig.ErrorBundle.Wip, 5124 ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace), 5125 ) Allocator.Error!void { 5126 const ip = &zcu.intern_pool; 5127 const gpa = zcu.comp.gpa; 5128 5129 if (frame_limit == 0) return; 5130 5131 const all_references = try zcu.resolveReferences(); 5132 5133 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty; 5134 defer seen.deinit(gpa); 5135 5136 var referenced_by = root; 5137 while (all_references.get(referenced_by)) |maybe_ref| { 5138 const ref = maybe_ref orelse break; 5139 const gop = try seen.getOrPut(gpa, ref.referencer); 5140 if (gop.found_existing) break; 5141 if (ref_trace.items.len < frame_limit) { 5142 var last_call_src = ref.src; 5143 var opt_inline_frame = ref.inline_frame; 5144 while (opt_inline_frame.unwrap()) |inline_frame| { 5145 const f = inline_frame.ptr(zcu).*; 5146 const func_nav = ip.indexToKey(f.callee).func.owner_nav; 5147 const func_name = ip.getNav(func_nav).name.toSlice(ip); 5148 addReferenceTraceFrame(zcu, eb, ref_trace, func_name, last_call_src, true) catch |err| switch (err) { 5149 error.OutOfMemory => |e| return e, 5150 error.AlreadyReported => { 5151 // An incomplete reference trace isn't the end of the world; just cut it off. 5152 return; 5153 }, 5154 }; 5155 last_call_src = f.call_src; 5156 opt_inline_frame = f.parent; 5157 } 5158 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) { 5159 .@"comptime" => "comptime", 5160 .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), 5161 .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).fqn.toSlice(ip), 5162 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), 5163 .memoized_state => null, 5164 }; 5165 if (root_name) |n| { 5166 addReferenceTraceFrame(zcu, eb, ref_trace, n, last_call_src, false) catch |err| switch (err) { 5167 error.OutOfMemory => |e| return e, 5168 error.AlreadyReported => { 5169 // An incomplete reference trace isn't the end of the world; just cut it off. 5170 return; 5171 }, 5172 }; 5173 } 5174 } 5175 referenced_by = ref.referencer; 5176 } 5177 5178 if (seen.count() > ref_trace.items.len) { 5179 try ref_trace.append(gpa, .{ 5180 .decl_name = @intCast(seen.count() - ref_trace.items.len), 5181 .src_loc = .none, 5182 }); 5183 } 5184 } 5185 fn addReferenceTraceFrame( 5186 zcu: *Zcu, 5187 eb: *std.zig.ErrorBundle.Wip, 5188 ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace), 5189 name: []const u8, 5190 lazy_src: Zcu.LazySrcLoc, 5191 inlined: bool, 5192 ) error{ OutOfMemory, AlreadyReported }!void { 5193 const gpa = zcu.gpa; 5194 const src = lazy_src.upgrade(zcu); 5195 const source = src.file_scope.getSource(zcu) catch |err| { 5196 try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err); 5197 return error.AlreadyReported; 5198 }; 5199 const span = src.span(zcu) catch |err| { 5200 try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err); 5201 return error.AlreadyReported; 5202 }; 5203 const loc = std.zig.findLineColumn(source, span.main); 5204 try ref_trace.append(gpa, .{ 5205 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), 5206 .src_loc = try eb.addSourceLocation(.{ 5207 .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}), 5208 .span_start = span.start, 5209 .span_main = span.main, 5210 .span_end = span.end, 5211 .line = @intCast(loc.line), 5212 .column = @intCast(loc.column), 5213 .source_line = 0, 5214 }), 5215 }); 5216 } 5217 5218 const TrackedUnitSema = struct { 5219 /// `null` means we created the node, so should end it. 5220 old_name: ?[std.Progress.Node.max_name_len]u8, 5221 old_analysis_timer: ?Compilation.Timer, 5222 analysis_timer_decl: ?InternPool.TrackedInst.Index, 5223 pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void { 5224 const comp = zcu.comp; 5225 const io = comp.io; 5226 if (tus.old_name) |old_name| { 5227 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion 5228 zcu.cur_sema_prog_node.setName(&old_name); 5229 } else { 5230 zcu.cur_sema_prog_node.end(); 5231 zcu.cur_sema_prog_node = .none; 5232 } 5233 report_time: { 5234 const sema_ns = zcu.cur_analysis_timer.?.finish(io) orelse break :report_time; 5235 const zir_decl = tus.analysis_timer_decl orelse break :report_time; 5236 comp.mutex.lockUncancelable(io); 5237 defer comp.mutex.unlock(io); 5238 comp.time_report.?.stats.cpu_ns_sema += sema_ns; 5239 const gop = comp.time_report.?.decl_sema_info.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) { 5240 error.OutOfMemory => { 5241 comp.setAllocFailure(); 5242 break :report_time; 5243 }, 5244 }; 5245 if (!gop.found_existing) gop.value_ptr.* = .{ .ns = 0, .count = 0 }; 5246 gop.value_ptr.ns += sema_ns; 5247 gop.value_ptr.count += 1; 5248 } 5249 zcu.cur_analysis_timer = tus.old_analysis_timer; 5250 if (zcu.cur_analysis_timer) |*t| t.@"resume"(io); 5251 } 5252 }; 5253 pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema { 5254 const comp = zcu.comp; 5255 const io = comp.io; 5256 if (zcu.cur_analysis_timer) |*t| t.pause(io); 5257 const old_analysis_timer = zcu.cur_analysis_timer; 5258 zcu.cur_analysis_timer = zcu.comp.startTimer(); 5259 const old_name: ?[std.Progress.Node.max_name_len]u8 = old_name: { 5260 if (zcu.cur_sema_prog_node.index == .none) { 5261 zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0); 5262 break :old_name null; 5263 } 5264 const old_name = zcu.cur_sema_prog_node.getName(); 5265 zcu.cur_sema_prog_node.setName(name); 5266 break :old_name old_name; 5267 }; 5268 return .{ 5269 .old_name = old_name, 5270 .old_analysis_timer = old_analysis_timer, 5271 .analysis_timer_decl = zir_inst, 5272 }; 5273 } 5274 5275 pub const CodegenTaskPool = struct { 5276 const CodegenResult = PerThread.RunCodegenError!codegen.AnyMir; 5277 5278 /// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is 5279 /// around 20. Going by that 50x multiplier, and assuming we want to consume no more than 500 MiB of 5280 /// memory on AIR/MIR, we see a limit of around 10 MiB of AIR in-flight. 5281 const max_air_bytes_in_flight = 10 * 1024 * 1024; 5282 5283 const max_funcs_in_flight = link.Queue.buffer_size; 5284 5285 available_air_bytes: u32, 5286 5287 /// Locks the freelist and `available_air_bytes`. 5288 mutex: Io.Mutex, 5289 5290 /// Signaled when an item is added to the freelist. 5291 free_cond: Io.Condition, 5292 /// Pre-allocated with enough capacity for all indices. 5293 free: std.ArrayList(Index), 5294 5295 /// `.none` means this task is in the freelist. The `task_air_bytes` and 5296 /// `task_futures` entries are `undefined`. 5297 task_funcs: []InternPool.Index, 5298 task_air_bytes: []u32, 5299 task_futures: []Io.Future(CodegenResult), 5300 5301 pub fn init(arena: Allocator) Allocator.Error!CodegenTaskPool { 5302 const task_funcs = try arena.alloc(InternPool.Index, max_funcs_in_flight); 5303 const task_air_bytes = try arena.alloc(u32, max_funcs_in_flight); 5304 const task_futures = try arena.alloc(Io.Future(CodegenResult), max_funcs_in_flight); 5305 @memset(task_funcs, .none); 5306 5307 var free: std.ArrayList(Index) = try .initCapacity(arena, max_funcs_in_flight); 5308 for (0..max_funcs_in_flight) |index| free.appendAssumeCapacity(@fromBackingInt(@intCast(index))); 5309 5310 return .{ 5311 .available_air_bytes = max_air_bytes_in_flight, 5312 .mutex = .init, 5313 .free_cond = .init, 5314 .free = free, 5315 .task_funcs = task_funcs, 5316 .task_air_bytes = task_air_bytes, 5317 .task_futures = task_futures, 5318 }; 5319 } 5320 5321 pub fn cancel(pool: *CodegenTaskPool, zcu: *const Zcu) void { 5322 const io = zcu.comp.io; 5323 for ( 5324 pool.task_funcs, 5325 pool.task_air_bytes, 5326 pool.task_futures, 5327 ) |func, effective_air_bytes, *future| { 5328 if (func == .none) continue; 5329 pool.available_air_bytes += effective_air_bytes; 5330 var mir = future.cancel(io) catch continue; 5331 mir.deinit(zcu); 5332 } 5333 assert(pool.available_air_bytes == max_air_bytes_in_flight); 5334 zcu.updateTracyPlot("air_bytes_in_flight", 0); 5335 } 5336 5337 pub fn start( 5338 pool: *CodegenTaskPool, 5339 zcu: *Zcu, 5340 func_index: InternPool.Index, 5341 air: *Air, 5342 /// If `true`, this function will take ownership of `air`, freeing it after codegen 5343 /// completes; it is not assumed that `air` will outlive this function. If `false`, 5344 /// codegen will operate on `air` via the given pointer, which it is assumed will 5345 /// outline the codegen task. 5346 move_air: bool, 5347 ) Io.Cancelable!Index { 5348 const io = zcu.comp.io; 5349 5350 // To avoid consuming an excessive amount of memory, there is a limit on the total number of AIR 5351 // bytes which can be in the codegen/link pipeline at one time. If we exceed this limit, we must 5352 // wait for codegen/link to finish some WIP functions so they catch up with us. 5353 const actual_air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4); 5354 // We need to let all AIR through eventually, even if one function exceeds `max_air_bytes_in_flight`. 5355 const effective_air_bytes: u32 = @min(actual_air_bytes, max_air_bytes_in_flight); 5356 assert(effective_air_bytes > 0); 5357 5358 const index: Index = index: { 5359 try pool.mutex.lock(io); 5360 defer pool.mutex.unlock(io); 5361 5362 while (pool.free.items.len == 0 or pool.available_air_bytes < effective_air_bytes) { 5363 // The linker thread needs to catch up! 5364 try pool.free_cond.wait(io, &pool.mutex); 5365 } 5366 5367 pool.available_air_bytes -= effective_air_bytes; 5368 5369 zcu.updateTracyPlot("air_bytes_in_flight", @max( 5370 max_air_bytes_in_flight - pool.available_air_bytes, 5371 actual_air_bytes, 5372 )); 5373 5374 break :index pool.free.pop().?; 5375 }; 5376 5377 // No turning back now: we're incrementing `pending_codegen_jobs` and starting the worker. 5378 errdefer comptime unreachable; 5379 5380 assert(zcu.pending_codegen_jobs.fetchAdd(1, .monotonic) > 0); // the "Code Generation" node is still active 5381 assert(pool.task_funcs[@backingInt(index)] == .none); 5382 pool.task_funcs[@backingInt(index)] = func_index; 5383 pool.task_air_bytes[@backingInt(index)] = actual_air_bytes; 5384 pool.task_futures[@backingInt(index)] = if (move_air) io.async( 5385 workerCodegenOwnedAir, 5386 .{ zcu, func_index, air.* }, 5387 ) else io.async( 5388 workerCodegenExternalAir, 5389 .{ zcu, func_index, air }, 5390 ); 5391 5392 return index; 5393 } 5394 pub const Index = enum(u32) { 5395 _, 5396 5397 /// Blocks until codegen has completed, successfully or otherwise. 5398 /// The returned MIR is owned by the caller. 5399 pub fn wait( 5400 index: Index, 5401 pool: *CodegenTaskPool, 5402 zcu: *const Zcu, 5403 ) PerThread.RunCodegenError!struct { InternPool.Index, codegen.AnyMir } { 5404 const io = zcu.comp.io; 5405 const func = pool.task_funcs[@backingInt(index)]; 5406 assert(func != .none); 5407 const effective_air_bytes = pool.task_air_bytes[@backingInt(index)]; 5408 const result = pool.task_futures[@backingInt(index)].await(io); 5409 5410 pool.task_funcs[@backingInt(index)] = .none; 5411 pool.task_air_bytes[@backingInt(index)] = undefined; 5412 pool.task_futures[@backingInt(index)] = undefined; 5413 5414 { 5415 pool.mutex.lockUncancelable(io); 5416 defer pool.mutex.unlock(io); 5417 pool.available_air_bytes += effective_air_bytes; 5418 pool.free.appendAssumeCapacity(index); 5419 pool.free_cond.signal(io); 5420 zcu.updateTracyPlot("air_bytes_in_flight", max_air_bytes_in_flight - pool.available_air_bytes); 5421 } 5422 5423 return .{ func, try result }; 5424 } 5425 }; 5426 fn workerCodegenOwnedAir( 5427 zcu: *Zcu, 5428 func_index: InternPool.Index, 5429 orig_air: Air, 5430 ) CodegenResult { 5431 // We own `air` now, so we are responsbile for freeing it. 5432 var air = orig_air; 5433 defer air.deinit(zcu.comp.gpa); 5434 const io = zcu.comp.io; 5435 const tid: Zcu.PerThread.Id = .acquire(io); 5436 defer tid.release(io); 5437 const active = zcu.activate(tid); 5438 defer active.deactivate(); 5439 return active.pt.runCodegen(func_index, &air); 5440 } 5441 fn workerCodegenExternalAir( 5442 zcu: *Zcu, 5443 func_index: InternPool.Index, 5444 air: *Air, 5445 ) CodegenResult { 5446 const io = zcu.comp.io; 5447 const tid: Zcu.PerThread.Id = .acquire(io); 5448 defer tid.release(io); 5449 const active = zcu.activate(tid); 5450 defer active.deactivate(); 5451 return active.pt.runCodegen(func_index, air); 5452 } 5453 }; 5454 5455 fn initTracyPlots(zcu: *const Zcu) void { 5456 if (zcu.comp.skip_linker_dependencies) return; 5457 5458 tracy.plotConfig("air_bytes_in_flight", .{ .format = .memory, .mode = .step }); 5459 5460 tracy.plotConfig("outdated + potentially_outdated", .{ .format = .number, .mode = .step, .color = 0xFFFF00 }); 5461 tracy.plotConfig("outdated", .{ .format = .number, .mode = .step, .color = 0xFF0000 }); 5462 tracy.plotConfig("potentially_outdated", .{ .format = .number, .mode = .step, .color = 0xFF7700 }); 5463 tracy.plotConfig("outdated_ready", .{ .format = .number, .mode = .step, .color = 0x00FF00 }); 5464 } 5465 5466 /// Marked `inline` to prevent binary bloat from trivial generic instances, and to ensure there is 5467 /// minimal overhead to this call when Tracy is disabled, even in Debug builds. 5468 inline fn updateTracyPlot(zcu: *const Zcu, comptime name: [*:0]const u8, val: u64) void { 5469 if (zcu.comp.skip_linker_dependencies) return; 5470 tracy.plotInt(name, @intCast(val)); 5471 } 5472 5473 /// Assumes that `zcu.outdated_lock` is already held. 5474 fn updateTracyOutdatedPlots(zcu: *const Zcu) void { 5475 zcu.updateTracyPlot("outdated + potentially_outdated", zcu.outdated.count() + zcu.potentially_outdated.count()); 5476 zcu.updateTracyPlot("outdated", zcu.outdated.count()); 5477 zcu.updateTracyPlot("potentially_outdated", zcu.potentially_outdated.count()); 5478 zcu.updateTracyPlot("outdated_ready", zcu.outdated_ready.funcs.count() + zcu.outdated_ready.other.count()); 5479 } 5480 5481 pub const Active = struct { 5482 pt: Zcu.PerThread, 5483 ip: InternPool.Active, 5484 pub fn deactivate(active: Active) void { 5485 active.ip.deactivate(); 5486 } 5487 pub fn release(active: Active) void { 5488 active.deactivate(); 5489 active.pt.tid.release(active.pt.zcu.comp.io); 5490 } 5491 }; 5492 pub fn activate(zcu: *Zcu, tid: PerThread.Id) Active { 5493 return .{ 5494 .pt = .{ .zcu = zcu, .tid = tid }, 5495 .ip = zcu.intern_pool.activate(), 5496 }; 5497 } 5498 pub fn acquire(zcu: *Zcu) Active { 5499 return zcu.activate(.acquire(zcu.comp.io)); 5500 } 5501