mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
plan9 linker: make more incremental
The incrementalness is now roughly the same as the c backend rather than the spirv backend before.
This commit is contained in:
parent
3a41e4430e
commit
91b1896184
@ -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();
|
||||
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);
|
||||
|
||||
// + 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.?;
|
||||
|
||||
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
|
||||
{
|
||||
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),
|
||||
};
|
||||
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.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;
|
||||
mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
self.syms.items[decl.link.plan9.sym_index.?].value = off;
|
||||
}
|
||||
// etext symbol
|
||||
self.syms.items[2].value = self.getAddr(text_i, .t);
|
||||
}
|
||||
// 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 {
|
||||
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;
|
||||
}
|
||||
// edata symbol
|
||||
self.syms.items[0].value = self.getAddr(data_i, .b);
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user