authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-28 22:45:59-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-28 22:45:59-04:00
logdadc4327e109e1cb13ae7e6132db586f5f849396
treeb83c3d6b74fe3f299de9a1869e4b64e614d2fa5e
parent2ae9e06363b294f60b62e765d31fce4417e14a9d
parent5d77fede896ff7446e9946f91c74b7632b63253e

Merge branch 'stage2-vaddr-alloc'


6 files changed, 810 insertions(+), 271 deletions(-)

src-self-hosted/Module.zig+367-113
......@@ -55,9 +55,20 @@ failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
5555/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
5656failed_exports: std.AutoHashMap(*Export, *ErrorMsg),
5757
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.
61generation: 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.
65deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){},
66
5867pub const WorkItem = union(enum) {
5968 /// Write the machine code for a Decl to the output file.
6069 codegen_decl: *Decl,
70 /// Decl has been determined to be outdated; perform semantic analysis again.
71 re_analyze_decl: *Decl,
6172};
6273
6374pub const Export = struct {
......@@ -68,6 +79,8 @@ pub const Export = struct {
6879 link: link.ElfFile.Export,
6980 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
7081 owner_decl: *Decl,
82 /// The Decl being exported. Note this is *not* the Decl performing the export.
83 exported_decl: *Decl,
7184 status: enum {
7285 in_progress,
7386 failed,
......@@ -94,8 +107,7 @@ pub const Decl = struct {
94107 /// This is the base offset that src offsets within this Decl are relative to.
95108 src: usize,
96109 /// 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) {
99111 never_succeeded: void,
100112 most_recent: TypedValue.Managed,
101113 },
......@@ -104,50 +116,56 @@ pub const Decl = struct {
104116 /// analysis of the function body is performed with this value set to `success`. Functions
105117 /// have their own analysis status field.
106118 analysis: enum {
107 initial_in_progress,
119 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
120 in_progress,
108121 /// 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.
112125 /// 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,
115127 /// There will be a corresponding ErrorMsg in Module.failed_decls.
116128 codegen_failure,
117 /// In this case the `typed_value.most_recent` can still be accessed.
118129 /// There will be a corresponding ErrorMsg in Module.failed_decls.
119130 /// This indicates the failure was something like running out of disk space,
120131 /// and attempting codegen again may succeed.
121132 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 /// 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.
132136 complete,
137 /// A Module update is in progress, and this Decl has been flagged as being known
138 /// to require re-analysis.
139 outdated,
133140 },
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,
134148
135149 /// Represents the position of the code in the output file.
136150 /// This is populated regardless of semantic analysis and code generation.
137 link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
151 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
152
153 contents_hash: Hash,
138154
139155 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
140156 /// typed_value is modified.
141 /// TODO look into using a lightweight map/set data structure rather than a linear array.
142157 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
143
144 contents_hash: Hash,
158 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
159 /// typed_value may need to be regenerated.
160 dependencies: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
145161
146162 pub fn destroy(self: *Decl, allocator: *Allocator) void {
147163 allocator.free(mem.spanZ(self.name));
148164 if (self.typedValueManaged()) |tvm| {
149165 tvm.deinit(allocator);
150166 }
167 self.dependants.deinit(allocator);
168 self.dependencies.deinit(allocator);
151169 allocator.destroy(self);
152170 }
153171
......@@ -172,7 +190,7 @@ pub const Decl = struct {
172190 pub fn fullyQualifiedNameHash(self: Decl) Hash {
173191 // Right now we only have ZIRModule as the source. So this is simply the
174192 // relative name of the decl.
175 return hashSimpleName(mem.spanZ(u8, self.name));
193 return hashSimpleName(mem.spanZ(self.name));
176194 }
177195
178196 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
......@@ -200,20 +218,31 @@ pub const Decl = struct {
200218 }
201219
202220 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
203 switch (self.analysis) {
204 .initial_in_progress,
205 .initial_dependency_failure,
206 .initial_sema_failure,
207 => return null,
208 .codegen_failure,
209 .codegen_failure_retryable,
210 .repeat_dependency_failure,
211 .repeat_sema_failure,
212 .repeat_in_progress,
213 .complete,
214 => return &self.typed_value.most_recent,
221 switch (self.typed_value) {
222 .most_recent => |*x| return x,
223 .never_succeeded => return null,
215224 }
216225 }
226
227 fn removeDependant(self: *Decl, other: *Decl) void {
228 for (self.dependants.items) |item, i| {
229 if (item == other) {
230 _ = self.dependants.swapRemove(i);
231 return;
232 }
233 }
234 unreachable;
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 }
217246};
218247
219248/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
......@@ -266,12 +295,12 @@ pub const Scope = struct {
266295
267296 /// Asserts the scope has a parent which is a DeclAnalysis and
268297 /// returns the Decl.
269 pub fn decl(self: *Scope) *Decl {
270 switch (self.tag) {
271 .block => return self.cast(Block).?.decl,
272 .decl => return self.cast(DeclAnalysis).?.decl,
273 .zir_module => unreachable,
274 }
298 pub fn decl(self: *Scope) ?*Decl {
299 return switch (self.tag) {
300 .block => self.cast(Block).?.decl,
301 .decl => self.cast(DeclAnalysis).?.decl,
302 .zir_module => null,
303 };
275304 }
276305
277306 /// Asserts the scope has a parent which is a ZIRModule and
......@@ -477,6 +506,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
477506pub fn deinit(self: *Module) void {
478507 self.bin_file.deinit();
479508 const allocator = self.allocator;
509 self.deletion_set.deinit(allocator);
480510 self.work_queue.deinit();
481511 {
482512 var it = self.decl_table.iterator();
......@@ -517,11 +547,7 @@ pub fn deinit(self: *Module) void {
517547 {
518548 var it = self.export_owners.iterator();
519549 while (it.next()) |kv| {
520 const export_list = kv.value;
521 for (export_list) |exp| {
522 allocator.destroy(exp);
523 }
524 allocator.free(export_list);
550 freeExportList(allocator, kv.value);
525551 }
526552 self.export_owners.deinit();
527553 }
......@@ -532,12 +558,21 @@ pub fn deinit(self: *Module) void {
532558 self.* = undefined;
533559}
534560
561fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
562 for (export_list) |exp| {
563 allocator.destroy(exp);
564 }
565 allocator.free(export_list);
566}
567
535568pub fn target(self: Module) std.Target {
536569 return self.bin_file.options.target;
537570}
538571
539572/// Detect changes to source files, perform semantic analysis, and update the output files.
540573pub fn update(self: *Module) !void {
574 self.generation += 1;
575
541576 // TODO Use the cache hash file system to detect which source files changed.
542577 // Here we simulate a full cache miss.
543578 // Analyze the root source file now.
......@@ -550,6 +585,15 @@ pub fn update(self: *Module) !void {
550585
551586 try self.performAllTheWork();
552587
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
553597 // Unload all the source files from memory.
554598 self.root_scope.unload(self.allocator);
555599
......@@ -634,15 +678,12 @@ const InnerError = error{ OutOfMemory, AnalysisFail };
634678pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
635679 while (self.work_queue.readItem()) |work_item| switch (work_item) {
636680 .codegen_decl => |decl| switch (decl.analysis) {
637 .initial_in_progress,
638 .repeat_in_progress,
639 => unreachable,
681 .in_progress => unreachable,
682 .outdated => unreachable,
640683
641 .initial_sema_failure,
642 .repeat_sema_failure,
684 .sema_failure,
643685 .codegen_failure,
644 .initial_dependency_failure,
645 .repeat_dependency_failure,
686 .dependency_failure,
646687 => continue,
647688
648689 .complete, .codegen_failure_retryable => {
......@@ -668,7 +709,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
668709 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
669710 error.OutOfMemory => return error.OutOfMemory,
670711 error.AnalysisFail => {
671 decl.analysis = .repeat_dependency_failure;
712 decl.analysis = .dependency_failure;
672713 },
673714 else => {
674715 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
......@@ -683,9 +724,60 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
683724 };
684725 },
685726 },
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 },
686761 };
687762}
688763
764fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
765 try depender.dependencies.ensureCapacity(self.allocator, depender.dependencies.items.len + 1);
766 try dependee.dependants.ensureCapacity(self.allocator, dependee.dependants.items.len + 1);
767
768 for (depender.dependencies.items) |item| {
769 if (item == dependee) break; // Already in the set.
770 } else {
771 depender.dependencies.appendAssumeCapacity(dependee);
772 }
773
774 for (dependee.dependants.items) |item| {
775 if (item == depender) break; // Already in the set.
776 } else {
777 dependee.dependants.appendAssumeCapacity(depender);
778 }
779}
780
689781fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {
690782 switch (root_scope.source) {
691783 .unloaded => {
......@@ -742,13 +834,6 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
742834}
743835
744836fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
745 // TODO use the cache to identify, from the modified source files, the decls which have
746 // changed based on the span of memory that represents the decl in the re-parsed source file.
747 // Use the cached dependency graph to recursively determine the set of decls which need
748 // regeneration.
749 // Here we simulate adding a source file which was previously not part of the compilation,
750 // which means scanning the decls looking for exports.
751 // TODO also identify decls that need to be deleted.
752837 switch (root_scope.status) {
753838 .never_loaded => {
754839 const src_module = try self.getSrcModule(root_scope);
......@@ -759,7 +844,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
759844
760845 for (src_module.decls) |decl| {
761846 if (decl.cast(zir.Inst.Export)) |export_inst| {
762 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
847 _ = try self.resolveDecl(&root_scope.base, &export_inst.base);
763848 }
764849 }
765850 },
......@@ -772,41 +857,112 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
772857 => {
773858 const src_module = try self.getSrcModule(root_scope);
774859
775 // Look for changed decls.
860 var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator);
861 defer exports_to_resolve.deinit();
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
776875 for (src_module.decls) |src_decl| {
777876 const name_hash = Decl.hashSimpleName(src_decl.name);
778877 if (self.decl_table.get(name_hash)) |kv| {
779878 const decl = kv.value;
879 deleted_decls.removeAssertDiscard(decl);
780880 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
781881 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
782 // TODO recursive dependency management
783 //std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});
784 self.decl_table.removeAssertDiscard(name_hash);
785 const saved_link = decl.link;
786 decl.destroy(self.allocator);
787 if (self.export_owners.getValue(decl)) |exports| {
788 @panic("TODO handle updating a decl that does an export");
789 }
790 const new_decl = self.resolveDecl(
791 &root_scope.base,
792 src_decl,
793 saved_link,
794 ) catch |err| switch (err) {
795 error.OutOfMemory => return error.OutOfMemory,
796 error.AnalysisFail => continue,
797 };
798 if (self.decl_exports.remove(decl)) |entry| {
799 self.decl_exports.putAssumeCapacityNoClobber(new_decl, entry.value);
800 }
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 });
801886 }
802887 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
803 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
888 try exports_to_resolve.append(&export_inst.base);
889 }
890 }
891 {
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);
804898 }
805899 }
900 for (exports_to_resolve.items) |export_inst| {
901 _ = try self.resolveDecl(&root_scope.base, export_inst);
902 }
806903 },
807904 }
808905}
809906
907fn 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).
937fn 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;
952 }
953 }
954 decl_exports_kv.value = self.allocator.shrink(list, new_len);
955 if (new_len == 0) {
956 self.decl_exports.removeAssertDiscard(exp.exported_decl);
957 }
958 }
959
960 self.bin_file.deleteExport(exp.link);
961 self.allocator.destroy(exp);
962 }
963 self.allocator.free(kv.value);
964}
965
810966fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
811967 // Use the Decl's arena for function memory.
812968 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
......@@ -836,15 +992,111 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
836992 };
837993}
838994
839fn resolveDecl(
840 self: *Module,
841 scope: *Scope,
842 old_inst: *zir.Inst,
843 bin_file_link: link.ElfFile.Decl,
844) InnerError!*Decl {
995fn 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
1094fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
8451095 const hash = Decl.hashSimpleName(old_inst.name);
8461096 if (self.decl_table.get(hash)) |kv| {
847 return kv.value;
1097 const decl = kv.value;
1098 try self.reAnalyzeDecl(decl, old_inst);
1099 return decl;
8481100 } else {
8491101 const new_decl = blk: {
8501102 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
......@@ -857,9 +1109,11 @@ fn resolveDecl(
8571109 .scope = scope.namespace(),
8581110 .src = old_inst.src,
8591111 .typed_value = .{ .never_succeeded = {} },
860 .analysis = .initial_in_progress,
1112 .analysis = .in_progress,
1113 .deletion_flag = false,
8611114 .contents_hash = Decl.hashSimpleName(old_inst.contents),
862 .link = bin_file_link,
1115 .link = link.ElfFile.TextBlock.empty,
1116 .generation = 0,
8631117 };
8641118 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
8651119 break :blk new_decl;
......@@ -875,10 +1129,10 @@ fn resolveDecl(
8751129 error.OutOfMemory => return error.OutOfMemory,
8761130 error.AnalysisFail => {
8771131 switch (new_decl.analysis) {
878 .initial_in_progress => new_decl.analysis = .initial_dependency_failure,
879 .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure,
1132 .in_progress => new_decl.analysis = .dependency_failure,
8801133 else => {},
8811134 }
1135 new_decl.generation = self.generation;
8821136 return error.AnalysisFail;
8831137 },
8841138 };
......@@ -893,34 +1147,37 @@ fn resolveDecl(
8931147 },
8941148 };
8951149 new_decl.analysis = .complete;
1150 new_decl.generation = self.generation;
8961151 if (typed_value.ty.hasCodeGenBits()) {
8971152 // We don't fully codegen the decl until later, but we do need to reserve a global
8981153 // offset table index for it. This allows us to codegen decls out of dependency order,
8991154 // increasing how many computations can be done in parallel.
9001155 try self.bin_file.allocateDeclIndexes(new_decl);
901
902 // We ensureCapacity when scanning for decls.
903 self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl });
1156 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
9041157 }
9051158 return new_decl;
9061159 }
9071160}
9081161
1162/// Declares a dependency on the decl.
9091163fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
910 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.Decl.empty);
1164 const decl = try self.resolveDecl(scope, old_inst);
9111165 switch (decl.analysis) {
912 .initial_in_progress => unreachable,
913 .repeat_in_progress => unreachable,
914 .initial_dependency_failure,
915 .repeat_dependency_failure,
916 .initial_sema_failure,
917 .repeat_sema_failure,
1166 .in_progress => unreachable,
1167 .outdated => unreachable,
1168
1169 .dependency_failure,
1170 .sema_failure,
9181171 .codegen_failure,
9191172 .codegen_failure_retryable,
9201173 => return error.AnalysisFail,
9211174
922 .complete => return decl,
1175 .complete => {},
9231176 }
1177 if (scope.decl()) |scope_decl| {
1178 try self.declareDeclDependency(scope_decl, decl);
1179 }
1180 return decl;
9241181}
9251182
9261183fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
......@@ -998,13 +1255,14 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
9981255 const new_export = try self.allocator.create(Export);
9991256 errdefer self.allocator.destroy(new_export);
10001257
1001 const owner_decl = scope.decl();
1258 const owner_decl = scope.decl().?;
10021259
10031260 new_export.* = .{
10041261 .options = .{ .name = symbol_name },
10051262 .src = export_inst.base.src,
10061263 .link = .{},
10071264 .owner_decl = owner_decl,
1265 .exported_decl = exported_decl,
10081266 .status = .in_progress,
10091267 };
10101268
......@@ -1327,7 +1585,7 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError
13271585 new_func.* = .{
13281586 .fn_type = fn_type,
13291587 .analysis = .{ .queued = fn_inst },
1330 .owner_decl = scope.decl(),
1588 .owner_decl = scope.decl().?,
13311589 };
13321590 const fn_payload = try scope.arena().create(Value.Payload.Function);
13331591 fn_payload.* = .{ .func = new_func };
......@@ -2024,11 +2282,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
20242282 switch (scope.tag) {
20252283 .decl => {
20262284 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
2027 switch (decl.analysis) {
2028 .initial_in_progress => decl.analysis = .initial_sema_failure,
2029 .repeat_in_progress => decl.analysis = .repeat_sema_failure,
2030 else => unreachable,
2031 }
2285 decl.analysis = .sema_failure;
20322286 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
20332287 },
20342288 .block => {
src-self-hosted/link.zig+330-147
......@@ -126,6 +126,10 @@ pub const ElfFile = struct {
126126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128128
129 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
130 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
131 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
132
129133 /// Same order as in the file. The value is the absolute vaddr value.
130134 /// If the vaddr of the executable program header changes, the entire
131135 /// offset table needs to be rewritten.
......@@ -138,11 +142,39 @@ pub const ElfFile = struct {
138142
139143 error_flags: ErrorFlags = ErrorFlags{},
140144
145 /// A list of text blocks that have surplus capacity. This list can have false
146 /// positives, as functions grow and shrink over time, only sometimes being added
147 /// or removed from the freelist.
148 ///
149 /// A text block has surplus capacity when its overcapacity value is greater than
150 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
151 /// much extra capacity, that we could fit a small new symbol in it, itself with
152 /// ideal_capacity or more.
153 ///
154 /// Ideal capacity is defined by size * alloc_num / alloc_den.
155 ///
156 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
157 /// overcapacity can be negative. A simple way to have negative overcapacity is to
158 /// allocate a fresh text block, which will have ideal capacity, and then grow it
159 /// by 1 byte. It will then have -1 overcapacity.
160 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
161 last_text_block: ?*TextBlock = null,
162
163 /// `alloc_num / alloc_den` is the factor of padding when allocating.
164 const alloc_num = 4;
165 const alloc_den = 3;
166
167 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
168 /// it as a possible place to put new symbols, it must have enough room for this many bytes
169 /// (plus extra for reserved capacity).
170 const minimum_text_block_size = 64;
171 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
172
141173 pub const ErrorFlags = struct {
142174 no_entry_point_found: bool = false,
143175 };
144176
145 pub const Decl = struct {
177 pub const TextBlock = struct {
146178 /// Each decl always gets a local symbol with the fully qualified name.
147179 /// The vaddr and size are found here directly.
148180 /// The file offset is found by computing the vaddr offset from the section vaddr
......@@ -152,11 +184,43 @@ pub const ElfFile = struct {
152184 local_sym_index: u32,
153185 /// This field is undefined for symbols with size = 0.
154186 offset_table_index: u32,
187 /// Points to the previous and next neighbors, based on the `text_offset`.
188 /// This can be used to find, for example, the capacity of this `TextBlock`.
189 prev: ?*TextBlock,
190 next: ?*TextBlock,
155191
156 pub const empty = Decl{
192 pub const empty = TextBlock{
157193 .local_sym_index = 0,
158194 .offset_table_index = undefined,
195 .prev = null,
196 .next = null,
159197 };
198
199 /// Returns how much room there is to grow in virtual address space.
200 /// File offset relocation happens transparently, so it is not included in
201 /// this calculation.
202 fn capacity(self: TextBlock, elf_file: ElfFile) u64 {
203 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
204 if (self.next) |next| {
205 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
206 return next_sym.st_value - self_sym.st_value;
207 } else {
208 // We are the last block. The capacity is limited only by virtual address space.
209 return std.math.maxInt(u32) - self_sym.st_value;
210 }
211 }
212
213 fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool {
214 // No need to keep a free list node for the last block.
215 const next = self.next orelse return false;
216 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
217 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
218 const cap = next_sym.st_value - self_sym.st_value;
219 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
220 if (cap <= ideal_cap) return false;
221 const surplus = cap - ideal_cap;
222 return surplus >= min_text_capacity;
223 }
160224 };
161225
162226 pub const Export = struct {
......@@ -169,6 +233,10 @@ pub const ElfFile = struct {
169233 self.shstrtab.deinit(self.allocator);
170234 self.local_symbols.deinit(self.allocator);
171235 self.global_symbols.deinit(self.allocator);
236 self.global_symbol_free_list.deinit(self.allocator);
237 self.local_symbol_free_list.deinit(self.allocator);
238 self.offset_table_free_list.deinit(self.allocator);
239 self.text_block_free_list.deinit(self.allocator);
172240 self.offset_table.deinit(self.allocator);
173241 if (self.owns_file_handle) {
174242 if (self.file) |f| f.close();
......@@ -193,10 +261,6 @@ pub const ElfFile = struct {
193261 });
194262 }
195263
196 // `alloc_num / alloc_den` is the factor of padding when allocation
197 const alloc_num = 4;
198 const alloc_den = 3;
199
200264 /// Returns end pos of collision, if any.
201265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
202266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
......@@ -448,6 +512,13 @@ pub const ElfFile = struct {
448512 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
449513 self.phdr_table_dirty = true;
450514 }
515 {
516 // Iterate over symbols, populating free_list and last_text_block.
517 if (self.local_symbols.items.len != 1) {
518 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
519 }
520 // We are starting with an empty file. The default values are correct, null and empty list.
521 }
451522 }
452523
453524 /// Commit pending changes and write headers.
......@@ -577,7 +648,6 @@ pub const ElfFile = struct {
577648 self.error_flags.no_entry_point_found = false;
578649 try self.writeElfHeader();
579650 }
580 // TODO find end pos and truncate
581651
582652 // The point of flush() is to commit changes, so nothing should be dirty after this.
583653 assert(!self.phdr_table_dirty);
......@@ -709,100 +779,222 @@ pub const ElfFile = struct {
709779 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
710780 }
711781
712 const AllocatedBlock = struct {
713 vaddr: u64,
714 file_offset: u64,
715 size_capacity: u64,
716 };
782 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {
783 var already_have_free_list_node = false;
784 {
785 var i: usize = 0;
786 while (i < self.text_block_free_list.items.len) {
787 if (self.text_block_free_list.items[i] == text_block) {
788 _ = self.text_block_free_list.swapRemove(i);
789 continue;
790 }
791 if (self.text_block_free_list.items[i] == text_block.prev) {
792 already_have_free_list_node = true;
793 }
794 i += 1;
795 }
796 }
717797
718 fn allocateTextBlock(self: *ElfFile, new_block_size: u64, alignment: u64) !AllocatedBlock {
719 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
720 const shdr = &self.sections.items[self.text_section_index.?];
798 if (self.last_text_block == text_block) {
799 // TODO shrink the .text section size here
800 self.last_text_block = text_block.prev;
801 }
721802
722 // TODO Also detect virtual address collisions.
723 const text_capacity = self.allocatedSize(shdr.sh_offset);
724 // TODO instead of looping here, maintain a free list and a pointer to the end.
725 var last_start: u64 = phdr.p_vaddr;
726 var last_size: u64 = 0;
727 for (self.local_symbols.items) |sym| {
728 if (sym.st_value + sym.st_size > last_start + last_size) {
729 last_start = sym.st_value;
730 last_size = sym.st_size;
803 if (text_block.prev) |prev| {
804 prev.next = text_block.next;
805
806 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
807 // The free list is heuristics, it doesn't have to be perfect, so we can
808 // ignore the OOM here.
809 self.text_block_free_list.append(self.allocator, prev) catch {};
731810 }
811 } else {
812 text_block.prev = null;
732813 }
733 const end_vaddr = last_start + (last_size * alloc_num / alloc_den);
734 const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment);
735 const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr;
736 if (needed_size > text_capacity) {
737 // Must move the entire text section.
738 const new_offset = self.findFreeSpace(needed_size, 0x1000);
739 const text_size = (last_start + last_size) - phdr.p_vaddr;
740 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
741 if (amt != text_size) return error.InputOutput;
742 shdr.sh_offset = new_offset;
743 phdr.p_offset = new_offset;
814
815 if (text_block.next) |next| {
816 next.prev = text_block.prev;
817 } else {
818 text_block.next = null;
744819 }
745 // Now that we know the code size, we need to update the program header for executable code
746 shdr.sh_size = needed_size;
747 phdr.p_memsz = needed_size;
748 phdr.p_filesz = needed_size;
749
750 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
751 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
752
753 return AllocatedBlock{
754 .vaddr = aligned_start_vaddr,
755 .file_offset = shdr.sh_offset + (aligned_start_vaddr - phdr.p_vaddr),
756 .size_capacity = text_capacity - needed_size,
757 };
758820 }
759821
760 fn findAllocatedTextBlock(self: *ElfFile, sym: elf.Elf64_Sym) AllocatedBlock {
822 fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void {
823 // TODO check the new capacity, and if it crosses the size threshold into a big enough
824 // capacity, insert a free list node for it.
825 }
826
827 fn growTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
828 const sym = self.local_symbols.items[text_block.local_sym_index];
829 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
830 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
831 if (!need_realloc) return sym.st_value;
832 return self.allocateTextBlock(text_block, new_block_size, alignment);
833 }
834
835 fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
761836 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
762837 const shdr = &self.sections.items[self.text_section_index.?];
838 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
839
840 // We use these to indicate our intention to update metadata, placing the new block,
841 // and possibly removing a free list node.
842 // It would be simpler to do it inside the for loop below, but that would cause a
843 // problem if an error was returned later in the function. So this action
844 // is actually carried out at the end of the function, when errors are no longer possible.
845 var block_placement: ?*TextBlock = null;
846 var free_list_removal: ?usize = null;
847
848 // First we look for an appropriately sized free list node.
849 // The list is unordered. We'll just take the first thing that works.
850 const vaddr = blk: {
851 var i: usize = 0;
852 while (i < self.text_block_free_list.items.len) {
853 const big_block = self.text_block_free_list.items[i];
854 // We now have a pointer to a live text block that has too much capacity.
855 // Is it enough that we could fit this new text block?
856 const sym = self.local_symbols.items[big_block.local_sym_index];
857 const capacity = big_block.capacity(self.*);
858 const ideal_capacity = capacity * alloc_num / alloc_den;
859 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
860 const capacity_end_vaddr = sym.st_value + capacity;
861 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
862 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
863 if (new_start_vaddr < ideal_capacity_end_vaddr) {
864 // Additional bookkeeping here to notice if this free list node
865 // should be deleted because the block that it points to has grown to take up
866 // more of the extra capacity.
867 if (!big_block.freeListEligible(self.*)) {
868 _ = self.text_block_free_list.swapRemove(i);
869 } else {
870 i += 1;
871 }
872 continue;
873 }
874 // At this point we know that we will place the new block here. But the
875 // remaining question is whether there is still yet enough capacity left
876 // over for there to still be a free list node.
877 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
878 const keep_free_list_node = remaining_capacity >= min_text_capacity;
879
880 // Set up the metadata to be updated, after errors are no longer possible.
881 block_placement = big_block;
882 if (!keep_free_list_node) {
883 free_list_removal = i;
884 }
885 break :blk new_start_vaddr;
886 } else if (self.last_text_block) |last| {
887 const sym = self.local_symbols.items[last.local_sym_index];
888 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
889 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
890 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
891 // Set up the metadata to be updated, after errors are no longer possible.
892 block_placement = last;
893 break :blk new_start_vaddr;
894 } else {
895 break :blk phdr.p_vaddr;
896 }
897 };
898
899 const expand_text_section = block_placement == null or block_placement.?.next == null;
900 if (expand_text_section) {
901 const text_capacity = self.allocatedSize(shdr.sh_offset);
902 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
903 if (needed_size > text_capacity) {
904 // Must move the entire text section.
905 const new_offset = self.findFreeSpace(needed_size, 0x1000);
906 const text_size = if (self.last_text_block) |last| blk: {
907 const sym = self.local_symbols.items[last.local_sym_index];
908 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
909 } else 0;
910 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
911 if (amt != text_size) return error.InputOutput;
912 shdr.sh_offset = new_offset;
913 phdr.p_offset = new_offset;
914 }
915 self.last_text_block = text_block;
763916
764 // Find the next sym after this one.
765 // TODO look into using a hash map to speed up perf.
766 const text_capacity = self.allocatedSize(shdr.sh_offset);
767 var next_vaddr_start = phdr.p_vaddr + text_capacity;
768 for (self.local_symbols.items) |elem| {
769 if (elem.st_value < sym.st_value) continue;
770 if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value;
917 shdr.sh_size = needed_size;
918 phdr.p_memsz = needed_size;
919 phdr.p_filesz = needed_size;
920
921 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
922 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
771923 }
772 return .{
773 .vaddr = sym.st_value,
774 .file_offset = shdr.sh_offset + (sym.st_value - phdr.p_vaddr),
775 .size_capacity = next_vaddr_start - sym.st_value,
776 };
924
925 // This function can also reallocate a text block.
926 // In this case we need to "unplug" it from its previous location before
927 // plugging it in to its new location.
928 if (text_block.prev) |prev| {
929 prev.next = text_block.next;
930 }
931 if (text_block.next) |next| {
932 next.prev = text_block.prev;
933 }
934
935 if (block_placement) |big_block| {
936 text_block.prev = big_block;
937 text_block.next = big_block.next;
938 big_block.next = text_block;
939 } else {
940 text_block.prev = null;
941 text_block.next = null;
942 }
943 if (free_list_removal) |i| {
944 _ = self.text_block_free_list.swapRemove(i);
945 }
946 return vaddr;
777947 }
778948
779949 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
780950 if (decl.link.local_sym_index != 0) return;
781951
952 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
782953 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
954 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
783955 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
784 const local_sym_index = self.local_symbols.items.len;
785 const offset_table_index = self.offset_table.items.len;
956 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
957
958 if (self.local_symbol_free_list.popOrNull()) |i| {
959 //std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
960 decl.link.local_sym_index = i;
961 } else {
962 //std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
963 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
964 _ = self.local_symbols.addOneAssumeCapacity();
965 }
966
967 if (self.offset_table_free_list.popOrNull()) |i| {
968 decl.link.offset_table_index = i;
969 } else {
970 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
971 _ = self.offset_table.addOneAssumeCapacity();
972 self.offset_table_count_dirty = true;
973 }
974
786975 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
787976
788 self.local_symbols.appendAssumeCapacity(.{
977 self.local_symbols.items[decl.link.local_sym_index] = .{
789978 .st_name = 0,
790979 .st_info = 0,
791980 .st_other = 0,
792981 .st_shndx = 0,
793982 .st_value = phdr.p_vaddr,
794983 .st_size = 0,
795 });
796 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
797 self.offset_table.appendAssumeCapacity(0);
798 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
984 };
985 self.offset_table.items[decl.link.offset_table_index] = 0;
986 }
799987
800 self.offset_table_count_dirty = true;
988 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {
989 self.freeTextBlock(&decl.link);
990 if (decl.link.local_sym_index != 0) {
991 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
992 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
801993
802 decl.link = .{
803 .local_sym_index = @intCast(u32, local_sym_index),
804 .offset_table_index = @intCast(u32, offset_table_index),
805 };
994 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
995
996 decl.link.local_sym_index = 0;
997 }
806998 }
807999
8081000 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
......@@ -822,80 +1014,60 @@ pub const ElfFile = struct {
8221014
8231015 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
8241016
825 const file_offset = blk: {
826 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
827 .Fn => elf.STT_FUNC,
828 else => elf.STT_OBJECT,
829 };
1017 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1018 .Fn => elf.STT_FUNC,
1019 else => elf.STT_OBJECT,
1020 };
8301021
831 if (decl.link.local_sym_index != 0) {
832 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
833 const existing_block = self.findAllocatedTextBlock(local_sym.*);
834 const need_realloc = local_sym.st_size == 0 or
835 code.len > existing_block.size_capacity or
836 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
837 // TODO check for collision with another symbol
838 const file_offset = if (need_realloc) fo: {
839 const new_block = try self.allocateTextBlock(code.len, required_alignment);
840 local_sym.st_value = new_block.vaddr;
841 self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;
842
843 //std.debug.warn("{}: writing got index {}=0x{x}\n", .{
844 // decl.name,
845 // decl.link.offset_table_index,
846 // self.offset_table.items[decl.link.offset_table_index],
847 //});
1022 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1023 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1024 if (local_sym.st_size != 0) {
1025 const capacity = decl.link.capacity(self.*);
1026 const need_realloc = code.len > capacity or
1027 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1028 if (need_realloc) {
1029 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1030 //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1031 if (vaddr != local_sym.st_value) {
1032 local_sym.st_value = vaddr;
1033
1034 //std.debug.warn(" (writing new offset table entry)\n", .{});
1035 self.offset_table.items[decl.link.offset_table_index] = vaddr;
8481036 try self.writeOffsetTableEntry(decl.link.offset_table_index);
849
850 break :fo new_block.file_offset;
851 } else existing_block.file_offset;
852 local_sym.st_size = code.len;
853 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
854 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
855 local_sym.st_other = 0;
856 local_sym.st_shndx = self.text_section_index.?;
857 // TODO this write could be avoided if no fields of the symbol were changed.
858 try self.writeSymbol(decl.link.local_sym_index);
859
860 //std.debug.warn("updating {} at vaddr 0x{x}\n", .{ decl.name, local_sym.st_value });
861 break :blk file_offset;
862 } else {
863 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
864 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
865 const decl_name = mem.spanZ(decl.name);
866 const name_str_index = try self.makeString(decl_name);
867 const new_block = try self.allocateTextBlock(code.len, required_alignment);
868 const local_sym_index = self.local_symbols.items.len;
869 const offset_table_index = self.offset_table.items.len;
870
871 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });
872 self.local_symbols.appendAssumeCapacity(.{
873 .st_name = name_str_index,
874 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
875 .st_other = 0,
876 .st_shndx = self.text_section_index.?,
877 .st_value = new_block.vaddr,
878 .st_size = code.len,
879 });
880 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
881 self.offset_table.appendAssumeCapacity(new_block.vaddr);
882 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
883
884 self.offset_table_count_dirty = true;
885
886 try self.writeSymbol(local_sym_index);
887 try self.writeOffsetTableEntry(offset_table_index);
888
889 decl.link = .{
890 .local_sym_index = @intCast(u32, local_sym_index),
891 .offset_table_index = @intCast(u32, offset_table_index),
892 };
893
894 //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr });
895 break :blk new_block.file_offset;
1037 }
1038 } else if (code.len < local_sym.st_size) {
1039 self.shrinkTextBlock(&decl.link, code.len);
8961040 }
897 };
1041 local_sym.st_size = code.len;
1042 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1043 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1044 local_sym.st_other = 0;
1045 local_sym.st_shndx = self.text_section_index.?;
1046 // TODO this write could be avoided if no fields of the symbol were changed.
1047 try self.writeSymbol(decl.link.local_sym_index);
1048 } else {
1049 const decl_name = mem.spanZ(decl.name);
1050 const name_str_index = try self.makeString(decl_name);
1051 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1052 //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1053 errdefer self.freeTextBlock(&decl.link);
1054
1055 local_sym.* = .{
1056 .st_name = name_str_index,
1057 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1058 .st_other = 0,
1059 .st_shndx = self.text_section_index.?,
1060 .st_value = vaddr,
1061 .st_size = code.len,
1062 };
1063 self.offset_table.items[decl.link.offset_table_index] = vaddr;
8981064
1065 try self.writeSymbol(decl.link.local_sym_index);
1066 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1067 }
1068
1069 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1070 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
8991071 try self.file.?.pwriteAll(code, file_offset);
9001072
9011073 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
......@@ -910,7 +1082,10 @@ pub const ElfFile = struct {
9101082 decl: *const Module.Decl,
9111083 exports: []const *Module.Export,
9121084 ) !void {
1085 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1086 // them, so that deleting exports is guaranteed to succeed.
9131087 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1088 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
9141089 const typed_value = decl.typed_value.most_recent.typed_value;
9151090 if (decl.link.local_sym_index == 0) return;
9161091 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
......@@ -957,22 +1132,30 @@ pub const ElfFile = struct {
9571132 };
9581133 } else {
9591134 const name = try self.makeString(exp.options.name);
960 const i = self.global_symbols.items.len;
961 self.global_symbols.appendAssumeCapacity(.{
1135 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1136 _ = self.global_symbols.addOneAssumeCapacity();
1137 break :blk self.global_symbols.items.len - 1;
1138 };
1139 self.global_symbols.items[i] = .{
9621140 .st_name = name,
9631141 .st_info = (stb_bits << 4) | stt_bits,
9641142 .st_other = 0,
9651143 .st_shndx = self.text_section_index.?,
9661144 .st_value = decl_sym.st_value,
9671145 .st_size = decl_sym.st_size,
968 });
969 errdefer self.global_symbols.shrink(self.allocator, self.global_symbols.items.len - 1);
1146 };
9701147
9711148 exp.link.sym_index = @intCast(u32, i);
9721149 }
9731150 }
9741151 }
9751152
1153 pub fn deleteExport(self: *ElfFile, exp: Export) void {
1154 const sym_index = exp.sym_index orelse return;
1155 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1156 self.global_symbols.items[sym_index].st_info = 0;
1157 }
1158
9761159 fn writeProgHeader(self: *ElfFile, index: usize) !void {
9771160 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
9781161 const offset = self.program_headers.items[index].p_offset;
src-self-hosted/type.zig+27-11
......@@ -92,13 +92,13 @@ pub const Type = extern union {
9292 return @fieldParentPtr(T, "base", self.ptr_otherwise);
9393 }
9494
95 pub fn eql(self: Type, other: Type) bool {
96 //std.debug.warn("test {} == {}\n", .{ self, other });
95 pub fn eql(a: Type, b: Type) bool {
96 //std.debug.warn("test {} == {}\n", .{ a, b });
9797 // As a shortcut, if the small tags / addresses match, we're done.
98 if (self.tag_if_small_enough == other.tag_if_small_enough)
98 if (a.tag_if_small_enough == b.tag_if_small_enough)
9999 return true;
100 const zig_tag_a = self.zigTypeTag();
101 const zig_tag_b = self.zigTypeTag();
100 const zig_tag_a = a.zigTypeTag();
101 const zig_tag_b = b.zigTypeTag();
102102 if (zig_tag_a != zig_tag_b)
103103 return false;
104104 switch (zig_tag_a) {
......@@ -111,24 +111,40 @@ pub const Type = extern union {
111111 .Undefined => return true,
112112 .Null => return true,
113113 .Pointer => {
114 const is_slice_a = isSlice(self);
115 const is_slice_b = isSlice(other);
114 const is_slice_a = isSlice(a);
115 const is_slice_b = isSlice(b);
116116 if (is_slice_a != is_slice_b)
117117 return false;
118118 @panic("TODO implement more pointer Type equality comparison");
119119 },
120120 .Int => {
121 if (self.tag() != other.tag()) {
121 if (a.tag() != b.tag()) {
122122 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
123123 return false;
124124 }
125125 // The target will not be branched upon, because we handled target-dependent cases above.
126 const info_a = self.intInfo(@as(Target, undefined));
127 const info_b = self.intInfo(@as(Target, undefined));
126 const info_a = a.intInfo(@as(Target, undefined));
127 const info_b = b.intInfo(@as(Target, undefined));
128128 return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
129129 },
130 .Array => {
131 if (a.arrayLen() != b.arrayLen())
132 return false;
133 if (a.elemType().eql(b.elemType()))
134 return false;
135 const sentinel_a = a.arraySentinel();
136 const sentinel_b = b.arraySentinel();
137 if (sentinel_a) |sa| {
138 if (sentinel_b) |sb| {
139 return sa.eql(sb);
140 } else {
141 return false;
142 }
143 } else {
144 return sentinel_b == null;
145 }
146 },
130147 .Float,
131 .Array,
132148 .Struct,
133149 .Optional,
134150 .ErrorUnion,
src-self-hosted/value.zig+5
......@@ -666,6 +666,11 @@ pub const Value = extern union {
666666 return orderAgainstZero(lhs).compare(op);
667667 }
668668
669 pub fn eql(a: Value, b: Value) bool {
670 // TODO non numerical comparisons
671 return compare(a, .eq, b);
672 }
673
669674 pub fn toBool(self: Value) bool {
670675 return switch (self.tag()) {
671676 .bool_true => true,
src-self-hosted/zir.zig+10
......@@ -442,6 +442,16 @@ pub const Module = struct {
442442
443443 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
444444
445 /// TODO Look into making a table to speed this up.
446 pub fn findDecl(self: Module, name: []const u8) ?*Inst {
447 for (self.decls) |decl| {
448 if (mem.eql(u8, decl.name, name)) {
449 return decl;
450 }
451 }
452 return null;
453 }
454
445455 /// The allocator is used for temporary storage, but this function always returns
446456 /// with no resources allocated.
447457 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
test/stage2/zir.zig+71
......@@ -200,6 +200,73 @@ pub fn addCases(ctx: *TestContext) void {
200200 \\@9 = str("_start")
201201 \\@10 = ref(@9)
202202 \\@11 = export(@10, @start)
203 ,
204 \\@noreturn = primitive(noreturn)
205 \\@void = primitive(void)
206 \\@usize = primitive(usize)
207 \\@0 = int(0)
208 \\@1 = int(1)
209 \\@2 = int(2)
210 \\@3 = int(3)
211 \\
212 \\@syscall_array = str("syscall")
213 \\@sysoutreg_array = str("={rax}")
214 \\@rax_array = str("{rax}")
215 \\@rdi_array = str("{rdi}")
216 \\@rcx_array = str("rcx")
217 \\@r11_array = str("r11")
218 \\@rdx_array = str("{rdx}")
219 \\@rsi_array = str("{rsi}")
220 \\@memory_array = str("memory")
221 \\@len_array = str("len")
222 \\
223 \\@msg = str("Hello, world!\n")
224 \\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n")
225 \\
226 \\@start_fnty = fntype([], @noreturn, cc=Naked)
227 \\@start = fn(@start_fnty, {
228 \\ %SYS_exit_group = int(231)
229 \\ %exit_code = as(@usize, @0)
230 \\
231 \\ %syscall = ref(@syscall_array)
232 \\ %sysoutreg = ref(@sysoutreg_array)
233 \\ %rax = ref(@rax_array)
234 \\ %rdi = ref(@rdi_array)
235 \\ %rcx = ref(@rcx_array)
236 \\ %rdx = ref(@rdx_array)
237 \\ %rsi = ref(@rsi_array)
238 \\ %r11 = ref(@r11_array)
239 \\ %memory = ref(@memory_array)
240 \\
241 \\ %SYS_write = as(@usize, @1)
242 \\ %STDOUT_FILENO = as(@usize, @1)
243 \\
244 \\ %msg_ptr = ref(@msg2)
245 \\ %msg_addr = ptrtoint(%msg_ptr)
246 \\
247 \\ %len_name = ref(@len_array)
248 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
249 \\ %msg_len = deref(%msg_len_ptr)
250 \\ %rc_write = asm(%syscall, @usize,
251 \\ volatile=1,
252 \\ output=%sysoutreg,
253 \\ inputs=[%rax, %rdi, %rsi, %rdx],
254 \\ clobbers=[%rcx, %r11, %memory],
255 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
256 \\
257 \\ %rc_exit = asm(%syscall, @usize,
258 \\ volatile=1,
259 \\ output=%sysoutreg,
260 \\ inputs=[%rax, %rdi],
261 \\ clobbers=[%rcx, %r11, %memory],
262 \\ args=[%SYS_exit_group, %exit_code])
263 \\
264 \\ %99 = unreachable()
265 \\});
266 \\
267 \\@9 = str("_start")
268 \\@10 = ref(@9)
269 \\@11 = export(@10, @start)
203270 },
204271 &[_][]const u8{
205272 \\Hello, world!
......@@ -207,6 +274,10 @@ pub fn addCases(ctx: *TestContext) void {
207274 ,
208275 \\HELL WORLD
209276 \\
277 ,
278 \\Editing the same msg2 decl but this time with a much longer message which will
279 \\cause the data to need to be relocated in virtual address space.
280 \\
210281 },
211282 );
212283