| 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 | 61 | |
| 62 | 62 | - pwsh: | |
| 63 | 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 | 66 | function CheckLastExitCode { |
| 66 | 67 | if (!$?) { |
| ... | ... | @@ -72,8 +73,7 @@ jobs: |
| 72 | 73 | & "$ZIGINSTALLDIR\bin\zig.exe" build test docs ` |
| 73 | 74 | --search-prefix "$ZIGPREFIXPATH" ` |
| 74 | 75 | -Dstatic-llvm ` |
| 75 | -Dskip-non-native ` | |
| 76 | -Dskip-stage2-tests | |
| 76 | -Dskip-non-native | |
| 77 | 77 | CheckLastExitCode |
| 78 | 78 | name: test |
| 79 | 79 | displayName: 'Test' |
doc/docgen.zig+1-2| ... | ... | @@ -1210,7 +1210,7 @@ fn genHtml( |
| 1210 | 1210 | var env_map = try process.getEnvMap(allocator); |
| 1211 | 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 | 1214 | const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe); |
| 1215 | 1215 | |
| 1216 | 1216 | for (toc.nodes) |node| { |
| ... | ... | @@ -1474,7 +1474,6 @@ fn genHtml( |
| 1474 | 1474 | .arch_os_abi = triple, |
| 1475 | 1475 | }); |
| 1476 | 1476 | const target_info = try std.zig.system.NativeTargetInfo.detect( |
| 1477 | allocator, | |
| 1478 | 1477 | cross_target, |
| 1479 | 1478 | ); |
| 1480 | 1479 | switch (host.getExternalExecutor(target_info, .{ |
lib/std/build.zig+2-2| ... | ... | @@ -171,7 +171,7 @@ pub const Builder = struct { |
| 171 | 171 | const env_map = try allocator.create(EnvMap); |
| 172 | 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 | 176 | const self = try allocator.create(Builder); |
| 177 | 177 | self.* = Builder{ |
| ... | ... | @@ -1798,7 +1798,7 @@ pub const LibExeObjStep = struct { |
| 1798 | 1798 | } |
| 1799 | 1799 | |
| 1800 | 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 | 1802 | unreachable; |
| 1803 | 1803 | |
| 1804 | 1804 | const target = self.target_info.target; |
lib/std/build/EmulatableRunStep.zig+1-1| ... | ... | @@ -158,7 +158,7 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { |
| 158 | 158 | |
| 159 | 159 | const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable; |
| 160 | 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 | 162 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; |
| 163 | 163 | switch (builder.host.getExternalExecutor(target_info, .{ |
| 164 | 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 | 990 | return index; |
| 991 | 991 | } |
| 992 | 992 | |
| 993 | /// On Windows, this function currently does alter the file pointer. | |
| 994 | /// https://github.com/ziglang/zig/issues/12783 | |
| 993 | 995 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 994 | 996 | if (is_windows) { |
| 995 | 997 | return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode); |
| ... | ... | @@ -1004,6 +1006,8 @@ pub const File = struct { |
| 1004 | 1006 | |
| 1005 | 1007 | /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it |
| 1006 | 1008 | /// means the file reached the end. Reaching the end of a file is not an error condition. |
| 1009 | /// On Windows, this function currently does alter the file pointer. | |
| 1010 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1007 | 1011 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 1008 | 1012 | var index: usize = 0; |
| 1009 | 1013 | while (index != buffer.len) { |
| ... | ... | @@ -1058,6 +1062,8 @@ pub const File = struct { |
| 1058 | 1062 | } |
| 1059 | 1063 | |
| 1060 | 1064 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1065 | /// On Windows, this function currently does alter the file pointer. | |
| 1066 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1061 | 1067 | pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { |
| 1062 | 1068 | if (is_windows) { |
| 1063 | 1069 | // TODO improve this to use ReadFileScatter |
| ... | ... | @@ -1079,6 +1085,8 @@ pub const File = struct { |
| 1079 | 1085 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1080 | 1086 | /// order to handle partial reads from the underlying OS layer. |
| 1081 | 1087 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1088 | /// On Windows, this function currently does alter the file pointer. | |
| 1089 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1082 | 1090 | pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize { |
| 1083 | 1091 | if (iovecs.len == 0) return 0; |
| 1084 | 1092 | |
| ... | ... | @@ -1122,6 +1130,8 @@ pub const File = struct { |
| 1122 | 1130 | } |
| 1123 | 1131 | } |
| 1124 | 1132 | |
| 1133 | /// On Windows, this function currently does alter the file pointer. | |
| 1134 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1125 | 1135 | pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1126 | 1136 | if (is_windows) { |
| 1127 | 1137 | return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode); |
| ... | ... | @@ -1134,6 +1144,8 @@ pub const File = struct { |
| 1134 | 1144 | } |
| 1135 | 1145 | } |
| 1136 | 1146 | |
| 1147 | /// On Windows, this function currently does alter the file pointer. | |
| 1148 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1137 | 1149 | pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void { |
| 1138 | 1150 | var index: usize = 0; |
| 1139 | 1151 | while (index < bytes.len) { |
| ... | ... | @@ -1179,6 +1191,8 @@ pub const File = struct { |
| 1179 | 1191 | } |
| 1180 | 1192 | |
| 1181 | 1193 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1194 | /// On Windows, this function currently does alter the file pointer. | |
| 1195 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1182 | 1196 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize { |
| 1183 | 1197 | if (is_windows) { |
| 1184 | 1198 | // TODO improve this to use WriteFileScatter |
| ... | ... | @@ -1197,6 +1211,8 @@ pub const File = struct { |
| 1197 | 1211 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1198 | 1212 | /// order to handle partial writes from the underlying OS layer. |
| 1199 | 1213 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1214 | /// On Windows, this function currently does alter the file pointer. | |
| 1215 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1200 | 1216 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void { |
| 1201 | 1217 | if (iovecs.len == 0) return; |
| 1202 | 1218 |
lib/std/io.zig+12| ... | ... | @@ -36,6 +36,10 @@ pub const default_mode: ModeOverride = if (is_async) Mode.evented else .blocking |
| 36 | 36 | |
| 37 | 37 | fn getStdOutHandle() os.fd_t { |
| 38 | 38 | if (builtin.os.tag == .windows) { |
| 39 | if (builtin.zig_backend == .stage2_x86_64) { | |
| 40 | // TODO: this is just a temporary workaround until we advance x86 backend further along. | |
| 41 | return os.windows.GetStdHandle(os.windows.STD_OUTPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE; | |
| 42 | } | |
| 39 | 43 | return os.windows.peb().ProcessParameters.hStdOutput; |
| 40 | 44 | } |
| 41 | 45 | |
| ... | ... | @@ -58,6 +62,10 @@ pub fn getStdOut() File { |
| 58 | 62 | |
| 59 | 63 | fn getStdErrHandle() os.fd_t { |
| 60 | 64 | if (builtin.os.tag == .windows) { |
| 65 | if (builtin.zig_backend == .stage2_x86_64) { | |
| 66 | // TODO: this is just a temporary workaround until we advance x86 backend further along. | |
| 67 | return os.windows.GetStdHandle(os.windows.STD_ERROR_HANDLE) catch os.windows.INVALID_HANDLE_VALUE; | |
| 68 | } | |
| 61 | 69 | return os.windows.peb().ProcessParameters.hStdError; |
| 62 | 70 | } |
| 63 | 71 | |
| ... | ... | @@ -80,6 +88,10 @@ pub fn getStdErr() File { |
| 80 | 88 | |
| 81 | 89 | fn getStdInHandle() os.fd_t { |
| 82 | 90 | if (builtin.os.tag == .windows) { |
| 91 | if (builtin.zig_backend == .stage2_x86_64) { | |
| 92 | // TODO: this is just a temporary workaround until we advance x86 backend further along. | |
| 93 | return os.windows.GetStdHandle(os.windows.STD_INPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE; | |
| 94 | } | |
| 83 | 95 | return os.windows.peb().ProcessParameters.hStdInput; |
| 84 | 96 | } |
| 85 | 97 |
lib/std/os/uefi/protocols/block_io_protocol.zig+2-2| ... | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | 2 | const uefi = std.os.uefi; |
| 3 | 3 | const Status = uefi.Status; |
| 4 | 4 | |
| 5 | const EfiBlockMedia = extern struct { | |
| 5 | pub const EfiBlockMedia = extern struct { | |
| 6 | 6 | /// The current media ID. If the media changes, this value is changed. |
| 7 | 7 | media_id: u32, |
| 8 | 8 | |
| ... | ... | @@ -38,7 +38,7 @@ const EfiBlockMedia = extern struct { |
| 38 | 38 | optimal_transfer_length_granularity: u32, |
| 39 | 39 | }; |
| 40 | 40 | |
| 41 | const BlockIoProtocol = extern struct { | |
| 41 | pub const BlockIoProtocol = extern struct { | |
| 42 | 42 | const Self = @This(); |
| 43 | 43 | |
| 44 | 44 | revision: u64, |
lib/std/os/windows/kernel32.zig+7-1| ... | ... | @@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile( |
| 348 | 348 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 349 | 349 | ) callconv(WINAPI) BOOL; |
| 350 | 350 | |
| 351 | pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: *OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(WINAPI) BOOL; | |
| 351 | pub extern "kernel32" fn WriteFileEx( | |
| 352 | hFile: HANDLE, | |
| 353 | lpBuffer: [*]const u8, | |
| 354 | nNumberOfBytesToWrite: DWORD, | |
| 355 | lpOverlapped: *OVERLAPPED, | |
| 356 | lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, | |
| 357 | ) callconv(WINAPI) BOOL; | |
| 352 | 358 | |
| 353 | 359 | pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE; |
| 354 | 360 |
lib/std/simd.zig+10-1| ... | ... | @@ -9,7 +9,7 @@ const builtin = @import("builtin"); |
| 9 | 9 | pub fn suggestVectorSizeForCpu(comptime T: type, comptime cpu: std.Target.Cpu) ?usize { |
| 10 | 10 | // This is guesswork, if you have better suggestions can add it or edit the current here |
| 11 | 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 | 13 | const vector_bit_size: u16 = blk: { |
| 14 | 14 | if (cpu.arch.isX86()) { |
| 15 | 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 | 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 | 69 | fn vectorLength(comptime VectorType: type) comptime_int { |
| 61 | 70 | return switch (@typeInfo(VectorType)) { |
| 62 | 71 | .Vector => |info| info.len, |
lib/std/start.zig+4| ... | ... | @@ -36,6 +36,10 @@ comptime { |
| 36 | 36 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { |
| 37 | 37 | @export(main2, .{ .name = "main" }); |
| 38 | 38 | } |
| 39 | } else if (builtin.os.tag == .windows) { | |
| 40 | if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { | |
| 41 | @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); | |
| 42 | } | |
| 39 | 43 | } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { |
| 40 | 44 | @export(wasiMain2, .{ .name = "_start" }); |
| 41 | 45 | } else { |
lib/std/zig/system/NativeTargetInfo.zig+320-218| ... | ... | @@ -28,6 +28,7 @@ pub const DetectError = error{ |
| 28 | 28 | SystemFdQuotaExceeded, |
| 29 | 29 | DeviceBusy, |
| 30 | 30 | OSVersionDetectionFail, |
| 31 | Unexpected, | |
| 31 | 32 | }; |
| 32 | 33 | |
| 33 | 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 | 37 | /// relative to that. |
| 37 | 38 | /// Any resources this function allocates are released before returning, and so there is no |
| 38 | 39 | /// deinitialization method. |
| 39 | /// TODO Remove the Allocator requirement from this function. | |
| 40 | pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo { | |
| 40 | pub fn detect(cross_target: CrossTarget) DetectError!NativeTargetInfo { | |
| 41 | 41 | var os = cross_target.getOsTag().defaultVersionRange(cross_target.getCpuArch()); |
| 42 | 42 | if (cross_target.os_tag == null) { |
| 43 | 43 | switch (builtin.target.os.tag) { |
| ... | ... | @@ -198,7 +198,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ |
| 198 | 198 | } orelse backup_cpu_detection: { |
| 199 | 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 | 202 | // For x86, we need to populate some CPU feature flags depending on architecture |
| 203 | 203 | // and mode: |
| 204 | 204 | // * 16bit_mode => if the abi is code16 |
| ... | ... | @@ -235,13 +235,20 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ |
| 235 | 235 | return result; |
| 236 | 236 | } |
| 237 | 237 | |
| 238 | /// First we attempt to use the executable's own binary. If it is dynamically | |
| 239 | /// linked, then it should answer both the C ABI question and the dynamic linker question. | |
| 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 | |
| 241 | /// we fall back to the defaults. | |
| 242 | /// TODO Remove the Allocator requirement from this function. | |
| 238 | /// In the past, this function attempted to use the executable's own binary if it was dynamically | |
| 239 | /// linked to answer both the C ABI question and the dynamic linker question. However, this | |
| 240 | /// could be problematic on a system that uses a RUNPATH for the compiler binary, locking | |
| 241 | /// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc | |
| 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 | 251 | fn detectAbiAndDynamicLinker( |
| 244 | allocator: Allocator, | |
| 245 | 252 | cpu: Target.Cpu, |
| 246 | 253 | os: Target.Os, |
| 247 | 254 | cross_target: CrossTarget, |
| ... | ... | @@ -279,8 +286,8 @@ fn detectAbiAndDynamicLinker( |
| 279 | 286 | const ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch); |
| 280 | 287 | |
| 281 | 288 | for (all_abis) |abi| { |
| 282 | // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and | |
| 283 | // skip adding it to `ld_info_list`. | |
| 289 | // This may be a nonsensical parameter. We detect this with | |
| 290 | // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`. | |
| 284 | 291 | const target: Target = .{ |
| 285 | 292 | .cpu = cpu, |
| 286 | 293 | .os = os, |
| ... | ... | @@ -300,64 +307,6 @@ fn detectAbiAndDynamicLinker( |
| 300 | 307 | |
| 301 | 308 | // Best case scenario: the executable is dynamically linked, and we can iterate |
| 302 | 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 | 310 | const elf_file = blk: { |
| 362 | 311 | // This block looks for a shebang line in /usr/bin/env, |
| 363 | 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 | 318 | // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) |
| 370 | 319 | var buffer: [258]u8 = undefined; |
| 371 | 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 | 322 | error.NoSpaceLeft => unreachable, |
| 374 | 323 | error.NameTooLong => unreachable, |
| 375 | 324 | error.PathAlreadyExists => unreachable, |
| ... | ... | @@ -390,44 +339,35 @@ fn detectAbiAndDynamicLinker( |
| 390 | 339 | error.FileTooBig, |
| 391 | 340 | error.Unexpected, |
| 392 | 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 | 343 | return defaultAbiAndDynamicLinker(cpu, os, cross_target); |
| 395 | 344 | }, |
| 396 | 345 | |
| 397 | 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) { | |
| 401 | error.IsDir => unreachable, // Handled before | |
| 402 | error.AccessDenied => unreachable, | |
| 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, | |
| 350 | const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) { | |
| 351 | error.UnexpectedEndOfFile, | |
| 352 | error.UnableToReadElfFile, | |
| 414 | 353 | => break :blk file, |
| 415 | 354 | |
| 416 | else => |e| { | |
| 417 | file.close(); | |
| 418 | return e; | |
| 419 | }, | |
| 355 | else => |e| return e, | |
| 420 | 356 | }; |
| 357 | const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file; | |
| 358 | const line = buffer[0..newline]; | |
| 421 | 359 | if (!mem.startsWith(u8, line, "#!")) break :blk file; |
| 422 | var it = std.mem.tokenize(u8, line[2..], " "); | |
| 423 | file.close(); | |
| 360 | var it = mem.tokenize(u8, line[2..], " "); | |
| 424 | 361 | file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target); |
| 362 | file.close(); | |
| 425 | 363 | } |
| 426 | 364 | }; |
| 427 | 365 | defer elf_file.close(); |
| 428 | 366 | |
| 429 | 367 | // If Zig is statically linked, such as via distributed binary static builds, the above |
| 430 | 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 | 371 | return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) { |
| 432 | 372 | error.FileSystem, |
| 433 | 373 | error.SystemResources, |
| ... | ... | @@ -447,31 +387,196 @@ fn detectAbiAndDynamicLinker( |
| 447 | 387 | error.NameTooLong, |
| 448 | 388 | // Finally, we fall back on the standard path. |
| 449 | 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 | 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 { | |
| 459 | var link_buf: [std.os.PATH_MAX]u8 = undefined; | |
| 460 | const link_name = std.os.readlinkZ(so_path.ptr, &link_buf) catch |err| switch (err) { | |
| 461 | error.AccessDenied => return error.GnuLibCVersionUnavailable, | |
| 462 | error.FileSystem => return error.FileSystem, | |
| 463 | error.SymLinkLoop => return error.SymLinkLoop, | |
| 410 | error.ProcessFdQuotaExceeded, | |
| 411 | error.SystemFdQuotaExceeded, | |
| 412 | error.SystemResources, | |
| 413 | error.SymLinkLoop, | |
| 414 | error.Unexpected, | |
| 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 | 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 | 429 | error.InvalidUtf8 => unreachable, // Windows only |
| 471 | 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 | 582 | fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version { |
| ... | ... | @@ -641,65 +746,65 @@ pub fn abiAndDynamicLinkerFromFile( |
| 641 | 746 | if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and |
| 642 | 747 | cross_target.glibc_version == null) |
| 643 | 748 | { |
| 644 | if (rpath_offset) |rpoff| { | |
| 645 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); | |
| 646 | ||
| 647 | var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); | |
| 648 | const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); | |
| 649 | const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); | |
| 650 | ||
| 651 | var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; | |
| 652 | if (sh_buf.len < shentsize) return error.InvalidElfFile; | |
| 653 | ||
| 654 | _ = try preadMin(file, &sh_buf, str_section_off, shentsize); | |
| 655 | const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf)); | |
| 656 | const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf)); | |
| 657 | const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); | |
| 658 | const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); | |
| 659 | var strtab_buf: [4096:0]u8 = undefined; | |
| 660 | const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len); | |
| 661 | const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); | |
| 662 | const shstrtab = strtab_buf[0..shstrtab_read_len]; | |
| 663 | ||
| 664 | const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); | |
| 665 | var sh_i: u16 = 0; | |
| 666 | const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { | |
| 667 | // Reserve some bytes so that we can deref the 64-bit struct fields | |
| 668 | // even when the ELF file is 32-bits. | |
| 669 | const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); | |
| 670 | const sh_read_byte_len = try preadMin( | |
| 671 | file, | |
| 672 | sh_buf[0 .. sh_buf.len - sh_reserve], | |
| 673 | shoff, | |
| 674 | shentsize, | |
| 749 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); | |
| 750 | ||
| 751 | 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); | |
| 753 | const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); | |
| 754 | ||
| 755 | var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; | |
| 756 | if (sh_buf.len < shentsize) return error.InvalidElfFile; | |
| 757 | ||
| 758 | _ = try preadMin(file, &sh_buf, str_section_off, shentsize); | |
| 759 | 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)); | |
| 761 | 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); | |
| 763 | var strtab_buf: [4096:0]u8 = undefined; | |
| 764 | 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); | |
| 766 | const shstrtab = strtab_buf[0..shstrtab_read_len]; | |
| 767 | ||
| 768 | const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); | |
| 769 | var sh_i: u16 = 0; | |
| 770 | 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 | |
| 772 | // even when the ELF file is 32-bits. | |
| 773 | const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); | |
| 774 | const sh_read_byte_len = try preadMin( | |
| 775 | file, | |
| 776 | sh_buf[0 .. sh_buf.len - sh_reserve], | |
| 777 | shoff, | |
| 778 | 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; | |
| 677 | while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ | |
| 678 | sh_i += 1; | |
| 679 | shoff += shentsize; | |
| 680 | sh_buf_i += shentsize; | |
| 681 | }) { | |
| 682 | const sh32 = @ptrCast( | |
| 683 | *elf.Elf32_Shdr, | |
| 684 | @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]), | |
| 685 | ); | |
| 686 | const sh64 = @ptrCast( | |
| 687 | *elf.Elf64_Shdr, | |
| 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 | } | |
| 790 | const sh64 = @ptrCast( | |
| 791 | *elf.Elf64_Shdr, | |
| 792 | @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]), | |
| 793 | ); | |
| 794 | const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); | |
| 795 | // TODO this pointer cast should not be necessary | |
| 796 | const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0); | |
| 797 | if (mem.eql(u8, sh_name, ".dynstr")) { | |
| 798 | break :find_dyn_str .{ | |
| 799 | .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), | |
| 800 | .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), | |
| 801 | }; | |
| 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 | 808 | // TODO this pointer cast should not be necessary |
| 704 | 809 | const rpoff_usize = std.math.cast(usize, rpoff) orelse return error.InvalidElfFile; |
| 705 | 810 | if (rpoff_usize > ds.size) return error.InvalidElfFile; |
| ... | ... | @@ -713,64 +818,31 @@ pub fn abiAndDynamicLinkerFromFile( |
| 713 | 818 | const rpath_list = mem.sliceTo(std.meta.assumeSentinel(strtab.ptr, 0), 0); |
| 714 | 819 | var it = mem.tokenize(u8, rpath_list, ":"); |
| 715 | 820 | while (it.next()) |rpath| { |
| 716 | var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { | |
| 717 | error.NameTooLong => unreachable, | |
| 718 | error.InvalidUtf8 => unreachable, | |
| 719 | error.BadPathName => unreachable, | |
| 720 | error.DeviceBusy => unreachable, | |
| 721 | ||
| 722 | error.FileNotFound, | |
| 723 | error.NotDir, | |
| 724 | error.InvalidHandle, | |
| 725 | error.AccessDenied, | |
| 726 | error.NoDevice, | |
| 727 | => continue, | |
| 728 | ||
| 729 | error.ProcessFdQuotaExceeded, | |
| 730 | error.SystemFdQuotaExceeded, | |
| 731 | error.SystemResources, | |
| 732 | error.SymLinkLoop, | |
| 733 | error.Unexpected, | |
| 734 | => |e| return e, | |
| 735 | }; | |
| 736 | defer dir.close(); | |
| 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; | |
| 821 | if (glibcVerFromRPath(rpath)) |ver| { | |
| 822 | result.target.os.version_range.linux.glibc = ver; | |
| 823 | return result; | |
| 824 | } else |err| switch (err) { | |
| 825 | error.GLibCNotFound => continue, | |
| 826 | else => |e| return e, | |
| 827 | } | |
| 828 | } | |
| 829 | } | |
| 830 | } | |
| 831 | ||
| 832 | if (result.dynamic_linker.get()) |dl_path| glibc_ver: { | |
| 833 | // There is no DT_RUNPATH so we try to find libc.so.6 inside the same | |
| 834 | // directory as the dynamic linker. | |
| 835 | if (fs.path.dirname(dl_path)) |rpath| { | |
| 836 | if (glibcVerFromRPath(rpath)) |ver| { | |
| 837 | result.target.os.version_range.linux.glibc = ver; | |
| 838 | return result; | |
| 839 | } else |err| switch (err) { | |
| 840 | error.GLibCNotFound => {}, | |
| 841 | else => |e| return e, | |
| 770 | 842 | } |
| 771 | 843 | } |
| 772 | } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: { | |
| 773 | // There is no DT_RUNPATH but we can try to see if the information is | |
| 844 | ||
| 845 | // So far, no luck. Next we try to see if the information is | |
| 774 | 846 | // present in the symlink data for the dynamic linker path. |
| 775 | 847 | var link_buf: [std.os.PATH_MAX]u8 = undefined; |
| 776 | 848 | const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { |
| ... | ... | @@ -799,6 +871,36 @@ pub fn abiAndDynamicLinkerFromFile( |
| 799 | 871 | error.InvalidGnuLibCVersion, |
| 800 | 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 | 878 | linker_shared_memory: bool = false, |
| 879 | 879 | linker_global_base: ?u64 = null, |
| 880 | 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 | 884 | each_lib_rpath: ?bool = null, |
| 882 | 885 | build_id: ?bool = null, |
| 883 | 886 | disable_c_depfile: bool = false, |
| ... | ... | @@ -1727,6 +1730,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1727 | 1730 | .shared_memory = options.linker_shared_memory, |
| 1728 | 1731 | .global_base = options.linker_global_base, |
| 1729 | 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 | 1736 | .z_nodelete = options.linker_z_nodelete, |
| 1731 | 1737 | .z_notext = options.linker_z_notext, |
| 1732 | 1738 | .z_defs = options.linker_z_defs, |
src/Module.zig+21-4| ... | ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { |
| 345 | 345 | /// During sema, this map is backed by the gpa. Once sema completes, |
| 346 | 346 | /// it is reallocated using the value_arena. |
| 347 | 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 | 359 | pub const WipCaptureScope = struct { |
| ... | ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { |
| 383 | 392 | pub fn deinit(noalias self: *@This()) void { |
| 384 | 393 | if (!self.finalized) { |
| 385 | 394 | self.scope.captures.deinit(self.gpa); |
| 395 | self.scope.fail(); | |
| 386 | 396 | } |
| 387 | 397 | self.* = undefined; |
| 388 | 398 | } |
| ... | ... | @@ -4274,11 +4284,14 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { |
| 4274 | 4284 | |
| 4275 | 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 | 4288 | comp.emit_asm == null and |
| 4279 | 4289 | comp.emit_llvm_ir == null and |
| 4280 | comp.emit_llvm_bc == null) | |
| 4281 | { | |
| 4290 | comp.emit_llvm_bc == null); | |
| 4291 | ||
| 4292 | const dump_air = builtin.mode == .Debug and comp.verbose_air; | |
| 4293 | ||
| 4294 | if (no_bin_file and !dump_air) { | |
| 4282 | 4295 | return; |
| 4283 | 4296 | } |
| 4284 | 4297 | |
| ... | ... | @@ -4286,7 +4299,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { |
| 4286 | 4299 | var liveness = try Liveness.analyze(gpa, air); |
| 4287 | 4300 | defer liveness.deinit(gpa); |
| 4288 | 4301 | |
| 4289 | if (builtin.mode == .Debug and comp.verbose_air) { | |
| 4302 | if (dump_air) { | |
| 4290 | 4303 | const fqn = try decl.getFullyQualifiedName(mod); |
| 4291 | 4304 | defer mod.gpa.free(fqn); |
| 4292 | 4305 | |
| ... | ... | @@ -4295,6 +4308,10 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { |
| 4295 | 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 | 4315 | comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) { |
| 4299 | 4316 | error.OutOfMemory => return error.OutOfMemory, |
| 4300 | 4317 | error.AnalysisFail => { |
src/Sema.zig+32-30| ... | ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( |
| 5956 | 5956 | error.NeededSourceLocation => { |
| 5957 | 5957 | _ = sema.inst_map.remove(inst); |
| 5958 | 5958 | const decl = sema.mod.declPtr(block.src_decl); |
| 5959 | child_block.src_decl = block.src_decl; | |
| 5960 | 5959 | try sema.analyzeInlineCallArg( |
| 5961 | 5960 | block, |
| 5962 | 5961 | &child_block, |
| ... | ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( |
| 13740 | 13739 | const tv = while (true) { |
| 13741 | 13740 | // Note: We don't need to add a dependency here, because |
| 13742 | 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 | 13752 | if (scope.captures.getPtr(inst_data.inst)) |tv| { |
| 13744 | 13753 | break tv; |
| 13745 | 13754 | } |
| ... | ... | @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 |
| 18076 | 18085 | const target = sema.mod.getTarget(); |
| 18077 | 18086 | |
| 18078 | 18087 | try sema.resolveTypeLayout(block, lhs_src, ty); |
| 18079 | switch (ty.tag()) { | |
| 18080 | .@"struct", .tuple, .anon_struct => {}, | |
| 18088 | switch (ty.zigTypeTag()) { | |
| 18089 | .Struct => {}, | |
| 18081 | 18090 | else => { |
| 18082 | 18091 | const msg = msg: { |
| 18083 | 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 | 19626 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19618 | 19627 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19619 | 19628 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19620 | const dest_ptr = try sema.resolveInst(extra.dest); | |
| 19621 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 19629 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); | |
| 19622 | 19630 | |
| 19623 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | |
| 19624 | if (dest_ptr_ty.isConstPtr()) { | |
| 19625 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | |
| 19626 | } | |
| 19631 | // TODO AstGen's coerced_ty cannot handle volatile here | |
| 19632 | var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; | |
| 19633 | dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); | |
| 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 | 19637 | const uncasted_src_ptr = try sema.resolveInst(extra.source); |
| 19629 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | |
| 19630 | try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); | |
| 19631 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | |
| 19632 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ | |
| 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); | |
| 19638 | var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data; | |
| 19639 | src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr(); | |
| 19640 | const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info); | |
| 19641 | const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src); | |
| 19642 | 19642 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19643 | 19643 | |
| 19644 | 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 | 19674 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19675 | 19675 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19676 | 19676 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19677 | const dest_ptr = try sema.resolveInst(extra.dest); | |
| 19678 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 19679 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | |
| 19680 | if (dest_ptr_ty.isConstPtr()) { | |
| 19681 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | |
| 19682 | } | |
| 19683 | const elem_ty = dest_ptr_ty.elemType2(); | |
| 19684 | const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src); | |
| 19677 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); | |
| 19678 | ||
| 19679 | // TODO AstGen's coerced_ty cannot handle volatile here | |
| 19680 | var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; | |
| 19681 | ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); | |
| 19682 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); | |
| 19683 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | |
| 19684 | ||
| 19685 | const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src); | |
| 19685 | 19686 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19686 | 19687 | |
| 19687 | 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 | 26014 | .pointee_type = decl_tv.ty, |
| 26014 | 26015 | .mutable = false, |
| 26015 | 26016 | .@"addrspace" = decl.@"addrspace", |
| 26017 | .@"align" = decl.@"align", | |
| 26016 | 26018 | }), |
| 26017 | 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 | 666 | self.locals.deinit(self.gpa); |
| 667 | 667 | self.mir_instructions.deinit(self.gpa); |
| 668 | 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 | 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 | 32 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 33 | 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 34 | 34 | |
| 35 | const callee_preserved_regs = abi.callee_preserved_regs; | |
| 36 | const caller_preserved_regs = abi.caller_preserved_regs; | |
| 37 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | |
| 38 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | |
| 39 | ||
| 40 | 35 | const Condition = bits.Condition; |
| 41 | 36 | const RegisterManager = abi.RegisterManager; |
| 42 | 37 | const RegisterLock = RegisterManager.RegisterLock; |
| ... | ... | @@ -137,6 +132,7 @@ pub const MCValue = union(enum) { |
| 137 | 132 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. |
| 138 | 133 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT. |
| 139 | 134 | got_load: u32, |
| 135 | imports_load: u32, | |
| 140 | 136 | /// The value is in memory referenced directly via symbol index. |
| 141 | 137 | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. |
| 142 | 138 | /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED. |
| ... | ... | @@ -156,6 +152,7 @@ pub const MCValue = union(enum) { |
| 156 | 152 | .ptr_stack_offset, |
| 157 | 153 | .direct_load, |
| 158 | 154 | .got_load, |
| 155 | .imports_load, | |
| 159 | 156 | => true, |
| 160 | 157 | else => false, |
| 161 | 158 | }; |
| ... | ... | @@ -203,6 +200,42 @@ const Branch = struct { |
| 203 | 200 | self.inst_table.deinit(gpa); |
| 204 | 201 | self.* = undefined; |
| 205 | 202 | } |
| 203 | ||
| 204 | const FormatContext = struct { | |
| 205 | insts: []const Air.Inst.Index, | |
| 206 | mcvs: []const MCValue, | |
| 207 | }; | |
| 208 | ||
| 209 | fn fmt( | |
| 210 | ctx: FormatContext, | |
| 211 | comptime unused_format_string: []const u8, | |
| 212 | options: std.fmt.FormatOptions, | |
| 213 | writer: anytype, | |
| 214 | ) @TypeOf(writer).Error!void { | |
| 215 | _ = options; | |
| 216 | comptime assert(unused_format_string.len == 0); | |
| 217 | try writer.writeAll("Branch {\n"); | |
| 218 | for (ctx.insts) |inst, i| { | |
| 219 | const mcv = ctx.mcvs[i]; | |
| 220 | try writer.print(" %{d} => {}\n", .{ inst, mcv }); | |
| 221 | } | |
| 222 | try writer.writeAll("}"); | |
| 223 | } | |
| 224 | ||
| 225 | fn format(branch: Branch, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 226 | _ = branch; | |
| 227 | _ = unused_format_string; | |
| 228 | _ = options; | |
| 229 | _ = writer; | |
| 230 | @compileError("do not format Branch directly; use ty.fmtDebug()"); | |
| 231 | } | |
| 232 | ||
| 233 | fn fmtDebug(self: @This()) std.fmt.Formatter(fmt) { | |
| 234 | return .{ .data = .{ | |
| 235 | .insts = self.inst_table.keys(), | |
| 236 | .mcvs = self.inst_table.values(), | |
| 237 | } }; | |
| 238 | } | |
| 206 | 239 | }; |
| 207 | 240 | |
| 208 | 241 | const StackAllocation = struct { |
| ... | ... | @@ -235,7 +268,7 @@ const BigTomb = struct { |
| 235 | 268 | fn finishAir(bt: *BigTomb, result: MCValue) void { |
| 236 | 269 | const is_used = !bt.function.liveness.isUnused(bt.inst); |
| 237 | 270 | if (is_used) { |
| 238 | log.debug("%{d} => {}", .{ bt.inst, result }); | |
| 271 | log.debug(" (saving %{d} => {})", .{ bt.inst, result }); | |
| 239 | 272 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; |
| 240 | 273 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); |
| 241 | 274 | } |
| ... | ... | @@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void { |
| 406 | 439 | }); |
| 407 | 440 | |
| 408 | 441 | if (self.ret_mcv == .stack_offset) { |
| 409 | // The address where to store the return value for the caller is in `.rdi` | |
| 442 | // The address where to store the return value for the caller is in a | |
| 410 | 443 | // register which the callee is free to clobber. Therefore, we purposely |
| 411 | 444 | // spill it to stack immediately. |
| 412 | 445 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8); |
| 413 | 446 | self.next_stack_offset = stack_offset; |
| 414 | 447 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); |
| 415 | 448 | |
| 416 | try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); | |
| 449 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; | |
| 450 | try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = ret_reg }, .{}); | |
| 417 | 451 | self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) }; |
| 418 | log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset}); | |
| 452 | log.debug("gen: spilling {s} to stack at offset {}", .{ @tagName(ret_reg), stack_offset }); | |
| 419 | 453 | } |
| 420 | 454 | |
| 421 | 455 | _ = try self.addInst(.{ |
| ... | ... | @@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void { |
| 446 | 480 | |
| 447 | 481 | // Create list of registers to save in the prologue. |
| 448 | 482 | // TODO handle register classes |
| 449 | var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{}; | |
| 450 | inline for (callee_preserved_regs) |reg| { | |
| 483 | var reg_list = Mir.RegisterList{}; | |
| 484 | const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*); | |
| 485 | for (callee_preserved_regs) |reg| { | |
| 451 | 486 | if (self.register_manager.isRegAllocated(reg)) { |
| 452 | reg_list.push(reg); | |
| 487 | reg_list.push(callee_preserved_regs, reg); | |
| 453 | 488 | } |
| 454 | 489 | } |
| 455 | 490 | const saved_regs_stack_space: u32 = reg_list.count() * 8; |
| ... | ... | @@ -797,6 +832,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 797 | 832 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 798 | 833 | const air_tags = self.air.instructions.items(.tag); |
| 799 | 834 | if (air_tags[inst] == .constant) return; // Constants are immortal. |
| 835 | log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } }); | |
| 800 | 836 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| 801 | 837 | const prev_value = self.getResolvedInstValue(inst); |
| 802 | 838 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | ... | @@ -2274,6 +2310,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2274 | 2310 | .memory, |
| 2275 | 2311 | .got_load, |
| 2276 | 2312 | .direct_load, |
| 2313 | .imports_load, | |
| 2277 | 2314 | => { |
| 2278 | 2315 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); |
| 2279 | 2316 | }, |
| ... | ... | @@ -2618,6 +2655,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2618 | 2655 | .memory, |
| 2619 | 2656 | .got_load, |
| 2620 | 2657 | .direct_load, |
| 2658 | .imports_load, | |
| 2621 | 2659 | => { |
| 2622 | 2660 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 2623 | 2661 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); |
| ... | ... | @@ -2655,6 +2693,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2655 | 2693 | switch (ptr) { |
| 2656 | 2694 | .got_load, |
| 2657 | 2695 | .direct_load, |
| 2696 | .imports_load, | |
| 2658 | 2697 | => |sym_index| { |
| 2659 | 2698 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2660 | 2699 | const mod = self.bin_file.options.module.?; |
| ... | ... | @@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2666 | 2705 | const flags: u2 = switch (ptr) { |
| 2667 | 2706 | .got_load => 0b00, |
| 2668 | 2707 | .direct_load => 0b01, |
| 2708 | .imports_load => 0b10, | |
| 2669 | 2709 | else => unreachable, |
| 2670 | 2710 | }; |
| 2671 | 2711 | _ = try self.addInst(.{ |
| ... | ... | @@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2763 | 2803 | }, |
| 2764 | 2804 | .got_load, |
| 2765 | 2805 | .direct_load, |
| 2806 | .imports_load, | |
| 2766 | 2807 | .memory, |
| 2767 | 2808 | .stack_offset, |
| 2768 | 2809 | => { |
| ... | ... | @@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2783 | 2824 | }, |
| 2784 | 2825 | .got_load, |
| 2785 | 2826 | .direct_load, |
| 2827 | .imports_load, | |
| 2786 | 2828 | .memory, |
| 2787 | 2829 | => { |
| 2788 | 2830 | const value_lock: ?RegisterLock = switch (value) { |
| ... | ... | @@ -2854,6 +2896,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2854 | 2896 | }, |
| 2855 | 2897 | .got_load, |
| 2856 | 2898 | .direct_load, |
| 2899 | .imports_load, | |
| 2857 | 2900 | .memory, |
| 2858 | 2901 | => { |
| 2859 | 2902 | if (abi_size <= 8) { |
| ... | ... | @@ -3565,6 +3608,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3565 | 3608 | .memory, |
| 3566 | 3609 | .got_load, |
| 3567 | 3610 | .direct_load, |
| 3611 | .imports_load, | |
| 3568 | 3612 | .eflags, |
| 3569 | 3613 | => { |
| 3570 | 3614 | assert(abi_size <= 8); |
| ... | ... | @@ -3650,7 +3694,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3650 | 3694 | => { |
| 3651 | 3695 | return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); |
| 3652 | 3696 | }, |
| 3653 | .got_load, .direct_load => { | |
| 3697 | .got_load, | |
| 3698 | .direct_load, | |
| 3699 | .imports_load, | |
| 3700 | => { | |
| 3654 | 3701 | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); |
| 3655 | 3702 | }, |
| 3656 | 3703 | .eflags => { |
| ... | ... | @@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3661 | 3708 | .memory => { |
| 3662 | 3709 | return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); |
| 3663 | 3710 | }, |
| 3664 | .got_load, .direct_load => { | |
| 3711 | .got_load, | |
| 3712 | .direct_load, | |
| 3713 | .imports_load, | |
| 3714 | => { | |
| 3665 | 3715 | return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); |
| 3666 | 3716 | }, |
| 3667 | 3717 | } |
| ... | ... | @@ -3729,7 +3779,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3729 | 3779 | .memory => { |
| 3730 | 3780 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 3731 | 3781 | }, |
| 3732 | .got_load, .direct_load => { | |
| 3782 | .got_load, | |
| 3783 | .direct_load, | |
| 3784 | .imports_load, | |
| 3785 | => { | |
| 3733 | 3786 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3734 | 3787 | }, |
| 3735 | 3788 | .eflags => { |
| ... | ... | @@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3773 | 3826 | .memory, .stack_offset => { |
| 3774 | 3827 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| 3775 | 3828 | }, |
| 3776 | .got_load, .direct_load => { | |
| 3829 | .got_load, | |
| 3830 | .direct_load, | |
| 3831 | .imports_load, | |
| 3832 | => { | |
| 3777 | 3833 | return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); |
| 3778 | 3834 | }, |
| 3779 | 3835 | .eflags => { |
| ... | ... | @@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3784 | 3840 | .memory => { |
| 3785 | 3841 | return self.fail("TODO implement x86 multiply destination memory", .{}); |
| 3786 | 3842 | }, |
| 3787 | .got_load, .direct_load => { | |
| 3843 | .got_load, | |
| 3844 | .direct_load, | |
| 3845 | .imports_load, | |
| 3846 | => { | |
| 3788 | 3847 | return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); |
| 3789 | 3848 | }, |
| 3790 | 3849 | } |
| ... | ... | @@ -3898,11 +3957,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3898 | 3957 | |
| 3899 | 3958 | try self.spillEflagsIfOccupied(); |
| 3900 | 3959 | |
| 3901 | for (caller_preserved_regs) |reg| { | |
| 3960 | for (abi.getCallerPreservedRegs(self.target.*)) |reg| { | |
| 3902 | 3961 | try self.register_manager.getReg(reg, null); |
| 3903 | 3962 | } |
| 3904 | 3963 | |
| 3905 | const rdi_lock: ?RegisterLock = blk: { | |
| 3964 | const ret_reg_lock: ?RegisterLock = blk: { | |
| 3906 | 3965 | if (info.return_value == .stack_offset) { |
| 3907 | 3966 | const ret_ty = fn_ty.fnReturnType(); |
| 3908 | 3967 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| ... | ... | @@ -3910,17 +3969,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3910 | 3969 | const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); |
| 3911 | 3970 | log.debug("airCall: return value on stack at offset {}", .{stack_offset}); |
| 3912 | 3971 | |
| 3913 | try self.register_manager.getReg(.rdi, null); | |
| 3914 | try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); | |
| 3915 | const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi); | |
| 3972 | const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; | |
| 3973 | try self.register_manager.getReg(ret_reg, null); | |
| 3974 | try self.genSetReg(Type.usize, ret_reg, .{ .ptr_stack_offset = stack_offset }); | |
| 3975 | const ret_reg_lock = self.register_manager.lockRegAssumeUnused(ret_reg); | |
| 3916 | 3976 | |
| 3917 | 3977 | info.return_value.stack_offset = stack_offset; |
| 3918 | 3978 | |
| 3919 | break :blk rdi_lock; | |
| 3979 | break :blk ret_reg_lock; | |
| 3920 | 3980 | } |
| 3921 | 3981 | break :blk null; |
| 3922 | 3982 | }; |
| 3923 | defer if (rdi_lock) |lock| self.register_manager.unlockReg(lock); | |
| 3983 | defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock); | |
| 3924 | 3984 | |
| 3925 | 3985 | for (args) |arg, arg_i| { |
| 3926 | 3986 | const mc_arg = info.args[arg_i]; |
| ... | ... | @@ -3948,6 +4008,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3948 | 4008 | .memory => unreachable, |
| 3949 | 4009 | .got_load => unreachable, |
| 3950 | 4010 | .direct_load => unreachable, |
| 4011 | .imports_load => unreachable, | |
| 3951 | 4012 | .eflags => unreachable, |
| 3952 | 4013 | .register_overflow => unreachable, |
| 3953 | 4014 | } |
| ... | ... | @@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3999 | 4060 | .data = undefined, |
| 4000 | 4061 | }); |
| 4001 | 4062 | } |
| 4002 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | |
| 4063 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4003 | 4064 | if (self.air.value(callee)) |func_value| { |
| 4004 | 4065 | if (func_value.castTag(.function)) |func_payload| { |
| 4005 | 4066 | const func = func_payload.data; |
| ... | ... | @@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 4015 | 4076 | }), |
| 4016 | 4077 | .data = undefined, |
| 4017 | 4078 | }); |
| 4018 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4019 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4079 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4080 | const extern_fn = func_payload.data; | |
| 4081 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4082 | if (extern_fn.lib_name) |lib_name| { | |
| 4083 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4084 | decl_name, | |
| 4085 | lib_name, | |
| 4086 | }); | |
| 4087 | } | |
| 4088 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | |
| 4089 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | |
| 4090 | .imports_load = sym_index, | |
| 4091 | }); | |
| 4092 | _ = try self.addInst(.{ | |
| 4093 | .tag = .call, | |
| 4094 | .ops = Mir.Inst.Ops.encode(.{ | |
| 4095 | .reg1 = .rax, | |
| 4096 | .flags = 0b01, | |
| 4097 | }), | |
| 4098 | .data = undefined, | |
| 4099 | }); | |
| 4020 | 4100 | } else { |
| 4021 | 4101 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 4022 | 4102 | } |
| ... | ... | @@ -4425,7 +4505,11 @@ fn genVarDbgInfo( |
| 4425 | 4505 | leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; |
| 4426 | 4506 | dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); |
| 4427 | 4507 | }, |
| 4428 | .memory, .got_load, .direct_load => { | |
| 4508 | .memory, | |
| 4509 | .got_load, | |
| 4510 | .direct_load, | |
| 4511 | .imports_load, | |
| 4512 | => { | |
| 4429 | 4513 | const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); |
| 4430 | 4514 | const is_ptr = switch (tag) { |
| 4431 | 4515 | .dbg_var_ptr => true, |
| ... | ... | @@ -4456,7 +4540,10 @@ fn genVarDbgInfo( |
| 4456 | 4540 | try dbg_info.append(DW.OP.deref); |
| 4457 | 4541 | } |
| 4458 | 4542 | switch (mcv) { |
| 4459 | .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr), | |
| 4543 | .got_load, | |
| 4544 | .direct_load, | |
| 4545 | .imports_load, | |
| 4546 | => |index| try dw.addExprlocReloc(index, offset, is_ptr), | |
| 4460 | 4547 | else => {}, |
| 4461 | 4548 | } |
| 4462 | 4549 | }, |
| ... | ... | @@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4626 | 4713 | |
| 4627 | 4714 | // Revert to the previous register and stack allocation state. |
| 4628 | 4715 | |
| 4629 | var saved_then_branch = self.branch_stack.pop(); | |
| 4630 | defer saved_then_branch.deinit(self.gpa); | |
| 4716 | var then_branch = self.branch_stack.pop(); | |
| 4717 | defer then_branch.deinit(self.gpa); | |
| 4631 | 4718 | |
| 4632 | 4719 | self.revertState(saved_state); |
| 4633 | 4720 | |
| 4634 | 4721 | try self.performReloc(reloc); |
| 4635 | 4722 | |
| 4636 | const else_branch = self.branch_stack.addOneAssumeCapacity(); | |
| 4637 | else_branch.* = .{}; | |
| 4723 | try self.branch_stack.append(.{}); | |
| 4724 | errdefer { | |
| 4725 | _ = self.branch_stack.pop(); | |
| 4726 | } | |
| 4638 | 4727 | |
| 4639 | 4728 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); |
| 4640 | 4729 | for (liveness_condbr.else_deaths) |operand| { |
| ... | ... | @@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4642 | 4731 | } |
| 4643 | 4732 | try self.genBody(else_body); |
| 4644 | 4733 | |
| 4734 | var else_branch = self.branch_stack.pop(); | |
| 4735 | defer else_branch.deinit(self.gpa); | |
| 4736 | ||
| 4645 | 4737 | // At this point, each branch will possibly have conflicting values for where |
| 4646 | 4738 | // each instruction is stored. They agree, however, on which instructions are alive/dead. |
| 4647 | 4739 | // We use the first ("then") branch as canonical, and here emit |
| ... | ... | @@ -4650,74 +4742,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4650 | 4742 | // that we can use all the code emitting abstractions. This is why at the bottom we |
| 4651 | 4743 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers |
| 4652 | 4744 | // rather than assigning it. |
| 4653 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; | |
| 4654 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); | |
| 4655 | ||
| 4656 | const else_slice = else_branch.inst_table.entries.slice(); | |
| 4657 | const else_keys = else_slice.items(.key); | |
| 4658 | const else_values = else_slice.items(.value); | |
| 4659 | for (else_keys) |else_key, else_idx| { | |
| 4660 | const else_value = else_values[else_idx]; | |
| 4661 | const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { | |
| 4662 | // The instruction's MCValue is overridden in both branches. | |
| 4663 | parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); | |
| 4664 | if (else_value == .dead) { | |
| 4665 | assert(then_entry.value == .dead); | |
| 4666 | continue; | |
| 4667 | } | |
| 4668 | break :blk then_entry.value; | |
| 4669 | } else blk: { | |
| 4670 | if (else_value == .dead) | |
| 4671 | continue; | |
| 4672 | // The instruction is only overridden in the else branch. | |
| 4673 | var i: usize = self.branch_stack.items.len - 2; | |
| 4674 | while (true) { | |
| 4675 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? | |
| 4676 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { | |
| 4677 | assert(mcv != .dead); | |
| 4678 | break :blk mcv; | |
| 4679 | } | |
| 4680 | } | |
| 4681 | }; | |
| 4682 | log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); | |
| 4683 | // TODO make sure the destination stack offset / register does not already have something | |
| 4684 | // going on there. | |
| 4685 | try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); | |
| 4686 | // TODO track the new register / stack allocation | |
| 4687 | } | |
| 4688 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); | |
| 4689 | const then_slice = saved_then_branch.inst_table.entries.slice(); | |
| 4690 | const then_keys = then_slice.items(.key); | |
| 4691 | const then_values = then_slice.items(.value); | |
| 4692 | for (then_keys) |then_key, then_idx| { | |
| 4693 | const then_value = then_values[then_idx]; | |
| 4694 | // We already deleted the items from this table that matched the else_branch. | |
| 4695 | // So these are all instructions that are only overridden in the then branch. | |
| 4696 | parent_branch.inst_table.putAssumeCapacity(then_key, then_value); | |
| 4697 | log.debug("then_value = {}", .{then_value}); | |
| 4698 | if (then_value == .dead) | |
| 4699 | continue; | |
| 4700 | const parent_mcv = blk: { | |
| 4701 | var i: usize = self.branch_stack.items.len - 2; | |
| 4702 | while (true) { | |
| 4703 | i -= 1; | |
| 4704 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { | |
| 4705 | assert(mcv != .dead); | |
| 4706 | break :blk mcv; | |
| 4707 | } | |
| 4708 | } | |
| 4709 | }; | |
| 4710 | log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); | |
| 4711 | // TODO make sure the destination stack offset / register does not already have something | |
| 4712 | // going on there. | |
| 4713 | try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value); | |
| 4714 | // TODO track the new register / stack allocation | |
| 4745 | log.debug("airCondBr: %{d}", .{inst}); | |
| 4746 | log.debug("Upper branches:", .{}); | |
| 4747 | for (self.branch_stack.items) |bs| { | |
| 4748 | log.debug("{}", .{bs.fmtDebug()}); | |
| 4715 | 4749 | } |
| 4716 | 4750 | |
| 4717 | { | |
| 4718 | var item = self.branch_stack.pop(); | |
| 4719 | item.deinit(self.gpa); | |
| 4720 | } | |
| 4751 | log.debug("Then branch: {}", .{then_branch.fmtDebug()}); | |
| 4752 | log.debug("Else branch: {}", .{else_branch.fmtDebug()}); | |
| 4753 | ||
| 4754 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | |
| 4755 | try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch); | |
| 4721 | 4756 | |
| 4722 | 4757 | // We already took care of pl_op.operand earlier, so we're going |
| 4723 | 4758 | // to pass .none here |
| ... | ... | @@ -5102,6 +5137,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5102 | 5137 | } |
| 5103 | 5138 | } |
| 5104 | 5139 | |
| 5140 | var branch_stack = std.ArrayList(Branch).init(self.gpa); | |
| 5141 | defer { | |
| 5142 | for (branch_stack.items) |*bs| { | |
| 5143 | bs.deinit(self.gpa); | |
| 5144 | } | |
| 5145 | branch_stack.deinit(); | |
| 5146 | } | |
| 5147 | try branch_stack.ensureTotalCapacityPrecise(switch_br.data.cases_len + 1); | |
| 5148 | ||
| 5105 | 5149 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 5106 | 5150 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 5107 | 5151 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| ... | ... | @@ -5131,10 +5175,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5131 | 5175 | |
| 5132 | 5176 | try self.genBody(case_body); |
| 5133 | 5177 | |
| 5134 | // Revert to the previous register and stack allocation state. | |
| 5135 | var saved_case_branch = self.branch_stack.pop(); | |
| 5136 | defer saved_case_branch.deinit(self.gpa); | |
| 5178 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); | |
| 5137 | 5179 | |
| 5180 | // Revert to the previous register and stack allocation state. | |
| 5138 | 5181 | self.revertState(saved_state); |
| 5139 | 5182 | |
| 5140 | 5183 | for (relocs) |reloc| { |
| ... | ... | @@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5144 | 5187 | |
| 5145 | 5188 | if (switch_br.data.else_body_len > 0) { |
| 5146 | 5189 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| 5190 | ||
| 5191 | // Capture the state of register and stack allocation state so that we can revert to it. | |
| 5192 | const saved_state = try self.captureState(); | |
| 5193 | ||
| 5147 | 5194 | try self.branch_stack.append(.{}); |
| 5148 | defer { | |
| 5149 | var item = self.branch_stack.pop(); | |
| 5150 | item.deinit(self.gpa); | |
| 5195 | errdefer { | |
| 5196 | _ = self.branch_stack.pop(); | |
| 5151 | 5197 | } |
| 5152 | 5198 | |
| 5153 | 5199 | const else_deaths = liveness.deaths.len - 1; |
| ... | ... | @@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5158 | 5204 | |
| 5159 | 5205 | try self.genBody(else_body); |
| 5160 | 5206 | |
| 5161 | // TODO consolidate returned MCValues between prongs and else branch like we do | |
| 5162 | // in airCondBr. | |
| 5207 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); | |
| 5208 | ||
| 5209 | // Revert to the previous register and stack allocation state. | |
| 5210 | self.revertState(saved_state); | |
| 5211 | } | |
| 5212 | ||
| 5213 | // Consolidate returned MCValues between prongs and else branch like we do | |
| 5214 | // in airCondBr. | |
| 5215 | log.debug("airSwitch: %{d}", .{inst}); | |
| 5216 | log.debug("Upper branches:", .{}); | |
| 5217 | for (self.branch_stack.items) |bs| { | |
| 5218 | log.debug("{}", .{bs.fmtDebug()}); | |
| 5219 | } | |
| 5220 | for (branch_stack.items) |bs, i| { | |
| 5221 | log.debug("Case-{d} branch: {}", .{ i, bs.fmtDebug() }); | |
| 5222 | } | |
| 5223 | ||
| 5224 | // TODO: can we reduce the complexity of this algorithm? | |
| 5225 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | |
| 5226 | var i: usize = branch_stack.items.len; | |
| 5227 | while (i > 1) : (i -= 1) { | |
| 5228 | const canon_branch = &branch_stack.items[i - 2]; | |
| 5229 | const target_branch = &branch_stack.items[i - 1]; | |
| 5230 | try self.canonicaliseBranches(parent_branch, canon_branch, target_branch); | |
| 5163 | 5231 | } |
| 5164 | 5232 | |
| 5165 | 5233 | // We already took care of pl_op.operand earlier, so we're going |
| ... | ... | @@ -5167,6 +5235,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5167 | 5235 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 5168 | 5236 | } |
| 5169 | 5237 | |
| 5238 | fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Branch, target_branch: *Branch) !void { | |
| 5239 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count()); | |
| 5240 | ||
| 5241 | const target_slice = target_branch.inst_table.entries.slice(); | |
| 5242 | const target_keys = target_slice.items(.key); | |
| 5243 | const target_values = target_slice.items(.value); | |
| 5244 | ||
| 5245 | for (target_keys) |target_key, target_idx| { | |
| 5246 | const target_value = target_values[target_idx]; | |
| 5247 | const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { | |
| 5248 | // The instruction's MCValue is overridden in both branches. | |
| 5249 | parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); | |
| 5250 | if (target_value == .dead) { | |
| 5251 | assert(canon_entry.value == .dead); | |
| 5252 | continue; | |
| 5253 | } | |
| 5254 | break :blk canon_entry.value; | |
| 5255 | } else blk: { | |
| 5256 | if (target_value == .dead) | |
| 5257 | continue; | |
| 5258 | // The instruction is only overridden in the else branch. | |
| 5259 | var i: usize = self.branch_stack.items.len - 1; | |
| 5260 | while (true) { | |
| 5261 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? | |
| 5262 | if (self.branch_stack.items[i].inst_table.get(target_key)) |mcv| { | |
| 5263 | assert(mcv != .dead); | |
| 5264 | break :blk mcv; | |
| 5265 | } | |
| 5266 | } | |
| 5267 | }; | |
| 5268 | log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv }); | |
| 5269 | // TODO make sure the destination stack offset / register does not already have something | |
| 5270 | // going on there. | |
| 5271 | try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value); | |
| 5272 | // TODO track the new register / stack allocation | |
| 5273 | } | |
| 5274 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count()); | |
| 5275 | const canon_slice = canon_branch.inst_table.entries.slice(); | |
| 5276 | const canon_keys = canon_slice.items(.key); | |
| 5277 | const canon_values = canon_slice.items(.value); | |
| 5278 | for (canon_keys) |canon_key, canon_idx| { | |
| 5279 | const canon_value = canon_values[canon_idx]; | |
| 5280 | // We already deleted the items from this table that matched the target_branch. | |
| 5281 | // So these are all instructions that are only overridden in the canon branch. | |
| 5282 | parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); | |
| 5283 | log.debug("canon_value = {}", .{canon_value}); | |
| 5284 | if (canon_value == .dead) | |
| 5285 | continue; | |
| 5286 | const parent_mcv = blk: { | |
| 5287 | var i: usize = self.branch_stack.items.len - 1; | |
| 5288 | while (true) { | |
| 5289 | i -= 1; | |
| 5290 | if (self.branch_stack.items[i].inst_table.get(canon_key)) |mcv| { | |
| 5291 | assert(mcv != .dead); | |
| 5292 | break :blk mcv; | |
| 5293 | } | |
| 5294 | } | |
| 5295 | }; | |
| 5296 | log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value }); | |
| 5297 | // TODO make sure the destination stack offset / register does not already have something | |
| 5298 | // going on there. | |
| 5299 | try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value); | |
| 5300 | // TODO track the new register / stack allocation | |
| 5301 | } | |
| 5302 | } | |
| 5303 | ||
| 5170 | 5304 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5171 | 5305 | const next_inst = @intCast(u32, self.mir_instructions.len); |
| 5172 | 5306 | switch (self.mir_instructions.items(.tag)[reloc]) { |
| ... | ... | @@ -5196,7 +5330,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5196 | 5330 | block_data.mcv = switch (operand_mcv) { |
| 5197 | 5331 | .none, .dead, .unreach => unreachable, |
| 5198 | 5332 | .register, .stack_offset, .memory => operand_mcv, |
| 5199 | .eflags, .immediate => blk: { | |
| 5333 | .eflags, .immediate, .ptr_stack_offset => blk: { | |
| 5200 | 5334 | const new_mcv = try self.allocRegOrMem(block, true); |
| 5201 | 5335 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 5202 | 5336 | break :blk new_mcv; |
| ... | ... | @@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5456 | 5590 | .memory, |
| 5457 | 5591 | .direct_load, |
| 5458 | 5592 | .got_load, |
| 5593 | .imports_load, | |
| 5459 | 5594 | => { |
| 5460 | 5595 | if (abi_size <= 8) { |
| 5461 | 5596 | const reg = try self.copyToTmpRegister(ty, mcv); |
| ... | ... | @@ -5703,6 +5838,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5703 | 5838 | .memory, |
| 5704 | 5839 | .got_load, |
| 5705 | 5840 | .direct_load, |
| 5841 | .imports_load, | |
| 5706 | 5842 | => { |
| 5707 | 5843 | if (abi_size <= 8) { |
| 5708 | 5844 | const reg = try self.copyToTmpRegister(ty, mcv); |
| ... | ... | @@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct { |
| 5796 | 5932 | dest_stack_base: ?Register = null, |
| 5797 | 5933 | }; |
| 5798 | 5934 | |
| 5799 | /// Spills .rax and .rcx. | |
| 5800 | 5935 | fn genInlineMemcpy( |
| 5801 | 5936 | self: *Self, |
| 5802 | 5937 | dst_ptr: MCValue, |
| ... | ... | @@ -5804,15 +5939,6 @@ fn genInlineMemcpy( |
| 5804 | 5939 | len: MCValue, |
| 5805 | 5940 | opts: InlineMemcpyOpts, |
| 5806 | 5941 | ) InnerError!void { |
| 5807 | // TODO preserve contents of .rax and .rcx if not free, and then restore | |
| 5808 | try self.register_manager.getReg(.rax, null); | |
| 5809 | try self.register_manager.getReg(.rcx, null); | |
| 5810 | ||
| 5811 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }); | |
| 5812 | defer for (reg_locks) |lock| { | |
| 5813 | self.register_manager.unlockReg(lock); | |
| 5814 | }; | |
| 5815 | ||
| 5816 | 5942 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| |
| 5817 | 5943 | self.register_manager.lockReg(reg) |
| 5818 | 5944 | else |
| ... | ... | @@ -5825,11 +5951,18 @@ fn genInlineMemcpy( |
| 5825 | 5951 | null; |
| 5826 | 5952 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); |
| 5827 | 5953 | |
| 5828 | const dst_addr_reg = try self.register_manager.allocReg(null, gp); | |
| 5954 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); | |
| 5955 | const dst_addr_reg = regs[0]; | |
| 5956 | const src_addr_reg = regs[1]; | |
| 5957 | const index_reg = regs[2].to64(); | |
| 5958 | const count_reg = regs[3].to64(); | |
| 5959 | const tmp_reg = regs[4].to8(); | |
| 5960 | ||
| 5829 | 5961 | switch (dst_ptr) { |
| 5830 | 5962 | .memory, |
| 5831 | 5963 | .got_load, |
| 5832 | 5964 | .direct_load, |
| 5965 | .imports_load, | |
| 5833 | 5966 | => { |
| 5834 | 5967 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr); |
| 5835 | 5968 | }, |
| ... | ... | @@ -5857,14 +5990,12 @@ fn genInlineMemcpy( |
| 5857 | 5990 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| 5858 | 5991 | }, |
| 5859 | 5992 | } |
| 5860 | const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); | |
| 5861 | defer self.register_manager.unlockReg(dst_addr_reg_lock); | |
| 5862 | 5993 | |
| 5863 | const src_addr_reg = try self.register_manager.allocReg(null, gp); | |
| 5864 | 5994 | switch (src_ptr) { |
| 5865 | 5995 | .memory, |
| 5866 | 5996 | .got_load, |
| 5867 | 5997 | .direct_load, |
| 5998 | .imports_load, | |
| 5868 | 5999 | => { |
| 5869 | 6000 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr); |
| 5870 | 6001 | }, |
| ... | ... | @@ -5892,26 +6023,13 @@ fn genInlineMemcpy( |
| 5892 | 6023 | return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); |
| 5893 | 6024 | }, |
| 5894 | 6025 | } |
| 5895 | const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); | |
| 5896 | defer self.register_manager.unlockReg(src_addr_reg_lock); | |
| 5897 | ||
| 5898 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | |
| 5899 | const count_reg = regs[0].to64(); | |
| 5900 | const tmp_reg = regs[1].to8(); | |
| 5901 | 6026 | |
| 5902 | 6027 | try self.genSetReg(Type.usize, count_reg, len); |
| 5903 | 6028 | |
| 5904 | // mov rcx, 0 | |
| 5905 | _ = try self.addInst(.{ | |
| 5906 | .tag = .mov, | |
| 5907 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), | |
| 5908 | .data = .{ .imm = 0 }, | |
| 5909 | }); | |
| 5910 | ||
| 5911 | // mov rax, 0 | |
| 6029 | // mov index_reg, 0 | |
| 5912 | 6030 | _ = try self.addInst(.{ |
| 5913 | 6031 | .tag = .mov, |
| 5914 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6032 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 5915 | 6033 | .data = .{ .imm = 0 }, |
| 5916 | 6034 | }); |
| 5917 | 6035 | |
| ... | ... | @@ -5933,37 +6051,30 @@ fn genInlineMemcpy( |
| 5933 | 6051 | } }, |
| 5934 | 6052 | }); |
| 5935 | 6053 | |
| 5936 | // mov tmp, [addr + rcx] | |
| 6054 | // mov tmp, [addr + index_reg] | |
| 5937 | 6055 | _ = try self.addInst(.{ |
| 5938 | 6056 | .tag = .mov_scale_src, |
| 5939 | 6057 | .ops = Mir.Inst.Ops.encode(.{ |
| 5940 | 6058 | .reg1 = tmp_reg.to8(), |
| 5941 | 6059 | .reg2 = src_addr_reg, |
| 5942 | 6060 | }), |
| 5943 | .data = .{ .imm = 0 }, | |
| 6061 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | |
| 5944 | 6062 | }); |
| 5945 | 6063 | |
| 5946 | // mov [stack_offset + rax], tmp | |
| 6064 | // mov [stack_offset + index_reg], tmp | |
| 5947 | 6065 | _ = try self.addInst(.{ |
| 5948 | 6066 | .tag = .mov_scale_dst, |
| 5949 | 6067 | .ops = Mir.Inst.Ops.encode(.{ |
| 5950 | 6068 | .reg1 = dst_addr_reg, |
| 5951 | 6069 | .reg2 = tmp_reg.to8(), |
| 5952 | 6070 | }), |
| 5953 | .data = .{ .imm = 0 }, | |
| 5954 | }); | |
| 5955 | ||
| 5956 | // add rcx, 1 | |
| 5957 | _ = try self.addInst(.{ | |
| 5958 | .tag = .add, | |
| 5959 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), | |
| 5960 | .data = .{ .imm = 1 }, | |
| 6071 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) }, | |
| 5961 | 6072 | }); |
| 5962 | 6073 | |
| 5963 | // add rax, 1 | |
| 6074 | // add index_reg, 1 | |
| 5964 | 6075 | _ = try self.addInst(.{ |
| 5965 | 6076 | .tag = .add, |
| 5966 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6077 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 5967 | 6078 | .data = .{ .imm = 1 }, |
| 5968 | 6079 | }); |
| 5969 | 6080 | |
| ... | ... | @@ -5985,7 +6096,6 @@ fn genInlineMemcpy( |
| 5985 | 6096 | try self.performReloc(loop_reloc); |
| 5986 | 6097 | } |
| 5987 | 6098 | |
| 5988 | /// Spills .rax register. | |
| 5989 | 6099 | fn genInlineMemset( |
| 5990 | 6100 | self: *Self, |
| 5991 | 6101 | dst_ptr: MCValue, |
| ... | ... | @@ -5993,16 +6103,27 @@ fn genInlineMemset( |
| 5993 | 6103 | len: MCValue, |
| 5994 | 6104 | opts: InlineMemcpyOpts, |
| 5995 | 6105 | ) InnerError!void { |
| 5996 | // TODO preserve contents of .rax and then restore | |
| 5997 | try self.register_manager.getReg(.rax, null); | |
| 5998 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); | |
| 5999 | defer self.register_manager.unlockReg(rax_lock); | |
| 6106 | const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| | |
| 6107 | self.register_manager.lockReg(reg) | |
| 6108 | else | |
| 6109 | null; | |
| 6110 | defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg); | |
| 6111 | ||
| 6112 | const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| | |
| 6113 | self.register_manager.lockReg(reg) | |
| 6114 | else | |
| 6115 | null; | |
| 6116 | defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); | |
| 6117 | ||
| 6118 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | |
| 6119 | const addr_reg = regs[0]; | |
| 6120 | const index_reg = regs[1].to64(); | |
| 6000 | 6121 | |
| 6001 | const addr_reg = try self.register_manager.allocReg(null, gp); | |
| 6002 | 6122 | switch (dst_ptr) { |
| 6003 | 6123 | .memory, |
| 6004 | 6124 | .got_load, |
| 6005 | 6125 | .direct_load, |
| 6126 | .imports_load, | |
| 6006 | 6127 | => { |
| 6007 | 6128 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr); |
| 6008 | 6129 | }, |
| ... | ... | @@ -6030,17 +6151,15 @@ fn genInlineMemset( |
| 6030 | 6151 | return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); |
| 6031 | 6152 | }, |
| 6032 | 6153 | } |
| 6033 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); | |
| 6034 | defer self.register_manager.unlockReg(addr_reg_lock); | |
| 6035 | 6154 | |
| 6036 | try self.genSetReg(Type.usize, .rax, len); | |
| 6037 | try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); | |
| 6155 | try self.genSetReg(Type.usize, index_reg, len); | |
| 6156 | try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 }); | |
| 6038 | 6157 | |
| 6039 | 6158 | // loop: |
| 6040 | // cmp rax, -1 | |
| 6159 | // cmp index_reg, -1 | |
| 6041 | 6160 | const loop_start = try self.addInst(.{ |
| 6042 | 6161 | .tag = .cmp, |
| 6043 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6162 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6044 | 6163 | .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, |
| 6045 | 6164 | }); |
| 6046 | 6165 | |
| ... | ... | @@ -6059,24 +6178,20 @@ fn genInlineMemset( |
| 6059 | 6178 | if (x > math.maxInt(i32)) { |
| 6060 | 6179 | return self.fail("TODO inline memset for value immediate larger than 32bits", .{}); |
| 6061 | 6180 | } |
| 6062 | // mov byte ptr [rbp + rax + stack_offset], imm | |
| 6063 | const payload = try self.addExtra(Mir.ImmPair{ | |
| 6064 | .dest_off = 0, | |
| 6065 | .operand = @truncate(u32, x), | |
| 6066 | }); | |
| 6181 | // mov byte ptr [rbp + index_reg + stack_offset], imm | |
| 6067 | 6182 | _ = try self.addInst(.{ |
| 6068 | 6183 | .tag = .mov_mem_index_imm, |
| 6069 | 6184 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), |
| 6070 | .data = .{ .payload = payload }, | |
| 6185 | .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDispImm.encode(index_reg, 0, @truncate(u32, x))) }, | |
| 6071 | 6186 | }); |
| 6072 | 6187 | }, |
| 6073 | 6188 | else => return self.fail("TODO inline memset for value of type {}", .{value}), |
| 6074 | 6189 | } |
| 6075 | 6190 | |
| 6076 | // sub rax, 1 | |
| 6191 | // sub index_reg, 1 | |
| 6077 | 6192 | _ = try self.addInst(.{ |
| 6078 | 6193 | .tag = .sub, |
| 6079 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), | |
| 6194 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }), | |
| 6080 | 6195 | .data = .{ .imm = 1 }, |
| 6081 | 6196 | }); |
| 6082 | 6197 | |
| ... | ... | @@ -6243,6 +6358,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6243 | 6358 | }, |
| 6244 | 6359 | .direct_load, |
| 6245 | 6360 | .got_load, |
| 6361 | .imports_load, | |
| 6246 | 6362 | => { |
| 6247 | 6363 | switch (ty.zigTypeTag()) { |
| 6248 | 6364 | .Float => { |
| ... | ... | @@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 6637 | 6753 | // TODO Is this the only condition for pointer dereference for memcpy? |
| 6638 | 6754 | const src: MCValue = blk: { |
| 6639 | 6755 | switch (src_ptr) { |
| 6640 | .got_load, .direct_load, .memory => { | |
| 6756 | .got_load, | |
| 6757 | .direct_load, | |
| 6758 | .imports_load, | |
| 6759 | .memory, | |
| 6760 | => { | |
| 6641 | 6761 | const reg = try self.register_manager.allocReg(null, gp); |
| 6642 | 6762 | try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); |
| 6643 | 6763 | _ = try self.addInst(.{ |
| ... | ... | @@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6901 | 7021 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6902 | 7022 | return MCValue{ .direct_load = local_sym_index }; |
| 6903 | 7023 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 6904 | return self.fail("TODO lower unnamed const in COFF", .{}); | |
| 7024 | return MCValue{ .direct_load = local_sym_index }; | |
| 6905 | 7025 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
| 6906 | 7026 | return self.fail("TODO lower unnamed const in Plan9", .{}); |
| 6907 | 7027 | } else { |
| ... | ... | @@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7066 | 7186 | result.stack_align = 1; |
| 7067 | 7187 | return result; |
| 7068 | 7188 | }, |
| 7069 | .Unspecified, .C => { | |
| 7189 | .C => { | |
| 7070 | 7190 | // Return values |
| 7071 | 7191 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 7072 | 7192 | result.return_value = .{ .unreach = {} }; |
| 7073 | 7193 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 7194 | // TODO: is this even possible for C calling convention? | |
| 7074 | 7195 | result.return_value = .{ .none = {} }; |
| 7075 | 7196 | } else { |
| 7076 | 7197 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| ... | ... | @@ -7078,84 +7199,106 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7078 | 7199 | assert(ret_ty.isError()); |
| 7079 | 7200 | result.return_value = .{ .immediate = 0 }; |
| 7080 | 7201 | } else if (ret_ty_size <= 8) { |
| 7081 | const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size); | |
| 7202 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); | |
| 7082 | 7203 | result.return_value = .{ .register = aliased_reg }; |
| 7083 | 7204 | } else { |
| 7084 | // We simply make the return MCValue a stack offset. However, the actual value | |
| 7085 | // for the offset will be populated later. We will also push the stack offset | |
| 7086 | // value into .rdi register when we resolve the offset. | |
| 7205 | // TODO: return argument cell should go first | |
| 7087 | 7206 | result.return_value = .{ .stack_offset = 0 }; |
| 7088 | 7207 | } |
| 7089 | 7208 | } |
| 7090 | 7209 | |
| 7091 | 7210 | // Input params |
| 7092 | // First, split into args that can be passed via registers. | |
| 7093 | // This will make it easier to then push the rest of args in reverse | |
| 7094 | // order on the stack. | |
| 7095 | var next_int_reg: usize = 0; | |
| 7096 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); | |
| 7097 | defer by_reg.deinit(); | |
| 7098 | ||
| 7099 | // If we want debug output, we store all args on stack for better liveness of args | |
| 7100 | // in debugging contexts such as previewing the args in the debugger anywhere in | |
| 7101 | // the procedure. Passing the args via registers can lead to reusing the register | |
| 7102 | // for local ops thus clobbering the input arg forever. | |
| 7103 | // This of course excludes C ABI calls. | |
| 7104 | const omit_args_in_registers = blk: { | |
| 7105 | if (cc == .C) break :blk false; | |
| 7106 | switch (self.bin_file.options.optimize_mode) { | |
| 7107 | .Debug => break :blk true, | |
| 7108 | else => break :blk false, | |
| 7211 | var next_stack_offset: u32 = switch (result.return_value) { | |
| 7212 | .stack_offset => |off| @intCast(u32, off), | |
| 7213 | else => 0, | |
| 7214 | }; | |
| 7215 | ||
| 7216 | for (param_types) |ty, i| { | |
| 7217 | assert(ty.hasRuntimeBits()); | |
| 7218 | ||
| 7219 | const classes: []const abi.Class = switch (self.target.os.tag) { | |
| 7220 | .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, | |
| 7221 | else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*), .none), | |
| 7222 | }; | |
| 7223 | if (classes.len > 1) { | |
| 7224 | return self.fail("TODO handle multiple classes per type", .{}); | |
| 7225 | } | |
| 7226 | switch (classes[0]) { | |
| 7227 | .integer => blk: { | |
| 7228 | if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough | |
| 7229 | result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] }; | |
| 7230 | continue; | |
| 7231 | }, | |
| 7232 | .memory => {}, // fallthrough | |
| 7233 | else => |class| return self.fail("TODO handle calling convention class {s}", .{ | |
| 7234 | @tagName(class), | |
| 7235 | }), | |
| 7236 | } | |
| 7237 | ||
| 7238 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 7239 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); | |
| 7240 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | |
| 7241 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | |
| 7242 | next_stack_offset = offset; | |
| 7243 | } | |
| 7244 | ||
| 7245 | // Align the stack to 16bytes before allocating shadow stack space (if any). | |
| 7246 | const aligned_next_stack_offset = mem.alignForwardGeneric(u32, next_stack_offset, 16); | |
| 7247 | const padding = aligned_next_stack_offset - next_stack_offset; | |
| 7248 | if (padding > 0) { | |
| 7249 | for (result.args) |*arg| { | |
| 7250 | if (arg.isRegister()) continue; | |
| 7251 | arg.stack_offset += @intCast(i32, padding); | |
| 7109 | 7252 | } |
| 7253 | } | |
| 7254 | ||
| 7255 | const shadow_stack_space: u32 = switch (self.target.os.tag) { | |
| 7256 | .windows => @intCast(u32, 4 * @sizeOf(u64)), | |
| 7257 | else => 0, | |
| 7110 | 7258 | }; |
| 7111 | if (!omit_args_in_registers) { | |
| 7112 | for (param_types) |ty, i| { | |
| 7113 | if (!ty.hasRuntimeBits()) continue; | |
| 7114 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 7115 | // For simplicity of codegen, slices and other types are always pushed onto the stack. | |
| 7116 | // TODO: look into optimizing this by passing things as registers sometimes, | |
| 7117 | // such as ptr and len of slices as separate registers. | |
| 7118 | // TODO: also we need to honor the C ABI for relevant types rather than passing on | |
| 7119 | // the stack here. | |
| 7120 | const pass_in_reg = switch (ty.zigTypeTag()) { | |
| 7121 | .Bool => true, | |
| 7122 | .Int, .Enum => param_size <= 8, | |
| 7123 | .Pointer => ty.ptrSize() != .Slice, | |
| 7124 | .Optional => ty.isPtrLikeOptional(), | |
| 7125 | else => false, | |
| 7126 | }; | |
| 7127 | if (pass_in_reg) { | |
| 7128 | if (next_int_reg >= c_abi_int_param_regs.len) break; | |
| 7129 | try by_reg.putNoClobber(i, next_int_reg); | |
| 7130 | next_int_reg += 1; | |
| 7131 | } | |
| 7259 | ||
| 7260 | // alignment padding | args ... | shadow stack space (if any) | ret addr | $rbp | | |
| 7261 | result.stack_byte_count = aligned_next_stack_offset + shadow_stack_space; | |
| 7262 | result.stack_align = 16; | |
| 7263 | }, | |
| 7264 | .Unspecified => { | |
| 7265 | // Return values | |
| 7266 | if (ret_ty.zigTypeTag() == .NoReturn) { | |
| 7267 | result.return_value = .{ .unreach = {} }; | |
| 7268 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { | |
| 7269 | result.return_value = .{ .none = {} }; | |
| 7270 | } else { | |
| 7271 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | |
| 7272 | if (ret_ty_size == 0) { | |
| 7273 | assert(ret_ty.isError()); | |
| 7274 | result.return_value = .{ .immediate = 0 }; | |
| 7275 | } else if (ret_ty_size <= 8) { | |
| 7276 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); | |
| 7277 | result.return_value = .{ .register = aliased_reg }; | |
| 7278 | } else { | |
| 7279 | // We simply make the return MCValue a stack offset. However, the actual value | |
| 7280 | // for the offset will be populated later. We will also push the stack offset | |
| 7281 | // value into an appropriate register when we resolve the offset. | |
| 7282 | result.return_value = .{ .stack_offset = 0 }; | |
| 7132 | 7283 | } |
| 7133 | 7284 | } |
| 7134 | 7285 | |
| 7286 | // Input params | |
| 7135 | 7287 | var next_stack_offset: u32 = switch (result.return_value) { |
| 7136 | 7288 | .stack_offset => |off| @intCast(u32, off), |
| 7137 | 7289 | else => 0, |
| 7138 | 7290 | }; |
| 7139 | var count: usize = param_types.len; | |
| 7140 | while (count > 0) : (count -= 1) { | |
| 7141 | const i = count - 1; | |
| 7142 | const ty = param_types[i]; | |
| 7291 | ||
| 7292 | for (param_types) |ty, i| { | |
| 7143 | 7293 | if (!ty.hasRuntimeBits()) { |
| 7144 | assert(cc != .C); | |
| 7145 | 7294 | result.args[i] = .{ .none = {} }; |
| 7146 | 7295 | continue; |
| 7147 | 7296 | } |
| 7148 | 7297 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 7149 | 7298 | const param_align = @intCast(u32, ty.abiAlignment(self.target.*)); |
| 7150 | if (by_reg.get(i)) |int_reg| { | |
| 7151 | const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size); | |
| 7152 | result.args[i] = .{ .register = aliased_reg }; | |
| 7153 | next_int_reg += 1; | |
| 7154 | } else { | |
| 7155 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | |
| 7156 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | |
| 7157 | next_stack_offset = offset; | |
| 7158 | } | |
| 7299 | const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align); | |
| 7300 | result.args[i] = .{ .stack_offset = @intCast(i32, offset) }; | |
| 7301 | next_stack_offset = offset; | |
| 7159 | 7302 | } |
| 7160 | 7303 | |
| 7161 | 7304 | result.stack_align = 16; |
src/arch/x86_64/Emit.zig+57-36| ... | ... | @@ -283,10 +283,11 @@ fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerErro |
| 283 | 283 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 284 | 284 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 285 | 285 | const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; |
| 286 | const reg_list = Mir.RegisterList(Register, &abi.callee_preserved_regs).fromInt(save_reg_list.register_list); | |
| 287 | 286 | var disp: i32 = -@intCast(i32, save_reg_list.stack_end); |
| 288 | inline for (abi.callee_preserved_regs) |reg| { | |
| 289 | if (reg_list.isSet(reg)) { | |
| 287 | const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); | |
| 288 | const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); | |
| 289 | for (callee_preserved_regs) |reg| { | |
| 290 | if (reg_list.isSet(callee_preserved_regs, reg)) { | |
| 290 | 291 | switch (tag) { |
| 291 | 292 | .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ |
| 292 | 293 | .disp = @bitCast(u32, disp), |
| ... | ... | @@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 { |
| 614 | 615 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 615 | 616 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 616 | 617 | const scale = ops.flags; |
| 617 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 618 | // OP reg1, [reg2 + scale*rcx + imm32] | |
| 618 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 619 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 620 | // OP reg1, [reg2 + scale*index + imm32] | |
| 619 | 621 | const scale_index = ScaleIndex{ |
| 620 | 622 | .scale = scale, |
| 621 | .index = .rcx, | |
| 623 | .index = index_reg_disp.index, | |
| 622 | 624 | }; |
| 623 | 625 | return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 624 | .disp = imm, | |
| 626 | .disp = index_reg_disp.disp, | |
| 625 | 627 | .base = ops.reg2, |
| 626 | 628 | .scale_index = scale_index, |
| 627 | 629 | }), emit.code); |
| ... | ... | @@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 630 | 632 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 631 | 633 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 632 | 634 | const scale = ops.flags; |
| 633 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 635 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 636 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 634 | 637 | const scale_index = ScaleIndex{ |
| 635 | 638 | .scale = scale, |
| 636 | .index = .rax, | |
| 639 | .index = index_reg_disp.index, | |
| 637 | 640 | }; |
| 638 | if (ops.reg2 == .none) { | |
| 639 | // OP qword ptr [reg1 + scale*rax + 0], imm32 | |
| 640 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ | |
| 641 | .disp = 0, | |
| 642 | .base = ops.reg1, | |
| 643 | .scale_index = scale_index, | |
| 644 | }), imm, emit.code); | |
| 645 | } | |
| 646 | // OP [reg1 + scale*rax + imm32], reg2 | |
| 641 | assert(ops.reg2 != .none); | |
| 642 | // OP [reg1 + scale*index + imm32], reg2 | |
| 647 | 643 | return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ |
| 648 | .disp = imm, | |
| 644 | .disp = index_reg_disp.disp, | |
| 649 | 645 | .base = ops.reg1, |
| 650 | 646 | .scale_index = scale_index, |
| 651 | 647 | }), ops.reg2, emit.code); |
| ... | ... | @@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 655 | 651 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 656 | 652 | const scale = ops.flags; |
| 657 | 653 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 658 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | |
| 654 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); | |
| 659 | 655 | const scale_index = ScaleIndex{ |
| 660 | 656 | .scale = scale, |
| 661 | .index = .rax, | |
| 657 | .index = index_reg_disp_imm.index, | |
| 662 | 658 | }; |
| 663 | // OP qword ptr [reg1 + scale*rax + imm32], imm32 | |
| 659 | // OP qword ptr [reg1 + scale*index + imm32], imm32 | |
| 664 | 660 | return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{ |
| 665 | .disp = imm_pair.dest_off, | |
| 661 | .disp = index_reg_disp_imm.disp, | |
| 666 | 662 | .base = ops.reg1, |
| 667 | 663 | .scale_index = scale_index, |
| 668 | }), imm_pair.operand, emit.code); | |
| 664 | }), index_reg_disp_imm.imm, emit.code); | |
| 669 | 665 | } |
| 670 | 666 | |
| 671 | 667 | fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 672 | 668 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 673 | 669 | assert(ops.reg2 == .none); |
| 674 | 670 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 675 | const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; | |
| 671 | const index_reg_disp_imm = emit.mir.extraData(Mir.IndexRegisterDispImm, payload).data.decode(); | |
| 676 | 672 | const ptr_size: Memory.PtrSize = switch (ops.flags) { |
| 677 | 673 | 0b00 => .byte_ptr, |
| 678 | 674 | 0b01 => .word_ptr, |
| ... | ... | @@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v |
| 681 | 677 | }; |
| 682 | 678 | const scale_index = ScaleIndex{ |
| 683 | 679 | .scale = 0, |
| 684 | .index = .rax, | |
| 680 | .index = index_reg_disp_imm.index, | |
| 685 | 681 | }; |
| 686 | // OP ptr [reg1 + rax*1 + imm32], imm32 | |
| 682 | // OP ptr [reg1 + index + imm32], imm32 | |
| 687 | 683 | return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ |
| 688 | .disp = imm_pair.dest_off, | |
| 684 | .disp = index_reg_disp_imm.disp, | |
| 689 | 685 | .base = ops.reg1, |
| 690 | 686 | .scale_index = scale_index, |
| 691 | }), imm_pair.operand, emit.code); | |
| 687 | }), index_reg_disp_imm.imm, emit.code); | |
| 692 | 688 | } |
| 693 | 689 | |
| 694 | 690 | fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| ... | ... | @@ -956,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 956 | 952 | mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); |
| 957 | 953 | }, |
| 958 | 954 | 0b10 => { |
| 959 | // lea reg, [rbp + rcx + imm32] | |
| 960 | const imm = emit.mir.instructions.items(.data)[inst].imm; | |
| 955 | // lea reg, [rbp + index + imm32] | |
| 956 | const payload = emit.mir.instructions.items(.data)[inst].payload; | |
| 957 | const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode(); | |
| 961 | 958 | const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; |
| 962 | 959 | const scale_index = ScaleIndex{ |
| 963 | 960 | .scale = 0, |
| 964 | .index = .rcx, | |
| 961 | .index = index_reg_disp.index, | |
| 965 | 962 | }; |
| 966 | 963 | return lowerToRmEnc( |
| 967 | 964 | .lea, |
| 968 | 965 | ops.reg1, |
| 969 | 966 | RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ |
| 970 | .disp = imm, | |
| 967 | .disp = index_reg_disp.disp, | |
| 971 | 968 | .base = src_reg, |
| 972 | 969 | .scale_index = scale_index, |
| 973 | 970 | }), |
| ... | ... | @@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 985 | 982 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; |
| 986 | 983 | |
| 987 | 984 | switch (ops.flags) { |
| 988 | 0b00, 0b01 => {}, | |
| 989 | else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}), | |
| 985 | 0b00, 0b01, 0b10 => {}, | |
| 986 | else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}), | |
| 990 | 987 | } |
| 991 | 988 | |
| 992 | 989 | // lea reg1, [rip + reloc] |
| ... | ... | @@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1024 | 1021 | .@"type" = switch (ops.flags) { |
| 1025 | 1022 | 0b00 => .got, |
| 1026 | 1023 | 0b01 => .direct, |
| 1024 | 0b10 => .imports, | |
| 1027 | 1025 | else => unreachable, |
| 1028 | 1026 | }, |
| 1029 | 1027 | .target = .{ .sym_index = relocation.sym_index, .file = null }, |
| ... | ... | @@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1031 | 1029 | .addend = 0, |
| 1032 | 1030 | .pcrel = true, |
| 1033 | 1031 | .length = 2, |
| 1034 | .prev_vaddr = atom.getSymbol(coff_file).value, | |
| 1035 | 1032 | }); |
| 1036 | 1033 | } else { |
| 1037 | 1034 | return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); |
| ... | ... | @@ -1157,6 +1154,17 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1157 | 1154 | .length = 2, |
| 1158 | 1155 | .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1159 | 1156 | }); |
| 1157 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 1158 | // Add relocation to the decl. | |
| 1159 | const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?; | |
| 1160 | try atom.addRelocation(coff_file, .{ | |
| 1161 | .@"type" = .direct, | |
| 1162 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | |
| 1163 | .offset = offset, | |
| 1164 | .addend = 0, | |
| 1165 | .pcrel = true, | |
| 1166 | .length = 2, | |
| 1167 | }); | |
| 1160 | 1168 | } else { |
| 1161 | 1169 | return emit.fail("TODO implement call_extern for linking backends different than MachO", .{}); |
| 1162 | 1170 | } |
| ... | ... | @@ -2241,6 +2249,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st |
| 2241 | 2249 | encoder.rex(.{ |
| 2242 | 2250 | .w = wide, |
| 2243 | 2251 | .b = base.isExtended(), |
| 2252 | .x = if (mem_op.scale_index) |si| si.index.isExtended() else false, | |
| 2244 | 2253 | }); |
| 2245 | 2254 | } |
| 2246 | 2255 | opc.encode(encoder); |
| ... | ... | @@ -2346,10 +2355,12 @@ fn lowerToMiXEnc( |
| 2346 | 2355 | encoder.rex(.{ |
| 2347 | 2356 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2348 | 2357 | .b = base.isExtended(), |
| 2358 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2349 | 2359 | }); |
| 2350 | 2360 | } else { |
| 2351 | 2361 | encoder.rex(.{ |
| 2352 | 2362 | .w = dst_mem.ptr_size == .qword_ptr, |
| 2363 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2353 | 2364 | }); |
| 2354 | 2365 | } |
| 2355 | 2366 | opc.encode(encoder); |
| ... | ... | @@ -2401,11 +2412,13 @@ fn lowerToRmEnc( |
| 2401 | 2412 | .w = setRexWRegister(reg), |
| 2402 | 2413 | .r = reg.isExtended(), |
| 2403 | 2414 | .b = base.isExtended(), |
| 2415 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2404 | 2416 | }); |
| 2405 | 2417 | } else { |
| 2406 | 2418 | encoder.rex(.{ |
| 2407 | 2419 | .w = setRexWRegister(reg), |
| 2408 | 2420 | .r = reg.isExtended(), |
| 2421 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2409 | 2422 | }); |
| 2410 | 2423 | } |
| 2411 | 2424 | opc.encode(encoder); |
| ... | ... | @@ -2446,11 +2459,13 @@ fn lowerToMrEnc( |
| 2446 | 2459 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2447 | 2460 | .r = reg.isExtended(), |
| 2448 | 2461 | .b = base.isExtended(), |
| 2462 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2449 | 2463 | }); |
| 2450 | 2464 | } else { |
| 2451 | 2465 | encoder.rex(.{ |
| 2452 | 2466 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), |
| 2453 | 2467 | .r = reg.isExtended(), |
| 2468 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2454 | 2469 | }); |
| 2455 | 2470 | } |
| 2456 | 2471 | opc.encode(encoder); |
| ... | ... | @@ -2490,11 +2505,13 @@ fn lowerToRmiEnc( |
| 2490 | 2505 | .w = setRexWRegister(reg), |
| 2491 | 2506 | .r = reg.isExtended(), |
| 2492 | 2507 | .b = base.isExtended(), |
| 2508 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2493 | 2509 | }); |
| 2494 | 2510 | } else { |
| 2495 | 2511 | encoder.rex(.{ |
| 2496 | 2512 | .w = setRexWRegister(reg), |
| 2497 | 2513 | .r = reg.isExtended(), |
| 2514 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2498 | 2515 | }); |
| 2499 | 2516 | } |
| 2500 | 2517 | opc.encode(encoder); |
| ... | ... | @@ -2531,10 +2548,12 @@ fn lowerToVmEnc( |
| 2531 | 2548 | vex.rex(.{ |
| 2532 | 2549 | .r = reg.isExtended(), |
| 2533 | 2550 | .b = base.isExtended(), |
| 2551 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2534 | 2552 | }); |
| 2535 | 2553 | } else { |
| 2536 | 2554 | vex.rex(.{ |
| 2537 | 2555 | .r = reg.isExtended(), |
| 2556 | .x = if (src_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2538 | 2557 | }); |
| 2539 | 2558 | } |
| 2540 | 2559 | encoder.vex(enc.prefix); |
| ... | ... | @@ -2571,10 +2590,12 @@ fn lowerToMvEnc( |
| 2571 | 2590 | vex.rex(.{ |
| 2572 | 2591 | .r = reg.isExtended(), |
| 2573 | 2592 | .b = base.isExtended(), |
| 2593 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2574 | 2594 | }); |
| 2575 | 2595 | } else { |
| 2576 | 2596 | vex.rex(.{ |
| 2577 | 2597 | .r = reg.isExtended(), |
| 2598 | .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false, | |
| 2578 | 2599 | }); |
| 2579 | 2600 | } |
| 2580 | 2601 | encoder.vex(enc.prefix); |
src/arch/x86_64/Mir.zig+107-42| ... | ... | @@ -44,25 +44,28 @@ pub const Inst = struct { |
| 44 | 44 | /// 0b01 word ptr [reg1 + imm32], imm16 |
| 45 | 45 | /// 0b10 dword ptr [reg1 + imm32], imm32 |
| 46 | 46 | /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64) |
| 47 | /// Notes: | |
| 48 | /// * Uses `ImmPair` as payload | |
| 47 | 49 | adc_mem_imm, |
| 48 | 50 | |
| 49 | /// form: reg1, [reg2 + scale*rcx + imm32] | |
| 51 | /// form: reg1, [reg2 + scale*index + imm32] | |
| 50 | 52 | /// ops flags scale |
| 51 | 53 | /// 0b00 1 |
| 52 | 54 | /// 0b01 2 |
| 53 | 55 | /// 0b10 4 |
| 54 | 56 | /// 0b11 8 |
| 57 | /// Notes: | |
| 58 | /// * Uses `IndexRegisterDisp` as payload | |
| 55 | 59 | adc_scale_src, |
| 56 | 60 | |
| 57 | /// form: [reg1 + scale*rax + imm32], reg2 | |
| 58 | /// form: [reg1 + scale*rax + 0], imm32 | |
| 61 | /// form: [reg1 + scale*index + imm32], reg2 | |
| 59 | 62 | /// ops flags scale |
| 60 | 63 | /// 0b00 1 |
| 61 | 64 | /// 0b01 2 |
| 62 | 65 | /// 0b10 4 |
| 63 | 66 | /// 0b11 8 |
| 64 | 67 | /// Notes: |
| 65 | /// * If reg2 is `none` then it means Data field `imm` is used as the immediate. | |
| 68 | /// * Uses `IndexRegisterDisp` payload. | |
| 66 | 69 | adc_scale_dst, |
| 67 | 70 | |
| 68 | 71 | /// form: [reg1 + scale*rax + imm32], imm32 |
| ... | ... | @@ -72,14 +75,16 @@ pub const Inst = struct { |
| 72 | 75 | /// 0b10 4 |
| 73 | 76 | /// 0b11 8 |
| 74 | 77 | /// Notes: |
| 75 | /// * Data field `payload` points at `ImmPair`. | |
| 78 | /// * Uses `IndexRegisterDispImm` payload. | |
| 76 | 79 | adc_scale_imm, |
| 77 | 80 | |
| 78 | 81 | /// ops flags: form: |
| 79 | /// 0b00 byte ptr [reg1 + rax + imm32], imm8 | |
| 80 | /// 0b01 word ptr [reg1 + rax + imm32], imm16 | |
| 81 | /// 0b10 dword ptr [reg1 + rax + imm32], imm32 | |
| 82 | /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64) | |
| 82 | /// 0b00 byte ptr [reg1 + index + imm32], imm8 | |
| 83 | /// 0b01 word ptr [reg1 + index + imm32], imm16 | |
| 84 | /// 0b10 dword ptr [reg1 + index + imm32], imm32 | |
| 85 | /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64) | |
| 86 | /// Notes: | |
| 87 | /// * Uses `IndexRegisterDispImm` payload. | |
| 83 | 88 | adc_mem_index_imm, |
| 84 | 89 | |
| 85 | 90 | // The following instructions all have the same encoding as `adc`. |
| ... | ... | @@ -174,12 +179,15 @@ pub const Inst = struct { |
| 174 | 179 | /// 0b00 reg1, [reg2 + imm32] |
| 175 | 180 | /// 0b00 reg1, [ds:imm32] |
| 176 | 181 | /// 0b01 reg1, [rip + imm32] |
| 177 | /// 0b10 reg1, [reg2 + rcx + imm32] | |
| 182 | /// 0b10 reg1, [reg2 + index + imm32] | |
| 183 | /// Notes: | |
| 184 | /// * 0b10 uses `IndexRegisterDisp` payload | |
| 178 | 185 | lea, |
| 179 | 186 | |
| 180 | 187 | /// ops flags: form: |
| 181 | 188 | /// 0b00 reg1, [rip + reloc] // via GOT PIC |
| 182 | 189 | /// 0b01 reg1, [rip + reloc] // direct load PIC |
| 190 | /// 0b10 reg1, [rip + reloc] // via imports table PIC | |
| 183 | 191 | /// Notes: |
| 184 | 192 | /// * `Data` contains `relocation` |
| 185 | 193 | lea_pic, |
| ... | ... | @@ -460,46 +468,103 @@ pub const Inst = struct { |
| 460 | 468 | } |
| 461 | 469 | }; |
| 462 | 470 | |
| 463 | pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type { | |
| 464 | assert(registers.len <= @bitSizeOf(u32)); | |
| 465 | return struct { | |
| 466 | bitset: RegBitSet = RegBitSet.initEmpty(), | |
| 471 | pub const IndexRegisterDisp = struct { | |
| 472 | /// Index register to use with SIB-based encoding | |
| 473 | index: u32, | |
| 467 | 474 | |
| 468 | const RegBitSet = IntegerBitSet(registers.len); | |
| 469 | const Self = @This(); | |
| 475 | /// Displacement value | |
| 476 | disp: u32, | |
| 470 | 477 | |
| 471 | fn getIndexForReg(reg: Reg) RegBitSet.MaskInt { | |
| 472 | inline for (registers) |cpreg, i| { | |
| 473 | if (reg.id() == cpreg.id()) return i; | |
| 474 | } | |
| 475 | unreachable; // register not in input register list! | |
| 476 | } | |
| 478 | pub fn encode(index: Register, disp: u32) IndexRegisterDisp { | |
| 479 | return .{ | |
| 480 | .index = @enumToInt(index), | |
| 481 | .disp = disp, | |
| 482 | }; | |
| 483 | } | |
| 477 | 484 | |
| 478 | pub fn push(self: *Self, reg: Reg) void { | |
| 479 | const index = getIndexForReg(reg); | |
| 480 | self.bitset.set(index); | |
| 481 | } | |
| 485 | pub fn decode(this: IndexRegisterDisp) struct { | |
| 486 | index: Register, | |
| 487 | disp: u32, | |
| 488 | } { | |
| 489 | return .{ | |
| 490 | .index = @intToEnum(Register, this.index), | |
| 491 | .disp = this.disp, | |
| 492 | }; | |
| 493 | } | |
| 494 | }; | |
| 482 | 495 | |
| 483 | pub fn isSet(self: Self, reg: Reg) bool { | |
| 484 | const index = getIndexForReg(reg); | |
| 485 | return self.bitset.isSet(index); | |
| 486 | } | |
| 496 | /// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list | |
| 497 | /// instead of having two structs, one a superset of the other one? | |
| 498 | pub const IndexRegisterDispImm = struct { | |
| 499 | /// Index register to use with SIB-based encoding | |
| 500 | index: u32, | |
| 487 | 501 | |
| 488 | pub fn asInt(self: Self) u32 { | |
| 489 | return self.bitset.mask; | |
| 490 | } | |
| 502 | /// Displacement value | |
| 503 | disp: u32, | |
| 491 | 504 | |
| 492 | pub fn fromInt(mask: u32) Self { | |
| 493 | return .{ | |
| 494 | .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) }, | |
| 495 | }; | |
| 496 | } | |
| 505 | /// Immediate | |
| 506 | imm: u32, | |
| 507 | ||
| 508 | pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm { | |
| 509 | return .{ | |
| 510 | .index = @enumToInt(index), | |
| 511 | .disp = disp, | |
| 512 | .imm = imm, | |
| 513 | }; | |
| 514 | } | |
| 497 | 515 | |
| 498 | pub fn count(self: Self) u32 { | |
| 499 | return @intCast(u32, self.bitset.count()); | |
| 516 | pub fn decode(this: IndexRegisterDispImm) struct { | |
| 517 | index: Register, | |
| 518 | disp: u32, | |
| 519 | imm: u32, | |
| 520 | } { | |
| 521 | return .{ | |
| 522 | .index = @intToEnum(Register, this.index), | |
| 523 | .disp = this.disp, | |
| 524 | .imm = this.imm, | |
| 525 | }; | |
| 526 | } | |
| 527 | }; | |
| 528 | ||
| 529 | /// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers | |
| 530 | /// in a compact manner. | |
| 531 | pub const RegisterList = struct { | |
| 532 | bitset: BitSet = BitSet.initEmpty(), | |
| 533 | ||
| 534 | const BitSet = IntegerBitSet(@ctz(@as(u32, 0))); | |
| 535 | const Self = @This(); | |
| 536 | ||
| 537 | fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt { | |
| 538 | for (registers) |cpreg, i| { | |
| 539 | if (reg.id() == cpreg.id()) return @intCast(u32, i); | |
| 500 | 540 | } |
| 501 | }; | |
| 502 | } | |
| 541 | unreachable; // register not in input register list! | |
| 542 | } | |
| 543 | ||
| 544 | pub fn push(self: *Self, registers: []const Register, reg: Register) void { | |
| 545 | const index = getIndexForReg(registers, reg); | |
| 546 | self.bitset.set(index); | |
| 547 | } | |
| 548 | ||
| 549 | pub fn isSet(self: Self, registers: []const Register, reg: Register) bool { | |
| 550 | const index = getIndexForReg(registers, reg); | |
| 551 | return self.bitset.isSet(index); | |
| 552 | } | |
| 553 | ||
| 554 | pub fn asInt(self: Self) u32 { | |
| 555 | return self.bitset.mask; | |
| 556 | } | |
| 557 | ||
| 558 | pub fn fromInt(mask: u32) Self { | |
| 559 | return .{ | |
| 560 | .bitset = BitSet{ .mask = @intCast(BitSet.MaskInt, mask) }, | |
| 561 | }; | |
| 562 | } | |
| 563 | ||
| 564 | pub fn count(self: Self) u32 { | |
| 565 | return @intCast(u32, self.bitset.count()); | |
| 566 | } | |
| 567 | }; | |
| 503 | 568 | |
| 504 | 569 | pub const SaveRegisterList = struct { |
| 505 | 570 | /// Use `RegisterList` to populate. |
src/arch/x86_64/abi.zig+60-14| ... | ... | @@ -392,23 +392,69 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { |
| 392 | 392 | } |
| 393 | 393 | } |
| 394 | 394 | |
| 395 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | |
| 396 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 397 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; | |
| 398 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | |
| 399 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | |
| 400 | /// In other words, these registers are free to use by the callee. | |
| 401 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 395 | pub const SysV = struct { | |
| 396 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | |
| 397 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 398 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; | |
| 399 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | |
| 400 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | |
| 401 | /// In other words, these registers are free to use by the callee. | |
| 402 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 402 | 403 | |
| 403 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | |
| 404 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | |
| 404 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | |
| 405 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | |
| 406 | }; | |
| 407 | ||
| 408 | pub const Win64 = struct { | |
| 409 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | |
| 410 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 411 | pub const callee_preserved_regs = [_]Register{ .rbx, .rsi, .rdi, .r12, .r13, .r14, .r15 }; | |
| 412 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | |
| 413 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | |
| 414 | /// In other words, these registers are free to use by the callee. | |
| 415 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 }; | |
| 405 | 416 | |
| 417 | pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 }; | |
| 418 | pub const c_abi_int_return_regs = [_]Register{.rax}; | |
| 419 | }; | |
| 420 | ||
| 421 | pub fn getCalleePreservedRegs(target: Target) []const Register { | |
| 422 | return switch (target.os.tag) { | |
| 423 | .windows => &Win64.callee_preserved_regs, | |
| 424 | else => &SysV.callee_preserved_regs, | |
| 425 | }; | |
| 426 | } | |
| 427 | ||
| 428 | pub fn getCallerPreservedRegs(target: Target) []const Register { | |
| 429 | return switch (target.os.tag) { | |
| 430 | .windows => &Win64.caller_preserved_regs, | |
| 431 | else => &SysV.caller_preserved_regs, | |
| 432 | }; | |
| 433 | } | |
| 434 | ||
| 435 | pub fn getCAbiIntParamRegs(target: Target) []const Register { | |
| 436 | return switch (target.os.tag) { | |
| 437 | .windows => &Win64.c_abi_int_param_regs, | |
| 438 | else => &SysV.c_abi_int_param_regs, | |
| 439 | }; | |
| 440 | } | |
| 441 | ||
| 442 | pub fn getCAbiIntReturnRegs(target: Target) []const Register { | |
| 443 | return switch (target.os.tag) { | |
| 444 | .windows => &Win64.c_abi_int_return_regs, | |
| 445 | else => &SysV.c_abi_int_return_regs, | |
| 446 | }; | |
| 447 | } | |
| 448 | ||
| 449 | const gp_regs = [_]Register{ | |
| 450 | .rbx, .r12, .r13, .r14, .r15, .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11, | |
| 451 | }; | |
| 406 | 452 | const sse_avx_regs = [_]Register{ |
| 407 | 453 | .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, |
| 408 | 454 | .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, |
| 409 | 455 | }; |
| 410 | const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs; | |
| 411 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); | |
| 456 | const allocatable_regs = gp_regs ++ sse_avx_regs; | |
| 457 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs); | |
| 412 | 458 | |
| 413 | 459 | // Register classes |
| 414 | 460 | const RegisterBitSet = RegisterManager.RegisterBitSet; |
| ... | ... | @@ -417,15 +463,15 @@ pub const RegisterClass = struct { |
| 417 | 463 | var set = RegisterBitSet.initEmpty(); |
| 418 | 464 | set.setRangeValue(.{ |
| 419 | 465 | .start = 0, |
| 420 | .end = caller_preserved_regs.len + callee_preserved_regs.len, | |
| 466 | .end = gp_regs.len, | |
| 421 | 467 | }, true); |
| 422 | 468 | break :blk set; |
| 423 | 469 | }; |
| 424 | 470 | pub const sse: RegisterBitSet = blk: { |
| 425 | 471 | var set = RegisterBitSet.initEmpty(); |
| 426 | 472 | set.setRangeValue(.{ |
| 427 | .start = caller_preserved_regs.len + callee_preserved_regs.len, | |
| 428 | .end = allocatable_registers.len, | |
| 473 | .start = gp_regs.len, | |
| 474 | .end = allocatable_regs.len, | |
| 429 | 475 | }, true); |
| 430 | 476 | break :blk set; |
| 431 | 477 | }; |
src/codegen/llvm.zig+11-2| ... | ... | @@ -3912,7 +3912,7 @@ pub const DeclGen = struct { |
| 3912 | 3912 | var b: usize = 0; |
| 3913 | 3913 | for (parent_ty.structFields().values()[0..field_index]) |field| { |
| 3914 | 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 | 3917 | break :b b; |
| 3918 | 3918 | }; |
| ... | ... | @@ -9385,6 +9385,12 @@ pub const FuncGen = struct { |
| 9385 | 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 | 9394 | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); |
| 9389 | 9395 | } |
| 9390 | 9396 | |
| ... | ... | @@ -9416,7 +9422,10 @@ pub const FuncGen = struct { |
| 9416 | 9422 | // Convert to equally-sized integer type in order to perform the bit |
| 9417 | 9423 | // operations on the value to store |
| 9418 | 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 | 9430 | var mask_val = value_bits_type.constAllOnes(); |
| 9422 | 9431 | mask_val = mask_val.constZExt(containing_int_ty); |
src/link.zig+4-1| ... | ... | @@ -166,6 +166,9 @@ pub const Options = struct { |
| 166 | 166 | version_script: ?[]const u8, |
| 167 | 167 | soname: ?[]const u8, |
| 168 | 168 | llvm_cpu_features: ?[*:0]const u8, |
| 169 | print_gc_sections: bool, | |
| 170 | print_icf_sections: bool, | |
| 171 | print_map: bool, | |
| 169 | 172 | |
| 170 | 173 | objects: []Compilation.LinkObject, |
| 171 | 174 | framework_dirs: []const []const u8, |
| ... | ... | @@ -476,7 +479,7 @@ pub const File = struct { |
| 476 | 479 | log.debug("getGlobalSymbol '{s}'", .{name}); |
| 477 | 480 | switch (base.tag) { |
| 478 | 481 | // zig fmt: off |
| 479 | .coff => unreachable, | |
| 482 | .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name), | |
| 480 | 483 | .elf => unreachable, |
| 481 | 484 | .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name), |
| 482 | 485 | .plan9 => unreachable, |
src/link/Coff.zig+674-221| ... | ... | @@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig"); |
| 30 | 30 | pub const base_tag: link.File.Tag = .coff; |
| 31 | 31 | |
| 32 | 32 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 33 | const N_DATA_DIRS: u5 = 16; | |
| 34 | 33 | |
| 35 | 34 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 36 | 35 | llvm_object: ?*LlvmObject = null, |
| ... | ... | @@ -44,24 +43,33 @@ page_size: u32, |
| 44 | 43 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 45 | 44 | |
| 46 | 45 | sections: std.MultiArrayList(Section) = .{}, |
| 47 | data_directories: [N_DATA_DIRS]coff.ImageDataDirectory, | |
| 46 | data_directories: [coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory, | |
| 48 | 47 | |
| 49 | 48 | text_section_index: ?u16 = null, |
| 50 | 49 | got_section_index: ?u16 = null, |
| 51 | 50 | rdata_section_index: ?u16 = null, |
| 52 | 51 | data_section_index: ?u16 = null, |
| 53 | 52 | reloc_section_index: ?u16 = null, |
| 53 | idata_section_index: ?u16 = null, | |
| 54 | 54 | |
| 55 | 55 | locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, |
| 56 | globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, | |
| 56 | globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, | |
| 57 | resolver: std.StringHashMapUnmanaged(u32) = .{}, | |
| 58 | unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{}, | |
| 57 | 59 | |
| 58 | 60 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 61 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 59 | 62 | |
| 60 | 63 | strtab: StringTable(.strtab) = .{}, |
| 61 | 64 | strtab_offset: ?u32 = null, |
| 62 | 65 | |
| 63 | got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | |
| 66 | got_entries: std.ArrayListUnmanaged(Entry) = .{}, | |
| 64 | 67 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 68 | got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | |
| 69 | ||
| 70 | imports: std.ArrayListUnmanaged(Entry) = .{}, | |
| 71 | imports_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 72 | imports_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | |
| 65 | 73 | |
| 66 | 74 | /// Virtual address of the entry point procedure relative to image base. |
| 67 | 75 | entry_addr: ?u32 = null, |
| ... | ... | @@ -109,17 +117,33 @@ relocs: RelocTable = .{}, |
| 109 | 117 | /// this will be a table indexed by index into the list of Atoms. |
| 110 | 118 | base_relocs: BaseRelocationTable = .{}, |
| 111 | 119 | |
| 120 | const Entry = struct { | |
| 121 | target: SymbolWithLoc, | |
| 122 | // Index into the synthetic symbol table (i.e., file == null). | |
| 123 | sym_index: u32, | |
| 124 | }; | |
| 125 | ||
| 112 | 126 | pub const Reloc = struct { |
| 113 | 127 | @"type": enum { |
| 114 | 128 | got, |
| 115 | 129 | direct, |
| 130 | imports, | |
| 116 | 131 | }, |
| 117 | 132 | target: SymbolWithLoc, |
| 118 | 133 | offset: u32, |
| 119 | 134 | addend: u32, |
| 120 | 135 | pcrel: bool, |
| 121 | 136 | length: u2, |
| 122 | prev_vaddr: u32, | |
| 137 | dirty: bool = true, | |
| 138 | ||
| 139 | /// Returns an Atom which is the target node of this relocation edge (if any). | |
| 140 | fn getTargetAtom(self: Reloc, coff_file: *Coff) ?*Atom { | |
| 141 | switch (self.@"type") { | |
| 142 | .got => return coff_file.getGotAtomForSymbol(self.target), | |
| 143 | .direct => return coff_file.getAtomForSymbol(self.target), | |
| 144 | .imports => return coff_file.getImportAtomForSymbol(self.target), | |
| 145 | } | |
| 146 | } | |
| 123 | 147 | }; |
| 124 | 148 | |
| 125 | 149 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); |
| ... | ... | @@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct { |
| 180 | 204 | |
| 181 | 205 | // null means it's a synthetic global or Zig source. |
| 182 | 206 | file: ?u32 = null, |
| 207 | ||
| 208 | pub fn eql(this: SymbolWithLoc, other: SymbolWithLoc) bool { | |
| 209 | if (this.file == null and other.file == null) { | |
| 210 | return this.sym_index == other.sym_index; | |
| 211 | } | |
| 212 | if (this.file != null and other.file != null) { | |
| 213 | return this.sym_index == other.sym_index and this.file.? == other.file.?; | |
| 214 | } | |
| 215 | return false; | |
| 216 | } | |
| 183 | 217 | }; |
| 184 | 218 | |
| 185 | 219 | /// When allocating, the ideal_capacity is calculated by |
| ... | ... | @@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { |
| 234 | 268 | }, |
| 235 | 269 | .ptr_width = ptr_width, |
| 236 | 270 | .page_size = page_size, |
| 237 | .data_directories = comptime mem.zeroes([N_DATA_DIRS]coff.ImageDataDirectory), | |
| 271 | .data_directories = comptime mem.zeroes([coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory), | |
| 238 | 272 | }; |
| 239 | 273 | |
| 240 | 274 | const use_llvm = build_options.have_llvm and options.use_llvm; |
| ... | ... | @@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void { |
| 269 | 303 | |
| 270 | 304 | self.locals.deinit(gpa); |
| 271 | 305 | self.globals.deinit(gpa); |
| 306 | ||
| 307 | { | |
| 308 | var it = self.resolver.keyIterator(); | |
| 309 | while (it.next()) |key_ptr| { | |
| 310 | gpa.free(key_ptr.*); | |
| 311 | } | |
| 312 | self.resolver.deinit(gpa); | |
| 313 | } | |
| 314 | ||
| 315 | self.unresolved.deinit(gpa); | |
| 272 | 316 | self.locals_free_list.deinit(gpa); |
| 273 | 317 | self.strtab.deinit(gpa); |
| 274 | 318 | self.got_entries.deinit(gpa); |
| 275 | 319 | self.got_entries_free_list.deinit(gpa); |
| 320 | self.got_entries_table.deinit(gpa); | |
| 321 | self.imports.deinit(gpa); | |
| 322 | self.imports_free_list.deinit(gpa); | |
| 323 | self.imports_table.deinit(gpa); | |
| 276 | 324 | self.decls.deinit(gpa); |
| 277 | 325 | self.atom_by_index_table.deinit(gpa); |
| 278 | 326 | |
| ... | ... | @@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 305 | 353 | assert(self.llvm_object == null); |
| 306 | 354 | const gpa = self.base.allocator; |
| 307 | 355 | |
| 356 | try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); | |
| 357 | self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); | |
| 358 | ||
| 359 | // Index 0 is always a null symbol. | |
| 360 | try self.locals.append(gpa, .{ | |
| 361 | .name = [_]u8{0} ** 8, | |
| 362 | .value = 0, | |
| 363 | .section_number = .UNDEFINED, | |
| 364 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | |
| 365 | .storage_class = .NULL, | |
| 366 | .number_of_aux_symbols = 0, | |
| 367 | }); | |
| 368 | ||
| 308 | 369 | if (self.text_section_index == null) { |
| 309 | self.text_section_index = @intCast(u16, self.sections.slice().len); | |
| 310 | 370 | const file_size = @intCast(u32, self.base.options.program_code_size_hint); |
| 311 | const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers | |
| 312 | log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 313 | var header = coff.SectionHeader{ | |
| 314 | .name = undefined, | |
| 315 | .virtual_size = file_size, | |
| 316 | .virtual_address = off, | |
| 317 | .size_of_raw_data = file_size, | |
| 318 | .pointer_to_raw_data = off, | |
| 319 | .pointer_to_relocations = 0, | |
| 320 | .pointer_to_linenumbers = 0, | |
| 321 | .number_of_relocations = 0, | |
| 322 | .number_of_linenumbers = 0, | |
| 323 | .flags = .{ | |
| 324 | .CNT_CODE = 1, | |
| 325 | .MEM_EXECUTE = 1, | |
| 326 | .MEM_READ = 1, | |
| 327 | }, | |
| 328 | }; | |
| 329 | try self.setSectionName(&header, ".text"); | |
| 330 | try self.sections.append(gpa, .{ .header = header }); | |
| 371 | self.text_section_index = try self.allocateSection(".text", file_size, .{ | |
| 372 | .CNT_CODE = 1, | |
| 373 | .MEM_EXECUTE = 1, | |
| 374 | .MEM_READ = 1, | |
| 375 | }); | |
| 331 | 376 | } |
| 332 | 377 | |
| 333 | 378 | if (self.got_section_index == null) { |
| 334 | self.got_section_index = @intCast(u16, self.sections.slice().len); | |
| 335 | 379 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); |
| 336 | const off = self.findFreeSpace(file_size, self.page_size); | |
| 337 | log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 338 | var header = coff.SectionHeader{ | |
| 339 | .name = undefined, | |
| 340 | .virtual_size = file_size, | |
| 341 | .virtual_address = off, | |
| 342 | .size_of_raw_data = file_size, | |
| 343 | .pointer_to_raw_data = off, | |
| 344 | .pointer_to_relocations = 0, | |
| 345 | .pointer_to_linenumbers = 0, | |
| 346 | .number_of_relocations = 0, | |
| 347 | .number_of_linenumbers = 0, | |
| 348 | .flags = .{ | |
| 349 | .CNT_INITIALIZED_DATA = 1, | |
| 350 | .MEM_READ = 1, | |
| 351 | }, | |
| 352 | }; | |
| 353 | try self.setSectionName(&header, ".got"); | |
| 354 | try self.sections.append(gpa, .{ .header = header }); | |
| 380 | self.got_section_index = try self.allocateSection(".got", file_size, .{ | |
| 381 | .CNT_INITIALIZED_DATA = 1, | |
| 382 | .MEM_READ = 1, | |
| 383 | }); | |
| 355 | 384 | } |
| 356 | 385 | |
| 357 | 386 | if (self.rdata_section_index == null) { |
| 358 | self.rdata_section_index = @intCast(u16, self.sections.slice().len); | |
| 359 | const file_size: u32 = 1024; | |
| 360 | const off = self.findFreeSpace(file_size, self.page_size); | |
| 361 | log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 362 | var header = coff.SectionHeader{ | |
| 363 | .name = undefined, | |
| 364 | .virtual_size = file_size, | |
| 365 | .virtual_address = off, | |
| 366 | .size_of_raw_data = file_size, | |
| 367 | .pointer_to_raw_data = off, | |
| 368 | .pointer_to_relocations = 0, | |
| 369 | .pointer_to_linenumbers = 0, | |
| 370 | .number_of_relocations = 0, | |
| 371 | .number_of_linenumbers = 0, | |
| 372 | .flags = .{ | |
| 373 | .CNT_INITIALIZED_DATA = 1, | |
| 374 | .MEM_READ = 1, | |
| 375 | }, | |
| 376 | }; | |
| 377 | try self.setSectionName(&header, ".rdata"); | |
| 378 | try self.sections.append(gpa, .{ .header = header }); | |
| 387 | const file_size: u32 = self.page_size; | |
| 388 | self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{ | |
| 389 | .CNT_INITIALIZED_DATA = 1, | |
| 390 | .MEM_READ = 1, | |
| 391 | }); | |
| 379 | 392 | } |
| 380 | 393 | |
| 381 | 394 | if (self.data_section_index == null) { |
| 382 | self.data_section_index = @intCast(u16, self.sections.slice().len); | |
| 383 | const file_size: u32 = 1024; | |
| 384 | const off = self.findFreeSpace(file_size, self.page_size); | |
| 385 | log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 386 | var header = coff.SectionHeader{ | |
| 387 | .name = undefined, | |
| 388 | .virtual_size = file_size, | |
| 389 | .virtual_address = off, | |
| 390 | .size_of_raw_data = file_size, | |
| 391 | .pointer_to_raw_data = off, | |
| 392 | .pointer_to_relocations = 0, | |
| 393 | .pointer_to_linenumbers = 0, | |
| 394 | .number_of_relocations = 0, | |
| 395 | .number_of_linenumbers = 0, | |
| 396 | .flags = .{ | |
| 397 | .CNT_INITIALIZED_DATA = 1, | |
| 398 | .MEM_READ = 1, | |
| 399 | .MEM_WRITE = 1, | |
| 400 | }, | |
| 401 | }; | |
| 402 | try self.setSectionName(&header, ".data"); | |
| 403 | try self.sections.append(gpa, .{ .header = header }); | |
| 395 | const file_size: u32 = self.page_size; | |
| 396 | self.data_section_index = try self.allocateSection(".data", file_size, .{ | |
| 397 | .CNT_INITIALIZED_DATA = 1, | |
| 398 | .MEM_READ = 1, | |
| 399 | .MEM_WRITE = 1, | |
| 400 | }); | |
| 401 | } | |
| 402 | ||
| 403 | if (self.idata_section_index == null) { | |
| 404 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); | |
| 405 | self.idata_section_index = try self.allocateSection(".idata", file_size, .{ | |
| 406 | .CNT_INITIALIZED_DATA = 1, | |
| 407 | .MEM_READ = 1, | |
| 408 | }); | |
| 404 | 409 | } |
| 405 | 410 | |
| 406 | 411 | if (self.reloc_section_index == null) { |
| 407 | self.reloc_section_index = @intCast(u16, self.sections.slice().len); | |
| 408 | 412 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation); |
| 409 | const off = self.findFreeSpace(file_size, self.page_size); | |
| 410 | log.debug("found .reloc free space 0x{x} to 0x{x}", .{ off, off + file_size }); | |
| 411 | var header = coff.SectionHeader{ | |
| 412 | .name = undefined, | |
| 413 | .virtual_size = file_size, | |
| 414 | .virtual_address = off, | |
| 415 | .size_of_raw_data = file_size, | |
| 416 | .pointer_to_raw_data = off, | |
| 417 | .pointer_to_relocations = 0, | |
| 418 | .pointer_to_linenumbers = 0, | |
| 419 | .number_of_relocations = 0, | |
| 420 | .number_of_linenumbers = 0, | |
| 421 | .flags = .{ | |
| 422 | .CNT_INITIALIZED_DATA = 1, | |
| 423 | .MEM_PURGEABLE = 1, | |
| 424 | .MEM_READ = 1, | |
| 425 | }, | |
| 426 | }; | |
| 427 | try self.setSectionName(&header, ".reloc"); | |
| 428 | try self.sections.append(gpa, .{ .header = header }); | |
| 413 | self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{ | |
| 414 | .CNT_INITIALIZED_DATA = 1, | |
| 415 | .MEM_DISCARDABLE = 1, | |
| 416 | .MEM_READ = 1, | |
| 417 | }); | |
| 429 | 418 | } |
| 430 | 419 | |
| 431 | 420 | if (self.strtab_offset == null) { |
| 432 | try self.strtab.buffer.append(gpa, 0); | |
| 433 | self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1); | |
| 434 | log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() }); | |
| 421 | const file_size = @intCast(u32, self.strtab.len()); | |
| 422 | self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here | |
| 423 | log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size }); | |
| 435 | 424 | } |
| 436 | 425 | |
| 437 | // Index 0 is always a null symbol. | |
| 438 | try self.locals.append(gpa, .{ | |
| 439 | .name = [_]u8{0} ** 8, | |
| 440 | .value = 0, | |
| 441 | .section_number = @intToEnum(coff.SectionNumber, 0), | |
| 442 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | |
| 443 | .storage_class = .NULL, | |
| 444 | .number_of_aux_symbols = 0, | |
| 445 | }); | |
| 446 | ||
| 447 | 426 | { |
| 448 | 427 | // We need to find out what the max file offset is according to section headers. |
| 449 | 428 | // Otherwise, we may end up with an COFF binary with file size not matching the final section's |
| ... | ... | @@ -459,6 +438,72 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 459 | 438 | } |
| 460 | 439 | } |
| 461 | 440 | |
| 441 | fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.SectionHeaderFlags) !u16 { | |
| 442 | const index = @intCast(u16, self.sections.slice().len); | |
| 443 | const off = self.findFreeSpace(size, default_file_alignment); | |
| 444 | // Memory is always allocated in sequence | |
| 445 | // TODO: investigate if we can allocate .text last; this way it would never need to grow in memory! | |
| 446 | const vaddr = blk: { | |
| 447 | if (index == 0) break :blk self.page_size; | |
| 448 | const prev_header = self.sections.items(.header)[index - 1]; | |
| 449 | break :blk mem.alignForwardGeneric(u32, prev_header.virtual_address + prev_header.virtual_size, self.page_size); | |
| 450 | }; | |
| 451 | // We commit more memory than needed upfront so that we don't have to reallocate too soon. | |
| 452 | const memsz = mem.alignForwardGeneric(u32, size, self.page_size) * 100; | |
| 453 | log.debug("found {s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ | |
| 454 | name, | |
| 455 | off, | |
| 456 | off + size, | |
| 457 | vaddr, | |
| 458 | vaddr + size, | |
| 459 | }); | |
| 460 | var header = coff.SectionHeader{ | |
| 461 | .name = undefined, | |
| 462 | .virtual_size = memsz, | |
| 463 | .virtual_address = vaddr, | |
| 464 | .size_of_raw_data = size, | |
| 465 | .pointer_to_raw_data = off, | |
| 466 | .pointer_to_relocations = 0, | |
| 467 | .pointer_to_linenumbers = 0, | |
| 468 | .number_of_relocations = 0, | |
| 469 | .number_of_linenumbers = 0, | |
| 470 | .flags = flags, | |
| 471 | }; | |
| 472 | try self.setSectionName(&header, name); | |
| 473 | try self.sections.append(self.base.allocator, .{ .header = header }); | |
| 474 | return index; | |
| 475 | } | |
| 476 | ||
| 477 | fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { | |
| 478 | const header = &self.sections.items(.header)[sect_id]; | |
| 479 | const increased_size = padToIdeal(needed_size); | |
| 480 | const old_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, header.virtual_size, self.page_size); | |
| 481 | const new_aligned_end = header.virtual_address + mem.alignForwardGeneric(u32, increased_size, self.page_size); | |
| 482 | const diff = new_aligned_end - old_aligned_end; | |
| 483 | log.debug("growing {s} in virtual memory by {x}", .{ self.getSectionName(header), diff }); | |
| 484 | ||
| 485 | // TODO: enforce order by increasing VM addresses in self.sections container. | |
| 486 | // This is required by the loader anyhow as far as I can tell. | |
| 487 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { | |
| 488 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id + 1 + next_sect_id]; | |
| 489 | next_header.virtual_address += diff; | |
| 490 | ||
| 491 | if (maybe_last_atom.*) |last_atom| { | |
| 492 | var atom = last_atom; | |
| 493 | while (true) { | |
| 494 | const sym = atom.getSymbolPtr(self); | |
| 495 | sym.value += diff; | |
| 496 | ||
| 497 | if (atom.prev) |prev| { | |
| 498 | atom = prev; | |
| 499 | } else break; | |
| 500 | } | |
| 501 | } | |
| 502 | } | |
| 503 | ||
| 504 | header.virtual_size = increased_size; | |
| 505 | } | |
| 506 | ||
| 462 | 507 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { |
| 463 | 508 | if (self.llvm_object) |_| return; |
| 464 | 509 | const decl = self.base.options.module.?.declPtr(decl_index); |
| ... | ... | @@ -542,16 +587,33 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 542 | 587 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 543 | 588 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; |
| 544 | 589 | if (needed_size > sect_capacity) { |
| 545 | @panic("TODO move section"); | |
| 590 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); | |
| 591 | const current_size = if (maybe_last_atom.*) |last_atom| blk: { | |
| 592 | const sym = last_atom.getSymbol(self); | |
| 593 | break :blk (sym.value + last_atom.size) - header.virtual_address; | |
| 594 | } else 0; | |
| 595 | log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset }); | |
| 596 | const amt = try self.base.file.?.copyRangeAll( | |
| 597 | header.pointer_to_raw_data, | |
| 598 | self.base.file.?, | |
| 599 | new_offset, | |
| 600 | current_size, | |
| 601 | ); | |
| 602 | if (amt != current_size) return error.InputOutput; | |
| 603 | header.pointer_to_raw_data = new_offset; | |
| 604 | } | |
| 605 | ||
| 606 | const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); | |
| 607 | if (needed_size > sect_vm_capacity) { | |
| 608 | try self.growSectionVM(sect_id, needed_size); | |
| 609 | self.markRelocsDirtyByAddress(header.virtual_address + needed_size); | |
| 546 | 610 | } |
| 611 | ||
| 612 | header.virtual_size = @maximum(header.virtual_size, needed_size); | |
| 613 | header.size_of_raw_data = needed_size; | |
| 547 | 614 | maybe_last_atom.* = atom; |
| 548 | // header.virtual_size = needed_size; | |
| 549 | // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment); | |
| 550 | 615 | } |
| 551 | 616 | |
| 552 | // if (header.getAlignment().? < alignment) { | |
| 553 | // header.setAlignment(alignment); | |
| 554 | // } | |
| 555 | 617 | atom.size = new_atom_size; |
| 556 | 618 | atom.alignment = alignment; |
| 557 | 619 | |
| ... | ... | @@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 { |
| 596 | 658 | self.locals.items[index] = .{ |
| 597 | 659 | .name = [_]u8{0} ** 8, |
| 598 | 660 | .value = 0, |
| 599 | .section_number = @intToEnum(coff.SectionNumber, 0), | |
| 661 | .section_number = .UNDEFINED, | |
| 600 | 662 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, |
| 601 | 663 | .storage_class = .NULL, |
| 602 | 664 | .number_of_aux_symbols = 0, |
| ... | ... | @@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 { |
| 605 | 667 | return index; |
| 606 | 668 | } |
| 607 | 669 | |
| 670 | fn allocateGlobal(self: *Coff) !u32 { | |
| 671 | const gpa = self.base.allocator; | |
| 672 | try self.globals.ensureUnusedCapacity(gpa, 1); | |
| 673 | ||
| 674 | const index = blk: { | |
| 675 | if (self.globals_free_list.popOrNull()) |index| { | |
| 676 | log.debug(" (reusing global index {d})", .{index}); | |
| 677 | break :blk index; | |
| 678 | } else { | |
| 679 | log.debug(" (allocating global index {d})", .{self.globals.items.len}); | |
| 680 | const index = @intCast(u32, self.globals.items.len); | |
| 681 | _ = self.globals.addOneAssumeCapacity(); | |
| 682 | break :blk index; | |
| 683 | } | |
| 684 | }; | |
| 685 | ||
| 686 | self.globals.items[index] = .{ | |
| 687 | .sym_index = 0, | |
| 688 | .file = null, | |
| 689 | }; | |
| 690 | ||
| 691 | return index; | |
| 692 | } | |
| 693 | ||
| 608 | 694 | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 609 | 695 | const gpa = self.base.allocator; |
| 610 | 696 | try self.got_entries.ensureUnusedCapacity(gpa, 1); |
| 697 | ||
| 611 | 698 | const index: u32 = blk: { |
| 612 | 699 | if (self.got_entries_free_list.popOrNull()) |index| { |
| 613 | 700 | log.debug(" (reusing GOT entry index {d})", .{index}); |
| 614 | if (self.got_entries.getIndex(target)) |existing| { | |
| 615 | assert(existing == index); | |
| 616 | } | |
| 617 | 701 | break :blk index; |
| 618 | 702 | } else { |
| 619 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len}); | |
| 620 | const index = @intCast(u32, self.got_entries.keys().len); | |
| 621 | self.got_entries.putAssumeCapacityNoClobber(target, 0); | |
| 703 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); | |
| 704 | const index = @intCast(u32, self.got_entries.items.len); | |
| 705 | _ = self.got_entries.addOneAssumeCapacity(); | |
| 622 | 706 | break :blk index; |
| 623 | 707 | } |
| 624 | 708 | }; |
| 625 | self.got_entries.keys()[index] = target; | |
| 709 | ||
| 710 | self.got_entries.items[index] = .{ .target = target, .sym_index = 0 }; | |
| 711 | try self.got_entries_table.putNoClobber(gpa, target, index); | |
| 712 | ||
| 713 | return index; | |
| 714 | } | |
| 715 | ||
| 716 | pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 { | |
| 717 | const gpa = self.base.allocator; | |
| 718 | try self.imports.ensureUnusedCapacity(gpa, 1); | |
| 719 | ||
| 720 | const index: u32 = blk: { | |
| 721 | if (self.imports_free_list.popOrNull()) |index| { | |
| 722 | log.debug(" (reusing import entry index {d})", .{index}); | |
| 723 | break :blk index; | |
| 724 | } else { | |
| 725 | log.debug(" (allocating import entry at index {d})", .{self.imports.items.len}); | |
| 726 | const index = @intCast(u32, self.imports.items.len); | |
| 727 | _ = self.imports.addOneAssumeCapacity(); | |
| 728 | break :blk index; | |
| 729 | } | |
| 730 | }; | |
| 731 | ||
| 732 | self.imports.items[index] = .{ .target = target, .sym_index = 0 }; | |
| 733 | try self.imports_table.putNoClobber(gpa, target, index); | |
| 734 | ||
| 626 | 735 | return index; |
| 627 | 736 | } |
| 628 | 737 | |
| ... | ... | @@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 637 | 746 | |
| 638 | 747 | try self.managed_atoms.append(gpa, atom); |
| 639 | 748 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); |
| 640 | self.got_entries.getPtr(target).?.* = atom.sym_index; | |
| 641 | 749 | |
| 642 | 750 | const sym = atom.getSymbolPtr(self); |
| 643 | 751 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); |
| ... | ... | @@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 652 | 760 | .addend = 0, |
| 653 | 761 | .pcrel = false, |
| 654 | 762 | .length = 3, |
| 655 | .prev_vaddr = sym.value, | |
| 656 | 763 | }); |
| 657 | 764 | |
| 658 | 765 | const target_sym = self.getSymbol(target); |
| ... | ... | @@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 666 | 773 | return atom; |
| 667 | 774 | } |
| 668 | 775 | |
| 776 | fn createImportAtom(self: *Coff) !*Atom { | |
| 777 | const gpa = self.base.allocator; | |
| 778 | const atom = try gpa.create(Atom); | |
| 779 | errdefer gpa.destroy(atom); | |
| 780 | atom.* = Atom.empty; | |
| 781 | atom.sym_index = try self.allocateSymbol(); | |
| 782 | atom.size = @sizeOf(u64); | |
| 783 | atom.alignment = @alignOf(u64); | |
| 784 | ||
| 785 | try self.managed_atoms.append(gpa, atom); | |
| 786 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 787 | ||
| 788 | const sym = atom.getSymbolPtr(self); | |
| 789 | sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1); | |
| 790 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | |
| 791 | ||
| 792 | log.debug("allocated import atom at 0x{x}", .{sym.value}); | |
| 793 | ||
| 794 | return atom; | |
| 795 | } | |
| 796 | ||
| 669 | 797 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { |
| 670 | 798 | const sym = atom.getSymbol(self); |
| 671 | 799 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; |
| ... | ... | @@ -686,12 +814,12 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void { |
| 686 | 814 | const sym = atom.getSymbol(self); |
| 687 | 815 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); |
| 688 | 816 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; |
| 689 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); | |
| 817 | log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ atom.getName(self), file_offset, file_offset + code.len }); | |
| 690 | 818 | try self.base.file.?.pwriteAll(code, file_offset); |
| 691 | 819 | try self.resolveRelocs(atom); |
| 692 | 820 | } |
| 693 | 821 | |
| 694 | fn writeGotAtom(self: *Coff, atom: *Atom) !void { | |
| 822 | fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void { | |
| 695 | 823 | switch (self.ptr_width) { |
| 696 | 824 | .p32 => { |
| 697 | 825 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); |
| ... | ... | @@ -704,6 +832,29 @@ fn writeGotAtom(self: *Coff, atom: *Atom) !void { |
| 704 | 832 | } |
| 705 | 833 | } |
| 706 | 834 | |
| 835 | fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { | |
| 836 | // TODO: reverse-lookup might come in handy here | |
| 837 | var it = self.relocs.valueIterator(); | |
| 838 | while (it.next()) |relocs| { | |
| 839 | for (relocs.items) |*reloc| { | |
| 840 | if (!reloc.target.eql(target)) continue; | |
| 841 | reloc.dirty = true; | |
| 842 | } | |
| 843 | } | |
| 844 | } | |
| 845 | ||
| 846 | fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { | |
| 847 | var it = self.relocs.valueIterator(); | |
| 848 | while (it.next()) |relocs| { | |
| 849 | for (relocs.items) |*reloc| { | |
| 850 | const target_atom = reloc.getTargetAtom(self) orelse continue; | |
| 851 | const target_sym = target_atom.getSymbol(self); | |
| 852 | if (target_sym.value < addr) continue; | |
| 853 | reloc.dirty = true; | |
| 854 | } | |
| 855 | } | |
| 856 | } | |
| 857 | ||
| 707 | 858 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 708 | 859 | const relocs = self.relocs.get(atom) orelse return; |
| 709 | 860 | const source_sym = atom.getSymbol(self); |
| ... | ... | @@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 713 | 864 | log.debug("relocating '{s}'", .{atom.getName(self)}); |
| 714 | 865 | |
| 715 | 866 | for (relocs.items) |*reloc| { |
| 716 | const target_vaddr = switch (reloc.@"type") { | |
| 717 | .got => blk: { | |
| 718 | const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue; | |
| 719 | break :blk got_atom.getSymbol(self).value; | |
| 720 | }, | |
| 721 | .direct => self.getSymbol(reloc.target).value, | |
| 722 | }; | |
| 723 | const target_vaddr_with_addend = target_vaddr + reloc.addend; | |
| 867 | if (!reloc.dirty) continue; | |
| 724 | 868 | |
| 725 | if (target_vaddr_with_addend == reloc.prev_vaddr) continue; | |
| 869 | const target_atom = reloc.getTargetAtom(self) orelse continue; | |
| 870 | const target_vaddr = target_atom.getSymbol(self).value; | |
| 871 | const target_vaddr_with_addend = target_vaddr + reloc.addend; | |
| 726 | 872 | |
| 727 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ | |
| 728 | reloc.offset, | |
| 873 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{ | |
| 874 | source_sym.value + reloc.offset, | |
| 729 | 875 | target_vaddr_with_addend, |
| 730 | 876 | self.getSymbolName(reloc.target), |
| 731 | 877 | @tagName(reloc.@"type"), |
| 878 | file_offset + reloc.offset, | |
| 732 | 879 | }); |
| 733 | 880 | |
| 881 | reloc.dirty = false; | |
| 882 | ||
| 734 | 883 | if (reloc.pcrel) { |
| 735 | 884 | const source_vaddr = source_sym.value + reloc.offset; |
| 736 | const disp = target_vaddr_with_addend - source_vaddr - 4; | |
| 737 | try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset); | |
| 738 | return; | |
| 885 | const disp = | |
| 886 | @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; | |
| 887 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); | |
| 888 | continue; | |
| 739 | 889 | } |
| 740 | 890 | |
| 741 | 891 | switch (self.ptr_width) { |
| ... | ... | @@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 755 | 905 | else => unreachable, |
| 756 | 906 | }, |
| 757 | 907 | } |
| 758 | ||
| 759 | reloc.prev_vaddr = target_vaddr_with_addend; | |
| 760 | 908 | } |
| 761 | 909 | } |
| 762 | 910 | |
| 763 | 911 | fn freeAtom(self: *Coff, atom: *Atom) void { |
| 764 | 912 | log.debug("freeAtom {*}", .{atom}); |
| 765 | 913 | |
| 914 | // Remove any relocs and base relocs associated with this Atom | |
| 915 | self.freeRelocationsForAtom(atom); | |
| 916 | ||
| 766 | 917 | const sym = atom.getSymbol(self); |
| 767 | 918 | const sect_id = @enumToInt(sym.section_number) - 1; |
| 768 | 919 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| ... | ... | @@ -825,11 +976,14 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 825 | 976 | const tracy = trace(@src()); |
| 826 | 977 | defer tracy.end(); |
| 827 | 978 | |
| 979 | const decl_index = func.owner_decl; | |
| 980 | const decl = module.declPtr(decl_index); | |
| 981 | self.freeUnnamedConsts(decl_index); | |
| 982 | self.freeRelocationsForAtom(&decl.link.coff); | |
| 983 | ||
| 828 | 984 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 829 | 985 | defer code_buffer.deinit(); |
| 830 | 986 | |
| 831 | const decl_index = func.owner_decl; | |
| 832 | const decl = module.declPtr(decl_index); | |
| 833 | 987 | const res = try codegen.generateFunction( |
| 834 | 988 | &self.base, |
| 835 | 989 | decl.srcLoc(), |
| ... | ... | @@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 856 | 1010 | } |
| 857 | 1011 | |
| 858 | 1012 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| 859 | _ = self; | |
| 860 | _ = tv; | |
| 861 | _ = decl_index; | |
| 862 | @panic("TODO lowerUnnamedConst"); | |
| 1013 | const gpa = self.base.allocator; | |
| 1014 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 1015 | defer code_buffer.deinit(); | |
| 1016 | ||
| 1017 | const mod = self.base.options.module.?; | |
| 1018 | const decl = mod.declPtr(decl_index); | |
| 1019 | ||
| 1020 | const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); | |
| 1021 | if (!gop.found_existing) { | |
| 1022 | gop.value_ptr.* = .{}; | |
| 1023 | } | |
| 1024 | const unnamed_consts = gop.value_ptr; | |
| 1025 | ||
| 1026 | const atom = try gpa.create(Atom); | |
| 1027 | errdefer gpa.destroy(atom); | |
| 1028 | atom.* = Atom.empty; | |
| 1029 | ||
| 1030 | atom.sym_index = try self.allocateSymbol(); | |
| 1031 | const sym = atom.getSymbolPtr(self); | |
| 1032 | const sym_name = blk: { | |
| 1033 | const decl_name = try decl.getFullyQualifiedName(mod); | |
| 1034 | defer gpa.free(decl_name); | |
| 1035 | ||
| 1036 | const index = unnamed_consts.items.len; | |
| 1037 | break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); | |
| 1038 | }; | |
| 1039 | defer gpa.free(sym_name); | |
| 1040 | try self.setSymbolName(sym, sym_name); | |
| 1041 | sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1); | |
| 1042 | ||
| 1043 | try self.managed_atoms.append(gpa, atom); | |
| 1044 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 1045 | ||
| 1046 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{ | |
| 1047 | .parent_atom_index = atom.sym_index, | |
| 1048 | }); | |
| 1049 | const code = switch (res) { | |
| 1050 | .externally_managed => |x| x, | |
| 1051 | .appended => code_buffer.items, | |
| 1052 | .fail => |em| { | |
| 1053 | decl.analysis = .codegen_failure; | |
| 1054 | try mod.failed_decls.put(mod.gpa, decl_index, em); | |
| 1055 | log.err("{s}", .{em.msg}); | |
| 1056 | return error.AnalysisFail; | |
| 1057 | }, | |
| 1058 | }; | |
| 1059 | ||
| 1060 | const required_alignment = tv.ty.abiAlignment(self.base.options.target); | |
| 1061 | atom.alignment = required_alignment; | |
| 1062 | atom.size = @intCast(u32, code.len); | |
| 1063 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | |
| 1064 | errdefer self.freeAtom(atom); | |
| 1065 | ||
| 1066 | try unnamed_consts.append(gpa, atom); | |
| 1067 | ||
| 1068 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value }); | |
| 1069 | log.debug(" (required alignment 0x{x})", .{required_alignment}); | |
| 1070 | ||
| 1071 | try self.writeAtom(atom, code); | |
| 1072 | ||
| 1073 | return atom.sym_index; | |
| 863 | 1074 | } |
| 864 | 1075 | |
| 865 | 1076 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | ... | @@ -884,6 +1095,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 884 | 1095 | } |
| 885 | 1096 | } |
| 886 | 1097 | |
| 1098 | self.freeRelocationsForAtom(&decl.link.coff); | |
| 1099 | ||
| 887 | 1100 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 888 | 1101 | defer code_buffer.deinit(); |
| 889 | 1102 | |
| ... | ... | @@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 892 | 1105 | .ty = decl.ty, |
| 893 | 1106 | .val = decl_val, |
| 894 | 1107 | }, &code_buffer, .none, .{ |
| 895 | .parent_atom_index = 0, | |
| 1108 | .parent_atom_index = decl.link.coff.sym_index, | |
| 896 | 1109 | }); |
| 897 | 1110 | const code = switch (res) { |
| 898 | 1111 | .externally_managed => |x| x, |
| ... | ... | @@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 970 | 1183 | if (vaddr != sym.value) { |
| 971 | 1184 | sym.value = vaddr; |
| 972 | 1185 | log.debug(" (updating GOT entry)", .{}); |
| 973 | const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?; | |
| 974 | try self.writeGotAtom(got_atom); | |
| 1186 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | |
| 1187 | const got_atom = self.getGotAtomForSymbol(got_target).?; | |
| 1188 | self.markRelocsDirtyByTarget(got_target); | |
| 1189 | try self.writePtrWidthAtom(got_atom); | |
| 975 | 1190 | } |
| 976 | 1191 | } else if (code_len < atom.size) { |
| 977 | 1192 | self.shrinkAtom(atom, code_len); |
| ... | ... | @@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 990 | 1205 | sym.value = vaddr; |
| 991 | 1206 | |
| 992 | 1207 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; |
| 993 | _ = try self.allocateGotEntry(got_target); | |
| 1208 | const got_index = try self.allocateGotEntry(got_target); | |
| 994 | 1209 | const got_atom = try self.createGotAtom(got_target); |
| 995 | try self.writeGotAtom(got_atom); | |
| 1210 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | |
| 1211 | try self.writePtrWidthAtom(got_atom); | |
| 996 | 1212 | } |
| 997 | 1213 | |
| 1214 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); | |
| 998 | 1215 | try self.writeAtom(atom, code); |
| 999 | 1216 | } |
| 1000 | 1217 | |
| 1218 | fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void { | |
| 1219 | _ = self.relocs.remove(atom); | |
| 1220 | _ = self.base_relocs.remove(atom); | |
| 1221 | } | |
| 1222 | ||
| 1223 | fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void { | |
| 1224 | const gpa = self.base.allocator; | |
| 1225 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; | |
| 1226 | for (unnamed_consts.items) |atom| { | |
| 1227 | self.freeAtom(atom); | |
| 1228 | self.locals_free_list.append(gpa, atom.sym_index) catch {}; | |
| 1229 | self.locals.items[atom.sym_index].section_number = .UNDEFINED; | |
| 1230 | _ = self.atom_by_index_table.remove(atom.sym_index); | |
| 1231 | log.debug(" adding local symbol index {d} to free list", .{atom.sym_index}); | |
| 1232 | atom.sym_index = 0; | |
| 1233 | } | |
| 1234 | unnamed_consts.clearAndFree(gpa); | |
| 1235 | } | |
| 1236 | ||
| 1001 | 1237 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1002 | 1238 | if (build_options.have_llvm) { |
| 1003 | 1239 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); |
| ... | ... | @@ -1011,6 +1247,7 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1011 | 1247 | const kv = self.decls.fetchRemove(decl_index); |
| 1012 | 1248 | if (kv.?.value) |_| { |
| 1013 | 1249 | self.freeAtom(&decl.link.coff); |
| 1250 | self.freeUnnamedConsts(decl_index); | |
| 1014 | 1251 | } |
| 1015 | 1252 | |
| 1016 | 1253 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| ... | ... | @@ -1021,14 +1258,20 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1021 | 1258 | |
| 1022 | 1259 | // Try freeing GOT atom if this decl had one |
| 1023 | 1260 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| 1024 | if (self.got_entries.getIndex(got_target)) |got_index| { | |
| 1261 | if (self.got_entries_table.get(got_target)) |got_index| { | |
| 1025 | 1262 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; |
| 1026 | self.got_entries.values()[got_index] = 0; | |
| 1263 | self.got_entries.items[got_index] = .{ | |
| 1264 | .target = .{ .sym_index = 0, .file = null }, | |
| 1265 | .sym_index = 0, | |
| 1266 | }; | |
| 1267 | _ = self.got_entries_table.remove(got_target); | |
| 1268 | ||
| 1027 | 1269 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); |
| 1028 | 1270 | } |
| 1029 | 1271 | |
| 1030 | self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0); | |
| 1272 | self.locals.items[sym_index].section_number = .UNDEFINED; | |
| 1031 | 1273 | _ = self.atom_by_index_table.remove(sym_index); |
| 1274 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | |
| 1032 | 1275 | decl.link.coff.sym_index = 0; |
| 1033 | 1276 | } |
| 1034 | 1277 | } |
| ... | ... | @@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1154 | 1397 | const sym = self.getSymbolPtr(sym_loc); |
| 1155 | 1398 | const sym_name = self.getSymbolName(sym_loc); |
| 1156 | 1399 | log.debug("deleting export '{s}'", .{sym_name}); |
| 1157 | assert(sym.storage_class == .EXTERNAL); | |
| 1400 | assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED); | |
| 1158 | 1401 | sym.* = .{ |
| 1159 | 1402 | .name = [_]u8{0} ** 8, |
| 1160 | 1403 | .value = 0, |
| 1161 | .section_number = @intToEnum(coff.SectionNumber, 0), | |
| 1404 | .section_number = .UNDEFINED, | |
| 1162 | 1405 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, |
| 1163 | 1406 | .storage_class = .NULL, |
| 1164 | 1407 | .number_of_aux_symbols = 0, |
| 1165 | 1408 | }; |
| 1166 | 1409 | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 1167 | 1410 | |
| 1168 | if (self.globals.get(sym_name)) |global| blk: { | |
| 1169 | if (global.sym_index != sym_index) break :blk; | |
| 1170 | if (global.file != null) break :blk; | |
| 1171 | const kv = self.globals.fetchSwapRemove(sym_name); | |
| 1172 | gpa.free(kv.?.key); | |
| 1411 | if (self.resolver.fetchRemove(sym_name)) |entry| { | |
| 1412 | defer gpa.free(entry.key); | |
| 1413 | self.globals_free_list.append(gpa, entry.value) catch {}; | |
| 1414 | self.globals.items[entry.value] = .{ | |
| 1415 | .sym_index = 0, | |
| 1416 | .file = null, | |
| 1417 | }; | |
| 1173 | 1418 | } |
| 1174 | 1419 | } |
| 1175 | 1420 | |
| 1176 | 1421 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { |
| 1177 | 1422 | const gpa = self.base.allocator; |
| 1178 | 1423 | const sym = self.getSymbol(current); |
| 1179 | _ = sym; | |
| 1180 | 1424 | const sym_name = self.getSymbolName(current); |
| 1181 | 1425 | |
| 1182 | const name = try gpa.dupe(u8, sym_name); | |
| 1183 | const global_index = @intCast(u32, self.globals.values().len); | |
| 1184 | _ = global_index; | |
| 1185 | const gop = try self.globals.getOrPut(gpa, name); | |
| 1186 | defer if (gop.found_existing) gpa.free(name); | |
| 1187 | ||
| 1188 | if (!gop.found_existing) { | |
| 1189 | gop.value_ptr.* = current; | |
| 1190 | // TODO undef + tentative | |
| 1426 | const global_index = self.resolver.get(sym_name) orelse { | |
| 1427 | const name = try gpa.dupe(u8, sym_name); | |
| 1428 | const global_index = try self.allocateGlobal(); | |
| 1429 | self.globals.items[global_index] = current; | |
| 1430 | try self.resolver.putNoClobber(gpa, name, global_index); | |
| 1431 | if (sym.section_number == .UNDEFINED) { | |
| 1432 | try self.unresolved.putNoClobber(gpa, global_index, false); | |
| 1433 | } | |
| 1191 | 1434 | return; |
| 1192 | } | |
| 1435 | }; | |
| 1193 | 1436 | |
| 1194 | 1437 | log.debug("TODO finish resolveGlobalSymbols implementation", .{}); |
| 1438 | ||
| 1439 | if (sym.section_number == .UNDEFINED) return; | |
| 1440 | ||
| 1441 | _ = self.unresolved.swapRemove(global_index); | |
| 1442 | self.globals.items[global_index] = current; | |
| 1195 | 1443 | } |
| 1196 | 1444 | |
| 1197 | 1445 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | ... | @@ -1227,6 +1475,17 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1227 | 1475 | sub_prog_node.activate(); |
| 1228 | 1476 | defer sub_prog_node.end(); |
| 1229 | 1477 | |
| 1478 | while (self.unresolved.popOrNull()) |entry| { | |
| 1479 | assert(entry.value); // We only expect imports generated by the incremental linker for now. | |
| 1480 | const global = self.globals.items[entry.key]; | |
| 1481 | if (self.imports_table.contains(global)) continue; | |
| 1482 | ||
| 1483 | const import_index = try self.allocateImportEntry(global); | |
| 1484 | const import_atom = try self.createImportAtom(); | |
| 1485 | self.imports.items[import_index].sym_index = import_atom.sym_index; | |
| 1486 | try self.writePtrWidthAtom(import_atom); | |
| 1487 | } | |
| 1488 | ||
| 1230 | 1489 | if (build_options.enable_logging) { |
| 1231 | 1490 | self.logSymtab(); |
| 1232 | 1491 | } |
| ... | ... | @@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1237 | 1496 | try self.resolveRelocs(atom.*); |
| 1238 | 1497 | } |
| 1239 | 1498 | } |
| 1499 | try self.writeImportTable(); | |
| 1240 | 1500 | try self.writeBaseRelocations(); |
| 1241 | 1501 | |
| 1242 | 1502 | if (self.getEntryPoint()) |entry_sym_loc| { |
| ... | ... | @@ -1262,10 +1522,47 @@ pub fn getDeclVAddr( |
| 1262 | 1522 | decl_index: Module.Decl.Index, |
| 1263 | 1523 | reloc_info: link.File.RelocInfo, |
| 1264 | 1524 | ) !u64 { |
| 1265 | _ = self; | |
| 1266 | _ = decl_index; | |
| 1267 | _ = reloc_info; | |
| 1268 | @panic("TODO getDeclVAddr"); | |
| 1525 | const mod = self.base.options.module.?; | |
| 1526 | const decl = mod.declPtr(decl_index); | |
| 1527 | ||
| 1528 | assert(self.llvm_object == null); | |
| 1529 | assert(decl.link.coff.sym_index != 0); | |
| 1530 | ||
| 1531 | const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?; | |
| 1532 | const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null }; | |
| 1533 | try atom.addRelocation(self, .{ | |
| 1534 | .@"type" = .direct, | |
| 1535 | .target = target, | |
| 1536 | .offset = @intCast(u32, reloc_info.offset), | |
| 1537 | .addend = reloc_info.addend, | |
| 1538 | .pcrel = false, | |
| 1539 | .length = 3, | |
| 1540 | }); | |
| 1541 | try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset)); | |
| 1542 | ||
| 1543 | return 0; | |
| 1544 | } | |
| 1545 | ||
| 1546 | pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { | |
| 1547 | if (self.resolver.get(name)) |global_index| { | |
| 1548 | return self.globals.items[global_index].sym_index; | |
| 1549 | } | |
| 1550 | ||
| 1551 | const gpa = self.base.allocator; | |
| 1552 | const sym_index = try self.allocateSymbol(); | |
| 1553 | const global_index = try self.allocateGlobal(); | |
| 1554 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 1555 | self.globals.items[global_index] = sym_loc; | |
| 1556 | ||
| 1557 | const sym_name = try gpa.dupe(u8, name); | |
| 1558 | const sym = self.getSymbolPtr(sym_loc); | |
| 1559 | try self.setSymbolName(sym, sym_name); | |
| 1560 | sym.storage_class = .EXTERNAL; | |
| 1561 | ||
| 1562 | try self.resolver.putNoClobber(gpa, sym_name, global_index); | |
| 1563 | try self.unresolved.putNoClobber(gpa, global_index, true); | |
| 1564 | ||
| 1565 | return sym_index; | |
| 1269 | 1566 | } |
| 1270 | 1567 | |
| 1271 | 1568 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1342 | 1639 | const header = &self.sections.items(.header)[self.reloc_section_index.?]; |
| 1343 | 1640 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 1344 | 1641 | const needed_size = @intCast(u32, buffer.items.len); |
| 1345 | assert(needed_size < sect_capacity); // TODO expand .reloc section | |
| 1642 | if (needed_size > sect_capacity) { | |
| 1643 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); | |
| 1644 | log.debug("writing {s} at 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ | |
| 1645 | self.getSectionName(header), | |
| 1646 | header.pointer_to_raw_data, | |
| 1647 | header.pointer_to_raw_data + needed_size, | |
| 1648 | new_offset, | |
| 1649 | new_offset + needed_size, | |
| 1650 | }); | |
| 1651 | header.pointer_to_raw_data = new_offset; | |
| 1652 | ||
| 1653 | const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); | |
| 1654 | if (needed_size > sect_vm_capacity) { | |
| 1655 | // TODO: we want to enforce .reloc after every alloc section. | |
| 1656 | try self.growSectionVM(self.reloc_section_index.?, needed_size); | |
| 1657 | } | |
| 1658 | } | |
| 1659 | header.virtual_size = @maximum(header.virtual_size, needed_size); | |
| 1660 | header.size_of_raw_data = needed_size; | |
| 1346 | 1661 | |
| 1347 | 1662 | try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); |
| 1348 | 1663 | |
| ... | ... | @@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1352 | 1667 | }; |
| 1353 | 1668 | } |
| 1354 | 1669 | |
| 1670 | fn writeImportTable(self: *Coff) !void { | |
| 1671 | if (self.idata_section_index == null) return; | |
| 1672 | ||
| 1673 | const gpa = self.base.allocator; | |
| 1674 | ||
| 1675 | const section = self.sections.get(self.idata_section_index.?); | |
| 1676 | const last_atom = section.last_atom orelse return; | |
| 1677 | ||
| 1678 | const iat_rva = section.header.virtual_address; | |
| 1679 | const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer | |
| 1680 | ||
| 1681 | const dll_name = "KERNEL32.dll"; | |
| 1682 | ||
| 1683 | var import_dir_entry = coff.ImportDirectoryEntry{ | |
| 1684 | .import_lookup_table_rva = @sizeOf(coff.ImportDirectoryEntry) * 2, | |
| 1685 | .time_date_stamp = 0, | |
| 1686 | .forwarder_chain = 0, | |
| 1687 | .name_rva = 0, | |
| 1688 | .import_address_table_rva = iat_rva, | |
| 1689 | }; | |
| 1690 | ||
| 1691 | // TODO: we currently assume there's only one (implicit) DLL - ntdll | |
| 1692 | var lookup_table = std.ArrayList(coff.ImportLookupEntry64.ByName).init(gpa); | |
| 1693 | defer lookup_table.deinit(); | |
| 1694 | ||
| 1695 | var names_table = std.ArrayList(u8).init(gpa); | |
| 1696 | defer names_table.deinit(); | |
| 1697 | ||
| 1698 | // TODO: check if import is still valid | |
| 1699 | for (self.imports.items) |entry| { | |
| 1700 | const target_name = self.getSymbolName(entry.target); | |
| 1701 | const start = names_table.items.len; | |
| 1702 | mem.writeIntLittle(u16, try names_table.addManyAsArray(2), 0); // TODO: currently, hint is set to 0 as we haven't yet parsed any DLL | |
| 1703 | try names_table.appendSlice(target_name); | |
| 1704 | try names_table.append(0); | |
| 1705 | const end = names_table.items.len; | |
| 1706 | if (!mem.isAlignedGeneric(usize, end - start, @sizeOf(u16))) { | |
| 1707 | try names_table.append(0); | |
| 1708 | } | |
| 1709 | try lookup_table.append(.{ .name_table_rva = @intCast(u31, start) }); | |
| 1710 | } | |
| 1711 | try lookup_table.append(.{ .name_table_rva = 0 }); // the sentinel | |
| 1712 | ||
| 1713 | const dir_entry_size = @sizeOf(coff.ImportDirectoryEntry) + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName) + names_table.items.len + dll_name.len + 1; | |
| 1714 | const needed_size = iat_size + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry); | |
| 1715 | const sect_capacity = self.allocatedSize(section.header.pointer_to_raw_data); | |
| 1716 | assert(needed_size < sect_capacity); // TODO: implement expanding .idata section | |
| 1717 | ||
| 1718 | // Fixup offsets | |
| 1719 | const base_rva = iat_rva + iat_size; | |
| 1720 | import_dir_entry.import_lookup_table_rva += base_rva; | |
| 1721 | import_dir_entry.name_rva = @intCast(u32, base_rva + dir_entry_size + @sizeOf(coff.ImportDirectoryEntry) - dll_name.len - 1); | |
| 1722 | ||
| 1723 | for (lookup_table.items[0 .. lookup_table.items.len - 1]) |*lk| { | |
| 1724 | lk.name_table_rva += @intCast(u31, base_rva + @sizeOf(coff.ImportDirectoryEntry) * 2 + lookup_table.items.len * @sizeOf(coff.ImportLookupEntry64.ByName)); | |
| 1725 | } | |
| 1726 | ||
| 1727 | var buffer = std.ArrayList(u8).init(gpa); | |
| 1728 | defer buffer.deinit(); | |
| 1729 | try buffer.ensureTotalCapacity(dir_entry_size + @sizeOf(coff.ImportDirectoryEntry)); | |
| 1730 | buffer.appendSliceAssumeCapacity(mem.asBytes(&import_dir_entry)); | |
| 1731 | buffer.appendNTimesAssumeCapacity(0, @sizeOf(coff.ImportDirectoryEntry)); // the sentinel; TODO: I think doing all of the above on bytes directly might be cleaner | |
| 1732 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(lookup_table.items)); | |
| 1733 | buffer.appendSliceAssumeCapacity(names_table.items); | |
| 1734 | buffer.appendSliceAssumeCapacity(dll_name); | |
| 1735 | buffer.appendAssumeCapacity(0); | |
| 1736 | ||
| 1737 | try self.base.file.?.pwriteAll(buffer.items, section.header.pointer_to_raw_data + iat_size); | |
| 1738 | // Override the IAT atoms | |
| 1739 | // TODO: we should rewrite only dirtied atoms, but that's for way later | |
| 1740 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(lookup_table.items), section.header.pointer_to_raw_data); | |
| 1741 | ||
| 1742 | self.data_directories[@enumToInt(coff.DirectoryEntry.IMPORT)] = .{ | |
| 1743 | .virtual_address = iat_rva + iat_size, | |
| 1744 | .size = @intCast(u32, @sizeOf(coff.ImportDirectoryEntry) * 2), | |
| 1745 | }; | |
| 1746 | ||
| 1747 | self.data_directories[@enumToInt(coff.DirectoryEntry.IAT)] = .{ | |
| 1748 | .virtual_address = iat_rva, | |
| 1749 | .size = iat_size, | |
| 1750 | }; | |
| 1751 | } | |
| 1752 | ||
| 1355 | 1753 | fn writeStrtab(self: *Coff) !void { |
| 1754 | if (self.strtab_offset == null) return; | |
| 1755 | ||
| 1356 | 1756 | const allocated_size = self.allocatedSize(self.strtab_offset.?); |
| 1357 | 1757 | const needed_size = @intCast(u32, self.strtab.len()); |
| 1358 | 1758 | |
| 1359 | 1759 | if (needed_size > allocated_size) { |
| 1360 | 1760 | self.strtab_offset = null; |
| 1361 | self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, 1)); | |
| 1761 | self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, @alignOf(u32))); | |
| 1362 | 1762 | } |
| 1363 | 1763 | |
| 1364 | 1764 | log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size }); |
| 1365 | try self.base.file.?.pwriteAll(self.strtab.buffer.items, self.strtab_offset.?); | |
| 1765 | ||
| 1766 | var buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 1767 | defer buffer.deinit(); | |
| 1768 | try buffer.ensureTotalCapacityPrecise(needed_size); | |
| 1769 | buffer.appendSliceAssumeCapacity(self.strtab.items()); | |
| 1770 | // Here, we do a trick in that we do not commit the size of the strtab to strtab buffer, instead | |
| 1771 | // we write the length of the strtab to a temporary buffer that goes to file. | |
| 1772 | mem.writeIntLittle(u32, buffer.items[0..4], @intCast(u32, self.strtab.len())); | |
| 1773 | ||
| 1774 | try self.base.file.?.pwriteAll(buffer.items, self.strtab_offset.?); | |
| 1366 | 1775 | } |
| 1367 | 1776 | |
| 1368 | 1777 | fn writeSectionHeaders(self: *Coff) !void { |
| ... | ... | @@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 1527 | 1936 | } |
| 1528 | 1937 | |
| 1529 | 1938 | fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1530 | const headers_size = self.getSizeOfHeaders(); | |
| 1939 | const headers_size = @maximum(self.getSizeOfHeaders(), self.page_size); | |
| 1531 | 1940 | if (start < headers_size) |
| 1532 | 1941 | return headers_size; |
| 1533 | 1942 | |
| 1534 | const end = start + size; | |
| 1943 | const end = start + padToIdeal(size); | |
| 1535 | 1944 | |
| 1536 | 1945 | if (self.strtab_offset) |off| { |
| 1537 | const increased_size = @intCast(u32, self.strtab.len()); | |
| 1946 | const tight_size = @intCast(u32, self.strtab.len()); | |
| 1947 | const increased_size = padToIdeal(tight_size); | |
| 1538 | 1948 | const test_end = off + increased_size; |
| 1539 | 1949 | if (end > off and start < test_end) { |
| 1540 | 1950 | return test_end; |
| ... | ... | @@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1542 | 1952 | } |
| 1543 | 1953 | |
| 1544 | 1954 | for (self.sections.items(.header)) |header| { |
| 1545 | const increased_size = header.size_of_raw_data; | |
| 1955 | const tight_size = header.size_of_raw_data; | |
| 1956 | const increased_size = padToIdeal(tight_size); | |
| 1546 | 1957 | const test_end = header.pointer_to_raw_data + increased_size; |
| 1547 | 1958 | if (end > header.pointer_to_raw_data and start < test_end) { |
| 1548 | 1959 | return test_end; |
| ... | ... | @@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1552 | 1963 | return null; |
| 1553 | 1964 | } |
| 1554 | 1965 | |
| 1555 | pub fn allocatedSize(self: *Coff, start: u32) u32 { | |
| 1966 | fn allocatedSize(self: *Coff, start: u32) u32 { | |
| 1556 | 1967 | if (start == 0) |
| 1557 | 1968 | return 0; |
| 1558 | 1969 | var min_pos: u32 = std.math.maxInt(u32); |
| ... | ... | @@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 { |
| 1566 | 1977 | return min_pos - start; |
| 1567 | 1978 | } |
| 1568 | 1979 | |
| 1569 | pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { | |
| 1980 | fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { | |
| 1570 | 1981 | var start: u32 = 0; |
| 1571 | 1982 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 1572 | 1983 | start = mem.alignForwardGeneric(u32, item_end, min_alignment); |
| ... | ... | @@ -1574,6 +1985,17 @@ pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { |
| 1574 | 1985 | return start; |
| 1575 | 1986 | } |
| 1576 | 1987 | |
| 1988 | fn allocatedVirtualSize(self: *Coff, start: u32) u32 { | |
| 1989 | if (start == 0) | |
| 1990 | return 0; | |
| 1991 | var min_pos: u32 = std.math.maxInt(u32); | |
| 1992 | for (self.sections.items(.header)) |header| { | |
| 1993 | if (header.virtual_address <= start) continue; | |
| 1994 | if (header.virtual_address < min_pos) min_pos = header.virtual_address; | |
| 1995 | } | |
| 1996 | return min_pos - start; | |
| 1997 | } | |
| 1998 | ||
| 1577 | 1999 | inline fn getSizeOfHeaders(self: Coff) u32 { |
| 1578 | 2000 | const msdos_hdr_size = msdos_stub.len + 4; |
| 1579 | 2001 | return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() + |
| ... | ... | @@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 { |
| 1614 | 2036 | |
| 1615 | 2037 | /// Returns symbol location corresponding to the set entrypoint (if any). |
| 1616 | 2038 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { |
| 1617 | const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete | |
| 1618 | return self.globals.get(entry_name); | |
| 2039 | const entry_name = self.base.options.entry orelse "wWinMainCRTStartup"; // TODO this is incomplete | |
| 2040 | const global_index = self.resolver.get(entry_name) orelse return null; | |
| 2041 | return self.globals.items[global_index]; | |
| 1619 | 2042 | } |
| 1620 | 2043 | |
| 1621 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. | |
| 2044 | /// Returns pointer-to-symbol described by `sym_loc` descriptor. | |
| 1622 | 2045 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { |
| 1623 | 2046 | assert(sym_loc.file == null); // TODO linking object files |
| 1624 | 2047 | return &self.locals.items[sym_loc.sym_index]; |
| 1625 | 2048 | } |
| 1626 | 2049 | |
| 1627 | /// Returns symbol described by `sym_with_loc` descriptor. | |
| 2050 | /// Returns symbol described by `sym_loc` descriptor. | |
| 1628 | 2051 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { |
| 1629 | 2052 | assert(sym_loc.file == null); // TODO linking object files |
| 1630 | 2053 | return &self.locals.items[sym_loc.sym_index]; |
| 1631 | 2054 | } |
| 1632 | 2055 | |
| 1633 | /// Returns name of the symbol described by `sym_with_loc` descriptor. | |
| 2056 | /// Returns name of the symbol described by `sym_loc` descriptor. | |
| 1634 | 2057 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1635 | 2058 | assert(sym_loc.file == null); // TODO linking object files |
| 1636 | 2059 | const sym = self.getSymbol(sym_loc); |
| ... | ... | @@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1638 | 2061 | return self.strtab.get(offset).?; |
| 1639 | 2062 | } |
| 1640 | 2063 | |
| 1641 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. | |
| 2064 | /// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor. | |
| 1642 | 2065 | /// Returns null on failure. |
| 1643 | 2066 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1644 | 2067 | assert(sym_loc.file == null); // TODO linking with object files |
| 1645 | 2068 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 1646 | 2069 | } |
| 1647 | 2070 | |
| 1648 | /// Returns GOT atom that references `sym_with_loc` if one exists. | |
| 2071 | /// Returns GOT atom that references `sym_loc` if one exists. | |
| 1649 | 2072 | /// Returns null otherwise. |
| 1650 | 2073 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1651 | const got_index = self.got_entries.get(sym_loc) orelse return null; | |
| 1652 | return self.atom_by_index_table.get(got_index); | |
| 2074 | const got_index = self.got_entries_table.get(sym_loc) orelse return null; | |
| 2075 | const got_entry = self.got_entries.items[got_index]; | |
| 2076 | return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); | |
| 2077 | } | |
| 2078 | ||
| 2079 | /// Returns import atom that references `sym_loc` if one exists. | |
| 2080 | /// Returns null otherwise. | |
| 2081 | pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | |
| 2082 | const imports_index = self.imports_table.get(sym_loc) orelse return null; | |
| 2083 | const imports_entry = self.imports.items[imports_index]; | |
| 2084 | return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null }); | |
| 1653 | 2085 | } |
| 1654 | 2086 | |
| 1655 | 2087 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
| ... | ... | @@ -1663,6 +2095,14 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v |
| 1663 | 2095 | mem.set(u8, header.name[name_offset.len..], 0); |
| 1664 | 2096 | } |
| 1665 | 2097 | |
| 2098 | fn getSectionName(self: *const Coff, header: *const coff.SectionHeader) []const u8 { | |
| 2099 | if (header.getName()) |name| { | |
| 2100 | return name; | |
| 2101 | } | |
| 2102 | const offset = header.getNameOffset().?; | |
| 2103 | return self.strtab.get(offset).?; | |
| 2104 | } | |
| 2105 | ||
| 1666 | 2106 | fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { |
| 1667 | 2107 | if (name.len <= 8) { |
| 1668 | 2108 | mem.copy(u8, &symbol.name, name); |
| ... | ... | @@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void { |
| 1725 | 2165 | } |
| 1726 | 2166 | |
| 1727 | 2167 | log.debug("globals table:", .{}); |
| 1728 | for (self.globals.keys()) |name, id| { | |
| 1729 | const value = self.globals.values()[id]; | |
| 1730 | log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file }); | |
| 2168 | for (self.globals.items) |sym_loc| { | |
| 2169 | const sym_name = self.getSymbolName(sym_loc); | |
| 2170 | log.debug(" {s} => %{d} in object({?d})", .{ sym_name, sym_loc.sym_index, sym_loc.file }); | |
| 1731 | 2171 | } |
| 1732 | 2172 | |
| 1733 | 2173 | log.debug("GOT entries:", .{}); |
| 1734 | for (self.got_entries.keys()) |target, i| { | |
| 1735 | const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null }); | |
| 1736 | const target_sym = self.getSymbol(target); | |
| 2174 | for (self.got_entries.items) |entry, i| { | |
| 2175 | const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); | |
| 2176 | const target_sym = self.getSymbol(entry.target); | |
| 1737 | 2177 | if (target_sym.section_number == .UNDEFINED) { |
| 1738 | 2178 | log.debug(" {d}@{x} => import('{s}')", .{ |
| 1739 | 2179 | i, |
| 1740 | 2180 | got_sym.value, |
| 1741 | self.getSymbolName(target), | |
| 2181 | self.getSymbolName(entry.target), | |
| 1742 | 2182 | }); |
| 1743 | 2183 | } else { |
| 1744 | 2184 | log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ |
| 1745 | 2185 | i, |
| 1746 | 2186 | got_sym.value, |
| 1747 | target.sym_index, | |
| 1748 | target.file, | |
| 2187 | entry.target.sym_index, | |
| 2188 | entry.target.file, | |
| 1749 | 2189 | logSymAttributes(target_sym, &buf), |
| 1750 | 2190 | }); |
| 1751 | 2191 | } |
| 1752 | 2192 | } |
| 1753 | 2193 | } |
| 2194 | ||
| 2195 | fn logSections(self: *Coff) void { | |
| 2196 | log.debug("sections:", .{}); | |
| 2197 | for (self.sections.items(.header)) |*header| { | |
| 2198 | log.debug(" {s}: VM({x}, {x}) FILE({x}, {x})", .{ | |
| 2199 | self.getSectionName(header), | |
| 2200 | header.virtual_address, | |
| 2201 | header.virtual_address + header.virtual_size, | |
| 2202 | header.pointer_to_raw_data, | |
| 2203 | header.pointer_to_raw_data + header.size_of_raw_data, | |
| 2204 | }); | |
| 2205 | } | |
| 2206 | } |
src/link/Coff/Atom.zig+10-7| ... | ... | @@ -4,8 +4,6 @@ const std = @import("std"); |
| 4 | 4 | const coff = std.coff; |
| 5 | 5 | const log = std.log.scoped(.link); |
| 6 | 6 | |
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | ||
| 9 | 7 | const Coff = @import("../Coff.zig"); |
| 10 | 8 | const Reloc = Coff.Reloc; |
| 11 | 9 | const SymbolWithLoc = Coff.SymbolWithLoc; |
| ... | ... | @@ -41,11 +39,6 @@ pub const empty = Atom{ |
| 41 | 39 | .next = null, |
| 42 | 40 | }; |
| 43 | 41 | |
| 44 | pub fn deinit(self: *Atom, gpa: Allocator) void { | |
| 45 | _ = self; | |
| 46 | _ = gpa; | |
| 47 | } | |
| 48 | ||
| 49 | 42 | /// Returns symbol referencing this atom. |
| 50 | 43 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { |
| 51 | 44 | return coff_file.getSymbol(.{ |
| ... | ... | @@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { |
| 118 | 111 | } |
| 119 | 112 | try gop.value_ptr.append(gpa, offset); |
| 120 | 113 | } |
| 114 | ||
| 115 | pub fn addBinding(self: *Atom, coff_file: *Coff, target: SymbolWithLoc) !void { | |
| 116 | const gpa = coff_file.base.allocator; | |
| 117 | log.debug(" (adding binding to target %{d} in %{d})", .{ target.sym_index, self.sym_index }); | |
| 118 | const gop = try coff_file.bindings.getOrPut(gpa, self); | |
| 119 | if (!gop.found_existing) { | |
| 120 | gop.value_ptr.* = .{}; | |
| 121 | } | |
| 122 | try gop.value_ptr.append(gpa, target); | |
| 123 | } |
src/link/Dwarf.zig+36-37| ... | ... | @@ -861,7 +861,8 @@ pub fn commitDeclState( |
| 861 | 861 | }, |
| 862 | 862 | .wasm => { |
| 863 | 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 | 867 | else => unreachable, |
| 867 | 868 | } |
| ... | ... | @@ -972,23 +973,21 @@ pub fn commitDeclState( |
| 972 | 973 | }, |
| 973 | 974 | .wasm => { |
| 974 | 975 | const wasm_file = file.cast(File.Wasm).?; |
| 975 | const segment_index = try wasm_file.getDebugLineIndex(); | |
| 976 | const segment = &wasm_file.segments.items[segment_index]; | |
| 977 | const debug_line = &wasm_file.debug_line; | |
| 978 | if (needed_size != segment.size) { | |
| 976 | const atom = wasm_file.debug_line_atom.?; | |
| 977 | const debug_line = &atom.code; | |
| 978 | const segment_size = debug_line.items.len; | |
| 979 | if (needed_size != segment_size) { | |
| 979 | 980 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); |
| 980 | if (needed_size > segment.size) { | |
| 981 | log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size}); | |
| 981 | if (needed_size > segment_size) { | |
| 982 | log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size}); | |
| 982 | 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 | 986 | debug_line.items.len = needed_size; |
| 987 | 987 | } |
| 988 | const offset = segment.offset + src_fn.off; | |
| 989 | 988 | writeDbgLineNopsBuffered( |
| 990 | 989 | debug_line.items, |
| 991 | offset, | |
| 990 | src_fn.off, | |
| 992 | 991 | prev_padding_size, |
| 993 | 992 | dbg_line_buffer.items, |
| 994 | 993 | next_padding_size, |
| ... | ... | @@ -1146,10 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 |
| 1146 | 1145 | }, |
| 1147 | 1146 | .wasm => { |
| 1148 | 1147 | const wasm_file = file.cast(File.Wasm).?; |
| 1149 | const segment_index = try wasm_file.getDebugInfoIndex(); | |
| 1150 | const segment = &wasm_file.segments.items[segment_index]; | |
| 1151 | const offset = segment.offset + atom.off; | |
| 1152 | try writeDbgInfoNopsToArrayList(gpa, &wasm_file.debug_info, offset, 0, &.{0}, atom.len, false); | |
| 1148 | const debug_info = &wasm_file.debug_info_atom.?.code; | |
| 1149 | try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); | |
| 1153 | 1150 | }, |
| 1154 | 1151 | else => unreachable, |
| 1155 | 1152 | } |
| ... | ... | @@ -1276,27 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co |
| 1276 | 1273 | }, |
| 1277 | 1274 | .wasm => { |
| 1278 | 1275 | const wasm_file = file.cast(File.Wasm).?; |
| 1279 | const segment_index = try wasm_file.getDebugInfoIndex(); | |
| 1280 | const segment = &wasm_file.segments.items[segment_index]; | |
| 1281 | const debug_info = &wasm_file.debug_info; | |
| 1282 | if (needed_size != segment.size) { | |
| 1276 | const info_atom = wasm_file.debug_info_atom.?; | |
| 1277 | const debug_info = &info_atom.code; | |
| 1278 | const segment_size = debug_info.items.len; | |
| 1279 | if (needed_size != segment_size) { | |
| 1283 | 1280 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); |
| 1284 | if (needed_size > segment.size) { | |
| 1285 | log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size}); | |
| 1281 | if (needed_size > segment_size) { | |
| 1282 | log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size}); | |
| 1286 | 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 | 1286 | debug_info.items.len = needed_size; |
| 1291 | 1287 | } |
| 1292 | const offset = segment.offset + atom.off; | |
| 1293 | 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 | 1291 | try writeDbgInfoNopsToArrayList( |
| 1297 | 1292 | gpa, |
| 1298 | 1293 | debug_info, |
| 1299 | offset, | |
| 1294 | atom.off, | |
| 1300 | 1295 | prev_padding_size, |
| 1301 | 1296 | dbg_info_buf, |
| 1302 | 1297 | next_padding_size, |
| ... | ... | @@ -1337,10 +1332,9 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) |
| 1337 | 1332 | }, |
| 1338 | 1333 | .wasm => { |
| 1339 | 1334 | const wasm_file = file.cast(File.Wasm).?; |
| 1340 | const segment_index = wasm_file.getDebugLineIndex() catch unreachable; | |
| 1341 | const segment = wasm_file.segments.items[segment_index]; | |
| 1342 | const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); | |
| 1343 | mem.copy(u8, wasm_file.debug_line.items[offset..], &data); | |
| 1335 | const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); | |
| 1336 | const atom = wasm_file.debug_line_atom.?; | |
| 1337 | mem.copy(u8, atom.code.items[offset..], &data); | |
| 1344 | 1338 | }, |
| 1345 | 1339 | else => unreachable, |
| 1346 | 1340 | } |
| ... | ... | @@ -1576,8 +1570,9 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { |
| 1576 | 1570 | }, |
| 1577 | 1571 | .wasm => { |
| 1578 | 1572 | const wasm_file = file.cast(File.Wasm).?; |
| 1579 | try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size); | |
| 1580 | mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf); | |
| 1573 | const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code; | |
| 1574 | try debug_abbrev.resize(wasm_file.base.allocator, needed_size); | |
| 1575 | mem.copy(u8, debug_abbrev.items, &abbrev_buf); | |
| 1581 | 1576 | }, |
| 1582 | 1577 | else => unreachable, |
| 1583 | 1578 | } |
| ... | ... | @@ -1687,7 +1682,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 |
| 1687 | 1682 | }, |
| 1688 | 1683 | .wasm => { |
| 1689 | 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 | 1688 | else => unreachable, |
| 1693 | 1689 | } |
| ... | ... | @@ -2016,8 +2012,9 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { |
| 2016 | 2012 | }, |
| 2017 | 2013 | .wasm => { |
| 2018 | 2014 | const wasm_file = file.cast(File.Wasm).?; |
| 2019 | try wasm_file.debug_aranges.resize(wasm_file.base.allocator, needed_size); | |
| 2020 | mem.copy(u8, wasm_file.debug_aranges.items, di_buf.items); | |
| 2015 | const debug_ranges = &wasm_file.debug_ranges_atom.?.code; | |
| 2016 | try debug_ranges.resize(wasm_file.base.allocator, needed_size); | |
| 2017 | mem.copy(u8, debug_ranges.items, di_buf.items); | |
| 2021 | 2018 | }, |
| 2022 | 2019 | else => unreachable, |
| 2023 | 2020 | } |
| ... | ... | @@ -2139,7 +2136,8 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { |
| 2139 | 2136 | }, |
| 2140 | 2137 | .wasm => { |
| 2141 | 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 | 2142 | else => unreachable, |
| 2145 | 2143 | } |
| ... | ... | @@ -2287,7 +2285,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { |
| 2287 | 2285 | }, |
| 2288 | 2286 | .wasm => { |
| 2289 | 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 | 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 | 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 | 1497 | if (self.base.options.eh_frame_hdr) { |
| 1486 | 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 | 793 | } |
| 794 | 794 | } else { |
| 795 | 795 | const sub_path = self.base.options.emit.?.sub_path; |
| 796 | self.base.file = try directory.handle.createFile(sub_path, .{ | |
| 797 | .truncate = true, | |
| 798 | .read = true, | |
| 799 | .mode = link.determineMode(self.base.options), | |
| 800 | }); | |
| 796 | if (self.base.file == null) { | |
| 797 | self.base.file = try directory.handle.createFile(sub_path, .{ | |
| 798 | .truncate = true, | |
| 799 | .read = true, | |
| 800 | .mode = link.determineMode(self.base.options), | |
| 801 | }); | |
| 802 | } | |
| 801 | 803 | // Index 0 is always a null symbol. |
| 802 | 804 | try self.locals.append(gpa, .{ |
| 803 | 805 | .n_strx = 0, |
| ... | ... | @@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) |
| 1155 | 1157 | var ncmds: u32 = 0; |
| 1156 | 1158 | |
| 1157 | 1159 | try self.writeLinkeditSegmentData(&ncmds, lc_writer); |
| 1160 | ||
| 1161 | // If the last section of __DATA segment is zerofill section, we need to ensure | |
| 1162 | // that the free space between the end of the last non-zerofill section of __DATA | |
| 1163 | // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will | |
| 1164 | // copy-paste this space into memory for quicker zerofill operation. | |
| 1165 | if (self.data_segment_cmd_index) |data_seg_id| blk: { | |
| 1166 | var physical_zerofill_start: u64 = 0; | |
| 1167 | const section_indexes = self.getSectionIndexes(data_seg_id); | |
| 1168 | for (self.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| { | |
| 1169 | if (header.isZerofill() and header.size > 0) break; | |
| 1170 | physical_zerofill_start = header.offset + header.size; | |
| 1171 | } else break :blk; | |
| 1172 | const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?]; | |
| 1173 | const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse | |
| 1174 | return error.Overflow; | |
| 1175 | if (physical_zerofill_size > 0) { | |
| 1176 | var padding = try self.base.allocator.alloc(u8, physical_zerofill_size); | |
| 1177 | defer self.base.allocator.free(padding); | |
| 1178 | mem.set(u8, padding, 0); | |
| 1179 | try self.base.file.?.pwriteAll(padding, physical_zerofill_start); | |
| 1180 | } | |
| 1181 | } | |
| 1182 | ||
| 1158 | 1183 | try writeDylinkerLC(&ncmds, lc_writer); |
| 1159 | 1184 | try self.writeMainLC(&ncmds, lc_writer); |
| 1160 | 1185 | try self.writeDylibIdLC(&ncmds, lc_writer); |
| ... | ... | @@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool { |
| 1435 | 1460 | |
| 1436 | 1461 | if (force_load) { |
| 1437 | 1462 | defer archive.deinit(gpa); |
| 1438 | defer file.close(); | |
| 1439 | 1463 | // Get all offsets from the ToC |
| 1440 | 1464 | var offsets = std.AutoArrayHashMap(u32, void).init(gpa); |
| 1441 | 1465 | defer offsets.deinit(); |
| ... | ... | @@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void { |
| 3086 | 3110 | self.atom_by_index_table.deinit(gpa); |
| 3087 | 3111 | } |
| 3088 | 3112 | |
| 3089 | pub fn closeFiles(self: MachO) void { | |
| 3090 | for (self.archives.items) |archive| { | |
| 3091 | archive.file.close(); | |
| 3092 | } | |
| 3093 | if (self.d_sym) |ds| { | |
| 3094 | ds.file.close(); | |
| 3095 | } | |
| 3096 | } | |
| 3097 | ||
| 3098 | 3113 | fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void { |
| 3099 | 3114 | log.debug("freeAtom {*}", .{atom}); |
| 3100 | 3115 | if (!owns_atom) { |
| ... | ... | @@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { |
| 5698 | 5713 | else => unreachable, |
| 5699 | 5714 | } |
| 5700 | 5715 | |
| 5701 | if (self.getSectionByName("__DATA", "__thread_vars")) |_| { | |
| 5702 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 5716 | if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| { | |
| 5717 | if (self.sections.items(.header)[sect_id].size > 0) { | |
| 5718 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 5719 | } | |
| 5703 | 5720 | } |
| 5704 | 5721 | |
| 5705 | 5722 | header.ncmds = ncmds; |
src/link/MachO/Archive.zig+1| ... | ... | @@ -88,6 +88,7 @@ const ar_hdr = extern struct { |
| 88 | 88 | }; |
| 89 | 89 | |
| 90 | 90 | pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 91 | self.file.close(); | |
| 91 | 92 | for (self.toc.keys()) |*key| { |
| 92 | 93 | allocator.free(key.*); |
| 93 | 94 | } |
src/link/MachO/DebugSymbols.zig+1| ... | ... | @@ -306,6 +306,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 306 | 306 | } |
| 307 | 307 | |
| 308 | 308 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { |
| 309 | self.file.close(); | |
| 309 | 310 | self.segments.deinit(allocator); |
| 310 | 311 | self.sections.deinit(allocator); |
| 311 | 312 | self.dwarf.deinit(); |
src/link/Wasm.zig+294-123| ... | ... | @@ -67,6 +67,18 @@ code_section_index: ?u32 = null, |
| 67 | 67 | debug_info_index: ?u32 = null, |
| 68 | 68 | /// The index of the segment representing the custom '.debug_line' section. |
| 69 | 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 | 82 | /// The count of imported functions. This number will be appended |
| 71 | 83 | /// to the function indexes as their index starts at the lowest non-extern function. |
| 72 | 84 | imported_functions_count: u32 = 0, |
| ... | ... | @@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, |
| 83 | 95 | segments: std.ArrayListUnmanaged(Segment) = .{}, |
| 84 | 96 | /// Maps a data segment key (such as .rodata) to the index into `segments`. |
| 85 | 97 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 86 | /// A list of `types.Segment` which provide meta data | |
| 87 | /// about a data symbol such as its name | |
| 88 | segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, | |
| 98 | /// A table of `types.Segment` which provide meta data | |
| 99 | /// about a data symbol such as its name where the key is | |
| 100 | /// the segment index, which can be found from `data_segments` | |
| 101 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, | |
| 89 | 102 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 90 | 103 | string_table: StringTable = .{}, |
| 91 | 104 | /// Debug information for wasm |
| 92 | 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 | 107 | // Output sections |
| 105 | 108 | /// Output type section |
| 106 | 109 | func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, |
| ... | ... | @@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 156 | 159 | /// The actual table is populated during `flush`. |
| 157 | 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 | 175 | pub const Segment = struct { |
| 160 | 176 | alignment: u32, |
| 161 | 177 | size: u32, |
| ... | ... | @@ -209,6 +225,18 @@ pub const SymbolLoc = struct { |
| 209 | 225 | } |
| 210 | 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 | 242 | /// Generic string table that duplicates strings |
| ... | ... | @@ -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 | 367 | return wasm_bin; |
| 339 | 368 | } |
| 340 | 369 | |
| ... | ... | @@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 363 | 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 | 413 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { |
| 367 | 414 | for (files) |path| { |
| 368 | 415 | if (try self.parseObjectFile(path)) continue; |
| ... | ... | @@ -644,16 +691,14 @@ pub fn deinit(self: *Wasm) void { |
| 644 | 691 | for (self.func_types.items) |*func_type| { |
| 645 | 692 | func_type.deinit(gpa); |
| 646 | 693 | } |
| 647 | for (self.segment_info.items) |segment_info| { | |
| 694 | for (self.segment_info.values()) |segment_info| { | |
| 648 | 695 | gpa.free(segment_info.name); |
| 649 | 696 | } |
| 650 | 697 | for (self.objects.items) |*object| { |
| 651 | object.file.?.close(); | |
| 652 | 698 | object.deinit(gpa); |
| 653 | 699 | } |
| 654 | 700 | |
| 655 | 701 | for (self.archives.items) |*archive| { |
| 656 | archive.file.close(); | |
| 657 | 702 | archive.deinit(gpa); |
| 658 | 703 | } |
| 659 | 704 | |
| ... | ... | @@ -692,11 +737,6 @@ pub fn deinit(self: *Wasm) void { |
| 692 | 737 | if (self.dwarf) |*dwarf| { |
| 693 | 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 | 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 | 1377 | const index = gop.value_ptr.*; |
| 1338 | 1378 | self.segments.items[index].size += atom.size; |
| 1339 | 1379 | |
| 1340 | // segment indexes can be off by 1 due to also containing a segment | |
| 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; | |
| 1380 | symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); | |
| 1350 | 1381 | // segment info already exists, so free its memory |
| 1351 | 1382 | self.base.allocator.free(segment_name); |
| 1352 | 1383 | break :result index; |
| ... | ... | @@ -1359,8 +1390,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1359 | 1390 | }); |
| 1360 | 1391 | gop.value_ptr.* = index; |
| 1361 | 1392 | |
| 1362 | const info_index = @intCast(u32, self.segment_info.items.len); | |
| 1363 | try self.segment_info.append(self.base.allocator, segment_info); | |
| 1393 | const info_index = @intCast(u32, self.segment_info.count()); | |
| 1394 | try self.segment_info.put(self.base.allocator, index, segment_info); | |
| 1364 | 1395 | symbol.index = info_index; |
| 1365 | 1396 | break :result index; |
| 1366 | 1397 | } |
| ... | ... | @@ -1370,18 +1401,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1370 | 1401 | const segment: *Segment = &self.segments.items[final_index]; |
| 1371 | 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 | 1411 | last.*.next = atom; |
| 1375 | 1412 | atom.prev = last.*; |
| 1376 | 1413 | last.* = atom; |
| 1377 | 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 | 1448 | fn allocateAtoms(self: *Wasm) !void { |
| 1383 | 1449 | // first sort the data segments |
| 1384 | 1450 | try sortDataSegments(self); |
| 1451 | try allocateDebugAtoms(self); | |
| 1385 | 1452 | |
| 1386 | 1453 | var it = self.atoms.iterator(); |
| 1387 | 1454 | while (it.next()) |entry| { |
| ... | ... | @@ -1399,7 +1466,7 @@ fn allocateAtoms(self: *Wasm) !void { |
| 1399 | 1466 | atom.size, |
| 1400 | 1467 | }); |
| 1401 | 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 | 1470 | atom = atom.next orelse break; |
| 1404 | 1471 | } |
| 1405 | 1472 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| ... | ... | @@ -1753,7 +1820,7 @@ fn setupMemory(self: *Wasm) !void { |
| 1753 | 1820 | /// From a given object's index and the index of the segment, returns the corresponding |
| 1754 | 1821 | /// index of the segment within the final data section. When the segment does not yet |
| 1755 | 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 | 1824 | const object: Object = self.objects.items[object_index]; |
| 1758 | 1825 | const relocatable_data = object.relocatable_data[relocatable_index]; |
| 1759 | 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 | 1832 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); |
| 1766 | 1833 | if (!result.found_existing) { |
| 1767 | 1834 | result.value_ptr.* = index; |
| 1768 | try self.segments.append(self.base.allocator, .{ | |
| 1769 | .alignment = 1, | |
| 1770 | .size = 0, | |
| 1771 | .offset = 0, | |
| 1772 | }); | |
| 1835 | try self.appendDummySegment(); | |
| 1773 | 1836 | return index; |
| 1774 | 1837 | } else return result.value_ptr.*; |
| 1775 | 1838 | }, |
| 1776 | 1839 | .code => return self.code_section_index orelse blk: { |
| 1777 | 1840 | self.code_section_index = index; |
| 1778 | try self.segments.append(self.base.allocator, .{ | |
| 1779 | .alignment = 1, | |
| 1780 | .size = 0, | |
| 1781 | .offset = 0, | |
| 1782 | }); | |
| 1841 | try self.appendDummySegment(); | |
| 1783 | 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 | 1912 | /// Returns the symbol index of the error name table. |
| 1790 | 1913 | /// |
| 1791 | 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 | 2026 | try self.parseAtom(names_atom, .{ .data = .read_only }); |
| 1904 | 2027 | } |
| 1905 | 2028 | |
| 1906 | pub fn getDebugInfoIndex(self: *Wasm) !u32 { | |
| 1907 | assert(self.dwarf != null); | |
| 1908 | return self.debug_info_index orelse { | |
| 1909 | self.debug_info_index = @intCast(u32, self.segments.items.len); | |
| 1910 | const segment = try self.segments.addOne(self.base.allocator); | |
| 1911 | segment.* = .{ | |
| 1912 | .size = 0, | |
| 1913 | .offset = 0, | |
| 1914 | // debug sections always have alignment '1' | |
| 1915 | .alignment = 1, | |
| 1916 | }; | |
| 1917 | return self.debug_info_index.?; | |
| 2029 | /// From a given index variable, creates a new debug section. | |
| 2030 | /// This initializes the index, appends a new segment, | |
| 2031 | /// and finally, creates a managed `Atom`. | |
| 2032 | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { | |
| 2033 | const new_index = @intCast(u32, self.segments.items.len); | |
| 2034 | index.* = new_index; | |
| 2035 | try self.appendDummySegment(); | |
| 2036 | // _ = index; | |
| 2037 | ||
| 2038 | const sym_index = self.symbols_free_list.popOrNull() orelse idx: { | |
| 2039 | const tmp_index = @intCast(u32, self.symbols.items.len); | |
| 2040 | _ = try self.symbols.addOne(self.base.allocator); | |
| 2041 | break :idx tmp_index; | |
| 1918 | 2042 | }; |
| 1919 | } | |
| 1920 | ||
| 1921 | pub fn getDebugLineIndex(self: *Wasm) !u32 { | |
| 1922 | assert(self.dwarf != null); | |
| 1923 | return self.debug_line_index orelse { | |
| 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.?; | |
| 2043 | self.symbols.items[sym_index] = .{ | |
| 2044 | .tag = .section, | |
| 2045 | .name = try self.string_table.put(self.base.allocator, name), | |
| 2046 | .index = 0, | |
| 2047 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | |
| 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 | 2059 | fn resetState(self: *Wasm) void { |
| 1936 | for (self.segment_info.items) |*segment_info| { | |
| 2060 | for (self.segment_info.values()) |segment_info| { | |
| 1937 | 2061 | self.base.allocator.free(segment_info.name); |
| 1938 | 2062 | } |
| 1939 | const mod = self.base.options.module.?; | |
| 1940 | var decl_it = self.decls.keyIterator(); | |
| 1941 | while (decl_it.next()) |decl_index_ptr| { | |
| 1942 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 1943 | const atom = &decl.link.wasm; | |
| 1944 | atom.next = null; | |
| 1945 | atom.prev = null; | |
| 2063 | if (self.base.options.module) |mod| { | |
| 2064 | var decl_it = self.decls.keyIterator(); | |
| 2065 | while (decl_it.next()) |decl_index_ptr| { | |
| 2066 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 2067 | const atom = &decl.link.wasm; | |
| 2068 | atom.next = null; | |
| 2069 | atom.prev = null; | |
| 1946 | 2070 | |
| 1947 | for (atom.locals.items) |*local_atom| { | |
| 1948 | local_atom.next = null; | |
| 1949 | local_atom.prev = null; | |
| 2071 | for (atom.locals.items) |*local_atom| { | |
| 2072 | local_atom.next = null; | |
| 2073 | local_atom.prev = null; | |
| 2074 | } | |
| 1950 | 2075 | } |
| 1951 | 2076 | } |
| 1952 | 2077 | self.functions.clearRetainingCapacity(); |
| ... | ... | @@ -1959,6 +2084,12 @@ fn resetState(self: *Wasm) void { |
| 1959 | 2084 | self.code_section_index = null; |
| 1960 | 2085 | self.debug_info_index = null; |
| 1961 | 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 | 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 | 2167 | defer self.resetState(); |
| 2037 | 2168 | try self.setupStart(); |
| 2038 | 2169 | try self.setupImports(); |
| 2039 | const mod = self.base.options.module.?; | |
| 2040 | var decl_it = self.decls.keyIterator(); | |
| 2041 | while (decl_it.next()) |decl_index_ptr| { | |
| 2042 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 2043 | if (decl.isExtern()) continue; | |
| 2044 | const atom = &decl.*.link.wasm; | |
| 2045 | if (decl.ty.zigTypeTag() == .Fn) { | |
| 2046 | try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); | |
| 2047 | } else if (decl.getVariable()) |variable| { | |
| 2048 | if (!variable.is_mutable) { | |
| 2049 | try self.parseAtom(atom, .{ .data = .read_only }); | |
| 2050 | } else if (variable.init.isUndefDeep()) { | |
| 2051 | try self.parseAtom(atom, .{ .data = .uninitialized }); | |
| 2170 | if (self.base.options.module) |mod| { | |
| 2171 | var decl_it = self.decls.keyIterator(); | |
| 2172 | while (decl_it.next()) |decl_index_ptr| { | |
| 2173 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 2174 | if (decl.isExtern()) continue; | |
| 2175 | const atom = &decl.*.link.wasm; | |
| 2176 | if (decl.ty.zigTypeTag() == .Fn) { | |
| 2177 | try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); | |
| 2178 | } else if (decl.getVariable()) |variable| { | |
| 2179 | if (!variable.is_mutable) { | |
| 2180 | try self.parseAtom(atom, .{ .data = .read_only }); | |
| 2181 | } else if (variable.init.isUndefDeep()) { | |
| 2182 | try self.parseAtom(atom, .{ .data = .uninitialized }); | |
| 2183 | } else { | |
| 2184 | try self.parseAtom(atom, .{ .data = .initialized }); | |
| 2185 | } | |
| 2052 | 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 | |
| 2060 | for (atom.locals.items) |*local_atom| { | |
| 2061 | try self.parseAtom(local_atom, .{ .data = .read_only }); | |
| 2196 | if (self.dwarf) |*dwarf| { | |
| 2197 | try dwarf.flushModule(&self.base, self.base.options.module.?); | |
| 2062 | 2198 | } |
| 2063 | 2199 | } |
| 2064 | 2200 | |
| ... | ... | @@ -2066,9 +2202,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2066 | 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 | 2205 | try self.allocateAtoms(); |
| 2073 | 2206 | try self.setupMemory(); |
| 2074 | 2207 | self.mapFunctionTable(); |
| ... | ... | @@ -2424,19 +2557,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2424 | 2557 | } |
| 2425 | 2558 | } else if (!self.base.options.strip) { |
| 2426 | 2559 | if (self.dwarf) |*dwarf| { |
| 2427 | if (self.debug_info_index != null) { | |
| 2428 | try dwarf.writeDbgAbbrev(&self.base); | |
| 2429 | // for debug info and ranges, the address is always 0, | |
| 2430 | // as locations are always offsets relative to 'code' section. | |
| 2431 | try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); | |
| 2432 | try dwarf.writeDbgAranges(&self.base, 0, code_section_size); | |
| 2433 | try dwarf.writeDbgLineHeader(&self.base, mod); | |
| 2434 | ||
| 2435 | try emitDebugSection(file, self.debug_info.items, ".debug_info"); | |
| 2436 | try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges"); | |
| 2437 | try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); | |
| 2438 | try emitDebugSection(file, self.debug_line.items, ".debug_line"); | |
| 2439 | try emitDebugSection(file, dwarf.strtab.items, ".debug_str"); | |
| 2560 | const mod = self.base.options.module.?; | |
| 2561 | try dwarf.writeDbgAbbrev(&self.base); | |
| 2562 | // for debug info and ranges, the address is always 0, | |
| 2563 | // as locations are always offsets relative to 'code' section. | |
| 2564 | try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); | |
| 2565 | try dwarf.writeDbgAranges(&self.base, 0, code_section_size); | |
| 2566 | try dwarf.writeDbgLineHeader(&self.base, mod); | |
| 2567 | } | |
| 2568 | ||
| 2569 | var debug_bytes = std.ArrayList(u8).init(self.base.allocator); | |
| 2570 | defer debug_bytes.deinit(); | |
| 2571 | ||
| 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 | 2600 | try self.emitNameSection(file, arena); |
| ... | ... | @@ -2444,6 +2602,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2444 | 2602 | } |
| 2445 | 2603 | |
| 2446 | 2604 | fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { |
| 2605 | if (data.len == 0) return; | |
| 2447 | 2606 | const header_offset = try reserveCustomSectionHeader(file); |
| 2448 | 2607 | const writer = file.writer(); |
| 2449 | 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 | 3216 | buf[0] = @enumToInt(section); |
| 3058 | 3217 | leb.writeUnsignedFixed(5, buf[1..6], size); |
| 3059 | 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 | 3228 | fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void { |
| 3064 | 3229 | var buf: [1 + 5]u8 = undefined; |
| 3065 | 3230 | buf[0] = 0; // 0 = 'custom' section |
| 3066 | 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 | 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 | 3320 | var payload = std.ArrayList(u8).init(arena); |
| 3150 | 3321 | const writer = payload.writer(); |
| 3151 | 3322 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); |
| 3152 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len)); | |
| 3153 | for (self.segment_info.items) |segment_info| { | |
| 3323 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); | |
| 3324 | for (self.segment_info.values()) |segment_info| { | |
| 3154 | 3325 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| 3155 | 3326 | segment_info.name, |
| 3156 | 3327 | @ctz(segment_info.alignment), |
src/link/Wasm/Archive.zig+1| ... | ... | @@ -95,6 +95,7 @@ const ar_hdr = extern struct { |
| 95 | 95 | }; |
| 96 | 96 | |
| 97 | 97 | pub fn deinit(archive: *Archive, allocator: Allocator) void { |
| 98 | archive.file.close(); | |
| 98 | 99 | for (archive.toc.keys()) |*key| { |
| 99 | 100 | allocator.free(key.*); |
| 100 | 101 | } |
src/link/Wasm/Atom.zig+35-7| ... | ... | @@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom { |
| 90 | 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 | 106 | /// Returns the location of the symbol that represents this `Atom` |
| 94 | 107 | pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { |
| 95 | 108 | return .{ .file = self.file, .index = self.sym_index }; |
| ... | ... | @@ -145,7 +158,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { |
| 145 | 158 | /// All values will be represented as a `u64` as all values can fit within it. |
| 146 | 159 | /// The final value must be casted to the correct size. |
| 147 | 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 | 162 | const symbol = target_loc.getSymbol(wasm_bin).*; |
| 150 | 163 | switch (relocation.relocation_type) { |
| 151 | 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 | 187 | => { |
| 175 | 188 | std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); |
| 176 | 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; | |
| 178 | const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?; | |
| 190 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; | |
| 179 | 191 | const segment_info = if (target_atom.file) |object_index| blk: { |
| 180 | 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 | 194 | const segment_name = segment_info[symbol.index].outputName(merge_segment); |
| 183 | 195 | const segment_index = wasm_bin.data_segments.get(segment_name).?; |
| 184 | 196 | const segment = wasm_bin.segments.items[segment_index]; |
| 185 | 197 | return target_atom.offset + segment.offset + (relocation.addend orelse 0); |
| 186 | 198 | }, |
| 187 | 199 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, |
| 188 | .R_WASM_SECTION_OFFSET_I32, | |
| 189 | .R_WASM_FUNCTION_OFFSET_I32, | |
| 190 | => return relocation.offset, | |
| 200 | .R_WASM_SECTION_OFFSET_I32 => { | |
| 201 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; | |
| 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 | 63 | /// import name, module name and export names. Each string will be deduplicated |
| 64 | 64 | /// and returns an offset into the table. |
| 65 | 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 | 71 | /// Represents a single item within a section (depending on its `type`) |
| 68 | 72 | const RelocatableData = struct { |
| 69 | 73 | /// The type of the relocatable data |
| 70 | type: enum { data, code, custom }, | |
| 74 | type: enum { data, code, debug }, | |
| 71 | 75 | /// Pointer to the data of the segment, where its length is written to `size` |
| 72 | 76 | data: [*]u8, |
| 73 | 77 | /// The size in bytes of the data representing the segment within the section |
| 74 | 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 | 81 | index: u32, |
| 77 | 82 | /// The offset within the section where the data starts |
| 78 | 83 | offset: u32, |
| ... | ... | @@ -96,9 +101,16 @@ const RelocatableData = struct { |
| 96 | 101 | return switch (self.type) { |
| 97 | 102 | .data => .data, |
| 98 | 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 | 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 | 123 | var object: Object = .{ |
| 112 | 124 | .file = file, |
| 113 | 125 | .name = try gpa.dupe(u8, name), |
| 126 | .debug_names = &.{}, | |
| 114 | 127 | }; |
| 115 | 128 | |
| 116 | 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 | 154 | /// Frees all memory of `Object` at once. The given `Allocator` must be |
| 142 | 155 | /// the same allocator that was used when `init` was called. |
| 143 | 156 | pub fn deinit(self: *Object, gpa: Allocator) void { |
| 157 | if (self.file) |file| { | |
| 158 | file.close(); | |
| 159 | } | |
| 144 | 160 | for (self.func_types) |func_ty| { |
| 145 | 161 | gpa.free(func_ty.params); |
| 146 | 162 | gpa.free(func_ty.returns); |
| ... | ... | @@ -197,6 +213,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32 |
| 197 | 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 | 221 | /// Checks if the object file is an MVP version. |
| 201 | 222 | /// When that's the case, we check if there's an import table definiton with its name |
| 202 | 223 | /// set to '__indirect_function_table". When that's also the case, |
| ... | ... | @@ -328,10 +349,15 @@ fn Parser(comptime ReaderType: type) type { |
| 328 | 349 | |
| 329 | 350 | self.object.version = version; |
| 330 | 351 | var relocatable_data = std.ArrayList(RelocatableData).init(gpa); |
| 331 | ||
| 332 | errdefer while (relocatable_data.popOrNull()) |rel_data| { | |
| 333 | gpa.free(rel_data.data[0..rel_data.size]); | |
| 334 | } else relocatable_data.deinit(); | |
| 352 | var debug_names = std.ArrayList(u8).init(gpa); | |
| 353 | ||
| 354 | errdefer { | |
| 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 | 362 | var section_index: u32 = 0; |
| 337 | 363 | while (self.reader.reader().readByte()) |byte| : (section_index += 1) { |
| ... | ... | @@ -347,11 +373,26 @@ fn Parser(comptime ReaderType: type) type { |
| 347 | 373 | |
| 348 | 374 | if (std.mem.eql(u8, name, "linking")) { |
| 349 | 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 | 377 | try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left)); |
| 351 | 378 | } else if (std.mem.startsWith(u8, name, "reloc")) { |
| 352 | 379 | try self.parseRelocations(gpa); |
| 353 | 380 | } else if (std.mem.eql(u8, name, "target_features")) { |
| 354 | 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 | 396 | } else { |
| 356 | 397 | try reader.skipBytes(reader.context.bytes_left, .{}); |
| 357 | 398 | } |
| ... | ... | @@ -737,7 +778,12 @@ fn Parser(comptime ReaderType: type) type { |
| 737 | 778 | }, |
| 738 | 779 | .section => { |
| 739 | 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 | 788 | else => { |
| 743 | 789 | symbol.index = try leb.readULEB128(u32, reader); |
| ... | ... | @@ -827,7 +873,6 @@ fn assertEnd(reader: anytype) !void { |
| 827 | 873 | |
| 828 | 874 | /// Parses an object file into atoms, for code and data sections |
| 829 | 875 | pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void { |
| 830 | log.debug("Parsing data section into atoms", .{}); | |
| 831 | 876 | const Key = struct { |
| 832 | 877 | kind: Symbol.Tag, |
| 833 | 878 | index: u32, |
| ... | ... | @@ -839,7 +884,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 839 | 884 | |
| 840 | 885 | for (self.symtable) |symbol, symbol_index| { |
| 841 | 886 | switch (symbol.tag) { |
| 842 | .function, .data => if (!symbol.isUndefined()) { | |
| 887 | .function, .data, .section => if (!symbol.isUndefined()) { | |
| 843 | 888 | const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); |
| 844 | 889 | const sym_idx = @intCast(u32, symbol_index); |
| 845 | 890 | if (!gop.found_existing) { |
| ... | ... | @@ -852,12 +897,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 852 | 897 | } |
| 853 | 898 | |
| 854 | 899 | for (self.relocatable_data) |relocatable_data, index| { |
| 855 | const symbols = symbol_for_segment.getPtr(.{ | |
| 856 | .kind = relocatable_data.getSymbolKind(), | |
| 857 | .index = @intCast(u32, relocatable_data.index), | |
| 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)); | |
| 900 | const final_index = (try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index))) orelse { | |
| 901 | continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. | |
| 902 | }; | |
| 861 | 903 | |
| 862 | 904 | const atom = try gpa.create(Atom); |
| 863 | 905 | atom.* = Atom.empty; |
| ... | ... | @@ -870,7 +912,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 870 | 912 | atom.file = object_index; |
| 871 | 913 | atom.size = relocatable_data.size; |
| 872 | 914 | atom.alignment = relocatable_data.getAlignment(self); |
| 873 | atom.sym_index = sym_index; | |
| 874 | 915 | |
| 875 | 916 | const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{}; |
| 876 | 917 | for (relocations) |relocation| { |
| ... | ... | @@ -892,28 +933,31 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin |
| 892 | 933 | |
| 893 | 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 | |
| 896 | // or as 'parent' when they are global. | |
| 897 | while (symbols.popOrNull()) |idx| { | |
| 898 | const alias_symbol = self.symtable[idx]; | |
| 899 | const symbol = self.symtable[atom.sym_index]; | |
| 900 | if (alias_symbol.isGlobal() and symbol.isLocal()) { | |
| 901 | atom.sym_index = idx; | |
| 936 | if (symbol_for_segment.getPtr(.{ | |
| 937 | .kind = relocatable_data.getSymbolKind(), | |
| 938 | .index = relocatable_data.getIndex(), | |
| 939 | })) |symbols| { | |
| 940 | atom.sym_index = symbols.pop(); | |
| 941 | ||
| 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 | 954 | const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; |
| 907 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | |
| 908 | ||
| 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); | |
| 955 | if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned | |
| 956 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | |
| 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 | 110 | return self.get(off) orelse unreachable; |
| 111 | 111 | } |
| 112 | 112 | |
| 113 | pub fn items(self: Self) []const u8 { | |
| 114 | return self.buffer.items; | |
| 115 | } | |
| 116 | ||
| 113 | 117 | pub fn len(self: Self) usize { |
| 114 | 118 | return self.buffer.items.len; |
| 115 | 119 | } |
src/main.zig+20-9| ... | ... | @@ -268,7 +268,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 268 | 268 | } else if (mem.eql(u8, cmd, "init-lib")) { |
| 269 | 269 | return cmdInit(gpa, arena, cmd_args, .Lib); |
| 270 | 270 | } else if (mem.eql(u8, cmd, "targets")) { |
| 271 | const info = try detectNativeTargetInfo(arena, .{}); | |
| 271 | const info = try detectNativeTargetInfo(.{}); | |
| 272 | 272 | const stdout = io.getStdOut().writer(); |
| 273 | 273 | return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); |
| 274 | 274 | } else if (mem.eql(u8, cmd, "version")) { |
| ... | ... | @@ -691,6 +691,9 @@ fn buildOutputType( |
| 691 | 691 | var linker_max_memory: ?u64 = null; |
| 692 | 692 | var linker_shared_memory: bool = false; |
| 693 | 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 | 697 | var linker_z_nodelete = false; |
| 695 | 698 | var linker_z_notext = false; |
| 696 | 699 | var linker_z_defs = false; |
| ... | ... | @@ -1816,6 +1819,12 @@ fn buildOutputType( |
| 1816 | 1819 | linker_gc_sections = true; |
| 1817 | 1820 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { |
| 1818 | 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 | 1828 | } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or |
| 1820 | 1829 | mem.eql(u8, arg, "-allow-shlib-undefined")) |
| 1821 | 1830 | { |
| ... | ... | @@ -2258,7 +2267,7 @@ fn buildOutputType( |
| 2258 | 2267 | } |
| 2259 | 2268 | |
| 2260 | 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 | 2272 | if (target_info.target.os.tag != .freestanding) { |
| 2264 | 2273 | if (ensure_libc_on_non_freestanding) |
| ... | ... | @@ -2911,6 +2920,9 @@ fn buildOutputType( |
| 2911 | 2920 | .linker_initial_memory = linker_initial_memory, |
| 2912 | 2921 | .linker_max_memory = linker_max_memory, |
| 2913 | 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 | 2926 | .linker_global_base = linker_global_base, |
| 2915 | 2927 | .linker_export_symbol_names = linker_export_symbol_names.items, |
| 2916 | 2928 | .linker_z_nodelete = linker_z_nodelete, |
| ... | ... | @@ -3271,7 +3283,7 @@ fn runOrTest( |
| 3271 | 3283 | if (std.process.can_execv and arg_mode == .run and !watch) { |
| 3272 | 3284 | // execv releases the locks; no need to destroy the Compilation here. |
| 3273 | 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 | 3287 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 3276 | 3288 | fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); |
| 3277 | 3289 | } else if (std.process.can_spawn) { |
| ... | ... | @@ -3288,7 +3300,7 @@ fn runOrTest( |
| 3288 | 3300 | } |
| 3289 | 3301 | |
| 3290 | 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 | 3304 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 3293 | 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 | 3914 | gimmeMoreOfThoseSweetSweetFileDescriptors(); |
| 3903 | 3915 | |
| 3904 | 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 | 3919 | const exe_basename = try std.zig.binNameAlloc(arena, .{ |
| 3908 | 3920 | .root_name = "build", |
| ... | ... | @@ -4944,8 +4956,8 @@ test "fds" { |
| 4944 | 4956 | gimmeMoreOfThoseSweetSweetFileDescriptors(); |
| 4945 | 4957 | } |
| 4946 | 4958 | |
| 4947 | fn detectNativeTargetInfo(gpa: Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { | |
| 4948 | return std.zig.system.NativeTargetInfo.detect(gpa, cross_target); | |
| 4959 | fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { | |
| 4960 | return std.zig.system.NativeTargetInfo.detect(cross_target); | |
| 4949 | 4961 | } |
| 4950 | 4962 | |
| 4951 | 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 | 5320 | } |
| 5309 | 5321 | |
| 5310 | 5322 | fn warnAboutForeignBinaries( |
| 5311 | gpa: Allocator, | |
| 5312 | 5323 | arena: Allocator, |
| 5313 | 5324 | arg_mode: ArgMode, |
| 5314 | 5325 | target_info: std.zig.system.NativeTargetInfo, |
| 5315 | 5326 | link_libc: bool, |
| 5316 | 5327 | ) !void { |
| 5317 | 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 | 5331 | switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) { |
| 5321 | 5332 | .native => return, |
src/test.zig+10-2| ... | ... | @@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct { |
| 177 | 177 | inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { |
| 178 | 178 | defaults = defaults ++ arch ++ "-macos" ++ ","; |
| 179 | 179 | } |
| 180 | // Windows | |
| 181 | defaults = defaults ++ "x86_64-windows" ++ ","; | |
| 180 | 182 | // Wasm |
| 181 | 183 | defaults = defaults ++ "wasm32-wasi"; |
| 182 | 184 | return defaults; |
| ... | ... | @@ -1211,7 +1213,7 @@ pub const TestContext = struct { |
| 1211 | 1213 | } |
| 1212 | 1214 | |
| 1213 | 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 | 1218 | var progress = std.Progress{}; |
| 1217 | 1219 | const root_node = progress.start("compiler", self.cases.items.len); |
| ... | ... | @@ -1300,7 +1302,7 @@ pub const TestContext = struct { |
| 1300 | 1302 | global_cache_directory: Compilation.Directory, |
| 1301 | 1303 | host: std.zig.system.NativeTargetInfo, |
| 1302 | 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 | 1306 | const target = target_info.target; |
| 1305 | 1307 | |
| 1306 | 1308 | var arena_allocator = std.heap.ArenaAllocator.init(allocator); |
| ... | ... | @@ -1546,6 +1548,12 @@ pub const TestContext = struct { |
| 1546 | 1548 | .self_exe_path = std.testing.zig_exe_path, |
| 1547 | 1549 | // TODO instead of turning off color, pass in a std.Progress.Node |
| 1548 | 1550 | .color = .off, |
| 1551 | // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in | |
| 1552 | // until the auto-select mechanism deems them worthy | |
| 1553 | .use_lld = switch (case.backend) { | |
| 1554 | .stage2 => false, | |
| 1555 | else => null, | |
| 1556 | }, | |
| 1549 | 1557 | }); |
| 1550 | 1558 | defer comp.destroy(); |
| 1551 | 1559 |
src/translate_c.zig+9-2| ... | ... | @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1167 | 1167 | } |
| 1168 | 1168 | |
| 1169 | 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 | 1173 | const record_payload = try c.arena.create(ast.Payload.Record); |
| ... | ... | @@ -5799,7 +5799,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5799 | 5799 | } |
| 5800 | 5800 | } |
| 5801 | 5801 | for (source) |c| { |
| 5802 | if (c == '\\') { | |
| 5802 | if (c == '\\' or c == '\t') { | |
| 5803 | 5803 | break; |
| 5804 | 5804 | } |
| 5805 | 5805 | } else return source; |
| ... | ... | @@ -5876,6 +5876,13 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5876 | 5876 | state = .Start; |
| 5877 | 5877 | }, |
| 5878 | 5878 | .Start => { |
| 5879 | if (c == '\t') { | |
| 5880 | bytes[i] = '\\'; | |
| 5881 | i += 1; | |
| 5882 | bytes[i] = 't'; | |
| 5883 | i += 1; | |
| 5884 | continue; | |
| 5885 | } | |
| 5879 | 5886 | if (c == '\\') { |
| 5880 | 5887 | state = .Escape; |
| 5881 | 5888 | } |
test/behavior.zig+1| ... | ... | @@ -86,6 +86,7 @@ test { |
| 86 | 86 | _ = @import("behavior/bugs/12430.zig"); |
| 87 | 87 | _ = @import("behavior/bugs/12486.zig"); |
| 88 | 88 | _ = @import("behavior/bugs/12680.zig"); |
| 89 | _ = @import("behavior/bugs/12776.zig"); | |
| 89 | 90 | _ = @import("behavior/byteswap.zig"); |
| 90 | 91 | _ = @import("behavior/byval_arg_var.zig"); |
| 91 | 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 | 486 | try S.doTheTest(); |
| 487 | 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 | 50 | #define CAST_TO_UINTPTR(X) (uintptr_t)(X) |
| 51 | 51 | |
| 52 | 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 | 2 | const std = @import("std"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const expectEqualStrings = std.testing.expectEqualStrings; | |
| 5 | 6 | |
| 6 | 7 | const h = @cImport(@cInclude("behavior/translate_c_macros.h")); |
| 7 | 8 | |
| ... | ... | @@ -123,3 +124,13 @@ test "large integer macro" { |
| 123 | 124 | |
| 124 | 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 | 2 | // output_mode=Exe |
| 3 | 3 | // target=aarch64-macos |
| 4 | 4 | // |
| 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 5 | // :109:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 6 | 6 | // :7:1: note: struct declared here |
test/cases/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 | 2 | // output_mode=Exe |
| 3 | 3 | // target=x86_64-linux |
| 4 | 4 | // |
| 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 5 | // :109:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 6 | 6 | // :7:1: note: struct declared here |
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1| ... | ... | @@ -2,5 +2,5 @@ |
| 2 | 2 | // output_mode=Exe |
| 3 | 3 | // target=x86_64-macos |
| 4 | 4 | // |
| 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 5 | // :109:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 6 | 6 | // :7:1: note: struct declared here |
test/cases/x86_64-windows/hello_world_with_updates.0.zig created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | // error | |
| 2 | // output_mode=Exe | |
| 3 | // target=x86_64-windows | |
| 4 | // | |
| 5 | // :130:9: error: struct 'tmp.tmp' has no member named 'main' | |
| 6 | // :7:1: note: struct declared here |
test/cases/x86_64-windows/hello_world_with_updates.1.zig created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | pub export fn main() noreturn {} | |
| 2 | ||
| 3 | // error | |
| 4 | // | |
| 5 | // :1:32: error: function declared 'noreturn' returns | |
| 6 | // :1:22: note: 'noreturn' declared here |
test/cases/x86_64-windows/hello_world_with_updates.2.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn main() void { | |
| 4 | print(); | |
| 5 | } | |
| 6 | ||
| 7 | fn print() void { | |
| 8 | const msg = "Hello, World!\n"; | |
| 9 | const stdout = std.io.getStdOut(); | |
| 10 | stdout.writeAll(msg) catch unreachable; | |
| 11 | } | |
| 12 | ||
| 13 | // run | |
| 14 | // | |
| 15 | // Hello, World! | |
| 16 | // |
test/link.zig+7-7| ... | ... | @@ -28,35 +28,35 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | 30 | fn addWasmCases(cases: *tests.StandaloneContext) void { |
| 31 | cases.addBuildFile("test/link/wasm/bss/build.zig", .{ | |
| 31 | cases.addBuildFile("test/link/wasm/archive/build.zig", .{ | |
| 32 | 32 | .build_modes = true, |
| 33 | 33 | .requires_stage2 = true, |
| 34 | 34 | }); |
| 35 | 35 | |
| 36 | cases.addBuildFile("test/link/wasm/segments/build.zig", .{ | |
| 36 | cases.addBuildFile("test/link/wasm/bss/build.zig", .{ | |
| 37 | 37 | .build_modes = true, |
| 38 | 38 | .requires_stage2 = true, |
| 39 | 39 | }); |
| 40 | 40 | |
| 41 | cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ | |
| 41 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ | |
| 42 | 42 | .build_modes = true, |
| 43 | 43 | .requires_stage2 = true, |
| 44 | .use_emulation = true, | |
| 44 | 45 | }); |
| 45 | 46 | |
| 46 | cases.addBuildFile("test/link/wasm/type/build.zig", .{ | |
| 47 | cases.addBuildFile("test/link/wasm/segments/build.zig", .{ | |
| 47 | 48 | .build_modes = true, |
| 48 | 49 | .requires_stage2 = true, |
| 49 | 50 | }); |
| 50 | 51 | |
| 51 | cases.addBuildFile("test/link/wasm/archive/build.zig", .{ | |
| 52 | cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{ | |
| 52 | 53 | .build_modes = true, |
| 53 | 54 | .requires_stage2 = true, |
| 54 | 55 | }); |
| 55 | 56 | |
| 56 | cases.addBuildFile("test/link/wasm/extern/build.zig", .{ | |
| 57 | cases.addBuildFile("test/link/wasm/type/build.zig", .{ | |
| 57 | 58 | .build_modes = true, |
| 58 | 59 | .requires_stage2 = true, |
| 59 | .use_emulation = true, | |
| 60 | 60 | }); |
| 61 | 61 | } |
| 62 | 62 |
test/tests.zig+10| ... | ... | @@ -108,6 +108,14 @@ const test_targets = blk: { |
| 108 | 108 | }, |
| 109 | 109 | .backend = .stage2_x86_64, |
| 110 | 110 | }, |
| 111 | .{ | |
| 112 | .target = .{ | |
| 113 | .cpu_arch = .x86_64, | |
| 114 | .os_tag = .windows, | |
| 115 | .abi = .gnu, | |
| 116 | }, | |
| 117 | .backend = .stage2_x86_64, | |
| 118 | }, | |
| 111 | 119 | |
| 112 | 120 | .{ |
| 113 | 121 | .target = .{ |
| ... | ... | @@ -693,6 +701,8 @@ pub fn addPkgTests( |
| 693 | 701 | else => { |
| 694 | 702 | these_tests.use_stage1 = false; |
| 695 | 703 | these_tests.use_llvm = false; |
| 704 | // TODO: force self-hosted linkers to avoid LLD creeping in until the auto-select mechanism deems them worthy | |
| 705 | these_tests.use_lld = false; | |
| 696 | 706 | }, |
| 697 | 707 | }; |
| 698 | 708 |