From fac120bc3ad58a10ab80952e42becd0084aec059 Mon Sep 17 00:00:00 2001 From: mlugg Date: Wed, 19 Apr 2023 17:40:29 +0100 Subject: [PATCH] Module: mark function body dependencies, don't re-analyze anonymous decls --- src/Module.zig | 140 +++++++++++++++++++++++++++++++------------------ src/Sema.zig | 4 +- 2 files changed, 92 insertions(+), 52 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 70732369ca..c3dbc396df 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -564,7 +564,23 @@ pub const Decl = struct { } }; - pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, void); + pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, DepType); + + /// Later types take priority; e.g. if a dependent decl has both `normal` + /// and `function_body` dependencies on another decl, it will be marked as + /// having a `function_body` dependency. + pub const DepType = enum { + /// The dependent references or uses the dependency's value, so must be + /// updated whenever it is changed. However, if the dependency is a + /// function and its type is unchanged, the dependent does not need to + /// be updated. + normal, + /// The dependent performs an inline or comptime call to the dependency, + /// or is a generic instantiation of it. It must therefore be updated + /// whenever the dependency is updated, even if the function type + /// remained the same. + function_body, + }; pub fn clearName(decl: *Decl, gpa: Allocator) void { gpa.free(mem.sliceTo(decl.name, 0)); @@ -4155,53 +4171,63 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { decl_prog_node.activate(); defer decl_prog_node.end(); - const type_changed = mod.semaDecl(decl_index) catch |err| switch (err) { - error.AnalysisFail => { - if (decl.analysis == .in_progress) { - // If this decl caused the compile error, the analysis field would - // be changed to indicate it was this Decl's fault. Because this - // did not happen, we infer here that it was a dependency failure. - decl.analysis = .dependency_failure; - } - return error.AnalysisFail; - }, - error.NeededSourceLocation => unreachable, - error.GenericPoison => unreachable, - else => |e| { - decl.analysis = .sema_failure_retryable; - try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( - mod.gpa, - decl.srcLoc(), - "unable to analyze: {s}", - .{@errorName(e)}, - )); - return error.AnalysisFail; - }, + const type_changed = blk: { + if (decl.zir_decl_index == 0 and !mod.declIsRoot(decl_index)) { + // Anonymous decl. We don't semantically analyze these. + break :blk false; // tv unchanged + } + + break :blk mod.semaDecl(decl_index) catch |err| switch (err) { + error.AnalysisFail => { + if (decl.analysis == .in_progress) { + // If this decl caused the compile error, the analysis field would + // be changed to indicate it was this Decl's fault. Because this + // did not happen, we infer here that it was a dependency failure. + decl.analysis = .dependency_failure; + } + return error.AnalysisFail; + }, + error.NeededSourceLocation => unreachable, + error.GenericPoison => unreachable, + else => |e| { + decl.analysis = .sema_failure_retryable; + try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); + mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( + mod.gpa, + decl.srcLoc(), + "unable to analyze: {s}", + .{@errorName(e)}, + )); + return error.AnalysisFail; + }, + }; }; if (subsequent_analysis) { - // We may need to chase the dependants and re-analyze them. - // However, if the decl is a function, and the type is the same, we do not need to. - if (type_changed or decl.ty.zigTypeTag() != .Fn) { - for (decl.dependants.keys()) |dep_index| { - const dep = mod.declPtr(dep_index); - switch (dep.analysis) { - .unreferenced => unreachable, - .in_progress => continue, // already doing analysis, ok - .outdated => continue, // already queued for update + // Update all dependents which have at least this level of dependency. + // If our type remained the same and we're a function, only update + // decls which depend on our body; otherwise, update all dependents. + const update_level: Decl.DepType = if (!type_changed and decl.ty.zigTypeTag() == .Fn) .function_body else .normal; - .file_failure, - .dependency_failure, - .sema_failure, - .sema_failure_retryable, - .codegen_failure, - .codegen_failure_retryable, - .complete, - => if (dep.generation != mod.generation) { - try mod.markOutdatedDecl(dep_index); - }, - } + for (decl.dependants.keys(), decl.dependants.values()) |dep_index, dep_type| { + if (@enumToInt(dep_type) < @enumToInt(update_level)) continue; + + const dep = mod.declPtr(dep_index); + switch (dep.analysis) { + .unreferenced => unreachable, + .in_progress => continue, // already doing analysis, ok + .outdated => continue, // already queued for update + + .file_failure, + .dependency_failure, + .sema_failure, + .sema_failure_retryable, + .codegen_failure, + .codegen_failure_retryable, + .complete, + => if (dep.generation != mod.generation) { + try mod.markOutdatedDecl(dep_index); + }, } } } @@ -4742,25 +4768,37 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { /// Returns the depender's index of the dependee. pub fn declareDeclDependency(mod: *Module, depender_index: Decl.Index, dependee_index: Decl.Index) !void { + return mod.declareDeclDependencyType(depender_index, dependee_index, .normal); +} + +/// Returns the depender's index of the dependee. +pub fn declareDeclDependencyType(mod: *Module, depender_index: Decl.Index, dependee_index: Decl.Index, dep_type: Decl.DepType) !void { if (depender_index == dependee_index) return; const depender = mod.declPtr(depender_index); const dependee = mod.declPtr(dependee_index); + if (depender.dependencies.get(dependee_index)) |cur_type| { + if (@enumToInt(cur_type) >= @enumToInt(dep_type)) { + // We already have this dependency (or stricter) marked + return; + } + } + log.debug("{*} ({s}) depends on {*} ({s})", .{ depender, depender.name, dependee, dependee.name, }); - try depender.dependencies.ensureUnusedCapacity(mod.gpa, 1); - try dependee.dependants.ensureUnusedCapacity(mod.gpa, 1); - if (dependee.deletion_flag) { dependee.deletion_flag = false; assert(mod.deletion_set.swapRemove(dependee_index)); } - dependee.dependants.putAssumeCapacity(depender_index, {}); - depender.dependencies.putAssumeCapacity(dependee_index, {}); + try depender.dependencies.ensureUnusedCapacity(mod.gpa, 1); + try dependee.dependants.ensureUnusedCapacity(mod.gpa, 1); + + dependee.dependants.putAssumeCapacity(depender_index, dep_type); + depender.dependencies.putAssumeCapacity(dependee_index, dep_type); } pub const ImportFileResult = struct { @@ -6342,8 +6380,8 @@ pub fn populateTestFunctions( try mod.declPtr(test_name_decl_index).finalizeNewArena(&name_decl_arena); break :n test_name_decl_index; }; - array_decl.dependencies.putAssumeCapacityNoClobber(test_decl_index, {}); - array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl_index, {}); + array_decl.dependencies.putAssumeCapacityNoClobber(test_decl_index, .normal); + array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl_index, .normal); try mod.linkerUpdateDecl(test_name_decl_index); const field_vals = try arena.create([3]Value); diff --git a/src/Sema.zig b/src/Sema.zig index c699cf3d78..b8afce2cdd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6635,6 +6635,8 @@ fn analyzeCall( sema.code = fn_owner_decl.getFileScope().zir; defer sema.code = parent_zir; + try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body); + const parent_inst_map = sema.inst_map; sema.inst_map = .{}; defer { @@ -7331,7 +7333,7 @@ fn instantiateGenericCall( // The generic function Decl is guaranteed to be the first dependency // of each of its instantiations. assert(new_decl.dependencies.keys().len == 0); - try mod.declareDeclDependency(new_decl_index, module_fn.owner_decl); + try mod.declareDeclDependencyType(new_decl_index, module_fn.owner_decl, .function_body); var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); const new_decl_arena_allocator = new_decl_arena.allocator();