Commit 2fc18b5278

Isaac Freund <ifreund@ifreund.xyz>
2020-08-08 01:18:22
stage2: make link data in Decl into unions
This will allow for implementation of non-Elf backends without wasting memory.
1 parent a2bb246
Changed files (3)
src-self-hosted/codegen.zig
@@ -145,10 +145,10 @@ pub fn generateSymbol(
             if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
                 const decl = payload.decl;
                 if (decl.analysis != .complete) return error.AnalysisFail;
-                assert(decl.link.local_sym_index != 0);
+                assert(decl.link.elf.local_sym_index != 0);
                 // TODO handle the dependency of this symbol on the decl's vaddr.
                 // If the decl changes vaddr, then this symbol needs to get regenerated.
-                const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
+                const vaddr = bin_file.local_symbols.items[decl.link.elf.local_sym_index].st_value;
                 const endian = bin_file.base.options.target.cpu.arch.endian();
                 switch (bin_file.base.options.target.cpu.arch.ptrBitWidth()) {
                     16 => {
@@ -1085,7 +1085,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
                             const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                             const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-                            const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
+                            const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
                             // ff 14 25 xx xx xx xx    call [addr]
                             try self.code.ensureCapacity(self.code.items.len + 7);
                             self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
@@ -1106,7 +1106,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
                             const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                             const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-                            const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
+                            const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
 
                             try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
                             const jalr = instructions.Jalr{
@@ -1934,7 +1934,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                     if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
                         const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
                         const decl = payload.decl;
-                        const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
+                        const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
                         return MCValue{ .memory = got_addr };
                     }
                     return self.fail(src, "TODO codegen more kinds of const pointers", .{});
src-self-hosted/link.zig
@@ -38,6 +38,16 @@ pub const Options = struct {
 
 
 pub const File = struct {
+    pub const LinkBlock = union {
+        elf: Elf.TextBlock,
+        c: void,
+    };
+
+    pub const LinkFn = union {
+        elf: Elf.SrcFn,
+        c: void,
+    };
+
     tag: Tag,
     options: Options,
     file: ?fs.File,
@@ -1720,31 +1730,31 @@ pub const File = struct {
         }
 
         pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
-            if (decl.link.local_sym_index != 0) return;
+            if (decl.link.elf.local_sym_index != 0) return;
 
             try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
             try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
 
             if (self.local_symbol_free_list.popOrNull()) |i| {
                 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
-                decl.link.local_sym_index = i;
+                decl.link.elf.local_sym_index = i;
             } else {
                 log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
-                decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
+                decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
                 _ = self.local_symbols.addOneAssumeCapacity();
             }
 
             if (self.offset_table_free_list.popOrNull()) |i| {
-                decl.link.offset_table_index = i;
+                decl.link.elf.offset_table_index = i;
             } else {
-                decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
+                decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
                 _ = self.offset_table.addOneAssumeCapacity();
                 self.offset_table_count_dirty = true;
             }
 
             const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
 
-            self.local_symbols.items[decl.link.local_sym_index] = .{
+            self.local_symbols.items[decl.link.elf.local_sym_index] = .{
                 .st_name = 0,
                 .st_info = 0,
                 .st_other = 0,
@@ -1752,39 +1762,39 @@ pub const File = struct {
                 .st_value = phdr.p_vaddr,
                 .st_size = 0,
             };
-            self.offset_table.items[decl.link.offset_table_index] = 0;
+            self.offset_table.items[decl.link.elf.offset_table_index] = 0;
         }
 
         pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
             // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
-            self.freeTextBlock(&decl.link);
-            if (decl.link.local_sym_index != 0) {
-                self.local_symbol_free_list.append(self.base.allocator, decl.link.local_sym_index) catch {};
-                self.offset_table_free_list.append(self.base.allocator, decl.link.offset_table_index) catch {};
+            self.freeTextBlock(&decl.link.elf);
+            if (decl.link.elf.local_sym_index != 0) {
+                self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
+                self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
 
-                self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
+                self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
 
-                decl.link.local_sym_index = 0;
+                decl.link.elf.local_sym_index = 0;
             }
             // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
             // is desired for both.
-            _ = self.dbg_line_fn_free_list.remove(&decl.fn_link);
-            if (decl.fn_link.prev) |prev| {
+            _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
+            if (decl.fn_link.elf.prev) |prev| {
                 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
-                prev.next = decl.fn_link.next;
-                if (decl.fn_link.next) |next| {
+                prev.next = decl.fn_link.elf.next;
+                if (decl.fn_link.elf.next) |next| {
                     next.prev = prev;
                 } else {
                     self.dbg_line_fn_last = prev;
                 }
-            } else if (decl.fn_link.next) |next| {
+            } else if (decl.fn_link.elf.next) |next| {
                 self.dbg_line_fn_first = next;
                 next.prev = null;
             }
-            if (self.dbg_line_fn_first == &decl.fn_link) {
+            if (self.dbg_line_fn_first == &decl.fn_link.elf) {
                 self.dbg_line_fn_first = null;
             }
-            if (self.dbg_line_fn_last == &decl.fn_link) {
+            if (self.dbg_line_fn_last == &decl.fn_link.elf) {
                 self.dbg_line_fn_last = null;
             }
         }
@@ -1870,24 +1880,24 @@ pub const File = struct {
 
             const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
 
-            assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
-            const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
+            assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
+            const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
             if (local_sym.st_size != 0) {
-                const capacity = decl.link.capacity(self.*);
+                const capacity = decl.link.elf.capacity(self.*);
                 const need_realloc = code.len > capacity or
                     !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
                 if (need_realloc) {
-                    const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
+                    const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
                     log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
                     if (vaddr != local_sym.st_value) {
                         local_sym.st_value = vaddr;
 
                         log.debug(.link, "  (writing new offset table entry)\n", .{});
-                        self.offset_table.items[decl.link.offset_table_index] = vaddr;
-                        try self.writeOffsetTableEntry(decl.link.offset_table_index);
+                        self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
+                        try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
                     }
                 } else if (code.len < local_sym.st_size) {
-                    self.shrinkTextBlock(&decl.link, code.len);
+                    self.shrinkTextBlock(&decl.link.elf, code.len);
                 }
                 local_sym.st_size = code.len;
                 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
@@ -1895,13 +1905,13 @@ pub const File = struct {
                 local_sym.st_other = 0;
                 local_sym.st_shndx = self.text_section_index.?;
                 // TODO this write could be avoided if no fields of the symbol were changed.
-                try self.writeSymbol(decl.link.local_sym_index);
+                try self.writeSymbol(decl.link.elf.local_sym_index);
             } else {
                 const decl_name = mem.spanZ(decl.name);
                 const name_str_index = try self.makeString(decl_name);
-                const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
+                const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
                 log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
-                errdefer self.freeTextBlock(&decl.link);
+                errdefer self.freeTextBlock(&decl.link.elf);
 
                 local_sym.* = .{
                     .st_name = name_str_index,
@@ -1911,10 +1921,10 @@ pub const File = struct {
                     .st_value = vaddr,
                     .st_size = code.len,
                 };
-                self.offset_table.items[decl.link.offset_table_index] = vaddr;
+                self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
 
-                try self.writeSymbol(decl.link.local_sym_index);
-                try self.writeOffsetTableEntry(decl.link.offset_table_index);
+                try self.writeSymbol(decl.link.elf.local_sym_index);
+                try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
             }
 
             const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
@@ -1941,7 +1951,7 @@ pub const File = struct {
                 // Now we have the full contents and may allocate a region to store it.
 
                 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
-                const src_fn = &decl.fn_link;
+                const src_fn = &decl.fn_link.elf;
                 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
                 if (self.dbg_line_fn_last) |last| {
                     if (src_fn.next) |next| {
@@ -2026,8 +2036,8 @@ pub const File = struct {
 
             try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
             const typed_value = decl.typed_value.most_recent.typed_value;
-            if (decl.link.local_sym_index == 0) return;
-            const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
+            if (decl.link.elf.local_sym_index == 0) return;
+            const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
 
             for (exports) |exp| {
                 if (exp.options.section) |section_name| {
@@ -2105,7 +2115,7 @@ pub const File = struct {
             const casted_line_off = @intCast(u28, line_delta);
 
             const shdr = &self.sections.items[self.debug_line_section_index.?];
-            const file_pos = shdr.sh_offset + decl.fn_link.off + self.getRelocDbgLineOff();
+            const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
             var data: [4]u8 = undefined;
             leb128.writeUnsignedFixed(4, &data, casted_line_off);
             try self.base.file.?.pwriteAll(&data, file_pos);
src-self-hosted/Module.zig
@@ -177,14 +177,14 @@ pub const Decl = struct {
 
     /// Represents the position of the code in the output file.
     /// This is populated regardless of semantic analysis and code generation.
-    link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
+    link: link.File.LinkBlock,
 
     /// Represents the function in the linked output file, if the `Decl` is a function.
     /// This is stored here and not in `Fn` because `Decl` survives across updates but
     /// `Fn` does not.
     /// TODO Look into making `Fn` a longer lived structure and moving this field there
     /// to save on memory usage.
-    fn_link: link.File.Elf.SrcFn = link.File.Elf.SrcFn.empty,
+    fn_link: link.File.LinkFn,
 
     contents_hash: std.zig.SrcHash,
 
@@ -1538,10 +1538,13 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
                     if (!srcHashEql(decl.contents_hash, contents_hash)) {
                         try self.markOutdatedDecl(decl);
                         decl.contents_hash = contents_hash;
-                    } else if (decl.fn_link.len != 0) {
-                        // TODO Look into detecting when this would be unnecessary by storing enough state
-                        // in `Decl` to notice that the line number did not change.
-                        self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+                    } else switch (self.bin_file.tag) {
+                        .elf => if (decl.fn_link.elf.len != 0) {
+                            // TODO Look into detecting when this would be unnecessary by storing enough state
+                            // in `Decl` to notice that the line number did not change.
+                            self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+                        },
+                        .c => {},
                     }
                 }
             } else {
@@ -1745,7 +1748,14 @@ fn allocateNewDecl(
         .analysis = .unreferenced,
         .deletion_flag = false,
         .contents_hash = contents_hash,
-        .link = link.File.Elf.TextBlock.empty,
+        .link = switch (self.bin_file.tag) {
+            .elf => .{ .elf = link.File.Elf.TextBlock.empty },
+            .c => .{ .c = {} },
+        },
+        .fn_link = switch (self.bin_file.tag) {
+            .elf => .{ .elf = link.File.Elf.SrcFn.empty },
+            .c => .{ .c = {} },
+        },
         .generation = 0,
     };
     return new_decl;