| author | |
| committer | |
| log | 37cdb5dbf90acd61584bae4a6661d0a6f9b54295 |
| tree | 6b369bbd58603d40b17b0e16e157a757b4a01616 |
| parent | b7900de1684021ff86c67105e14e34968821ea02 |
| parent | 9e070b653c89a9216f9dd9f78ed7c78c11460ac7 |
52 files changed, 2454 insertions(+), 1056 deletions(-)
ci/azure/pipelines.yml+2-2| ... | @@ -61,6 +61,7 @@ jobs: | ... | @@ -61,6 +61,7 @@ jobs: |
| 61 | 61 | ||
| 62 | - pwsh: | | 62 | - pwsh: | |
| 63 | Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\stage3-release" | 63 | Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\stage3-release" |
| 64 | Set-Variable -Name ZIGPREFIXPATH -Value "$(Get-Location)\$(ZIG_LLVM_CLANG_LLD_NAME)" | ||
| 64 | 65 | ||
| 65 | function CheckLastExitCode { | 66 | function CheckLastExitCode { |
| 66 | if (!$?) { | 67 | if (!$?) { |
| ... | @@ -72,8 +73,7 @@ jobs: | ... | @@ -72,8 +73,7 @@ jobs: |
| 72 | & "$ZIGINSTALLDIR\bin\zig.exe" build test docs ` | 73 | & "$ZIGINSTALLDIR\bin\zig.exe" build test docs ` |
| 73 | --search-prefix "$ZIGPREFIXPATH" ` | 74 | --search-prefix "$ZIGPREFIXPATH" ` |
| 74 | -Dstatic-llvm ` | 75 | -Dstatic-llvm ` |
| 75 | -Dskip-non-native ` | 76 | -Dskip-non-native |
| 76 | -Dskip-stage2-tests | ||
| 77 | CheckLastExitCode | 77 | CheckLastExitCode |
| 78 | name: test | 78 | name: test |
| 79 | displayName: 'Test' | 79 | displayName: 'Test' |
doc/docgen.zig+1-2| ... | @@ -1210,7 +1210,7 @@ fn genHtml( | ... | @@ -1210,7 +1210,7 @@ fn genHtml( |
| 1210 | var env_map = try process.getEnvMap(allocator); | 1210 | var env_map = try process.getEnvMap(allocator); |
| 1211 | try env_map.put("ZIG_DEBUG_COLOR", "1"); | 1211 | try env_map.put("ZIG_DEBUG_COLOR", "1"); |
| 1212 | 1212 | ||
| 1213 | const host = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); | 1213 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); |
| 1214 | const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe); | 1214 | const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe); |
| 1215 | 1215 | ||
| 1216 | for (toc.nodes) |node| { | 1216 | for (toc.nodes) |node| { |
| ... | @@ -1474,7 +1474,6 @@ fn genHtml( | ... | @@ -1474,7 +1474,6 @@ fn genHtml( |
| 1474 | .arch_os_abi = triple, | 1474 | .arch_os_abi = triple, |
| 1475 | }); | 1475 | }); |
| 1476 | const target_info = try std.zig.system.NativeTargetInfo.detect( | 1476 | const target_info = try std.zig.system.NativeTargetInfo.detect( |
| 1477 | allocator, | ||
| 1478 | cross_target, | 1477 | cross_target, |
| 1479 | ); | 1478 | ); |
| 1480 | switch (host.getExternalExecutor(target_info, .{ | 1479 | switch (host.getExternalExecutor(target_info, .{ |
lib/std/build.zig+2-2| ... | @@ -171,7 +171,7 @@ pub const Builder = struct { | ... | @@ -171,7 +171,7 @@ pub const Builder = struct { |
| 171 | const env_map = try allocator.create(EnvMap); | 171 | const env_map = try allocator.create(EnvMap); |
| 172 | env_map.* = try process.getEnvMap(allocator); | 172 | env_map.* = try process.getEnvMap(allocator); |
| 173 | 173 | ||
| 174 | const host = try NativeTargetInfo.detect(allocator, .{}); | 174 | const host = try NativeTargetInfo.detect(.{}); |
| 175 | 175 | ||
| 176 | const self = try allocator.create(Builder); | 176 | const self = try allocator.create(Builder); |
| 177 | self.* = Builder{ | 177 | self.* = Builder{ |
| ... | @@ -1798,7 +1798,7 @@ pub const LibExeObjStep = struct { | ... | @@ -1798,7 +1798,7 @@ pub const LibExeObjStep = struct { |
| 1798 | } | 1798 | } |
| 1799 | 1799 | ||
| 1800 | fn computeOutFileNames(self: *LibExeObjStep) void { | 1800 | fn computeOutFileNames(self: *LibExeObjStep) void { |
| 1801 | self.target_info = NativeTargetInfo.detect(self.builder.allocator, self.target) catch | 1801 | self.target_info = NativeTargetInfo.detect(self.target) catch |
| 1802 | unreachable; | 1802 | unreachable; |
| 1803 | 1803 | ||
| 1804 | const target = self.target_info.target; | 1804 | const target = self.target_info.target; |
lib/std/build/EmulatableRunStep.zig+1-1| ... | @@ -158,7 +158,7 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { | ... | @@ -158,7 +158,7 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { |
| 158 | 158 | ||
| 159 | const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable; | 159 | const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable; |
| 160 | const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable; | 160 | const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable; |
| 161 | const target_info = std.zig.system.NativeTargetInfo.detect(builder.allocator, artifact.target) catch unreachable; | 161 | const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable; |
| 162 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; | 162 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; |
| 163 | switch (builder.host.getExternalExecutor(target_info, .{ | 163 | switch (builder.host.getExternalExecutor(target_info, .{ |
| 164 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | 164 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, |
lib/std/fs/file.zig+16| ... | @@ -990,6 +990,8 @@ pub const File = struct { | ... | @@ -990,6 +990,8 @@ pub const File = struct { |
| 990 | return index; | 990 | return index; |
| 991 | } | 991 | } |
| 992 | 992 | ||
| 993 | /// On Windows, this function currently does alter the file pointer. | ||
| 994 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 993 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { | 995 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 994 | if (is_windows) { | 996 | if (is_windows) { |
| 995 | return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode); | 997 | return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode); |
| ... | @@ -1004,6 +1006,8 @@ pub const File = struct { | ... | @@ -1004,6 +1006,8 @@ pub const File = struct { |
| 1004 | 1006 | ||
| 1005 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it | 1007 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it |
| 1006 | /// means the file reached the end. Reaching the end of a file is not an error condition. | 1008 | /// means the file reached the end. Reaching the end of a file is not an error condition. |
| 1009 | /// On Windows, this function currently does alter the file pointer. | ||
| 1010 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1007 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize { | 1011 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 1008 | var index: usize = 0; | 1012 | var index: usize = 0; |
| 1009 | while (index != buffer.len) { | 1013 | while (index != buffer.len) { |
| ... | @@ -1058,6 +1062,8 @@ pub const File = struct { | ... | @@ -1058,6 +1062,8 @@ pub const File = struct { |
| 1058 | } | 1062 | } |
| 1059 | 1063 | ||
| 1060 | /// See https://github.com/ziglang/zig/issues/7699 | 1064 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1065 | /// On Windows, this function currently does alter the file pointer. | ||
| 1066 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1061 | pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { | 1067 | pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { |
| 1062 | if (is_windows) { | 1068 | if (is_windows) { |
| 1063 | // TODO improve this to use ReadFileScatter | 1069 | // TODO improve this to use ReadFileScatter |
| ... | @@ -1079,6 +1085,8 @@ pub const File = struct { | ... | @@ -1079,6 +1085,8 @@ pub const File = struct { |
| 1079 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 1085 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1080 | /// order to handle partial reads from the underlying OS layer. | 1086 | /// order to handle partial reads from the underlying OS layer. |
| 1081 | /// See https://github.com/ziglang/zig/issues/7699 | 1087 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1088 | /// On Windows, this function currently does alter the file pointer. | ||
| 1089 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1082 | pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize { | 1090 | pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize { |
| 1083 | if (iovecs.len == 0) return 0; | 1091 | if (iovecs.len == 0) return 0; |
| 1084 | 1092 | ||
| ... | @@ -1122,6 +1130,8 @@ pub const File = struct { | ... | @@ -1122,6 +1130,8 @@ pub const File = struct { |
| 1122 | } | 1130 | } |
| 1123 | } | 1131 | } |
| 1124 | 1132 | ||
| 1133 | /// On Windows, this function currently does alter the file pointer. | ||
| 1134 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1125 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { | 1135 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1126 | if (is_windows) { | 1136 | if (is_windows) { |
| 1127 | return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode); | 1137 | return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode); |
| ... | @@ -1134,6 +1144,8 @@ pub const File = struct { | ... | @@ -1134,6 +1144,8 @@ pub const File = struct { |
| 1134 | } | 1144 | } |
| 1135 | } | 1145 | } |
| 1136 | 1146 | ||
| 1147 | /// On Windows, this function currently does alter the file pointer. | ||
| 1148 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1137 | pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void { | 1149 | pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void { |
| 1138 | var index: usize = 0; | 1150 | var index: usize = 0; |
| 1139 | while (index < bytes.len) { | 1151 | while (index < bytes.len) { |
| ... | @@ -1179,6 +1191,8 @@ pub const File = struct { | ... | @@ -1179,6 +1191,8 @@ pub const File = struct { |
| 1179 | } | 1191 | } |
| 1180 | 1192 | ||
| 1181 | /// See https://github.com/ziglang/zig/issues/7699 | 1193 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1194 | /// On Windows, this function currently does alter the file pointer. | ||
| 1195 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1182 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize { | 1196 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize { |
| 1183 | if (is_windows) { | 1197 | if (is_windows) { |
| 1184 | // TODO improve this to use WriteFileScatter | 1198 | // TODO improve this to use WriteFileScatter |
| ... | @@ -1197,6 +1211,8 @@ pub const File = struct { | ... | @@ -1197,6 +1211,8 @@ pub const File = struct { |
| 1197 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 1211 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1198 | /// order to handle partial writes from the underlying OS layer. | 1212 | /// order to handle partial writes from the underlying OS layer. |
| 1199 | /// See https://github.com/ziglang/zig/issues/7699 | 1213 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1214 | /// On Windows, this function currently does alter the file pointer. | ||
| 1215 | /// https://github.com/ziglang/zig/issues/12783 | ||
| 1200 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void { | 1216 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void { |
| 1201 | if (iovecs.len == 0) return; | 1217 | if (iovecs.len == 0) return; |
| 1202 | 1218 |
lib/std/io.zig+12| ... | @@ -36,6 +36,10 @@ pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking | ... | @@ -36,6 +36,10 @@ pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking |
| 36 | 36 | ||
| 37 | fn getStdOutHandle() os.fd_t { | 37 | fn getStdOutHandle() os.fd_t { |
| 38 | if (builtin.os.tag == .windows) { | 38 | if (builtin.os.tag == .windows) { |
| 39 | if (builtin.zig_backend == .stage2_x86_64) { | ||
| 40 | // TODO: this is just a temporary workaround until we advance x86 backend further along. | ||
| 41 | return os.windows.GetStdHandle(os.windows.STD_OUTPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE; | ||
| 42 | } | ||
| 39 | return os.windows.peb().ProcessParameters.hStdOutput; | 43 | return os.windows.peb().ProcessParameters.hStdOutput; |
| 40 | } | 44 | } |
| 41 | 45 | ||
| ... | @@ -58,6 +62,10 @@ pub fn getStdOut() File { | ... | @@ -58,6 +62,10 @@ pub fn getStdOut() File { |
| 58 | 62 | ||
| 59 | fn getStdErrHandle() os.fd_t { | 63 | fn getStdErrHandle() os.fd_t { |
| 60 | if (builtin.os.tag == .windows) { | 64 | if (builtin.os.tag == .windows) { |
| 65 | if (builtin.zig_backend == .stage2_x86_64) { | ||
| 66 | // TODO: this is just a temporary workaround until we advance x86 backend further along. | ||
| 67 | return os.windows.GetStdHandle(os.windows.STD_ERROR_HANDLE) catch os.windows.INVALID_HANDLE_VALUE; | ||
| 68 | } | ||
| 61 | return os.windows.peb().ProcessParameters.hStdError; | 69 | return os.windows.peb().ProcessParameters.hStdError; |
| 62 | } | 70 | } |
| 63 | 71 | ||
| ... | @@ -80,6 +88,10 @@ pub fn getStdErr() File { | ... | @@ -80,6 +88,10 @@ pub fn getStdErr() File { |
| 80 | 88 | ||
| 81 | fn getStdInHandle() os.fd_t { | 89 | fn getStdInHandle() os.fd_t { |
| 82 | if (builtin.os.tag == .windows) { | 90 | if (builtin.os.tag == .windows) { |
| 91 | if (builtin.zig_backend == .stage2_x86_64) { | ||
| 92 | // TODO: this is just a temporary workaround until we advance x86 backend further along. | ||
| 93 | return os.windows.GetStdHandle(os.windows.STD_INPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE; | ||
| 94 | } | ||
| 83 | return os.windows.peb().ProcessParameters.hStdInput; | 95 | return os.windows.peb().ProcessParameters.hStdInput; |
| 84 | } | 96 | } |
| 85 | 97 |
lib/std/os/uefi/protocols/block_io_protocol.zig+2-2| ... | @@ -2,7 +2,7 @@ const std = @import("std"); | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | const uefi = std.os.uefi; | 2 | const uefi = std.os.uefi; |
| 3 | const Status = uefi.Status; | 3 | const Status = uefi.Status; |
| 4 | 4 | ||
| 5 | const EfiBlockMedia = extern struct { | 5 | pub const EfiBlockMedia = extern struct { |
| 6 | /// The current media ID. If the media changes, this value is changed. | 6 | /// The current media ID. If the media changes, this value is changed. |
| 7 | media_id: u32, | 7 | media_id: u32, |
| 8 | 8 | ||
| ... | @@ -38,7 +38,7 @@ const EfiBlockMedia = extern struct { | ... | @@ -38,7 +38,7 @@ const EfiBlockMedia = extern struct { |
| 38 | optimal_transfer_length_granularity: u32, | 38 | optimal_transfer_length_granularity: u32, |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | const BlockIoProtocol = extern struct { | 41 | pub const BlockIoProtocol = extern struct { |
| 42 | const Self = @This(); | 42 | const Self = @This(); |
| 43 | 43 | ||
| 44 | revision: u64, | 44 | revision: u64, |
lib/std/os/windows/kernel32.zig+7-1| ... | @@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile( | ... | @@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile( |
| 348 | in_out_lpOverlapped: ?*OVERLAPPED, | 348 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 349 | ) callconv(WINAPI) BOOL; | 349 | ) callconv(WINAPI) BOOL; |
| 350 | 350 | ||
| 351 | pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: *OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(WINAPI) BOOL; | 351 | pub extern "kernel32" fn WriteFileEx( |
| 352 | hFile: HANDLE, | ||
| 353 | lpBuffer: [*]const u8, | ||
| 354 | nNumberOfBytesToWrite: DWORD, | ||
| 355 | lpOverlapped: *OVERLAPPED, | ||
| 356 | lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, | ||
| 357 | ) callconv(WINAPI) BOOL; | ||
| 352 | 358 | ||
| 353 | pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE; | 359 | pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE; |
| 354 | 360 |
lib/std/simd.zig+10-1| ... | @@ -9,7 +9,7 @@ const builtin = @import("builtin"); | ... | @@ -9,7 +9,7 @@ const builtin = @import("builtin"); |
| 9 | pub fn suggestVectorSizeForCpu(comptime T: type, comptime cpu: std.Target.Cpu) ?usize { | 9 | pub fn suggestVectorSizeForCpu(comptime T: type, comptime cpu: std.Target.Cpu) ?usize { |
| 10 | // This is guesswork, if you have better suggestions can add it or edit the current here | 10 | // This is guesswork, if you have better suggestions can add it or edit the current here |
| 11 | // This can run in comptime only, but stage 1 fails at it, stage 2 can understand it | 11 | // This can run in comptime only, but stage 1 fails at it, stage 2 can understand it |
| 12 | const element_bit_size = @maximum(8, std.math.ceilPowerOfTwo(T, @bitSizeOf(T)) catch unreachable); | 12 | const element_bit_size = @maximum(8, std.math.ceilPowerOfTwo(u16, @bitSizeOf(T)) catch unreachable); |
| 13 | const vector_bit_size: u16 = blk: { | 13 | const vector_bit_size: u16 = blk: { |
| 14 | if (cpu.arch.isX86()) { | 14 | if (cpu.arch.isX86()) { |
| 15 | if (T == bool and std.Target.x86.featureSetHas(.prefer_mask_registers)) return 64; | 15 | if (T == bool and std.Target.x86.featureSetHas(.prefer_mask_registers)) return 64; |
| ... | @@ -57,6 +57,15 @@ pub fn suggestVectorSize(comptime T: type) ?usize { | ... | @@ -57,6 +57,15 @@ pub fn suggestVectorSize(comptime T: type) ?usize { |
| 57 | return suggestVectorSizeForCpu(T, builtin.cpu); | 57 | return suggestVectorSizeForCpu(T, builtin.cpu); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | test "suggestVectorSizeForCpu works with signed and unsigned values" { | ||
| 61 | comptime var cpu = std.Target.Cpu.baseline(std.Target.Cpu.Arch.x86_64); | ||
| 62 | comptime cpu.features.addFeature(@enumToInt(std.Target.x86.Feature.avx512f)); | ||
| 63 | const signed_integer_size = suggestVectorSizeForCpu(i32, cpu).?; | ||
| 64 | const unsigned_integer_size = suggestVectorSizeForCpu(u32, cpu).?; | ||
| 65 | try std.testing.expectEqual(@as(usize, 16), unsigned_integer_size); | ||
| 66 | try std.testing.expectEqual(@as(usize, 16), signed_integer_size); | ||
| 67 | } | ||
| 68 | |||
| 60 | fn vectorLength(comptime VectorType: type) comptime_int { | 69 | fn vectorLength(comptime VectorType: type) comptime_int { |
| 61 | return switch (@typeInfo(VectorType)) { | 70 | return switch (@typeInfo(VectorType)) { |
| 62 | .Vector => |info| info.len, | 71 | .Vector => |info| info.len, |
lib/std/start.zig+4| ... | @@ -36,6 +36,10 @@ comptime { | ... | @@ -36,6 +36,10 @@ comptime { |
| 36 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { | 36 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { |
| 37 | @export(main2, .{ .name = "main" }); | 37 | @export(main2, .{ .name = "main" }); |
| 38 | } | 38 | } |
| 39 | } else if (builtin.os.tag == .windows) { | ||
| 40 | if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { | ||
| 41 | @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); | ||
| 42 | } | ||
| 39 | } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { | 43 | } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { |
| 40 | @export(wasiMain2, .{ .name = "_start" }); | 44 | @export(wasiMain2, .{ .name = "_start" }); |
| 41 | } else { | 45 | } else { |
lib/std/zig/system/NativeTargetInfo.zig+320-218| ... | @@ -28,6 +28,7 @@ pub const DetectError = error{ | ... | @@ -28,6 +28,7 @@ pub const DetectError = error{ |
| 28 | SystemFdQuotaExceeded, | 28 | SystemFdQuotaExceeded, |
| 29 | DeviceBusy, | 29 | DeviceBusy, |
| 30 | OSVersionDetectionFail, | 30 | OSVersionDetectionFail, |
| 31 | Unexpected, | ||
| 31 | }; | 32 | }; |
| 32 | 33 | ||
| 33 | /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected | 34 | /// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected |
| ... | @@ -36,8 +37,7 @@ pub const DetectError = error{ | ... | @@ -36,8 +37,7 @@ pub const DetectError = error{ |
| 36 | /// relative to that. | 37 | /// relative to that. |
| 37 | /// Any resources this function allocates are released before returning, and so there is no | 38 | /// Any resources this function allocates are released before returning, and so there is no |
| 38 | /// deinitialization method. | 39 | /// deinitialization method. |
| 39 | /// TODO Remove the Allocator requirement from this function. | 40 | pub fn detect(cross_target: CrossTarget) DetectError!NativeTargetInfo { |
| 40 | pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { | ||
| 41 | var os = cross_target.getOsTag().defaultVersionRange(cross_target.getCpuArch()); | 41 | var os = cross_target.getOsTag().defaultVersionRange(cross_target.getCpuArch()); |
| 42 | if (cross_target.os_tag == null) { | 42 | if (cross_target.os_tag == null) { |
| 43 | switch (builtin.target.os.tag) { | 43 | switch (builtin.target.os.tag) { |
| ... | @@ -198,7 +198,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ | ... | @@ -198,7 +198,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ |
| 198 | } orelse backup_cpu_detection: { | 198 | } orelse backup_cpu_detection: { |
| 199 | break :backup_cpu_detection Target.Cpu.baseline(cpu_arch); | 199 | break :backup_cpu_detection Target.Cpu.baseline(cpu_arch); |
| 200 | }; | 200 | }; |
| 201 | var result = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target); | 201 | var result = try detectAbiAndDynamicLinker(cpu, os, cross_target); |
| 202 | // For x86, we need to populate some CPU feature flags depending on architecture | 202 | // For x86, we need to populate some CPU feature flags depending on architecture |
| 203 | // and mode: | 203 | // and mode: |
| 204 | // * 16bit_mode => if the abi is code16 | 204 | // * 16bit_mode => if the abi is code16 |
| ... | @@ -235,13 +235,20 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ | ... | @@ -235,13 +235,20 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ |
| 235 | return result; | 235 | return result; |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | /// First we attempt to use the executable's own binary. If it is dynamically | 238 | /// In the past, this function attempted to use the executable's own binary if it was dynamically |
| 239 | /// linked, then it should answer both the C ABI question and the dynamic linker question. | 239 | /// linked to answer both the C ABI question and the dynamic linker question. However, this |
| 240 | /// If it is statically linked, then we try /usr/bin/env (or the file it references in shebang). If that does not provide the answer, then | 240 | /// could be problematic on a system that uses a RUNPATH for the compiler binary, locking |
| 241 | /// we fall back to the defaults. | 241 | /// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc |
| 242 | /// TODO Remove the Allocator requirement from this function. | 242 | /// version. The problem is that libc.so.6 glibc version will match that of the system while |
| 243 | /// the dynamic linker will match that of the compiler binary. Executables with these versions | ||
| 244 | /// mismatching will fail to run. | ||
| 245 | /// | ||
| 246 | /// Therefore, this function works the same regardless of whether the compiler binary is | ||
| 247 | /// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the | ||
| 248 | /// answer to these questions, or if there is a shebang line, then it chases the referenced | ||
| 249 | /// file recursively. If that does not provide the answer, then the function falls back to | ||
| 250 | /// defaults. | ||
| 243 | fn detectAbiAndDynamicLinker( | 251 | fn detectAbiAndDynamicLinker( |
| 244 | allocator: Allocator, | ||
| 245 | cpu: Target.Cpu, | 252 | cpu: Target.Cpu, |
| 246 | os: Target.Os, | 253 | os: Target.Os, |
| 247 | cross_target: CrossTarget, | 254 | cross_target: CrossTarget, |
| ... | @@ -279,8 +286,8 @@ fn detectAbiAndDynamicLinker( | ... | @@ -279,8 +286,8 @@ fn detectAbiAndDynamicLinker( |
| 279 | const ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch); | 286 | const ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch); |
| 280 | 287 | ||
| 281 | for (all_abis) |abi| { | 288 | for (all_abis) |abi| { |
| 282 | // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and | 289 | // This may be a nonsensical parameter. We detect this with |
| 283 | // skip adding it to `ld_info_list`. | 290 | // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`. |
| 284 | const target: Target = .{ | 291 | const target: Target = .{ |
| 285 | .cpu = cpu, | 292 | .cpu = cpu, |
| 286 | .os = os, | 293 | .os = os, |
| ... | @@ -300,64 +307,6 @@ fn detectAbiAndDynamicLinker( | ... | @@ -300,64 +307,6 @@ fn detectAbiAndDynamicLinker( |
| 300 | 307 | ||
| 301 | // Best case scenario: the executable is dynamically linked, and we can iterate | 308 | // Best case scenario: the executable is dynamically linked, and we can iterate |
| 302 | // over our own shared objects and find a dynamic linker. | 309 | // over our own shared objects and find a dynamic linker. |
| 303 | self_exe: { | ||
| 304 | const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); | ||
| 305 | defer { | ||
| 306 | for (lib_paths) |lib_path| { | ||
| 307 | allocator.free(lib_path); | ||
| 308 | } | ||
| 309 | allocator.free(lib_paths); | ||
| 310 | } | ||
| 311 | |||
| 312 | var found_ld_info: LdInfo = undefined; | ||
| 313 | var found_ld_path: [:0]const u8 = undefined; | ||
| 314 | |||
| 315 | // Look for dynamic linker. | ||
| 316 | // This is O(N^M) but typical case here is N=2 and M=10. | ||
| 317 | find_ld: for (lib_paths) |lib_path| { | ||
| 318 | for (ld_info_list) |ld_info| { | ||
| 319 | const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); | ||
| 320 | if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { | ||
| 321 | found_ld_info = ld_info; | ||
| 322 | found_ld_path = lib_path; | ||
| 323 | break :find_ld; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } else break :self_exe; | ||
| 327 | |||
| 328 | // Look for glibc version. | ||
| 329 | var os_adjusted = os; | ||
| 330 | if (builtin.target.os.tag == .linux and found_ld_info.abi.isGnu() and | ||
| 331 | cross_target.glibc_version == null) | ||
| 332 | { | ||
| 333 | for (lib_paths) |lib_path| { | ||
| 334 | if (std.mem.endsWith(u8, lib_path, glibc_so_basename)) { | ||
| 335 | os_adjusted.version_range.linux.glibc = glibcVerFromSO(lib_path) catch |err| switch (err) { | ||
| 336 | error.UnrecognizedGnuLibCFileName => continue, | ||
| 337 | error.InvalidGnuLibCVersion => continue, | ||
| 338 | error.GnuLibCVersionUnavailable => continue, | ||
| 339 | else => |e| return e, | ||
| 340 | }; | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | var result: NativeTargetInfo = .{ | ||
| 347 | .target = .{ | ||
| 348 | .cpu = cpu, | ||
| 349 | .os = os_adjusted, | ||
| 350 | .abi = cross_target.abi orelse found_ld_info.abi, | ||
| 351 | .ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os_adjusted.tag, cpu.arch), | ||
| 352 | }, | ||
| 353 | .dynamic_linker = if (cross_target.dynamic_linker.get() == null) | ||
| 354 | DynamicLinker.init(found_ld_path) | ||
| 355 | else | ||
| 356 | cross_target.dynamic_linker, | ||
| 357 | }; | ||
| 358 | return result; | ||
| 359 | } | ||
| 360 | |||
| 361 | const elf_file = blk: { | 310 | const elf_file = blk: { |
| 362 | // This block looks for a shebang line in /usr/bin/env, | 311 | // This block looks for a shebang line in /usr/bin/env, |
| 363 | // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, | 312 | // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, |
| ... | @@ -369,7 +318,7 @@ fn detectAbiAndDynamicLinker( | ... | @@ -369,7 +318,7 @@ fn detectAbiAndDynamicLinker( |
| 369 | // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) | 318 | // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) |
| 370 | var buffer: [258]u8 = undefined; | 319 | var buffer: [258]u8 = undefined; |
| 371 | while (true) { | 320 | while (true) { |
| 372 | const file = std.fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { | 321 | const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { |
| 373 | error.NoSpaceLeft => unreachable, | 322 | error.NoSpaceLeft => unreachable, |
| 374 | error.NameTooLong => unreachable, | 323 | error.NameTooLong => unreachable, |
| 375 | error.PathAlreadyExists => unreachable, | 324 | error.PathAlreadyExists => unreachable, |
| ... | @@ -390,44 +339,35 @@ fn detectAbiAndDynamicLinker( | ... | @@ -390,44 +339,35 @@ fn detectAbiAndDynamicLinker( |
| 390 | error.FileTooBig, | 339 | error.FileTooBig, |
| 391 | error.Unexpected, | 340 | error.Unexpected, |
| 392 | => |e| { | 341 | => |e| { |
| 393 | std.log.warn("Encoutered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); | 342 | std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); |
| 394 | return defaultAbiAndDynamicLinker(cpu, os, cross_target); | 343 | return defaultAbiAndDynamicLinker(cpu, os, cross_target); |
| 395 | }, | 344 | }, |
| 396 | 345 | ||
| 397 | else => |e| return e, | 346 | else => |e| return e, |
| 398 | }; | 347 | }; |
| 348 | errdefer file.close(); | ||
| 399 | 349 | ||
| 400 | const line = file.reader().readUntilDelimiter(&buffer, '\n') catch |err| switch (err) { | 350 | const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) { |
| 401 | error.IsDir => unreachable, // Handled before | 351 | error.UnexpectedEndOfFile, |
| 402 | error.AccessDenied => unreachable, | 352 | error.UnableToReadElfFile, |
| 403 | error.WouldBlock => unreachable, // Did not request blocking mode | ||
| 404 | error.OperationAborted => unreachable, // Windows-only | ||
| 405 | error.BrokenPipe => unreachable, | ||
| 406 | error.ConnectionResetByPeer => unreachable, | ||
| 407 | error.ConnectionTimedOut => unreachable, | ||
| 408 | error.InputOutput => unreachable, | ||
| 409 | error.Unexpected => unreachable, | ||
| 410 | |||
| 411 | error.StreamTooLong, | ||
| 412 | error.EndOfStream, | ||
| 413 | error.NotOpenForReading, | ||
| 414 | => break :blk file, | 353 | => break :blk file, |
| 415 | 354 | ||
| 416 | else => |e| { | 355 | else => |e| return e, |
| 417 | file.close(); | ||
| 418 | return e; | ||
| 419 | }, | ||
| 420 | }; | 356 | }; |
| 357 | const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file; | ||
| 358 | const line = buffer[0..newline]; | ||
| 421 | if (!mem.startsWith(u8, line, "#!")) break :blk file; | 359 | if (!mem.startsWith(u8, line, "#!")) break :blk file; |
| 422 | var it = std.mem.tokenize(u8, line[2..], " "); | 360 | var it = mem.tokenize(u8, line[2..], " "); |
| 423 | file.close(); | ||
| 424 | file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target); | 361 | file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target); |
| 362 | file.close(); | ||
| 425 | } | 363 | } |
| 426 | }; | 364 | }; |
| 427 | defer elf_file.close(); | 365 | defer elf_file.close(); |
| 428 | 366 | ||
| 429 | // If Zig is statically linked, such as via distributed binary static builds, the above | 367 | // If Zig is statically linked, such as via distributed binary static builds, the above |
| 430 | // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file. | 368 | // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file. |
| 369 | // TODO: inline this function and combine the buffer we already read above to find | ||
| 370 | // the possible shebang line with the buffer we use for the ELF header. | ||
| 431 | return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) { | 371 | return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) { |
| 432 | error.FileSystem, | 372 | error.FileSystem, |
| 433 | error.SystemResources, | 373 | error.SystemResources, |
| ... | @@ -447,31 +387,196 @@ fn detectAbiAndDynamicLinker( | ... | @@ -447,31 +387,196 @@ fn detectAbiAndDynamicLinker( |
| 447 | error.NameTooLong, | 387 | error.NameTooLong, |
| 448 | // Finally, we fall back on the standard path. | 388 | // Finally, we fall back on the standard path. |
| 449 | => |e| { | 389 | => |e| { |
| 450 | std.log.warn("Encoutered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); | 390 | std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); |
| 451 | return defaultAbiAndDynamicLinker(cpu, os, cross_target); | 391 | return defaultAbiAndDynamicLinker(cpu, os, cross_target); |
| 452 | }, | 392 | }, |
| 453 | }; | 393 | }; |
| 454 | } | 394 | } |
| 455 | 395 | ||
| 456 | const glibc_so_basename = "libc.so.6"; | 396 | fn glibcVerFromRPath(rpath: []const u8) !std.builtin.Version { |
| 397 | var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { | ||
| 398 | error.NameTooLong => unreachable, | ||
| 399 | error.InvalidUtf8 => unreachable, | ||
| 400 | error.BadPathName => unreachable, | ||
| 401 | error.DeviceBusy => unreachable, | ||
| 402 | |||
| 403 | error.FileNotFound, | ||
| 404 | error.NotDir, | ||
| 405 | error.InvalidHandle, | ||
| 406 | error.AccessDenied, | ||
| 407 | error.NoDevice, | ||
| 408 | => return error.GLibCNotFound, | ||
| 457 | 409 | ||
| 458 | fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version { | 410 | error.ProcessFdQuotaExceeded, |
| 459 | var link_buf: [std.os.PATH_MAX]u8 = undefined; | 411 | error.SystemFdQuotaExceeded, |
| 460 | const link_name = std.os.readlinkZ(so_path.ptr, &link_buf) catch |err| switch (err) { | 412 | error.SystemResources, |
| 461 | error.AccessDenied => return error.GnuLibCVersionUnavailable, | 413 | error.SymLinkLoop, |
| 462 | error.FileSystem => return error.FileSystem, | 414 | error.Unexpected, |
| 463 | error.SymLinkLoop => return error.SymLinkLoop, | 415 | => |e| return e, |
| 416 | }; | ||
| 417 | defer dir.close(); | ||
| 418 | |||
| 419 | // Now we have a candidate for the path to libc shared object. In | ||
| 420 | // the past, we used readlink() here because the link name would | ||
| 421 | // reveal the glibc version. However, in more recent GNU/Linux | ||
| 422 | // installations, there is no symlink. Thus we instead use a more | ||
| 423 | // robust check of opening the libc shared object and looking at the | ||
| 424 | // .dynstr section, and finding the max version number of symbols | ||
| 425 | // that start with "GLIBC_2.". | ||
| 426 | const glibc_so_basename = "libc.so.6"; | ||
| 427 | var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) { | ||
| 464 | error.NameTooLong => unreachable, | 428 | error.NameTooLong => unreachable, |
| 465 | error.NotLink => return error.GnuLibCVersionUnavailable, | ||
| 466 | error.FileNotFound => return error.GnuLibCVersionUnavailable, | ||
| 467 | error.SystemResources => return error.SystemResources, | ||
| 468 | error.NotDir => return error.GnuLibCVersionUnavailable, | ||
| 469 | error.Unexpected => return error.GnuLibCVersionUnavailable, | ||
| 470 | error.InvalidUtf8 => unreachable, // Windows only | 429 | error.InvalidUtf8 => unreachable, // Windows only |
| 471 | error.BadPathName => unreachable, // Windows only | 430 | error.BadPathName => unreachable, // Windows only |
| 472 | error.UnsupportedReparsePointType => unreachable, // Windows only | 431 | error.PipeBusy => unreachable, // Windows-only |
| 432 | error.SharingViolation => unreachable, // Windows-only | ||
| 433 | error.FileLocksNotSupported => unreachable, // No lock requested. | ||
| 434 | error.NoSpaceLeft => unreachable, // read-only | ||
| 435 | error.PathAlreadyExists => unreachable, // read-only | ||
| 436 | error.DeviceBusy => unreachable, // read-only | ||
| 437 | error.FileBusy => unreachable, // read-only | ||
| 438 | error.InvalidHandle => unreachable, // should not be in the error set | ||
| 439 | error.WouldBlock => unreachable, // not using O_NONBLOCK | ||
| 440 | error.NoDevice => unreachable, // not asking for a special device | ||
| 441 | |||
| 442 | error.AccessDenied, | ||
| 443 | error.FileNotFound, | ||
| 444 | error.NotDir, | ||
| 445 | error.IsDir, | ||
| 446 | => return error.GLibCNotFound, | ||
| 447 | |||
| 448 | error.FileTooBig => return error.Unexpected, | ||
| 449 | |||
| 450 | error.ProcessFdQuotaExceeded, | ||
| 451 | error.SystemFdQuotaExceeded, | ||
| 452 | error.SystemResources, | ||
| 453 | error.SymLinkLoop, | ||
| 454 | error.Unexpected, | ||
| 455 | => |e| return e, | ||
| 456 | }; | ||
| 457 | defer f.close(); | ||
| 458 | |||
| 459 | return glibcVerFromSoFile(f) catch |err| switch (err) { | ||
| 460 | error.InvalidElfMagic, | ||
| 461 | error.InvalidElfEndian, | ||
| 462 | error.InvalidElfClass, | ||
| 463 | error.InvalidElfFile, | ||
| 464 | error.InvalidElfVersion, | ||
| 465 | error.InvalidGnuLibCVersion, | ||
| 466 | error.UnexpectedEndOfFile, | ||
| 467 | => return error.GLibCNotFound, | ||
| 468 | |||
| 469 | error.SystemResources, | ||
| 470 | error.UnableToReadElfFile, | ||
| 471 | error.Unexpected, | ||
| 472 | error.FileSystem, | ||
| 473 | => |e| return e, | ||
| 473 | }; | 474 | }; |
| 474 | return glibcVerFromLinkName(link_name, "libc-"); | 475 | } |
| 476 | |||
| 477 | fn glibcVerFromSoFile(file: fs.File) !std.builtin.Version { | ||
| 478 | var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; | ||
| 479 | _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); | ||
| 480 | const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); | ||
| 481 | const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); | ||
| 482 | if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; | ||
| 483 | const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { | ||
| 484 | elf.ELFDATA2LSB => .Little, | ||
| 485 | elf.ELFDATA2MSB => .Big, | ||
| 486 | else => return error.InvalidElfEndian, | ||
| 487 | }; | ||
| 488 | const need_bswap = elf_endian != native_endian; | ||
| 489 | if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; | ||
| 490 | |||
| 491 | const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { | ||
| 492 | elf.ELFCLASS32 => false, | ||
| 493 | elf.ELFCLASS64 => true, | ||
| 494 | else => return error.InvalidElfClass, | ||
| 495 | }; | ||
| 496 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); | ||
| 497 | var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); | ||
| 498 | const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); | ||
| 499 | const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); | ||
| 500 | var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; | ||
| 501 | if (sh_buf.len < shentsize) return error.InvalidElfFile; | ||
| 502 | |||
| 503 | _ = try preadMin(file, &sh_buf, str_section_off, shentsize); | ||
| 504 | const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); | ||
| 505 | const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); | ||
| 506 | const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); | ||
| 507 | const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); | ||
| 508 | var strtab_buf: [4096:0]u8 = undefined; | ||
| 509 | const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); | ||
| 510 | const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); | ||
| 511 | const shstrtab = strtab_buf[0..shstrtab_read_len]; | ||
| 512 | const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); | ||
| 513 | var sh_i: u16 = 0; | ||
| 514 | const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { | ||
| 515 | // Reserve some bytes so that we can deref the 64-bit struct fields | ||
| 516 | // even when the ELF file is 32-bits. | ||
| 517 | const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); | ||
| 518 | const sh_read_byte_len = try preadMin( | ||
| 519 | file, | ||
| 520 | sh_buf[0 .. sh_buf.len - sh_reserve], | ||
| 521 | shoff, | ||
| 522 | shentsize, | ||
| 523 | ); | ||
| 524 | var sh_buf_i: usize = 0; | ||
| 525 | while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ | ||
| 526 | sh_i += 1; | ||
| 527 | shoff += shentsize; | ||
| 528 | sh_buf_i += shentsize; | ||
| 529 | }) { | ||
| 530 | const sh32 = @ptrCast( | ||
| 531 | *elf.Elf32_Shdr, | ||
| 532 | @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]), | ||
| 533 | ); | ||
| 534 | const sh64 = @ptrCast( | ||
| 535 | *elf.Elf64_Shdr, | ||
| 536 | @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]), | ||
| 537 | ); | ||
| 538 | const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); | ||
| 539 | // TODO this pointer cast should not be necessary | ||
| 540 | const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0); | ||
| 541 | if (mem.eql(u8, sh_name, ".dynstr")) { | ||
| 542 | break :find_dyn_str .{ | ||
| 543 | .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), | ||
| 544 | .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), | ||
| 545 | }; | ||
| 546 | } | ||
| 547 | } | ||
| 548 | } else return error.InvalidGnuLibCVersion; | ||
| 549 | |||
| 550 | // Here we loop over all the strings in the dynstr string table, assuming that any | ||
| 551 | // strings that start with "GLIBC_2." indicate the existence of such a glibc version, | ||
| 552 | // and furthermore, that the system-installed glibc is at minimum that version. | ||
| 553 | |||
| 554 | // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system. | ||
| 555 | // Here I use double this value plus some headroom. This makes it only need | ||
| 556 | // a single read syscall here. | ||
| 557 | var buf: [80000]u8 = undefined; | ||
| 558 | if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion; | ||
| 559 | |||
| 560 | const dynstr_size = @intCast(usize, dynstr.size); | ||
| 561 | const dynstr_bytes = buf[0..dynstr_size]; | ||
| 562 | _ = try preadMin(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len); | ||
| 563 | var it = mem.split(u8, dynstr_bytes, &.{0}); | ||
| 564 | var max_ver: std.builtin.Version = .{ .major = 2, .minor = 2, .patch = 5 }; | ||
| 565 | while (it.next()) |s| { | ||
| 566 | if (mem.startsWith(u8, s, "GLIBC_2.")) { | ||
| 567 | const chopped = s["GLIBC_".len..]; | ||
| 568 | const ver = std.builtin.Version.parse(chopped) catch |err| switch (err) { | ||
| 569 | error.Overflow => return error.InvalidGnuLibCVersion, | ||
| 570 | error.InvalidCharacter => return error.InvalidGnuLibCVersion, | ||
| 571 | error.InvalidVersion => return error.InvalidGnuLibCVersion, | ||
| 572 | }; | ||
| 573 | switch (ver.order(max_ver)) { | ||
| 574 | .gt => max_ver = ver, | ||
| 575 | .lt, .eq => continue, | ||
| 576 | } | ||
| 577 | } | ||
| 578 | } | ||
| 579 | return max_ver; | ||
| 475 | } | 580 | } |
| 476 | 581 | ||
| 477 | fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version { | 582 | fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version { |
| ... | @@ -641,65 +746,65 @@ pub fn abiAndDynamicLinkerFromFile( | ... | @@ -641,65 +746,65 @@ pub fn abiAndDynamicLinkerFromFile( |
| 641 | if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and | 746 | if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and |
| 642 | cross_target.glibc_version == null) | 747 | cross_target.glibc_version == null) |
| 643 | { | 748 | { |
| 644 | if (rpath_offset) |rpoff| { | 749 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); |
| 645 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); | 750 | |
| 646 | 751 | var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); | |
| 647 | var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); | 752 | const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); |
| 648 | const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); | 753 | const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); |
| 649 | const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); | 754 | |
| 650 | 755 | var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; | |
| 651 | var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; | 756 | if (sh_buf.len < shentsize) return error.InvalidElfFile; |
| 652 | if (sh_buf.len < shentsize) return error.InvalidElfFile; | 757 | |
| 653 | 758 | _ = try preadMin(file, &sh_buf, str_section_off, shentsize); | |
| 654 | _ = try preadMin(file, &sh_buf, str_section_off, shentsize); | 759 | const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); |
| 655 | const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); | 760 | const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); |
| 656 | const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); | 761 | const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); |
| 657 | const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); | 762 | const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); |
| 658 | const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); | 763 | var strtab_buf: [4096:0]u8 = undefined; |
| 659 | var strtab_buf: [4096:0]u8 = undefined; | 764 | const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); |
| 660 | const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); | 765 | const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); |
| 661 | const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); | 766 | const shstrtab = strtab_buf[0..shstrtab_read_len]; |
| 662 | const shstrtab = strtab_buf[0..shstrtab_read_len]; | 767 | |
| 663 | 768 | const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); | |
| 664 | const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); | 769 | var sh_i: u16 = 0; |
| 665 | var sh_i: u16 = 0; | 770 | const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { |
| 666 | const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { | 771 | // Reserve some bytes so that we can deref the 64-bit struct fields |
| 667 | // Reserve some bytes so that we can deref the 64-bit struct fields | 772 | // even when the ELF file is 32-bits. |
| 668 | // even when the ELF file is 32-bits. | 773 | const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); |
| 669 | const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); | 774 | const sh_read_byte_len = try preadMin( |
| 670 | const sh_read_byte_len = try preadMin( | 775 | file, |
| 671 | file, | 776 | sh_buf[0 .. sh_buf.len - sh_reserve], |
| 672 | sh_buf[0 .. sh_buf.len - sh_reserve], | 777 | shoff, |
| 673 | shoff, | 778 | shentsize, |
| 674 | shentsize, | 779 | ); |
| 780 | var sh_buf_i: usize = 0; | ||
| 781 | while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ | ||
| 782 | sh_i += 1; | ||
| 783 | shoff += shentsize; | ||
| 784 | sh_buf_i += shentsize; | ||
| 785 | }) { | ||
| 786 | const sh32 = @ptrCast( | ||
| 787 | *elf.Elf32_Shdr, | ||
| 788 | @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]), | ||
| 675 | ); | 789 | ); |
| 676 | var sh_buf_i: usize = 0; | 790 | const sh64 = @ptrCast( |
| 677 | while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ | 791 | *elf.Elf64_Shdr, |
| 678 | sh_i += 1; | 792 | @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]), |
| 679 | shoff += shentsize; | 793 | ); |
| 680 | sh_buf_i += shentsize; | 794 | const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); |
| 681 | }) { | 795 | // TODO this pointer cast should not be necessary |
| 682 | const sh32 = @ptrCast( | 796 | const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0); |
| 683 | *elf.Elf32_Shdr, | 797 | if (mem.eql(u8, sh_name, ".dynstr")) { |
| 684 | @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]), | 798 | break :find_dyn_str .{ |
| 685 | ); | 799 | .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), |
| 686 | const sh64 = @ptrCast( | 800 | .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), |
| 687 | *elf.Elf64_Shdr, | 801 | }; |
| 688 | @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]), | ||
| 689 | ); | ||
| 690 | const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); | ||
| 691 | // TODO this pointer cast should not be necessary | ||
| 692 | const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0); | ||
| 693 | if (mem.eql(u8, sh_name, ".dynstr")) { | ||
| 694 | break :find_dyn_str .{ | ||
| 695 | .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), | ||
| 696 | .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), | ||
| 697 | }; | ||
| 698 | } | ||
| 699 | } | 802 | } |
| 700 | } else null; | 803 | } |
| 804 | } else null; | ||
| 701 | 805 | ||
| 702 | if (dynstr) |ds| { | 806 | if (dynstr) |ds| { |
| 807 | if (rpath_offset) |rpoff| { | ||
| 703 | // TODO this pointer cast should not be necessary | 808 | // TODO this pointer cast should not be necessary |
| 704 | const rpoff_usize = std.math.cast(usize, rpoff) orelse return error.InvalidElfFile; | 809 | const rpoff_usize = std.math.cast(usize, rpoff) orelse return error.InvalidElfFile; |
| 705 | if (rpoff_usize > ds.size) return error.InvalidElfFile; | 810 | if (rpoff_usize > ds.size) return error.InvalidElfFile; |
| ... | @@ -713,64 +818,31 @@ pub fn abiAndDynamicLinkerFromFile( | ... | @@ -713,64 +818,31 @@ pub fn abiAndDynamicLinkerFromFile( |
| 713 | const rpath_list = mem.sliceTo(std.meta.assumeSentinel(strtab.ptr, 0), 0); | 818 | const rpath_list = mem.sliceTo(std.meta.assumeSentinel(strtab.ptr, 0), 0); |
| 714 | var it = mem.tokenize(u8, rpath_list, ":"); | 819 | var it = mem.tokenize(u8, rpath_list, ":"); |
| 715 | while (it.next()) |rpath| { | 820 | while (it.next()) |rpath| { |
| 716 | var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { | 821 | if (glibcVerFromRPath(rpath)) |ver| { |
| 717 | error.NameTooLong => unreachable, | 822 | result.target.os.version_range.linux.glibc = ver; |
| 718 | error.InvalidUtf8 => unreachable, | 823 | return result; |
| 719 | error.BadPathName => unreachable, | 824 | } else |err| switch (err) { |
| 720 | error.DeviceBusy => unreachable, | 825 | error.GLibCNotFound => continue, |
| 721 | 826 | else => |e| return e, | |
| 722 | error.FileNotFound, | 827 | } |
| 723 | error.NotDir, | 828 | } |
| 724 | error.InvalidHandle, | 829 | } |
| 725 | error.AccessDenied, | 830 | } |
| 726 | error.NoDevice, | 831 | |
| 727 | => continue, | 832 | if (result.dynamic_linker.get()) |dl_path| glibc_ver: { |
| 728 | 833 | // There is no DT_RUNPATH so we try to find libc.so.6 inside the same | |
| 729 | error.ProcessFdQuotaExceeded, | 834 | // directory as the dynamic linker. |
| 730 | error.SystemFdQuotaExceeded, | 835 | if (fs.path.dirname(dl_path)) |rpath| { |
| 731 | error.SystemResources, | 836 | if (glibcVerFromRPath(rpath)) |ver| { |
| 732 | error.SymLinkLoop, | 837 | result.target.os.version_range.linux.glibc = ver; |
| 733 | error.Unexpected, | 838 | return result; |
| 734 | => |e| return e, | 839 | } else |err| switch (err) { |
| 735 | }; | 840 | error.GLibCNotFound => {}, |
| 736 | defer dir.close(); | 841 | else => |e| return e, |
| 737 | |||
| 738 | var link_buf: [std.os.PATH_MAX]u8 = undefined; | ||
| 739 | const link_name = std.os.readlinkatZ( | ||
| 740 | dir.fd, | ||
| 741 | glibc_so_basename, | ||
| 742 | &link_buf, | ||
| 743 | ) catch |err| switch (err) { | ||
| 744 | error.NameTooLong => unreachable, | ||
| 745 | error.InvalidUtf8 => unreachable, // Windows only | ||
| 746 | error.BadPathName => unreachable, // Windows only | ||
| 747 | error.UnsupportedReparsePointType => unreachable, // Windows only | ||
| 748 | |||
| 749 | error.AccessDenied, | ||
| 750 | error.FileNotFound, | ||
| 751 | error.NotLink, | ||
| 752 | error.NotDir, | ||
| 753 | => continue, | ||
| 754 | |||
| 755 | error.SystemResources, | ||
| 756 | error.FileSystem, | ||
| 757 | error.SymLinkLoop, | ||
| 758 | error.Unexpected, | ||
| 759 | => |e| return e, | ||
| 760 | }; | ||
| 761 | result.target.os.version_range.linux.glibc = glibcVerFromLinkName( | ||
| 762 | link_name, | ||
| 763 | "libc-", | ||
| 764 | ) catch |err| switch (err) { | ||
| 765 | error.UnrecognizedGnuLibCFileName, | ||
| 766 | error.InvalidGnuLibCVersion, | ||
| 767 | => continue, | ||
| 768 | }; | ||
| 769 | break; | ||
| 770 | } | 842 | } |
| 771 | } | 843 | } |
| 772 | } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: { | 844 | |
| 773 | // There is no DT_RUNPATH but we can try to see if the information is | 845 | // So far, no luck. Next we try to see if the information is |
| 774 | // present in the symlink data for the dynamic linker path. | 846 | // present in the symlink data for the dynamic linker path. |
| 775 | var link_buf: [std.os.PATH_MAX]u8 = undefined; | 847 | var link_buf: [std.os.PATH_MAX]u8 = undefined; |
| 776 | const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { | 848 | const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { |
| ... | @@ -799,6 +871,36 @@ pub fn abiAndDynamicLinkerFromFile( | ... | @@ -799,6 +871,36 @@ pub fn abiAndDynamicLinkerFromFile( |
| 799 | error.InvalidGnuLibCVersion, | 871 | error.InvalidGnuLibCVersion, |
| 800 | => break :glibc_ver, | 872 | => break :glibc_ver, |
| 801 | }; | 873 | }; |
| 874 | return result; | ||
| 875 | } | ||
| 876 | |||
| 877 | // Nothing worked so far. Finally we fall back to hard-coded search paths. | ||
| 878 | // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`. | ||
| 879 | var path_buf: [std.os.PATH_MAX]u8 = undefined; | ||
| 880 | var index: usize = 0; | ||
| 881 | const prefix = "/lib/"; | ||
| 882 | const cpu_arch = @tagName(result.target.cpu.arch); | ||
| 883 | const os_tag = @tagName(result.target.os.tag); | ||
| 884 | const abi = @tagName(result.target.abi); | ||
| 885 | mem.copy(u8, path_buf[index..], prefix); | ||
| 886 | index += prefix.len; | ||
| 887 | mem.copy(u8, path_buf[index..], cpu_arch); | ||
| 888 | index += cpu_arch.len; | ||
| 889 | path_buf[index] = '-'; | ||
| 890 | index += 1; | ||
| 891 | mem.copy(u8, path_buf[index..], os_tag); | ||
| 892 | index += os_tag.len; | ||
| 893 | path_buf[index] = '-'; | ||
| 894 | index += 1; | ||
| 895 | mem.copy(u8, path_buf[index..], abi); | ||
| 896 | index += abi.len; | ||
| 897 | const rpath = path_buf[0..index]; | ||
| 898 | if (glibcVerFromRPath(rpath)) |ver| { | ||
| 899 | result.target.os.version_range.linux.glibc = ver; | ||
| 900 | return result; | ||
| 901 | } else |err| switch (err) { | ||
| 902 | error.GLibCNotFound => {}, | ||
| 903 | else => |e| return e, | ||
| 802 | } | 904 | } |
| 803 | } | 905 | } |
| 804 | 906 |
src/Compilation.zig+6| ... | @@ -878,6 +878,9 @@ pub const InitOptions = struct { | ... | @@ -878,6 +878,9 @@ pub const InitOptions = struct { |
| 878 | linker_shared_memory: bool = false, | 878 | linker_shared_memory: bool = false, |
| 879 | linker_global_base: ?u64 = null, | 879 | linker_global_base: ?u64 = null, |
| 880 | linker_export_symbol_names: []const []const u8 = &.{}, | 880 | linker_export_symbol_names: []const []const u8 = &.{}, |
| 881 | linker_print_gc_sections: bool = false, | ||
| 882 | linker_print_icf_sections: bool = false, | ||
| 883 | linker_print_map: bool = false, | ||
| 881 | each_lib_rpath: ?bool = null, | 884 | each_lib_rpath: ?bool = null, |
| 882 | build_id: ?bool = null, | 885 | build_id: ?bool = null, |
| 883 | disable_c_depfile: bool = false, | 886 | disable_c_depfile: bool = false, |
| ... | @@ -1727,6 +1730,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1727,6 +1730,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1727 | .shared_memory = options.linker_shared_memory, | 1730 | .shared_memory = options.linker_shared_memory, |
| 1728 | .global_base = options.linker_global_base, | 1731 | .global_base = options.linker_global_base, |
| 1729 | .export_symbol_names = options.linker_export_symbol_names, | 1732 | .export_symbol_names = options.linker_export_symbol_names, |
| 1733 | .print_gc_sections = options.linker_print_gc_sections, | ||
| 1734 | .print_icf_sections = options.linker_print_icf_sections, | ||
| 1735 | .print_map = options.linker_print_map, | ||
| 1730 | .z_nodelete = options.linker_z_nodelete, | 1736 | .z_nodelete = options.linker_z_nodelete, |
| 1731 | .z_notext = options.linker_z_notext, | 1737 | .z_notext = options.linker_z_notext, |
| 1732 | .z_defs = options.linker_z_defs, | 1738 | .z_defs = options.linker_z_defs, |
src/Module.zig+21-4| ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { | ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { |
| 345 | /// During sema, this map is backed by the gpa. Once sema completes, | 345 | /// During sema, this map is backed by the gpa. Once sema completes, |
| 346 | /// it is reallocated using the value_arena. | 346 | /// it is reallocated using the value_arena. |
| 347 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, | 347 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, |
| 348 | |||
| 349 | pub fn failed(noalias self: *const @This()) bool { | ||
| 350 | return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32); | ||
| 351 | } | ||
| 352 | |||
| 353 | pub fn fail(noalias self: *@This()) void { | ||
| 354 | self.captures.available = 0; | ||
| 355 | self.captures.size = std.math.maxInt(u32); | ||
| 356 | } | ||
| 348 | }; | 357 | }; |
| 349 | 358 | ||
| 350 | pub const WipCaptureScope = struct { | 359 | pub const WipCaptureScope = struct { |
| ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { | ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { |
| 383 | pub fn deinit(noalias self: *@This()) void { | 392 | pub fn deinit(noalias self: *@This()) void { |
| 384 | if (!self.finalized) { | 393 | if (!self.finalized) { |
| 385 | self.scope.captures.deinit(self.gpa); | 394 | self.scope.captures.deinit(self.gpa); |
| 395 | self.scope.fail(); | ||
| 386 | } | 396 | } |
| 387 | self.* = undefined; | 397 | self.* = undefined; |
| 388 | } | 398 | } |
| ... | @@ -4274,11 +4284,14 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { | ... | @@ -4274,11 +4284,14 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { |
| 4274 | 4284 | ||
| 4275 | const comp = mod.comp; | 4285 | const comp = mod.comp; |
| 4276 | 4286 | ||
| 4277 | if (comp.bin_file.options.emit == null and | 4287 | const no_bin_file = (comp.bin_file.options.emit == null and |
| 4278 | comp.emit_asm == null and | 4288 | comp.emit_asm == null and |
| 4279 | comp.emit_llvm_ir == null and | 4289 | comp.emit_llvm_ir == null and |
| 4280 | comp.emit_llvm_bc == null) | 4290 | comp.emit_llvm_bc == null); |
| 4281 | { | 4291 | |
| 4292 | const dump_air = builtin.mode == .Debug and comp.verbose_air; | ||
| 4293 | |||
| 4294 | if (no_bin_file and !dump_air) { | ||
| 4282 | return; | 4295 | return; |
| 4283 | } | 4296 | } |
| 4284 | 4297 | ||
| ... | @@ -4286,7 +4299,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { | ... | @@ -4286,7 +4299,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { |
| 4286 | var liveness = try Liveness.analyze(gpa, air); | 4299 | var liveness = try Liveness.analyze(gpa, air); |
| 4287 | defer liveness.deinit(gpa); | 4300 | defer liveness.deinit(gpa); |
| 4288 | 4301 | ||
| 4289 | if (builtin.mode == .Debug and comp.verbose_air) { | 4302 | if (dump_air) { |
| 4290 | const fqn = try decl.getFullyQualifiedName(mod); | 4303 | const fqn = try decl.getFullyQualifiedName(mod); |
| 4291 | defer mod.gpa.free(fqn); | 4304 | defer mod.gpa.free(fqn); |
| 4292 | 4305 | ||
| ... | @@ -4295,6 +4308,10 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { | ... | @@ -4295,6 +4308,10 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { |
| 4295 | std.debug.print("# End Function AIR: {s}\n\n", .{fqn}); | 4308 | std.debug.print("# End Function AIR: {s}\n\n", .{fqn}); |
| 4296 | } | 4309 | } |
| 4297 | 4310 | ||
| 4311 | if (no_bin_file) { | ||
| 4312 | return; | ||
| 4313 | } | ||
| 4314 | |||
| 4298 | comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) { | 4315 | comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) { |
| 4299 | error.OutOfMemory => return error.OutOfMemory, | 4316 | error.OutOfMemory => return error.OutOfMemory, |
| 4300 | error.AnalysisFail => { | 4317 | error.AnalysisFail => { |
src/Sema.zig+32-30| ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( | ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( |
| 5956 | error.NeededSourceLocation => { | 5956 | error.NeededSourceLocation => { |
| 5957 | _ = sema.inst_map.remove(inst); | 5957 | _ = sema.inst_map.remove(inst); |
| 5958 | const decl = sema.mod.declPtr(block.src_decl); | 5958 | const decl = sema.mod.declPtr(block.src_decl); |
| 5959 | child_block.src_decl = block.src_decl; | ||
| 5960 | try sema.analyzeInlineCallArg( | 5959 | try sema.analyzeInlineCallArg( |
| 5961 | block, | 5960 | block, |
| 5962 | &child_block, | 5961 | &child_block, |
| ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( | ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( |
| 13740 | const tv = while (true) { | 13739 | const tv = while (true) { |
| 13741 | // Note: We don't need to add a dependency here, because | 13740 | // Note: We don't need to add a dependency here, because |
| 13742 | // decls always depend on their lexical parents. | 13741 | // decls always depend on their lexical parents. |
| 13742 | |||
| 13743 | // Fail this decl if a scope it depended on failed. | ||
| 13744 | if (scope.failed()) { | ||
| 13745 | if (sema.owner_func) |owner_func| { | ||
| 13746 | owner_func.state = .dependency_failure; | ||
| 13747 | } else { | ||
| 13748 | sema.owner_decl.analysis = .dependency_failure; | ||
| 13749 | } | ||
| 13750 | return error.AnalysisFail; | ||
| 13751 | } | ||
| 13743 | if (scope.captures.getPtr(inst_data.inst)) |tv| { | 13752 | if (scope.captures.getPtr(inst_data.inst)) |tv| { |
| 13744 | break tv; | 13753 | break tv; |
| 13745 | } | 13754 | } |
| ... | @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 | ... | @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 |
| 18076 | const target = sema.mod.getTarget(); | 18085 | const target = sema.mod.getTarget(); |
| 18077 | 18086 | ||
| 18078 | try sema.resolveTypeLayout(block, lhs_src, ty); | 18087 | try sema.resolveTypeLayout(block, lhs_src, ty); |
| 18079 | switch (ty.tag()) { | 18088 | switch (ty.zigTypeTag()) { |
| 18080 | .@"struct", .tuple, .anon_struct => {}, | 18089 | .Struct => {}, |
| 18081 | else => { | 18090 | else => { |
| 18082 | const msg = msg: { | 18091 | const msg = msg: { |
| 18083 | const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); | 18092 | const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); |
| ... | @@ -19617,28 +19626,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -19617,28 +19626,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 19617 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 19626 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19618 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 19627 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19619 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 19628 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19620 | const dest_ptr = try sema.resolveInst(extra.dest); | 19629 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); |
| 19621 | const dest_ptr_ty = sema.typeOf(dest_ptr); | ||
| 19622 | 19630 | ||
| 19623 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | 19631 | // TODO AstGen's coerced_ty cannot handle volatile here |
| 19624 | if (dest_ptr_ty.isConstPtr()) { | 19632 | var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; |
| 19625 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | 19633 | dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); |
| 19626 | } | 19634 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info); |
| 19635 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | ||
| 19627 | 19636 | ||
| 19628 | const uncasted_src_ptr = try sema.resolveInst(extra.source); | 19637 | const uncasted_src_ptr = try sema.resolveInst(extra.source); |
| 19629 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | 19638 | var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data; |
| 19630 | try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); | 19639 | src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr(); |
| 19631 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | 19640 | const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info); |
| 19632 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ | 19641 | const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src); |
| 19633 | .pointee_type = dest_ptr_ty.elemType2(), | ||
| 19634 | .@"align" = src_ptr_info.@"align", | ||
| 19635 | .@"addrspace" = src_ptr_info.@"addrspace", | ||
| 19636 | .mutable = false, | ||
| 19637 | .@"allowzero" = src_ptr_info.@"allowzero", | ||
| 19638 | .@"volatile" = src_ptr_info.@"volatile", | ||
| 19639 | .size = .Many, | ||
| 19640 | }); | ||
| 19641 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); | ||
| 19642 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); | 19642 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19643 | 19643 | ||
| 19644 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { | 19644 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { |
| ... | @@ -19674,14 +19674,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -19674,14 +19674,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 19674 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 19674 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19675 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 19675 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19676 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 19676 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19677 | const dest_ptr = try sema.resolveInst(extra.dest); | 19677 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); |
| 19678 | const dest_ptr_ty = sema.typeOf(dest_ptr); | 19678 | |
| 19679 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | 19679 | // TODO AstGen's coerced_ty cannot handle volatile here |
| 19680 | if (dest_ptr_ty.isConstPtr()) { | 19680 | var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; |
| 19681 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | 19681 | ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); |
| 19682 | } | 19682 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); |
| 19683 | const elem_ty = dest_ptr_ty.elemType2(); | 19683 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); |
| 19684 | const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src); | 19684 | |
| 19685 | const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src); | ||
| 19685 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); | 19686 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19686 | 19687 | ||
| 19687 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { | 19688 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { |
| ... | @@ -26013,6 +26014,7 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref | ... | @@ -26013,6 +26014,7 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref |
| 26013 | .pointee_type = decl_tv.ty, | 26014 | .pointee_type = decl_tv.ty, |
| 26014 | .mutable = false, | 26015 | .mutable = false, |
| 26015 | .@"addrspace" = decl.@"addrspace", | 26016 | .@"addrspace" = decl.@"addrspace", |
| 26017 | .@"align" = decl.@"align", | ||
| 26016 | }), | 26018 | }), |
| 26017 | try Value.Tag.decl_ref.create(sema.arena, decl_index), | 26019 | try Value.Tag.decl_ref.create(sema.arena, decl_index), |
| 26018 | ); | 26020 | ); |
src/arch/wasm/CodeGen.zig+4| ... | @@ -666,6 +666,10 @@ pub fn deinit(self: *Self) void { | ... | @@ -666,6 +666,10 @@ pub fn deinit(self: *Self) void { |
| 666 | self.locals.deinit(self.gpa); | 666 | self.locals.deinit(self.gpa); |
| 667 | self.mir_instructions.deinit(self.gpa); | 667 | self.mir_instructions.deinit(self.gpa); |
| 668 | self.mir_extra.deinit(self.gpa); | 668 | self.mir_extra.deinit(self.gpa); |
| 669 | self.free_locals_i32.deinit(self.gpa); | ||
| 670 | self.free_locals_i64.deinit(self.gpa); | ||
| 671 | self.free_locals_f32.deinit(self.gpa); | ||
| 672 | self.free_locals_f64.deinit(self.gpa); | ||
| 669 | self.* = undefined; | 673 | self.* = undefined; |
| 670 | } | 674 | } |
| 671 | 675 |
src/arch/x86_64/CodeGen.zig+372-229| ... | @@ -32,11 +32,6 @@ const abi = @import("abi.zig"); | ... | @@ -32,11 +32,6 @@ const abi = @import("abi.zig"); |
| 32 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | 32 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 34 | 34 | ||
| 35 | const callee_preserved_regs = abi.callee_preserved_regs; | ||
| 36 | const caller_preserved_regs = abi.caller_preserved_regs; | ||
| 37 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ||
| 38 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | ||
| 39 | |||
| 40 | const Condition = bits.Condition; | 35 | const Condition = bits.Condition; |
| 41 | const RegisterManager = abi.RegisterManager; | 36 | const RegisterManager = abi.RegisterManager; |
| 42 | const RegisterLock = RegisterManager.RegisterLock; | 37 | const RegisterLock = RegisterManager.RegisterLock; |
| ... | @@ -137,6 +132,7 @@ pub const MCValue = union(enum) { | ... | @@ -137,6 +132,7 @@ pub const MCValue = union(enum) { |
| 137 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. | 132 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. |
| 138 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT. | 133 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT. |
| 139 | got_load: u32, | 134 | got_load: u32, |
| 135 | imports_load: u32, | ||
| 140 | /// The value is in memory referenced directly via symbol index. | 136 | /// The value is in memory referenced directly via symbol index. |
| 141 | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. | 137 | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. |
| 142 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED. | 138 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED. |
| ... | @@ -156,6 +152,7 @@ pub const MCValue = union(enum) { | ... | @@ -156,6 +152,7 @@ pub const MCValue = union(enum) { |
| 156 | .ptr_stack_offset, | 152 | .ptr_stack_offset, |
| 157 | .direct_load, | 153 | .direct_load, |
| 158 | .got_load, | 154 | .got_load, |
| 155 | .imports_load, | ||
| 159 | => true, | 156 | => true, |
| 160 | else => false, | 157 | else => false, |
| 161 | }; | 158 | }; |
| ... | @@ -203,6 +200,42 @@ const Branch = struct { | ... | @@ -203,6 +200,42 @@ const Branch = struct { |
| 203 | self.inst_table.deinit(gpa); | 200 | self.inst_table.deinit(gpa); |
| 204 | self.* = undefined; | 201 | self.* = undefined; |
| 205 | } | 202 | } |
| 203 | |||
| 204 | const FormatContext = struct { | ||
| 205 | insts: []const Air.Inst.Index, | ||
| 206 | mcvs: []const MCValue, | ||
| 207 | }; | ||
| 208 | |||
| 209 | fn fmt( | ||
| 210 | ctx: FormatContext, | ||
| 211 | comptime unused_format_string: []const u8, | ||
| 212 | options: std.fmt.FormatOptions, | ||
| 213 | writer: anytype, | ||
| 214 | ) @TypeOf(writer).Error!void { | ||
| 215 | _ = options; | ||
| 216 | comptime assert(unused_format_string.len == 0); | ||
| 217 | try writer.writeAll("Branch {\n"); | ||
| 218 | for (ctx.insts) |inst, i| { | ||
| 219 | const mcv = ctx.mcvs[i]; | ||
| 220 | try writer.print(" %{d} => {}\n", .{ inst, mcv }); | ||
| 221 | } | ||
| 222 | try writer.writeAll("}"); | ||
| 223 | } | ||
| 224 | |||
| 225 | fn format(branch: Branch, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | ||
| 226 | _ = branch; | ||
| 227 | _ = unused_format_string; | ||
| 228 | _ = options; | ||
| 229 | _ = writer; | ||
| 230 | @compileError("do not format Branch directly; use ty.fmtDebug()"); | ||
| 231 | } | ||
| 232 | |||
| 233 | fn fmtDebug(self: @This()) std.fmt.Formatter(fmt) { | ||
| 234 | return .{ .data = .{ | ||
| 235 | .insts = self.inst_table.keys(), | ||
| 236 | .mcvs = self.inst_table.values(), | ||
| 237 | } }; | ||
| 238 | } | ||
| 206 | }; | 239 | }; |
| 207 | 240 | ||
| 208 | const StackAllocation = struct { | 241 | const StackAllocation = struct { |
| ... | @@ -235,7 +268,7 @@ const BigTomb = struct { | ... | @@ -235,7 +268,7 @@ const BigTomb = struct { |
| 235 | fn finishAir(bt: *BigTomb, result: MCValue) void { | 268 | fn finishAir(bt: *BigTomb, result: MCValue) void { |
| 236 | const is_used = !bt.function.liveness.isUnused(bt.inst); | 269 | const is_used = !bt.function.liveness.isUnused(bt.inst); |
| 237 | if (is_used) { | 270 | if (is_used) { |
| 238 | log.debug("%{d} => {}", .{ bt.inst, result }); | 271 | log.debug(" (saving %{d} => {})", .{ bt.inst, result }); |
| 239 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; | 272 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; |
| 240 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); | 273 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); |
| 241 | } | 274 | } |
| ... | @@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void { |
| 406 | }); | 439 | }); |
| 407 | 440 | ||
| 408 | if (self.ret_mcv == .stack_offset) { | 441 | if (self.ret_mcv == .stack_offset) { |
| 409 | // The address where to store the return value for the caller is in `.rdi` | 442 | // The address where to store the return value for the caller is in a |
| 410 | // register which the callee is free to clobber. Therefore, we purposely | 443 | // register which the callee is free to clobber. Therefore, we purposely |
| 411 | // spill it to stack immediately. | 444 | // spill it to stack immediately. |
| 412 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8); | 445 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8); |
| 413 | self.next_stack_offset = stack_offset; | 446 | self.next_stack_offset = stack_offset; |
| 414 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); | 447 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); |
| 415 | 448 | ||
| 416 | try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); | 449 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; |
| 450 | try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = ret_reg }, .{}); | ||
| 417 | self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) }; | 451 | self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) }; |
| 418 | log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset}); | 452 | log.debug("gen: spilling {s} to stack at offset {}", .{ @tagName(ret_reg), stack_offset }); |
| 419 | } | 453 | } |
| 420 | 454 | ||
| 421 | _ = try self.addInst(.{ | 455 | _ = try self.addInst(.{ |
| ... | @@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void { |
| 446 | 480 | ||
| 447 | // Create list of registers to save in the prologue. | 481 | // Create list of registers to save in the prologue. |
| 448 | // TODO handle register classes | 482 | // TODO handle register classes |
| 449 | var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{}; | 483 | var reg_list = Mir.RegisterList{}; |
| 450 | inline for (callee_preserved_regs) |reg| { | 484 | const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*); |
| 485 | for (callee_preserved_regs) |reg| { | ||
| 451 | if (self.register_manager.isRegAllocated(reg)) { | 486 | if (self.register_manager.isRegAllocated(reg)) { |
| 452 | reg_list.push(reg); | 487 | reg_list.push(callee_preserved_regs, reg); |
| 453 | } | 488 | } |
| 454 | } | 489 | } |
| 455 | const saved_regs_stack_space: u32 = reg_list.count() * 8; | 490 | const saved_regs_stack_space: u32 = reg_list.count() * 8; |
| ... | @@ -797,6 +832,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -797,6 +832,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 797 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 832 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 798 | const air_tags = self.air.instructions.items(.tag); | 833 | const air_tags = self.air.instructions.items(.tag); |
| 799 | if (air_tags[inst] == .constant) return; // Constants are immortal. | 834 | if (air_tags[inst] == .constant) return; // Constants are immortal. |
| 835 | log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } }); | ||
| 800 | // When editing this function, note that the logic must synchronize with `reuseOperand`. | 836 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| 801 | const prev_value = self.getResolvedInstValue(inst); | 837 | const prev_value = self.getResolvedInstValue(inst); |
| 802 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 838 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | @@ -2274,6 +2310,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2274,6 +2310,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2274 | .memory, | 2310 | .memory, |
| 2275 | .got_load, | 2311 | .got_load, |
| 2276 | .direct_load, | 2312 | .direct_load, |
| 2313 | .imports_load, | ||
| 2277 | => { | 2314 | => { |
| 2278 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); | 2315 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); |
| 2279 | }, | 2316 | }, |
| ... | @@ -2618,6 +2655,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -2618,6 +2655,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2618 | .memory, | 2655 | .memory, |
| 2619 | .got_load, | 2656 | .got_load, |
| 2620 | .direct_load, | 2657 | .direct_load, |
| 2658 | .imports_load, | ||
| 2621 | => { | 2659 | => { |
| 2622 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); | 2660 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 2623 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); | 2661 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); |
| ... | @@ -2655,6 +2693,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue | ... | @@ -2655,6 +2693,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2655 | switch (ptr) { | 2693 | switch (ptr) { |
| 2656 | .got_load, | 2694 | .got_load, |
| 2657 | .direct_load, | 2695 | .direct_load, |
| 2696 | .imports_load, | ||
| 2658 | => |sym_index| { | 2697 | => |sym_index| { |
| 2659 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); | 2698 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2660 | const mod = self.bin_file.options.module.?; | 2699 | const mod = self.bin_file.options.module.?; |
| ... | @@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue | ... | @@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2666 | const flags: u2 = switch (ptr) { | 2705 | const flags: u2 = switch (ptr) { |
| 2667 | .got_load => 0b00, | 2706 | .got_load => 0b00, |
| 2668 | .direct_load => 0b01, | 2707 | .direct_load => 0b01, |
| 2708 | .imports_load => 0b10, | ||
| 2669 | else => unreachable, | 2709 | else => unreachable, |
| 2670 | }; | 2710 | }; |
| 2671 | _ = try self.addInst(.{ | 2711 | _ = try self.addInst(.{ |
| ... | @@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2763 | }, | 2803 | }, |
| 2764 | .got_load, | 2804 | .got_load, |
| 2765 | .direct_load, | 2805 | .direct_load, |
| 2806 | .imports_load, | ||
| 2766 | .memory, | 2807 | .memory, |
| 2767 | .stack_offset, | 2808 | .stack_offset, |
| 2768 | => { | 2809 | => { |
| ... | @@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2783 | }, | 2824 | }, |
| 2784 | .got_load, | 2825 | .got_load, |
| 2785 | .direct_load, | 2826 | .direct_load, |
| 2827 | .imports_load, | ||
| 2786 | .memory, | 2828 | .memory, |
| 2787 | => { | 2829 | => { |
| 2788 | const value_lock: ?RegisterLock = switch (value) { | 2830 | const value_lock: ?RegisterLock = switch (value) { |
| ... | @@ -2854,6 +2896,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2854,6 +2896,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2854 | }, | 2896 | }, |
| 2855 | .got_load, | 2897 | .got_load, |
| 2856 | .direct_load, | 2898 | .direct_load, |
| 2899 | .imports_load, | ||
| 2857 | .memory, | 2900 | .memory, |
| 2858 | => { | 2901 | => { |
| 2859 | if (abi_size <= 8) { | 2902 | if (abi_size <= 8) { |
| ... | @@ -3565,6 +3608,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -3565,6 +3608,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3565 | .memory, | 3608 | .memory, |
| 3566 | .got_load, | 3609 | .got_load, |
| 3567 | .direct_load, | 3610 | .direct_load, |
| 3611 | .imports_load, | ||
| 3568 | .eflags, | 3612 | .eflags, |
| 3569 | => { | 3613 | => { |
| 3570 | assert(abi_size <= 8); | 3614 | assert(abi_size <= 8); |
| ... | @@ -3650,7 +3694,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -3650,7 +3694,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3650 | => { | 3694 | => { |
| 3651 | return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); | 3695 | return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); |
| 3652 | }, | 3696 | }, |
| 3653 | .got_load, .direct_load => { | 3697 | .got_load, |
| 3698 | .direct_load, | ||
| 3699 | .imports_load, | ||
| 3700 | => { | ||
| 3654 | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); | 3701 | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); |
| 3655 | }, | 3702 | }, |
| 3656 | .eflags => { | 3703 | .eflags => { |
| ... | @@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3661 | .memory => { | 3708 | .memory => { |
| 3662 | return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); | 3709 | return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); |
| 3663 | }, | 3710 | }, |
| 3664 | .got_load, .direct_load => { | 3711 | .got_load, |
| 3712 | .direct_load, | ||
| 3713 | .imports_load, | ||
| 3714 | => { | ||
| 3665 | return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); | 3715 | return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); |
| 3666 | }, | 3716 | }, |
| 3667 | } | 3717 | } |
| ... | @@ -3729,7 +3779,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3729,7 +3779,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3729 | .memory => { | 3779 | .memory => { |
| 3730 | return self.fail("TODO implement x86 multiply source memory", .{}); | 3780 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 3731 | }, | 3781 | }, |
| 3732 | .got_load, .direct_load => { | 3782 | .got_load, |
| 3783 | .direct_load, | ||
| 3784 | .imports_load, | ||
| 3785 | => { | ||
| 3733 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); | 3786 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3734 | }, | 3787 | }, |
| 3735 | .eflags => { | 3788 | .eflags => { |
| ... | @@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3773 | .memory, .stack_offset => { | 3826 | .memory, .stack_offset => { |
| 3774 | return self.fail("TODO implement x86 multiply source memory", .{}); | 3827 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 3775 | }, | 3828 | }, |
| 3776 | .got_load, .direct_load => { | 3829 | .got_load, |
| 3830 | .direct_load, | ||
| 3831 | .imports_load, | ||
| 3832 | => { | ||
| 3777 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); | 3833 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3778 | }, | 3834 | }, |
| 3779 | .eflags => { | 3835 | .eflags => { |
| ... | @@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3784 | .memory => { | 3840 | .memory => { |
| 3785 | return self.fail("TODO implement x86 multiply destination memory", .{}); | 3841 | return self.fail("TODO implement x86 multiply destination memory", .{}); |
| 3786 | }, | 3842 | }, |
| 3787 | .got_load, .direct_load => { | 3843 | .got_load, |
| 3844 | .direct_load, | ||
| 3845 | .imports_load, | ||
| 3846 | => { | ||
| 3788 | return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); | 3847 | return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); |
| 3789 | }, | 3848 | }, |
| 3790 | } | 3849 | } |
| ... | @@ -3898,11 +3957,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3898,11 +3957,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3898 | 3957 | ||
| 3899 | try self.spillEflagsIfOccupied(); | 3958 | try self.spillEflagsIfOccupied(); |
| 3900 | 3959 | ||
| 3901 | for (caller_preserved_regs) |reg| { | 3960 | for (abi.getCallerPreservedRegs(self.target.*)) |reg| { |
| 3902 | try self.register_manager.getReg(reg, null); | 3961 | try self.register_manager.getReg(reg, null); |
| 3903 | } | 3962 | } |
| 3904 | 3963 | ||
| 3905 | const rdi_lock: ?RegisterLock = blk: { | 3964 | const ret_reg_lock: ?RegisterLock = blk: { |
| 3906 | if (info.return_value == .stack_offset) { | 3965 | if (info.return_value == .stack_offset) { |
| 3907 | const ret_ty = fn_ty.fnReturnType(); | 3966 | const ret_ty = fn_ty.fnReturnType(); |
| 3908 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 3967 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| ... | @@ -3910,17 +3969,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3910,17 +3969,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3910 | const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); | 3969 | const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); |
| 3911 | log.debug("airCall: return value on stack at offset {}", .{stack_offset}); | 3970 | log.debug("airCall: return value on stack at offset {}", .{stack_offset}); |
| 3912 | 3971 | ||
| 3913 | try self.register_manager.getReg(.rdi, null); | 3972 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; |
| 3914 | try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); | 3973 | try self.register_manager.getReg(ret_reg, null); |
| 3915 | const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi); | 3974 | try self.genSetReg(Type.usize, ret_reg, .{ .ptr_stack_offset = stack_offset }); |
| 3975 | const ret_reg_lock = self.register_manager.lockRegAssumeUnused(ret_reg); | ||
| 3916 | 3976 | ||
| 3917 | info.return_value.stack_offset = stack_offset; | 3977 | info.return_value.stack_offset = stack_offset; |
| 3918 | 3978 | ||
| 3919 | break :blk rdi_lock; | 3979 | break :blk ret_reg_lock; |
| 3920 | } | 3980 | } |
| 3921 | break :blk null; | 3981 | break :blk null; |
| 3922 | }; | 3982 | }; |
| 3923 | defer if (rdi_lock) |lock| self.register_manager.unlockReg(lock); | 3983 | defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3924 | 3984 | ||
| 3925 | for (args) |arg, arg_i| { | 3985 | for (args) |arg, arg_i| { |
| 3926 | const mc_arg = info.args[arg_i]; | 3986 | const mc_arg = info.args[arg_i]; |
| ... | @@ -3948,6 +4008,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3948,6 +4008,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3948 | .memory => unreachable, | 4008 | .memory => unreachable, |
| 3949 | .got_load => unreachable, | 4009 | .got_load => unreachable, |
| 3950 | .direct_load => unreachable, | 4010 | .direct_load => unreachable, |
| 4011 | .imports_load => unreachable, | ||
| 3951 | .eflags => unreachable, | 4012 | .eflags => unreachable, |
| 3952 | .register_overflow => unreachable, | 4013 | .register_overflow => unreachable, |
| 3953 | } | 4014 | } |
| ... | @@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3999 | .data = undefined, | 4060 | .data = undefined, |
| 4000 | }); | 4061 | }); |
| 4001 | } | 4062 | } |
| 4002 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | 4063 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 4003 | if (self.air.value(callee)) |func_value| { | 4064 | if (self.air.value(callee)) |func_value| { |
| 4004 | if (func_value.castTag(.function)) |func_payload| { | 4065 | if (func_value.castTag(.function)) |func_payload| { |
| 4005 | const func = func_payload.data; | 4066 | const func = func_payload.data; |
| ... | @@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4015 | }), | 4076 | }), |
| 4016 | .data = undefined, | 4077 | .data = undefined, |
| 4017 | }); | 4078 | }); |
| 4018 | } else if (func_value.castTag(.extern_fn)) |_| { | 4079 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 4019 | return self.fail("TODO implement calling extern functions", .{}); | 4080 | const extern_fn = func_payload.data; |
| 4081 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | ||
| 4082 | if (extern_fn.lib_name) |lib_name| { | ||
| 4083 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | ||
| 4084 | decl_name, | ||
| 4085 | lib_name, | ||
| 4086 | }); | ||
| 4087 | } | ||
| 4088 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | ||
| 4089 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | ||
| 4090 | .imports_load = sym_index, | ||
| 4091 | }); | ||
| 4092 | _ = try self.addInst(.{ | ||
| 4093 | .tag = .call, | ||
| 4094 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4095 | .reg1 = .rax, | ||
| 4096 | .flags = 0b01, | ||
| 4097 | }), | ||
| 4098 | .data = undefined, | ||
| 4099 | }); | ||
| 4020 | } else { | 4100 | } else { |
| 4021 | return self.fail("TODO implement calling bitcasted functions", .{}); | 4101 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 4022 | } | 4102 | } |
| ... | @@ -4425,7 +4505,11 @@ fn genVarDbgInfo( | ... | @@ -4425,7 +4505,11 @@ fn genVarDbgInfo( |
| 4425 | leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; | 4505 | leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; |
| 4426 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); | 4506 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); |
| 4427 | }, | 4507 | }, |
| 4428 | .memory, .got_load, .direct_load => { | 4508 | .memory, |
| 4509 | .got_load, | ||
| 4510 | .direct_load, | ||
| 4511 | .imports_load, | ||
| 4512 | => { | ||
| 4429 | const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); | 4513 | const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); |
| 4430 | const is_ptr = switch (tag) { | 4514 | const is_ptr = switch (tag) { |
| 4431 | .dbg_var_ptr => true, | 4515 | .dbg_var_ptr => true, |
| ... | @@ -4456,7 +4540,10 @@ fn genVarDbgInfo( | ... | @@ -4456,7 +4540,10 @@ fn genVarDbgInfo( |
| 4456 | try dbg_info.append(DW.OP.deref); | 4540 | try dbg_info.append(DW.OP.deref); |
| 4457 | } | 4541 | } |
| 4458 | switch (mcv) { | 4542 | switch (mcv) { |
| 4459 | .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr), | 4543 | .got_load, |
| 4544 | .direct_load, | ||
| 4545 | .imports_load, | ||
| 4546 | => |index| try dw.addExprlocReloc(index, offset, is_ptr), | ||
| 4460 | else => {}, | 4547 | else => {}, |
| 4461 | } | 4548 | } |
| 4462 | }, | 4549 | }, |
| ... | @@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4626 | 4713 | ||
| 4627 | // Revert to the previous register and stack allocation state. | 4714 | // Revert to the previous register and stack allocation state. |
| 4628 | 4715 | ||
| 4629 | var saved_then_branch = self.branch_stack.pop(); | 4716 | var then_branch = self.branch_stack.pop(); |
| 4630 | defer saved_then_branch.deinit(self.gpa); | 4717 | defer then_branch.deinit(self.gpa); |
| 4631 | 4718 | ||
| 4632 | self.revertState(saved_state); | 4719 | self.revertState(saved_state); |
| 4633 | 4720 | ||
| 4634 | try self.performReloc(reloc); | 4721 | try self.performReloc(reloc); |
| 4635 | 4722 | ||
| 4636 | const else_branch = self.branch_stack.addOneAssumeCapacity(); | 4723 | try self.branch_stack.append(.{}); |
| 4637 | else_branch.* = .{}; | 4724 | errdefer { |
| 4725 | _ = self.branch_stack.pop(); | ||
| 4726 | } | ||
| 4638 | 4727 | ||
| 4639 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); | 4728 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); |
| 4640 | for (liveness_condbr.else_deaths) |operand| { | 4729 | for (liveness_condbr.else_deaths) |operand| { |
| ... | @@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4642 | } | 4731 | } |
| 4643 | try self.genBody(else_body); | 4732 | try self.genBody(else_body); |
| 4644 | 4733 | ||
| 4734 | var else_branch = self.branch_stack.pop(); | ||
| 4735 | defer else_branch.deinit(self.gpa); | ||
| 4736 | |||
| 4645 | // At this point, each branch will possibly have conflicting values for where | 4737 | // At this point, each branch will possibly have conflicting values for where |
| 4646 | // each instruction is stored. They agree, however, on which instructions are alive/dead. | 4738 | // each instruction is stored. They agree, however, on which instructions are alive/dead. |
| 4647 | // We use the first ("then") branch as canonical, and here emit | 4739 | // We use the first ("then") branch as canonical, and here emit |
| ... | @@ -4650,74 +4742,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4650,74 +4742,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4650 | // that we can use all the code emitting abstractions. This is why at the bottom we | 4742 | // that we can use all the code emitting abstractions. This is why at the bottom we |
| 4651 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers | 4743 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers |
| 4652 | // rather than assigning it. | 4744 | // rather than assigning it. |
| 4653 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; | 4745 | log.debug("airCondBr: %{d}", .{inst}); |
| 4654 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); | 4746 | log.debug("Upper branches:", .{}); |
| 4655 | 4747 | for (self.branch_stack.items) |bs| { | |
| 4656 | const else_slice = else_branch.inst_table.entries.slice(); | 4748 | log.debug("{}", .{bs.fmtDebug()}); |
| 4657 | const else_keys = else_slice.items(.key); | ||
| 4658 | const else_values = else_slice.items(.value); | ||
| 4659 | for (else_keys) |else_key, else_idx| { | ||
| 4660 | const else_value = else_values[else_idx]; | ||
| 4661 | const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { | ||
| 4662 | // The instruction's MCValue is overridden in both branches. | ||
| 4663 | parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); | ||
| 4664 | if (else_value == .dead) { | ||
| 4665 | assert(then_entry.value == .dead); | ||
| 4666 | continue; | ||
| 4667 | } | ||
| 4668 | break :blk then_entry.value; | ||
| 4669 | } else blk: { | ||
| 4670 | if (else_value == .dead) | ||
| 4671 | continue; | ||
| 4672 | // The instruction is only overridden in the else branch. | ||
| 4673 | var i: usize = self.branch_stack.items.len - 2; | ||
| 4674 | while (true) { | ||
| 4675 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? | ||
| 4676 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { | ||
| 4677 | assert(mcv != .dead); | ||
| 4678 | break :blk mcv; | ||
| 4679 | } | ||
| 4680 | } | ||
| 4681 | }; | ||
| 4682 | log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); | ||
| 4683 | // TODO make sure the destination stack offset / register does not already have something | ||
| 4684 | // going on there. | ||
| 4685 | try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); | ||
| 4686 | // TODO track the new register / stack allocation | ||
| 4687 | } | ||
| 4688 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); | ||
| 4689 | const then_slice = saved_then_branch.inst_table.entries.slice(); | ||
| 4690 | const then_keys = then_slice.items(.key); | ||
| 4691 | const then_values = then_slice.items(.value); | ||
| 4692 | for (then_keys) |then_key, then_idx| { | ||
| 4693 | const then_value = then_values[then_idx]; | ||
| 4694 | // We already deleted the items from this table that matched the else_branch. | ||
| 4695 | // So these are all instructions that are only overridden in the then branch. | ||
| 4696 | parent_branch.inst_table.putAssumeCapacity(then_key, then_value); | ||
| 4697 | log.debug("then_value = {}", .{then_value}); | ||
| 4698 | if (then_value == .dead) | ||
| 4699 | continue; | ||
| 4700 | const parent_mcv = blk: { | ||
| 4701 | var i: usize = self.branch_stack.items.len - 2; | ||
| 4702 | while (true) { | ||
| 4703 | i -= 1; | ||
| 4704 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { | ||
| 4705 | assert(mcv != .dead); | ||
| 4706 | break :blk mcv; | ||
| 4707 | } | ||
| 4708 | } | ||
| 4709 | }; | ||
| 4710 | log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); | ||
| 4711 | // TODO make sure the destination stack offset / register does not already have something | ||
| 4712 | // going on there. | ||
| 4713 | try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value); | ||
| 4714 | // TODO track the new register / stack allocation | ||
| 4715 | } | 4749 | } |
| 4716 | 4750 | ||
| 4717 | { | 4751 | log.debug("Then branch: {}", .{then_branch.fmtDebug()}); |
| 4718 | var item = self.branch_stack.pop(); | 4752 | log.debug("Else branch: {}", .{else_branch.fmtDebug()}); |
| 4719 | item.deinit(self.gpa); | 4753 | |
| 4720 | } | 4754 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 4755 | try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch); | ||
| 4721 | 4756 | ||
| 4722 | // We already took care of pl_op.operand earlier, so we're going | 4757 | // We already took care of pl_op.operand earlier, so we're going |
| 4723 | // to pass .none here | 4758 | // to pass .none here |
| ... | @@ -5102,6 +5137,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5102,6 +5137,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5102 | } | 5137 | } |
| 5103 | } | 5138 | } |
| 5104 | 5139 | ||
| 5140 | var branch_stack = std.ArrayList(Branch).init(self.gpa); | ||
| 5141 | defer { | ||
| 5142 | for (branch_stack.items) |*bs| { | ||
| 5143 | bs.deinit(self.gpa); | ||
| 5144 | } | ||
| 5145 | branch_stack.deinit(); | ||
| 5146 | } | ||
| 5147 | try branch_stack.ensureTotalCapacityPrecise(switch_br.data.cases_len + 1); | ||
| 5148 | |||
| 5105 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { | 5149 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 5106 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); | 5150 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 5107 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); | 5151 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| ... | @@ -5131,10 +5175,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5131,10 +5175,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5131 | 5175 | ||
| 5132 | try self.genBody(case_body); | 5176 | try self.genBody(case_body); |
| 5133 | 5177 | ||
| 5134 | // Revert to the previous register and stack allocation state. | 5178 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); |
| 5135 | var saved_case_branch = self.branch_stack.pop(); | ||
| 5136 | defer saved_case_branch.deinit(self.gpa); | ||
| 5137 | 5179 | ||
| 5180 | // Revert to the previous register and stack allocation state. | ||
| 5138 | self.revertState(saved_state); | 5181 | self.revertState(saved_state); |
| 5139 | 5182 | ||
| 5140 | for (relocs) |reloc| { | 5183 | for (relocs) |reloc| { |
| ... | @@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5144 | 5187 | ||
| 5145 | if (switch_br.data.else_body_len > 0) { | 5188 | if (switch_br.data.else_body_len > 0) { |
| 5146 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; | 5189 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| 5190 | |||
| 5191 | // Capture the state of register and stack allocation state so that we can revert to it. | ||
| 5192 | const saved_state = try self.captureState(); | ||
| 5193 | |||
| 5147 | try self.branch_stack.append(.{}); | 5194 | try self.branch_stack.append(.{}); |
| 5148 | defer { | 5195 | errdefer { |
| 5149 | var item = self.branch_stack.pop(); | 5196 | _ = self.branch_stack.pop(); |
| 5150 | item.deinit(self.gpa); | ||
| 5151 | } | 5197 | } |
| 5152 | 5198 | ||
| 5153 | const else_deaths = liveness.deaths.len - 1; | 5199 | const else_deaths = liveness.deaths.len - 1; |
| ... | @@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5158 | 5204 | ||
| 5159 | try self.genBody(else_body); | 5205 | try self.genBody(else_body); |
| 5160 | 5206 | ||
| 5161 | // TODO consolidate returned MCValues between prongs and else branch like we do | 5207 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); |
| 5162 | // in airCondBr. | 5208 | |
| 5209 | // Revert to the previous register and stack allocation state. | ||
| 5210 | self.revertState(saved_state); | ||
| 5211 | } | ||
| 5212 | |||
| 5213 | // Consolidate returned MCValues between prongs and else branch like we do | ||
| 5214 | // in airCondBr. | ||
| 5215 | log.debug("airSwitch: %{d}", .{inst}); | ||
| 5216 | log.debug("Upper branches:", .{}); | ||
| 5217 | for (self.branch_stack.items) |bs| { | ||
| 5218 | log.debug("{}", .{bs.fmtDebug()}); | ||
| 5219 | } | ||
| 5220 | for (branch_stack.items) |bs, i| { | ||
| 5221 | log.debug("Case-{d} branch: {}", .{ i, bs.fmtDebug() }); | ||
| 5222 | } | ||
| 5223 | |||
| 5224 | // TODO: can we reduce the complexity of this algorithm? | ||
| 5225 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | ||
| 5226 | var i: usize = branch_stack.items.len; | ||
| 5227 | while (i > 1) : (i -= 1) { | ||
| 5228 | const canon_branch = &branch_stack.items[i - 2]; | ||
| 5229 | const target_branch = &branch_stack.items[i - 1]; | ||
| 5230 | try self.canonicaliseBranches(parent_branch, canon_branch, target_branch); | ||
| 5163 | } | 5231 | } |
| 5164 | 5232 | ||
| 5165 | // We already took care of pl_op.operand earlier, so we're going | 5233 | // We already took care of pl_op.operand earlier, so we're going |
| ... | @@ -5167,6 +5235,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5167,6 +5235,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5167 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | 5235 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 5168 | } | 5236 | } |
| 5169 | 5237 | ||
| 5238 | fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Branch, target_branch: *Branch) !void { | ||
| 5239 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count()); | ||
| 5240 | |||
| 5241 | const target_slice = target_branch.inst_table.entries.slice(); | ||
| 5242 | const target_keys = target_slice.items(.key); | ||
| 5243 | const target_values = target_slice.items(.value); | ||
| 5244 | |||
| 5245 | for (target_keys) |target_key, target_idx| { | ||
| 5246 | const target_value = target_values[target_idx]; | ||
| 5247 | const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { | ||
| 5248 | // The instruction's MCValue is overridden in both branches. | ||
| 5249 | parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); | ||
| 5250 | if (target_value == .dead) { | ||
| 5251 | assert(canon_entry.value == .dead); | ||
| 5252 | continue; | ||
| 5253 | } | ||
| 5254 | break :blk canon_entry.value; | ||
| 5255 | } else blk: { | ||
| 5256 | if (target_value == .dead) | ||
| 5257 | continue; | ||
| 5258 | // The instruction is only overridden in the else branch. | ||
| 5259 | var i: usize = self.branch_stack.items.len - 1; | ||
| 5260 | while (true) { | ||
| 5261 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? | ||
| 5262 | if (self.branch_stack.items[i].inst_table.get(target_key)) |mcv| { | ||
| 5263 | assert(mcv != .dead); | ||
| 5264 | break :blk mcv; | ||
| 5265 | } | ||
| 5266 | } | ||
| 5267 | }; | ||
| 5268 | log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv }); | ||
| 5269 | // TODO make sure the destination stack offset / register does not already have something | ||
| 5270 | // going on there. | ||
| 5271 | try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value); | ||
| 5272 | // TODO track the new register / stack allocation | ||
| 5273 | } | ||
| 5274 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count()); | ||
| 5275 | const canon_slice = canon_branch.inst_table.entries.slice(); | ||
| 5276 | const canon_keys = canon_slice.items(.key); | ||
| 5277 | const canon_values = canon_slice.items(.value); | ||
| 5278 | for (canon_keys) |canon_key, canon_idx| { | ||
| 5279 | const canon_value = canon_values[canon_idx]; | ||
| 5280 | // We already deleted the items from this table that matched the target_branch. | ||
| 5281 | // So these are all instructions that are only overridden in the canon branch. | ||
| 5282 | parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); | ||
| 5283 | log.debug("canon_value = {}", .{canon_value}); | ||
| 5284 | if (canon_value == .dead) | ||
| 5285 | continue; | ||
| 5286 | const parent_mcv = blk: { | ||
| 5287 | var i: usize = self.branch_stack.items.len - 1; | ||
| 5288 | while (true) { | ||
| 5289 | i -= 1; | ||
| 5290 | if (self.branch_stack.items[i].inst_table.get(canon_key)) |mcv| { | ||
| 5291 | assert(mcv != .dead); | ||
| 5292 | break :blk mcv; | ||
| 5293 | } | ||
| 5294 | } | ||
| 5295 | }; | ||
| 5296 | log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value }); | ||
| 5297 | // TODO make sure the destination stack offset / register does not already have something | ||
| 5298 | // going on there. | ||
| 5299 | try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value); | ||
| 5300 | // TODO track the new register / stack allocation | ||
| 5301 | } | ||
| 5302 | } | ||
| 5303 | |||
| 5170 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { | 5304 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5171 | const next_inst = @intCast(u32, self.mir_instructions.len); | 5305 | const next_inst = @intCast(u32, self.mir_instructions.len); |
| 5172 | switch (self.mir_instructions.items(.tag)[reloc]) { | 5306 | switch (self.mir_instructions.items(.tag)[reloc]) { |
| ... | @@ -5196,7 +5330,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { | ... | @@ -5196,7 +5330,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5196 | block_data.mcv = switch (operand_mcv) { | 5330 | block_data.mcv = switch (operand_mcv) { |
| 5197 | .none, .dead, .unreach => unreachable, | 5331 | .none, .dead, .unreach => unreachable, |
| 5198 | .register, .stack_offset, .memory => operand_mcv, | 5332 | .register, .stack_offset, .memory => operand_mcv, |
| 5199 | .eflags, .immediate => blk: { | 5333 | .eflags, .immediate, .ptr_stack_offset => blk: { |
| 5200 | const new_mcv = try self.allocRegOrMem(block, true); | 5334 | const new_mcv = try self.allocRegOrMem(block, true); |
| 5201 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); | 5335 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 5202 | break :blk new_mcv; | 5336 | break :blk new_mcv; |
| ... | @@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5456 | .memory, | 5590 | .memory, |
| 5457 | .direct_load, | 5591 | .direct_load, |
| 5458 | .got_load, | 5592 | .got_load, |
| 5593 | .imports_load, | ||
| 5459 | => { | 5594 | => { |
| 5460 | if (abi_size <= 8) { | 5595 | if (abi_size <= 8) { |
| 5461 | const reg = try self.copyToTmpRegister(ty, mcv); | 5596 | const reg = try self.copyToTmpRegister(ty, mcv); |
| ... | @@ -5703,6 +5838,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5703,6 +5838,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5703 | .memory, | 5838 | .memory, |
| 5704 | .got_load, | 5839 | .got_load, |
| 5705 | .direct_load, | 5840 | .direct_load, |
| 5841 | .imports_load, | ||
| 5706 | => { | 5842 | => { |
| 5707 | if (abi_size <= 8) { | 5843 | if (abi_size <= 8) { |
| 5708 | const reg = try self.copyToTmpRegister(ty, mcv); | 5844 | const reg = try self.copyToTmpRegister(ty, mcv); |
| ... | @@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct { | ... | @@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct { |
| 5796 | dest_stack_base: ?Register = null, | 5932 | dest_stack_base: ?Register = null, |
| 5797 | }; | 5933 | }; |
| 5798 | 5934 | ||
| 5799 | /// Spills .rax and .rcx. | ||
| 5800 | fn genInlineMemcpy( | 5935 | fn genInlineMemcpy( |
| 5801 | self: *Self, | 5936 | self: *Self, |
| 5802 | dst_ptr: MCValue, | 5937 | dst_ptr: MCValue, |
| ... | @@ -5804,15 +5939,6 @@ fn genInlineMemcpy( | ... | @@ -5804,15 +5939,6 @@ fn genInlineMemcpy( |
| 5804 | len: MCValue, | 5939 | len: MCValue, |
| 5805 | opts: InlineMemcpyOpts, | 5940 | opts: InlineMemcpyOpts, |
| 5806 | ) InnerError!void { | 5941 | ) InnerError!void { |
| 5807 | // TODO preserve contents of .rax and .rcx if not free, and then restore | ||
| 5808 | try self.register_manager.getReg(.rax, null); | ||
| 5809 | try self.register_manager.getReg(.rcx, null); | ||
| 5810 | |||
| 5811 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }); | ||
| 5812 | defer for (reg_locks) |lock| { | ||
| 5813 | self.register_manager.unlockReg(lock); | ||
| 5814 | }; | ||
| 5815 | |||
| 5816 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| | 5942 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| |
| 5817 | self.register_manager.lockReg(reg) | 5943 | self.register_manager.lockReg(reg) |
| 5818 | else | 5944 | else |
| ... | @@ -5825,11 +5951,18 @@ fn genInlineMemcpy( | ... | @@ -5825,11 +5951,18 @@ fn genInlineMemcpy( |
| 5825 | null; | 5951 | null; |
| 5826 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); | 5952 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); |
| 5827 | 5953 | ||
| 5828 | const dst_addr_reg = try self.register_manager.allocReg(null, gp); | 5954 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); |
| 5955 | const dst_addr_reg = regs[0]; | ||
| 5956 | const src_addr_reg = regs[1]; | ||
| 5957 | const index_reg = regs[2].to64(); | ||
| 5958 | const count_reg = regs[3].to64(); | ||
| 5959 | const tmp_reg = regs[4].to8(); | ||
| 5960 | |||
| 5829 | switch (dst_ptr) { | 5961 | switch (dst_ptr) { |
| 5830 | .memory, | 5962 | .memory, |
| 5831 | .got_load, | 5963 | .got_load, |
| 5832 | .direct_load, | 5964 | .direct_load, |
| 5965 | .imports_load, | ||
| 5833 | => { | 5966 | => { |
| 5834 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); | 5967 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); |
| 5835 | }, | 5968 | }, |
| ... | @@ -5857,14 +5990,12 @@ fn genInlineMemcpy( | ... | @@ -5857,14 +5990,12 @@ fn genInlineMemcpy( |
| 5857 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); | 5990 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| 5858 | }, | 5991 | }, |
| 5859 | } | 5992 | } |
| 5860 | const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); | ||
| 5861 | defer self.register_manager.unlockReg(dst_addr_reg_lock); | ||
| 5862 | 5993 | ||
| 5863 | const src_addr_reg = try self.register_manager.allocReg(null, gp); | ||
| 5864 | switch (src_ptr) { | 5994 | switch (src_ptr) { |
| 5865 | .memory, | 5995 | .memory, |
| 5866 | .got_load, | 5996 | .got_load, |
| 5867 | .direct_load, | 5997 | .direct_load, |
| 5998 | .imports_load, | ||
| 5868 | => { | 5999 | => { |
| 5869 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); | 6000 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); |
| 5870 | }, | 6001 | }, |
| ... | @@ -5892,26 +6023,13 @@ fn genInlineMemcpy( | ... | @@ -5892,26 +6023,13 @@ fn genInlineMemcpy( |
| 5892 | return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); | 6023 | return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); |
| 5893 | }, | 6024 | }, |
| 5894 | } | 6025 | } |
| 5895 | const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); | ||
| 5896 | defer self.register_manager.unlockReg(src_addr_reg_lock); | ||
| 5897 | |||
| 5898 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | ||
| 5899 | const count_reg = regs[0].to64(); | ||
| 5900 | const tmp_reg = regs[1].to8(); | ||
| 5901 | 6026 | ||
| 5902 | try self.genSetReg(Type.usize, count_reg, len); | 6027 | try self.genSetReg(Type.usize, count_reg, len); |
| 5903 | 6028 | ||
| 5904 | // mov rcx, 0 | 6029 | // mov index_reg, 0 |
| 5905 | _ = try self.addInst(.{ | ||
| 5906 | .tag = .mov, | ||
| 5907 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), | ||
| 5908 | .data = .{ .imm = 0 }, | ||
| 5909 | }); | ||
| 5910 | |||
| 5911 | // mov rax, 0 | ||
| 5912 | _ = try self.addInst(.{ | 6030 | _ = try self.addInst(.{ |
| 5913 | .tag = .mov, | 6031 | .tag = .mov, |
| 5914 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | 6032 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), |
| 5915 | .data = .{ .imm = 0 }, | 6033 | .data = .{ .imm = 0 }, |
| 5916 | }); | 6034 | }); |
| 5917 | 6035 | ||
| ... | @@ -5933,37 +6051,30 @@ fn genInlineMemcpy( | ... | @@ -5933,37 +6051,30 @@ fn genInlineMemcpy( |
| 5933 | } }, | 6051 | } }, |
| 5934 | }); | 6052 | }); |
| 5935 | 6053 | ||
| 5936 | // mov tmp, [addr + rcx] | 6054 | // mov tmp, [addr + index_reg] |
| 5937 | _ = try self.addInst(.{ | 6055 | _ = try self.addInst(.{ |
| 5938 | .tag = .mov_scale_src, | 6056 | .tag = .mov_scale_src, |
| 5939 | .ops = Mir.Inst.Ops.encode(.{ | 6057 | .ops = Mir.Inst.Ops.encode(.{ |
| 5940 | .reg1 = tmp_reg.to8(), | 6058 | .reg1 = tmp_reg.to8(), |
| 5941 | .reg2 = src_addr_reg, | 6059 | .reg2 = src_addr_reg, |
| 5942 | }), | 6060 | }), |
| 5943 | .data = .{ .imm = 0 }, | 6061 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, |
| 5944 | }); | 6062 | }); |
| 5945 | 6063 | ||
| 5946 | // mov [stack_offset + rax], tmp | 6064 | // mov [stack_offset + index_reg], tmp |
| 5947 | _ = try self.addInst(.{ | 6065 | _ = try self.addInst(.{ |
| 5948 | .tag = .mov_scale_dst, | 6066 | .tag = .mov_scale_dst, |
| 5949 | .ops = Mir.Inst.Ops.encode(.{ | 6067 | .ops = Mir.Inst.Ops.encode(.{ |
| 5950 | .reg1 = dst_addr_reg, | 6068 | .reg1 = dst_addr_reg, |
| 5951 | .reg2 = tmp_reg.to8(), | 6069 | .reg2 = tmp_reg.to8(), |
| 5952 | }), | 6070 | }), |
| 5953 | .data = .{ .imm = 0 }, | 6071 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, |
| 5954 | }); | ||
| 5955 | |||
| 5956 | // add rcx, 1 | ||
| 5957 | _ = try self.addInst(.{ | ||
| 5958 | .tag = .add, | ||
| 5959 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), | ||
| 5960 | .data = .{ .imm = 1 }, | ||
| 5961 | }); | 6072 | }); |
| 5962 | 6073 | ||
| 5963 | // add rax, 1 | 6074 | // add index_reg, 1 |
| 5964 | _ = try self.addInst(.{ | 6075 | _ = try self.addInst(.{ |
| 5965 | .tag = .add, | 6076 | .tag = .add, |
| 5966 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | 6077 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), |
| 5967 | .data = .{ .imm = 1 }, | 6078 | .data = .{ .imm = 1 }, |
| 5968 | }); | 6079 | }); |
| 5969 | 6080 | ||
| ... | @@ -5985,7 +6096,6 @@ fn genInlineMemcpy( | ... | @@ -5985,7 +6096,6 @@ fn genInlineMemcpy( |
| 5985 | try self.performReloc(loop_reloc); | 6096 | try self.performReloc(loop_reloc); |
| 5986 | } | 6097 | } |
| 5987 | 6098 | ||
| 5988 | /// Spills .rax register. | ||
| 5989 | fn genInlineMemset( | 6099 | fn genInlineMemset( |
| 5990 | self: *Self, | 6100 | self: *Self, |
| 5991 | dst_ptr: MCValue, | 6101 | dst_ptr: MCValue, |
| ... | @@ -5993,16 +6103,27 @@ fn genInlineMemset( | ... | @@ -5993,16 +6103,27 @@ fn genInlineMemset( |
| 5993 | len: MCValue, | 6103 | len: MCValue, |
| 5994 | opts: InlineMemcpyOpts, | 6104 | opts: InlineMemcpyOpts, |
| 5995 | ) InnerError!void { | 6105 | ) InnerError!void { |
| 5996 | // TODO preserve contents of .rax and then restore | 6106 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| |
| 5997 | try self.register_manager.getReg(.rax, null); | 6107 | self.register_manager.lockReg(reg) |
| 5998 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); | 6108 | else |
| 5999 | defer self.register_manager.unlockReg(rax_lock); | 6109 | null; |
| 6110 | defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg); | ||
| 6111 | |||
| 6112 | const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| | ||
| 6113 | self.register_manager.lockReg(reg) | ||
| 6114 | else | ||
| 6115 | null; | ||
| 6116 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 6117 | |||
| 6118 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | ||
| 6119 | const addr_reg = regs[0]; | ||
| 6120 | const index_reg = regs[1].to64(); | ||
| 6000 | 6121 | ||
| 6001 | const addr_reg = try self.register_manager.allocReg(null, gp); | ||
| 6002 | switch (dst_ptr) { | 6122 | switch (dst_ptr) { |
| 6003 | .memory, | 6123 | .memory, |
| 6004 | .got_load, | 6124 | .got_load, |
| 6005 | .direct_load, | 6125 | .direct_load, |
| 6126 | .imports_load, | ||
| 6006 | => { | 6127 | => { |
| 6007 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); | 6128 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); |
| 6008 | }, | 6129 | }, |
| ... | @@ -6030,17 +6151,15 @@ fn genInlineMemset( | ... | @@ -6030,17 +6151,15 @@ fn genInlineMemset( |
| 6030 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); | 6151 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| 6031 | }, | 6152 | }, |
| 6032 | } | 6153 | } |
| 6033 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); | ||
| 6034 | defer self.register_manager.unlockReg(addr_reg_lock); | ||
| 6035 | 6154 | ||
| 6036 | try self.genSetReg(Type.usize, .rax, len); | 6155 | try self.genSetReg(Type.usize, index_reg, len); |
| 6037 | try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); | 6156 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); |
| 6038 | 6157 | ||
| 6039 | // loop: | 6158 | // loop: |
| 6040 | // cmp rax, -1 | 6159 | // cmp index_reg, -1 |
| 6041 | const loop_start = try self.addInst(.{ | 6160 | const loop_start = try self.addInst(.{ |
| 6042 | .tag = .cmp, | 6161 | .tag = .cmp, |
| 6043 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | 6162 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), |
| 6044 | .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, | 6163 | .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, |
| 6045 | }); | 6164 | }); |
| 6046 | 6165 | ||
| ... | @@ -6059,24 +6178,20 @@ fn genInlineMemset( | ... | @@ -6059,24 +6178,20 @@ fn genInlineMemset( |
| 6059 | if (x > math.maxInt(i32)) { | 6178 | if (x > math.maxInt(i32)) { |
| 6060 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); | 6179 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); |
| 6061 | } | 6180 | } |
| 6062 | // mov byte ptr [rbp + rax + stack_offset], imm | 6181 | // mov byte ptr [rbp + index_reg + stack_offset], imm |
| 6063 | const payload = try self.addExtra(Mir.ImmPair{ | ||
| 6064 | .dest_off = 0, | ||
| 6065 | .operand = @truncate(u32, x), | ||
| 6066 | }); | ||
| 6067 | _ = try self.addInst(.{ | 6182 | _ = try self.addInst(.{ |
| 6068 | .tag = .mov_mem_index_imm, | 6183 | .tag = .mov_mem_index_imm, |
| 6069 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), | 6184 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), |
| 6070 | .data = .{ .payload = payload }, | 6185 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) }, |
| 6071 | }); | 6186 | }); |
| 6072 | }, | 6187 | }, |
| 6073 | else => return self.fail("TODO inline memset for value of type {}", .{value}), | 6188 | else => return self.fail("TODO inline memset for value of type {}", .{value}), |
| 6074 | } | 6189 | } |
| 6075 | 6190 | ||
| 6076 | // sub rax, 1 | 6191 | // sub index_reg, 1 |
| 6077 | _ = try self.addInst(.{ | 6192 | _ = try self.addInst(.{ |
| 6078 | .tag = .sub, | 6193 | .tag = .sub, |
| 6079 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | 6194 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), |
| 6080 | .data = .{ .imm = 1 }, | 6195 | .data = .{ .imm = 1 }, |
| 6081 | }); | 6196 | }); |
| 6082 | 6197 | ||
| ... | @@ -6243,6 +6358,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -6243,6 +6358,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6243 | }, | 6358 | }, |
| 6244 | .direct_load, | 6359 | .direct_load, |
| 6245 | .got_load, | 6360 | .got_load, |
| 6361 | .imports_load, | ||
| 6246 | => { | 6362 | => { |
| 6247 | switch (ty.zigTypeTag()) { | 6363 | switch (ty.zigTypeTag()) { |
| 6248 | .Float => { | 6364 | .Float => { |
| ... | @@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 6637 | // TODO Is this the only condition for pointer dereference for memcpy? | 6753 | // TODO Is this the only condition for pointer dereference for memcpy? |
| 6638 | const src: MCValue = blk: { | 6754 | const src: MCValue = blk: { |
| 6639 | switch (src_ptr) { | 6755 | switch (src_ptr) { |
| 6640 | .got_load, .direct_load, .memory => { | 6756 | .got_load, |
| 6757 | .direct_load, | ||
| 6758 | .imports_load, | ||
| 6759 | .memory, | ||
| 6760 | => { | ||
| 6641 | const reg = try self.register_manager.allocReg(null, gp); | 6761 | const reg = try self.register_manager.allocReg(null, gp); |
| 6642 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); | 6762 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); |
| 6643 | _ = try self.addInst(.{ | 6763 | _ = try self.addInst(.{ |
| ... | @@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { | ... | @@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6901 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 7021 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6902 | return MCValue{ .direct_load = local_sym_index }; | 7022 | return MCValue{ .direct_load = local_sym_index }; |
| 6903 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | 7023 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 6904 | return self.fail("TODO lower unnamed const in COFF", .{}); | 7024 | return MCValue{ .direct_load = local_sym_index }; |
| 6905 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { | 7025 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
| 6906 | return self.fail("TODO lower unnamed const in Plan9", .{}); | 7026 | return self.fail("TODO lower unnamed const in Plan9", .{}); |
| 6907 | } else { | 7027 | } else { |
| ... | @@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7066 | result.stack_align = 1; | 7186 | result.stack_align = 1; |
| 7067 | return result; | 7187 | return result; |
| 7068 | }, | 7188 | }, |
| 7069 | .Unspecified, .C => { | 7189 | .C => { |
| 7070 | // Return values | 7190 | // Return values |
| 7071 | if (ret_ty.zigTypeTag() == .NoReturn) { | 7191 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 7072 | result.return_value = .{ .unreach = {} }; | 7192 | result.return_value = .{ .unreach = {} }; |
| 7073 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { | 7193 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 7194 | // TODO: is this even possible for C calling convention? | ||
| 7074 | result.return_value = .{ .none = {} }; | 7195 | result.return_value = .{ .none = {} }; |
| 7075 | } else { | 7196 | } else { |
| 7076 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 7197 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| ... | @@ -7078,84 +7199,106 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -7078,84 +7199,106 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7078 | assert(ret_ty.isError()); | 7199 | assert(ret_ty.isError()); |
| 7079 | result.return_value = .{ .immediate = 0 }; | 7200 | result.return_value = .{ .immediate = 0 }; |
| 7080 | } else if (ret_ty_size <= 8) { | 7201 | } else if (ret_ty_size <= 8) { |
| 7081 | const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size); | 7202 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); |
| 7082 | result.return_value = .{ .register = aliased_reg }; | 7203 | result.return_value = .{ .register = aliased_reg }; |
| 7083 | } else { | 7204 | } else { |
| 7084 | // We simply make the return MCValue a stack offset. However, the actual value | 7205 | // TODO: return argument cell should go first |
| 7085 | // for the offset will be populated later. We will also push the stack offset | ||
| 7086 | // value into .rdi register when we resolve the offset. | ||
| 7087 | result.return_value = .{ .stack_offset = 0 }; | 7206 | result.return_value = .{ .stack_offset = 0 }; |
| 7088 | } | 7207 | } |
| 7089 | } | 7208 | } |
| 7090 | 7209 | ||
| 7091 | // Input params | 7210 | // Input params |
| 7092 | // First, split into args that can be passed via registers. | 7211 | var next_stack_offset: u32 = switch (result.return_value) { |
| 7093 | // This will make it easier to then push the rest of args in reverse | 7212 | .stack_offset => |off| @intCast(u32, off), |
| 7094 | // order on the stack. | 7213 | else => 0, |
| 7095 | var next_int_reg: usize = 0; | 7214 | }; |
| 7096 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); | 7215 | |
| 7097 | defer by_reg.deinit(); | 7216 | for (param_types) |ty, i| { |
| 7098 | 7217 | assert(ty.hasRuntimeBits()); | |
| 7099 | // If we want debug output, we store all args on stack for better liveness of args | 7218 | |
| 7100 | // in debugging contexts such as previewing the args in the debugger anywhere in | 7219 | const classes: []const abi.Class = switch (self.target.os.tag) { |
| 7101 | // the procedure. Passing the args via registers can lead to reusing the register | 7220 | .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, |
| 7102 | // for local ops thus clobbering the input arg forever. | 7221 | else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*), .none), |
| 7103 | // This of course excludes C ABI calls. | 7222 | }; |
| 7104 | const omit_args_in_registers = blk: { | 7223 | if (classes.len > 1) { |
| 7105 | if (cc == .C) break :blk false; | 7224 | return self.fail("TODO handle multiple classes per type", .{}); |
| 7106 | switch (self.bin_file.options.optimize_mode) { | 7225 | } |
| 7107 | .Debug => break :blk true, | 7226 | switch (classes[0]) { |
| 7108 | else => break :blk false, | 7227 | .integer => blk: { |
| 7228 | if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough | ||
| 7229 | result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] }; | ||
| 7230 | continue; | ||
| 7231 | }, | ||
| 7232 | .memory => {}, // fallthrough | ||
| 7233 | else => |class| return self.fail("TODO handle calling convention class {s}", .{ | ||
| 7234 | @tagName(class), | ||
| 7235 | }), | ||
| 7236 | } | ||
| 7237 | |||
| 7238 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | ||
| 7239 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); | ||
| 7240 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | ||
| 7241 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | ||
| 7242 | next_stack_offset = offset; | ||
| 7243 | } | ||
| 7244 | |||
| 7245 | // Align the stack to 16bytes before allocating shadow stack space (if any). | ||
| 7246 | const aligned_next_stack_offset = mem.alignForwardGeneric(u32, next_stack_offset, 16); | ||
| 7247 | const padding = aligned_next_stack_offset - next_stack_offset; | ||
| 7248 | if (padding > 0) { | ||
| 7249 | for (result.args) |*arg| { | ||
| 7250 | if (arg.isRegister()) continue; | ||
| 7251 | arg.stack_offset += @intCast(i32, padding); | ||
| 7109 | } | 7252 | } |
| 7253 | } | ||
| 7254 | |||
| 7255 | const shadow_stack_space: u32 = switch (self.target.os.tag) { | ||
| 7256 | .windows => @intCast(u32, 4 * @sizeOf(u64)), | ||
| 7257 | else => 0, | ||
| 7110 | }; | 7258 | }; |
| 7111 | if (!omit_args_in_registers) { | 7259 | |
| 7112 | for (param_types) |ty, i| { | 7260 | // alignment padding | args ... | shadow stack space (if any) | ret addr | $rbp | |
| 7113 | if (!ty.hasRuntimeBits()) continue; | 7261 | result.stack_byte_count = aligned_next_stack_offset + shadow_stack_space; |
| 7114 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | 7262 | result.stack_align = 16; |
| 7115 | // For simplicity of codegen, slices and other types are always pushed onto the stack. | 7263 | }, |
| 7116 | // TODO: look into optimizing this by passing things as registers sometimes, | 7264 | .Unspecified => { |
| 7117 | // such as ptr and len of slices as separate registers. | 7265 | // Return values |
| 7118 | // TODO: also we need to honor the C ABI for relevant types rather than passing on | 7266 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 7119 | // the stack here. | 7267 | result.return_value = .{ .unreach = {} }; |
| 7120 | const pass_in_reg = switch (ty.zigTypeTag()) { | 7268 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 7121 | .Bool => true, | 7269 | result.return_value = .{ .none = {} }; |
| 7122 | .Int, .Enum => param_size <= 8, | 7270 | } else { |
| 7123 | .Pointer => ty.ptrSize() != .Slice, | 7271 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 7124 | .Optional => ty.isPtrLikeOptional(), | 7272 | if (ret_ty_size == 0) { |
| 7125 | else => false, | 7273 | assert(ret_ty.isError()); |
| 7126 | }; | 7274 | result.return_value = .{ .immediate = 0 }; |
| 7127 | if (pass_in_reg) { | 7275 | } else if (ret_ty_size <= 8) { |
| 7128 | if (next_int_reg >= c_abi_int_param_regs.len) break; | 7276 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); |
| 7129 | try by_reg.putNoClobber(i, next_int_reg); | 7277 | result.return_value = .{ .register = aliased_reg }; |
| 7130 | next_int_reg += 1; | 7278 | } else { |
| 7131 | } | 7279 | // We simply make the return MCValue a stack offset. However, the actual value |
| 7280 | // for the offset will be populated later. We will also push the stack offset | ||
| 7281 | // value into an appropriate register when we resolve the offset. | ||
| 7282 | result.return_value = .{ .stack_offset = 0 }; | ||
| 7132 | } | 7283 | } |
| 7133 | } | 7284 | } |
| 7134 | 7285 | ||
| 7286 | // Input params | ||
| 7135 | var next_stack_offset: u32 = switch (result.return_value) { | 7287 | var next_stack_offset: u32 = switch (result.return_value) { |
| 7136 | .stack_offset => |off| @intCast(u32, off), | 7288 | .stack_offset => |off| @intCast(u32, off), |
| 7137 | else => 0, | 7289 | else => 0, |
| 7138 | }; | 7290 | }; |
| 7139 | var count: usize = param_types.len; | 7291 | |
| 7140 | while (count > 0) : (count -= 1) { | 7292 | for (param_types) |ty, i| { |
| 7141 | const i = count - 1; | ||
| 7142 | const ty = param_types[i]; | ||
| 7143 | if (!ty.hasRuntimeBits()) { | 7293 | if (!ty.hasRuntimeBits()) { |
| 7144 | assert(cc != .C); | ||
| 7145 | result.args[i] = .{ .none = {} }; | 7294 | result.args[i] = .{ .none = {} }; |
| 7146 | continue; | 7295 | continue; |
| 7147 | } | 7296 | } |
| 7148 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | 7297 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 7149 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); | 7298 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); |
| 7150 | if (by_reg.get(i)) |int_reg| { | 7299 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); |
| 7151 | const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size); | 7300 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; |
| 7152 | result.args[i] = .{ .register = aliased_reg }; | 7301 | next_stack_offset = offset; |
| 7153 | next_int_reg += 1; | ||
| 7154 | } else { | ||
| 7155 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | ||
| 7156 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | ||
| 7157 | next_stack_offset = offset; | ||
| 7158 | } | ||
| 7159 | } | 7302 | } |
| 7160 | 7303 | ||
| 7161 | result.stack_align = 16; | 7304 | result.stack_align = 16; |
src/arch/x86_64/Emit.zig+57-36| ... | @@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro | ... | @@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro |
| 283 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 283 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 284 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 284 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 285 | const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; | 285 | const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; |
| 286 | const reg_list = Mir.RegisterList(Register, &abi.callee_preserved_regs).fromInt(save_reg_list.register_list); | ||
| 287 | var disp: i32 = -@intCast(i32, save_reg_list.stack_end); | 286 | var disp: i32 = -@intCast(i32, save_reg_list.stack_end); |
| 288 | inline for (abi.callee_preserved_regs) |reg| { | 287 | const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); |
| 289 | if (reg_list.isSet(reg)) { | 288 | const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); |
| 289 | for (callee_preserved_regs) |reg| { | ||
| 290 | if (reg_list.isSet(callee_preserved_regs, reg)) { | ||
| 290 | switch (tag) { | 291 | switch (tag) { |
| 291 | .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ | 292 | .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ |
| 292 | .disp = @bitCast(u32, disp), | 293 | .disp = @bitCast(u32, disp), |
| ... | @@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 { | ... | @@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 { |
| 614 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 615 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 615 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 616 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 616 | const scale = ops.flags; | 617 | const scale = ops.flags; |
| 617 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 618 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 618 | // OP reg1, [reg2 + scale*rcx + imm32] | 619 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); |
| 620 | // OP reg1, [reg2 + scale*index + imm32] | ||
| 619 | const scale_index = ScaleIndex{ | 621 | const scale_index = ScaleIndex{ |
| 620 | .scale = scale, | 622 | .scale = scale, |
| 621 | .index = .rcx, | 623 | .index = index_reg_disp.index, |
| 622 | }; | 624 | }; |
| 623 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | 625 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 624 | .disp = imm, | 626 | .disp = index_reg_disp.disp, |
| 625 | .base = ops.reg2, | 627 | .base = ops.reg2, |
| 626 | .scale_index = scale_index, | 628 | .scale_index = scale_index, |
| 627 | }), emit.code); | 629 | }), emit.code); |
| ... | @@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 630 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 632 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 631 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 633 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 632 | const scale = ops.flags; | 634 | const scale = ops.flags; |
| 633 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 635 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 636 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | ||
| 634 | const scale_index = ScaleIndex{ | 637 | const scale_index = ScaleIndex{ |
| 635 | .scale = scale, | 638 | .scale = scale, |
| 636 | .index = .rax, | 639 | .index = index_reg_disp.index, |
| 637 | }; | 640 | }; |
| 638 | if (ops.reg2 == .none) { | 641 | assert(ops.reg2 != .none); |
| 639 | // OP qword ptr [reg1 + scale*rax + 0], imm32 | 642 | // OP [reg1 + scale*index + imm32], reg2 |
| 640 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ | ||
| 641 | .disp = 0, | ||
| 642 | .base = ops.reg1, | ||
| 643 | .scale_index = scale_index, | ||
| 644 | }), imm, emit.code); | ||
| 645 | } | ||
| 646 | // OP [reg1 + scale*rax + imm32], reg2 | ||
| 647 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ | 643 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ |
| 648 | .disp = imm, | 644 | .disp = index_reg_disp.disp, |
| 649 | .base = ops.reg1, | 645 | .base = ops.reg1, |
| 650 | .scale_index = scale_index, | 646 | .scale_index = scale_index, |
| 651 | }), ops.reg2, emit.code); | 647 | }), ops.reg2, emit.code); |
| ... | @@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 655 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 651 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 656 | const scale = ops.flags; | 652 | const scale = ops.flags; |
| 657 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 653 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 658 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | 654 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); |
| 659 | const scale_index = ScaleIndex{ | 655 | const scale_index = ScaleIndex{ |
| 660 | .scale = scale, | 656 | .scale = scale, |
| 661 | .index = .rax, | 657 | .index = index_reg_disp_imm.index, |
| 662 | }; | 658 | }; |
| 663 | // OP qword ptr [reg1 + scale*rax + imm32], imm32 | 659 | // OP qword ptr [reg1 + scale*index + imm32], imm32 |
| 664 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ | 660 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ |
| 665 | .disp = imm_pair.dest_off, | 661 | .disp = index_reg_disp_imm.disp, |
| 666 | .base = ops.reg1, | 662 | .base = ops.reg1, |
| 667 | .scale_index = scale_index, | 663 | .scale_index = scale_index, |
| 668 | }), imm_pair.operand, emit.code); | 664 | }), index_reg_disp_imm.imm, emit.code); |
| 669 | } | 665 | } |
| 670 | 666 | ||
| 671 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 667 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 672 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 668 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 673 | assert(ops.reg2 == .none); | 669 | assert(ops.reg2 == .none); |
| 674 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 670 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 675 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | 671 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); |
| 676 | const ptr_size: Memory.PtrSize = switch (ops.flags) { | 672 | const ptr_size: Memory.PtrSize = switch (ops.flags) { |
| 677 | 0b00 => .byte_ptr, | 673 | 0b00 => .byte_ptr, |
| 678 | 0b01 => .word_ptr, | 674 | 0b01 => .word_ptr, |
| ... | @@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v | ... | @@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v |
| 681 | }; | 677 | }; |
| 682 | const scale_index = ScaleIndex{ | 678 | const scale_index = ScaleIndex{ |
| 683 | .scale = 0, | 679 | .scale = 0, |
| 684 | .index = .rax, | 680 | .index = index_reg_disp_imm.index, |
| 685 | }; | 681 | }; |
| 686 | // OP ptr [reg1 + rax*1 + imm32], imm32 | 682 | // OP ptr [reg1 + index + imm32], imm32 |
| 687 | return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ | 683 | return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ |
| 688 | .disp = imm_pair.dest_off, | 684 | .disp = index_reg_disp_imm.disp, |
| 689 | .base = ops.reg1, | 685 | .base = ops.reg1, |
| 690 | .scale_index = scale_index, | 686 | .scale_index = scale_index, |
| 691 | }), imm_pair.operand, emit.code); | 687 | }), index_reg_disp_imm.imm, emit.code); |
| 692 | } | 688 | } |
| 693 | 689 | ||
| 694 | fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 690 | fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| ... | @@ -956,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -956,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 956 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); | 952 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); |
| 957 | }, | 953 | }, |
| 958 | 0b10 => { | 954 | 0b10 => { |
| 959 | // lea reg, [rbp + rcx + imm32] | 955 | // lea reg, [rbp + index + imm32] |
| 960 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 956 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 957 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | ||
| 961 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; | 958 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; |
| 962 | const scale_index = ScaleIndex{ | 959 | const scale_index = ScaleIndex{ |
| 963 | .scale = 0, | 960 | .scale = 0, |
| 964 | .index = .rcx, | 961 | .index = index_reg_disp.index, |
| 965 | }; | 962 | }; |
| 966 | return lowerToRmEnc( | 963 | return lowerToRmEnc( |
| 967 | .lea, | 964 | .lea, |
| 968 | ops.reg1, | 965 | ops.reg1, |
| 969 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ | 966 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 970 | .disp = imm, | 967 | .disp = index_reg_disp.disp, |
| 971 | .base = src_reg, | 968 | .base = src_reg, |
| 972 | .scale_index = scale_index, | 969 | .scale_index = scale_index, |
| 973 | }), | 970 | }), |
| ... | @@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 985 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; | 982 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; |
| 986 | 983 | ||
| 987 | switch (ops.flags) { | 984 | switch (ops.flags) { |
| 988 | 0b00, 0b01 => {}, | 985 | 0b00, 0b01, 0b10 => {}, |
| 989 | else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}), | 986 | else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), |
| 990 | } | 987 | } |
| 991 | 988 | ||
| 992 | // lea reg1, [rip + reloc] | 989 | // lea reg1, [rip + reloc] |
| ... | @@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1024 | .@"type" = switch (ops.flags) { | 1021 | .@"type" = switch (ops.flags) { |
| 1025 | 0b00 => .got, | 1022 | 0b00 => .got, |
| 1026 | 0b01 => .direct, | 1023 | 0b01 => .direct, |
| 1024 | 0b10 => .imports, | ||
| 1027 | else => unreachable, | 1025 | else => unreachable, |
| 1028 | }, | 1026 | }, |
| 1029 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | 1027 | .target = .{ .sym_index = relocation.sym_index, .file = null }, |
| ... | @@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1031 | .addend = 0, | 1029 | .addend = 0, |
| 1032 | .pcrel = true, | 1030 | .pcrel = true, |
| 1033 | .length = 2, | 1031 | .length = 2, |
| 1034 | .prev_vaddr = atom.getSymbol(coff_file).value, | ||
| 1035 | }); | 1032 | }); |
| 1036 | } else { | 1033 | } else { |
| 1037 | return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); | 1034 | return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); |
| ... | @@ -1157,6 +1154,17 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1157,6 +1154,17 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1157 | .length = 2, | 1154 | .length = 2, |
| 1158 | .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), | 1155 | .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1159 | }); | 1156 | }); |
| 1157 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | ||
| 1158 | // Add relocation to the decl. | ||
| 1159 | const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?; | ||
| 1160 | try atom.addRelocation(coff_file, .{ | ||
| 1161 | .@"type" = .direct, | ||
| 1162 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | ||
| 1163 | .offset = offset, | ||
| 1164 | .addend = 0, | ||
| 1165 | .pcrel = true, | ||
| 1166 | .length = 2, | ||
| 1167 | }); | ||
| 1160 | } else { | 1168 | } else { |
| 1161 | return emit.fail("TODO implement call_extern for linking backends different than MachO", .{}); | 1169 | return emit.fail("TODO implement call_extern for linking backends different than MachO", .{}); |
| 1162 | } | 1170 | } |
| ... | @@ -2241,6 +2249,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st | ... | @@ -2241,6 +2249,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st |
| 2241 | encoder.rex(.{ | 2249 | encoder.rex(.{ |
| 2242 | .w = wide, | 2250 | .w = wide, |
| 2243 | .b = base.isExtended(), | 2251 | .b = base.isExtended(), |
| 2252 | .x = if (mem_op.scale_index) |si| si.index.isExtended() else false, | ||
| 2244 | }); | 2253 | }); |
| 2245 | } | 2254 | } |
| 2246 | opc.encode(encoder); | 2255 | opc.encode(encoder); |
| ... | @@ -2346,10 +2355,12 @@ fn lowerToMiXEnc( | ... | @@ -2346,10 +2355,12 @@ fn lowerToMiXEnc( |
| 2346 | encoder.rex(.{ | 2355 | encoder.rex(.{ |
| 2347 | .w = dst_mem.ptr_size == .qword_ptr, | 2356 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2348 | .b = base.isExtended(), | 2357 | .b = base.isExtended(), |
| 2358 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2349 | }); | 2359 | }); |
| 2350 | } else { | 2360 | } else { |
| 2351 | encoder.rex(.{ | 2361 | encoder.rex(.{ |
| 2352 | .w = dst_mem.ptr_size == .qword_ptr, | 2362 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2363 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2353 | }); | 2364 | }); |
| 2354 | } | 2365 | } |
| 2355 | opc.encode(encoder); | 2366 | opc.encode(encoder); |
| ... | @@ -2401,11 +2412,13 @@ fn lowerToRmEnc( | ... | @@ -2401,11 +2412,13 @@ fn lowerToRmEnc( |
| 2401 | .w = setRexWRegister(reg), | 2412 | .w = setRexWRegister(reg), |
| 2402 | .r = reg.isExtended(), | 2413 | .r = reg.isExtended(), |
| 2403 | .b = base.isExtended(), | 2414 | .b = base.isExtended(), |
| 2415 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2404 | }); | 2416 | }); |
| 2405 | } else { | 2417 | } else { |
| 2406 | encoder.rex(.{ | 2418 | encoder.rex(.{ |
| 2407 | .w = setRexWRegister(reg), | 2419 | .w = setRexWRegister(reg), |
| 2408 | .r = reg.isExtended(), | 2420 | .r = reg.isExtended(), |
| 2421 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2409 | }); | 2422 | }); |
| 2410 | } | 2423 | } |
| 2411 | opc.encode(encoder); | 2424 | opc.encode(encoder); |
| ... | @@ -2446,11 +2459,13 @@ fn lowerToMrEnc( | ... | @@ -2446,11 +2459,13 @@ fn lowerToMrEnc( |
| 2446 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), | 2459 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2447 | .r = reg.isExtended(), | 2460 | .r = reg.isExtended(), |
| 2448 | .b = base.isExtended(), | 2461 | .b = base.isExtended(), |
| 2462 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2449 | }); | 2463 | }); |
| 2450 | } else { | 2464 | } else { |
| 2451 | encoder.rex(.{ | 2465 | encoder.rex(.{ |
| 2452 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), | 2466 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2453 | .r = reg.isExtended(), | 2467 | .r = reg.isExtended(), |
| 2468 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2454 | }); | 2469 | }); |
| 2455 | } | 2470 | } |
| 2456 | opc.encode(encoder); | 2471 | opc.encode(encoder); |
| ... | @@ -2490,11 +2505,13 @@ fn lowerToRmiEnc( | ... | @@ -2490,11 +2505,13 @@ fn lowerToRmiEnc( |
| 2490 | .w = setRexWRegister(reg), | 2505 | .w = setRexWRegister(reg), |
| 2491 | .r = reg.isExtended(), | 2506 | .r = reg.isExtended(), |
| 2492 | .b = base.isExtended(), | 2507 | .b = base.isExtended(), |
| 2508 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2493 | }); | 2509 | }); |
| 2494 | } else { | 2510 | } else { |
| 2495 | encoder.rex(.{ | 2511 | encoder.rex(.{ |
| 2496 | .w = setRexWRegister(reg), | 2512 | .w = setRexWRegister(reg), |
| 2497 | .r = reg.isExtended(), | 2513 | .r = reg.isExtended(), |
| 2514 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2498 | }); | 2515 | }); |
| 2499 | } | 2516 | } |
| 2500 | opc.encode(encoder); | 2517 | opc.encode(encoder); |
| ... | @@ -2531,10 +2548,12 @@ fn lowerToVmEnc( | ... | @@ -2531,10 +2548,12 @@ fn lowerToVmEnc( |
| 2531 | vex.rex(.{ | 2548 | vex.rex(.{ |
| 2532 | .r = reg.isExtended(), | 2549 | .r = reg.isExtended(), |
| 2533 | .b = base.isExtended(), | 2550 | .b = base.isExtended(), |
| 2551 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2534 | }); | 2552 | }); |
| 2535 | } else { | 2553 | } else { |
| 2536 | vex.rex(.{ | 2554 | vex.rex(.{ |
| 2537 | .r = reg.isExtended(), | 2555 | .r = reg.isExtended(), |
| 2556 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2538 | }); | 2557 | }); |
| 2539 | } | 2558 | } |
| 2540 | encoder.vex(enc.prefix); | 2559 | encoder.vex(enc.prefix); |
| ... | @@ -2571,10 +2590,12 @@ fn lowerToMvEnc( | ... | @@ -2571,10 +2590,12 @@ fn lowerToMvEnc( |
| 2571 | vex.rex(.{ | 2590 | vex.rex(.{ |
| 2572 | .r = reg.isExtended(), | 2591 | .r = reg.isExtended(), |
| 2573 | .b = base.isExtended(), | 2592 | .b = base.isExtended(), |
| 2593 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2574 | }); | 2594 | }); |
| 2575 | } else { | 2595 | } else { |
| 2576 | vex.rex(.{ | 2596 | vex.rex(.{ |
| 2577 | .r = reg.isExtended(), | 2597 | .r = reg.isExtended(), |
| 2598 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | ||
| 2578 | }); | 2599 | }); |
| 2579 | } | 2600 | } |
| 2580 | encoder.vex(enc.prefix); | 2601 | encoder.vex(enc.prefix); |
src/arch/x86_64/Mir.zig+107-42| ... | @@ -44,25 +44,28 @@ pub const Inst = struct { | ... | @@ -44,25 +44,28 @@ pub const Inst = struct { |
| 44 | /// 0b01 word ptr [reg1 + imm32], imm16 | 44 | /// 0b01 word ptr [reg1 + imm32], imm16 |
| 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 | 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 |
| 46 | /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) | 46 | /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) |
| 47 | /// Notes: | ||
| 48 | /// * Uses `ImmPair` as payload | ||
| 47 | adc_mem_imm, | 49 | adc_mem_imm, |
| 48 | 50 | ||
| 49 | /// form: reg1, [reg2 + scale*rcx + imm32] | 51 | /// form: reg1, [reg2 + scale*index + imm32] |
| 50 | /// ops flags scale | 52 | /// ops flags scale |
| 51 | /// 0b00 1 | 53 | /// 0b00 1 |
| 52 | /// 0b01 2 | 54 | /// 0b01 2 |
| 53 | /// 0b10 4 | 55 | /// 0b10 4 |
| 54 | /// 0b11 8 | 56 | /// 0b11 8 |
| 57 | /// Notes: | ||
| 58 | /// * Uses `IndexRegisterDisp` as payload | ||
| 55 | adc_scale_src, | 59 | adc_scale_src, |
| 56 | 60 | ||
| 57 | /// form: [reg1 + scale*rax + imm32], reg2 | 61 | /// form: [reg1 + scale*index + imm32], reg2 |
| 58 | /// form: [reg1 + scale*rax + 0], imm32 | ||
| 59 | /// ops flags scale | 62 | /// ops flags scale |
| 60 | /// 0b00 1 | 63 | /// 0b00 1 |
| 61 | /// 0b01 2 | 64 | /// 0b01 2 |
| 62 | /// 0b10 4 | 65 | /// 0b10 4 |
| 63 | /// 0b11 8 | 66 | /// 0b11 8 |
| 64 | /// Notes: | 67 | /// Notes: |
| 65 | /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. | 68 | /// * Uses `IndexRegisterDisp` payload. |
| 66 | adc_scale_dst, | 69 | adc_scale_dst, |
| 67 | 70 | ||
| 68 | /// form: [reg1 + scale*rax + imm32], imm32 | 71 | /// form: [reg1 + scale*rax + imm32], imm32 |
| ... | @@ -72,14 +75,16 @@ pub const Inst = struct { | ... | @@ -72,14 +75,16 @@ pub const Inst = struct { |
| 72 | /// 0b10 4 | 75 | /// 0b10 4 |
| 73 | /// 0b11 8 | 76 | /// 0b11 8 |
| 74 | /// Notes: | 77 | /// Notes: |
| 75 | /// * Data field `payload` points at `ImmPair`. | 78 | /// * Uses `IndexRegisterDispImm` payload. |
| 76 | adc_scale_imm, | 79 | adc_scale_imm, |
| 77 | 80 | ||
| 78 | /// ops flags: form: | 81 | /// ops flags: form: |
| 79 | /// 0b00 byte ptr [reg1 + rax + imm32], imm8 | 82 | /// 0b00 byte ptr [reg1 + index + imm32], imm8 |
| 80 | /// 0b01 word ptr [reg1 + rax + imm32], imm16 | 83 | /// 0b01 word ptr [reg1 + index + imm32], imm16 |
| 81 | /// 0b10 dword ptr [reg1 + rax + imm32], imm32 | 84 | /// 0b10 dword ptr [reg1 + index + imm32], imm32 |
| 82 | /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64) | 85 | /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64) |
| 86 | /// Notes: | ||
| 87 | /// * Uses `IndexRegisterDispImm` payload. | ||
| 83 | adc_mem_index_imm, | 88 | adc_mem_index_imm, |
| 84 | 89 | ||
| 85 | // The following instructions all have the same encoding as `adc`. | 90 | // The following instructions all have the same encoding as `adc`. |
| ... | @@ -174,12 +179,15 @@ pub const Inst = struct { | ... | @@ -174,12 +179,15 @@ pub const Inst = struct { |
| 174 | /// 0b00 reg1, [reg2 + imm32] | 179 | /// 0b00 reg1, [reg2 + imm32] |
| 175 | /// 0b00 reg1, [ds:imm32] | 180 | /// 0b00 reg1, [ds:imm32] |
| 176 | /// 0b01 reg1, [rip + imm32] | 181 | /// 0b01 reg1, [rip + imm32] |
| 177 | /// 0b10 reg1, [reg2 + rcx + imm32] | 182 | /// 0b10 reg1, [reg2 + index + imm32] |
| 183 | /// Notes: | ||
| 184 | /// * 0b10 uses `IndexRegisterDisp` payload | ||
| 178 | lea, | 185 | lea, |
| 179 | 186 | ||
| 180 | /// ops flags: form: | 187 | /// ops flags: form: |
| 181 | /// 0b00 reg1, [rip + reloc] // via GOT PIC | 188 | /// 0b00 reg1, [rip + reloc] // via GOT PIC |
| 182 | /// 0b01 reg1, [rip + reloc] // direct load PIC | 189 | /// 0b01 reg1, [rip + reloc] // direct load PIC |
| 190 | /// 0b10 reg1, [rip + reloc] // via imports table PIC | ||
| 183 | /// Notes: | 191 | /// Notes: |
| 184 | /// * `Data` contains `relocation` | 192 | /// * `Data` contains `relocation` |
| 185 | lea_pic, | 193 | lea_pic, |
| ... | @@ -460,46 +468,103 @@ pub const Inst = struct { | ... | @@ -460,46 +468,103 @@ pub const Inst = struct { |
| 460 | } | 468 | } |
| 461 | }; | 469 | }; |
| 462 | 470 | ||
| 463 | pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type { | 471 | pub const IndexRegisterDisp = struct { |
| 464 | assert(registers.len <= @bitSizeOf(u32)); | 472 | /// Index register to use with SIB-based encoding |
| 465 | return struct { | 473 | index: u32, |
| 466 | bitset: RegBitSet = RegBitSet.initEmpty(), | ||
| 467 | 474 | ||
| 468 | const RegBitSet = IntegerBitSet(registers.len); | 475 | /// Displacement value |
| 469 | const Self = @This(); | 476 | disp: u32, |
| 470 | 477 | ||
| 471 | fn getIndexForReg(reg: Reg) RegBitSet.MaskInt { | 478 | pub fn encode(index: Register, disp: u32) IndexRegisterDisp { |
| 472 | inline for (registers) |cpreg, i| { | 479 | return .{ |
| 473 | if (reg.id() == cpreg.id()) return i; | 480 | .index = @enumToInt(index), |
| 474 | } | 481 | .disp = disp, |
| 475 | unreachable; // register not in input register list! | 482 | }; |
| 476 | } | 483 | } |
| 477 | 484 | ||
| 478 | pub fn push(self: *Self, reg: Reg) void { | 485 | pub fn decode(this: IndexRegisterDisp) struct { |
| 479 | const index = getIndexForReg(reg); | 486 | index: Register, |
| 480 | self.bitset.set(index); | 487 | disp: u32, |
| 481 | } | 488 | } { |
| 489 | return .{ | ||
| 490 | .index = @intToEnum(Register, this.index), | ||
| 491 | .disp = this.disp, | ||
| 492 | }; | ||
| 493 | } | ||
| 494 | }; | ||
| 482 | 495 | ||
| 483 | pub fn isSet(self: Self, reg: Reg) bool { | 496 | /// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list |
| 484 | const index = getIndexForReg(reg); | 497 | /// instead of having two structs, one a superset of the other one? |
| 485 | return self.bitset.isSet(index); | 498 | pub const IndexRegisterDispImm = struct { |
| 486 | } | 499 | /// Index register to use with SIB-based encoding |
| 500 | index: u32, | ||
| 487 | 501 | ||
| 488 | pub fn asInt(self: Self) u32 { | 502 | /// Displacement value |
| 489 | return self.bitset.mask; | 503 | disp: u32, |
| 490 | } | ||
| 491 | 504 | ||
| 492 | pub fn fromInt(mask: u32) Self { | 505 | /// Immediate |
| 493 | return .{ | 506 | imm: u32, |
| 494 | .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) }, | 507 | |
| 495 | }; | 508 | pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm { |
| 496 | } | 509 | return .{ |
| 510 | .index = @enumToInt(index), | ||
| 511 | .disp = disp, | ||
| 512 | .imm = imm, | ||
| 513 | }; | ||
| 514 | } | ||
| 497 | 515 | ||
| 498 | pub fn count(self: Self) u32 { | 516 | pub fn decode(this: IndexRegisterDispImm) struct { |
| 499 | return @intCast(u32, self.bitset.count()); | 517 | index: Register, |
| 518 | disp: u32, | ||
| 519 | imm: u32, | ||
| 520 | } { | ||
| 521 | return .{ | ||
| 522 | .index = @intToEnum(Register, this.index), | ||
| 523 | .disp = this.disp, | ||
| 524 | .imm = this.imm, | ||
| 525 | }; | ||
| 526 | } | ||
| 527 | }; | ||
| 528 | |||
| 529 | /// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers | ||
| 530 | /// in a compact manner. | ||
| 531 | pub const RegisterList = struct { | ||
| 532 | bitset: BitSet = BitSet.initEmpty(), | ||
| 533 | |||
| 534 | const BitSet = IntegerBitSet(@ctz(@as(u32, 0))); | ||
| 535 | const Self = @This(); | ||
| 536 | |||
| 537 | fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt { | ||
| 538 | for (registers) |cpreg, i| { | ||
| 539 | if (reg.id() == cpreg.id()) return @intCast(u32, i); | ||
| 500 | } | 540 | } |
| 501 | }; | 541 | unreachable; // register not in input register list! |
| 502 | } | 542 | } |
| 543 | |||
| 544 | pub fn push(self: *Self, registers: []const Register, reg: Register) void { | ||
| 545 | const index = getIndexForReg(registers, reg); | ||
| 546 | self.bitset.set(index); | ||
| 547 | } | ||
| 548 | |||
| 549 | pub fn isSet(self: Self, registers: []const Register, reg: Register) bool { | ||
| 550 | const index = getIndexForReg(registers, reg); | ||
| 551 | return self.bitset.isSet(index); | ||
| 552 | } | ||
| 553 | |||
| 554 | pub fn asInt(self: Self) u32 { | ||
| 555 | return self.bitset.mask; | ||
| 556 | } | ||
| 557 | |||
| 558 | pub fn fromInt(mask: u32) Self { | ||
| 559 | return .{ | ||
| 560 | .bitset = BitSet{ .mask = @intCast(BitSet.MaskInt, mask) }, | ||
| 561 | }; | ||
| 562 | } | ||
| 563 | |||
| 564 | pub fn count(self: Self) u32 { | ||
| 565 | return @intCast(u32, self.bitset.count()); | ||
| 566 | } | ||
| 567 | }; | ||
| 503 | 568 | ||
| 504 | pub const SaveRegisterList = struct { | 569 | pub const SaveRegisterList = struct { |
| 505 | /// Use `RegisterList` to populate. | 570 | /// Use `RegisterList` to populate. |
src/arch/x86_64/abi.zig+60-14| ... | @@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { | ... | @@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { |
| 392 | } | 392 | } |
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | 395 | pub const SysV = struct { |
| 396 | /// for anything else but stack offset tracking therefore we exclude them from this set. | 396 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them |
| 397 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; | 397 | /// for anything else but stack offset tracking therefore we exclude them from this set. |
| 398 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | 398 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; |
| 399 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | 399 | /// These registers need to be preserved (saved on the stack) and restored by the caller before |
| 400 | /// In other words, these registers are free to use by the callee. | 400 | /// the caller relinquishes control to a subroutine via call instruction (or similar). |
| 401 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | 401 | /// In other words, these registers are free to use by the callee. |
| 402 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | ||
| 402 | 403 | ||
| 403 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | 404 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; |
| 404 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | 405 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; |
| 406 | }; | ||
| 407 | |||
| 408 | pub const Win64 = struct { | ||
| 409 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | ||
| 410 | /// for anything else but stack offset tracking therefore we exclude them from this set. | ||
| 411 | pub const callee_preserved_regs = [_]Register{ .rbx, .rsi, .rdi, .r12, .r13, .r14, .r15 }; | ||
| 412 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | ||
| 413 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | ||
| 414 | /// In other words, these registers are free to use by the callee. | ||
| 415 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 }; | ||
| 405 | 416 | ||
| 417 | pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 }; | ||
| 418 | pub const c_abi_int_return_regs = [_]Register{.rax}; | ||
| 419 | }; | ||
| 420 | |||
| 421 | pub fn getCalleePreservedRegs(target: Target) []const Register { | ||
| 422 | return switch (target.os.tag) { | ||
| 423 | .windows => &Win64.callee_preserved_regs, | ||
| 424 | else => &SysV.callee_preserved_regs, | ||
| 425 | }; | ||
| 426 | } | ||
| 427 | |||
| 428 | pub fn getCallerPreservedRegs(target: Target) []const Register { | ||
| 429 | return switch (target.os.tag) { | ||
| 430 | .windows => &Win64.caller_preserved_regs, | ||
| 431 | else => &SysV.caller_preserved_regs, | ||
| 432 | }; | ||
| 433 | } | ||
| 434 | |||
| 435 | pub fn getCAbiIntParamRegs(target: Target) []const Register { | ||
| 436 | return switch (target.os.tag) { | ||
| 437 | .windows => &Win64.c_abi_int_param_regs, | ||
| 438 | else => &SysV.c_abi_int_param_regs, | ||
| 439 | }; | ||
| 440 | } | ||
| 441 | |||
| 442 | pub fn getCAbiIntReturnRegs(target: Target) []const Register { | ||
| 443 | return switch (target.os.tag) { | ||
| 444 | .windows => &Win64.c_abi_int_return_regs, | ||
| 445 | else => &SysV.c_abi_int_return_regs, | ||
| 446 | }; | ||
| 447 | } | ||
| 448 | |||
| 449 | const gp_regs = [_]Register{ | ||
| 450 | .rbx, .r12, .r13, .r14, .r15, .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11, | ||
| 451 | }; | ||
| 406 | const sse_avx_regs = [_]Register{ | 452 | const sse_avx_regs = [_]Register{ |
| 407 | .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, | 453 | .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, |
| 408 | .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, | 454 | .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, |
| 409 | }; | 455 | }; |
| 410 | const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs; | 456 | const allocatable_regs = gp_regs ++ sse_avx_regs; |
| 411 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); | 457 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs); |
| 412 | 458 | ||
| 413 | // Register classes | 459 | // Register classes |
| 414 | const RegisterBitSet = RegisterManager.RegisterBitSet; | 460 | const RegisterBitSet = RegisterManager.RegisterBitSet; |
| ... | @@ -417,15 +463,15 @@ pub const RegisterClass = struct { | ... | @@ -417,15 +463,15 @@ pub const RegisterClass = struct { |
| 417 | var set = RegisterBitSet.initEmpty(); | 463 | var set = RegisterBitSet.initEmpty(); |
| 418 | set.setRangeValue(.{ | 464 | set.setRangeValue(.{ |
| 419 | .start = 0, | 465 | .start = 0, |
| 420 | .end = caller_preserved_regs.len + callee_preserved_regs.len, | 466 | .end = gp_regs.len, |
| 421 | }, true); | 467 | }, true); |
| 422 | break :blk set; | 468 | break :blk set; |
| 423 | }; | 469 | }; |
| 424 | pub const sse: RegisterBitSet = blk: { | 470 | pub const sse: RegisterBitSet = blk: { |
| 425 | var set = RegisterBitSet.initEmpty(); | 471 | var set = RegisterBitSet.initEmpty(); |
| 426 | set.setRangeValue(.{ | 472 | set.setRangeValue(.{ |
| 427 | .start = caller_preserved_regs.len + callee_preserved_regs.len, | 473 | .start = gp_regs.len, |
| 428 | .end = allocatable_registers.len, | 474 | .end = allocatable_regs.len, |
| 429 | }, true); | 475 | }, true); |
| 430 | break :blk set; | 476 | break :blk set; |
| 431 | }; | 477 | }; |
src/codegen/llvm.zig+11-2| ... | @@ -3912,7 +3912,7 @@ pub const DeclGen = struct { | ... | @@ -3912,7 +3912,7 @@ pub const DeclGen = struct { |
| 3912 | var b: usize = 0; | 3912 | var b: usize = 0; |
| 3913 | for (parent_ty.structFields().values()[0..field_index]) |field| { | 3913 | for (parent_ty.structFields().values()[0..field_index]) |field| { |
| 3914 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; | 3914 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 3915 | b += field.ty.bitSize(target); | 3915 | b += @intCast(usize, field.ty.bitSize(target)); |
| 3916 | } | 3916 | } |
| 3917 | break :b b; | 3917 | break :b b; |
| 3918 | }; | 3918 | }; |
| ... | @@ -9385,6 +9385,12 @@ pub const FuncGen = struct { | ... | @@ -9385,6 +9385,12 @@ pub const FuncGen = struct { |
| 9385 | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); | 9385 | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); |
| 9386 | } | 9386 | } |
| 9387 | 9387 | ||
| 9388 | if (info.pointee_type.isPtrAtRuntime()) { | ||
| 9389 | const same_size_int = self.context.intType(elem_bits); | ||
| 9390 | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); | ||
| 9391 | return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); | ||
| 9392 | } | ||
| 9393 | |||
| 9388 | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); | 9394 | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); |
| 9389 | } | 9395 | } |
| 9390 | 9396 | ||
| ... | @@ -9416,7 +9422,10 @@ pub const FuncGen = struct { | ... | @@ -9416,7 +9422,10 @@ pub const FuncGen = struct { |
| 9416 | // Convert to equally-sized integer type in order to perform the bit | 9422 | // Convert to equally-sized integer type in order to perform the bit |
| 9417 | // operations on the value to store | 9423 | // operations on the value to store |
| 9418 | const value_bits_type = self.context.intType(elem_bits); | 9424 | const value_bits_type = self.context.intType(elem_bits); |
| 9419 | const value_bits = self.builder.buildBitCast(elem, value_bits_type, ""); | 9425 | const value_bits = if (elem_ty.isPtrAtRuntime()) |
| 9426 | self.builder.buildPtrToInt(elem, value_bits_type, "") | ||
| 9427 | else | ||
| 9428 | self.builder.buildBitCast(elem, value_bits_type, ""); | ||
| 9420 | 9429 | ||
| 9421 | var mask_val = value_bits_type.constAllOnes(); | 9430 | var mask_val = value_bits_type.constAllOnes(); |
| 9422 | mask_val = mask_val.constZExt(containing_int_ty); | 9431 | mask_val = mask_val.constZExt(containing_int_ty); |
src/link.zig+4-1| ... | @@ -166,6 +166,9 @@ pub const Options = struct { | ... | @@ -166,6 +166,9 @@ pub const Options = struct { |
| 166 | version_script: ?[]const u8, | 166 | version_script: ?[]const u8, |
| 167 | soname: ?[]const u8, | 167 | soname: ?[]const u8, |
| 168 | llvm_cpu_features: ?[*:0]const u8, | 168 | llvm_cpu_features: ?[*:0]const u8, |
| 169 | print_gc_sections: bool, | ||
| 170 | print_icf_sections: bool, | ||
| 171 | print_map: bool, | ||
| 169 | 172 | ||
| 170 | objects: []Compilation.LinkObject, | 173 | objects: []Compilation.LinkObject, |
| 171 | framework_dirs: []const []const u8, | 174 | framework_dirs: []const []const u8, |
| ... | @@ -476,7 +479,7 @@ pub const File = struct { | ... | @@ -476,7 +479,7 @@ pub const File = struct { |
| 476 | log.debug("getGlobalSymbol '{s}'", .{name}); | 479 | log.debug("getGlobalSymbol '{s}'", .{name}); |
| 477 | switch (base.tag) { | 480 | switch (base.tag) { |
| 478 | // zig fmt: off | 481 | // zig fmt: off |
| 479 | .coff => unreachable, | 482 | .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name), |
| 480 | .elf => unreachable, | 483 | .elf => unreachable, |
| 481 | .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name), | 484 | .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name), |
| 482 | .plan9 => unreachable, | 485 | .plan9 => unreachable, |
src/link/Coff.zig+674-221| ... | @@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig"); | ... | @@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig"); |
| 30 | pub const base_tag: link.File.Tag = .coff; | 30 | pub const base_tag: link.File.Tag = .coff; |
| 31 | 31 | ||
| 32 | const msdos_stub = @embedFile("msdos-stub.bin"); | 32 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 33 | const N_DATA_DIRS: u5 = 16; | ||
| 34 | 33 | ||
| 35 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 34 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 36 | llvm_object: ?*LlvmObject = null, | 35 | llvm_object: ?*LlvmObject = null, |
| ... | @@ -44,24 +43,33 @@ page_size: u32, | ... | @@ -44,24 +43,33 @@ page_size: u32, |
| 44 | objects: std.ArrayListUnmanaged(Object) = .{}, | 43 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 45 | 44 | ||
| 46 | sections: std.MultiArrayList(Section) = .{}, | 45 | sections: std.MultiArrayList(Section) = .{}, |
| 47 | data_directories: [N_DATA_DIRS]coff.ImageDataDirectory, | 46 | data_directories: [coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory, |
| 48 | 47 | ||
| 49 | text_section_index: ?u16 = null, | 48 | text_section_index: ?u16 = null, |
| 50 | got_section_index: ?u16 = null, | 49 | got_section_index: ?u16 = null, |
| 51 | rdata_section_index: ?u16 = null, | 50 | rdata_section_index: ?u16 = null, |
| 52 | data_section_index: ?u16 = null, | 51 | data_section_index: ?u16 = null, |
| 53 | reloc_section_index: ?u16 = null, | 52 | reloc_section_index: ?u16 = null, |
| 53 | idata_section_index: ?u16 = null, | ||
| 54 | 54 | ||
| 55 | locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, | 55 | locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, |
| 56 | globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, | 56 | globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, |
| 57 | resolver: std.StringHashMapUnmanaged(u32) = .{}, | ||
| 58 | unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{}, | ||
| 57 | 59 | ||
| 58 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, | 60 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 61 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 59 | 62 | ||
| 60 | strtab: StringTable(.strtab) = .{}, | 63 | strtab: StringTable(.strtab) = .{}, |
| 61 | strtab_offset: ?u32 = null, | 64 | strtab_offset: ?u32 = null, |
| 62 | 65 | ||
| 63 | got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | 66 | got_entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 64 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, | 67 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 68 | got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | ||
| 69 | |||
| 70 | imports: std.ArrayListUnmanaged(Entry) = .{}, | ||
| 71 | imports_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 72 | imports_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | ||
| 65 | 73 | ||
| 66 | /// Virtual address of the entry point procedure relative to image base. | 74 | /// Virtual address of the entry point procedure relative to image base. |
| 67 | entry_addr: ?u32 = null, | 75 | entry_addr: ?u32 = null, |
| ... | @@ -109,17 +117,33 @@ relocs: RelocTable = .{}, | ... | @@ -109,17 +117,33 @@ relocs: RelocTable = .{}, |
| 109 | /// this will be a table indexed by index into the list of Atoms. | 117 | /// this will be a table indexed by index into the list of Atoms. |
| 110 | base_relocs: BaseRelocationTable = .{}, | 118 | base_relocs: BaseRelocationTable = .{}, |
| 111 | 119 | ||
| 120 | const Entry = struct { | ||
| 121 | target: SymbolWithLoc, | ||
| 122 | // Index into the synthetic symbol table (i.e., file == null). | ||
| 123 | sym_index: u32, | ||
| 124 | }; | ||
| 125 | |||
| 112 | pub const Reloc = struct { | 126 | pub const Reloc = struct { |
| 113 | @"type": enum { | 127 | @"type": enum { |
| 114 | got, | 128 | got, |
| 115 | direct, | 129 | direct, |
| 130 | imports, | ||
| 116 | }, | 131 | }, |
| 117 | target: SymbolWithLoc, | 132 | target: SymbolWithLoc, |
| 118 | offset: u32, | 133 | offset: u32, |
| 119 | addend: u32, | 134 | addend: u32, |
| 120 | pcrel: bool, | 135 | pcrel: bool, |
| 121 | length: u2, | 136 | length: u2, |
| 122 | prev_vaddr: u32, | 137 | dirty: bool = true, |
| 138 | |||
| 139 | /// Returns an Atom which is the target node of this relocation edge (if any). | ||
| 140 | fn getTargetAtom(self: Reloc, coff_file: *Coff) ?*Atom { | ||
| 141 | switch (self.@"type") { | ||
| 142 | .got => return coff_file.getGotAtomForSymbol(self.target), | ||
| 143 | .direct => return coff_file.getAtomForSymbol(self.target), | ||
| 144 | .imports => return coff_file.getImportAtomForSymbol(self.target), | ||
| 145 | } | ||
| 146 | } | ||
| 123 | }; | 147 | }; |
| 124 | 148 | ||
| 125 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); | 149 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); |
| ... | @@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct { | ... | @@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct { |
| 180 | 204 | ||
| 181 | // null means it's a synthetic global or Zig source. | 205 | // null means it's a synthetic global or Zig source. |
| 182 | file: ?u32 = null, | 206 | file: ?u32 = null, |
| 207 | |||
| 208 | pub fn eql(this: SymbolWithLoc, other: SymbolWithLoc) bool { | ||
| 209 | if (this.file == null and other.file == null) { | ||
| 210 | return this.sym_index == other.sym_index; | ||
| 211 | } | ||
| 212 | if (this.file != null and other.file != null) { | ||
| 213 | return this.sym_index == other.sym_index and this.file.? == other.file.?; | ||
| 214 | } | ||
| 215 | return false; | ||
| 216 | } | ||
| 183 | }; | 217 | }; |
| 184 | 218 | ||
| 185 | /// When allocating, the ideal_capacity is calculated by | 219 | /// When allocating, the ideal_capacity is calculated by |
| ... | @@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { | ... | @@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { |
| 234 | }, | 268 | }, |
| 235 | .ptr_width = ptr_width, | 269 | .ptr_width = ptr_width, |
| 236 | .page_size = page_size, | 270 | .page_size = page_size, |
| 237 | .data_directories = comptime mem.zeroes([N_DATA_DIRS]coff.ImageDataDirectory), | 271 | .data_directories = comptime mem.zeroes([coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory), |
| 238 | }; | 272 | }; |
| 239 | 273 | ||
| 240 | const use_llvm = build_options.have_llvm and options.use_llvm; | 274 | const use_llvm = build_options.have_llvm and options.use_llvm; |
| ... | @@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void { | ... | @@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void { |
| 269 | 303 | ||
| 270 | self.locals.deinit(gpa); | 304 | self.locals.deinit(gpa); |
| 271 | self.globals.deinit(gpa); | 305 | self.globals.deinit(gpa); |
| 306 | |||
| 307 | { | ||
| 308 | var it = self.resolver.keyIterator(); | ||
| 309 | while (it.next()) |key_ptr| { | ||
| 310 | gpa.free(key_ptr.*); | ||
| 311 | } | ||
| 312 | self.resolver.deinit(gpa); | ||
| 313 | } | ||
| 314 | |||
| 315 | self.unresolved.deinit(gpa); | ||
| 272 | self.locals_free_list.deinit(gpa); | 316 | self.locals_free_list.deinit(gpa); |
| 273 | self.strtab.deinit(gpa); | 317 | self.strtab.deinit(gpa); |
| 274 | self.got_entries.deinit(gpa); | 318 | self.got_entries.deinit(gpa); |
| 275 | self.got_entries_free_list.deinit(gpa); | 319 | self.got_entries_free_list.deinit(gpa); |
| 320 | self.got_entries_table.deinit(gpa); | ||
| 321 | self.imports.deinit(gpa); | ||
| 322 | self.imports_free_list.deinit(gpa); | ||
| 323 | self.imports_table.deinit(gpa); | ||
| 276 | self.decls.deinit(gpa); | 324 | self.decls.deinit(gpa); |
| 277 | self.atom_by_index_table.deinit(gpa); | 325 | self.atom_by_index_table.deinit(gpa); |
| 278 | 326 | ||
| ... | @@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void { | ... | @@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 305 | assert(self.llvm_object == null); | 353 | assert(self.llvm_object == null); |
| 306 | const gpa = self.base.allocator; | 354 | const gpa = self.base.allocator; |
| 307 | 355 | ||
| 356 | try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); | ||
| 357 | self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); | ||
| 358 | |||
| 359 | // Index 0 is always a null symbol. | ||
| 360 | try self.locals.append(gpa, .{ | ||
| 361 | .name = [_]u8{0} ** 8, | ||
| 362 | .value = 0, | ||
| 363 | .section_number = .UNDEFINED, | ||
| 364 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | ||
| 365 | .storage_class = .NULL, | ||
| 366 | .number_of_aux_symbols = 0, | ||
| 367 | }); | ||
| 368 | |||
| 308 | if (self.text_section_index == null) { | 369 | if (self.text_section_index == null) { |
| 309 | self.text_section_index = @intCast(u16, self.sections.slice().len); | ||
| 310 | const file_size = @intCast(u32, self.base.options.program_code_size_hint); | 370 | const file_size = @intCast(u32, self.base.options.program_code_size_hint); |
| 311 | const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers | 371 | self.text_section_index = try self.allocateSection(".text", file_size, .{ |
| 312 | log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 372 | .CNT_CODE = 1, |
| 313 | var header = coff.SectionHeader{ | 373 | .MEM_EXECUTE = 1, |
| 314 | .name = undefined, | 374 | .MEM_READ = 1, |
| 315 | .virtual_size = file_size, | 375 | }); |
| 316 | .virtual_address = off, | ||
| 317 | .size_of_raw_data = file_size, | ||
| 318 | .pointer_to_raw_data = off, | ||
| 319 | .pointer_to_relocations = 0, | ||
| 320 | .pointer_to_linenumbers = 0, | ||
| 321 | .number_of_relocations = 0, | ||
| 322 | .number_of_linenumbers = 0, | ||
| 323 | .flags = .{ | ||
| 324 | .CNT_CODE = 1, | ||
| 325 | .MEM_EXECUTE = 1, | ||
| 326 | .MEM_READ = 1, | ||
| 327 | }, | ||
| 328 | }; | ||
| 329 | try self.setSectionName(&header, ".text"); | ||
| 330 | try self.sections.append(gpa, .{ .header = header }); | ||
| 331 | } | 376 | } |
| 332 | 377 | ||
| 333 | if (self.got_section_index == null) { | 378 | if (self.got_section_index == null) { |
| 334 | self.got_section_index = @intCast(u16, self.sections.slice().len); | ||
| 335 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); | 379 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); |
| 336 | const off = self.findFreeSpace(file_size, self.page_size); | 380 | self.got_section_index = try self.allocateSection(".got", file_size, .{ |
| 337 | log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 381 | .CNT_INITIALIZED_DATA = 1, |
| 338 | var header = coff.SectionHeader{ | 382 | .MEM_READ = 1, |
| 339 | .name = undefined, | 383 | }); |
| 340 | .virtual_size = file_size, | ||
| 341 | .virtual_address = off, | ||
| 342 | .size_of_raw_data = file_size, | ||
| 343 | .pointer_to_raw_data = off, | ||
| 344 | .pointer_to_relocations = 0, | ||
| 345 | .pointer_to_linenumbers = 0, | ||
| 346 | .number_of_relocations = 0, | ||
| 347 | .number_of_linenumbers = 0, | ||
| 348 | .flags = .{ | ||
| 349 | .CNT_INITIALIZED_DATA = 1, | ||
| 350 | .MEM_READ = 1, | ||
| 351 | }, | ||
| 352 | }; | ||
| 353 | try self.setSectionName(&header, ".got"); | ||
| 354 | try self.sections.append(gpa, .{ .header = header }); | ||
| 355 | } | 384 | } |
| 356 | 385 | ||
| 357 | if (self.rdata_section_index == null) { | 386 | if (self.rdata_section_index == null) { |
| 358 | self.rdata_section_index = @intCast(u16, self.sections.slice().len); | 387 | const file_size: u32 = self.page_size; |
| 359 | const file_size: u32 = 1024; | 388 | self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{ |
| 360 | const off = self.findFreeSpace(file_size, self.page_size); | 389 | .CNT_INITIALIZED_DATA = 1, |
| 361 | log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 390 | .MEM_READ = 1, |
| 362 | var header = coff.SectionHeader{ | 391 | }); |
| 363 | .name = undefined, | ||
| 364 | .virtual_size = file_size, | ||
| 365 | .virtual_address = off, | ||
| 366 | .size_of_raw_data = file_size, | ||
| 367 | .pointer_to_raw_data = off, | ||
| 368 | .pointer_to_relocations = 0, | ||
| 369 | .pointer_to_linenumbers = 0, | ||
| 370 | .number_of_relocations = 0, | ||
| 371 | .number_of_linenumbers = 0, | ||
| 372 | .flags = .{ | ||
| 373 | .CNT_INITIALIZED_DATA = 1, | ||
| 374 | .MEM_READ = 1, | ||
| 375 | }, | ||
| 376 | }; | ||
| 377 | try self.setSectionName(&header, ".rdata"); | ||
| 378 | try self.sections.append(gpa, .{ .header = header }); | ||
| 379 | } | 392 | } |
| 380 | 393 | ||
| 381 | if (self.data_section_index == null) { | 394 | if (self.data_section_index == null) { |
| 382 | self.data_section_index = @intCast(u16, self.sections.slice().len); | 395 | const file_size: u32 = self.page_size; |
| 383 | const file_size: u32 = 1024; | 396 | self.data_section_index = try self.allocateSection(".data", file_size, .{ |
| 384 | const off = self.findFreeSpace(file_size, self.page_size); | 397 | .CNT_INITIALIZED_DATA = 1, |
| 385 | log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 398 | .MEM_READ = 1, |
| 386 | var header = coff.SectionHeader{ | 399 | .MEM_WRITE = 1, |
| 387 | .name = undefined, | 400 | }); |
| 388 | .virtual_size = file_size, | 401 | } |
| 389 | .virtual_address = off, | 402 | |
| 390 | .size_of_raw_data = file_size, | 403 | if (self.idata_section_index == null) { |
| 391 | .pointer_to_raw_data = off, | 404 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); |
| 392 | .pointer_to_relocations = 0, | 405 | self.idata_section_index = try self.allocateSection(".idata", file_size, .{ |
| 393 | .pointer_to_linenumbers = 0, | 406 | .CNT_INITIALIZED_DATA = 1, |
| 394 | .number_of_relocations = 0, | 407 | .MEM_READ = 1, |
| 395 | .number_of_linenumbers = 0, | 408 | }); |
| 396 | .flags = .{ | ||
| 397 | .CNT_INITIALIZED_DATA = 1, | ||
| 398 | .MEM_READ = 1, | ||
| 399 | .MEM_WRITE = 1, | ||
| 400 | }, | ||
| 401 | }; | ||
| 402 | try self.setSectionName(&header, ".data"); | ||
| 403 | try self.sections.append(gpa, .{ .header = header }); | ||
| 404 | } | 409 | } |
| 405 | 410 | ||
| 406 | if (self.reloc_section_index == null) { | 411 | if (self.reloc_section_index == null) { |
| 407 | self.reloc_section_index = @intCast(u16, self.sections.slice().len); | ||
| 408 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation); | 412 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation); |
| 409 | const off = self.findFreeSpace(file_size, self.page_size); | 413 | self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{ |
| 410 | log.debug("found .reloc free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 414 | .CNT_INITIALIZED_DATA = 1, |
| 411 | var header = coff.SectionHeader{ | 415 | .MEM_DISCARDABLE = 1, |
| 412 | .name = undefined, | 416 | .MEM_READ = 1, |
| 413 | .virtual_size = file_size, | 417 | }); |
| 414 | .virtual_address = off, | ||
| 415 | .size_of_raw_data = file_size, | ||
| 416 | .pointer_to_raw_data = off, | ||
| 417 | .pointer_to_relocations = 0, | ||
| 418 | .pointer_to_linenumbers = 0, | ||
| 419 | .number_of_relocations = 0, | ||
| 420 | .number_of_linenumbers = 0, | ||
| 421 | .flags = .{ | ||
| 422 | .CNT_INITIALIZED_DATA = 1, | ||
| 423 | .MEM_PURGEABLE = 1, | ||
| 424 | .MEM_READ = 1, | ||
| 425 | }, | ||
| 426 | }; | ||
| 427 | try self.setSectionName(&header, ".reloc"); | ||
| 428 | try self.sections.append(gpa, .{ .header = header }); | ||
| 429 | } | 418 | } |
| 430 | 419 | ||
| 431 | if (self.strtab_offset == null) { | 420 | if (self.strtab_offset == null) { |
| 432 | try self.strtab.buffer.append(gpa, 0); | 421 | const file_size = @intCast(u32, self.strtab.len()); |
| 433 | self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1); | 422 | self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here |
| 434 | log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() }); | 423 | log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size }); |
| 435 | } | 424 | } |
| 436 | 425 | ||
| 437 | // Index 0 is always a null symbol. | ||
| 438 | try self.locals.append(gpa, .{ | ||
| 439 | .name = [_]u8{0} ** 8, | ||
| 440 | .value = 0, | ||
| 441 | .section_number = @intToEnum(coff.SectionNumber, 0), | ||
| 442 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | ||
| 443 | .storage_class = .NULL, | ||
| 444 | .number_of_aux_symbols = 0, | ||
| 445 | }); | ||
| 446 | |||
| 447 | { | 426 | { |
| 448 | // We need to find out what the max file offset is according to section headers. | 427 | // We need to find out what the max file offset is according to section headers. |
| 449 | // Otherwise, we may end up with an COFF binary with file size not matching the final section's | 428 | // Otherwise, we may end up with an COFF binary with file size not matching the final section's |
| ... | @@ -459,6 +438,72 @@ fn populateMissingMetadata(self: *Coff) !void { | ... | @@ -459,6 +438,72 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 459 | } | 438 | } |
| 460 | } | 439 | } |
| 461 | 440 | ||
| 441 | fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.SectionHeaderFlags) !u16 { | ||
| 442 | const index = @intCast(u16, self.sections.slice().len); | ||
| 443 | const off = self.findFreeSpace(size, default_file_alignment); | ||
| 444 | // Memory is always allocated in sequence | ||
| 445 | // TODO: investigate if we can allocate .text last; this way it would never need to grow in memory! | ||
| 446 | const vaddr = blk: { | ||
| 447 | if (index == 0) break :blk self.page_size; | ||
| 448 | const prev_header = self.sections.items(.header)[index - 1]; | ||
| 449 | break :blk mem.alignForwardGeneric(u32, prev_header.virtual_address + prev_header.virtual_size, self.page_size); | ||
| 450 | }; | ||
| 451 | // We commit more memory than needed upfront so that we don't have to reallocate too soon. | ||
| 452 | const memsz = mem.alignForwardGeneric(u32, size, self.page_size) * 100; | ||
| 453 | log.debug("found {s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ | ||
| 454 | name, | ||
| 455 | off, | ||
| 456 | off + size, | ||
| 457 | vaddr, | ||
| 458 | vaddr + size, | ||
| 459 | }); | ||
| 460 | var header = coff.SectionHeader{ | ||
| 461 | .name = undefined, | ||
| 462 | .virtual_size = memsz, | ||
| 463 | .virtual_address = vaddr, | ||
| 464 | .size_of_raw_data = size, | ||
| 465 | .pointer_to_raw_data = off, | ||
| 466 | .pointer_to_relocations = 0, | ||
| 467 | .pointer_to_linenumbers = 0, | ||
| 468 | .number_of_relocations = 0, | ||
| 469 | .number_of_linenumbers = 0, | ||
| 470 | .flags = flags, | ||
| 471 | }; | ||
| 472 | try self.setSectionName(&header, name); | ||
| 473 | try self.sections.append(self.base.allocator, .{ .header = header }); | ||
| 474 | return index; | ||
| 475 | } | ||
| 476 | |||
| 477 | fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { | ||
| 478 | const header = &self.sections.items(.header)[sect_id]; | ||
| 479 | const increased_size = padToIdeal(needed_size); | ||
| 480 | const old_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, header.virtual_size, self.page_size); | ||
| 481 | const new_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, increased_size, self.page_size); | ||
| 482 | const diff = new_aligned_end - old_aligned_end; | ||
| 483 | log.debug("growing {s} in virtual memory by {x}", .{ self.getSectionName(header), diff }); | ||
| 484 | |||
| 485 | // TODO: enforce order by increasing VM addresses in self.sections container. | ||
| 486 | // This is required by the loader anyhow as far as I can tell. | ||
| 487 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { | ||
| 488 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id + 1 + next_sect_id]; | ||
| 489 | next_header.virtual_address += diff; | ||
| 490 | |||
| 491 | if (maybe_last_atom.*) |last_atom| { | ||
| 492 | var atom = last_atom; | ||
| 493 | while (true) { | ||
| 494 | const sym = atom.getSymbolPtr(self); | ||
| 495 | sym.value += diff; | ||
| 496 | |||
| 497 | if (atom.prev) |prev| { | ||
| 498 | atom = prev; | ||
| 499 | } else break; | ||
| 500 | } | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | header.virtual_size = increased_size; | ||
| 505 | } | ||
| 506 | |||
| 462 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { | 507 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { |
| 463 | if (self.llvm_object) |_| return; | 508 | if (self.llvm_object) |_| return; |
| 464 | const decl = self.base.options.module.?.declPtr(decl_index); | 509 | const decl = self.base.options.module.?.declPtr(decl_index); |
| ... | @@ -542,16 +587,33 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u | ... | @@ -542,16 +587,33 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 542 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); | 587 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 543 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; | 588 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; |
| 544 | if (needed_size > sect_capacity) { | 589 | if (needed_size > sect_capacity) { |
| 545 | @panic("TODO move section"); | 590 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); |
| 591 | const current_size = if (maybe_last_atom.*) |last_atom| blk: { | ||
| 592 | const sym = last_atom.getSymbol(self); | ||
| 593 | break :blk (sym.value + last_atom.size) - header.virtual_address; | ||
| 594 | } else 0; | ||
| 595 | log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset }); | ||
| 596 | const amt = try self.base.file.?.copyRangeAll( | ||
| 597 | header.pointer_to_raw_data, | ||
| 598 | self.base.file.?, | ||
| 599 | new_offset, | ||
| 600 | current_size, | ||
| 601 | ); | ||
| 602 | if (amt != current_size) return error.InputOutput; | ||
| 603 | header.pointer_to_raw_data = new_offset; | ||
| 604 | } | ||
| 605 | |||
| 606 | const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); | ||
| 607 | if (needed_size > sect_vm_capacity) { | ||
| 608 | try self.growSectionVM(sect_id, needed_size); | ||
| 609 | self.markRelocsDirtyByAddress(header.virtual_address + needed_size); | ||
| 546 | } | 610 | } |
| 611 | |||
| 612 | header.virtual_size = @maximum(header.virtual_size, needed_size); | ||
| 613 | header.size_of_raw_data = needed_size; | ||
| 547 | maybe_last_atom.* = atom; | 614 | maybe_last_atom.* = atom; |
| 548 | // header.virtual_size = needed_size; | ||
| 549 | // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment); | ||
| 550 | } | 615 | } |
| 551 | 616 | ||
| 552 | // if (header.getAlignment().? < alignment) { | ||
| 553 | // header.setAlignment(alignment); | ||
| 554 | // } | ||
| 555 | atom.size = new_atom_size; | 617 | atom.size = new_atom_size; |
| 556 | atom.alignment = alignment; | 618 | atom.alignment = alignment; |
| 557 | 619 | ||
| ... | @@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 { | ... | @@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 { |
| 596 | self.locals.items[index] = .{ | 658 | self.locals.items[index] = .{ |
| 597 | .name = [_]u8{0} ** 8, | 659 | .name = [_]u8{0} ** 8, |
| 598 | .value = 0, | 660 | .value = 0, |
| 599 | .section_number = @intToEnum(coff.SectionNumber, 0), | 661 | .section_number = .UNDEFINED, |
| 600 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | 662 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, |
| 601 | .storage_class = .NULL, | 663 | .storage_class = .NULL, |
| 602 | .number_of_aux_symbols = 0, | 664 | .number_of_aux_symbols = 0, |
| ... | @@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 { | ... | @@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 { |
| 605 | return index; | 667 | return index; |
| 606 | } | 668 | } |
| 607 | 669 | ||
| 670 | fn allocateGlobal(self: *Coff) !u32 { | ||
| 671 | const gpa = self.base.allocator; | ||
| 672 | try self.globals.ensureUnusedCapacity(gpa, 1); | ||
| 673 | |||
| 674 | const index = blk: { | ||
| 675 | if (self.globals_free_list.popOrNull()) |index| { | ||
| 676 | log.debug(" (reusing global index {d})", .{index}); | ||
| 677 | break :blk index; | ||
| 678 | } else { | ||
| 679 | log.debug(" (allocating global index {d})", .{self.globals.items.len}); | ||
| 680 | const index = @intCast(u32, self.globals.items.len); | ||
| 681 | _ = self.globals.addOneAssumeCapacity(); | ||
| 682 | break :blk index; | ||
| 683 | } | ||
| 684 | }; | ||
| 685 | |||
| 686 | self.globals.items[index] = .{ | ||
| 687 | .sym_index = 0, | ||
| 688 | .file = null, | ||
| 689 | }; | ||
| 690 | |||
| 691 | return index; | ||
| 692 | } | ||
| 693 | |||
| 608 | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { | 694 | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 609 | const gpa = self.base.allocator; | 695 | const gpa = self.base.allocator; |
| 610 | try self.got_entries.ensureUnusedCapacity(gpa, 1); | 696 | try self.got_entries.ensureUnusedCapacity(gpa, 1); |
| 697 | |||
| 611 | const index: u32 = blk: { | 698 | const index: u32 = blk: { |
| 612 | if (self.got_entries_free_list.popOrNull()) |index| { | 699 | if (self.got_entries_free_list.popOrNull()) |index| { |
| 613 | log.debug(" (reusing GOT entry index {d})", .{index}); | 700 | log.debug(" (reusing GOT entry index {d})", .{index}); |
| 614 | if (self.got_entries.getIndex(target)) |existing| { | ||
| 615 | assert(existing == index); | ||
| 616 | } | ||
| 617 | break :blk index; | 701 | break :blk index; |
| 618 | } else { | 702 | } else { |
| 619 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len}); | 703 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); |
| 620 | const index = @intCast(u32, self.got_entries.keys().len); | 704 | const index = @intCast(u32, self.got_entries.items.len); |
| 621 | self.got_entries.putAssumeCapacityNoClobber(target, 0); | 705 | _ = self.got_entries.addOneAssumeCapacity(); |
| 622 | break :blk index; | 706 | break :blk index; |
| 623 | } | 707 | } |
| 624 | }; | 708 | }; |
| 625 | self.got_entries.keys()[index] = target; | 709 | |
| 710 | self.got_entries.items[index] = .{ .target = target, .sym_index = 0 }; | ||
| 711 | try self.got_entries_table.putNoClobber(gpa, target, index); | ||
| 712 | |||
| 713 | return index; | ||
| 714 | } | ||
| 715 | |||
| 716 | pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 { | ||
| 717 | const gpa = self.base.allocator; | ||
| 718 | try self.imports.ensureUnusedCapacity(gpa, 1); | ||
| 719 | |||
| 720 | const index: u32 = blk: { | ||
| 721 | if (self.imports_free_list.popOrNull()) |index| { | ||
| 722 | log.debug(" (reusing import entry index {d})", .{index}); | ||
| 723 | break :blk index; | ||
| 724 | } else { | ||
| 725 | log.debug(" (allocating import entry at index {d})", .{self.imports.items.len}); | ||
| 726 | const index = @intCast(u32, self.imports.items.len); | ||
| 727 | _ = self.imports.addOneAssumeCapacity(); | ||
| 728 | break :blk index; | ||
| 729 | } | ||
| 730 | }; | ||
| 731 | |||
| 732 | self.imports.items[index] = .{ .target = target, .sym_index = 0 }; | ||
| 733 | try self.imports_table.putNoClobber(gpa, target, index); | ||
| 734 | |||
| 626 | return index; | 735 | return index; |
| 627 | } | 736 | } |
| 628 | 737 | ||
| ... | @@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | ... | @@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 637 | 746 | ||
| 638 | try self.managed_atoms.append(gpa, atom); | 747 | try self.managed_atoms.append(gpa, atom); |
| 639 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | 748 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); |
| 640 | self.got_entries.getPtr(target).?.* = atom.sym_index; | ||
| 641 | 749 | ||
| 642 | const sym = atom.getSymbolPtr(self); | 750 | const sym = atom.getSymbolPtr(self); |
| 643 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); | 751 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); |
| ... | @@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | ... | @@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 652 | .addend = 0, | 760 | .addend = 0, |
| 653 | .pcrel = false, | 761 | .pcrel = false, |
| 654 | .length = 3, | 762 | .length = 3, |
| 655 | .prev_vaddr = sym.value, | ||
| 656 | }); | 763 | }); |
| 657 | 764 | ||
| 658 | const target_sym = self.getSymbol(target); | 765 | const target_sym = self.getSymbol(target); |
| ... | @@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | ... | @@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 666 | return atom; | 773 | return atom; |
| 667 | } | 774 | } |
| 668 | 775 | ||
| 776 | fn createImportAtom(self: *Coff) !*Atom { | ||
| 777 | const gpa = self.base.allocator; | ||
| 778 | const atom = try gpa.create(Atom); | ||
| 779 | errdefer gpa.destroy(atom); | ||
| 780 | atom.* = Atom.empty; | ||
| 781 | atom.sym_index = try self.allocateSymbol(); | ||
| 782 | atom.size = @sizeOf(u64); | ||
| 783 | atom.alignment = @alignOf(u64); | ||
| 784 | |||
| 785 | try self.managed_atoms.append(gpa, atom); | ||
| 786 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | ||
| 787 | |||
| 788 | const sym = atom.getSymbolPtr(self); | ||
| 789 | sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1); | ||
| 790 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | ||
| 791 | |||
| 792 | log.debug("allocated import atom at 0x{x}", .{sym.value}); | ||
| 793 | |||
| 794 | return atom; | ||
| 795 | } | ||
| 796 | |||
| 669 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { | 797 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { |
| 670 | const sym = atom.getSymbol(self); | 798 | const sym = atom.getSymbol(self); |
| 671 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; | 799 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; |
| ... | @@ -686,12 +814,12 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void { | ... | @@ -686,12 +814,12 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void { |
| 686 | const sym = atom.getSymbol(self); | 814 | const sym = atom.getSymbol(self); |
| 687 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); | 815 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); |
| 688 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; | 816 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; |
| 689 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); | 817 | log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ atom.getName(self), file_offset, file_offset + code.len }); |
| 690 | try self.base.file.?.pwriteAll(code, file_offset); | 818 | try self.base.file.?.pwriteAll(code, file_offset); |
| 691 | try self.resolveRelocs(atom); | 819 | try self.resolveRelocs(atom); |
| 692 | } | 820 | } |
| 693 | 821 | ||
| 694 | fn writeGotAtom(self: *Coff, atom: *Atom) !void { | 822 | fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void { |
| 695 | switch (self.ptr_width) { | 823 | switch (self.ptr_width) { |
| 696 | .p32 => { | 824 | .p32 => { |
| 697 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); | 825 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); |
| ... | @@ -704,6 +832,29 @@ fn writeGotAtom(self: *Coff, atom: *Atom) !void { | ... | @@ -704,6 +832,29 @@ fn writeGotAtom(self: *Coff, atom: *Atom) !void { |
| 704 | } | 832 | } |
| 705 | } | 833 | } |
| 706 | 834 | ||
| 835 | fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { | ||
| 836 | // TODO: reverse-lookup might come in handy here | ||
| 837 | var it = self.relocs.valueIterator(); | ||
| 838 | while (it.next()) |relocs| { | ||
| 839 | for (relocs.items) |*reloc| { | ||
| 840 | if (!reloc.target.eql(target)) continue; | ||
| 841 | reloc.dirty = true; | ||
| 842 | } | ||
| 843 | } | ||
| 844 | } | ||
| 845 | |||
| 846 | fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { | ||
| 847 | var it = self.relocs.valueIterator(); | ||
| 848 | while (it.next()) |relocs| { | ||
| 849 | for (relocs.items) |*reloc| { | ||
| 850 | const target_atom = reloc.getTargetAtom(self) orelse continue; | ||
| 851 | const target_sym = target_atom.getSymbol(self); | ||
| 852 | if (target_sym.value < addr) continue; | ||
| 853 | reloc.dirty = true; | ||
| 854 | } | ||
| 855 | } | ||
| 856 | } | ||
| 857 | |||
| 707 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { | 858 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 708 | const relocs = self.relocs.get(atom) orelse return; | 859 | const relocs = self.relocs.get(atom) orelse return; |
| 709 | const source_sym = atom.getSymbol(self); | 860 | const source_sym = atom.getSymbol(self); |
| ... | @@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { | ... | @@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 713 | log.debug("relocating '{s}'", .{atom.getName(self)}); | 864 | log.debug("relocating '{s}'", .{atom.getName(self)}); |
| 714 | 865 | ||
| 715 | for (relocs.items) |*reloc| { | 866 | for (relocs.items) |*reloc| { |
| 716 | const target_vaddr = switch (reloc.@"type") { | 867 | if (!reloc.dirty) continue; |
| 717 | .got => blk: { | ||
| 718 | const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue; | ||
| 719 | break :blk got_atom.getSymbol(self).value; | ||
| 720 | }, | ||
| 721 | .direct => self.getSymbol(reloc.target).value, | ||
| 722 | }; | ||
| 723 | const target_vaddr_with_addend = target_vaddr + reloc.addend; | ||
| 724 | 868 | ||
| 725 | if (target_vaddr_with_addend == reloc.prev_vaddr) continue; | 869 | const target_atom = reloc.getTargetAtom(self) orelse continue; |
| 870 | const target_vaddr = target_atom.getSymbol(self).value; | ||
| 871 | const target_vaddr_with_addend = target_vaddr + reloc.addend; | ||
| 726 | 872 | ||
| 727 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ | 873 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{ |
| 728 | reloc.offset, | 874 | source_sym.value + reloc.offset, |
| 729 | target_vaddr_with_addend, | 875 | target_vaddr_with_addend, |
| 730 | self.getSymbolName(reloc.target), | 876 | self.getSymbolName(reloc.target), |
| 731 | @tagName(reloc.@"type"), | 877 | @tagName(reloc.@"type"), |
| 878 | file_offset + reloc.offset, | ||
| 732 | }); | 879 | }); |
| 733 | 880 | ||
| 881 | reloc.dirty = false; | ||
| 882 | |||
| 734 | if (reloc.pcrel) { | 883 | if (reloc.pcrel) { |
| 735 | const source_vaddr = source_sym.value + reloc.offset; | 884 | const source_vaddr = source_sym.value + reloc.offset; |
| 736 | const disp = target_vaddr_with_addend - source_vaddr - 4; | 885 | const disp = |
| 737 | try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset); | 886 | @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 738 | return; | 887 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 888 | continue; | ||
| 739 | } | 889 | } |
| 740 | 890 | ||
| 741 | switch (self.ptr_width) { | 891 | switch (self.ptr_width) { |
| ... | @@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { | ... | @@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 755 | else => unreachable, | 905 | else => unreachable, |
| 756 | }, | 906 | }, |
| 757 | } | 907 | } |
| 758 | |||
| 759 | reloc.prev_vaddr = target_vaddr_with_addend; | ||
| 760 | } | 908 | } |
| 761 | } | 909 | } |
| 762 | 910 | ||
| 763 | fn freeAtom(self: *Coff, atom: *Atom) void { | 911 | fn freeAtom(self: *Coff, atom: *Atom) void { |
| 764 | log.debug("freeAtom {*}", .{atom}); | 912 | log.debug("freeAtom {*}", .{atom}); |
| 765 | 913 | ||
| 914 | // Remove any relocs and base relocs associated with this Atom | ||
| 915 | self.freeRelocationsForAtom(atom); | ||
| 916 | |||
| 766 | const sym = atom.getSymbol(self); | 917 | const sym = atom.getSymbol(self); |
| 767 | const sect_id = @enumToInt(sym.section_number) - 1; | 918 | const sect_id = @enumToInt(sym.section_number) - 1; |
| 768 | const free_list = &self.sections.items(.free_list)[sect_id]; | 919 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| ... | @@ -825,11 +976,14 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live | ... | @@ -825,11 +976,14 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 825 | const tracy = trace(@src()); | 976 | const tracy = trace(@src()); |
| 826 | defer tracy.end(); | 977 | defer tracy.end(); |
| 827 | 978 | ||
| 979 | const decl_index = func.owner_decl; | ||
| 980 | const decl = module.declPtr(decl_index); | ||
| 981 | self.freeUnnamedConsts(decl_index); | ||
| 982 | self.freeRelocationsForAtom(&decl.link.coff); | ||
| 983 | |||
| 828 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 984 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 829 | defer code_buffer.deinit(); | 985 | defer code_buffer.deinit(); |
| 830 | 986 | ||
| 831 | const decl_index = func.owner_decl; | ||
| 832 | const decl = module.declPtr(decl_index); | ||
| 833 | const res = try codegen.generateFunction( | 987 | const res = try codegen.generateFunction( |
| 834 | &self.base, | 988 | &self.base, |
| 835 | decl.srcLoc(), | 989 | decl.srcLoc(), |
| ... | @@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live | ... | @@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 856 | } | 1010 | } |
| 857 | 1011 | ||
| 858 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { | 1012 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| 859 | _ = self; | 1013 | const gpa = self.base.allocator; |
| 860 | _ = tv; | 1014 | var code_buffer = std.ArrayList(u8).init(gpa); |
| 861 | _ = decl_index; | 1015 | defer code_buffer.deinit(); |
| 862 | @panic("TODO lowerUnnamedConst"); | 1016 | |
| 1017 | const mod = self.base.options.module.?; | ||
| 1018 | const decl = mod.declPtr(decl_index); | ||
| 1019 | |||
| 1020 | const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); | ||
| 1021 | if (!gop.found_existing) { | ||
| 1022 | gop.value_ptr.* = .{}; | ||
| 1023 | } | ||
| 1024 | const unnamed_consts = gop.value_ptr; | ||
| 1025 | |||
| 1026 | const atom = try gpa.create(Atom); | ||
| 1027 | errdefer gpa.destroy(atom); | ||
| 1028 | atom.* = Atom.empty; | ||
| 1029 | |||
| 1030 | atom.sym_index = try self.allocateSymbol(); | ||
| 1031 | const sym = atom.getSymbolPtr(self); | ||
| 1032 | const sym_name = blk: { | ||
| 1033 | const decl_name = try decl.getFullyQualifiedName(mod); | ||
| 1034 | defer gpa.free(decl_name); | ||
| 1035 | |||
| 1036 | const index = unnamed_consts.items.len; | ||
| 1037 | break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); | ||
| 1038 | }; | ||
| 1039 | defer gpa.free(sym_name); | ||
| 1040 | try self.setSymbolName(sym, sym_name); | ||
| 1041 | sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1); | ||
| 1042 | |||
| 1043 | try self.managed_atoms.append(gpa, atom); | ||
| 1044 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | ||
| 1045 | |||
| 1046 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{ | ||
| 1047 | .parent_atom_index = atom.sym_index, | ||
| 1048 | }); | ||
| 1049 | const code = switch (res) { | ||
| 1050 | .externally_managed => |x| x, | ||
| 1051 | .appended => code_buffer.items, | ||
| 1052 | .fail => |em| { | ||
| 1053 | decl.analysis = .codegen_failure; | ||
| 1054 | try mod.failed_decls.put(mod.gpa, decl_index, em); | ||
| 1055 | log.err("{s}", .{em.msg}); | ||
| 1056 | return error.AnalysisFail; | ||
| 1057 | }, | ||
| 1058 | }; | ||
| 1059 | |||
| 1060 | const required_alignment = tv.ty.abiAlignment(self.base.options.target); | ||
| 1061 | atom.alignment = required_alignment; | ||
| 1062 | atom.size = @intCast(u32, code.len); | ||
| 1063 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | ||
| 1064 | errdefer self.freeAtom(atom); | ||
| 1065 | |||
| 1066 | try unnamed_consts.append(gpa, atom); | ||
| 1067 | |||
| 1068 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value }); | ||
| 1069 | log.debug(" (required alignment 0x{x})", .{required_alignment}); | ||
| 1070 | |||
| 1071 | try self.writeAtom(atom, code); | ||
| 1072 | |||
| 1073 | return atom.sym_index; | ||
| 863 | } | 1074 | } |
| 864 | 1075 | ||
| 865 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { | 1076 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | @@ -884,6 +1095,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! | ... | @@ -884,6 +1095,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 884 | } | 1095 | } |
| 885 | } | 1096 | } |
| 886 | 1097 | ||
| 1098 | self.freeRelocationsForAtom(&decl.link.coff); | ||
| 1099 | |||
| 887 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 1100 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 888 | defer code_buffer.deinit(); | 1101 | defer code_buffer.deinit(); |
| 889 | 1102 | ||
| ... | @@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! | ... | @@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 892 | .ty = decl.ty, | 1105 | .ty = decl.ty, |
| 893 | .val = decl_val, | 1106 | .val = decl_val, |
| 894 | }, &code_buffer, .none, .{ | 1107 | }, &code_buffer, .none, .{ |
| 895 | .parent_atom_index = 0, | 1108 | .parent_atom_index = decl.link.coff.sym_index, |
| 896 | }); | 1109 | }); |
| 897 | const code = switch (res) { | 1110 | const code = switch (res) { |
| 898 | .externally_managed => |x| x, | 1111 | .externally_managed => |x| x, |
| ... | @@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, | ... | @@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 970 | if (vaddr != sym.value) { | 1183 | if (vaddr != sym.value) { |
| 971 | sym.value = vaddr; | 1184 | sym.value = vaddr; |
| 972 | log.debug(" (updating GOT entry)", .{}); | 1185 | log.debug(" (updating GOT entry)", .{}); |
| 973 | const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?; | 1186 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; |
| 974 | try self.writeGotAtom(got_atom); | 1187 | const got_atom = self.getGotAtomForSymbol(got_target).?; |
| 1188 | self.markRelocsDirtyByTarget(got_target); | ||
| 1189 | try self.writePtrWidthAtom(got_atom); | ||
| 975 | } | 1190 | } |
| 976 | } else if (code_len < atom.size) { | 1191 | } else if (code_len < atom.size) { |
| 977 | self.shrinkAtom(atom, code_len); | 1192 | self.shrinkAtom(atom, code_len); |
| ... | @@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, | ... | @@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 990 | sym.value = vaddr; | 1205 | sym.value = vaddr; |
| 991 | 1206 | ||
| 992 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | 1207 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; |
| 993 | _ = try self.allocateGotEntry(got_target); | 1208 | const got_index = try self.allocateGotEntry(got_target); |
| 994 | const got_atom = try self.createGotAtom(got_target); | 1209 | const got_atom = try self.createGotAtom(got_target); |
| 995 | try self.writeGotAtom(got_atom); | 1210 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; |
| 1211 | try self.writePtrWidthAtom(got_atom); | ||
| 996 | } | 1212 | } |
| 997 | 1213 | ||
| 1214 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); | ||
| 998 | try self.writeAtom(atom, code); | 1215 | try self.writeAtom(atom, code); |
| 999 | } | 1216 | } |
| 1000 | 1217 | ||
| 1218 | fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void { | ||
| 1219 | _ = self.relocs.remove(atom); | ||
| 1220 | _ = self.base_relocs.remove(atom); | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void { | ||
| 1224 | const gpa = self.base.allocator; | ||
| 1225 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; | ||
| 1226 | for (unnamed_consts.items) |atom| { | ||
| 1227 | self.freeAtom(atom); | ||
| 1228 | self.locals_free_list.append(gpa, atom.sym_index) catch {}; | ||
| 1229 | self.locals.items[atom.sym_index].section_number = .UNDEFINED; | ||
| 1230 | _ = self.atom_by_index_table.remove(atom.sym_index); | ||
| 1231 | log.debug(" adding local symbol index {d} to free list", .{atom.sym_index}); | ||
| 1232 | atom.sym_index = 0; | ||
| 1233 | } | ||
| 1234 | unnamed_consts.clearAndFree(gpa); | ||
| 1235 | } | ||
| 1236 | |||
| 1001 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | 1237 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1002 | if (build_options.have_llvm) { | 1238 | if (build_options.have_llvm) { |
| 1003 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); | 1239 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); |
| ... | @@ -1011,6 +1247,7 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | ... | @@ -1011,6 +1247,7 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1011 | const kv = self.decls.fetchRemove(decl_index); | 1247 | const kv = self.decls.fetchRemove(decl_index); |
| 1012 | if (kv.?.value) |_| { | 1248 | if (kv.?.value) |_| { |
| 1013 | self.freeAtom(&decl.link.coff); | 1249 | self.freeAtom(&decl.link.coff); |
| 1250 | self.freeUnnamedConsts(decl_index); | ||
| 1014 | } | 1251 | } |
| 1015 | 1252 | ||
| 1016 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | 1253 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| ... | @@ -1021,14 +1258,20 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | ... | @@ -1021,14 +1258,20 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1021 | 1258 | ||
| 1022 | // Try freeing GOT atom if this decl had one | 1259 | // Try freeing GOT atom if this decl had one |
| 1023 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | 1260 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| 1024 | if (self.got_entries.getIndex(got_target)) |got_index| { | 1261 | if (self.got_entries_table.get(got_target)) |got_index| { |
| 1025 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; | 1262 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; |
| 1026 | self.got_entries.values()[got_index] = 0; | 1263 | self.got_entries.items[got_index] = .{ |
| 1264 | .target = .{ .sym_index = 0, .file = null }, | ||
| 1265 | .sym_index = 0, | ||
| 1266 | }; | ||
| 1267 | _ = self.got_entries_table.remove(got_target); | ||
| 1268 | |||
| 1027 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); | 1269 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); |
| 1028 | } | 1270 | } |
| 1029 | 1271 | ||
| 1030 | self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0); | 1272 | self.locals.items[sym_index].section_number = .UNDEFINED; |
| 1031 | _ = self.atom_by_index_table.remove(sym_index); | 1273 | _ = self.atom_by_index_table.remove(sym_index); |
| 1274 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | ||
| 1032 | decl.link.coff.sym_index = 0; | 1275 | decl.link.coff.sym_index = 0; |
| 1033 | } | 1276 | } |
| 1034 | } | 1277 | } |
| ... | @@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void { | ... | @@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1154 | const sym = self.getSymbolPtr(sym_loc); | 1397 | const sym = self.getSymbolPtr(sym_loc); |
| 1155 | const sym_name = self.getSymbolName(sym_loc); | 1398 | const sym_name = self.getSymbolName(sym_loc); |
| 1156 | log.debug("deleting export '{s}'", .{sym_name}); | 1399 | log.debug("deleting export '{s}'", .{sym_name}); |
| 1157 | assert(sym.storage_class == .EXTERNAL); | 1400 | assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED); |
| 1158 | sym.* = .{ | 1401 | sym.* = .{ |
| 1159 | .name = [_]u8{0} ** 8, | 1402 | .name = [_]u8{0} ** 8, |
| 1160 | .value = 0, | 1403 | .value = 0, |
| 1161 | .section_number = @intToEnum(coff.SectionNumber, 0), | 1404 | .section_number = .UNDEFINED, |
| 1162 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | 1405 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, |
| 1163 | .storage_class = .NULL, | 1406 | .storage_class = .NULL, |
| 1164 | .number_of_aux_symbols = 0, | 1407 | .number_of_aux_symbols = 0, |
| 1165 | }; | 1408 | }; |
| 1166 | self.locals_free_list.append(gpa, sym_index) catch {}; | 1409 | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 1167 | 1410 | ||
| 1168 | if (self.globals.get(sym_name)) |global| blk: { | 1411 | if (self.resolver.fetchRemove(sym_name)) |entry| { |
| 1169 | if (global.sym_index != sym_index) break :blk; | 1412 | defer gpa.free(entry.key); |
| 1170 | if (global.file != null) break :blk; | 1413 | self.globals_free_list.append(gpa, entry.value) catch {}; |
| 1171 | const kv = self.globals.fetchSwapRemove(sym_name); | 1414 | self.globals.items[entry.value] = .{ |
| 1172 | gpa.free(kv.?.key); | 1415 | .sym_index = 0, |
| 1416 | .file = null, | ||
| 1417 | }; | ||
| 1173 | } | 1418 | } |
| 1174 | } | 1419 | } |
| 1175 | 1420 | ||
| 1176 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { | 1421 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { |
| 1177 | const gpa = self.base.allocator; | 1422 | const gpa = self.base.allocator; |
| 1178 | const sym = self.getSymbol(current); | 1423 | const sym = self.getSymbol(current); |
| 1179 | _ = sym; | ||
| 1180 | const sym_name = self.getSymbolName(current); | 1424 | const sym_name = self.getSymbolName(current); |
| 1181 | 1425 | ||
| 1182 | const name = try gpa.dupe(u8, sym_name); | 1426 | const global_index = self.resolver.get(sym_name) orelse { |
| 1183 | const global_index = @intCast(u32, self.globals.values().len); | 1427 | const name = try gpa.dupe(u8, sym_name); |
| 1184 | _ = global_index; | 1428 | const global_index = try self.allocateGlobal(); |
| 1185 | const gop = try self.globals.getOrPut(gpa, name); | 1429 | self.globals.items[global_index] = current; |
| 1186 | defer if (gop.found_existing) gpa.free(name); | 1430 | try self.resolver.putNoClobber(gpa, name, global_index); |
| 1187 | 1431 | if (sym.section_number == .UNDEFINED) { | |
| 1188 | if (!gop.found_existing) { | 1432 | try self.unresolved.putNoClobber(gpa, global_index, false); |
| 1189 | gop.value_ptr.* = current; | 1433 | } |
| 1190 | // TODO undef + tentative | ||
| 1191 | return; | 1434 | return; |
| 1192 | } | 1435 | }; |
| 1193 | 1436 | ||
| 1194 | log.debug("TODO finish resolveGlobalSymbols implementation", .{}); | 1437 | log.debug("TODO finish resolveGlobalSymbols implementation", .{}); |
| 1438 | |||
| 1439 | if (sym.section_number == .UNDEFINED) return; | ||
| 1440 | |||
| 1441 | _ = self.unresolved.swapRemove(global_index); | ||
| 1442 | self.globals.items[global_index] = current; | ||
| 1195 | } | 1443 | } |
| 1196 | 1444 | ||
| 1197 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 1445 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | @@ -1227,6 +1475,17 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -1227,6 +1475,17 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1227 | sub_prog_node.activate(); | 1475 | sub_prog_node.activate(); |
| 1228 | defer sub_prog_node.end(); | 1476 | defer sub_prog_node.end(); |
| 1229 | 1477 | ||
| 1478 | while (self.unresolved.popOrNull()) |entry| { | ||
| 1479 | assert(entry.value); // We only expect imports generated by the incremental linker for now. | ||
| 1480 | const global = self.globals.items[entry.key]; | ||
| 1481 | if (self.imports_table.contains(global)) continue; | ||
| 1482 | |||
| 1483 | const import_index = try self.allocateImportEntry(global); | ||
| 1484 | const import_atom = try self.createImportAtom(); | ||
| 1485 | self.imports.items[import_index].sym_index = import_atom.sym_index; | ||
| 1486 | try self.writePtrWidthAtom(import_atom); | ||
| 1487 | } | ||
| 1488 | |||
| 1230 | if (build_options.enable_logging) { | 1489 | if (build_options.enable_logging) { |
| 1231 | self.logSymtab(); | 1490 | self.logSymtab(); |
| 1232 | } | 1491 | } |
| ... | @@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1237 | try self.resolveRelocs(atom.*); | 1496 | try self.resolveRelocs(atom.*); |
| 1238 | } | 1497 | } |
| 1239 | } | 1498 | } |
| 1499 | try self.writeImportTable(); | ||
| 1240 | try self.writeBaseRelocations(); | 1500 | try self.writeBaseRelocations(); |
| 1241 | 1501 | ||
| 1242 | if (self.getEntryPoint()) |entry_sym_loc| { | 1502 | if (self.getEntryPoint()) |entry_sym_loc| { |
| ... | @@ -1262,10 +1522,47 @@ pub fn getDeclVAddr( | ... | @@ -1262,10 +1522,47 @@ pub fn getDeclVAddr( |
| 1262 | decl_index: Module.Decl.Index, | 1522 | decl_index: Module.Decl.Index, |
| 1263 | reloc_info: link.File.RelocInfo, | 1523 | reloc_info: link.File.RelocInfo, |
| 1264 | ) !u64 { | 1524 | ) !u64 { |
| 1265 | _ = self; | 1525 | const mod = self.base.options.module.?; |
| 1266 | _ = decl_index; | 1526 | const decl = mod.declPtr(decl_index); |
| 1267 | _ = reloc_info; | 1527 | |
| 1268 | @panic("TODO getDeclVAddr"); | 1528 | assert(self.llvm_object == null); |
| 1529 | assert(decl.link.coff.sym_index != 0); | ||
| 1530 | |||
| 1531 | const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?; | ||
| 1532 | const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null }; | ||
| 1533 | try atom.addRelocation(self, .{ | ||
| 1534 | .@"type" = .direct, | ||
| 1535 | .target = target, | ||
| 1536 | .offset = @intCast(u32, reloc_info.offset), | ||
| 1537 | .addend = reloc_info.addend, | ||
| 1538 | .pcrel = false, | ||
| 1539 | .length = 3, | ||
| 1540 | }); | ||
| 1541 | try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset)); | ||
| 1542 | |||
| 1543 | return 0; | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { | ||
| 1547 | if (self.resolver.get(name)) |global_index| { | ||
| 1548 | return self.globals.items[global_index].sym_index; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | const gpa = self.base.allocator; | ||
| 1552 | const sym_index = try self.allocateSymbol(); | ||
| 1553 | const global_index = try self.allocateGlobal(); | ||
| 1554 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | ||
| 1555 | self.globals.items[global_index] = sym_loc; | ||
| 1556 | |||
| 1557 | const sym_name = try gpa.dupe(u8, name); | ||
| 1558 | const sym = self.getSymbolPtr(sym_loc); | ||
| 1559 | try self.setSymbolName(sym, sym_name); | ||
| 1560 | sym.storage_class = .EXTERNAL; | ||
| 1561 | |||
| 1562 | try self.resolver.putNoClobber(gpa, sym_name, global_index); | ||
| 1563 | try self.unresolved.putNoClobber(gpa, global_index, true); | ||
| 1564 | |||
| 1565 | return sym_index; | ||
| 1269 | } | 1566 | } |
| 1270 | 1567 | ||
| 1271 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { | 1568 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| ... | @@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void { | ... | @@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1342 | const header = &self.sections.items(.header)[self.reloc_section_index.?]; | 1639 | const header = &self.sections.items(.header)[self.reloc_section_index.?]; |
| 1343 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); | 1640 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 1344 | const needed_size = @intCast(u32, buffer.items.len); | 1641 | const needed_size = @intCast(u32, buffer.items.len); |
| 1345 | assert(needed_size < sect_capacity); // TODO expand .reloc section | 1642 | if (needed_size > sect_capacity) { |
| 1643 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); | ||
| 1644 | log.debug("writing {s} at 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ | ||
| 1645 | self.getSectionName(header), | ||
| 1646 | header.pointer_to_raw_data, | ||
| 1647 | header.pointer_to_raw_data + needed_size, | ||
| 1648 | new_offset, | ||
| 1649 | new_offset + needed_size, | ||
| 1650 | }); | ||
| 1651 | header.pointer_to_raw_data = new_offset; | ||
| 1652 | |||
| 1653 | const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); | ||
| 1654 | if (needed_size > sect_vm_capacity) { | ||
| 1655 | // TODO: we want to enforce .reloc after every alloc section. | ||
| 1656 | try self.growSectionVM(self.reloc_section_index.?, needed_size); | ||
| 1657 | } | ||
| 1658 | } | ||
| 1659 | header.virtual_size = @maximum(header.virtual_size, needed_size); | ||
| 1660 | header.size_of_raw_data = needed_size; | ||
| 1346 | 1661 | ||
| 1347 | try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); | 1662 | try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); |
| 1348 | 1663 | ||
| ... | @@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void { | ... | @@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1352 | }; | 1667 | }; |
| 1353 | } | 1668 | } |
| 1354 | 1669 | ||
| 1670 | fn writeImportTable(self: *Coff) !void { | ||
| 1671 | if (self.idata_section_index == null) return; | ||
| 1672 | |||
| 1673 | const gpa = self.base.allocator; | ||
| 1674 | |||
| 1675 | const section = self.sections.get(self.idata_section_index.?); | ||
| 1676 | const last_atom = section.last_atom orelse return; | ||
| 1677 | |||
| 1678 | const iat_rva = section.header.virtual_address; | ||
| 1679 | const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer | ||
| 1680 | |||
| 1681 | const dll_name = "KERNEL32.dll"; | ||
| 1682 | |||
| 1683 | var import_dir_entry = coff.ImportDirectoryEntry{ | ||
| 1684 | .import_lookup_table_rva = @sizeOf(coff.ImportDirectoryEntry) * 2, | ||
| 1685 | .time_date_stamp = 0, | ||
| 1686 | .forwarder_chain = 0, | ||
| 1687 | .name_rva = 0, | ||
| 1688 | .import_address_table_rva = iat_rva, | ||
| 1689 | }; | ||
| 1690 | |||
| 1691 | // TODO: we currently assume there's only one (implicit) DLL - ntdll | ||
| 1692 | var lookup_table = std.ArrayList(coff.ImportLookupEntry64.ByName).init(gpa); | ||
| 1693 | defer lookup_table.deinit(); | ||
| 1694 | |||
| 1695 | var names_table = std.ArrayList(u8).init(gpa); | ||
| 1696 | defer names_table.deinit(); | ||
| 1697 | |||
| 1698 | // TODO: check if import is still valid | ||
| 1699 | for (self.imports.items) |entry| { | ||
| 1700 | const target_name = self.getSymbolName(entry.target); | ||
| 1701 | const start = names_table.items.len; | ||
| 1702 | mem.writeIntLittle(u16, try names_table.addManyAsArray(2), 0); // TODO: currently, hint is set to 0 as we haven't yet parsed any DLL | ||
| 1703 | try names_table.appendSlice(target_name); | ||
| 1704 | try names_table.append(0); | ||
| 1705 | const end = names_table.items.len; | ||
| 1706 | if (!mem.isAlignedGeneric(usize, end - start, @sizeOf(u16))) { | ||
| 1707 | try names_table.append(0); | ||
| 1708 | } | ||
| 1709 | try lookup_table.append(.{ .name_table_rva = @intCast(u31, start) }); | ||
| 1710 | } | ||
| 1711 | try lookup_table.append(.{ .name_table_rva = 0 }); // the sentinel | ||
| 1712 | |||
| 1713 | const dir_entry_size = @sizeOf(coff.ImportDirectoryEntry) + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName) + names_table.items.len + dll_name.len + 1; | ||
| 1714 | const needed_size = iat_size + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry); | ||
| 1715 | const sect_capacity = self.allocatedSize(section.header.pointer_to_raw_data); | ||
| 1716 | assert(needed_size < sect_capacity); // TODO: implement expanding .idata section | ||
| 1717 | |||
| 1718 | // Fixup offsets | ||
| 1719 | const base_rva = iat_rva + iat_size; | ||
| 1720 | import_dir_entry.import_lookup_table_rva += base_rva; | ||
| 1721 | import_dir_entry.name_rva = @intCast(u32, base_rva + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry) - dll_name.len - 1); | ||
| 1722 | |||
| 1723 | for (lookup_table.items[0 .. lookup_table.items.len - 1]) |*lk| { | ||
| 1724 | lk.name_table_rva += @intCast(u31, base_rva + @sizeOf(coff.ImportDirectoryEntry) * 2 + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName)); | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | var buffer = std.ArrayList(u8).init(gpa); | ||
| 1728 | defer buffer.deinit(); | ||
| 1729 | try buffer.ensureTotalCapacity(dir_entry_size + @sizeOf(coff.ImportDirectoryEntry)); | ||
| 1730 | buffer.appendSliceAssumeCapacity(mem.asBytes(&import_dir_entry)); | ||
| 1731 | buffer.appendNTimesAssumeCapacity(0, @sizeOf(coff.ImportDirectoryEntry)); // the sentinel; TODO: I think doing all of the above on bytes directly might be cleaner | ||
| 1732 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(lookup_table.items)); | ||
| 1733 | buffer.appendSliceAssumeCapacity(names_table.items); | ||
| 1734 | buffer.appendSliceAssumeCapacity(dll_name); | ||
| 1735 | buffer.appendAssumeCapacity(0); | ||
| 1736 | |||
| 1737 | try self.base.file.?.pwriteAll(buffer.items, section.header.pointer_to_raw_data + iat_size); | ||
| 1738 | // Override the IAT atoms | ||
| 1739 | // TODO: we should rewrite only dirtied atoms, but that's for way later | ||
| 1740 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(lookup_table.items), section.header.pointer_to_raw_data); | ||
| 1741 | |||
| 1742 | self.data_directories[@enumToInt(coff.DirectoryEntry.IMPORT)] = .{ | ||
| 1743 | .virtual_address = iat_rva + iat_size, | ||
| 1744 | .size = @intCast(u32, @sizeOf(coff.ImportDirectoryEntry) * 2), | ||
| 1745 | }; | ||
| 1746 | |||
| 1747 | self.data_directories[@enumToInt(coff.DirectoryEntry.IAT)] = .{ | ||
| 1748 | .virtual_address = iat_rva, | ||
| 1749 | .size = iat_size, | ||
| 1750 | }; | ||
| 1751 | } | ||
| 1752 | |||
| 1355 | fn writeStrtab(self: *Coff) !void { | 1753 | fn writeStrtab(self: *Coff) !void { |
| 1754 | if (self.strtab_offset == null) return; | ||
| 1755 | |||
| 1356 | const allocated_size = self.allocatedSize(self.strtab_offset.?); | 1756 | const allocated_size = self.allocatedSize(self.strtab_offset.?); |
| 1357 | const needed_size = @intCast(u32, self.strtab.len()); | 1757 | const needed_size = @intCast(u32, self.strtab.len()); |
| 1358 | 1758 | ||
| 1359 | if (needed_size > allocated_size) { | 1759 | if (needed_size > allocated_size) { |
| 1360 | self.strtab_offset = null; | 1760 | self.strtab_offset = null; |
| 1361 | self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, 1)); | 1761 | self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, @alignOf(u32))); |
| 1362 | } | 1762 | } |
| 1363 | 1763 | ||
| 1364 | log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size }); | 1764 | log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size }); |
| 1365 | try self.base.file.?.pwriteAll(self.strtab.buffer.items, self.strtab_offset.?); | 1765 | |
| 1766 | var buffer = std.ArrayList(u8).init(self.base.allocator); | ||
| 1767 | defer buffer.deinit(); | ||
| 1768 | try buffer.ensureTotalCapacityPrecise(needed_size); | ||
| 1769 | buffer.appendSliceAssumeCapacity(self.strtab.items()); | ||
| 1770 | // Here, we do a trick in that we do not commit the size of the strtab to strtab buffer, instead | ||
| 1771 | // we write the length of the strtab to a temporary buffer that goes to file. | ||
| 1772 | mem.writeIntLittle(u32, buffer.items[0..4], @intCast(u32, self.strtab.len())); | ||
| 1773 | |||
| 1774 | try self.base.file.?.pwriteAll(buffer.items, self.strtab_offset.?); | ||
| 1366 | } | 1775 | } |
| 1367 | 1776 | ||
| 1368 | fn writeSectionHeaders(self: *Coff) !void { | 1777 | fn writeSectionHeaders(self: *Coff) !void { |
| ... | @@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | ... | @@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 1527 | } | 1936 | } |
| 1528 | 1937 | ||
| 1529 | fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { | 1938 | fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1530 | const headers_size = self.getSizeOfHeaders(); | 1939 | const headers_size = @maximum(self.getSizeOfHeaders(), self.page_size); |
| 1531 | if (start < headers_size) | 1940 | if (start < headers_size) |
| 1532 | return headers_size; | 1941 | return headers_size; |
| 1533 | 1942 | ||
| 1534 | const end = start + size; | 1943 | const end = start + padToIdeal(size); |
| 1535 | 1944 | ||
| 1536 | if (self.strtab_offset) |off| { | 1945 | if (self.strtab_offset) |off| { |
| 1537 | const increased_size = @intCast(u32, self.strtab.len()); | 1946 | const tight_size = @intCast(u32, self.strtab.len()); |
| 1947 | const increased_size = padToIdeal(tight_size); | ||
| 1538 | const test_end = off + increased_size; | 1948 | const test_end = off + increased_size; |
| 1539 | if (end > off and start < test_end) { | 1949 | if (end > off and start < test_end) { |
| 1540 | return test_end; | 1950 | return test_end; |
| ... | @@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { | ... | @@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1542 | } | 1952 | } |
| 1543 | 1953 | ||
| 1544 | for (self.sections.items(.header)) |header| { | 1954 | for (self.sections.items(.header)) |header| { |
| 1545 | const increased_size = header.size_of_raw_data; | 1955 | const tight_size = header.size_of_raw_data; |
| 1956 | const increased_size = padToIdeal(tight_size); | ||
| 1546 | const test_end = header.pointer_to_raw_data + increased_size; | 1957 | const test_end = header.pointer_to_raw_data + increased_size; |
| 1547 | if (end > header.pointer_to_raw_data and start < test_end) { | 1958 | if (end > header.pointer_to_raw_data and start < test_end) { |
| 1548 | return test_end; | 1959 | return test_end; |
| ... | @@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { | ... | @@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1552 | return null; | 1963 | return null; |
| 1553 | } | 1964 | } |
| 1554 | 1965 | ||
| 1555 | pub fn allocatedSize(self: *Coff, start: u32) u32 { | 1966 | fn allocatedSize(self: *Coff, start: u32) u32 { |
| 1556 | if (start == 0) | 1967 | if (start == 0) |
| 1557 | return 0; | 1968 | return 0; |
| 1558 | var min_pos: u32 = std.math.maxInt(u32); | 1969 | var min_pos: u32 = std.math.maxInt(u32); |
| ... | @@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 { | ... | @@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 { |
| 1566 | return min_pos - start; | 1977 | return min_pos - start; |
| 1567 | } | 1978 | } |
| 1568 | 1979 | ||
| 1569 | pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { | 1980 | fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { |
| 1570 | var start: u32 = 0; | 1981 | var start: u32 = 0; |
| 1571 | while (self.detectAllocCollision(start, object_size)) |item_end| { | 1982 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 1572 | start = mem.alignForwardGeneric(u32, item_end, min_alignment); | 1983 | start = mem.alignForwardGeneric(u32, item_end, min_alignment); |
| ... | @@ -1574,6 +1985,17 @@ pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { | ... | @@ -1574,6 +1985,17 @@ pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { |
| 1574 | return start; | 1985 | return start; |
| 1575 | } | 1986 | } |
| 1576 | 1987 | ||
| 1988 | fn allocatedVirtualSize(self: *Coff, start: u32) u32 { | ||
| 1989 | if (start == 0) | ||
| 1990 | return 0; | ||
| 1991 | var min_pos: u32 = std.math.maxInt(u32); | ||
| 1992 | for (self.sections.items(.header)) |header| { | ||
| 1993 | if (header.virtual_address <= start) continue; | ||
| 1994 | if (header.virtual_address < min_pos) min_pos = header.virtual_address; | ||
| 1995 | } | ||
| 1996 | return min_pos - start; | ||
| 1997 | } | ||
| 1998 | |||
| 1577 | inline fn getSizeOfHeaders(self: Coff) u32 { | 1999 | inline fn getSizeOfHeaders(self: Coff) u32 { |
| 1578 | const msdos_hdr_size = msdos_stub.len + 4; | 2000 | const msdos_hdr_size = msdos_stub.len + 4; |
| 1579 | return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() + | 2001 | return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() + |
| ... | @@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 { | ... | @@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 { |
| 1614 | 2036 | ||
| 1615 | /// Returns symbol location corresponding to the set entrypoint (if any). | 2037 | /// Returns symbol location corresponding to the set entrypoint (if any). |
| 1616 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { | 2038 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { |
| 1617 | const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete | 2039 | const entry_name = self.base.options.entry orelse "wWinMainCRTStartup"; // TODO this is incomplete |
| 1618 | return self.globals.get(entry_name); | 2040 | const global_index = self.resolver.get(entry_name) orelse return null; |
| 2041 | return self.globals.items[global_index]; | ||
| 1619 | } | 2042 | } |
| 1620 | 2043 | ||
| 1621 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. | 2044 | /// Returns pointer-to-symbol described by `sym_loc` descriptor. |
| 1622 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { | 2045 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { |
| 1623 | assert(sym_loc.file == null); // TODO linking object files | 2046 | assert(sym_loc.file == null); // TODO linking object files |
| 1624 | return &self.locals.items[sym_loc.sym_index]; | 2047 | return &self.locals.items[sym_loc.sym_index]; |
| 1625 | } | 2048 | } |
| 1626 | 2049 | ||
| 1627 | /// Returns symbol described by `sym_with_loc` descriptor. | 2050 | /// Returns symbol described by `sym_loc` descriptor. |
| 1628 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { | 2051 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { |
| 1629 | assert(sym_loc.file == null); // TODO linking object files | 2052 | assert(sym_loc.file == null); // TODO linking object files |
| 1630 | return &self.locals.items[sym_loc.sym_index]; | 2053 | return &self.locals.items[sym_loc.sym_index]; |
| 1631 | } | 2054 | } |
| 1632 | 2055 | ||
| 1633 | /// Returns name of the symbol described by `sym_with_loc` descriptor. | 2056 | /// Returns name of the symbol described by `sym_loc` descriptor. |
| 1634 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { | 2057 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1635 | assert(sym_loc.file == null); // TODO linking object files | 2058 | assert(sym_loc.file == null); // TODO linking object files |
| 1636 | const sym = self.getSymbol(sym_loc); | 2059 | const sym = self.getSymbol(sym_loc); |
| ... | @@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { | ... | @@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1638 | return self.strtab.get(offset).?; | 2061 | return self.strtab.get(offset).?; |
| 1639 | } | 2062 | } |
| 1640 | 2063 | ||
| 1641 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. | 2064 | /// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor. |
| 1642 | /// Returns null on failure. | 2065 | /// Returns null on failure. |
| 1643 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | 2066 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1644 | assert(sym_loc.file == null); // TODO linking with object files | 2067 | assert(sym_loc.file == null); // TODO linking with object files |
| 1645 | return self.atom_by_index_table.get(sym_loc.sym_index); | 2068 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 1646 | } | 2069 | } |
| 1647 | 2070 | ||
| 1648 | /// Returns GOT atom that references `sym_with_loc` if one exists. | 2071 | /// Returns GOT atom that references `sym_loc` if one exists. |
| 1649 | /// Returns null otherwise. | 2072 | /// Returns null otherwise. |
| 1650 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | 2073 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1651 | const got_index = self.got_entries.get(sym_loc) orelse return null; | 2074 | const got_index = self.got_entries_table.get(sym_loc) orelse return null; |
| 1652 | return self.atom_by_index_table.get(got_index); | 2075 | const got_entry = self.got_entries.items[got_index]; |
| 2076 | return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | /// Returns import atom that references `sym_loc` if one exists. | ||
| 2080 | /// Returns null otherwise. | ||
| 2081 | pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | ||
| 2082 | const imports_index = self.imports_table.get(sym_loc) orelse return null; | ||
| 2083 | const imports_entry = self.imports.items[imports_index]; | ||
| 2084 | return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null }); | ||
| 1653 | } | 2085 | } |
| 1654 | 2086 | ||
| 1655 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { | 2087 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
| ... | @@ -1663,6 +2095,14 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v | ... | @@ -1663,6 +2095,14 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v |
| 1663 | mem.set(u8, header.name[name_offset.len..], 0); | 2095 | mem.set(u8, header.name[name_offset.len..], 0); |
| 1664 | } | 2096 | } |
| 1665 | 2097 | ||
| 2098 | fn getSectionName(self: *const Coff, header: *const coff.SectionHeader) []const u8 { | ||
| 2099 | if (header.getName()) |name| { | ||
| 2100 | return name; | ||
| 2101 | } | ||
| 2102 | const offset = header.getNameOffset().?; | ||
| 2103 | return self.strtab.get(offset).?; | ||
| 2104 | } | ||
| 2105 | |||
| 1666 | fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { | 2106 | fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { |
| 1667 | if (name.len <= 8) { | 2107 | if (name.len <= 8) { |
| 1668 | mem.copy(u8, &symbol.name, name); | 2108 | mem.copy(u8, &symbol.name, name); |
| ... | @@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void { | ... | @@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void { |
| 1725 | } | 2165 | } |
| 1726 | 2166 | ||
| 1727 | log.debug("globals table:", .{}); | 2167 | log.debug("globals table:", .{}); |
| 1728 | for (self.globals.keys()) |name, id| { | 2168 | for (self.globals.items) |sym_loc| { |
| 1729 | const value = self.globals.values()[id]; | 2169 | const sym_name = self.getSymbolName(sym_loc); |
| 1730 | log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file }); | 2170 | log.debug(" {s} => %{d} in object({?d})", .{ sym_name, sym_loc.sym_index, sym_loc.file }); |
| 1731 | } | 2171 | } |
| 1732 | 2172 | ||
| 1733 | log.debug("GOT entries:", .{}); | 2173 | log.debug("GOT entries:", .{}); |
| 1734 | for (self.got_entries.keys()) |target, i| { | 2174 | for (self.got_entries.items) |entry, i| { |
| 1735 | const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null }); | 2175 | const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); |
| 1736 | const target_sym = self.getSymbol(target); | 2176 | const target_sym = self.getSymbol(entry.target); |
| 1737 | if (target_sym.section_number == .UNDEFINED) { | 2177 | if (target_sym.section_number == .UNDEFINED) { |
| 1738 | log.debug(" {d}@{x} => import('{s}')", .{ | 2178 | log.debug(" {d}@{x} => import('{s}')", .{ |
| 1739 | i, | 2179 | i, |
| 1740 | got_sym.value, | 2180 | got_sym.value, |
| 1741 | self.getSymbolName(target), | 2181 | self.getSymbolName(entry.target), |
| 1742 | }); | 2182 | }); |
| 1743 | } else { | 2183 | } else { |
| 1744 | log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ | 2184 | log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ |
| 1745 | i, | 2185 | i, |
| 1746 | got_sym.value, | 2186 | got_sym.value, |
| 1747 | target.sym_index, | 2187 | entry.target.sym_index, |
| 1748 | target.file, | 2188 | entry.target.file, |
| 1749 | logSymAttributes(target_sym, &buf), | 2189 | logSymAttributes(target_sym, &buf), |
| 1750 | }); | 2190 | }); |
| 1751 | } | 2191 | } |
| 1752 | } | 2192 | } |
| 1753 | } | 2193 | } |
| 2194 | |||
| 2195 | fn logSections(self: *Coff) void { | ||
| 2196 | log.debug("sections:", .{}); | ||
| 2197 | for (self.sections.items(.header)) |*header| { | ||
| 2198 | log.debug(" {s}: VM({x}, {x}) FILE({x}, {x})", .{ | ||
| 2199 | self.getSectionName(header), | ||
| 2200 | header.virtual_address, | ||
| 2201 | header.virtual_address + header.virtual_size, | ||
| 2202 | header.pointer_to_raw_data, | ||
| 2203 | header.pointer_to_raw_data + header.size_of_raw_data, | ||
| 2204 | }); | ||
| 2205 | } | ||
| 2206 | } |
src/link/Coff/Atom.zig+10-7| ... | @@ -4,8 +4,6 @@ const std = @import("std"); | ... | @@ -4,8 +4,6 @@ const std = @import("std"); |
| 4 | const coff = std.coff; | 4 | const coff = std.coff; |
| 5 | const log = std.log.scoped(.link); | 5 | const log = std.log.scoped(.link); |
| 6 | 6 | ||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | |||
| 9 | const Coff = @import("../Coff.zig"); | 7 | const Coff = @import("../Coff.zig"); |
| 10 | const Reloc = Coff.Reloc; | 8 | const Reloc = Coff.Reloc; |
| 11 | const SymbolWithLoc = Coff.SymbolWithLoc; | 9 | const SymbolWithLoc = Coff.SymbolWithLoc; |
| ... | @@ -41,11 +39,6 @@ pub const empty = Atom{ | ... | @@ -41,11 +39,6 @@ pub const empty = Atom{ |
| 41 | .next = null, | 39 | .next = null, |
| 42 | }; | 40 | }; |
| 43 | 41 | ||
| 44 | pub fn deinit(self: *Atom, gpa: Allocator) void { | ||
| 45 | _ = self; | ||
| 46 | _ = gpa; | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Returns symbol referencing this atom. | 42 | /// Returns symbol referencing this atom. |
| 50 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { | 43 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { |
| 51 | return coff_file.getSymbol(.{ | 44 | return coff_file.getSymbol(.{ |
| ... | @@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { | ... | @@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { |
| 118 | } | 111 | } |
| 119 | try gop.value_ptr.append(gpa, offset); | 112 | try gop.value_ptr.append(gpa, offset); |
| 120 | } | 113 | } |
| 114 | |||
| 115 | pub fn addBinding(self: *Atom, coff_file: *Coff, target: SymbolWithLoc) !void { | ||
| 116 | const gpa = coff_file.base.allocator; | ||
| 117 | log.debug(" (adding binding to target %{d} in %{d})", .{ target.sym_index, self.sym_index }); | ||
| 118 | const gop = try coff_file.bindings.getOrPut(gpa, self); | ||
| 119 | if (!gop.found_existing) { | ||
| 120 | gop.value_ptr.* = .{}; | ||
| 121 | } | ||
| 122 | try gop.value_ptr.append(gpa, target); | ||
| 123 | } |
src/link/Dwarf.zig+36-37| ... | @@ -861,7 +861,8 @@ pub fn commitDeclState( | ... | @@ -861,7 +861,8 @@ pub fn commitDeclState( |
| 861 | }, | 861 | }, |
| 862 | .wasm => { | 862 | .wasm => { |
| 863 | const wasm_file = file.cast(File.Wasm).?; | 863 | const wasm_file = file.cast(File.Wasm).?; |
| 864 | writeDbgLineNopsBuffered(wasm_file.debug_line.items, src_fn.off, 0, &.{}, src_fn.len); | 864 | const debug_line = wasm_file.debug_line_atom.?.code; |
| 865 | writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); | ||
| 865 | }, | 866 | }, |
| 866 | else => unreachable, | 867 | else => unreachable, |
| 867 | } | 868 | } |
| ... | @@ -972,23 +973,21 @@ pub fn commitDeclState( | ... | @@ -972,23 +973,21 @@ pub fn commitDeclState( |
| 972 | }, | 973 | }, |
| 973 | .wasm => { | 974 | .wasm => { |
| 974 | const wasm_file = file.cast(File.Wasm).?; | 975 | const wasm_file = file.cast(File.Wasm).?; |
| 975 | const segment_index = try wasm_file.getDebugLineIndex(); | 976 | const atom = wasm_file.debug_line_atom.?; |
| 976 | const segment = &wasm_file.segments.items[segment_index]; | 977 | const debug_line = &atom.code; |
| 977 | const debug_line = &wasm_file.debug_line; | 978 | const segment_size = debug_line.items.len; |
| 978 | if (needed_size != segment.size) { | 979 | if (needed_size != segment_size) { |
| 979 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); | 980 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); |
| 980 | if (needed_size > segment.size) { | 981 | if (needed_size > segment_size) { |
| 981 | log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size}); | 982 | log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size}); |
| 982 | try debug_line.resize(self.allocator, needed_size); | 983 | try debug_line.resize(self.allocator, needed_size); |
| 983 | mem.set(u8, debug_line.items[segment.size..], 0); | 984 | mem.set(u8, debug_line.items[segment_size..], 0); |
| 984 | } | 985 | } |
| 985 | segment.size = needed_size; | ||
| 986 | debug_line.items.len = needed_size; | 986 | debug_line.items.len = needed_size; |
| 987 | } | 987 | } |
| 988 | const offset = segment.offset + src_fn.off; | ||
| 989 | writeDbgLineNopsBuffered( | 988 | writeDbgLineNopsBuffered( |
| 990 | debug_line.items, | 989 | debug_line.items, |
| 991 | offset, | 990 | src_fn.off, |
| 992 | prev_padding_size, | 991 | prev_padding_size, |
| 993 | dbg_line_buffer.items, | 992 | dbg_line_buffer.items, |
| 994 | next_padding_size, | 993 | next_padding_size, |
| ... | @@ -1146,10 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 | ... | @@ -1146,10 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 |
| 1146 | }, | 1145 | }, |
| 1147 | .wasm => { | 1146 | .wasm => { |
| 1148 | const wasm_file = file.cast(File.Wasm).?; | 1147 | const wasm_file = file.cast(File.Wasm).?; |
| 1149 | const segment_index = try wasm_file.getDebugInfoIndex(); | 1148 | const debug_info = &wasm_file.debug_info_atom.?.code; |
| 1150 | const segment = &wasm_file.segments.items[segment_index]; | 1149 | try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); |
| 1151 | const offset = segment.offset + atom.off; | ||
| 1152 | try writeDbgInfoNopsToArrayList(gpa, &wasm_file.debug_info, offset, 0, &.{0}, atom.len, false); | ||
| 1153 | }, | 1150 | }, |
| 1154 | else => unreachable, | 1151 | else => unreachable, |
| 1155 | } | 1152 | } |
| ... | @@ -1276,27 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co | ... | @@ -1276,27 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co |
| 1276 | }, | 1273 | }, |
| 1277 | .wasm => { | 1274 | .wasm => { |
| 1278 | const wasm_file = file.cast(File.Wasm).?; | 1275 | const wasm_file = file.cast(File.Wasm).?; |
| 1279 | const segment_index = try wasm_file.getDebugInfoIndex(); | 1276 | const info_atom = wasm_file.debug_info_atom.?; |
| 1280 | const segment = &wasm_file.segments.items[segment_index]; | 1277 | const debug_info = &info_atom.code; |
| 1281 | const debug_info = &wasm_file.debug_info; | 1278 | const segment_size = debug_info.items.len; |
| 1282 | if (needed_size != segment.size) { | 1279 | if (needed_size != segment_size) { |
| 1283 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); | 1280 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); |
| 1284 | if (needed_size > segment.size) { | 1281 | if (needed_size > segment_size) { |
| 1285 | log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size}); | 1282 | log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size}); |
| 1286 | try debug_info.resize(self.allocator, needed_size); | 1283 | try debug_info.resize(self.allocator, needed_size); |
| 1287 | mem.set(u8, debug_info.items[segment.size..], 0); | 1284 | mem.set(u8, debug_info.items[segment_size..], 0); |
| 1288 | } | 1285 | } |
| 1289 | segment.size = needed_size; | ||
| 1290 | debug_info.items.len = needed_size; | 1286 | debug_info.items.len = needed_size; |
| 1291 | } | 1287 | } |
| 1292 | const offset = segment.offset + atom.off; | ||
| 1293 | log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{ | 1288 | log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{ |
| 1294 | debug_info.items.len, offset, dbg_info_buf.len, next_padding_size, | 1289 | debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size, |
| 1295 | }); | 1290 | }); |
| 1296 | try writeDbgInfoNopsToArrayList( | 1291 | try writeDbgInfoNopsToArrayList( |
| 1297 | gpa, | 1292 | gpa, |
| 1298 | debug_info, | 1293 | debug_info, |
| 1299 | offset, | 1294 | atom.off, |
| 1300 | prev_padding_size, | 1295 | prev_padding_size, |
| 1301 | dbg_info_buf, | 1296 | dbg_info_buf, |
| 1302 | next_padding_size, | 1297 | next_padding_size, |
| ... | @@ -1337,10 +1332,9 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) | ... | @@ -1337,10 +1332,9 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) |
| 1337 | }, | 1332 | }, |
| 1338 | .wasm => { | 1333 | .wasm => { |
| 1339 | const wasm_file = file.cast(File.Wasm).?; | 1334 | const wasm_file = file.cast(File.Wasm).?; |
| 1340 | const segment_index = wasm_file.getDebugLineIndex() catch unreachable; | 1335 | const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); |
| 1341 | const segment = wasm_file.segments.items[segment_index]; | 1336 | const atom = wasm_file.debug_line_atom.?; |
| 1342 | const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); | 1337 | mem.copy(u8, atom.code.items[offset..], &data); |
| 1343 | mem.copy(u8, wasm_file.debug_line.items[offset..], &data); | ||
| 1344 | }, | 1338 | }, |
| 1345 | else => unreachable, | 1339 | else => unreachable, |
| 1346 | } | 1340 | } |
| ... | @@ -1576,8 +1570,9 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { | ... | @@ -1576,8 +1570,9 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { |
| 1576 | }, | 1570 | }, |
| 1577 | .wasm => { | 1571 | .wasm => { |
| 1578 | const wasm_file = file.cast(File.Wasm).?; | 1572 | const wasm_file = file.cast(File.Wasm).?; |
| 1579 | try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size); | 1573 | const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code; |
| 1580 | mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf); | 1574 | try debug_abbrev.resize(wasm_file.base.allocator, needed_size); |
| 1575 | mem.copy(u8, debug_abbrev.items, &abbrev_buf); | ||
| 1581 | }, | 1576 | }, |
| 1582 | else => unreachable, | 1577 | else => unreachable, |
| 1583 | } | 1578 | } |
| ... | @@ -1687,7 +1682,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 | ... | @@ -1687,7 +1682,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 |
| 1687 | }, | 1682 | }, |
| 1688 | .wasm => { | 1683 | .wasm => { |
| 1689 | const wasm_file = file.cast(File.Wasm).?; | 1684 | const wasm_file = file.cast(File.Wasm).?; |
| 1690 | try writeDbgInfoNopsToArrayList(self.allocator, &wasm_file.debug_info, 0, 0, di_buf.items, jmp_amt, false); | 1685 | const debug_info = &wasm_file.debug_info_atom.?.code; |
| 1686 | try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); | ||
| 1691 | }, | 1687 | }, |
| 1692 | else => unreachable, | 1688 | else => unreachable, |
| 1693 | } | 1689 | } |
| ... | @@ -2016,8 +2012,9 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { | ... | @@ -2016,8 +2012,9 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { |
| 2016 | }, | 2012 | }, |
| 2017 | .wasm => { | 2013 | .wasm => { |
| 2018 | const wasm_file = file.cast(File.Wasm).?; | 2014 | const wasm_file = file.cast(File.Wasm).?; |
| 2019 | try wasm_file.debug_aranges.resize(wasm_file.base.allocator, needed_size); | 2015 | const debug_ranges = &wasm_file.debug_ranges_atom.?.code; |
| 2020 | mem.copy(u8, wasm_file.debug_aranges.items, di_buf.items); | 2016 | try debug_ranges.resize(wasm_file.base.allocator, needed_size); |
| 2017 | mem.copy(u8, debug_ranges.items, di_buf.items); | ||
| 2021 | }, | 2018 | }, |
| 2022 | else => unreachable, | 2019 | else => unreachable, |
| 2023 | } | 2020 | } |
| ... | @@ -2139,7 +2136,8 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { | ... | @@ -2139,7 +2136,8 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { |
| 2139 | }, | 2136 | }, |
| 2140 | .wasm => { | 2137 | .wasm => { |
| 2141 | const wasm_file = file.cast(File.Wasm).?; | 2138 | const wasm_file = file.cast(File.Wasm).?; |
| 2142 | writeDbgLineNopsBuffered(wasm_file.debug_line.items, 0, 0, di_buf.items, jmp_amt); | 2139 | const debug_line = wasm_file.debug_line_atom.?.code; |
| 2140 | writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); | ||
| 2143 | }, | 2141 | }, |
| 2144 | else => unreachable, | 2142 | else => unreachable, |
| 2145 | } | 2143 | } |
| ... | @@ -2287,7 +2285,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { | ... | @@ -2287,7 +2285,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { |
| 2287 | }, | 2285 | }, |
| 2288 | .wasm => { | 2286 | .wasm => { |
| 2289 | const wasm_file = file.cast(File.Wasm).?; | 2287 | const wasm_file = file.cast(File.Wasm).?; |
| 2290 | mem.copy(u8, wasm_file.debug_info.items[reloc.atom.off + reloc.offset ..], &buf); | 2288 | const debug_info = wasm_file.debug_info_atom.?.code; |
| 2289 | mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf); | ||
| 2291 | }, | 2290 | }, |
| 2292 | else => unreachable, | 2291 | else => unreachable, |
| 2293 | } | 2292 | } |
src/link/Elf.zig+12| ... | @@ -1482,6 +1482,18 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v | ... | @@ -1482,6 +1482,18 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v |
| 1482 | try argv.append("--gc-sections"); | 1482 | try argv.append("--gc-sections"); |
| 1483 | } | 1483 | } |
| 1484 | 1484 | ||
| 1485 | if (self.base.options.print_gc_sections) { | ||
| 1486 | try argv.append("--print-gc-sections"); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | if (self.base.options.print_icf_sections) { | ||
| 1490 | try argv.append("--print-icf-sections"); | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | if (self.base.options.print_map) { | ||
| 1494 | try argv.append("--print-map"); | ||
| 1495 | } | ||
| 1496 | |||
| 1485 | if (self.base.options.eh_frame_hdr) { | 1497 | if (self.base.options.eh_frame_hdr) { |
| 1486 | try argv.append("--eh-frame-hdr"); | 1498 | try argv.append("--eh-frame-hdr"); |
| 1487 | } | 1499 | } |
src/link/MachO.zig+34-17| ... | @@ -793,11 +793,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -793,11 +793,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 793 | } | 793 | } |
| 794 | } else { | 794 | } else { |
| 795 | const sub_path = self.base.options.emit.?.sub_path; | 795 | const sub_path = self.base.options.emit.?.sub_path; |
| 796 | self.base.file = try directory.handle.createFile(sub_path, .{ | 796 | if (self.base.file == null) { |
| 797 | .truncate = true, | 797 | self.base.file = try directory.handle.createFile(sub_path, .{ |
| 798 | .read = true, | 798 | .truncate = true, |
| 799 | .mode = link.determineMode(self.base.options), | 799 | .read = true, |
| 800 | }); | 800 | .mode = link.determineMode(self.base.options), |
| 801 | }); | ||
| 802 | } | ||
| 801 | // Index 0 is always a null symbol. | 803 | // Index 0 is always a null symbol. |
| 802 | try self.locals.append(gpa, .{ | 804 | try self.locals.append(gpa, .{ |
| 803 | .n_strx = 0, | 805 | .n_strx = 0, |
| ... | @@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 1155 | var ncmds: u32 = 0; | 1157 | var ncmds: u32 = 0; |
| 1156 | 1158 | ||
| 1157 | try self.writeLinkeditSegmentData(&ncmds, lc_writer); | 1159 | try self.writeLinkeditSegmentData(&ncmds, lc_writer); |
| 1160 | |||
| 1161 | // If the last section of __DATA segment is zerofill section, we need to ensure | ||
| 1162 | // that the free space between the end of the last non-zerofill section of __DATA | ||
| 1163 | // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will | ||
| 1164 | // copy-paste this space into memory for quicker zerofill operation. | ||
| 1165 | if (self.data_segment_cmd_index) |data_seg_id| blk: { | ||
| 1166 | var physical_zerofill_start: u64 = 0; | ||
| 1167 | const section_indexes = self.getSectionIndexes(data_seg_id); | ||
| 1168 | for (self.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| { | ||
| 1169 | if (header.isZerofill() and header.size > 0) break; | ||
| 1170 | physical_zerofill_start = header.offset + header.size; | ||
| 1171 | } else break :blk; | ||
| 1172 | const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?]; | ||
| 1173 | const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse | ||
| 1174 | return error.Overflow; | ||
| 1175 | if (physical_zerofill_size > 0) { | ||
| 1176 | var padding = try self.base.allocator.alloc(u8, physical_zerofill_size); | ||
| 1177 | defer self.base.allocator.free(padding); | ||
| 1178 | mem.set(u8, padding, 0); | ||
| 1179 | try self.base.file.?.pwriteAll(padding, physical_zerofill_start); | ||
| 1180 | } | ||
| 1181 | } | ||
| 1182 | |||
| 1158 | try writeDylinkerLC(&ncmds, lc_writer); | 1183 | try writeDylinkerLC(&ncmds, lc_writer); |
| 1159 | try self.writeMainLC(&ncmds, lc_writer); | 1184 | try self.writeMainLC(&ncmds, lc_writer); |
| 1160 | try self.writeDylibIdLC(&ncmds, lc_writer); | 1185 | try self.writeDylibIdLC(&ncmds, lc_writer); |
| ... | @@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool { | ... | @@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool { |
| 1435 | 1460 | ||
| 1436 | if (force_load) { | 1461 | if (force_load) { |
| 1437 | defer archive.deinit(gpa); | 1462 | defer archive.deinit(gpa); |
| 1438 | defer file.close(); | ||
| 1439 | // Get all offsets from the ToC | 1463 | // Get all offsets from the ToC |
| 1440 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); | 1464 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); |
| 1441 | defer offsets.deinit(); | 1465 | defer offsets.deinit(); |
| ... | @@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void { | ... | @@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void { |
| 3086 | self.atom_by_index_table.deinit(gpa); | 3110 | self.atom_by_index_table.deinit(gpa); |
| 3087 | } | 3111 | } |
| 3088 | 3112 | ||
| 3089 | pub fn closeFiles(self: MachO) void { | ||
| 3090 | for (self.archives.items) |archive| { | ||
| 3091 | archive.file.close(); | ||
| 3092 | } | ||
| 3093 | if (self.d_sym) |ds| { | ||
| 3094 | ds.file.close(); | ||
| 3095 | } | ||
| 3096 | } | ||
| 3097 | |||
| 3098 | fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void { | 3113 | fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void { |
| 3099 | log.debug("freeAtom {*}", .{atom}); | 3114 | log.debug("freeAtom {*}", .{atom}); |
| 3100 | if (!owns_atom) { | 3115 | if (!owns_atom) { |
| ... | @@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { | ... | @@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 5698 | else => unreachable, | 5713 | else => unreachable, |
| 5699 | } | 5714 | } |
| 5700 | 5715 | ||
| 5701 | if (self.getSectionByName("__DATA", "__thread_vars")) |_| { | 5716 | if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| { |
| 5702 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | 5717 | if (self.sections.items(.header)[sect_id].size > 0) { |
| 5718 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | ||
| 5719 | } | ||
| 5703 | } | 5720 | } |
| 5704 | 5721 | ||
| 5705 | header.ncmds = ncmds; | 5722 | header.ncmds = ncmds; |
src/link/MachO/Archive.zig+1| ... | @@ -88,6 +88,7 @@ const ar_hdr = extern struct { | ... | @@ -88,6 +88,7 @@ const ar_hdr = extern struct { |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | pub fn deinit(self: *Archive, allocator: Allocator) void { | 90 | pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 91 | self.file.close(); | ||
| 91 | for (self.toc.keys()) |*key| { | 92 | for (self.toc.keys()) |*key| { |
| 92 | allocator.free(key.*); | 93 | allocator.free(key.*); |
| 93 | } | 94 | } |
src/link/MachO/DebugSymbols.zig+1| ... | @@ -306,6 +306,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti | ... | @@ -306,6 +306,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { | 308 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { |
| 309 | self.file.close(); | ||
| 309 | self.segments.deinit(allocator); | 310 | self.segments.deinit(allocator); |
| 310 | self.sections.deinit(allocator); | 311 | self.sections.deinit(allocator); |
| 311 | self.dwarf.deinit(); | 312 | self.dwarf.deinit(); |
src/link/Wasm.zig+294-123| ... | @@ -67,6 +67,18 @@ code_section_index: ?u32 = null, | ... | @@ -67,6 +67,18 @@ code_section_index: ?u32 = null, |
| 67 | debug_info_index: ?u32 = null, | 67 | debug_info_index: ?u32 = null, |
| 68 | /// The index of the segment representing the custom '.debug_line' section. | 68 | /// The index of the segment representing the custom '.debug_line' section. |
| 69 | debug_line_index: ?u32 = null, | 69 | debug_line_index: ?u32 = null, |
| 70 | /// The index of the segment representing the custom '.debug_loc' section. | ||
| 71 | debug_loc_index: ?u32 = null, | ||
| 72 | /// The index of the segment representing the custom '.debug_ranges' section. | ||
| 73 | debug_ranges_index: ?u32 = null, | ||
| 74 | /// The index of the segment representing the custom '.debug_pubnames' section. | ||
| 75 | debug_pubnames_index: ?u32 = null, | ||
| 76 | /// The index of the segment representing the custom '.debug_pubtypes' section. | ||
| 77 | debug_pubtypes_index: ?u32 = null, | ||
| 78 | /// The index of the segment representing the custom '.debug_pubtypes' section. | ||
| 79 | debug_str_index: ?u32 = null, | ||
| 80 | /// The index of the segment representing the custom '.debug_pubtypes' section. | ||
| 81 | debug_abbrev_index: ?u32 = null, | ||
| 70 | /// The count of imported functions. This number will be appended | 82 | /// The count of imported functions. This number will be appended |
| 71 | /// to the function indexes as their index starts at the lowest non-extern function. | 83 | /// to the function indexes as their index starts at the lowest non-extern function. |
| 72 | imported_functions_count: u32 = 0, | 84 | imported_functions_count: u32 = 0, |
| ... | @@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, | ... | @@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, |
| 83 | segments: std.ArrayListUnmanaged(Segment) = .{}, | 95 | segments: std.ArrayListUnmanaged(Segment) = .{}, |
| 84 | /// Maps a data segment key (such as .rodata) to the index into `segments`. | 96 | /// Maps a data segment key (such as .rodata) to the index into `segments`. |
| 85 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, | 97 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 86 | /// A list of `types.Segment` which provide meta data | 98 | /// A table of `types.Segment` which provide meta data |
| 87 | /// about a data symbol such as its name | 99 | /// about a data symbol such as its name where the key is |
| 88 | segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, | 100 | /// the segment index, which can be found from `data_segments` |
| 101 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, | ||
| 89 | /// Deduplicated string table for strings used by symbols, imports and exports. | 102 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 90 | string_table: StringTable = .{}, | 103 | string_table: StringTable = .{}, |
| 91 | /// Debug information for wasm | 104 | /// Debug information for wasm |
| 92 | dwarf: ?Dwarf = null, | 105 | dwarf: ?Dwarf = null, |
| 93 | 106 | ||
| 94 | // *debug information* // | ||
| 95 | /// Contains all bytes for the '.debug_info' section | ||
| 96 | debug_info: std.ArrayListUnmanaged(u8) = .{}, | ||
| 97 | /// Contains all bytes for the '.debug_line' section | ||
| 98 | debug_line: std.ArrayListUnmanaged(u8) = .{}, | ||
| 99 | /// Contains all bytes for the '.debug_abbrev' section | ||
| 100 | debug_abbrev: std.ArrayListUnmanaged(u8) = .{}, | ||
| 101 | /// Contains all bytes for the '.debug_ranges' section | ||
| 102 | debug_aranges: std.ArrayListUnmanaged(u8) = .{}, | ||
| 103 | |||
| 104 | // Output sections | 107 | // Output sections |
| 105 | /// Output type section | 108 | /// Output type section |
| 106 | func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, | 109 | func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, |
| ... | @@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, | ... | @@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 156 | /// The actual table is populated during `flush`. | 159 | /// The actual table is populated during `flush`. |
| 157 | error_table_symbol: ?u32 = null, | 160 | error_table_symbol: ?u32 = null, |
| 158 | 161 | ||
| 162 | // Debug section atoms. These are only set when the current compilation | ||
| 163 | // unit contains Zig code. The lifetime of these atoms are extended | ||
| 164 | // until the end of the compiler's lifetime. Meaning they're not freed | ||
| 165 | // during `flush()` in incremental-mode. | ||
| 166 | debug_info_atom: ?*Atom = null, | ||
| 167 | debug_line_atom: ?*Atom = null, | ||
| 168 | debug_loc_atom: ?*Atom = null, | ||
| 169 | debug_ranges_atom: ?*Atom = null, | ||
| 170 | debug_abbrev_atom: ?*Atom = null, | ||
| 171 | debug_str_atom: ?*Atom = null, | ||
| 172 | debug_pubnames_atom: ?*Atom = null, | ||
| 173 | debug_pubtypes_atom: ?*Atom = null, | ||
| 174 | |||
| 159 | pub const Segment = struct { | 175 | pub const Segment = struct { |
| 160 | alignment: u32, | 176 | alignment: u32, |
| 161 | size: u32, | 177 | size: u32, |
| ... | @@ -209,6 +225,18 @@ pub const SymbolLoc = struct { | ... | @@ -209,6 +225,18 @@ pub const SymbolLoc = struct { |
| 209 | } | 225 | } |
| 210 | return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name); | 226 | return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name); |
| 211 | } | 227 | } |
| 228 | |||
| 229 | /// From a given symbol location, returns the final location. | ||
| 230 | /// e.g. when a symbol was resolved and replaced by the symbol | ||
| 231 | /// in a different file, this will return said location. | ||
| 232 | /// If the symbol wasn't replaced by another, this will return | ||
| 233 | /// the given location itself. | ||
| 234 | pub fn finalLoc(self: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { | ||
| 235 | if (wasm_bin.discarded.get(self)) |new_loc| { | ||
| 236 | return new_loc.finalLoc(wasm_bin); | ||
| 237 | } | ||
| 238 | return self; | ||
| 239 | } | ||
| 212 | }; | 240 | }; |
| 213 | 241 | ||
| 214 | /// Generic string table that duplicates strings | 242 | /// Generic string table that duplicates strings |
| ... | @@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 335 | }; | 363 | }; |
| 336 | } | 364 | } |
| 337 | 365 | ||
| 366 | try wasm_bin.initDebugSections(); | ||
| 338 | return wasm_bin; | 367 | return wasm_bin; |
| 339 | } | 368 | } |
| 340 | 369 | ||
| ... | @@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { | ... | @@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 363 | return self; | 392 | return self; |
| 364 | } | 393 | } |
| 365 | 394 | ||
| 395 | /// Initializes symbols and atoms for the debug sections | ||
| 396 | /// Initialization is only done when compiling Zig code. | ||
| 397 | /// When Zig is invoked as a linker instead, the atoms | ||
| 398 | /// and symbols come from the object files instead. | ||
| 399 | pub fn initDebugSections(self: *Wasm) !void { | ||
| 400 | if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections | ||
| 401 | assert(self.debug_info_index == null); | ||
| 402 | // this will create an Atom and set the index for us. | ||
| 403 | self.debug_info_atom = try self.createDebugSectionForIndex(&self.debug_info_index, ".debug_info"); | ||
| 404 | self.debug_line_atom = try self.createDebugSectionForIndex(&self.debug_line_index, ".debug_line"); | ||
| 405 | self.debug_loc_atom = try self.createDebugSectionForIndex(&self.debug_loc_index, ".debug_loc"); | ||
| 406 | self.debug_abbrev_atom = try self.createDebugSectionForIndex(&self.debug_abbrev_index, ".debug_abbrev"); | ||
| 407 | self.debug_ranges_atom = try self.createDebugSectionForIndex(&self.debug_ranges_index, ".debug_ranges"); | ||
| 408 | self.debug_str_atom = try self.createDebugSectionForIndex(&self.debug_str_index, ".debug_str"); | ||
| 409 | self.debug_pubnames_atom = try self.createDebugSectionForIndex(&self.debug_pubnames_index, ".debug_pubnames"); | ||
| 410 | self.debug_pubtypes_atom = try self.createDebugSectionForIndex(&self.debug_pubtypes_index, ".debug_pubtypes"); | ||
| 411 | } | ||
| 412 | |||
| 366 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { | 413 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { |
| 367 | for (files) |path| { | 414 | for (files) |path| { |
| 368 | if (try self.parseObjectFile(path)) continue; | 415 | if (try self.parseObjectFile(path)) continue; |
| ... | @@ -644,16 +691,14 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -644,16 +691,14 @@ pub fn deinit(self: *Wasm) void { |
| 644 | for (self.func_types.items) |*func_type| { | 691 | for (self.func_types.items) |*func_type| { |
| 645 | func_type.deinit(gpa); | 692 | func_type.deinit(gpa); |
| 646 | } | 693 | } |
| 647 | for (self.segment_info.items) |segment_info| { | 694 | for (self.segment_info.values()) |segment_info| { |
| 648 | gpa.free(segment_info.name); | 695 | gpa.free(segment_info.name); |
| 649 | } | 696 | } |
| 650 | for (self.objects.items) |*object| { | 697 | for (self.objects.items) |*object| { |
| 651 | object.file.?.close(); | ||
| 652 | object.deinit(gpa); | 698 | object.deinit(gpa); |
| 653 | } | 699 | } |
| 654 | 700 | ||
| 655 | for (self.archives.items) |*archive| { | 701 | for (self.archives.items) |*archive| { |
| 656 | archive.file.close(); | ||
| 657 | archive.deinit(gpa); | 702 | archive.deinit(gpa); |
| 658 | } | 703 | } |
| 659 | 704 | ||
| ... | @@ -692,11 +737,6 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -692,11 +737,6 @@ pub fn deinit(self: *Wasm) void { |
| 692 | if (self.dwarf) |*dwarf| { | 737 | if (self.dwarf) |*dwarf| { |
| 693 | dwarf.deinit(); | 738 | dwarf.deinit(); |
| 694 | } | 739 | } |
| 695 | |||
| 696 | self.debug_info.deinit(gpa); | ||
| 697 | self.debug_line.deinit(gpa); | ||
| 698 | self.debug_abbrev.deinit(gpa); | ||
| 699 | self.debug_aranges.deinit(gpa); | ||
| 700 | } | 740 | } |
| 701 | 741 | ||
| 702 | pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { | 742 | pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { |
| ... | @@ -1337,16 +1377,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1337,16 +1377,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1337 | const index = gop.value_ptr.*; | 1377 | const index = gop.value_ptr.*; |
| 1338 | self.segments.items[index].size += atom.size; | 1378 | self.segments.items[index].size += atom.size; |
| 1339 | 1379 | ||
| 1340 | // segment indexes can be off by 1 due to also containing a segment | 1380 | symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); |
| 1341 | // for the code section, so we must check if the existing segment | ||
| 1342 | // is larger than that of the code section, and substract the index by 1 in such case. | ||
| 1343 | var info_add = if (self.code_section_index) |idx| blk: { | ||
| 1344 | if (idx < index) break :blk @as(u32, 1); | ||
| 1345 | break :blk 0; | ||
| 1346 | } else @as(u32, 0); | ||
| 1347 | if (self.debug_info_index != null) info_add += 1; | ||
| 1348 | if (self.debug_line_index != null) info_add += 1; | ||
| 1349 | symbol.index = index - info_add; | ||
| 1350 | // segment info already exists, so free its memory | 1381 | // segment info already exists, so free its memory |
| 1351 | self.base.allocator.free(segment_name); | 1382 | self.base.allocator.free(segment_name); |
| 1352 | break :result index; | 1383 | break :result index; |
| ... | @@ -1359,8 +1390,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1359,8 +1390,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1359 | }); | 1390 | }); |
| 1360 | gop.value_ptr.* = index; | 1391 | gop.value_ptr.* = index; |
| 1361 | 1392 | ||
| 1362 | const info_index = @intCast(u32, self.segment_info.items.len); | 1393 | const info_index = @intCast(u32, self.segment_info.count()); |
| 1363 | try self.segment_info.append(self.base.allocator, segment_info); | 1394 | try self.segment_info.put(self.base.allocator, index, segment_info); |
| 1364 | symbol.index = info_index; | 1395 | symbol.index = info_index; |
| 1365 | break :result index; | 1396 | break :result index; |
| 1366 | } | 1397 | } |
| ... | @@ -1370,18 +1401,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1370,18 +1401,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1370 | const segment: *Segment = &self.segments.items[final_index]; | 1401 | const segment: *Segment = &self.segments.items[final_index]; |
| 1371 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | 1402 | segment.alignment = std.math.max(segment.alignment, atom.alignment); |
| 1372 | 1403 | ||
| 1373 | if (self.atoms.getPtr(final_index)) |last| { | 1404 | try self.appendAtomAtIndex(final_index, atom); |
| 1405 | } | ||
| 1406 | |||
| 1407 | /// From a given index, append the given `Atom` at the back of the linked list. | ||
| 1408 | /// Simply inserts it into the map of atoms when it doesn't exist yet. | ||
| 1409 | pub fn appendAtomAtIndex(self: *Wasm, index: u32, atom: *Atom) !void { | ||
| 1410 | if (self.atoms.getPtr(index)) |last| { | ||
| 1374 | last.*.next = atom; | 1411 | last.*.next = atom; |
| 1375 | atom.prev = last.*; | 1412 | atom.prev = last.*; |
| 1376 | last.* = atom; | 1413 | last.* = atom; |
| 1377 | } else { | 1414 | } else { |
| 1378 | try self.atoms.putNoClobber(self.base.allocator, final_index, atom); | 1415 | try self.atoms.putNoClobber(self.base.allocator, index, atom); |
| 1379 | } | 1416 | } |
| 1380 | } | 1417 | } |
| 1381 | 1418 | ||
| 1419 | /// Allocates debug atoms into their respective debug sections | ||
| 1420 | /// to merge them with maybe-existing debug atoms from object files. | ||
| 1421 | fn allocateDebugAtoms(self: *Wasm) !void { | ||
| 1422 | if (self.dwarf == null) return; | ||
| 1423 | |||
| 1424 | const allocAtom = struct { | ||
| 1425 | fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void { | ||
| 1426 | const index = maybe_index.* orelse idx: { | ||
| 1427 | const index = @intCast(u32, bin.segments.items.len); | ||
| 1428 | try bin.appendDummySegment(); | ||
| 1429 | maybe_index.* = index; | ||
| 1430 | break :idx index; | ||
| 1431 | }; | ||
| 1432 | atom.size = @intCast(u32, atom.code.items.len); | ||
| 1433 | bin.symbols.items[atom.sym_index].index = index; | ||
| 1434 | try bin.appendAtomAtIndex(index, atom); | ||
| 1435 | } | ||
| 1436 | }.f; | ||
| 1437 | |||
| 1438 | try allocAtom(self, &self.debug_info_index, self.debug_info_atom.?); | ||
| 1439 | try allocAtom(self, &self.debug_line_index, self.debug_line_atom.?); | ||
| 1440 | try allocAtom(self, &self.debug_loc_index, self.debug_loc_atom.?); | ||
| 1441 | try allocAtom(self, &self.debug_str_index, self.debug_str_atom.?); | ||
| 1442 | try allocAtom(self, &self.debug_ranges_index, self.debug_ranges_atom.?); | ||
| 1443 | try allocAtom(self, &self.debug_abbrev_index, self.debug_abbrev_atom.?); | ||
| 1444 | try allocAtom(self, &self.debug_pubnames_index, self.debug_pubnames_atom.?); | ||
| 1445 | try allocAtom(self, &self.debug_pubtypes_index, self.debug_pubtypes_atom.?); | ||
| 1446 | } | ||
| 1447 | |||
| 1382 | fn allocateAtoms(self: *Wasm) !void { | 1448 | fn allocateAtoms(self: *Wasm) !void { |
| 1383 | // first sort the data segments | 1449 | // first sort the data segments |
| 1384 | try sortDataSegments(self); | 1450 | try sortDataSegments(self); |
| 1451 | try allocateDebugAtoms(self); | ||
| 1385 | 1452 | ||
| 1386 | var it = self.atoms.iterator(); | 1453 | var it = self.atoms.iterator(); |
| 1387 | while (it.next()) |entry| { | 1454 | while (it.next()) |entry| { |
| ... | @@ -1399,7 +1466,7 @@ fn allocateAtoms(self: *Wasm) !void { | ... | @@ -1399,7 +1466,7 @@ fn allocateAtoms(self: *Wasm) !void { |
| 1399 | atom.size, | 1466 | atom.size, |
| 1400 | }); | 1467 | }); |
| 1401 | offset += atom.size; | 1468 | offset += atom.size; |
| 1402 | self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers | 1469 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers |
| 1403 | atom = atom.next orelse break; | 1470 | atom = atom.next orelse break; |
| 1404 | } | 1471 | } |
| 1405 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); | 1472 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| ... | @@ -1753,7 +1820,7 @@ fn setupMemory(self: *Wasm) !void { | ... | @@ -1753,7 +1820,7 @@ fn setupMemory(self: *Wasm) !void { |
| 1753 | /// From a given object's index and the index of the segment, returns the corresponding | 1820 | /// From a given object's index and the index of the segment, returns the corresponding |
| 1754 | /// index of the segment within the final data section. When the segment does not yet | 1821 | /// index of the segment within the final data section. When the segment does not yet |
| 1755 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. | 1822 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. |
| 1756 | pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !u32 { | 1823 | pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !?u32 { |
| 1757 | const object: Object = self.objects.items[object_index]; | 1824 | const object: Object = self.objects.items[object_index]; |
| 1758 | const relocatable_data = object.relocatable_data[relocatable_index]; | 1825 | const relocatable_data = object.relocatable_data[relocatable_index]; |
| 1759 | const index = @intCast(u32, self.segments.items.len); | 1826 | const index = @intCast(u32, self.segments.items.len); |
| ... | @@ -1765,27 +1832,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 | ... | @@ -1765,27 +1832,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 |
| 1765 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); | 1832 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); |
| 1766 | if (!result.found_existing) { | 1833 | if (!result.found_existing) { |
| 1767 | result.value_ptr.* = index; | 1834 | result.value_ptr.* = index; |
| 1768 | try self.segments.append(self.base.allocator, .{ | 1835 | try self.appendDummySegment(); |
| 1769 | .alignment = 1, | ||
| 1770 | .size = 0, | ||
| 1771 | .offset = 0, | ||
| 1772 | }); | ||
| 1773 | return index; | 1836 | return index; |
| 1774 | } else return result.value_ptr.*; | 1837 | } else return result.value_ptr.*; |
| 1775 | }, | 1838 | }, |
| 1776 | .code => return self.code_section_index orelse blk: { | 1839 | .code => return self.code_section_index orelse blk: { |
| 1777 | self.code_section_index = index; | 1840 | self.code_section_index = index; |
| 1778 | try self.segments.append(self.base.allocator, .{ | 1841 | try self.appendDummySegment(); |
| 1779 | .alignment = 1, | ||
| 1780 | .size = 0, | ||
| 1781 | .offset = 0, | ||
| 1782 | }); | ||
| 1783 | break :blk index; | 1842 | break :blk index; |
| 1784 | }, | 1843 | }, |
| 1785 | .custom => return error.@"TODO: Custom section relocations for wasm", | 1844 | .debug => { |
| 1845 | const debug_name = object.getDebugName(relocatable_data); | ||
| 1846 | if (mem.eql(u8, debug_name, ".debug_info")) { | ||
| 1847 | return self.debug_info_index orelse blk: { | ||
| 1848 | self.debug_info_index = index; | ||
| 1849 | try self.appendDummySegment(); | ||
| 1850 | break :blk index; | ||
| 1851 | }; | ||
| 1852 | } else if (mem.eql(u8, debug_name, ".debug_line")) { | ||
| 1853 | return self.debug_line_index orelse blk: { | ||
| 1854 | self.debug_line_index = index; | ||
| 1855 | try self.appendDummySegment(); | ||
| 1856 | break :blk index; | ||
| 1857 | }; | ||
| 1858 | } else if (mem.eql(u8, debug_name, ".debug_loc")) { | ||
| 1859 | return self.debug_loc_index orelse blk: { | ||
| 1860 | self.debug_loc_index = index; | ||
| 1861 | try self.appendDummySegment(); | ||
| 1862 | break :blk index; | ||
| 1863 | }; | ||
| 1864 | } else if (mem.eql(u8, debug_name, ".debug_ranges")) { | ||
| 1865 | return self.debug_line_index orelse blk: { | ||
| 1866 | self.debug_ranges_index = index; | ||
| 1867 | try self.appendDummySegment(); | ||
| 1868 | break :blk index; | ||
| 1869 | }; | ||
| 1870 | } else if (mem.eql(u8, debug_name, ".debug_pubnames")) { | ||
| 1871 | return self.debug_pubnames_index orelse blk: { | ||
| 1872 | self.debug_pubnames_index = index; | ||
| 1873 | try self.appendDummySegment(); | ||
| 1874 | break :blk index; | ||
| 1875 | }; | ||
| 1876 | } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) { | ||
| 1877 | return self.debug_pubtypes_index orelse blk: { | ||
| 1878 | self.debug_pubtypes_index = index; | ||
| 1879 | try self.appendDummySegment(); | ||
| 1880 | break :blk index; | ||
| 1881 | }; | ||
| 1882 | } else if (mem.eql(u8, debug_name, ".debug_abbrev")) { | ||
| 1883 | return self.debug_abbrev_index orelse blk: { | ||
| 1884 | self.debug_abbrev_index = index; | ||
| 1885 | try self.appendDummySegment(); | ||
| 1886 | break :blk index; | ||
| 1887 | }; | ||
| 1888 | } else if (mem.eql(u8, debug_name, ".debug_str")) { | ||
| 1889 | return self.debug_str_index orelse blk: { | ||
| 1890 | self.debug_str_index = index; | ||
| 1891 | try self.appendDummySegment(); | ||
| 1892 | break :blk index; | ||
| 1893 | }; | ||
| 1894 | } else { | ||
| 1895 | log.warn("found unknown debug section '{s}'", .{debug_name}); | ||
| 1896 | log.warn(" debug section will be skipped", .{}); | ||
| 1897 | return null; | ||
| 1898 | } | ||
| 1899 | }, | ||
| 1786 | } | 1900 | } |
| 1787 | } | 1901 | } |
| 1788 | 1902 | ||
| 1903 | /// Appends a new segment with default field values | ||
| 1904 | fn appendDummySegment(self: *Wasm) !void { | ||
| 1905 | try self.segments.append(self.base.allocator, .{ | ||
| 1906 | .alignment = 1, | ||
| 1907 | .size = 0, | ||
| 1908 | .offset = 0, | ||
| 1909 | }); | ||
| 1910 | } | ||
| 1911 | |||
| 1789 | /// Returns the symbol index of the error name table. | 1912 | /// Returns the symbol index of the error name table. |
| 1790 | /// | 1913 | /// |
| 1791 | /// When the symbol does not yet exist, it will create a new one instead. | 1914 | /// When the symbol does not yet exist, it will create a new one instead. |
| ... | @@ -1903,50 +2026,52 @@ fn populateErrorNameTable(self: *Wasm) !void { | ... | @@ -1903,50 +2026,52 @@ fn populateErrorNameTable(self: *Wasm) !void { |
| 1903 | try self.parseAtom(names_atom, .{ .data = .read_only }); | 2026 | try self.parseAtom(names_atom, .{ .data = .read_only }); |
| 1904 | } | 2027 | } |
| 1905 | 2028 | ||
| 1906 | pub fn getDebugInfoIndex(self: *Wasm) !u32 { | 2029 | /// From a given index variable, creates a new debug section. |
| 1907 | assert(self.dwarf != null); | 2030 | /// This initializes the index, appends a new segment, |
| 1908 | return self.debug_info_index orelse { | 2031 | /// and finally, creates a managed `Atom`. |
| 1909 | self.debug_info_index = @intCast(u32, self.segments.items.len); | 2032 | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { |
| 1910 | const segment = try self.segments.addOne(self.base.allocator); | 2033 | const new_index = @intCast(u32, self.segments.items.len); |
| 1911 | segment.* = .{ | 2034 | index.* = new_index; |
| 1912 | .size = 0, | 2035 | try self.appendDummySegment(); |
| 1913 | .offset = 0, | 2036 | // _ = index; |
| 1914 | // debug sections always have alignment '1' | 2037 | |
| 1915 | .alignment = 1, | 2038 | const sym_index = self.symbols_free_list.popOrNull() orelse idx: { |
| 1916 | }; | 2039 | const tmp_index = @intCast(u32, self.symbols.items.len); |
| 1917 | return self.debug_info_index.?; | 2040 | _ = try self.symbols.addOne(self.base.allocator); |
| 2041 | break :idx tmp_index; | ||
| 1918 | }; | 2042 | }; |
| 1919 | } | 2043 | self.symbols.items[sym_index] = .{ |
| 1920 | 2044 | .tag = .section, | |
| 1921 | pub fn getDebugLineIndex(self: *Wasm) !u32 { | 2045 | .name = try self.string_table.put(self.base.allocator, name), |
| 1922 | assert(self.dwarf != null); | 2046 | .index = 0, |
| 1923 | return self.debug_line_index orelse { | 2047 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1924 | self.debug_line_index = @intCast(u32, self.segments.items.len); | ||
| 1925 | const segment = try self.segments.addOne(self.base.allocator); | ||
| 1926 | segment.* = .{ | ||
| 1927 | .size = 0, | ||
| 1928 | .offset = 0, | ||
| 1929 | .alignment = 1, | ||
| 1930 | }; | ||
| 1931 | return self.debug_line_index.?; | ||
| 1932 | }; | 2048 | }; |
| 2049 | |||
| 2050 | const atom = try self.base.allocator.create(Atom); | ||
| 2051 | atom.* = Atom.empty; | ||
| 2052 | atom.alignment = 1; // debug sections are always 1-byte-aligned | ||
| 2053 | atom.sym_index = sym_index; | ||
| 2054 | try self.managed_atoms.append(self.base.allocator, atom); | ||
| 2055 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); | ||
| 2056 | return atom; | ||
| 1933 | } | 2057 | } |
| 1934 | 2058 | ||
| 1935 | fn resetState(self: *Wasm) void { | 2059 | fn resetState(self: *Wasm) void { |
| 1936 | for (self.segment_info.items) |*segment_info| { | 2060 | for (self.segment_info.values()) |segment_info| { |
| 1937 | self.base.allocator.free(segment_info.name); | 2061 | self.base.allocator.free(segment_info.name); |
| 1938 | } | 2062 | } |
| 1939 | const mod = self.base.options.module.?; | 2063 | if (self.base.options.module) |mod| { |
| 1940 | var decl_it = self.decls.keyIterator(); | 2064 | var decl_it = self.decls.keyIterator(); |
| 1941 | while (decl_it.next()) |decl_index_ptr| { | 2065 | while (decl_it.next()) |decl_index_ptr| { |
| 1942 | const decl = mod.declPtr(decl_index_ptr.*); | 2066 | const decl = mod.declPtr(decl_index_ptr.*); |
| 1943 | const atom = &decl.link.wasm; | 2067 | const atom = &decl.link.wasm; |
| 1944 | atom.next = null; | 2068 | atom.next = null; |
| 1945 | atom.prev = null; | 2069 | atom.prev = null; |
| 1946 | 2070 | ||
| 1947 | for (atom.locals.items) |*local_atom| { | 2071 | for (atom.locals.items) |*local_atom| { |
| 1948 | local_atom.next = null; | 2072 | local_atom.next = null; |
| 1949 | local_atom.prev = null; | 2073 | local_atom.prev = null; |
| 2074 | } | ||
| 1950 | } | 2075 | } |
| 1951 | } | 2076 | } |
| 1952 | self.functions.clearRetainingCapacity(); | 2077 | self.functions.clearRetainingCapacity(); |
| ... | @@ -1959,6 +2084,12 @@ fn resetState(self: *Wasm) void { | ... | @@ -1959,6 +2084,12 @@ fn resetState(self: *Wasm) void { |
| 1959 | self.code_section_index = null; | 2084 | self.code_section_index = null; |
| 1960 | self.debug_info_index = null; | 2085 | self.debug_info_index = null; |
| 1961 | self.debug_line_index = null; | 2086 | self.debug_line_index = null; |
| 2087 | self.debug_loc_index = null; | ||
| 2088 | self.debug_str_index = null; | ||
| 2089 | self.debug_ranges_index = null; | ||
| 2090 | self.debug_abbrev_index = null; | ||
| 2091 | self.debug_pubnames_index = null; | ||
| 2092 | self.debug_pubtypes_index = null; | ||
| 1962 | } | 2093 | } |
| 1963 | 2094 | ||
| 1964 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 2095 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | @@ -2036,29 +2167,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2036,29 +2167,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2036 | defer self.resetState(); | 2167 | defer self.resetState(); |
| 2037 | try self.setupStart(); | 2168 | try self.setupStart(); |
| 2038 | try self.setupImports(); | 2169 | try self.setupImports(); |
| 2039 | const mod = self.base.options.module.?; | 2170 | if (self.base.options.module) |mod| { |
| 2040 | var decl_it = self.decls.keyIterator(); | 2171 | var decl_it = self.decls.keyIterator(); |
| 2041 | while (decl_it.next()) |decl_index_ptr| { | 2172 | while (decl_it.next()) |decl_index_ptr| { |
| 2042 | const decl = mod.declPtr(decl_index_ptr.*); | 2173 | const decl = mod.declPtr(decl_index_ptr.*); |
| 2043 | if (decl.isExtern()) continue; | 2174 | if (decl.isExtern()) continue; |
| 2044 | const atom = &decl.*.link.wasm; | 2175 | const atom = &decl.*.link.wasm; |
| 2045 | if (decl.ty.zigTypeTag() == .Fn) { | 2176 | if (decl.ty.zigTypeTag() == .Fn) { |
| 2046 | try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); | 2177 | try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); |
| 2047 | } else if (decl.getVariable()) |variable| { | 2178 | } else if (decl.getVariable()) |variable| { |
| 2048 | if (!variable.is_mutable) { | 2179 | if (!variable.is_mutable) { |
| 2049 | try self.parseAtom(atom, .{ .data = .read_only }); | 2180 | try self.parseAtom(atom, .{ .data = .read_only }); |
| 2050 | } else if (variable.init.isUndefDeep()) { | 2181 | } else if (variable.init.isUndefDeep()) { |
| 2051 | try self.parseAtom(atom, .{ .data = .uninitialized }); | 2182 | try self.parseAtom(atom, .{ .data = .uninitialized }); |
| 2183 | } else { | ||
| 2184 | try self.parseAtom(atom, .{ .data = .initialized }); | ||
| 2185 | } | ||
| 2052 | } else { | 2186 | } else { |
| 2053 | try self.parseAtom(atom, .{ .data = .initialized }); | 2187 | try self.parseAtom(atom, .{ .data = .read_only }); |
| 2188 | } | ||
| 2189 | |||
| 2190 | // also parse atoms for a decl's locals | ||
| 2191 | for (atom.locals.items) |*local_atom| { | ||
| 2192 | try self.parseAtom(local_atom, .{ .data = .read_only }); | ||
| 2054 | } | 2193 | } |
| 2055 | } else { | ||
| 2056 | try self.parseAtom(atom, .{ .data = .read_only }); | ||
| 2057 | } | 2194 | } |
| 2058 | 2195 | ||
| 2059 | // also parse atoms for a decl's locals | 2196 | if (self.dwarf) |*dwarf| { |
| 2060 | for (atom.locals.items) |*local_atom| { | 2197 | try dwarf.flushModule(&self.base, self.base.options.module.?); |
| 2061 | try self.parseAtom(local_atom, .{ .data = .read_only }); | ||
| 2062 | } | 2198 | } |
| 2063 | } | 2199 | } |
| 2064 | 2200 | ||
| ... | @@ -2066,9 +2202,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2066,9 +2202,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2066 | try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); | 2202 | try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); |
| 2067 | } | 2203 | } |
| 2068 | 2204 | ||
| 2069 | if (self.dwarf) |*dwarf| { | ||
| 2070 | try dwarf.flushModule(&self.base, self.base.options.module.?); | ||
| 2071 | } | ||
| 2072 | try self.allocateAtoms(); | 2205 | try self.allocateAtoms(); |
| 2073 | try self.setupMemory(); | 2206 | try self.setupMemory(); |
| 2074 | self.mapFunctionTable(); | 2207 | self.mapFunctionTable(); |
| ... | @@ -2424,19 +2557,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2424,19 +2557,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2424 | } | 2557 | } |
| 2425 | } else if (!self.base.options.strip) { | 2558 | } else if (!self.base.options.strip) { |
| 2426 | if (self.dwarf) |*dwarf| { | 2559 | if (self.dwarf) |*dwarf| { |
| 2427 | if (self.debug_info_index != null) { | 2560 | const mod = self.base.options.module.?; |
| 2428 | try dwarf.writeDbgAbbrev(&self.base); | 2561 | try dwarf.writeDbgAbbrev(&self.base); |
| 2429 | // for debug info and ranges, the address is always 0, | 2562 | // for debug info and ranges, the address is always 0, |
| 2430 | // as locations are always offsets relative to 'code' section. | 2563 | // as locations are always offsets relative to 'code' section. |
| 2431 | try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); | 2564 | try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); |
| 2432 | try dwarf.writeDbgAranges(&self.base, 0, code_section_size); | 2565 | try dwarf.writeDbgAranges(&self.base, 0, code_section_size); |
| 2433 | try dwarf.writeDbgLineHeader(&self.base, mod); | 2566 | try dwarf.writeDbgLineHeader(&self.base, mod); |
| 2434 | 2567 | } | |
| 2435 | try emitDebugSection(file, self.debug_info.items, ".debug_info"); | 2568 | |
| 2436 | try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges"); | 2569 | var debug_bytes = std.ArrayList(u8).init(self.base.allocator); |
| 2437 | try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); | 2570 | defer debug_bytes.deinit(); |
| 2438 | try emitDebugSection(file, self.debug_line.items, ".debug_line"); | 2571 | |
| 2439 | try emitDebugSection(file, dwarf.strtab.items, ".debug_str"); | 2572 | const DebugSection = struct { |
| 2573 | name: []const u8, | ||
| 2574 | index: ?u32, | ||
| 2575 | }; | ||
| 2576 | |||
| 2577 | const debug_sections: []const DebugSection = &.{ | ||
| 2578 | .{ .name = ".debug_info", .index = self.debug_info_index }, | ||
| 2579 | .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index }, | ||
| 2580 | .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index }, | ||
| 2581 | .{ .name = ".debug_line", .index = self.debug_line_index }, | ||
| 2582 | .{ .name = ".debug_str", .index = self.debug_str_index }, | ||
| 2583 | .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index }, | ||
| 2584 | .{ .name = ".debug_loc", .index = self.debug_loc_index }, | ||
| 2585 | .{ .name = ".debug_ranges", .index = self.debug_ranges_index }, | ||
| 2586 | }; | ||
| 2587 | |||
| 2588 | for (debug_sections) |item| { | ||
| 2589 | if (item.index) |index| { | ||
| 2590 | var atom = self.atoms.get(index).?.getFirst(); | ||
| 2591 | while (true) { | ||
| 2592 | atom.resolveRelocs(self); | ||
| 2593 | try debug_bytes.appendSlice(atom.code.items); | ||
| 2594 | atom = atom.next orelse break; | ||
| 2595 | } | ||
| 2596 | try emitDebugSection(file, debug_bytes.items, item.name); | ||
| 2597 | debug_bytes.clearRetainingCapacity(); | ||
| 2440 | } | 2598 | } |
| 2441 | } | 2599 | } |
| 2442 | try self.emitNameSection(file, arena); | 2600 | try self.emitNameSection(file, arena); |
| ... | @@ -2444,6 +2602,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2444,6 +2602,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2444 | } | 2602 | } |
| 2445 | 2603 | ||
| 2446 | fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { | 2604 | fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { |
| 2605 | if (data.len == 0) return; | ||
| 2447 | const header_offset = try reserveCustomSectionHeader(file); | 2606 | const header_offset = try reserveCustomSectionHeader(file); |
| 2448 | const writer = file.writer(); | 2607 | const writer = file.writer(); |
| 2449 | try leb.writeULEB128(writer, @intCast(u32, name.len)); | 2608 | try leb.writeULEB128(writer, @intCast(u32, name.len)); |
| ... | @@ -3057,14 +3216,26 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size | ... | @@ -3057,14 +3216,26 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size |
| 3057 | buf[0] = @enumToInt(section); | 3216 | buf[0] = @enumToInt(section); |
| 3058 | leb.writeUnsignedFixed(5, buf[1..6], size); | 3217 | leb.writeUnsignedFixed(5, buf[1..6], size); |
| 3059 | leb.writeUnsignedFixed(5, buf[6..], items); | 3218 | leb.writeUnsignedFixed(5, buf[6..], items); |
| 3060 | 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); | ||
| 3061 | } | 3226 | } |
| 3062 | 3227 | ||
| 3063 | fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { | 3228 | fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { |
| 3064 | var buf: [1 + 5]u8 = undefined; | 3229 | var buf: [1 + 5]u8 = undefined; |
| 3065 | buf[0] = 0; // 0 = 'custom' section | 3230 | buf[0] = 0; // 0 = 'custom' section |
| 3066 | leb.writeUnsignedFixed(5, buf[1..6], size); | 3231 | leb.writeUnsignedFixed(5, buf[1..6], size); |
| 3067 | 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); | ||
| 3068 | } | 3239 | } |
| 3069 | 3240 | ||
| 3070 | fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { | 3241 | fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void { |
| ... | @@ -3149,8 +3320,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { | ... | @@ -3149,8 +3320,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 3149 | var payload = std.ArrayList(u8).init(arena); | 3320 | var payload = std.ArrayList(u8).init(arena); |
| 3150 | const writer = payload.writer(); | 3321 | const writer = payload.writer(); |
| 3151 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); | 3322 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); |
| 3152 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len)); | 3323 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); |
| 3153 | for (self.segment_info.items) |segment_info| { | 3324 | for (self.segment_info.values()) |segment_info| { |
| 3154 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ | 3325 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| 3155 | segment_info.name, | 3326 | segment_info.name, |
| 3156 | @ctz(segment_info.alignment), | 3327 | @ctz(segment_info.alignment), |
src/link/Wasm/Archive.zig+1| ... | @@ -95,6 +95,7 @@ const ar_hdr = extern struct { | ... | @@ -95,6 +95,7 @@ const ar_hdr = extern struct { |
| 95 | }; | 95 | }; |
| 96 | 96 | ||
| 97 | pub fn deinit(archive: *Archive, allocator: Allocator) void { | 97 | pub fn deinit(archive: *Archive, allocator: Allocator) void { |
| 98 | archive.file.close(); | ||
| 98 | for (archive.toc.keys()) |*key| { | 99 | for (archive.toc.keys()) |*key| { |
| 99 | allocator.free(key.*); | 100 | allocator.free(key.*); |
| 100 | } | 101 | } |
src/link/Wasm/Atom.zig+35-7| ... | @@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom { | ... | @@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom { |
| 90 | return tmp; | 90 | return tmp; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | /// Unlike `getFirst` this returns the first `*Atom` that was | ||
| 94 | /// produced from Zig code, rather than an object file. | ||
| 95 | /// This is useful for debug sections where we want to extend | ||
| 96 | /// the bytes, and don't want to overwrite existing Atoms. | ||
| 97 | pub fn getFirstZigAtom(self: *Atom) *Atom { | ||
| 98 | if (self.file == null) return self; | ||
| 99 | var tmp = self; | ||
| 100 | return while (tmp.prev) |prev| { | ||
| 101 | if (prev.file == null) break prev; | ||
| 102 | tmp = prev; | ||
| 103 | } else unreachable; // must allocate an Atom first! | ||
| 104 | } | ||
| 105 | |||
| 93 | /// Returns the location of the symbol that represents this `Atom` | 106 | /// Returns the location of the symbol that represents this `Atom` |
| 94 | pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { | 107 | pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { |
| 95 | return .{ .file = self.file, .index = self.sym_index }; | 108 | return .{ .file = self.file, .index = self.sym_index }; |
| ... | @@ -145,7 +158,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { | ... | @@ -145,7 +158,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { |
| 145 | /// All values will be represented as a `u64` as all values can fit within it. | 158 | /// All values will be represented as a `u64` as all values can fit within it. |
| 146 | /// The final value must be casted to the correct size. | 159 | /// The final value must be casted to the correct size. |
| 147 | fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { | 160 | fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { |
| 148 | const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index }; | 161 | const target_loc = (Wasm.SymbolLoc{ .file = self.file, .index = relocation.index }).finalLoc(wasm_bin); |
| 149 | const symbol = target_loc.getSymbol(wasm_bin).*; | 162 | const symbol = target_loc.getSymbol(wasm_bin).*; |
| 150 | switch (relocation.relocation_type) { | 163 | switch (relocation.relocation_type) { |
| 151 | .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, | 164 | .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, |
| ... | @@ -174,19 +187,34 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa | ... | @@ -174,19 +187,34 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa |
| 174 | => { | 187 | => { |
| 175 | std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); | 188 | std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); |
| 176 | const merge_segment = wasm_bin.base.options.output_mode != .Obj; | 189 | const merge_segment = wasm_bin.base.options.output_mode != .Obj; |
| 177 | const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc; | 190 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; |
| 178 | const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?; | ||
| 179 | const segment_info = if (target_atom.file) |object_index| blk: { | 191 | const segment_info = if (target_atom.file) |object_index| blk: { |
| 180 | break :blk wasm_bin.objects.items[object_index].segment_info; | 192 | break :blk wasm_bin.objects.items[object_index].segment_info; |
| 181 | } else wasm_bin.segment_info.items; | 193 | } else wasm_bin.segment_info.values(); |
| 182 | const segment_name = segment_info[symbol.index].outputName(merge_segment); | 194 | const segment_name = segment_info[symbol.index].outputName(merge_segment); |
| 183 | const segment_index = wasm_bin.data_segments.get(segment_name).?; | 195 | const segment_index = wasm_bin.data_segments.get(segment_name).?; |
| 184 | const segment = wasm_bin.segments.items[segment_index]; | 196 | const segment = wasm_bin.segments.items[segment_index]; |
| 185 | return target_atom.offset + segment.offset + (relocation.addend orelse 0); | 197 | return target_atom.offset + segment.offset + (relocation.addend orelse 0); |
| 186 | }, | 198 | }, |
| 187 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, | 199 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, |
| 188 | .R_WASM_SECTION_OFFSET_I32, | 200 | .R_WASM_SECTION_OFFSET_I32 => { |
| 189 | .R_WASM_FUNCTION_OFFSET_I32, | 201 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; |
| 190 | => return relocation.offset, | 202 | return target_atom.offset + (relocation.addend orelse 0); |
| 203 | }, | ||
| 204 | .R_WASM_FUNCTION_OFFSET_I32 => { | ||
| 205 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; | ||
| 206 | var atom = target_atom.getFirst(); | ||
| 207 | var offset: u32 = 0; | ||
| 208 | // TODO: Calculate this during atom allocation, rather than | ||
| 209 | // this linear calculation. For now it's done here as atoms | ||
| 210 | // are being sorted after atom allocation, as functions aren't | ||
| 211 | // merged until later. | ||
| 212 | while (true) { | ||
| 213 | offset += 5; // each atom uses 5 bytes to store its body's size | ||
| 214 | if (atom == target_atom) break; | ||
| 215 | atom = atom.next.?; | ||
| 216 | } | ||
| 217 | return target_atom.offset + offset + (relocation.addend orelse 0); | ||
| 218 | }, | ||
| 191 | } | 219 | } |
| 192 | } | 220 | } |
src/link/Wasm/Object.zig+78-34| ... | @@ -63,16 +63,21 @@ relocatable_data: []const RelocatableData = &.{}, | ... | @@ -63,16 +63,21 @@ relocatable_data: []const RelocatableData = &.{}, |
| 63 | /// import name, module name and export names. Each string will be deduplicated | 63 | /// import name, module name and export names. Each string will be deduplicated |
| 64 | /// and returns an offset into the table. | 64 | /// and returns an offset into the table. |
| 65 | string_table: Wasm.StringTable = .{}, | 65 | string_table: Wasm.StringTable = .{}, |
| 66 | /// All the names of each debug section found in the current object file. | ||
| 67 | /// Each name is terminated by a null-terminator. The name can be found, | ||
| 68 | /// from the `index` offset within the `RelocatableData`. | ||
| 69 | debug_names: [:0]const u8, | ||
| 66 | 70 | ||
| 67 | /// Represents a single item within a section (depending on its `type`) | 71 | /// Represents a single item within a section (depending on its `type`) |
| 68 | const RelocatableData = struct { | 72 | const RelocatableData = struct { |
| 69 | /// The type of the relocatable data | 73 | /// The type of the relocatable data |
| 70 | type: enum { data, code, custom }, | 74 | type: enum { data, code, debug }, |
| 71 | /// Pointer to the data of the segment, where its length is written to `size` | 75 | /// Pointer to the data of the segment, where its length is written to `size` |
| 72 | data: [*]u8, | 76 | data: [*]u8, |
| 73 | /// The size in bytes of the data representing the segment within the section | 77 | /// The size in bytes of the data representing the segment within the section |
| 74 | size: u32, | 78 | size: u32, |
| 75 | /// The index within the section itself | 79 | /// The index within the section itself, or in case of a debug section, |
| 80 | /// the offset within the `string_table`. | ||
| 76 | index: u32, | 81 | index: u32, |
| 77 | /// The offset within the section where the data starts | 82 | /// The offset within the section where the data starts |
| 78 | offset: u32, | 83 | offset: u32, |
| ... | @@ -96,9 +101,16 @@ const RelocatableData = struct { | ... | @@ -96,9 +101,16 @@ const RelocatableData = struct { |
| 96 | return switch (self.type) { | 101 | return switch (self.type) { |
| 97 | .data => .data, | 102 | .data => .data, |
| 98 | .code => .function, | 103 | .code => .function, |
| 99 | .custom => .section, | 104 | .debug => .section, |
| 100 | }; | 105 | }; |
| 101 | } | 106 | } |
| 107 | |||
| 108 | /// Returns the index within a section itself, or in case of a debug section, | ||
| 109 | /// returns the section index within the object file. | ||
| 110 | pub fn getIndex(self: RelocatableData) u32 { | ||
| 111 | if (self.type == .debug) return self.section_index; | ||
| 112 | return self.index; | ||
| 113 | } | ||
| 102 | }; | 114 | }; |
| 103 | 115 | ||
| 104 | pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError; | 116 | pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError; |
| ... | @@ -111,6 +123,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz | ... | @@ -111,6 +123,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz |
| 111 | var object: Object = .{ | 123 | var object: Object = .{ |
| 112 | .file = file, | 124 | .file = file, |
| 113 | .name = try gpa.dupe(u8, name), | 125 | .name = try gpa.dupe(u8, name), |
| 126 | .debug_names = &.{}, | ||
| 114 | }; | 127 | }; |
| 115 | 128 | ||
| 116 | var is_object_file: bool = false; | 129 | var is_object_file: bool = false; |
| ... | @@ -141,6 +154,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz | ... | @@ -141,6 +154,9 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz |
| 141 | /// Frees all memory of `Object` at once. The given `Allocator` must be | 154 | /// Frees all memory of `Object` at once. The given `Allocator` must be |
| 142 | /// the same allocator that was used when `init` was called. | 155 | /// the same allocator that was used when `init` was called. |
| 143 | pub fn deinit(self: *Object, gpa: Allocator) void { | 156 | pub fn deinit(self: *Object, gpa: Allocator) void { |
| 157 | if (self.file) |file| { | ||
| 158 | file.close(); | ||
| 159 | } | ||
| 144 | for (self.func_types) |func_ty| { | 160 | for (self.func_types) |func_ty| { |
| 145 | gpa.free(func_ty.params); | 161 | gpa.free(func_ty.params); |
| 146 | gpa.free(func_ty.returns); | 162 | gpa.free(func_ty.returns); |
| ... | @@ -197,6 +213,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32 | ... | @@ -197,6 +213,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32 |
| 197 | } else i; | 213 | } else i; |
| 198 | } | 214 | } |
| 199 | 215 | ||
| 216 | /// From a given `RelocatableDate`, find the corresponding debug section name | ||
| 217 | pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 { | ||
| 218 | return self.string_table.get(relocatable_data.index); | ||
| 219 | } | ||
| 220 | |||
| 200 | /// Checks if the object file is an MVP version. | 221 | /// Checks if the object file is an MVP version. |
| 201 | /// When that's the case, we check if there's an import table definiton with its name | 222 | /// When that's the case, we check if there's an import table definiton with its name |
| 202 | /// set to '__indirect_function_table". When that's also the case, | 223 | /// set to '__indirect_function_table". When that's also the case, |
| ... | @@ -328,10 +349,15 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -328,10 +349,15 @@ fn Parser(comptime ReaderType: type) type { |
| 328 | 349 | ||
| 329 | self.object.version = version; | 350 | self.object.version = version; |
| 330 | var relocatable_data = std.ArrayList(RelocatableData).init(gpa); | 351 | var relocatable_data = std.ArrayList(RelocatableData).init(gpa); |
| 331 | 352 | var debug_names = std.ArrayList(u8).init(gpa); | |
| 332 | errdefer while (relocatable_data.popOrNull()) |rel_data| { | 353 | |
| 333 | gpa.free(rel_data.data[0..rel_data.size]); | 354 | errdefer { |
| 334 | } else relocatable_data.deinit(); | 355 | while (relocatable_data.popOrNull()) |rel_data| { |
| 356 | gpa.free(rel_data.data[0..rel_data.size]); | ||
| 357 | } else relocatable_data.deinit(); | ||
| 358 | gpa.free(debug_names.items); | ||
| 359 | debug_names.deinit(); | ||
| 360 | } | ||
| 335 | 361 | ||
| 336 | var section_index: u32 = 0; | 362 | var section_index: u32 = 0; |
| 337 | while (self.reader.reader().readByte()) |byte| : (section_index += 1) { | 363 | while (self.reader.reader().readByte()) |byte| : (section_index += 1) { |
| ... | @@ -347,11 +373,26 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -347,11 +373,26 @@ fn Parser(comptime ReaderType: type) type { |
| 347 | 373 | ||
| 348 | if (std.mem.eql(u8, name, "linking")) { | 374 | if (std.mem.eql(u8, name, "linking")) { |
| 349 | is_object_file.* = true; | 375 | is_object_file.* = true; |
| 376 | self.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them. | ||
| 350 | try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left)); | 377 | try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left)); |
| 351 | } else if (std.mem.startsWith(u8, name, "reloc")) { | 378 | } else if (std.mem.startsWith(u8, name, "reloc")) { |
| 352 | try self.parseRelocations(gpa); | 379 | try self.parseRelocations(gpa); |
| 353 | } else if (std.mem.eql(u8, name, "target_features")) { | 380 | } else if (std.mem.eql(u8, name, "target_features")) { |
| 354 | try self.parseFeatures(gpa); | 381 | try self.parseFeatures(gpa); |
| 382 | } else if (std.mem.startsWith(u8, name, ".debug")) { | ||
| 383 | const debug_size = @intCast(u32, reader.context.bytes_left); | ||
| 384 | const debug_content = try gpa.alloc(u8, debug_size); | ||
| 385 | errdefer gpa.free(debug_content); | ||
| 386 | try reader.readNoEof(debug_content); | ||
| 387 | |||
| 388 | try relocatable_data.append(.{ | ||
| 389 | .type = .debug, | ||
| 390 | .data = debug_content.ptr, | ||
| 391 | .size = debug_size, | ||
| 392 | .index = try self.object.string_table.put(gpa, name), | ||
| 393 | .offset = 0, // debug sections only contain 1 entry, so no need to calculate offset | ||
| 394 | .section_index = section_index, | ||
| 395 | }); | ||
| 355 | } else { | 396 | } else { |
| 356 | try reader.skipBytes(reader.context.bytes_left, .{}); | 397 | try reader.skipBytes(reader.context.bytes_left, .{}); |
| 357 | } | 398 | } |
| ... | @@ -737,7 +778,12 @@ fn Parser(comptime ReaderType: type) type { | ... | @@ -737,7 +778,12 @@ fn Parser(comptime ReaderType: type) type { |
| 737 | }, | 778 | }, |
| 738 | .section => { | 779 | .section => { |
| 739 | symbol.index = try leb.readULEB128(u32, reader); | 780 | symbol.index = try leb.readULEB128(u32, reader); |
| 740 | symbol.name = try self.object.string_table.put(gpa, @tagName(symbol.tag)); | 781 | for (self.object.relocatable_data) |data| { |
| 782 | if (data.section_index == symbol.index) { | ||
| 783 | symbol.name = data.index; | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | } | ||
| 741 | }, | 787 | }, |
| 742 | else => { | 788 | else => { |
| 743 | symbol.index = try leb.readULEB128(u32, reader); | 789 | symbol.index = try leb.readULEB128(u32, reader); |
| ... | @@ -827,7 +873,6 @@ fn assertEnd(reader: anytype) !void { | ... | @@ -827,7 +873,6 @@ fn assertEnd(reader: anytype) !void { |
| 827 | 873 | ||
| 828 | /// Parses an object file into atoms, for code and data sections | 874 | /// Parses an object file into atoms, for code and data sections |
| 829 | pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { | 875 | pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { |
| 830 | log.debug("Parsing data section into atoms", .{}); | ||
| 831 | const Key = struct { | 876 | const Key = struct { |
| 832 | kind: Symbol.Tag, | 877 | kind: Symbol.Tag, |
| 833 | index: u32, | 878 | index: u32, |
| ... | @@ -839,7 +884,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin | ... | @@ -839,7 +884,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 839 | 884 | ||
| 840 | for (self.symtable) |symbol, symbol_index| { | 885 | for (self.symtable) |symbol, symbol_index| { |
| 841 | switch (symbol.tag) { | 886 | switch (symbol.tag) { |
| 842 | .function, .data => if (!symbol.isUndefined()) { | 887 | .function, .data, .section => if (!symbol.isUndefined()) { |
| 843 | const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); | 888 | const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); |
| 844 | const sym_idx = @intCast(u32, symbol_index); | 889 | const sym_idx = @intCast(u32, symbol_index); |
| 845 | if (!gop.found_existing) { | 890 | if (!gop.found_existing) { |
| ... | @@ -852,12 +897,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin | ... | @@ -852,12 +897,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 852 | } | 897 | } |
| 853 | 898 | ||
| 854 | for (self.relocatable_data) |relocatable_data, index| { | 899 | for (self.relocatable_data) |relocatable_data, index| { |
| 855 | const symbols = symbol_for_segment.getPtr(.{ | 900 | const final_index = (try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index))) orelse { |
| 856 | .kind = relocatable_data.getSymbolKind(), | 901 | continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. |
| 857 | .index = @intCast(u32, relocatable_data.index), | 902 | }; |
| 858 | }) orelse continue; // encountered a segment we do not create an atom for | ||
| 859 | const sym_index = symbols.pop(); | ||
| 860 | const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index)); | ||
| 861 | 903 | ||
| 862 | const atom = try gpa.create(Atom); | 904 | const atom = try gpa.create(Atom); |
| 863 | atom.* = Atom.empty; | 905 | atom.* = Atom.empty; |
| ... | @@ -870,7 +912,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin | ... | @@ -870,7 +912,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 870 | atom.file = object_index; | 912 | atom.file = object_index; |
| 871 | atom.size = relocatable_data.size; | 913 | atom.size = relocatable_data.size; |
| 872 | atom.alignment = relocatable_data.getAlignment(self); | 914 | atom.alignment = relocatable_data.getAlignment(self); |
| 873 | atom.sym_index = sym_index; | ||
| 874 | 915 | ||
| 875 | const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{}; | 916 | const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{}; |
| 876 | for (relocations) |relocation| { | 917 | for (relocations) |relocation| { |
| ... | @@ -892,28 +933,31 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin | ... | @@ -892,28 +933,31 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 892 | 933 | ||
| 893 | try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); | 934 | try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); |
| 894 | 935 | ||
| 895 | // symbols referencing the same atom will be added as alias | 936 | if (symbol_for_segment.getPtr(.{ |
| 896 | // or as 'parent' when they are global. | 937 | .kind = relocatable_data.getSymbolKind(), |
| 897 | while (symbols.popOrNull()) |idx| { | 938 | .index = relocatable_data.getIndex(), |
| 898 | const alias_symbol = self.symtable[idx]; | 939 | })) |symbols| { |
| 899 | const symbol = self.symtable[atom.sym_index]; | 940 | atom.sym_index = symbols.pop(); |
| 900 | if (alias_symbol.isGlobal() and symbol.isLocal()) { | 941 | |
| 901 | atom.sym_index = idx; | 942 | // symbols referencing the same atom will be added as alias |
| 943 | // or as 'parent' when they are global. | ||
| 944 | while (symbols.popOrNull()) |idx| { | ||
| 945 | const alias_symbol = self.symtable[idx]; | ||
| 946 | const symbol = self.symtable[atom.sym_index]; | ||
| 947 | if (alias_symbol.isGlobal() and symbol.isLocal()) { | ||
| 948 | atom.sym_index = idx; | ||
| 949 | } | ||
| 902 | } | 950 | } |
| 951 | try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); | ||
| 903 | } | 952 | } |
| 904 | try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); | ||
| 905 | 953 | ||
| 906 | const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; | 954 | const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; |
| 907 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | 955 | if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned |
| 908 | 956 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | |
| 909 | if (wasm_bin.atoms.getPtr(final_index)) |last| { | ||
| 910 | last.*.next = atom; | ||
| 911 | atom.prev = last.*; | ||
| 912 | last.* = atom; | ||
| 913 | } else { | ||
| 914 | try wasm_bin.atoms.putNoClobber(gpa, final_index, atom); | ||
| 915 | } | 957 | } |
| 916 | log.debug("Parsed into atom: '{s}'", .{self.string_table.get(self.symtable[atom.sym_index].name)}); | 958 | |
| 959 | try wasm_bin.appendAtomAtIndex(final_index, atom); | ||
| 960 | log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index }); | ||
| 917 | } | 961 | } |
| 918 | } | 962 | } |
| 919 | 963 |
src/link/strtab.zig+4| ... | @@ -110,6 +110,10 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { | ... | @@ -110,6 +110,10 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { |
| 110 | return self.get(off) orelse unreachable; | 110 | return self.get(off) orelse unreachable; |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | pub fn items(self: Self) []const u8 { | ||
| 114 | return self.buffer.items; | ||
| 115 | } | ||
| 116 | |||
| 113 | pub fn len(self: Self) usize { | 117 | pub fn len(self: Self) usize { |
| 114 | return self.buffer.items.len; | 118 | return self.buffer.items.len; |
| 115 | } | 119 | } |
src/main.zig+20-9| ... | @@ -268,7 +268,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -268,7 +268,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 268 | } else if (mem.eql(u8, cmd, "init-lib")) { | 268 | } else if (mem.eql(u8, cmd, "init-lib")) { |
| 269 | return cmdInit(gpa, arena, cmd_args, .Lib); | 269 | return cmdInit(gpa, arena, cmd_args, .Lib); |
| 270 | } else if (mem.eql(u8, cmd, "targets")) { | 270 | } else if (mem.eql(u8, cmd, "targets")) { |
| 271 | const info = try detectNativeTargetInfo(arena, .{}); | 271 | const info = try detectNativeTargetInfo(.{}); |
| 272 | const stdout = io.getStdOut().writer(); | 272 | const stdout = io.getStdOut().writer(); |
| 273 | return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); | 273 | return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); |
| 274 | } else if (mem.eql(u8, cmd, "version")) { | 274 | } else if (mem.eql(u8, cmd, "version")) { |
| ... | @@ -691,6 +691,9 @@ fn buildOutputType( | ... | @@ -691,6 +691,9 @@ fn buildOutputType( |
| 691 | var linker_max_memory: ?u64 = null; | 691 | var linker_max_memory: ?u64 = null; |
| 692 | var linker_shared_memory: bool = false; | 692 | var linker_shared_memory: bool = false; |
| 693 | var linker_global_base: ?u64 = null; | 693 | var linker_global_base: ?u64 = null; |
| 694 | var linker_print_gc_sections: bool = false; | ||
| 695 | var linker_print_icf_sections: bool = false; | ||
| 696 | var linker_print_map: bool = false; | ||
| 694 | var linker_z_nodelete = false; | 697 | var linker_z_nodelete = false; |
| 695 | var linker_z_notext = false; | 698 | var linker_z_notext = false; |
| 696 | var linker_z_defs = false; | 699 | var linker_z_defs = false; |
| ... | @@ -1816,6 +1819,12 @@ fn buildOutputType( | ... | @@ -1816,6 +1819,12 @@ fn buildOutputType( |
| 1816 | linker_gc_sections = true; | 1819 | linker_gc_sections = true; |
| 1817 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { | 1820 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { |
| 1818 | linker_gc_sections = false; | 1821 | linker_gc_sections = false; |
| 1822 | } else if (mem.eql(u8, arg, "--print-gc-sections")) { | ||
| 1823 | linker_print_gc_sections = true; | ||
| 1824 | } else if (mem.eql(u8, arg, "--print-icf-sections")) { | ||
| 1825 | linker_print_icf_sections = true; | ||
| 1826 | } else if (mem.eql(u8, arg, "--print-map")) { | ||
| 1827 | linker_print_map = true; | ||
| 1819 | } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or | 1828 | } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or |
| 1820 | mem.eql(u8, arg, "-allow-shlib-undefined")) | 1829 | mem.eql(u8, arg, "-allow-shlib-undefined")) |
| 1821 | { | 1830 | { |
| ... | @@ -2258,7 +2267,7 @@ fn buildOutputType( | ... | @@ -2258,7 +2267,7 @@ fn buildOutputType( |
| 2258 | } | 2267 | } |
| 2259 | 2268 | ||
| 2260 | const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options); | 2269 | const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options); |
| 2261 | const target_info = try detectNativeTargetInfo(gpa, cross_target); | 2270 | const target_info = try detectNativeTargetInfo(cross_target); |
| 2262 | 2271 | ||
| 2263 | if (target_info.target.os.tag != .freestanding) { | 2272 | if (target_info.target.os.tag != .freestanding) { |
| 2264 | if (ensure_libc_on_non_freestanding) | 2273 | if (ensure_libc_on_non_freestanding) |
| ... | @@ -2911,6 +2920,9 @@ fn buildOutputType( | ... | @@ -2911,6 +2920,9 @@ fn buildOutputType( |
| 2911 | .linker_initial_memory = linker_initial_memory, | 2920 | .linker_initial_memory = linker_initial_memory, |
| 2912 | .linker_max_memory = linker_max_memory, | 2921 | .linker_max_memory = linker_max_memory, |
| 2913 | .linker_shared_memory = linker_shared_memory, | 2922 | .linker_shared_memory = linker_shared_memory, |
| 2923 | .linker_print_gc_sections = linker_print_gc_sections, | ||
| 2924 | .linker_print_icf_sections = linker_print_icf_sections, | ||
| 2925 | .linker_print_map = linker_print_map, | ||
| 2914 | .linker_global_base = linker_global_base, | 2926 | .linker_global_base = linker_global_base, |
| 2915 | .linker_export_symbol_names = linker_export_symbol_names.items, | 2927 | .linker_export_symbol_names = linker_export_symbol_names.items, |
| 2916 | .linker_z_nodelete = linker_z_nodelete, | 2928 | .linker_z_nodelete = linker_z_nodelete, |
| ... | @@ -3271,7 +3283,7 @@ fn runOrTest( | ... | @@ -3271,7 +3283,7 @@ fn runOrTest( |
| 3271 | if (std.process.can_execv and arg_mode == .run and !watch) { | 3283 | if (std.process.can_execv and arg_mode == .run and !watch) { |
| 3272 | // execv releases the locks; no need to destroy the Compilation here. | 3284 | // execv releases the locks; no need to destroy the Compilation here. |
| 3273 | const err = std.process.execv(gpa, argv.items); | 3285 | const err = std.process.execv(gpa, argv.items); |
| 3274 | try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); | 3286 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); |
| 3275 | const cmd = try std.mem.join(arena, " ", argv.items); | 3287 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 3276 | fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); | 3288 | fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); |
| 3277 | } else if (std.process.can_spawn) { | 3289 | } else if (std.process.can_spawn) { |
| ... | @@ -3288,7 +3300,7 @@ fn runOrTest( | ... | @@ -3288,7 +3300,7 @@ fn runOrTest( |
| 3288 | } | 3300 | } |
| 3289 | 3301 | ||
| 3290 | const term = child.spawnAndWait() catch |err| { | 3302 | const term = child.spawnAndWait() catch |err| { |
| 3291 | try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); | 3303 | try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); |
| 3292 | const cmd = try std.mem.join(arena, " ", argv.items); | 3304 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 3293 | fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); | 3305 | fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); |
| 3294 | }; | 3306 | }; |
| ... | @@ -3902,7 +3914,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -3902,7 +3914,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 3902 | gimmeMoreOfThoseSweetSweetFileDescriptors(); | 3914 | gimmeMoreOfThoseSweetSweetFileDescriptors(); |
| 3903 | 3915 | ||
| 3904 | const cross_target: std.zig.CrossTarget = .{}; | 3916 | const cross_target: std.zig.CrossTarget = .{}; |
| 3905 | const target_info = try detectNativeTargetInfo(gpa, cross_target); | 3917 | const target_info = try detectNativeTargetInfo(cross_target); |
| 3906 | 3918 | ||
| 3907 | const exe_basename = try std.zig.binNameAlloc(arena, .{ | 3919 | const exe_basename = try std.zig.binNameAlloc(arena, .{ |
| 3908 | .root_name = "build", | 3920 | .root_name = "build", |
| ... | @@ -4944,8 +4956,8 @@ test "fds" { | ... | @@ -4944,8 +4956,8 @@ test "fds" { |
| 4944 | gimmeMoreOfThoseSweetSweetFileDescriptors(); | 4956 | gimmeMoreOfThoseSweetSweetFileDescriptors(); |
| 4945 | } | 4957 | } |
| 4946 | 4958 | ||
| 4947 | fn detectNativeTargetInfo(gpa: Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { | 4959 | fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { |
| 4948 | return std.zig.system.NativeTargetInfo.detect(gpa, cross_target); | 4960 | return std.zig.system.NativeTargetInfo.detect(cross_target); |
| 4949 | } | 4961 | } |
| 4950 | 4962 | ||
| 4951 | /// Indicate that we are now terminating with a successful exit code. | 4963 | /// Indicate that we are now terminating with a successful exit code. |
| ... | @@ -5308,14 +5320,13 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { | ... | @@ -5308,14 +5320,13 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { |
| 5308 | } | 5320 | } |
| 5309 | 5321 | ||
| 5310 | fn warnAboutForeignBinaries( | 5322 | fn warnAboutForeignBinaries( |
| 5311 | gpa: Allocator, | ||
| 5312 | arena: Allocator, | 5323 | arena: Allocator, |
| 5313 | arg_mode: ArgMode, | 5324 | arg_mode: ArgMode, |
| 5314 | target_info: std.zig.system.NativeTargetInfo, | 5325 | target_info: std.zig.system.NativeTargetInfo, |
| 5315 | link_libc: bool, | 5326 | link_libc: bool, |
| 5316 | ) !void { | 5327 | ) !void { |
| 5317 | const host_cross_target: std.zig.CrossTarget = .{}; | 5328 | const host_cross_target: std.zig.CrossTarget = .{}; |
| 5318 | const host_target_info = try detectNativeTargetInfo(gpa, host_cross_target); | 5329 | const host_target_info = try detectNativeTargetInfo(host_cross_target); |
| 5319 | 5330 | ||
| 5320 | switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) { | 5331 | switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) { |
| 5321 | .native => return, | 5332 | .native => return, |
src/test.zig+10-2| ... | @@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct { | ... | @@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct { |
| 177 | inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { | 177 | inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { |
| 178 | defaults = defaults ++ arch ++ "-macos" ++ ","; | 178 | defaults = defaults ++ arch ++ "-macos" ++ ","; |
| 179 | } | 179 | } |
| 180 | // Windows | ||
| 181 | defaults = defaults ++ "x86_64-windows" ++ ","; | ||
| 180 | // Wasm | 182 | // Wasm |
| 181 | defaults = defaults ++ "wasm32-wasi"; | 183 | defaults = defaults ++ "wasm32-wasi"; |
| 182 | return defaults; | 184 | return defaults; |
| ... | @@ -1211,7 +1213,7 @@ pub const TestContext = struct { | ... | @@ -1211,7 +1213,7 @@ pub const TestContext = struct { |
| 1211 | } | 1213 | } |
| 1212 | 1214 | ||
| 1213 | fn run(self: *TestContext) !void { | 1215 | fn run(self: *TestContext) !void { |
| 1214 | const host = try std.zig.system.NativeTargetInfo.detect(self.gpa, .{}); | 1216 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); |
| 1215 | 1217 | ||
| 1216 | var progress = std.Progress{}; | 1218 | var progress = std.Progress{}; |
| 1217 | const root_node = progress.start("compiler", self.cases.items.len); | 1219 | const root_node = progress.start("compiler", self.cases.items.len); |
| ... | @@ -1300,7 +1302,7 @@ pub const TestContext = struct { | ... | @@ -1300,7 +1302,7 @@ pub const TestContext = struct { |
| 1300 | global_cache_directory: Compilation.Directory, | 1302 | global_cache_directory: Compilation.Directory, |
| 1301 | host: std.zig.system.NativeTargetInfo, | 1303 | host: std.zig.system.NativeTargetInfo, |
| 1302 | ) !void { | 1304 | ) !void { |
| 1303 | const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target); | 1305 | const target_info = try std.zig.system.NativeTargetInfo.detect(case.target); |
| 1304 | const target = target_info.target; | 1306 | const target = target_info.target; |
| 1305 | 1307 | ||
| 1306 | var arena_allocator = std.heap.ArenaAllocator.init(allocator); | 1308 | var arena_allocator = std.heap.ArenaAllocator.init(allocator); |
| ... | @@ -1546,6 +1548,12 @@ pub const TestContext = struct { | ... | @@ -1546,6 +1548,12 @@ pub const TestContext = struct { |
| 1546 | .self_exe_path = std.testing.zig_exe_path, | 1548 | .self_exe_path = std.testing.zig_exe_path, |
| 1547 | // TODO instead of turning off color, pass in a std.Progress.Node | 1549 | // TODO instead of turning off color, pass in a std.Progress.Node |
| 1548 | .color = .off, | 1550 | .color = .off, |
| 1551 | // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in | ||
| 1552 | // until the auto-select mechanism deems them worthy | ||
| 1553 | .use_lld = switch (case.backend) { | ||
| 1554 | .stage2 => false, | ||
| 1555 | else => null, | ||
| 1556 | }, | ||
| 1549 | }); | 1557 | }); |
| 1550 | defer comp.destroy(); | 1558 | defer comp.destroy(); |
| 1551 | 1559 |
src/translate_c.zig+9-2| ... | @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD | ... | @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1167 | } | 1167 | } |
| 1168 | 1168 | ||
| 1169 | if (!c.zig_is_stage1 and is_packed) { | 1169 | if (!c.zig_is_stage1 and is_packed) { |
| 1170 | return failDecl(c, record_loc, bare_name, "cannot translate packed record union", .{}); | 1170 | return failDecl(c, record_loc, name, "cannot translate packed record union", .{}); |
| 1171 | } | 1171 | } |
| 1172 | 1172 | ||
| 1173 | const record_payload = try c.arena.create(ast.Payload.Record); | 1173 | const record_payload = try c.arena.create(ast.Payload.Record); |
| ... | @@ -5799,7 +5799,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { | ... | @@ -5799,7 +5799,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5799 | } | 5799 | } |
| 5800 | } | 5800 | } |
| 5801 | for (source) |c| { | 5801 | for (source) |c| { |
| 5802 | if (c == '\\') { | 5802 | if (c == '\\' or c == '\t') { |
| 5803 | break; | 5803 | break; |
| 5804 | } | 5804 | } |
| 5805 | } else return source; | 5805 | } else return source; |
| ... | @@ -5876,6 +5876,13 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { | ... | @@ -5876,6 +5876,13 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5876 | state = .Start; | 5876 | state = .Start; |
| 5877 | }, | 5877 | }, |
| 5878 | .Start => { | 5878 | .Start => { |
| 5879 | if (c == '\t') { | ||
| 5880 | bytes[i] = '\\'; | ||
| 5881 | i += 1; | ||
| 5882 | bytes[i] = 't'; | ||
| 5883 | i += 1; | ||
| 5884 | continue; | ||
| 5885 | } | ||
| 5879 | if (c == '\\') { | 5886 | if (c == '\\') { |
| 5880 | state = .Escape; | 5887 | state = .Escape; |
| 5881 | } | 5888 | } |
test/behavior.zig+1| ... | @@ -86,6 +86,7 @@ test { | ... | @@ -86,6 +86,7 @@ test { |
| 86 | _ = @import("behavior/bugs/12430.zig"); | 86 | _ = @import("behavior/bugs/12430.zig"); |
| 87 | _ = @import("behavior/bugs/12486.zig"); | 87 | _ = @import("behavior/bugs/12486.zig"); |
| 88 | _ = @import("behavior/bugs/12680.zig"); | 88 | _ = @import("behavior/bugs/12680.zig"); |
| 89 | _ = @import("behavior/bugs/12776.zig"); | ||
| 89 | _ = @import("behavior/byteswap.zig"); | 90 | _ = @import("behavior/byteswap.zig"); |
| 90 | _ = @import("behavior/byval_arg_var.zig"); | 91 | _ = @import("behavior/byval_arg_var.zig"); |
| 91 | _ = @import("behavior/call.zig"); | 92 | _ = @import("behavior/call.zig"); |
test/behavior/bugs/12776.zig created+42| ... | @@ -0,0 +1,42 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const RAM = struct { | ||
| 5 | data: [0xFFFF + 1]u8, | ||
| 6 | fn new() !RAM { | ||
| 7 | return RAM{ .data = [_]u8{0} ** 0x10000 }; | ||
| 8 | } | ||
| 9 | fn get(self: *RAM, addr: u16) u8 { | ||
| 10 | return self.data[addr]; | ||
| 11 | } | ||
| 12 | }; | ||
| 13 | |||
| 14 | const CPU = packed struct { | ||
| 15 | interrupts: bool, | ||
| 16 | ram: *RAM, | ||
| 17 | fn new(ram: *RAM) !CPU { | ||
| 18 | return CPU{ | ||
| 19 | .ram = ram, | ||
| 20 | .interrupts = false, | ||
| 21 | }; | ||
| 22 | } | ||
| 23 | fn tick(self: *CPU) !void { | ||
| 24 | var queued_interrupts = self.ram.get(0xFFFF) & self.ram.get(0xFF0F); | ||
| 25 | if (self.interrupts and queued_interrupts != 0) { | ||
| 26 | self.interrupts = false; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | test { | ||
| 32 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 33 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 34 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 35 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 36 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 37 | |||
| 38 | var ram = try RAM.new(); | ||
| 39 | var cpu = try CPU.new(&ram); | ||
| 40 | try cpu.tick(); | ||
| 41 | try std.testing.expect(cpu.interrupts == false); | ||
| 42 | } | ||
test/behavior/pointers.zig+11| ... | @@ -486,3 +486,14 @@ test "array slicing to slice" { | ... | @@ -486,3 +486,14 @@ test "array slicing to slice" { |
| 486 | try S.doTheTest(); | 486 | try S.doTheTest(); |
| 487 | comptime try S.doTheTest(); | 487 | comptime try S.doTheTest(); |
| 488 | } | 488 | } |
| 489 | |||
| 490 | test "pointer to constant decl preserves alignment" { | ||
| 491 | const S = struct { | ||
| 492 | a: u8, | ||
| 493 | b: u8, | ||
| 494 | const aligned align(8) = @This(){ .a = 3, .b = 4 }; | ||
| 495 | }; | ||
| 496 | |||
| 497 | const alignment = @typeInfo(@TypeOf(&S.aligned)).Pointer.alignment; | ||
| 498 | try std.testing.expect(alignment == 8); | ||
| 499 | } |
test/behavior/translate_c_macros.h+2| ... | @@ -50,3 +50,5 @@ typedef _Bool uintptr_t; | ... | @@ -50,3 +50,5 @@ typedef _Bool uintptr_t; |
| 50 | #define CAST_TO_UINTPTR(X) (uintptr_t)(X) | 50 | #define CAST_TO_UINTPTR(X) (uintptr_t)(X) |
| 51 | 51 | ||
| 52 | #define LARGE_INT 18446744073709550592 | 52 | #define LARGE_INT 18446744073709550592 |
| 53 | |||
| 54 | #define EMBEDDED_TAB "hello	" |
test/behavior/translate_c_macros.zig+11| ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); | ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const expectEqualStrings = std.testing.expectEqualStrings; | ||
| 5 | 6 | ||
| 6 | const h = @cImport(@cInclude("behavior/translate_c_macros.h")); | 7 | const h = @cImport(@cInclude("behavior/translate_c_macros.h")); |
| 7 | 8 | ||
| ... | @@ -123,3 +124,13 @@ test "large integer macro" { | ... | @@ -123,3 +124,13 @@ test "large integer macro" { |
| 123 | 124 | ||
| 124 | try expectEqual(@as(c_ulonglong, 18446744073709550592), h.LARGE_INT); | 125 | try expectEqual(@as(c_ulonglong, 18446744073709550592), h.LARGE_INT); |
| 125 | } | 126 | } |
| 127 | |||
| 128 | test "string literal macro with embedded tab character" { | ||
| 129 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 130 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 131 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 132 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 133 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 134 | |||
| 135 | try expectEqualStrings("hello\t", h.EMBEDDED_TAB); | ||
| 136 | } |
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=aarch64-macos | 3 | // target=aarch64-macos |
| 4 | // | 4 | // |
| 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' | 5 | // :109:9: error: struct 'tmp.tmp' has no member named 'main' |
| 6 | // :7:1: note: struct declared here | 6 | // :7:1: note: struct declared here |
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | pub inline fn instanceRequestAdapter() void {} | ||
| 2 | |||
| 3 | pub inline fn requestAdapter( | ||
| 4 | comptime callbackArg: fn () callconv(.Inline) void, | ||
| 5 | ) void { | ||
| 6 | _ = (struct { | ||
| 7 | pub fn callback() callconv(.C) void { | ||
| 8 | callbackArg(); | ||
| 9 | } | ||
| 10 | }).callback; | ||
| 11 | instanceRequestAdapter(undefined); // note wrong number of arguments here | ||
| 12 | } | ||
| 13 | |||
| 14 | inline fn foo() void {} | ||
| 15 | |||
| 16 | pub export fn entry() void { | ||
| 17 | requestAdapter(foo); | ||
| 18 | } | ||
| 19 | |||
| 20 | // error | ||
| 21 | // backend=stage2 | ||
| 22 | // target=native | ||
| 23 | // | ||
| 24 | // :11:5: error: expected 0 argument(s), found 1 | ||
| 25 | // :1:12: note: function declared here | ||
| 26 | // :17:19: note: called from here | ||
test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | fn Observable(comptime T: type) type { | ||
| 2 | return struct { | ||
| 3 | fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst { | ||
| 4 | _ = Src; | ||
| 5 | _ = function; | ||
| 6 | return Observable(Dst); | ||
| 7 | } | ||
| 8 | }; | ||
| 9 | } | ||
| 10 | |||
| 11 | fn u32Tou64(x: u32) u64 { | ||
| 12 | _ = x; | ||
| 13 | return 0; | ||
| 14 | } | ||
| 15 | |||
| 16 | pub export fn entry() void { | ||
| 17 | Observable(u32).map(u32, u64, u32Tou64(0)); | ||
| 18 | } | ||
| 19 | |||
| 20 | // error | ||
| 21 | // backend=stage2 | ||
| 22 | // target=native | ||
| 23 | // | ||
| 24 | // :17:25: error: expected type 'u32', found 'type' | ||
test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | pub export fn entry() void { | ||
| 2 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 3 | var slice: []u8 = &buf; | ||
| 4 | const a: u32 = 1234; | ||
| 5 | @memcpy(slice, @ptrCast([*]const u8, &a), 4); | ||
| 6 | } | ||
| 7 | pub export fn entry1() void { | ||
| 8 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 9 | var ptr: *u8 = &buf[0]; | ||
| 10 | @memcpy(ptr, 0, 4); | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // backend=stage2 | ||
| 15 | // target=native | ||
| 16 | // | ||
| 17 | // :5:13: error: expected type '[*]u8', found '[]u8' | ||
| 18 | // :10:13: error: expected type '[*]u8', found '*u8' | ||
| 19 | // :10:13: note: a single pointer cannot cast into a many pointer | ||
test/cases/x86_64-linux/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=x86_64-linux | 3 | // target=x86_64-linux |
| 4 | // | 4 | // |
| 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' | 5 | // :109:9: error: struct 'tmp.tmp' has no member named 'main' |
| 6 | // :7:1: note: struct declared here | 6 | // :7:1: note: struct declared here |
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=x86_64-macos | 3 | // target=x86_64-macos |
| 4 | // | 4 | // |
| 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' | 5 | // :109:9: error: struct 'tmp.tmp' has no member named 'main' |
| 6 | // :7:1: note: struct declared here | 6 | // :7:1: note: struct declared here |
test/cases/x86_64-windows/hello_world_with_updates.0.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | // error | ||
| 2 | // output_mode=Exe | ||
| 3 | // target=x86_64-windows | ||
| 4 | // | ||
| 5 | // :130:9: error: struct 'tmp.tmp' has no member named 'main' | ||
| 6 | // :7:1: note: struct declared here | ||
test/cases/x86_64-windows/hello_world_with_updates.1.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | pub export fn main() noreturn {} | ||
| 2 | |||
| 3 | // error | ||
| 4 | // | ||
| 5 | // :1:32: error: function declared 'noreturn' returns | ||
| 6 | // :1:22: note: 'noreturn' declared here | ||
test/cases/x86_64-windows/hello_world_with_updates.2.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn main() void { | ||
| 4 | print(); | ||
| 5 | } | ||
| 6 | |||
| 7 | fn print() void { | ||
| 8 | const msg = "Hello, World!\n"; | ||
| 9 | const stdout = std.io.getStdOut(); | ||
| 10 | stdout.writeAll(msg) catch unreachable; | ||
| 11 | } | ||
| 12 | |||
| 13 | // run | ||
| 14 | // | ||
| 15 | // Hello, World! | ||
| 16 | // | ||
test/link.zig+7-7| ... | @@ -28,35 +28,35 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -28,35 +28,35 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | fn addWasmCases(cases: *tests.StandaloneContext) void { | 30 | fn addWasmCases(cases: *tests.StandaloneContext) void { |
| 31 | cases.addBuildFile("test/link/wasm/bss/build.zig", .{ | 31 | cases.addBuildFile("test/link/wasm/archive/build.zig", .{ |
| 32 | .build_modes = true, | 32 | .build_modes = true, |
| 33 | .requires_stage2 = true, | 33 | .requires_stage2 = true, |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | cases.addBuildFile("test/link/wasm/segments/build.zig", .{ | 36 | cases.addBuildFile("test/link/wasm/bss/build.zig", .{ |
| 37 | .build_modes = true, | 37 | .build_modes = true, |
| 38 | .requires_stage2 = true, | 38 | .requires_stage2 = true, |
| 39 | }); | 39 | }); |
| 40 | 40 | ||
| 41 | cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ | 41 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ |
| 42 | .build_modes = true, | 42 | .build_modes = true, |
| 43 | .requires_stage2 = true, | 43 | .requires_stage2 = true, |
| 44 | .use_emulation = true, | ||
| 44 | }); | 45 | }); |
| 45 | 46 | ||
| 46 | cases.addBuildFile("test/link/wasm/type/build.zig", .{ | 47 | cases.addBuildFile("test/link/wasm/segments/build.zig", .{ |
| 47 | .build_modes = true, | 48 | .build_modes = true, |
| 48 | .requires_stage2 = true, | 49 | .requires_stage2 = true, |
| 49 | }); | 50 | }); |
| 50 | 51 | ||
| 51 | cases.addBuildFile("test/link/wasm/archive/build.zig", .{ | 52 | cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ |
| 52 | .build_modes = true, | 53 | .build_modes = true, |
| 53 | .requires_stage2 = true, | 54 | .requires_stage2 = true, |
| 54 | }); | 55 | }); |
| 55 | 56 | ||
| 56 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ | 57 | cases.addBuildFile("test/link/wasm/type/build.zig", .{ |
| 57 | .build_modes = true, | 58 | .build_modes = true, |
| 58 | .requires_stage2 = true, | 59 | .requires_stage2 = true, |
| 59 | .use_emulation = true, | ||
| 60 | }); | 60 | }); |
| 61 | } | 61 | } |
| 62 | 62 |
test/tests.zig+10| ... | @@ -108,6 +108,14 @@ const test_targets = blk: { | ... | @@ -108,6 +108,14 @@ const test_targets = blk: { |
| 108 | }, | 108 | }, |
| 109 | .backend = .stage2_x86_64, | 109 | .backend = .stage2_x86_64, |
| 110 | }, | 110 | }, |
| 111 | .{ | ||
| 112 | .target = .{ | ||
| 113 | .cpu_arch = .x86_64, | ||
| 114 | .os_tag = .windows, | ||
| 115 | .abi = .gnu, | ||
| 116 | }, | ||
| 117 | .backend = .stage2_x86_64, | ||
| 118 | }, | ||
| 111 | 119 | ||
| 112 | .{ | 120 | .{ |
| 113 | .target = .{ | 121 | .target = .{ |
| ... | @@ -693,6 +701,8 @@ pub fn addPkgTests( | ... | @@ -693,6 +701,8 @@ pub fn addPkgTests( |
| 693 | else => { | 701 | else => { |
| 694 | these_tests.use_stage1 = false; | 702 | these_tests.use_stage1 = false; |
| 695 | these_tests.use_llvm = false; | 703 | these_tests.use_llvm = false; |
| 704 | // TODO: force self-hosted linkers to avoid LLD creeping in until the auto-select mechanism deems them worthy | ||
| 705 | these_tests.use_lld = false; | ||
| 696 | }, | 706 | }, |
| 697 | }; | 707 | }; |
| 698 | 708 |