Commit 91b1896184

Jacob G-W <jacoblevgw@gmail.com>
2021-07-14 02:40:29
plan9 linker: make more incremental
The incrementalness is now roughly the same as the c backend rather than the spirv backend before.
1 parent 3a41e44
Changed files (1)
src
src/link/Plan9.zig
@@ -25,20 +25,22 @@ sixtyfour_bit: bool,
 error_flags: File.ErrorFlags = File.ErrorFlags{},
 bases: Bases,
 
-decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
-/// is just casted down when 32 bit
+/// A symbol's value is just casted down when compiling
+/// for a 32 bit target.
 syms: std.ArrayListUnmanaged(aout.Sym) = .{},
-text_buf: std.ArrayListUnmanaged(u8) = .{},
-data_buf: std.ArrayListUnmanaged(u8) = .{},
+
+fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
+data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
 
 hdr: aout.ExecHdr = undefined,
 
 entry_decl: ?*Module.Decl = null,
 
-got: std.ArrayListUnmanaged(u64) = .{},
+got_len: u64 = 0,
+
 const Bases = struct {
     text: u64,
-    /// the addr of the got
+    /// the Global Offset Table starts at the beginning of the data section
     data: u64,
 };
 
@@ -49,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
         else => unreachable,
     };
 }
-/// opposite of getAddr
-fn takeAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
-    return addr - switch (t) {
-        .T, .t, .l, .L => self.bases.text,
-        .D, .d, .B, .b => self.bases.data,
-        else => unreachable,
-    };
-}
 
 fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
     return self.getAddr(s.value, s.type);
@@ -127,18 +121,80 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
     if (build_options.skip_non_native and builtin.object_format != .plan9) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
-    _ = module;
-    // Keep track of all decls so we can iterate over them on flush().
-    _ = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl);
 
-    _ = air;
-    _ = liveness;
-    @panic("TODO Plan9 needs to keep track of Air and Liveness so it can use them later");
+    const decl = func.owner_decl;
+    log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
+
+    var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+    defer code_buffer.deinit();
+    const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .none = .{} });
+    const code = switch (res) {
+        .appended => code_buffer.toOwnedSlice(),
+        .fail => |em| {
+            decl.analysis = .codegen_failure;
+            try module.failed_decls.put(module.gpa, decl, em);
+            return;
+        },
+    };
+    try self.fn_decl_table.put(self.base.allocator, decl, code);
+    return self.updateFinish(decl);
 }
 
 pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
