authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-09 10:26:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-09 10:26:17-07:00
log37cdb5dbf90acd61584bae4a6661d0a6f9b54295
tree6b369bbd58603d40b17b0e16e157a757b4a01616
parentb7900de1684021ff86c67105e14e34968821ea02
parent9e070b653c89a9216f9dd9f78ed7c78c11460ac7

Merge remote-tracking branch 'origin/master' into llvm15


52 files changed, 2454 insertions(+), 1056 deletions(-)

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