authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2021-07-13 20:40:29-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
log91b1896184cc89e21d12dd246ce7d658b6d3f365
treebea8430c79d4f4776ae9243e95cb93933f0f16c7
parent3a41e4430eae16e5aa739b7a71b1fded1f1029e3

plan9 linker: make more incremental

The incrementalness is now roughly the same as the c backend rather than the spirv backend before.

1 files changed, 166 insertions(+), 155 deletions(-)

src/link/Plan9.zig+166-155
...@@ -25,20 +25,22 @@ sixtyfour_bit: bool,...@@ -25,20 +25,22 @@ sixtyfour_bit: bool,
25error_flags: File.ErrorFlags = File.ErrorFlags{},25error_flags: File.ErrorFlags = File.ErrorFlags{},
26bases: Bases,26bases: Bases,
2727
28decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},28/// A symbol's value is just casted down when compiling
29/// is just casted down when 32 bit29/// for a 32 bit target.
30syms: std.ArrayListUnmanaged(aout.Sym) = .{},30syms: std.ArrayListUnmanaged(aout.Sym) = .{},
31text_buf: std.ArrayListUnmanaged(u8) = .{},31
32data_buf: std.ArrayListUnmanaged(u8) = .{},32fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
33data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
3334
34hdr: aout.ExecHdr = undefined,35hdr: aout.ExecHdr = undefined,
3536
36entry_decl: ?*Module.Decl = null,37entry_decl: ?*Module.Decl = null,
3738
38got: std.ArrayListUnmanaged(u64) = .{},39got_len: u64 = 0,
40
39const Bases = struct {41const Bases = struct {
40 text: u64,42 text: u64,
41 /// the addr of the got43 /// the Global Offset Table starts at the beginning of the data section
42 data: u64,44 data: u64,
43};45};
4446
...@@ -49,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {...@@ -49,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
49 else => unreachable,51 else => unreachable,
50 };52 };
51}53}
52/// opposite of getAddr
53fn takeAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
54 return addr - switch (t) {
55 .T, .t, .l, .L => self.bases.text,
56 .D, .d, .B, .b => self.bases.data,
57 else => unreachable,
58 };
59}
6054
61fn getSymAddr(self: Plan9, s: aout.Sym) u64 {55fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
62 return self.getAddr(s.value, s.type);56 return self.getAddr(s.value, s.type);
...@@ -127,18 +121,80 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -127,18 +121,80 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
127 if (build_options.skip_non_native and builtin.object_format != .plan9) {121 if (build_options.skip_non_native and builtin.object_format != .plan9) {
128 @panic("Attempted to compile for object format that was disabled by build configuration");122 @panic("Attempted to compile for object format that was disabled by build configuration");
129 }123 }
130 _ = module;
131 // Keep track of all decls so we can iterate over them on flush().
132 _ = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl);
133124
134 _ = air;125 const decl = func.owner_decl;
135 _ = liveness;126 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
136 @panic("TODO Plan9 needs to keep track of Air and Liveness so it can use them later");127
128 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
129 defer code_buffer.deinit();
130 const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .none = .{} });
131 const code = switch (res) {
132 .appended => code_buffer.toOwnedSlice(),
133 .fail => |em| {
134 decl.analysis = .codegen_failure;
135 try module.failed_decls.put(module.gpa, decl, em);
136 return;
137 },
138 };
139 try self.fn_decl_table.put(self.base.allocator, decl, code);
140 return self.updateFinish(decl);
137}141}
138142
139pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {143pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
140 _ = module;144 if (decl.val.tag() == .extern_fn) {
141 _ = try self.decl_table.getOrPut(self.base.allocator, decl);145 return; // TODO Should we do more when front-end analyzed extern decl?
146 }
147 if (decl.val.castTag(.variable)) |payload| {
148 const variable = payload.data;
149 if (variable.is_extern) {
150 return; // TODO Should we do more when front-end analyzed extern decl?
151 }
152 }
153
154 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
155
156 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
157 defer code_buffer.deinit();
158 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
159 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
160 .ty = decl.ty,
161 .val = decl_val,
162 }, &code_buffer, .{ .none = .{} });
163 const code = switch (res) {
164 .externally_managed => |x| x,
165 .appended => code_buffer.items,
166 .fail => |em| {
167 decl.analysis = .codegen_failure;
168 try module.failed_decls.put(module.gpa, decl, em);
169 return;
170 },
171 };
172 var duped_code = try std.mem.dupe(self.base.allocator, u8, code);
173 errdefer self.base.allocator.free(duped_code);
174 try self.data_decl_table.put(self.base.allocator, decl, duped_code);
175 return self.updateFinish(decl);
176}
177/// called at the end of update{Decl,Func}
178fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
179 const is_fn = (decl.ty.zigTypeTag() == .Fn);
180 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
181 const sym_t: aout.Sym.Type = if (is_fn) .t else .d;
182 // write the internal linker metadata
183 decl.link.plan9.type = sym_t;
184 // write the symbol
185 // we already have the got index because that got allocated in allocateDeclIndexes
186 const sym: aout.Sym = .{
187 .value = undefined, // the value of stuff gets filled in in flushModule
188 .type = decl.link.plan9.type,
189 .name = mem.span(decl.name),
190 };
191
192 if (decl.link.plan9.sym_index) |s| {
193 self.syms.items[s] = sym;
194 } else {
195 try self.syms.append(self.base.allocator, sym);
196 decl.link.plan9.sym_index = self.syms.items.len - 1;
197 }
142}198}
143199
144pub fn flush(self: *Plan9, comp: *Compilation) !void {200pub fn flush(self: *Plan9, comp: *Compilation) !void {
...@@ -165,160 +221,107 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {...@@ -165,160 +221,107 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
165221
166 defer assert(self.hdr.entry != 0x0);222 defer assert(self.hdr.entry != 0x0);
167223
168 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;224 _ = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
169225
170 self.text_buf.items.len = 0;226 assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count());
171 self.data_buf.items.len = 0;227 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
172 // ensure space to write the got later228 var got_table = try self.base.allocator.alloc(u8, got_size);
173 assert(self.got.items.len == self.decl_table.count());229 defer self.base.allocator.free(got_table);
174 try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (!self.sixtyfour_bit) @as(u32, 4) else 8);
175 // temporary buffer
176 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
177 defer code_buffer.deinit();
178 {
179 for (self.decl_table.keys()) |decl| {
180 if (!decl.has_tv) continue;
181 const is_fn = (decl.ty.zigTypeTag() == .Fn);
182
183 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
184 decl.link.plan9 = if (is_fn) .{
185 .offset = self.getAddr(self.text_buf.items.len, .t),
186 .type = .t,
187 .sym_index = decl.link.plan9.sym_index,
188 .got_index = decl.link.plan9.got_index,
189 } else .{
190 .offset = self.getAddr(self.data_buf.items.len, .d),
191 .type = .d,
192 .sym_index = decl.link.plan9.sym_index,
193 .got_index = decl.link.plan9.got_index,
194 };
195 self.got.items[decl.link.plan9.got_index.?] = decl.link.plan9.offset.?;
196 if (decl.link.plan9.sym_index) |s| {
197 self.syms.items[s] = .{
198 .value = decl.link.plan9.offset.?,
199 .type = decl.link.plan9.type,
200 .name = mem.span(decl.name),
201 };
202 } else {
203 try self.syms.append(self.base.allocator, .{
204 .value = decl.link.plan9.offset.?,
205 .type = decl.link.plan9.type,
206 .name = mem.span(decl.name),
207 });
208 decl.link.plan9.sym_index = self.syms.items.len - 1;
209 }
210230
211 if (module.decl_exports.get(decl)) |exports| {231 // + 2 for header, got, symbols
212 for (exports) |exp| {232 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);
213 // plan9 does not support custom sections233
214 if (exp.options.section) |section_name| {234 const file = self.base.file.?;
215 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
216 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));
217 break;
218 }
219 }
220 if (std.mem.eql(u8, exp.options.name, "_start")) {
221 assert(decl.link.plan9.type == .t); // we tried to link a non-function as the entry
222 self.entry_decl = decl;
223 }
224 if (exp.link.plan9) |i| {
225 self.syms.items[i] = .{
226 .value = decl.link.plan9.offset.?,
227 .type = decl.link.plan9.type.toGlobal(),
228 .name = exp.options.name,
229 };
230 } else {
231 try self.syms.append(self.base.allocator, .{
232 .value = decl.link.plan9.offset.?,
233 .type = decl.link.plan9.type.toGlobal(),
234 .name = exp.options.name,
235 });
236 exp.link.plan9 = self.syms.items.len - 1;
237 }
238 }
239 }
240235
241 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });236 var hdr_buf: [40]u8 = undefined;
242 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{237 // account for the fat header
243 .ty = decl.ty,238 const hdr_size = if (self.sixtyfour_bit) @as(usize, 40) else 32;
244 .val = decl.val,239 const hdr_slice: []u8 = hdr_buf[0..hdr_size];
245 }, &code_buffer, .{ .none = {} });240 var foff = hdr_size;
246 const code = switch (res) {241 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
247 .externally_managed => |x| x,242 var iovecs_i: u64 = 1;
248 .appended => code_buffer.items,243 var text_i: u64 = 0;
249 .fail => |em| {244 // text
250 decl.analysis = .codegen_failure;245 {
251 try module.failed_decls.put(module.gpa, decl, em);246 var it = self.fn_decl_table.iterator();
252 // TODO try to do more decls247 while (it.next()) |entry| {
253 return;248 const decl = entry.key_ptr.*;
254 },249 const code = entry.value_ptr.*;
255 };250 foff += code.len;
256 if (is_fn) {251 text_i += code.len;
257 try self.text_buf.appendSlice(self.base.allocator, code);252 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
258 code_buffer.items.len = 0;253 iovecs_i += 1;
254 const off = self.getAddr(text_i, .t);
255 decl.link.plan9.offset = off;
256 if (!self.sixtyfour_bit) {
257 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
258 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
259 } else {259 } else {
260 try self.data_buf.appendSlice(self.base.allocator, code);260 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
261 code_buffer.items.len = 0;
262 }261 }
262 self.syms.items[decl.link.plan9.sym_index.?].value = off;
263 }263 }
264 // etext symbol
265 self.syms.items[2].value = self.getAddr(text_i, .t);
264 }266 }
265267 // data
266 // write the got268 var data_i: u64 = got_size;
267 if (!self.sixtyfour_bit) {269 {
268 for (self.got.items) |p, i| {270 var it = self.data_decl_table.iterator();
269 mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian());271 while (it.next()) |entry| {
270 }272 const decl = entry.key_ptr.*;
271 } else {273 const code = entry.value_ptr.*;
272 for (self.got.items) |p, i| {274 foff += code.len;
273 mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian());275 data_i += code.len;
276 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
277 iovecs_i += 1;
278 const off = self.getAddr(data_i, .d);
279 decl.link.plan9.offset = off;
280 if (!self.sixtyfour_bit) {
281 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
282 } else {
283 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
284 }
285 self.syms.items[decl.link.plan9.sym_index.?].value = off;
274 }286 }
287 // edata symbol
288 self.syms.items[0].value = self.getAddr(data_i, .b);
275 }289 }
276290 // edata
277 self.hdr.entry = @truncate(u32, self.entry_decl.?.link.plan9.offset.?);
278
279 // edata, end, etext
280 self.syms.items[0].value = self.getAddr(0x0, .b);
281 self.syms.items[1].value = self.getAddr(0x0, .b);291 self.syms.items[1].value = self.getAddr(0x0, .b);
282 self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t);
283
284 var sym_buf = std.ArrayList(u8).init(self.base.allocator);292 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
285 defer sym_buf.deinit();293 defer sym_buf.deinit();
286 try self.writeSyms(&sym_buf);294 try self.writeSyms(&sym_buf);
287295 iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };
296 iovecs_i += 1;
297 assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls
298 iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };
299 iovecs_i += 1;
288 // generate the header300 // generate the header
289 self.hdr = .{301 self.hdr = .{
290 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),302 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
291 .text = @intCast(u32, self.text_buf.items.len),303 .text = @intCast(u32, text_i),
292 .data = @intCast(u32, self.data_buf.items.len),304 .data = @intCast(u32, data_i),
293 .syms = @intCast(u32, sym_buf.items.len),305 .syms = @intCast(u32, sym_buf.items.len),
294 .bss = 0,306 .bss = 0,
295 .pcsz = 0,307 .pcsz = 0,
296 .spsz = 0,308 .spsz = 0,
297 .entry = self.hdr.entry,309 .entry = @intCast(u32, self.entry_decl.?.link.plan9.offset.?),
298 };310 };
299311 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
300 const file = self.base.file.?;
301
302 var hdr_buf = self.hdr.toU8s();
303 const hdr_slice: []const u8 = &hdr_buf;
304 // account for the fat header
305 const hdr_size: u8 = if (!self.sixtyfour_bit) 32 else 40;
306 // write the fat header for 64 bit entry points312 // write the fat header for 64 bit entry points
307 if (self.sixtyfour_bit) {313 if (self.sixtyfour_bit) {
308 mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry);314 mem.writeIntSliceBig(u64, hdr_buf[32..40], self.entry_decl.?.link.plan9.offset.?);
309 }315 }
310 // write it all!316 // write it all!
311 var vectors: [4]std.os.iovec_const = .{317 try file.pwritevAll(iovecs, 0);
312 .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size },
313 .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len },
314 .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len },
315 .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len },
316 // TODO spsz, pcsz
317 };
318 try file.pwritevAll(&vectors, 0);
319}318}
320pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {319pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
321 assert(self.decl_table.swapRemove(decl));320 const is_fn = (decl.ty.zigTypeTag() == .Fn);
321 if (is_fn)
322 assert(self.fn_decl_table.swapRemove(decl))
323 else
324 assert(self.data_decl_table.swapRemove(decl));
322}325}
323326
324pub fn updateDeclExports(327pub fn updateDeclExports(
...@@ -334,11 +337,17 @@ pub fn updateDeclExports(...@@ -334,11 +337,17 @@ pub fn updateDeclExports(
334 _ = exports;337 _ = exports;
335}338}
336pub fn deinit(self: *Plan9) void {339pub fn deinit(self: *Plan9) void {
337 self.decl_table.deinit(self.base.allocator);340 var itf = self.fn_decl_table.iterator();
341 while (itf.next()) |entry| {
342 self.base.allocator.free(entry.value_ptr.*);
343 }
344 self.fn_decl_table.deinit(self.base.allocator);
345 var itd = self.data_decl_table.iterator();
346 while (itd.next()) |entry| {
347 self.base.allocator.free(entry.value_ptr.*);
348 }
349 self.data_decl_table.deinit(self.base.allocator);
338 self.syms.deinit(self.base.allocator);350 self.syms.deinit(self.base.allocator);
339 self.text_buf.deinit(self.base.allocator);
340 self.data_buf.deinit(self.base.allocator);
341 self.got.deinit(self.base.allocator);
342}351}
343352
344pub const Export = ?usize;353pub const Export = ?usize;
...@@ -397,6 +406,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -397,6 +406,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
397}406}
398407
399pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {408pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
400 try self.got.append(self.base.allocator, 0xdeadbeef);409 if (decl.link.plan9.got_index != null) {
401 decl.link.plan9.got_index = self.got.items.len - 1;410 self.got_len += 1;
411 decl.link.plan9.got_index = self.got_len - 1;
412 }
402}413}