| ... | ... | @@ -275,10 +275,16 @@ potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, |
| 275 | 275 | /// Value is the number of PO dependencies of this AnalUnit. |
| 276 | 276 | /// Once this value drops to 0, the AnalUnit is a candidate for re-analysis. |
| 277 | 277 | outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, |
| 278 | | /// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0. |
| 278 | /// This is the set of all `AnalUnit`s in `outdated` whose PO dependency count is 0. |
| 279 | 279 | /// Such `AnalUnit`s are ready for immediate re-analysis. |
| 280 | 280 | /// See `findOutdatedToAnalyze` for details. |
| 281 | | outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, |
| 281 | outdated_ready: struct { |
| 282 | /// These are separate from other units because it allows `findOutdatedToAnalyze` to prioritize |
| 283 | /// functions, which is useful because it means they will be sent to codegen more quickly. |
| 284 | funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), |
| 285 | /// Does not contain `.func` units. |
| 286 | other: std.AutoArrayHashMapUnmanaged(AnalUnit, void), |
| 287 | } = .{ .funcs = .empty, .other = .empty }, |
| 282 | 288 | /// This contains a list of AnalUnit whose analysis or codegen failed, but the |
| 283 | 289 | /// failure was something like running out of disk space, and trying again may |
| 284 | 290 | /// succeed. On the next update, we will flush this list, marking all members of |
| ... | ... | @@ -2835,7 +2841,8 @@ pub fn deinit(zcu: *Zcu) void { |
| 2835 | 2841 | |
| 2836 | 2842 | zcu.potentially_outdated.deinit(gpa); |
| 2837 | 2843 | zcu.outdated.deinit(gpa); |
| 2838 | | zcu.outdated_ready.deinit(gpa); |
| 2844 | zcu.outdated_ready.funcs.deinit(gpa); |
| 2845 | zcu.outdated_ready.other.deinit(gpa); |
| 2839 | 2846 | zcu.retryable_failures.deinit(gpa); |
| 2840 | 2847 | |
| 2841 | 2848 | zcu.test_functions.deinit(gpa); |
| ... | ... | @@ -3065,6 +3072,7 @@ pub fn markDependeeOutdated( |
| 3065 | 3072 | marked_po: enum { not_marked_po, marked_po }, |
| 3066 | 3073 | dependee: InternPool.Dependee, |
| 3067 | 3074 | ) !void { |
| 3075 | const gpa = zcu.comp.gpa; |
| 3068 | 3076 | deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)}); |
| 3069 | 3077 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| 3070 | 3078 | if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); |
| ... | ... | @@ -3078,7 +3086,10 @@ pub fn markDependeeOutdated( |
| 3078 | 3086 | deps_log.debug("outdated {f} => already outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* }); |
| 3079 | 3087 | if (po_dep_count.* == 0) { |
| 3080 | 3088 | deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); |
| 3081 | | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| 3089 | switch (depender.unwrap()) { |
| 3090 | .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}), |
| 3091 | else => try zcu.outdated_ready.other.put(gpa, depender, {}), |
| 3092 | } |
| 3082 | 3093 | } |
| 3083 | 3094 | }, |
| 3084 | 3095 | } |
| ... | ... | @@ -3094,14 +3105,17 @@ pub fn markDependeeOutdated( |
| 3094 | 3105 | }, |
| 3095 | 3106 | }; |
| 3096 | 3107 | try zcu.outdated.putNoClobber( |
| 3097 | | zcu.gpa, |
| 3108 | gpa, |
| 3098 | 3109 | depender, |
| 3099 | 3110 | new_po_dep_count, |
| 3100 | 3111 | ); |
| 3101 | 3112 | deps_log.debug("outdated {f} => new outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count }); |
| 3102 | 3113 | if (new_po_dep_count == 0) { |
| 3103 | 3114 | deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); |
| 3104 | | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| 3115 | switch (depender.unwrap()) { |
| 3116 | .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}), |
| 3117 | else => try zcu.outdated_ready.other.put(gpa, depender, {}), |
| 3118 | } |
| 3105 | 3119 | } |
| 3106 | 3120 | // If this is a Decl and was not previously PO, we must recursively |
| 3107 | 3121 | // mark dependencies on its tyval as PO. |
| ... | ... | @@ -3119,6 +3133,7 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 3119 | 3133 | } |
| 3120 | 3134 | /// Assumes that `zcu.outdated_lock` is already held exclusively. |
| 3121 | 3135 | fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 3136 | const gpa = zcu.comp.gpa; |
| 3122 | 3137 | deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)}); |
| 3123 | 3138 | var it = zcu.intern_pool.dependencyIterator(dependee); |
| 3124 | 3139 | while (it.next()) |depender| { |
| ... | ... | @@ -3129,7 +3144,10 @@ fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 3129 | 3144 | deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* }); |
| 3130 | 3145 | if (po_dep_count.* == 0) { |
| 3131 | 3146 | deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); |
| 3132 | | try zcu.outdated_ready.put(zcu.gpa, depender, {}); |
| 3147 | switch (depender.unwrap()) { |
| 3148 | .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}), |
| 3149 | else => try zcu.outdated_ready.other.put(gpa, depender, {}), |
| 3150 | } |
| 3133 | 3151 | } |
| 3134 | 3152 | continue; |
| 3135 | 3153 | } |
| ... | ... | @@ -3167,7 +3185,8 @@ fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 3167 | 3185 | /// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES. |
| 3168 | 3186 | /// |
| 3169 | 3187 | /// Assumes that `zcu.outdated_lock` is already held exclusively. |
| 3170 | | fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void { |
| 3188 | fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) Allocator.Error!void { |
| 3189 | const gpa = zcu.comp.gpa; |
| 3171 | 3190 | const ip = &zcu.intern_pool; |
| 3172 | 3191 | const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) { |
| 3173 | 3192 | .@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies |
| ... | ... | @@ -3181,10 +3200,12 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni |
| 3181 | 3200 | var it = ip.dependencyIterator(dependee); |
| 3182 | 3201 | while (it.next()) |po| { |
| 3183 | 3202 | if (zcu.outdated.getPtr(po)) |po_dep_count| { |
| 3184 | | // This dependency is already outdated, but it now has one more PO |
| 3185 | | // dependency. |
| 3203 | // This dependency is already outdated, but it now has one more PO dependency. |
| 3186 | 3204 | if (po_dep_count.* == 0) { |
| 3187 | | _ = zcu.outdated_ready.swapRemove(po); |
| 3205 | switch (po.unwrap()) { |
| 3206 | .func => |func| _ = zcu.outdated_ready.funcs.swapRemove(func), |
| 3207 | else => _ = zcu.outdated_ready.other.swapRemove(po), |
| 3208 | } |
| 3188 | 3209 | } |
| 3189 | 3210 | po_dep_count.* += 1; |
| 3190 | 3211 | deps_log.debug("po {f} => {f} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* }); |
| ... | ... | @@ -3196,7 +3217,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni |
| 3196 | 3217 | deps_log.debug("po {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* }); |
| 3197 | 3218 | continue; |
| 3198 | 3219 | } |
| 3199 | | try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); |
| 3220 | try zcu.potentially_outdated.putNoClobber(gpa, po, 1); |
| 3200 | 3221 | deps_log.debug("po {f} => {f} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) }); |
| 3201 | 3222 | // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO. |
| 3202 | 3223 | try zcu.markTransitiveDependersPotentiallyOutdated(po); |
| ... | ... | @@ -3208,10 +3229,20 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni |
| 3208 | 3229 | /// recursive analysis (all of its previously-marked dependencies are already up-to-date), because |
| 3209 | 3230 | /// recursive analysis can cause over-analysis on incremental updates. |
| 3210 | 3231 | pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 3211 | | // MLUGG TODO: priorize `func` units, just like we used to do in the Compilation job queue. |
| 3232 | // We prioritize functions, because the sooner they get analyzed, the sooner they can be send to |
| 3233 | // the codegen backend and linker, which are usually running in parallel (so this can increase |
| 3234 | // parallelism). |
| 3235 | // TODO: perhaps we should also experiment with *avoiding* functions if the codegen/link queue |
| 3236 | // is backed up (for instance due to a very large function). That could help minimize blocking |
| 3237 | // on the main thread in `CodegenTaskPool.start` waiting for the linker to catch up. |
| 3238 | if (zcu.outdated_ready.funcs.count() > 0) { |
| 3239 | const unit: AnalUnit = .wrap(.{ .func = zcu.outdated_ready.funcs.keys()[0] }); |
| 3240 | log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)}); |
| 3241 | return unit; |
| 3242 | } |
| 3212 | 3243 | |
| 3213 | | if (zcu.outdated_ready.count() > 0) { |
| 3214 | | const unit = zcu.outdated_ready.keys()[0]; |
| 3244 | if (zcu.outdated_ready.other.count() > 0) { |
| 3245 | const unit = zcu.outdated_ready.other.keys()[0]; |
| 3215 | 3246 | log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)}); |
| 3216 | 3247 | return unit; |
| 3217 | 3248 | } |
| ... | ... | @@ -3502,13 +3533,12 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void { |
| 3502 | 3533 | assert(func == ip.unwrapCoercedFunc(func)); // analyze the body of the original function, not a coerced one |
| 3503 | 3534 | if (ip.setWantRuntimeFnAnalysis(io, func)) { |
| 3504 | 3535 | // This is the first reference to this function, so we must ensure it will be analyzed. |
| 3505 | | const unit: AnalUnit = .wrap(.{ .func = func }); |
| 3506 | 3536 | if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); |
| 3507 | 3537 | defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); |
| 3508 | 3538 | try zcu.outdated.ensureUnusedCapacity(gpa, 1); |
| 3509 | | try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); |
| 3510 | | zcu.outdated.putAssumeCapacityNoClobber(unit, 0); |
| 3511 | | zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {}); |
| 3539 | try zcu.outdated_ready.funcs.ensureUnusedCapacity(gpa, 1); |
| 3540 | zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .func = func }), 0); |
| 3541 | zcu.outdated_ready.funcs.putAssumeCapacityNoClobber(func, {}); |
| 3512 | 3542 | } |
| 3513 | 3543 | } |
| 3514 | 3544 | |
| ... | ... | @@ -3522,11 +3552,11 @@ pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void { |
| 3522 | 3552 | if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); |
| 3523 | 3553 | defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); |
| 3524 | 3554 | try zcu.outdated.ensureUnusedCapacity(gpa, 2); |
| 3525 | | try zcu.outdated_ready.ensureUnusedCapacity(gpa, 2); |
| 3555 | try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 2); |
| 3526 | 3556 | zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0); |
| 3527 | 3557 | zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), 0); |
| 3528 | | zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), {}); |
| 3529 | | zcu.outdated_ready.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), {}); |
| 3558 | zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), {}); |
| 3559 | zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), {}); |
| 3530 | 3560 | } |
| 3531 | 3561 | } |
| 3532 | 3562 | |
| ... | ... | @@ -3540,9 +3570,9 @@ pub fn queueComptimeUnitAnalysis(zcu: *Zcu, cu: InternPool.ComptimeUnit.Id) Allo |
| 3540 | 3570 | if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io); |
| 3541 | 3571 | defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io); |
| 3542 | 3572 | try zcu.outdated.ensureUnusedCapacity(gpa, 1); |
| 3543 | | try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); |
| 3573 | try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 1); |
| 3544 | 3574 | zcu.outdated.putAssumeCapacityNoClobber(unit, 0); |
| 3545 | | zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {}); |
| 3575 | zcu.outdated_ready.other.putAssumeCapacityNoClobber(unit, {}); |
| 3546 | 3576 | } |
| 3547 | 3577 | |
| 3548 | 3578 | /// If `unit` was marked as outdated or porentially outdated, clears that status and returns `true`. |
| ... | ... | @@ -3552,7 +3582,15 @@ pub fn clearOutdatedState(zcu: *Zcu, unit: AnalUnit) bool { |
| 3552 | 3582 | if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io); |
| 3553 | 3583 | defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io); |
| 3554 | 3584 | if (zcu.outdated.fetchSwapRemove(unit)) |kv| { |
| 3555 | | if (kv.value == 0) assert(zcu.outdated_ready.swapRemove(unit)); |
| 3585 | const was_ready = switch (unit.unwrap()) { |
| 3586 | .func => |func| zcu.outdated_ready.funcs.swapRemove(func), |
| 3587 | else => zcu.outdated_ready.other.swapRemove(unit), |
| 3588 | }; |
| 3589 | if (kv.value == 0) { |
| 3590 | assert(was_ready); |
| 3591 | } else { |
| 3592 | assert(!was_ready); |
| 3593 | } |
| 3556 | 3594 | return true; |
| 3557 | 3595 | } else if (zcu.potentially_outdated.swapRemove(unit)) { |
| 3558 | 3596 | return true; |