| ... | ... | @@ -55,9 +55,20 @@ failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg), |
| 55 | 55 | /// The ErrorMsg memory is owned by the `Export`, using Module's allocator. |
| 56 | 56 | failed_exports: std.AutoHashMap(*Export, *ErrorMsg), |
| 57 | 57 | |
| 58 | /// Incrementing integer used to compare against the corresponding Decl |
| 59 | /// field to determine whether a Decl's status applies to an ongoing update, or a |
| 60 | /// previous analysis. |
| 61 | generation: u32 = 0, |
| 62 | |
| 63 | /// Candidates for deletion. After a semantic analysis update completes, this list |
| 64 | /// contains Decls that need to be deleted if they end up having no references to them. |
| 65 | deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){}, |
| 66 | |
| 58 | 67 | pub const WorkItem = union(enum) { |
| 59 | 68 | /// Write the machine code for a Decl to the output file. |
| 60 | 69 | codegen_decl: *Decl, |
| 70 | /// Decl has been determined to be outdated; perform semantic analysis again. |
| 71 | re_analyze_decl: *Decl, |
| 61 | 72 | }; |
| 62 | 73 | |
| 63 | 74 | pub const Export = struct { |
| ... | ... | @@ -68,6 +79,8 @@ pub const Export = struct { |
| 68 | 79 | link: link.ElfFile.Export, |
| 69 | 80 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. |
| 70 | 81 | owner_decl: *Decl, |
| 82 | /// The Decl being exported. Note this is *not* the Decl performing the export. |
| 83 | exported_decl: *Decl, |
| 71 | 84 | status: enum { |
| 72 | 85 | in_progress, |
| 73 | 86 | failed, |
| ... | ... | @@ -94,8 +107,7 @@ pub const Decl = struct { |
| 94 | 107 | /// This is the base offset that src offsets within this Decl are relative to. |
| 95 | 108 | src: usize, |
| 96 | 109 | /// The most recent value of the Decl after a successful semantic analysis. |
| 97 | | /// The tag for this union is determined by the tag value of the analysis field. |
| 98 | | typed_value: union { |
| 110 | typed_value: union(enum) { |
| 99 | 111 | never_succeeded: void, |
| 100 | 112 | most_recent: TypedValue.Managed, |
| 101 | 113 | }, |
| ... | ... | @@ -104,36 +116,35 @@ pub const Decl = struct { |
| 104 | 116 | /// analysis of the function body is performed with this value set to `success`. Functions |
| 105 | 117 | /// have their own analysis status field. |
| 106 | 118 | analysis: enum { |
| 107 | | initial_in_progress, |
| 119 | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. |
| 120 | in_progress, |
| 108 | 121 | /// This Decl might be OK but it depends on another one which did not successfully complete |
| 109 | | /// semantic analysis. This Decl never had a value computed. |
| 110 | | initial_dependency_failure, |
| 111 | | /// Semantic analysis failure. This Decl never had a value computed. |
| 122 | /// semantic analysis. |
| 123 | dependency_failure, |
| 124 | /// Semantic analysis failure. |
| 112 | 125 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 113 | | initial_sema_failure, |
| 114 | | /// In this case the `typed_value.most_recent` can still be accessed. |
| 126 | sema_failure, |
| 115 | 127 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 116 | 128 | codegen_failure, |
| 117 | | /// In this case the `typed_value.most_recent` can still be accessed. |
| 118 | 129 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 119 | 130 | /// This indicates the failure was something like running out of disk space, |
| 120 | 131 | /// and attempting codegen again may succeed. |
| 121 | 132 | codegen_failure_retryable, |
| 122 | | /// This Decl might be OK but it depends on another one which did not successfully complete |
| 123 | | /// semantic analysis. There is a most recent value available. |
| 124 | | repeat_dependency_failure, |
| 125 | | /// Semantic anlaysis failure, but the `typed_value.most_recent` can be accessed. |
| 126 | | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 127 | | repeat_sema_failure, |
| 128 | | /// Completed successfully before; the `typed_value.most_recent` can be accessed, and |
| 129 | | /// new semantic analysis is in progress. |
| 130 | | repeat_in_progress, |
| 131 | | /// Failed before; the `typed_value.most_recent` is not available, and |
| 132 | | /// new semantic analysis is in progress. |
| 133 | | repeat_in_progress_novalue, |
| 134 | | /// Everything is done and updated. |
| 133 | /// Everything is done. During an update, this Decl may be out of date, depending |
| 134 | /// on its dependencies. The `generation` field can be used to determine if this |
| 135 | /// completion status occurred before or after a given update. |
| 135 | 136 | complete, |
| 137 | /// A Module update is in progress, and this Decl has been flagged as being known |
| 138 | /// to require re-analysis. |
| 139 | outdated, |
| 136 | 140 | }, |
| 141 | /// This flag is set when this Decl is added to a check_for_deletion set, and cleared |
| 142 | /// when removed. |
| 143 | deletion_flag: bool, |
| 144 | /// An integer that can be checked against the corresponding incrementing |
| 145 | /// generation field of Module. This is used to determine whether `complete` status |
| 146 | /// represents pre- or post- re-analysis. |
| 147 | generation: u32, |
| 137 | 148 | |
| 138 | 149 | /// Represents the position of the code in the output file. |
| 139 | 150 | /// This is populated regardless of semantic analysis and code generation. |
| ... | ... | @@ -143,11 +154,9 @@ pub const Decl = struct { |
| 143 | 154 | |
| 144 | 155 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's |
| 145 | 156 | /// typed_value is modified. |
| 146 | | /// TODO look into using a lightweight map/set data structure rather than a linear array. |
| 147 | 157 | dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){}, |
| 148 | 158 | /// The shallow set of other decls whose typed_value changing indicates that this Decl's |
| 149 | 159 | /// typed_value may need to be regenerated. |
| 150 | | /// TODO look into using a lightweight map/set data structure rather than a linear array. |
| 151 | 160 | dependencies: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){}, |
| 152 | 161 | |
| 153 | 162 | pub fn destroy(self: *Decl, allocator: *Allocator) void { |
| ... | ... | @@ -181,7 +190,7 @@ pub const Decl = struct { |
| 181 | 190 | pub fn fullyQualifiedNameHash(self: Decl) Hash { |
| 182 | 191 | // Right now we only have ZIRModule as the source. So this is simply the |
| 183 | 192 | // relative name of the decl. |
| 184 | | return hashSimpleName(mem.spanZ(u8, self.name)); |
| 193 | return hashSimpleName(mem.spanZ(self.name)); |
| 185 | 194 | } |
| 186 | 195 | |
| 187 | 196 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { |
| ... | ... | @@ -209,37 +218,12 @@ pub const Decl = struct { |
| 209 | 218 | } |
| 210 | 219 | |
| 211 | 220 | fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { |
| 212 | | switch (self.analysis) { |
| 213 | | .initial_in_progress, |
| 214 | | .initial_dependency_failure, |
| 215 | | .initial_sema_failure, |
| 216 | | .repeat_in_progress_novalue, |
| 217 | | => return null, |
| 218 | | .codegen_failure, |
| 219 | | .codegen_failure_retryable, |
| 220 | | .repeat_dependency_failure, |
| 221 | | .repeat_sema_failure, |
| 222 | | .repeat_in_progress, |
| 223 | | .complete, |
| 224 | | => return &self.typed_value.most_recent, |
| 225 | | } |
| 226 | | } |
| 227 | | |
| 228 | | fn flagForRegeneration(self: *Decl) void { |
| 229 | | if (self.typedValueManaged() == null) { |
| 230 | | self.analysis = .repeat_in_progress_novalue; |
| 231 | | } else { |
| 232 | | self.analysis = .repeat_in_progress; |
| 221 | switch (self.typed_value) { |
| 222 | .most_recent => |*x| return x, |
| 223 | .never_succeeded => return null, |
| 233 | 224 | } |
| 234 | 225 | } |
| 235 | 226 | |
| 236 | | fn isFlaggedForRegeneration(self: *Decl) bool { |
| 237 | | return switch (self.analysis) { |
| 238 | | .repeat_in_progress, .repeat_in_progress_novalue => true, |
| 239 | | else => false, |
| 240 | | }; |
| 241 | | } |
| 242 | | |
| 243 | 227 | fn removeDependant(self: *Decl, other: *Decl) void { |
| 244 | 228 | for (self.dependants.items) |item, i| { |
| 245 | 229 | if (item == other) { |
| ... | ... | @@ -249,6 +233,16 @@ pub const Decl = struct { |
| 249 | 233 | } |
| 250 | 234 | unreachable; |
| 251 | 235 | } |
| 236 | |
| 237 | fn removeDependency(self: *Decl, other: *Decl) void { |
| 238 | for (self.dependencies.items) |item, i| { |
| 239 | if (item == other) { |
| 240 | _ = self.dependencies.swapRemove(i); |
| 241 | return; |
| 242 | } |
| 243 | } |
| 244 | unreachable; |
| 245 | } |
| 252 | 246 | }; |
| 253 | 247 | |
| 254 | 248 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| ... | ... | @@ -512,6 +506,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 512 | 506 | pub fn deinit(self: *Module) void { |
| 513 | 507 | self.bin_file.deinit(); |
| 514 | 508 | const allocator = self.allocator; |
| 509 | self.deletion_set.deinit(allocator); |
| 515 | 510 | self.work_queue.deinit(); |
| 516 | 511 | { |
| 517 | 512 | var it = self.decl_table.iterator(); |
| ... | ... | @@ -576,6 +571,8 @@ pub fn target(self: Module) std.Target { |
| 576 | 571 | |
| 577 | 572 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 578 | 573 | pub fn update(self: *Module) !void { |
| 574 | self.generation += 1; |
| 575 | |
| 579 | 576 | // TODO Use the cache hash file system to detect which source files changed. |
| 580 | 577 | // Here we simulate a full cache miss. |
| 581 | 578 | // Analyze the root source file now. |
| ... | ... | @@ -588,6 +585,15 @@ pub fn update(self: *Module) !void { |
| 588 | 585 | |
| 589 | 586 | try self.performAllTheWork(); |
| 590 | 587 | |
| 588 | // Process the deletion set. |
| 589 | while (self.deletion_set.popOrNull()) |decl| { |
| 590 | if (decl.dependants.items.len != 0) { |
| 591 | decl.deletion_flag = false; |
| 592 | continue; |
| 593 | } |
| 594 | try self.deleteDecl(decl); |
| 595 | } |
| 596 | |
| 591 | 597 | // Unload all the source files from memory. |
| 592 | 598 | self.root_scope.unload(self.allocator); |
| 593 | 599 | |
| ... | ... | @@ -672,15 +678,12 @@ const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 672 | 678 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 673 | 679 | while (self.work_queue.readItem()) |work_item| switch (work_item) { |
| 674 | 680 | .codegen_decl => |decl| switch (decl.analysis) { |
| 675 | | .initial_in_progress => unreachable, |
| 676 | | .repeat_in_progress => unreachable, |
| 677 | | .repeat_in_progress_novalue => unreachable, |
| 681 | .in_progress => unreachable, |
| 682 | .outdated => unreachable, |
| 678 | 683 | |
| 679 | | .initial_sema_failure, |
| 680 | | .repeat_sema_failure, |
| 684 | .sema_failure, |
| 681 | 685 | .codegen_failure, |
| 682 | | .initial_dependency_failure, |
| 683 | | .repeat_dependency_failure, |
| 686 | .dependency_failure, |
| 684 | 687 | => continue, |
| 685 | 688 | |
| 686 | 689 | .complete, .codegen_failure_retryable => { |
| ... | ... | @@ -706,7 +709,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 706 | 709 | self.bin_file.updateDecl(self, decl) catch |err| switch (err) { |
| 707 | 710 | error.OutOfMemory => return error.OutOfMemory, |
| 708 | 711 | error.AnalysisFail => { |
| 709 | | decl.analysis = .repeat_dependency_failure; |
| 712 | decl.analysis = .dependency_failure; |
| 710 | 713 | }, |
| 711 | 714 | else => { |
| 712 | 715 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| ... | ... | @@ -721,6 +724,40 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 721 | 724 | }; |
| 722 | 725 | }, |
| 723 | 726 | }, |
| 727 | .re_analyze_decl => |decl| switch (decl.analysis) { |
| 728 | .in_progress => unreachable, |
| 729 | |
| 730 | .sema_failure, |
| 731 | .codegen_failure, |
| 732 | .dependency_failure, |
| 733 | .complete, |
| 734 | .codegen_failure_retryable, |
| 735 | => continue, |
| 736 | |
| 737 | .outdated => { |
| 738 | const zir_module = self.getSrcModule(decl.scope) catch |err| switch (err) { |
| 739 | error.OutOfMemory => return error.OutOfMemory, |
| 740 | else => { |
| 741 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 742 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 743 | self.allocator, |
| 744 | decl.src, |
| 745 | "unable to load source file '{}': {}", |
| 746 | .{decl.scope.sub_file_path, @errorName(err)}, |
| 747 | )); |
| 748 | decl.analysis = .codegen_failure_retryable; |
| 749 | continue; |
| 750 | }, |
| 751 | }; |
| 752 | const decl_name = mem.spanZ(decl.name); |
| 753 | // We already detected deletions, so we know this will be found. |
| 754 | const src_decl = zir_module.findDecl(decl_name).?; |
| 755 | self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) { |
| 756 | error.OutOfMemory => return error.OutOfMemory, |
| 757 | error.AnalysisFail => continue, |
| 758 | }; |
| 759 | } |
| 760 | }, |
| 724 | 761 | }; |
| 725 | 762 | } |
| 726 | 763 | |
| ... | ... | @@ -797,13 +834,6 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 797 | 834 | } |
| 798 | 835 | |
| 799 | 836 | fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 800 | | // TODO use the cache to identify, from the modified source files, the decls which have |
| 801 | | // changed based on the span of memory that represents the decl in the re-parsed source file. |
| 802 | | // Use the cached dependency graph to recursively determine the set of decls which need |
| 803 | | // regeneration. |
| 804 | | // Here we simulate adding a source file which was previously not part of the compilation, |
| 805 | | // which means scanning the decls looking for exports. |
| 806 | | // TODO also identify decls that need to be deleted. |
| 807 | 837 | switch (root_scope.status) { |
| 808 | 838 | .never_loaded => { |
| 809 | 839 | const src_module = try self.getSrcModule(root_scope); |
| ... | ... | @@ -814,7 +844,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 814 | 844 | |
| 815 | 845 | for (src_module.decls) |decl| { |
| 816 | 846 | if (decl.cast(zir.Inst.Export)) |export_inst| { |
| 817 | | _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.TextBlock.empty); |
| 847 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base); |
| 818 | 848 | } |
| 819 | 849 | } |
| 820 | 850 | }, |
| ... | ... | @@ -827,107 +857,110 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 827 | 857 | => { |
| 828 | 858 | const src_module = try self.getSrcModule(root_scope); |
| 829 | 859 | |
| 830 | | // Look for changed decls. First we add all the decls that changed |
| 831 | | // into the set. |
| 832 | | var regen_decl_set = std.ArrayList(*Decl).init(self.allocator); |
| 833 | | defer regen_decl_set.deinit(); |
| 834 | | try regen_decl_set.ensureCapacity(src_module.decls.len); |
| 835 | | |
| 836 | 860 | var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator); |
| 837 | 861 | defer exports_to_resolve.deinit(); |
| 838 | 862 | |
| 863 | // Keep track of the decls that we expect to see in this file so that |
| 864 | // we know which ones have been deleted. |
| 865 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); |
| 866 | defer deleted_decls.deinit(); |
| 867 | try deleted_decls.ensureCapacity(self.decl_table.size); |
| 868 | { |
| 869 | var it = self.decl_table.iterator(); |
| 870 | while (it.next()) |kv| { |
| 871 | deleted_decls.putAssumeCapacityNoClobber(kv.value, {}); |
| 872 | } |
| 873 | } |
| 874 | |
| 839 | 875 | for (src_module.decls) |src_decl| { |
| 840 | 876 | const name_hash = Decl.hashSimpleName(src_decl.name); |
| 841 | 877 | if (self.decl_table.get(name_hash)) |kv| { |
| 842 | 878 | const decl = kv.value; |
| 879 | deleted_decls.removeAssertDiscard(decl); |
| 843 | 880 | const new_contents_hash = Decl.hashSimpleName(src_decl.contents); |
| 844 | 881 | if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) { |
| 845 | | std.debug.warn("noticed that '{}' changed\n", .{src_decl.name}); |
| 846 | | regen_decl_set.appendAssumeCapacity(decl); |
| 882 | std.debug.warn("noticed '{}' source changed\n", .{src_decl.name}); |
| 883 | decl.analysis = .outdated; |
| 884 | decl.contents_hash = new_contents_hash; |
| 885 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); |
| 847 | 886 | } |
| 848 | 887 | } else if (src_decl.cast(zir.Inst.Export)) |export_inst| { |
| 849 | 888 | try exports_to_resolve.append(&export_inst.base); |
| 850 | 889 | } |
| 851 | 890 | } |
| 852 | | |
| 853 | | // Next, recursively chase the dependency graph, to populate the set. |
| 854 | 891 | { |
| 855 | | var i: usize = 0; |
| 856 | | while (i < regen_decl_set.items.len) : (i += 1) { |
| 857 | | const decl = regen_decl_set.items[i]; |
| 858 | | if (decl.isFlaggedForRegeneration()) { |
| 859 | | // We already looked at this decl's dependency graph. |
| 860 | | continue; |
| 861 | | } |
| 862 | | decl.flagForRegeneration(); |
| 863 | | // Remove itself from its dependencies, because we are about to destroy the |
| 864 | | // decl pointer. |
| 865 | | for (decl.dependencies.items) |dep| { |
| 866 | | dep.removeDependant(decl); |
| 867 | | } |
| 868 | | // Populate the set with decls that need to get regenerated because they |
| 869 | | // depend on this one. |
| 870 | | // TODO If it is only a function body that is modified, it should break the chain |
| 871 | | // and not cause its dependants to be regenerated. |
| 872 | | for (decl.dependants.items) |dep| { |
| 873 | | if (!dep.isFlaggedForRegeneration()) { |
| 874 | | regen_decl_set.appendAssumeCapacity(dep); |
| 875 | | } |
| 876 | | } |
| 892 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 893 | // with when we delete decls because they are no longer referenced. |
| 894 | var it = deleted_decls.iterator(); |
| 895 | while (it.next()) |kv| { |
| 896 | std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); |
| 897 | try self.deleteDecl(kv.key); |
| 877 | 898 | } |
| 878 | 899 | } |
| 879 | | |
| 880 | | // Remove them all from the decl_table. |
| 881 | | for (regen_decl_set.items) |decl| { |
| 882 | | const decl_name = mem.spanZ(decl.name); |
| 883 | | const old_name_hash = Decl.hashSimpleName(decl_name); |
| 884 | | self.decl_table.removeAssertDiscard(old_name_hash); |
| 885 | | |
| 886 | | if (self.export_owners.remove(decl)) |kv| { |
| 887 | | for (kv.value) |exp| { |
| 888 | | self.bin_file.deleteExport(exp.link); |
| 889 | | } |
| 890 | | freeExportList(self.allocator, kv.value); |
| 891 | | } |
| 900 | for (exports_to_resolve.items) |export_inst| { |
| 901 | _ = try self.resolveDecl(&root_scope.base, export_inst); |
| 892 | 902 | } |
| 903 | }, |
| 904 | } |
| 905 | } |
| 893 | 906 | |
| 894 | | // Regenerate the decls in the set. |
| 895 | | const zir_module = try self.getSrcModule(root_scope); |
| 896 | | |
| 897 | | while (regen_decl_set.popOrNull()) |decl| { |
| 898 | | const decl_name = mem.spanZ(decl.name); |
| 899 | | std.debug.warn("regenerating {}\n", .{decl_name}); |
| 900 | | const saved_link = decl.link; |
| 901 | | const decl_exports_entry = if (self.decl_exports.remove(decl)) |kv| kv.value else null; |
| 902 | | const src_decl = zir_module.findDecl(decl_name) orelse { |
| 903 | | @panic("TODO treat this as a deleted decl"); |
| 904 | | }; |
| 905 | | |
| 906 | | decl.destroy(self.allocator); |
| 907 | | |
| 908 | | const new_decl = self.resolveDecl( |
| 909 | | &root_scope.base, |
| 910 | | src_decl, |
| 911 | | saved_link, |
| 912 | | ) catch |err| switch (err) { |
| 913 | | error.OutOfMemory => return error.OutOfMemory, |
| 914 | | error.AnalysisFail => continue, |
| 915 | | }; |
| 916 | | if (decl_exports_entry) |entry| { |
| 917 | | const gop = try self.decl_exports.getOrPut(new_decl); |
| 918 | | if (gop.found_existing) { |
| 919 | | self.allocator.free(entry); |
| 920 | | } else { |
| 921 | | gop.kv.value = entry; |
| 922 | | } |
| 907 | fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 908 | std.debug.warn("deleting decl '{}'\n", .{decl.name}); |
| 909 | const name_hash = decl.fullyQualifiedNameHash(); |
| 910 | self.decl_table.removeAssertDiscard(name_hash); |
| 911 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| 912 | for (decl.dependencies.items) |dep| { |
| 913 | dep.removeDependant(decl); |
| 914 | if (dep.dependants.items.len == 0) { |
| 915 | // We don't recursively perform a deletion here, because during the update, |
| 916 | // another reference to it may turn up. |
| 917 | assert(!dep.deletion_flag); |
| 918 | dep.deletion_flag = true; |
| 919 | try self.deletion_set.append(self.allocator, dep); |
| 920 | } |
| 921 | } |
| 922 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. |
| 923 | for (decl.dependants.items) |dep| { |
| 924 | dep.removeDependency(decl); |
| 925 | if (dep.analysis != .outdated) { |
| 926 | dep.analysis = .outdated; |
| 927 | try self.work_queue.writeItem(.{ .re_analyze_decl = dep }); |
| 928 | } |
| 929 | } |
| 930 | self.deleteDeclExports(decl); |
| 931 | self.bin_file.freeDecl(decl); |
| 932 | decl.destroy(self.allocator); |
| 933 | } |
| 934 | |
| 935 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of |
| 936 | /// this Decl will cause them to be re-created (or not). |
| 937 | fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 938 | const kv = self.export_owners.remove(decl) orelse return; |
| 939 | |
| 940 | for (kv.value) |exp| { |
| 941 | if (self.decl_exports.get(exp.exported_decl)) |decl_exports_kv| { |
| 942 | // Remove exports with owner_decl matching the regenerating decl. |
| 943 | const list = decl_exports_kv.value; |
| 944 | var i: usize = 0; |
| 945 | var new_len = list.len; |
| 946 | while (i < new_len) { |
| 947 | if (list[i].owner_decl == decl) { |
| 948 | mem.copyBackwards(*Export, list[i..], list[i + 1..new_len]); |
| 949 | new_len -= 1; |
| 950 | } else { |
| 951 | i += 1; |
| 923 | 952 | } |
| 924 | 953 | } |
| 925 | | |
| 926 | | for (exports_to_resolve.items) |export_inst| { |
| 927 | | _ = try self.resolveDecl(&root_scope.base, export_inst, link.ElfFile.TextBlock.empty); |
| 954 | decl_exports_kv.value = self.allocator.shrink(list, new_len); |
| 955 | if (new_len == 0) { |
| 956 | self.decl_exports.removeAssertDiscard(exp.exported_decl); |
| 928 | 957 | } |
| 929 | | }, |
| 958 | } |
| 959 | |
| 960 | self.bin_file.deleteExport(exp.link); |
| 961 | self.allocator.destroy(exp); |
| 930 | 962 | } |
| 963 | self.allocator.free(kv.value); |
| 931 | 964 | } |
| 932 | 965 | |
| 933 | 966 | fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| ... | ... | @@ -959,15 +992,111 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 959 | 992 | }; |
| 960 | 993 | } |
| 961 | 994 | |
| 962 | | fn resolveDecl( |
| 963 | | self: *Module, |
| 964 | | scope: *Scope, |
| 965 | | old_inst: *zir.Inst, |
| 966 | | bin_file_link: link.ElfFile.TextBlock, |
| 967 | | ) InnerError!*Decl { |
| 995 | fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void { |
| 996 | switch (decl.analysis) { |
| 997 | .in_progress => unreachable, |
| 998 | .dependency_failure, |
| 999 | .sema_failure, |
| 1000 | .codegen_failure, |
| 1001 | .codegen_failure_retryable, |
| 1002 | .complete, |
| 1003 | => return, |
| 1004 | |
| 1005 | .outdated => {}, // Decl re-analysis |
| 1006 | } |
| 1007 | std.debug.warn("re-analyzing {}\n", .{decl.name}); |
| 1008 | decl.src = old_inst.src; |
| 1009 | |
| 1010 | // The exports this Decl performs will be re-discovered, so we remove them here |
| 1011 | // prior to re-analysis. |
| 1012 | self.deleteDeclExports(decl); |
| 1013 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. |
| 1014 | for (decl.dependencies.items) |dep| { |
| 1015 | dep.removeDependant(decl); |
| 1016 | if (dep.dependants.items.len == 0) { |
| 1017 | // We don't perform a deletion here, because this Decl or another one |
| 1018 | // may end up referencing it before the update is complete. |
| 1019 | assert(!dep.deletion_flag); |
| 1020 | dep.deletion_flag = true; |
| 1021 | try self.deletion_set.append(self.allocator, dep); |
| 1022 | } |
| 1023 | } |
| 1024 | decl.dependencies.shrink(self.allocator, 0); |
| 1025 | var decl_scope: Scope.DeclAnalysis = .{ |
| 1026 | .decl = decl, |
| 1027 | .arena = std.heap.ArenaAllocator.init(self.allocator), |
| 1028 | }; |
| 1029 | errdefer decl_scope.arena.deinit(); |
| 1030 | |
| 1031 | const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) { |
| 1032 | error.OutOfMemory => return error.OutOfMemory, |
| 1033 | error.AnalysisFail => { |
| 1034 | switch (decl.analysis) { |
| 1035 | .in_progress => decl.analysis = .dependency_failure, |
| 1036 | else => {}, |
| 1037 | } |
| 1038 | decl.generation = self.generation; |
| 1039 | return error.AnalysisFail; |
| 1040 | }, |
| 1041 | }; |
| 1042 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1043 | arena_state.* = decl_scope.arena.state; |
| 1044 | |
| 1045 | var prev_type_has_bits = false; |
| 1046 | var type_changed = true; |
| 1047 | |
| 1048 | if (decl.typedValueManaged()) |tvm| { |
| 1049 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); |
| 1050 | type_changed = !tvm.typed_value.ty.eql(typed_value.ty); |
| 1051 | |
| 1052 | tvm.deinit(self.allocator); |
| 1053 | } |
| 1054 | decl.typed_value = .{ |
| 1055 | .most_recent = .{ |
| 1056 | .typed_value = typed_value, |
| 1057 | .arena = arena_state, |
| 1058 | }, |
| 1059 | }; |
| 1060 | decl.analysis = .complete; |
| 1061 | decl.generation = self.generation; |
| 1062 | if (typed_value.ty.hasCodeGenBits()) { |
| 1063 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 1064 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1065 | // increasing how many computations can be done in parallel. |
| 1066 | try self.bin_file.allocateDeclIndexes(decl); |
| 1067 | try self.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1068 | } else if (prev_type_has_bits) { |
| 1069 | self.bin_file.freeDecl(decl); |
| 1070 | } |
| 1071 | |
| 1072 | // If the decl is a function, and the type is the same, we do not need |
| 1073 | // to chase the dependants. |
| 1074 | if (type_changed or typed_value.val.tag() != .function) { |
| 1075 | for (decl.dependants.items) |dep| { |
| 1076 | switch (dep.analysis) { |
| 1077 | .in_progress => unreachable, |
| 1078 | .outdated => continue, // already queued for update |
| 1079 | |
| 1080 | .dependency_failure, |
| 1081 | .sema_failure, |
| 1082 | .codegen_failure, |
| 1083 | .codegen_failure_retryable, |
| 1084 | .complete, |
| 1085 | => if (dep.generation != self.generation) { |
| 1086 | dep.analysis = .outdated; |
| 1087 | try self.work_queue.writeItem(.{ .re_analyze_decl = dep }); |
| 1088 | }, |
| 1089 | } |
| 1090 | } |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { |
| 968 | 1095 | const hash = Decl.hashSimpleName(old_inst.name); |
| 969 | 1096 | if (self.decl_table.get(hash)) |kv| { |
| 970 | | return kv.value; |
| 1097 | const decl = kv.value; |
| 1098 | try self.reAnalyzeDecl(decl, old_inst); |
| 1099 | return decl; |
| 971 | 1100 | } else { |
| 972 | 1101 | const new_decl = blk: { |
| 973 | 1102 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); |
| ... | ... | @@ -980,9 +1109,11 @@ fn resolveDecl( |
| 980 | 1109 | .scope = scope.namespace(), |
| 981 | 1110 | .src = old_inst.src, |
| 982 | 1111 | .typed_value = .{ .never_succeeded = {} }, |
| 983 | | .analysis = .initial_in_progress, |
| 1112 | .analysis = .in_progress, |
| 1113 | .deletion_flag = false, |
| 984 | 1114 | .contents_hash = Decl.hashSimpleName(old_inst.contents), |
| 985 | | .link = bin_file_link, |
| 1115 | .link = link.ElfFile.TextBlock.empty, |
| 1116 | .generation = 0, |
| 986 | 1117 | }; |
| 987 | 1118 | self.decl_table.putAssumeCapacityNoClobber(hash, new_decl); |
| 988 | 1119 | break :blk new_decl; |
| ... | ... | @@ -998,10 +1129,10 @@ fn resolveDecl( |
| 998 | 1129 | error.OutOfMemory => return error.OutOfMemory, |
| 999 | 1130 | error.AnalysisFail => { |
| 1000 | 1131 | switch (new_decl.analysis) { |
| 1001 | | .initial_in_progress => new_decl.analysis = .initial_dependency_failure, |
| 1002 | | .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure, |
| 1132 | .in_progress => new_decl.analysis = .dependency_failure, |
| 1003 | 1133 | else => {}, |
| 1004 | 1134 | } |
| 1135 | new_decl.generation = self.generation; |
| 1005 | 1136 | return error.AnalysisFail; |
| 1006 | 1137 | }, |
| 1007 | 1138 | }; |
| ... | ... | @@ -1016,14 +1147,13 @@ fn resolveDecl( |
| 1016 | 1147 | }, |
| 1017 | 1148 | }; |
| 1018 | 1149 | new_decl.analysis = .complete; |
| 1150 | new_decl.generation = self.generation; |
| 1019 | 1151 | if (typed_value.ty.hasCodeGenBits()) { |
| 1020 | 1152 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 1021 | 1153 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1022 | 1154 | // increasing how many computations can be done in parallel. |
| 1023 | 1155 | try self.bin_file.allocateDeclIndexes(new_decl); |
| 1024 | | |
| 1025 | | // We ensureCapacity when scanning for decls. |
| 1026 | | self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl }); |
| 1156 | try self.work_queue.writeItem(.{ .codegen_decl = new_decl }); |
| 1027 | 1157 | } |
| 1028 | 1158 | return new_decl; |
| 1029 | 1159 | } |
| ... | ... | @@ -1031,15 +1161,13 @@ fn resolveDecl( |
| 1031 | 1161 | |
| 1032 | 1162 | /// Declares a dependency on the decl. |
| 1033 | 1163 | fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { |
| 1034 | | const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.TextBlock.empty); |
| 1164 | const decl = try self.resolveDecl(scope, old_inst); |
| 1035 | 1165 | switch (decl.analysis) { |
| 1036 | | .initial_in_progress => unreachable, |
| 1037 | | .repeat_in_progress => unreachable, |
| 1038 | | .repeat_in_progress_novalue => unreachable, |
| 1039 | | .initial_dependency_failure, |
| 1040 | | .repeat_dependency_failure, |
| 1041 | | .initial_sema_failure, |
| 1042 | | .repeat_sema_failure, |
| 1166 | .in_progress => unreachable, |
| 1167 | .outdated => unreachable, |
| 1168 | |
| 1169 | .dependency_failure, |
| 1170 | .sema_failure, |
| 1043 | 1171 | .codegen_failure, |
| 1044 | 1172 | .codegen_failure_retryable, |
| 1045 | 1173 | => return error.AnalysisFail, |
| ... | ... | @@ -1134,6 +1262,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In |
| 1134 | 1262 | .src = export_inst.base.src, |
| 1135 | 1263 | .link = .{}, |
| 1136 | 1264 | .owner_decl = owner_decl, |
| 1265 | .exported_decl = exported_decl, |
| 1137 | 1266 | .status = .in_progress, |
| 1138 | 1267 | }; |
| 1139 | 1268 | |
| ... | ... | @@ -2153,11 +2282,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err |
| 2153 | 2282 | switch (scope.tag) { |
| 2154 | 2283 | .decl => { |
| 2155 | 2284 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; |
| 2156 | | switch (decl.analysis) { |
| 2157 | | .initial_in_progress => decl.analysis = .initial_sema_failure, |
| 2158 | | .repeat_in_progress => decl.analysis = .repeat_sema_failure, |
| 2159 | | else => unreachable, |
| 2160 | | } |
| 2285 | decl.analysis = .sema_failure; |
| 2161 | 2286 | self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); |
| 2162 | 2287 | }, |
| 2163 | 2288 | .block => { |