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:
6161
6262 - pwsh: |
6363 Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\stage3-release"
64 Set-Variable -Name ZIGPREFIXPATH -Value "$(Get-Location)\$(ZIG_LLVM_CLANG_LLD_NAME)"
6465
6566 function CheckLastExitCode {
6667 if (!$?) {
......@@ -72,8 +73,7 @@ jobs:
7273 & "$ZIGINSTALLDIR\bin\zig.exe" build test docs `
7374 --search-prefix "$ZIGPREFIXPATH" `
7475 -Dstatic-llvm `
75 -Dskip-non-native `
76 -Dskip-stage2-tests
76 -Dskip-non-native
7777 CheckLastExitCode
7878 name: test
7979 displayName: 'Test'
doc/docgen.zig+1-2
......@@ -1210,7 +1210,7 @@ fn genHtml(
12101210 var env_map = try process.getEnvMap(allocator);
12111211 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(.{});
12141214 const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe);
12151215
12161216 for (toc.nodes) |node| {
......@@ -1474,7 +1474,6 @@ fn genHtml(
14741474 .arch_os_abi = triple,
14751475 });
14761476 const target_info = try std.zig.system.NativeTargetInfo.detect(
1477 allocator,
14781477 cross_target,
14791478 );
14801479 switch (host.getExternalExecutor(target_info, .{
lib/std/build.zig+2-2
......@@ -171,7 +171,7 @@ pub const Builder = struct {
171171 const env_map = try allocator.create(EnvMap);
172172 env_map.* = try process.getEnvMap(allocator);
173173
174 const host = try NativeTargetInfo.detect(allocator, .{});
174 const host = try NativeTargetInfo.detect(.{});
175175
176176 const self = try allocator.create(Builder);
177177 self.* = Builder{
......@@ -1798,7 +1798,7 @@ pub const LibExeObjStep = struct {
17981798 }
17991799
18001800 fn computeOutFileNames(self: *LibExeObjStep) void {
1801 self.target_info = NativeTargetInfo.detect(self.builder.allocator, self.target) catch
1801 self.target_info = NativeTargetInfo.detect(self.target) catch
18021802 unreachable;
18031803
18041804 const target = self.target_info.target;
lib/std/build/EmulatableRunStep.zig+1-1
......@@ -158,7 +158,7 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
158158
159159 const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
160160 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;
162162 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
163163 switch (builder.host.getExternalExecutor(target_info, .{
164164 .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 {
990990 return index;
991991 }
992992
993 /// On Windows, this function currently does alter the file pointer.
994 /// https://github.com/ziglang/zig/issues/12783
993995 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
994996 if (is_windows) {
995997 return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode);
......@@ -1004,6 +1006,8 @@ pub const File = struct {
10041006
10051007 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
10061008 /// 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
10071011 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
10081012 var index: usize = 0;
10091013 while (index != buffer.len) {
......@@ -1058,6 +1062,8 @@ pub const File = struct {
10581062 }
10591063
10601064 /// 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
10611067 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {
10621068 if (is_windows) {
10631069 // TODO improve this to use ReadFileScatter
......@@ -1079,6 +1085,8 @@ pub const File = struct {
10791085 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
10801086 /// order to handle partial reads from the underlying OS layer.
10811087 /// 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
10821090 pub fn preadvAll(self: File, iovecs: []os.iovec, offset: u64) PReadError!usize {
10831091 if (iovecs.len == 0) return 0;
10841092
......@@ -1122,6 +1130,8 @@ pub const File = struct {
11221130 }
11231131 }
11241132
1133 /// On Windows, this function currently does alter the file pointer.
1134 /// https://github.com/ziglang/zig/issues/12783
11251135 pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
11261136 if (is_windows) {
11271137 return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode);
......@@ -1134,6 +1144,8 @@ pub const File = struct {
11341144 }
11351145 }
11361146
1147 /// On Windows, this function currently does alter the file pointer.
1148 /// https://github.com/ziglang/zig/issues/12783
11371149 pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void {
11381150 var index: usize = 0;
11391151 while (index < bytes.len) {
......@@ -1179,6 +1191,8 @@ pub const File = struct {
11791191 }
11801192
11811193 /// 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
11821196 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {
11831197 if (is_windows) {
11841198 // TODO improve this to use WriteFileScatter
......@@ -1197,6 +1211,8 @@ pub const File = struct {
11971211 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
11981212 /// order to handle partial writes from the underlying OS layer.
11991213 /// 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
12001216 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {
12011217 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
3636
3737fn getStdOutHandle() os.fd_t {
3838 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 }
3943 return os.windows.peb().ProcessParameters.hStdOutput;
4044 }
4145
......@@ -58,6 +62,10 @@ pub fn getStdOut() File {
5862
5963fn getStdErrHandle() os.fd_t {
6064 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 }
6169 return os.windows.peb().ProcessParameters.hStdError;
6270 }
6371
......@@ -80,6 +88,10 @@ pub fn getStdErr() File {
8088
8189fn getStdInHandle() os.fd_t {
8290 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 }
8395 return os.windows.peb().ProcessParameters.hStdInput;
8496 }
8597
lib/std/os/uefi/protocols/block_io_protocol.zig+2-2
......@@ -2,7 +2,7 @@ const std = @import("std");
22const uefi = std.os.uefi;
33const Status = uefi.Status;
44
5const EfiBlockMedia = extern struct {
5pub const EfiBlockMedia = extern struct {
66 /// The current media ID. If the media changes, this value is changed.
77 media_id: u32,
88
......@@ -38,7 +38,7 @@ const EfiBlockMedia = extern struct {
3838 optimal_transfer_length_granularity: u32,
3939};
4040
41const BlockIoProtocol = extern struct {
41pub const BlockIoProtocol = extern struct {
4242 const Self = @This();
4343
4444 revision: u64,
lib/std/os/windows/kernel32.zig+7-1
......@@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile(
348348 in_out_lpOverlapped: ?*OVERLAPPED,
349349) 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
353359pub 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");
99pub fn suggestVectorSizeForCpu(comptime T: type, comptime cpu: std.Target.Cpu) ?usize {
1010 // This is guesswork, if you have better suggestions can add it or edit the current here
1111 // 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);
1313 const vector_bit_size: u16 = blk: {
1414 if (cpu.arch.isX86()) {
1515 if (T == bool and std.Target.x86.featureSetHas(.prefer_mask_registers)) return 64;
......@@ -57,6 +57,15 @@ pub fn suggestVectorSize(comptime T: type) ?usize {
5757 return suggestVectorSizeForCpu(T, builtin.cpu);
5858}
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
6069fn vectorLength(comptime VectorType: type) comptime_int {
6170 return switch (@typeInfo(VectorType)) {
6271 .Vector => |info| info.len,
lib/std/start.zig+4
......@@ -36,6 +36,10 @@ comptime {
3636 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
3737 @export(main2, .{ .name = "main" });
3838 }
39 } else if (builtin.os.tag == .windows) {
40 if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
41 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
42 }
3943 } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {
4044 @export(wasiMain2, .{ .name = "_start" });
4145 } else {
lib/std/zig/system/NativeTargetInfo.zig+320-218
......@@ -28,6 +28,7 @@ pub const DetectError = error{
2828 SystemFdQuotaExceeded,
2929 DeviceBusy,
3030 OSVersionDetectionFail,
31 Unexpected,
3132};
3233
3334/// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected
......@@ -36,8 +37,7 @@ pub const DetectError = error{
3637/// relative to that.
3738/// Any resources this function allocates are released before returning, and so there is no
3839/// deinitialization method.
39/// TODO Remove the Allocator requirement from this function.
40pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
40pub fn detect(cross_target: CrossTarget) DetectError!NativeTargetInfo {
4141 var os = cross_target.getOsTag().defaultVersionRange(cross_target.getCpuArch());
4242 if (cross_target.os_tag == null) {
4343 switch (builtin.target.os.tag) {
......@@ -198,7 +198,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
198198 } orelse backup_cpu_detection: {
199199 break :backup_cpu_detection Target.Cpu.baseline(cpu_arch);
200200 };
201 var result = try detectAbiAndDynamicLinker(allocator, cpu, os, cross_target);
201 var result = try detectAbiAndDynamicLinker(cpu, os, cross_target);
202202 // For x86, we need to populate some CPU feature flags depending on architecture
203203 // and mode:
204204 // * 16bit_mode => if the abi is code16
......@@ -235,13 +235,20 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
235235 return result;
236236}
237237
238/// First we attempt to use the executable's own binary. If it is dynamically
239/// linked, then it should answer both the C ABI question and the dynamic linker question.
240/// If it is statically linked, then we try /usr/bin/env (or the file it references in shebang). If that does not provide the answer, then
241/// we fall back to the defaults.
242/// TODO Remove the Allocator requirement from this function.
238/// In the past, this function attempted to use the executable's own binary if it was dynamically
239/// linked to answer both the C ABI question and the dynamic linker question. However, this
240/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking
241/// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc
242/// version. The problem is that libc.so.6 glibc version will match that of the system while
243/// the dynamic linker will match that of the compiler binary. Executables with these versions
244/// mismatching will fail to run.
245///
246/// Therefore, this function works the same regardless of whether the compiler binary is
247/// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the
248/// answer to these questions, or if there is a shebang line, then it chases the referenced
249/// file recursively. If that does not provide the answer, then the function falls back to
250/// defaults.
243251fn detectAbiAndDynamicLinker(
244 allocator: Allocator,
245252 cpu: Target.Cpu,
246253 os: Target.Os,
247254 cross_target: CrossTarget,
......@@ -279,8 +286,8 @@ fn detectAbiAndDynamicLinker(
279286 const ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch);
280287
281288 for (all_abis) |abi| {
282 // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and
283 // skip adding it to `ld_info_list`.
289 // This may be a nonsensical parameter. We detect this with
290 // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`.
284291 const target: Target = .{
285292 .cpu = cpu,
286293 .os = os,
......@@ -300,64 +307,6 @@ fn detectAbiAndDynamicLinker(
300307
301308 // Best case scenario: the executable is dynamically linked, and we can iterate
302309 // 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
361310 const elf_file = blk: {
362311 // This block looks for a shebang line in /usr/bin/env,
363312 // 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(
369318 // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
370319 var buffer: [258]u8 = undefined;
371320 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) {
373322 error.NoSpaceLeft => unreachable,
374323 error.NameTooLong => unreachable,
375324 error.PathAlreadyExists => unreachable,
......@@ -390,44 +339,35 @@ fn detectAbiAndDynamicLinker(
390339 error.FileTooBig,
391340 error.Unexpected,
392341 => |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)});
394343 return defaultAbiAndDynamicLinker(cpu, os, cross_target);
395344 },
396345
397346 else => |e| return e,
398347 };
348 errdefer file.close();
399349
400 const line = file.reader().readUntilDelimiter(&buffer, '\n') catch |err| switch (err) {
401 error.IsDir => unreachable, // Handled before
402 error.AccessDenied => unreachable,
403 error.WouldBlock => unreachable, // Did not request blocking mode
404 error.OperationAborted => unreachable, // Windows-only
405 error.BrokenPipe => unreachable,
406 error.ConnectionResetByPeer => unreachable,
407 error.ConnectionTimedOut => unreachable,
408 error.InputOutput => unreachable,
409 error.Unexpected => unreachable,
410
411 error.StreamTooLong,
412 error.EndOfStream,
413 error.NotOpenForReading,
350 const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) {
351 error.UnexpectedEndOfFile,
352 error.UnableToReadElfFile,
414353 => break :blk file,
415354
416 else => |e| {
417 file.close();
418 return e;
419 },
355 else => |e| return e,
420356 };
357 const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file;
358 const line = buffer[0..newline];
421359 if (!mem.startsWith(u8, line, "#!")) break :blk file;
422 var it = std.mem.tokenize(u8, line[2..], " ");
423 file.close();
360 var it = mem.tokenize(u8, line[2..], " ");
424361 file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target);
362 file.close();
425363 }
426364 };
427365 defer elf_file.close();
428366
429367 // If Zig is statically linked, such as via distributed binary static builds, the above
430368 // 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.
431371 return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
432372 error.FileSystem,
433373 error.SystemResources,
......@@ -447,31 +387,196 @@ fn detectAbiAndDynamicLinker(
447387 error.NameTooLong,
448388 // Finally, we fall back on the standard path.
449389 => |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)});
451391 return defaultAbiAndDynamicLinker(cpu, os, cross_target);
452392 },
453393 };
454394}
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 {
459 var link_buf: [std.os.PATH_MAX]u8 = undefined;
460 const link_name = std.os.readlinkZ(so_path.ptr, &link_buf) catch |err| switch (err) {
461 error.AccessDenied => return error.GnuLibCVersionUnavailable,
462 error.FileSystem => return error.FileSystem,
463 error.SymLinkLoop => return error.SymLinkLoop,
410 error.ProcessFdQuotaExceeded,
411 error.SystemFdQuotaExceeded,
412 error.SystemResources,
413 error.SymLinkLoop,
414 error.Unexpected,
415 => |e| return e,
416 };
417 defer dir.close();
418
419 // Now we have a candidate for the path to libc shared object. In
420 // the past, we used readlink() here because the link name would
421 // reveal the glibc version. However, in more recent GNU/Linux
422 // installations, there is no symlink. Thus we instead use a more
423 // robust check of opening the libc shared object and looking at the
424 // .dynstr section, and finding the max version number of symbols
425 // that start with "GLIBC_2.".
426 const glibc_so_basename = "libc.so.6";
427 var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
464428 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,
470429 error.InvalidUtf8 => unreachable, // Windows only
471430 error.BadPathName => unreachable, // Windows only
472 error.UnsupportedReparsePointType => unreachable, // Windows only
431 error.PipeBusy => unreachable, // Windows-only
432 error.SharingViolation => unreachable, // Windows-only
433 error.FileLocksNotSupported => unreachable, // No lock requested.
434 error.NoSpaceLeft => unreachable, // read-only
435 error.PathAlreadyExists => unreachable, // read-only
436 error.DeviceBusy => unreachable, // read-only
437 error.FileBusy => unreachable, // read-only
438 error.InvalidHandle => unreachable, // should not be in the error set
439 error.WouldBlock => unreachable, // not using O_NONBLOCK
440 error.NoDevice => unreachable, // not asking for a special device
441
442 error.AccessDenied,
443 error.FileNotFound,
444 error.NotDir,
445 error.IsDir,
446 => return error.GLibCNotFound,
447
448 error.FileTooBig => return error.Unexpected,
449
450 error.ProcessFdQuotaExceeded,
451 error.SystemFdQuotaExceeded,
452 error.SystemResources,
453 error.SymLinkLoop,
454 error.Unexpected,
455 => |e| return e,
456 };
457 defer f.close();
458
459 return glibcVerFromSoFile(f) catch |err| switch (err) {
460 error.InvalidElfMagic,
461 error.InvalidElfEndian,
462 error.InvalidElfClass,
463 error.InvalidElfFile,
464 error.InvalidElfVersion,
465 error.InvalidGnuLibCVersion,
466 error.UnexpectedEndOfFile,
467 => return error.GLibCNotFound,
468
469 error.SystemResources,
470 error.UnableToReadElfFile,
471 error.Unexpected,
472 error.FileSystem,
473 => |e| return e,
473474 };
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;
475580}
476581
477582fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version {
......@@ -641,65 +746,65 @@ pub fn abiAndDynamicLinkerFromFile(
641746 if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and
642747 cross_target.glibc_version == null)
643748 {
644 if (rpath_offset) |rpoff| {
645 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
646
647 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
648 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
649 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
650
651 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
652 if (sh_buf.len < shentsize) return error.InvalidElfFile;
653
654 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
655 const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
656 const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
657 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
658 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
659 var strtab_buf: [4096:0]u8 = undefined;
660 const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
661 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
662 const shstrtab = strtab_buf[0..shstrtab_read_len];
663
664 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
665 var sh_i: u16 = 0;
666 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
667 // Reserve some bytes so that we can deref the 64-bit struct fields
668 // even when the ELF file is 32-bits.
669 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
670 const sh_read_byte_len = try preadMin(
671 file,
672 sh_buf[0 .. sh_buf.len - sh_reserve],
673 shoff,
674 shentsize,
749 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
750
751 var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
752 const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
753 const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
754
755 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
756 if (sh_buf.len < shentsize) return error.InvalidElfFile;
757
758 _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
759 const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
760 const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
761 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
762 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
763 var strtab_buf: [4096:0]u8 = undefined;
764 const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
765 const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
766 const shstrtab = strtab_buf[0..shstrtab_read_len];
767
768 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
769 var sh_i: u16 = 0;
770 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
771 // Reserve some bytes so that we can deref the 64-bit struct fields
772 // even when the ELF file is 32-bits.
773 const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
774 const sh_read_byte_len = try preadMin(
775 file,
776 sh_buf[0 .. sh_buf.len - sh_reserve],
777 shoff,
778 shentsize,
779 );
780 var sh_buf_i: usize = 0;
781 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
782 sh_i += 1;
783 shoff += shentsize;
784 sh_buf_i += shentsize;
785 }) {
786 const sh32 = @ptrCast(
787 *elf.Elf32_Shdr,
788 @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]),
675789 );
676 var sh_buf_i: usize = 0;
677 while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
678 sh_i += 1;
679 shoff += shentsize;
680 sh_buf_i += shentsize;
681 }) {
682 const sh32 = @ptrCast(
683 *elf.Elf32_Shdr,
684 @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]),
685 );
686 const sh64 = @ptrCast(
687 *elf.Elf64_Shdr,
688 @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]),
689 );
690 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
691 // TODO this pointer cast should not be necessary
692 const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0);
693 if (mem.eql(u8, sh_name, ".dynstr")) {
694 break :find_dyn_str .{
695 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
696 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
697 };
698 }
790 const sh64 = @ptrCast(
791 *elf.Elf64_Shdr,
792 @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]),
793 );
794 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
795 // TODO this pointer cast should not be necessary
796 const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0);
797 if (mem.eql(u8, sh_name, ".dynstr")) {
798 break :find_dyn_str .{
799 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
800 .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
801 };
699802 }
700 } else null;
803 }
804 } else null;
701805
702 if (dynstr) |ds| {
806 if (dynstr) |ds| {
807 if (rpath_offset) |rpoff| {
703808 // TODO this pointer cast should not be necessary
704809 const rpoff_usize = std.math.cast(usize, rpoff) orelse return error.InvalidElfFile;
705810 if (rpoff_usize > ds.size) return error.InvalidElfFile;
......@@ -713,64 +818,31 @@ pub fn abiAndDynamicLinkerFromFile(
713818 const rpath_list = mem.sliceTo(std.meta.assumeSentinel(strtab.ptr, 0), 0);
714819 var it = mem.tokenize(u8, rpath_list, ":");
715820 while (it.next()) |rpath| {
716 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
717 error.NameTooLong => unreachable,
718 error.InvalidUtf8 => unreachable,
719 error.BadPathName => unreachable,
720 error.DeviceBusy => unreachable,
721
722 error.FileNotFound,
723 error.NotDir,
724 error.InvalidHandle,
725 error.AccessDenied,
726 error.NoDevice,
727 => continue,
728
729 error.ProcessFdQuotaExceeded,
730 error.SystemFdQuotaExceeded,
731 error.SystemResources,
732 error.SymLinkLoop,
733 error.Unexpected,
734 => |e| return e,
735 };
736 defer dir.close();
737
738 var link_buf: [std.os.PATH_MAX]u8 = undefined;
739 const link_name = std.os.readlinkatZ(
740 dir.fd,
741 glibc_so_basename,
742 &link_buf,
743 ) catch |err| switch (err) {
744 error.NameTooLong => unreachable,
745 error.InvalidUtf8 => unreachable, // Windows only
746 error.BadPathName => unreachable, // Windows only
747 error.UnsupportedReparsePointType => unreachable, // Windows only
748
749 error.AccessDenied,
750 error.FileNotFound,
751 error.NotLink,
752 error.NotDir,
753 => continue,
754
755 error.SystemResources,
756 error.FileSystem,
757 error.SymLinkLoop,
758 error.Unexpected,
759 => |e| return e,
760 };
761 result.target.os.version_range.linux.glibc = glibcVerFromLinkName(
762 link_name,
763 "libc-",
764 ) catch |err| switch (err) {
765 error.UnrecognizedGnuLibCFileName,
766 error.InvalidGnuLibCVersion,
767 => continue,
768 };
769 break;
821 if (glibcVerFromRPath(rpath)) |ver| {
822 result.target.os.version_range.linux.glibc = ver;
823 return result;
824 } else |err| switch (err) {
825 error.GLibCNotFound => continue,
826 else => |e| return e,
827 }
828 }
829 }
830 }
831
832 if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
833 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same
834 // directory as the dynamic linker.
835 if (fs.path.dirname(dl_path)) |rpath| {
836 if (glibcVerFromRPath(rpath)) |ver| {
837 result.target.os.version_range.linux.glibc = ver;
838 return result;
839 } else |err| switch (err) {
840 error.GLibCNotFound => {},
841 else => |e| return e,
770842 }
771843 }
772 } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
773 // There is no DT_RUNPATH but we can try to see if the information is
844
845 // So far, no luck. Next we try to see if the information is
774846 // present in the symlink data for the dynamic linker path.
775847 var link_buf: [std.os.PATH_MAX]u8 = undefined;
776848 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {
......@@ -799,6 +871,36 @@ pub fn abiAndDynamicLinkerFromFile(
799871 error.InvalidGnuLibCVersion,
800872 => break :glibc_ver,
801873 };
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,
802904 }
803905 }
804906
src/Compilation.zig+6
......@@ -878,6 +878,9 @@ pub const InitOptions = struct {
878878 linker_shared_memory: bool = false,
879879 linker_global_base: ?u64 = null,
880880 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,
881884 each_lib_rpath: ?bool = null,
882885 build_id: ?bool = null,
883886 disable_c_depfile: bool = false,
......@@ -1727,6 +1730,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17271730 .shared_memory = options.linker_shared_memory,
17281731 .global_base = options.linker_global_base,
17291732 .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,
17301736 .z_nodelete = options.linker_z_nodelete,
17311737 .z_notext = options.linker_z_notext,
17321738 .z_defs = options.linker_z_defs,
src/Module.zig+21-4
......@@ -345,6 +345,15 @@ pub const CaptureScope = struct {
345345 /// During sema, this map is backed by the gpa. Once sema completes,
346346 /// it is reallocated using the value_arena.
347347 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 }
348357};
349358
350359pub const WipCaptureScope = struct {
......@@ -383,6 +392,7 @@ pub const WipCaptureScope = struct {
383392 pub fn deinit(noalias self: *@This()) void {
384393 if (!self.finalized) {
385394 self.scope.captures.deinit(self.gpa);
395 self.scope.fail();
386396 }
387397 self.* = undefined;
388398 }
......@@ -4274,11 +4284,14 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
42744284
42754285 const comp = mod.comp;
42764286
4277 if (comp.bin_file.options.emit == null and
4287 const no_bin_file = (comp.bin_file.options.emit == null and
42784288 comp.emit_asm == null and
42794289 comp.emit_llvm_ir == null and
4280 comp.emit_llvm_bc == null)
4281 {
4290 comp.emit_llvm_bc == null);
4291
4292 const dump_air = builtin.mode == .Debug and comp.verbose_air;
4293
4294 if (no_bin_file and !dump_air) {
42824295 return;
42834296 }
42844297
......@@ -4286,7 +4299,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
42864299 var liveness = try Liveness.analyze(gpa, air);
42874300 defer liveness.deinit(gpa);
42884301
4289 if (builtin.mode == .Debug and comp.verbose_air) {
4302 if (dump_air) {
42904303 const fqn = try decl.getFullyQualifiedName(mod);
42914304 defer mod.gpa.free(fqn);
42924305
......@@ -4295,6 +4308,10 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
42954308 std.debug.print("# End Function AIR: {s}\n\n", .{fqn});
42964309 }
42974310
4311 if (no_bin_file) {
4312 return;
4313 }
4314
42984315 comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) {
42994316 error.OutOfMemory => return error.OutOfMemory,
43004317 error.AnalysisFail => {
src/Sema.zig+32-30
......@@ -5956,7 +5956,6 @@ fn analyzeCall(
59565956 error.NeededSourceLocation => {
59575957 _ = sema.inst_map.remove(inst);
59585958 const decl = sema.mod.declPtr(block.src_decl);
5959 child_block.src_decl = block.src_decl;
59605959 try sema.analyzeInlineCallArg(
59615960 block,
59625961 &child_block,
......@@ -13740,6 +13739,16 @@ fn zirClosureGet(
1374013739 const tv = while (true) {
1374113740 // Note: We don't need to add a dependency here, because
1374213741 // 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 }
1374313752 if (scope.captures.getPtr(inst_data.inst)) |tv| {
1374413753 break tv;
1374513754 }
......@@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
1807618085 const target = sema.mod.getTarget();
1807718086
1807818087 try sema.resolveTypeLayout(block, lhs_src, ty);
18079 switch (ty.tag()) {
18080 .@"struct", .tuple, .anon_struct => {},
18088 switch (ty.zigTypeTag()) {
18089 .Struct => {},
1808118090 else => {
1808218091 const msg = msg: {
1808318092 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
1961719626 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1961819627 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1961919628 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
19620 const dest_ptr = try sema.resolveInst(extra.dest);
19621 const dest_ptr_ty = sema.typeOf(dest_ptr);
19629 const uncasted_dest_ptr = try sema.resolveInst(extra.dest);
1962219630
19623 try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
19624 if (dest_ptr_ty.isConstPtr()) {
19625 return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)});
19626 }
19631 // TODO AstGen's coerced_ty cannot handle volatile here
19632 var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data;
19633 dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr();
19634 const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info);
19635 const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src);
1962719636
1962819637 const uncasted_src_ptr = try sema.resolveInst(extra.source);
19629 const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr);
19630 try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty);
19631 const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data;
19632 const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
19633 .pointee_type = dest_ptr_ty.elemType2(),
19634 .@"align" = src_ptr_info.@"align",
19635 .@"addrspace" = src_ptr_info.@"addrspace",
19636 .mutable = false,
19637 .@"allowzero" = src_ptr_info.@"allowzero",
19638 .@"volatile" = src_ptr_info.@"volatile",
19639 .size = .Many,
19640 });
19641 const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src);
19638 var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data;
19639 src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr();
19640 const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info);
19641 const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src);
1964219642 const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);
1964319643
1964419644 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
1967419674 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1967519675 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1967619676 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
19677 const dest_ptr = try sema.resolveInst(extra.dest);
19678 const dest_ptr_ty = sema.typeOf(dest_ptr);
19679 try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
19680 if (dest_ptr_ty.isConstPtr()) {
19681 return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)});
19682 }
19683 const elem_ty = dest_ptr_ty.elemType2();
19684 const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src);
19677 const uncasted_dest_ptr = try sema.resolveInst(extra.dest);
19678
19679 // TODO AstGen's coerced_ty cannot handle volatile here
19680 var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data;
19681 ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr();
19682 const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
19683 const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src);
19684
19685 const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src);
1968519686 const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);
1968619687
1968719688 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
2601326014 .pointee_type = decl_tv.ty,
2601426015 .mutable = false,
2601526016 .@"addrspace" = decl.@"addrspace",
26017 .@"align" = decl.@"align",
2601626018 }),
2601726019 try Value.Tag.decl_ref.create(sema.arena, decl_index),
2601826020 );
src/arch/wasm/CodeGen.zig+4
......@@ -666,6 +666,10 @@ pub fn deinit(self: *Self) void {
666666 self.locals.deinit(self.gpa);
667667 self.mir_instructions.deinit(self.gpa);
668668 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);
669673 self.* = undefined;
670674}
671675
src/arch/x86_64/CodeGen.zig+372-229
......@@ -32,11 +32,6 @@ const abi = @import("abi.zig");
3232const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
3333const 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
4035const Condition = bits.Condition;
4136const RegisterManager = abi.RegisterManager;
4237const RegisterLock = RegisterManager.RegisterLock;
......@@ -137,6 +132,7 @@ pub const MCValue = union(enum) {
137132 /// If the type is a pointer, it means the pointer is referenced indirectly via GOT.
138133 /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT.
139134 got_load: u32,
135 imports_load: u32,
140136 /// The value is in memory referenced directly via symbol index.
141137 /// If the type is a pointer, it means the pointer is referenced directly via symbol index.
142138 /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED.
......@@ -156,6 +152,7 @@ pub const MCValue = union(enum) {
156152 .ptr_stack_offset,
157153 .direct_load,
158154 .got_load,
155 .imports_load,
159156 => true,
160157 else => false,
161158 };
......@@ -203,6 +200,42 @@ const Branch = struct {
203200 self.inst_table.deinit(gpa);
204201 self.* = undefined;
205202 }
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 }
206239};
207240
208241const StackAllocation = struct {
......@@ -235,7 +268,7 @@ const BigTomb = struct {
235268 fn finishAir(bt: *BigTomb, result: MCValue) void {
236269 const is_used = !bt.function.liveness.isUnused(bt.inst);
237270 if (is_used) {
238 log.debug("%{d} => {}", .{ bt.inst, result });
271 log.debug(" (saving %{d} => {})", .{ bt.inst, result });
239272 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
240273 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
241274 }
......@@ -406,16 +439,17 @@ fn gen(self: *Self) InnerError!void {
406439 });
407440
408441 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
410443 // register which the callee is free to clobber. Therefore, we purposely
411444 // spill it to stack immediately.
412445 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8);
413446 self.next_stack_offset = stack_offset;
414447 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 }, .{});
417451 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 });
419453 }
420454
421455 _ = try self.addInst(.{
......@@ -446,10 +480,11 @@ fn gen(self: *Self) InnerError!void {
446480
447481 // Create list of registers to save in the prologue.
448482 // TODO handle register classes
449 var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{};
450 inline for (callee_preserved_regs) |reg| {
483 var reg_list = Mir.RegisterList{};
484 const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*);
485 for (callee_preserved_regs) |reg| {
451486 if (self.register_manager.isRegAllocated(reg)) {
452 reg_list.push(reg);
487 reg_list.push(callee_preserved_regs, reg);
453488 }
454489 }
455490 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 {
797832fn processDeath(self: *Self, inst: Air.Inst.Index) void {
798833 const air_tags = self.air.instructions.items(.tag);
799834 if (air_tags[inst] == .constant) return; // Constants are immortal.
835 log.debug("%{d} => {}", .{ inst, MCValue{ .dead = {} } });
800836 // When editing this function, note that the logic must synchronize with `reuseOperand`.
801837 const prev_value = self.getResolvedInstValue(inst);
802838 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 {
22742310 .memory,
22752311 .got_load,
22762312 .direct_load,
2313 .imports_load,
22772314 => {
22782315 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);
22792316 },
......@@ -2618,6 +2655,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
26182655 .memory,
26192656 .got_load,
26202657 .direct_load,
2658 .imports_load,
26212659 => {
26222660 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
26232661 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
......@@ -2655,6 +2693,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26552693 switch (ptr) {
26562694 .got_load,
26572695 .direct_load,
2696 .imports_load,
26582697 => |sym_index| {
26592698 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
26602699 const mod = self.bin_file.options.module.?;
......@@ -2666,6 +2705,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26662705 const flags: u2 = switch (ptr) {
26672706 .got_load => 0b00,
26682707 .direct_load => 0b01,
2708 .imports_load => 0b10,
26692709 else => unreachable,
26702710 };
26712711 _ = try self.addInst(.{
......@@ -2763,6 +2803,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27632803 },
27642804 .got_load,
27652805 .direct_load,
2806 .imports_load,
27662807 .memory,
27672808 .stack_offset,
27682809 => {
......@@ -2783,6 +2824,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
27832824 },
27842825 .got_load,
27852826 .direct_load,
2827 .imports_load,
27862828 .memory,
27872829 => {
27882830 const value_lock: ?RegisterLock = switch (value) {
......@@ -2854,6 +2896,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
28542896 },
28552897 .got_load,
28562898 .direct_load,
2899 .imports_load,
28572900 .memory,
28582901 => {
28592902 if (abi_size <= 8) {
......@@ -3565,6 +3608,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
35653608 .memory,
35663609 .got_load,
35673610 .direct_load,
3611 .imports_load,
35683612 .eflags,
35693613 => {
35703614 assert(abi_size <= 8);
......@@ -3650,7 +3694,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36503694 => {
36513695 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
36523696 },
3653 .got_load, .direct_load => {
3697 .got_load,
3698 .direct_load,
3699 .imports_load,
3700 => {
36543701 return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{});
36553702 },
36563703 .eflags => {
......@@ -3661,7 +3708,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36613708 .memory => {
36623709 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});
36633710 },
3664 .got_load, .direct_load => {
3711 .got_load,
3712 .direct_load,
3713 .imports_load,
3714 => {
36653715 return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{});
36663716 },
36673717 }
......@@ -3729,7 +3779,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37293779 .memory => {
37303780 return self.fail("TODO implement x86 multiply source memory", .{});
37313781 },
3732 .got_load, .direct_load => {
3782 .got_load,
3783 .direct_load,
3784 .imports_load,
3785 => {
37333786 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
37343787 },
37353788 .eflags => {
......@@ -3773,7 +3826,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37733826 .memory, .stack_offset => {
37743827 return self.fail("TODO implement x86 multiply source memory", .{});
37753828 },
3776 .got_load, .direct_load => {
3829 .got_load,
3830 .direct_load,
3831 .imports_load,
3832 => {
37773833 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
37783834 },
37793835 .eflags => {
......@@ -3784,7 +3840,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
37843840 .memory => {
37853841 return self.fail("TODO implement x86 multiply destination memory", .{});
37863842 },
3787 .got_load, .direct_load => {
3843 .got_load,
3844 .direct_load,
3845 .imports_load,
3846 => {
37883847 return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{});
37893848 },
37903849 }
......@@ -3898,11 +3957,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
38983957
38993958 try self.spillEflagsIfOccupied();
39003959
3901 for (caller_preserved_regs) |reg| {
3960 for (abi.getCallerPreservedRegs(self.target.*)) |reg| {
39023961 try self.register_manager.getReg(reg, null);
39033962 }
39043963
3905 const rdi_lock: ?RegisterLock = blk: {
3964 const ret_reg_lock: ?RegisterLock = blk: {
39063965 if (info.return_value == .stack_offset) {
39073966 const ret_ty = fn_ty.fnReturnType();
39083967 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.
39103969 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));
39113970 log.debug("airCall: return value on stack at offset {}", .{stack_offset});
39123971
3913 try self.register_manager.getReg(.rdi, null);
3914 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });
3915 const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi);
3972 const ret_reg = abi.getCAbiIntParamRegs(self.target.*)[0];
3973 try self.register_manager.getReg(ret_reg, null);
3974 try self.genSetReg(Type.usize, ret_reg, .{ .ptr_stack_offset = stack_offset });
3975 const ret_reg_lock = self.register_manager.lockRegAssumeUnused(ret_reg);
39163976
39173977 info.return_value.stack_offset = stack_offset;
39183978
3919 break :blk rdi_lock;
3979 break :blk ret_reg_lock;
39203980 }
39213981 break :blk null;
39223982 };
3923 defer if (rdi_lock) |lock| self.register_manager.unlockReg(lock);
3983 defer if (ret_reg_lock) |lock| self.register_manager.unlockReg(lock);
39243984
39253985 for (args) |arg, arg_i| {
39263986 const mc_arg = info.args[arg_i];
......@@ -3948,6 +4008,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39484008 .memory => unreachable,
39494009 .got_load => unreachable,
39504010 .direct_load => unreachable,
4011 .imports_load => unreachable,
39514012 .eflags => unreachable,
39524013 .register_overflow => unreachable,
39534014 }
......@@ -3999,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39994060 .data = undefined,
40004061 });
40014062 }
4002 } else if (self.bin_file.cast(link.File.Coff)) |_| {
4063 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
40034064 if (self.air.value(callee)) |func_value| {
40044065 if (func_value.castTag(.function)) |func_payload| {
40054066 const func = func_payload.data;
......@@ -4015,8 +4076,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
40154076 }),
40164077 .data = undefined,
40174078 });
4018 } else if (func_value.castTag(.extern_fn)) |_| {
4019 return self.fail("TODO implement calling extern functions", .{});
4079 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4080 const extern_fn = func_payload.data;
4081 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4082 if (extern_fn.lib_name) |lib_name| {
4083 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4084 decl_name,
4085 lib_name,
4086 });
4087 }
4088 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4089 try self.genSetReg(Type.initTag(.usize), .rax, .{
4090 .imports_load = sym_index,
4091 });
4092 _ = try self.addInst(.{
4093 .tag = .call,
4094 .ops = Mir.Inst.Ops.encode(.{
4095 .reg1 = .rax,
4096 .flags = 0b01,
4097 }),
4098 .data = undefined,
4099 });
40204100 } else {
40214101 return self.fail("TODO implement calling bitcasted functions", .{});
40224102 }
......@@ -4425,7 +4505,11 @@ fn genVarDbgInfo(
44254505 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
44264506 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
44274507 },
4428 .memory, .got_load, .direct_load => {
4508 .memory,
4509 .got_load,
4510 .direct_load,
4511 .imports_load,
4512 => {
44294513 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
44304514 const is_ptr = switch (tag) {
44314515 .dbg_var_ptr => true,
......@@ -4456,7 +4540,10 @@ fn genVarDbgInfo(
44564540 try dbg_info.append(DW.OP.deref);
44574541 }
44584542 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),
44604547 else => {},
44614548 }
44624549 },
......@@ -4626,15 +4713,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
46264713
46274714 // Revert to the previous register and stack allocation state.
46284715
4629 var saved_then_branch = self.branch_stack.pop();
4630 defer saved_then_branch.deinit(self.gpa);
4716 var then_branch = self.branch_stack.pop();
4717 defer then_branch.deinit(self.gpa);
46314718
46324719 self.revertState(saved_state);
46334720
46344721 try self.performReloc(reloc);
46354722
4636 const else_branch = self.branch_stack.addOneAssumeCapacity();
4637 else_branch.* = .{};
4723 try self.branch_stack.append(.{});
4724 errdefer {
4725 _ = self.branch_stack.pop();
4726 }
46384727
46394728 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
46404729 for (liveness_condbr.else_deaths) |operand| {
......@@ -4642,6 +4731,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
46424731 }
46434732 try self.genBody(else_body);
46444733
4734 var else_branch = self.branch_stack.pop();
4735 defer else_branch.deinit(self.gpa);
4736
46454737 // At this point, each branch will possibly have conflicting values for where
46464738 // each instruction is stored. They agree, however, on which instructions are alive/dead.
46474739 // We use the first ("then") branch as canonical, and here emit
......@@ -4650,74 +4742,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
46504742 // that we can use all the code emitting abstractions. This is why at the bottom we
46514743 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
46524744 // rather than assigning it.
4653 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
4654 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
4655
4656 const else_slice = else_branch.inst_table.entries.slice();
4657 const else_keys = else_slice.items(.key);
4658 const else_values = else_slice.items(.value);
4659 for (else_keys) |else_key, else_idx| {
4660 const else_value = else_values[else_idx];
4661 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
4662 // The instruction's MCValue is overridden in both branches.
4663 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
4664 if (else_value == .dead) {
4665 assert(then_entry.value == .dead);
4666 continue;
4667 }
4668 break :blk then_entry.value;
4669 } else blk: {
4670 if (else_value == .dead)
4671 continue;
4672 // The instruction is only overridden in the else branch.
4673 var i: usize = self.branch_stack.items.len - 2;
4674 while (true) {
4675 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
4676 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
4677 assert(mcv != .dead);
4678 break :blk mcv;
4679 }
4680 }
4681 };
4682 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
4683 // TODO make sure the destination stack offset / register does not already have something
4684 // going on there.
4685 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
4686 // TODO track the new register / stack allocation
4687 }
4688 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
4689 const then_slice = saved_then_branch.inst_table.entries.slice();
4690 const then_keys = then_slice.items(.key);
4691 const then_values = then_slice.items(.value);
4692 for (then_keys) |then_key, then_idx| {
4693 const then_value = then_values[then_idx];
4694 // We already deleted the items from this table that matched the else_branch.
4695 // So these are all instructions that are only overridden in the then branch.
4696 parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
4697 log.debug("then_value = {}", .{then_value});
4698 if (then_value == .dead)
4699 continue;
4700 const parent_mcv = blk: {
4701 var i: usize = self.branch_stack.items.len - 2;
4702 while (true) {
4703 i -= 1;
4704 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
4705 assert(mcv != .dead);
4706 break :blk mcv;
4707 }
4708 }
4709 };
4710 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
4711 // TODO make sure the destination stack offset / register does not already have something
4712 // going on there.
4713 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
4714 // TODO track the new register / stack allocation
4745 log.debug("airCondBr: %{d}", .{inst});
4746 log.debug("Upper branches:", .{});
4747 for (self.branch_stack.items) |bs| {
4748 log.debug("{}", .{bs.fmtDebug()});
47154749 }
47164750
4717 {
4718 var item = self.branch_stack.pop();
4719 item.deinit(self.gpa);
4720 }
4751 log.debug("Then branch: {}", .{then_branch.fmtDebug()});
4752 log.debug("Else branch: {}", .{else_branch.fmtDebug()});
4753
4754 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
4755 try self.canonicaliseBranches(parent_branch, &then_branch, &else_branch);
47214756
47224757 // We already took care of pl_op.operand earlier, so we're going
47234758 // to pass .none here
......@@ -5102,6 +5137,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51025137 }
51035138 }
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
51055149 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
51065150 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
51075151 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 {
51315175
51325176 try self.genBody(case_body);
51335177
5134 // Revert to the previous register and stack allocation state.
5135 var saved_case_branch = self.branch_stack.pop();
5136 defer saved_case_branch.deinit(self.gpa);
5178 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
51375179
5180 // Revert to the previous register and stack allocation state.
51385181 self.revertState(saved_state);
51395182
51405183 for (relocs) |reloc| {
......@@ -5144,10 +5187,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51445187
51455188 if (switch_br.data.else_body_len > 0) {
51465189 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
51475194 try self.branch_stack.append(.{});
5148 defer {
5149 var item = self.branch_stack.pop();
5150 item.deinit(self.gpa);
5195 errdefer {
5196 _ = self.branch_stack.pop();
51515197 }
51525198
51535199 const else_deaths = liveness.deaths.len - 1;
......@@ -5158,8 +5204,30 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51585204
51595205 try self.genBody(else_body);
51605206
5161 // TODO consolidate returned MCValues between prongs and else branch like we do
5162 // in airCondBr.
5207 branch_stack.appendAssumeCapacity(self.branch_stack.pop());
5208
5209 // Revert to the previous register and stack allocation state.
5210 self.revertState(saved_state);
5211 }
5212
5213 // Consolidate returned MCValues between prongs and else branch like we do
5214 // in airCondBr.
5215 log.debug("airSwitch: %{d}", .{inst});
5216 log.debug("Upper branches:", .{});
5217 for (self.branch_stack.items) |bs| {
5218 log.debug("{}", .{bs.fmtDebug()});
5219 }
5220 for (branch_stack.items) |bs, i| {
5221 log.debug("Case-{d} branch: {}", .{ i, bs.fmtDebug() });
5222 }
5223
5224 // TODO: can we reduce the complexity of this algorithm?
5225 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
5226 var i: usize = branch_stack.items.len;
5227 while (i > 1) : (i -= 1) {
5228 const canon_branch = &branch_stack.items[i - 2];
5229 const target_branch = &branch_stack.items[i - 1];
5230 try self.canonicaliseBranches(parent_branch, canon_branch, target_branch);
51635231 }
51645232
51655233 // 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 {
51675235 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
51685236}
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
51705304fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {
51715305 const next_inst = @intCast(u32, self.mir_instructions.len);
51725306 switch (self.mir_instructions.items(.tag)[reloc]) {
......@@ -5196,7 +5330,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
51965330 block_data.mcv = switch (operand_mcv) {
51975331 .none, .dead, .unreach => unreachable,
51985332 .register, .stack_offset, .memory => operand_mcv,
5199 .eflags, .immediate => blk: {
5333 .eflags, .immediate, .ptr_stack_offset => blk: {
52005334 const new_mcv = try self.allocRegOrMem(block, true);
52015335 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
52025336 break :blk new_mcv;
......@@ -5456,6 +5590,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
54565590 .memory,
54575591 .direct_load,
54585592 .got_load,
5593 .imports_load,
54595594 => {
54605595 if (abi_size <= 8) {
54615596 const reg = try self.copyToTmpRegister(ty, mcv);
......@@ -5703,6 +5838,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
57035838 .memory,
57045839 .got_load,
57055840 .direct_load,
5841 .imports_load,
57065842 => {
57075843 if (abi_size <= 8) {
57085844 const reg = try self.copyToTmpRegister(ty, mcv);
......@@ -5796,7 +5932,6 @@ const InlineMemcpyOpts = struct {
57965932 dest_stack_base: ?Register = null,
57975933};
57985934
5799/// Spills .rax and .rcx.
58005935fn genInlineMemcpy(
58015936 self: *Self,
58025937 dst_ptr: MCValue,
......@@ -5804,15 +5939,6 @@ fn genInlineMemcpy(
58045939 len: MCValue,
58055940 opts: InlineMemcpyOpts,
58065941) 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
58165942 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
58175943 self.register_manager.lockReg(reg)
58185944 else
......@@ -5825,11 +5951,18 @@ fn genInlineMemcpy(
58255951 null;
58265952 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
58295961 switch (dst_ptr) {
58305962 .memory,
58315963 .got_load,
58325964 .direct_load,
5965 .imports_load,
58335966 => {
58345967 try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_ptr);
58355968 },
......@@ -5857,14 +5990,12 @@ fn genInlineMemcpy(
58575990 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
58585991 },
58595992 }
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);
58645994 switch (src_ptr) {
58655995 .memory,
58665996 .got_load,
58675997 .direct_load,
5998 .imports_load,
58685999 => {
58696000 try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_ptr);
58706001 },
......@@ -5892,26 +6023,13 @@ fn genInlineMemcpy(
58926023 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
58936024 },
58946025 }
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
59026027 try self.genSetReg(Type.usize, count_reg, len);
59036028
5904 // mov rcx, 0
5905 _ = try self.addInst(.{
5906 .tag = .mov,
5907 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),
5908 .data = .{ .imm = 0 },
5909 });
5910
5911 // mov rax, 0
6029 // mov index_reg, 0
59126030 _ = try self.addInst(.{
59136031 .tag = .mov,
5914 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
6032 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
59156033 .data = .{ .imm = 0 },
59166034 });
59176035
......@@ -5933,37 +6051,30 @@ fn genInlineMemcpy(
59336051 } },
59346052 });
59356053
5936 // mov tmp, [addr + rcx]
6054 // mov tmp, [addr + index_reg]
59376055 _ = try self.addInst(.{
59386056 .tag = .mov_scale_src,
59396057 .ops = Mir.Inst.Ops.encode(.{
59406058 .reg1 = tmp_reg.to8(),
59416059 .reg2 = src_addr_reg,
59426060 }),
5943 .data = .{ .imm = 0 },
6061 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },
59446062 });
59456063
5946 // mov [stack_offset + rax], tmp
6064 // mov [stack_offset + index_reg], tmp
59476065 _ = try self.addInst(.{
59486066 .tag = .mov_scale_dst,
59496067 .ops = Mir.Inst.Ops.encode(.{
59506068 .reg1 = dst_addr_reg,
59516069 .reg2 = tmp_reg.to8(),
59526070 }),
5953 .data = .{ .imm = 0 },
5954 });
5955
5956 // add rcx, 1
5957 _ = try self.addInst(.{
5958 .tag = .add,
5959 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }),
5960 .data = .{ .imm = 1 },
6071 .data = .{ .payload = try self.addExtra(Mir.IndexRegisterDisp.encode(index_reg, 0)) },
59616072 });
59626073
5963 // add rax, 1
6074 // add index_reg, 1
59646075 _ = try self.addInst(.{
59656076 .tag = .add,
5966 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
6077 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
59676078 .data = .{ .imm = 1 },
59686079 });
59696080
......@@ -5985,7 +6096,6 @@ fn genInlineMemcpy(
59856096 try self.performReloc(loop_reloc);
59866097}
59876098
5988/// Spills .rax register.
59896099fn genInlineMemset(
59906100 self: *Self,
59916101 dst_ptr: MCValue,
......@@ -5993,16 +6103,27 @@ fn genInlineMemset(
59936103 len: MCValue,
59946104 opts: InlineMemcpyOpts,
59956105) InnerError!void {
5996 // TODO preserve contents of .rax and then restore
5997 try self.register_manager.getReg(.rax, null);
5998 const rax_lock = self.register_manager.lockRegAssumeUnused(.rax);
5999 defer self.register_manager.unlockReg(rax_lock);
6106 const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
6107 self.register_manager.lockReg(reg)
6108 else
6109 null;
6110 defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg);
6111
6112 const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg|
6113 self.register_manager.lockReg(reg)
6114 else
6115 null;
6116 defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);
6117
6118 const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
6119 const addr_reg = regs[0];
6120 const index_reg = regs[1].to64();
60006121
6001 const addr_reg = try self.register_manager.allocReg(null, gp);
60026122 switch (dst_ptr) {
60036123 .memory,
60046124 .got_load,
60056125 .direct_load,
6126 .imports_load,
60066127 => {
60076128 try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_ptr);
60086129 },
......@@ -6030,17 +6151,15 @@ fn genInlineMemset(
60306151 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
60316152 },
60326153 }
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);
6037 try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });
6155 try self.genSetReg(Type.usize, index_reg, len);
6156 try self.genBinOpMir(.sub, Type.usize, .{ .register = index_reg }, .{ .immediate = 1 });
60386157
60396158 // loop:
6040 // cmp rax, -1
6159 // cmp index_reg, -1
60416160 const loop_start = try self.addInst(.{
60426161 .tag = .cmp,
6043 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
6162 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
60446163 .data = .{ .imm = @bitCast(u32, @as(i32, -1)) },
60456164 });
60466165
......@@ -6059,24 +6178,20 @@ fn genInlineMemset(
60596178 if (x > math.maxInt(i32)) {
60606179 return self.fail("TODO inline memset for value immediate larger than 32bits", .{});
60616180 }
6062 // mov byte ptr [rbp + rax + stack_offset], imm
6063 const payload = try self.addExtra(Mir.ImmPair{
6064 .dest_off = 0,
6065 .operand = @truncate(u32, x),
6066 });
6181 // mov byte ptr [rbp + index_reg + stack_offset], imm
60676182 _ = try self.addInst(.{
60686183 .tag = .mov_mem_index_imm,
60696184 .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))) },
60716186 });
60726187 },
60736188 else => return self.fail("TODO inline memset for value of type {}", .{value}),
60746189 }
60756190
6076 // sub rax, 1
6191 // sub index_reg, 1
60776192 _ = try self.addInst(.{
60786193 .tag = .sub,
6079 .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }),
6194 .ops = Mir.Inst.Ops.encode(.{ .reg1 = index_reg }),
60806195 .data = .{ .imm = 1 },
60816196 });
60826197
......@@ -6243,6 +6358,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
62436358 },
62446359 .direct_load,
62456360 .got_load,
6361 .imports_load,
62466362 => {
62476363 switch (ty.zigTypeTag()) {
62486364 .Float => {
......@@ -6637,7 +6753,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
66376753 // TODO Is this the only condition for pointer dereference for memcpy?
66386754 const src: MCValue = blk: {
66396755 switch (src_ptr) {
6640 .got_load, .direct_load, .memory => {
6756 .got_load,
6757 .direct_load,
6758 .imports_load,
6759 .memory,
6760 => {
66416761 const reg = try self.register_manager.allocReg(null, gp);
66426762 try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);
66436763 _ = try self.addInst(.{
......@@ -6901,7 +7021,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
69017021 } else if (self.bin_file.cast(link.File.MachO)) |_| {
69027022 return MCValue{ .direct_load = local_sym_index };
69037023 } 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 };
69057025 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
69067026 return self.fail("TODO lower unnamed const in Plan9", .{});
69077027 } else {
......@@ -7066,11 +7186,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70667186 result.stack_align = 1;
70677187 return result;
70687188 },
7069 .Unspecified, .C => {
7189 .C => {
70707190 // Return values
70717191 if (ret_ty.zigTypeTag() == .NoReturn) {
70727192 result.return_value = .{ .unreach = {} };
70737193 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7194 // TODO: is this even possible for C calling convention?
70747195 result.return_value = .{ .none = {} };
70757196 } else {
70767197 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
......@@ -7078,84 +7199,106 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
70787199 assert(ret_ty.isError());
70797200 result.return_value = .{ .immediate = 0 };
70807201 } 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);
70827203 result.return_value = .{ .register = aliased_reg };
70837204 } else {
7084 // We simply make the return MCValue a stack offset. However, the actual value
7085 // for the offset will be populated later. We will also push the stack offset
7086 // value into .rdi register when we resolve the offset.
7205 // TODO: return argument cell should go first
70877206 result.return_value = .{ .stack_offset = 0 };
70887207 }
70897208 }
70907209
70917210 // Input params
7092 // First, split into args that can be passed via registers.
7093 // This will make it easier to then push the rest of args in reverse
7094 // order on the stack.
7095 var next_int_reg: usize = 0;
7096 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
7097 defer by_reg.deinit();
7098
7099 // If we want debug output, we store all args on stack for better liveness of args
7100 // in debugging contexts such as previewing the args in the debugger anywhere in
7101 // the procedure. Passing the args via registers can lead to reusing the register
7102 // for local ops thus clobbering the input arg forever.
7103 // This of course excludes C ABI calls.
7104 const omit_args_in_registers = blk: {
7105 if (cc == .C) break :blk false;
7106 switch (self.bin_file.options.optimize_mode) {
7107 .Debug => break :blk true,
7108 else => break :blk false,
7211 var next_stack_offset: u32 = switch (result.return_value) {
7212 .stack_offset => |off| @intCast(u32, off),
7213 else => 0,
7214 };
7215
7216 for (param_types) |ty, i| {
7217 assert(ty.hasRuntimeBits());
7218
7219 const classes: []const abi.Class = switch (self.target.os.tag) {
7220 .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)},
7221 else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*), .none),
7222 };
7223 if (classes.len > 1) {
7224 return self.fail("TODO handle multiple classes per type", .{});
7225 }
7226 switch (classes[0]) {
7227 .integer => blk: {
7228 if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough
7229 result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };
7230 continue;
7231 },
7232 .memory => {}, // fallthrough
7233 else => |class| return self.fail("TODO handle calling convention class {s}", .{
7234 @tagName(class),
7235 }),
7236 }
7237
7238 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7239 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7240 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7241 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7242 next_stack_offset = offset;
7243 }
7244
7245 // Align the stack to 16bytes before allocating shadow stack space (if any).
7246 const aligned_next_stack_offset = mem.alignForwardGeneric(u32, next_stack_offset, 16);
7247 const padding = aligned_next_stack_offset - next_stack_offset;
7248 if (padding > 0) {
7249 for (result.args) |*arg| {
7250 if (arg.isRegister()) continue;
7251 arg.stack_offset += @intCast(i32, padding);
71097252 }
7253 }
7254
7255 const shadow_stack_space: u32 = switch (self.target.os.tag) {
7256 .windows => @intCast(u32, 4 * @sizeOf(u64)),
7257 else => 0,
71107258 };
7111 if (!omit_args_in_registers) {
7112 for (param_types) |ty, i| {
7113 if (!ty.hasRuntimeBits()) continue;
7114 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7115 // For simplicity of codegen, slices and other types are always pushed onto the stack.
7116 // TODO: look into optimizing this by passing things as registers sometimes,
7117 // such as ptr and len of slices as separate registers.
7118 // TODO: also we need to honor the C ABI for relevant types rather than passing on
7119 // the stack here.
7120 const pass_in_reg = switch (ty.zigTypeTag()) {
7121 .Bool => true,
7122 .Int, .Enum => param_size <= 8,
7123 .Pointer => ty.ptrSize() != .Slice,
7124 .Optional => ty.isPtrLikeOptional(),
7125 else => false,
7126 };
7127 if (pass_in_reg) {
7128 if (next_int_reg >= c_abi_int_param_regs.len) break;
7129 try by_reg.putNoClobber(i, next_int_reg);
7130 next_int_reg += 1;
7131 }
7259
7260 // alignment padding | args ... | shadow stack space (if any) | ret addr | $rbp |
7261 result.stack_byte_count = aligned_next_stack_offset + shadow_stack_space;
7262 result.stack_align = 16;
7263 },
7264 .Unspecified => {
7265 // Return values
7266 if (ret_ty.zigTypeTag() == .NoReturn) {
7267 result.return_value = .{ .unreach = {} };
7268 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7269 result.return_value = .{ .none = {} };
7270 } else {
7271 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
7272 if (ret_ty_size == 0) {
7273 assert(ret_ty.isError());
7274 result.return_value = .{ .immediate = 0 };
7275 } else if (ret_ty_size <= 8) {
7276 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
7277 result.return_value = .{ .register = aliased_reg };
7278 } else {
7279 // We simply make the return MCValue a stack offset. However, the actual value
7280 // for the offset will be populated later. We will also push the stack offset
7281 // value into an appropriate register when we resolve the offset.
7282 result.return_value = .{ .stack_offset = 0 };
71327283 }
71337284 }
71347285
7286 // Input params
71357287 var next_stack_offset: u32 = switch (result.return_value) {
71367288 .stack_offset => |off| @intCast(u32, off),
71377289 else => 0,
71387290 };
7139 var count: usize = param_types.len;
7140 while (count > 0) : (count -= 1) {
7141 const i = count - 1;
7142 const ty = param_types[i];
7291
7292 for (param_types) |ty, i| {
71437293 if (!ty.hasRuntimeBits()) {
7144 assert(cc != .C);
71457294 result.args[i] = .{ .none = {} };
71467295 continue;
71477296 }
71487297 const param_size = @intCast(u32, ty.abiSize(self.target.*));
71497298 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7150 if (by_reg.get(i)) |int_reg| {
7151 const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size);
7152 result.args[i] = .{ .register = aliased_reg };
7153 next_int_reg += 1;
7154 } else {
7155 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7156 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7157 next_stack_offset = offset;
7158 }
7299 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7300 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7301 next_stack_offset = offset;
71597302 }
71607303
71617304 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
283283 const ops = emit.mir.instructions.items(.ops)[inst].decode();
284284 const payload = emit.mir.instructions.items(.data)[inst].payload;
285285 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);
287286 var disp: i32 = -@intCast(i32, save_reg_list.stack_end);
288 inline for (abi.callee_preserved_regs) |reg| {
289 if (reg_list.isSet(reg)) {
287 const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list);
288 const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*);
289 for (callee_preserved_regs) |reg| {
290 if (reg_list.isSet(callee_preserved_regs, reg)) {
290291 switch (tag) {
291292 .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
292293 .disp = @bitCast(u32, disp),
......@@ -614,14 +615,15 @@ inline fn immOpSize(u_imm: u32) u6 {
614615fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
615616 const ops = emit.mir.instructions.items(.ops)[inst].decode();
616617 const scale = ops.flags;
617 const imm = emit.mir.instructions.items(.data)[inst].imm;
618 // OP reg1, [reg2 + scale*rcx + imm32]
618 const payload = emit.mir.instructions.items(.data)[inst].payload;
619 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
620 // OP reg1, [reg2 + scale*index + imm32]
619621 const scale_index = ScaleIndex{
620622 .scale = scale,
621 .index = .rcx,
623 .index = index_reg_disp.index,
622624 };
623625 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
624 .disp = imm,
626 .disp = index_reg_disp.disp,
625627 .base = ops.reg2,
626628 .scale_index = scale_index,
627629 }), emit.code);
......@@ -630,22 +632,16 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
630632fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
631633 const ops = emit.mir.instructions.items(.ops)[inst].decode();
632634 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();
634637 const scale_index = ScaleIndex{
635638 .scale = scale,
636 .index = .rax,
639 .index = index_reg_disp.index,
637640 };
638 if (ops.reg2 == .none) {
639 // OP qword ptr [reg1 + scale*rax + 0], imm32
640 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{
641 .disp = 0,
642 .base = ops.reg1,
643 .scale_index = scale_index,
644 }), imm, emit.code);
645 }
646 // OP [reg1 + scale*rax + imm32], reg2
641 assert(ops.reg2 != .none);
642 // OP [reg1 + scale*index + imm32], reg2
647643 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
648 .disp = imm,
644 .disp = index_reg_disp.disp,
649645 .base = ops.reg1,
650646 .scale_index = scale_index,
651647 }), ops.reg2, emit.code);
......@@ -655,24 +651,24 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
655651 const ops = emit.mir.instructions.items(.ops)[inst].decode();
656652 const scale = ops.flags;
657653 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();
659655 const scale_index = ScaleIndex{
660656 .scale = scale,
661 .index = .rax,
657 .index = index_reg_disp_imm.index,
662658 };
663 // OP qword ptr [reg1 + scale*rax + imm32], imm32
659 // OP qword ptr [reg1 + scale*index + imm32], imm32
664660 return lowerToMiEnc(tag, RegisterOrMemory.mem(.qword_ptr, .{
665 .disp = imm_pair.dest_off,
661 .disp = index_reg_disp_imm.disp,
666662 .base = ops.reg1,
667663 .scale_index = scale_index,
668 }), imm_pair.operand, emit.code);
664 }), index_reg_disp_imm.imm, emit.code);
669665}
670666
671667fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
672668 const ops = emit.mir.instructions.items(.ops)[inst].decode();
673669 assert(ops.reg2 == .none);
674670 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();
676672 const ptr_size: Memory.PtrSize = switch (ops.flags) {
677673 0b00 => .byte_ptr,
678674 0b01 => .word_ptr,
......@@ -681,14 +677,14 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v
681677 };
682678 const scale_index = ScaleIndex{
683679 .scale = 0,
684 .index = .rax,
680 .index = index_reg_disp_imm.index,
685681 };
686 // OP ptr [reg1 + rax*1 + imm32], imm32
682 // OP ptr [reg1 + index + imm32], imm32
687683 return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{
688 .disp = imm_pair.dest_off,
684 .disp = index_reg_disp_imm.disp,
689685 .base = ops.reg1,
690686 .scale_index = scale_index,
691 }), imm_pair.operand, emit.code);
687 }), index_reg_disp_imm.imm, emit.code);
692688}
693689
694690fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -956,18 +952,19 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
956952 mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp);
957953 },
958954 0b10 => {
959 // lea reg, [rbp + rcx + imm32]
960 const imm = emit.mir.instructions.items(.data)[inst].imm;
955 // lea reg, [rbp + index + imm32]
956 const payload = emit.mir.instructions.items(.data)[inst].payload;
957 const index_reg_disp = emit.mir.extraData(Mir.IndexRegisterDisp, payload).data.decode();
961958 const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null;
962959 const scale_index = ScaleIndex{
963960 .scale = 0,
964 .index = .rcx,
961 .index = index_reg_disp.index,
965962 };
966963 return lowerToRmEnc(
967964 .lea,
968965 ops.reg1,
969966 RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
970 .disp = imm,
967 .disp = index_reg_disp.disp,
971968 .base = src_reg,
972969 .scale_index = scale_index,
973970 }),
......@@ -985,8 +982,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
985982 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
986983
987984 switch (ops.flags) {
988 0b00, 0b01 => {},
989 else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}),
985 0b00, 0b01, 0b10 => {},
986 else => return emit.fail("TODO unused LEA PIC variant 0b11", .{}),
990987 }
991988
992989 // lea reg1, [rip + reloc]
......@@ -1024,6 +1021,7 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10241021 .@"type" = switch (ops.flags) {
10251022 0b00 => .got,
10261023 0b01 => .direct,
1024 0b10 => .imports,
10271025 else => unreachable,
10281026 },
10291027 .target = .{ .sym_index = relocation.sym_index, .file = null },
......@@ -1031,7 +1029,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10311029 .addend = 0,
10321030 .pcrel = true,
10331031 .length = 2,
1034 .prev_vaddr = atom.getSymbol(coff_file).value,
10351032 });
10361033 } else {
10371034 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 {
11571154 .length = 2,
11581155 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
11591156 });
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 });
11601168 } else {
11611169 return emit.fail("TODO implement call_extern for linking backends different than MachO", .{});
11621170 }
......@@ -2241,6 +2249,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st
22412249 encoder.rex(.{
22422250 .w = wide,
22432251 .b = base.isExtended(),
2252 .x = if (mem_op.scale_index) |si| si.index.isExtended() else false,
22442253 });
22452254 }
22462255 opc.encode(encoder);
......@@ -2346,10 +2355,12 @@ fn lowerToMiXEnc(
23462355 encoder.rex(.{
23472356 .w = dst_mem.ptr_size == .qword_ptr,
23482357 .b = base.isExtended(),
2358 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
23492359 });
23502360 } else {
23512361 encoder.rex(.{
23522362 .w = dst_mem.ptr_size == .qword_ptr,
2363 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
23532364 });
23542365 }
23552366 opc.encode(encoder);
......@@ -2401,11 +2412,13 @@ fn lowerToRmEnc(
24012412 .w = setRexWRegister(reg),
24022413 .r = reg.isExtended(),
24032414 .b = base.isExtended(),
2415 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
24042416 });
24052417 } else {
24062418 encoder.rex(.{
24072419 .w = setRexWRegister(reg),
24082420 .r = reg.isExtended(),
2421 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
24092422 });
24102423 }
24112424 opc.encode(encoder);
......@@ -2446,11 +2459,13 @@ fn lowerToMrEnc(
24462459 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
24472460 .r = reg.isExtended(),
24482461 .b = base.isExtended(),
2462 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
24492463 });
24502464 } else {
24512465 encoder.rex(.{
24522466 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
24532467 .r = reg.isExtended(),
2468 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
24542469 });
24552470 }
24562471 opc.encode(encoder);
......@@ -2490,11 +2505,13 @@ fn lowerToRmiEnc(
24902505 .w = setRexWRegister(reg),
24912506 .r = reg.isExtended(),
24922507 .b = base.isExtended(),
2508 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
24932509 });
24942510 } else {
24952511 encoder.rex(.{
24962512 .w = setRexWRegister(reg),
24972513 .r = reg.isExtended(),
2514 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
24982515 });
24992516 }
25002517 opc.encode(encoder);
......@@ -2531,10 +2548,12 @@ fn lowerToVmEnc(
25312548 vex.rex(.{
25322549 .r = reg.isExtended(),
25332550 .b = base.isExtended(),
2551 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
25342552 });
25352553 } else {
25362554 vex.rex(.{
25372555 .r = reg.isExtended(),
2556 .x = if (src_mem.scale_index) |si| si.index.isExtended() else false,
25382557 });
25392558 }
25402559 encoder.vex(enc.prefix);
......@@ -2571,10 +2590,12 @@ fn lowerToMvEnc(
25712590 vex.rex(.{
25722591 .r = reg.isExtended(),
25732592 .b = base.isExtended(),
2593 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
25742594 });
25752595 } else {
25762596 vex.rex(.{
25772597 .r = reg.isExtended(),
2598 .x = if (dst_mem.scale_index) |si| si.index.isExtended() else false,
25782599 });
25792600 }
25802601 encoder.vex(enc.prefix);
src/arch/x86_64/Mir.zig+107-42
......@@ -44,25 +44,28 @@ pub const Inst = struct {
4444 /// 0b01 word ptr [reg1 + imm32], imm16
4545 /// 0b10 dword ptr [reg1 + imm32], imm32
4646 /// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)
47 /// Notes:
48 /// * Uses `ImmPair` as payload
4749 adc_mem_imm,
4850
49 /// form: reg1, [reg2 + scale*rcx + imm32]
51 /// form: reg1, [reg2 + scale*index + imm32]
5052 /// ops flags scale
5153 /// 0b00 1
5254 /// 0b01 2
5355 /// 0b10 4
5456 /// 0b11 8
57 /// Notes:
58 /// * Uses `IndexRegisterDisp` as payload
5559 adc_scale_src,
5660
57 /// form: [reg1 + scale*rax + imm32], reg2
58 /// form: [reg1 + scale*rax + 0], imm32
61 /// form: [reg1 + scale*index + imm32], reg2
5962 /// ops flags scale
6063 /// 0b00 1
6164 /// 0b01 2
6265 /// 0b10 4
6366 /// 0b11 8
6467 /// Notes:
65 /// * If reg2 is `none` then it means Data field `imm` is used as the immediate.
68 /// * Uses `IndexRegisterDisp` payload.
6669 adc_scale_dst,
6770
6871 /// form: [reg1 + scale*rax + imm32], imm32
......@@ -72,14 +75,16 @@ pub const Inst = struct {
7275 /// 0b10 4
7376 /// 0b11 8
7477 /// Notes:
75 /// * Data field `payload` points at `ImmPair`.
78 /// * Uses `IndexRegisterDispImm` payload.
7679 adc_scale_imm,
7780
7881 /// ops flags: form:
79 /// 0b00 byte ptr [reg1 + rax + imm32], imm8
80 /// 0b01 word ptr [reg1 + rax + imm32], imm16
81 /// 0b10 dword ptr [reg1 + rax + imm32], imm32
82 /// 0b11 qword ptr [reg1 + rax + imm32], imm32 (sign-extended to imm64)
82 /// 0b00 byte ptr [reg1 + index + imm32], imm8
83 /// 0b01 word ptr [reg1 + index + imm32], imm16
84 /// 0b10 dword ptr [reg1 + index + imm32], imm32
85 /// 0b11 qword ptr [reg1 + index + imm32], imm32 (sign-extended to imm64)
86 /// Notes:
87 /// * Uses `IndexRegisterDispImm` payload.
8388 adc_mem_index_imm,
8489
8590 // The following instructions all have the same encoding as `adc`.
......@@ -174,12 +179,15 @@ pub const Inst = struct {
174179 /// 0b00 reg1, [reg2 + imm32]
175180 /// 0b00 reg1, [ds:imm32]
176181 /// 0b01 reg1, [rip + imm32]
177 /// 0b10 reg1, [reg2 + rcx + imm32]
182 /// 0b10 reg1, [reg2 + index + imm32]
183 /// Notes:
184 /// * 0b10 uses `IndexRegisterDisp` payload
178185 lea,
179186
180187 /// ops flags: form:
181188 /// 0b00 reg1, [rip + reloc] // via GOT PIC
182189 /// 0b01 reg1, [rip + reloc] // direct load PIC
190 /// 0b10 reg1, [rip + reloc] // via imports table PIC
183191 /// Notes:
184192 /// * `Data` contains `relocation`
185193 lea_pic,
......@@ -460,46 +468,103 @@ pub const Inst = struct {
460468 }
461469};
462470
463pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type {
464 assert(registers.len <= @bitSizeOf(u32));
465 return struct {
466 bitset: RegBitSet = RegBitSet.initEmpty(),
471pub const IndexRegisterDisp = struct {
472 /// Index register to use with SIB-based encoding
473 index: u32,
467474
468 const RegBitSet = IntegerBitSet(registers.len);
469 const Self = @This();
475 /// Displacement value
476 disp: u32,
470477
471 fn getIndexForReg(reg: Reg) RegBitSet.MaskInt {
472 inline for (registers) |cpreg, i| {
473 if (reg.id() == cpreg.id()) return i;
474 }
475 unreachable; // register not in input register list!
476 }
478 pub fn encode(index: Register, disp: u32) IndexRegisterDisp {
479 return .{
480 .index = @enumToInt(index),
481 .disp = disp,
482 };
483 }
477484
478 pub fn push(self: *Self, reg: Reg) void {
479 const index = getIndexForReg(reg);
480 self.bitset.set(index);
481 }
485 pub fn decode(this: IndexRegisterDisp) struct {
486 index: Register,
487 disp: u32,
488 } {
489 return .{
490 .index = @intToEnum(Register, this.index),
491 .disp = this.disp,
492 };
493 }
494};
482495
483 pub fn isSet(self: Self, reg: Reg) bool {
484 const index = getIndexForReg(reg);
485 return self.bitset.isSet(index);
486 }
496/// TODO: would it be worth making `IndexRegisterDisp` and `IndexRegisterDispImm` a variable length list
497/// instead of having two structs, one a superset of the other one?
498pub const IndexRegisterDispImm = struct {
499 /// Index register to use with SIB-based encoding
500 index: u32,
487501
488 pub fn asInt(self: Self) u32 {
489 return self.bitset.mask;
490 }
502 /// Displacement value
503 disp: u32,
491504
492 pub fn fromInt(mask: u32) Self {
493 return .{
494 .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) },
495 };
496 }
505 /// Immediate
506 imm: u32,
507
508 pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm {
509 return .{
510 .index = @enumToInt(index),
511 .disp = disp,
512 .imm = imm,
513 };
514 }
497515
498 pub fn count(self: Self) u32 {
499 return @intCast(u32, self.bitset.count());
516 pub fn decode(this: IndexRegisterDispImm) struct {
517 index: Register,
518 disp: u32,
519 imm: u32,
520 } {
521 return .{
522 .index = @intToEnum(Register, this.index),
523 .disp = this.disp,
524 .imm = this.imm,
525 };
526 }
527};
528
529/// Used in conjunction with `SaveRegisterList` payload to transfer a list of used registers
530/// in a compact manner.
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);
500540 }
501 };
502}
541 unreachable; // register not in input register list!
542 }
543
544 pub fn push(self: *Self, registers: []const Register, reg: Register) void {
545 const index = getIndexForReg(registers, reg);
546 self.bitset.set(index);
547 }
548
549 pub fn isSet(self: Self, registers: []const Register, reg: Register) bool {
550 const index = getIndexForReg(registers, reg);
551 return self.bitset.isSet(index);
552 }
553
554 pub fn asInt(self: Self) u32 {
555 return self.bitset.mask;
556 }
557
558 pub fn fromInt(mask: u32) Self {
559 return .{
560 .bitset = BitSet{ .mask = @intCast(BitSet.MaskInt, mask) },
561 };
562 }
563
564 pub fn count(self: Self) u32 {
565 return @intCast(u32, self.bitset.count());
566 }
567};
503568
504569pub const SaveRegisterList = struct {
505570 /// 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 {
392392 }
393393}
394394
395/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
396/// for anything else but stack offset tracking therefore we exclude them from this set.
397pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
398/// These registers need to be preserved (saved on the stack) and restored by the caller before
399/// the caller relinquishes control to a subroutine via call instruction (or similar).
400/// In other words, these registers are free to use by the callee.
401pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
395pub const SysV = struct {
396 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
397 /// for anything else but stack offset tracking therefore we exclude them from this set.
398 pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
399 /// These registers need to be preserved (saved on the stack) and restored by the caller before
400 /// the caller relinquishes control to a subroutine via call instruction (or similar).
401 /// In other words, these registers are free to use by the callee.
402 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
402403
403pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
404pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
404 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
405 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
406};
407
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};
406452const sse_avx_regs = [_]Register{
407453 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
408454 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
409455};
410const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs;
411pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
456const allocatable_regs = gp_regs ++ sse_avx_regs;
457pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
412458
413459// Register classes
414460const RegisterBitSet = RegisterManager.RegisterBitSet;
......@@ -417,15 +463,15 @@ pub const RegisterClass = struct {
417463 var set = RegisterBitSet.initEmpty();
418464 set.setRangeValue(.{
419465 .start = 0,
420 .end = caller_preserved_regs.len + callee_preserved_regs.len,
466 .end = gp_regs.len,
421467 }, true);
422468 break :blk set;
423469 };
424470 pub const sse: RegisterBitSet = blk: {
425471 var set = RegisterBitSet.initEmpty();
426472 set.setRangeValue(.{
427 .start = caller_preserved_regs.len + callee_preserved_regs.len,
428 .end = allocatable_registers.len,
473 .start = gp_regs.len,
474 .end = allocatable_regs.len,
429475 }, true);
430476 break :blk set;
431477 };
src/codegen/llvm.zig+11-2
......@@ -3912,7 +3912,7 @@ pub const DeclGen = struct {
39123912 var b: usize = 0;
39133913 for (parent_ty.structFields().values()[0..field_index]) |field| {
39143914 if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
3915 b += field.ty.bitSize(target);
3915 b += @intCast(usize, field.ty.bitSize(target));
39163916 }
39173917 break :b b;
39183918 };
......@@ -9385,6 +9385,12 @@ pub const FuncGen = struct {
93859385 return self.builder.buildBitCast(truncated_int, elem_llvm_ty, "");
93869386 }
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
93889394 return self.builder.buildTrunc(shifted_value, elem_llvm_ty, "");
93899395 }
93909396
......@@ -9416,7 +9422,10 @@ pub const FuncGen = struct {
94169422 // Convert to equally-sized integer type in order to perform the bit
94179423 // operations on the value to store
94189424 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
94219430 var mask_val = value_bits_type.constAllOnes();
94229431 mask_val = mask_val.constZExt(containing_int_ty);
src/link.zig+4-1
......@@ -166,6 +166,9 @@ pub const Options = struct {
166166 version_script: ?[]const u8,
167167 soname: ?[]const u8,
168168 llvm_cpu_features: ?[*:0]const u8,
169 print_gc_sections: bool,
170 print_icf_sections: bool,
171 print_map: bool,
169172
170173 objects: []Compilation.LinkObject,
171174 framework_dirs: []const []const u8,
......@@ -476,7 +479,7 @@ pub const File = struct {
476479 log.debug("getGlobalSymbol '{s}'", .{name});
477480 switch (base.tag) {
478481 // zig fmt: off
479 .coff => unreachable,
482 .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name),
480483 .elf => unreachable,
481484 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name),
482485 .plan9 => unreachable,
src/link/Coff.zig+674-221
......@@ -30,7 +30,6 @@ const TypedValue = @import("../TypedValue.zig");
3030pub const base_tag: link.File.Tag = .coff;
3131
3232const msdos_stub = @embedFile("msdos-stub.bin");
33const N_DATA_DIRS: u5 = 16;
3433
3534/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
3635llvm_object: ?*LlvmObject = null,
......@@ -44,24 +43,33 @@ page_size: u32,
4443objects: std.ArrayListUnmanaged(Object) = .{},
4544
4645sections: std.MultiArrayList(Section) = .{},
47data_directories: [N_DATA_DIRS]coff.ImageDataDirectory,
46data_directories: [coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory,
4847
4948text_section_index: ?u16 = null,
5049got_section_index: ?u16 = null,
5150rdata_section_index: ?u16 = null,
5251data_section_index: ?u16 = null,
5352reloc_section_index: ?u16 = null,
53idata_section_index: ?u16 = null,
5454
5555locals: std.ArrayListUnmanaged(coff.Symbol) = .{},
56globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
56globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
57resolver: std.StringHashMapUnmanaged(u32) = .{},
58unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{},
5759
5860locals_free_list: std.ArrayListUnmanaged(u32) = .{},
61globals_free_list: std.ArrayListUnmanaged(u32) = .{},
5962
6063strtab: StringTable(.strtab) = .{},
6164strtab_offset: ?u32 = null,
6265
63got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
66got_entries: std.ArrayListUnmanaged(Entry) = .{},
6467got_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
6674/// Virtual address of the entry point procedure relative to image base.
6775entry_addr: ?u32 = null,
......@@ -109,17 +117,33 @@ relocs: RelocTable = .{},
109117/// this will be a table indexed by index into the list of Atoms.
110118base_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
112126pub const Reloc = struct {
113127 @"type": enum {
114128 got,
115129 direct,
130 imports,
116131 },
117132 target: SymbolWithLoc,
118133 offset: u32,
119134 addend: u32,
120135 pcrel: bool,
121136 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 }
123147};
124148
125149const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc));
......@@ -180,6 +204,16 @@ pub const SymbolWithLoc = struct {
180204
181205 // null means it's a synthetic global or Zig source.
182206 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 }
183217};
184218
185219/// When allocating, the ideal_capacity is calculated by
......@@ -234,7 +268,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
234268 },
235269 .ptr_width = ptr_width,
236270 .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),
238272 };
239273
240274 const use_llvm = build_options.have_llvm and options.use_llvm;
......@@ -269,10 +303,24 @@ pub fn deinit(self: *Coff) void {
269303
270304 self.locals.deinit(gpa);
271305 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);
272316 self.locals_free_list.deinit(gpa);
273317 self.strtab.deinit(gpa);
274318 self.got_entries.deinit(gpa);
275319 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);
276324 self.decls.deinit(gpa);
277325 self.atom_by_index_table.deinit(gpa);
278326
......@@ -305,145 +353,76 @@ fn populateMissingMetadata(self: *Coff) !void {
305353 assert(self.llvm_object == null);
306354 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
308369 if (self.text_section_index == null) {
309 self.text_section_index = @intCast(u16, self.sections.slice().len);
310370 const file_size = @intCast(u32, self.base.options.program_code_size_hint);
311 const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers
312 log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size });
313 var header = coff.SectionHeader{
314 .name = undefined,
315 .virtual_size = file_size,
316 .virtual_address = off,
317 .size_of_raw_data = file_size,
318 .pointer_to_raw_data = off,
319 .pointer_to_relocations = 0,
320 .pointer_to_linenumbers = 0,
321 .number_of_relocations = 0,
322 .number_of_linenumbers = 0,
323 .flags = .{
324 .CNT_CODE = 1,
325 .MEM_EXECUTE = 1,
326 .MEM_READ = 1,
327 },
328 };
329 try self.setSectionName(&header, ".text");
330 try self.sections.append(gpa, .{ .header = header });
371 self.text_section_index = try self.allocateSection(".text", file_size, .{
372 .CNT_CODE = 1,
373 .MEM_EXECUTE = 1,
374 .MEM_READ = 1,
375 });
331376 }
332377
333378 if (self.got_section_index == null) {
334 self.got_section_index = @intCast(u16, self.sections.slice().len);
335379 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
336 const off = self.findFreeSpace(file_size, self.page_size);
337 log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size });
338 var header = coff.SectionHeader{
339 .name = undefined,
340 .virtual_size = file_size,
341 .virtual_address = off,
342 .size_of_raw_data = file_size,
343 .pointer_to_raw_data = off,
344 .pointer_to_relocations = 0,
345 .pointer_to_linenumbers = 0,
346 .number_of_relocations = 0,
347 .number_of_linenumbers = 0,
348 .flags = .{
349 .CNT_INITIALIZED_DATA = 1,
350 .MEM_READ = 1,
351 },
352 };
353 try self.setSectionName(&header, ".got");
354 try self.sections.append(gpa, .{ .header = header });
380 self.got_section_index = try self.allocateSection(".got", file_size, .{
381 .CNT_INITIALIZED_DATA = 1,
382 .MEM_READ = 1,
383 });
355384 }
356385
357386 if (self.rdata_section_index == null) {
358 self.rdata_section_index = @intCast(u16, self.sections.slice().len);
359 const file_size: u32 = 1024;
360 const off = self.findFreeSpace(file_size, self.page_size);
361 log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size });
362 var header = coff.SectionHeader{
363 .name = undefined,
364 .virtual_size = file_size,
365 .virtual_address = off,
366 .size_of_raw_data = file_size,
367 .pointer_to_raw_data = off,
368 .pointer_to_relocations = 0,
369 .pointer_to_linenumbers = 0,
370 .number_of_relocations = 0,
371 .number_of_linenumbers = 0,
372 .flags = .{
373 .CNT_INITIALIZED_DATA = 1,
374 .MEM_READ = 1,
375 },
376 };
377 try self.setSectionName(&header, ".rdata");
378 try self.sections.append(gpa, .{ .header = header });
387 const file_size: u32 = self.page_size;
388 self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{
389 .CNT_INITIALIZED_DATA = 1,
390 .MEM_READ = 1,
391 });
379392 }
380393
381394 if (self.data_section_index == null) {
382 self.data_section_index = @intCast(u16, self.sections.slice().len);
383 const file_size: u32 = 1024;
384 const off = self.findFreeSpace(file_size, self.page_size);
385 log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size });
386 var header = coff.SectionHeader{
387 .name = undefined,
388 .virtual_size = file_size,
389 .virtual_address = off,
390 .size_of_raw_data = file_size,
391 .pointer_to_raw_data = off,
392 .pointer_to_relocations = 0,
393 .pointer_to_linenumbers = 0,
394 .number_of_relocations = 0,
395 .number_of_linenumbers = 0,
396 .flags = .{
397 .CNT_INITIALIZED_DATA = 1,
398 .MEM_READ = 1,
399 .MEM_WRITE = 1,
400 },
401 };
402 try self.setSectionName(&header, ".data");
403 try self.sections.append(gpa, .{ .header = header });
395 const file_size: u32 = self.page_size;
396 self.data_section_index = try self.allocateSection(".data", file_size, .{
397 .CNT_INITIALIZED_DATA = 1,
398 .MEM_READ = 1,
399 .MEM_WRITE = 1,
400 });
401 }
402
403 if (self.idata_section_index == null) {
404 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
405 self.idata_section_index = try self.allocateSection(".idata", file_size, .{
406 .CNT_INITIALIZED_DATA = 1,
407 .MEM_READ = 1,
408 });
404409 }
405410
406411 if (self.reloc_section_index == null) {
407 self.reloc_section_index = @intCast(u16, self.sections.slice().len);
408412 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation);
409 const off = self.findFreeSpace(file_size, self.page_size);
410 log.debug("found .reloc free space 0x{x} to 0x{x}", .{ off, off + file_size });
411 var header = coff.SectionHeader{
412 .name = undefined,
413 .virtual_size = file_size,
414 .virtual_address = off,
415 .size_of_raw_data = file_size,
416 .pointer_to_raw_data = off,
417 .pointer_to_relocations = 0,
418 .pointer_to_linenumbers = 0,
419 .number_of_relocations = 0,
420 .number_of_linenumbers = 0,
421 .flags = .{
422 .CNT_INITIALIZED_DATA = 1,
423 .MEM_PURGEABLE = 1,
424 .MEM_READ = 1,
425 },
426 };
427 try self.setSectionName(&header, ".reloc");
428 try self.sections.append(gpa, .{ .header = header });
413 self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{
414 .CNT_INITIALIZED_DATA = 1,
415 .MEM_DISCARDABLE = 1,
416 .MEM_READ = 1,
417 });
429418 }
430419
431420 if (self.strtab_offset == null) {
432 try self.strtab.buffer.append(gpa, 0);
433 self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1);
434 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() });
421 const file_size = @intCast(u32, self.strtab.len());
422 self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here
423 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size });
435424 }
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
447426 {
448427 // We need to find out what the max file offset is according to section headers.
449428 // 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 {
459438 }
460439}
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
462507pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
463508 if (self.llvm_object) |_| return;
464509 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
542587 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
543588 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
544589 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);
546610 }
611
612 header.virtual_size = @maximum(header.virtual_size, needed_size);
613 header.size_of_raw_data = needed_size;
547614 maybe_last_atom.* = atom;
548 // header.virtual_size = needed_size;
549 // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment);
550615 }
551616
552 // if (header.getAlignment().? < alignment) {
553 // header.setAlignment(alignment);
554 // }
555617 atom.size = new_atom_size;
556618 atom.alignment = alignment;
557619
......@@ -596,7 +658,7 @@ fn allocateSymbol(self: *Coff) !u32 {
596658 self.locals.items[index] = .{
597659 .name = [_]u8{0} ** 8,
598660 .value = 0,
599 .section_number = @intToEnum(coff.SectionNumber, 0),
661 .section_number = .UNDEFINED,
600662 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
601663 .storage_class = .NULL,
602664 .number_of_aux_symbols = 0,
......@@ -605,24 +667,71 @@ fn allocateSymbol(self: *Coff) !u32 {
605667 return index;
606668}
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
608694pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
609695 const gpa = self.base.allocator;
610696 try self.got_entries.ensureUnusedCapacity(gpa, 1);
697
611698 const index: u32 = blk: {
612699 if (self.got_entries_free_list.popOrNull()) |index| {
613700 log.debug(" (reusing GOT entry index {d})", .{index});
614 if (self.got_entries.getIndex(target)) |existing| {
615 assert(existing == index);
616 }
617701 break :blk index;
618702 } else {
619 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len});
620 const index = @intCast(u32, self.got_entries.keys().len);
621 self.got_entries.putAssumeCapacityNoClobber(target, 0);
703 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len});
704 const index = @intCast(u32, self.got_entries.items.len);
705 _ = self.got_entries.addOneAssumeCapacity();
622706 break :blk index;
623707 }
624708 };
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
626735 return index;
627736}
628737
......@@ -637,7 +746,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
637746
638747 try self.managed_atoms.append(gpa, atom);
639748 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
640 self.got_entries.getPtr(target).?.* = atom.sym_index;
641749
642750 const sym = atom.getSymbolPtr(self);
643751 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
......@@ -652,7 +760,6 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
652760 .addend = 0,
653761 .pcrel = false,
654762 .length = 3,
655 .prev_vaddr = sym.value,
656763 });
657764
658765 const target_sym = self.getSymbol(target);
......@@ -666,6 +773,27 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
666773 return atom;
667774}
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
669797fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
670798 const sym = atom.getSymbol(self);
671799 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 {
686814 const sym = atom.getSymbol(self);
687815 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
688816 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 });
690818 try self.base.file.?.pwriteAll(code, file_offset);
691819 try self.resolveRelocs(atom);
692820}
693821
694fn writeGotAtom(self: *Coff, atom: *Atom) !void {
822fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void {
695823 switch (self.ptr_width) {
696824 .p32 => {
697825 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
......@@ -704,6 +832,29 @@ fn writeGotAtom(self: *Coff, atom: *Atom) !void {
704832 }
705833}
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
707858fn resolveRelocs(self: *Coff, atom: *Atom) !void {
708859 const relocs = self.relocs.get(atom) orelse return;
709860 const source_sym = atom.getSymbol(self);
......@@ -713,29 +864,28 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void {
713864 log.debug("relocating '{s}'", .{atom.getName(self)});
714865
715866 for (relocs.items) |*reloc| {
716 const target_vaddr = switch (reloc.@"type") {
717 .got => blk: {
718 const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue;
719 break :blk got_atom.getSymbol(self).value;
720 },
721 .direct => self.getSymbol(reloc.target).value,
722 };
723 const target_vaddr_with_addend = target_vaddr + reloc.addend;
867 if (!reloc.dirty) continue;
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})", .{
728 reloc.offset,
873 log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{
874 source_sym.value + reloc.offset,
729875 target_vaddr_with_addend,
730876 self.getSymbolName(reloc.target),
731877 @tagName(reloc.@"type"),
878 file_offset + reloc.offset,
732879 });
733880
881 reloc.dirty = false;
882
734883 if (reloc.pcrel) {
735884 const source_vaddr = source_sym.value + reloc.offset;
736 const disp = target_vaddr_with_addend - source_vaddr - 4;
737 try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset);
738 return;
885 const disp =
886 @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4;
887 try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset);
888 continue;
739889 }
740890
741891 switch (self.ptr_width) {
......@@ -755,14 +905,15 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void {
755905 else => unreachable,
756906 },
757907 }
758
759 reloc.prev_vaddr = target_vaddr_with_addend;
760908 }
761909}
762910
763911fn freeAtom(self: *Coff, atom: *Atom) void {
764912 log.debug("freeAtom {*}", .{atom});
765913
914 // Remove any relocs and base relocs associated with this Atom
915 self.freeRelocationsForAtom(atom);
916
766917 const sym = atom.getSymbol(self);
767918 const sect_id = @enumToInt(sym.section_number) - 1;
768919 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
825976 const tracy = trace(@src());
826977 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
828984 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
829985 defer code_buffer.deinit();
830986
831 const decl_index = func.owner_decl;
832 const decl = module.declPtr(decl_index);
833987 const res = try codegen.generateFunction(
834988 &self.base,
835989 decl.srcLoc(),
......@@ -856,10 +1010,67 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
8561010}
8571011
8581012pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
859 _ = self;
860 _ = tv;
861 _ = decl_index;
862 @panic("TODO lowerUnnamedConst");
1013 const gpa = self.base.allocator;
1014 var code_buffer = std.ArrayList(u8).init(gpa);
1015 defer code_buffer.deinit();
1016
1017 const mod = self.base.options.module.?;
1018 const decl = mod.declPtr(decl_index);
1019
1020 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
1021 if (!gop.found_existing) {
1022 gop.value_ptr.* = .{};
1023 }
1024 const unnamed_consts = gop.value_ptr;
1025
1026 const atom = try gpa.create(Atom);
1027 errdefer gpa.destroy(atom);
1028 atom.* = Atom.empty;
1029
1030 atom.sym_index = try self.allocateSymbol();
1031 const sym = atom.getSymbolPtr(self);
1032 const sym_name = blk: {
1033 const decl_name = try decl.getFullyQualifiedName(mod);
1034 defer gpa.free(decl_name);
1035
1036 const index = unnamed_consts.items.len;
1037 break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
1038 };
1039 defer gpa.free(sym_name);
1040 try self.setSymbolName(sym, sym_name);
1041 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
1042
1043 try self.managed_atoms.append(gpa, atom);
1044 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
1045
1046 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{
1047 .parent_atom_index = atom.sym_index,
1048 });
1049 const code = switch (res) {
1050 .externally_managed => |x| x,
1051 .appended => code_buffer.items,
1052 .fail => |em| {
1053 decl.analysis = .codegen_failure;
1054 try mod.failed_decls.put(mod.gpa, decl_index, em);
1055 log.err("{s}", .{em.msg});
1056 return error.AnalysisFail;
1057 },
1058 };
1059
1060 const required_alignment = tv.ty.abiAlignment(self.base.options.target);
1061 atom.alignment = required_alignment;
1062 atom.size = @intCast(u32, code.len);
1063 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
1064 errdefer self.freeAtom(atom);
1065
1066 try unnamed_consts.append(gpa, atom);
1067
1068 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value });
1069 log.debug(" (required alignment 0x{x})", .{required_alignment});
1070
1071 try self.writeAtom(atom, code);
1072
1073 return atom.sym_index;
8631074}
8641075
8651076pub 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) !
8841095 }
8851096 }
8861097
1098 self.freeRelocationsForAtom(&decl.link.coff);
1099
8871100 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
8881101 defer code_buffer.deinit();
8891102
......@@ -892,7 +1105,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
8921105 .ty = decl.ty,
8931106 .val = decl_val,
8941107 }, &code_buffer, .none, .{
895 .parent_atom_index = 0,
1108 .parent_atom_index = decl.link.coff.sym_index,
8961109 });
8971110 const code = switch (res) {
8981111 .externally_managed => |x| x,
......@@ -970,8 +1183,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
9701183 if (vaddr != sym.value) {
9711184 sym.value = vaddr;
9721185 log.debug(" (updating GOT entry)", .{});
973 const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?;
974 try self.writeGotAtom(got_atom);
1186 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
1187 const got_atom = self.getGotAtomForSymbol(got_target).?;
1188 self.markRelocsDirtyByTarget(got_target);
1189 try self.writePtrWidthAtom(got_atom);
9751190 }
9761191 } else if (code_len < atom.size) {
9771192 self.shrinkAtom(atom, code_len);
......@@ -990,14 +1205,35 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
9901205 sym.value = vaddr;
9911206
9921207 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);
9941209 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);
9961212 }
9971213
1214 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
9981215 try self.writeAtom(atom, code);
9991216}
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
10011237pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
10021238 if (build_options.have_llvm) {
10031239 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 {
10111247 const kv = self.decls.fetchRemove(decl_index);
10121248 if (kv.?.value) |_| {
10131249 self.freeAtom(&decl.link.coff);
1250 self.freeUnnamedConsts(decl_index);
10141251 }
10151252
10161253 // 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 {
10211258
10221259 // Try freeing GOT atom if this decl had one
10231260 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| {
10251262 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
10271269 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
10281270 }
10291271
1030 self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0);
1272 self.locals.items[sym_index].section_number = .UNDEFINED;
10311273 _ = self.atom_by_index_table.remove(sym_index);
1274 log.debug(" adding local symbol index {d} to free list", .{sym_index});
10321275 decl.link.coff.sym_index = 0;
10331276 }
10341277}
......@@ -1154,44 +1397,49 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
11541397 const sym = self.getSymbolPtr(sym_loc);
11551398 const sym_name = self.getSymbolName(sym_loc);
11561399 log.debug("deleting export '{s}'", .{sym_name});
1157 assert(sym.storage_class == .EXTERNAL);
1400 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);
11581401 sym.* = .{
11591402 .name = [_]u8{0} ** 8,
11601403 .value = 0,
1161 .section_number = @intToEnum(coff.SectionNumber, 0),
1404 .section_number = .UNDEFINED,
11621405 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
11631406 .storage_class = .NULL,
11641407 .number_of_aux_symbols = 0,
11651408 };
11661409 self.locals_free_list.append(gpa, sym_index) catch {};
11671410
1168 if (self.globals.get(sym_name)) |global| blk: {
1169 if (global.sym_index != sym_index) break :blk;
1170 if (global.file != null) break :blk;
1171 const kv = self.globals.fetchSwapRemove(sym_name);
1172 gpa.free(kv.?.key);
1411 if (self.resolver.fetchRemove(sym_name)) |entry| {
1412 defer gpa.free(entry.key);
1413 self.globals_free_list.append(gpa, entry.value) catch {};
1414 self.globals.items[entry.value] = .{
1415 .sym_index = 0,
1416 .file = null,
1417 };
11731418 }
11741419}
11751420
11761421fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
11771422 const gpa = self.base.allocator;
11781423 const sym = self.getSymbol(current);
1179 _ = sym;
11801424 const sym_name = self.getSymbolName(current);
11811425
1182 const name = try gpa.dupe(u8, sym_name);
1183 const global_index = @intCast(u32, self.globals.values().len);
1184 _ = global_index;
1185 const gop = try self.globals.getOrPut(gpa, name);
1186 defer if (gop.found_existing) gpa.free(name);
1187
1188 if (!gop.found_existing) {
1189 gop.value_ptr.* = current;
1190 // TODO undef + tentative
1426 const global_index = self.resolver.get(sym_name) orelse {
1427 const name = try gpa.dupe(u8, sym_name);
1428 const global_index = try self.allocateGlobal();
1429 self.globals.items[global_index] = current;
1430 try self.resolver.putNoClobber(gpa, name, global_index);
1431 if (sym.section_number == .UNDEFINED) {
1432 try self.unresolved.putNoClobber(gpa, global_index, false);
1433 }
11911434 return;
1192 }
1435 };
11931436
11941437 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;
11951443}
11961444
11971445pub 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
12271475 sub_prog_node.activate();
12281476 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
12301489 if (build_options.enable_logging) {
12311490 self.logSymtab();
12321491 }
......@@ -1237,6 +1496,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
12371496 try self.resolveRelocs(atom.*);
12381497 }
12391498 }
1499 try self.writeImportTable();
12401500 try self.writeBaseRelocations();
12411501
12421502 if (self.getEntryPoint()) |entry_sym_loc| {
......@@ -1262,10 +1522,47 @@ pub fn getDeclVAddr(
12621522 decl_index: Module.Decl.Index,
12631523 reloc_info: link.File.RelocInfo,
12641524) !u64 {
1265 _ = self;
1266 _ = decl_index;
1267 _ = reloc_info;
1268 @panic("TODO getDeclVAddr");
1525 const mod = self.base.options.module.?;
1526 const decl = mod.declPtr(decl_index);
1527
1528 assert(self.llvm_object == null);
1529 assert(decl.link.coff.sym_index != 0);
1530
1531 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
1532 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
1533 try atom.addRelocation(self, .{
1534 .@"type" = .direct,
1535 .target = target,
1536 .offset = @intCast(u32, reloc_info.offset),
1537 .addend = reloc_info.addend,
1538 .pcrel = false,
1539 .length = 3,
1540 });
1541 try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset));
1542
1543 return 0;
1544}
1545
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;
12691566}
12701567
12711568pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
......@@ -1342,7 +1639,25 @@ fn writeBaseRelocations(self: *Coff) !void {
13421639 const header = &self.sections.items(.header)[self.reloc_section_index.?];
13431640 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
13441641 const needed_size = @intCast(u32, buffer.items.len);
1345 assert(needed_size < sect_capacity); // TODO expand .reloc section
1642 if (needed_size > sect_capacity) {
1643 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);
1644 log.debug("writing {s} at 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
1645 self.getSectionName(header),
1646 header.pointer_to_raw_data,
1647 header.pointer_to_raw_data + needed_size,
1648 new_offset,
1649 new_offset + needed_size,
1650 });
1651 header.pointer_to_raw_data = new_offset;
1652
1653 const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);
1654 if (needed_size > sect_vm_capacity) {
1655 // TODO: we want to enforce .reloc after every alloc section.
1656 try self.growSectionVM(self.reloc_section_index.?, needed_size);
1657 }
1658 }
1659 header.virtual_size = @maximum(header.virtual_size, needed_size);
1660 header.size_of_raw_data = needed_size;
13461661
13471662 try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data);
13481663
......@@ -1352,17 +1667,111 @@ fn writeBaseRelocations(self: *Coff) !void {
13521667 };
13531668}
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
13551753fn writeStrtab(self: *Coff) !void {
1754 if (self.strtab_offset == null) return;
1755
13561756 const allocated_size = self.allocatedSize(self.strtab_offset.?);
13571757 const needed_size = @intCast(u32, self.strtab.len());
13581758
13591759 if (needed_size > allocated_size) {
13601760 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)));
13621762 }
13631763
13641764 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.?);
13661775}
13671776
13681777fn writeSectionHeaders(self: *Coff) !void {
......@@ -1527,14 +1936,15 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
15271936}
15281937
15291938fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
1530 const headers_size = self.getSizeOfHeaders();
1939 const headers_size = @maximum(self.getSizeOfHeaders(), self.page_size);
15311940 if (start < headers_size)
15321941 return headers_size;
15331942
1534 const end = start + size;
1943 const end = start + padToIdeal(size);
15351944
15361945 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);
15381948 const test_end = off + increased_size;
15391949 if (end > off and start < test_end) {
15401950 return test_end;
......@@ -1542,7 +1952,8 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
15421952 }
15431953
15441954 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);
15461957 const test_end = header.pointer_to_raw_data + increased_size;
15471958 if (end > header.pointer_to_raw_data and start < test_end) {
15481959 return test_end;
......@@ -1552,7 +1963,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
15521963 return null;
15531964}
15541965
1555pub fn allocatedSize(self: *Coff, start: u32) u32 {
1966fn allocatedSize(self: *Coff, start: u32) u32 {
15561967 if (start == 0)
15571968 return 0;
15581969 var min_pos: u32 = std.math.maxInt(u32);
......@@ -1566,7 +1977,7 @@ pub fn allocatedSize(self: *Coff, start: u32) u32 {
15661977 return min_pos - start;
15671978}
15681979
1569pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
1980fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
15701981 var start: u32 = 0;
15711982 while (self.detectAllocCollision(start, object_size)) |item_end| {
15721983 start = mem.alignForwardGeneric(u32, item_end, min_alignment);
......@@ -1574,6 +1985,17 @@ pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
15741985 return start;
15751986}
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
15771999inline fn getSizeOfHeaders(self: Coff) u32 {
15782000 const msdos_hdr_size = msdos_stub.len + 4;
15792001 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() +
......@@ -1614,23 +2036,24 @@ inline fn getSizeOfImage(self: Coff) u32 {
16142036
16152037/// Returns symbol location corresponding to the set entrypoint (if any).
16162038pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {
1617 const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete
1618 return self.globals.get(entry_name);
2039 const entry_name = self.base.options.entry orelse "wWinMainCRTStartup"; // TODO this is incomplete
2040 const global_index = self.resolver.get(entry_name) orelse return null;
2041 return self.globals.items[global_index];
16192042}
16202043
1621/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
2044/// Returns pointer-to-symbol described by `sym_loc` descriptor.
16222045pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol {
16232046 assert(sym_loc.file == null); // TODO linking object files
16242047 return &self.locals.items[sym_loc.sym_index];
16252048}
16262049
1627/// Returns symbol described by `sym_with_loc` descriptor.
2050/// Returns symbol described by `sym_loc` descriptor.
16282051pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol {
16292052 assert(sym_loc.file == null); // TODO linking object files
16302053 return &self.locals.items[sym_loc.sym_index];
16312054}
16322055
1633/// Returns name of the symbol described by `sym_with_loc` descriptor.
2056/// Returns name of the symbol described by `sym_loc` descriptor.
16342057pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {
16352058 assert(sym_loc.file == null); // TODO linking object files
16362059 const sym = self.getSymbol(sym_loc);
......@@ -1638,18 +2061,27 @@ pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {
16382061 return self.strtab.get(offset).?;
16392062}
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.
16422065/// Returns null on failure.
16432066pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
16442067 assert(sym_loc.file == null); // TODO linking with object files
16452068 return self.atom_by_index_table.get(sym_loc.sym_index);
16462069}
16472070
1648/// Returns GOT atom that references `sym_with_loc` if one exists.
2071/// Returns GOT atom that references `sym_loc` if one exists.
16492072/// Returns null otherwise.
16502073pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1651 const got_index = self.got_entries.get(sym_loc) orelse return null;
1652 return self.atom_by_index_table.get(got_index);
2074 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
2075 const got_entry = self.got_entries.items[got_index];
2076 return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2077}
2078
2079/// Returns import atom that references `sym_loc` if one exists.
2080/// Returns null otherwise.
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 });
16532085}
16542086
16552087fn 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
16632095 mem.set(u8, header.name[name_offset.len..], 0);
16642096}
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
16662106fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void {
16672107 if (name.len <= 8) {
16682108 mem.copy(u8, &symbol.name, name);
......@@ -1725,29 +2165,42 @@ fn logSymtab(self: *Coff) void {
17252165 }
17262166
17272167 log.debug("globals table:", .{});
1728 for (self.globals.keys()) |name, id| {
1729 const value = self.globals.values()[id];
1730 log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file });
2168 for (self.globals.items) |sym_loc| {
2169 const sym_name = self.getSymbolName(sym_loc);
2170 log.debug(" {s} => %{d} in object({?d})", .{ sym_name, sym_loc.sym_index, sym_loc.file });
17312171 }
17322172
17332173 log.debug("GOT entries:", .{});
1734 for (self.got_entries.keys()) |target, i| {
1735 const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null });
1736 const target_sym = self.getSymbol(target);
2174 for (self.got_entries.items) |entry, i| {
2175 const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
2176 const target_sym = self.getSymbol(entry.target);
17372177 if (target_sym.section_number == .UNDEFINED) {
17382178 log.debug(" {d}@{x} => import('{s}')", .{
17392179 i,
17402180 got_sym.value,
1741 self.getSymbolName(target),
2181 self.getSymbolName(entry.target),
17422182 });
17432183 } else {
17442184 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
17452185 i,
17462186 got_sym.value,
1747 target.sym_index,
1748 target.file,
2187 entry.target.sym_index,
2188 entry.target.file,
17492189 logSymAttributes(target_sym, &buf),
17502190 });
17512191 }
17522192 }
17532193}
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");
44const coff = std.coff;
55const log = std.log.scoped(.link);
66
7const Allocator = std.mem.Allocator;
8
97const Coff = @import("../Coff.zig");
108const Reloc = Coff.Reloc;
119const SymbolWithLoc = Coff.SymbolWithLoc;
......@@ -41,11 +39,6 @@ pub const empty = Atom{
4139 .next = null,
4240};
4341
44pub fn deinit(self: *Atom, gpa: Allocator) void {
45 _ = self;
46 _ = gpa;
47}
48
4942/// Returns symbol referencing this atom.
5043pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
5144 return coff_file.getSymbol(.{
......@@ -118,3 +111,13 @@ pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {
118111 }
119112 try gop.value_ptr.append(gpa, offset);
120113}
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(
861861 },
862862 .wasm => {
863863 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);
865866 },
866867 else => unreachable,
867868 }
......@@ -972,23 +973,21 @@ pub fn commitDeclState(
972973 },
973974 .wasm => {
974975 const wasm_file = file.cast(File.Wasm).?;
975 const segment_index = try wasm_file.getDebugLineIndex();
976 const segment = &wasm_file.segments.items[segment_index];
977 const debug_line = &wasm_file.debug_line;
978 if (needed_size != segment.size) {
976 const atom = wasm_file.debug_line_atom.?;
977 const debug_line = &atom.code;
978 const segment_size = debug_line.items.len;
979 if (needed_size != segment_size) {
979980 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
980 if (needed_size > segment.size) {
981 log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size});
981 if (needed_size > segment_size) {
982 log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
982983 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);
984985 }
985 segment.size = needed_size;
986986 debug_line.items.len = needed_size;
987987 }
988 const offset = segment.offset + src_fn.off;
989988 writeDbgLineNopsBuffered(
990989 debug_line.items,
991 offset,
990 src_fn.off,
992991 prev_padding_size,
993992 dbg_line_buffer.items,
994993 next_padding_size,
......@@ -1146,10 +1145,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
11461145 },
11471146 .wasm => {
11481147 const wasm_file = file.cast(File.Wasm).?;
1149 const segment_index = try wasm_file.getDebugInfoIndex();
1150 const segment = &wasm_file.segments.items[segment_index];
1151 const offset = segment.offset + atom.off;
1152 try writeDbgInfoNopsToArrayList(gpa, &wasm_file.debug_info, offset, 0, &.{0}, atom.len, false);
1148 const debug_info = &wasm_file.debug_info_atom.?.code;
1149 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
11531150 },
11541151 else => unreachable,
11551152 }
......@@ -1276,27 +1273,25 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12761273 },
12771274 .wasm => {
12781275 const wasm_file = file.cast(File.Wasm).?;
1279 const segment_index = try wasm_file.getDebugInfoIndex();
1280 const segment = &wasm_file.segments.items[segment_index];
1281 const debug_info = &wasm_file.debug_info;
1282 if (needed_size != segment.size) {
1276 const info_atom = wasm_file.debug_info_atom.?;
1277 const debug_info = &info_atom.code;
1278 const segment_size = debug_info.items.len;
1279 if (needed_size != segment_size) {
12831280 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
1284 if (needed_size > segment.size) {
1285 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size});
1281 if (needed_size > segment_size) {
1282 log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
12861283 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);
12881285 }
1289 segment.size = needed_size;
12901286 debug_info.items.len = needed_size;
12911287 }
1292 const offset = segment.offset + atom.off;
12931288 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,
12951290 });
12961291 try writeDbgInfoNopsToArrayList(
12971292 gpa,
12981293 debug_info,
1299 offset,
1294 atom.off,
13001295 prev_padding_size,
13011296 dbg_info_buf,
13021297 next_padding_size,
......@@ -1337,10 +1332,9 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
13371332 },
13381333 .wasm => {
13391334 const wasm_file = file.cast(File.Wasm).?;
1340 const segment_index = wasm_file.getDebugLineIndex() catch unreachable;
1341 const segment = wasm_file.segments.items[segment_index];
1342 const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
1343 mem.copy(u8, wasm_file.debug_line.items[offset..], &data);
1335 const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
1336 const atom = wasm_file.debug_line_atom.?;
1337 mem.copy(u8, atom.code.items[offset..], &data);
13441338 },
13451339 else => unreachable,
13461340 }
......@@ -1576,8 +1570,9 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
15761570 },
15771571 .wasm => {
15781572 const wasm_file = file.cast(File.Wasm).?;
1579 try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size);
1580 mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf);
1573 const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code;
1574 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);
1575 mem.copy(u8, debug_abbrev.items, &abbrev_buf);
15811576 },
15821577 else => unreachable,
15831578 }
......@@ -1687,7 +1682,8 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6
16871682 },
16881683 .wasm => {
16891684 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);
16911687 },
16921688 else => unreachable,
16931689 }
......@@ -2016,8 +2012,9 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void {
20162012 },
20172013 .wasm => {
20182014 const wasm_file = file.cast(File.Wasm).?;
2019 try wasm_file.debug_aranges.resize(wasm_file.base.allocator, needed_size);
2020 mem.copy(u8, wasm_file.debug_aranges.items, di_buf.items);
2015 const debug_ranges = &wasm_file.debug_ranges_atom.?.code;
2016 try debug_ranges.resize(wasm_file.base.allocator, needed_size);
2017 mem.copy(u8, debug_ranges.items, di_buf.items);
20212018 },
20222019 else => unreachable,
20232020 }
......@@ -2139,7 +2136,8 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void {
21392136 },
21402137 .wasm => {
21412138 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);
21432141 },
21442142 else => unreachable,
21452143 }
......@@ -2287,7 +2285,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void {
22872285 },
22882286 .wasm => {
22892287 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);
22912290 },
22922291 else => unreachable,
22932292 }
src/link/Elf.zig+12
......@@ -1482,6 +1482,18 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
14821482 try argv.append("--gc-sections");
14831483 }
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
14851497 if (self.base.options.eh_frame_hdr) {
14861498 try argv.append("--eh-frame-hdr");
14871499 }
src/link/MachO.zig+34-17
......@@ -793,11 +793,13 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
793793 }
794794 } else {
795795 const sub_path = self.base.options.emit.?.sub_path;
796 self.base.file = try directory.handle.createFile(sub_path, .{
797 .truncate = true,
798 .read = true,
799 .mode = link.determineMode(self.base.options),
800 });
796 if (self.base.file == null) {
797 self.base.file = try directory.handle.createFile(sub_path, .{
798 .truncate = true,
799 .read = true,
800 .mode = link.determineMode(self.base.options),
801 });
802 }
801803 // Index 0 is always a null symbol.
802804 try self.locals.append(gpa, .{
803805 .n_strx = 0,
......@@ -1155,6 +1157,29 @@ fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node)
11551157 var ncmds: u32 = 0;
11561158
11571159 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
11581183 try writeDylinkerLC(&ncmds, lc_writer);
11591184 try self.writeMainLC(&ncmds, lc_writer);
11601185 try self.writeDylibIdLC(&ncmds, lc_writer);
......@@ -1435,7 +1460,6 @@ fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
14351460
14361461 if (force_load) {
14371462 defer archive.deinit(gpa);
1438 defer file.close();
14391463 // Get all offsets from the ToC
14401464 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
14411465 defer offsets.deinit();
......@@ -3086,15 +3110,6 @@ pub fn deinit(self: *MachO) void {
30863110 self.atom_by_index_table.deinit(gpa);
30873111}
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
30983113fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
30993114 log.debug("freeAtom {*}", .{atom});
31003115 if (!owns_atom) {
......@@ -5698,8 +5713,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
56985713 else => unreachable,
56995714 }
57005715
5701 if (self.getSectionByName("__DATA", "__thread_vars")) |_| {
5702 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
5716 if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
5717 if (self.sections.items(.header)[sect_id].size > 0) {
5718 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
5719 }
57035720 }
57045721
57055722 header.ncmds = ncmds;
src/link/MachO/Archive.zig+1
......@@ -88,6 +88,7 @@ const ar_hdr = extern struct {
8888};
8989
9090pub fn deinit(self: *Archive, allocator: Allocator) void {
91 self.file.close();
9192 for (self.toc.keys()) |*key| {
9293 allocator.free(key.*);
9394 }
src/link/MachO/DebugSymbols.zig+1
......@@ -306,6 +306,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
306306}
307307
308308pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
309 self.file.close();
309310 self.segments.deinit(allocator);
310311 self.sections.deinit(allocator);
311312 self.dwarf.deinit();
src/link/Wasm.zig+294-123
......@@ -67,6 +67,18 @@ code_section_index: ?u32 = null,
6767debug_info_index: ?u32 = null,
6868/// The index of the segment representing the custom '.debug_line' section.
6969debug_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,
7082/// The count of imported functions. This number will be appended
7183/// to the function indexes as their index starts at the lowest non-extern function.
7284imported_functions_count: u32 = 0,
......@@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{},
8395segments: std.ArrayListUnmanaged(Segment) = .{},
8496/// Maps a data segment key (such as .rodata) to the index into `segments`.
8597data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},
86/// A list of `types.Segment` which provide meta data
87/// about a data symbol such as its name
88segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
98/// A table of `types.Segment` which provide meta data
99/// about a data symbol such as its name where the key is
100/// the segment index, which can be found from `data_segments`
101segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{},
89102/// Deduplicated string table for strings used by symbols, imports and exports.
90103string_table: StringTable = .{},
91104/// Debug information for wasm
92105dwarf: ?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
104107// Output sections
105108/// Output type section
106109func_types: std.ArrayListUnmanaged(wasm.Type) = .{},
......@@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
156159/// The actual table is populated during `flush`.
157160error_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
159175pub const Segment = struct {
160176 alignment: u32,
161177 size: u32,
......@@ -209,6 +225,18 @@ pub const SymbolLoc = struct {
209225 }
210226 return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name);
211227 }
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 }
212240};
213241
214242/// Generic string table that duplicates strings
......@@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
335363 };
336364 }
337365
366 try wasm_bin.initDebugSections();
338367 return wasm_bin;
339368}
340369
......@@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
363392 return self;
364393}
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
366413fn parseInputFiles(self: *Wasm, files: []const []const u8) !void {
367414 for (files) |path| {
368415 if (try self.parseObjectFile(path)) continue;
......@@ -644,16 +691,14 @@ pub fn deinit(self: *Wasm) void {
644691 for (self.func_types.items) |*func_type| {
645692 func_type.deinit(gpa);
646693 }
647 for (self.segment_info.items) |segment_info| {
694 for (self.segment_info.values()) |segment_info| {
648695 gpa.free(segment_info.name);
649696 }
650697 for (self.objects.items) |*object| {
651 object.file.?.close();
652698 object.deinit(gpa);
653699 }
654700
655701 for (self.archives.items) |*archive| {
656 archive.file.close();
657702 archive.deinit(gpa);
658703 }
659704
......@@ -692,11 +737,6 @@ pub fn deinit(self: *Wasm) void {
692737 if (self.dwarf) |*dwarf| {
693738 dwarf.deinit();
694739 }
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);
700740}
701741
702742pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void {
......@@ -1337,16 +1377,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
13371377 const index = gop.value_ptr.*;
13381378 self.segments.items[index].size += atom.size;
13391379
1340 // segment indexes can be off by 1 due to also containing a segment
1341 // for the code section, so we must check if the existing segment
1342 // is larger than that of the code section, and substract the index by 1 in such case.
1343 var info_add = if (self.code_section_index) |idx| blk: {
1344 if (idx < index) break :blk @as(u32, 1);
1345 break :blk 0;
1346 } else @as(u32, 0);
1347 if (self.debug_info_index != null) info_add += 1;
1348 if (self.debug_line_index != null) info_add += 1;
1349 symbol.index = index - info_add;
1380 symbol.index = @intCast(u32, self.segment_info.getIndex(index).?);
13501381 // segment info already exists, so free its memory
13511382 self.base.allocator.free(segment_name);
13521383 break :result index;
......@@ -1359,8 +1390,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
13591390 });
13601391 gop.value_ptr.* = index;
13611392
1362 const info_index = @intCast(u32, self.segment_info.items.len);
1363 try self.segment_info.append(self.base.allocator, segment_info);
1393 const info_index = @intCast(u32, self.segment_info.count());
1394 try self.segment_info.put(self.base.allocator, index, segment_info);
13641395 symbol.index = info_index;
13651396 break :result index;
13661397 }
......@@ -1370,18 +1401,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
13701401 const segment: *Segment = &self.segments.items[final_index];
13711402 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| {
13741411 last.*.next = atom;
13751412 atom.prev = last.*;
13761413 last.* = atom;
13771414 } else {
1378 try self.atoms.putNoClobber(self.base.allocator, final_index, atom);
1415 try self.atoms.putNoClobber(self.base.allocator, index, atom);
13791416 }
13801417}
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
13821448fn allocateAtoms(self: *Wasm) !void {
13831449 // first sort the data segments
13841450 try sortDataSegments(self);
1451 try allocateDebugAtoms(self);
13851452
13861453 var it = self.atoms.iterator();
13871454 while (it.next()) |entry| {
......@@ -1399,7 +1466,7 @@ fn allocateAtoms(self: *Wasm) !void {
13991466 atom.size,
14001467 });
14011468 offset += atom.size;
1402 self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers
1469 try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers
14031470 atom = atom.next orelse break;
14041471 }
14051472 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
......@@ -1753,7 +1820,7 @@ fn setupMemory(self: *Wasm) !void {
17531820/// From a given object's index and the index of the segment, returns the corresponding
17541821/// index of the segment within the final data section. When the segment does not yet
17551822/// 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 {
17571824 const object: Object = self.objects.items[object_index];
17581825 const relocatable_data = object.relocatable_data[relocatable_index];
17591826 const index = @intCast(u32, self.segments.items.len);
......@@ -1765,27 +1832,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32
17651832 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment));
17661833 if (!result.found_existing) {
17671834 result.value_ptr.* = index;
1768 try self.segments.append(self.base.allocator, .{
1769 .alignment = 1,
1770 .size = 0,
1771 .offset = 0,
1772 });
1835 try self.appendDummySegment();
17731836 return index;
17741837 } else return result.value_ptr.*;
17751838 },
17761839 .code => return self.code_section_index orelse blk: {
17771840 self.code_section_index = index;
1778 try self.segments.append(self.base.allocator, .{
1779 .alignment = 1,
1780 .size = 0,
1781 .offset = 0,
1782 });
1841 try self.appendDummySegment();
17831842 break :blk index;
17841843 },
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 },
17861900 }
17871901}
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
17891912/// Returns the symbol index of the error name table.
17901913///
17911914/// When the symbol does not yet exist, it will create a new one instead.
......@@ -1903,50 +2026,52 @@ fn populateErrorNameTable(self: *Wasm) !void {
19032026 try self.parseAtom(names_atom, .{ .data = .read_only });
19042027}
19052028
1906pub fn getDebugInfoIndex(self: *Wasm) !u32 {
1907 assert(self.dwarf != null);
1908 return self.debug_info_index orelse {
1909 self.debug_info_index = @intCast(u32, self.segments.items.len);
1910 const segment = try self.segments.addOne(self.base.allocator);
1911 segment.* = .{
1912 .size = 0,
1913 .offset = 0,
1914 // debug sections always have alignment '1'
1915 .alignment = 1,
1916 };
1917 return self.debug_info_index.?;
2029/// From a given index variable, creates a new debug section.
2030/// This initializes the index, appends a new segment,
2031/// and finally, creates a managed `Atom`.
2032pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom {
2033 const new_index = @intCast(u32, self.segments.items.len);
2034 index.* = new_index;
2035 try self.appendDummySegment();
2036 // _ = index;
2037
2038 const sym_index = self.symbols_free_list.popOrNull() orelse idx: {
2039 const tmp_index = @intCast(u32, self.symbols.items.len);
2040 _ = try self.symbols.addOne(self.base.allocator);
2041 break :idx tmp_index;
19182042 };
1919}
1920
1921pub fn getDebugLineIndex(self: *Wasm) !u32 {
1922 assert(self.dwarf != null);
1923 return self.debug_line_index orelse {
1924 self.debug_line_index = @intCast(u32, self.segments.items.len);
1925 const segment = try self.segments.addOne(self.base.allocator);
1926 segment.* = .{
1927 .size = 0,
1928 .offset = 0,
1929 .alignment = 1,
1930 };
1931 return self.debug_line_index.?;
2043 self.symbols.items[sym_index] = .{
2044 .tag = .section,
2045 .name = try self.string_table.put(self.base.allocator, name),
2046 .index = 0,
2047 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
19322048 };
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;
19332057}
19342058
19352059fn resetState(self: *Wasm) void {
1936 for (self.segment_info.items) |*segment_info| {
2060 for (self.segment_info.values()) |segment_info| {
19372061 self.base.allocator.free(segment_info.name);
19382062 }
1939 const mod = self.base.options.module.?;
1940 var decl_it = self.decls.keyIterator();
1941 while (decl_it.next()) |decl_index_ptr| {
1942 const decl = mod.declPtr(decl_index_ptr.*);
1943 const atom = &decl.link.wasm;
1944 atom.next = null;
1945 atom.prev = null;
2063 if (self.base.options.module) |mod| {
2064 var decl_it = self.decls.keyIterator();
2065 while (decl_it.next()) |decl_index_ptr| {
2066 const decl = mod.declPtr(decl_index_ptr.*);
2067 const atom = &decl.link.wasm;
2068 atom.next = null;
2069 atom.prev = null;
19462070
1947 for (atom.locals.items) |*local_atom| {
1948 local_atom.next = null;
1949 local_atom.prev = null;
2071 for (atom.locals.items) |*local_atom| {
2072 local_atom.next = null;
2073 local_atom.prev = null;
2074 }
19502075 }
19512076 }
19522077 self.functions.clearRetainingCapacity();
......@@ -1959,6 +2084,12 @@ fn resetState(self: *Wasm) void {
19592084 self.code_section_index = null;
19602085 self.debug_info_index = null;
19612086 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;
19622093}
19632094
19642095pub 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
20362167 defer self.resetState();
20372168 try self.setupStart();
20382169 try self.setupImports();
2039 const mod = self.base.options.module.?;
2040 var decl_it = self.decls.keyIterator();
2041 while (decl_it.next()) |decl_index_ptr| {
2042 const decl = mod.declPtr(decl_index_ptr.*);
2043 if (decl.isExtern()) continue;
2044 const atom = &decl.*.link.wasm;
2045 if (decl.ty.zigTypeTag() == .Fn) {
2046 try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });
2047 } else if (decl.getVariable()) |variable| {
2048 if (!variable.is_mutable) {
2049 try self.parseAtom(atom, .{ .data = .read_only });
2050 } else if (variable.init.isUndefDeep()) {
2051 try self.parseAtom(atom, .{ .data = .uninitialized });
2170 if (self.base.options.module) |mod| {
2171 var decl_it = self.decls.keyIterator();
2172 while (decl_it.next()) |decl_index_ptr| {
2173 const decl = mod.declPtr(decl_index_ptr.*);
2174 if (decl.isExtern()) continue;
2175 const atom = &decl.*.link.wasm;
2176 if (decl.ty.zigTypeTag() == .Fn) {
2177 try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });
2178 } else if (decl.getVariable()) |variable| {
2179 if (!variable.is_mutable) {
2180 try self.parseAtom(atom, .{ .data = .read_only });
2181 } else if (variable.init.isUndefDeep()) {
2182 try self.parseAtom(atom, .{ .data = .uninitialized });
2183 } else {
2184 try self.parseAtom(atom, .{ .data = .initialized });
2185 }
20522186 } 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 });
20542193 }
2055 } else {
2056 try self.parseAtom(atom, .{ .data = .read_only });
20572194 }
20582195
2059 // also parse atoms for a decl's locals
2060 for (atom.locals.items) |*local_atom| {
2061 try self.parseAtom(local_atom, .{ .data = .read_only });
2196 if (self.dwarf) |*dwarf| {
2197 try dwarf.flushModule(&self.base, self.base.options.module.?);
20622198 }
20632199 }
20642200
......@@ -2066,9 +2202,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
20662202 try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self);
20672203 }
20682204
2069 if (self.dwarf) |*dwarf| {
2070 try dwarf.flushModule(&self.base, self.base.options.module.?);
2071 }
20722205 try self.allocateAtoms();
20732206 try self.setupMemory();
20742207 self.mapFunctionTable();
......@@ -2424,19 +2557,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
24242557 }
24252558 } else if (!self.base.options.strip) {
24262559 if (self.dwarf) |*dwarf| {
2427 if (self.debug_info_index != null) {
2428 try dwarf.writeDbgAbbrev(&self.base);
2429 // for debug info and ranges, the address is always 0,
2430 // as locations are always offsets relative to 'code' section.
2431 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
2432 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
2433 try dwarf.writeDbgLineHeader(&self.base, mod);
2434
2435 try emitDebugSection(file, self.debug_info.items, ".debug_info");
2436 try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges");
2437 try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev");
2438 try emitDebugSection(file, self.debug_line.items, ".debug_line");
2439 try emitDebugSection(file, dwarf.strtab.items, ".debug_str");
2560 const mod = self.base.options.module.?;
2561 try dwarf.writeDbgAbbrev(&self.base);
2562 // for debug info and ranges, the address is always 0,
2563 // as locations are always offsets relative to 'code' section.
2564 try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
2565 try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
2566 try dwarf.writeDbgLineHeader(&self.base, mod);
2567 }
2568
2569 var debug_bytes = std.ArrayList(u8).init(self.base.allocator);
2570 defer debug_bytes.deinit();
2571
2572 const DebugSection = struct {
2573 name: []const u8,
2574 index: ?u32,
2575 };
2576
2577 const debug_sections: []const DebugSection = &.{
2578 .{ .name = ".debug_info", .index = self.debug_info_index },
2579 .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index },
2580 .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index },
2581 .{ .name = ".debug_line", .index = self.debug_line_index },
2582 .{ .name = ".debug_str", .index = self.debug_str_index },
2583 .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index },
2584 .{ .name = ".debug_loc", .index = self.debug_loc_index },
2585 .{ .name = ".debug_ranges", .index = self.debug_ranges_index },
2586 };
2587
2588 for (debug_sections) |item| {
2589 if (item.index) |index| {
2590 var atom = self.atoms.get(index).?.getFirst();
2591 while (true) {
2592 atom.resolveRelocs(self);
2593 try debug_bytes.appendSlice(atom.code.items);
2594 atom = atom.next orelse break;
2595 }
2596 try emitDebugSection(file, debug_bytes.items, item.name);
2597 debug_bytes.clearRetainingCapacity();
24402598 }
24412599 }
24422600 try self.emitNameSection(file, arena);
......@@ -2444,6 +2602,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
24442602}
24452603
24462604fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
2605 if (data.len == 0) return;
24472606 const header_offset = try reserveCustomSectionHeader(file);
24482607 const writer = file.writer();
24492608 try leb.writeULEB128(writer, @intCast(u32, name.len));
......@@ -3057,14 +3216,26 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size
30573216 buf[0] = @enumToInt(section);
30583217 leb.writeUnsignedFixed(5, buf[1..6], size);
30593218 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);
30613226}
30623227
30633228fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
30643229 var buf: [1 + 5]u8 = undefined;
30653230 buf[0] = 0; // 0 = 'custom' section
30663231 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);
30683239}
30693240
30703241fn 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 {
31493320 var payload = std.ArrayList(u8).init(arena);
31503321 const writer = payload.writer();
31513322 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO));
3152 try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len));
3153 for (self.segment_info.items) |segment_info| {
3323 try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count()));
3324 for (self.segment_info.values()) |segment_info| {
31543325 log.debug("Emit segment: {s} align({d}) flags({b})", .{
31553326 segment_info.name,
31563327 @ctz(segment_info.alignment),
src/link/Wasm/Archive.zig+1
......@@ -95,6 +95,7 @@ const ar_hdr = extern struct {
9595};
9696
9797pub fn deinit(archive: *Archive, allocator: Allocator) void {
98 archive.file.close();
9899 for (archive.toc.keys()) |*key| {
99100 allocator.free(key.*);
100101 }
src/link/Wasm/Atom.zig+35-7
......@@ -90,6 +90,19 @@ pub fn getFirst(self: *Atom) *Atom {
9090 return tmp;
9191}
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
93106/// Returns the location of the symbol that represents this `Atom`
94107pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
95108 return .{ .file = self.file, .index = self.sym_index };
......@@ -145,7 +158,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void {
145158/// All values will be represented as a `u64` as all values can fit within it.
146159/// The final value must be casted to the correct size.
147160fn 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);
149162 const symbol = target_loc.getSymbol(wasm_bin).*;
150163 switch (relocation.relocation_type) {
151164 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
......@@ -174,19 +187,34 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
174187 => {
175188 std.debug.assert(symbol.tag == .data and !symbol.isUndefined());
176189 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
177 const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc;
178 const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?;
190 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
179191 const segment_info = if (target_atom.file) |object_index| blk: {
180192 break :blk wasm_bin.objects.items[object_index].segment_info;
181 } else wasm_bin.segment_info.items;
193 } else wasm_bin.segment_info.values();
182194 const segment_name = segment_info[symbol.index].outputName(merge_segment);
183195 const segment_index = wasm_bin.data_segments.get(segment_name).?;
184196 const segment = wasm_bin.segments.items[segment_index];
185197 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
186198 },
187199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
188 .R_WASM_SECTION_OFFSET_I32,
189 .R_WASM_FUNCTION_OFFSET_I32,
190 => return relocation.offset,
200 .R_WASM_SECTION_OFFSET_I32 => {
201 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
202 return target_atom.offset + (relocation.addend orelse 0);
203 },
204 .R_WASM_FUNCTION_OFFSET_I32 => {
205 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
206 var atom = target_atom.getFirst();
207 var offset: u32 = 0;
208 // TODO: Calculate this during atom allocation, rather than
209 // this linear calculation. For now it's done here as atoms
210 // are being sorted after atom allocation, as functions aren't
211 // merged until later.
212 while (true) {
213 offset += 5; // each atom uses 5 bytes to store its body's size
214 if (atom == target_atom) break;
215 atom = atom.next.?;
216 }
217 return target_atom.offset + offset + (relocation.addend orelse 0);
218 },
191219 }
192220}
src/link/Wasm/Object.zig+78-34
......@@ -63,16 +63,21 @@ relocatable_data: []const RelocatableData = &.{},
6363/// import name, module name and export names. Each string will be deduplicated
6464/// and returns an offset into the table.
6565string_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
6771/// Represents a single item within a section (depending on its `type`)
6872const RelocatableData = struct {
6973 /// The type of the relocatable data
70 type: enum { data, code, custom },
74 type: enum { data, code, debug },
7175 /// Pointer to the data of the segment, where its length is written to `size`
7276 data: [*]u8,
7377 /// The size in bytes of the data representing the segment within the section
7478 size: u32,
75 /// The index within the section itself
79 /// The index within the section itself, or in case of a debug section,
80 /// the offset within the `string_table`.
7681 index: u32,
7782 /// The offset within the section where the data starts
7883 offset: u32,
......@@ -96,9 +101,16 @@ const RelocatableData = struct {
96101 return switch (self.type) {
97102 .data => .data,
98103 .code => .function,
99 .custom => .section,
104 .debug => .section,
100105 };
101106 }
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 }
102114};
103115
104116pub 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
111123 var object: Object = .{
112124 .file = file,
113125 .name = try gpa.dupe(u8, name),
126 .debug_names = &.{},
114127 };
115128
116129 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
141154/// Frees all memory of `Object` at once. The given `Allocator` must be
142155/// the same allocator that was used when `init` was called.
143156pub fn deinit(self: *Object, gpa: Allocator) void {
157 if (self.file) |file| {
158 file.close();
159 }
144160 for (self.func_types) |func_ty| {
145161 gpa.free(func_ty.params);
146162 gpa.free(func_ty.returns);
......@@ -197,6 +213,11 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
197213 } else i;
198214}
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
200221/// Checks if the object file is an MVP version.
201222/// When that's the case, we check if there's an import table definiton with its name
202223/// set to '__indirect_function_table". When that's also the case,
......@@ -328,10 +349,15 @@ fn Parser(comptime ReaderType: type) type {
328349
329350 self.object.version = version;
330351 var relocatable_data = std.ArrayList(RelocatableData).init(gpa);
331
332 errdefer while (relocatable_data.popOrNull()) |rel_data| {
333 gpa.free(rel_data.data[0..rel_data.size]);
334 } else relocatable_data.deinit();
352 var debug_names = std.ArrayList(u8).init(gpa);
353
354 errdefer {
355 while (relocatable_data.popOrNull()) |rel_data| {
356 gpa.free(rel_data.data[0..rel_data.size]);
357 } else relocatable_data.deinit();
358 gpa.free(debug_names.items);
359 debug_names.deinit();
360 }
335361
336362 var section_index: u32 = 0;
337363 while (self.reader.reader().readByte()) |byte| : (section_index += 1) {
......@@ -347,11 +373,26 @@ fn Parser(comptime ReaderType: type) type {
347373
348374 if (std.mem.eql(u8, name, "linking")) {
349375 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.
350377 try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));
351378 } else if (std.mem.startsWith(u8, name, "reloc")) {
352379 try self.parseRelocations(gpa);
353380 } else if (std.mem.eql(u8, name, "target_features")) {
354381 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 });
355396 } else {
356397 try reader.skipBytes(reader.context.bytes_left, .{});
357398 }
......@@ -737,7 +778,12 @@ fn Parser(comptime ReaderType: type) type {
737778 },
738779 .section => {
739780 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 }
741787 },
742788 else => {
743789 symbol.index = try leb.readULEB128(u32, reader);
......@@ -827,7 +873,6 @@ fn assertEnd(reader: anytype) !void {
827873
828874/// Parses an object file into atoms, for code and data sections
829875pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {
830 log.debug("Parsing data section into atoms", .{});
831876 const Key = struct {
832877 kind: Symbol.Tag,
833878 index: u32,
......@@ -839,7 +884,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
839884
840885 for (self.symtable) |symbol, symbol_index| {
841886 switch (symbol.tag) {
842 .function, .data => if (!symbol.isUndefined()) {
887 .function, .data, .section => if (!symbol.isUndefined()) {
843888 const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
844889 const sym_idx = @intCast(u32, symbol_index);
845890 if (!gop.found_existing) {
......@@ -852,12 +897,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
852897 }
853898
854899 for (self.relocatable_data) |relocatable_data, index| {
855 const symbols = symbol_for_segment.getPtr(.{
856 .kind = relocatable_data.getSymbolKind(),
857 .index = @intCast(u32, relocatable_data.index),
858 }) orelse continue; // encountered a segment we do not create an atom for
859 const sym_index = symbols.pop();
860 const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index));
900 const final_index = (try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index))) orelse {
901 continue; // found unknown section, so skip parsing into atom as we do not know how to handle it.
902 };
861903
862904 const atom = try gpa.create(Atom);
863905 atom.* = Atom.empty;
......@@ -870,7 +912,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
870912 atom.file = object_index;
871913 atom.size = relocatable_data.size;
872914 atom.alignment = relocatable_data.getAlignment(self);
873 atom.sym_index = sym_index;
874915
875916 const relocations: []types.Relocation = self.relocations.get(relocatable_data.section_index) orelse &.{};
876917 for (relocations) |relocation| {
......@@ -892,28 +933,31 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
892933
893934 try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
894935
895 // symbols referencing the same atom will be added as alias
896 // or as 'parent' when they are global.
897 while (symbols.popOrNull()) |idx| {
898 const alias_symbol = self.symtable[idx];
899 const symbol = self.symtable[atom.sym_index];
900 if (alias_symbol.isGlobal() and symbol.isLocal()) {
901 atom.sym_index = idx;
936 if (symbol_for_segment.getPtr(.{
937 .kind = relocatable_data.getSymbolKind(),
938 .index = relocatable_data.getIndex(),
939 })) |symbols| {
940 atom.sym_index = symbols.pop();
941
942 // symbols referencing the same atom will be added as alias
943 // or as 'parent' when they are global.
944 while (symbols.popOrNull()) |idx| {
945 const alias_symbol = self.symtable[idx];
946 const symbol = self.symtable[atom.sym_index];
947 if (alias_symbol.isGlobal() and symbol.isLocal()) {
948 atom.sym_index = idx;
949 }
902950 }
951 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
903952 }
904 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
905953
906954 const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
907 segment.alignment = std.math.max(segment.alignment, atom.alignment);
908
909 if (wasm_bin.atoms.getPtr(final_index)) |last| {
910 last.*.next = atom;
911 atom.prev = last.*;
912 last.* = atom;
913 } else {
914 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);
955 if (relocatable_data.type == .data) { //code section and debug sections are 1-byte aligned
956 segment.alignment = std.math.max(segment.alignment, atom.alignment);
915957 }
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 });
917961 }
918962}
919963
src/link/strtab.zig+4
......@@ -110,6 +110,10 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
110110 return self.get(off) orelse unreachable;
111111 }
112112
113 pub fn items(self: Self) []const u8 {
114 return self.buffer.items;
115 }
116
113117 pub fn len(self: Self) usize {
114118 return self.buffer.items.len;
115119 }
src/main.zig+20-9
......@@ -268,7 +268,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
268268 } else if (mem.eql(u8, cmd, "init-lib")) {
269269 return cmdInit(gpa, arena, cmd_args, .Lib);
270270 } else if (mem.eql(u8, cmd, "targets")) {
271 const info = try detectNativeTargetInfo(arena, .{});
271 const info = try detectNativeTargetInfo(.{});
272272 const stdout = io.getStdOut().writer();
273273 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
274274 } else if (mem.eql(u8, cmd, "version")) {
......@@ -691,6 +691,9 @@ fn buildOutputType(
691691 var linker_max_memory: ?u64 = null;
692692 var linker_shared_memory: bool = false;
693693 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;
694697 var linker_z_nodelete = false;
695698 var linker_z_notext = false;
696699 var linker_z_defs = false;
......@@ -1816,6 +1819,12 @@ fn buildOutputType(
18161819 linker_gc_sections = true;
18171820 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
18181821 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;
18191828 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
18201829 mem.eql(u8, arg, "-allow-shlib-undefined"))
18211830 {
......@@ -2258,7 +2267,7 @@ fn buildOutputType(
22582267 }
22592268
22602269 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
22632272 if (target_info.target.os.tag != .freestanding) {
22642273 if (ensure_libc_on_non_freestanding)
......@@ -2911,6 +2920,9 @@ fn buildOutputType(
29112920 .linker_initial_memory = linker_initial_memory,
29122921 .linker_max_memory = linker_max_memory,
29132922 .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,
29142926 .linker_global_base = linker_global_base,
29152927 .linker_export_symbol_names = linker_export_symbol_names.items,
29162928 .linker_z_nodelete = linker_z_nodelete,
......@@ -3271,7 +3283,7 @@ fn runOrTest(
32713283 if (std.process.can_execv and arg_mode == .run and !watch) {
32723284 // execv releases the locks; no need to destroy the Compilation here.
32733285 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);
32753287 const cmd = try std.mem.join(arena, " ", argv.items);
32763288 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
32773289 } else if (std.process.can_spawn) {
......@@ -3288,7 +3300,7 @@ fn runOrTest(
32883300 }
32893301
32903302 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);
32923304 const cmd = try std.mem.join(arena, " ", argv.items);
32933305 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
32943306 };
......@@ -3902,7 +3914,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
39023914 gimmeMoreOfThoseSweetSweetFileDescriptors();
39033915
39043916 const cross_target: std.zig.CrossTarget = .{};
3905 const target_info = try detectNativeTargetInfo(gpa, cross_target);
3917 const target_info = try detectNativeTargetInfo(cross_target);
39063918
39073919 const exe_basename = try std.zig.binNameAlloc(arena, .{
39083920 .root_name = "build",
......@@ -4944,8 +4956,8 @@ test "fds" {
49444956 gimmeMoreOfThoseSweetSweetFileDescriptors();
49454957}
49464958
4947fn detectNativeTargetInfo(gpa: Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
4948 return std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
4959fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
4960 return std.zig.system.NativeTargetInfo.detect(cross_target);
49494961}
49504962
49514963/// Indicate that we are now terminating with a successful exit code.
......@@ -5308,14 +5320,13 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 {
53085320}
53095321
53105322fn warnAboutForeignBinaries(
5311 gpa: Allocator,
53125323 arena: Allocator,
53135324 arg_mode: ArgMode,
53145325 target_info: std.zig.system.NativeTargetInfo,
53155326 link_libc: bool,
53165327) !void {
53175328 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
53205331 switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) {
53215332 .native => return,
src/test.zig+10-2
......@@ -177,6 +177,8 @@ const TestManifestConfigDefaults = struct {
177177 inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| {
178178 defaults = defaults ++ arch ++ "-macos" ++ ",";
179179 }
180 // Windows
181 defaults = defaults ++ "x86_64-windows" ++ ",";
180182 // Wasm
181183 defaults = defaults ++ "wasm32-wasi";
182184 return defaults;
......@@ -1211,7 +1213,7 @@ pub const TestContext = struct {
12111213 }
12121214
12131215 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
12161218 var progress = std.Progress{};
12171219 const root_node = progress.start("compiler", self.cases.items.len);
......@@ -1300,7 +1302,7 @@ pub const TestContext = struct {
13001302 global_cache_directory: Compilation.Directory,
13011303 host: std.zig.system.NativeTargetInfo,
13021304 ) !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);
13041306 const target = target_info.target;
13051307
13061308 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
......@@ -1546,6 +1548,12 @@ pub const TestContext = struct {
15461548 .self_exe_path = std.testing.zig_exe_path,
15471549 // TODO instead of turning off color, pass in a std.Progress.Node
15481550 .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 },
15491557 });
15501558 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
11671167 }
11681168
11691169 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", .{});
11711171 }
11721172
11731173 const record_payload = try c.arena.create(ast.Payload.Record);
......@@ -5799,7 +5799,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
57995799 }
58005800 }
58015801 for (source) |c| {
5802 if (c == '\\') {
5802 if (c == '\\' or c == '\t') {
58035803 break;
58045804 }
58055805 } else return source;
......@@ -5876,6 +5876,13 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
58765876 state = .Start;
58775877 },
58785878 .Start => {
5879 if (c == '\t') {
5880 bytes[i] = '\\';
5881 i += 1;
5882 bytes[i] = 't';
5883 i += 1;
5884 continue;
5885 }
58795886 if (c == '\\') {
58805887 state = .Escape;
58815888 }
test/behavior.zig+1
......@@ -86,6 +86,7 @@ test {
8686 _ = @import("behavior/bugs/12430.zig");
8787 _ = @import("behavior/bugs/12486.zig");
8888 _ = @import("behavior/bugs/12680.zig");
89 _ = @import("behavior/bugs/12776.zig");
8990 _ = @import("behavior/byteswap.zig");
9091 _ = @import("behavior/byval_arg_var.zig");
9192 _ = @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" {
486486 try S.doTheTest();
487487 comptime try S.doTheTest();
488488}
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;
5050#define CAST_TO_UINTPTR(X) (uintptr_t)(X)
5151
5252#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");
22const std = @import("std");
33const expect = std.testing.expect;
44const expectEqual = std.testing.expectEqual;
5const expectEqualStrings = std.testing.expectEqualStrings;
56
67const h = @cImport(@cInclude("behavior/translate_c_macros.h"));
78
......@@ -123,3 +124,13 @@ test "large integer macro" {
123124
124125 try expectEqual(@as(c_ulonglong, 18446744073709550592), h.LARGE_INT);
125126}
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 @@
22// output_mode=Exe
33// target=aarch64-macos
44//
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
5// :109:9: error: struct 'tmp.tmp' has no member named 'main'
66// :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 @@
22// output_mode=Exe
33// target=x86_64-linux
44//
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
5// :109:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=x86_64-macos
44//
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
5// :109:9: error: struct 'tmp.tmp' has no member named 'main'
66// :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 {
2828}
2929
3030fn addWasmCases(cases: *tests.StandaloneContext) void {
31 cases.addBuildFile("test/link/wasm/bss/build.zig", .{
31 cases.addBuildFile("test/link/wasm/archive/build.zig", .{
3232 .build_modes = true,
3333 .requires_stage2 = true,
3434 });
3535
36 cases.addBuildFile("test/link/wasm/segments/build.zig", .{
36 cases.addBuildFile("test/link/wasm/bss/build.zig", .{
3737 .build_modes = true,
3838 .requires_stage2 = true,
3939 });
4040
41 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{
41 cases.addBuildFile("test/link/wasm/extern/build.zig", .{
4242 .build_modes = true,
4343 .requires_stage2 = true,
44 .use_emulation = true,
4445 });
4546
46 cases.addBuildFile("test/link/wasm/type/build.zig", .{
47 cases.addBuildFile("test/link/wasm/segments/build.zig", .{
4748 .build_modes = true,
4849 .requires_stage2 = true,
4950 });
5051
51 cases.addBuildFile("test/link/wasm/archive/build.zig", .{
52 cases.addBuildFile("test/link/wasm/stack_pointer/build.zig", .{
5253 .build_modes = true,
5354 .requires_stage2 = true,
5455 });
5556
56 cases.addBuildFile("test/link/wasm/extern/build.zig", .{
57 cases.addBuildFile("test/link/wasm/type/build.zig", .{
5758 .build_modes = true,
5859 .requires_stage2 = true,
59 .use_emulation = true,
6060 });
6161}
6262
test/tests.zig+10
......@@ -108,6 +108,14 @@ const test_targets = blk: {
108108 },
109109 .backend = .stage2_x86_64,
110110 },
111 .{
112 .target = .{
113 .cpu_arch = .x86_64,
114 .os_tag = .windows,
115 .abi = .gnu,
116 },
117 .backend = .stage2_x86_64,
118 },
111119
112120 .{
113121 .target = .{
......@@ -693,6 +701,8 @@ pub fn addPkgTests(
693701 else => {
694702 these_tests.use_stage1 = false;
695703 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;
696706 },
697707 };
698708