Merge pull request #13082 from g-w1/unnamed-decls-and-relocs-p9

Plan9: Fix The Backend
This commit is contained in:
Andrew Kelley 2022-10-29 17:45:00 -04:00 committed by GitHub
commit 28dc208f65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 283 additions and 36 deletions

View File

@ -1091,10 +1091,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// Once they are capable this condition could be removed. When removing this condition,
// also test the use case of `build-obj -fcompiler-rt` with the native backends
// and make sure the compiler-rt symbols are emitted.
const capable_of_building_compiler_rt = build_options.have_llvm;
const capable_of_building_compiler_rt = build_options.have_llvm and options.target.os.tag != .plan9;
const capable_of_building_zig_libc = build_options.have_llvm;
const capable_of_building_ssp = build_options.have_llvm;
const capable_of_building_zig_libc = build_options.have_llvm and options.target.os.tag != .plan9;
const capable_of_building_ssp = build_options.have_llvm and options.target.os.tag != .plan9;
const comp: *Compilation = comp: {
// For allocations that have the same lifetime as Compilation. This arena is used only during this

View File

@ -6953,8 +6953,12 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
.@"type" = .direct,
.sym_index = local_sym_index,
} };
} else if (self.bin_file.cast(link.File.Plan9)) |_| {
return self.fail("TODO lower unnamed const in Plan9", .{});
} else if (self.bin_file.cast(link.File.Plan9)) |p9| {
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
const got_index = local_sym_index; // the plan9 backend returns the got_index
const got_addr = p9.bases.data + got_index * ptr_bytes;
return MCValue{ .memory = got_addr };
} else {
return self.fail("TODO lower unnamed const", .{});
}

View File

@ -581,7 +581,7 @@ pub const File = struct {
.macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
.c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
.wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),
.plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"),
.plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl),
.spirv, .nvptx => {},
}
}

View File

@ -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;
}

View File

@ -0,0 +1,5 @@
pub fn main() void {}
// run
// target=x86_64-plan9
//

View File

@ -0,0 +1,28 @@
pub fn main() void {
const str = "Hello World!\n";
asm volatile (
\\push $0
\\push %%r10
\\push %%r11
\\push $1
\\push $0
\\syscall
\\pop %%r11
\\pop %%r11
\\pop %%r11
\\pop %%r11
\\pop %%r11
:
// pwrite
: [syscall_number] "{rbp}" (51),
[hey] "{r11}" (@ptrToInt(str)),
[strlen] "{r10}" (str.len),
: "rcx", "rbp", "r11", "memory"
);
}
// run
// target=x86_64-plan9
//
// Hello World
//

View File

@ -0,0 +1,10 @@
const std = @import("std");
pub fn main() void {
const str = "Hello World!\n";
_ = std.os.plan9.pwrite(1, str, str.len, 0);
}
// run
//
// Hello World
//