| ... | @@ -149,9 +149,14 @@ error_limit: ErrorInt, | ... | @@ -149,9 +149,14 @@ error_limit: ErrorInt, |
| 149 | /// previous analysis. | 149 | /// previous analysis. |
| 150 | generation: u32 = 0, | 150 | generation: u32 = 0, |
| 151 | | 151 | |
| 152 | /// Value is the number of PO dependencies of this Depender. | 152 | /// Value is the number of PO or outdated Decls which this Depender depends on. |
| 153 | potentially_outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{}, | 153 | potentially_outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{}, |
| 154 | outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, void) = .{}, | 154 | /// Value is the number of PO or outdated Decls which this Depender depends on. |
| | 155 | /// Once this value drops to 0, the Depender is a candidate for re-analysis. |
| | 156 | outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{}, |
| | 157 | /// This contains all `Depender`s in `outdated` whose PO dependency count is 0. |
| | 158 | /// Such `Depender`s are ready for immediate re-analysis. |
| | 159 | outdated_ready: std.AutoArrayHashMapUnmanaged(InternPool.Depender, void) = .{}, |
| 155 | | 160 | |
| 156 | stage1_flags: packed struct { | 161 | stage1_flags: packed struct { |
| 157 | have_winmain: bool = false, | 162 | have_winmain: bool = false, |
| ... | @@ -2485,6 +2490,8 @@ pub fn deinit(zcu: *Zcu) void { | ... | @@ -2485,6 +2490,8 @@ pub fn deinit(zcu: *Zcu) void { |
| 2485 | zcu.global_error_set.deinit(gpa); | 2490 | zcu.global_error_set.deinit(gpa); |
| 2486 | | 2491 | |
| 2487 | zcu.potentially_outdated.deinit(gpa); | 2492 | zcu.potentially_outdated.deinit(gpa); |
| | 2493 | zcu.outdated.deinit(gpa); |
| | 2494 | zcu.outdated_ready.deinit(gpa); |
| 2488 | | 2495 | |
| 2489 | zcu.test_functions.deinit(gpa); | 2496 | zcu.test_functions.deinit(gpa); |
| 2490 | | 2497 | |
| ... | @@ -2851,11 +2858,27 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -2851,11 +2858,27 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 2851 | file.prev_zir = null; | 2858 | file.prev_zir = null; |
| 2852 | } | 2859 | } |
| 2853 | | 2860 | |
| 2854 | if (file.root_decl.unwrap()) |root_decl| { | 2861 | if (file.root_decl.unwrap()) |root_decl| mark_outdated: { |
| 2855 | // The root of this file must be re-analyzed, since the file has changed. | 2862 | // The root of this file must be re-analyzed, since the file has changed. |
| 2856 | comp.mutex.lock(); | 2863 | comp.mutex.lock(); |
| 2857 | defer comp.mutex.unlock(); | 2864 | defer comp.mutex.unlock(); |
| 2858 | try mod.outdated.put(gpa, InternPool.Depender.wrap(.{ .decl = root_decl }), {}); | 2865 | |
| | 2866 | const root_decl_depender = InternPool.Depender.wrap(.{ .decl = root_decl }); |
| | 2867 | |
| | 2868 | const gop = try mod.outdated.getOrPut(gpa, root_decl_depender); |
| | 2869 | // If this Decl is already marked as outdated, nothing needs to be done. |
| | 2870 | if (gop.found_existing) break :mark_outdated; |
| | 2871 | |
| | 2872 | log.debug("outdated: {} (root Decl)", .{root_decl}); |
| | 2873 | |
| | 2874 | // If it's already PO, forward its existing PO dependency count. |
| | 2875 | // Otherwise, it has no PO dependencies yet. |
| | 2876 | if (mod.potentially_outdated.fetchSwapRemove(root_decl_depender)) |kv| { |
| | 2877 | gop.value_ptr.* = kv.value; |
| | 2878 | } else { |
| | 2879 | gop.value_ptr.* = 0; |
| | 2880 | try mod.outdated_ready.put(mod.gpa, root_decl_depender, {}); |
| | 2881 | } |
| 2859 | } | 2882 | } |
| 2860 | } | 2883 | } |
| 2861 | | 2884 | |
| ... | @@ -2953,6 +2976,7 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -2953,6 +2976,7 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { |
| 2953 | // Tracking failed for this instruction. Invalidate associated `src_hash` deps. | 2976 | // Tracking failed for this instruction. Invalidate associated `src_hash` deps. |
| 2954 | zcu.comp.mutex.lock(); | 2977 | zcu.comp.mutex.lock(); |
| 2955 | defer zcu.comp.mutex.unlock(); | 2978 | defer zcu.comp.mutex.unlock(); |
| | 2979 | log.debug("tracking failed for %{d}", .{old_inst}); |
| 2956 | try zcu.markDependeeOutdated(.{ .src_hash = ti_idx }); | 2980 | try zcu.markDependeeOutdated(.{ .src_hash = ti_idx }); |
| 2957 | continue; | 2981 | continue; |
| 2958 | }; | 2982 | }; |
| ... | @@ -2962,6 +2986,12 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -2962,6 +2986,12 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { |
| 2962 | if (std.zig.srcHashEql(old_hash, new_hash)) { | 2986 | if (std.zig.srcHashEql(old_hash, new_hash)) { |
| 2963 | break :hash_changed; | 2987 | break :hash_changed; |
| 2964 | } | 2988 | } |
| | 2989 | log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{ |
| | 2990 | old_inst, |
| | 2991 | ti.inst, |
| | 2992 | std.fmt.fmtSliceHexLower(&old_hash), |
| | 2993 | std.fmt.fmtSliceHexLower(&new_hash), |
| | 2994 | }); |
| 2965 | } | 2995 | } |
| 2966 | // The source hash associated with this instruction changed - invalidate relevant dependencies. | 2996 | // The source hash associated with this instruction changed - invalidate relevant dependencies. |
| 2967 | zcu.comp.mutex.lock(); | 2997 | zcu.comp.mutex.lock(); |
| ... | @@ -3042,25 +3072,80 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { | ... | @@ -3042,25 +3072,80 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { |
| 3042 | } | 3072 | } |
| 3043 | | 3073 | |
| 3044 | pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { | 3074 | pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| | 3075 | log.debug("outdated dependee: {}", .{dependee}); |
| 3045 | var it = zcu.intern_pool.dependencyIterator(dependee); | 3076 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| 3046 | while (it.next()) |depender| { | 3077 | while (it.next()) |depender| { |
| 3047 | if (zcu.outdated.contains(depender)) continue; | 3078 | if (zcu.outdated.contains(depender)) { |
| 3048 | const was_po = zcu.potentially_outdated.swapRemove(depender); | 3079 | // We do not need to increment the PO dep count, as if the outdated |
| 3049 | try zcu.outdated.putNoClobber(zcu.gpa, depender, {}); | 3080 | // dependee is a Decl, we had already marked this as PO. |
| | 3081 | continue; |
| | 3082 | } |
| | 3083 | const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender); |
| | 3084 | try zcu.outdated.putNoClobber( |
| | 3085 | zcu.gpa, |
| | 3086 | depender, |
| | 3087 | // We do not need to increment this count for the same reason as above. |
| | 3088 | if (opt_po_entry) |e| e.value else 0, |
| | 3089 | ); |
| | 3090 | log.debug("outdated: {}", .{depender}); |
| | 3091 | if (opt_po_entry != null) { |
| | 3092 | // This is a new entry with no PO dependencies. |
| | 3093 | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| | 3094 | } |
| 3050 | // If this is a Decl and was not previously PO, we must recursively | 3095 | // If this is a Decl and was not previously PO, we must recursively |
| 3051 | // mark dependencies on its tyval as PO. | 3096 | // mark dependencies on its tyval as PO. |
| 3052 | if (was_po) switch (depender.unwrap()) { | 3097 | if (opt_po_entry == null) switch (depender.unwrap()) { |
| 3053 | .decl => |decl_index| try zcu.markDeclDependenciesPotentiallyOutdated(decl_index), | 3098 | .decl => |decl_index| try zcu.markDeclDependenciesPotentiallyOutdated(decl_index), |
| 3054 | .func => {}, | 3099 | .func => {}, |
| 3055 | }; | 3100 | }; |
| 3056 | } | 3101 | } |
| 3057 | } | 3102 | } |
| 3058 | | 3103 | |
| | 3104 | fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| | 3105 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| | 3106 | while (it.next()) |depender| { |
| | 3107 | if (zcu.outdated.getPtr(depender)) |po_dep_count| { |
| | 3108 | // This depender is already outdated, but it now has one |
| | 3109 | // less PO dependency! |
| | 3110 | po_dep_count.* -= 1; |
| | 3111 | if (po_dep_count.* == 0) { |
| | 3112 | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| | 3113 | } |
| | 3114 | continue; |
| | 3115 | } |
| | 3116 | // This depender is definitely at least PO, because this Decl was just analyzed |
| | 3117 | // due to being outdated. |
| | 3118 | const ptr = zcu.potentially_outdated.getPtr(depender).?; |
| | 3119 | if (ptr.* > 1) { |
| | 3120 | ptr.* -= 1; |
| | 3121 | continue; |
| | 3122 | } |
| | 3123 | |
| | 3124 | // This dependency is no longer PO, i.e. is known to be up-to-date. |
| | 3125 | assert(zcu.potentially_outdated.swapRemove(depender)); |
| | 3126 | // If this is a Decl, we must recursively mark dependencies on its tyval |
| | 3127 | // as no longer PO. |
| | 3128 | switch (depender.unwrap()) { |
| | 3129 | .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }), |
| | 3130 | .func => {}, |
| | 3131 | } |
| | 3132 | } |
| | 3133 | } |
| | 3134 | |
| 3059 | /// Given a Decl which is newly outdated or PO, mark all dependers which depend | 3135 | /// Given a Decl which is newly outdated or PO, mark all dependers which depend |
| 3060 | /// on its tyval as PO. | 3136 | /// on its tyval as PO. |
| 3061 | fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !void { | 3137 | fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !void { |
| 3062 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); | 3138 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); |
| 3063 | while (it.next()) |po| { | 3139 | while (it.next()) |po| { |
| | 3140 | if (zcu.outdated.getPtr(po)) |po_dep_count| { |
| | 3141 | // This dependency is already outdated, but it now has one more PO |
| | 3142 | // dependency. |
| | 3143 | if (po_dep_count.* == 0) { |
| | 3144 | _ = zcu.outdated_ready.swapRemove(po); |
| | 3145 | } |
| | 3146 | po_dep_count.* += 1; |
| | 3147 | continue; |
| | 3148 | } |
| 3064 | if (zcu.potentially_outdated.getPtr(po)) |n| { | 3149 | if (zcu.potentially_outdated.getPtr(po)) |n| { |
| 3065 | // There is now one more PO dependency. | 3150 | // There is now one more PO dependency. |
| 3066 | n.* += 1; | 3151 | n.* += 1; |
| ... | @@ -3077,6 +3162,92 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v | ... | @@ -3077,6 +3162,92 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v |
| 3077 | // TODO: repeat the above for `decl_ty` dependencies when they are introduced | 3162 | // TODO: repeat the above for `decl_ty` dependencies when they are introduced |
| 3078 | } | 3163 | } |
| 3079 | | 3164 | |
| | 3165 | pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender { |
| | 3166 | if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) { |
| | 3167 | log.debug("findOutdatedToAnalyze: no outdated depender", .{}); |
| | 3168 | return null; |
| | 3169 | } |
| | 3170 | |
| | 3171 | // Our goal is to find an outdated Depender which itself has no outdated or |
| | 3172 | // PO dependencies. Most of the time, such a Depender will exist - we track |
| | 3173 | // them in the `outdated_ready` set for efficiency. However, this is not |
| | 3174 | // necessarily the case, since the Decl dependency graph may contain loops |
| | 3175 | // via mutually recursive definitions: |
| | 3176 | // pub const A = struct { b: *B }; |
| | 3177 | // pub const B = struct { b: *A }; |
| | 3178 | // In this case, we must defer to more complex logic below. |
| | 3179 | |
| | 3180 | if (zcu.outdated_ready.count() > 0) { |
| | 3181 | log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{ |
| | 3182 | @tagName(zcu.outdated_ready.keys()[0].unwrap()), |
| | 3183 | switch (zcu.outdated_ready.keys()[0].unwrap()) { |
| | 3184 | inline else => |x| @intFromEnum(x), |
| | 3185 | }, |
| | 3186 | }); |
| | 3187 | return zcu.outdated_ready.keys()[0]; |
| | 3188 | } |
| | 3189 | |
| | 3190 | // There is no single Depender which is ready for re-analysis. Instead, we |
| | 3191 | // must assume that some Decl with PO dependencies is outdated - e.g. in the |
| | 3192 | // above example we arbitrarily pick one of A or B. We should select a Decl, |
| | 3193 | // since a Decl is definitely responsible for the loop in the dependency |
| | 3194 | // graph (since you can't depend on a runtime function analysis!). |
| | 3195 | |
| | 3196 | // The choice of this Decl could have a big impact on how much total |
| | 3197 | // analysis we perform, since if analysis concludes its tyval is unchanged, |
| | 3198 | // then other PO Dependers may be resolved as up-to-date. To hopefully avoid |
| | 3199 | // doing too much work, let's find a Decl which the most things depend on - |
| | 3200 | // the idea is that this will resolve a lot of loops (but this is only a |
| | 3201 | // heuristic). |
| | 3202 | |
| | 3203 | log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{ |
| | 3204 | zcu.outdated.count(), |
| | 3205 | zcu.potentially_outdated.count(), |
| | 3206 | }); |
| | 3207 | |
| | 3208 | var chosen_decl_idx: ?Decl.Index = null; |
| | 3209 | var chosen_decl_dependers: u32 = undefined; |
| | 3210 | |
| | 3211 | for (zcu.outdated.keys()) |depender| { |
| | 3212 | const decl_index = switch (depender.unwrap()) { |
| | 3213 | .decl => |d| d, |
| | 3214 | .func => continue, |
| | 3215 | }; |
| | 3216 | |
| | 3217 | var n: u32 = 0; |
| | 3218 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); |
| | 3219 | while (it.next()) |_| n += 1; |
| | 3220 | |
| | 3221 | if (chosen_decl_idx == null or n > chosen_decl_dependers) { |
| | 3222 | chosen_decl_idx = decl_index; |
| | 3223 | chosen_decl_dependers = n; |
| | 3224 | } |
| | 3225 | } |
| | 3226 | |
| | 3227 | for (zcu.potentially_outdated.keys()) |depender| { |
| | 3228 | const decl_index = switch (depender.unwrap()) { |
| | 3229 | .decl => |d| d, |
| | 3230 | .func => continue, |
| | 3231 | }; |
| | 3232 | |
| | 3233 | var n: u32 = 0; |
| | 3234 | var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); |
| | 3235 | while (it.next()) |_| n += 1; |
| | 3236 | |
| | 3237 | if (chosen_decl_idx == null or n > chosen_decl_dependers) { |
| | 3238 | chosen_decl_idx = decl_index; |
| | 3239 | chosen_decl_dependers = n; |
| | 3240 | } |
| | 3241 | } |
| | 3242 | |
| | 3243 | log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{ |
| | 3244 | chosen_decl_idx.?, |
| | 3245 | chosen_decl_dependers, |
| | 3246 | }); |
| | 3247 | |
| | 3248 | return InternPool.Depender.wrap(.{ .decl = chosen_decl_idx.? }); |
| | 3249 | } |
| | 3250 | |
| 3080 | pub fn mapOldZirToNew( | 3251 | pub fn mapOldZirToNew( |
| 3081 | gpa: Allocator, | 3252 | gpa: Allocator, |
| 3082 | old_zir: Zir, | 3253 | old_zir: Zir, |
| ... | @@ -3204,7 +3375,7 @@ pub fn mapOldZirToNew( | ... | @@ -3204,7 +3375,7 @@ pub fn mapOldZirToNew( |
| 3204 | } | 3375 | } |
| 3205 | } | 3376 | } |
| 3206 | | 3377 | |
| 3207 | /// This ensures that the Decl will have a Type and Value populated. | 3378 | /// This ensures that the Decl will have an up-to-date Type and Value populated. |
| 3208 | /// However the resolution status of the Type may not be fully resolved. | 3379 | /// However the resolution status of the Type may not be fully resolved. |
| 3209 | /// For example an inferred error set is not resolved until after `analyzeFnBody`. | 3380 | /// For example an inferred error set is not resolved until after `analyzeFnBody`. |
| 3210 | /// is called. | 3381 | /// is called. |
| ... | @@ -3214,7 +3385,25 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3214,7 +3385,25 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3214 | | 3385 | |
| 3215 | const decl = mod.declPtr(decl_index); | 3386 | const decl = mod.declPtr(decl_index); |
| 3216 | | 3387 | |
| 3217 | const subsequent_analysis = switch (decl.analysis) { | 3388 | // Determine whether or not this Decl is outdated, i.e. requires re-analysis |
| | 3389 | // even if `complete`. If a Decl is PO, we pessismistically assume that it |
| | 3390 | // *does* require re-analysis, to ensure that the Decl is definitely |
| | 3391 | // up-to-date when this function returns. |
| | 3392 | |
| | 3393 | // If analysis occurs in a poor order, this could result in over-analysis. |
| | 3394 | // We do our best to avoid this by the other dependency logic in this file |
| | 3395 | // which tries to limit re-analysis to Decls whose previously listed |
| | 3396 | // dependencies are all up-to-date. |
| | 3397 | |
| | 3398 | const decl_as_depender = InternPool.Depender.wrap(.{ .decl = decl_index }); |
| | 3399 | const was_outdated = mod.outdated.swapRemove(decl_as_depender) or |
| | 3400 | mod.potentially_outdated.swapRemove(decl_as_depender); |
| | 3401 | |
| | 3402 | if (was_outdated) { |
| | 3403 | _ = mod.outdated_ready.swapRemove(decl_as_depender); |
| | 3404 | } |
| | 3405 | |
| | 3406 | switch (decl.analysis) { |
| 3218 | .in_progress => unreachable, | 3407 | .in_progress => unreachable, |
| 3219 | | 3408 | |
| 3220 | .file_failure, | 3409 | .file_failure, |
| ... | @@ -3226,28 +3415,29 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3226,28 +3415,29 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3226 | .codegen_failure_retryable, | 3415 | .codegen_failure_retryable, |
| 3227 | => return error.AnalysisFail, | 3416 | => return error.AnalysisFail, |
| 3228 | | 3417 | |
| 3229 | .complete => return, | 3418 | .complete => if (was_outdated) { |
| 3230 | | | |
| 3231 | .outdated => blk: { | | |
| 3232 | if (build_options.only_c) unreachable; | 3419 | if (build_options.only_c) unreachable; |
| 3233 | // The exports this Decl performs will be re-discovered, so we remove them here | 3420 | // The exports this Decl performs will be re-discovered, so we remove them here |
| 3234 | // prior to re-analysis. | 3421 | // prior to re-analysis. |
| 3235 | try mod.deleteDeclExports(decl_index); | 3422 | try mod.deleteDeclExports(decl_index); |
| | 3423 | } else return, |
| 3236 | | 3424 | |
| 3237 | break :blk true; | 3425 | .outdated => unreachable, // TODO: remove this field |
| 3238 | }, | | |
| 3239 | | 3426 | |
| 3240 | .unreferenced => false, | 3427 | .unreferenced => {}, |
| 3241 | }; | 3428 | } |
| 3242 | | 3429 | |
| 3243 | var decl_prog_node = mod.sema_prog_node.start("", 0); | 3430 | var decl_prog_node = mod.sema_prog_node.start("", 0); |
| 3244 | decl_prog_node.activate(); | 3431 | decl_prog_node.activate(); |
| 3245 | defer decl_prog_node.end(); | 3432 | defer decl_prog_node.end(); |
| 3246 | | 3433 | |
| 3247 | const type_changed = blk: { | 3434 | const sema_result: SemaDeclResult = blk: { |
| 3248 | if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) { | 3435 | if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) { |
| 3249 | // Anonymous decl. We don't semantically analyze these. | 3436 | // Anonymous decl. We don't semantically analyze these. |
| 3250 | break :blk false; // tv unchanged | 3437 | break :blk .{ |
| | 3438 | .invalidate_decl_val = false, |
| | 3439 | .invalidate_decl_ref = false, |
| | 3440 | }; |
| 3251 | } | 3441 | } |
| 3252 | | 3442 | |
| 3253 | break :blk mod.semaDecl(decl_index) catch |err| switch (err) { | 3443 | break :blk mod.semaDecl(decl_index) catch |err| switch (err) { |
| ... | @@ -3276,9 +3466,18 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { | ... | @@ -3276,9 +3466,18 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 3276 | }; | 3466 | }; |
| 3277 | }; | 3467 | }; |
| 3278 | | 3468 | |
| 3279 | if (subsequent_analysis) { | 3469 | // TODO: we do not yet have separate dependencies for decl values vs types. |
| 3280 | _ = type_changed; | 3470 | if (was_outdated) { |
| 3281 | @panic("TODO re-implement incremental compilation"); | 3471 | if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) { |
| | 3472 | // This dependency was marked as PO, meaning dependees were waiting |
| | 3473 | // on its analysis result, and it has turned out to be outdated. |
| | 3474 | // Update dependees accordingly. |
| | 3475 | try mod.markDependeeOutdated(.{ .decl_val = decl_index }); |
| | 3476 | } else { |
| | 3477 | // This dependency was previously PO, but turned out to be up-to-date. |
| | 3478 | // We do not need to queue successive analysis. |
| | 3479 | try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index }); |
| | 3480 | } |
| 3282 | } | 3481 | } |
| 3283 | } | 3482 | } |
| 3284 | | 3483 | |
| ... | @@ -3591,12 +3790,19 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { | ... | @@ -3591,12 +3790,19 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { |
| 3591 | }, | 3790 | }, |
| 3592 | .incremental => {}, | 3791 | .incremental => {}, |
| 3593 | } | 3792 | } |
| | 3793 | |
| | 3794 | // Since this is our first time analyzing this file, there can be no dependencies on |
| | 3795 | // its root Decl. Thus, we do not need to invalidate any dependencies. |
| 3594 | } | 3796 | } |
| 3595 | | 3797 | |
| 3596 | /// Returns `true` if the Decl type changed. | 3798 | const SemaDeclResult = packed struct { |
| 3597 | /// Returns `true` if this is the first time analyzing the Decl. | 3799 | /// Whether the value of a `decl_val` of this Decl changed. |
| 3598 | /// Returns `false` otherwise. | 3800 | invalidate_decl_val: bool, |
| 3599 | fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | 3801 | /// Whether the type of a `decl_ref` of this Decl changed. |
| | 3802 | invalidate_decl_ref: bool, |
| | 3803 | }; |
| | 3804 | |
| | 3805 | fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { |
| 3600 | const tracy = trace(@src()); | 3806 | const tracy = trace(@src()); |
| 3601 | defer tracy.end(); | 3807 | defer tracy.end(); |
| 3602 | | 3808 | |
| ... | @@ -3674,8 +3880,10 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3674,8 +3880,10 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3674 | }; | 3880 | }; |
| 3675 | defer sema.deinit(); | 3881 | defer sema.deinit(); |
| 3676 | | 3882 | |
| 3677 | // Every Decl has a dependency on its own source. | 3883 | // Every Decl other (than file root Decls, which do not have a ZIR index) has a dependency on its own source. |
| 3678 | try sema.declareDependency(.{ .src_hash = try ip.trackZir(sema.gpa, decl.getFileScope(mod), decl.zir_decl_index.unwrap().?) }); | 3884 | if (decl.zir_decl_index.unwrap()) |zir_decl_index| { |
| | 3885 | try sema.declareDependency(.{ .src_hash = try ip.trackZir(sema.gpa, decl.getFileScope(mod), zir_decl_index) }); |
| | 3886 | } |
| 3679 | | 3887 | |
| 3680 | assert(!mod.declIsRoot(decl_index)); | 3888 | assert(!mod.declIsRoot(decl_index)); |
| 3681 | | 3889 | |
| ... | @@ -3735,7 +3943,11 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3735,7 +3943,11 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3735 | decl.analysis = .complete; | 3943 | decl.analysis = .complete; |
| 3736 | decl.generation = mod.generation; | 3944 | decl.generation = mod.generation; |
| 3737 | | 3945 | |
| 3738 | return true; | 3946 | // TODO: usingnamespace cannot currently participate in incremental compilation |
| | 3947 | return .{ |
| | 3948 | .invalidate_decl_val = true, |
| | 3949 | .invalidate_decl_ref = true, |
| | 3950 | }; |
| 3739 | } | 3951 | } |
| 3740 | | 3952 | |
| 3741 | switch (ip.indexToKey(decl_tv.val.toIntern())) { | 3953 | switch (ip.indexToKey(decl_tv.val.toIntern())) { |
| ... | @@ -3771,15 +3983,16 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3771,15 +3983,16 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3771 | // The scope needs to have the decl in it. | 3983 | // The scope needs to have the decl in it. |
| 3772 | try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); | 3984 | try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); |
| 3773 | } | 3985 | } |
| 3774 | return type_changed or is_inline != prev_is_inline; | 3986 | // TODO: align, linksection, addrspace? |
| | 3987 | const changed = type_changed or is_inline != prev_is_inline; |
| | 3988 | return .{ |
| | 3989 | .invalidate_decl_val = changed, |
| | 3990 | .invalidate_decl_ref = changed, |
| | 3991 | }; |
| 3775 | } | 3992 | } |
| 3776 | }, | 3993 | }, |
| 3777 | else => {}, | 3994 | else => {}, |
| 3778 | } | 3995 | } |
| 3779 | var type_changed = true; | | |
| 3780 | if (decl.has_tv) { | | |
| 3781 | type_changed = !decl.ty.eql(decl_tv.ty, mod); | | |
| 3782 | } | | |
| 3783 | | 3996 | |
| 3784 | decl.owns_tv = false; | 3997 | decl.owns_tv = false; |
| 3785 | var queue_linker_work = false; | 3998 | var queue_linker_work = false; |
| ... | @@ -3807,6 +4020,14 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3807,6 +4020,14 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3807 | }, | 4020 | }, |
| 3808 | } | 4021 | } |
| 3809 | | 4022 | |
| | 4023 | const old_has_tv = decl.has_tv; |
| | 4024 | // The following values are ignored if `!old_has_tv` |
| | 4025 | const old_ty = decl.ty; |
| | 4026 | const old_val = decl.val; |
| | 4027 | const old_align = decl.alignment; |
| | 4028 | const old_linksection = decl.@"linksection"; |
| | 4029 | const old_addrspace = decl.@"addrspace"; |
| | 4030 | |
| 3810 | decl.ty = decl_tv.ty; | 4031 | decl.ty = decl_tv.ty; |
| 3811 | decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); | 4032 | decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); |
| 3812 | decl.alignment = blk: { | 4033 | decl.alignment = blk: { |
| ... | @@ -3850,6 +4071,17 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3850,6 +4071,17 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3850 | decl.analysis = .complete; | 4071 | decl.analysis = .complete; |
| 3851 | decl.generation = mod.generation; | 4072 | decl.generation = mod.generation; |
| 3852 | | 4073 | |
| | 4074 | const result: SemaDeclResult = if (old_has_tv) .{ |
| | 4075 | .invalidate_decl_val = !decl.ty.eql(old_ty, mod) or !decl.val.eql(old_val, decl.ty, mod), |
| | 4076 | .invalidate_decl_ref = !decl.ty.eql(old_ty, mod) or |
| | 4077 | decl.alignment != old_align or |
| | 4078 | decl.@"linksection" != old_linksection or |
| | 4079 | decl.@"addrspace" != old_addrspace, |
| | 4080 | } else .{ |
| | 4081 | .invalidate_decl_val = true, |
| | 4082 | .invalidate_decl_ref = true, |
| | 4083 | }; |
| | 4084 | |
| 3853 | const has_runtime_bits = is_extern or | 4085 | const has_runtime_bits = is_extern or |
| 3854 | (queue_linker_work and try sema.typeHasRuntimeBits(decl.ty)); | 4086 | (queue_linker_work and try sema.typeHasRuntimeBits(decl.ty)); |
| 3855 | | 4087 | |
| ... | @@ -3861,7 +4093,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3861,7 +4093,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3861 | | 4093 | |
| 3862 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); | 4094 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); |
| 3863 | | 4095 | |
| 3864 | if (type_changed and mod.emit_h != null) { | 4096 | if (result.invalidate_decl_ref and mod.emit_h != null) { |
| 3865 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); | 4097 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); |
| 3866 | } | 4098 | } |
| 3867 | } | 4099 | } |
| ... | @@ -3872,7 +4104,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -3872,7 +4104,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 3872 | try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); | 4104 | try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); |
| 3873 | } | 4105 | } |
| 3874 | | 4106 | |
| 3875 | return type_changed; | 4107 | return result; |
| 3876 | } | 4108 | } |
| 3877 | | 4109 | |
| 3878 | pub const ImportFileResult = struct { | 4110 | pub const ImportFileResult = struct { |