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),...@@ -55,9 +55,20 @@ failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
55/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.55/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
56failed_exports: std.AutoHashMap(*Export, *ErrorMsg),56failed_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
58pub const WorkItem = union(enum) {67pub const WorkItem = union(enum) {
59 /// Write the machine code for a Decl to the output file.68 /// Write the machine code for a Decl to the output file.
60 codegen_decl: *Decl,69 codegen_decl: *Decl,
70 /// Decl has been determined to be outdated; perform semantic analysis again.
71 re_analyze_decl: *Decl,
61};72};
6273
63pub const Export = struct {74pub const Export = struct {
...@@ -68,6 +79,8 @@ pub const Export = struct {...@@ -68,6 +79,8 @@ pub const Export = struct {
68 link: link.ElfFile.Export,79 link: link.ElfFile.Export,
69 /// The Decl that performs the export. Note that this is *not* the Decl being exported.80 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
70 owner_decl: *Decl,81 owner_decl: *Decl,
82 /// The Decl being exported. Note this is *not* the Decl performing the export.
83 exported_decl: *Decl,
71 status: enum {84 status: enum {
72 in_progress,85 in_progress,
73 failed,86 failed,
...@@ -94,8 +107,7 @@ pub const Decl = struct {...@@ -94,8 +107,7 @@ pub const Decl = struct {
94 /// This is the base offset that src offsets within this Decl are relative to.107 /// This is the base offset that src offsets within this Decl are relative to.
95 src: usize,108 src: usize,
96 /// The most recent value of the Decl after a successful semantic analysis.109 /// The most recent value of the Decl after a successful semantic analysis.
97 /// The tag for this union is determined by the tag value of the analysis field.110 typed_value: union(enum) {
98 typed_value: union {
99 never_succeeded: void,111 never_succeeded: void,
100 most_recent: TypedValue.Managed,112 most_recent: TypedValue.Managed,
101 },113 },
...@@ -104,50 +116,56 @@ pub const Decl = struct {...@@ -104,50 +116,56 @@ pub const Decl = struct {
104 /// analysis of the function body is performed with this value set to `success`. Functions116 /// analysis of the function body is performed with this value set to `success`. Functions
105 /// have their own analysis status field.117 /// have their own analysis status field.
106 analysis: enum {118 analysis: enum {
107 initial_in_progress,119 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
120 in_progress,
108 /// This Decl might be OK but it depends on another one which did not successfully complete121 /// 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.122 /// semantic analysis.
110 initial_dependency_failure,123 dependency_failure,
111 /// Semantic analysis failure. This Decl never had a value computed.124 /// Semantic analysis failure.
112 /// There will be a corresponding ErrorMsg in Module.failed_decls.125 /// There will be a corresponding ErrorMsg in Module.failed_decls.
113 initial_sema_failure,126 sema_failure,
114 /// In this case the `typed_value.most_recent` can still be accessed.
115 /// There will be a corresponding ErrorMsg in Module.failed_decls.127 /// There will be a corresponding ErrorMsg in Module.failed_decls.
116 codegen_failure,128 codegen_failure,
117 /// In this case the `typed_value.most_recent` can still be accessed.
118 /// There will be a corresponding ErrorMsg in Module.failed_decls.129 /// There will be a corresponding ErrorMsg in Module.failed_decls.
119 /// This indicates the failure was something like running out of disk space,130 /// This indicates the failure was something like running out of disk space,
120 /// and attempting codegen again may succeed.131 /// and attempting codegen again may succeed.
121 codegen_failure_retryable,132 codegen_failure_retryable,
122 /// This Decl might be OK but it depends on another one which did not successfully complete133 /// Everything is done. During an update, this Decl may be out of date, depending
123 /// semantic analysis. There is a most recent value available.134 /// on its dependencies. The `generation` field can be used to determine if this
124 repeat_dependency_failure,135 /// completion status occurred before or after a given update.
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.
132 complete,136 complete,
137 /// A Module update is in progress, and this Decl has been flagged as being known
138 /// to require re-analysis.
139 outdated,
133 },140 },
141 /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
142 /// when removed.
143 deletion_flag: bool,
144 /// An integer that can be checked against the corresponding incrementing
145 /// generation field of Module. This is used to determine whether `complete` status
146 /// represents pre- or post- re-analysis.
147 generation: u32,
134148
135 /// Represents the position of the code in the output file.149 /// Represents the position of the code in the output file.
136 /// This is populated regardless of semantic analysis and code generation.150 /// 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
139 /// The shallow set of other decls whose typed_value could possibly change if this Decl's155 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
140 /// typed_value is modified.156 /// typed_value is modified.
141 /// TODO look into using a lightweight map/set data structure rather than a linear array.
142 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},157 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
143158 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
144 contents_hash: Hash,159 /// typed_value may need to be regenerated.
160 dependencies: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
145161
146 pub fn destroy(self: *Decl, allocator: *Allocator) void {162 pub fn destroy(self: *Decl, allocator: *Allocator) void {
147 allocator.free(mem.spanZ(self.name));163 allocator.free(mem.spanZ(self.name));
148 if (self.typedValueManaged()) |tvm| {164 if (self.typedValueManaged()) |tvm| {
149 tvm.deinit(allocator);165 tvm.deinit(allocator);
150 }166 }
167 self.dependants.deinit(allocator);
168 self.dependencies.deinit(allocator);
151 allocator.destroy(self);169 allocator.destroy(self);
152 }170 }
153171
...@@ -172,7 +190,7 @@ pub const Decl = struct {...@@ -172,7 +190,7 @@ pub const Decl = struct {
172 pub fn fullyQualifiedNameHash(self: Decl) Hash {190 pub fn fullyQualifiedNameHash(self: Decl) Hash {
173 // Right now we only have ZIRModule as the source. So this is simply the191 // Right now we only have ZIRModule as the source. So this is simply the
174 // relative name of the decl.192 // relative name of the decl.
175 return hashSimpleName(mem.spanZ(u8, self.name));193 return hashSimpleName(mem.spanZ(self.name));
176 }194 }
177195
178 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {196 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
...@@ -200,20 +218,31 @@ pub const Decl = struct {...@@ -200,20 +218,31 @@ pub const Decl = struct {
200 }218 }
201219
202 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {220 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
203 switch (self.analysis) {221 switch (self.typed_value) {
204 .initial_in_progress,222 .most_recent => |*x| return x,
205 .initial_dependency_failure,223 .never_succeeded => return null,
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,
215 }224 }
216 }225 }
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 }
217};246};
218247
219/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.248/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
...@@ -266,12 +295,12 @@ pub const Scope = struct {...@@ -266,12 +295,12 @@ pub const Scope = struct {
266295
267 /// Asserts the scope has a parent which is a DeclAnalysis and296 /// Asserts the scope has a parent which is a DeclAnalysis and
268 /// returns the Decl.297 /// returns the Decl.
269 pub fn decl(self: *Scope) *Decl {298 pub fn decl(self: *Scope) ?*Decl {
270 switch (self.tag) {299 return switch (self.tag) {
271 .block => return self.cast(Block).?.decl,300 .block => self.cast(Block).?.decl,
272 .decl => return self.cast(DeclAnalysis).?.decl,301 .decl => self.cast(DeclAnalysis).?.decl,
273 .zir_module => unreachable,302 .zir_module => null,
274 }303 };
275 }304 }
276305
277 /// Asserts the scope has a parent which is a ZIRModule and306 /// Asserts the scope has a parent which is a ZIRModule and
...@@ -477,6 +506,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -477,6 +506,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
477pub fn deinit(self: *Module) void {506pub fn deinit(self: *Module) void {
478 self.bin_file.deinit();507 self.bin_file.deinit();
479 const allocator = self.allocator;508 const allocator = self.allocator;
509 self.deletion_set.deinit(allocator);
480 self.work_queue.deinit();510 self.work_queue.deinit();
481 {511 {
482 var it = self.decl_table.iterator();512 var it = self.decl_table.iterator();
...@@ -517,11 +547,7 @@ pub fn deinit(self: *Module) void {...@@ -517,11 +547,7 @@ pub fn deinit(self: *Module) void {
517 {547 {
518 var it = self.export_owners.iterator();548 var it = self.export_owners.iterator();
519 while (it.next()) |kv| {549 while (it.next()) |kv| {
520 const export_list = kv.value;550 freeExportList(allocator, kv.value);
521 for (export_list) |exp| {
522 allocator.destroy(exp);
523 }
524 allocator.free(export_list);
525 }551 }
526 self.export_owners.deinit();552 self.export_owners.deinit();
527 }553 }
...@@ -532,12 +558,21 @@ pub fn deinit(self: *Module) void {...@@ -532,12 +558,21 @@ pub fn deinit(self: *Module) void {
532 self.* = undefined;558 self.* = undefined;
533}559}
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
535pub fn target(self: Module) std.Target {568pub fn target(self: Module) std.Target {
536 return self.bin_file.options.target;569 return self.bin_file.options.target;
537}570}
538571
539/// Detect changes to source files, perform semantic analysis, and update the output files.572/// Detect changes to source files, perform semantic analysis, and update the output files.
540pub fn update(self: *Module) !void {573pub fn update(self: *Module) !void {
574 self.generation += 1;
575
541 // TODO Use the cache hash file system to detect which source files changed.576 // TODO Use the cache hash file system to detect which source files changed.
542 // Here we simulate a full cache miss.577 // Here we simulate a full cache miss.
543 // Analyze the root source file now.578 // Analyze the root source file now.
...@@ -550,6 +585,15 @@ pub fn update(self: *Module) !void {...@@ -550,6 +585,15 @@ pub fn update(self: *Module) !void {
550585
551 try self.performAllTheWork();586 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
553 // Unload all the source files from memory.597 // Unload all the source files from memory.
554 self.root_scope.unload(self.allocator);598 self.root_scope.unload(self.allocator);
555599
...@@ -634,15 +678,12 @@ const InnerError = error{ OutOfMemory, AnalysisFail };...@@ -634,15 +678,12 @@ const InnerError = error{ OutOfMemory, AnalysisFail };
634pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {678pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
635 while (self.work_queue.readItem()) |work_item| switch (work_item) {679 while (self.work_queue.readItem()) |work_item| switch (work_item) {
636 .codegen_decl => |decl| switch (decl.analysis) {680 .codegen_decl => |decl| switch (decl.analysis) {
637 .initial_in_progress,681 .in_progress => unreachable,
638 .repeat_in_progress,682 .outdated => unreachable,
639 => unreachable,
640683
641 .initial_sema_failure,684 .sema_failure,
642 .repeat_sema_failure,
643 .codegen_failure,685 .codegen_failure,
644 .initial_dependency_failure,686 .dependency_failure,
645 .repeat_dependency_failure,
646 => continue,687 => continue,
647688
648 .complete, .codegen_failure_retryable => {689 .complete, .codegen_failure_retryable => {
...@@ -668,7 +709,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -668,7 +709,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
668 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {709 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
669 error.OutOfMemory => return error.OutOfMemory,710 error.OutOfMemory => return error.OutOfMemory,
670 error.AnalysisFail => {711 error.AnalysisFail => {
671 decl.analysis = .repeat_dependency_failure;712 decl.analysis = .dependency_failure;
672 },713 },
673 else => {714 else => {
674 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);715 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
...@@ -683,9 +724,60 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -683,9 +724,60 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
683 };724 };
684 },725 },
685 },726 },
727 .re_analyze_decl => |decl| switch (decl.analysis) {
728 .in_progress => unreachable,
729
730 .sema_failure,
731 .codegen_failure,
732 .dependency_failure,
733 .complete,
734 .codegen_failure_retryable,
735 => continue,
736
737 .outdated => {
738 const zir_module = self.getSrcModule(decl.scope) catch |err| switch (err) {
739 error.OutOfMemory => return error.OutOfMemory,
740 else => {
741 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
742 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
743 self.allocator,
744 decl.src,
745 "unable to load source file '{}': {}",
746 .{decl.scope.sub_file_path, @errorName(err)},
747 ));
748 decl.analysis = .codegen_failure_retryable;
749 continue;
750 },
751 };
752 const decl_name = mem.spanZ(decl.name);
753 // We already detected deletions, so we know this will be found.
754 const src_decl = zir_module.findDecl(decl_name).?;
755 self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) {
756 error.OutOfMemory => return error.OutOfMemory,
757 error.AnalysisFail => continue,
758 };
759 }
760 },
686 };761 };
687}762}
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
689fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {781fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {
690 switch (root_scope.source) {782 switch (root_scope.source) {
691 .unloaded => {783 .unloaded => {
...@@ -742,13 +834,6 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {...@@ -742,13 +834,6 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
742}834}
743835
744fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {836fn 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.
752 switch (root_scope.status) {837 switch (root_scope.status) {
753 .never_loaded => {838 .never_loaded => {
754 const src_module = try self.getSrcModule(root_scope);839 const src_module = try self.getSrcModule(root_scope);
...@@ -759,7 +844,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -759,7 +844,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
759844
760 for (src_module.decls) |decl| {845 for (src_module.decls) |decl| {
761 if (decl.cast(zir.Inst.Export)) |export_inst| {846 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);
763 }848 }
764 }849 }
765 },850 },
...@@ -772,41 +857,112 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -772,41 +857,112 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
772 => {857 => {
773 const src_module = try self.getSrcModule(root_scope);858 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
776 for (src_module.decls) |src_decl| {875 for (src_module.decls) |src_decl| {
777 const name_hash = Decl.hashSimpleName(src_decl.name);876 const name_hash = Decl.hashSimpleName(src_decl.name);
778 if (self.decl_table.get(name_hash)) |kv| {877 if (self.decl_table.get(name_hash)) |kv| {
779 const decl = kv.value;878 const decl = kv.value;
879 deleted_decls.removeAssertDiscard(decl);
780 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);880 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
781 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {881 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
782 // TODO recursive dependency management882 //std.debug.warn("noticed '{}' source changed\n", .{src_decl.name});
783 //std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});883 decl.analysis = .outdated;
784 self.decl_table.removeAssertDiscard(name_hash);884 decl.contents_hash = new_contents_hash;
785 const saved_link = decl.link;885 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
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 }886 }
802 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {887 } 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);
804 }898 }
805 }899 }
900 for (exports_to_resolve.items) |export_inst| {
901 _ = try self.resolveDecl(&root_scope.base, export_inst);
902 }
806 },903 },
807 }904 }
808}905}
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
810fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {966fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
811 // Use the Decl's arena for function memory.967 // Use the Decl's arena for function memory.
812 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);968 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
...@@ -836,15 +992,111 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -836,15 +992,111 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
836 };992 };
837}993}
838994
839fn resolveDecl(995fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void {
840 self: *Module,996 switch (decl.analysis) {
841 scope: *Scope,997 .in_progress => unreachable,
842 old_inst: *zir.Inst,998 .dependency_failure,
843 bin_file_link: link.ElfFile.Decl,999 .sema_failure,
844) InnerError!*Decl {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 {
845 const hash = Decl.hashSimpleName(old_inst.name);1095 const hash = Decl.hashSimpleName(old_inst.name);
846 if (self.decl_table.get(hash)) |kv| {1096 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;
848 } else {1100 } else {
849 const new_decl = blk: {1101 const new_decl = blk: {
850 try self.decl_table.ensureCapacity(self.decl_table.size + 1);1102 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
...@@ -857,9 +1109,11 @@ fn resolveDecl(...@@ -857,9 +1109,11 @@ fn resolveDecl(
857 .scope = scope.namespace(),1109 .scope = scope.namespace(),
858 .src = old_inst.src,1110 .src = old_inst.src,
859 .typed_value = .{ .never_succeeded = {} },1111 .typed_value = .{ .never_succeeded = {} },
860 .analysis = .initial_in_progress,1112 .analysis = .in_progress,
1113 .deletion_flag = false,
861 .contents_hash = Decl.hashSimpleName(old_inst.contents),1114 .contents_hash = Decl.hashSimpleName(old_inst.contents),
862 .link = bin_file_link,1115 .link = link.ElfFile.TextBlock.empty,
1116 .generation = 0,
863 };1117 };
864 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);1118 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
865 break :blk new_decl;1119 break :blk new_decl;
...@@ -875,10 +1129,10 @@ fn resolveDecl(...@@ -875,10 +1129,10 @@ fn resolveDecl(
875 error.OutOfMemory => return error.OutOfMemory,1129 error.OutOfMemory => return error.OutOfMemory,
876 error.AnalysisFail => {1130 error.AnalysisFail => {
877 switch (new_decl.analysis) {1131 switch (new_decl.analysis) {
878 .initial_in_progress => new_decl.analysis = .initial_dependency_failure,1132 .in_progress => new_decl.analysis = .dependency_failure,
879 .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure,
880 else => {},1133 else => {},
881 }1134 }
1135 new_decl.generation = self.generation;
882 return error.AnalysisFail;1136 return error.AnalysisFail;
883 },1137 },
884 };1138 };
...@@ -893,34 +1147,37 @@ fn resolveDecl(...@@ -893,34 +1147,37 @@ fn resolveDecl(
893 },1147 },
894 };1148 };
895 new_decl.analysis = .complete;1149 new_decl.analysis = .complete;
1150 new_decl.generation = self.generation;
896 if (typed_value.ty.hasCodeGenBits()) {1151 if (typed_value.ty.hasCodeGenBits()) {
897 // We don't fully codegen the decl until later, but we do need to reserve a global1152 // We don't fully codegen the decl until later, but we do need to reserve a global
898 // offset table index for it. This allows us to codegen decls out of dependency order,1153 // offset table index for it. This allows us to codegen decls out of dependency order,
899 // increasing how many computations can be done in parallel.1154 // increasing how many computations can be done in parallel.
900 try self.bin_file.allocateDeclIndexes(new_decl);1155 try self.bin_file.allocateDeclIndexes(new_decl);
9011156 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
902 // We ensureCapacity when scanning for decls.
903 self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl });
904 }1157 }
905 return new_decl;1158 return new_decl;
906 }1159 }
907}1160}
9081161
1162/// Declares a dependency on the decl.
909fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {1163fn 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);
911 switch (decl.analysis) {1165 switch (decl.analysis) {
912 .initial_in_progress => unreachable,1166 .in_progress => unreachable,
913 .repeat_in_progress => unreachable,1167 .outdated => unreachable,
914 .initial_dependency_failure,1168
915 .repeat_dependency_failure,1169 .dependency_failure,
916 .initial_sema_failure,1170 .sema_failure,
917 .repeat_sema_failure,
918 .codegen_failure,1171 .codegen_failure,
919 .codegen_failure_retryable,1172 .codegen_failure_retryable,
920 => return error.AnalysisFail,1173 => return error.AnalysisFail,
9211174
922 .complete => return decl,1175 .complete => {},
923 }1176 }
1177 if (scope.decl()) |scope_decl| {
1178 try self.declareDeclDependency(scope_decl, decl);
1179 }
1180 return decl;
924}1181}
9251182
926fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {1183fn 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...@@ -998,13 +1255,14 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
998 const new_export = try self.allocator.create(Export);1255 const new_export = try self.allocator.create(Export);
999 errdefer self.allocator.destroy(new_export);1256 errdefer self.allocator.destroy(new_export);
10001257
1001 const owner_decl = scope.decl();1258 const owner_decl = scope.decl().?;
10021259
1003 new_export.* = .{1260 new_export.* = .{
1004 .options = .{ .name = symbol_name },1261 .options = .{ .name = symbol_name },
1005 .src = export_inst.base.src,1262 .src = export_inst.base.src,
1006 .link = .{},1263 .link = .{},
1007 .owner_decl = owner_decl,1264 .owner_decl = owner_decl,
1265 .exported_decl = exported_decl,
1008 .status = .in_progress,1266 .status = .in_progress,
1009 };1267 };
10101268
...@@ -1327,7 +1585,7 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError...@@ -1327,7 +1585,7 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError
1327 new_func.* = .{1585 new_func.* = .{
1328 .fn_type = fn_type,1586 .fn_type = fn_type,
1329 .analysis = .{ .queued = fn_inst },1587 .analysis = .{ .queued = fn_inst },
1330 .owner_decl = scope.decl(),1588 .owner_decl = scope.decl().?,
1331 };1589 };
1332 const fn_payload = try scope.arena().create(Value.Payload.Function);1590 const fn_payload = try scope.arena().create(Value.Payload.Function);
1333 fn_payload.* = .{ .func = new_func };1591 fn_payload.* = .{ .func = new_func };
...@@ -2024,11 +2282,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err...@@ -2024,11 +2282,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
2024 switch (scope.tag) {2282 switch (scope.tag) {
2025 .decl => {2283 .decl => {
2026 const decl = scope.cast(Scope.DeclAnalysis).?.decl;2284 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
2027 switch (decl.analysis) {2285 decl.analysis = .sema_failure;
2028 .initial_in_progress => decl.analysis = .initial_sema_failure,
2029 .repeat_in_progress => decl.analysis = .repeat_sema_failure,
2030 else => unreachable,
2031 }
2032 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);2286 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
2033 },2287 },
2034 .block => {2288 .block => {
src-self-hosted/link.zig+330-147
...@@ -126,6 +126,10 @@ pub const ElfFile = struct {...@@ -126,6 +126,10 @@ 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 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
129 /// Same order as in the file. The value is the absolute vaddr value.133 /// Same order as in the file. The value is the absolute vaddr value.
130 /// If the vaddr of the executable program header changes, the entire134 /// If the vaddr of the executable program header changes, the entire
131 /// offset table needs to be rewritten.135 /// offset table needs to be rewritten.
...@@ -138,11 +142,39 @@ pub const ElfFile = struct {...@@ -138,11 +142,39 @@ pub const ElfFile = struct {
138142
139 error_flags: ErrorFlags = ErrorFlags{},143 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
141 pub const ErrorFlags = struct {173 pub const ErrorFlags = struct {
142 no_entry_point_found: bool = false,174 no_entry_point_found: bool = false,
143 };175 };
144176
145 pub const Decl = struct {177 pub const TextBlock = struct {
146 /// Each decl always gets a local symbol with the fully qualified name.178 /// Each decl always gets a local symbol with the fully qualified name.
147 /// The vaddr and size are found here directly.179 /// The vaddr and size are found here directly.
148 /// The file offset is found by computing the vaddr offset from the section vaddr180 /// The file offset is found by computing the vaddr offset from the section vaddr
...@@ -152,11 +184,43 @@ pub const ElfFile = struct {...@@ -152,11 +184,43 @@ pub const ElfFile = struct {
152 local_sym_index: u32,184 local_sym_index: u32,
153 /// This field is undefined for symbols with size = 0.185 /// This field is undefined for symbols with size = 0.
154 offset_table_index: u32,186 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{
157 .local_sym_index = 0,193 .local_sym_index = 0,
158 .offset_table_index = undefined,194 .offset_table_index = undefined,
195 .prev = null,
196 .next = null,
159 };197 };
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 }
160 };224 };
161225
162 pub const Export = struct {226 pub const Export = struct {
...@@ -169,6 +233,10 @@ pub const ElfFile = struct {...@@ -169,6 +233,10 @@ pub const ElfFile = struct {
169 self.shstrtab.deinit(self.allocator);233 self.shstrtab.deinit(self.allocator);
170 self.local_symbols.deinit(self.allocator);234 self.local_symbols.deinit(self.allocator);
171 self.global_symbols.deinit(self.allocator);235 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);
172 self.offset_table.deinit(self.allocator);240 self.offset_table.deinit(self.allocator);
173 if (self.owns_file_handle) {241 if (self.owns_file_handle) {
174 if (self.file) |f| f.close();242 if (self.file) |f| f.close();
...@@ -193,10 +261,6 @@ pub const ElfFile = struct {...@@ -193,10 +261,6 @@ pub const ElfFile = struct {
193 });261 });
194 }262 }
195263
196 // `alloc_num / alloc_den` is the factor of padding when allocation
197 const alloc_num = 4;
198 const alloc_den = 3;
199
200 /// Returns end pos of collision, if any.264 /// Returns end pos of collision, if any.
201 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
202 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
...@@ -448,6 +512,13 @@ pub const ElfFile = struct {...@@ -448,6 +512,13 @@ pub const ElfFile = struct {
448 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);512 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
449 self.phdr_table_dirty = true;513 self.phdr_table_dirty = true;
450 }514 }
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 }
451 }522 }
452523
453 /// Commit pending changes and write headers.524 /// Commit pending changes and write headers.
...@@ -577,7 +648,6 @@ pub const ElfFile = struct {...@@ -577,7 +648,6 @@ pub const ElfFile = struct {
577 self.error_flags.no_entry_point_found = false;648 self.error_flags.no_entry_point_found = false;
578 try self.writeElfHeader();649 try self.writeElfHeader();
579 }650 }
580 // TODO find end pos and truncate
581651
582 // The point of flush() is to commit changes, so nothing should be dirty after this.652 // The point of flush() is to commit changes, so nothing should be dirty after this.
583 assert(!self.phdr_table_dirty);653 assert(!self.phdr_table_dirty);
...@@ -709,100 +779,222 @@ pub const ElfFile = struct {...@@ -709,100 +779,222 @@ pub const ElfFile = struct {
709 try self.file.?.pwriteAll(hdr_buf[0..index], 0);779 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
710 }780 }
711781
712 const AllocatedBlock = struct {782 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {
713 vaddr: u64,783 var already_have_free_list_node = false;
714 file_offset: u64,784 {
715 size_capacity: u64,785 var i: usize = 0;
716 };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 {798 if (self.last_text_block == text_block) {
719 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];799 // TODO shrink the .text section size here
720 const shdr = &self.sections.items[self.text_section_index.?];800 self.last_text_block = text_block.prev;
801 }
721802
722 // TODO Also detect virtual address collisions.803 if (text_block.prev) |prev| {
723 const text_capacity = self.allocatedSize(shdr.sh_offset);804 prev.next = text_block.next;
724 // TODO instead of looping here, maintain a free list and a pointer to the end.805
725 var last_start: u64 = phdr.p_vaddr;806 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
726 var last_size: u64 = 0;807 // The free list is heuristics, it doesn't have to be perfect, so we can
727 for (self.local_symbols.items) |sym| {808 // ignore the OOM here.
728 if (sym.st_value + sym.st_size > last_start + last_size) {809 self.text_block_free_list.append(self.allocator, prev) catch {};
729 last_start = sym.st_value;
730 last_size = sym.st_size;
731 }810 }
811 } else {
812 text_block.prev = null;
732 }813 }
733 const end_vaddr = last_start + (last_size * alloc_num / alloc_den);814
734 const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment);815 if (text_block.next) |next| {
735 const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr;816 next.prev = text_block.prev;
736 if (needed_size > text_capacity) {817 } else {
737 // Must move the entire text section.818 text_block.next = null;
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;
744 }819 }
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 };
758 }820 }
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 {
761 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];836 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
762 const shdr = &self.sections.items[self.text_section_index.?];837 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.917 shdr.sh_size = needed_size;
765 // TODO look into using a hash map to speed up perf.918 phdr.p_memsz = needed_size;
766 const text_capacity = self.allocatedSize(shdr.sh_offset);919 phdr.p_filesz = needed_size;
767 var next_vaddr_start = phdr.p_vaddr + text_capacity;920
768 for (self.local_symbols.items) |elem| {921 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
769 if (elem.st_value < sym.st_value) continue;922 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
770 if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value;
771 }923 }
772 return .{924
773 .vaddr = sym.st_value,925 // This function can also reallocate a text block.
774 .file_offset = shdr.sh_offset + (sym.st_value - phdr.p_vaddr),926 // In this case we need to "unplug" it from its previous location before
775 .size_capacity = next_vaddr_start - sym.st_value,927 // plugging it in to its new location.
776 };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;
777 }947 }
778948
779 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {949 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
780 if (decl.link.local_sym_index != 0) return;950 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.
782 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);953 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);
783 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);955 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
784 const local_sym_index = self.local_symbols.items.len;956 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
785 const offset_table_index = self.offset_table.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
786 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];975 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] = .{
789 .st_name = 0,978 .st_name = 0,
790 .st_info = 0,979 .st_info = 0,
791 .st_other = 0,980 .st_other = 0,
792 .st_shndx = 0,981 .st_shndx = 0,
793 .st_value = phdr.p_vaddr,982 .st_value = phdr.p_vaddr,
794 .st_size = 0,983 .st_size = 0,
795 });984 };
796 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);985 self.offset_table.items[decl.link.offset_table_index] = 0;
797 self.offset_table.appendAssumeCapacity(0);986 }
798 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
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 = .{994 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
803 .local_sym_index = @intCast(u32, local_sym_index),995
804 .offset_table_index = @intCast(u32, offset_table_index),996 decl.link.local_sym_index = 0;
805 };997 }
806 }998 }
807999
808 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {1000 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
...@@ -822,80 +1014,60 @@ pub const ElfFile = struct {...@@ -822,80 +1014,60 @@ pub const ElfFile = struct {
8221014
823 const required_alignment = typed_value.ty.abiAlignment(self.options.target);1015 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
8241016
825 const file_offset = blk: {1017 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
826 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {1018 .Fn => elf.STT_FUNC,
827 .Fn => elf.STT_FUNC,1019 else => elf.STT_OBJECT,
828 else => elf.STT_OBJECT,1020 };
829 };
8301021
831 if (decl.link.local_sym_index != 0) {1022 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
832 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];1023 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
833 const existing_block = self.findAllocatedTextBlock(local_sym.*);1024 if (local_sym.st_size != 0) {
834 const need_realloc = local_sym.st_size == 0 or1025 const capacity = decl.link.capacity(self.*);
835 code.len > existing_block.size_capacity or1026 const need_realloc = code.len > capacity or
836 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1027 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
837 // TODO check for collision with another symbol1028 if (need_realloc) {
838 const file_offset = if (need_realloc) fo: {1029 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
839 const new_block = try self.allocateTextBlock(code.len, required_alignment);1030 //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
840 local_sym.st_value = new_block.vaddr;1031 if (vaddr != local_sym.st_value) {
841 self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;1032 local_sym.st_value = vaddr;
8421033
843 //std.debug.warn("{}: writing got index {}=0x{x}\n", .{1034 //std.debug.warn(" (writing new offset table entry)\n", .{});
844 // decl.name,1035 self.offset_table.items[decl.link.offset_table_index] = vaddr;
845 // decl.link.offset_table_index,
846 // self.offset_table.items[decl.link.offset_table_index],
847 //});
848 try self.writeOffsetTableEntry(decl.link.offset_table_index);1036 try self.writeOffsetTableEntry(decl.link.offset_table_index);
8491037 }
850 break :fo new_block.file_offset;1038 } else if (code.len < local_sym.st_size) {
851 } else existing_block.file_offset;1039 self.shrinkTextBlock(&decl.link, code.len);
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;
896 }1040 }
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;
899 try self.file.?.pwriteAll(code, file_offset);1071 try self.file.?.pwriteAll(code, file_offset);
9001072
901 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.1073 // 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 {...@@ -910,7 +1082,10 @@ pub const ElfFile = struct {
910 decl: *const Module.Decl,1082 decl: *const Module.Decl,
911 exports: []const *Module.Export,1083 exports: []const *Module.Export,
912 ) !void {1084 ) !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.
913 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);1087 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);
914 const typed_value = decl.typed_value.most_recent.typed_value;1089 const typed_value = decl.typed_value.most_recent.typed_value;
915 if (decl.link.local_sym_index == 0) return;1090 if (decl.link.local_sym_index == 0) return;
916 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];1091 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
...@@ -957,22 +1132,30 @@ pub const ElfFile = struct {...@@ -957,22 +1132,30 @@ pub const ElfFile = struct {
957 };1132 };
958 } else {1133 } else {
959 const name = try self.makeString(exp.options.name);1134 const name = try self.makeString(exp.options.name);
960 const i = self.global_symbols.items.len;1135 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
961 self.global_symbols.appendAssumeCapacity(.{1136 _ = self.global_symbols.addOneAssumeCapacity();
1137 break :blk self.global_symbols.items.len - 1;
1138 };
1139 self.global_symbols.items[i] = .{
962 .st_name = name,1140 .st_name = name,
963 .st_info = (stb_bits << 4) | stt_bits,1141 .st_info = (stb_bits << 4) | stt_bits,
964 .st_other = 0,1142 .st_other = 0,
965 .st_shndx = self.text_section_index.?,1143 .st_shndx = self.text_section_index.?,
966 .st_value = decl_sym.st_value,1144 .st_value = decl_sym.st_value,
967 .st_size = decl_sym.st_size,1145 .st_size = decl_sym.st_size,
968 });1146 };
969 errdefer self.global_symbols.shrink(self.allocator, self.global_symbols.items.len - 1);
9701147
971 exp.link.sym_index = @intCast(u32, i);1148 exp.link.sym_index = @intCast(u32, i);
972 }1149 }
973 }1150 }
974 }1151 }
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
976 fn writeProgHeader(self: *ElfFile, index: usize) !void {1159 fn writeProgHeader(self: *ElfFile, index: usize) !void {
977 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1160 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
978 const offset = self.program_headers.items[index].p_offset;1161 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 {...@@ -92,13 +92,13 @@ pub const Type = extern union {
92 return @fieldParentPtr(T, "base", self.ptr_otherwise);92 return @fieldParentPtr(T, "base", self.ptr_otherwise);
93 }93 }
9494
95 pub fn eql(self: Type, other: Type) bool {95 pub fn eql(a: Type, b: Type) bool {
96 //std.debug.warn("test {} == {}\n", .{ self, other });96 //std.debug.warn("test {} == {}\n", .{ a, b });
97 // As a shortcut, if the small tags / addresses match, we're done.97 // 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)
99 return true;99 return true;
100 const zig_tag_a = self.zigTypeTag();100 const zig_tag_a = a.zigTypeTag();
101 const zig_tag_b = self.zigTypeTag();101 const zig_tag_b = b.zigTypeTag();
102 if (zig_tag_a != zig_tag_b)102 if (zig_tag_a != zig_tag_b)
103 return false;103 return false;
104 switch (zig_tag_a) {104 switch (zig_tag_a) {
...@@ -111,24 +111,40 @@ pub const Type = extern union {...@@ -111,24 +111,40 @@ pub const Type = extern union {
111 .Undefined => return true,111 .Undefined => return true,
112 .Null => return true,112 .Null => return true,
113 .Pointer => {113 .Pointer => {
114 const is_slice_a = isSlice(self);114 const is_slice_a = isSlice(a);
115 const is_slice_b = isSlice(other);115 const is_slice_b = isSlice(b);
116 if (is_slice_a != is_slice_b)116 if (is_slice_a != is_slice_b)
117 return false;117 return false;
118 @panic("TODO implement more pointer Type equality comparison");118 @panic("TODO implement more pointer Type equality comparison");
119 },119 },
120 .Int => {120 .Int => {
121 if (self.tag() != other.tag()) {121 if (a.tag() != b.tag()) {
122 // Detect that e.g. u64 != usize, even if the bits match on a particular target.122 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
123 return false;123 return false;
124 }124 }
125 // The target will not be branched upon, because we handled target-dependent cases above.125 // The target will not be branched upon, because we handled target-dependent cases above.
126 const info_a = self.intInfo(@as(Target, undefined));126 const info_a = a.intInfo(@as(Target, undefined));
127 const info_b = self.intInfo(@as(Target, undefined));127 const info_b = b.intInfo(@as(Target, undefined));
128 return info_a.signed == info_b.signed and info_a.bits == info_b.bits;128 return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
129 },129 },
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 },
130 .Float,147 .Float,
131 .Array,
132 .Struct,148 .Struct,
133 .Optional,149 .Optional,
134 .ErrorUnion,150 .ErrorUnion,
src-self-hosted/value.zig+5
...@@ -666,6 +666,11 @@ pub const Value = extern union {...@@ -666,6 +666,11 @@ pub const Value = extern union {
666 return orderAgainstZero(lhs).compare(op);666 return orderAgainstZero(lhs).compare(op);
667 }667 }
668668
669 pub fn eql(a: Value, b: Value) bool {
670 // TODO non numerical comparisons
671 return compare(a, .eq, b);
672 }
673
669 pub fn toBool(self: Value) bool {674 pub fn toBool(self: Value) bool {
670 return switch (self.tag()) {675 return switch (self.tag()) {
671 .bool_true => true,676 .bool_true => true,
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
...@@ -200,6 +200,73 @@ pub fn addCases(ctx: *TestContext) void {...@@ -200,6 +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 ,
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)
203 },270 },
204 &[_][]const u8{271 &[_][]const u8{
205 \\Hello, world!272 \\Hello, world!
...@@ -207,6 +274,10 @@ pub fn addCases(ctx: *TestContext) void {...@@ -207,6 +274,10 @@ pub fn addCases(ctx: *TestContext) void {
207 ,274 ,
208 \\HELL WORLD275 \\HELL WORLD
209 \\276 \\
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 \\
210 },281 },
211 );282 );
212283