diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-10-29 17:45:00 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-29 17:45:00 -0400 |
| commit | 28dc208f65c0a3feca4d6018fb316d7c219d29a7 (patch) | |
| tree | 80b19479b1a50431e138e138cb8eaaf750905db6 /src/link/Plan9.zig | |
| parent | e036cc48d59e5f7c025df7bbd9923a13e30c6ec9 (diff) | |
| parent | 5aef54cbc83cce5c1afe9aededfc505d89559741 (diff) | |
| download | zig-28dc208f65c0a3feca4d6018fb316d7c219d29a7.tar.gz zig-28dc208f65c0a3feca4d6018fb316d7c219d29a7.zip | |
Merge pull request #13082 from g-w1/unnamed-decls-and-relocs-p9
Plan9: Fix The Backend
Diffstat (limited to 'src/link/Plan9.zig')
| -rw-r--r-- | src/link/Plan9.zig | 262 |
1 files changed, 231 insertions, 31 deletions
diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 0af4fe81d5..6c827437ac 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -22,7 +22,8 @@ const log = std.log.scoped(.link); const assert = std.debug.assert; const FnDeclOutput = struct { - code: []const u8, + /// this code is modified when relocated so it is mutable + code: []u8, /// this might have to be modified in the linker, so thats why its mutable lineinfo: []u8, start_line: u32, @@ -61,10 +62,34 @@ fn_decl_table: std.AutoArrayHashMapUnmanaged( *Module.File, struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, FnDeclOutput) = .{} }, ) = .{}, -data_decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, []const u8) = .{}, - +/// the code is modified when relocated, so that is why it is mutable +data_decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, []u8) = .{}, + +/// Table of unnamed constants associated with a parent `Decl`. +/// We store them here so that we can free the constants whenever the `Decl` +/// needs updating or is freed. +/// +/// For example, +/// +/// ```zig +/// const Foo = struct{ +/// a: u8, +/// }; +/// +/// pub fn main() void { +/// var foo = Foo{ .a = 1 }; +/// _ = foo; +/// } +/// ``` +/// +/// value assigned to label `foo` is an unnamed constant belonging/associated +/// with `Decl` `main`, and lives as long as that `Decl`. +unnamed_const_atoms: UnnamedConstTable = .{}, + +relocs: std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Reloc)) = .{}, hdr: aout.ExecHdr = undefined, +// relocs: std. magic: u32, entry_val: ?u64 = null, @@ -76,12 +101,20 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{}, syms_index_free_list: std.ArrayListUnmanaged(usize) = .{}, +const Reloc = struct { + target: Module.Decl.Index, + offset: u64, + addend: u32, +}; + const Bases = struct { text: u64, /// the Global Offset Table starts at the beginning of the data section data: u64, }; +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 })); + fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { return addr + switch (t) { .T, .t, .l, .L => self.bases.text, @@ -233,6 +266,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv const decl_index = func.owner_decl; const decl = module.declPtr(decl_index); + self.freeUnnamedConsts(decl_index); try self.seeDecl(decl_index); log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); @@ -280,11 +314,62 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv } pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { - _ = self; - _ = tv; - _ = decl_index; - log.debug("TODO lowerUnnamedConst for Plan9", .{}); - return error.AnalysisFail; + try self.seeDecl(decl_index); + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + + const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + const unnamed_consts = gop.value_ptr; + + const decl_name = try decl.getFullyQualifiedName(mod); + defer self.base.allocator.free(decl_name); + + const index = unnamed_consts.items.len; + // name is freed when the unnamed const is freed + const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index }); + + const sym_index = try self.allocateSymbolIndex(); + + const info: DeclBlock = .{ + .type = .d, + .offset = null, + .sym_index = sym_index, + .got_index = self.allocateGotIndex(), + }; + const sym: aout.Sym = .{ + .value = undefined, + .type = info.type, + .name = name, + }; + self.syms.items[info.sym_index.?] = sym; + + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .{ + .none = {}, + }, .{ + .parent_atom_index = @enumToInt(decl_index), + }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try mod.failed_decls.put(mod.gpa, decl_index, em); + log.err("{s}", .{em.msg}); + return error.AnalysisFail; + }, + }; + // duped_code is freed when the unnamed const is freed + var duped_code = try self.base.allocator.dupe(u8, code); + errdefer self.base.allocator.free(duped_code); + try unnamed_consts.append(self.base.allocator, .{ .info = info, .code = duped_code }); + // we return the got_index to codegen so that it can reference to the place of the data in the got + return @intCast(u32, info.got_index.?); } pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) !void { @@ -302,18 +387,17 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) try self.seeDecl(decl_index); - log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); + log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index }); 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; // TODO we need the symbol index for symbol in the table of locals for the containing atom - const sym_index = decl.link.plan9.sym_index orelse 0; const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ .none = {} }, .{ - .parent_atom_index = @intCast(u32, sym_index), + .parent_atom_index = @enumToInt(decl_index), }); const code = switch (res) { .externally_managed => |x| x, @@ -347,12 +431,26 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void { if (decl.link.plan9.sym_index) |s| { self.syms.items[s] = sym; } else { - if (self.syms_index_free_list.popOrNull()) |i| { - decl.link.plan9.sym_index = i; - } else { - try self.syms.append(self.base.allocator, sym); - decl.link.plan9.sym_index = self.syms.items.len - 1; - } + const s = try self.allocateSymbolIndex(); + decl.link.plan9.sym_index = s; + self.syms.items[s] = sym; + } +} + +fn allocateSymbolIndex(self: *Plan9) !usize { + if (self.syms_index_free_list.popOrNull()) |i| { + return i; + } else { + _ = try self.syms.addOne(self.base.allocator); + return self.syms.items.len - 1; + } +} +fn allocateGotIndex(self: *Plan9) usize { + if (self.got_index_free_list.popOrNull()) |i| { + return i; + } else { + self.got_len += 1; + return self.got_len - 1; } } @@ -381,7 +479,8 @@ pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { } } -fn declCount(self: *Plan9) usize { +// counts decls and unnamed consts +fn atomCount(self: *Plan9) usize { var fn_decl_count: usize = 0; var itf_files = self.fn_decl_table.iterator(); while (itf_files.next()) |ent| { @@ -389,7 +488,13 @@ fn declCount(self: *Plan9) usize { var submap = ent.value_ptr.functions; fn_decl_count += submap.count(); } - return self.data_decl_table.count() + fn_decl_count; + const data_decl_count = self.data_decl_table.count(); + var unnamed_const_count: usize = 0; + var it_unc = self.unnamed_const_atoms.iterator(); + while (it_unc.next()) |unnamed_consts| { + unnamed_const_count += unnamed_consts.value_ptr.items.len; + } + return data_decl_count + fn_decl_count + unnamed_const_count; } pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { @@ -411,13 +516,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - assert(self.got_len == self.declCount() + self.got_index_free_list.items.len); + assert(self.got_len == self.atomCount() + self.got_index_free_list.items.len); 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); // + 4 for header, got, symbols, linecountinfo - var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.declCount() + 4); + var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.atomCount() + 4); defer self.base.allocator.free(iovecs); const file = self.base.file.?; @@ -509,6 +614,26 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No try self.addDeclExports(mod, decl, exports); } } + // write the unnamed constants after the other data decls + var it_unc = self.unnamed_const_atoms.iterator(); + while (it_unc.next()) |unnamed_consts| { + for (unnamed_consts.value_ptr.items) |*unnamed_const| { + const code = unnamed_const.code; + log.debug("write unnamed const: ({s})", .{self.syms.items[unnamed_const.info.sym_index.?].name}); + foff += code.len; + iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len }; + iovecs_i += 1; + const off = self.getAddr(data_i, .d); + data_i += code.len; + unnamed_const.info.offset = off; + if (!self.sixtyfour_bit) { + mem.writeInt(u32, got_table[unnamed_const.info.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); + } else { + mem.writeInt(u64, got_table[unnamed_const.info.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + } + self.syms.items[unnamed_const.info.sym_index.?].value = off; + } + } // edata symbol self.syms.items[0].value = self.getAddr(data_i, .b); } @@ -518,7 +643,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No try self.writeSyms(&sym_buf); const syms = sym_buf.toOwnedSlice(); defer self.base.allocator.free(syms); - assert(2 + self.declCount() == iovecs_i); // we didn't write all the decls + assert(2 + self.atomCount() == iovecs_i); // we didn't write all the decls iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len }; iovecs_i += 1; iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len }; @@ -539,6 +664,42 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No if (self.sixtyfour_bit) { mem.writeIntSliceBig(u64, hdr_buf[32..40], self.entry_val.?); } + // perform the relocs + { + var it = self.relocs.iterator(); + while (it.next()) |kv| { + const source_decl_index = kv.key_ptr.*; + const source_decl = mod.declPtr(source_decl_index); + for (kv.value_ptr.items) |reloc| { + const target_decl_index = reloc.target; + const target_decl = mod.declPtr(target_decl_index); + const target_decl_offset = target_decl.link.plan9.offset.?; + + const offset = reloc.offset; + const addend = reloc.addend; + + log.debug("relocating the address of '{s}' + {d} into '{s}' + {d}", .{ target_decl.name, addend, source_decl.name, offset }); + + const code = blk: { + const is_fn = source_decl.ty.zigTypeTag() == .Fn; + if (is_fn) { + const table = self.fn_decl_table.get(source_decl.getFileScope()).?.functions; + const output = table.get(source_decl_index).?; + break :blk output.code; + } else { + const code = self.data_decl_table.get(source_decl_index).?; + break :blk code; + } + }; + + if (!self.sixtyfour_bit) { + mem.writeInt(u32, code[@intCast(usize, offset)..][0..4], @intCast(u32, target_decl_offset + addend), self.base.options.target.cpu.arch.endian()); + } else { + mem.writeInt(u64, code[@intCast(usize, offset)..][0..8], target_decl_offset + addend, self.base.options.target.cpu.arch.endian()); + } + } + } + } // write it all! try file.pwritevAll(iovecs, 0); } @@ -599,18 +760,29 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { self.syms_index_free_list.append(self.base.allocator, i) catch {}; self.syms.items[i] = aout.Sym.undefined_symbol; } + self.freeUnnamedConsts(decl_index); + { + const relocs = self.relocs.getPtr(decl_index) orelse return; + relocs.clearAndFree(self.base.allocator); + assert(self.relocs.remove(decl_index)); + } +} +fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void { + const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; + for (unnamed_consts.items) |c| { + self.base.allocator.free(self.syms.items[c.info.sym_index.?].name); + self.base.allocator.free(c.code); + self.syms.items[c.info.sym_index.?] = aout.Sym.undefined_symbol; + self.syms_index_free_list.append(self.base.allocator, c.info.sym_index.?) catch {}; + } + unnamed_consts.clearAndFree(self.base.allocator); } pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void { const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); if (decl.link.plan9.got_index == null) { - if (self.got_index_free_list.popOrNull()) |i| { - decl.link.plan9.got_index = i; - } else { - self.got_len += 1; - decl.link.plan9.got_index = self.got_len - 1; - } + decl.link.plan9.got_index = self.allocateGotIndex(); } } @@ -627,6 +799,19 @@ pub fn updateDeclExports( } pub fn deinit(self: *Plan9) void { const gpa = self.base.allocator; + { + var it = self.relocs.valueIterator(); + while (it.next()) |relocs| { + relocs.deinit(self.base.allocator); + } + self.relocs.deinit(self.base.allocator); + } + // free the unnamed consts + var it_unc = self.unnamed_const_atoms.iterator(); + while (it_unc.next()) |kv| { + self.freeUnnamedConsts(kv.key_ptr.*); + } + self.unnamed_const_atoms.deinit(gpa); var itf_files = self.fn_decl_table.iterator(); while (itf_files.next()) |ent| { // get the submap @@ -771,12 +956,18 @@ pub fn allocateDeclIndexes(self: *Plan9, decl_index: Module.Decl.Index) !void { _ = self; _ = decl_index; } +/// Must be called only after a successful call to `updateDecl`. +pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void { + _ = self; + _ = mod; + _ = decl; +} + pub fn getDeclVAddr( self: *Plan9, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo, ) !u64 { - _ = reloc_info; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); if (decl.ty.zigTypeTag() == .Fn) { @@ -790,7 +981,6 @@ pub fn getDeclVAddr( start += entry.value_ptr.code.len; } } - unreachable; } else { var start = self.bases.data + self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8; var it = self.data_decl_table.iterator(); @@ -798,6 +988,16 @@ pub fn getDeclVAddr( if (decl_index == kv.key_ptr.*) return start; start += kv.value_ptr.len; } - unreachable; } + // the parent_atom_index in this case is just the decl_index of the parent + const gop = try self.relocs.getOrPut(self.base.allocator, @intToEnum(Module.Decl.Index, reloc_info.parent_atom_index)); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(self.base.allocator, .{ + .target = decl_index, + .offset = reloc_info.offset, + .addend = reloc_info.addend, + }); + return undefined; } |