-    _ = module;
-    _ = try self.decl_table.getOrPut(self.base.allocator, decl);
+    if (decl.val.tag() == .extern_fn) {
+        return; // TODO Should we do more when front-end analyzed extern decl?
+    }
+    if (decl.val.castTag(.variable)) |payload| {
+        const variable = payload.data;
+        if (variable.is_extern) {
+            return; // TODO Should we do more when front-end analyzed extern decl?
+        }
+    }
+
+    log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
+
+    var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+    defer code_buffer.deinit();
+    const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        .ty = decl.ty,
+        .val = decl_val,
+    }, &code_buffer, .{ .none = .{} });
+    const code = switch (res) {
+        .externally_managed => |x| x,
+        .appended => code_buffer.items,
+        .fail => |em| {
+            decl.analysis = .codegen_failure;
+            try module.failed_decls.put(module.gpa, decl, em);
+            return;
+        },
+    };
+    var duped_code = try std.mem.dupe(self.base.allocator, u8, code);
+    errdefer self.base.allocator.free(duped_code);
+    try self.data_decl_table.put(self.base.allocator, decl, duped_code);
+    return self.updateFinish(decl);
+}
+/// called at the end of update{Decl,Func}
+fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
+    const is_fn = (decl.ty.zigTypeTag() == .Fn);
+    log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
+    const sym_t: aout.Sym.Type = if (is_fn) .t else .d;
+    // write the internal linker metadata
+    decl.link.plan9.type = sym_t;
+    // write the symbol
+    // we already have the got index because that got allocated in allocateDeclIndexes
+    const sym: aout.Sym = .{
+        .value = undefined, // the value of stuff gets filled in in flushModule
+        .type = decl.link.plan9.type,
+        .name = mem.span(decl.name),
+    };
+
+    if (decl.link.plan9.sym_index) |s| {
+        self.syms.items[s] = sym;
+    } else {
+        try self.syms.append(self.base.allocator, sym);
+        decl.link.plan9.sym_index = self.syms.items.len - 1;
+    }
 }
 
 pub fn flush(self: *Plan9, comp: *Compilation) !void {
@@ -165,160 +221,107 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
 
     defer assert(self.hdr.entry != 0x0);
 
-    const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
+    _ = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
 
-    self.text_buf.items.len = 0;
-    self.data_buf.items.len = 0;
-    // ensure space to write the got later
-    assert(self.got.items.len == self.decl_table.count());
-    try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (!self.sixtyfour_bit) @as(u32, 4) else 8);
-    // temporary buffer
-    var code_buffer = std.ArrayList(u8).init(self.base.allocator);
-    defer code_buffer.deinit();
-    {
-        for (self.decl_table.keys()) |decl| {
-            if (!decl.has_tv) continue;
-            const is_fn = (decl.ty.zigTypeTag() == .Fn);
-
-            log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
-            decl.link.plan9 = if (is_fn) .{
-                .offset = self.getAddr(self.text_buf.items.len, .t),
-                .type = .t,
-                .sym_index = decl.link.plan9.sym_index,
-                .got_index = decl.link.plan9.got_index,
-            } else .{
-                .offset = self.getAddr(self.data_buf.items.len, .d),
-                .type = .d,
-                .sym_index = decl.link.plan9.sym_index,
-                .got_index = decl.link.plan9.got_index,
-            };
-            self.got.items[decl.link.plan9.got_index.?] = decl.link.plan9.offset.?;
-            if (decl.link.plan9.sym_index) |s| {
-                self.syms.items[s] = .{
-                    .value = decl.link.plan9.offset.?,
-                    .type = decl.link.plan9.type,
-                    .name = mem.span(decl.name),
-                };
-            } else {
-                try self.syms.append(self.base.allocator, .{
-                    .value = decl.link.plan9.offset.?,
-                    .type = decl.link.plan9.type,
-                    .name = mem.span(decl.name),
-                });
-                decl.link.plan9.sym_index = self.syms.items.len - 1;
-            }
+    assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count());
+    const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
+    var got_table = try self.base.allocator.alloc(u8, got_size);
+    defer self.base.allocator.free(got_table);
 
-            if (module.decl_exports.get(decl)) |exports| {
-                for (exports) |exp| {
-                    // plan9 does not support custom sections
-                    if (exp.options.section) |section_name| {
-                        if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
-                            try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));
-                            break;
-                        }
-                    }
-                    if (std.mem.eql(u8, exp.options.name, "_start")) {
-                        assert(decl.link.plan9.type == .t); // we tried to link a non-function as the entry
-                        self.entry_decl = decl;
-                    }
-                    if (exp.link.plan9) |i| {
-                        self.syms.items[i] = .{
-                            .value = decl.link.plan9.offset.?,
-                            .type = decl.link.plan9.type.toGlobal(),
-                            .name = exp.options.name,
-                        };
-                    } else {
-                        try self.syms.append(self.base.allocator, .{
-                            .value = decl.link.plan9.offset.?,
-                            .type = decl.link.plan9.type.toGlobal(),
-                            .name = exp.options.name,
-                        });
-                        exp.link.plan9 = self.syms.items.len - 1;
-                    }
-                }
-            }
+    // + 2 for header, got, symbols
+    var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);
+
+    const file = self.base.file.?;
 
-            log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
-            const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
-                .ty = decl.ty,
-                .val = decl.val,
-            }, &code_buffer, .{ .none = {} });
-            const code = switch (res) {
-                .externally_managed => |x| x,
-                .appended => code_buffer.items,
-                .fail => |em| {
-                    decl.analysis = .codegen_failure;
-                    try module.failed_decls.put(module.gpa, decl, em);
-                    // TODO try to do more decls
-                    return;
-                },
-            };
-            if (is_fn) {
-                try self.text_buf.appendSlice(self.base.allocator, code);
-                code_buffer.items.len = 0;
+    var hdr_buf: [40]u8 = undefined;
+    // account for the fat header
+    const hdr_size = if (self.sixtyfour_bit) @as(usize, 40) else 32;
+    const hdr_slice: []u8 = hdr_buf[0..hdr_size];
+    var foff = hdr_size;
+    iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
+    var iovecs_i: u64 = 1;
+    var text_i: u64 = 0;
+    // text
+    {
+        var it = self.fn_decl_table.iterator();
+        while (it.next()) |entry| {
+            const decl = entry.key_ptr.*;
+            const code = entry.value_ptr.*;
+            foff += code.len;
+            text_i += code.len;
+            iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
+            iovecs_i += 1;
+            const off = self.getAddr(text_i, .t);
+            decl.link.plan9.offset = off;
+            if (!self.sixtyfour_bit) {
+                mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
+                mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
             } else {
-                try self.data_buf.appendSlice(self.base.allocator, code);
-                code_buffer.items.len = 0;
+                mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
             }
+            self.syms.items[decl.link.plan9.sym_index.?].value = off;
         }
+        // etext symbol
+        self.syms.items[2].value = self.getAddr(text_i, .t);
     }
-
-    // write the got
-    if (!self.sixtyfour_bit) {
-        for (self.got.items) |p, i| {
-            mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian());
-        }
-    } else {
-        for (self.got.items) |p, i| {
-            mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian());
+    // data
+    var data_i: u64 = got_size;
+    {
+        var it = self.data_decl_table.iterator();
+        while (it.next()) |entry| {
+            const decl = entry.key_ptr.*;
+            const code = entry.value_ptr.*;
+            foff += code.len;
+            data_i += code.len;
+            iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
+            iovecs_i += 1;
+            const off = self.getAddr(data_i, .d);
+            decl.link.plan9.offset = off;
+            if (!self.sixtyfour_bit) {
+                mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
+            } else {
+                mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
+            }
+            self.syms.items[decl.link.plan9.sym_index.?].value = off;
         }
+        // edata symbol
+        self.syms.items[0].value = self.getAddr(data_i, .b);
     }
-
-    self.hdr.entry = @truncate(u32, self.entry_decl.?.link.plan9.offset.?);
-
-    // edata, end, etext
-    self.syms.items[0].value = self.getAddr(0x0, .b);
+    // edata
     self.syms.items[1].value = self.getAddr(0x0, .b);
-    self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t);
-
     var sym_buf = std.ArrayList(u8).init(self.base.allocator);
     defer sym_buf.deinit();
     try self.writeSyms(&sym_buf);
