| 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 | 73 | & "$ZIGINSTALLDIR\bin\zig.exe" build test docs ` |
| 74 | 74 | --search-prefix "$ZIGPREFIXPATH" ` |
| 75 | 75 | -Dstatic-llvm ` |
| 76 | -Dskip-non-native ` | |
| 77 | -Dskip-stage2-tests | |
| 76 | -Dskip-non-native | |
| 78 | 77 | CheckLastExitCode |
| 79 | 78 | name: test |
| 80 | 79 | displayName: 'Test' |
lib/std/fs/file.zig+16| ... | ... | @@ -990,6 +990,8 @@ pub const File = struct { |
| 990 | 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 | 995 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 994 | 996 | if (is_windows) { |
| 995 | 997 | return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode); |
| ... | ... | @@ -1004,6 +1006,8 @@ pub const File = struct { |
| 1004 | 1006 | |
| 1005 | 1007 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it |
| 1006 | 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 | 1011 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 1008 | 1012 | var index: usize = 0; |
| 1009 | 1013 | while (index != buffer.len) { |
| ... | ... | @@ -1058,6 +1062,8 @@ pub const File = struct { |
| 1058 | 1062 | } |
| 1059 | 1063 | |
| 1060 | 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 | 1067 | pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { |
| 1062 | 1068 | if (is_windows) { |
| 1063 | 1069 | // TODO improve this to use ReadFileScatter |
| ... | ... | @@ -1079,6 +1085,8 @@ pub const File = struct { |
| 1079 | 1085 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1080 | 1086 | /// order to handle partial reads from the underlying OS layer. |
| 1081 | 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 | 1090 | pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize { |
| 1083 | 1091 | if (iovecs.len == 0) return 0; |
| 1084 | 1092 | |
| ... | ... | @@ -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 | 1135 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1126 | 1136 | if (is_windows) { |
| 1127 | 1137 | return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode); |
| ... | ... | @@ -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 | 1149 | pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void { |
| 1138 | 1150 | var index: usize = 0; |
| 1139 | 1151 | while (index < bytes.len) { |
| ... | ... | @@ -1179,6 +1191,8 @@ pub const File = struct { |
| 1179 | 1191 | } |
| 1180 | 1192 | |
| 1181 | 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 | 1196 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize { |
| 1183 | 1197 | if (is_windows) { |
| 1184 | 1198 | // TODO improve this to use WriteFileScatter |
| ... | ... | @@ -1197,6 +1211,8 @@ pub const File = struct { |
| 1197 | 1211 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1198 | 1212 | /// order to handle partial writes from the underlying OS layer. |
| 1199 | 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 | 1216 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void { |
| 1201 | 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 | 36 | |
| 37 | 37 | fn getStdOutHandle() os.fd_t { |
| 38 | 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 | 43 | return os.windows.peb().ProcessParameters.hStdOutput; |
| 40 | 44 | } |
| 41 | 45 | |
| ... | ... | @@ -58,6 +62,10 @@ pub fn getStdOut() File { |
| 58 | 62 | |
| 59 | 63 | fn getStdErrHandle() os.fd_t { |
| 60 | 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 | 69 | return os.windows.peb().ProcessParameters.hStdError; |
| 62 | 70 | } |
| 63 | 71 | |
| ... | ... | @@ -80,6 +88,10 @@ pub fn getStdErr() File { |
| 80 | 88 | |
| 81 | 89 | fn getStdInHandle() os.fd_t { |
| 82 | 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 | 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 | 348 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 349 | 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 | 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 | 36 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { |
| 37 | 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 | 43 | } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { |
| 40 | 44 | @export(wasiMain2, .{ .name = "_start" }); |
| 41 | 45 | } else { |
src/arch/x86_64/CodeGen.zig+372-229| ... | ... | @@ -32,11 +32,6 @@ const abi = @import("abi.zig"); |
| 32 | 32 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 33 | 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 | 35 | const Condition = bits.Condition; |
| 41 | 36 | const RegisterManager = abi.RegisterManager; |
| 42 | 37 | const RegisterLock = RegisterManager.RegisterLock; |
| ... | ... | @@ -137,6 +132,7 @@ pub const MCValue = union(enum) { |
| 137 | 132 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. |
| 138 | 133 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT. |
| 139 | 134 | got_load: u32, |
| 135 | imports_load: u32, | |
| 140 | 136 | /// The value is in memory referenced directly via symbol index. |
| 141 | 137 | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. |
| 142 | 138 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED. |
| ... | ... | @@ -156,6 +152,7 @@ pub const MCValue = union(enum) { |
| 156 | 152 | .ptr_stack_offset, |
| 157 | 153 | .direct_load, |
| 158 | 154 | .got_load, |
| 155 | .imports_load, | |
| 159 | 156 | => true, |
| 160 | 157 | else => false, |
| 161 | 158 | }; |
| ... | ... | @@ -203,6 +200,42 @@ const Branch = struct { |
| 203 | 200 | self.inst_table.deinit(gpa); |
| 204 | 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 | 241 | const StackAllocation = struct { |
| ... | ... | @@ -235,7 +268,7 @@ const BigTomb = struct { |
| 235 | 268 | fn finishAir(bt: *BigTomb, result: MCValue) void { |
| 236 | 269 | const is_used = !bt.function.liveness.isUnused(bt.inst); |
| 237 | 270 | if (is_used) { |
| 238 | log.debug("%{d} => {}", .{ bt.inst, result }); | |
| 271 | log.debug(" (saving %{d} => {})", .{ bt.inst, result }); | |
| 239 | 272 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; |
| 240 | 273 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); |
| 241 | 274 | } |
| ... | ... | @@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void { |
| 406 | 439 | }); |
| 407 | 440 | |
| 408 | 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 | 443 | // register which the callee is free to clobber. Therefore, we purposely |
| 411 | 444 | // spill it to stack immediately. |
| 412 | 445 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8); |
| 413 | 446 | self.next_stack_offset = stack_offset; |
| 414 | 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 | 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 | 455 | _ = try self.addInst(.{ |
| ... | ... | @@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void { |
| 446 | 480 | |
| 447 | 481 | // Create list of registers to save in the prologue. |
| 448 | 482 | // TODO handle register classes |
| 449 | var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{}; | |
| 450 | inline for (callee_preserved_regs) |reg| { | |
| 483 | var reg_list = Mir.RegisterList{}; | |
| 484 | const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*); | |
| 485 | for (callee_preserved_regs) |reg| { | |
| 451 | 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 | 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 | 832 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 798 | 833 | const air_tags = self.air.instructions.items(.tag); |
| 799 | 834 | if (air_tags[inst] == .constant) return; // Constants are immortal. |
| 835 | log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } }); | |
| 800 | 836 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| 801 | 837 | const prev_value = self.getResolvedInstValue(inst); |
| 802 | 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 | 2310 | .memory, |
| 2275 | 2311 | .got_load, |
| 2276 | 2312 | .direct_load, |
| 2313 | .imports_load, | |
| 2277 | 2314 | => { |
| 2278 | 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 | 2655 | .memory, |
| 2619 | 2656 | .got_load, |
| 2620 | 2657 | .direct_load, |
| 2658 | .imports_load, | |
| 2621 | 2659 | => { |
| 2622 | 2660 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 2623 | 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 | 2693 | switch (ptr) { |
| 2656 | 2694 | .got_load, |
| 2657 | 2695 | .direct_load, |
| 2696 | .imports_load, | |
| 2658 | 2697 | => |sym_index| { |
| 2659 | 2698 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2660 | 2699 | const mod = self.bin_file.options.module.?; |
| ... | ... | @@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2666 | 2705 | const flags: u2 = switch (ptr) { |
| 2667 | 2706 | .got_load => 0b00, |
| 2668 | 2707 | .direct_load => 0b01, |
| 2708 | .imports_load => 0b10, | |
| 2669 | 2709 | else => unreachable, |
| 2670 | 2710 | }; |
| 2671 | 2711 | _ = try self.addInst(.{ |
| ... | ... | @@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2763 | 2803 | }, |
| 2764 | 2804 | .got_load, |
| 2765 | 2805 | .direct_load, |
| 2806 | .imports_load, | |
| 2766 | 2807 | .memory, |
| 2767 | 2808 | .stack_offset, |
| 2768 | 2809 | => { |
| ... | ... | @@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2783 | 2824 | }, |
| 2784 | 2825 | .got_load, |
| 2785 | 2826 | .direct_load, |
| 2827 | .imports_load, | |
| 2786 | 2828 | .memory, |
| 2787 | 2829 | => { |
| 2788 | 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 | 2896 | }, |
| 2855 | 2897 | .got_load, |
| 2856 | 2898 | .direct_load, |
| 2899 | .imports_load, | |
| 2857 | 2900 | .memory, |
| 2858 | 2901 | => { |
| 2859 | 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 | 3608 | .memory, |
| 3566 | 3609 | .got_load, |
| 3567 | 3610 | .direct_load, |
| 3611 | .imports_load, | |
| 3568 | 3612 | .eflags, |
| 3569 | 3613 | => { |
| 3570 | 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 | 3694 | => { |
| 3651 | 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 | 3701 | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); |
| 3655 | 3702 | }, |
| 3656 | 3703 | .eflags => { |
| ... | ... | @@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3661 | 3708 | .memory => { |
| 3662 | 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 | 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 | 3779 | .memory => { |
| 3730 | 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 | 3786 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3734 | 3787 | }, |
| 3735 | 3788 | .eflags => { |
| ... | ... | @@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3773 | 3826 | .memory, .stack_offset => { |
| 3774 | 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 | 3833 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3778 | 3834 | }, |
| 3779 | 3835 | .eflags => { |
| ... | ... | @@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3784 | 3840 | .memory => { |
| 3785 | 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 | 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 | 3957 | |
| 3899 | 3958 | try self.spillEflagsIfOccupied(); |
| 3900 | 3959 | |
| 3901 | for (caller_preserved_regs) |reg| { | |
| 3960 | for (abi.getCallerPreservedRegs(self.target.*)) |reg| { | |
| 3902 | 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 | 3965 | if (info.return_value == .stack_offset) { |
| 3907 | 3966 | const ret_ty = fn_ty.fnReturnType(); |
| 3908 | 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 | 3969 | const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); |
| 3911 | 3970 | log.debug("airCall: return value on stack at offset {}", .{stack_offset}); |
| 3912 | 3971 | |
| 3913 | try self.register_manager.getReg(.rdi, null); | |
| 3914 | try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); | |
| 3915 | const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi); | |
| 3972 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; | |
| 3973 | try self.register_manager.getReg(ret_reg, null); | |
| 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 | 3977 | info.return_value.stack_offset = stack_offset; |
| 3918 | 3978 | |
| 3919 | break :blk rdi_lock; | |
| 3979 | break :blk ret_reg_lock; | |
| 3920 | 3980 | } |
| 3921 | 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 | 3985 | for (args) |arg, arg_i| { |
| 3926 | 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 | 4008 | .memory => unreachable, |
| 3949 | 4009 | .got_load => unreachable, |
| 3950 | 4010 | .direct_load => unreachable, |
| 4011 | .imports_load => unreachable, | |
| 3951 | 4012 | .eflags => unreachable, |
| 3952 | 4013 | .register_overflow => unreachable, |
| 3953 | 4014 | } |
| ... | ... | @@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3999 | 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 | 4064 | if (self.air.value(callee)) |func_value| { |
| 4004 | 4065 | if (func_value.castTag(.function)) |func_payload| { |
| 4005 | 4066 | const func = func_payload.data; |
| ... | ... | @@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4015 | 4076 | }), |
| 4016 | 4077 | .data = undefined, |
| 4017 | 4078 | }); |
| 4018 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4019 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4079 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 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 | 4100 | } else { |
| 4021 | 4101 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 4022 | 4102 | } |
| ... | ... | @@ -4425,7 +4505,11 @@ fn genVarDbgInfo( |
| 4425 | 4505 | leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; |
| 4426 | 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 | 4513 | const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); |
| 4430 | 4514 | const is_ptr = switch (tag) { |
| 4431 | 4515 | .dbg_var_ptr => true, |
| ... | ... | @@ -4456,7 +4540,10 @@ fn genVarDbgInfo( |
| 4456 | 4540 | try dbg_info.append(DW.OP.deref); |
| 4457 | 4541 | } |
| 4458 | 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 | 4547 | else => {}, |
| 4461 | 4548 | } |
| 4462 | 4549 | }, |
| ... | ... | @@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4626 | 4713 | |
| 4627 | 4714 | // Revert to the previous register and stack allocation state. |
| 4628 | 4715 | |
| 4629 | var saved_then_branch = self.branch_stack.pop(); | |
| 4630 | defer saved_then_branch.deinit(self.gpa); | |
| 4716 | var then_branch = self.branch_stack.pop(); | |
| 4717 | defer then_branch.deinit(self.gpa); | |
| 4631 | 4718 | |
| 4632 | 4719 | self.revertState(saved_state); |
| 4633 | 4720 | |
| 4634 | 4721 | try self.performReloc(reloc); |
| 4635 | 4722 | |
| 4636 | const else_branch = self.branch_stack.addOneAssumeCapacity(); | |
| 4637 | else_branch.* = .{}; | |
| 4723 | try self.branch_stack.append(.{}); | |
| 4724 | errdefer { | |
| 4725 | _ = self.branch_stack.pop(); | |
| 4726 | } | |
| 4638 | 4727 | |
| 4639 | 4728 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); |
| 4640 | 4729 | for (liveness_condbr.else_deaths) |operand| { |
| ... | ... | @@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4642 | 4731 | } |
| 4643 | 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 | 4737 | // At this point, each branch will possibly have conflicting values for where |
| 4646 | 4738 | // each instruction is stored. They agree, however, on which instructions are alive/dead. |
| 4647 | 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 | 4742 | // that we can use all the code emitting abstractions. This is why at the bottom we |
| 4651 | 4743 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers |
| 4652 | 4744 | // rather than assigning it. |
| 4653 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; | |
| 4654 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); | |
| 4655 | ||
| 4656 | const else_slice = else_branch.inst_table.entries.slice(); | |
| 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 | |
| 4745 | log.debug("airCondBr: %{d}", .{inst}); | |
| 4746 | log.debug("Upper branches:", .{}); | |
| 4747 | for (self.branch_stack.items) |bs| { | |
| 4748 | log.debug("{}", .{bs.fmtDebug()}); | |
| 4715 | 4749 | } |
| 4716 | 4750 | |
| 4717 | { | |
| 4718 | var item = self.branch_stack.pop(); | |
| 4719 | item.deinit(self.gpa); | |
| 4720 | } | |
| 4751 | log.debug("Then branch: {}", .{then_branch.fmtDebug()}); | |
| 4752 | log.debug("Else branch: {}", .{else_branch.fmtDebug()}); | |
| 4753 | ||
| 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 | 4757 | // We already took care of pl_op.operand earlier, so we're going |
| 4723 | 4758 | // to pass .none here |
| ... | ... | @@ -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 | 5149 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 5106 | 5150 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 5107 | 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 | 5175 | |
| 5132 | 5176 | try self.genBody(case_body); |
| 5133 | 5177 | |
| 5134 | // Revert to the previous register and stack allocation state. | |
| 5135 | var saved_case_branch = self.branch_stack.pop(); | |
| 5136 | defer saved_case_branch.deinit(self.gpa); | |
| 5178 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); | |
| 5137 | 5179 | |
| 5180 | // Revert to the previous register and stack allocation state. | |
| 5138 | 5181 | self.revertState(saved_state); |
| 5139 | 5182 | |
| 5140 | 5183 | for (relocs) |reloc| { |
| ... | ... | @@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5144 | 5187 | |
| 5145 | 5188 | if (switch_br.data.else_body_len > 0) { |
| 5146 | 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 | 5194 | try self.branch_stack.append(.{}); |
| 5148 | defer { | |
| 5149 | var item = self.branch_stack.pop(); | |
| 5150 | item.deinit(self.gpa); | |
| 5195 | errdefer { | |
| 5196 | _ = self.branch_stack.pop(); | |
| 5151 | 5197 | } |
| 5152 | 5198 | |
| 5153 | 5199 | const else_deaths = liveness.deaths.len - 1; |
| ... | ... | @@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5158 | 5204 | |
| 5159 | 5205 | try self.genBody(else_body); |
| 5160 | 5206 | |
| 5161 | // TODO consolidate returned MCValues between prongs and else branch like we do | |
| 5162 | // in airCondBr. | |
| 5207 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); | |
| 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 | 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 | 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 | 5304 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5171 | 5305 | const next_inst = @intCast(u32, self.mir_instructions.len); |
| 5172 | 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 | 5330 | block_data.mcv = switch (operand_mcv) { |
| 5197 | 5331 | .none, .dead, .unreach => unreachable, |
| 5198 | 5332 | .register, .stack_offset, .memory => operand_mcv, |
| 5199 | .eflags, .immediate => blk: { | |
| 5333 | .eflags, .immediate, .ptr_stack_offset => blk: { | |
| 5200 | 5334 | const new_mcv = try self.allocRegOrMem(block, true); |
| 5201 | 5335 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 5202 | 5336 | break :blk new_mcv; |
| ... | ... | @@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5456 | 5590 | .memory, |
| 5457 | 5591 | .direct_load, |
| 5458 | 5592 | .got_load, |
| 5593 | .imports_load, | |
| 5459 | 5594 | => { |
| 5460 | 5595 | if (abi_size <= 8) { |
| 5461 | 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 | 5838 | .memory, |
| 5704 | 5839 | .got_load, |
| 5705 | 5840 | .direct_load, |
| 5841 | .imports_load, | |
| 5706 | 5842 | => { |
| 5707 | 5843 | if (abi_size <= 8) { |
| 5708 | 5844 | const reg = try self.copyToTmpRegister(ty, mcv); |
| ... | ... | @@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct { |
| 5796 | 5932 | dest_stack_base: ?Register = null, |
| 5797 | 5933 | }; |
| 5798 | 5934 | |
| 5799 | /// Spills .rax and .rcx. | |
| 5800 | 5935 | fn genInlineMemcpy( |
| 5801 | 5936 | self: *Self, |
| 5802 | 5937 | dst_ptr: MCValue, |
| ... | ... | @@ -5804,15 +5939,6 @@ fn genInlineMemcpy( |
| 5804 | 5939 | len: MCValue, |
| 5805 | 5940 | opts: InlineMemcpyOpts, |
| 5806 | 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 | 5942 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| |
| 5817 | 5943 | self.register_manager.lockReg(reg) |
| 5818 | 5944 | else |
| ... | ... | @@ -5825,11 +5951,18 @@ fn genInlineMemcpy( |
| 5825 | 5951 | null; |
| 5826 | 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 | 5961 | switch (dst_ptr) { |
| 5830 | 5962 | .memory, |
| 5831 | 5963 | .got_load, |
| 5832 | 5964 | .direct_load, |
| 5965 | .imports_load, | |
| 5833 | 5966 | => { |
| 5834 | 5967 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); |
| 5835 | 5968 | }, |
| ... | ... | @@ -5857,14 +5990,12 @@ fn genInlineMemcpy( |
| 5857 | 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 | 5994 | switch (src_ptr) { |
| 5865 | 5995 | .memory, |
| 5866 | 5996 | .got_load, |
| 5867 | 5997 | .direct_load, |
| 5998 | .imports_load, | |
| 5868 | 5999 | => { |
| 5869 | 6000 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); |
| 5870 | 6001 | }, |
| ... | ... | @@ -5892,26 +6023,13 @@ fn genInlineMemcpy( |
| 5892 | 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 | 6027 | try self.genSetReg(Type.usize, count_reg, len); |
| 5903 | 6028 | |
| 5904 | // mov rcx, 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 | |
| 6029 | // mov index_reg, 0 | |
| 5912 | 6030 | _ = try self.addInst(.{ |
| 5913 | 6031 | .tag = .mov, |
| 5914 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6032 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 5915 | 6033 | .data = .{ .imm = 0 }, |
| 5916 | 6034 | }); |
| 5917 | 6035 | |
| ... | ... | @@ -5933,37 +6051,30 @@ fn genInlineMemcpy( |
| 5933 | 6051 | } }, |
| 5934 | 6052 | }); |
| 5935 | 6053 | |
| 5936 | // mov tmp, [addr + rcx] | |
| 6054 | // mov tmp, [addr + index_reg] | |
| 5937 | 6055 | _ = try self.addInst(.{ |
| 5938 | 6056 | .tag = .mov_scale_src, |
| 5939 | 6057 | .ops = Mir.Inst.Ops.encode(.{ |
| 5940 | 6058 | .reg1 = tmp_reg.to8(), |
| 5941 | 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 | 6065 | _ = try self.addInst(.{ |
| 5948 | 6066 | .tag = .mov_scale_dst, |
| 5949 | 6067 | .ops = Mir.Inst.Ops.encode(.{ |
| 5950 | 6068 | .reg1 = dst_addr_reg, |
| 5951 | 6069 | .reg2 = tmp_reg.to8(), |
| 5952 | 6070 | }), |
| 5953 | .data = .{ .imm = 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 }, | |
| 6071 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | |
| 5961 | 6072 | }); |
| 5962 | 6073 | |
| 5963 | // add rax, 1 | |
| 6074 | // add index_reg, 1 | |
| 5964 | 6075 | _ = try self.addInst(.{ |
| 5965 | 6076 | .tag = .add, |
| 5966 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6077 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 5967 | 6078 | .data = .{ .imm = 1 }, |
| 5968 | 6079 | }); |
| 5969 | 6080 | |
| ... | ... | @@ -5985,7 +6096,6 @@ fn genInlineMemcpy( |
| 5985 | 6096 | try self.performReloc(loop_reloc); |
| 5986 | 6097 | } |
| 5987 | 6098 | |
| 5988 | /// Spills .rax register. | |
| 5989 | 6099 | fn genInlineMemset( |
| 5990 | 6100 | self: *Self, |
| 5991 | 6101 | dst_ptr: MCValue, |
| ... | ... | @@ -5993,16 +6103,27 @@ fn genInlineMemset( |
| 5993 | 6103 | len: MCValue, |
| 5994 | 6104 | opts: InlineMemcpyOpts, |
| 5995 | 6105 | ) InnerError!void { |
| 5996 | // TODO preserve contents of .rax and then restore | |
| 5997 | try self.register_manager.getReg(.rax, null); | |
| 5998 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); | |
| 5999 | defer self.register_manager.unlockReg(rax_lock); | |
| 6106 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| | |
| 6107 | self.register_manager.lockReg(reg) | |
| 6108 | else | |
| 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 | 6122 | switch (dst_ptr) { |
| 6003 | 6123 | .memory, |
| 6004 | 6124 | .got_load, |
| 6005 | 6125 | .direct_load, |
| 6126 | .imports_load, | |
| 6006 | 6127 | => { |
| 6007 | 6128 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); |
| 6008 | 6129 | }, |
| ... | ... | @@ -6030,17 +6151,15 @@ fn genInlineMemset( |
| 6030 | 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); | |
| 6037 | try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); | |
| 6155 | try self.genSetReg(Type.usize, index_reg, len); | |
| 6156 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); | |
| 6038 | 6157 | |
| 6039 | 6158 | // loop: |
| 6040 | // cmp rax, -1 | |
| 6159 | // cmp index_reg, -1 | |
| 6041 | 6160 | const loop_start = try self.addInst(.{ |
| 6042 | 6161 | .tag = .cmp, |
| 6043 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6162 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6044 | 6163 | .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, |
| 6045 | 6164 | }); |
| 6046 | 6165 | |
| ... | ... | @@ -6059,24 +6178,20 @@ fn genInlineMemset( |
| 6059 | 6178 | if (x > math.maxInt(i32)) { |
| 6060 | 6179 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); |
| 6061 | 6180 | } |
| 6062 | // mov byte ptr [rbp + rax + stack_offset], imm | |
| 6063 | const payload = try self.addExtra(Mir.ImmPair{ | |
| 6064 | .dest_off = 0, | |
| 6065 | .operand = @truncate(u32, x), | |
| 6066 | }); | |
| 6181 | // mov byte ptr [rbp + index_reg + stack_offset], imm | |
| 6067 | 6182 | _ = try self.addInst(.{ |
| 6068 | 6183 | .tag = .mov_mem_index_imm, |
| 6069 | 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 | 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 | 6192 | _ = try self.addInst(.{ |
| 6078 | 6193 | .tag = .sub, |
| 6079 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6194 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6080 | 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 | 6358 | }, |
| 6244 | 6359 | .direct_load, |
| 6245 | 6360 | .got_load, |
| 6361 | .imports_load, | |
| 6246 | 6362 | => { |
| 6247 | 6363 | switch (ty.zigTypeTag()) { |
| 6248 | 6364 | .Float => { |
| ... | ... | @@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 6637 | 6753 | // TODO Is this the only condition for pointer dereference for memcpy? |
| 6638 | 6754 | const src: MCValue = blk: { |
| 6639 | 6755 | switch (src_ptr) { |
| 6640 | .got_load, .direct_load, .memory => { | |
| 6756 | .got_load, | |
| 6757 | .direct_load, | |
| 6758 | .imports_load, | |
| 6759 | .memory, | |
| 6760 | => { | |
| 6641 | 6761 | const reg = try self.register_manager.allocReg(null, gp); |
| 6642 | 6762 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); |
| 6643 | 6763 | _ = try self.addInst(.{ |
| ... | ... | @@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6901 | 7021 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6902 | 7022 | return MCValue{ .direct_load = local_sym_index }; |
| 6903 | 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 | 7025 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
| 6906 | 7026 | return self.fail("TODO lower unnamed const in Plan9", .{}); |
| 6907 | 7027 | } else { |
| ... | ... | @@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7066 | 7186 | result.stack_align = 1; |
| 7067 | 7187 | return result; |
| 7068 | 7188 | }, |
| 7069 | .Unspecified, .C => { | |
| 7189 | .C => { | |
| 7070 | 7190 | // Return values |
| 7071 | 7191 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 7072 | 7192 | result.return_value = .{ .unreach = {} }; |
| 7073 | 7193 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 7194 | // TODO: is this even possible for C calling convention? | |
| 7074 | 7195 | result.return_value = .{ .none = {} }; |
| 7075 | 7196 | } else { |
| 7076 | 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 | 7199 | assert(ret_ty.isError()); |
| 7079 | 7200 | result.return_value = .{ .immediate = 0 }; |
| 7080 | 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 | 7203 | result.return_value = .{ .register = aliased_reg }; |
| 7083 | 7204 | } else { |
| 7084 | // We simply make the return MCValue a stack offset. However, the actual value | |
| 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. | |
| 7205 | // TODO: return argument cell should go first | |
| 7087 | 7206 | result.return_value = .{ .stack_offset = 0 }; |
| 7088 | 7207 | } |
| 7089 | 7208 | } |
| 7090 | 7209 | |
| 7091 | 7210 | // Input params |
| 7092 | // First, split into args that can be passed via registers. | |
| 7093 | // This will make it easier to then push the rest of args in reverse | |
| 7094 | // order on the stack. | |
| 7095 | var next_int_reg: usize = 0; | |
| 7096 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); | |
| 7097 | defer by_reg.deinit(); | |
| 7098 | ||
| 7099 | // If we want debug output, we store all args on stack for better liveness of args | |
| 7100 | // in debugging contexts such as previewing the args in the debugger anywhere in | |
| 7101 | // the procedure. Passing the args via registers can lead to reusing the register | |
| 7102 | // for local ops thus clobbering the input arg forever. | |
| 7103 | // This of course excludes C ABI calls. | |
| 7104 | const omit_args_in_registers = blk: { | |
| 7105 | if (cc == .C) break :blk false; | |
| 7106 | switch (self.bin_file.options.optimize_mode) { | |
| 7107 | .Debug => break :blk true, | |
| 7108 | else => break :blk false, | |
| 7211 | var next_stack_offset: u32 = switch (result.return_value) { | |
| 7212 | .stack_offset => |off| @intCast(u32, off), | |
| 7213 | else => 0, | |
| 7214 | }; | |
| 7215 | ||
| 7216 | for (param_types) |ty, i| { | |
| 7217 | assert(ty.hasRuntimeBits()); | |
| 7218 | ||
| 7219 | const classes: []const abi.Class = switch (self.target.os.tag) { | |
| 7220 | .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, | |
| 7221 | else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*), .none), | |
| 7222 | }; | |
| 7223 | if (classes.len > 1) { | |
| 7224 | return self.fail("TODO handle multiple classes per type", .{}); | |
| 7225 | } | |
| 7226 | switch (classes[0]) { | |
| 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) { | |
| 7112 | for (param_types) |ty, i| { | |
| 7113 | if (!ty.hasRuntimeBits()) continue; | |
| 7114 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 7115 | // For simplicity of codegen, slices and other types are always pushed onto the stack. | |
| 7116 | // TODO: look into optimizing this by passing things as registers sometimes, | |
| 7117 | // such as ptr and len of slices as separate registers. | |
| 7118 | // TODO: also we need to honor the C ABI for relevant types rather than passing on | |
| 7119 | // the stack here. | |
| 7120 | const pass_in_reg = switch (ty.zigTypeTag()) { | |
| 7121 | .Bool => true, | |
| 7122 | .Int, .Enum => param_size <= 8, | |
| 7123 | .Pointer => ty.ptrSize() != .Slice, | |
| 7124 | .Optional => ty.isPtrLikeOptional(), | |
| 7125 | else => false, | |
| 7126 | }; | |
| 7127 | if (pass_in_reg) { | |
| 7128 | if (next_int_reg >= c_abi_int_param_regs.len) break; | |
| 7129 | try by_reg.putNoClobber(i, next_int_reg); | |
| 7130 | next_int_reg += 1; | |
| 7131 | } | |
| 7259 | ||
| 7260 | // alignment padding | args ... | shadow stack space (if any) | ret addr | $rbp | | |
| 7261 | result.stack_byte_count = aligned_next_stack_offset + shadow_stack_space; | |
| 7262 | result.stack_align = 16; | |
| 7263 | }, | |
| 7264 | .Unspecified => { | |
| 7265 | // Return values | |
| 7266 | if (ret_ty.zigTypeTag() == .NoReturn) { | |
| 7267 | result.return_value = .{ .unreach = {} }; | |
| 7268 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { | |
| 7269 | result.return_value = .{ .none = {} }; | |
| 7270 | } else { | |
| 7271 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | |
| 7272 | if (ret_ty_size == 0) { | |
| 7273 | assert(ret_ty.isError()); | |
| 7274 | result.return_value = .{ .immediate = 0 }; | |
| 7275 | } else if (ret_ty_size <= 8) { | |
| 7276 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); | |
| 7277 | result.return_value = .{ .register = aliased_reg }; | |
| 7278 | } else { | |
| 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 | 7287 | var next_stack_offset: u32 = switch (result.return_value) { |
| 7136 | 7288 | .stack_offset => |off| @intCast(u32, off), |
| 7137 | 7289 | else => 0, |
| 7138 | 7290 | }; |
| 7139 | var count: usize = param_types.len; | |
| 7140 | while (count > 0) : (count -= 1) { | |
| 7141 | const i = count - 1; | |
| 7142 | const ty = param_types[i]; | |
| 7291 | ||
| 7292 | for (param_types) |ty, i| { | |
| 7143 | 7293 | if (!ty.hasRuntimeBits()) { |
| 7144 | assert(cc != .C); | |
| 7145 | 7294 | result.args[i] = .{ .none = {} }; |
| 7146 | 7295 | continue; |
| 7147 | 7296 | } |
| 7148 | 7297 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 7149 | 7298 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); |
| 7150 | if (by_reg.get(i)) |int_reg| { | |
| 7151 | const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size); | |
| 7152 | result.args[i] = .{ .register = aliased_reg }; | |
| 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 | } | |
| 7299 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | |
| 7300 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | |
| 7301 | next_stack_offset = offset; | |
| 7159 | 7302 | } |
| 7160 | 7303 | |
| 7161 | 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 | 283 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 284 | 284 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 285 | 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 | 286 | var disp: i32 = -@intCast(i32, save_reg_list.stack_end); |
| 288 | inline for (abi.callee_preserved_regs) |reg| { | |
| 289 | if (reg_list.isSet(reg)) { | |
| 287 | const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); | |
| 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 | 291 | switch (tag) { |
| 291 | 292 | .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ |
| 292 | 293 | .disp = @bitCast(u32, disp), |
| ... | ... | @@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 { |
| 614 | 615 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 615 | 616 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 616 | 617 | const scale = ops.flags; |
| 617 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 618 | // OP reg1, [reg2 + scale*rcx + imm32] | |
| 618 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 619 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 620 | // OP reg1, [reg2 + scale*index + imm32] | |
| 619 | 621 | const scale_index = ScaleIndex{ |
| 620 | 622 | .scale = scale, |
| 621 | .index = .rcx, | |
| 623 | .index = index_reg_disp.index, | |
| 622 | 624 | }; |
| 623 | 625 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 624 | .disp = imm, | |
| 626 | .disp = index_reg_disp.disp, | |
| 625 | 627 | .base = ops.reg2, |
| 626 | 628 | .scale_index = scale_index, |
| 627 | 629 | }), emit.code); |
| ... | ... | @@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 630 | 632 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 631 | 633 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 632 | 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 | 637 | const scale_index = ScaleIndex{ |
| 635 | 638 | .scale = scale, |
| 636 | .index = .rax, | |
| 639 | .index = index_reg_disp.index, | |
| 637 | 640 | }; |
| 638 | if (ops.reg2 == .none) { | |
| 639 | // OP qword ptr [reg1 + scale*rax + 0], imm32 | |
| 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 | |
| 641 | assert(ops.reg2 != .none); | |
| 642 | // OP [reg1 + scale*index + imm32], reg2 | |
| 647 | 643 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ |
| 648 | .disp = imm, | |
| 644 | .disp = index_reg_disp.disp, | |
| 649 | 645 | .base = ops.reg1, |
| 650 | 646 | .scale_index = scale_index, |
| 651 | 647 | }), ops.reg2, emit.code); |
| ... | ... | @@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 655 | 651 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 656 | 652 | const scale = ops.flags; |
| 657 | 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 | 655 | const scale_index = ScaleIndex{ |
| 660 | 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 | 660 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ |
| 665 | .disp = imm_pair.dest_off, | |
| 661 | .disp = index_reg_disp_imm.disp, | |
| 666 | 662 | .base = ops.reg1, |
| 667 | 663 | .scale_index = scale_index, |
| 668 | }), imm_pair.operand, emit.code); | |
| 664 | }), index_reg_disp_imm.imm, emit.code); | |
| 669 | 665 | } |
| 670 | 666 | |
| 671 | 667 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 672 | 668 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 673 | 669 | assert(ops.reg2 == .none); |
| 674 | 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 | 672 | const ptr_size: Memory.PtrSize = switch (ops.flags) { |
| 677 | 673 | 0b00 => .byte_ptr, |
| 678 | 674 | 0b01 => .word_ptr, |
| ... | ... | @@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v |
| 681 | 677 | }; |
| 682 | 678 | const scale_index = ScaleIndex{ |
| 683 | 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 | 683 | return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ |
| 688 | .disp = imm_pair.dest_off, | |
| 684 | .disp = index_reg_disp_imm.disp, | |
| 689 | 685 | .base = ops.reg1, |
| 690 | 686 | .scale_index = scale_index, |
| 691 | }), imm_pair.operand, emit.code); | |
| 687 | }), index_reg_disp_imm.imm, emit.code); | |
| 692 | 688 | } |
| 693 | 689 | |
| 694 | 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 | 952 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); |
| 957 | 953 | }, |
| 958 | 954 | 0b10 => { |
| 959 | // lea reg, [rbp + rcx + imm32] | |
| 960 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 955 | // lea reg, [rbp + index + imm32] | |
| 956 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 957 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 961 | 958 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; |
| 962 | 959 | const scale_index = ScaleIndex{ |
| 963 | 960 | .scale = 0, |
| 964 | .index = .rcx, | |
| 961 | .index = index_reg_disp.index, | |
| 965 | 962 | }; |
| 966 | 963 | return lowerToRmEnc( |
| 967 | 964 | .lea, |
| 968 | 965 | ops.reg1, |
| 969 | 966 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 970 | .disp = imm, | |
| 967 | .disp = index_reg_disp.disp, | |
| 971 | 968 | .base = src_reg, |
| 972 | 969 | .scale_index = scale_index, |
| 973 | 970 | }), |
| ... | ... | @@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 985 | 982 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; |
| 986 | 983 | |
| 987 | 984 | switch (ops.flags) { |
| 988 | 0b00, 0b01 => {}, | |
| 989 | else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}), | |
| 985 | 0b00, 0b01, 0b10 => {}, | |
| 986 | else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), | |
| 990 | 987 | } |
| 991 | 988 | |
| 992 | 989 | // lea reg1, [rip + reloc] |
| ... | ... | @@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1024 | 1021 | .@"type" = switch (ops.flags) { |
| 1025 | 1022 | 0b00 => .got, |
| 1026 | 1023 | 0b01 => .direct, |
| 1024 | 0b10 => .imports, | |
| 1027 | 1025 | else => unreachable, |
| 1028 | 1026 | }, |
| 1029 | 1027 | .target = .{ .sym_index = relocation.sym_index, .file = null }, |
| ... | ... | @@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1031 | 1029 | .addend = 0, |
| 1032 | 1030 | .pcrel = true, |
| 1033 | 1031 | .length = 2, |
| 1034 | .prev_vaddr = atom.getSymbol(coff_file).value, | |
| 1035 | 1032 | }); |
| 1036 | 1033 | } else { |
| 1037 | 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 | 1154 | .length = 2, |
| 1158 | 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 | 1168 | } else { |
| 1161 | 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 | 2249 | encoder.rex(.{ |
| 2242 | 2250 | .w = wide, |
| 2243 | 2251 | .b = base.isExtended(), |
| 2252 | .x = if (mem_op.scale_index) |si| si.index.isExtended() else false, | |
| 2244 | 2253 | }); |
| 2245 | 2254 | } |
| 2246 | 2255 | opc.encode(encoder); |
| ... | ... | @@ -2346,10 +2355,12 @@ fn lowerToMiXEnc( |
| 2346 | 2355 | encoder.rex(.{ |
| 2347 | 2356 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2348 | 2357 | .b = base.isExtended(), |
| 2358 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2349 | 2359 | }); |
| 2350 | 2360 | } else { |
| 2351 | 2361 | encoder.rex(.{ |
| 2352 | 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 | 2366 | opc.encode(encoder); |
| ... | ... | @@ -2401,11 +2412,13 @@ fn lowerToRmEnc( |
| 2401 | 2412 | .w = setRexWRegister(reg), |
| 2402 | 2413 | .r = reg.isExtended(), |
| 2403 | 2414 | .b = base.isExtended(), |
| 2415 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2404 | 2416 | }); |
| 2405 | 2417 | } else { |
| 2406 | 2418 | encoder.rex(.{ |
| 2407 | 2419 | .w = setRexWRegister(reg), |
| 2408 | 2420 | .r = reg.isExtended(), |
| 2421 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2409 | 2422 | }); |
| 2410 | 2423 | } |
| 2411 | 2424 | opc.encode(encoder); |
| ... | ... | @@ -2446,11 +2459,13 @@ fn lowerToMrEnc( |
| 2446 | 2459 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2447 | 2460 | .r = reg.isExtended(), |
| 2448 | 2461 | .b = base.isExtended(), |
| 2462 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2449 | 2463 | }); |
| 2450 | 2464 | } else { |
| 2451 | 2465 | encoder.rex(.{ |
| 2452 | 2466 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2453 | 2467 | .r = reg.isExtended(), |
| 2468 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2454 | 2469 | }); |
| 2455 | 2470 | } |
| 2456 | 2471 | opc.encode(encoder); |
| ... | ... | @@ -2490,11 +2505,13 @@ fn lowerToRmiEnc( |
| 2490 | 2505 | .w = setRexWRegister(reg), |
| 2491 | 2506 | .r = reg.isExtended(), |
| 2492 | 2507 | .b = base.isExtended(), |
| 2508 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2493 | 2509 | }); |
| 2494 | 2510 | } else { |
| 2495 | 2511 | encoder.rex(.{ |
| 2496 | 2512 | .w = setRexWRegister(reg), |
| 2497 | 2513 | .r = reg.isExtended(), |
| 2514 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2498 | 2515 | }); |
| 2499 | 2516 | } |
| 2500 | 2517 | opc.encode(encoder); |
| ... | ... | @@ -2531,10 +2548,12 @@ fn lowerToVmEnc( |
| 2531 | 2548 | vex.rex(.{ |
| 2532 | 2549 | .r = reg.isExtended(), |
| 2533 | 2550 | .b = base.isExtended(), |
| 2551 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2534 | 2552 | }); |
| 2535 | 2553 | } else { |
| 2536 | 2554 | vex.rex(.{ |
| 2537 | 2555 | .r = reg.isExtended(), |
| 2556 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2538 | 2557 | }); |
| 2539 | 2558 | } |
| 2540 | 2559 | encoder.vex(enc.prefix); |
| ... | ... | @@ -2571,10 +2590,12 @@ fn lowerToMvEnc( |
| 2571 | 2590 | vex.rex(.{ |
| 2572 | 2591 | .r = reg.isExtended(), |
| 2573 | 2592 | .b = base.isExtended(), |
| 2593 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2574 | 2594 | }); |
| 2575 | 2595 | } else { |
| 2576 | 2596 | vex.rex(.{ |
| 2577 | 2597 | .r = reg.isExtended(), |
| 2598 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2578 | 2599 | }); |
| 2579 | 2600 | } |
| 2580 | 2601 | encoder.vex(enc.prefix); |
src/arch/x86_64/Mir.zig+107-42| ... | ... | @@ -44,25 +44,28 @@ pub const Inst = struct { |
| 44 | 44 | /// 0b01 word ptr [reg1 + imm32], imm16 |
| 45 | 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 |
| 46 | 46 | /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) |
| 47 | /// Notes: | |
| 48 | /// * Uses `ImmPair` as payload | |
| 47 | 49 | adc_mem_imm, |
| 48 | 50 | |
| 49 | /// form: reg1, [reg2 + scale*rcx + imm32] | |
| 51 | /// form: reg1, [reg2 + scale*index + imm32] | |
| 50 | 52 | /// ops flags scale |
| 51 | 53 | /// 0b00 1 |
| 52 | 54 | /// 0b01 2 |
| 53 | 55 | /// 0b10 4 |
| 54 | 56 | /// 0b11 8 |
| 57 | /// Notes: | |
| 58 | /// * Uses `IndexRegisterDisp` as payload | |
| 55 | 59 | adc_scale_src, |
| 56 | 60 | |
| 57 | /// form: [reg1 + scale*rax + imm32], reg2 | |
| 58 | /// form: [reg1 + scale*rax + 0], imm32 | |
| 61 | /// form: [reg1 + scale*index + imm32], reg2 | |
| 59 | 62 | /// ops flags scale |
| 60 | 63 | /// 0b00 1 |
| 61 | 64 | /// 0b01 2 |
| 62 | 65 | /// 0b10 4 |
| 63 | 66 | /// 0b11 8 |
| 64 | 67 | /// Notes: |
| 65 | /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. | |
| 68 | /// * Uses `IndexRegisterDisp` payload. | |
| 66 | 69 | adc_scale_dst, |
| 67 | 70 | |
| 68 | 71 | /// form: [reg1 + scale*rax + imm32], imm32 |
| ... | ... | @@ -72,14 +75,16 @@ pub const Inst = struct { |
| 72 | 75 | /// 0b10 4 |
| 73 | 76 | /// 0b11 8 |
| 74 | 77 | /// Notes: |
| 75 | /// * Data field `payload` points at `ImmPair`. | |
| 78 | /// * Uses `IndexRegisterDispImm` payload. | |
| 76 | 79 | adc_scale_imm, |
| 77 | 80 | |
| 78 | 81 | /// ops flags: form: |
| 79 | /// 0b00 byte ptr [reg1 + rax + imm32], imm8 | |
| 80 | /// 0b01 word ptr [reg1 + rax + imm32], imm16 | |
| 81 | /// 0b10 dword ptr [reg1 + rax + imm32], imm32 | |
| 82 | /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64) | |
| 82 | /// 0b00 byte ptr [reg1 + index + imm32], imm8 | |
| 83 | /// 0b01 word ptr [reg1 + index + imm32], imm16 | |
| 84 | /// 0b10 dword ptr [reg1 + index + imm32], imm32 | |
| 85 | /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64) | |
| 86 | /// Notes: | |
| 87 | /// * Uses `IndexRegisterDispImm` payload. | |
| 83 | 88 | adc_mem_index_imm, |
| 84 | 89 | |
| 85 | 90 | // The following instructions all have the same encoding as `adc`. |
| ... | ... | @@ -174,12 +179,15 @@ pub const Inst = struct { |
| 174 | 179 | /// 0b00 reg1, [reg2 + imm32] |
| 175 | 180 | /// 0b00 reg1, [ds:imm32] |
| 176 | 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 | 185 | lea, |
| 179 | 186 | |
| 180 | 187 | /// ops flags: form: |
| 181 | 188 | /// 0b00 reg1, [rip + reloc] // via GOT PIC |
| 182 | 189 | /// 0b01 reg1, [rip + reloc] // direct load PIC |
| 190 | /// 0b10 reg1, [rip + reloc] // via imports table PIC | |
| 183 | 191 | /// Notes: |
| 184 | 192 | /// * `Data` contains `relocation` |
| 185 | 193 | lea_pic, |
| ... | ... | @@ -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 { | |
| 464 | assert(registers.len <= @bitSizeOf(u32)); | |
| 465 | return struct { | |
| 466 | bitset: RegBitSet = RegBitSet.initEmpty(), | |
| 471 | pub const IndexRegisterDisp = struct { | |
| 472 | /// Index register to use with SIB-based encoding | |
| 473 | index: u32, | |
| 467 | 474 | |
| 468 | const RegBitSet = IntegerBitSet(registers.len); | |
| 469 | const Self = @This(); | |
| 475 | /// Displacement value | |
| 476 | disp: u32, | |
| 470 | 477 | |
| 471 | fn getIndexForReg(reg: Reg) RegBitSet.MaskInt { | |
| 472 | inline for (registers) |cpreg, i| { | |
| 473 | if (reg.id() == cpreg.id()) return i; | |
| 474 | } | |
| 475 | unreachable; // register not in input register list! | |
| 476 | } | |
| 478 | pub fn encode(index: Register, disp: u32) IndexRegisterDisp { | |
| 479 | return .{ | |
| 480 | .index = @enumToInt(index), | |
| 481 | .disp = disp, | |
| 482 | }; | |
| 483 | } | |
| 477 | 484 | |
| 478 | pub fn push(self: *Self, reg: Reg) void { | |
| 479 | const index = getIndexForReg(reg); | |
| 480 | self.bitset.set(index); | |
| 481 | } | |
| 485 | pub fn decode(this: IndexRegisterDisp) struct { | |
| 486 | index: Register, | |
| 487 | disp: u32, | |
| 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 { | |
| 484 | const index = getIndexForReg(reg); | |
| 485 | return self.bitset.isSet(index); | |
| 486 | } | |
| 496 | /// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list | |
| 497 | /// instead of having two structs, one a superset of the other one? | |
| 498 | pub const IndexRegisterDispImm = struct { | |
| 499 | /// Index register to use with SIB-based encoding | |
| 500 | index: u32, | |
| 487 | 501 | |
| 488 | pub fn asInt(self: Self) u32 { | |
| 489 | return self.bitset.mask; | |
| 490 | } | |
| 502 | /// Displacement value | |
| 503 | disp: u32, | |
| 491 | 504 | |
| 492 | pub fn fromInt(mask: u32) Self { | |
| 493 | return .{ | |
| 494 | .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) }, | |
| 495 | }; | |
| 496 | } | |
| 505 | /// Immediate | |
| 506 | imm: u32, | |
| 507 | ||
| 508 | pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm { | |
| 509 | return .{ | |
| 510 | .index = @enumToInt(index), | |
| 511 | .disp = disp, | |
| 512 | .imm = imm, | |
| 513 | }; | |
| 514 | } | |
| 497 | 515 | |
| 498 | pub fn count(self: Self) u32 { | |
| 499 | return @intCast(u32, self.bitset.count()); | |
| 516 | pub fn decode(this: IndexRegisterDispImm) struct { | |
| 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 | }; | |
| 502 | } | |
| 541 | unreachable; // register not in input register list! | |
| 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 | 569 | pub const SaveRegisterList = struct { |
| 505 | 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 | 392 | } |
| 393 | 393 | } |
| 394 | 394 | |
| 395 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | |
| 396 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 397 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; | |
| 398 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | |
| 399 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | |
| 400 | /// In other words, these registers are free to use by the callee. | |
| 401 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 395 | pub const SysV = struct { | |
| 396 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | |
| 397 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 398 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; | |
| 399 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | |
| 400 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | |
| 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_return_regs = [_]Register{ .rax, .rdx }; | |
| 404 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | |
| 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 | 452 | const sse_avx_regs = [_]Register{ |
| 407 | 453 | .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, |
| 408 | 454 | .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, |
| 409 | 455 | }; |
| 410 | const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs; | |
| 411 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); | |
| 456 | const allocatable_regs = gp_regs ++ sse_avx_regs; | |
| 457 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs); | |
| 412 | 458 | |
| 413 | 459 | // Register classes |
| 414 | 460 | const RegisterBitSet = RegisterManager.RegisterBitSet; |
| ... | ... | @@ -417,15 +463,15 @@ pub const RegisterClass = struct { |
| 417 | 463 | var set = RegisterBitSet.initEmpty(); |
| 418 | 464 | set.setRangeValue(.{ |
| 419 | 465 | .start = 0, |
| 420 | .end = caller_preserved_regs.len + callee_preserved_regs.len, | |
| 466 | .end = gp_regs.len, | |
| 421 | 467 | }, true); |
| 422 | 468 | break :blk set; |
| 423 | 469 | }; |
| 424 | 470 | pub const sse: RegisterBitSet = blk: { |
| 425 | 471 | var set = RegisterBitSet.initEmpty(); |
| 426 | 472 | set.setRangeValue(.{ |
| 427 | .start = caller_preserved_regs.len + callee_preserved_regs.len, | |
| 428 | .end = allocatable_registers.len, | |
| 473 | .start = gp_regs.len, | |
| 474 | .end = allocatable_regs.len, | |
| 429 | 475 | }, true); |
| 430 | 476 | break :blk set; |
| 431 | 477 | }; |
src/link.zig+1-1| ... | ... | @@ -476,7 +476,7 @@ pub const File = struct { |
| 476 | 476 | log.debug("getGlobalSymbol '{s}'", .{name}); |
| 477 | 477 | switch (base.tag) { |
| 478 | 478 | // zig fmt: off |
| 479 | .coff => unreachable, | |
| 479 | .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name), | |
| 480 | 480 | .elf => unreachable, |
| 481 | 481 | .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name), |
| 482 | 482 | .plan9 => unreachable, |
src/link/Coff.zig+674-221| ... | ... | @@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig"); |
| 30 | 30 | pub const base_tag: link.File.Tag = .coff; |
| 31 | 31 | |
| 32 | 32 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 33 | const N_DATA_DIRS: u5 = 16; | |
| 34 | 33 | |
| 35 | 34 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 36 | 35 | llvm_object: ?*LlvmObject = null, |
| ... | ... | @@ -44,24 +43,33 @@ page_size: u32, |
| 44 | 43 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 45 | 44 | |
| 46 | 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 | 48 | text_section_index: ?u16 = null, |
| 50 | 49 | got_section_index: ?u16 = null, |
| 51 | 50 | rdata_section_index: ?u16 = null, |
| 52 | 51 | data_section_index: ?u16 = null, |
| 53 | 52 | reloc_section_index: ?u16 = null, |
| 53 | idata_section_index: ?u16 = null, | |
| 54 | 54 | |
| 55 | 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 | 60 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 61 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 59 | 62 | |
| 60 | 63 | strtab: StringTable(.strtab) = .{}, |
| 61 | 64 | strtab_offset: ?u32 = null, |
| 62 | 65 | |
| 63 | got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | |
| 66 | got_entries: std.ArrayListUnmanaged(Entry) = .{}, | |
| 64 | 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 | 74 | /// Virtual address of the entry point procedure relative to image base. |
| 67 | 75 | entry_addr: ?u32 = null, |
| ... | ... | @@ -109,17 +117,33 @@ relocs: RelocTable = .{}, |
| 109 | 117 | /// this will be a table indexed by index into the list of Atoms. |
| 110 | 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 | 126 | pub const Reloc = struct { |
| 113 | 127 | @"type": enum { |
| 114 | 128 | got, |
| 115 | 129 | direct, |
| 130 | imports, | |
| 116 | 131 | }, |
| 117 | 132 | target: SymbolWithLoc, |
| 118 | 133 | offset: u32, |
| 119 | 134 | addend: u32, |
| 120 | 135 | pcrel: bool, |
| 121 | 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 | 149 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); |
| ... | ... | @@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct { |
| 180 | 204 | |
| 181 | 205 | // null means it's a synthetic global or Zig source. |
| 182 | 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 | 219 | /// When allocating, the ideal_capacity is calculated by |
| ... | ... | @@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { |
| 234 | 268 | }, |
| 235 | 269 | .ptr_width = ptr_width, |
| 236 | 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 | 274 | const use_llvm = build_options.have_llvm and options.use_llvm; |
| ... | ... | @@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void { |
| 269 | 303 | |
| 270 | 304 | self.locals.deinit(gpa); |
| 271 | 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 | 316 | self.locals_free_list.deinit(gpa); |
| 273 | 317 | self.strtab.deinit(gpa); |
| 274 | 318 | self.got_entries.deinit(gpa); |
| 275 | 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 | 324 | self.decls.deinit(gpa); |
| 277 | 325 | self.atom_by_index_table.deinit(gpa); |
| 278 | 326 | |
| ... | ... | @@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 305 | 353 | assert(self.llvm_object == null); |
| 306 | 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 | 369 | if (self.text_section_index == null) { |
| 309 | self.text_section_index = @intCast(u16, self.sections.slice().len); | |
| 310 | 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 | |
| 312 | log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 313 | var header = coff.SectionHeader{ | |
| 314 | .name = undefined, | |
| 315 | .virtual_size = file_size, | |
| 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 }); | |
| 371 | self.text_section_index = try self.allocateSection(".text", file_size, .{ | |
| 372 | .CNT_CODE = 1, | |
| 373 | .MEM_EXECUTE = 1, | |
| 374 | .MEM_READ = 1, | |
| 375 | }); | |
| 331 | 376 | } |
| 332 | 377 | |
| 333 | 378 | if (self.got_section_index == null) { |
| 334 | self.got_section_index = @intCast(u16, self.sections.slice().len); | |
| 335 | 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); | |
| 337 | log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 338 | var header = coff.SectionHeader{ | |
| 339 | .name = undefined, | |
| 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 }); | |
| 380 | self.got_section_index = try self.allocateSection(".got", file_size, .{ | |
| 381 | .CNT_INITIALIZED_DATA = 1, | |
| 382 | .MEM_READ = 1, | |
| 383 | }); | |
| 355 | 384 | } |
| 356 | 385 | |
| 357 | 386 | if (self.rdata_section_index == null) { |
| 358 | self.rdata_section_index = @intCast(u16, self.sections.slice().len); | |
| 359 | const file_size: u32 = 1024; | |
| 360 | const off = self.findFreeSpace(file_size, self.page_size); | |
| 361 | log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 362 | var header = coff.SectionHeader{ | |
| 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 }); | |
| 387 | const file_size: u32 = self.page_size; | |
| 388 | self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{ | |
| 389 | .CNT_INITIALIZED_DATA = 1, | |
| 390 | .MEM_READ = 1, | |
| 391 | }); | |
| 379 | 392 | } |
| 380 | 393 | |
| 381 | 394 | if (self.data_section_index == null) { |
| 382 | self.data_section_index = @intCast(u16, self.sections.slice().len); | |
| 383 | const file_size: u32 = 1024; | |
| 384 | const off = self.findFreeSpace(file_size, self.page_size); | |
| 385 | log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 386 | var header = coff.SectionHeader{ | |
| 387 | .name = undefined, | |
| 388 | .virtual_size = file_size, | |
| 389 | .virtual_address = off, | |
| 390 | .size_of_raw_data = file_size, | |
| 391 | .pointer_to_raw_data = off, | |
| 392 | .pointer_to_relocations = 0, | |
| 393 | .pointer_to_linenumbers = 0, | |
| 394 | .number_of_relocations = 0, | |
| 395 | .number_of_linenumbers = 0, | |
| 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 }); | |
| 395 | const file_size: u32 = self.page_size; | |
| 396 | self.data_section_index = try self.allocateSection(".data", file_size, .{ | |
| 397 | .CNT_INITIALIZED_DATA = 1, | |
| 398 | .MEM_READ = 1, | |
| 399 | .MEM_WRITE = 1, | |
| 400 | }); | |
| 401 | } | |
| 402 | ||
| 403 | if (self.idata_section_index == null) { | |
| 404 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); | |
| 405 | self.idata_section_index = try self.allocateSection(".idata", file_size, .{ | |
| 406 | .CNT_INITIALIZED_DATA = 1, | |
| 407 | .MEM_READ = 1, | |
| 408 | }); | |
| 404 | 409 | } |
| 405 | 410 | |
| 406 | 411 | if (self.reloc_section_index == null) { |
| 407 | self.reloc_section_index = @intCast(u16, self.sections.slice().len); | |
| 408 | 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); | |
| 410 | log.debug("found .reloc free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 411 | var header = coff.SectionHeader{ | |
| 412 | .name = undefined, | |
| 413 | .virtual_size = file_size, | |
| 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 }); | |
| 413 | self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{ | |
| 414 | .CNT_INITIALIZED_DATA = 1, | |
| 415 | .MEM_DISCARDABLE = 1, | |
| 416 | .MEM_READ = 1, | |
| 417 | }); | |
| 429 | 418 | } |
| 430 | 419 | |
| 431 | 420 | if (self.strtab_offset == null) { |
| 432 | try self.strtab.buffer.append(gpa, 0); | |
| 433 | self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1); | |
| 434 | log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() }); | |
| 421 | const file_size = @intCast(u32, self.strtab.len()); | |
| 422 | self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here | |
| 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 | 427 | // We need to find out what the max file offset is according to section headers. |
| 449 | 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 | 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 | 507 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { |
| 463 | 508 | if (self.llvm_object) |_| return; |
| 464 | 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 | 587 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 543 | 588 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; |
| 544 | 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 | 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 | 617 | atom.size = new_atom_size; |
| 556 | 618 | atom.alignment = alignment; |
| 557 | 619 | |
| ... | ... | @@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 { |
| 596 | 658 | self.locals.items[index] = .{ |
| 597 | 659 | .name = [_]u8{0} ** 8, |
| 598 | 660 | .value = 0, |
| 599 | .section_number = @intToEnum(coff.SectionNumber, 0), | |
| 661 | .section_number = .UNDEFINED, | |
| 600 | 662 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, |
| 601 | 663 | .storage_class = .NULL, |
| 602 | 664 | .number_of_aux_symbols = 0, |
| ... | ... | @@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 { |
| 605 | 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 | 694 | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 609 | 695 | const gpa = self.base.allocator; |
| 610 | 696 | try self.got_entries.ensureUnusedCapacity(gpa, 1); |
| 697 | ||
| 611 | 698 | const index: u32 = blk: { |
| 612 | 699 | if (self.got_entries_free_list.popOrNull()) |index| { |
| 613 | 700 | log.debug(" (reusing GOT entry index {d})", .{index}); |
| 614 | if (self.got_entries.getIndex(target)) |existing| { | |
| 615 | assert(existing == index); | |
| 616 | } | |
| 617 | 701 | break :blk index; |
| 618 | 702 | } else { |
| 619 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len}); | |
| 620 | const index = @intCast(u32, self.got_entries.keys().len); | |
| 621 | self.got_entries.putAssumeCapacityNoClobber(target, 0); | |
| 703 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); | |
| 704 | const index = @intCast(u32, self.got_entries.items.len); | |
| 705 | _ = self.got_entries.addOneAssumeCapacity(); | |
| 622 | 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 | 735 | return index; |
| 627 | 736 | } |
| 628 | 737 | |
| ... | ... | @@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 637 | 746 | |
| 638 | 747 | try self.managed_atoms.append(gpa, atom); |
| 639 | 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 | 750 | const sym = atom.getSymbolPtr(self); |
| 643 | 751 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); |
| ... | ... | @@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 652 | 760 | .addend = 0, |
| 653 | 761 | .pcrel = false, |
| 654 | 762 | .length = 3, |
| 655 | .prev_vaddr = sym.value, | |
| 656 | 763 | }); |
| 657 | 764 | |
| 658 | 765 | const target_sym = self.getSymbol(target); |
| ... | ... | @@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 666 | 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 | 797 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { |
| 670 | 798 | const sym = atom.getSymbol(self); |
| 671 | 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 | 814 | const sym = atom.getSymbol(self); |
| 687 | 815 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); |
| 688 | 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 | 818 | try self.base.file.?.pwriteAll(code, file_offset); |
| 691 | 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 | 823 | switch (self.ptr_width) { |
| 696 | 824 | .p32 => { |
| 697 | 825 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); |
| ... | ... | @@ -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 | 858 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 708 | 859 | const relocs = self.relocs.get(atom) orelse return; |
| 709 | 860 | const source_sym = atom.getSymbol(self); |
| ... | ... | @@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 713 | 864 | log.debug("relocating '{s}'", .{atom.getName(self)}); |
| 714 | 865 | |
| 715 | 866 | for (relocs.items) |*reloc| { |
| 716 | const target_vaddr = switch (reloc.@"type") { | |
| 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; | |
| 867 | if (!reloc.dirty) continue; | |
| 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})", .{ | |
| 728 | reloc.offset, | |
| 873 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{ | |
| 874 | source_sym.value + reloc.offset, | |
| 729 | 875 | target_vaddr_with_addend, |
| 730 | 876 | self.getSymbolName(reloc.target), |
| 731 | 877 | @tagName(reloc.@"type"), |
| 878 | file_offset + reloc.offset, | |
| 732 | 879 | }); |
| 733 | 880 | |
| 881 | reloc.dirty = false; | |
| 882 | ||
| 734 | 883 | if (reloc.pcrel) { |
| 735 | 884 | const source_vaddr = source_sym.value + reloc.offset; |
| 736 | const disp = target_vaddr_with_addend - source_vaddr - 4; | |
| 737 | try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset); | |
| 738 | return; | |
| 885 | const disp = | |
| 886 | @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; | |
| 887 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); | |
| 888 | continue; | |
| 739 | 889 | } |
| 740 | 890 | |
| 741 | 891 | switch (self.ptr_width) { |
| ... | ... | @@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 755 | 905 | else => unreachable, |
| 756 | 906 | }, |
| 757 | 907 | } |
| 758 | ||
| 759 | reloc.prev_vaddr = target_vaddr_with_addend; | |
| 760 | 908 | } |
| 761 | 909 | } |
| 762 | 910 | |
| 763 | 911 | fn freeAtom(self: *Coff, atom: *Atom) void { |
| 764 | 912 | log.debug("freeAtom {*}", .{atom}); |
| 765 | 913 | |
| 914 | // Remove any relocs and base relocs associated with this Atom | |
| 915 | self.freeRelocationsForAtom(atom); | |
| 916 | ||
| 766 | 917 | const sym = atom.getSymbol(self); |
| 767 | 918 | const sect_id = @enumToInt(sym.section_number) - 1; |
| 768 | 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 | 976 | const tracy = trace(@src()); |
| 826 | 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 | 984 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 829 | 985 | defer code_buffer.deinit(); |
| 830 | 986 | |
| 831 | const decl_index = func.owner_decl; | |
| 832 | const decl = module.declPtr(decl_index); | |
| 833 | 987 | const res = try codegen.generateFunction( |
| 834 | 988 | &self.base, |
| 835 | 989 | decl.srcLoc(), |
| ... | ... | @@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 856 | 1010 | } |
| 857 | 1011 | |
| 858 | 1012 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| 859 | _ = self; | |
| 860 | _ = tv; | |
| 861 | _ = decl_index; | |
| 862 | @panic("TODO lowerUnnamedConst"); | |
| 1013 | const gpa = self.base.allocator; | |
| 1014 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1015 | defer code_buffer.deinit(); | |
| 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 | 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 | 1095 | } |
| 885 | 1096 | } |
| 886 | 1097 | |
| 1098 | self.freeRelocationsForAtom(&decl.link.coff); | |
| 1099 | ||
| 887 | 1100 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 888 | 1101 | defer code_buffer.deinit(); |
| 889 | 1102 | |
| ... | ... | @@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 892 | 1105 | .ty = decl.ty, |
| 893 | 1106 | .val = decl_val, |
| 894 | 1107 | }, &code_buffer, .none, .{ |
| 895 | .parent_atom_index = 0, | |
| 1108 | .parent_atom_index = decl.link.coff.sym_index, | |
| 896 | 1109 | }); |
| 897 | 1110 | const code = switch (res) { |
| 898 | 1111 | .externally_managed => |x| x, |
| ... | ... | @@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 970 | 1183 | if (vaddr != sym.value) { |
| 971 | 1184 | sym.value = vaddr; |
| 972 | 1185 | log.debug(" (updating GOT entry)", .{}); |
| 973 | const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?; | |
| 974 | try self.writeGotAtom(got_atom); | |
| 1186 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | |
| 1187 | const got_atom = self.getGotAtomForSymbol(got_target).?; | |
| 1188 | self.markRelocsDirtyByTarget(got_target); | |
| 1189 | try self.writePtrWidthAtom(got_atom); | |
| 975 | 1190 | } |
| 976 | 1191 | } else if (code_len < atom.size) { |
| 977 | 1192 | self.shrinkAtom(atom, code_len); |
| ... | ... | @@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 990 | 1205 | sym.value = vaddr; |
| 991 | 1206 | |
| 992 | 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 | 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 | 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 | 1237 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1002 | 1238 | if (build_options.have_llvm) { |
| 1003 | 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 | 1247 | const kv = self.decls.fetchRemove(decl_index); |
| 1012 | 1248 | if (kv.?.value) |_| { |
| 1013 | 1249 | self.freeAtom(&decl.link.coff); |
| 1250 | self.freeUnnamedConsts(decl_index); | |
| 1014 | 1251 | } |
| 1015 | 1252 | |
| 1016 | 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 | 1258 | |
| 1022 | 1259 | // Try freeing GOT atom if this decl had one |
| 1023 | 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 | 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 | 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 | 1273 | _ = self.atom_by_index_table.remove(sym_index); |
| 1274 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | |
| 1032 | 1275 | decl.link.coff.sym_index = 0; |
| 1033 | 1276 | } |
| 1034 | 1277 | } |
| ... | ... | @@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1154 | 1397 | const sym = self.getSymbolPtr(sym_loc); |
| 1155 | 1398 | const sym_name = self.getSymbolName(sym_loc); |
| 1156 | 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 | 1401 | sym.* = .{ |
| 1159 | 1402 | .name = [_]u8{0} ** 8, |
| 1160 | 1403 | .value = 0, |
| 1161 | .section_number = @intToEnum(coff.SectionNumber, 0), | |
| 1404 | .section_number = .UNDEFINED, | |
| 1162 | 1405 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, |
| 1163 | 1406 | .storage_class = .NULL, |
| 1164 | 1407 | .number_of_aux_symbols = 0, |
| 1165 | 1408 | }; |
| 1166 | 1409 | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 1167 | 1410 | |
| 1168 | if (self.globals.get(sym_name)) |global| blk: { | |
| 1169 | if (global.sym_index != sym_index) break :blk; | |
| 1170 | if (global.file != null) break :blk; | |
| 1171 | const kv = self.globals.fetchSwapRemove(sym_name); | |
| 1172 | gpa.free(kv.?.key); | |
| 1411 | if (self.resolver.fetchRemove(sym_name)) |entry| { | |
| 1412 | defer gpa.free(entry.key); | |
| 1413 | self.globals_free_list.append(gpa, entry.value) catch {}; | |
| 1414 | self.globals.items[entry.value] = .{ | |
| 1415 | .sym_index = 0, | |
| 1416 | .file = null, | |
| 1417 | }; | |
| 1173 | 1418 | } |
| 1174 | 1419 | } |
| 1175 | 1420 | |
| 1176 | 1421 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { |
| 1177 | 1422 | const gpa = self.base.allocator; |
| 1178 | 1423 | const sym = self.getSymbol(current); |
| 1179 | _ = sym; | |
| 1180 | 1424 | const sym_name = self.getSymbolName(current); |
| 1181 | 1425 | |
| 1182 | const name = try gpa.dupe(u8, sym_name); | |
| 1183 | const global_index = @intCast(u32, self.globals.values().len); | |
| 1184 | _ = global_index; | |
| 1185 | const gop = try self.globals.getOrPut(gpa, name); | |
| 1186 | defer if (gop.found_existing) gpa.free(name); | |
| 1187 | ||
| 1188 | if (!gop.found_existing) { | |
| 1189 | gop.value_ptr.* = current; | |
| 1190 | // TODO undef + tentative | |
| 1426 | const global_index = self.resolver.get(sym_name) orelse { | |
| 1427 | const name = try gpa.dupe(u8, sym_name); | |
| 1428 | const global_index = try self.allocateGlobal(); | |
| 1429 | self.globals.items[global_index] = current; | |
| 1430 | try self.resolver.putNoClobber(gpa, name, global_index); | |
| 1431 | if (sym.section_number == .UNDEFINED) { | |
| 1432 | try self.unresolved.putNoClobber(gpa, global_index, false); | |
| 1433 | } | |
| 1191 | 1434 | return; |
| 1192 | } | |
| 1435 | }; | |
| 1193 | 1436 | |
| 1194 | 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 | 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 | 1475 | sub_prog_node.activate(); |
| 1228 | 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 | 1489 | if (build_options.enable_logging) { |
| 1231 | 1490 | self.logSymtab(); |
| 1232 | 1491 | } |
| ... | ... | @@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1237 | 1496 | try self.resolveRelocs(atom.*); |
| 1238 | 1497 | } |
| 1239 | 1498 | } |
| 1499 | try self.writeImportTable(); | |
| 1240 | 1500 | try self.writeBaseRelocations(); |
| 1241 | 1501 | |
| 1242 | 1502 | if (self.getEntryPoint()) |entry_sym_loc| { |
| ... | ... | @@ -1262,10 +1522,47 @@ pub fn getDeclVAddr( |
| 1262 | 1522 | decl_index: Module.Decl.Index, |
| 1263 | 1523 | reloc_info: link.File.RelocInfo, |
| 1264 | 1524 | ) !u64 { |
| 1265 | _ = self; | |
| 1266 | _ = decl_index; | |
| 1267 | _ = reloc_info; | |
| 1268 | @panic("TODO getDeclVAddr"); | |
| 1525 | const mod = self.base.options.module.?; | |
| 1526 | const decl = mod.declPtr(decl_index); | |
| 1527 | ||
| 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 | 1568 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1342 | 1639 | const header = &self.sections.items(.header)[self.reloc_section_index.?]; |
| 1343 | 1640 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 1344 | 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 | 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 | 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 | 1753 | fn writeStrtab(self: *Coff) !void { |
| 1754 | if (self.strtab_offset == null) return; | |
| 1755 | ||
| 1356 | 1756 | const allocated_size = self.allocatedSize(self.strtab_offset.?); |
| 1357 | 1757 | const needed_size = @intCast(u32, self.strtab.len()); |
| 1358 | 1758 | |
| 1359 | 1759 | if (needed_size > allocated_size) { |
| 1360 | 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 | 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 | 1777 | fn writeSectionHeaders(self: *Coff) !void { |
| ... | ... | @@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 1527 | 1936 | } |
| 1528 | 1937 | |
| 1529 | 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 | 1940 | if (start < headers_size) |
| 1532 | 1941 | return headers_size; |
| 1533 | 1942 | |
| 1534 | const end = start + size; | |
| 1943 | const end = start + padToIdeal(size); | |
| 1535 | 1944 | |
| 1536 | 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 | 1948 | const test_end = off + increased_size; |
| 1539 | 1949 | if (end > off and start < test_end) { |
| 1540 | 1950 | return test_end; |
| ... | ... | @@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1542 | 1952 | } |
| 1543 | 1953 | |
| 1544 | 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 | 1957 | const test_end = header.pointer_to_raw_data + increased_size; |
| 1547 | 1958 | if (end > header.pointer_to_raw_data and start < test_end) { |
| 1548 | 1959 | return test_end; |
| ... | ... | @@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1552 | 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 | 1967 | if (start == 0) |
| 1557 | 1968 | return 0; |
| 1558 | 1969 | var min_pos: u32 = std.math.maxInt(u32); |
| ... | ... | @@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 { |
| 1566 | 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 | 1981 | var start: u32 = 0; |
| 1571 | 1982 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 1572 | 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 | 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 | 1999 | inline fn getSizeOfHeaders(self: Coff) u32 { |
| 1578 | 2000 | const msdos_hdr_size = msdos_stub.len + 4; |
| 1579 | 2001 | return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() + |
| ... | ... | @@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 { |
| 1614 | 2036 | |
| 1615 | 2037 | /// Returns symbol location corresponding to the set entrypoint (if any). |
| 1616 | 2038 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { |
| 1617 | const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete | |
| 1618 | return self.globals.get(entry_name); | |
| 2039 | const entry_name = self.base.options.entry orelse "wWinMainCRTStartup"; // TODO this is incomplete | |
| 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 | 2045 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { |
| 1623 | 2046 | assert(sym_loc.file == null); // TODO linking object files |
| 1624 | 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 | 2051 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { |
| 1629 | 2052 | assert(sym_loc.file == null); // TODO linking object files |
| 1630 | 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 | 2057 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1635 | 2058 | assert(sym_loc.file == null); // TODO linking object files |
| 1636 | 2059 | const sym = self.getSymbol(sym_loc); |
| ... | ... | @@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1638 | 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 | 2065 | /// Returns null on failure. |
| 1643 | 2066 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1644 | 2067 | assert(sym_loc.file == null); // TODO linking with object files |
| 1645 | 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 | 2072 | /// Returns null otherwise. |
| 1650 | 2073 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1651 | const got_index = self.got_entries.get(sym_loc) orelse return null; | |
| 1652 | return self.atom_by_index_table.get(got_index); | |
| 2074 | const got_index = self.got_entries_table.get(sym_loc) orelse return null; | |
| 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 | 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 | 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 | 2106 | fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { |
| 1667 | 2107 | if (name.len <= 8) { |
| 1668 | 2108 | mem.copy(u8, &symbol.name, name); |
| ... | ... | @@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void { |
| 1725 | 2165 | } |
| 1726 | 2166 | |
| 1727 | 2167 | log.debug("globals table:", .{}); |
| 1728 | for (self.globals.keys()) |name, id| { | |
| 1729 | const value = self.globals.values()[id]; | |
| 1730 | log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file }); | |
| 2168 | for (self.globals.items) |sym_loc| { | |
| 2169 | const sym_name = self.getSymbolName(sym_loc); | |
| 2170 | log.debug(" {s} => %{d} in object({?d})", .{ sym_name, sym_loc.sym_index, sym_loc.file }); | |
| 1731 | 2171 | } |
| 1732 | 2172 | |
| 1733 | 2173 | log.debug("GOT entries:", .{}); |
| 1734 | for (self.got_entries.keys()) |target, i| { | |
| 1735 | const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null }); | |
| 1736 | const target_sym = self.getSymbol(target); | |
| 2174 | for (self.got_entries.items) |entry, i| { | |
| 2175 | const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); | |
| 2176 | const target_sym = self.getSymbol(entry.target); | |
| 1737 | 2177 | if (target_sym.section_number == .UNDEFINED) { |
| 1738 | 2178 | log.debug(" {d}@{x} => import('{s}')", .{ |
| 1739 | 2179 | i, |
| 1740 | 2180 | got_sym.value, |
| 1741 | self.getSymbolName(target), | |
| 2181 | self.getSymbolName(entry.target), | |
| 1742 | 2182 | }); |
| 1743 | 2183 | } else { |
| 1744 | 2184 | log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ |
| 1745 | 2185 | i, |
| 1746 | 2186 | got_sym.value, |
| 1747 | target.sym_index, | |
| 1748 | target.file, | |
| 2187 | entry.target.sym_index, | |
| 2188 | entry.target.file, | |
| 1749 | 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 | 4 | const coff = std.coff; |
| 5 | 5 | const log = std.log.scoped(.link); |
| 6 | 6 | |
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | ||
| 9 | 7 | const Coff = @import("../Coff.zig"); |
| 10 | 8 | const Reloc = Coff.Reloc; |
| 11 | 9 | const SymbolWithLoc = Coff.SymbolWithLoc; |
| ... | ... | @@ -41,11 +39,6 @@ pub const empty = Atom{ |
| 41 | 39 | .next = null, |
| 42 | 40 | }; |
| 43 | 41 | |
| 44 | pub fn deinit(self: *Atom, gpa: Allocator) void { | |
| 45 | _ = self; | |
| 46 | _ = gpa; | |
| 47 | } | |
| 48 | ||
| 49 | 42 | /// Returns symbol referencing this atom. |
| 50 | 43 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { |
| 51 | 44 | return coff_file.getSymbol(.{ |
| ... | ... | @@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { |
| 118 | 111 | } |
| 119 | 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 | 793 | } |
| 794 | 794 | } else { |
| 795 | 795 | const sub_path = self.base.options.emit.?.sub_path; |
| 796 | self.base.file = try directory.handle.createFile(sub_path, .{ | |
| 797 | .truncate = true, | |
| 798 | .read = true, | |
| 799 | .mode = link.determineMode(self.base.options), | |
| 800 | }); | |
| 796 | if (self.base.file == null) { | |
| 797 | self.base.file = try directory.handle.createFile(sub_path, .{ | |
| 798 | .truncate = true, | |
| 799 | .read = true, | |
| 800 | .mode = link.determineMode(self.base.options), | |
| 801 | }); | |
| 802 | } | |
| 801 | 803 | // Index 0 is always a null symbol. |
| 802 | 804 | try self.locals.append(gpa, .{ |
| 803 | 805 | .n_strx = 0, |
| ... | ... | @@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 1155 | 1157 | var ncmds: u32 = 0; |
| 1156 | 1158 | |
| 1157 | 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 | 1183 | try writeDylinkerLC(&ncmds, lc_writer); |
| 1159 | 1184 | try self.writeMainLC(&ncmds, lc_writer); |
| 1160 | 1185 | try self.writeDylibIdLC(&ncmds, lc_writer); |
| ... | ... | @@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool { |
| 1435 | 1460 | |
| 1436 | 1461 | if (force_load) { |
| 1437 | 1462 | defer archive.deinit(gpa); |
| 1438 | defer file.close(); | |
| 1439 | 1463 | // Get all offsets from the ToC |
| 1440 | 1464 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); |
| 1441 | 1465 | defer offsets.deinit(); |
| ... | ... | @@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void { |
| 3086 | 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 | 3113 | fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void { |
| 3099 | 3114 | log.debug("freeAtom {*}", .{atom}); |
| 3100 | 3115 | if (!owns_atom) { |
| ... | ... | @@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 5698 | 5713 | else => unreachable, |
| 5699 | 5714 | } |
| 5700 | 5715 | |
| 5701 | if (self.getSectionByName("__DATA", "__thread_vars")) |_| { | |
| 5702 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 5716 | if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| { | |
| 5717 | if (self.sections.items(.header)[sect_id].size > 0) { | |
| 5718 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 5719 | } | |
| 5703 | 5720 | } |
| 5704 | 5721 | |
| 5705 | 5722 | header.ncmds = ncmds; |
src/link/MachO/Archive.zig+1| ... | ... | @@ -88,6 +88,7 @@ const ar_hdr = extern struct { |
| 88 | 88 | }; |
| 89 | 89 | |
| 90 | 90 | pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 91 | self.file.close(); | |
| 91 | 92 | for (self.toc.keys()) |*key| { |
| 92 | 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 | 306 | } |
| 307 | 307 | |
| 308 | 308 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { |
| 309 | self.file.close(); | |
| 309 | 310 | self.segments.deinit(allocator); |
| 310 | 311 | self.sections.deinit(allocator); |
| 311 | 312 | self.dwarf.deinit(); |
src/link/Wasm.zig+14-4| ... | ... | @@ -695,12 +695,10 @@ pub fn deinit(self: *Wasm) void { |
| 695 | 695 | gpa.free(segment_info.name); |
| 696 | 696 | } |
| 697 | 697 | for (self.objects.items) |*object| { |
| 698 | object.file.?.close(); | |
| 699 | 698 | object.deinit(gpa); |
| 700 | 699 | } |
| 701 | 700 | |
| 702 | 701 | for (self.archives.items) |*archive| { |
| 703 | archive.file.close(); | |
| 704 | 702 | archive.deinit(gpa); |
| 705 | 703 | } |
| 706 | 704 | |
| ... | ... | @@ -3218,14 +3216,26 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size |
| 3218 | 3216 | buf[0] = @enumToInt(section); |
| 3219 | 3217 | leb.writeUnsignedFixed(5, buf[1..6], size); |
| 3220 | 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 | 3228 | fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { |
| 3225 | 3229 | var buf: [1 + 5]u8 = undefined; |
| 3226 | 3230 | buf[0] = 0; // 0 = 'custom' section |
| 3227 | 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 | 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 | 95 | }; |
| 96 | 96 | |
| 97 | 97 | pub fn deinit(archive: *Archive, allocator: Allocator) void { |
| 98 | archive.file.close(); | |
| 98 | 99 | for (archive.toc.keys()) |*key| { |
| 99 | 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 | 154 | /// Frees all memory of `Object` at once. The given `Allocator` must be |
| 155 | 155 | /// the same allocator that was used when `init` was called. |
| 156 | 156 | pub fn deinit(self: *Object, gpa: Allocator) void { |
| 157 | if (self.file) |file| { | |
| 158 | file.close(); | |
| 159 | } | |
| 157 | 160 | for (self.func_types) |func_ty| { |
| 158 | 161 | gpa.free(func_ty.params); |
| 159 | 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 | 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 | 117 | pub fn len(self: Self) usize { |
| 114 | 118 | return self.buffer.items.len; |
| 115 | 119 | } |
src/test.zig+8| ... | ... | @@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct { |
| 177 | 177 | inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { |
| 178 | 178 | defaults = defaults ++ arch ++ "-macos" ++ ","; |
| 179 | 179 | } |
| 180 | // Windows | |
| 181 | defaults = defaults ++ "x86_64-windows" ++ ","; | |
| 180 | 182 | // Wasm |
| 181 | 183 | defaults = defaults ++ "wasm32-wasi"; |
| 182 | 184 | return defaults; |
| ... | ... | @@ -1546,6 +1548,12 @@ pub const TestContext = struct { |
| 1546 | 1548 | .self_exe_path = std.testing.zig_exe_path, |
| 1547 | 1549 | // TODO instead of turning off color, pass in a std.Progress.Node |
| 1548 | 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 | 1558 | defer comp.destroy(); |
| 1551 | 1559 |
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1| ... | ... | @@ -2,5 +2,5 @@ |
| 2 | 2 | // output_mode=Exe |
| 3 | 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 | 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 | 2 | // output_mode=Exe |
| 3 | 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 | 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 | 2 | // output_mode=Exe |
| 3 | 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 | 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 | 28 | } |
| 29 | 29 | |
| 30 | 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 | 32 | .build_modes = true, |
| 33 | 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 | 37 | .build_modes = true, |
| 38 | 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 | 42 | .build_modes = true, |
| 43 | 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 | 48 | .build_modes = true, |
| 48 | 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 | 53 | .build_modes = true, |
| 53 | 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 | 58 | .build_modes = true, |
| 58 | 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 | 108 | }, |
| 109 | 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 | 121 | .target = .{ |
| ... | ... | @@ -693,6 +701,8 @@ pub fn addPkgTests( |
| 693 | 701 | else => { |
| 694 | 702 | these_tests.use_stage1 = false; |
| 695 | 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 |