| author | |
| committer | |
| log | 895267c916f874593b0788b198b7de140b6b335b |
| tree | cd1256cc5f48f6bdbc1283bf19bb0f224f45d293 |
| parent | 2b05e85107dd1c637ab40f8b145b232d18e8d6c6 |
This commit makes more progress towards incremental compilation, fixing
some crashes in the frontend. Notably, it fixes the regressions introduced
by #20964. It also cleans up the "outdated file root" mechanism, by
virtue of deleting it: we now detect outdated file roots just after
updating ZIR refs, and re-scan their namespaces.9 files changed, 410 insertions(+), 305 deletions(-)
src/Compilation.zig+24-16| ... | @@ -3081,7 +3081,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 { | ... | @@ -3081,7 +3081,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 { |
| 3081 | for (zcu.failed_analysis.keys()) |anal_unit| { | 3081 | for (zcu.failed_analysis.keys()) |anal_unit| { |
| 3082 | const file_index = switch (anal_unit.unwrap()) { | 3082 | const file_index = switch (anal_unit.unwrap()) { |
| 3083 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, | 3083 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, |
| 3084 | .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip).file, | 3084 | .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file, |
| 3085 | }; | 3085 | }; |
| 3086 | if (zcu.fileByIndex(file_index).okToReportErrors()) { | 3086 | if (zcu.fileByIndex(file_index).okToReportErrors()) { |
| 3087 | total += 1; | 3087 | total += 1; |
| ... | @@ -3091,11 +3091,13 @@ pub fn totalErrorCount(comp: *Compilation) u32 { | ... | @@ -3091,11 +3091,13 @@ pub fn totalErrorCount(comp: *Compilation) u32 { |
| 3091 | } | 3091 | } |
| 3092 | } | 3092 | } |
| 3093 | 3093 | ||
| 3094 | if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) { | 3094 | for (zcu.failed_codegen.keys()) |nav| { |
| 3095 | total += 1; | 3095 | if (zcu.navFileScope(nav).okToReportErrors()) { |
| 3096 | total += 1; | ||
| 3097 | } | ||
| 3096 | } | 3098 | } |
| 3097 | 3099 | ||
| 3098 | for (zcu.failed_codegen.keys()) |_| { | 3100 | if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) { |
| 3099 | total += 1; | 3101 | total += 1; |
| 3100 | } | 3102 | } |
| 3101 | } | 3103 | } |
| ... | @@ -3114,7 +3116,13 @@ pub fn totalErrorCount(comp: *Compilation) u32 { | ... | @@ -3114,7 +3116,13 @@ pub fn totalErrorCount(comp: *Compilation) u32 { |
| 3114 | } | 3116 | } |
| 3115 | } | 3117 | } |
| 3116 | 3118 | ||
| 3117 | return @as(u32, @intCast(total)); | 3119 | if (comp.module) |zcu| { |
| 3120 | if (total == 0 and zcu.transitive_failed_analysis.count() > 0) { | ||
| 3121 | @panic("Transitive analysis errors, but none actually emitted"); | ||
| 3122 | } | ||
| 3123 | } | ||
| 3124 | |||
| 3125 | return @intCast(total); | ||
| 3118 | } | 3126 | } |
| 3119 | 3127 | ||
| 3120 | /// This function is temporally single-threaded. | 3128 | /// This function is temporally single-threaded. |
| ... | @@ -3214,7 +3222,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3214,7 +3222,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3214 | for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| { | 3222 | for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| { |
| 3215 | const file_index = switch (anal_unit.unwrap()) { | 3223 | const file_index = switch (anal_unit.unwrap()) { |
| 3216 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, | 3224 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, |
| 3217 | .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip).file, | 3225 | .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file, |
| 3218 | }; | 3226 | }; |
| 3219 | 3227 | ||
| 3220 | // Skip errors for AnalUnits within files that had a parse failure. | 3228 | // Skip errors for AnalUnits within files that had a parse failure. |
| ... | @@ -3243,7 +3251,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3243,7 +3251,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3243 | } | 3251 | } |
| 3244 | } | 3252 | } |
| 3245 | } | 3253 | } |
| 3246 | for (zcu.failed_codegen.values()) |error_msg| { | 3254 | for (zcu.failed_codegen.keys(), zcu.failed_codegen.values()) |nav, error_msg| { |
| 3255 | if (!zcu.navFileScope(nav).okToReportErrors()) continue; | ||
| 3247 | try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references); | 3256 | try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references); |
| 3248 | } | 3257 | } |
| 3249 | for (zcu.failed_exports.values()) |value| { | 3258 | for (zcu.failed_exports.values()) |value| { |
| ... | @@ -3608,10 +3617,9 @@ fn performAllTheWorkInner( | ... | @@ -3608,10 +3617,9 @@ fn performAllTheWorkInner( |
| 3608 | // Pre-load these things from our single-threaded context since they | 3617 | // Pre-load these things from our single-threaded context since they |
| 3609 | // will be needed by the worker threads. | 3618 | // will be needed by the worker threads. |
| 3610 | const path_digest = zcu.filePathDigest(file_index); | 3619 | const path_digest = zcu.filePathDigest(file_index); |
| 3611 | const old_root_type = zcu.fileRootType(file_index); | ||
| 3612 | const file = zcu.fileByIndex(file_index); | 3620 | const file = zcu.fileByIndex(file_index); |
| 3613 | comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{ | 3621 | comp.thread_pool.spawnWgId(&astgen_wait_group, workerAstGenFile, .{ |
| 3614 | comp, file, file_index, path_digest, old_root_type, zir_prog_node, &astgen_wait_group, .root, | 3622 | comp, file, file_index, path_digest, zir_prog_node, &astgen_wait_group, .root, |
| 3615 | }); | 3623 | }); |
| 3616 | } | 3624 | } |
| 3617 | } | 3625 | } |
| ... | @@ -3649,6 +3657,7 @@ fn performAllTheWorkInner( | ... | @@ -3649,6 +3657,7 @@ fn performAllTheWorkInner( |
| 3649 | } | 3657 | } |
| 3650 | try reportMultiModuleErrors(pt); | 3658 | try reportMultiModuleErrors(pt); |
| 3651 | try zcu.flushRetryableFailures(); | 3659 | try zcu.flushRetryableFailures(); |
| 3660 | |||
| 3652 | zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); | 3661 | zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); |
| 3653 | zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); | 3662 | zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); |
| 3654 | } | 3663 | } |
| ... | @@ -4283,7 +4292,6 @@ fn workerAstGenFile( | ... | @@ -4283,7 +4292,6 @@ fn workerAstGenFile( |
| 4283 | file: *Zcu.File, | 4292 | file: *Zcu.File, |
| 4284 | file_index: Zcu.File.Index, | 4293 | file_index: Zcu.File.Index, |
| 4285 | path_digest: Cache.BinDigest, | 4294 | path_digest: Cache.BinDigest, |
| 4286 | old_root_type: InternPool.Index, | ||
| 4287 | prog_node: std.Progress.Node, | 4295 | prog_node: std.Progress.Node, |
| 4288 | wg: *WaitGroup, | 4296 | wg: *WaitGroup, |
| 4289 | src: Zcu.AstGenSrc, | 4297 | src: Zcu.AstGenSrc, |
| ... | @@ -4292,7 +4300,7 @@ fn workerAstGenFile( | ... | @@ -4292,7 +4300,7 @@ fn workerAstGenFile( |
| 4292 | defer child_prog_node.end(); | 4300 | defer child_prog_node.end(); |
| 4293 | 4301 | ||
| 4294 | const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) }; | 4302 | const pt: Zcu.PerThread = .{ .zcu = comp.module.?, .tid = @enumFromInt(tid) }; |
| 4295 | pt.astGenFile(file, path_digest, old_root_type) catch |err| switch (err) { | 4303 | pt.astGenFile(file, path_digest) catch |err| switch (err) { |
| 4296 | error.AnalysisFail => return, | 4304 | error.AnalysisFail => return, |
| 4297 | else => { | 4305 | else => { |
| 4298 | file.status = .retryable_failure; | 4306 | file.status = .retryable_failure; |
| ... | @@ -4323,7 +4331,7 @@ fn workerAstGenFile( | ... | @@ -4323,7 +4331,7 @@ fn workerAstGenFile( |
| 4323 | // `@import("builtin")` is handled specially. | 4331 | // `@import("builtin")` is handled specially. |
| 4324 | if (mem.eql(u8, import_path, "builtin")) continue; | 4332 | if (mem.eql(u8, import_path, "builtin")) continue; |
| 4325 | 4333 | ||
| 4326 | const import_result, const imported_path_digest, const imported_root_type = blk: { | 4334 | const import_result, const imported_path_digest = blk: { |
| 4327 | comp.mutex.lock(); | 4335 | comp.mutex.lock(); |
| 4328 | defer comp.mutex.unlock(); | 4336 | defer comp.mutex.unlock(); |
| 4329 | 4337 | ||
| ... | @@ -4338,8 +4346,7 @@ fn workerAstGenFile( | ... | @@ -4338,8 +4346,7 @@ fn workerAstGenFile( |
| 4338 | comp.appendFileSystemInput(fsi, res.file.mod.root, res.file.sub_file_path) catch continue; | 4346 | comp.appendFileSystemInput(fsi, res.file.mod.root, res.file.sub_file_path) catch continue; |
| 4339 | }; | 4347 | }; |
| 4340 | const imported_path_digest = pt.zcu.filePathDigest(res.file_index); | 4348 | const imported_path_digest = pt.zcu.filePathDigest(res.file_index); |
| 4341 | const imported_root_type = pt.zcu.fileRootType(res.file_index); | 4349 | break :blk .{ res, imported_path_digest }; |
| 4342 | break :blk .{ res, imported_path_digest, imported_root_type }; | ||
| 4343 | }; | 4350 | }; |
| 4344 | if (import_result.is_new) { | 4351 | if (import_result.is_new) { |
| 4345 | log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ | 4352 | log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ |
| ... | @@ -4350,7 +4357,7 @@ fn workerAstGenFile( | ... | @@ -4350,7 +4357,7 @@ fn workerAstGenFile( |
| 4350 | .import_tok = item.data.token, | 4357 | .import_tok = item.data.token, |
| 4351 | } }; | 4358 | } }; |
| 4352 | comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{ | 4359 | comp.thread_pool.spawnWgId(wg, workerAstGenFile, .{ |
| 4353 | comp, import_result.file, import_result.file_index, imported_path_digest, imported_root_type, prog_node, wg, sub_src, | 4360 | comp, import_result.file, import_result.file_index, imported_path_digest, prog_node, wg, sub_src, |
| 4354 | }); | 4361 | }); |
| 4355 | } | 4362 | } |
| 4356 | } | 4363 | } |
| ... | @@ -6443,7 +6450,8 @@ fn buildOutputFromZig( | ... | @@ -6443,7 +6450,8 @@ fn buildOutputFromZig( |
| 6443 | 6450 | ||
| 6444 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); | 6451 | try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); |
| 6445 | 6452 | ||
| 6446 | assert(out.* == null); | 6453 | // Under incremental compilation, `out` may already be populated from a prior update. |
| 6454 | assert(out.* == null or comp.incremental); | ||
| 6447 | out.* = try sub_compilation.toCrtFile(); | 6455 | out.* = try sub_compilation.toCrtFile(); |
| 6448 | } | 6456 | } |
| 6449 | 6457 |
src/InternPool.zig+135-17| ... | @@ -65,19 +65,49 @@ pub const single_threaded = builtin.single_threaded or !want_multi_threaded; | ... | @@ -65,19 +65,49 @@ pub const single_threaded = builtin.single_threaded or !want_multi_threaded; |
| 65 | pub const TrackedInst = extern struct { | 65 | pub const TrackedInst = extern struct { |
| 66 | file: FileIndex, | 66 | file: FileIndex, |
| 67 | inst: Zir.Inst.Index, | 67 | inst: Zir.Inst.Index, |
| 68 | comptime { | 68 | |
| 69 | // The fields should be tightly packed. See also serialiation logic in `Compilation.saveState`. | 69 | pub const MaybeLost = extern struct { |
| 70 | assert(@sizeOf(@This()) == @sizeOf(FileIndex) + @sizeOf(Zir.Inst.Index)); | 70 | file: FileIndex, |
| 71 | } | 71 | inst: ZirIndex, |
| 72 | pub const ZirIndex = enum(u32) { | ||
| 73 | /// Tracking failed for this ZIR instruction. Uses of it should fail. | ||
| 74 | lost = std.math.maxInt(u32), | ||
| 75 | _, | ||
| 76 | pub fn unwrap(inst: ZirIndex) ?Zir.Inst.Index { | ||
| 77 | return switch (inst) { | ||
| 78 | .lost => null, | ||
| 79 | _ => @enumFromInt(@intFromEnum(inst)), | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | pub fn wrap(inst: Zir.Inst.Index) ZirIndex { | ||
| 83 | return @enumFromInt(@intFromEnum(inst)); | ||
| 84 | } | ||
| 85 | }; | ||
| 86 | comptime { | ||
| 87 | // The fields should be tightly packed. See also serialiation logic in `Compilation.saveState`. | ||
| 88 | assert(@sizeOf(@This()) == @sizeOf(FileIndex) + @sizeOf(ZirIndex)); | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | |||
| 72 | pub const Index = enum(u32) { | 92 | pub const Index = enum(u32) { |
| 73 | _, | 93 | _, |
| 74 | pub fn resolveFull(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) TrackedInst { | 94 | pub fn resolveFull(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) ?TrackedInst { |
| 75 | const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip); | 95 | const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip); |
| 76 | const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire(); | 96 | const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire(); |
| 77 | return tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index]; | 97 | const maybe_lost = tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index]; |
| 98 | return .{ | ||
| 99 | .file = maybe_lost.file, | ||
| 100 | .inst = maybe_lost.inst.unwrap() orelse return null, | ||
| 101 | }; | ||
| 78 | } | 102 | } |
| 79 | pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Zir.Inst.Index { | 103 | pub fn resolveFile(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) FileIndex { |
| 80 | return i.resolveFull(ip).inst; | 104 | const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip); |
| 105 | const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire(); | ||
| 106 | const maybe_lost = tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index]; | ||
| 107 | return maybe_lost.file; | ||
| 108 | } | ||
| 109 | pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) ?Zir.Inst.Index { | ||
| 110 | return (i.resolveFull(ip) orelse return null).inst; | ||
| 81 | } | 111 | } |
| 82 | 112 | ||
| 83 | pub fn toOptional(i: TrackedInst.Index) Optional { | 113 | pub fn toOptional(i: TrackedInst.Index) Optional { |
| ... | @@ -120,7 +150,11 @@ pub fn trackZir( | ... | @@ -120,7 +150,11 @@ pub fn trackZir( |
| 120 | tid: Zcu.PerThread.Id, | 150 | tid: Zcu.PerThread.Id, |
| 121 | key: TrackedInst, | 151 | key: TrackedInst, |
| 122 | ) Allocator.Error!TrackedInst.Index { | 152 | ) Allocator.Error!TrackedInst.Index { |
| 123 | const full_hash = Hash.hash(0, std.mem.asBytes(&key)); | 153 | const maybe_lost_key: TrackedInst.MaybeLost = .{ |
| 154 | .file = key.file, | ||
| 155 | .inst = TrackedInst.MaybeLost.ZirIndex.wrap(key.inst), | ||
| 156 | }; | ||
| 157 | const full_hash = Hash.hash(0, std.mem.asBytes(&maybe_lost_key)); | ||
| 124 | const hash: u32 = @truncate(full_hash >> 32); | 158 | const hash: u32 = @truncate(full_hash >> 32); |
| 125 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; | 159 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; |
| 126 | var map = shard.shared.tracked_inst_map.acquire(); | 160 | var map = shard.shared.tracked_inst_map.acquire(); |
| ... | @@ -132,12 +166,11 @@ pub fn trackZir( | ... | @@ -132,12 +166,11 @@ pub fn trackZir( |
| 132 | const entry = &map.entries[map_index]; | 166 | const entry = &map.entries[map_index]; |
| 133 | const index = entry.acquire().unwrap() orelse break; | 167 | const index = entry.acquire().unwrap() orelse break; |
| 134 | if (entry.hash != hash) continue; | 168 | if (entry.hash != hash) continue; |
| 135 | if (std.meta.eql(index.resolveFull(ip), key)) return index; | 169 | if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index; |
| 136 | } | 170 | } |
| 137 | shard.mutate.tracked_inst_map.mutex.lock(); | 171 | shard.mutate.tracked_inst_map.mutex.lock(); |
| 138 | defer shard.mutate.tracked_inst_map.mutex.unlock(); | 172 | defer shard.mutate.tracked_inst_map.mutex.unlock(); |
| 139 | if (map.entries != shard.shared.tracked_inst_map.entries) { | 173 | if (map.entries != shard.shared.tracked_inst_map.entries) { |
| 140 | shard.mutate.tracked_inst_map.len += 1; | ||
| 141 | map = shard.shared.tracked_inst_map; | 174 | map = shard.shared.tracked_inst_map; |
| 142 | map_mask = map.header().mask(); | 175 | map_mask = map.header().mask(); |
| 143 | map_index = hash; | 176 | map_index = hash; |
| ... | @@ -147,7 +180,7 @@ pub fn trackZir( | ... | @@ -147,7 +180,7 @@ pub fn trackZir( |
| 147 | const entry = &map.entries[map_index]; | 180 | const entry = &map.entries[map_index]; |
| 148 | const index = entry.acquire().unwrap() orelse break; | 181 | const index = entry.acquire().unwrap() orelse break; |
| 149 | if (entry.hash != hash) continue; | 182 | if (entry.hash != hash) continue; |
| 150 | if (std.meta.eql(index.resolveFull(ip), key)) return index; | 183 | if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index; |
| 151 | } | 184 | } |
| 152 | defer shard.mutate.tracked_inst_map.len += 1; | 185 | defer shard.mutate.tracked_inst_map.len += 1; |
| 153 | const local = ip.getLocal(tid); | 186 | const local = ip.getLocal(tid); |
| ... | @@ -161,7 +194,7 @@ pub fn trackZir( | ... | @@ -161,7 +194,7 @@ pub fn trackZir( |
| 161 | .tid = tid, | 194 | .tid = tid, |
| 162 | .index = list.mutate.len, | 195 | .index = list.mutate.len, |
| 163 | }).wrap(ip); | 196 | }).wrap(ip); |
| 164 | list.appendAssumeCapacity(.{key}); | 197 | list.appendAssumeCapacity(.{maybe_lost_key}); |
| 165 | entry.release(index.toOptional()); | 198 | entry.release(index.toOptional()); |
| 166 | return index; | 199 | return index; |
| 167 | } | 200 | } |
| ... | @@ -205,12 +238,91 @@ pub fn trackZir( | ... | @@ -205,12 +238,91 @@ pub fn trackZir( |
| 205 | .tid = tid, | 238 | .tid = tid, |
| 206 | .index = list.mutate.len, | 239 | .index = list.mutate.len, |
| 207 | }).wrap(ip); | 240 | }).wrap(ip); |
| 208 | list.appendAssumeCapacity(.{key}); | 241 | list.appendAssumeCapacity(.{maybe_lost_key}); |
| 209 | map.entries[map_index] = .{ .value = index.toOptional(), .hash = hash }; | 242 | map.entries[map_index] = .{ .value = index.toOptional(), .hash = hash }; |
| 210 | shard.shared.tracked_inst_map.release(new_map); | 243 | shard.shared.tracked_inst_map.release(new_map); |
| 211 | return index; | 244 | return index; |
| 212 | } | 245 | } |
| 213 | 246 | ||
| 247 | pub fn rehashTrackedInsts( | ||
| 248 | ip: *InternPool, | ||
| 249 | gpa: Allocator, | ||
| 250 | /// TODO: maybe don't take this? it doesn't actually matter, only one thread is running at this point | ||
| 251 | tid: Zcu.PerThread.Id, | ||
| 252 | ) Allocator.Error!void { | ||
| 253 | // TODO: this function doesn't handle OOM well. What should it do? | ||
| 254 | // Indeed, what should anyone do when they run out of memory? | ||
| 255 | |||
| 256 | // We don't lock anything, as this function assumes that no other thread is | ||
| 257 | // accessing `tracked_insts`. This is necessary because we're going to be | ||
| 258 | // iterating the `TrackedInst`s in each `Local`, so we have to know that | ||
| 259 | // none will be added as we work. | ||
| 260 | |||
| 261 | // Figure out how big each shard need to be and store it in its mutate `len`. | ||
| 262 | for (ip.shards) |*shard| shard.mutate.tracked_inst_map.len = 0; | ||
| 263 | for (ip.locals) |*local| { | ||
| 264 | // `getMutableTrackedInsts` is okay only because no other thread is currently active. | ||
| 265 | // We need the `mutate` for the len. | ||
| 266 | for (local.getMutableTrackedInsts(gpa).viewAllowEmpty().items(.@"0")) |tracked_inst| { | ||
| 267 | if (tracked_inst.inst == .lost) continue; // we can ignore this one! | ||
| 268 | const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst)); | ||
| 269 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; | ||
| 270 | shard.mutate.tracked_inst_map.len += 1; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | const Map = Shard.Map(TrackedInst.Index.Optional); | ||
| 275 | |||
| 276 | const arena_state = &ip.getLocal(tid).mutate.arena; | ||
| 277 | |||
| 278 | // We know how big each shard must be, so ensure we have the capacity we need. | ||
| 279 | for (ip.shards) |*shard| { | ||
| 280 | const want_capacity = std.math.ceilPowerOfTwo(u32, shard.mutate.tracked_inst_map.len * 5 / 3) catch unreachable; | ||
| 281 | const have_capacity = shard.shared.tracked_inst_map.header().capacity; // no acquire because we hold the mutex | ||
| 282 | if (have_capacity >= want_capacity) { | ||
| 283 | @memset(shard.shared.tracked_inst_map.entries[0..have_capacity], .{ .value = .none, .hash = undefined }); | ||
| 284 | continue; | ||
| 285 | } | ||
| 286 | var arena = arena_state.promote(gpa); | ||
| 287 | defer arena_state.* = arena.state; | ||
| 288 | const new_map_buf = try arena.allocator().alignedAlloc( | ||
| 289 | u8, | ||
| 290 | Map.alignment, | ||
| 291 | Map.entries_offset + want_capacity * @sizeOf(Map.Entry), | ||
| 292 | ); | ||
| 293 | const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) }; | ||
| 294 | new_map.header().* = .{ .capacity = want_capacity }; | ||
| 295 | @memset(new_map.entries[0..want_capacity], .{ .value = .none, .hash = undefined }); | ||
| 296 | shard.shared.tracked_inst_map.release(new_map); | ||
| 297 | } | ||
| 298 | |||
| 299 | // Now, actually insert the items. | ||
| 300 | for (ip.locals, 0..) |*local, local_tid| { | ||
| 301 | // `getMutableTrackedInsts` is okay only because no other thread is currently active. | ||
| 302 | // We need the `mutate` for the len. | ||
| 303 | for (local.getMutableTrackedInsts(gpa).viewAllowEmpty().items(.@"0"), 0..) |tracked_inst, local_inst_index| { | ||
| 304 | if (tracked_inst.inst == .lost) continue; // we can ignore this one! | ||
| 305 | const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst)); | ||
| 306 | const hash: u32 = @truncate(full_hash >> 32); | ||
| 307 | const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; | ||
| 308 | const map = shard.shared.tracked_inst_map; // no acquire because we hold the mutex | ||
| 309 | const map_mask = map.header().mask(); | ||
| 310 | var map_index = hash; | ||
| 311 | const entry = while (true) : (map_index += 1) { | ||
| 312 | map_index &= map_mask; | ||
| 313 | const entry = &map.entries[map_index]; | ||
| 314 | if (entry.acquire() == .none) break entry; | ||
| 315 | }; | ||
| 316 | const index = TrackedInst.Index.Unwrapped.wrap(.{ | ||
| 317 | .tid = @enumFromInt(local_tid), | ||
| 318 | .index = @intCast(local_inst_index), | ||
| 319 | }, ip); | ||
| 320 | entry.hash = hash; | ||
| 321 | entry.release(index.toOptional()); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 214 | /// Analysis Unit. Represents a single entity which undergoes semantic analysis. | 326 | /// Analysis Unit. Represents a single entity which undergoes semantic analysis. |
| 215 | /// This is either a `Cau` or a runtime function. | 327 | /// This is either a `Cau` or a runtime function. |
| 216 | /// The LSB is used as a tag bit. | 328 | /// The LSB is used as a tag bit. |
| ... | @@ -728,7 +840,7 @@ const Local = struct { | ... | @@ -728,7 +840,7 @@ const Local = struct { |
| 728 | else => @compileError("unsupported host"), | 840 | else => @compileError("unsupported host"), |
| 729 | }; | 841 | }; |
| 730 | const Strings = List(struct { u8 }); | 842 | const Strings = List(struct { u8 }); |
| 731 | const TrackedInsts = List(struct { TrackedInst }); | 843 | const TrackedInsts = List(struct { TrackedInst.MaybeLost }); |
| 732 | const Maps = List(struct { FieldMap }); | 844 | const Maps = List(struct { FieldMap }); |
| 733 | const Caus = List(struct { Cau }); | 845 | const Caus = List(struct { Cau }); |
| 734 | const Navs = List(Nav.Repr); | 846 | const Navs = List(Nav.Repr); |
| ... | @@ -959,6 +1071,14 @@ const Local = struct { | ... | @@ -959,6 +1071,14 @@ const Local = struct { |
| 959 | mutable.list.release(new_list); | 1071 | mutable.list.release(new_list); |
| 960 | } | 1072 | } |
| 961 | 1073 | ||
| 1074 | pub fn viewAllowEmpty(mutable: Mutable) View { | ||
| 1075 | const capacity = mutable.list.header().capacity; | ||
| 1076 | return .{ | ||
| 1077 | .bytes = mutable.list.bytes, | ||
| 1078 | .len = mutable.mutate.len, | ||
| 1079 | .capacity = capacity, | ||
| 1080 | }; | ||
| 1081 | } | ||
| 962 | pub fn view(mutable: Mutable) View { | 1082 | pub fn view(mutable: Mutable) View { |
| 963 | const capacity = mutable.list.header().capacity; | 1083 | const capacity = mutable.list.header().capacity; |
| 964 | assert(capacity > 0); // optimizes `MultiArrayList.Slice.items` | 1084 | assert(capacity > 0); // optimizes `MultiArrayList.Slice.items` |
| ... | @@ -996,7 +1116,6 @@ const Local = struct { | ... | @@ -996,7 +1116,6 @@ const Local = struct { |
| 996 | fn header(list: ListSelf) *Header { | 1116 | fn header(list: ListSelf) *Header { |
| 997 | return @ptrFromInt(@intFromPtr(list.bytes) - bytes_offset); | 1117 | return @ptrFromInt(@intFromPtr(list.bytes) - bytes_offset); |
| 998 | } | 1118 | } |
| 999 | |||
| 1000 | pub fn view(list: ListSelf) View { | 1119 | pub fn view(list: ListSelf) View { |
| 1001 | const capacity = list.header().capacity; | 1120 | const capacity = list.header().capacity; |
| 1002 | assert(capacity > 0); // optimizes `MultiArrayList.Slice.items` | 1121 | assert(capacity > 0); // optimizes `MultiArrayList.Slice.items` |
| ... | @@ -11000,7 +11119,6 @@ pub fn getOrPutTrailingString( | ... | @@ -11000,7 +11119,6 @@ pub fn getOrPutTrailingString( |
| 11000 | shard.mutate.string_map.mutex.lock(); | 11119 | shard.mutate.string_map.mutex.lock(); |
| 11001 | defer shard.mutate.string_map.mutex.unlock(); | 11120 | defer shard.mutate.string_map.mutex.unlock(); |
| 11002 | if (map.entries != shard.shared.string_map.entries) { | 11121 | if (map.entries != shard.shared.string_map.entries) { |
| 11003 | shard.mutate.string_map.len += 1; | ||
| 11004 | map = shard.shared.string_map; | 11122 | map = shard.shared.string_map; |
| 11005 | map_mask = map.header().mask(); | 11123 | map_mask = map.header().mask(); |
| 11006 | map_index = hash; | 11124 | map_index = hash; |
src/Sema.zig+14-15| ... | @@ -999,7 +999,7 @@ fn analyzeBodyInner( | ... | @@ -999,7 +999,7 @@ fn analyzeBodyInner( |
| 999 | // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. | 999 | // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. |
| 1000 | if (build_options.enable_logging) { | 1000 | if (build_options.enable_logging) { |
| 1001 | std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ sub_file_path: { | 1001 | std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ sub_file_path: { |
| 1002 | const file_index = block.src_base_inst.resolveFull(&zcu.intern_pool).file; | 1002 | const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); |
| 1003 | const file = zcu.fileByIndex(file_index); | 1003 | const file = zcu.fileByIndex(file_index); |
| 1004 | break :sub_file_path file.sub_file_path; | 1004 | break :sub_file_path file.sub_file_path; |
| 1005 | }, inst }); | 1005 | }, inst }); |
| ... | @@ -2873,7 +2873,7 @@ fn createTypeName( | ... | @@ -2873,7 +2873,7 @@ fn createTypeName( |
| 2873 | .anon => {}, // handled after switch | 2873 | .anon => {}, // handled after switch |
| 2874 | .parent => return block.type_name_ctx, | 2874 | .parent => return block.type_name_ctx, |
| 2875 | .func => func_strat: { | 2875 | .func => func_strat: { |
| 2876 | const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip)); | 2876 | const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail); |
| 2877 | const zir_tags = sema.code.instructions.items(.tag); | 2877 | const zir_tags = sema.code.instructions.items(.tag); |
| 2878 | 2878 | ||
| 2879 | var buf: std.ArrayListUnmanaged(u8) = .{}; | 2879 | var buf: std.ArrayListUnmanaged(u8) = .{}; |
| ... | @@ -5487,7 +5487,7 @@ fn failWithBadMemberAccess( | ... | @@ -5487,7 +5487,7 @@ fn failWithBadMemberAccess( |
| 5487 | .Enum => "enum", | 5487 | .Enum => "enum", |
| 5488 | else => unreachable, | 5488 | else => unreachable, |
| 5489 | }; | 5489 | }; |
| 5490 | if (agg_ty.typeDeclInst(zcu)) |inst| if (inst.resolve(ip) == .main_struct_inst) { | 5490 | if (agg_ty.typeDeclInst(zcu)) |inst| if ((inst.resolve(ip) orelse return error.AnalysisFail) == .main_struct_inst) { |
| 5491 | return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{ | 5491 | return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{ |
| 5492 | agg_ty.fmt(pt), field_name.fmt(ip), | 5492 | agg_ty.fmt(pt), field_name.fmt(ip), |
| 5493 | }); | 5493 | }); |
| ... | @@ -6041,8 +6041,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -6041,8 +6041,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr |
| 6041 | return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); | 6041 | return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); |
| 6042 | 6042 | ||
| 6043 | const path_digest = zcu.filePathDigest(result.file_index); | 6043 | const path_digest = zcu.filePathDigest(result.file_index); |
| 6044 | const old_root_type = zcu.fileRootType(result.file_index); | 6044 | pt.astGenFile(result.file, path_digest) catch |err| |
| 6045 | pt.astGenFile(result.file, path_digest, old_root_type) catch |err| | ||
| 6046 | return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); | 6045 | return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); |
| 6047 | 6046 | ||
| 6048 | // TODO: register some kind of dependency on the file. | 6047 | // TODO: register some kind of dependency on the file. |
| ... | @@ -7778,7 +7777,7 @@ fn analyzeCall( | ... | @@ -7778,7 +7777,7 @@ fn analyzeCall( |
| 7778 | // the AIR instructions of the callsite. The callee could be a generic function | 7777 | // the AIR instructions of the callsite. The callee could be a generic function |
| 7779 | // which means its parameter type expressions must be resolved in order and used | 7778 | // which means its parameter type expressions must be resolved in order and used |
| 7780 | // to successively coerce the arguments. | 7779 | // to successively coerce the arguments. |
| 7781 | const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst.resolve(ip)); | 7780 | const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst.resolve(ip) orelse return error.AnalysisFail); |
| 7782 | try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); | 7781 | try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); |
| 7783 | 7782 | ||
| 7784 | var arg_i: u32 = 0; | 7783 | var arg_i: u32 = 0; |
| ... | @@ -7823,7 +7822,7 @@ fn analyzeCall( | ... | @@ -7823,7 +7822,7 @@ fn analyzeCall( |
| 7823 | // each of the parameters, resolving the return type and providing it to the child | 7822 | // each of the parameters, resolving the return type and providing it to the child |
| 7824 | // `Sema` so that it can be used for the `ret_ptr` instruction. | 7823 | // `Sema` so that it can be used for the `ret_ptr` instruction. |
| 7825 | const ret_ty_inst = if (fn_info.ret_ty_body.len != 0) | 7824 | const ret_ty_inst = if (fn_info.ret_ty_body.len != 0) |
| 7826 | try sema.resolveInlineBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip)) | 7825 | try sema.resolveInlineBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip) orelse return error.AnalysisFail) |
| 7827 | else | 7826 | else |
| 7828 | try sema.resolveInst(fn_info.ret_ty_ref); | 7827 | try sema.resolveInst(fn_info.ret_ty_ref); |
| 7829 | const ret_ty_src: LazySrcLoc = .{ .base_node_inst = module_fn.zir_body_inst, .offset = .{ .node_offset_fn_type_ret_ty = 0 } }; | 7828 | const ret_ty_src: LazySrcLoc = .{ .base_node_inst = module_fn.zir_body_inst, .offset = .{ .node_offset_fn_type_ret_ty = 0 } }; |
| ... | @@ -8210,7 +8209,7 @@ fn instantiateGenericCall( | ... | @@ -8210,7 +8209,7 @@ fn instantiateGenericCall( |
| 8210 | const fn_nav = ip.getNav(generic_owner_func.owner_nav); | 8209 | const fn_nav = ip.getNav(generic_owner_func.owner_nav); |
| 8211 | const fn_cau = ip.getCau(fn_nav.analysis_owner.unwrap().?); | 8210 | const fn_cau = ip.getCau(fn_nav.analysis_owner.unwrap().?); |
| 8212 | const fn_zir = zcu.namespacePtr(fn_cau.namespace).fileScope(zcu).zir; | 8211 | const fn_zir = zcu.namespacePtr(fn_cau.namespace).fileScope(zcu).zir; |
| 8213 | const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip)); | 8212 | const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip) orelse return error.AnalysisFail); |
| 8214 | 8213 | ||
| 8215 | const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count()); | 8214 | const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count()); |
| 8216 | @memset(comptime_args, .none); | 8215 | @memset(comptime_args, .none); |
| ... | @@ -9416,7 +9415,7 @@ fn zirFunc( | ... | @@ -9416,7 +9415,7 @@ fn zirFunc( |
| 9416 | break :cau generic_owner_nav.analysis_owner.unwrap().?; | 9415 | break :cau generic_owner_nav.analysis_owner.unwrap().?; |
| 9417 | } else sema.owner.unwrap().cau; | 9416 | } else sema.owner.unwrap().cau; |
| 9418 | const fn_is_exported = exported: { | 9417 | const fn_is_exported = exported: { |
| 9419 | const decl_inst = ip.getCau(func_decl_cau).zir_index.resolve(ip); | 9418 | const decl_inst = ip.getCau(func_decl_cau).zir_index.resolve(ip) orelse return error.AnalysisFail; |
| 9420 | const zir_decl = sema.code.getDeclaration(decl_inst)[0]; | 9419 | const zir_decl = sema.code.getDeclaration(decl_inst)[0]; |
| 9421 | break :exported zir_decl.flags.is_export; | 9420 | break :exported zir_decl.flags.is_export; |
| 9422 | }; | 9421 | }; |
| ... | @@ -26125,7 +26124,7 @@ fn zirVarExtended( | ... | @@ -26125,7 +26124,7 @@ fn zirVarExtended( |
| 26125 | const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 }); | 26124 | const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 }); |
| 26126 | 26125 | ||
| 26127 | const decl_inst, const decl_bodies = decl: { | 26126 | const decl_inst, const decl_bodies = decl: { |
| 26128 | const decl_inst = sema.getOwnerCauDeclInst().resolve(ip); | 26127 | const decl_inst = sema.getOwnerCauDeclInst().resolve(ip) orelse return error.AnalysisFail; |
| 26129 | const zir_decl, const extra_end = sema.code.getDeclaration(decl_inst); | 26128 | const zir_decl, const extra_end = sema.code.getDeclaration(decl_inst); |
| 26130 | break :decl .{ decl_inst, zir_decl.getBodies(extra_end, sema.code) }; | 26129 | break :decl .{ decl_inst, zir_decl.getBodies(extra_end, sema.code) }; |
| 26131 | }; | 26130 | }; |
| ... | @@ -26354,7 +26353,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -26354,7 +26353,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 26354 | break :decl_inst cau.zir_index; | 26353 | break :decl_inst cau.zir_index; |
| 26355 | } else sema.getOwnerCauDeclInst(); // not an instantiation so we're analyzing a function declaration Cau | 26354 | } else sema.getOwnerCauDeclInst(); // not an instantiation so we're analyzing a function declaration Cau |
| 26356 | 26355 | ||
| 26357 | const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&mod.intern_pool))[0]; | 26356 | const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&mod.intern_pool) orelse return error.AnalysisFail)[0]; |
| 26358 | if (zir_decl.flags.is_export) { | 26357 | if (zir_decl.flags.is_export) { |
| 26359 | break :cc .C; | 26358 | break :cc .C; |
| 26360 | } | 26359 | } |
| ... | @@ -35505,7 +35504,7 @@ fn semaBackingIntType(pt: Zcu.PerThread, struct_type: InternPool.LoadedStructTyp | ... | @@ -35505,7 +35504,7 @@ fn semaBackingIntType(pt: Zcu.PerThread, struct_type: InternPool.LoadedStructTyp |
| 35505 | break :blk accumulator; | 35504 | break :blk accumulator; |
| 35506 | }; | 35505 | }; |
| 35507 | 35506 | ||
| 35508 | const zir_index = struct_type.zir_index.unwrap().?.resolve(ip); | 35507 | const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail; |
| 35509 | const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; | 35508 | const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; |
| 35510 | assert(extended.opcode == .struct_decl); | 35509 | assert(extended.opcode == .struct_decl); |
| 35511 | const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); | 35510 | const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); |
| ... | @@ -36120,7 +36119,7 @@ fn semaStructFields( | ... | @@ -36120,7 +36119,7 @@ fn semaStructFields( |
| 36120 | const cau_index = struct_type.cau.unwrap().?; | 36119 | const cau_index = struct_type.cau.unwrap().?; |
| 36121 | const namespace_index = ip.getCau(cau_index).namespace; | 36120 | const namespace_index = ip.getCau(cau_index).namespace; |
| 36122 | const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; | 36121 | const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; |
| 36123 | const zir_index = struct_type.zir_index.unwrap().?.resolve(ip); | 36122 | const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail; |
| 36124 | 36123 | ||
| 36125 | const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); | 36124 | const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); |
| 36126 | 36125 | ||
| ... | @@ -36343,7 +36342,7 @@ fn semaStructFieldInits( | ... | @@ -36343,7 +36342,7 @@ fn semaStructFieldInits( |
| 36343 | const cau_index = struct_type.cau.unwrap().?; | 36342 | const cau_index = struct_type.cau.unwrap().?; |
| 36344 | const namespace_index = ip.getCau(cau_index).namespace; | 36343 | const namespace_index = ip.getCau(cau_index).namespace; |
| 36345 | const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; | 36344 | const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; |
| 36346 | const zir_index = struct_type.zir_index.unwrap().?.resolve(ip); | 36345 | const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail; |
| 36347 | const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); | 36346 | const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); |
| 36348 | 36347 | ||
| 36349 | var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa); | 36348 | var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa); |
| ... | @@ -36477,7 +36476,7 @@ fn semaUnionFields(pt: Zcu.PerThread, arena: Allocator, union_ty: InternPool.Ind | ... | @@ -36477,7 +36476,7 @@ fn semaUnionFields(pt: Zcu.PerThread, arena: Allocator, union_ty: InternPool.Ind |
| 36477 | const ip = &zcu.intern_pool; | 36476 | const ip = &zcu.intern_pool; |
| 36478 | const cau_index = union_type.cau; | 36477 | const cau_index = union_type.cau; |
| 36479 | const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir; | 36478 | const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir; |
| 36480 | const zir_index = union_type.zir_index.resolve(ip); | 36479 | const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail; |
| 36481 | const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; | 36480 | const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; |
| 36482 | assert(extended.opcode == .union_decl); | 36481 | assert(extended.opcode == .union_decl); |
| 36483 | const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); | 36482 | const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); |
src/Type.zig+1-1| ... | @@ -3437,7 +3437,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { | ... | @@ -3437,7 +3437,7 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { |
| 3437 | }, | 3437 | }, |
| 3438 | else => return null, | 3438 | else => return null, |
| 3439 | }; | 3439 | }; |
| 3440 | const info = tracked.resolveFull(&zcu.intern_pool); | 3440 | const info = tracked.resolveFull(&zcu.intern_pool) orelse return null; |
| 3441 | const file = zcu.fileByIndex(info.file); | 3441 | const file = zcu.fileByIndex(info.file); |
| 3442 | assert(file.zir_loaded); | 3442 | assert(file.zir_loaded); |
| 3443 | const zir = file.zir; | 3443 | const zir = file.zir; |
src/Zcu.zig+40-79| ... | @@ -162,12 +162,6 @@ outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, | ... | @@ -162,12 +162,6 @@ outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, |
| 162 | /// Such `AnalUnit`s are ready for immediate re-analysis. | 162 | /// Such `AnalUnit`s are ready for immediate re-analysis. |
| 163 | /// See `findOutdatedToAnalyze` for details. | 163 | /// See `findOutdatedToAnalyze` for details. |
| 164 | outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, | 164 | outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, |
| 165 | /// This contains a set of struct types whose corresponding `Cau` may not be in | ||
| 166 | /// `outdated`, but are the root types of files which have updated source and | ||
| 167 | /// thus must be re-analyzed. If such a type is only in this set, the struct type | ||
| 168 | /// index may be preserved (only the namespace might change). If its owned `Cau` | ||
| 169 | /// is also outdated, the struct type index must be recreated. | ||
| 170 | outdated_file_root: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, | ||
| 171 | /// This contains a list of AnalUnit whose analysis or codegen failed, but the | 165 | /// This contains a list of AnalUnit whose analysis or codegen failed, but the |
| 172 | /// failure was something like running out of disk space, and trying again may | 166 | /// failure was something like running out of disk space, and trying again may |
| 173 | /// succeed. On the next update, we will flush this list, marking all members of | 167 | /// succeed. On the next update, we will flush this list, marking all members of |
| ... | @@ -2025,7 +2019,7 @@ pub const LazySrcLoc = struct { | ... | @@ -2025,7 +2019,7 @@ pub const LazySrcLoc = struct { |
| 2025 | pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) struct { *File, Ast.Node.Index } { | 2019 | pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) struct { *File, Ast.Node.Index } { |
| 2026 | const ip = &zcu.intern_pool; | 2020 | const ip = &zcu.intern_pool; |
| 2027 | const file_index, const zir_inst = inst: { | 2021 | const file_index, const zir_inst = inst: { |
| 2028 | const info = base_node_inst.resolveFull(ip); | 2022 | const info = base_node_inst.resolveFull(ip) orelse @panic("TODO: resolve source location relative to lost inst"); |
| 2029 | break :inst .{ info.file, info.inst }; | 2023 | break :inst .{ info.file, info.inst }; |
| 2030 | }; | 2024 | }; |
| 2031 | const file = zcu.fileByIndex(file_index); | 2025 | const file = zcu.fileByIndex(file_index); |
| ... | @@ -2148,7 +2142,6 @@ pub fn deinit(zcu: *Zcu) void { | ... | @@ -2148,7 +2142,6 @@ pub fn deinit(zcu: *Zcu) void { |
| 2148 | zcu.potentially_outdated.deinit(gpa); | 2142 | zcu.potentially_outdated.deinit(gpa); |
| 2149 | zcu.outdated.deinit(gpa); | 2143 | zcu.outdated.deinit(gpa); |
| 2150 | zcu.outdated_ready.deinit(gpa); | 2144 | zcu.outdated_ready.deinit(gpa); |
| 2151 | zcu.outdated_file_root.deinit(gpa); | ||
| 2152 | zcu.retryable_failures.deinit(gpa); | 2145 | zcu.retryable_failures.deinit(gpa); |
| 2153 | 2146 | ||
| 2154 | zcu.test_functions.deinit(gpa); | 2147 | zcu.test_functions.deinit(gpa); |
| ... | @@ -2355,8 +2348,6 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni | ... | @@ -2355,8 +2348,6 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni |
| 2355 | pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { | 2348 | pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 2356 | if (!zcu.comp.incremental) return null; | 2349 | if (!zcu.comp.incremental) return null; |
| 2357 | 2350 | ||
| 2358 | if (true) @panic("TODO: findOutdatedToAnalyze"); | ||
| 2359 | |||
| 2360 | if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) { | 2351 | if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) { |
| 2361 | log.debug("findOutdatedToAnalyze: no outdated depender", .{}); | 2352 | log.debug("findOutdatedToAnalyze: no outdated depender", .{}); |
| 2362 | return null; | 2353 | return null; |
| ... | @@ -2381,87 +2372,57 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { | ... | @@ -2381,87 +2372,57 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 2381 | return zcu.outdated_ready.keys()[0]; | 2372 | return zcu.outdated_ready.keys()[0]; |
| 2382 | } | 2373 | } |
| 2383 | 2374 | ||
| 2384 | // Next, we will see if there is any outdated file root which was not in | 2375 | // There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some |
| 2385 | // `outdated`. This set will be small (number of files changed in this | 2376 | // Cau with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of |
| 2386 | // update), so it's alright for us to just iterate here. | 2377 | // A or B. We should select a Cau, since a Cau is definitely responsible for the loop in the |
| 2387 | for (zcu.outdated_file_root.keys()) |file_decl| { | 2378 | // dependency graph (since IES dependencies can't have loops). We should also, of course, not |
| 2388 | const decl_depender = AnalUnit.wrap(.{ .decl = file_decl }); | 2379 | // select a Cau owned by a `comptime` declaration, since you can't depend on those! |
| 2389 | if (zcu.outdated.contains(decl_depender)) { | ||
| 2390 | // Since we didn't hit this in the first loop, this Decl must have | ||
| 2391 | // pending dependencies, so is ineligible. | ||
| 2392 | continue; | ||
| 2393 | } | ||
| 2394 | if (zcu.potentially_outdated.contains(decl_depender)) { | ||
| 2395 | // This Decl's struct may or may not need to be recreated depending | ||
| 2396 | // on whether it is outdated. If we analyzed it now, we would have | ||
| 2397 | // to assume it was outdated and recreate it! | ||
| 2398 | continue; | ||
| 2399 | } | ||
| 2400 | log.debug("findOutdatedToAnalyze: outdated file root decl '{d}'", .{file_decl}); | ||
| 2401 | return decl_depender; | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | // There is no single AnalUnit which is ready for re-analysis. Instead, we | ||
| 2405 | // must assume that some Decl with PO dependencies is outdated - e.g. in the | ||
| 2406 | // above example we arbitrarily pick one of A or B. We should select a Decl, | ||
| 2407 | // since a Decl is definitely responsible for the loop in the dependency | ||
| 2408 | // graph (since you can't depend on a runtime function analysis!). | ||
| 2409 | 2380 | ||
| 2410 | // The choice of this Decl could have a big impact on how much total | 2381 | // The choice of this Cau could have a big impact on how much total analysis we perform, since |
| 2411 | // analysis we perform, since if analysis concludes its tyval is unchanged, | 2382 | // if analysis concludes any dependencies on its result are up-to-date, then other PO AnalUnit |
| 2412 | // then other PO AnalUnit may be resolved as up-to-date. To hopefully avoid | 2383 | // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a Decl |
| 2413 | // doing too much work, let's find a Decl which the most things depend on - | 2384 | // which the most things depend on - the idea is that this will resolve a lot of loops (but this |
| 2414 | // the idea is that this will resolve a lot of loops (but this is only a | 2385 | // is only a heuristic). |
| 2415 | // heuristic). | ||
| 2416 | 2386 | ||
| 2417 | log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{ | 2387 | log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{ |
| 2418 | zcu.outdated.count(), | 2388 | zcu.outdated.count(), |
| 2419 | zcu.potentially_outdated.count(), | 2389 | zcu.potentially_outdated.count(), |
| 2420 | }); | 2390 | }); |
| 2421 | 2391 | ||
| 2422 | const Decl = {}; | 2392 | const ip = &zcu.intern_pool; |
| 2423 | |||
| 2424 | var chosen_decl_idx: ?Decl.Index = null; | ||
| 2425 | var chosen_decl_dependers: u32 = undefined; | ||
| 2426 | |||
| 2427 | for (zcu.outdated.keys()) |depender| { | ||
| 2428 | const decl_index = switch (depender.unwrap()) { | ||
| 2429 | .decl => |d| d, | ||
| 2430 | .func => continue, | ||
| 2431 | }; | ||
| 2432 | |||
| 2433 | var n: u32 = 0; | ||
| 2434 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); | ||
| 2435 | while (it.next()) |_| n += 1; | ||
| 2436 | 2393 | ||
| 2437 | if (chosen_decl_idx == null or n > chosen_decl_dependers) { | 2394 | var chosen_cau: ?InternPool.Cau.Index = null; |
| 2438 | chosen_decl_idx = decl_index; | 2395 | var chosen_cau_dependers: u32 = undefined; |
| 2439 | chosen_decl_dependers = n; | ||
| 2440 | } | ||
| 2441 | } | ||
| 2442 | 2396 | ||
| 2443 | for (zcu.potentially_outdated.keys()) |depender| { | 2397 | inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| { |
| 2444 | const decl_index = switch (depender.unwrap()) { | 2398 | for (outdated_units) |unit| { |
| 2445 | .decl => |d| d, | 2399 | const cau = switch (unit.unwrap()) { |
| 2446 | .func => continue, | 2400 | .cau => |cau| cau, |
| 2447 | }; | 2401 | .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice |
| 2402 | }; | ||
| 2403 | const cau_owner = ip.getCau(cau).owner; | ||
| 2448 | 2404 | ||
| 2449 | var n: u32 = 0; | 2405 | var n: u32 = 0; |
| 2450 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); | 2406 | var it = ip.dependencyIterator(switch (cau_owner.unwrap()) { |
| 2451 | while (it.next()) |_| n += 1; | 2407 | .none => continue, // there can be no dependencies on this `Cau` so it is a terrible choice |
| 2408 | .type => |ty| .{ .interned = ty }, | ||
| 2409 | .nav => |nav| .{ .nav_val = nav }, | ||
| 2410 | }); | ||
| 2411 | while (it.next()) |_| n += 1; | ||
| 2452 | 2412 | ||
| 2453 | if (chosen_decl_idx == null or n > chosen_decl_dependers) { | 2413 | if (chosen_cau == null or n > chosen_cau_dependers) { |
| 2454 | chosen_decl_idx = decl_index; | 2414 | chosen_cau = cau; |
| 2455 | chosen_decl_dependers = n; | 2415 | chosen_cau_dependers = n; |
| 2416 | } | ||
| 2456 | } | 2417 | } |
| 2457 | } | 2418 | } |
| 2458 | 2419 | ||
| 2459 | log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{ | 2420 | log.debug("findOutdatedToAnalyze: heuristic returned Cau {d} ({d} dependers)", .{ |
| 2460 | chosen_decl_idx.?, | 2421 | @intFromEnum(chosen_cau.?), |
| 2461 | chosen_decl_dependers, | 2422 | chosen_cau_dependers, |
| 2462 | }); | 2423 | }); |
| 2463 | 2424 | ||
| 2464 | return AnalUnit.wrap(.{ .decl = chosen_decl_idx.? }); | 2425 | return AnalUnit.wrap(.{ .cau = chosen_cau.? }); |
| 2465 | } | 2426 | } |
| 2466 | 2427 | ||
| 2467 | /// During an incremental update, before semantic analysis, call this to flush all values from | 2428 | /// During an incremental update, before semantic analysis, call this to flush all values from |
| ... | @@ -2583,7 +2544,7 @@ pub fn mapOldZirToNew( | ... | @@ -2583,7 +2544,7 @@ pub fn mapOldZirToNew( |
| 2583 | break :inst unnamed_tests.items[unnamed_test_idx]; | 2544 | break :inst unnamed_tests.items[unnamed_test_idx]; |
| 2584 | }, | 2545 | }, |
| 2585 | _ => inst: { | 2546 | _ => inst: { |
| 2586 | const name_nts = new_decl.name.toString(old_zir).?; | 2547 | const name_nts = new_decl.name.toString(new_zir).?; |
| 2587 | const name = new_zir.nullTerminatedString(name_nts); | 2548 | const name = new_zir.nullTerminatedString(name_nts); |
| 2588 | if (new_decl.name.isNamedTest(new_zir)) { | 2549 | if (new_decl.name.isNamedTest(new_zir)) { |
| 2589 | break :inst named_tests.get(name) orelse continue; | 2550 | break :inst named_tests.get(name) orelse continue; |
| ... | @@ -3093,7 +3054,7 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc { | ... | @@ -3093,7 +3054,7 @@ pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc { |
| 3093 | 3054 | ||
| 3094 | pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { | 3055 | pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { |
| 3095 | const ip = &zcu.intern_pool; | 3056 | const ip = &zcu.intern_pool; |
| 3096 | const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip); | 3057 | const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?; |
| 3097 | const zir = zcu.fileByIndex(inst_info.file).zir; | 3058 | const zir = zcu.fileByIndex(inst_info.file).zir; |
| 3098 | const inst = zir.instructions.get(@intFromEnum(inst_info.inst)); | 3059 | const inst = zir.instructions.get(@intFromEnum(inst_info.inst)); |
| 3099 | assert(inst.tag == .declaration); | 3060 | assert(inst.tag == .declaration); |
| ... | @@ -3106,7 +3067,7 @@ pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value { | ... | @@ -3106,7 +3067,7 @@ pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value { |
| 3106 | 3067 | ||
| 3107 | pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index { | 3068 | pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index { |
| 3108 | const ip = &zcu.intern_pool; | 3069 | const ip = &zcu.intern_pool; |
| 3109 | return ip.getNav(nav).srcInst(ip).resolveFull(ip).file; | 3070 | return ip.getNav(nav).srcInst(ip).resolveFile(ip); |
| 3110 | } | 3071 | } |
| 3111 | 3072 | ||
| 3112 | pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File { | 3073 | pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File { |
| ... | @@ -3115,6 +3076,6 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File { | ... | @@ -3115,6 +3076,6 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File { |
| 3115 | 3076 | ||
| 3116 | pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File { | 3077 | pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File { |
| 3117 | const ip = &zcu.intern_pool; | 3078 | const ip = &zcu.intern_pool; |
| 3118 | const file_index = ip.getCau(cau).zir_index.resolveFull(ip).file; | 3079 | const file_index = ip.getCau(cau).zir_index.resolveFile(ip); |
| 3119 | return zcu.fileByIndex(file_index); | 3080 | return zcu.fileByIndex(file_index); |
| 3120 | } | 3081 | } |
src/Zcu/PerThread.zig+191-172| ... | @@ -39,7 +39,6 @@ pub fn astGenFile( | ... | @@ -39,7 +39,6 @@ pub fn astGenFile( |
| 39 | pt: Zcu.PerThread, | 39 | pt: Zcu.PerThread, |
| 40 | file: *Zcu.File, | 40 | file: *Zcu.File, |
| 41 | path_digest: Cache.BinDigest, | 41 | path_digest: Cache.BinDigest, |
| 42 | old_root_type: InternPool.Index, | ||
| 43 | ) !void { | 42 | ) !void { |
| 44 | dev.check(.ast_gen); | 43 | dev.check(.ast_gen); |
| 45 | assert(!file.mod.isBuiltin()); | 44 | assert(!file.mod.isBuiltin()); |
| ... | @@ -299,25 +298,15 @@ pub fn astGenFile( | ... | @@ -299,25 +298,15 @@ pub fn astGenFile( |
| 299 | file.status = .astgen_failure; | 298 | file.status = .astgen_failure; |
| 300 | return error.AnalysisFail; | 299 | return error.AnalysisFail; |
| 301 | } | 300 | } |
| 302 | |||
| 303 | if (old_root_type != .none) { | ||
| 304 | // The root of this file must be re-analyzed, since the file has changed. | ||
| 305 | comp.mutex.lock(); | ||
| 306 | defer comp.mutex.unlock(); | ||
| 307 | |||
| 308 | log.debug("outdated file root type: {}", .{old_root_type}); | ||
| 309 | try zcu.outdated_file_root.put(gpa, old_root_type, {}); | ||
| 310 | } | ||
| 311 | } | 301 | } |
| 312 | 302 | ||
| 313 | const UpdatedFile = struct { | 303 | const UpdatedFile = struct { |
| 314 | file_index: Zcu.File.Index, | ||
| 315 | file: *Zcu.File, | 304 | file: *Zcu.File, |
| 316 | inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), | 305 | inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), |
| 317 | }; | 306 | }; |
| 318 | 307 | ||
| 319 | fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.ArrayListUnmanaged(UpdatedFile)) void { | 308 | fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile)) void { |
| 320 | for (updated_files.items) |*elem| elem.inst_map.deinit(gpa); | 309 | for (updated_files.values()) |*elem| elem.inst_map.deinit(gpa); |
| 321 | updated_files.deinit(gpa); | 310 | updated_files.deinit(gpa); |
| 322 | } | 311 | } |
| 323 | 312 | ||
| ... | @@ -328,143 +317,166 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { | ... | @@ -328,143 +317,166 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { |
| 328 | const gpa = zcu.gpa; | 317 | const gpa = zcu.gpa; |
| 329 | 318 | ||
| 330 | // We need to visit every updated File for every TrackedInst in InternPool. | 319 | // We need to visit every updated File for every TrackedInst in InternPool. |
| 331 | var updated_files: std.ArrayListUnmanaged(UpdatedFile) = .{}; | 320 | var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .{}; |
| 332 | defer cleanupUpdatedFiles(gpa, &updated_files); | 321 | defer cleanupUpdatedFiles(gpa, &updated_files); |
| 333 | for (zcu.import_table.values()) |file_index| { | 322 | for (zcu.import_table.values()) |file_index| { |
| 334 | const file = zcu.fileByIndex(file_index); | 323 | const file = zcu.fileByIndex(file_index); |
| 335 | const old_zir = file.prev_zir orelse continue; | 324 | const old_zir = file.prev_zir orelse continue; |
| 336 | const new_zir = file.zir; | 325 | const new_zir = file.zir; |
| 337 | try updated_files.append(gpa, .{ | 326 | const gop = try updated_files.getOrPut(gpa, file_index); |
| 338 | .file_index = file_index, | 327 | assert(!gop.found_existing); |
| 328 | gop.value_ptr.* = .{ | ||
| 339 | .file = file, | 329 | .file = file, |
| 340 | .inst_map = .{}, | 330 | .inst_map = .{}, |
| 341 | }); | 331 | }; |
| 342 | const inst_map = &updated_files.items[updated_files.items.len - 1].inst_map; | 332 | if (!new_zir.hasCompileErrors()) { |
| 343 | try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, inst_map); | 333 | try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map); |
| 334 | } | ||
| 344 | } | 335 | } |
| 345 | 336 | ||
| 346 | if (updated_files.items.len == 0) | 337 | if (updated_files.count() == 0) |
| 347 | return; | 338 | return; |
| 348 | 339 | ||
| 349 | for (ip.locals, 0..) |*local, tid| { | 340 | for (ip.locals, 0..) |*local, tid| { |
| 350 | const tracked_insts_list = local.getMutableTrackedInsts(gpa); | 341 | const tracked_insts_list = local.getMutableTrackedInsts(gpa); |
| 351 | for (tracked_insts_list.view().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| { | 342 | for (tracked_insts_list.viewAllowEmpty().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| { |
| 352 | for (updated_files.items) |updated_file| { | 343 | const file_index = tracked_inst.file; |
| 353 | const file_index = updated_file.file_index; | 344 | const updated_file = updated_files.get(file_index) orelse continue; |
| 354 | if (tracked_inst.file != file_index) continue; | ||
| 355 | |||
| 356 | const file = updated_file.file; | ||
| 357 | const old_zir = file.prev_zir.?.*; | ||
| 358 | const new_zir = file.zir; | ||
| 359 | const old_tag = old_zir.instructions.items(.tag); | ||
| 360 | const old_data = old_zir.instructions.items(.data); | ||
| 361 | const inst_map = &updated_file.inst_map; | ||
| 362 | |||
| 363 | const old_inst = tracked_inst.inst; | ||
| 364 | const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{ | ||
| 365 | .tid = @enumFromInt(tid), | ||
| 366 | .index = @intCast(tracked_inst_unwrapped_index), | ||
| 367 | }).wrap(ip); | ||
| 368 | tracked_inst.inst = inst_map.get(old_inst) orelse { | ||
| 369 | // Tracking failed for this instruction. Invalidate associated `src_hash` deps. | ||
| 370 | log.debug("tracking failed for %{d}", .{old_inst}); | ||
| 371 | try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index }); | ||
| 372 | continue; | ||
| 373 | }; | ||
| 374 | 345 | ||
| 375 | if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { | 346 | const file = updated_file.file; |
| 376 | if (new_zir.getAssociatedSrcHash(tracked_inst.inst)) |new_hash| { | 347 | |
| 377 | if (std.zig.srcHashEql(old_hash, new_hash)) { | 348 | if (file.zir.hasCompileErrors()) { |
| 378 | break :hash_changed; | 349 | // If we mark this as outdated now, users of this inst will just get a transitive analysis failure. |
| 379 | } | 350 | // Ultimately, they would end up throwing out potentially useful analysis results. |
| 380 | log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{ | 351 | // So, do nothing. We already have the file failure -- that's sufficient for now! |
| 381 | old_inst, | 352 | continue; |
| 382 | tracked_inst.inst, | 353 | } |
| 383 | std.fmt.fmtSliceHexLower(&old_hash), | 354 | const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts |
| 384 | std.fmt.fmtSliceHexLower(&new_hash), | 355 | const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{ |
| 385 | }); | 356 | .tid = @enumFromInt(tid), |
| 357 | .index = @intCast(tracked_inst_unwrapped_index), | ||
| 358 | }).wrap(ip); | ||
| 359 | const new_inst = updated_file.inst_map.get(old_inst) orelse { | ||
| 360 | // Tracking failed for this instruction. Invalidate associated `src_hash` deps. | ||
| 361 | log.debug("tracking failed for %{d}", .{old_inst}); | ||
| 362 | tracked_inst.inst = .lost; | ||
| 363 | try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index }); | ||
| 364 | continue; | ||
| 365 | }; | ||
| 366 | tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst); | ||
| 367 | |||
| 368 | const old_zir = file.prev_zir.?.*; | ||
| 369 | const new_zir = file.zir; | ||
| 370 | const old_tag = old_zir.instructions.items(.tag); | ||
| 371 | const old_data = old_zir.instructions.items(.data); | ||
| 372 | |||
| 373 | if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { | ||
| 374 | if (new_zir.getAssociatedSrcHash(new_inst)) |new_hash| { | ||
| 375 | if (std.zig.srcHashEql(old_hash, new_hash)) { | ||
| 376 | break :hash_changed; | ||
| 386 | } | 377 | } |
| 387 | // The source hash associated with this instruction changed - invalidate relevant dependencies. | 378 | log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{ |
| 388 | try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index }); | 379 | old_inst, |
| 380 | new_inst, | ||
| 381 | std.fmt.fmtSliceHexLower(&old_hash), | ||
| 382 | std.fmt.fmtSliceHexLower(&new_hash), | ||
| 383 | }); | ||
| 389 | } | 384 | } |
| 385 | // The source hash associated with this instruction changed - invalidate relevant dependencies. | ||
| 386 | try zcu.markDependeeOutdated(.{ .src_hash = tracked_inst_index }); | ||
| 387 | } | ||
| 390 | 388 | ||
| 391 | // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies. | 389 | // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies. |
| 392 | const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) { | 390 | const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) { |
| 393 | .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) { | 391 | .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) { |
| 394 | .struct_decl, .union_decl, .opaque_decl, .enum_decl => true, | 392 | .struct_decl, .union_decl, .opaque_decl, .enum_decl => true, |
| 395 | else => false, | ||
| 396 | }, | ||
| 397 | else => false, | 393 | else => false, |
| 398 | }; | 394 | }, |
| 399 | if (!has_namespace) continue; | 395 | else => false, |
| 400 | 396 | }; | |
| 401 | var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; | 397 | if (!has_namespace) continue; |
| 402 | defer old_names.deinit(zcu.gpa); | 398 | |
| 403 | { | 399 | var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; |
| 404 | var it = old_zir.declIterator(old_inst); | 400 | defer old_names.deinit(zcu.gpa); |
| 405 | while (it.next()) |decl_inst| { | 401 | { |
| 406 | const decl_name = old_zir.getDeclaration(decl_inst)[0].name; | 402 | var it = old_zir.declIterator(old_inst); |
| 407 | switch (decl_name) { | 403 | while (it.next()) |decl_inst| { |
| 408 | .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, | 404 | const decl_name = old_zir.getDeclaration(decl_inst)[0].name; |
| 409 | _ => if (decl_name.isNamedTest(old_zir)) continue, | 405 | switch (decl_name) { |
| 410 | } | 406 | .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, |
| 411 | const name_zir = decl_name.toString(old_zir).?; | 407 | _ => if (decl_name.isNamedTest(old_zir)) continue, |
| 412 | const name_ip = try zcu.intern_pool.getOrPutString( | ||
| 413 | zcu.gpa, | ||
| 414 | pt.tid, | ||
| 415 | old_zir.nullTerminatedString(name_zir), | ||
| 416 | .no_embedded_nulls, | ||
| 417 | ); | ||
| 418 | try old_names.put(zcu.gpa, name_ip, {}); | ||
| 419 | } | 408 | } |
| 409 | const name_zir = decl_name.toString(old_zir).?; | ||
| 410 | const name_ip = try zcu.intern_pool.getOrPutString( | ||
| 411 | zcu.gpa, | ||
| 412 | pt.tid, | ||
| 413 | old_zir.nullTerminatedString(name_zir), | ||
| 414 | .no_embedded_nulls, | ||
| 415 | ); | ||
| 416 | try old_names.put(zcu.gpa, name_ip, {}); | ||
| 420 | } | 417 | } |
| 421 | var any_change = false; | 418 | } |
| 422 | { | 419 | var any_change = false; |
| 423 | var it = new_zir.declIterator(tracked_inst.inst); | 420 | { |
| 424 | while (it.next()) |decl_inst| { | 421 | var it = new_zir.declIterator(new_inst); |
| 425 | const decl_name = new_zir.getDeclaration(decl_inst)[0].name; | 422 | while (it.next()) |decl_inst| { |
| 426 | switch (decl_name) { | 423 | const decl_name = new_zir.getDeclaration(decl_inst)[0].name; |
| 427 | .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, | 424 | switch (decl_name) { |
| 428 | _ => if (decl_name.isNamedTest(new_zir)) continue, | 425 | .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, |
| 429 | } | 426 | _ => if (decl_name.isNamedTest(new_zir)) continue, |
| 430 | const name_zir = decl_name.toString(new_zir).?; | ||
| 431 | const name_ip = try zcu.intern_pool.getOrPutString( | ||
| 432 | zcu.gpa, | ||
| 433 | pt.tid, | ||
| 434 | new_zir.nullTerminatedString(name_zir), | ||
| 435 | .no_embedded_nulls, | ||
| 436 | ); | ||
| 437 | if (!old_names.swapRemove(name_ip)) continue; | ||
| 438 | // Name added | ||
| 439 | any_change = true; | ||
| 440 | try zcu.markDependeeOutdated(.{ .namespace_name = .{ | ||
| 441 | .namespace = tracked_inst_index, | ||
| 442 | .name = name_ip, | ||
| 443 | } }); | ||
| 444 | } | 427 | } |
| 445 | } | 428 | const name_zir = decl_name.toString(new_zir).?; |
| 446 | // The only elements remaining in `old_names` now are any names which were removed. | 429 | const name_ip = try zcu.intern_pool.getOrPutString( |
| 447 | for (old_names.keys()) |name_ip| { | 430 | zcu.gpa, |
| 431 | pt.tid, | ||
| 432 | new_zir.nullTerminatedString(name_zir), | ||
| 433 | .no_embedded_nulls, | ||
| 434 | ); | ||
| 435 | if (!old_names.swapRemove(name_ip)) continue; | ||
| 436 | // Name added | ||
| 448 | any_change = true; | 437 | any_change = true; |
| 449 | try zcu.markDependeeOutdated(.{ .namespace_name = .{ | 438 | try zcu.markDependeeOutdated(.{ .namespace_name = .{ |
| 450 | .namespace = tracked_inst_index, | 439 | .namespace = tracked_inst_index, |
| 451 | .name = name_ip, | 440 | .name = name_ip, |
| 452 | } }); | 441 | } }); |
| 453 | } | 442 | } |
| 443 | } | ||
| 444 | // The only elements remaining in `old_names` now are any names which were removed. | ||
| 445 | for (old_names.keys()) |name_ip| { | ||
| 446 | any_change = true; | ||
| 447 | try zcu.markDependeeOutdated(.{ .namespace_name = .{ | ||
| 448 | .namespace = tracked_inst_index, | ||
| 449 | .name = name_ip, | ||
| 450 | } }); | ||
| 451 | } | ||
| 454 | 452 | ||
| 455 | if (any_change) { | 453 | if (any_change) { |
| 456 | try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index }); | 454 | try zcu.markDependeeOutdated(.{ .namespace = tracked_inst_index }); |
| 457 | } | ||
| 458 | } | 455 | } |
| 459 | } | 456 | } |
| 460 | } | 457 | } |
| 461 | 458 | ||
| 462 | for (updated_files.items) |updated_file| { | 459 | try ip.rehashTrackedInsts(gpa, pt.tid); |
| 460 | |||
| 461 | for (updated_files.keys(), updated_files.values()) |file_index, updated_file| { | ||
| 463 | const file = updated_file.file; | 462 | const file = updated_file.file; |
| 464 | const prev_zir = file.prev_zir.?; | 463 | if (file.zir.hasCompileErrors()) { |
| 465 | file.prev_zir = null; | 464 | // Keep `prev_zir` around: it's the last non-error ZIR. |
| 466 | prev_zir.deinit(gpa); | 465 | // Don't update the namespace, as we have no new data to update *to*. |
| 467 | gpa.destroy(prev_zir); | 466 | } else { |
| 467 | const prev_zir = file.prev_zir.?; | ||
| 468 | file.prev_zir = null; | ||
| 469 | prev_zir.deinit(gpa); | ||
| 470 | gpa.destroy(prev_zir); | ||
| 471 | |||
| 472 | // For every file which has changed, re-scan the namespace of the file's root struct type. | ||
| 473 | // These types are special-cased because they don't have an enclosing declaration which will | ||
| 474 | // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this | ||
| 475 | // now because this work is fast (no actual Sema work is happening, we're just updating the | ||
| 476 | // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace` | ||
| 477 | // will track some instructions. | ||
| 478 | try pt.updateFileNamespace(file_index); | ||
| 479 | } | ||
| 468 | } | 480 | } |
| 469 | } | 481 | } |
| 470 | 482 | ||
| ... | @@ -473,6 +485,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { | ... | @@ -473,6 +485,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { |
| 473 | pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { | 485 | pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { |
| 474 | const file_root_type = pt.zcu.fileRootType(file_index); | 486 | const file_root_type = pt.zcu.fileRootType(file_index); |
| 475 | if (file_root_type != .none) { | 487 | if (file_root_type != .none) { |
| 488 | // The namespace is already up-to-date thanks to the `updateFileNamespace` calls at the | ||
| 489 | // start of this update. We just have to check whether the type itself is okay! | ||
| 476 | const file_root_type_cau = pt.zcu.intern_pool.loadStructType(file_root_type).cau.unwrap().?; | 490 | const file_root_type_cau = pt.zcu.intern_pool.loadStructType(file_root_type).cau.unwrap().?; |
| 477 | return pt.ensureCauAnalyzed(file_root_type_cau); | 491 | return pt.ensureCauAnalyzed(file_root_type_cau); |
| 478 | } else { | 492 | } else { |
| ... | @@ -493,7 +507,6 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -493,7 +507,6 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 493 | 507 | ||
| 494 | const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index }); | 508 | const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index }); |
| 495 | const cau = ip.getCau(cau_index); | 509 | const cau = ip.getCau(cau_index); |
| 496 | const inst_info = cau.zir_index.resolveFull(ip); | ||
| 497 | 510 | ||
| 498 | log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)}); | 511 | log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)}); |
| 499 | 512 | ||
| ... | @@ -516,12 +529,9 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -516,12 +529,9 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 516 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 529 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 517 | } | 530 | } |
| 518 | 531 | ||
| 519 | // TODO: this only works if namespace lookups in Sema trigger `ensureCauAnalyzed`, because | 532 | const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail; |
| 520 | // `outdated_file_root` information is not "viral", so we need that a namespace lookup first | 533 | |
| 521 | // handles the case where the file root is not an outdated *type* but does have an outdated | 534 | // TODO: document this elsewhere mlugg! |
| 522 | // *namespace*. A more logically simple alternative may be for a file's root struct to register | ||
| 523 | // a dependency on the file's entire source code (hash). Alternatively, we could make sure that | ||
| 524 | // these are always handled first in an update. Actually, that's probably the best option. | ||
| 525 | // For my own benefit, here's how a namespace update for a normal (non-file-root) type works: | 535 | // For my own benefit, here's how a namespace update for a normal (non-file-root) type works: |
| 526 | // `const S = struct { ... };` | 536 | // `const S = struct { ... };` |
| 527 | // We are adding or removing a declaration within this `struct`. | 537 | // We are adding or removing a declaration within this `struct`. |
| ... | @@ -535,16 +545,12 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -535,16 +545,12 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 535 | // * we basically do `scanDecls`, updating the namespace as needed | 545 | // * we basically do `scanDecls`, updating the namespace as needed |
| 536 | // * TODO: optimize this to make sure we only do it once a generation i guess? | 546 | // * TODO: optimize this to make sure we only do it once a generation i guess? |
| 537 | // * so everyone lived happily ever after | 547 | // * so everyone lived happily ever after |
| 538 | const file_root_outdated = switch (cau.owner.unwrap()) { | ||
| 539 | .type => |ty| zcu.outdated_file_root.swapRemove(ty), | ||
| 540 | .nav, .none => false, | ||
| 541 | }; | ||
| 542 | 548 | ||
| 543 | if (zcu.fileByIndex(inst_info.file).status != .success_zir) { | 549 | if (zcu.fileByIndex(inst_info.file).status != .success_zir) { |
| 544 | return error.AnalysisFail; | 550 | return error.AnalysisFail; |
| 545 | } | 551 | } |
| 546 | 552 | ||
| 547 | if (!cau_outdated and !file_root_outdated) { | 553 | if (!cau_outdated) { |
| 548 | // We can trust the current information about this `Cau`. | 554 | // We can trust the current information about this `Cau`. |
| 549 | if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) { | 555 | if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) { |
| 550 | return error.AnalysisFail; | 556 | return error.AnalysisFail; |
| ... | @@ -571,10 +577,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu | ... | @@ -571,10 +577,13 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu |
| 571 | 577 | ||
| 572 | const sema_result: SemaCauResult = res: { | 578 | const sema_result: SemaCauResult = res: { |
| 573 | if (inst_info.inst == .main_struct_inst) { | 579 | if (inst_info.inst == .main_struct_inst) { |
| 574 | const changed = try pt.semaFileUpdate(inst_info.file, cau_outdated); | 580 | // Note that this is definitely a *recreation* due to outdated, because |
| 581 | // this instruction indicates that `cau.owner` is a `type`, which only | ||
| 582 | // reaches here if `cau_outdated`. | ||
| 583 | try pt.recreateFileRoot(inst_info.file); | ||
| 575 | break :res .{ | 584 | break :res .{ |
| 576 | .invalidate_decl_val = changed, | 585 | .invalidate_decl_val = true, |
| 577 | .invalidate_decl_ref = changed, | 586 | .invalidate_decl_ref = true, |
| 578 | }; | 587 | }; |
| 579 | } | 588 | } |
| 580 | 589 | ||
| ... | @@ -690,8 +699,8 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter | ... | @@ -690,8 +699,8 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 690 | zcu.potentially_outdated.swapRemove(anal_unit); | 699 | zcu.potentially_outdated.swapRemove(anal_unit); |
| 691 | 700 | ||
| 692 | if (func_outdated) { | 701 | if (func_outdated) { |
| 693 | dev.check(.incremental); | ||
| 694 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 702 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 703 | dev.check(.incremental); | ||
| 695 | zcu.deleteUnitExports(anal_unit); | 704 | zcu.deleteUnitExports(anal_unit); |
| 696 | zcu.deleteUnitReferences(anal_unit); | 705 | zcu.deleteUnitReferences(anal_unit); |
| 697 | } | 706 | } |
| ... | @@ -920,12 +929,9 @@ fn createFileRootStruct( | ... | @@ -920,12 +929,9 @@ fn createFileRootStruct( |
| 920 | return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index); | 929 | return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index); |
| 921 | } | 930 | } |
| 922 | 931 | ||
| 923 | /// Re-analyze the root type of a file on an incremental update. | 932 | /// Recreate the root type of a file after it becomes outdated. A new struct type |
| 924 | /// If `type_outdated`, the struct type itself is considered outdated and is | 933 | /// is constructed at a new InternPool index, reusing the namespace for efficiency. |
| 925 | /// reconstructed at a new InternPool index. Otherwise, the namespace is just | 934 | fn recreateFileRoot(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { |
| 926 | /// re-analyzed. Returns whether the decl's tyval was invalidated. | ||
| 927 | /// Returns `error.AnalysisFail` if the file has an error. | ||
| 928 | fn semaFileUpdate(pt: Zcu.PerThread, file_index: Zcu.File.Index, type_outdated: bool) Zcu.SemaError!bool { | ||
| 929 | const zcu = pt.zcu; | 935 | const zcu = pt.zcu; |
| 930 | const ip = &zcu.intern_pool; | 936 | const ip = &zcu.intern_pool; |
| 931 | const file = zcu.fileByIndex(file_index); | 937 | const file = zcu.fileByIndex(file_index); |
| ... | @@ -934,48 +940,58 @@ fn semaFileUpdate(pt: Zcu.PerThread, file_index: Zcu.File.Index, type_outdated: | ... | @@ -934,48 +940,58 @@ fn semaFileUpdate(pt: Zcu.PerThread, file_index: Zcu.File.Index, type_outdated: |
| 934 | 940 | ||
| 935 | assert(file_root_type != .none); | 941 | assert(file_root_type != .none); |
| 936 | 942 | ||
| 937 | log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{ | 943 | log.debug("recreateFileRoot mod={s} sub_file_path={s}", .{ |
| 938 | file.mod.fully_qualified_name, | 944 | file.mod.fully_qualified_name, |
| 939 | file.sub_file_path, | 945 | file.sub_file_path, |
| 940 | type_outdated, | ||
| 941 | }); | 946 | }); |
| 942 | 947 | ||
| 943 | if (file.status != .success_zir) { | 948 | if (file.status != .success_zir) { |
| 944 | return error.AnalysisFail; | 949 | return error.AnalysisFail; |
| 945 | } | 950 | } |
| 946 | 951 | ||
| 947 | if (type_outdated) { | 952 | // Invalidate the existing type, reusing its namespace. |
| 948 | // Invalidate the existing type, reusing its namespace. | 953 | const file_root_type_cau = ip.loadStructType(file_root_type).cau.unwrap().?; |
| 949 | const file_root_type_cau = ip.loadStructType(file_root_type).cau.unwrap().?; | 954 | ip.removeDependenciesForDepender( |
| 950 | ip.removeDependenciesForDepender( | 955 | zcu.gpa, |
| 951 | zcu.gpa, | 956 | InternPool.AnalUnit.wrap(.{ .cau = file_root_type_cau }), |
| 952 | InternPool.AnalUnit.wrap(.{ .cau = file_root_type_cau }), | 957 | ); |
| 953 | ); | 958 | ip.remove(pt.tid, file_root_type); |
| 954 | ip.remove(pt.tid, file_root_type); | 959 | _ = try pt.createFileRootStruct(file_index, namespace_index); |
| 955 | _ = try pt.createFileRootStruct(file_index, namespace_index); | 960 | } |
| 956 | return true; | ||
| 957 | } | ||
| 958 | |||
| 959 | // Only the struct's namespace is outdated. | ||
| 960 | // Preserve the type - just scan the namespace again. | ||
| 961 | 961 | ||
| 962 | const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; | 962 | /// Re-scan the namespace of a file's root struct type on an incremental update. |
| 963 | const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); | 963 | /// The file must have successfully populated ZIR. |
| 964 | /// If the file's root struct type is not populated (the file is unreferenced), nothing is done. | ||
| 965 | /// This is called by `updateZirRefs` for all updated files before the main work loop. | ||
| 966 | /// This function does not perform any semantic analysis. | ||
| 967 | fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void { | ||
| 968 | const zcu = pt.zcu; | ||
| 964 | 969 | ||
| 965 | var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; | 970 | const file = zcu.fileByIndex(file_index); |
| 966 | extra_index += @intFromBool(small.has_fields_len); | 971 | assert(file.status == .success_zir); |
| 967 | const decls_len = if (small.has_decls_len) blk: { | 972 | const file_root_type = zcu.fileRootType(file_index); |
| 968 | const decls_len = file.zir.extra[extra_index]; | 973 | if (file_root_type == .none) return; |
| 969 | extra_index += 1; | ||
| 970 | break :blk decls_len; | ||
| 971 | } else 0; | ||
| 972 | const decls = file.zir.bodySlice(extra_index, decls_len); | ||
| 973 | 974 | ||
| 974 | if (!type_outdated) { | 975 | log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{ |
| 975 | try pt.scanNamespace(namespace_index, decls); | 976 | file.mod.fully_qualified_name, |
| 976 | } | 977 | file.sub_file_path, |
| 978 | }); | ||
| 977 | 979 | ||
| 978 | return false; | 980 | const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu); |
| 981 | const decls = decls: { | ||
| 982 | const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; | ||
| 983 | const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); | ||
| 984 | |||
| 985 | var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; | ||
| 986 | extra_index += @intFromBool(small.has_fields_len); | ||
| 987 | const decls_len = if (small.has_decls_len) blk: { | ||
| 988 | const decls_len = file.zir.extra[extra_index]; | ||
| 989 | extra_index += 1; | ||
| 990 | break :blk decls_len; | ||
| 991 | } else 0; | ||
| 992 | break :decls file.zir.bodySlice(extra_index, decls_len); | ||
| 993 | }; | ||
| 994 | try pt.scanNamespace(namespace_index, decls); | ||
| 979 | } | 995 | } |
| 980 | 996 | ||
| 981 | /// Regardless of the file status, will create a `Decl` if none exists so that we can track | 997 | /// Regardless of the file status, will create a `Decl` if none exists so that we can track |
| ... | @@ -1052,7 +1068,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult { | ... | @@ -1052,7 +1068,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult { |
| 1052 | const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index }); | 1068 | const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index }); |
| 1053 | 1069 | ||
| 1054 | const cau = ip.getCau(cau_index); | 1070 | const cau = ip.getCau(cau_index); |
| 1055 | const inst_info = cau.zir_index.resolveFull(ip); | 1071 | const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail; |
| 1056 | const file = zcu.fileByIndex(inst_info.file); | 1072 | const file = zcu.fileByIndex(inst_info.file); |
| 1057 | const zir = file.zir; | 1073 | const zir = file.zir; |
| 1058 | 1074 | ||
| ... | @@ -1944,6 +1960,9 @@ const ScanDeclIter = struct { | ... | @@ -1944,6 +1960,9 @@ const ScanDeclIter = struct { |
| 1944 | const cau, const nav = if (existing_cau) |cau_index| cau_nav: { | 1960 | const cau, const nav = if (existing_cau) |cau_index| cau_nav: { |
| 1945 | const nav_index = ip.getCau(cau_index).owner.unwrap().nav; | 1961 | const nav_index = ip.getCau(cau_index).owner.unwrap().nav; |
| 1946 | const nav = ip.getNav(nav_index); | 1962 | const nav = ip.getNav(nav_index); |
| 1963 | if (nav.name != name) { | ||
| 1964 | std.debug.panic("'{}' vs '{}'", .{ nav.name.fmt(ip), name.fmt(ip) }); | ||
| 1965 | } | ||
| 1947 | assert(nav.name == name); | 1966 | assert(nav.name == name); |
| 1948 | assert(nav.fqn == fqn); | 1967 | assert(nav.fqn == fqn); |
| 1949 | break :cau_nav .{ cau_index, nav_index }; | 1968 | break :cau_nav .{ cau_index, nav_index }; |
| ... | @@ -2011,7 +2030,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! | ... | @@ -2011,7 +2030,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! |
| 2011 | 2030 | ||
| 2012 | const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index }); | 2031 | const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index }); |
| 2013 | const func = zcu.funcInfo(func_index); | 2032 | const func = zcu.funcInfo(func_index); |
| 2014 | const inst_info = func.zir_body_inst.resolveFull(ip); | 2033 | const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; |
| 2015 | const file = zcu.fileByIndex(inst_info.file); | 2034 | const file = zcu.fileByIndex(inst_info.file); |
| 2016 | const zir = file.zir; | 2035 | const zir = file.zir; |
| 2017 | 2036 | ||
| ... | @@ -2097,7 +2116,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! | ... | @@ -2097,7 +2116,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! |
| 2097 | }; | 2116 | }; |
| 2098 | defer inner_block.instructions.deinit(gpa); | 2117 | defer inner_block.instructions.deinit(gpa); |
| 2099 | 2118 | ||
| 2100 | const fn_info = sema.code.getFnInfo(func.zirBodyInstUnordered(ip).resolve(ip)); | 2119 | const fn_info = sema.code.getFnInfo(func.zirBodyInstUnordered(ip).resolve(ip) orelse return error.AnalysisFail); |
| 2101 | 2120 | ||
| 2102 | // Here we are performing "runtime semantic analysis" for a function body, which means | 2121 | // Here we are performing "runtime semantic analysis" for a function body, which means |
| 2103 | // we must map the parameter ZIR instructions to `arg` AIR instructions. | 2122 | // we must map the parameter ZIR instructions to `arg` AIR instructions. |
src/codegen.zig+1-1| ... | @@ -98,7 +98,7 @@ pub fn generateLazyFunction( | ... | @@ -98,7 +98,7 @@ pub fn generateLazyFunction( |
| 98 | debug_output: DebugInfoOutput, | 98 | debug_output: DebugInfoOutput, |
| 99 | ) CodeGenError!Result { | 99 | ) CodeGenError!Result { |
| 100 | const zcu = pt.zcu; | 100 | const zcu = pt.zcu; |
| 101 | const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(&zcu.intern_pool).file; | 101 | const file = Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(&zcu.intern_pool); |
| 102 | const target = zcu.fileByIndex(file).mod.resolved_target.result; | 102 | const target = zcu.fileByIndex(file).mod.resolved_target.result; |
| 103 | switch (target_util.zigBackend(target, false)) { | 103 | switch (target_util.zigBackend(target, false)) { |
| 104 | else => unreachable, | 104 | else => unreachable, |
src/codegen/c.zig+1-1| ... | @@ -2585,7 +2585,7 @@ pub fn genTypeDecl( | ... | @@ -2585,7 +2585,7 @@ pub fn genTypeDecl( |
| 2585 | const ty = Type.fromInterned(index); | 2585 | const ty = Type.fromInterned(index); |
| 2586 | _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{}); | 2586 | _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{}); |
| 2587 | try writer.writeByte(';'); | 2587 | try writer.writeByte(';'); |
| 2588 | const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file; | 2588 | const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip); |
| 2589 | if (!zcu.fileByIndex(file_scope).mod.strip) try writer.print(" /* {} */", .{ | 2589 | if (!zcu.fileByIndex(file_scope).mod.strip) try writer.print(" /* {} */", .{ |
| 2590 | ty.containerTypeName(ip).fmt(ip), | 2590 | ty.containerTypeName(ip).fmt(ip), |
| 2591 | }); | 2591 | }); |
src/codegen/llvm.zig+3-3| ... | @@ -1959,7 +1959,7 @@ pub const Object = struct { | ... | @@ -1959,7 +1959,7 @@ pub const Object = struct { |
| 1959 | ); | 1959 | ); |
| 1960 | } | 1960 | } |
| 1961 | 1961 | ||
| 1962 | const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file); | 1962 | const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); |
| 1963 | const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| | 1963 | const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| |
| 1964 | try o.namespaceToDebugScope(parent_namespace) | 1964 | try o.namespaceToDebugScope(parent_namespace) |
| 1965 | else | 1965 | else |
| ... | @@ -2137,7 +2137,7 @@ pub const Object = struct { | ... | @@ -2137,7 +2137,7 @@ pub const Object = struct { |
| 2137 | const name = try o.allocTypeName(ty); | 2137 | const name = try o.allocTypeName(ty); |
| 2138 | defer gpa.free(name); | 2138 | defer gpa.free(name); |
| 2139 | 2139 | ||
| 2140 | const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file); | 2140 | const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); |
| 2141 | const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| | 2141 | const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| |
| 2142 | try o.namespaceToDebugScope(parent_namespace) | 2142 | try o.namespaceToDebugScope(parent_namespace) |
| 2143 | else | 2143 | else |
| ... | @@ -2772,7 +2772,7 @@ pub const Object = struct { | ... | @@ -2772,7 +2772,7 @@ pub const Object = struct { |
| 2772 | fn makeEmptyNamespaceDebugType(o: *Object, ty: Type) !Builder.Metadata { | 2772 | fn makeEmptyNamespaceDebugType(o: *Object, ty: Type) !Builder.Metadata { |
| 2773 | const zcu = o.pt.zcu; | 2773 | const zcu = o.pt.zcu; |
| 2774 | const ip = &zcu.intern_pool; | 2774 | const ip = &zcu.intern_pool; |
| 2775 | const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFull(ip).file); | 2775 | const file = try o.getDebugFile(ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); |
| 2776 | const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| | 2776 | const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| |
| 2777 | try o.namespaceToDebugScope(parent_namespace) | 2777 | try o.namespaceToDebugScope(parent_namespace) |
| 2778 | else | 2778 | else |