-
+    iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };
+    iovecs_i += 1;
+    assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls
+    iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };
+    iovecs_i += 1;
     // generate the header
     self.hdr = .{
         .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
-        .text = @intCast(u32, self.text_buf.items.len),
-        .data = @intCast(u32, self.data_buf.items.len),
+        .text = @intCast(u32, text_i),
+        .data = @intCast(u32, data_i),
         .syms = @intCast(u32, sym_buf.items.len),
         .bss = 0,
         .pcsz = 0,
         .spsz = 0,
-        .entry = self.hdr.entry,
+        .entry = @intCast(u32, self.entry_decl.?.link.plan9.offset.?),
     };
-
-    const file = self.base.file.?;
-
-    var hdr_buf = self.hdr.toU8s();
-    const hdr_slice: []const u8 = &hdr_buf;
-    // account for the fat header
-    const hdr_size: u8 = if (!self.sixtyfour_bit) 32 else 40;
+    std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
     // write the fat header for 64 bit entry points
     if (self.sixtyfour_bit) {
-        mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry);
+        mem.writeIntSliceBig(u64, hdr_buf[32..40], self.entry_decl.?.link.plan9.offset.?);
     }
     // write it all!
-    var vectors: [4]std.os.iovec_const = .{
-        .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size },
-        .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len },
-        .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len },
-        .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len },
-        // TODO spsz, pcsz
-    };
-    try file.pwritevAll(&vectors, 0);
+    try file.pwritevAll(iovecs, 0);
 }
 pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
-    assert(self.decl_table.swapRemove(decl));
+    const is_fn = (decl.ty.zigTypeTag() == .Fn);
+    if (is_fn)
+        assert(self.fn_decl_table.swapRemove(decl))
+    else
+        assert(self.data_decl_table.swapRemove(decl));
 }
 
 pub fn updateDeclExports(
@@ -334,11 +337,17 @@ pub fn updateDeclExports(
     _ = exports;
 }
 pub fn deinit(self: *Plan9) void {
-    self.decl_table.deinit(self.base.allocator);
+    var itf = self.fn_decl_table.iterator();
+    while (itf.next()) |entry| {
+        self.base.allocator.free(entry.value_ptr.*);
+    }
+    self.fn_decl_table.deinit(self.base.allocator);
+    var itd = self.data_decl_table.iterator();
+    while (itd.next()) |entry| {
+        self.base.allocator.free(entry.value_ptr.*);
+    }
+    self.data_decl_table.deinit(self.base.allocator);
     self.syms.deinit(self.base.allocator);
-    self.text_buf.deinit(self.base.allocator);
-    self.data_buf.deinit(self.base.allocator);
-    self.got.deinit(self.base.allocator);
 }
 
 pub const Export = ?usize;
@@ -397,6 +406,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
 }
 
 pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
-    try self.got.append(self.base.allocator, 0xdeadbeef);
-    decl.link.plan9.got_index = self.got.items.len - 1;
+    if (decl.link.plan9.got_index != null) {
+        self.got_len += 1;
+        decl.link.plan9.got_index = self.got_len - 1;
+    }
 }