╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Zcu.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
4178 pub fn unionTagFieldIndex(zcu: *const Zcu, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 { 4179 const ip = &zcu.intern_pool; 4180 if (enum_tag.toIntern() == .none) return null; 4181 const enum_tag_key = ip.indexToKey(enum_tag.toIntern()).enum_tag; 4182 assert(enum_tag_key.ty == loaded_union.enum_tag_type); 4183 const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type); 4184 return loaded_enum.tagValueIndex(ip, enum_tag_key.int); 4185 } 4186 4187 pub const ResolvedReference = struct { 4188 referencer: AnalUnit, 4189 /// If `inline_frame` is not `.none`, this is the *deepest* source location in the chain of 4190 /// inline calls. For source locations further up the inline call stack, consult `inline_frame`. 4191 src: LazySrcLoc, 4192 inline_frame: InlineReferenceFrame.Index.Optional, 4193 }; 4194 4195 /// Returns a mapping from an `AnalUnit` to where it is referenced. 4196 /// If the value is `null`, the `AnalUnit` is a root of analysis. 4197 /// If an `AnalUnit` is not in the returned map, it is unreferenced. 4198 /// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller. 4199 /// This hashmap is cached, so repeated calls to this function are cheap. 4200 pub fn resolveReferences(zcu: *Zcu) Allocator.Error!*const std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) { 4201 if (zcu.resolved_references == null) { 4202 zcu.resolved_references = try zcu.resolveReferencesInner(); 4203 } 4204 return &zcu.resolved_references.?; 4205 } 4206 fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) { 4207 const trace = tracy.trace(@src()); 4208 defer trace.end(); 4209 4210 const gpa = zcu.gpa; 4211 const comp = zcu.comp; 4212 const ip = &zcu.intern_pool; 4213 4214 var units: std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) = .empty; 4215 var types: std.array_hash_map.Auto(InternPool.Index, ?ResolvedReference) = .empty; 4216 defer { 4217 units.deinit(gpa); 4218 types.deinit(gpa); 4219 } 4220 4221 // This is not a sufficient size, but an approximate lower bound. 4222 try units.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count())); 4223 4224 try types.ensureTotalCapacity(gpa, zcu.analysis_roots_len); 4225 for (zcu.analysisRoots()) |mod| { 4226 const file = zcu.module_roots.get(mod).?.unwrap() orelse continue; 4227 const root_ty = zcu.fileRootType(file); 4228 if (root_ty == .none) continue; 4229 types.putAssumeCapacityNoClobber(root_ty, null); 4230 } 4231 4232 var unit_idx: usize = 0; 4233 var type_idx: usize = 0; 4234 while (true) { 4235 if (type_idx < types.count()) { 4236 const ty = types.keys()[type_idx]; 4237 const referencer = types.values()[type_idx]; 4238 type_idx += 1; 4239 4240 refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip)}); 4241 4242 // Queue any decls within this type which would be automatically analyzed. 4243 // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`. 4244 const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?; 4245 for (zcu.namespacePtr(ns).comptime_decls.items) |cu| { 4246 // `comptime` decls are always analyzed. 4247 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu }); 4248 const gop = try units.getOrPut(gpa, unit); 4249 if (!gop.found_existing) { 4250 refs_log.debug("type '{f}': ref comptime %{}", .{ 4251 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 4252 @backingInt(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue), 4253 }); 4254 gop.value_ptr.* = referencer; 4255 } 4256 } 4257 for (zcu.namespacePtr(ns).test_decls.items) |nav_id| { 4258 const nav = ip.getNav(nav_id); 4259 // `test` declarations are analyzed depending on the test filter. 4260 const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue; 4261 const file = zcu.fileByIndex(inst_info.file); 4262 const decl = file.zir.?.getDeclaration(inst_info.inst); 4263 4264 if (!comp.config.is_test or file.mod != zcu.main_mod) continue; 4265 4266 const want_analysis = switch (decl.kind) { 4267 .@"const", .@"var" => unreachable, 4268 .@"comptime" => unreachable, 4269 .unnamed_test => true, 4270 .@"test", .decltest => a: { 4271 const fqn_slice = nav.fqn.toSlice(ip); 4272 if (comp.test_filters.len > 0) { 4273 for (comp.test_filters) |test_filter| { 4274 if (std.mem.find(u8, fqn_slice, test_filter) != null) break; 4275 } else break :a false; 4276 } 4277 break :a true; 4278 }, 4279 }; 4280 if (want_analysis) { 4281 { 4282 const gop = try units.getOrPut(gpa, .wrap(.{ .nav_val = nav_id })); 4283 if (!gop.found_existing) { 4284 refs_log.debug("type '{f}': ref test %{}", .{ 4285 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 4286 @backingInt(inst_info.inst), 4287 }); 4288 gop.value_ptr.* = referencer; 4289 } 4290 } 4291 // Non-fatal AstGen errors could mean this test decl failed 4292 if (nav.resolved != null and nav.resolved.?.value != .none) { 4293 const gop = try units.getOrPut(gpa, .wrap(.{ .func = nav.resolved.?.value })); 4294 if (!gop.found_existing) gop.value_ptr.* = referencer; 4295 } 4296 } 4297 } 4298 for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| { 4299 // These are named declarations. They are analyzed only if marked `export`. 4300 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue; 4301 const file = zcu.fileByIndex(inst_info.file); 4302 const decl = file.zir.?.getDeclaration(inst_info.inst); 4303 if (decl.linkage == .@"export") { 4304 const unit: AnalUnit = .wrap(.{ .nav_val = nav }); 4305 const gop = try units.getOrPut(gpa, unit); 4306 if (!gop.found_existing) { 4307 refs_log.debug("type '{f}': ref named %{}", .{ 4308 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 4309 @backingInt(inst_info.inst), 4310 }); 4311 gop.value_ptr.* = referencer; 4312 } 4313 } 4314 } 4315 for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| { 4316 // These are named declarations. They are analyzed only if marked `export`. 4317 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue; 4318 const file = zcu.fileByIndex(inst_info.file); 4319 const decl = file.zir.?.getDeclaration(inst_info.inst); 4320 if (decl.linkage == .@"export") { 4321 const unit: AnalUnit = .wrap(.{ .nav_val = nav }); 4322 const gop = try units.getOrPut(gpa, unit); 4323 if (!gop.found_existing) { 4324 refs_log.debug("type '{f}': ref named %{}", .{ 4325 Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), 4326 @backingInt(inst_info.inst), 4327 }); 4328 gop.value_ptr.* = referencer; 4329 } 4330 } 4331 } 4332 continue; 4333 } 4334 if (unit_idx < units.count()) { 4335 const unit = units.keys()[unit_idx]; 4336 unit_idx += 1; 4337 4338 // `nav_val` and `nav_ty` reference each other *implicitly* to save memory. 4339 // Likewise for `type_layout` and `struct_defaults` of a struct type. 4340 queue_paired: { 4341 const other: AnalUnit = .wrap(switch (unit.unwrap()) { 4342 .nav_val => |n| .{ .nav_ty = n }, 4343 .nav_ty => |n| .{ .nav_val = n }, 4344 .struct_defaults => |ty| .{ .type_layout = ty }, 4345 .type_layout => |ty| switch (ip.indexToKey(ty)) { 4346 .struct_type => .{ .struct_defaults = ty }, 4347 .union_type, .enum_type, .opaque_type => break :queue_paired, 4348 else => unreachable, 4349 }, 4350 .@"comptime", .func, .memoized_state => break :queue_paired, 4351 }); 4352 const gop = try units.getOrPut(gpa, other); 4353 if (gop.found_existing) break :queue_paired; 4354 gop.value_ptr.* = units.values()[unit_idx - 1]; // same reference location 4355 } 4356 4357 refs_log.debug("handle unit '{f}'", .{zcu.fmtAnalUnit(unit)}); 4358 4359 if (zcu.reference_table.get(unit)) |first_ref_idx| { 4360 assert(first_ref_idx != std.math.maxInt(u32)); 4361 var ref_idx = first_ref_idx; 4362 while (ref_idx != std.math.maxInt(u32)) { 4363 const ref = zcu.all_references.items[ref_idx]; 4364 const gop = try units.getOrPut(gpa, ref.referenced); 4365 if (!gop.found_existing) { 4366 refs_log.debug("unit '{f}': ref unit '{f}'", .{ 4367 zcu.fmtAnalUnit(unit), 4368 zcu.fmtAnalUnit(ref.referenced), 4369 }); 4370 gop.value_ptr.* = .{ 4371 .referencer = unit, 4372 .src = ref.src, 4373 .inline_frame = ref.inline_frame, 4374 }; 4375 } 4376 ref_idx = ref.next; 4377 } 4378 } 4379 if (zcu.type_reference_table.get(unit)) |first_ref_idx| { 4380 assert(first_ref_idx != std.math.maxInt(u32)); 4381 var ref_idx = first_ref_idx; 4382 while (ref_idx != std.math.maxInt(u32)) { 4383 const ref = zcu.all_type_references.items[ref_idx]; 4384 const gop = try types.getOrPut(gpa, ref.referenced); 4385 if (!gop.found_existing) { 4386 refs_log.debug("unit '{f}': ref type '{f}'", .{ 4387 zcu.fmtAnalUnit(unit), 4388 Type.fromInterned(ref.referenced).containerTypeName(ip).fqn.fmt(ip), 4389 }); 4390 gop.value_ptr.* = .{ 4391 .referencer = unit, 4392 .src = ref.src, 4393 .inline_frame = .none, 4394 }; 4395 } 4396 ref_idx = ref.next; 4397 } 4398 } 4399 continue; 4400 } 4401 break; 4402 } 4403 4404 return units.move(); 4405 } 4406 4407 pub fn analysisRoots(zcu: *Zcu) []*Module { 4408 return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len]; 4409 } 4410 4411 pub fn fileByIndex(zcu: *const Zcu, file_index: File.Index) *File { 4412 return zcu.intern_pool.filePtr(file_index); 4413 } 4414 4415 /// Returns the struct that represents this `File`. 4416 /// If the struct has not been created, returns `.none`. 4417 pub fn fileRootType(zcu: *const Zcu, file_index: File.Index) InternPool.Index { 4418 const ip = &zcu.intern_pool; 4419 const file_index_unwrapped = file_index.unwrap(ip); 4420 const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire(); 4421 return files.view().items(.root_type)[file_index_unwrapped.index]; 4422 } 4423 4424 pub fn setFileRootType(zcu: *Zcu, file_index: File.Index, root_type: InternPool.Index) void { 4425 const ip = &zcu.intern_pool; 4426 const file_index_unwrapped = file_index.unwrap(ip); 4427 const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire(); 4428 files.view().items(.root_type)[file_index_unwrapped.index] = root_type; 4429 } 4430 4431 pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc { 4432 const ip = &zcu.intern_pool; 4433 return .{ 4434 .base_node_inst = ip.getNav(nav_index).srcInst(ip), 4435 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 4436 }; 4437 } 4438 4439 pub fn typeSrcLoc(zcu: *const Zcu, ty_index: InternPool.Index) LazySrcLoc { 4440 _ = zcu; 4441 _ = ty_index; 4442 @panic("TODO"); 4443 } 4444 4445 pub fn typeFileScope(zcu: *Zcu, ty_index: InternPool.Index) *File { 4446 _ = zcu; 4447 _ = ty_index; 4448 @panic("TODO"); 4449 } 4450 4451 pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { 4452 const ip = &zcu.intern_pool; 4453 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?; 4454 const zir = zcu.fileByIndex(inst_info.file).zir; 4455 return zir.?.getDeclaration(inst_info.inst).src_line; 4456 } 4457 4458 pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value { 4459 return .fromInterned(zcu.intern_pool.getNav(nav_index).resolved.?.value); 4460 } 4461 4462 pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index { 4463 const ip = &zcu.intern_pool; 4464 return ip.getNav(nav).srcInst(ip).resolveFile(ip); 4465 } 4466 4467 pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File { 4468 return zcu.fileByIndex(zcu.navFileScopeIndex(nav)); 4469 } 4470 4471 pub fn navAlignment(zcu: *Zcu, nav_id: InternPool.Nav.Index) InternPool.Alignment { 4472 const resolved = zcu.intern_pool.getNav(nav_id).resolved.?; 4473 return switch (resolved.@"align") { 4474 else => |a| a, 4475 .none => Type.fromInterned(resolved.type).abiAlignment(zcu), 4476 }; 4477 } 4478 4479 pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Alt(FormatAnalUnit, formatAnalUnit) { 4480 return .{ .data = .{ .unit = unit, .zcu = zcu } }; 4481 } 4482 pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Alt(FormatDependee, formatDependee) { 4483 return .{ .data = .{ .dependee = d, .zcu = zcu } }; 4484 } 4485 4486 const FormatAnalUnit = struct { unit: AnalUnit, zcu: *const Zcu }; 4487 fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void { 4488 const zcu = data.zcu; 4489 const ip = &zcu.intern_pool; 4490 switch (data.unit.unwrap()) { 4491 .@"comptime" => |cu_id| { 4492 const cu = ip.getComptimeUnit(cu_id); 4493 if (cu.zir_index.resolveFull(ip)) |resolved| { 4494 const file_path = zcu.fileByIndex(resolved.file).path; 4495 return writer.print("comptime(inst=('{f}', %{}) [{}])", .{ file_path.fmt(zcu.comp), @backingInt(resolved.inst), @backingInt(cu_id) }); 4496 } else { 4497 return writer.print("comptime(inst=<lost> [{}])", .{@backingInt(cu_id)}); 4498 } 4499 }, 4500 .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @backingInt(nav) }), 4501 .type_layout, .struct_defaults => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fqn.fmt(ip), @backingInt(ty) }), 4502 .func => |func| { 4503 const nav = zcu.funcInfo(func).owner_nav; 4504 return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @backingInt(func) }); 4505 }, 4506 .memoized_state => return writer.writeAll("memoized_state"), 4507 } 4508 } 4509 4510 const FormatDependee = struct { dependee: InternPool.Dependee, zcu: *const Zcu }; 4511 fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void { 4512 const zcu = data.zcu; 4513 const ip = &zcu.intern_pool; 4514 switch (data.dependee) { 4515 .src_hash => |ti| { 4516 const info = ti.resolveFull(ip) orelse { 4517 return writer.writeAll("inst(<lost>)"); 4518 }; 4519 const file_path = zcu.fileByIndex(info.file).path; 4520 return writer.print("inst('{f}', %{d})", .{ file_path.fmt(zcu.comp), @backingInt(info.inst) }); 4521 }, 4522 .nav_val, .nav_ty => |nav, tag| { 4523 const fqn = ip.getNav(nav).fqn; 4524 return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) }); 4525 }, 4526 .type_layout, .struct_defaults => |ip_index, tag| { 4527 const fqn = Type.fromInterned(ip_index).containerTypeName(ip).fqn; 4528 return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) }); 4529 }, 4530 .func_ies => |ip_index| { 4531 const fqn = ip.getNav(ip.indexToKey(ip_index).func.owner_nav).fqn; 4532 return writer.print("func_ies('{f}')", .{fqn.fmt(ip)}); 4533 }, 4534 .source_file => |file| { 4535 const file_path = zcu.fileByIndex(file).path; 4536 return writer.print("source_file('{f}')", .{file_path.fmt(zcu.comp)}); 4537 }, 4538 .embed_file => |ef_idx| { 4539 const ef = ef_idx.get(zcu); 4540 return writer.print("embed_file('{f}')", .{ef.path.fmt(zcu.comp)}); 4541 }, 4542 .namespace => |ti| { 4543 const info = ti.resolveFull(ip) orelse { 4544 return writer.writeAll("namespace(<lost>)"); 4545 }; 4546 const file_path = zcu.fileByIndex(info.file).path; 4547 return writer.print("namespace('{f}', %{d})", .{ file_path.fmt(zcu.comp), @backingInt(info.inst) }); 4548 }, 4549 .namespace_name => |k| { 4550 const info = k.namespace.resolveFull(ip) orelse { 4551 return writer.print("namespace(<lost>, '{f}')", .{k.name.fmt(ip)}); 4552 }; 4553 const file_path = zcu.fileByIndex(info.file).path; 4554 return writer.print("namespace('{f}', %{d}, '{f}')", .{ file_path.fmt(zcu.comp), @backingInt(info.inst), k.name.fmt(ip) }); 4555 }, 4556 .memoized_state => return writer.writeAll("memoized_state"), 4557 } 4558 } 4559 4560 pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) { 4561 ok, 4562 bad_arch: []const std.Target.Cpu.Arch, // value is allowed archs for cc 4563 bad_backend: std.lang.CompilerBackend, // value is current backend 4564 } { 4565 const target = zcu.getTarget(); 4566 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); 4567 switch (cc) { 4568 .auto, .@"inline" => return .ok, 4569 .async => return .{ .bad_backend = backend }, // nothing supports async currently 4570 .naked => {}, // depends only on backend 4571 else => for (cc.archs()) |allowed_arch| { 4572 if (allowed_arch == target.cpu.arch) break; 4573 } else return .{ .bad_arch = cc.archs() }, 4574 } 4575 const backend_ok = ok: switch (backend) { 4576 .stage1 => unreachable, 4577 .other => unreachable, 4578 _ => unreachable, 4579 4580 .stage2_llvm => { 4581 dev.check(.llvm_backend); 4582 break :ok @import("codegen/llvm.zig").toLlvmCallConv(cc, target) != null; 4583 }, 4584 .stage2_c => { 4585 dev.check(.c_backend); 4586 if (target.cCallingConvention()) |default_c| { 4587 if (cc.eql(default_c)) { 4588 break :ok true; 4589 } 4590 } 4591 break :ok switch (cc) { 4592 .x86_16_cdecl, 4593 .x86_16_stdcall, 4594 .x86_16_regparmcall, 4595 .x86_16_interrupt, 4596 .x86_64_sysv, 4597 .x86_64_win, 4598 .x86_64_vectorcall, 4599 .x86_64_regcall_v3_sysv, 4600 .x86_64_regcall_v4_win, 4601 .x86_64_interrupt, 4602 .x86_64_preserve_none, 4603 .x86_fastcall, 4604 .x86_thiscall, 4605 .x86_vectorcall, 4606 .x86_regcall_v3, 4607 .x86_regcall_v4_win, 4608 .x86_interrupt, 4609 .aarch64_vfabi, 4610 .aarch64_vfabi_sve, 4611 .aarch64_preserve_none, 4612 .arm_aapcs, 4613 .csky_interrupt, 4614 .riscv64_lp64_v, 4615 .riscv32_ilp32_v, 4616 .m68k_rtd, 4617 .m68k_interrupt, 4618 .msp430_interrupt, 4619 .arm_aapcs_vfp, 4620 .arc_interrupt, 4621 .arm_interrupt, 4622 .microblaze_interrupt, 4623 .mips_interrupt, 4624 .mips64_interrupt, 4625 .riscv32_interrupt, 4626 .riscv64_interrupt, 4627 .sh_interrupt, 4628 .avr_interrupt, 4629 .avr_signal, 4630 .ez80_tiflags, 4631 .naked, 4632 => true, // incoming stack alignment supported 4633 4634 .x86_sysv, 4635 .x86_win, 4636 .x86_mingw, 4637 .x86_stdcall, 4638 => |opts| opts.register_params == 0, // incoming stack alignment supported 4639 4640 else => false, 4641 }; 4642 }, 4643 .stage2_wasm => { 4644 dev.check(.wasm_backend); 4645 break :ok switch (cc) { 4646 .wasm_mvp => |opts| opts.incoming_stack_alignment == null, 4647 else => false, 4648 }; 4649 }, 4650 .stage2_arm => { 4651 dev.check(.arm_backend); 4652 break :ok switch (cc) { 4653 .arm_aapcs => |opts| opts.incoming_stack_alignment == null, 4654 .naked => true, 4655 else => false, 4656 }; 4657 }, 4658 .stage2_x86_64 => { 4659 dev.check(.x86_64_backend); 4660 break :ok switch (cc) { 4661 .x86_64_sysv, .x86_64_win, .naked => true, // incoming stack alignment supported 4662 else => false, 4663 }; 4664 }, 4665 .stage2_aarch64 => { 4666 dev.check(.aarch64_backend); 4667 break :ok switch (cc) { 4668 .aarch64_aapcs, .aarch64_aapcs_darwin, .naked => true, 4669 else => false, 4670 }; 4671 }, 4672 .stage2_x86 => { 4673 dev.check(.x86_backend); 4674 break :ok switch (cc) { 4675 .x86_sysv, 4676 .x86_win, 4677 .x86_mingw, 4678 => |opts| opts.incoming_stack_alignment == null and opts.register_params == 0, 4679 .naked => true, 4680 else => false, 4681 }; 4682 }, 4683 .stage2_powerpc => { 4684 dev.check(.powerpc_backend); 4685 break :ok switch (target.cpu.arch) { 4686 .powerpc, .powerpcle => switch (cc) { 4687 .powerpc_sysv, 4688 .powerpc_sysv_altivec, 4689 .powerpc_aix, 4690 .powerpc_aix_altivec, 4691 .naked, 4692 => true, 4693 else => false, 4694 }, 4695 .powerpc64, .powerpc64le => switch (cc) { 4696 .powerpc64_elf, 4697 .powerpc64_elf_altivec, 4698 .powerpc64_elf_v2, 4699 .naked, 4700 => true, 4701 else => false, 4702 }, 4703 else => unreachable, 4704 }; 4705 }, 4706 .stage2_riscv64 => { 4707 dev.check(.riscv64_backend); 4708 break :ok switch (cc) { 4709 .riscv64_lp64 => |opts| opts.incoming_stack_alignment == null, 4710 .naked => true, 4711 else => false, 4712 }; 4713 }, 4714 .stage2_sparc64 => { 4715 dev.check(.sparc64_backend); 4716 break :ok switch (cc) { 4717 .sparc64_sysv => |opts| opts.incoming_stack_alignment == null, 4718 .naked => true, 4719 else => false, 4720 }; 4721 }, 4722 .stage2_spirv => { 4723 dev.check(.spirv_backend); 4724 break :ok switch (cc) { 4725 .spirv_device, .spirv_kernel => true, 4726 .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl, 4727 .spirv_task, .spirv_mesh => target.os.tag == .vulkan, 4728 else => false, 4729 }; 4730 }, 4731 .stage2_loongarch => { 4732 dev.check(.loongarch_backend); 4733 break :ok switch (cc) { 4734 .loongarch64_lp64, .loongarch32_ilp32, .naked => true, 4735 else => false, 4736 }; 4737 }, 4738 .zsf_spork8 => { 4739 dev.check(.spork8_backend); 4740 break :ok switch (cc) { 4741 .spork8, .naked => true, 4742 else => false, 4743 }; 4744 }, 4745 }; 4746 if (!backend_ok) return .{ .bad_backend = backend }; 4747 return .ok; 4748 } 4749 4750 pub const CodegenFailError = Io.Cancelable || error{ 4751 /// Indicates the error message has been already stored at `Zcu.failed_codegen`. 4752 AlreadyReported, 4753 OutOfMemory, 4754 }; 4755 4756 pub fn codegenFail( 4757 zcu: *Zcu, 4758 nav_index: InternPool.Nav.Index, 4759 comptime format: []const u8, 4760 args: anytype, 4761 ) CodegenFailError { 4762 const msg = try Zcu.ErrorMsg.create(zcu.gpa, zcu.navSrcLoc(nav_index), format, args); 4763 return zcu.codegenFailMsg(nav_index, msg); 4764 } 4765 4766 /// Takes ownership of `msg`, even on OOM. 4767 pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError { 4768 const comp = zcu.comp; 4769 const gpa = comp.gpa; 4770 const io = comp.io; 4771 { 4772 comp.mutex.lockUncancelable(io); 4773 defer comp.mutex.unlock(io); 4774 errdefer msg.deinit(gpa); 4775 try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg); 4776 } 4777 return error.AlreadyReported; 4778 } 4779 4780 pub fn codegenFailType( 4781 zcu: *Zcu, 4782 ty_index: InternPool.Index, 4783 comptime format: []const u8, 4784 args: anytype, 4785 ) CodegenFailError { 4786 const gpa = zcu.gpa; 4787 try zcu.failed_types.ensureUnusedCapacity(gpa, 1); 4788 const msg = try Zcu.ErrorMsg.create(gpa, zcu.typeSrcLoc(ty_index), format, args); 4789 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); 4790 return error.AlreadyReported; 4791 } 4792 4793 pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) CodegenFailError { 4794 const gpa = zcu.gpa; 4795 { 4796 errdefer msg.deinit(gpa); 4797 try zcu.failed_types.ensureUnusedCapacity(gpa, 1); 4798 } 4799 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); 4800 return error.AlreadyReported; 4801 } 4802 4803 /// Asserts that `zcu.multi_module_err != null`. 4804 pub fn addFileInMultipleModulesError( 4805 zcu: *Zcu, 4806 eb: *std.zig.ErrorBundle.Wip, 4807 ) Allocator.Error!void { 4808 const gpa = zcu.gpa; 4809 4810 const info = zcu.multi_module_err.?; 4811 const file = info.file; 4812 4813 // error: file exists in modules 'root.foo' and 'root.bar' 4814 // note: files must belong to only one module 4815 // note: file is imported here 4816 // note: which is imported here 4817 // note: which is the root of module 'root.foo' imported here 4818 // note: file is the root of module 'root.bar' imported here 4819 4820 const file_src = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb); 4821 const root_msg = try eb.printString("file exists in modules '{s}' and '{s}'", .{ 4822 info.modules[0].fully_qualified_name, 4823 info.modules[1].fully_qualified_name, 4824 }); 4825 4826 var notes: std.ArrayList(std.zig.ErrorBundle.MessageIndex) = .empty; 4827 defer notes.deinit(gpa); 4828 4829 try notes.append(gpa, try eb.addErrorMessage(.{ 4830 .msg = try eb.addString("files must belong to only one module"), 4831 .src_loc = file_src, 4832 })); 4833 4834 try zcu.explainWhyFileIsInModule(eb, &notes, file, info.modules[0], info.refs[0]); 4835 try zcu.explainWhyFileIsInModule(eb, &notes, file, info.modules[1], info.refs[1]); 4836 4837 try eb.addRootErrorMessage(.{ 4838 .msg = root_msg, 4839 .src_loc = file_src, 4840 .notes_len = @intCast(notes.items.len), 4841 }); 4842 const notes_start = try eb.reserveNotes(@intCast(notes.items.len)); 4843 const notes_slice: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]); 4844 @memcpy(notes_slice, notes.items); 4845 } 4846 4847 fn explainWhyFileIsInModule( 4848 zcu: *Zcu, 4849 eb: *std.zig.ErrorBundle.Wip, 4850 notes_out: *std.ArrayList(std.zig.ErrorBundle.MessageIndex), 4851 file: File.Index, 4852 in_module: *Module, 4853 ref: File.Reference, 4854 ) Allocator.Error!void { 4855 const gpa = zcu.gpa; 4856 4857 // error: file is the root of module 'foo' 4858 // 4859 // error: file is imported here by the root of module 'foo' 4860 // 4861 // error: file is imported here 4862 // note: which is imported here 4863 // note: which is imported here by the root of module 'foo' 4864 4865 var import = switch (ref) { 4866 .analysis_root => |mod| { 4867 assert(mod == in_module); 4868 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4869 .msg = try eb.printString("file is the root of module '{s}'", .{mod.fully_qualified_name}), 4870 .src_loc = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb), 4871 })); 4872 return; 4873 }, 4874 .import => |import| if (import.module) |mod| { 4875 assert(mod == in_module); 4876 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4877 .msg = try eb.printString("file is the root of module '{s}'", .{mod.fully_qualified_name}), 4878 .src_loc = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb), 4879 })); 4880 return; 4881 } else import, 4882 }; 4883 4884 var is_first = true; 4885 while (true) { 4886 const thing: []const u8 = if (is_first) "file" else "which"; 4887 is_first = false; 4888 4889 const importer_file = zcu.fileByIndex(import.importer); 4890 // `errorBundleTokenSrc` expects the tree to be loaded 4891 _ = importer_file.getTree(zcu) catch |err| { 4892 try Compilation.unableToLoadZcuFile(zcu, eb, importer_file, err); 4893 return; // stop the explanation early 4894 }; 4895 const import_src = try importer_file.errorBundleTokenSrc(import.tok, zcu, eb); 4896 4897 const importer_ref = zcu.alive_files.get(import.importer).?; 4898 const importer_root: ?*Module = switch (importer_ref) { 4899 .analysis_root => |mod| mod, 4900 .import => |i| i.module, 4901 }; 4902 4903 if (importer_root) |m| { 4904 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4905 .msg = try eb.printString("{s} is imported here by the root of module '{s}'", .{ thing, m.fully_qualified_name }), 4906 .src_loc = import_src, 4907 })); 4908 return; 4909 } 4910 4911 try notes_out.append(gpa, try eb.addErrorMessage(.{ 4912 .msg = try eb.printString("{s} is imported here", .{thing}), 4913 .src_loc = import_src, 4914 })); 4915 4916 import = importer_ref.import; 4917 } 4918 } 4919 4920 pub fn addDependencyLoopErrors(zcu: *Zcu, eb: *std.zig.ErrorBundle.Wip) Allocator.Error!void { 4921 const gpa = zcu.comp.gpa; 4922 4923 const all_references = try zcu.resolveReferences(); 4924 4925 var units: std.ArrayList(AnalUnit) = .empty; 4926 defer units.deinit(gpa); 4927 4928 // TODO: sort the dependency loops somehow to make the error bundle reproducible 4929 for (zcu.dependency_loops.keys()) |arbitrary_unit| { 4930 units.clearRetainingCapacity(); 4931 4932 var cur = arbitrary_unit; 4933 while (true) { 4934 try units.append(gpa, cur); 4935 cur = zcu.dependency_loop_nodes.get(cur).?.unit; 4936 if (cur == arbitrary_unit) break; 4937 } 4938 4939 // `units` now contains all units in the loop. We need to pick a starting point somewhere 4940 // along that loop to begin. We will pick whichever node has the shortest reference trace, 4941 // because the other units may well just be referenced *by* that one! This is also likely 4942 // to match the user's intuition for where the loop "starts". 4943 var start_index: usize = 0; 4944 var start_depth: u32 = depth: { 4945 var depth: u32 = 0; 4946 var opt_ref = all_references.get(units.items[0]) orelse { 4947 // This dependency loop is actually unreferenced, so we don't need to emit a compile 4948 // error at all! Move onto the next dependency loop. 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