authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-09 13:08:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-09-09 13:08:58+02:00
log56b96cd61b0bdb7f5b11a5283fe6dd5b585ef10e
treea19765f949fd7ce1d1d6fdbcf684a812d30e634a
parenta833bdcd7e6fcfee6e9cc33a3f7de78b16a36941
parent5006fb6846ccaa7edb1547588cf1aa08c8decf2b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12772 from ziglang/coff-basic-imports

coff: implement enough of the incremental linker to pass behavior and incremental tests on Windows

28 files changed, 1435 insertions(+), 584 deletions(-)

ci/azure/pipelines.yml+1-2
...@@ -73,8 +73,7 @@ jobs:...@@ -73,8 +73,7 @@ jobs:
73 & "$ZIGINSTALLDIR\bin\zig.exe" build test docs `73 & "$ZIGINSTALLDIR\bin\zig.exe" build test docs `
74 --search-prefix "$ZIGPREFIXPATH" `74 --search-prefix "$ZIGPREFIXPATH" `
75 -Dstatic-llvm `75 -Dstatic-llvm `
76 -Dskip-non-native `76 -Dskip-non-native
77 -Dskip-stage2-tests
78 CheckLastExitCode77 CheckLastExitCode
79 name: test78 name: test
80 displayName: 'Test'79 displayName: 'Test'
lib/std/fs/file.zig+16
...@@ -990,6 +990,8 @@ pub const File = struct {...@@ -990,6 +990,8 @@ pub const File = struct {
990 return index;990 return index;
991 }991 }
992992
993 /// On Windows, this function currently does alter the file pointer.
994 /// https://github.com/ziglang/zig/issues/12783
993 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {995 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
994 if (is_windows) {996 if (is_windows) {
995 return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode);997 return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode);
...@@ -1004,6 +1006,8 @@ pub const File = struct {...@@ -1004,6 +1006,8 @@ pub const File = struct {
10041006
1005 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it1007 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
1006 /// means the file reached the end. Reaching the end of a file is not an error condition.1008 /// means the file reached the end. Reaching the end of a file is not an error condition.
1009 /// On Windows, this function currently does alter the file pointer.
1010 /// https://github.com/ziglang/zig/issues/12783
1007 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {1011 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
1008 var index: usize = 0;1012 var index: usize = 0;
1009 while (index != buffer.len) {1013 while (index != buffer.len) {
...@@ -1058,6 +1062,8 @@ pub const File = struct {...@@ -1058,6 +1062,8 @@ pub const File = struct {
1058 }1062 }
10591063
1060 /// See https://github.com/ziglang/zig/issues/76991064 /// See https://github.com/ziglang/zig/issues/7699
1065 /// On Windows, this function currently does alter the file pointer.
1066 /// https://github.com/ziglang/zig/issues/12783
1061 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {1067 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {
1062 if (is_windows) {1068 if (is_windows) {
1063 // TODO improve this to use ReadFileScatter1069 // TODO improve this to use ReadFileScatter
...@@ -1079,6 +1085,8 @@ pub const File = struct {...@@ -1079,6 +1085,8 @@ pub const File = struct {
1079 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in1085 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1080 /// order to handle partial reads from the underlying OS layer.1086 /// order to handle partial reads from the underlying OS layer.
1081 /// See https://github.com/ziglang/zig/issues/76991087 /// See https://github.com/ziglang/zig/issues/7699
1088 /// On Windows, this function currently does alter the file pointer.
1089 /// https://github.com/ziglang/zig/issues/12783
1082 pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize {1090 pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize {
1083 if (iovecs.len == 0) return 0;1091 if (iovecs.len == 0) return 0;
10841092
...@@ -1122,6 +1130,8 @@ pub const File = struct {...@@ -1122,6 +1130,8 @@ pub const File = struct {
1122 }1130 }
1123 }1131 }
11241132
1133 /// On Windows, this function currently does alter the file pointer.
1134 /// https://github.com/ziglang/zig/issues/12783
1125 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {1135 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
1126 if (is_windows) {1136 if (is_windows) {
1127 return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode);1137 return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode);
...@@ -1134,6 +1144,8 @@ pub const File = struct {...@@ -1134,6 +1144,8 @@ pub const File = struct {
1134 }1144 }
1135 }1145 }
11361146
1147 /// On Windows, this function currently does alter the file pointer.
1148 /// https://github.com/ziglang/zig/issues/12783
1137 pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void {1149 pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void {
1138 var index: usize = 0;1150 var index: usize = 0;
1139 while (index < bytes.len) {1151 while (index < bytes.len) {
...@@ -1179,6 +1191,8 @@ pub const File = struct {...@@ -1179,6 +1191,8 @@ pub const File = struct {
1179 }1191 }
11801192
1181 /// See https://github.com/ziglang/zig/issues/76991193 /// See https://github.com/ziglang/zig/issues/7699
1194 /// On Windows, this function currently does alter the file pointer.
1195 /// https://github.com/ziglang/zig/issues/12783
1182 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {1196 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {
1183 if (is_windows) {1197 if (is_windows) {
1184 // TODO improve this to use WriteFileScatter1198 // TODO improve this to use WriteFileScatter
...@@ -1197,6 +1211,8 @@ pub const File = struct {...@@ -1197,6 +1211,8 @@ pub const File = struct {
1197 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in1211 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1198 /// order to handle partial writes from the underlying OS layer.1212 /// order to handle partial writes from the underlying OS layer.
1199 /// See https://github.com/ziglang/zig/issues/76991213 /// See https://github.com/ziglang/zig/issues/7699
1214 /// On Windows, this function currently does alter the file pointer.
1215 /// https://github.com/ziglang/zig/issues/12783
1200 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {1216 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {
1201 if (iovecs.len == 0) return;1217 if (iovecs.len == 0) return;
12021218
lib/std/io.zig+12
...@@ -36,6 +36,10 @@ pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking...@@ -36,6 +36,10 @@ pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking
3636
37fn getStdOutHandle() os.fd_t {37fn getStdOutHandle() os.fd_t {
38 if (builtin.os.tag == .windows) {38 if (builtin.os.tag == .windows) {
39 if (builtin.zig_backend == .stage2_x86_64) {
40 // TODO: this is just a temporary workaround until we advance x86 backend further along.
41 return os.windows.GetStdHandle(os.windows.STD_OUTPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE;
42 }
39 return os.windows.peb().ProcessParameters.hStdOutput;43 return os.windows.peb().ProcessParameters.hStdOutput;
40 }44 }
4145
...@@ -58,6 +62,10 @@ pub fn getStdOut() File {...@@ -58,6 +62,10 @@ pub fn getStdOut() File {
5862
59fn getStdErrHandle() os.fd_t {63fn getStdErrHandle() os.fd_t {
60 if (builtin.os.tag == .windows) {64 if (builtin.os.tag == .windows) {
65 if (builtin.zig_backend == .stage2_x86_64) {
66 // TODO: this is just a temporary workaround until we advance x86 backend further along.
67 return os.windows.GetStdHandle(os.windows.STD_ERROR_HANDLE) catch os.windows.INVALID_HANDLE_VALUE;
68 }
61 return os.windows.peb().ProcessParameters.hStdError;69 return os.windows.peb().ProcessParameters.hStdError;
62 }70 }
6371
...@@ -80,6 +88,10 @@ pub fn getStdErr() File {...@@ -80,6 +88,10 @@ pub fn getStdErr() File {
8088
81fn getStdInHandle() os.fd_t {89fn getStdInHandle() os.fd_t {
82 if (builtin.os.tag == .windows) {90 if (builtin.os.tag == .windows) {
91 if (builtin.zig_backend == .stage2_x86_64) {
92 // TODO: this is just a temporary workaround until we advance x86 backend further along.
93 return os.windows.GetStdHandle(os.windows.STD_INPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE;
94 }
83 return os.windows.peb().ProcessParameters.hStdInput;95 return os.windows.peb().ProcessParameters.hStdInput;
84 }96 }
8597
lib/std/os/windows/kernel32.zig+7-1
...@@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile(...@@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile(
348 in_out_lpOverlapped: ?*OVERLAPPED,348 in_out_lpOverlapped: ?*OVERLAPPED,
349) callconv(WINAPI) BOOL;349) callconv(WINAPI) BOOL;
350350
351pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: *OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(WINAPI) BOOL;351pub extern "kernel32" fn WriteFileEx(
352 hFile: HANDLE,
353 lpBuffer: [*]const u8,
354 nNumberOfBytesToWrite: DWORD,
355 lpOverlapped: *OVERLAPPED,
356 lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
357) callconv(WINAPI) BOOL;
352358
353pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE;359pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE;
354360
lib/std/start.zig+4
...@@ -36,6 +36,10 @@ comptime {...@@ -36,6 +36,10 @@ comptime {
36 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {36 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
37 @export(main2, .{ .name = "main" });37 @export(main2, .{ .name = "main" });
38 }38 }
39 } else if (builtin.os.tag == .windows) {
40 if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
41 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
42 }
39 } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {43 } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {
40 @export(wasiMain2, .{ .name = "_start" });44 @export(wasiMain2, .{ .name = "_start" });
41 } else {45 } else {
src/arch/x86_64/CodeGen.zig+372-229
...@@ -32,11 +32,6 @@ const abi = @import("abi.zig");...@@ -32,11 +32,6 @@ const abi = @import("abi.zig");
32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
35const callee_preserved_regs = abi.callee_preserved_regs;
36const caller_preserved_regs = abi.caller_preserved_regs;
37const c_abi_int_param_regs = abi.c_abi_int_param_regs;
38const c_abi_int_return_regs = abi.c_abi_int_return_regs;
39
40const Condition = bits.Condition;35const Condition = bits.Condition;
41const RegisterManager = abi.RegisterManager;36const RegisterManager = abi.RegisterManager;
42const RegisterLock = RegisterManager.RegisterLock;37const RegisterLock = RegisterManager.RegisterLock;
...@@ -137,6 +132,7 @@ pub const MCValue = union(enum) {...@@ -137,6 +132,7 @@ pub const MCValue = union(enum) {
137 /// If the type is a pointer, it means the pointer is referenced indirectly via GOT.132 /// If the type is a pointer, it means the pointer is referenced indirectly via GOT.
138 /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT.133 /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT.
139 got_load: u32,134 got_load: u32,
135 imports_load: u32,
140 /// The value is in memory referenced directly via symbol index.136 /// The value is in memory referenced directly via symbol index.
141 /// If the type is a pointer, it means the pointer is referenced directly via symbol index.137 /// If the type is a pointer, it means the pointer is referenced directly via symbol index.
142 /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED.138 /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED.
...@@ -156,6 +152,7 @@ pub const MCValue = union(enum) {...@@ -156,6 +152,7 @@ pub const MCValue = union(enum) {
156 .ptr_stack_offset,152 .ptr_stack_offset,
157 .direct_load,153 .direct_load,
158 .got_load,154 .got_load,
155 .imports_load,
159 => true,156 => true,
160 else => false,157 else => false,
161 };158 };
...@@ -203,6 +200,42 @@ const Branch = struct {...@@ -203,6 +200,42 @@ const Branch = struct {
203 self.inst_table.deinit(gpa);200 self.inst_table.deinit(gpa);
204 self.* = undefined;201 self.* = undefined;
205 }202 }
203
204 const FormatContext = struct {
205 insts: []const Air.Inst.Index,
206 mcvs: []const MCValue,
207 };
208
209 fn fmt(
210 ctx: FormatContext,
211 comptime unused_format_string: []const u8,
212 options: std.fmt.FormatOptions,
213 writer: anytype,
214 ) @TypeOf(writer).Error!void {
215 _ = options;
216 comptime assert(unused_format_string.len == 0);
217 try writer.writeAll("Branch {\n");
218 for (ctx.insts) |inst, i| {
219 const mcv = ctx.mcvs[i];
220 try writer.print(" %{d} => {}\n", .{ inst, mcv });
221 }
222 try writer.writeAll("}");
223 }
224
225 fn format(branch: Branch, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
226 _ = branch;
227 _ = unused_format_string;
228 _ = options;
229 _ = writer;
230 @compileError("do not format Branch directly; use ty.fmtDebug()");
231 }
232
233 fn fmtDebug(self: @This()) std.fmt.Formatter(fmt) {
234 return .{ .data = .{
235 .insts = self.inst_table.keys(),
236 .mcvs = self.inst_table.values(),
237 } };
238 }
206};239};
207240
208const StackAllocation = struct {241const StackAllocation = struct {
...@@ -235,7 +268,7 @@ const BigTomb = struct {...@@ -235,7 +268,7 @@ const BigTomb = struct {
235 fn finishAir(bt: *BigTomb, result: MCValue) void {268 fn finishAir(bt: *BigTomb, result: MCValue) void {
236 const is_used = !bt.function.liveness.isUnused(bt.inst);269 const is_used = !bt.function.liveness.isUnused(bt.inst);
237 if (is_used) {270 if (is_used) {
238 log.debug("%{d} => {}", .{ bt.inst, result });271 log.debug(" (saving %{d} => {})", .{ bt.inst, result });
239 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];272 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
240 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);273 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
241 }274 }
...@@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void {...@@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void {
406 });439 });
407440
408 if (self.ret_mcv == .stack_offset) {441 if (self.ret_mcv == .stack_offset) {
409 // The address where to store the return value for the caller is in `.rdi`442 // The address where to store the return value for the caller is in a
410 // register which the callee is free to clobber. Therefore, we purposely443 // register which the callee is free to clobber. Therefore, we purposely
411 // spill it to stack immediately.444 // spill it to stack immediately.
412 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8);445 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8);
413 self.next_stack_offset = stack_offset;446 self.next_stack_offset = stack_offset;
414 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);447 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
415448
416 try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{});449 const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0];
450 try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = ret_reg }, .{});
417 self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) };451 self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) };
418 log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset});452 log.debug("gen: spilling {s} to stack at offset {}", .{ @tagName(ret_reg), stack_offset });
419 }453 }
420454
421 _ = try self.addInst(.{455 _ = try self.addInst(.{
...@@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void {...@@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void {
446480
447 // Create list of registers to save in the prologue.481 // Create list of registers to save in the prologue.
448 // TODO handle register classes482 // TODO handle register classes
449 var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{};483 var reg_list = Mir.RegisterList{};
450 inline for (callee_preserved_regs) |reg| {484 const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*);
485 for (callee_preserved_regs) |reg| {
451 if (self.register_manager.isRegAllocated(reg)) {486 if (self.register_manager.isRegAllocated(reg)) {
452 reg_list.push(reg);487 reg_list.push(callee_preserved_regs, reg);
453 }488 }
454 }489 }
455 const saved_regs_stack_space: u32 = reg_list.count() * 8;490 const saved_regs_stack_space: u32 = reg_list.count() * 8;
...@@ -797,6 +832,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -797,6 +832,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
797fn processDeath(self: *Self, inst: Air.Inst.Index) void {832fn processDeath(self: *Self, inst: Air.Inst.Index) void {
798 const air_tags = self.air.instructions.items(.tag);833 const air_tags = self.air.instructions.items(.tag);
799 if (air_tags[inst] == .constant) return; // Constants are immortal.834 if (air_tags[inst] == .constant) return; // Constants are immortal.
835 log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } });
800 // When editing this function, note that the logic must synchronize with `reuseOperand`.836 // When editing this function, note that the logic must synchronize with `reuseOperand`.
801 const prev_value = self.getResolvedInstValue(inst);837 const prev_value = self.getResolvedInstValue(inst);
802 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];838 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
...@@ -2274,6 +2310,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2274,6 +2310,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2274 .memory,2310 .memory,
2275 .got_load,2311 .got_load,
2276 .direct_load,2312 .direct_load,
2313 .imports_load,
2277 => {2314 => {
2278 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);2315 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);
2279 },2316 },
...@@ -2618,6 +2655,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -2618,6 +2655,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
2618 .memory,2655 .memory,
2619 .got_load,2656 .got_load,
2620 .direct_load,2657 .direct_load,
2658 .imports_load,
2621 => {2659 => {
2622 const reg = try self.copyToTmpRegister(ptr_ty, ptr);2660 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
2623 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);2661 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
...@@ -2655,6 +2693,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2655,6 +2693,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2655 switch (ptr) {2693 switch (ptr) {
2656 .got_load,2694 .got_load,
2657 .direct_load,2695 .direct_load,
2696 .imports_load,
2658 => |sym_index| {2697 => |sym_index| {
2659 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));2698 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
2660 const mod = self.bin_file.options.module.?;2699 const mod = self.bin_file.options.module.?;
...@@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2666 const flags: u2 = switch (ptr) {2705 const flags: u2 = switch (ptr) {
2667 .got_load => 0b00,2706 .got_load => 0b00,
2668 .direct_load => 0b01,2707 .direct_load => 0b01,
2708 .imports_load => 0b10,
2669 else => unreachable,2709 else => unreachable,
2670 };2710 };
2671 _ = try self.addInst(.{2711 _ = try self.addInst(.{
...@@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2763 },2803 },
2764 .got_load,2804 .got_load,
2765 .direct_load,2805 .direct_load,
2806 .imports_load,
2766 .memory,2807 .memory,
2767 .stack_offset,2808 .stack_offset,
2768 => {2809 => {
...@@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2783 },2824 },
2784 .got_load,2825 .got_load,
2785 .direct_load,2826 .direct_load,
2827 .imports_load,
2786 .memory,2828 .memory,
2787 => {2829 => {
2788 const value_lock: ?RegisterLock = switch (value) {2830 const value_lock: ?RegisterLock = switch (value) {
...@@ -2854,6 +2896,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2854,6 +2896,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2854 },2896 },
2855 .got_load,2897 .got_load,
2856 .direct_load,2898 .direct_load,
2899 .imports_load,
2857 .memory,2900 .memory,
2858 => {2901 => {
2859 if (abi_size <= 8) {2902 if (abi_size <= 8) {
...@@ -3565,6 +3608,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3565,6 +3608,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3565 .memory,3608 .memory,
3566 .got_load,3609 .got_load,
3567 .direct_load,3610 .direct_load,
3611 .imports_load,
3568 .eflags,3612 .eflags,
3569 => {3613 => {
3570 assert(abi_size <= 8);3614 assert(abi_size <= 8);
...@@ -3650,7 +3694,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3650,7 +3694,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3650 => {3694 => {
3651 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});3695 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
3652 },3696 },
3653 .got_load, .direct_load => {3697 .got_load,
3698 .direct_load,
3699 .imports_load,
3700 => {
3654 return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{});3701 return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{});
3655 },3702 },
3656 .eflags => {3703 .eflags => {
...@@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3661 .memory => {3708 .memory => {
3662 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});3709 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});
3663 },3710 },
3664 .got_load, .direct_load => {3711 .got_load,
3712 .direct_load,
3713 .imports_load,
3714 => {
3665 return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{});3715 return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{});
3666 },3716 },
3667 }3717 }
...@@ -3729,7 +3779,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -3729,7 +3779,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
3729 .memory => {3779 .memory => {
3730 return self.fail("TODO implement x86 multiply source memory", .{});3780 return self.fail("TODO implement x86 multiply source memory", .{});
3731 },3781 },
3732 .got_load, .direct_load => {3782 .got_load,
3783 .direct_load,
3784 .imports_load,
3785 => {
3733 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});3786 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
3734 },3787 },
3735 .eflags => {3788 .eflags => {
...@@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
3773 .memory, .stack_offset => {3826 .memory, .stack_offset => {
3774 return self.fail("TODO implement x86 multiply source memory", .{});3827 return self.fail("TODO implement x86 multiply source memory", .{});
3775 },3828 },
3776 .got_load, .direct_load => {3829 .got_load,
3830 .direct_load,
3831 .imports_load,
3832 => {
3777 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});3833 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
3778 },3834 },
3779 .eflags => {3835 .eflags => {
...@@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
3784 .memory => {3840 .memory => {
3785 return self.fail("TODO implement x86 multiply destination memory", .{});3841 return self.fail("TODO implement x86 multiply destination memory", .{});
3786 },3842 },
3787 .got_load, .direct_load => {3843 .got_load,
3844 .direct_load,
3845 .imports_load,
3846 => {
3788 return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{});3847 return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{});
3789 },3848 },
3790 }3849 }
...@@ -3898,11 +3957,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3898,11 +3957,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
38983957
3899 try self.spillEflagsIfOccupied();3958 try self.spillEflagsIfOccupied();
39003959
3901 for (caller_preserved_regs) |reg| {3960 for (abi.getCallerPreservedRegs(self.target.*)) |reg| {
3902 try self.register_manager.getReg(reg, null);3961 try self.register_manager.getReg(reg, null);
3903 }3962 }
39043963
3905 const rdi_lock: ?RegisterLock = blk: {3964 const ret_reg_lock: ?RegisterLock = blk: {
3906 if (info.return_value == .stack_offset) {3965 if (info.return_value == .stack_offset) {
3907 const ret_ty = fn_ty.fnReturnType();3966 const ret_ty = fn_ty.fnReturnType();
3908 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));3967 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
...@@ -3910,17 +3969,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3910,17 +3969,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3910 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));3969 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));
3911 log.debug("airCall: return value on stack at offset {}", .{stack_offset});3970 log.debug("airCall: return value on stack at offset {}", .{stack_offset});
39123971
3913 try self.register_manager.getReg(.rdi, null);3972 const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0];
3914 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });3973 try self.register_manager.getReg(ret_reg, null);
3915 const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi);3974 try self.genSetReg(Type.usize, ret_reg, .{ .ptr_stack_offset = stack_offset });
3975 const ret_reg_lock = self.register_manager.lockRegAssumeUnused(ret_reg);
39163976
3917 info.return_value.stack_offset = stack_offset;3977 info.return_value.stack_offset = stack_offset;
39183978
3919 break :blk rdi_lock;3979 break :blk ret_reg_lock;
3920 }3980 }
3921 break :blk null;3981 break :blk null;
3922 };3982 };
3923 defer if (rdi_lock) |lock| self.register_manager.unlockReg(lock);3983 defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock);
39243984
3925 for (args) |arg, arg_i| {3985 for (args) |arg, arg_i| {
3926 const mc_arg = info.args[arg_i];3986 const mc_arg = info.args[arg_i];
...@@ -3948,6 +4008,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3948,6 +4008,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3948 .memory => unreachable,4008 .memory => unreachable,
3949 .got_load => unreachable,4009 .got_load => unreachable,
3950 .direct_load => unreachable,4010 .direct_load => unreachable,
4011 .imports_load => unreachable,
3951 .eflags => unreachable,4012 .eflags => unreachable,
3952 .register_overflow => unreachable,4013 .register_overflow => unreachable,
3953 }4014 }
...@@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3999 .data = undefined,4060 .data = undefined,
4000 });4061 });
4001 }4062 }
4002 } else if (self.bin_file.cast(link.File.Coff)) |_| {4063 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4003 if (self.air.value(callee)) |func_value| {4064 if (self.air.value(callee)) |func_value| {
4004 if (func_value.castTag(.function)) |func_payload| {4065 if (func_value.castTag(.function)) |func_payload| {
4005 const func = func_payload.data;4066 const func = func_payload.data;
...@@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
4015 }),4076 }),
4016 .data = undefined,4077 .data = undefined,
4017 });4078 });
4018 } else if (func_value.castTag(.extern_fn)) |_| {4079 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4019 return self.fail("TODO implement calling extern functions", .{});4080 const extern_fn = func_payload.data;
4081 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4082 if (extern_fn.lib_name) |lib_name| {
4083 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4084 decl_name,
4085 lib_name,
4086 });
4087 }
4088 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4089 try self.genSetReg(Type.initTag(.usize), .rax, .{
4090 .imports_load = sym_index,
4091 });
4092 _ = try self.addInst(.{
4093 .tag = .call,
4094 .ops = Mir.Inst.Ops.encode(.{
4095 .reg1 = .rax,
4096 .flags = 0b01,
4097 }),
4098 .data = undefined,
4099 });
4020 } else {4100 } else {
4021 return self.fail("TODO implement calling bitcasted functions", .{});4101 return self.fail("TODO implement calling bitcasted functions", .{});
4022 }4102 }
...@@ -4425,7 +4505,11 @@ fn genVarDbgInfo(...@@ -4425,7 +4505,11 @@ fn genVarDbgInfo(
4425 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;4505 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
4426 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);4506 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
4427 },4507 },
4428 .memory, .got_load, .direct_load => {4508 .memory,
4509 .got_load,
4510 .direct_load,
4511 .imports_load,
4512 => {
4429 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));4513 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
4430 const is_ptr = switch (tag) {4514 const is_ptr = switch (tag) {
4431 .dbg_var_ptr => true,4515 .dbg_var_ptr => true,
...@@ -4456,7 +4540,10 @@ fn genVarDbgInfo(...@@ -4456,7 +4540,10 @@ fn genVarDbgInfo(
4456 try dbg_info.append(DW.OP.deref);4540 try dbg_info.append(DW.OP.deref);
4457 }4541 }
4458 switch (mcv) {4542 switch (mcv) {
4459 .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr),4543 .got_load,
4544 .direct_load,
4545 .imports_load,
4546 => |index| try dw.addExprlocReloc(index, offset, is_ptr),
4460 else => {},4547 else => {},
4461 }4548 }
4462 },4549 },
...@@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
46264713
4627 // Revert to the previous register and stack allocation state.4714 // Revert to the previous register and stack allocation state.
46284715
4629 var saved_then_branch = self.branch_stack.pop();4716 var then_branch = self.branch_stack.pop();
4630 defer saved_then_branch.deinit(self.gpa);4717 defer then_branch.deinit(self.gpa);
46314718
4632 self.revertState(saved_state);4719 self.revertState(saved_state);
46334720
4634 try self.performReloc(reloc);4721 try self.performReloc(reloc);
46354722
4636 const else_branch = self.branch_stack.addOneAssumeCapacity();4723 try self.branch_stack.append(.{});
4637 else_branch.* = .{};4724 errdefer {
4725 _ = self.branch_stack.pop();
4726 }
46384727
4639 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);4728 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
4640 for (liveness_condbr.else_deaths) |operand| {4729 for (liveness_condbr.else_deaths) |operand| {
...@@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4642 }4731 }
4643 try self.genBody(else_body);4732 try self.genBody(else_body);
46444733
4734 var else_branch = self.branch_stack.pop();
4735 defer else_branch.deinit(self.gpa);
4736
4645 // At this point, each branch will possibly have conflicting values for where4737 // At this point, each branch will possibly have conflicting values for where
4646 // each instruction is stored. They agree, however, on which instructions are alive/dead.4738 // each instruction is stored. They agree, however, on which instructions are alive/dead.
4647 // We use the first ("then") branch as canonical, and here emit4739 // We use the first ("then") branch as canonical, and here emit
...@@ -4650,74 +4742,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4650,74 +4742,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4650 // that we can use all the code emitting abstractions. This is why at the bottom we4742 // that we can use all the code emitting abstractions. This is why at the bottom we
4651 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers4743 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
4652 // rather than assigning it.4744 // rather than assigning it.
4653 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];4745 log.debug("airCondBr: %{d}", .{inst});
4654 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());4746 log.debug("Upper branches:", .{});
46554747 for (self.branch_stack.items) |bs| {
4656 const else_slice = else_branch.inst_table.entries.slice();4748 log.debug("{}", .{bs.fmtDebug()});
4657 const else_keys = else_slice.items(.key);
4658 const else_values = else_slice.items(.value);
4659 for (else_keys) |else_key, else_idx| {
4660 const else_value = else_values[else_idx];
4661 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
4662 // The instruction's MCValue is overridden in both branches.
4663 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
4664 if (else_value == .dead) {
4665 assert(then_entry.value == .dead);
4666 continue;
4667 }
4668 break :blk then_entry.value;
4669 } else blk: {
4670 if (else_value == .dead)
4671 continue;
4672 // The instruction is only overridden in the else branch.
4673 var i: usize = self.branch_stack.items.len - 2;
4674 while (true) {
4675 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
4676 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
4677 assert(mcv != .dead);
4678 break :blk mcv;
4679 }
4680 }
4681 };
4682 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
4683 // TODO make sure the destination stack offset / register does not already have something
4684 // going on there.
4685 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
4686 // TODO track the new register / stack allocation
4687 }
4688 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
4689 const then_slice = saved_then_branch.inst_table.entries.slice();
4690 const then_keys = then_slice.items(.key);
4691 const then_values = then_slice.items(.value);
4692 for (then_keys) |then_key, then_idx| {
4693 const then_value = then_values[then_idx];
4694 // We already deleted the items from this table that matched the else_branch.
4695 // So these are all instructions that are only overridden in the then branch.
4696 parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
4697 log.debug("then_value = {}", .{then_value});
4698 if (then_value == .dead)
4699 continue;
4700 const parent_mcv = blk: {
4701 var i: usize = self.branch_stack.items.len - 2;
4702 while (true) {
4703 i -= 1;
4704 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
4705 assert(mcv != .dead);
4706 break :blk mcv;
4707 }
4708 }
4709 };
4710 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
4711 // TODO make sure the destination stack offset / register does not already have something
4712 // going on there.
4713 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
4714 // TODO track the new register / stack allocation
4715 }4749 }
47164750
4717 {4751 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
4718 var item = self.branch_stack.pop();4752 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
4719 item.deinit(self.gpa);4753
4720 }4754 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
4755 try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch);
47214756
4722 // We already took care of pl_op.operand earlier, so we're going4757 // We already took care of pl_op.operand earlier, so we're going
4723 // to pass .none here4758 // to pass .none here
...@@ -5102,6 +5137,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5102,6 +5137,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5102 }5137 }
5103 }5138 }
51045139
5140 var branch_stack = std.ArrayList(Branch).init(self.gpa);
5141 defer {
5142 for (branch_stack.items) |*bs| {
5143 bs.deinit(self.gpa);
5144 }
5145 branch_stack.deinit();
5146 }
5147 try branch_stack.ensureTotalCapacityPrecise(switch_br.data.cases_len + 1);
5148
5105 while (case_i < switch_br.data.cases_len) : (case_i += 1) {5149 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
5106 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);5150 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
5107 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);5151 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
...@@ -5131,10 +5175,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5131,10 +5175,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51315175
5132 try self.genBody(case_body);5176 try self.genBody(case_body);
51335177
5134 // Revert to the previous register and stack allocation state.5178 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5135 var saved_case_branch = self.branch_stack.pop();
5136 defer saved_case_branch.deinit(self.gpa);
51375179
5180 // Revert to the previous register and stack allocation state.
5138 self.revertState(saved_state);5181 self.revertState(saved_state);
51395182
5140 for (relocs) |reloc| {5183 for (relocs) |reloc| {
...@@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51445187
5145 if (switch_br.data.else_body_len > 0) {5188 if (switch_br.data.else_body_len > 0) {
5146 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];5189 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
5190
5191 // Capture the state of register and stack allocation state so that we can revert to it.
5192 const saved_state = try self.captureState();
5193
5147 try self.branch_stack.append(.{});5194 try self.branch_stack.append(.{});
5148 defer {5195 errdefer {
5149 var item = self.branch_stack.pop();5196 _ = self.branch_stack.pop();
5150 item.deinit(self.gpa);
5151 }5197 }
51525198
5153 const else_deaths = liveness.deaths.len - 1;5199 const else_deaths = liveness.deaths.len - 1;
...@@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51585204
5159 try self.genBody(else_body);5205 try self.genBody(else_body);
51605206
5161 // TODO consolidate returned MCValues between prongs and else branch like we do5207 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5162 // in airCondBr.5208
5209 // Revert to the previous register and stack allocation state.
5210 self.revertState(saved_state);
5211 }
5212
5213 // Consolidate returned MCValues between prongs and else branch like we do
5214 // in airCondBr.
5215 log.debug("airSwitch: %{d}", .{inst});
5216 log.debug("Upper branches:", .{});
5217 for (self.branch_stack.items) |bs| {
5218 log.debug("{}", .{bs.fmtDebug()});
5219 }
5220 for (branch_stack.items) |bs, i| {
5221 log.debug("Case-{d} branch: {}", .{ i, bs.fmtDebug() });
5222 }
5223
5224 // TODO: can we reduce the complexity of this algorithm?
5225 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
5226 var i: usize = branch_stack.items.len;
5227 while (i > 1) : (i -= 1) {
5228 const canon_branch = &branch_stack.items[i - 2];
5229 const target_branch = &branch_stack.items[i - 1];
5230 try self.canonicaliseBranches(parent_branch, canon_branch, target_branch);
5163 }5231 }
51645232
5165 // We already took care of pl_op.operand earlier, so we're going5233 // We already took care of pl_op.operand earlier, so we're going
...@@ -5167,6 +5235,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5167,6 +5235,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5167 return self.finishAir(inst, .unreach, .{ .none, .none, .none });5235 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
5168}5236}
51695237
5238fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Branch, target_branch: *Branch) !void {
5239 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count());
5240
5241 const target_slice = target_branch.inst_table.entries.slice();
5242 const target_keys = target_slice.items(.key);
5243 const target_values = target_slice.items(.value);
5244
5245 for (target_keys) |target_key, target_idx| {
5246 const target_value = target_values[target_idx];
5247 const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: {
5248 // The instruction's MCValue is overridden in both branches.
5249 parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value);
5250 if (target_value == .dead) {
5251 assert(canon_entry.value == .dead);
5252 continue;
5253 }
5254 break :blk canon_entry.value;
5255 } else blk: {
5256 if (target_value == .dead)
5257 continue;
5258 // The instruction is only overridden in the else branch.
5259 var i: usize = self.branch_stack.items.len - 1;
5260 while (true) {
5261 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
5262 if (self.branch_stack.items[i].inst_table.get(target_key)) |mcv| {
5263 assert(mcv != .dead);
5264 break :blk mcv;
5265 }
5266 }
5267 };
5268 log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv });
5269 // TODO make sure the destination stack offset / register does not already have something
5270 // going on there.
5271 try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value);
5272 // TODO track the new register / stack allocation
5273 }
5274 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count());
5275 const canon_slice = canon_branch.inst_table.entries.slice();
5276 const canon_keys = canon_slice.items(.key);
5277 const canon_values = canon_slice.items(.value);
5278 for (canon_keys) |canon_key, canon_idx| {
5279 const canon_value = canon_values[canon_idx];
5280 // We already deleted the items from this table that matched the target_branch.
5281 // So these are all instructions that are only overridden in the canon branch.
5282 parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value);
5283 log.debug("canon_value = {}", .{canon_value});
5284 if (canon_value == .dead)
5285 continue;
5286 const parent_mcv = blk: {
5287 var i: usize = self.branch_stack.items.len - 1;
5288 while (true) {
5289 i -= 1;
5290 if (self.branch_stack.items[i].inst_table.get(canon_key)) |mcv| {
5291 assert(mcv != .dead);
5292 break :blk mcv;
5293 }
5294 }
5295 };
5296 log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value });
5297 // TODO make sure the destination stack offset / register does not already have something
5298 // going on there.
5299 try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value);
5300 // TODO track the new register / stack allocation
5301 }
5302}
5303
5170fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {5304fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
5171 const next_inst = @intCast(u32, self.mir_instructions.len);5305 const next_inst = @intCast(u32, self.mir_instructions.len);
5172 switch (self.mir_instructions.items(.tag)[reloc]) {5306 switch (self.mir_instructions.items(.tag)[reloc]) {
...@@ -5196,7 +5330,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {...@@ -5196,7 +5330,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
5196 block_data.mcv = switch (operand_mcv) {5330 block_data.mcv = switch (operand_mcv) {
5197 .none, .dead, .unreach => unreachable,5331 .none, .dead, .unreach => unreachable,
5198 .register, .stack_offset, .memory => operand_mcv,5332 .register, .stack_offset, .memory => operand_mcv,
5199 .eflags, .immediate => blk: {5333 .eflags, .immediate, .ptr_stack_offset => blk: {
5200 const new_mcv = try self.allocRegOrMem(block, true);5334 const new_mcv = try self.allocRegOrMem(block, true);
5201 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);5335 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
5202 break :blk new_mcv;5336 break :blk new_mcv;
...@@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
5456 .memory,5590 .memory,
5457 .direct_load,5591 .direct_load,
5458 .got_load,5592 .got_load,
5593 .imports_load,
5459 => {5594 => {
5460 if (abi_size <= 8) {5595 if (abi_size <= 8) {
5461 const reg = try self.copyToTmpRegister(ty, mcv);5596 const reg = try self.copyToTmpRegister(ty, mcv);
...@@ -5703,6 +5838,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -5703,6 +5838,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
5703 .memory,5838 .memory,
5704 .got_load,5839 .got_load,
5705 .direct_load,5840 .direct_load,
5841 .imports_load,
5706 => {5842 => {
5707 if (abi_size <= 8) {5843 if (abi_size <= 8) {
5708 const reg = try self.copyToTmpRegister(ty, mcv);5844 const reg = try self.copyToTmpRegister(ty, mcv);
...@@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct {...@@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct {
5796 dest_stack_base: ?Register = null,5932 dest_stack_base: ?Register = null,
5797};5933};
57985934
5799/// Spills .rax and .rcx.
5800fn genInlineMemcpy(5935fn genInlineMemcpy(
5801 self: *Self,5936 self: *Self,
5802 dst_ptr: MCValue,5937 dst_ptr: MCValue,
...@@ -5804,15 +5939,6 @@ fn genInlineMemcpy(...@@ -5804,15 +5939,6 @@ fn genInlineMemcpy(
5804 len: MCValue,5939 len: MCValue,
5805 opts: InlineMemcpyOpts,5940 opts: InlineMemcpyOpts,
5806) InnerError!void {5941) InnerError!void {
5807 // TODO preserve contents of .rax and .rcx if not free, and then restore
5808 try self.register_manager.getReg(.rax, null);
5809 try self.register_manager.getReg(.rcx, null);
5810
5811 const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx });
5812 defer for (reg_locks) |lock| {
5813 self.register_manager.unlockReg(lock);
5814 };
5815
5816 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|5942 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
5817 self.register_manager.lockReg(reg)5943 self.register_manager.lockReg(reg)
5818 else5944 else
...@@ -5825,11 +5951,18 @@ fn genInlineMemcpy(...@@ -5825,11 +5951,18 @@ fn genInlineMemcpy(
5825 null;5951 null;
5826 defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);5952 defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);
58275953
5828 const dst_addr_reg = try self.register_manager.allocReg(null, gp);5954 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
5955 const dst_addr_reg = regs[0];
5956 const src_addr_reg = regs[1];
5957 const index_reg = regs[2].to64();
5958 const count_reg = regs[3].to64();
5959 const tmp_reg = regs[4].to8();
5960
5829 switch (dst_ptr) {5961 switch (dst_ptr) {
5830 .memory,5962 .memory,
5831 .got_load,5963 .got_load,
5832 .direct_load,5964 .direct_load,
5965 .imports_load,
5833 => {5966 => {
5834 try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr);5967 try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr);
5835 },5968 },
...@@ -5857,14 +5990,12 @@ fn genInlineMemcpy(...@@ -5857,14 +5990,12 @@ fn genInlineMemcpy(
5857 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});5990 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
5858 },5991 },
5859 }5992 }
5860 const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);
5861 defer self.register_manager.unlockReg(dst_addr_reg_lock);
58625993
5863 const src_addr_reg = try self.register_manager.allocReg(null, gp);
5864 switch (src_ptr) {5994 switch (src_ptr) {
5865 .memory,5995 .memory,
5866 .got_load,5996 .got_load,
5867 .direct_load,5997 .direct_load,
5998 .imports_load,
5868 => {5999 => {
5869 try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr);6000 try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr);
5870 },6001 },
...@@ -5892,26 +6023,13 @@ fn genInlineMemcpy(...@@ -5892,26 +6023,13 @@ fn genInlineMemcpy(
5892 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});6023 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
5893 },6024 },
5894 }6025 }
5895 const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg);
5896 defer self.register_manager.unlockReg(src_addr_reg_lock);
5897
5898 const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
5899 const count_reg = regs[0].to64();
5900 const tmp_reg = regs[1].to8();
59016026
5902 try self.genSetReg(Type.usize, count_reg, len);6027 try self.genSetReg(Type.usize, count_reg, len);
59036028
5904 // mov rcx, 06029 // mov index_reg, 0
5905 _ = try self.addInst(.{
5906 .tag = .mov,
5907 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),
5908 .data = .{ .imm = 0 },
5909 });
5910
5911 // mov rax, 0
5912 _ = try self.addInst(.{6030 _ = try self.addInst(.{
5913 .tag = .mov,6031 .tag = .mov,
5914 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6032 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
5915 .data = .{ .imm = 0 },6033 .data = .{ .imm = 0 },
5916 });6034 });
59176035
...@@ -5933,37 +6051,30 @@ fn genInlineMemcpy(...@@ -5933,37 +6051,30 @@ fn genInlineMemcpy(
5933 } },6051 } },
5934 });6052 });
59356053
5936 // mov tmp, [addr + rcx]6054 // mov tmp, [addr + index_reg]
5937 _ = try self.addInst(.{6055 _ = try self.addInst(.{
5938 .tag = .mov_scale_src,6056 .tag = .mov_scale_src,
5939 .ops = Mir.Inst.Ops.encode(.{6057 .ops = Mir.Inst.Ops.encode(.{
5940 .reg1 = tmp_reg.to8(),6058 .reg1 = tmp_reg.to8(),
5941 .reg2 = src_addr_reg,6059 .reg2 = src_addr_reg,
5942 }),6060 }),
5943 .data = .{ .imm = 0 },6061 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },
5944 });6062 });
59456063
5946 // mov [stack_offset + rax], tmp6064 // mov [stack_offset + index_reg], tmp
5947 _ = try self.addInst(.{6065 _ = try self.addInst(.{
5948 .tag = .mov_scale_dst,6066 .tag = .mov_scale_dst,
5949 .ops = Mir.Inst.Ops.encode(.{6067 .ops = Mir.Inst.Ops.encode(.{
5950 .reg1 = dst_addr_reg,6068 .reg1 = dst_addr_reg,
5951 .reg2 = tmp_reg.to8(),6069 .reg2 = tmp_reg.to8(),
5952 }),6070 }),
5953 .data = .{ .imm = 0 },6071 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },
5954 });
5955
5956 // add rcx, 1
5957 _ = try self.addInst(.{
5958 .tag = .add,
5959 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),
5960 .data = .{ .imm = 1 },
5961 });6072 });
59626073
5963 // add rax, 16074 // add index_reg, 1
5964 _ = try self.addInst(.{6075 _ = try self.addInst(.{
5965 .tag = .add,6076 .tag = .add,
5966 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6077 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
5967 .data = .{ .imm = 1 },6078 .data = .{ .imm = 1 },
5968 });6079 });
59696080
...@@ -5985,7 +6096,6 @@ fn genInlineMemcpy(...@@ -5985,7 +6096,6 @@ fn genInlineMemcpy(
5985 try self.performReloc(loop_reloc);6096 try self.performReloc(loop_reloc);
5986}6097}
59876098
5988/// Spills .rax register.
5989fn genInlineMemset(6099fn genInlineMemset(
5990 self: *Self,6100 self: *Self,
5991 dst_ptr: MCValue,6101 dst_ptr: MCValue,
...@@ -5993,16 +6103,27 @@ fn genInlineMemset(...@@ -5993,16 +6103,27 @@ fn genInlineMemset(
5993 len: MCValue,6103 len: MCValue,
5994 opts: InlineMemcpyOpts,6104 opts: InlineMemcpyOpts,
5995) InnerError!void {6105) InnerError!void {
5996 // TODO preserve contents of .rax and then restore6106 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
5997 try self.register_manager.getReg(.rax, null);6107 self.register_manager.lockReg(reg)
5998 const rax_lock = self.register_manager.lockRegAssumeUnused(.rax);6108 else
5999 defer self.register_manager.unlockReg(rax_lock);6109 null;
6110 defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg);
6111
6112 const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg|
6113 self.register_manager.lockReg(reg)
6114 else
6115 null;
6116 defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);
6117
6118 const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
6119 const addr_reg = regs[0];
6120 const index_reg = regs[1].to64();
60006121
6001 const addr_reg = try self.register_manager.allocReg(null, gp);
6002 switch (dst_ptr) {6122 switch (dst_ptr) {
6003 .memory,6123 .memory,
6004 .got_load,6124 .got_load,
6005 .direct_load,6125 .direct_load,
6126 .imports_load,
6006 => {6127 => {
6007 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr);6128 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr);
6008 },6129 },
...@@ -6030,17 +6151,15 @@ fn genInlineMemset(...@@ -6030,17 +6151,15 @@ fn genInlineMemset(
6030 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});6151 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
6031 },6152 },
6032 }6153 }
6033 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
6034 defer self.register_manager.unlockReg(addr_reg_lock);
60356154
6036 try self.genSetReg(Type.usize, .rax, len);6155 try self.genSetReg(Type.usize, index_reg, len);
6037 try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });6156 try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 });
60386157
6039 // loop:6158 // loop:
6040 // cmp rax, -16159 // cmp index_reg, -1
6041 const loop_start = try self.addInst(.{6160 const loop_start = try self.addInst(.{
6042 .tag = .cmp,6161 .tag = .cmp,
6043 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6162 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
6044 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },6163 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },
6045 });6164 });
60466165
...@@ -6059,24 +6178,20 @@ fn genInlineMemset(...@@ -6059,24 +6178,20 @@ fn genInlineMemset(
6059 if (x > math.maxInt(i32)) {6178 if (x > math.maxInt(i32)) {
6060 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});6179 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});
6061 }6180 }
6062 // mov byte ptr [rbp + rax + stack_offset], imm6181 // mov byte ptr [rbp + index_reg + stack_offset], imm
6063 const payload = try self.addExtra(Mir.ImmPair{
6064 .dest_off = 0,
6065 .operand = @truncate(u32, x),
6066 });
6067 _ = try self.addInst(.{6182 _ = try self.addInst(.{
6068 .tag = .mov_mem_index_imm,6183 .tag = .mov_mem_index_imm,
6069 .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }),6184 .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }),
6070 .data = .{ .payload = payload },6185 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) },
6071 });6186 });
6072 },6187 },
6073 else => return self.fail("TODO inline memset for value of type {}", .{value}),6188 else => return self.fail("TODO inline memset for value of type {}", .{value}),
6074 }6189 }
60756190
6076 // sub rax, 16191 // sub index_reg, 1
6077 _ = try self.addInst(.{6192 _ = try self.addInst(.{
6078 .tag = .sub,6193 .tag = .sub,
6079 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),6194 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
6080 .data = .{ .imm = 1 },6195 .data = .{ .imm = 1 },
6081 });6196 });
60826197
...@@ -6243,6 +6358,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6243,6 +6358,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6243 },6358 },
6244 .direct_load,6359 .direct_load,
6245 .got_load,6360 .got_load,
6361 .imports_load,
6246 => {6362 => {
6247 switch (ty.zigTypeTag()) {6363 switch (ty.zigTypeTag()) {
6248 .Float => {6364 .Float => {
...@@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {...@@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
6637 // TODO Is this the only condition for pointer dereference for memcpy?6753 // TODO Is this the only condition for pointer dereference for memcpy?
6638 const src: MCValue = blk: {6754 const src: MCValue = blk: {
6639 switch (src_ptr) {6755 switch (src_ptr) {
6640 .got_load, .direct_load, .memory => {6756 .got_load,
6757 .direct_load,
6758 .imports_load,
6759 .memory,
6760 => {
6641 const reg = try self.register_manager.allocReg(null, gp);6761 const reg = try self.register_manager.allocReg(null, gp);
6642 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);6762 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);
6643 _ = try self.addInst(.{6763 _ = try self.addInst(.{
...@@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6901 } else if (self.bin_file.cast(link.File.MachO)) |_| {7021 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6902 return MCValue{ .direct_load = local_sym_index };7022 return MCValue{ .direct_load = local_sym_index };
6903 } else if (self.bin_file.cast(link.File.Coff)) |_| {7023 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6904 return self.fail("TODO lower unnamed const in COFF", .{});7024 return MCValue{ .direct_load = local_sym_index };
6905 } else if (self.bin_file.cast(link.File.Plan9)) |_| {7025 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
6906 return self.fail("TODO lower unnamed const in Plan9", .{});7026 return self.fail("TODO lower unnamed const in Plan9", .{});
6907 } else {7027 } else {
...@@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7066 result.stack_align = 1;7186 result.stack_align = 1;
7067 return result;7187 return result;
7068 },7188 },
7069 .Unspecified, .C => {7189 .C => {
7070 // Return values7190 // Return values
7071 if (ret_ty.zigTypeTag() == .NoReturn) {7191 if (ret_ty.zigTypeTag() == .NoReturn) {
7072 result.return_value = .{ .unreach = {} };7192 result.return_value = .{ .unreach = {} };
7073 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {7193 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7194 // TODO: is this even possible for C calling convention?
7074 result.return_value = .{ .none = {} };7195 result.return_value = .{ .none = {} };
7075 } else {7196 } else {
7076 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));7197 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
...@@ -7078,84 +7199,106 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7078,84 +7199,106 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7078 assert(ret_ty.isError());7199 assert(ret_ty.isError());
7079 result.return_value = .{ .immediate = 0 };7200 result.return_value = .{ .immediate = 0 };
7080 } else if (ret_ty_size <= 8) {7201 } else if (ret_ty_size <= 8) {
7081 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);7202 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
7082 result.return_value = .{ .register = aliased_reg };7203 result.return_value = .{ .register = aliased_reg };
7083 } else {7204 } else {
7084 // We simply make the return MCValue a stack offset. However, the actual value7205 // TODO: return argument cell should go first
7085 // for the offset will be populated later. We will also push the stack offset
7086 // value into .rdi register when we resolve the offset.
7087 result.return_value = .{ .stack_offset = 0 };7206 result.return_value = .{ .stack_offset = 0 };
7088 }7207 }
7089 }7208 }
70907209
7091 // Input params7210 // Input params
7092 // First, split into args that can be passed via registers.7211 var next_stack_offset: u32 = switch (result.return_value) {
7093 // This will make it easier to then push the rest of args in reverse7212 .stack_offset => |off| @intCast(u32, off),
7094 // order on the stack.7213 else => 0,
7095 var next_int_reg: usize = 0;7214 };
7096 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);7215
7097 defer by_reg.deinit();7216 for (param_types) |ty, i| {
70987217 assert(ty.hasRuntimeBits());
7099 // If we want debug output, we store all args on stack for better liveness of args7218
7100 // in debugging contexts such as previewing the args in the debugger anywhere in7219 const classes: []const abi.Class = switch (self.target.os.tag) {
7101 // the procedure. Passing the args via registers can lead to reusing the register7220 .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)},
7102 // for local ops thus clobbering the input arg forever.7221 else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*), .none),
7103 // This of course excludes C ABI calls.7222 };
7104 const omit_args_in_registers = blk: {7223 if (classes.len > 1) {
7105 if (cc == .C) break :blk false;7224 return self.fail("TODO handle multiple classes per type", .{});
7106 switch (self.bin_file.options.optimize_mode) {7225 }
7107 .Debug => break :blk true,7226 switch (classes[0]) {
7108 else => break :blk false,7227 .integer => blk: {
7228 if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough
7229 result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };
7230 continue;
7231 },
7232 .memory => {}, // fallthrough
7233 else => |class| return self.fail("TODO handle calling convention class {s}", .{
7234 @tagName(class),
7235 }),
7236 }
7237
7238 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7239 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7240 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7241 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7242 next_stack_offset = offset;
7243 }
7244
7245 // Align the stack to 16bytes before allocating shadow stack space (if any).
7246 const aligned_next_stack_offset = mem.alignForwardGeneric(u32, next_stack_offset, 16);
7247 const padding = aligned_next_stack_offset - next_stack_offset;
7248 if (padding > 0) {
7249 for (result.args) |*arg| {
7250 if (arg.isRegister()) continue;
7251 arg.stack_offset += @intCast(i32, padding);
7109 }7252 }
7253 }
7254
7255 const shadow_stack_space: u32 = switch (self.target.os.tag) {
7256 .windows => @intCast(u32, 4 * @sizeOf(u64)),
7257 else => 0,
7110 };7258 };
7111 if (!omit_args_in_registers) {7259
7112 for (param_types) |ty, i| {7260 // alignment padding | args ... | shadow stack space (if any) | ret addr | $rbp |
7113 if (!ty.hasRuntimeBits()) continue;7261 result.stack_byte_count = aligned_next_stack_offset + shadow_stack_space;
7114 const param_size = @intCast(u32, ty.abiSize(self.target.*));7262 result.stack_align = 16;
7115 // For simplicity of codegen, slices and other types are always pushed onto the stack.7263 },
7116 // TODO: look into optimizing this by passing things as registers sometimes,7264 .Unspecified => {
7117 // such as ptr and len of slices as separate registers.7265 // Return values
7118 // TODO: also we need to honor the C ABI for relevant types rather than passing on7266 if (ret_ty.zigTypeTag() == .NoReturn) {
7119 // the stack here.7267 result.return_value = .{ .unreach = {} };
7120 const pass_in_reg = switch (ty.zigTypeTag()) {7268 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7121 .Bool => true,7269 result.return_value = .{ .none = {} };
7122 .Int, .Enum => param_size <= 8,7270 } else {
7123 .Pointer => ty.ptrSize() != .Slice,7271 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
7124 .Optional => ty.isPtrLikeOptional(),7272 if (ret_ty_size == 0) {
7125 else => false,7273 assert(ret_ty.isError());
7126 };7274 result.return_value = .{ .immediate = 0 };
7127 if (pass_in_reg) {7275 } else if (ret_ty_size <= 8) {
7128 if (next_int_reg >= c_abi_int_param_regs.len) break;7276 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
7129 try by_reg.putNoClobber(i, next_int_reg);7277 result.return_value = .{ .register = aliased_reg };
7130 next_int_reg += 1;7278 } else {
7131 }7279 // We simply make the return MCValue a stack offset. However, the actual value
7280 // for the offset will be populated later. We will also push the stack offset
7281 // value into an appropriate register when we resolve the offset.
7282 result.return_value = .{ .stack_offset = 0 };
7132 }7283 }
7133 }7284 }
71347285
7286 // Input params
7135 var next_stack_offset: u32 = switch (result.return_value) {7287 var next_stack_offset: u32 = switch (result.return_value) {
7136 .stack_offset => |off| @intCast(u32, off),7288 .stack_offset => |off| @intCast(u32, off),
7137 else => 0,7289 else => 0,
7138 };7290 };
7139 var count: usize = param_types.len;7291
7140 while (count > 0) : (count -= 1) {7292 for (param_types) |ty, i| {
7141 const i = count - 1;
7142 const ty = param_types[i];
7143 if (!ty.hasRuntimeBits()) {7293 if (!ty.hasRuntimeBits()) {
7144 assert(cc != .C);
7145 result.args[i] = .{ .none = {} };7294 result.args[i] = .{ .none = {} };
7146 continue;7295 continue;
7147 }7296 }
7148 const param_size = @intCast(u32, ty.abiSize(self.target.*));7297 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7149 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));7298 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7150 if (by_reg.get(i)) |int_reg| {7299 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7151 const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size);7300 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7152 result.args[i] = .{ .register = aliased_reg };7301 next_stack_offset = offset;
7153 next_int_reg += 1;
7154 } else {
7155 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7156 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7157 next_stack_offset = offset;
7158 }
7159 }7302 }
71607303
7161 result.stack_align = 16;7304 result.stack_align = 16;
src/arch/x86_64/Emit.zig+57-36
...@@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro...@@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro
283 const ops = emit.mir.instructions.items(.ops)[inst].decode();283 const ops = emit.mir.instructions.items(.ops)[inst].decode();
284 const payload = emit.mir.instructions.items(.data)[inst].payload;284 const payload = emit.mir.instructions.items(.data)[inst].payload;
285 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;285 const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data;
286 const reg_list = Mir.RegisterList(Register, &abi.callee_preserved_regs).fromInt(save_reg_list.register_list);
287 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);286 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
288 inline for (abi.callee_preserved_regs) |reg| {287 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
289 if (reg_list.isSet(reg)) {288 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
289 for (callee_preserved_regs) |reg| {
290 if (reg_list.isSet(callee_preserved_regs, reg)) {
290 switch (tag) {291 switch (tag) {
291 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{292 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
292 .disp = @bitCast(u32, disp),293 .disp = @bitCast(u32, disp),
...@@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 {...@@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 {
614fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {615fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
615 const ops = emit.mir.instructions.items(.ops)[inst].decode();616 const ops = emit.mir.instructions.items(.ops)[inst].decode();
616 const scale = ops.flags;617 const scale = ops.flags;
617 const imm = emit.mir.instructions.items(.data)[inst].imm;618 const payload = emit.mir.instructions.items(.data)[inst].payload;
618 // OP reg1, [reg2 + scale*rcx + imm32]619 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
620 // OP reg1, [reg2 + scale*index + imm32]
619 const scale_index = ScaleIndex{621 const scale_index = ScaleIndex{
620 .scale = scale,622 .scale = scale,
621 .index = .rcx,623 .index = index_reg_disp.index,
622 };624 };
623 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{625 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
624 .disp = imm,626 .disp = index_reg_disp.disp,
625 .base = ops.reg2,627 .base = ops.reg2,
626 .scale_index = scale_index,628 .scale_index = scale_index,
627 }), emit.code);629 }), emit.code);
...@@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
630fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {632fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
631 const ops = emit.mir.instructions.items(.ops)[inst].decode();633 const ops = emit.mir.instructions.items(.ops)[inst].decode();
632 const scale = ops.flags;634 const scale = ops.flags;
633 const imm = emit.mir.instructions.items(.data)[inst].imm;635 const payload = emit.mir.instructions.items(.data)[inst].payload;
636 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
634 const scale_index = ScaleIndex{637 const scale_index = ScaleIndex{
635 .scale = scale,638 .scale = scale,
636 .index = .rax,639 .index = index_reg_disp.index,
637 };640 };
638 if (ops.reg2 == .none) {641 assert(ops.reg2 != .none);
639 // OP qword ptr [reg1 + scale*rax + 0], imm32642 // OP [reg1 + scale*index + imm32], reg2
640 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{
641 .disp = 0,
642 .base = ops.reg1,
643 .scale_index = scale_index,
644 }), imm, emit.code);
645 }
646 // OP [reg1 + scale*rax + imm32], reg2
647 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{643 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
648 .disp = imm,644 .disp = index_reg_disp.disp,
649 .base = ops.reg1,645 .base = ops.reg1,
650 .scale_index = scale_index,646 .scale_index = scale_index,
651 }), ops.reg2, emit.code);647 }), ops.reg2, emit.code);
...@@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
655 const ops = emit.mir.instructions.items(.ops)[inst].decode();651 const ops = emit.mir.instructions.items(.ops)[inst].decode();
656 const scale = ops.flags;652 const scale = ops.flags;
657 const payload = emit.mir.instructions.items(.data)[inst].payload;653 const payload = emit.mir.instructions.items(.data)[inst].payload;
658 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;654 const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode();
659 const scale_index = ScaleIndex{655 const scale_index = ScaleIndex{
660 .scale = scale,656 .scale = scale,
661 .index = .rax,657 .index = index_reg_disp_imm.index,
662 };658 };
663 // OP qword ptr [reg1 + scale*rax + imm32], imm32659 // OP qword ptr [reg1 + scale*index + imm32], imm32
664 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{660 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{
665 .disp = imm_pair.dest_off,661 .disp = index_reg_disp_imm.disp,
666 .base = ops.reg1,662 .base = ops.reg1,
667 .scale_index = scale_index,663 .scale_index = scale_index,
668 }), imm_pair.operand, emit.code);664 }), index_reg_disp_imm.imm, emit.code);
669}665}
670666
671fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {667fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
672 const ops = emit.mir.instructions.items(.ops)[inst].decode();668 const ops = emit.mir.instructions.items(.ops)[inst].decode();
673 assert(ops.reg2 == .none);669 assert(ops.reg2 == .none);
674 const payload = emit.mir.instructions.items(.data)[inst].payload;670 const payload = emit.mir.instructions.items(.data)[inst].payload;
675 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;671 const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode();
676 const ptr_size: Memory.PtrSize = switch (ops.flags) {672 const ptr_size: Memory.PtrSize = switch (ops.flags) {
677 0b00 => .byte_ptr,673 0b00 => .byte_ptr,
678 0b01 => .word_ptr,674 0b01 => .word_ptr,
...@@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v...@@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v
681 };677 };
682 const scale_index = ScaleIndex{678 const scale_index = ScaleIndex{
683 .scale = 0,679 .scale = 0,
684 .index = .rax,680 .index = index_reg_disp_imm.index,
685 };681 };
686 // OP ptr [reg1 + rax*1 + imm32], imm32682 // OP ptr [reg1 + index + imm32], imm32
687 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{683 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
688 .disp = imm_pair.dest_off,684 .disp = index_reg_disp_imm.disp,
689 .base = ops.reg1,685 .base = ops.reg1,
690 .scale_index = scale_index,686 .scale_index = scale_index,
691 }), imm_pair.operand, emit.code);687 }), index_reg_disp_imm.imm, emit.code);
692}688}
693689
694fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {690fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
...@@ -956,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -956,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
956 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);952 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);
957 },953 },
958 0b10 => {954 0b10 => {
959 // lea reg, [rbp + rcx + imm32]955 // lea reg, [rbp + index + imm32]
960 const imm = emit.mir.instructions.items(.data)[inst].imm;956 const payload = emit.mir.instructions.items(.data)[inst].payload;
957 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
961 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;958 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
962 const scale_index = ScaleIndex{959 const scale_index = ScaleIndex{
963 .scale = 0,960 .scale = 0,
964 .index = .rcx,961 .index = index_reg_disp.index,
965 };962 };
966 return lowerToRmEnc(963 return lowerToRmEnc(
967 .lea,964 .lea,
968 ops.reg1,965 ops.reg1,
969 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{966 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
970 .disp = imm,967 .disp = index_reg_disp.disp,
971 .base = src_reg,968 .base = src_reg,
972 .scale_index = scale_index,969 .scale_index = scale_index,
973 }),970 }),
...@@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
985 const relocation = emit.mir.instructions.items(.data)[inst].relocation;982 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
986983
987 switch (ops.flags) {984 switch (ops.flags) {
988 0b00, 0b01 => {},985 0b00, 0b01, 0b10 => {},
989 else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}),986 else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}),
990 }987 }
991988
992 // lea reg1, [rip + reloc]989 // lea reg1, [rip + reloc]
...@@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1024 .@"type" = switch (ops.flags) {1021 .@"type" = switch (ops.flags) {
1025 0b00 => .got,1022 0b00 => .got,
1026 0b01 => .direct,1023 0b01 => .direct,
1024 0b10 => .imports,
1027 else => unreachable,1025 else => unreachable,
1028 },1026 },
1029 .target = .{ .sym_index = relocation.sym_index, .file = null },1027 .target = .{ .sym_index = relocation.sym_index, .file = null },
...@@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1031 .addend = 0,1029 .addend = 0,
1032 .pcrel = true,1030 .pcrel = true,
1033 .length = 2,1031 .length = 2,
1034 .prev_vaddr = atom.getSymbol(coff_file).value,
1035 });1032 });
1036 } else {1033 } else {
1037 return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{});1034 return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{});
...@@ -1157,6 +1154,17 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1157,6 +1154,17 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1157 .length = 2,1154 .length = 2,
1158 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1155 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1159 });1156 });
1157 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1158 // Add relocation to the decl.
1159 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;
1160 try atom.addRelocation(coff_file, .{
1161 .@"type" = .direct,
1162 .target = .{ .sym_index = relocation.sym_index, .file = null },
1163 .offset = offset,
1164 .addend = 0,
1165 .pcrel = true,
1166 .length = 2,
1167 });
1160 } else {1168 } else {
1161 return emit.fail("TODO implement call_extern for linking backends different than MachO", .{});1169 return emit.fail("TODO implement call_extern for linking backends different than MachO", .{});
1162 }1170 }
...@@ -2241,6 +2249,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st...@@ -2241,6 +2249,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st
2241 encoder.rex(.{2249 encoder.rex(.{
2242 .w = wide,2250 .w = wide,
2243 .b = base.isExtended(),2251 .b = base.isExtended(),
2252 .x = if (mem_op.scale_index) |si| si.index.isExtended() else false,
2244 });2253 });
2245 }2254 }
2246 opc.encode(encoder);2255 opc.encode(encoder);
...@@ -2346,10 +2355,12 @@ fn lowerToMiXEnc(...@@ -2346,10 +2355,12 @@ fn lowerToMiXEnc(
2346 encoder.rex(.{2355 encoder.rex(.{
2347 .w = dst_mem.ptr_size == .qword_ptr,2356 .w = dst_mem.ptr_size == .qword_ptr,
2348 .b = base.isExtended(),2357 .b = base.isExtended(),
2358 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2349 });2359 });
2350 } else {2360 } else {
2351 encoder.rex(.{2361 encoder.rex(.{
2352 .w = dst_mem.ptr_size == .qword_ptr,2362 .w = dst_mem.ptr_size == .qword_ptr,
2363 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2353 });2364 });
2354 }2365 }
2355 opc.encode(encoder);2366 opc.encode(encoder);
...@@ -2401,11 +2412,13 @@ fn lowerToRmEnc(...@@ -2401,11 +2412,13 @@ fn lowerToRmEnc(
2401 .w = setRexWRegister(reg),2412 .w = setRexWRegister(reg),
2402 .r = reg.isExtended(),2413 .r = reg.isExtended(),
2403 .b = base.isExtended(),2414 .b = base.isExtended(),
2415 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2404 });2416 });
2405 } else {2417 } else {
2406 encoder.rex(.{2418 encoder.rex(.{
2407 .w = setRexWRegister(reg),2419 .w = setRexWRegister(reg),
2408 .r = reg.isExtended(),2420 .r = reg.isExtended(),
2421 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2409 });2422 });
2410 }2423 }
2411 opc.encode(encoder);2424 opc.encode(encoder);
...@@ -2446,11 +2459,13 @@ fn lowerToMrEnc(...@@ -2446,11 +2459,13 @@ fn lowerToMrEnc(
2446 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),2459 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2447 .r = reg.isExtended(),2460 .r = reg.isExtended(),
2448 .b = base.isExtended(),2461 .b = base.isExtended(),
2462 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2449 });2463 });
2450 } else {2464 } else {
2451 encoder.rex(.{2465 encoder.rex(.{
2452 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),2466 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
2453 .r = reg.isExtended(),2467 .r = reg.isExtended(),
2468 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2454 });2469 });
2455 }2470 }
2456 opc.encode(encoder);2471 opc.encode(encoder);
...@@ -2490,11 +2505,13 @@ fn lowerToRmiEnc(...@@ -2490,11 +2505,13 @@ fn lowerToRmiEnc(
2490 .w = setRexWRegister(reg),2505 .w = setRexWRegister(reg),
2491 .r = reg.isExtended(),2506 .r = reg.isExtended(),
2492 .b = base.isExtended(),2507 .b = base.isExtended(),
2508 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2493 });2509 });
2494 } else {2510 } else {
2495 encoder.rex(.{2511 encoder.rex(.{
2496 .w = setRexWRegister(reg),2512 .w = setRexWRegister(reg),
2497 .r = reg.isExtended(),2513 .r = reg.isExtended(),
2514 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2498 });2515 });
2499 }2516 }
2500 opc.encode(encoder);2517 opc.encode(encoder);
...@@ -2531,10 +2548,12 @@ fn lowerToVmEnc(...@@ -2531,10 +2548,12 @@ fn lowerToVmEnc(
2531 vex.rex(.{2548 vex.rex(.{
2532 .r = reg.isExtended(),2549 .r = reg.isExtended(),
2533 .b = base.isExtended(),2550 .b = base.isExtended(),
2551 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2534 });2552 });
2535 } else {2553 } else {
2536 vex.rex(.{2554 vex.rex(.{
2537 .r = reg.isExtended(),2555 .r = reg.isExtended(),
2556 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
2538 });2557 });
2539 }2558 }
2540 encoder.vex(enc.prefix);2559 encoder.vex(enc.prefix);
...@@ -2571,10 +2590,12 @@ fn lowerToMvEnc(...@@ -2571,10 +2590,12 @@ fn lowerToMvEnc(
2571 vex.rex(.{2590 vex.rex(.{
2572 .r = reg.isExtended(),2591 .r = reg.isExtended(),
2573 .b = base.isExtended(),2592 .b = base.isExtended(),
2593 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2574 });2594 });
2575 } else {2595 } else {
2576 vex.rex(.{2596 vex.rex(.{
2577 .r = reg.isExtended(),2597 .r = reg.isExtended(),
2598 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
2578 });2599 });
2579 }2600 }
2580 encoder.vex(enc.prefix);2601 encoder.vex(enc.prefix);
src/arch/x86_64/Mir.zig+107-42
...@@ -44,25 +44,28 @@ pub const Inst = struct {...@@ -44,25 +44,28 @@ pub const Inst = struct {
44 /// 0b01 word ptr [reg1 + imm32], imm1644 /// 0b01 word ptr [reg1 + imm32], imm16
45 /// 0b10 dword ptr [reg1 + imm32], imm3245 /// 0b10 dword ptr [reg1 + imm32], imm32
46 /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)46 /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)
47 /// Notes:
48 /// * Uses `ImmPair` as payload
47 adc_mem_imm,49 adc_mem_imm,
4850
49 /// form: reg1, [reg2 + scale*rcx + imm32]51 /// form: reg1, [reg2 + scale*index + imm32]
50 /// ops flags scale52 /// ops flags scale
51 /// 0b00 153 /// 0b00 1
52 /// 0b01 254 /// 0b01 2
53 /// 0b10 455 /// 0b10 4
54 /// 0b11 856 /// 0b11 8
57 /// Notes:
58 /// * Uses `IndexRegisterDisp` as payload
55 adc_scale_src,59 adc_scale_src,
5660
57 /// form: [reg1 + scale*rax + imm32], reg261 /// form: [reg1 + scale*index + imm32], reg2
58 /// form: [reg1 + scale*rax + 0], imm32
59 /// ops flags scale62 /// ops flags scale
60 /// 0b00 163 /// 0b00 1
61 /// 0b01 264 /// 0b01 2
62 /// 0b10 465 /// 0b10 4
63 /// 0b11 866 /// 0b11 8
64 /// Notes:67 /// Notes:
65 /// * If reg2 is `none` then it means Data field `imm` is used as the immediate.68 /// * Uses `IndexRegisterDisp` payload.
66 adc_scale_dst,69 adc_scale_dst,
6770
68 /// form: [reg1 + scale*rax + imm32], imm3271 /// form: [reg1 + scale*rax + imm32], imm32
...@@ -72,14 +75,16 @@ pub const Inst = struct {...@@ -72,14 +75,16 @@ pub const Inst = struct {
72 /// 0b10 475 /// 0b10 4
73 /// 0b11 876 /// 0b11 8
74 /// Notes:77 /// Notes:
75 /// * Data field `payload` points at `ImmPair`.78 /// * Uses `IndexRegisterDispImm` payload.
76 adc_scale_imm,79 adc_scale_imm,
7780
78 /// ops flags: form:81 /// ops flags: form:
79 /// 0b00 byte ptr [reg1 + rax + imm32], imm882 /// 0b00 byte ptr [reg1 + index + imm32], imm8
80 /// 0b01 word ptr [reg1 + rax + imm32], imm1683 /// 0b01 word ptr [reg1 + index + imm32], imm16
81 /// 0b10 dword ptr [reg1 + rax + imm32], imm3284 /// 0b10 dword ptr [reg1 + index + imm32], imm32
82 /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64)85 /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64)
86 /// Notes:
87 /// * Uses `IndexRegisterDispImm` payload.
83 adc_mem_index_imm,88 adc_mem_index_imm,
8489
85 // The following instructions all have the same encoding as `adc`.90 // The following instructions all have the same encoding as `adc`.
...@@ -174,12 +179,15 @@ pub const Inst = struct {...@@ -174,12 +179,15 @@ pub const Inst = struct {
174 /// 0b00 reg1, [reg2 + imm32]179 /// 0b00 reg1, [reg2 + imm32]
175 /// 0b00 reg1, [ds:imm32]180 /// 0b00 reg1, [ds:imm32]
176 /// 0b01 reg1, [rip + imm32]181 /// 0b01 reg1, [rip + imm32]
177 /// 0b10 reg1, [reg2 + rcx + imm32]182 /// 0b10 reg1, [reg2 + index + imm32]
183 /// Notes:
184 /// * 0b10 uses `IndexRegisterDisp` payload
178 lea,185 lea,
179186
180 /// ops flags: form:187 /// ops flags: form:
181 /// 0b00 reg1, [rip + reloc] // via GOT PIC188 /// 0b00 reg1, [rip + reloc] // via GOT PIC
182 /// 0b01 reg1, [rip + reloc] // direct load PIC189 /// 0b01 reg1, [rip + reloc] // direct load PIC
190 /// 0b10 reg1, [rip + reloc] // via imports table PIC
183 /// Notes:191 /// Notes:
184 /// * `Data` contains `relocation`192 /// * `Data` contains `relocation`
185 lea_pic,193 lea_pic,
...@@ -460,46 +468,103 @@ pub const Inst = struct {...@@ -460,46 +468,103 @@ pub const Inst = struct {
460 }468 }
461};469};
462470
463pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type {471pub const IndexRegisterDisp = struct {
464 assert(registers.len <= @bitSizeOf(u32));472 /// Index register to use with SIB-based encoding
465 return struct {473 index: u32,
466 bitset: RegBitSet = RegBitSet.initEmpty(),
467474
468 const RegBitSet = IntegerBitSet(registers.len);475 /// Displacement value
469 const Self = @This();476 disp: u32,
470477
471 fn getIndexForReg(reg: Reg) RegBitSet.MaskInt {478 pub fn encode(index: Register, disp: u32) IndexRegisterDisp {
472 inline for (registers) |cpreg, i| {479 return .{
473 if (reg.id() == cpreg.id()) return i;480 .index = @enumToInt(index),
474 }481 .disp = disp,
475 unreachable; // register not in input register list!482 };
476 }483 }
477484
478 pub fn push(self: *Self, reg: Reg) void {485 pub fn decode(this: IndexRegisterDisp) struct {
479 const index = getIndexForReg(reg);486 index: Register,
480 self.bitset.set(index);487 disp: u32,
481 }488 } {
489 return .{
490 .index = @intToEnum(Register, this.index),
491 .disp = this.disp,
492 };
493 }
494};
482495
483 pub fn isSet(self: Self, reg: Reg) bool {496/// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list
484 const index = getIndexForReg(reg);497/// instead of having two structs, one a superset of the other one?
485 return self.bitset.isSet(index);498pub const IndexRegisterDispImm = struct {
486 }499 /// Index register to use with SIB-based encoding
500 index: u32,
487501
488 pub fn asInt(self: Self) u32 {502 /// Displacement value
489 return self.bitset.mask;503 disp: u32,
490 }
491504
492 pub fn fromInt(mask: u32) Self {505 /// Immediate
493 return .{506 imm: u32,
494 .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) },507
495 };508 pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm {
496 }509 return .{
510 .index = @enumToInt(index),
511 .disp = disp,
512 .imm = imm,
513 };
514 }
497515
498 pub fn count(self: Self) u32 {516 pub fn decode(this: IndexRegisterDispImm) struct {
499 return @intCast(u32, self.bitset.count());517 index: Register,
518 disp: u32,
519 imm: u32,
520 } {
521 return .{
522 .index = @intToEnum(Register, this.index),
523 .disp = this.disp,
524 .imm = this.imm,
525 };
526 }
527};
528
529/// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers
530/// in a compact manner.
531pub const RegisterList = struct {
532 bitset: BitSet = BitSet.initEmpty(),
533
534 const BitSet = IntegerBitSet(@ctz(@as(u32, 0)));
535 const Self = @This();
536
537 fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt {
538 for (registers) |cpreg, i| {
539 if (reg.id() == cpreg.id()) return @intCast(u32, i);
500 }540 }
501 };541 unreachable; // register not in input register list!
502}542 }
543
544 pub fn push(self: *Self, registers: []const Register, reg: Register) void {
545 const index = getIndexForReg(registers, reg);
546 self.bitset.set(index);
547 }
548
549 pub fn isSet(self: Self, registers: []const Register, reg: Register) bool {
550 const index = getIndexForReg(registers, reg);
551 return self.bitset.isSet(index);
552 }
553
554 pub fn asInt(self: Self) u32 {
555 return self.bitset.mask;
556 }
557
558 pub fn fromInt(mask: u32) Self {
559 return .{
560 .bitset = BitSet{ .mask = @intCast(BitSet.MaskInt, mask) },
561 };
562 }
563
564 pub fn count(self: Self) u32 {
565 return @intCast(u32, self.bitset.count());
566 }
567};
503568
504pub const SaveRegisterList = struct {569pub const SaveRegisterList = struct {
505 /// Use `RegisterList` to populate.570 /// Use `RegisterList` to populate.
src/arch/x86_64/abi.zig+60-14
...@@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {...@@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
392 }392 }
393}393}
394394
395/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them395pub const SysV = struct {
396/// for anything else but stack offset tracking therefore we exclude them from this set.396 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
397pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };397 /// for anything else but stack offset tracking therefore we exclude them from this set.
398/// These registers need to be preserved (saved on the stack) and restored by the caller before398 pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
399/// the caller relinquishes control to a subroutine via call instruction (or similar).399 /// These registers need to be preserved (saved on the stack) and restored by the caller before
400/// In other words, these registers are free to use by the callee.400 /// the caller relinquishes control to a subroutine via call instruction (or similar).
401pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };401 /// In other words, these registers are free to use by the callee.
402 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
402403
403pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };404 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
404pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };405 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
406};
407
408pub const Win64 = struct {
409 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
410 /// for anything else but stack offset tracking therefore we exclude them from this set.
411 pub const callee_preserved_regs = [_]Register{ .rbx, .rsi, .rdi, .r12, .r13, .r14, .r15 };
412 /// These registers need to be preserved (saved on the stack) and restored by the caller before
413 /// the caller relinquishes control to a subroutine via call instruction (or similar).
414 /// In other words, these registers are free to use by the callee.
415 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 };
405416
417 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
418 pub const c_abi_int_return_regs = [_]Register{.rax};
419};
420
421pub fn getCalleePreservedRegs(target: Target) []const Register {
422 return switch (target.os.tag) {
423 .windows => &Win64.callee_preserved_regs,
424 else => &SysV.callee_preserved_regs,
425 };
426}
427
428pub fn getCallerPreservedRegs(target: Target) []const Register {
429 return switch (target.os.tag) {
430 .windows => &Win64.caller_preserved_regs,
431 else => &SysV.caller_preserved_regs,
432 };
433}
434
435pub fn getCAbiIntParamRegs(target: Target) []const Register {
436 return switch (target.os.tag) {
437 .windows => &Win64.c_abi_int_param_regs,
438 else => &SysV.c_abi_int_param_regs,
439 };
440}
441
442pub fn getCAbiIntReturnRegs(target: Target) []const Register {
443 return switch (target.os.tag) {
444 .windows => &Win64.c_abi_int_return_regs,
445 else => &SysV.c_abi_int_return_regs,
446 };
447}
448
449const gp_regs = [_]Register{
450 .rbx, .r12, .r13, .r14, .r15, .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11,
451};
406const sse_avx_regs = [_]Register{452const sse_avx_regs = [_]Register{
407 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,453 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
408 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,454 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
409};455};
410const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs;456const allocatable_regs = gp_regs ++ sse_avx_regs;
411pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);457pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
412458
413// Register classes459// Register classes
414const RegisterBitSet = RegisterManager.RegisterBitSet;460const RegisterBitSet = RegisterManager.RegisterBitSet;
...@@ -417,15 +463,15 @@ pub const RegisterClass = struct {...@@ -417,15 +463,15 @@ pub const RegisterClass = struct {
417 var set = RegisterBitSet.initEmpty();463 var set = RegisterBitSet.initEmpty();
418 set.setRangeValue(.{464 set.setRangeValue(.{
419 .start = 0,465 .start = 0,
420 .end = caller_preserved_regs.len + callee_preserved_regs.len,466 .end = gp_regs.len,
421 }, true);467 }, true);
422 break :blk set;468 break :blk set;
423 };469 };
424 pub const sse: RegisterBitSet = blk: {470 pub const sse: RegisterBitSet = blk: {
425 var set = RegisterBitSet.initEmpty();471 var set = RegisterBitSet.initEmpty();
426 set.setRangeValue(.{472 set.setRangeValue(.{
427 .start = caller_preserved_regs.len + callee_preserved_regs.len,473 .start = gp_regs.len,
428 .end = allocatable_registers.len,474 .end = allocatable_regs.len,
429 }, true);475 }, true);
430 break :blk set;476 break :blk set;
431 };477 };
src/link.zig+1-1
...@@ -476,7 +476,7 @@ pub const File = struct {...@@ -476,7 +476,7 @@ pub const File = struct {
476 log.debug("getGlobalSymbol '{s}'", .{name});476 log.debug("getGlobalSymbol '{s}'", .{name});
477 switch (base.tag) {477 switch (base.tag) {
478 // zig fmt: off478 // zig fmt: off
479 .coff => unreachable,479 .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name),
480 .elf => unreachable,480 .elf => unreachable,
481 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name),481 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name),
482 .plan9 => unreachable,482 .plan9 => unreachable,
src/link/Coff.zig+674-221
...@@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig");...@@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig");
30pub const base_tag: link.File.Tag = .coff;30pub const base_tag: link.File.Tag = .coff;
3131
32const msdos_stub = @embedFile("msdos-stub.bin");32const msdos_stub = @embedFile("msdos-stub.bin");
33const N_DATA_DIRS: u5 = 16;
3433
35/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.34/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
36llvm_object: ?*LlvmObject = null,35llvm_object: ?*LlvmObject = null,
...@@ -44,24 +43,33 @@ page_size: u32,...@@ -44,24 +43,33 @@ page_size: u32,
44objects: std.ArrayListUnmanaged(Object) = .{},43objects: std.ArrayListUnmanaged(Object) = .{},
4544
46sections: std.MultiArrayList(Section) = .{},45sections: std.MultiArrayList(Section) = .{},
47data_directories: [N_DATA_DIRS]coff.ImageDataDirectory,46data_directories: [coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory,
4847
49text_section_index: ?u16 = null,48text_section_index: ?u16 = null,
50got_section_index: ?u16 = null,49got_section_index: ?u16 = null,
51rdata_section_index: ?u16 = null,50rdata_section_index: ?u16 = null,
52data_section_index: ?u16 = null,51data_section_index: ?u16 = null,
53reloc_section_index: ?u16 = null,52reloc_section_index: ?u16 = null,
53idata_section_index: ?u16 = null,
5454
55locals: std.ArrayListUnmanaged(coff.Symbol) = .{},55locals: std.ArrayListUnmanaged(coff.Symbol) = .{},
56globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},56globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
57resolver: std.StringHashMapUnmanaged(u32) = .{},
58unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{},
5759
58locals_free_list: std.ArrayListUnmanaged(u32) = .{},60locals_free_list: std.ArrayListUnmanaged(u32) = .{},
61globals_free_list: std.ArrayListUnmanaged(u32) = .{},
5962
60strtab: StringTable(.strtab) = .{},63strtab: StringTable(.strtab) = .{},
61strtab_offset: ?u32 = null,64strtab_offset: ?u32 = null,
6265
63got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},66got_entries: std.ArrayListUnmanaged(Entry) = .{},
64got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},67got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
68got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
69
70imports: std.ArrayListUnmanaged(Entry) = .{},
71imports_free_list: std.ArrayListUnmanaged(u32) = .{},
72imports_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
6573
66/// Virtual address of the entry point procedure relative to image base.74/// Virtual address of the entry point procedure relative to image base.
67entry_addr: ?u32 = null,75entry_addr: ?u32 = null,
...@@ -109,17 +117,33 @@ relocs: RelocTable = .{},...@@ -109,17 +117,33 @@ relocs: RelocTable = .{},
109/// this will be a table indexed by index into the list of Atoms.117/// this will be a table indexed by index into the list of Atoms.
110base_relocs: BaseRelocationTable = .{},118base_relocs: BaseRelocationTable = .{},
111119
120const Entry = struct {
121 target: SymbolWithLoc,
122 // Index into the synthetic symbol table (i.e., file == null).
123 sym_index: u32,
124};
125
112pub const Reloc = struct {126pub const Reloc = struct {
113 @"type": enum {127 @"type": enum {
114 got,128 got,
115 direct,129 direct,
130 imports,
116 },131 },
117 target: SymbolWithLoc,132 target: SymbolWithLoc,
118 offset: u32,133 offset: u32,
119 addend: u32,134 addend: u32,
120 pcrel: bool,135 pcrel: bool,
121 length: u2,136 length: u2,
122 prev_vaddr: u32,137 dirty: bool = true,
138
139 /// Returns an Atom which is the target node of this relocation edge (if any).
140 fn getTargetAtom(self: Reloc, coff_file: *Coff) ?*Atom {
141 switch (self.@"type") {
142 .got => return coff_file.getGotAtomForSymbol(self.target),
143 .direct => return coff_file.getAtomForSymbol(self.target),
144 .imports => return coff_file.getImportAtomForSymbol(self.target),
145 }
146 }
123};147};
124148
125const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc));149const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc));
...@@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct {...@@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct {
180204
181 // null means it's a synthetic global or Zig source.205 // null means it's a synthetic global or Zig source.
182 file: ?u32 = null,206 file: ?u32 = null,
207
208 pub fn eql(this: SymbolWithLoc, other: SymbolWithLoc) bool {
209 if (this.file == null and other.file == null) {
210 return this.sym_index == other.sym_index;
211 }
212 if (this.file != null and other.file != null) {
213 return this.sym_index == other.sym_index and this.file.? == other.file.?;
214 }
215 return false;
216 }
183};217};
184218
185/// When allocating, the ideal_capacity is calculated by219/// When allocating, the ideal_capacity is calculated by
...@@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {...@@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
234 },268 },
235 .ptr_width = ptr_width,269 .ptr_width = ptr_width,
236 .page_size = page_size,270 .page_size = page_size,
237 .data_directories = comptime mem.zeroes([N_DATA_DIRS]coff.ImageDataDirectory),271 .data_directories = comptime mem.zeroes([coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory),
238 };272 };
239273
240 const use_llvm = build_options.have_llvm and options.use_llvm;274 const use_llvm = build_options.have_llvm and options.use_llvm;
...@@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void {...@@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void {
269303
270 self.locals.deinit(gpa);304 self.locals.deinit(gpa);
271 self.globals.deinit(gpa);305 self.globals.deinit(gpa);
306
307 {
308 var it = self.resolver.keyIterator();
309 while (it.next()) |key_ptr| {
310 gpa.free(key_ptr.*);
311 }
312 self.resolver.deinit(gpa);
313 }
314
315 self.unresolved.deinit(gpa);
272 self.locals_free_list.deinit(gpa);316 self.locals_free_list.deinit(gpa);
273 self.strtab.deinit(gpa);317 self.strtab.deinit(gpa);
274 self.got_entries.deinit(gpa);318 self.got_entries.deinit(gpa);
275 self.got_entries_free_list.deinit(gpa);319 self.got_entries_free_list.deinit(gpa);
320 self.got_entries_table.deinit(gpa);
321 self.imports.deinit(gpa);
322 self.imports_free_list.deinit(gpa);
323 self.imports_table.deinit(gpa);
276 self.decls.deinit(gpa);324 self.decls.deinit(gpa);
277 self.atom_by_index_table.deinit(gpa);325 self.atom_by_index_table.deinit(gpa);
278326
...@@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void {...@@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void {
305 assert(self.llvm_object == null);353 assert(self.llvm_object == null);
306 const gpa = self.base.allocator;354 const gpa = self.base.allocator;
307355
356 try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32));
357 self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32));
358
359 // Index 0 is always a null symbol.
360 try self.locals.append(gpa, .{
361 .name = [_]u8{0} ** 8,
362 .value = 0,
363 .section_number = .UNDEFINED,
364 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
365 .storage_class = .NULL,
366 .number_of_aux_symbols = 0,
367 });
368
308 if (self.text_section_index == null) {369 if (self.text_section_index == null) {
309 self.text_section_index = @intCast(u16, self.sections.slice().len);
310 const file_size = @intCast(u32, self.base.options.program_code_size_hint);370 const file_size = @intCast(u32, self.base.options.program_code_size_hint);
311 const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers371 self.text_section_index = try self.allocateSection(".text", file_size, .{
312 log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size });372 .CNT_CODE = 1,
313 var header = coff.SectionHeader{373 .MEM_EXECUTE = 1,
314 .name = undefined,374 .MEM_READ = 1,
315 .virtual_size = file_size,375 });
316 .virtual_address = off,
317 .size_of_raw_data = file_size,
318 .pointer_to_raw_data = off,
319 .pointer_to_relocations = 0,
320 .pointer_to_linenumbers = 0,
321 .number_of_relocations = 0,
322 .number_of_linenumbers = 0,
323 .flags = .{
324 .CNT_CODE = 1,
325 .MEM_EXECUTE = 1,
326 .MEM_READ = 1,
327 },
328 };
329 try self.setSectionName(&header, ".text");
330 try self.sections.append(gpa, .{ .header = header });
331 }376 }
332377
333 if (self.got_section_index == null) {378 if (self.got_section_index == null) {
334 self.got_section_index = @intCast(u16, self.sections.slice().len);
335 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();379 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
336 const off = self.findFreeSpace(file_size, self.page_size);380 self.got_section_index = try self.allocateSection(".got", file_size, .{
337 log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size });381 .CNT_INITIALIZED_DATA = 1,
338 var header = coff.SectionHeader{382 .MEM_READ = 1,
339 .name = undefined,383 });
340 .virtual_size = file_size,
341 .virtual_address = off,
342 .size_of_raw_data = file_size,
343 .pointer_to_raw_data = off,
344 .pointer_to_relocations = 0,
345 .pointer_to_linenumbers = 0,
346 .number_of_relocations = 0,
347 .number_of_linenumbers = 0,
348 .flags = .{
349 .CNT_INITIALIZED_DATA = 1,
350 .MEM_READ = 1,
351 },
352 };
353 try self.setSectionName(&header, ".got");
354 try self.sections.append(gpa, .{ .header = header });
355 }384 }
356385
357 if (self.rdata_section_index == null) {386 if (self.rdata_section_index == null) {
358 self.rdata_section_index = @intCast(u16, self.sections.slice().len);387 const file_size: u32 = self.page_size;
359 const file_size: u32 = 1024;388 self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{
360 const off = self.findFreeSpace(file_size, self.page_size);389 .CNT_INITIALIZED_DATA = 1,
361 log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size });390 .MEM_READ = 1,
362 var header = coff.SectionHeader{391 });
363 .name = undefined,
364 .virtual_size = file_size,
365 .virtual_address = off,
366 .size_of_raw_data = file_size,
367 .pointer_to_raw_data = off,
368 .pointer_to_relocations = 0,
369 .pointer_to_linenumbers = 0,
370 .number_of_relocations = 0,
371 .number_of_linenumbers = 0,
372 .flags = .{
373 .CNT_INITIALIZED_DATA = 1,
374 .MEM_READ = 1,
375 },
376 };
377 try self.setSectionName(&header, ".rdata");
378 try self.sections.append(gpa, .{ .header = header });
379 }392 }
380393
381 if (self.data_section_index == null) {394 if (self.data_section_index == null) {
382 self.data_section_index = @intCast(u16, self.sections.slice().len);395 const file_size: u32 = self.page_size;
383 const file_size: u32 = 1024;396 self.data_section_index = try self.allocateSection(".data", file_size, .{
384 const off = self.findFreeSpace(file_size, self.page_size);397 .CNT_INITIALIZED_DATA = 1,
385 log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size });398 .MEM_READ = 1,
386 var header = coff.SectionHeader{399 .MEM_WRITE = 1,
387 .name = undefined,400 });
388 .virtual_size = file_size,401 }
389 .virtual_address = off,402
390 .size_of_raw_data = file_size,403 if (self.idata_section_index == null) {
391 .pointer_to_raw_data = off,404 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
392 .pointer_to_relocations = 0,405 self.idata_section_index = try self.allocateSection(".idata", file_size, .{
393 .pointer_to_linenumbers = 0,406 .CNT_INITIALIZED_DATA = 1,
394 .number_of_relocations = 0,407 .MEM_READ = 1,
395 .number_of_linenumbers = 0,408 });
396 .flags = .{
397 .CNT_INITIALIZED_DATA = 1,
398 .MEM_READ = 1,
399 .MEM_WRITE = 1,
400 },
401 };
402 try self.setSectionName(&header, ".data");
403 try self.sections.append(gpa, .{ .header = header });
404 }409 }
405410
406 if (self.reloc_section_index == null) {411 if (self.reloc_section_index == null) {
407 self.reloc_section_index = @intCast(u16, self.sections.slice().len);
408 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation);412 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation);
409 const off = self.findFreeSpace(file_size, self.page_size);413 self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{
410 log.debug("found .reloc free space 0x{x} to 0x{x}", .{ off, off + file_size });414 .CNT_INITIALIZED_DATA = 1,
411 var header = coff.SectionHeader{415 .MEM_DISCARDABLE = 1,
412 .name = undefined,416 .MEM_READ = 1,
413 .virtual_size = file_size,417 });
414 .virtual_address = off,
415 .size_of_raw_data = file_size,
416 .pointer_to_raw_data = off,
417 .pointer_to_relocations = 0,
418 .pointer_to_linenumbers = 0,
419 .number_of_relocations = 0,
420 .number_of_linenumbers = 0,
421 .flags = .{
422 .CNT_INITIALIZED_DATA = 1,
423 .MEM_PURGEABLE = 1,
424 .MEM_READ = 1,
425 },
426 };
427 try self.setSectionName(&header, ".reloc");
428 try self.sections.append(gpa, .{ .header = header });
429 }418 }
430419
431 if (self.strtab_offset == null) {420 if (self.strtab_offset == null) {
432 try self.strtab.buffer.append(gpa, 0);421 const file_size = @intCast(u32, self.strtab.len());
433 self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1);422 self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here
434 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() });423 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size });
435 }424 }
436425
437 // Index 0 is always a null symbol.
438 try self.locals.append(gpa, .{
439 .name = [_]u8{0} ** 8,
440 .value = 0,
441 .section_number = @intToEnum(coff.SectionNumber, 0),
442 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
443 .storage_class = .NULL,
444 .number_of_aux_symbols = 0,
445 });
446
447 {426 {
448 // We need to find out what the max file offset is according to section headers.427 // We need to find out what the max file offset is according to section headers.
449 // Otherwise, we may end up with an COFF binary with file size not matching the final section's428 // Otherwise, we may end up with an COFF binary with file size not matching the final section's
...@@ -459,6 +438,72 @@ fn populateMissingMetadata(self: *Coff) !void {...@@ -459,6 +438,72 @@ fn populateMissingMetadata(self: *Coff) !void {
459 }438 }
460}439}
461440
441fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.SectionHeaderFlags) !u16 {
442 const index = @intCast(u16, self.sections.slice().len);
443 const off = self.findFreeSpace(size, default_file_alignment);
444 // Memory is always allocated in sequence
445 // TODO: investigate if we can allocate .text last; this way it would never need to grow in memory!
446 const vaddr = blk: {
447 if (index == 0) break :blk self.page_size;
448 const prev_header = self.sections.items(.header)[index - 1];
449 break :blk mem.alignForwardGeneric(u32, prev_header.virtual_address + prev_header.virtual_size, self.page_size);
450 };
451 // We commit more memory than needed upfront so that we don't have to reallocate too soon.
452 const memsz = mem.alignForwardGeneric(u32, size, self.page_size) * 100;
453 log.debug("found {s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
454 name,
455 off,
456 off + size,
457 vaddr,
458 vaddr + size,
459 });
460 var header = coff.SectionHeader{
461 .name = undefined,
462 .virtual_size = memsz,
463 .virtual_address = vaddr,
464 .size_of_raw_data = size,
465 .pointer_to_raw_data = off,
466 .pointer_to_relocations = 0,
467 .pointer_to_linenumbers = 0,
468 .number_of_relocations = 0,
469 .number_of_linenumbers = 0,
470 .flags = flags,
471 };
472 try self.setSectionName(&header, name);
473 try self.sections.append(self.base.allocator, .{ .header = header });
474 return index;
475}
476
477fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
478 const header = &self.sections.items(.header)[sect_id];
479 const increased_size = padToIdeal(needed_size);
480 const old_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, header.virtual_size, self.page_size);
481 const new_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, increased_size, self.page_size);
482 const diff = new_aligned_end - old_aligned_end;
483 log.debug("growing {s} in virtual memory by {x}", .{ self.getSectionName(header), diff });
484
485 // TODO: enforce order by increasing VM addresses in self.sections container.
486 // This is required by the loader anyhow as far as I can tell.
487 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
488 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id + 1 + next_sect_id];
489 next_header.virtual_address += diff;
490
491 if (maybe_last_atom.*) |last_atom| {
492 var atom = last_atom;
493 while (true) {
494 const sym = atom.getSymbolPtr(self);
495 sym.value += diff;
496
497 if (atom.prev) |prev| {
498 atom = prev;
499 } else break;
500 }
501 }
502 }
503
504 header.virtual_size = increased_size;
505}
506
462pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {507pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
463 if (self.llvm_object) |_| return;508 if (self.llvm_object) |_| return;
464 const decl = self.base.options.module.?.declPtr(decl_index);509 const decl = self.base.options.module.?.declPtr(decl_index);
...@@ -542,16 +587,33 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -542,16 +587,33 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
542 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);587 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
543 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;588 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
544 if (needed_size > sect_capacity) {589 if (needed_size > sect_capacity) {
545 @panic("TODO move section");590 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);
591 const current_size = if (maybe_last_atom.*) |last_atom| blk: {
592 const sym = last_atom.getSymbol(self);
593 break :blk (sym.value + last_atom.size) - header.virtual_address;
594 } else 0;
595 log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset });
596 const amt = try self.base.file.?.copyRangeAll(
597 header.pointer_to_raw_data,
598 self.base.file.?,
599 new_offset,
600 current_size,
601 );
602 if (amt != current_size) return error.InputOutput;
603 header.pointer_to_raw_data = new_offset;
604 }
605
606 const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);
607 if (needed_size > sect_vm_capacity) {
608 try self.growSectionVM(sect_id, needed_size);
609 self.markRelocsDirtyByAddress(header.virtual_address + needed_size);
546 }610 }
611
612 header.virtual_size = @maximum(header.virtual_size, needed_size);
613 header.size_of_raw_data = needed_size;
547 maybe_last_atom.* = atom;614 maybe_last_atom.* = atom;
548 // header.virtual_size = needed_size;
549 // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment);
550 }615 }
551616
552 // if (header.getAlignment().? < alignment) {
553 // header.setAlignment(alignment);
554 // }
555 atom.size = new_atom_size;617 atom.size = new_atom_size;
556 atom.alignment = alignment;618 atom.alignment = alignment;
557619
...@@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 {...@@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 {
596 self.locals.items[index] = .{658 self.locals.items[index] = .{
597 .name = [_]u8{0} ** 8,659 .name = [_]u8{0} ** 8,
598 .value = 0,660 .value = 0,
599 .section_number = @intToEnum(coff.SectionNumber, 0),661 .section_number = .UNDEFINED,
600 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },662 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
601 .storage_class = .NULL,663 .storage_class = .NULL,
602 .number_of_aux_symbols = 0,664 .number_of_aux_symbols = 0,
...@@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 {...@@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 {
605 return index;667 return index;
606}668}
607669
670fn allocateGlobal(self: *Coff) !u32 {
671 const gpa = self.base.allocator;
672 try self.globals.ensureUnusedCapacity(gpa, 1);
673
674 const index = blk: {
675 if (self.globals_free_list.popOrNull()) |index| {
676 log.debug(" (reusing global index {d})", .{index});
677 break :blk index;
678 } else {
679 log.debug(" (allocating global index {d})", .{self.globals.items.len});
680 const index = @intCast(u32, self.globals.items.len);
681 _ = self.globals.addOneAssumeCapacity();
682 break :blk index;
683 }
684 };
685
686 self.globals.items[index] = .{
687 .sym_index = 0,
688 .file = null,
689 };
690
691 return index;
692}
693
608pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {694pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
609 const gpa = self.base.allocator;695 const gpa = self.base.allocator;
610 try self.got_entries.ensureUnusedCapacity(gpa, 1);696 try self.got_entries.ensureUnusedCapacity(gpa, 1);
697
611 const index: u32 = blk: {698 const index: u32 = blk: {
612 if (self.got_entries_free_list.popOrNull()) |index| {699 if (self.got_entries_free_list.popOrNull()) |index| {
613 log.debug(" (reusing GOT entry index {d})", .{index});700 log.debug(" (reusing GOT entry index {d})", .{index});
614 if (self.got_entries.getIndex(target)) |existing| {
615 assert(existing == index);
616 }
617 break :blk index;701 break :blk index;
618 } else {702 } else {
619 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len});703 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len});
620 const index = @intCast(u32, self.got_entries.keys().len);704 const index = @intCast(u32, self.got_entries.items.len);
621 self.got_entries.putAssumeCapacityNoClobber(target, 0);705 _ = self.got_entries.addOneAssumeCapacity();
622 break :blk index;706 break :blk index;
623 }707 }
624 };708 };
625 self.got_entries.keys()[index] = target;709
710 self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
711 try self.got_entries_table.putNoClobber(gpa, target, index);
712
713 return index;
714}
715
716pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {
717 const gpa = self.base.allocator;
718 try self.imports.ensureUnusedCapacity(gpa, 1);
719
720 const index: u32 = blk: {
721 if (self.imports_free_list.popOrNull()) |index| {
722 log.debug(" (reusing import entry index {d})", .{index});
723 break :blk index;
724 } else {
725 log.debug(" (allocating import entry at index {d})", .{self.imports.items.len});
726 const index = @intCast(u32, self.imports.items.len);
727 _ = self.imports.addOneAssumeCapacity();
728 break :blk index;
729 }
730 };
731
732 self.imports.items[index] = .{ .target = target, .sym_index = 0 };
733 try self.imports_table.putNoClobber(gpa, target, index);
734
626 return index;735 return index;
627}736}
628737
...@@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {...@@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
637746
638 try self.managed_atoms.append(gpa, atom);747 try self.managed_atoms.append(gpa, atom);
639 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);748 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
640 self.got_entries.getPtr(target).?.* = atom.sym_index;
641749
642 const sym = atom.getSymbolPtr(self);750 const sym = atom.getSymbolPtr(self);
643 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);751 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
...@@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {...@@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
652 .addend = 0,760 .addend = 0,
653 .pcrel = false,761 .pcrel = false,
654 .length = 3,762 .length = 3,
655 .prev_vaddr = sym.value,
656 });763 });
657764
658 const target_sym = self.getSymbol(target);765 const target_sym = self.getSymbol(target);
...@@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {...@@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
666 return atom;773 return atom;
667}774}
668775
776fn createImportAtom(self: *Coff) !*Atom {
777 const gpa = self.base.allocator;
778 const atom = try gpa.create(Atom);
779 errdefer gpa.destroy(atom);
780 atom.* = Atom.empty;
781 atom.sym_index = try self.allocateSymbol();
782 atom.size = @sizeOf(u64);
783 atom.alignment = @alignOf(u64);
784
785 try self.managed_atoms.append(gpa, atom);
786 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
787
788 const sym = atom.getSymbolPtr(self);
789 sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);
790 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
791
792 log.debug("allocated import atom at 0x{x}", .{sym.value});
793
794 return atom;
795}
796
669fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {797fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
670 const sym = atom.getSymbol(self);798 const sym = atom.getSymbol(self);
671 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;799 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
...@@ -686,12 +814,12 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {...@@ -686,12 +814,12 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
686 const sym = atom.getSymbol(self);814 const sym = atom.getSymbol(self);
687 const section = self.sections.get(@enumToInt(sym.section_number) - 1);815 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
688 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;816 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;
689 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });817 log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ atom.getName(self), file_offset, file_offset + code.len });
690 try self.base.file.?.pwriteAll(code, file_offset);818 try self.base.file.?.pwriteAll(code, file_offset);
691 try self.resolveRelocs(atom);819 try self.resolveRelocs(atom);
692}820}
693821
694fn writeGotAtom(self: *Coff, atom: *Atom) !void {822fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void {
695 switch (self.ptr_width) {823 switch (self.ptr_width) {
696 .p32 => {824 .p32 => {
697 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);825 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
...@@ -704,6 +832,29 @@ fn writeGotAtom(self: *Coff, atom: *Atom) !void {...@@ -704,6 +832,29 @@ fn writeGotAtom(self: *Coff, atom: *Atom) !void {
704 }832 }
705}833}
706834
835fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
836 // TODO: reverse-lookup might come in handy here
837 var it = self.relocs.valueIterator();
838 while (it.next()) |relocs| {
839 for (relocs.items) |*reloc| {
840 if (!reloc.target.eql(target)) continue;
841 reloc.dirty = true;
842 }
843 }
844}
845
846fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
847 var it = self.relocs.valueIterator();
848 while (it.next()) |relocs| {
849 for (relocs.items) |*reloc| {
850 const target_atom = reloc.getTargetAtom(self) orelse continue;
851 const target_sym = target_atom.getSymbol(self);
852 if (target_sym.value < addr) continue;
853 reloc.dirty = true;
854 }
855 }
856}
857
707fn resolveRelocs(self: *Coff, atom: *Atom) !void {858fn resolveRelocs(self: *Coff, atom: *Atom) !void {
708 const relocs = self.relocs.get(atom) orelse return;859 const relocs = self.relocs.get(atom) orelse return;
709 const source_sym = atom.getSymbol(self);860 const source_sym = atom.getSymbol(self);
...@@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void {...@@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void {
713 log.debug("relocating '{s}'", .{atom.getName(self)});864 log.debug("relocating '{s}'", .{atom.getName(self)});
714865
715 for (relocs.items) |*reloc| {866 for (relocs.items) |*reloc| {
716 const target_vaddr = switch (reloc.@"type") {867 if (!reloc.dirty) continue;
717 .got => blk: {
718 const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue;
719 break :blk got_atom.getSymbol(self).value;
720 },
721 .direct => self.getSymbol(reloc.target).value,
722 };
723 const target_vaddr_with_addend = target_vaddr + reloc.addend;
724868
725 if (target_vaddr_with_addend == reloc.prev_vaddr) continue;869 const target_atom = reloc.getTargetAtom(self) orelse continue;
870 const target_vaddr = target_atom.getSymbol(self).value;
871 const target_vaddr_with_addend = target_vaddr + reloc.addend;
726872
727 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{873 log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{
728 reloc.offset,874 source_sym.value + reloc.offset,
729 target_vaddr_with_addend,875 target_vaddr_with_addend,
730 self.getSymbolName(reloc.target),876 self.getSymbolName(reloc.target),
731 @tagName(reloc.@"type"),877 @tagName(reloc.@"type"),
878 file_offset + reloc.offset,
732 });879 });
733880
881 reloc.dirty = false;
882
734 if (reloc.pcrel) {883 if (reloc.pcrel) {
735 const source_vaddr = source_sym.value + reloc.offset;884 const source_vaddr = source_sym.value + reloc.offset;
736 const disp = target_vaddr_with_addend - source_vaddr - 4;885 const disp =
737 try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset);886 @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4;
738 return;887 try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset);
888 continue;
739 }889 }
740890
741 switch (self.ptr_width) {891 switch (self.ptr_width) {
...@@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void {...@@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void {
755 else => unreachable,905 else => unreachable,
756 },906 },
757 }907 }
758
759 reloc.prev_vaddr = target_vaddr_with_addend;
760 }908 }
761}909}
762910
763fn freeAtom(self: *Coff, atom: *Atom) void {911fn freeAtom(self: *Coff, atom: *Atom) void {
764 log.debug("freeAtom {*}", .{atom});912 log.debug("freeAtom {*}", .{atom});
765913
914 // Remove any relocs and base relocs associated with this Atom
915 self.freeRelocationsForAtom(atom);
916
766 const sym = atom.getSymbol(self);917 const sym = atom.getSymbol(self);
767 const sect_id = @enumToInt(sym.section_number) - 1;918 const sect_id = @enumToInt(sym.section_number) - 1;
768 const free_list = &self.sections.items(.free_list)[sect_id];919 const free_list = &self.sections.items(.free_list)[sect_id];
...@@ -825,11 +976,14 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live...@@ -825,11 +976,14 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
825 const tracy = trace(@src());976 const tracy = trace(@src());
826 defer tracy.end();977 defer tracy.end();
827978
979 const decl_index = func.owner_decl;
980 const decl = module.declPtr(decl_index);
981 self.freeUnnamedConsts(decl_index);
982 self.freeRelocationsForAtom(&decl.link.coff);
983
828 var code_buffer = std.ArrayList(u8).init(self.base.allocator);984 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
829 defer code_buffer.deinit();985 defer code_buffer.deinit();
830986
831 const decl_index = func.owner_decl;
832 const decl = module.declPtr(decl_index);
833 const res = try codegen.generateFunction(987 const res = try codegen.generateFunction(
834 &self.base,988 &self.base,
835 decl.srcLoc(),989 decl.srcLoc(),
...@@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live...@@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
856}1010}
8571011
858pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {1012pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
859 _ = self;1013 const gpa = self.base.allocator;
860 _ = tv;1014 var code_buffer = std.ArrayList(u8).init(gpa);
861 _ = decl_index;1015 defer code_buffer.deinit();
862 @panic("TODO lowerUnnamedConst");1016
1017 const mod = self.base.options.module.?;
1018 const decl = mod.declPtr(decl_index);
1019
1020 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
1021 if (!gop.found_existing) {
1022 gop.value_ptr.* = .{};
1023 }
1024 const unnamed_consts = gop.value_ptr;
1025
1026 const atom = try gpa.create(Atom);
1027 errdefer gpa.destroy(atom);
1028 atom.* = Atom.empty;
1029
1030 atom.sym_index = try self.allocateSymbol();
1031 const sym = atom.getSymbolPtr(self);
1032 const sym_name = blk: {
1033 const decl_name = try decl.getFullyQualifiedName(mod);
1034 defer gpa.free(decl_name);
1035
1036 const index = unnamed_consts.items.len;
1037 break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
1038 };
1039 defer gpa.free(sym_name);
1040 try self.setSymbolName(sym, sym_name);
1041 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
1042
1043 try self.managed_atoms.append(gpa, atom);
1044 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
1045
1046 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{
1047 .parent_atom_index = atom.sym_index,
1048 });
1049 const code = switch (res) {
1050 .externally_managed => |x| x,
1051 .appended => code_buffer.items,
1052 .fail => |em| {
1053 decl.analysis = .codegen_failure;
1054 try mod.failed_decls.put(mod.gpa, decl_index, em);
1055 log.err("{s}", .{em.msg});
1056 return error.AnalysisFail;
1057 },
1058 };
1059
1060 const required_alignment = tv.ty.abiAlignment(self.base.options.target);
1061 atom.alignment = required_alignment;
1062 atom.size = @intCast(u32, code.len);
1063 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
1064 errdefer self.freeAtom(atom);
1065
1066 try unnamed_consts.append(gpa, atom);
1067
1068 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value });
1069 log.debug(" (required alignment 0x{x})", .{required_alignment});
1070
1071 try self.writeAtom(atom, code);
1072
1073 return atom.sym_index;
863}1074}
8641075
865pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {1076pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -884,6 +1095,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -884,6 +1095,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
884 }1095 }
885 }1096 }
8861097
1098 self.freeRelocationsForAtom(&decl.link.coff);
1099
887 var code_buffer = std.ArrayList(u8).init(self.base.allocator);1100 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
888 defer code_buffer.deinit();1101 defer code_buffer.deinit();
8891102
...@@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
892 .ty = decl.ty,1105 .ty = decl.ty,
893 .val = decl_val,1106 .val = decl_val,
894 }, &code_buffer, .none, .{1107 }, &code_buffer, .none, .{
895 .parent_atom_index = 0,1108 .parent_atom_index = decl.link.coff.sym_index,
896 });1109 });
897 const code = switch (res) {1110 const code = switch (res) {
898 .externally_managed => |x| x,1111 .externally_managed => |x| x,
...@@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
970 if (vaddr != sym.value) {1183 if (vaddr != sym.value) {
971 sym.value = vaddr;1184 sym.value = vaddr;
972 log.debug(" (updating GOT entry)", .{});1185 log.debug(" (updating GOT entry)", .{});
973 const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?;1186 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
974 try self.writeGotAtom(got_atom);1187 const got_atom = self.getGotAtomForSymbol(got_target).?;
1188 self.markRelocsDirtyByTarget(got_target);
1189 try self.writePtrWidthAtom(got_atom);
975 }1190 }
976 } else if (code_len < atom.size) {1191 } else if (code_len < atom.size) {
977 self.shrinkAtom(atom, code_len);1192 self.shrinkAtom(atom, code_len);
...@@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
990 sym.value = vaddr;1205 sym.value = vaddr;
9911206
992 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };1207 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
993 _ = try self.allocateGotEntry(got_target);1208 const got_index = try self.allocateGotEntry(got_target);
994 const got_atom = try self.createGotAtom(got_target);1209 const got_atom = try self.createGotAtom(got_target);
995 try self.writeGotAtom(got_atom);1210 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
1211 try self.writePtrWidthAtom(got_atom);
996 }1212 }
9971213
1214 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
998 try self.writeAtom(atom, code);1215 try self.writeAtom(atom, code);
999}1216}
10001217
1218fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void {
1219 _ = self.relocs.remove(atom);
1220 _ = self.base_relocs.remove(atom);
1221}
1222
1223fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {
1224 const gpa = self.base.allocator;
1225 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
1226 for (unnamed_consts.items) |atom| {
1227 self.freeAtom(atom);
1228 self.locals_free_list.append(gpa, atom.sym_index) catch {};
1229 self.locals.items[atom.sym_index].section_number = .UNDEFINED;
1230 _ = self.atom_by_index_table.remove(atom.sym_index);
1231 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
1232 atom.sym_index = 0;
1233 }
1234 unnamed_consts.clearAndFree(gpa);
1235}
1236
1001pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {1237pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
1002 if (build_options.have_llvm) {1238 if (build_options.have_llvm) {
1003 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);1239 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
...@@ -1011,6 +1247,7 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {...@@ -1011,6 +1247,7 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
1011 const kv = self.decls.fetchRemove(decl_index);1247 const kv = self.decls.fetchRemove(decl_index);
1012 if (kv.?.value) |_| {1248 if (kv.?.value) |_| {
1013 self.freeAtom(&decl.link.coff);1249 self.freeAtom(&decl.link.coff);
1250 self.freeUnnamedConsts(decl_index);
1014 }1251 }
10151252
1016 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.1253 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
...@@ -1021,14 +1258,20 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {...@@ -1021,14 +1258,20 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
10211258
1022 // Try freeing GOT atom if this decl had one1259 // Try freeing GOT atom if this decl had one
1023 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };1260 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1024 if (self.got_entries.getIndex(got_target)) |got_index| {1261 if (self.got_entries_table.get(got_target)) |got_index| {
1025 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};1262 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1026 self.got_entries.values()[got_index] = 0;1263 self.got_entries.items[got_index] = .{
1264 .target = .{ .sym_index = 0, .file = null },
1265 .sym_index = 0,
1266 };
1267 _ = self.got_entries_table.remove(got_target);
1268
1027 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });1269 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1028 }1270 }
10291271
1030 self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0);1272 self.locals.items[sym_index].section_number = .UNDEFINED;
1031 _ = self.atom_by_index_table.remove(sym_index);1273 _ = self.atom_by_index_table.remove(sym_index);
1274 log.debug(" adding local symbol index {d} to free list", .{sym_index});
1032 decl.link.coff.sym_index = 0;1275 decl.link.coff.sym_index = 0;
1033 }1276 }
1034}1277}
...@@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void {...@@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
1154 const sym = self.getSymbolPtr(sym_loc);1397 const sym = self.getSymbolPtr(sym_loc);
1155 const sym_name = self.getSymbolName(sym_loc);1398 const sym_name = self.getSymbolName(sym_loc);
1156 log.debug("deleting export '{s}'", .{sym_name});1399 log.debug("deleting export '{s}'", .{sym_name});
1157 assert(sym.storage_class == .EXTERNAL);1400 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);
1158 sym.* = .{1401 sym.* = .{
1159 .name = [_]u8{0} ** 8,1402 .name = [_]u8{0} ** 8,
1160 .value = 0,1403 .value = 0,
1161 .section_number = @intToEnum(coff.SectionNumber, 0),1404 .section_number = .UNDEFINED,
1162 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },1405 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
1163 .storage_class = .NULL,1406 .storage_class = .NULL,
1164 .number_of_aux_symbols = 0,1407 .number_of_aux_symbols = 0,
1165 };1408 };
1166 self.locals_free_list.append(gpa, sym_index) catch {};1409 self.locals_free_list.append(gpa, sym_index) catch {};
11671410
1168 if (self.globals.get(sym_name)) |global| blk: {1411 if (self.resolver.fetchRemove(sym_name)) |entry| {
1169 if (global.sym_index != sym_index) break :blk;1412 defer gpa.free(entry.key);
1170 if (global.file != null) break :blk;1413 self.globals_free_list.append(gpa, entry.value) catch {};
1171 const kv = self.globals.fetchSwapRemove(sym_name);1414 self.globals.items[entry.value] = .{
1172 gpa.free(kv.?.key);1415 .sym_index = 0,
1416 .file = null,
1417 };
1173 }1418 }
1174}1419}
11751420
1176fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {1421fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
1177 const gpa = self.base.allocator;1422 const gpa = self.base.allocator;
1178 const sym = self.getSymbol(current);1423 const sym = self.getSymbol(current);
1179 _ = sym;
1180 const sym_name = self.getSymbolName(current);1424 const sym_name = self.getSymbolName(current);
11811425
1182 const name = try gpa.dupe(u8, sym_name);1426 const global_index = self.resolver.get(sym_name) orelse {
1183 const global_index = @intCast(u32, self.globals.values().len);1427 const name = try gpa.dupe(u8, sym_name);
1184 _ = global_index;1428 const global_index = try self.allocateGlobal();
1185 const gop = try self.globals.getOrPut(gpa, name);1429 self.globals.items[global_index] = current;
1186 defer if (gop.found_existing) gpa.free(name);1430 try self.resolver.putNoClobber(gpa, name, global_index);
11871431 if (sym.section_number == .UNDEFINED) {
1188 if (!gop.found_existing) {1432 try self.unresolved.putNoClobber(gpa, global_index, false);
1189 gop.value_ptr.* = current;1433 }
1190 // TODO undef + tentative
1191 return;1434 return;
1192 }1435 };
11931436
1194 log.debug("TODO finish resolveGlobalSymbols implementation", .{});1437 log.debug("TODO finish resolveGlobalSymbols implementation", .{});
1438
1439 if (sym.section_number == .UNDEFINED) return;
1440
1441 _ = self.unresolved.swapRemove(global_index);
1442 self.globals.items[global_index] = current;
1195}1443}
11961444
1197pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {1445pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -1227,6 +1475,17 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1227,6 +1475,17 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1227 sub_prog_node.activate();1475 sub_prog_node.activate();
1228 defer sub_prog_node.end();1476 defer sub_prog_node.end();
12291477
1478 while (self.unresolved.popOrNull()) |entry| {
1479 assert(entry.value); // We only expect imports generated by the incremental linker for now.
1480 const global = self.globals.items[entry.key];
1481 if (self.imports_table.contains(global)) continue;
1482
1483 const import_index = try self.allocateImportEntry(global);
1484 const import_atom = try self.createImportAtom();
1485 self.imports.items[import_index].sym_index = import_atom.sym_index;
1486 try self.writePtrWidthAtom(import_atom);
1487 }
1488
1230 if (build_options.enable_logging) {1489 if (build_options.enable_logging) {
1231 self.logSymtab();1490 self.logSymtab();
1232 }1491 }
...@@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1237 try self.resolveRelocs(atom.*);1496 try self.resolveRelocs(atom.*);
1238 }1497 }
1239 }1498 }
1499 try self.writeImportTable();
1240 try self.writeBaseRelocations();1500 try self.writeBaseRelocations();
12411501
1242 if (self.getEntryPoint()) |entry_sym_loc| {1502 if (self.getEntryPoint()) |entry_sym_loc| {
...@@ -1262,10 +1522,47 @@ pub fn getDeclVAddr(...@@ -1262,10 +1522,47 @@ pub fn getDeclVAddr(
1262 decl_index: Module.Decl.Index,1522 decl_index: Module.Decl.Index,
1263 reloc_info: link.File.RelocInfo,1523 reloc_info: link.File.RelocInfo,
1264) !u64 {1524) !u64 {
1265 _ = self;1525 const mod = self.base.options.module.?;
1266 _ = decl_index;1526 const decl = mod.declPtr(decl_index);
1267 _ = reloc_info;1527
1268 @panic("TODO getDeclVAddr");1528 assert(self.llvm_object == null);
1529 assert(decl.link.coff.sym_index != 0);
1530
1531 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
1532 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
1533 try atom.addRelocation(self, .{
1534 .@"type" = .direct,
1535 .target = target,
1536 .offset = @intCast(u32, reloc_info.offset),
1537 .addend = reloc_info.addend,
1538 .pcrel = false,
1539 .length = 3,
1540 });
1541 try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset));
1542
1543 return 0;
1544}
1545
1546pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {
1547 if (self.resolver.get(name)) |global_index| {
1548 return self.globals.items[global_index].sym_index;
1549 }
1550
1551 const gpa = self.base.allocator;
1552 const sym_index = try self.allocateSymbol();
1553 const global_index = try self.allocateGlobal();
1554 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1555 self.globals.items[global_index] = sym_loc;
1556
1557 const sym_name = try gpa.dupe(u8, name);
1558 const sym = self.getSymbolPtr(sym_loc);
1559 try self.setSymbolName(sym, sym_name);
1560 sym.storage_class = .EXTERNAL;
1561
1562 try self.resolver.putNoClobber(gpa, sym_name, global_index);
1563 try self.unresolved.putNoClobber(gpa, global_index, true);
1564
1565 return sym_index;
1269}1566}
12701567
1271pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {1568pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
...@@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void {...@@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void {
1342 const header = &self.sections.items(.header)[self.reloc_section_index.?];1639 const header = &self.sections.items(.header)[self.reloc_section_index.?];
1343 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);1640 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
1344 const needed_size = @intCast(u32, buffer.items.len);1641 const needed_size = @intCast(u32, buffer.items.len);
1345 assert(needed_size < sect_capacity); // TODO expand .reloc section1642 if (needed_size > sect_capacity) {
1643 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);
1644 log.debug("writing {s} at 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
1645 self.getSectionName(header),
1646 header.pointer_to_raw_data,
1647 header.pointer_to_raw_data + needed_size,
1648 new_offset,
1649 new_offset + needed_size,
1650 });
1651 header.pointer_to_raw_data = new_offset;
1652
1653 const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);
1654 if (needed_size > sect_vm_capacity) {
1655 // TODO: we want to enforce .reloc after every alloc section.
1656 try self.growSectionVM(self.reloc_section_index.?, needed_size);
1657 }
1658 }
1659 header.virtual_size = @maximum(header.virtual_size, needed_size);
1660 header.size_of_raw_data = needed_size;
13461661
1347 try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);1662 try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);
13481663
...@@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void {...@@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void {
1352 };1667 };
1353}1668}
13541669
1670fn writeImportTable(self: *Coff) !void {
1671 if (self.idata_section_index == null) return;
1672
1673 const gpa = self.base.allocator;
1674
1675 const section = self.sections.get(self.idata_section_index.?);
1676 const last_atom = section.last_atom orelse return;
1677
1678 const iat_rva = section.header.virtual_address;
1679 const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer
1680
1681 const dll_name = "KERNEL32.dll";
1682
1683 var import_dir_entry = coff.ImportDirectoryEntry{
1684 .import_lookup_table_rva = @sizeOf(coff.ImportDirectoryEntry) * 2,
1685 .time_date_stamp = 0,
1686 .forwarder_chain = 0,
1687 .name_rva = 0,
1688 .import_address_table_rva = iat_rva,
1689 };
1690
1691 // TODO: we currently assume there's only one (implicit) DLL - ntdll
1692 var lookup_table = std.ArrayList(coff.ImportLookupEntry64.ByName).init(gpa);
1693 defer lookup_table.deinit();
1694
1695 var names_table = std.ArrayList(u8).init(gpa);
1696 defer names_table.deinit();
1697
1698 // TODO: check if import is still valid
1699 for (self.imports.items) |entry| {
1700 const target_name = self.getSymbolName(entry.target);
1701 const start = names_table.items.len;
1702 mem.writeIntLittle(u16, try names_table.addManyAsArray(2), 0); // TODO: currently, hint is set to 0 as we haven't yet parsed any DLL
1703 try names_table.appendSlice(target_name);
1704 try names_table.append(0);
1705 const end = names_table.items.len;
1706 if (!mem.isAlignedGeneric(usize, end - start, @sizeOf(u16))) {
1707 try names_table.append(0);
1708 }
1709 try lookup_table.append(.{ .name_table_rva = @intCast(u31, start) });
1710 }
1711 try lookup_table.append(.{ .name_table_rva = 0 }); // the sentinel
1712
1713 const dir_entry_size = @sizeOf(coff.ImportDirectoryEntry) + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName) + names_table.items.len + dll_name.len + 1;
1714 const needed_size = iat_size + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry);
1715 const sect_capacity = self.allocatedSize(section.header.pointer_to_raw_data);
1716 assert(needed_size < sect_capacity); // TODO: implement expanding .idata section
1717
1718 // Fixup offsets
1719 const base_rva = iat_rva + iat_size;
1720 import_dir_entry.import_lookup_table_rva += base_rva;
1721 import_dir_entry.name_rva = @intCast(u32, base_rva + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry) - dll_name.len - 1);
1722
1723 for (lookup_table.items[0 .. lookup_table.items.len - 1]) |*lk| {
1724 lk.name_table_rva += @intCast(u31, base_rva + @sizeOf(coff.ImportDirectoryEntry) * 2 + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName));
1725 }
1726
1727 var buffer = std.ArrayList(u8).init(gpa);
1728 defer buffer.deinit();
1729 try buffer.ensureTotalCapacity(dir_entry_size + @sizeOf(coff.ImportDirectoryEntry));
1730 buffer.appendSliceAssumeCapacity(mem.asBytes(&import_dir_entry));
1731 buffer.appendNTimesAssumeCapacity(0, @sizeOf(coff.ImportDirectoryEntry)); // the sentinel; TODO: I think doing all of the above on bytes directly might be cleaner
1732 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(lookup_table.items));
1733 buffer.appendSliceAssumeCapacity(names_table.items);
1734 buffer.appendSliceAssumeCapacity(dll_name);
1735 buffer.appendAssumeCapacity(0);
1736
1737 try self.base.file.?.pwriteAll(buffer.items, section.header.pointer_to_raw_data + iat_size);
1738 // Override the IAT atoms
1739 // TODO: we should rewrite only dirtied atoms, but that's for way later
1740 try self.base.file.?.pwriteAll(mem.sliceAsBytes(lookup_table.items), section.header.pointer_to_raw_data);
1741
1742 self.data_directories[@enumToInt(coff.DirectoryEntry.IMPORT)] = .{
1743 .virtual_address = iat_rva + iat_size,
1744 .size = @intCast(u32, @sizeOf(coff.ImportDirectoryEntry) * 2),
1745 };
1746
1747 self.data_directories[@enumToInt(coff.DirectoryEntry.IAT)] = .{
1748 .virtual_address = iat_rva,
1749 .size = iat_size,
1750 };
1751}
1752
1355fn writeStrtab(self: *Coff) !void {1753fn writeStrtab(self: *Coff) !void {
1754 if (self.strtab_offset == null) return;
1755
1356 const allocated_size = self.allocatedSize(self.strtab_offset.?);1756 const allocated_size = self.allocatedSize(self.strtab_offset.?);
1357 const needed_size = @intCast(u32, self.strtab.len());1757 const needed_size = @intCast(u32, self.strtab.len());
13581758
1359 if (needed_size > allocated_size) {1759 if (needed_size > allocated_size) {
1360 self.strtab_offset = null;1760 self.strtab_offset = null;
1361 self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, 1));1761 self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, @alignOf(u32)));
1362 }1762 }
13631763
1364 log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size });1764 log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size });
1365 try self.base.file.?.pwriteAll(self.strtab.buffer.items, self.strtab_offset.?);1765
1766 var buffer = std.ArrayList(u8).init(self.base.allocator);
1767 defer buffer.deinit();
1768 try buffer.ensureTotalCapacityPrecise(needed_size);
1769 buffer.appendSliceAssumeCapacity(self.strtab.items());
1770 // Here, we do a trick in that we do not commit the size of the strtab to strtab buffer, instead
1771 // we write the length of the strtab to a temporary buffer that goes to file.
1772 mem.writeIntLittle(u32, buffer.items[0..4], @intCast(u32, self.strtab.len()));
1773
1774 try self.base.file.?.pwriteAll(buffer.items, self.strtab_offset.?);
1366}1775}
13671776
1368fn writeSectionHeaders(self: *Coff) !void {1777fn writeSectionHeaders(self: *Coff) !void {
...@@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {...@@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
1527}1936}
15281937
1529fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {1938fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
1530 const headers_size = self.getSizeOfHeaders();1939 const headers_size = @maximum(self.getSizeOfHeaders(), self.page_size);
1531 if (start < headers_size)1940 if (start < headers_size)
1532 return headers_size;1941 return headers_size;
15331942
1534 const end = start + size;1943 const end = start + padToIdeal(size);
15351944
1536 if (self.strtab_offset) |off| {1945 if (self.strtab_offset) |off| {
1537 const increased_size = @intCast(u32, self.strtab.len());1946 const tight_size = @intCast(u32, self.strtab.len());
1947 const increased_size = padToIdeal(tight_size);
1538 const test_end = off + increased_size;1948 const test_end = off + increased_size;
1539 if (end > off and start < test_end) {1949 if (end > off and start < test_end) {
1540 return test_end;1950 return test_end;
...@@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {...@@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
1542 }1952 }
15431953
1544 for (self.sections.items(.header)) |header| {1954 for (self.sections.items(.header)) |header| {
1545 const increased_size = header.size_of_raw_data;1955 const tight_size = header.size_of_raw_data;
1956 const increased_size = padToIdeal(tight_size);
1546 const test_end = header.pointer_to_raw_data + increased_size;1957 const test_end = header.pointer_to_raw_data + increased_size;
1547 if (end > header.pointer_to_raw_data and start < test_end) {1958 if (end > header.pointer_to_raw_data and start < test_end) {
1548 return test_end;1959 return test_end;
...@@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {...@@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
1552 return null;1963 return null;
1553}1964}
15541965
1555pub fn allocatedSize(self: *Coff, start: u32) u32 {1966fn allocatedSize(self: *Coff, start: u32) u32 {
1556 if (start == 0)1967 if (start == 0)
1557 return 0;1968 return 0;
1558 var min_pos: u32 = std.math.maxInt(u32);1969 var min_pos: u32 = std.math.maxInt(u32);
...@@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 {...@@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 {
1566 return min_pos - start;1977 return min_pos - start;
1567}1978}
15681979
1569pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {1980fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
1570 var start: u32 = 0;1981 var start: u32 = 0;
1571 while (self.detectAllocCollision(start, object_size)) |item_end| {1982 while (self.detectAllocCollision(start, object_size)) |item_end| {
1572 start = mem.alignForwardGeneric(u32, item_end, min_alignment);1983 start = mem.alignForwardGeneric(u32, item_end, min_alignment);
...@@ -1574,6 +1985,17 @@ pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {...@@ -1574,6 +1985,17 @@ pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
1574 return start;1985 return start;
1575}1986}
15761987
1988fn allocatedVirtualSize(self: *Coff, start: u32) u32 {
1989 if (start == 0)
1990 return 0;
1991 var min_pos: u32 = std.math.maxInt(u32);
1992 for (self.sections.items(.header)) |header| {
1993 if (header.virtual_address <= start) continue;
1994 if (header.virtual_address < min_pos) min_pos = header.virtual_address;
1995 }
1996 return min_pos - start;
1997}
1998
1577inline fn getSizeOfHeaders(self: Coff) u32 {1999inline fn getSizeOfHeaders(self: Coff) u32 {
1578 const msdos_hdr_size = msdos_stub.len + 4;2000 const msdos_hdr_size = msdos_stub.len + 4;
1579 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() +2001 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() +
...@@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 {...@@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 {
16142036
1615/// Returns symbol location corresponding to the set entrypoint (if any).2037/// Returns symbol location corresponding to the set entrypoint (if any).
1616pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {2038pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {
1617 const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete2039 const entry_name = self.base.options.entry orelse "wWinMainCRTStartup"; // TODO this is incomplete
1618 return self.globals.get(entry_name);2040 const global_index = self.resolver.get(entry_name) orelse return null;
2041 return self.globals.items[global_index];
1619}2042}
16202043
1621/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.2044/// Returns pointer-to-symbol described by `sym_loc` descriptor.
1622pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol {2045pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol {
1623 assert(sym_loc.file == null); // TODO linking object files2046 assert(sym_loc.file == null); // TODO linking object files
1624 return &self.locals.items[sym_loc.sym_index];2047 return &self.locals.items[sym_loc.sym_index];
1625}2048}
16262049
1627/// Returns symbol described by `sym_with_loc` descriptor.2050/// Returns symbol described by `sym_loc` descriptor.
1628pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol {2051pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol {
1629 assert(sym_loc.file == null); // TODO linking object files2052 assert(sym_loc.file == null); // TODO linking object files
1630 return &self.locals.items[sym_loc.sym_index];2053 return &self.locals.items[sym_loc.sym_index];
1631}2054}
16322055
1633/// Returns name of the symbol described by `sym_with_loc` descriptor.2056/// Returns name of the symbol described by `sym_loc` descriptor.
1634pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {2057pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {
1635 assert(sym_loc.file == null); // TODO linking object files2058 assert(sym_loc.file == null); // TODO linking object files
1636 const sym = self.getSymbol(sym_loc);2059 const sym = self.getSymbol(sym_loc);
...@@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {...@@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {
1638 return self.strtab.get(offset).?;2061 return self.strtab.get(offset).?;
1639}2062}
16402063
1641/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.2064/// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor.
1642/// Returns null on failure.2065/// Returns null on failure.
1643pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2066pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1644 assert(sym_loc.file == null); // TODO linking with object files2067 assert(sym_loc.file == null); // TODO linking with object files
1645 return self.atom_by_index_table.get(sym_loc.sym_index);2068 return self.atom_by_index_table.get(sym_loc.sym_index);
1646}2069}
16472070
1648/// Returns GOT atom that references `sym_with_loc` if one exists.2071/// Returns GOT atom that references `sym_loc` if one exists.
1649/// Returns null otherwise.2072/// Returns null otherwise.
1650pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2073pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1651 const got_index = self.got_entries.get(sym_loc) orelse return null;2074 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
1652 return self.atom_by_index_table.get(got_index);2075 const got_entry = self.got_entries.items[got_index];
2076 return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2077}
2078
2079/// Returns import atom that references `sym_loc` if one exists.
2080/// Returns null otherwise.
2081pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2082 const imports_index = self.imports_table.get(sym_loc) orelse return null;
2083 const imports_entry = self.imports.items[imports_index];
2084 return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });
1653}2085}
16542086
1655fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {2087fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
...@@ -1663,6 +2095,14 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v...@@ -1663,6 +2095,14 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v
1663 mem.set(u8, header.name[name_offset.len..], 0);2095 mem.set(u8, header.name[name_offset.len..], 0);
1664}2096}
16652097
2098fn getSectionName(self: *const Coff, header: *const coff.SectionHeader) []const u8 {
2099 if (header.getName()) |name| {
2100 return name;
2101 }
2102 const offset = header.getNameOffset().?;
2103 return self.strtab.get(offset).?;
2104}
2105
1666fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void {2106fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void {
1667 if (name.len <= 8) {2107 if (name.len <= 8) {
1668 mem.copy(u8, &symbol.name, name);2108 mem.copy(u8, &symbol.name, name);
...@@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void {...@@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void {
1725 }2165 }
17262166
1727 log.debug("globals table:", .{});2167 log.debug("globals table:", .{});
1728 for (self.globals.keys()) |name, id| {2168 for (self.globals.items) |sym_loc| {
1729 const value = self.globals.values()[id];2169 const sym_name = self.getSymbolName(sym_loc);
1730 log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file });2170 log.debug(" {s} => %{d} in object({?d})", .{ sym_name, sym_loc.sym_index, sym_loc.file });
1731 }2171 }
17322172
1733 log.debug("GOT entries:", .{});2173 log.debug("GOT entries:", .{});
1734 for (self.got_entries.keys()) |target, i| {2174 for (self.got_entries.items) |entry, i| {
1735 const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null });2175 const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
1736 const target_sym = self.getSymbol(target);2176 const target_sym = self.getSymbol(entry.target);
1737 if (target_sym.section_number == .UNDEFINED) {2177 if (target_sym.section_number == .UNDEFINED) {
1738 log.debug(" {d}@{x} => import('{s}')", .{2178 log.debug(" {d}@{x} => import('{s}')", .{
1739 i,2179 i,
1740 got_sym.value,2180 got_sym.value,
1741 self.getSymbolName(target),2181 self.getSymbolName(entry.target),
1742 });2182 });
1743 } else {2183 } else {
1744 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{2184 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
1745 i,2185 i,
1746 got_sym.value,2186 got_sym.value,
1747 target.sym_index,2187 entry.target.sym_index,
1748 target.file,2188 entry.target.file,
1749 logSymAttributes(target_sym, &buf),2189 logSymAttributes(target_sym, &buf),
1750 });2190 });
1751 }2191 }
1752 }2192 }
1753}2193}
2194
2195fn logSections(self: *Coff) void {
2196 log.debug("sections:", .{});
2197 for (self.sections.items(.header)) |*header| {
2198 log.debug(" {s}: VM({x}, {x}) FILE({x}, {x})", .{
2199 self.getSectionName(header),
2200 header.virtual_address,
2201 header.virtual_address + header.virtual_size,
2202 header.pointer_to_raw_data,
2203 header.pointer_to_raw_data + header.size_of_raw_data,
2204 });
2205 }
2206}
src/link/Coff/Atom.zig+10-7
...@@ -4,8 +4,6 @@ const std = @import("std");...@@ -4,8 +4,6 @@ const std = @import("std");
4const coff = std.coff;4const coff = std.coff;
5const log = std.log.scoped(.link);5const log = std.log.scoped(.link);
66
7const Allocator = std.mem.Allocator;
8
9const Coff = @import("../Coff.zig");7const Coff = @import("../Coff.zig");
10const Reloc = Coff.Reloc;8const Reloc = Coff.Reloc;
11const SymbolWithLoc = Coff.SymbolWithLoc;9const SymbolWithLoc = Coff.SymbolWithLoc;
...@@ -41,11 +39,6 @@ pub const empty = Atom{...@@ -41,11 +39,6 @@ pub const empty = Atom{
41 .next = null,39 .next = null,
42};40};
4341
44pub fn deinit(self: *Atom, gpa: Allocator) void {
45 _ = self;
46 _ = gpa;
47}
48
49/// Returns symbol referencing this atom.42/// Returns symbol referencing this atom.
50pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {43pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
51 return coff_file.getSymbol(.{44 return coff_file.getSymbol(.{
...@@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {...@@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {
118 }111 }
119 try gop.value_ptr.append(gpa, offset);112 try gop.value_ptr.append(gpa, offset);
120}113}
114
115pub fn addBinding(self: *Atom, coff_file: *Coff, target: SymbolWithLoc) !void {
116 const gpa = coff_file.base.allocator;
117 log.debug(" (adding binding to target %{d} in %{d})", .{ target.sym_index, self.sym_index });
118 const gop = try coff_file.bindings.getOrPut(gpa, self);
119 if (!gop.found_existing) {
120 gop.value_ptr.* = .{};
121 }
122 try gop.value_ptr.append(gpa, target);
123}
src/link/MachO.zig+34-17
...@@ -793,11 +793,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -793,11 +793,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
793 }793 }
794 } else {794 } else {
795 const sub_path = self.base.options.emit.?.sub_path;795 const sub_path = self.base.options.emit.?.sub_path;
796 self.base.file = try directory.handle.createFile(sub_path, .{796 if (self.base.file == null) {
797 .truncate = true,797 self.base.file = try directory.handle.createFile(sub_path, .{
798 .read = true,798 .truncate = true,
799 .mode = link.determineMode(self.base.options),799 .read = true,
800 });800 .mode = link.determineMode(self.base.options),
801 });
802 }
801 // Index 0 is always a null symbol.803 // Index 0 is always a null symbol.
802 try self.locals.append(gpa, .{804 try self.locals.append(gpa, .{
803 .n_strx = 0,805 .n_strx = 0,
...@@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)...@@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
1155 var ncmds: u32 = 0;1157 var ncmds: u32 = 0;
11561158
1157 try self.writeLinkeditSegmentData(&ncmds, lc_writer);1159 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
1160
1161 // If the last section of __DATA segment is zerofill section, we need to ensure
1162 // that the free space between the end of the last non-zerofill section of __DATA
1163 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
1164 // copy-paste this space into memory for quicker zerofill operation.
1165 if (self.data_segment_cmd_index) |data_seg_id| blk: {
1166 var physical_zerofill_start: u64 = 0;
1167 const section_indexes = self.getSectionIndexes(data_seg_id);
1168 for (self.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
1169 if (header.isZerofill() and header.size > 0) break;
1170 physical_zerofill_start = header.offset + header.size;
1171 } else break :blk;
1172 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
1173 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
1174 return error.Overflow;
1175 if (physical_zerofill_size > 0) {
1176 var padding = try self.base.allocator.alloc(u8, physical_zerofill_size);
1177 defer self.base.allocator.free(padding);
1178 mem.set(u8, padding, 0);
1179 try self.base.file.?.pwriteAll(padding, physical_zerofill_start);
1180 }
1181 }
1182
1158 try writeDylinkerLC(&ncmds, lc_writer);1183 try writeDylinkerLC(&ncmds, lc_writer);
1159 try self.writeMainLC(&ncmds, lc_writer);1184 try self.writeMainLC(&ncmds, lc_writer);
1160 try self.writeDylibIdLC(&ncmds, lc_writer);1185 try self.writeDylibIdLC(&ncmds, lc_writer);
...@@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {...@@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
14351460
1436 if (force_load) {1461 if (force_load) {
1437 defer archive.deinit(gpa);1462 defer archive.deinit(gpa);
1438 defer file.close();
1439 // Get all offsets from the ToC1463 // Get all offsets from the ToC
1440 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);1464 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
1441 defer offsets.deinit();1465 defer offsets.deinit();
...@@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void {...@@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void {
3086 self.atom_by_index_table.deinit(gpa);3110 self.atom_by_index_table.deinit(gpa);
3087}3111}
30883112
3089pub fn closeFiles(self: MachO) void {
3090 for (self.archives.items) |archive| {
3091 archive.file.close();
3092 }
3093 if (self.d_sym) |ds| {
3094 ds.file.close();
3095 }
3096}
3097
3098fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {3113fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
3099 log.debug("freeAtom {*}", .{atom});3114 log.debug("freeAtom {*}", .{atom});
3100 if (!owns_atom) {3115 if (!owns_atom) {
...@@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {...@@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
5698 else => unreachable,5713 else => unreachable,
5699 }5714 }
57005715
5701 if (self.getSectionByName("__DATA", "__thread_vars")) |_| {5716 if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
5702 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;5717 if (self.sections.items(.header)[sect_id].size > 0) {
5718 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
5719 }
5703 }5720 }
57045721
5705 header.ncmds = ncmds;5722 header.ncmds = ncmds;
src/link/MachO/Archive.zig+1
...@@ -88,6 +88,7 @@ const ar_hdr = extern struct {...@@ -88,6 +88,7 @@ const ar_hdr = extern struct {
88};88};
8989
90pub fn deinit(self: *Archive, allocator: Allocator) void {90pub fn deinit(self: *Archive, allocator: Allocator) void {
91 self.file.close();
91 for (self.toc.keys()) |*key| {92 for (self.toc.keys()) |*key| {
92 allocator.free(key.*);93 allocator.free(key.*);
93 }94 }
src/link/MachO/DebugSymbols.zig+1
...@@ -306,6 +306,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -306,6 +306,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
306}306}
307307
308pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {308pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
309 self.file.close();
309 self.segments.deinit(allocator);310 self.segments.deinit(allocator);
310 self.sections.deinit(allocator);311 self.sections.deinit(allocator);
311 self.dwarf.deinit();312 self.dwarf.deinit();
src/link/Wasm.zig+14-4
...@@ -695,12 +695,10 @@ pub fn deinit(self: *Wasm) void {...@@ -695,12 +695,10 @@ pub fn deinit(self: *Wasm) void {
695 gpa.free(segment_info.name);695 gpa.free(segment_info.name);
696 }696 }
697 for (self.objects.items) |*object| {697 for (self.objects.items) |*object| {
698 object.file.?.close();
699 object.deinit(gpa);698 object.deinit(gpa);
700 }699 }
701700
702 for (self.archives.items) |*archive| {701 for (self.archives.items) |*archive| {
703 archive.file.close();
704 archive.deinit(gpa);702 archive.deinit(gpa);
705 }703 }
706704
...@@ -3218,14 +3216,26 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size...@@ -3218,14 +3216,26 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size
3218 buf[0] = @enumToInt(section);3216 buf[0] = @enumToInt(section);
3219 leb.writeUnsignedFixed(5, buf[1..6], size);3217 leb.writeUnsignedFixed(5, buf[1..6], size);
3220 leb.writeUnsignedFixed(5, buf[6..], items);3218 leb.writeUnsignedFixed(5, buf[6..], items);
3221 try file.pwriteAll(&buf, offset);3219
3220 if (builtin.target.os.tag == .windows) {
3221 // https://github.com/ziglang/zig/issues/12783
3222 const curr_pos = try file.getPos();
3223 try file.pwriteAll(&buf, offset);
3224 try file.seekTo(curr_pos);
3225 } else try file.pwriteAll(&buf, offset);
3222}3226}
32233227
3224fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {3228fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
3225 var buf: [1 + 5]u8 = undefined;3229 var buf: [1 + 5]u8 = undefined;
3226 buf[0] = 0; // 0 = 'custom' section3230 buf[0] = 0; // 0 = 'custom' section
3227 leb.writeUnsignedFixed(5, buf[1..6], size);3231 leb.writeUnsignedFixed(5, buf[1..6], size);
3228 try file.pwriteAll(&buf, offset);3232
3233 if (builtin.target.os.tag == .windows) {
3234 // https://github.com/ziglang/zig/issues/12783
3235 const curr_pos = try file.getPos();
3236 try file.pwriteAll(&buf, offset);
3237 try file.seekTo(curr_pos);
3238 } else try file.pwriteAll(&buf, offset);
3229}3239}
32303240
3231fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {3241fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
src/link/Wasm/Archive.zig+1
...@@ -95,6 +95,7 @@ const ar_hdr = extern struct {...@@ -95,6 +95,7 @@ const ar_hdr = extern struct {
95};95};
9696
97pub fn deinit(archive: *Archive, allocator: Allocator) void {97pub fn deinit(archive: *Archive, allocator: Allocator) void {
98 archive.file.close();
98 for (archive.toc.keys()) |*key| {99 for (archive.toc.keys()) |*key| {
99 allocator.free(key.*);100 allocator.free(key.*);
100 }101 }
src/link/Wasm/Object.zig+3
...@@ -154,6 +154,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz...@@ -154,6 +154,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
154/// Frees all memory of `Object` at once. The given `Allocator` must be154/// Frees all memory of `Object` at once. The given `Allocator` must be
155/// the same allocator that was used when `init` was called.155/// the same allocator that was used when `init` was called.
156pub fn deinit(self: *Object, gpa: Allocator) void {156pub fn deinit(self: *Object, gpa: Allocator) void {
157 if (self.file) |file| {
158 file.close();
159 }
157 for (self.func_types) |func_ty| {160 for (self.func_types) |func_ty| {
158 gpa.free(func_ty.params);161 gpa.free(func_ty.params);
159 gpa.free(func_ty.returns);162 gpa.free(func_ty.returns);
src/link/strtab.zig+4
...@@ -110,6 +110,10 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {...@@ -110,6 +110,10 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
110 return self.get(off) orelse unreachable;110 return self.get(off) orelse unreachable;
111 }111 }
112112
113 pub fn items(self: Self) []const u8 {
114 return self.buffer.items;
115 }
116
113 pub fn len(self: Self) usize {117 pub fn len(self: Self) usize {
114 return self.buffer.items.len;118 return self.buffer.items.len;
115 }119 }
src/test.zig+8
...@@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct {...@@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct {
177 inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| {177 inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| {
178 defaults = defaults ++ arch ++ "-macos" ++ ",";178 defaults = defaults ++ arch ++ "-macos" ++ ",";
179 }179 }
180 // Windows
181 defaults = defaults ++ "x86_64-windows" ++ ",";
180 // Wasm182 // Wasm
181 defaults = defaults ++ "wasm32-wasi";183 defaults = defaults ++ "wasm32-wasi";
182 return defaults;184 return defaults;
...@@ -1546,6 +1548,12 @@ pub const TestContext = struct {...@@ -1546,6 +1548,12 @@ pub const TestContext = struct {
1546 .self_exe_path = std.testing.zig_exe_path,1548 .self_exe_path = std.testing.zig_exe_path,
1547 // TODO instead of turning off color, pass in a std.Progress.Node1549 // TODO instead of turning off color, pass in a std.Progress.Node
1548 .color = .off,1550 .color = .off,
1551 // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in
1552 // until the auto-select mechanism deems them worthy
1553 .use_lld = switch (case.backend) {
1554 .stage2 => false,
1555 else => null,
1556 },
1549 });1557 });
1550 defer comp.destroy();1558 defer comp.destroy();
15511559
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1
...@@ -2,5 +2,5 @@...@@ -2,5 +2,5 @@
2// output_mode=Exe2// output_mode=Exe
3// target=aarch64-macos3// target=aarch64-macos
4//4//
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'5// :109:9: error: struct 'tmp.tmp' has no member named 'main'
6// :7:1: note: struct declared here6// :7:1: note: struct declared here
test/cases/x86_64-linux/hello_world_with_updates.0.zig+1-1
...@@ -2,5 +2,5 @@...@@ -2,5 +2,5 @@
2// output_mode=Exe2// output_mode=Exe
3// target=x86_64-linux3// target=x86_64-linux
4//4//
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'5// :109:9: error: struct 'tmp.tmp' has no member named 'main'
6// :7:1: note: struct declared here6// :7:1: note: struct declared here
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1
...@@ -2,5 +2,5 @@...@@ -2,5 +2,5 @@
2// output_mode=Exe2// output_mode=Exe
3// target=x86_64-macos3// target=x86_64-macos
4//4//
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'5// :109:9: error: struct 'tmp.tmp' has no member named 'main'
6// :7:1: note: struct declared here6// :7:1: note: struct declared here
test/cases/x86_64-windows/hello_world_with_updates.0.zig created+6
...@@ -0,0 +1,6 @@
1// error
2// output_mode=Exe
3// target=x86_64-windows
4//
5// :130:9: error: struct 'tmp.tmp' has no member named 'main'
6// :7:1: note: struct declared here
test/cases/x86_64-windows/hello_world_with_updates.1.zig created+6
...@@ -0,0 +1,6 @@
1pub export fn main() noreturn {}
2
3// error
4//
5// :1:32: error: function declared 'noreturn' returns
6// :1:22: note: 'noreturn' declared here
test/cases/x86_64-windows/hello_world_with_updates.2.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2
3pub fn main() void {
4 print();
5}
6
7fn print() void {
8 const msg = "Hello, World!\n";
9 const stdout = std.io.getStdOut();
10 stdout.writeAll(msg) catch unreachable;
11}
12
13// run
14//
15// Hello, World!
16//
test/link.zig+7-7
...@@ -28,35 +28,35 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -28,35 +28,35 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
28}28}
2929
30fn addWasmCases(cases: *tests.StandaloneContext) void {30fn addWasmCases(cases: *tests.StandaloneContext) void {
31 cases.addBuildFile("test/link/wasm/bss/build.zig", .{31 cases.addBuildFile("test/link/wasm/archive/build.zig", .{
32 .build_modes = true,32 .build_modes = true,
33 .requires_stage2 = true,33 .requires_stage2 = true,
34 });34 });
3535
36 cases.addBuildFile("test/link/wasm/segments/build.zig", .{36 cases.addBuildFile("test/link/wasm/bss/build.zig", .{
37 .build_modes = true,37 .build_modes = true,
38 .requires_stage2 = true,38 .requires_stage2 = true,
39 });39 });
4040
41 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{41 cases.addBuildFile("test/link/wasm/extern/build.zig", .{
42 .build_modes = true,42 .build_modes = true,
43 .requires_stage2 = true,43 .requires_stage2 = true,
44 .use_emulation = true,
44 });45 });
4546
46 cases.addBuildFile("test/link/wasm/type/build.zig", .{47 cases.addBuildFile("test/link/wasm/segments/build.zig", .{
47 .build_modes = true,48 .build_modes = true,
48 .requires_stage2 = true,49 .requires_stage2 = true,
49 });50 });
5051
51 cases.addBuildFile("test/link/wasm/archive/build.zig", .{52 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{
52 .build_modes = true,53 .build_modes = true,
53 .requires_stage2 = true,54 .requires_stage2 = true,
54 });55 });
5556
56 cases.addBuildFile("test/link/wasm/extern/build.zig", .{57 cases.addBuildFile("test/link/wasm/type/build.zig", .{
57 .build_modes = true,58 .build_modes = true,
58 .requires_stage2 = true,59 .requires_stage2 = true,
59 .use_emulation = true,
60 });60 });
61}61}
6262
test/tests.zig+10
...@@ -108,6 +108,14 @@ const test_targets = blk: {...@@ -108,6 +108,14 @@ const test_targets = blk: {
108 },108 },
109 .backend = .stage2_x86_64,109 .backend = .stage2_x86_64,
110 },110 },
111 .{
112 .target = .{
113 .cpu_arch = .x86_64,
114 .os_tag = .windows,
115 .abi = .gnu,
116 },
117 .backend = .stage2_x86_64,
118 },
111119
112 .{120 .{
113 .target = .{121 .target = .{
...@@ -693,6 +701,8 @@ pub fn addPkgTests(...@@ -693,6 +701,8 @@ pub fn addPkgTests(
693 else => {701 else => {
694 these_tests.use_stage1 = false;702 these_tests.use_stage1 = false;
695 these_tests.use_llvm = false;703 these_tests.use_llvm = false;
704 // TODO: force self-hosted linkers to avoid LLD creeping in until the auto-select mechanism deems them worthy
705 these_tests.use_lld = false;
696 },706 },
697 };707 };
698708