| author | |
| committer | |
| log | 56b96cd61b0bdb7f5b11a5283fe6dd5b585ef10e |
| tree | a19765f949fd7ce1d1d6fdbcf684a812d30e634a |
| parent | a833bdcd7e6fcfee6e9cc33a3f7de78b16a36941 |
| parent | 5006fb6846ccaa7edb1547588cf1aa08c8decf2b |
| signature |
coff: implement enough of the incremental linker to pass behavior and incremental tests on Windows28 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 | CheckLastExitCode | 77 | CheckLastExitCode |
| 79 | name: test | 78 | 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 | } |
| 992 | 992 | ||
| 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 { |
| 1004 | 1006 | ||
| 1005 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it | 1007 | /// 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 | } |
| 1059 | 1063 | ||
| 1060 | /// See https://github.com/ziglang/zig/issues/7699 | 1064 | /// 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 ReadFileScatter | 1069 | // 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 in | 1085 | /// 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/7699 | 1087 | /// 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; |
| 1084 | 1092 | ||
| ... | @@ -1122,6 +1130,8 @@ pub const File = struct { | ... | @@ -1122,6 +1130,8 @@ pub const File = struct { |
| 1122 | } | 1130 | } |
| 1123 | } | 1131 | } |
| 1124 | 1132 | ||
| 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 | } |
| 1136 | 1146 | ||
| 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 | } |
| 1180 | 1192 | ||
| 1181 | /// See https://github.com/ziglang/zig/issues/7699 | 1193 | /// 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 WriteFileScatter | 1198 | // 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 in | 1211 | /// 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/7699 | 1213 | /// 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; |
| 1202 | 1218 |
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 |
| 36 | 36 | ||
| 37 | fn getStdOutHandle() os.fd_t { | 37 | fn 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 | } |
| 41 | 45 | ||
| ... | @@ -58,6 +62,10 @@ pub fn getStdOut() File { | ... | @@ -58,6 +62,10 @@ pub fn getStdOut() File { |
| 58 | 62 | ||
| 59 | fn getStdErrHandle() os.fd_t { | 63 | fn 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 | } |
| 63 | 71 | ||
| ... | @@ -80,6 +88,10 @@ pub fn getStdErr() File { | ... | @@ -80,6 +88,10 @@ pub fn getStdErr() File { |
| 80 | 88 | ||
| 81 | fn getStdInHandle() os.fd_t { | 89 | fn 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 | } |
| 85 | 97 |
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; |
| 350 | 350 | ||
| 351 | pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: *OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(WINAPI) BOOL; | 351 | pub 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; | ||
| 352 | 358 | ||
| 353 | pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE; | 359 | pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE; |
| 354 | 360 |
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"); |
| 32 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | 32 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 34 | 34 | ||
| 35 | const callee_preserved_regs = abi.callee_preserved_regs; | ||
| 36 | const caller_preserved_regs = abi.caller_preserved_regs; | ||
| 37 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ||
| 38 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | ||
| 39 | |||
| 40 | const Condition = bits.Condition; | 35 | const Condition = bits.Condition; |
| 41 | const RegisterManager = abi.RegisterManager; | 36 | const RegisterManager = abi.RegisterManager; |
| 42 | const RegisterLock = RegisterManager.RegisterLock; | 37 | const 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 | }; |
| 207 | 240 | ||
| 208 | const StackAllocation = struct { | 241 | const 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 | }); |
| 407 | 440 | ||
| 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 purposely | 443 | // 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); |
| 415 | 448 | ||
| 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 | } |
| 420 | 454 | ||
| 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 { |
| 446 | 480 | ||
| 447 | // Create list of registers to save in the prologue. | 481 | // Create list of registers to save in the prologue. |
| 448 | // TODO handle register classes | 482 | // 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 { |
| 797 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 832 | fn 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. |
| 3898 | 3957 | ||
| 3899 | try self.spillEflagsIfOccupied(); | 3958 | try self.spillEflagsIfOccupied(); |
| 3900 | 3959 | ||
| 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 | } |
| 3904 | 3963 | ||
| 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}); |
| 3912 | 3971 | ||
| 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); | ||
| 3916 | 3976 | ||
| 3917 | info.return_value.stack_offset = stack_offset; | 3977 | info.return_value.stack_offset = stack_offset; |
| 3918 | 3978 | ||
| 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); |
| 3924 | 3984 | ||
| 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 { |
| 4626 | 4713 | ||
| 4627 | // Revert to the previous register and stack allocation state. | 4714 | // Revert to the previous register and stack allocation state. |
| 4628 | 4715 | ||
| 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); |
| 4631 | 4718 | ||
| 4632 | self.revertState(saved_state); | 4719 | self.revertState(saved_state); |
| 4633 | 4720 | ||
| 4634 | try self.performReloc(reloc); | 4721 | try self.performReloc(reloc); |
| 4635 | 4722 | ||
| 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 | } | ||
| 4638 | 4727 | ||
| 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); |
| 4644 | 4733 | ||
| 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 where | 4737 | // 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 emit | 4739 | // 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 we | 4742 | // 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_registers | 4743 | // 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:", .{}); |
| 4655 | 4747 | 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 | } |
| 4716 | 4750 | ||
| 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); | ||
| 4721 | 4756 | ||
| 4722 | // We already took care of pl_op.operand earlier, so we're going | 4757 | // We already took care of pl_op.operand earlier, so we're going |
| 4723 | // to pass .none here | 4758 | // 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 | } |
| 5104 | 5139 | ||
| 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 { |
| 5131 | 5175 | ||
| 5132 | try self.genBody(case_body); | 5176 | try self.genBody(case_body); |
| 5133 | 5177 | ||
| 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); | ||
| 5137 | 5179 | ||
| 5180 | // Revert to the previous register and stack allocation state. | ||
| 5138 | self.revertState(saved_state); | 5181 | self.revertState(saved_state); |
| 5139 | 5182 | ||
| 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 { |
| 5144 | 5187 | ||
| 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 | } |
| 5152 | 5198 | ||
| 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 { |
| 5158 | 5204 | ||
| 5159 | try self.genBody(else_body); | 5205 | try self.genBody(else_body); |
| 5160 | 5206 | ||
| 5161 | // TODO consolidate returned MCValues between prongs and else branch like we do | 5207 | 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 | } |
| 5164 | 5232 | ||
| 5165 | // We already took care of pl_op.operand earlier, so we're going | 5233 | // 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 | } |
| 5169 | 5237 | ||
| 5238 | fn 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 | |||
| 5170 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { | 5304 | fn 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 | }; |
| 5798 | 5934 | ||
| 5799 | /// Spills .rax and .rcx. | ||
| 5800 | fn genInlineMemcpy( | 5935 | fn 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 | else | 5944 | 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); |
| 5827 | 5953 | ||
| 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); | ||
| 5862 | 5993 | ||
| 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(); | ||
| 5901 | 6026 | ||
| 5902 | try self.genSetReg(Type.usize, count_reg, len); | 6027 | try self.genSetReg(Type.usize, count_reg, len); |
| 5903 | 6028 | ||
| 5904 | // mov rcx, 0 | 6029 | // 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 | }); |
| 5917 | 6035 | ||
| ... | @@ -5933,37 +6051,30 @@ fn genInlineMemcpy( | ... | @@ -5933,37 +6051,30 @@ fn genInlineMemcpy( |
| 5933 | } }, | 6051 | } }, |
| 5934 | }); | 6052 | }); |
| 5935 | 6053 | ||
| 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 | }); |
| 5945 | 6063 | ||
| 5946 | // mov [stack_offset + rax], tmp | 6064 | // 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 | }); |
| 5962 | 6073 | ||
| 5963 | // add rax, 1 | 6074 | // 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 | }); |
| 5969 | 6080 | ||
| ... | @@ -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 | } |
| 5987 | 6098 | ||
| 5988 | /// Spills .rax register. | ||
| 5989 | fn genInlineMemset( | 6099 | fn 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 restore | 6106 | 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(); | ||
| 6000 | 6121 | ||
| 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); | ||
| 6035 | 6154 | ||
| 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 }); |
| 6038 | 6157 | ||
| 6039 | // loop: | 6158 | // loop: |
| 6040 | // cmp rax, -1 | 6159 | // 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 | }); |
| 6046 | 6165 | ||
| ... | @@ -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], imm | 6181 | // 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 | } |
| 6075 | 6190 | ||
| 6076 | // sub rax, 1 | 6191 | // 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 | }); |
| 6082 | 6197 | ||
| ... | @@ -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 values | 7190 | // 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 value | 7205 | // 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 | } |
| 7090 | 7209 | ||
| 7091 | // Input params | 7210 | // 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 reverse | 7212 | .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| { |
| 7098 | 7217 | assert(ty.hasRuntimeBits()); | |
| 7099 | // If we want debug output, we store all args on stack for better liveness of args | 7218 | |
| 7100 | // in debugging contexts such as previewing the args in the debugger anywhere in | 7219 | const classes: []const abi.Class = switch (self.target.os.tag) { |
| 7101 | // the procedure. Passing the args via registers can lead to reusing the register | 7220 | .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 on | 7266 | 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 | } |
| 7134 | 7285 | ||
| 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 | } |
| 7160 | 7303 | ||
| 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 { |
| 614 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 615 | fn 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 |
| 630 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 632 | fn 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], imm32 | 642 | // 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], imm32 | 659 | // 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 | } |
| 670 | 666 | ||
| 671 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 667 | fn 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], imm32 | 682 | // 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 | } |
| 693 | 689 | ||
| 694 | fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 690 | fn 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; |
| 986 | 983 | ||
| 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 | } |
| 991 | 988 | ||
| 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], imm16 | 44 | /// 0b01 word ptr [reg1 + imm32], imm16 |
| 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 | 45 | /// 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, |
| 48 | 50 | ||
| 49 | /// form: reg1, [reg2 + scale*rcx + imm32] | 51 | /// form: reg1, [reg2 + scale*index + imm32] |
| 50 | /// ops flags scale | 52 | /// ops flags scale |
| 51 | /// 0b00 1 | 53 | /// 0b00 1 |
| 52 | /// 0b01 2 | 54 | /// 0b01 2 |
| 53 | /// 0b10 4 | 55 | /// 0b10 4 |
| 54 | /// 0b11 8 | 56 | /// 0b11 8 |
| 57 | /// Notes: | ||
| 58 | /// * Uses `IndexRegisterDisp` as payload | ||
| 55 | adc_scale_src, | 59 | adc_scale_src, |
| 56 | 60 | ||
| 57 | /// form: [reg1 + scale*rax + imm32], reg2 | 61 | /// form: [reg1 + scale*index + imm32], reg2 |
| 58 | /// form: [reg1 + scale*rax + 0], imm32 | ||
| 59 | /// ops flags scale | 62 | /// ops flags scale |
| 60 | /// 0b00 1 | 63 | /// 0b00 1 |
| 61 | /// 0b01 2 | 64 | /// 0b01 2 |
| 62 | /// 0b10 4 | 65 | /// 0b10 4 |
| 63 | /// 0b11 8 | 66 | /// 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, |
| 67 | 70 | ||
| 68 | /// form: [reg1 + scale*rax + imm32], imm32 | 71 | /// form: [reg1 + scale*rax + imm32], imm32 |
| ... | @@ -72,14 +75,16 @@ pub const Inst = struct { | ... | @@ -72,14 +75,16 @@ pub const Inst = struct { |
| 72 | /// 0b10 4 | 75 | /// 0b10 4 |
| 73 | /// 0b11 8 | 76 | /// 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, |
| 77 | 80 | ||
| 78 | /// ops flags: form: | 81 | /// ops flags: form: |
| 79 | /// 0b00 byte ptr [reg1 + rax + imm32], imm8 | 82 | /// 0b00 byte ptr [reg1 + index + imm32], imm8 |
| 80 | /// 0b01 word ptr [reg1 + rax + imm32], imm16 | 83 | /// 0b01 word ptr [reg1 + index + imm32], imm16 |
| 81 | /// 0b10 dword ptr [reg1 + rax + imm32], imm32 | 84 | /// 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, |
| 84 | 89 | ||
| 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, |
| 179 | 186 | ||
| 180 | /// ops flags: form: | 187 | /// ops flags: form: |
| 181 | /// 0b00 reg1, [rip + reloc] // via GOT PIC | 188 | /// 0b00 reg1, [rip + reloc] // via GOT PIC |
| 182 | /// 0b01 reg1, [rip + reloc] // direct load PIC | 189 | /// 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 | }; |
| 462 | 470 | ||
| 463 | pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type { | 471 | pub 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(), | ||
| 467 | 474 | ||
| 468 | const RegBitSet = IntegerBitSet(registers.len); | 475 | /// Displacement value |
| 469 | const Self = @This(); | 476 | disp: u32, |
| 470 | 477 | ||
| 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 | } |
| 477 | 484 | ||
| 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 | }; | ||
| 482 | 495 | ||
| 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); | 498 | pub const IndexRegisterDispImm = struct { |
| 486 | } | 499 | /// Index register to use with SIB-based encoding |
| 500 | index: u32, | ||
| 487 | 501 | ||
| 488 | pub fn asInt(self: Self) u32 { | 502 | /// Displacement value |
| 489 | return self.bitset.mask; | 503 | disp: u32, |
| 490 | } | ||
| 491 | 504 | ||
| 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 | } | ||
| 497 | 515 | ||
| 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. | ||
| 531 | pub 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 | }; | ||
| 503 | 568 | ||
| 504 | pub const SaveRegisterList = struct { | 569 | pub 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 | } |
| 394 | 394 | ||
| 395 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | 395 | pub 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 |
| 397 | pub 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 before | 398 | 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). |
| 401 | pub 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 }; | ||
| 402 | 403 | ||
| 403 | pub 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 }; |
| 404 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | 405 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; |
| 406 | }; | ||
| 407 | |||
| 408 | pub 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 }; | ||
| 405 | 416 | ||
| 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 | |||
| 421 | pub 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 | |||
| 428 | pub 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 | |||
| 435 | pub 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 | |||
| 442 | pub 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 | |||
| 449 | const gp_regs = [_]Register{ | ||
| 450 | .rbx, .r12, .r13, .r14, .r15, .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11, | ||
| 451 | }; | ||
| 406 | const sse_avx_regs = [_]Register{ | 452 | const 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 | }; |
| 410 | const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs; | 456 | const allocatable_regs = gp_regs ++ sse_avx_regs; |
| 411 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); | 457 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs); |
| 412 | 458 | ||
| 413 | // Register classes | 459 | // Register classes |
| 414 | const RegisterBitSet = RegisterManager.RegisterBitSet; | 460 | const 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: off | 478 | // 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"); |
| 30 | pub const base_tag: link.File.Tag = .coff; | 30 | pub const base_tag: link.File.Tag = .coff; |
| 31 | 31 | ||
| 32 | const msdos_stub = @embedFile("msdos-stub.bin"); | 32 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 33 | const N_DATA_DIRS: u5 = 16; | ||
| 34 | 33 | ||
| 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. |
| 36 | llvm_object: ?*LlvmObject = null, | 35 | llvm_object: ?*LlvmObject = null, |
| ... | @@ -44,24 +43,33 @@ page_size: u32, | ... | @@ -44,24 +43,33 @@ page_size: u32, |
| 44 | objects: std.ArrayListUnmanaged(Object) = .{}, | 43 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 45 | 44 | ||
| 46 | sections: std.MultiArrayList(Section) = .{}, | 45 | sections: std.MultiArrayList(Section) = .{}, |
| 47 | data_directories: [N_DATA_DIRS]coff.ImageDataDirectory, | 46 | data_directories: [coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory, |
| 48 | 47 | ||
| 49 | text_section_index: ?u16 = null, | 48 | text_section_index: ?u16 = null, |
| 50 | got_section_index: ?u16 = null, | 49 | got_section_index: ?u16 = null, |
| 51 | rdata_section_index: ?u16 = null, | 50 | rdata_section_index: ?u16 = null, |
| 52 | data_section_index: ?u16 = null, | 51 | data_section_index: ?u16 = null, |
| 53 | reloc_section_index: ?u16 = null, | 52 | reloc_section_index: ?u16 = null, |
| 53 | idata_section_index: ?u16 = null, | ||
| 54 | 54 | ||
| 55 | locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, | 55 | locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, |
| 56 | globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, | 56 | globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, |
| 57 | resolver: std.StringHashMapUnmanaged(u32) = .{}, | ||
| 58 | unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{}, | ||
| 57 | 59 | ||
| 58 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, | 60 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 61 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 59 | 62 | ||
| 60 | strtab: StringTable(.strtab) = .{}, | 63 | strtab: StringTable(.strtab) = .{}, |
| 61 | strtab_offset: ?u32 = null, | 64 | strtab_offset: ?u32 = null, |
| 62 | 65 | ||
| 63 | got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | 66 | got_entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 64 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, | 67 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 68 | got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | ||
| 69 | |||
| 70 | imports: std.ArrayListUnmanaged(Entry) = .{}, | ||
| 71 | imports_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 72 | imports_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | ||
| 65 | 73 | ||
| 66 | /// Virtual address of the entry point procedure relative to image base. | 74 | /// Virtual address of the entry point procedure relative to image base. |
| 67 | entry_addr: ?u32 = null, | 75 | entry_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. |
| 110 | base_relocs: BaseRelocationTable = .{}, | 118 | base_relocs: BaseRelocationTable = .{}, |
| 111 | 119 | ||
| 120 | const Entry = struct { | ||
| 121 | target: SymbolWithLoc, | ||
| 122 | // Index into the synthetic symbol table (i.e., file == null). | ||
| 123 | sym_index: u32, | ||
| 124 | }; | ||
| 125 | |||
| 112 | pub const Reloc = struct { | 126 | pub 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 | }; |
| 124 | 148 | ||
| 125 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); | 149 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); |
| ... | @@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct { | ... | @@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct { |
| 180 | 204 | ||
| 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 | }; |
| 184 | 218 | ||
| 185 | /// When allocating, the ideal_capacity is calculated by | 219 | /// 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 | }; |
| 239 | 273 | ||
| 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 { |
| 269 | 303 | ||
| 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); |
| 278 | 326 | ||
| ... | @@ -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; |
| 307 | 355 | ||
| 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 pointers | 371 | 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 | } |
| 332 | 377 | ||
| 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 | } |
| 356 | 385 | ||
| 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 | } |
| 380 | 393 | ||
| 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 | } |
| 405 | 410 | ||
| 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 | } |
| 430 | 419 | ||
| 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 | } |
| 436 | 425 | ||
| 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's | 428 | // 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 | } |
| 461 | 440 | ||
| 441 | fn 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 | |||
| 477 | fn 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 | |||
| 462 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { | 507 | pub 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 | } |
| 551 | 616 | ||
| 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; |
| 557 | 619 | ||
| ... | @@ -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 | } |
| 607 | 669 | ||
| 670 | fn 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 | |||
| 608 | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { | 694 | pub 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 | |||
| 716 | pub 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 | } |
| 628 | 737 | ||
| ... | @@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | ... | @@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 637 | 746 | ||
| 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; | ||
| 641 | 749 | ||
| 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 | }); |
| 657 | 764 | ||
| 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 | } |
| 668 | 775 | ||
| 776 | fn 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 | |||
| 669 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { | 797 | fn 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 | } |
| 693 | 821 | ||
| 694 | fn writeGotAtom(self: *Coff, atom: *Atom) !void { | 822 | fn 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 | } |
| 706 | 834 | ||
| 835 | fn 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 | |||
| 846 | fn 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 | |||
| 707 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { | 858 | fn 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)}); |
| 714 | 865 | ||
| 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; | ||
| 724 | 868 | ||
| 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; | ||
| 726 | 872 | ||
| 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 | }); |
| 733 | 880 | ||
| 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 | } |
| 740 | 890 | ||
| 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 | } |
| 762 | 910 | ||
| 763 | fn freeAtom(self: *Coff, atom: *Atom) void { | 911 | fn freeAtom(self: *Coff, atom: *Atom) void { |
| 764 | log.debug("freeAtom {*}", .{atom}); | 912 | log.debug("freeAtom {*}", .{atom}); |
| 765 | 913 | ||
| 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(); |
| 827 | 978 | ||
| 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(); |
| 830 | 986 | ||
| 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 | } |
| 857 | 1011 | ||
| 858 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { | 1012 | pub 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 | } |
| 864 | 1075 | ||
| 865 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { | 1076 | pub 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 | } |
| 886 | 1097 | ||
| 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(); |
| 889 | 1102 | ||
| ... | @@ -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; |
| 991 | 1206 | ||
| 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 | } |
| 997 | 1213 | ||
| 1214 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); | ||
| 998 | try self.writeAtom(atom, code); | 1215 | try self.writeAtom(atom, code); |
| 999 | } | 1216 | } |
| 1000 | 1217 | ||
| 1218 | fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void { | ||
| 1219 | _ = self.relocs.remove(atom); | ||
| 1220 | _ = self.base_relocs.remove(atom); | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | fn 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 | |||
| 1001 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | 1237 | pub 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 | } |
| 1015 | 1252 | ||
| 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 { |
| 1021 | 1258 | ||
| 1022 | // Try freeing GOT atom if this decl had one | 1259 | // 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 | } |
| 1029 | 1271 | ||
| 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 {}; |
| 1167 | 1410 | ||
| 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 | } |
| 1175 | 1420 | ||
| 1176 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { | 1421 | fn 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); |
| 1181 | 1425 | ||
| 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); |
| 1187 | 1431 | 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 | }; |
| 1193 | 1436 | ||
| 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 | } |
| 1196 | 1444 | ||
| 1197 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 1445 | pub 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(); |
| 1229 | 1477 | ||
| 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(); |
| 1241 | 1501 | ||
| 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 | |||
| 1546 | pub 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 | } |
| 1270 | 1567 | ||
| 1271 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { | 1568 | pub 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 section | 1642 | 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; | ||
| 1346 | 1661 | ||
| 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); |
| 1348 | 1663 | ||
| ... | @@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void { | ... | @@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1352 | }; | 1667 | }; |
| 1353 | } | 1668 | } |
| 1354 | 1669 | ||
| 1670 | fn 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 | |||
| 1355 | fn writeStrtab(self: *Coff) !void { | 1753 | fn 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()); |
| 1358 | 1758 | ||
| 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 | } |
| 1363 | 1763 | ||
| 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 | } |
| 1367 | 1776 | ||
| 1368 | fn writeSectionHeaders(self: *Coff) !void { | 1777 | fn 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 | } |
| 1528 | 1937 | ||
| 1529 | fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { | 1938 | fn 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; |
| 1533 | 1942 | ||
| 1534 | const end = start + size; | 1943 | const end = start + padToIdeal(size); |
| 1535 | 1944 | ||
| 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 | } |
| 1543 | 1953 | ||
| 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 | } |
| 1554 | 1965 | ||
| 1555 | pub fn allocatedSize(self: *Coff, start: u32) u32 { | 1966 | fn 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 | } |
| 1568 | 1979 | ||
| 1569 | pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { | 1980 | fn 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 | } |
| 1576 | 1987 | ||
| 1988 | fn 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 | |||
| 1577 | inline fn getSizeOfHeaders(self: Coff) u32 { | 1999 | inline 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 { |
| 1614 | 2036 | ||
| 1615 | /// Returns symbol location corresponding to the set entrypoint (if any). | 2037 | /// Returns symbol location corresponding to the set entrypoint (if any). |
| 1616 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { | 2038 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { |
| 1617 | const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete | 2039 | 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 | } |
| 1620 | 2043 | ||
| 1621 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. | 2044 | /// Returns pointer-to-symbol described by `sym_loc` descriptor. |
| 1622 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { | 2045 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { |
| 1623 | assert(sym_loc.file == null); // TODO linking object files | 2046 | 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 | } |
| 1626 | 2049 | ||
| 1627 | /// Returns symbol described by `sym_with_loc` descriptor. | 2050 | /// Returns symbol described by `sym_loc` descriptor. |
| 1628 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { | 2051 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { |
| 1629 | assert(sym_loc.file == null); // TODO linking object files | 2052 | 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 | } |
| 1632 | 2055 | ||
| 1633 | /// Returns name of the symbol described by `sym_with_loc` descriptor. | 2056 | /// Returns name of the symbol described by `sym_loc` descriptor. |
| 1634 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { | 2057 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1635 | assert(sym_loc.file == null); // TODO linking object files | 2058 | 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 | } |
| 1640 | 2063 | ||
| 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. |
| 1643 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | 2066 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1644 | assert(sym_loc.file == null); // TODO linking with object files | 2067 | 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 | } |
| 1647 | 2070 | ||
| 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. |
| 1650 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | 2073 | pub 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. | ||
| 2081 | pub 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 | } |
| 1654 | 2086 | ||
| 1655 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { | 2087 | fn 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 | } |
| 1665 | 2097 | ||
| 2098 | fn 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 | |||
| 1666 | fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { | 2106 | fn 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 | } |
| 1726 | 2166 | ||
| 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 | } |
| 1732 | 2172 | ||
| 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 | |||
| 2195 | fn 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"); |
| 4 | const coff = std.coff; | 4 | const coff = std.coff; |
| 5 | const log = std.log.scoped(.link); | 5 | const log = std.log.scoped(.link); |
| 6 | 6 | ||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | |||
| 9 | const Coff = @import("../Coff.zig"); | 7 | const Coff = @import("../Coff.zig"); |
| 10 | const Reloc = Coff.Reloc; | 8 | const Reloc = Coff.Reloc; |
| 11 | const SymbolWithLoc = Coff.SymbolWithLoc; | 9 | const 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 | }; |
| 43 | 41 | ||
| 44 | pub 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. |
| 50 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { | 43 | pub 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 | |||
| 115 | pub 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; |
| 1156 | 1158 | ||
| 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 { |
| 1435 | 1460 | ||
| 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 ToC | 1463 | // 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 | } |
| 3088 | 3112 | ||
| 3089 | pub 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 | |||
| 3098 | fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void { | 3113 | fn 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 | } |
| 5700 | 5715 | ||
| 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 | } |
| 5704 | 5721 | ||
| 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 | }; |
| 89 | 89 | ||
| 90 | pub fn deinit(self: *Archive, allocator: Allocator) void { | 90 | pub 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 | } |
| 307 | 307 | ||
| 308 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { | 308 | pub 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 | } |
| 701 | 700 | ||
| 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 | } |
| 706 | 704 | ||
| ... | @@ -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 | } |
| 3223 | 3227 | ||
| 3224 | fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { | 3228 | fn 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' section | 3230 | 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 | } |
| 3230 | 3240 | ||
| 3231 | fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { | 3241 | fn 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 | }; |
| 96 | 96 | ||
| 97 | pub fn deinit(archive: *Archive, allocator: Allocator) void { | 97 | pub 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 be | 154 | /// 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. |
| 156 | pub fn deinit(self: *Object, gpa: Allocator) void { | 156 | pub 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 | } |
| 112 | 112 | ||
| 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 | // Wasm | 182 | // 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.Node | 1549 | // 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(); |
| 1551 | 1559 |
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=aarch64-macos | 3 | // 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 here | 6 | // :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=Exe | 2 | // output_mode=Exe |
| 3 | // target=x86_64-linux | 3 | // 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 here | 6 | // :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=Exe | 2 | // output_mode=Exe |
| 3 | // target=x86_64-macos | 3 | // 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 here | 6 | // :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 @@ | ||
| 1 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn main() void { | ||
| 4 | print(); | ||
| 5 | } | ||
| 6 | |||
| 7 | fn 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 | } |
| 29 | 29 | ||
| 30 | fn addWasmCases(cases: *tests.StandaloneContext) void { | 30 | fn 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 | }); |
| 35 | 35 | ||
| 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 | }); |
| 40 | 40 | ||
| 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 | }); |
| 45 | 46 | ||
| 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 | }); |
| 50 | 51 | ||
| 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 | }); |
| 55 | 56 | ||
| 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 | } |
| 62 | 62 |
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 | }, | ||
| 111 | 119 | ||
| 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 | }; |
| 698 | 708 |