authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-28 12:19:00-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-28 12:19:00-04:00
log3eed7a4dea3b66bf236278caba7f96228b13214f
tree81ea8de9e0c2f066f78a2b53c0efa89287bfd4b3
parentc7ca1fe6f7b8796a42de908faeaa6ec24e8eb118

stage2: first pass at recursive dependency resolution


4 files changed, 290 insertions(+), 129 deletions(-)

src-self-hosted/Module.zig+169-40
...@@ -128,6 +128,9 @@ pub const Decl = struct {...@@ -128,6 +128,9 @@ pub const Decl = struct {
128 /// Completed successfully before; the `typed_value.most_recent` can be accessed, and128 /// Completed successfully before; the `typed_value.most_recent` can be accessed, and
129 /// new semantic analysis is in progress.129 /// new semantic analysis is in progress.
130 repeat_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,
131 /// Everything is done and updated.134 /// Everything is done and updated.
132 complete,135 complete,
133 },136 },
...@@ -136,18 +139,24 @@ pub const Decl = struct {...@@ -136,18 +139,24 @@ pub const Decl = struct {
136 /// This is populated regardless of semantic analysis and code generation.139 /// This is populated regardless of semantic analysis and code generation.
137 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,140 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
138141
142 contents_hash: Hash,
143
139 /// The shallow set of other decls whose typed_value could possibly change if this Decl's144 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
140 /// typed_value is modified.145 /// typed_value is modified.
141 /// TODO look into using a lightweight map/set data structure rather than a linear array.146 /// TODO look into using a lightweight map/set data structure rather than a linear array.
142 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},147 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
143148 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
144 contents_hash: Hash,149 /// typed_value may need to be regenerated.
150 /// TODO look into using a lightweight map/set data structure rather than a linear array.
151 dependencies: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
145152
146 pub fn destroy(self: *Decl, allocator: *Allocator) void {153 pub fn destroy(self: *Decl, allocator: *Allocator) void {
147 allocator.free(mem.spanZ(self.name));154 allocator.free(mem.spanZ(self.name));
148 if (self.typedValueManaged()) |tvm| {155 if (self.typedValueManaged()) |tvm| {
149 tvm.deinit(allocator);156 tvm.deinit(allocator);
150 }157 }
158 self.dependants.deinit(allocator);
159 self.dependencies.deinit(allocator);
151 allocator.destroy(self);160 allocator.destroy(self);
152 }161 }
153162
...@@ -204,6 +213,7 @@ pub const Decl = struct {...@@ -204,6 +213,7 @@ pub const Decl = struct {
204 .initial_in_progress,213 .initial_in_progress,
205 .initial_dependency_failure,214 .initial_dependency_failure,
206 .initial_sema_failure,215 .initial_sema_failure,
216 .repeat_in_progress_novalue,
207 => return null,217 => return null,
208 .codegen_failure,218 .codegen_failure,
209 .codegen_failure_retryable,219 .codegen_failure_retryable,
...@@ -214,6 +224,31 @@ pub const Decl = struct {...@@ -214,6 +224,31 @@ pub const Decl = struct {
214 => return &self.typed_value.most_recent,224 => return &self.typed_value.most_recent,
215 }225 }
216 }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;
233 }
234 }
235
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 fn removeDependant(self: *Decl, other: *Decl) void {
244 for (self.dependants.items) |item, i| {
245 if (item == other) {
246 _ = self.dependants.swapRemove(i);
247 return;
248 }
249 }
250 unreachable;
251 }
217};252};
218253
219/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.254/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
...@@ -266,12 +301,12 @@ pub const Scope = struct {...@@ -266,12 +301,12 @@ pub const Scope = struct {
266301
267 /// Asserts the scope has a parent which is a DeclAnalysis and302 /// Asserts the scope has a parent which is a DeclAnalysis and
268 /// returns the Decl.303 /// returns the Decl.
269 pub fn decl(self: *Scope) *Decl {304 pub fn decl(self: *Scope) ?*Decl {
270 switch (self.tag) {305 return switch (self.tag) {
271 .block => return self.cast(Block).?.decl,306 .block => self.cast(Block).?.decl,
272 .decl => return self.cast(DeclAnalysis).?.decl,307 .decl => self.cast(DeclAnalysis).?.decl,
273 .zir_module => unreachable,308 .zir_module => null,
274 }309 };
275 }310 }
276311
277 /// Asserts the scope has a parent which is a ZIRModule and312 /// Asserts the scope has a parent which is a ZIRModule and
...@@ -517,11 +552,7 @@ pub fn deinit(self: *Module) void {...@@ -517,11 +552,7 @@ pub fn deinit(self: *Module) void {
517 {552 {
518 var it = self.export_owners.iterator();553 var it = self.export_owners.iterator();
519 while (it.next()) |kv| {554 while (it.next()) |kv| {
520 const export_list = kv.value;555 freeExportList(allocator, kv.value);
521 for (export_list) |exp| {
522 allocator.destroy(exp);
523 }
524 allocator.free(export_list);
525 }556 }
526 self.export_owners.deinit();557 self.export_owners.deinit();
527 }558 }
...@@ -532,6 +563,13 @@ pub fn deinit(self: *Module) void {...@@ -532,6 +563,13 @@ pub fn deinit(self: *Module) void {
532 self.* = undefined;563 self.* = undefined;
533}564}
534565
566fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
567 for (export_list) |exp| {
568 allocator.destroy(exp);
569 }
570 allocator.free(export_list);
571}
572
535pub fn target(self: Module) std.Target {573pub fn target(self: Module) std.Target {
536 return self.bin_file.options.target;574 return self.bin_file.options.target;
537}575}
...@@ -634,9 +672,9 @@ const InnerError = error{ OutOfMemory, AnalysisFail };...@@ -634,9 +672,9 @@ const InnerError = error{ OutOfMemory, AnalysisFail };
634pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {672pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
635 while (self.work_queue.readItem()) |work_item| switch (work_item) {673 while (self.work_queue.readItem()) |work_item| switch (work_item) {
636 .codegen_decl => |decl| switch (decl.analysis) {674 .codegen_decl => |decl| switch (decl.analysis) {
637 .initial_in_progress,675 .initial_in_progress => unreachable,
638 .repeat_in_progress,676 .repeat_in_progress => unreachable,
639 => unreachable,677 .repeat_in_progress_novalue => unreachable,
640678
641 .initial_sema_failure,679 .initial_sema_failure,
642 .repeat_sema_failure,680 .repeat_sema_failure,
...@@ -686,6 +724,23 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -686,6 +724,23 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
686 };724 };
687}725}
688726
727fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
728 try depender.dependencies.ensureCapacity(self.allocator, depender.dependencies.items.len + 1);
729 try dependee.dependants.ensureCapacity(self.allocator, dependee.dependants.items.len + 1);
730
731 for (depender.dependencies.items) |item| {
732 if (item == dependee) break; // Already in the set.
733 } else {
734 depender.dependencies.appendAssumeCapacity(dependee);
735 }
736
737 for (dependee.dependants.items) |item| {
738 if (item == depender) break; // Already in the set.
739 } else {
740 dependee.dependants.appendAssumeCapacity(depender);
741 }
742}
743
689fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {744fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {
690 switch (root_scope.source) {745 switch (root_scope.source) {
691 .unloaded => {746 .unloaded => {
...@@ -772,37 +827,105 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -772,37 +827,105 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
772 => {827 => {
773 const src_module = try self.getSrcModule(root_scope);828 const src_module = try self.getSrcModule(root_scope);
774829
775 // Look for changed decls.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 var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator);
837 defer exports_to_resolve.deinit();
838
776 for (src_module.decls) |src_decl| {839 for (src_module.decls) |src_decl| {
777 const name_hash = Decl.hashSimpleName(src_decl.name);840 const name_hash = Decl.hashSimpleName(src_decl.name);
778 if (self.decl_table.get(name_hash)) |kv| {841 if (self.decl_table.get(name_hash)) |kv| {
779 const decl = kv.value;842 const decl = kv.value;
780 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);843 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
781 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {844 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
782 // TODO recursive dependency management845 std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});
783 //std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});846 regen_decl_set.appendAssumeCapacity(decl);
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 }
801 }847 }
802 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {848 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
803 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.TextBlock.empty);849 try exports_to_resolve.append(&export_inst.base);
804 }850 }
805 }851 }
852
853 // Next, recursively chase the dependency graph, to populate the set.
854 {
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 }
877 }
878 }
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 }
892 }
893
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 }
923 }
924 }
925
926 for (exports_to_resolve.items) |export_inst| {
927 _ = try self.resolveDecl(&root_scope.base, export_inst, link.ElfFile.TextBlock.empty);
928 }
806 },929 },
807 }930 }
808}931}
...@@ -906,11 +1029,13 @@ fn resolveDecl(...@@ -906,11 +1029,13 @@ fn resolveDecl(
906 }1029 }
907}1030}
9081031
1032/// Declares a dependency on the decl.
909fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {1033fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
910 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.TextBlock.empty);1034 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.TextBlock.empty);
911 switch (decl.analysis) {1035 switch (decl.analysis) {
912 .initial_in_progress => unreachable,1036 .initial_in_progress => unreachable,
913 .repeat_in_progress => unreachable,1037 .repeat_in_progress => unreachable,
1038 .repeat_in_progress_novalue => unreachable,
914 .initial_dependency_failure,1039 .initial_dependency_failure,
915 .repeat_dependency_failure,1040 .repeat_dependency_failure,
916 .initial_sema_failure,1041 .initial_sema_failure,
...@@ -919,8 +1044,12 @@ fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerE...@@ -919,8 +1044,12 @@ fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerE
919 .codegen_failure_retryable,1044 .codegen_failure_retryable,
920 => return error.AnalysisFail,1045 => return error.AnalysisFail,
9211046
922 .complete => return decl,1047 .complete => {},
1048 }
1049 if (scope.decl()) |scope_decl| {
1050 try self.declareDeclDependency(scope_decl, decl);
923 }1051 }
1052 return decl;
924}1053}
9251054
926fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {1055fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
...@@ -998,7 +1127,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In...@@ -998,7 +1127,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
998 const new_export = try self.allocator.create(Export);1127 const new_export = try self.allocator.create(Export);
999 errdefer self.allocator.destroy(new_export);1128 errdefer self.allocator.destroy(new_export);
10001129
1001 const owner_decl = scope.decl();1130 const owner_decl = scope.decl().?;
10021131
1003 new_export.* = .{1132 new_export.* = .{
1004 .options = .{ .name = symbol_name },1133 .options = .{ .name = symbol_name },
...@@ -1327,7 +1456,7 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError...@@ -1327,7 +1456,7 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError
1327 new_func.* = .{1456 new_func.* = .{
1328 .fn_type = fn_type,1457 .fn_type = fn_type,
1329 .analysis = .{ .queued = fn_inst },1458 .analysis = .{ .queued = fn_inst },
1330 .owner_decl = scope.decl(),1459 .owner_decl = scope.decl().?,
1331 };1460 };
1332 const fn_payload = try scope.arena().create(Value.Payload.Function);1461 const fn_payload = try scope.arena().create(Value.Payload.Function);
1333 fn_payload.* = .{ .func = new_func };1462 fn_payload.* = .{ .func = new_func };
src-self-hosted/link.zig+40-18
...@@ -126,6 +126,8 @@ pub const ElfFile = struct {...@@ -126,6 +126,8 @@ pub const ElfFile = struct {
126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128128
129 global_symbol_free_list: std.ArrayListUnmanaged(usize) = std.ArrayListUnmanaged(usize){},
130
129 /// Same order as in the file. The value is the absolute vaddr value.131 /// Same order as in the file. The value is the absolute vaddr value.
130 /// If the vaddr of the executable program header changes, the entire132 /// If the vaddr of the executable program header changes, the entire
131 /// offset table needs to be rewritten.133 /// offset table needs to be rewritten.
...@@ -153,7 +155,7 @@ pub const ElfFile = struct {...@@ -153,7 +155,7 @@ pub const ElfFile = struct {
153 /// overcapacity can be negative. A simple way to have negative overcapacity is to155 /// overcapacity can be negative. A simple way to have negative overcapacity is to
154 /// allocate a fresh text block, which will have ideal capacity, and then grow it156 /// allocate a fresh text block, which will have ideal capacity, and then grow it
155 /// by 1 byte. It will then have -1 overcapacity.157 /// by 1 byte. It will then have -1 overcapacity.
156 free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},158 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
157 last_text_block: ?*TextBlock = null,159 last_text_block: ?*TextBlock = null,
158160
159 /// `alloc_num / alloc_den` is the factor of padding when allocating.161 /// `alloc_num / alloc_den` is the factor of padding when allocating.
...@@ -229,6 +231,8 @@ pub const ElfFile = struct {...@@ -229,6 +231,8 @@ pub const ElfFile = struct {
229 self.shstrtab.deinit(self.allocator);231 self.shstrtab.deinit(self.allocator);
230 self.local_symbols.deinit(self.allocator);232 self.local_symbols.deinit(self.allocator);
231 self.global_symbols.deinit(self.allocator);233 self.global_symbols.deinit(self.allocator);
234 self.global_symbol_free_list.deinit(self.allocator);
235 self.text_block_free_list.deinit(self.allocator);
232 self.offset_table.deinit(self.allocator);236 self.offset_table.deinit(self.allocator);
233 if (self.owns_file_handle) {237 if (self.owns_file_handle) {
234 if (self.file) |f| f.close();238 if (self.file) |f| f.close();
...@@ -775,12 +779,12 @@ pub const ElfFile = struct {...@@ -775,12 +779,12 @@ pub const ElfFile = struct {
775 var already_have_free_list_node = false;779 var already_have_free_list_node = false;
776 {780 {
777 var i: usize = 0;781 var i: usize = 0;
778 while (i < self.free_list.items.len) {782 while (i < self.text_block_free_list.items.len) {
779 if (self.free_list.items[i] == text_block) {783 if (self.text_block_free_list.items[i] == text_block) {
780 _ = self.free_list.swapRemove(i);784 _ = self.text_block_free_list.swapRemove(i);
781 continue;785 continue;
782 }786 }
783 if (self.free_list.items[i] == text_block.prev) {787 if (self.text_block_free_list.items[i] == text_block.prev) {
784 already_have_free_list_node = true;788 already_have_free_list_node = true;
785 }789 }
786 i += 1;790 i += 1;
...@@ -797,7 +801,7 @@ pub const ElfFile = struct {...@@ -797,7 +801,7 @@ pub const ElfFile = struct {
797 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {801 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
798 // The free list is heuristics, it doesn't have to be perfect, so we can802 // The free list is heuristics, it doesn't have to be perfect, so we can
799 // ignore the OOM here.803 // ignore the OOM here.
800 self.free_list.append(self.allocator, prev) catch {};804 self.text_block_free_list.append(self.allocator, prev) catch {};
801 }805 }
802 } else {806 } else {
803 text_block.prev = null;807 text_block.prev = null;
...@@ -840,8 +844,8 @@ pub const ElfFile = struct {...@@ -840,8 +844,8 @@ pub const ElfFile = struct {
840 // The list is unordered. We'll just take the first thing that works.844 // The list is unordered. We'll just take the first thing that works.
841 const vaddr = blk: {845 const vaddr = blk: {
842 var i: usize = 0;846 var i: usize = 0;
843 while (i < self.free_list.items.len) {847 while (i < self.text_block_free_list.items.len) {
844 const big_block = self.free_list.items[i];848 const big_block = self.text_block_free_list.items[i];
845 // We now have a pointer to a live text block that has too much capacity.849 // We now have a pointer to a live text block that has too much capacity.
846 // Is it enough that we could fit this new text block?850 // Is it enough that we could fit this new text block?
847 const sym = self.local_symbols.items[big_block.local_sym_index];851 const sym = self.local_symbols.items[big_block.local_sym_index];
...@@ -856,7 +860,7 @@ pub const ElfFile = struct {...@@ -856,7 +860,7 @@ pub const ElfFile = struct {
856 // should be deleted because the block that it points to has grown to take up860 // should be deleted because the block that it points to has grown to take up
857 // more of the extra capacity.861 // more of the extra capacity.
858 if (!big_block.freeListEligible(self.*)) {862 if (!big_block.freeListEligible(self.*)) {
859 _ = self.free_list.swapRemove(i);863 _ = self.text_block_free_list.swapRemove(i);
860 } else {864 } else {
861 i += 1;865 i += 1;
862 }866 }
...@@ -932,7 +936,7 @@ pub const ElfFile = struct {...@@ -932,7 +936,7 @@ pub const ElfFile = struct {
932 text_block.next = null;936 text_block.next = null;
933 }937 }
934 if (free_list_removal) |i| {938 if (free_list_removal) |i| {
935 _ = self.free_list.swapRemove(i);939 _ = self.text_block_free_list.swapRemove(i);
936 }940 }
937 return vaddr;941 return vaddr;
938 }942 }
...@@ -958,11 +962,18 @@ pub const ElfFile = struct {...@@ -958,11 +962,18 @@ pub const ElfFile = struct {
958962
959 self.offset_table_count_dirty = true;963 self.offset_table_count_dirty = true;
960964
961 //std.debug.warn("allocating symbol index {}\n", .{local_sym_index});965 std.debug.warn("allocating symbol index {} for {}\n", .{local_sym_index, decl.name});
962 decl.link.local_sym_index = @intCast(u32, local_sym_index);966 decl.link.local_sym_index = @intCast(u32, local_sym_index);
963 decl.link.offset_table_index = @intCast(u32, offset_table_index);967 decl.link.offset_table_index = @intCast(u32, offset_table_index);
964 }968 }
965969
970 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {
971 self.freeTextBlock(&decl.link);
972 if (decl.link.local_sym_index != 0) {
973 @panic("TODO free the symbol entry and offset table entry");
974 }
975 }
976
966 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {977 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
967 var code_buffer = std.ArrayList(u8).init(self.allocator);978 var code_buffer = std.ArrayList(u8).init(self.allocator);
968 defer code_buffer.deinit();979 defer code_buffer.deinit();
...@@ -993,11 +1004,11 @@ pub const ElfFile = struct {...@@ -993,11 +1004,11 @@ pub const ElfFile = struct {
993 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1004 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
994 if (need_realloc) {1005 if (need_realloc) {
995 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);1006 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
996 //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });1007 std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
997 if (vaddr != local_sym.st_value) {1008 if (vaddr != local_sym.st_value) {
998 local_sym.st_value = vaddr;1009 local_sym.st_value = vaddr;
9991010
1000 //std.debug.warn(" (writing new offset table entry)\n", .{});1011 std.debug.warn(" (writing new offset table entry)\n", .{});
1001 self.offset_table.items[decl.link.offset_table_index] = vaddr;1012 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1002 try self.writeOffsetTableEntry(decl.link.offset_table_index);1013 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1003 }1014 }
...@@ -1015,7 +1026,7 @@ pub const ElfFile = struct {...@@ -1015,7 +1026,7 @@ pub const ElfFile = struct {
1015 const decl_name = mem.spanZ(decl.name);1026 const decl_name = mem.spanZ(decl.name);
1016 const name_str_index = try self.makeString(decl_name);1027 const name_str_index = try self.makeString(decl_name);
1017 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);1028 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1018 //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });1029 std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1019 errdefer self.freeTextBlock(&decl.link);1030 errdefer self.freeTextBlock(&decl.link);
10201031
1021 local_sym.* = .{1032 local_sym.* = .{
...@@ -1048,7 +1059,10 @@ pub const ElfFile = struct {...@@ -1048,7 +1059,10 @@ pub const ElfFile = struct {
1048 decl: *const Module.Decl,1059 decl: *const Module.Decl,
1049 exports: []const *Module.Export,1060 exports: []const *Module.Export,
1050 ) !void {1061 ) !void {
1062 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1063 // them, so that deleting exports is guaranteed to succeed.
1051 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);1064 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1065 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1052 const typed_value = decl.typed_value.most_recent.typed_value;1066 const typed_value = decl.typed_value.most_recent.typed_value;
1053 if (decl.link.local_sym_index == 0) return;1067 if (decl.link.local_sym_index == 0) return;
1054 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];1068 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
...@@ -1095,22 +1109,30 @@ pub const ElfFile = struct {...@@ -1095,22 +1109,30 @@ pub const ElfFile = struct {
1095 };1109 };
1096 } else {1110 } else {
1097 const name = try self.makeString(exp.options.name);1111 const name = try self.makeString(exp.options.name);
1098 const i = self.global_symbols.items.len;1112 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1099 self.global_symbols.appendAssumeCapacity(.{1113 _ = self.global_symbols.addOneAssumeCapacity();
1114 break :blk self.global_symbols.items.len - 1;
1115 };
1116 self.global_symbols.items[i] = .{
1100 .st_name = name,1117 .st_name = name,
1101 .st_info = (stb_bits << 4) | stt_bits,1118 .st_info = (stb_bits << 4) | stt_bits,
1102 .st_other = 0,1119 .st_other = 0,
1103 .st_shndx = self.text_section_index.?,1120 .st_shndx = self.text_section_index.?,
1104 .st_value = decl_sym.st_value,1121 .st_value = decl_sym.st_value,
1105 .st_size = decl_sym.st_size,1122 .st_size = decl_sym.st_size,
1106 });1123 };
1107 errdefer self.global_symbols.shrink(self.allocator, self.global_symbols.items.len - 1);
11081124
1109 exp.link.sym_index = @intCast(u32, i);1125 exp.link.sym_index = @intCast(u32, i);
1110 }1126 }
1111 }1127 }
1112 }1128 }
11131129
1130 pub fn deleteExport(self: *ElfFile, exp: Export) void {
1131 const sym_index = exp.sym_index orelse return;
1132 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1133 self.global_symbols.items[sym_index].st_info = 0;
1134 }
1135
1114 fn writeProgHeader(self: *ElfFile, index: usize) !void {1136 fn writeProgHeader(self: *ElfFile, index: usize) !void {
1115 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1137 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1116 const offset = self.program_headers.items[index].p_offset;1138 const offset = self.program_headers.items[index].p_offset;
src-self-hosted/zir.zig+10
...@@ -442,6 +442,16 @@ pub const Module = struct {...@@ -442,6 +442,16 @@ pub const Module = struct {
442442
443 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });443 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
445 /// The allocator is used for temporary storage, but this function always returns455 /// The allocator is used for temporary storage, but this function always returns
446 /// with no resources allocated.456 /// with no resources allocated.
447 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {457 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
test/stage2/zir.zig+71-71
...@@ -200,73 +200,73 @@ pub fn addCases(ctx: *TestContext) void {...@@ -200,73 +200,73 @@ pub fn addCases(ctx: *TestContext) void {
200 \\@9 = str("_start")200 \\@9 = str("_start")
201 \\@10 = ref(@9)201 \\@10 = ref(@9)
202 \\@11 = export(@10, @start)202 \\@11 = export(@10, @start)
203// ,203 ,
204// \\@noreturn = primitive(noreturn)204 \\@noreturn = primitive(noreturn)
205// \\@void = primitive(void)205 \\@void = primitive(void)
206// \\@usize = primitive(usize)206 \\@usize = primitive(usize)
207// \\@0 = int(0)207 \\@0 = int(0)
208// \\@1 = int(1)208 \\@1 = int(1)
209// \\@2 = int(2)209 \\@2 = int(2)
210// \\@3 = int(3)210 \\@3 = int(3)
211// \\211 \\
212// \\@syscall_array = str("syscall")212 \\@syscall_array = str("syscall")
213// \\@sysoutreg_array = str("={rax}")213 \\@sysoutreg_array = str("={rax}")
214// \\@rax_array = str("{rax}")214 \\@rax_array = str("{rax}")
215// \\@rdi_array = str("{rdi}")215 \\@rdi_array = str("{rdi}")
216// \\@rcx_array = str("rcx")216 \\@rcx_array = str("rcx")
217// \\@r11_array = str("r11")217 \\@r11_array = str("r11")
218// \\@rdx_array = str("{rdx}")218 \\@rdx_array = str("{rdx}")
219// \\@rsi_array = str("{rsi}")219 \\@rsi_array = str("{rsi}")
220// \\@memory_array = str("memory")220 \\@memory_array = str("memory")
221// \\@len_array = str("len")221 \\@len_array = str("len")
222// \\222 \\
223// \\@msg = str("Hello, world!\n")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")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// \\225 \\
226// \\@start_fnty = fntype([], @noreturn, cc=Naked)226 \\@start_fnty = fntype([], @noreturn, cc=Naked)
227// \\@start = fn(@start_fnty, {227 \\@start = fn(@start_fnty, {
228// \\ %SYS_exit_group = int(231)228 \\ %SYS_exit_group = int(231)
229// \\ %exit_code = as(@usize, @0)229 \\ %exit_code = as(@usize, @0)
230// \\230 \\
231// \\ %syscall = ref(@syscall_array)231 \\ %syscall = ref(@syscall_array)
232// \\ %sysoutreg = ref(@sysoutreg_array)232 \\ %sysoutreg = ref(@sysoutreg_array)
233// \\ %rax = ref(@rax_array)233 \\ %rax = ref(@rax_array)
234// \\ %rdi = ref(@rdi_array)234 \\ %rdi = ref(@rdi_array)
235// \\ %rcx = ref(@rcx_array)235 \\ %rcx = ref(@rcx_array)
236// \\ %rdx = ref(@rdx_array)236 \\ %rdx = ref(@rdx_array)
237// \\ %rsi = ref(@rsi_array)237 \\ %rsi = ref(@rsi_array)
238// \\ %r11 = ref(@r11_array)238 \\ %r11 = ref(@r11_array)
239// \\ %memory = ref(@memory_array)239 \\ %memory = ref(@memory_array)
240// \\240 \\
241// \\ %SYS_write = as(@usize, @1)241 \\ %SYS_write = as(@usize, @1)
242// \\ %STDOUT_FILENO = as(@usize, @1)242 \\ %STDOUT_FILENO = as(@usize, @1)
243// \\243 \\
244// \\ %msg_ptr = ref(@msg2)244 \\ %msg_ptr = ref(@msg2)
245// \\ %msg_addr = ptrtoint(%msg_ptr)245 \\ %msg_addr = ptrtoint(%msg_ptr)
246// \\246 \\
247// \\ %len_name = ref(@len_array)247 \\ %len_name = ref(@len_array)
248// \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)248 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
249// \\ %msg_len = deref(%msg_len_ptr)249 \\ %msg_len = deref(%msg_len_ptr)
250// \\ %rc_write = asm(%syscall, @usize,250 \\ %rc_write = asm(%syscall, @usize,
251// \\ volatile=1,251 \\ volatile=1,
252// \\ output=%sysoutreg,252 \\ output=%sysoutreg,
253// \\ inputs=[%rax, %rdi, %rsi, %rdx],253 \\ inputs=[%rax, %rdi, %rsi, %rdx],
254// \\ clobbers=[%rcx, %r11, %memory],254 \\ clobbers=[%rcx, %r11, %memory],
255// \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])255 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
256// \\256 \\
257// \\ %rc_exit = asm(%syscall, @usize,257 \\ %rc_exit = asm(%syscall, @usize,
258// \\ volatile=1,258 \\ volatile=1,
259// \\ output=%sysoutreg,259 \\ output=%sysoutreg,
260// \\ inputs=[%rax, %rdi],260 \\ inputs=[%rax, %rdi],
261// \\ clobbers=[%rcx, %r11, %memory],261 \\ clobbers=[%rcx, %r11, %memory],
262// \\ args=[%SYS_exit_group, %exit_code])262 \\ args=[%SYS_exit_group, %exit_code])
263// \\263 \\
264// \\ %99 = unreachable()264 \\ %99 = unreachable()
265// \\});265 \\});
266// \\266 \\
267// \\@9 = str("_start")267 \\@9 = str("_start")
268// \\@10 = ref(@9)268 \\@10 = ref(@9)
269// \\@11 = export(@10, @start)269 \\@11 = export(@10, @start)
270 },270 },
271 &[_][]const u8{271 &[_][]const u8{
272 \\Hello, world!272 \\Hello, world!
...@@ -274,10 +274,10 @@ pub fn addCases(ctx: *TestContext) void {...@@ -274,10 +274,10 @@ pub fn addCases(ctx: *TestContext) void {
274 ,274 ,
275 \\HELL WORLD275 \\HELL WORLD
276 \\276 \\
277// ,277 ,
278// \\Editing the same msg2 decl but this time with a much longer message which will278 \\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.279 \\cause the data to need to be relocated in virtual address space.
280// \\280 \\
281 },281 },
282 );282 );
283283