From 1dd26042cc11e15eb7406e750c9454a2e2289a0f Mon Sep 17 00:00:00 2001 From: Manlio Perillo Date: Sat, 25 Feb 2023 10:41:37 +0100 Subject: [PATCH 001/725] docgen: remove line support in printShell Line support in printShell breaks the support to terminal colors, when a color is active over multiple lines. An example is when printing reference traces. Remove line support in printShell, since they are not really necessary like in printSourceBlock. In genHtml, remove an empty line when printing the execution of the executable generated by `zig build-exe`. Update the "shell parsed" tests. Remove the call to log.emerg, since it is no longer supported. Add an extra space after "--build-option1 \", as documented. Fixes #13280 --- doc/docgen.zig | 75 ++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index 277316dd37..e7663cdfe2 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1224,41 +1224,39 @@ fn printShell(out: anytype, shell_content: []const u8, escape: bool) !void { while (iter.next()) |orig_line| { const line = mem.trimRight(u8, orig_line, " "); if (!cmd_cont and line.len > 1 and mem.eql(u8, line[0..2], "$ ") and line[line.len - 1] != '\\') { - try out.writeAll(start_line ++ "$ "); + try out.writeAll("$ "); const s = std.mem.trimLeft(u8, line[1..], " "); if (escape) { try writeEscaped(out, s); } else { try out.writeAll(s); } - try out.writeAll("" ++ end_line ++ "\n"); + try out.writeAll("" ++ "\n"); } else if (!cmd_cont and line.len > 1 and mem.eql(u8, line[0..2], "$ ") and line[line.len - 1] == '\\') { - try out.writeAll(start_line ++ "$ "); + try out.writeAll("$ "); const s = std.mem.trimLeft(u8, line[1..], " "); if (escape) { try writeEscaped(out, s); } else { try out.writeAll(s); } - try out.writeAll(end_line ++ "\n"); + try out.writeAll("\n"); cmd_cont = true; } else if (line.len > 0 and line[line.len - 1] != '\\' and cmd_cont) { - try out.writeAll(start_line); if (escape) { try writeEscaped(out, line); } else { try out.writeAll(line); } - try out.writeAll("" ++ end_line ++ "\n"); + try out.writeAll("" ++ "\n"); cmd_cont = false; } else { - try out.writeAll(start_line); if (escape) { try writeEscaped(out, line); } else { try out.writeAll(line); } - try out.writeAll(end_line ++ "\n"); + try out.writeAll("\n"); } } @@ -1511,7 +1509,7 @@ fn genHtml( const colored_stderr = try termColor(allocator, escaped_stderr); const colored_stdout = try termColor(allocator, escaped_stdout); - try shell_out.print("\n$ ./{s}\n{s}{s}", .{ code.name, colored_stdout, colored_stderr }); + try shell_out.print("$ ./{s}\n{s}{s}", .{ code.name, colored_stdout, colored_stderr }); if (exited_with_signal) { try shell_out.print("(process terminated by signal)", .{}); } @@ -1884,7 +1882,7 @@ test "shell parsed" { \\$ zig build test.zig ; const expected = - \\
Shell
$ zig build test.zig
+            \\
Shell
$ zig build test.zig
             \\
; @@ -1892,7 +1890,6 @@ test "shell parsed" { defer buffer.deinit(); try printShell(buffer.writer(), shell_out, false); - std.log.emerg("{s}", .{buffer.items}); try testing.expectEqualSlices(u8, expected, buffer.items); } { @@ -1901,8 +1898,8 @@ test "shell parsed" { \\build output ; const expected = - \\
Shell
$ zig build test.zig
-            \\build output
+            \\
Shell
$ zig build test.zig
+            \\build output
             \\
; @@ -1919,9 +1916,9 @@ test "shell parsed" { \\$ ./test ; const expected = - \\
Shell
$ zig build test.zig
-            \\build output
-            \\$ ./test
+            \\
Shell
$ zig build test.zig
+            \\build output
+            \\$ ./test
             \\
; @@ -1939,10 +1936,10 @@ test "shell parsed" { \\output ; const expected = - \\
Shell
$ zig build test.zig
-            \\
-            \\$ ./test
-            \\output
+            \\
Shell
$ zig build test.zig
+            \\
+            \\$ ./test
+            \\output
             \\
; @@ -1959,9 +1956,9 @@ test "shell parsed" { \\output ; const expected = - \\
Shell
$ zig build test.zig
-            \\$ ./test
-            \\output
+            \\
Shell
$ zig build test.zig
+            \\$ ./test
+            \\output
             \\
; @@ -1980,11 +1977,11 @@ test "shell parsed" { \\output ; const expected = - \\
Shell
$ zig build test.zig \
-            \\ --build-option
-            \\build output
-            \\$ ./test
-            \\output
+            \\
Shell
$ zig build test.zig \
+            \\ --build-option
+            \\build output
+            \\$ ./test
+            \\output
             \\
; @@ -1998,15 +1995,15 @@ test "shell parsed" { // intentional space after "--build-option1 \" const shell_out = \\$ zig build test.zig \ - \\ --build-option1 \ + \\ --build-option1 \ \\ --build-option2 \\$ ./test ; const expected = - \\
Shell
$ zig build test.zig \
-            \\ --build-option1 \
-            \\ --build-option2
-            \\$ ./test
+            \\
Shell
$ zig build test.zig \
+            \\ --build-option1 \
+            \\ --build-option2
+            \\$ ./test
             \\
; @@ -2022,8 +2019,8 @@ test "shell parsed" { \\$ ./test ; const expected = - \\
Shell
$ zig build test.zig \
-            \\$ ./test
+            \\
Shell
$ zig build test.zig \
+            \\$ ./test
             \\
; @@ -2040,9 +2037,9 @@ test "shell parsed" { \\$1 ; const expected = - \\
Shell
$ zig build test.zig
-            \\$ ./test
-            \\$1
+            \\
Shell
$ zig build test.zig
+            \\$ ./test
+            \\$1
             \\
; @@ -2057,7 +2054,7 @@ test "shell parsed" { \\$zig build test.zig ; const expected = - \\
Shell
$zig build test.zig
+            \\
Shell
$zig build test.zig
             \\
; From 7ca052a6fb017882de1ebcf86226f3b2927a11c7 Mon Sep 17 00:00:00 2001 From: Manlio Perillo Date: Sat, 25 Feb 2023 11:14:43 +0100 Subject: [PATCH 002/725] docgen: improve the termColor function Make the termColor function more robust, ensuring that the generated HTML classes are more consistent and that they are supported in the CSS. Add documentation about the ANSI codes generated by Zig, and remove the previous comment with the supported colors. Improve test coverage for the termColor function, and move the tests at the end of the file before the printShell tests. Rename the "term color" test to "term supported colors" and add an additional "term output from zig" test, using test data generated from the Zig compiler. Update the langref.html.in CSS to use the new names and remove incorrect or obsolete colors like .t0_1, .t37 and .t37_1. Fix support for the 1m color. Change font-weight to normal for the kbd element, to avoid both the command (like `zig build-exe`) and the first line of the error message having a bold font. Rename the "shell parsed" test to "printShell". --- doc/docgen.zig | 258 +++++++++++++++++++++++++++++++++++++++----- doc/langref.html.in | 20 ++-- 2 files changed, 243 insertions(+), 35 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index e7663cdfe2..6f2438c857 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -793,28 +793,37 @@ fn writeEscaped(out: anytype, input: []const u8) !void { } } -//#define VT_RED "\x1b[31;1m" -//#define VT_GREEN "\x1b[32;1m" -//#define VT_CYAN "\x1b[36;1m" -//#define VT_WHITE "\x1b[37;1m" -//#define VT_BOLD "\x1b[0;1m" -//#define VT_RESET "\x1b[0m" - -test "term color" { - const input_bytes = "A\x1b[32;1mgreen\x1b[0mB"; - const result = try termColor(std.testing.allocator, input_bytes); - defer std.testing.allocator.free(result); - try testing.expectEqualSlices(u8, "AgreenB", result); +// Returns true if number is in slice. +fn in(slice: []const u8, number: u8) bool { + for (slice) |n| { + if (number == n) return true; + } + return false; } fn termColor(allocator: Allocator, input: []const u8) ![]u8 { + // The SRG sequences generates by the Zig compiler are in the format: + // ESC [ ; m + // or + // ESC [ m + // + // where + // foreground-color is 31 (red), 32 (green), 36 (cyan) + // n is 0 (reset), 1 (bold), 2 (dim) + // + // Note that 37 (white) is currently not used by the compiler. + // + // See std.debug.TTY.Color. + const supported_sgr_colors = [_]u8{ 31, 32, 36 }; + const supported_sgr_numbers = [_]u8{ 0, 1, 2 }; + var buf = std.ArrayList(u8).init(allocator); defer buf.deinit(); var out = buf.writer(); - var number_start_index: usize = undefined; - var first_number: usize = undefined; - var second_number: usize = undefined; + var sgr_param_start_index: usize = undefined; + var sgr_num: u8 = undefined; + var sgr_color: u8 = undefined; var i: usize = 0; var state: enum { start, @@ -845,7 +854,7 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 { }, .lbracket => switch (c) { '0'...'9' => { - number_start_index = i; + sgr_param_start_index = i; state = .number; }, else => return error.UnsupportedEscape, @@ -853,13 +862,12 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 { .number => switch (c) { '0'...'9' => {}, else => { - first_number = std.fmt.parseInt(usize, input[number_start_index..i], 10) catch unreachable; - second_number = 0; + sgr_num = try std.fmt.parseInt(u8, input[sgr_param_start_index..i], 10); + sgr_color = 0; state = .after_number; i -= 1; }, }, - .after_number => switch (c) { ';' => state = .arg, 'D' => state = .start, @@ -874,7 +882,7 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 { }, .arg => switch (c) { '0'...'9' => { - number_start_index = i; + sgr_param_start_index = i; state = .arg_number; }, else => return error.UnsupportedEscape, @@ -882,7 +890,15 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 { .arg_number => switch (c) { '0'...'9' => {}, else => { - second_number = std.fmt.parseInt(usize, input[number_start_index..i], 10) catch unreachable; + // Keep the sequence consistent, foreground color first. + // 32;1m is equivalent to 1;32m, but the latter will + // generate an incorrect HTML class without notice. + sgr_color = sgr_num; + if (!in(&supported_sgr_colors, sgr_color)) return error.UnsupportedForegroundColor; + + sgr_num = try std.fmt.parseInt(u8, input[sgr_param_start_index..i], 10); + if (!in(&supported_sgr_numbers, sgr_num)) return error.UnsupportedNumber; + state = .expect_end; i -= 1; }, @@ -893,10 +909,16 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 { while (open_span_count != 0) : (open_span_count -= 1) { try out.writeAll(""); } - if (first_number != 0 or second_number != 0) { - try out.print("", .{ first_number, second_number }); - open_span_count += 1; + if (sgr_num == 0) { + if (sgr_color != 0) return error.UnsupportedColor; + continue; } + if (sgr_color != 0) { + try out.print("", .{ sgr_color, sgr_num }); + } else { + try out.print("", .{sgr_num}); + } + open_span_count += 1; }, else => return error.UnsupportedEscape, }, @@ -1874,7 +1896,193 @@ fn dumpArgs(args: []const []const u8) void { print("\n", .{}); } -test "shell parsed" { +test "term supported colors" { + const test_allocator = testing.allocator; + + { + const input = "A\x1b[31;1mred\x1b[0mB"; + const expect = "AredB"; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + const input = "A\x1b[32;1mgreen\x1b[0mB"; + const expect = "AgreenB"; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + const input = "A\x1b[36;1mcyan\x1b[0mB"; + const expect = "AcyanB"; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + const input = "A\x1b[1mbold\x1b[0mB"; + const expect = "AboldB"; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + const input = "A\x1b[2mdim\x1b[0mB"; + const expect = "AdimB"; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } +} + +test "term output from zig" { + // Use data generated by https://github.com/perillo/zig-tty-test-data, + // with zig version 0.11.0-dev.1898+36d47dd19. + const test_allocator = testing.allocator; + + { + // 1.1-with-build-progress.out + const input = "Semantic Analysis [1324] \x1b[25D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLD Link... \x1b[12D\x1b[0K"; + const expect = ""; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + // 2.1-with-reference-traces.out + const input = "\x1b[1msrc/2.1-with-reference-traces.zig:3:7: \x1b[31;1merror: \x1b[0m\x1b[1mcannot assign to constant\n\x1b[0m x += 1;\n \x1b[32;1m~~^~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.1-with-reference-traces.zig:7:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m"; + const expect = + \\src/2.1-with-reference-traces.zig:3:7: error: cannot assign to constant + \\ x += 1; + \\ ~~^~~~ + \\referenced by: + \\ main: src/2.1-with-reference-traces.zig:7:5 + \\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17 + \\ remaining reference traces hidden; use '-freference-trace' to see all reference traces + \\ + \\ + ; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + // 2.2-without-reference-traces.out + const input = "\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: \x1b[31;1merror: \x1b[0m\x1b[1minvalid type given to fixedBufferStream\n\x1b[0m else => @compileError(\"invalid type given to fixedBufferStream\"),\n \x1b[32;1m^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\x1b[0m\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: \x1b[36;1mnote: \x1b[0m\x1b[1mcalled from here\n\x1b[0mpub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {\n; \x1b[32;1m~~~~~^~~~~~~~~~~~~~~~~\n\x1b[0m"; + const expect = + \\/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: error: invalid type given to fixedBufferStream + \\ else => @compileError("invalid type given to fixedBufferStream"), + \\ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + \\/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: note: called from here + \\pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) { + \\; ~~~~~^~~~~~~~~~~~~~~~~ + \\ + ; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + // 2.3-with-notes.out + const input = "\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[31;1merror: \x1b[0m\x1b[1mexpected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'\n\x1b[0m bar(w);\n \x1b[32;1m^\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[36;1mnote: \x1b[0m\x1b[1mpointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:2:13: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Wat = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:1:14: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Derp = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:4:18: \x1b[36;1mnote: \x1b[0m\x1b[1mparameter type declared here\n\x1b[0mextern fn bar(d: *Derp) void;\n \x1b[32;1m^~~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.3-with-notes.zig:10:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m"; + const expect = + \\src/2.3-with-notes.zig:6:9: error: expected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat' + \\ bar(w); + \\ ^ + \\src/2.3-with-notes.zig:6:9: note: pointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp' + \\src/2.3-with-notes.zig:2:13: note: opaque declared here + \\const Wat = opaque {}; + \\ ^~~~~~~~~ + \\src/2.3-with-notes.zig:1:14: note: opaque declared here + \\const Derp = opaque {}; + \\ ^~~~~~~~~ + \\src/2.3-with-notes.zig:4:18: note: parameter type declared here + \\extern fn bar(d: *Derp) void; + \\ ^~~~~ + \\referenced by: + \\ main: src/2.3-with-notes.zig:10:5 + \\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17 + \\ remaining reference traces hidden; use '-freference-trace' to see all reference traces + \\ + \\ + ; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + // 3.1-with-error-return-traces.out + + const input = "error: Error\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:5:5\x1b[0m: \x1b[2m0x20b008 in callee (3.1-with-error-return-traces)\x1b[0m\n return error.Error;\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:9:5\x1b[0m: \x1b[2m0x20b113 in caller (3.1-with-error-return-traces)\x1b[0m\n try callee();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:13:5\x1b[0m: \x1b[2m0x20b153 in main (3.1-with-error-return-traces)\x1b[0m\n try caller();\n \x1b[32;1m^\x1b[0m\n"; + const expect = + \\error: Error + \\/home/zig/src/3.1-with-error-return-traces.zig:5:5: 0x20b008 in callee (3.1-with-error-return-traces) + \\ return error.Error; + \\ ^ + \\/home/zig/src/3.1-with-error-return-traces.zig:9:5: 0x20b113 in caller (3.1-with-error-return-traces) + \\ try callee(); + \\ ^ + \\/home/zig/src/3.1-with-error-return-traces.zig:13:5: 0x20b153 in main (3.1-with-error-return-traces) + \\ try caller(); + \\ ^ + \\ + ; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } + + { + // 3.2-with-stack-trace.out + const input = "\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:561:19\x1b[0m: \x1b[2m0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)\x1b[0m\n while (it.next()) |return_address| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:157:80\x1b[0m: \x1b[2m0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)\x1b[0m\n writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:5:36\x1b[0m: \x1b[2m0x20d3b2 in foo (3.2-with-stack-trace)\x1b[0m\n std.debug.dumpCurrentStackTrace(null);\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:9:8\x1b[0m: \x1b[2m0x20b458 in main (3.2-with-stack-trace)\x1b[0m\n foo();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:607:22\x1b[0m: \x1b[2m0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)\x1b[0m\n root.main();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:376:5\x1b[0m: \x1b[2m0x20a411 in _start (3.2-with-stack-trace)\x1b[0m\n @call(.never_inline, posixCallMainAndExit, .{});\n \x1b[32;1m^\x1b[0m\n"; + const expect = + \\/usr/local/lib/zig/lib/std/debug.zig:561:19: 0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace) + \\ while (it.next()) |return_address| { + \\ ^ + \\/usr/local/lib/zig/lib/std/debug.zig:157:80: 0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace) + \\ writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| { + \\ ^ + \\/home/zig/src/3.2-with-stack-trace.zig:5:36: 0x20d3b2 in foo (3.2-with-stack-trace) + \\ std.debug.dumpCurrentStackTrace(null); + \\ ^ + \\/home/zig/src/3.2-with-stack-trace.zig:9:8: 0x20b458 in main (3.2-with-stack-trace) + \\ foo(); + \\ ^ + \\/usr/local/lib/zig/lib/std/start.zig:607:22: 0x20a965 in posixCallMainAndExit (3.2-with-stack-trace) + \\ root.main(); + \\ ^ + \\/usr/local/lib/zig/lib/std/start.zig:376:5: 0x20a411 in _start (3.2-with-stack-trace) + \\ @call(.never_inline, posixCallMainAndExit, .{}); + \\ ^ + \\ + ; + + const result = try termColor(test_allocator, input); + defer test_allocator.free(result); + try testing.expectEqualSlices(u8, expect, result); + } +} + +test "printShell" { const test_allocator = std.testing.allocator; { diff --git a/doc/langref.html.in b/doc/langref.html.in index 907464867e..c88c91560c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -71,19 +71,19 @@ text-align: left; font-weight: normal; } - .t0_1, .t37, .t37_1 { + .sgr-1m { font-weight: bold; } - .t2_0 { + .sgr-2m { color: #575757; } - .t31_1 { + .sgr-31_1m { color: #b40000; } - .t32_1 { + .sgr-32_1m { color: green; } - .t36_1 { + .sgr-36_1m { color: #005C7A; } .file { @@ -114,7 +114,7 @@ line-height: normal; } kbd { - font-weight: bold; + font-weight: normal; } .table-wrapper { width: 100%; @@ -232,16 +232,16 @@ table, th, td { border-color: grey; } - .t2_0 { + .sgr-2m { color: grey; } - .t31_1 { + .sgr-31_1m { color: red; } - .t32_1 { + .sgr-32_1m { color: #00B800; } - .t36_1 { + .sgr-36_1m { color: #0086b3; } code { From c579a23c5d997964a67883db5677c290bdbd7da6 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 16 Apr 2023 13:27:38 +0100 Subject: [PATCH 003/725] std: add CCRandomGenerateBytes macOs native api. --- lib/std/c/darwin.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index a46337b721..4ede0b3891 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -897,7 +897,35 @@ pub extern "c" fn pthread_attr_get_qos_class_np(attr: *pthread_attr_t, qos_class pub extern "c" fn pthread_set_qos_class_self_np(qos_class: qos_class_t, relative_priority: c_int) c_int; pub extern "c" fn pthread_get_qos_class_np(pthread: std.c.pthread_t, qos_class: *qos_class_t, relative_priority: *c_int) c_int; +pub const CCryptorStatus = enum(i32) { + /// Operation completed + kCCSuccess = 0, + /// Illegal parameter + kCCParamError = -4300, + /// Provided buffer too small + kCCBufferTooSmall = -4301, + /// Failed memory allocation + kCCMemoryFailure = -4302, + /// Size alignment issue + kCCAlignmentError = -4303, + /// Decoding issue + kCCDecodeError = -4304, + /// Call not implemented + kCCUnimplemented = -4305, + kCCOverflow = -4306, + kCCRNGFailure = -4307, + /// Unspecified error + kCCUnspecifiedError = -4308, + kCCCallSequenceError = -4309, + kCCKeySizeError = -4310, + /// Invalid key + kCCInvalidKey = -4311, +}; + +pub const CCRNGStatus = CCryptorStatus; + pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; +pub extern "c" fn CCRandomGenerateBytes(bytes: ?*anyopaque, count: usize) CCRNGStatus; // Grand Central Dispatch is exposed by libSystem. pub extern "c" fn dispatch_release(object: *anyopaque) void; From 6248ac535b9bfc7113101c1ff0f0bb1e41c394d4 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 16 Apr 2023 15:49:42 +0100 Subject: [PATCH 004/725] os: getrandom wrapper favoring it for macOs/iOs only --- lib/std/os.zig | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 13b0c62455..c2591ac355 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -512,7 +512,18 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { return; } switch (builtin.os.tag) { - .netbsd, .openbsd, .macos, .ios, .tvos, .watchos => { + .macos, .ios => { + const rc = darwin.CCRandomGenerateBytes(buffer.ptr, buffer.len); + if (rc != darwin.CCRNGStatus.kCCSuccess) { + if (rc == darwin.CCRNGStatus.kCCParamError or rc == darwin.CCRNGStatus.kCCBufferTooSmall) { + return error.InvalidHandle; + } else { + return error.SystemResources; + } + } + return; + }, + .netbsd, .openbsd, .tvos, .watchos => { system.arc4random_buf(buffer.ptr, buffer.len); return; }, @@ -991,7 +1002,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { if (have_pread_but_not_preadv) { // We could loop here; but proper usage of `preadv` must handle partial reads anyway. // So we simply read into the first vector only. - if (iov.len == 0) return @as(usize, 0); + if (iov.len == 0) return @intCast(usize, 0); const first = iov[0]; return pread(fd, first.iov_base[0..first.iov_len], offset); } From d339e86fb1308f8df37fc351ee01007492e72f25 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 4 Jan 2023 10:46:05 +0700 Subject: [PATCH 005/725] stage2: sparc64: Skip unimplemented tests --- test/behavior/array.zig | 4 ++++ test/behavior/bugs/12051.zig | 1 + test/behavior/bugs/12450.zig | 1 + test/behavior/comptime_memory.zig | 3 +++ test/behavior/packed-struct.zig | 4 +++- test/behavior/ptrcast.zig | 10 ++++++++++ test/behavior/tuple.zig | 5 +++++ test/behavior/union.zig | 1 + test/behavior/var_args.zig | 2 ++ test/behavior/vector.zig | 1 + 10 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index ec31d4a3fb..6cf0ab9e1d 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -47,6 +47,7 @@ fn getArrayLen(a: []const u32) usize { test "array concat with undefined" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -70,6 +71,7 @@ test "array concat with undefined" { test "array concat with tuple" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const array: [2]u8 = .{ 1, 2 }; { @@ -103,6 +105,7 @@ test "array init with mult" { test "array literal with explicit type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 }; @@ -203,6 +206,7 @@ test "nested arrays of strings" { test "nested arrays of integers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const array_of_numbers = [_][2]u8{ [2]u8{ 1, 2 }, diff --git a/test/behavior/bugs/12051.zig b/test/behavior/bugs/12051.zig index 5e2087d422..2b2765358b 100644 --- a/test/behavior/bugs/12051.zig +++ b/test/behavior/bugs/12051.zig @@ -6,6 +6,7 @@ test { if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const x = X{}; try std.testing.expectEqual(@as(u16, 0), x.y.a); try std.testing.expectEqual(false, x.y.b); diff --git a/test/behavior/bugs/12450.zig b/test/behavior/bugs/12450.zig index 89e5c774e0..4d0c7cd9a1 100644 --- a/test/behavior/bugs/12450.zig +++ b/test/behavior/bugs/12450.zig @@ -13,6 +13,7 @@ test { if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var f1: *align(16) Foo = @alignCast(16, @ptrCast(*align(1) Foo, &buffer[0])); try expect(@typeInfo(@TypeOf(f1)).Pointer.alignment == 16); try expect(@ptrToInt(f1) == @ptrToInt(&f1.a)); diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 98e6e65725..5c3012d1dc 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -76,6 +76,7 @@ fn bigToNativeEndian(comptime T: type, v: T) T { } test "type pun endianness" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO comptime { const StructOfBytes = extern struct { x: [4]u8 }; @@ -376,6 +377,8 @@ test "offset field ptr by enclosing array element size" { test "accessing reinterpreted memory of parent object" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + const S = extern struct { a: f32, b: [4]u8, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index ac4b62b0b8..8a0ef72382 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -563,7 +563,7 @@ test "packed struct passed to callconv(.C) function" { test "overaligned pointer to packed struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = packed struct { a: u32, b: u32 }; var foo: S align(4) = .{ .a = 123, .b = 456 }; const ptr: *align(4) S = &foo; @@ -583,6 +583,7 @@ test "overaligned pointer to packed struct" { test "packed struct initialized in bitcast" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const T = packed struct { val: u8 }; var val: u8 = 123; @@ -595,6 +596,7 @@ test "pointer to container level packed struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = packed struct(u32) { test_bit: bool, diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index becdee6b05..6a5ae726ae 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -4,6 +4,8 @@ const expect = std.testing.expect; const native_endian = builtin.target.cpu.arch.endian(); test "reinterpret bytes as integer with nonzero offset" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + try testReinterpretBytesAsInteger(); comptime try testReinterpretBytesAsInteger(); } @@ -74,6 +76,8 @@ fn testReinterpretBytesAsExternStruct() !void { } test "reinterpret bytes of an extern struct (with under-aligned fields) into another" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + try testReinterpretExternStructAsExternStruct(); comptime try testReinterpretExternStructAsExternStruct(); } @@ -96,6 +100,8 @@ fn testReinterpretExternStructAsExternStruct() !void { } test "reinterpret bytes of an extern struct into another" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + try testReinterpretOverAlignedExternStructAsExternStruct(); comptime try testReinterpretOverAlignedExternStructAsExternStruct(); } @@ -191,6 +197,8 @@ const Bytes = struct { }; test "comptime ptrcast keeps larger alignment" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + comptime { const a: u32 = 1234; const p = @ptrCast([*]const u8, &a); @@ -199,6 +207,8 @@ test "comptime ptrcast keeps larger alignment" { } test "ptrcast of const integer has the correct object size" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + const is_value = ~@intCast(isize, std.math.minInt(isize)); const is_bytes = @ptrCast([*]const u8, &is_value)[0..@sizeOf(isize)]; if (@sizeOf(isize) == 8) { diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 1cf68a0769..11cc8b2dce 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -296,6 +296,7 @@ test "coerce tuple to tuple" { test "tuple type with void field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const T = std.meta.Tuple(&[_]type{void}); const x = T{{}}; @@ -341,6 +342,7 @@ test "tuple type with void field and a runtime field" { test "branching inside tuple literal" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn foo(a: anytype) !void { @@ -355,6 +357,7 @@ test "tuple initialized with a runtime known value" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const E = union(enum) { e: []const u8 }; const W = union(enum) { w: E }; @@ -368,6 +371,7 @@ test "tuple of struct concatenation and coercion to array" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const StructWithDefault = struct { value: f32 = 42 }; const SomeStruct = struct { array: [4]StructWithDefault }; @@ -381,6 +385,7 @@ test "tuple of struct concatenation and coercion to array" { test "nested runtime conditionals in tuple initializer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var data: u8 = 0; const x = .{ diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 37b1f0cb49..025eb4f6bd 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1495,6 +1495,7 @@ test "packed union with zero-bit field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = packed struct { nested: packed union { diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index cdceea96c6..968b15b218 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -95,6 +95,7 @@ fn doNothingWithFirstArg(args: anytype) void { test "simple variadic function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -143,6 +144,7 @@ test "simple variadic function" { test "variadic functions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index d549588dee..f6521f04c3 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1282,6 +1282,7 @@ test "store to vector in slice" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var v = [_]@Vector(3, f32){ .{ 1, 1, 1 }, From 1794a45572a30a493a5c10fcdc45ba0fa3ac4218 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 4 Jan 2023 10:46:27 +0700 Subject: [PATCH 006/725] stage2: sparc64: Add stub for c_va_* --- src/arch/sparc64/CodeGen.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index cc5c9e9832..58b384c832 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -720,10 +720,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .error_set_has_value => @panic("TODO implement error_set_has_value"), .vector_store_elem => @panic("TODO implement vector_store_elem"), - .c_va_arg => @panic("TODO implement c_va_arg"), - .c_va_copy => @panic("TODO implement c_va_copy"), - .c_va_end => @panic("TODO implement c_va_end"), - .c_va_start => @panic("TODO implement c_va_start"), + .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), + .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), + .c_va_end => return self.fail("TODO implement c_va_end", .{}), + .c_va_start => return self.fail("TODO implement c_va_start", .{}), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, From cc2a5185d631b8b33eb5d466cc52ceb092435fd4 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 4 Jan 2023 10:58:40 +0700 Subject: [PATCH 007/725] stage2: sparc64: Implement airStructFieldPtr --- src/arch/sparc64/CodeGen.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 58b384c832..d682ce8c2b 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -595,7 +595,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ret_load => try self.airRetLoad(inst), .store => try self.airStore(inst, false), .store_safe => try self.airStore(inst, true), - .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), + .struct_field_ptr=> try self.airStructFieldPtr(inst), .struct_field_val=> try self.airStructFieldVal(inst), .array_to_slice => try self.airArrayToSlice(inst), .int_to_float => try self.airIntToFloat(inst), @@ -2425,6 +2425,13 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; + const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); + return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); +} + fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result = try self.structFieldPtr(inst, ty_op.operand, index); From 486ab3852e22f8d5ba474691a5b068f1f1729f2e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 4 Jan 2023 12:53:11 +0700 Subject: [PATCH 008/725] stage2: sparc64: Factor machine offset calculation --- src/arch/sparc64/CodeGen.zig | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index d682ce8c2b..3d82cad4ec 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -141,6 +141,8 @@ const MCValue = union(enum) { /// The value is one of the stack variables. /// If the type is a pointer, it means the pointer address is in the stack at this offset. /// Note that this stores the plain value (i.e without the effects of the stack bias). + /// Always convert this value into machine offsets with realStackOffset() before + /// lowering into asm! stack_offset: u32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: u32, @@ -3651,7 +3653,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .ptr_stack_offset => |off| { - const real_offset = off + abi.stack_bias + abi.stack_reserved_area; + const real_offset = realStackOffset(off); const simm13 = math.cast(i13, real_offset) orelse return self.fail("TODO larger stack offsets: {}", .{real_offset}); @@ -3783,7 +3785,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const real_offset = off + abi.stack_bias + abi.stack_reserved_area; + const real_offset = realStackOffset(off); const simm13 = math.cast(i13, real_offset) orelse return self.fail("TODO larger stack offsets: {}", .{real_offset}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); @@ -3817,7 +3819,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const real_offset = stack_offset + abi.stack_bias + abi.stack_reserved_area; + const real_offset = realStackOffset(stack_offset); const simm13 = math.cast(i13, real_offset) orelse return self.fail("TODO larger stack offsets: {}", .{real_offset}); return self.genStore(reg, .sp, i13, simm13, abi_size); @@ -4252,6 +4254,17 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { } } +/// Turns stack_offset MCV into a real SPARCv9 stack offset usable for asm. +fn realStackOffset(off: u32) u32 { + return off + // SPARCv9 %sp points away from the stack by some amount. + + abi.stack_bias + // The first couple bytes of each stack frame is reserved + // for ABI and hardware purposes. + + abi.stack_reserved_area; + // Only after that we have the usable stack frame portion. +} + /// Caller must call `CallMCValues.deinit`. fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues { const cc = fn_ty.fnCallingConvention(); From 83e6223192acd635275e67614e55b7a4c579a969 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 4 Jan 2023 16:38:15 +0700 Subject: [PATCH 009/725] stage2: sparc64: Implement airByteSwap --- src/arch/sparc64/CodeGen.zig | 156 +++++++++++++++++++++++++++++++++-- src/arch/sparc64/Emit.zig | 10 +++ src/arch/sparc64/Mir.zig | 30 +++++++ 3 files changed, 189 insertions(+), 7 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 3d82cad4ec..a4fa7e179c 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -22,6 +22,7 @@ const Type = @import("../../type.zig").Type; const CodeGenError = codegen.CodeGenError; const Result = @import("../../codegen.zig").Result; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const Endian = std.builtin.Endian; const build_options = @import("build_options"); @@ -30,6 +31,7 @@ const abi = @import("abi.zig"); const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; const Instruction = bits.Instruction; +const ASI = Instruction.ASI; const ShiftWidth = Instruction.ShiftWidth; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; @@ -615,7 +617,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), - .byte_swap => @panic("TODO try self.airByteSwap(inst)"), + .byte_swap => try self.airByteSwap(inst), .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), @@ -1200,6 +1202,90 @@ fn airBreakpoint(self: *Self) !void { return self.finishAirBookkeeping(); } +fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + + // We have hardware byteswapper in SPARCv9, don't let mainstream compilers mislead you. + // That being said, the strategy to lower this is: + // - If src is an immediate, comptime-swap it. + // - If src is in memory then issue an LD*A with #ASI_P_[oppposite-endian] + // - If src is a register then issue an ST*A with #ASI_P_[oppposite-endian] + // to a stack slot, then follow with a normal load from said stack slot. + // This is because on some implementations, ASI-tagged memory operations are non-piplelinable + // and loads tend to have longer latency than stores, so the sequence will minimize stall. + // The result will always be either another immediate or stored in a register. + // TODO: Fold byteswap+store into a single ST*A and load+byteswap into a single LD*A. + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.air.typeOf(ty_op.operand); + switch (operand_ty.zigTypeTag()) { + .Vector => return self.fail("TODO byteswap for vectors", .{}), + .Int => { + const int_info = operand_ty.intInfo(self.target.*); + if (int_info.bits == 8) break :result operand; + + const abi_size = int_info.bits >> 3; + const abi_align = operand_ty.abiAlignment(self.target.*); + const opposite_endian_asi = switch (self.target.cpu.arch.endian()) { + Endian.Big => ASI.asi_primary_little, + Endian.Little => ASI.asi_primary, + }; + + switch (operand) { + .immediate => |imm| { + const swapped = switch (int_info.bits) { + 16 => @byteSwap(@intCast(u16, imm)), + 24 => @byteSwap(@intCast(u24, imm)), + 32 => @byteSwap(@intCast(u32, imm)), + 40 => @byteSwap(@intCast(u40, imm)), + 48 => @byteSwap(@intCast(u48, imm)), + 56 => @byteSwap(@intCast(u56, imm)), + 64 => @byteSwap(@intCast(u64, imm)), + else => return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}), + }; + break :result .{ .immediate = swapped }; + }, + .register => |reg| { + if (int_info.bits > 64 or @popCount(int_info.bits) != 1) + return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}); + + const off = try self.allocMem(inst, abi_size, abi_align); + const off_reg = try self.copyToTmpRegister(operand_ty, .{ .immediate = realStackOffset(off) }); + + try self.genStoreASI(reg, .sp, off_reg, abi_size, opposite_endian_asi); + try self.genLoad(reg, .sp, Register, off_reg, abi_size); + break :result reg; + }, + .memory => { + if (int_info.bits > 64 or @popCount(int_info.bits) != 1) + return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}); + + const addr_reg = try self.copyToTmpRegister(operand_ty, operand); + const dst_reg = try self.register_manager.allocReg(null, gp); + + try self.genLoadASI(dst_reg, addr_reg, .g0, abi_size, opposite_endian_asi); + break :result dst_reg; + }, + .stack_offset => |off| { + if (int_info.bits > 64 or @popCount(int_info.bits) != 1) + return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}); + + const off_reg = try self.copyToTmpRegister(operand_ty, .{ .immediate = realStackOffset(off) }); + const dst_reg = try self.register_manager.allocReg(null, gp); + + try self.genLoadASI(dst_reg, .sp, off_reg, abi_size, opposite_endian_asi); + break :result dst_reg; + }, + else => unreachable, + } + }, + else => unreachable, + } + }; + + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); @@ -3583,6 +3669,34 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty } } +fn genLoadASI(self: *Self, value_reg: Register, addr_reg: Register, off_reg: Register, abi_size: u64, asi: ASI) !void { + switch (abi_size) { + 1, 2, 4, 8 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .lduba, + 2 => .lduha, + 4 => .lduwa, + 8 => .ldxa, + else => unreachable, // unexpected abi size + }; + + _ = try self.addInst(.{ + .tag = tag, + .data = .{ + .mem_asi = .{ + .rd = value_reg, + .rs1 = addr_reg, + .rs2 = off_reg, + .asi = asi, + }, + }, + }); + }, + 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), + else => unreachable, + } +} + fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { switch (mcv) { .dead => unreachable, @@ -3942,6 +4056,34 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t } } +fn genStoreASI(self: *Self, value_reg: Register, addr_reg: Register, off_reg: Register, abi_size: u64, asi: ASI) !void { + switch (abi_size) { + 1, 2, 4, 8 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .stba, + 2 => .stha, + 4 => .stwa, + 8 => .stxa, + else => unreachable, // unexpected abi size + }; + + _ = try self.addInst(.{ + .tag = tag, + .data = .{ + .mem_asi = .{ + .rd = value_reg, + .rs1 = addr_reg, + .rs2 = off_reg, + .asi = asi, + }, + }, + }); + }, + 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), + else => unreachable, + } +} + fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const mcv: MCValue = switch (try codegen.genTypedValue( self.bin_file, @@ -4257,12 +4399,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { /// Turns stack_offset MCV into a real SPARCv9 stack offset usable for asm. fn realStackOffset(off: u32) u32 { return off - // SPARCv9 %sp points away from the stack by some amount. - + abi.stack_bias - // The first couple bytes of each stack frame is reserved - // for ABI and hardware purposes. - + abi.stack_reserved_area; - // Only after that we have the usable stack frame portion. + // SPARCv9 %sp points away from the stack by some amount. + + abi.stack_bias + // The first couple bytes of each stack frame is reserved + // for ABI and hardware purposes. + + abi.stack_reserved_area; + // Only after that we have the usable stack frame portion. } /// Caller must call `CallMCValues.deinit`. diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 7e71492af7..c0dbab5a14 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -91,6 +91,11 @@ pub fn emitMir( .lduw => try emit.mirArithmetic3Op(inst), .ldx => try emit.mirArithmetic3Op(inst), + .lduba => unreachable, + .lduha => unreachable, + .lduwa => unreachable, + .ldxa => unreachable, + .@"and" => try emit.mirArithmetic3Op(inst), .@"or" => try emit.mirArithmetic3Op(inst), .xor => try emit.mirArithmetic3Op(inst), @@ -127,6 +132,11 @@ pub fn emitMir( .stw => try emit.mirArithmetic3Op(inst), .stx => try emit.mirArithmetic3Op(inst), + .stba => unreachable, + .stha => unreachable, + .stwa => unreachable, + .stxa => unreachable, + .sub => try emit.mirArithmetic3Op(inst), .subcc => try emit.mirArithmetic3Op(inst), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index f854152a2f..f9a4056705 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -15,6 +15,7 @@ const bits = @import("bits.zig"); const Air = @import("../../Air.zig"); const Instruction = bits.Instruction; +const ASI = bits.Instruction.ASI; const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, @@ -70,6 +71,16 @@ pub const Inst = struct { lduw, ldx, + /// A.28 Load Integer from Alternate Space + /// This uses the mem_asi field. + /// Note that the ldda variant of this instruction is deprecated, so do not emit + /// it unless specifically requested (e.g. by inline assembly). + // TODO add other operations. + lduba, + lduha, + lduwa, + ldxa, + /// A.31 Logical Operations /// This uses the arithmetic_3op field. // TODO add other operations. @@ -132,6 +143,16 @@ pub const Inst = struct { stw, stx, + /// A.55 Store Integer into Alternate Space + /// This uses the mem_asi field. + /// Note that the stda variant of this instruction is deprecated, so do not emit + /// it unless specifically requested (e.g. by inline assembly). + // TODO add other operations. + stba, + stha, + stwa, + stxa, + /// A.56 Subtract /// This uses the arithmetic_3op field. // TODO add other operations. @@ -241,6 +262,15 @@ pub const Inst = struct { inst: Index, }, + /// ASI-tagged memory operations. + /// Used by e.g. ldxa, stxa + mem_asi: struct { + rd: Register, + rs1: Register, + rs2: Register = .g0, + asi: ASI, + }, + /// Membar mask, controls the barrier behavior /// Used by e.g. membar membar_mask: struct { From 75a1360cdd1745ac267c9a25b84ec7ee765d9262 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 20 Feb 2023 22:40:08 +0700 Subject: [PATCH 010/725] stage2: sparc64: Implement ASI load/store ops --- src/arch/sparc64/CodeGen.zig | 6 +++--- src/arch/sparc64/Emit.zig | 39 ++++++++++++++++++++++++++++-------- src/arch/sparc64/bits.zig | 32 +++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index a4fa7e179c..7c9476161c 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1254,7 +1254,7 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { try self.genStoreASI(reg, .sp, off_reg, abi_size, opposite_endian_asi); try self.genLoad(reg, .sp, Register, off_reg, abi_size); - break :result reg; + break :result .{ .register = reg }; }, .memory => { if (int_info.bits > 64 or @popCount(int_info.bits) != 1) @@ -1264,7 +1264,7 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { const dst_reg = try self.register_manager.allocReg(null, gp); try self.genLoadASI(dst_reg, addr_reg, .g0, abi_size, opposite_endian_asi); - break :result dst_reg; + break :result .{ .register = dst_reg }; }, .stack_offset => |off| { if (int_info.bits > 64 or @popCount(int_info.bits) != 1) @@ -1274,7 +1274,7 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { const dst_reg = try self.register_manager.allocReg(null, gp); try self.genLoadASI(dst_reg, .sp, off_reg, abi_size, opposite_endian_asi); - break :result dst_reg; + break :result .{ .register = dst_reg }; }, else => unreachable, } diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index c0dbab5a14..7d16105348 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -91,10 +91,10 @@ pub fn emitMir( .lduw => try emit.mirArithmetic3Op(inst), .ldx => try emit.mirArithmetic3Op(inst), - .lduba => unreachable, - .lduha => unreachable, - .lduwa => unreachable, - .ldxa => unreachable, + .lduba => try emit.mirMemASI(inst), + .lduha => try emit.mirMemASI(inst), + .lduwa => try emit.mirMemASI(inst), + .ldxa => try emit.mirMemASI(inst), .@"and" => try emit.mirArithmetic3Op(inst), .@"or" => try emit.mirArithmetic3Op(inst), @@ -132,10 +132,10 @@ pub fn emitMir( .stw => try emit.mirArithmetic3Op(inst), .stx => try emit.mirArithmetic3Op(inst), - .stba => unreachable, - .stha => unreachable, - .stwa => unreachable, - .stxa => unreachable, + .stba => try emit.mirMemASI(inst), + .stha => try emit.mirMemASI(inst), + .stwa => try emit.mirMemASI(inst), + .stxa => try emit.mirMemASI(inst), .sub => try emit.mirArithmetic3Op(inst), .subcc => try emit.mirArithmetic3Op(inst), @@ -378,6 +378,29 @@ fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirMemASI(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].mem_asi; + + const rd = data.rd; + const rs1 = data.rs1; + const rs2 = data.rs2; + const asi = data.asi; + + switch (tag) { + .lduba => try emit.writeInstruction(Instruction.lduba(rs1, rs2, asi, rd)), + .lduha => try emit.writeInstruction(Instruction.lduha(rs1, rs2, asi, rd)), + .lduwa => try emit.writeInstruction(Instruction.lduwa(rs1, rs2, asi, rd)), + .ldxa => try emit.writeInstruction(Instruction.ldxa(rs1, rs2, asi, rd)), + + .stba => try emit.writeInstruction(Instruction.stba(rs1, rs2, asi, rd)), + .stha => try emit.writeInstruction(Instruction.stha(rs1, rs2, asi, rd)), + .stwa => try emit.writeInstruction(Instruction.stwa(rs1, rs2, asi, rd)), + .stxa => try emit.writeInstruction(Instruction.stxa(rs1, rs2, asi, rd)), + else => unreachable, + } +} + fn mirMembar(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const mask = emit.mir.instructions.items(.data)[inst].membar_mask; diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 0446a84d6d..7c943626f9 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1229,6 +1229,22 @@ pub const Instruction = union(enum) { }; } + pub fn lduba(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_0001, rs1, rs2, rd, asi); + } + + pub fn lduha(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_0010, rs1, rs2, rd, asi); + } + + pub fn lduwa(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_0000, rs1, rs2, rd, asi); + } + + pub fn ldxa(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_1011, rs1, rs2, rd, asi); + } + pub fn @"and"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_0001, rs1, rs2, rd), @@ -1417,6 +1433,22 @@ pub const Instruction = union(enum) { }; } + pub fn stba(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_0101, rs1, rs2, rd, asi); + } + + pub fn stha(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_0110, rs1, rs2, rd, asi); + } + + pub fn stwa(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_0100, rs1, rs2, rd, asi); + } + + pub fn stxa(rs1: Register, rs2: Register, asi: ASI, rd: Register) Instruction { + return format3i(0b11, 0b01_1110, rs1, rs2, rd, asi); + } + pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), From ccc9b8caf632bf97f7765c1b1d7821118cf34008 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 24 Apr 2023 13:23:17 +0700 Subject: [PATCH 011/725] stage2: sparc64: Implement airPtrSliceLenPtr/airPtrSlicePtrPtr stubs --- src/arch/sparc64/CodeGen.zig | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 7c9476161c..226a0c6fc9 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -667,8 +667,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .slice_ptr => try self.airSlicePtr(inst), .slice_len => try self.airSliceLen(inst), - .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"), - .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), + .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), + .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), @@ -2238,6 +2238,38 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } +fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes = @divExact(ptr_bits, 8); + const mcv = try self.resolveInst(ty_op.operand); + switch (mcv) { + .dead, .unreach, .none => unreachable, + .ptr_stack_offset => |off| { + break :result MCValue{ .ptr_stack_offset = off - ptr_bytes }; + }, + else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), + } + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const mcv = try self.resolveInst(ty_op.operand); + switch (mcv) { + .dead, .unreach, .none => unreachable, + .ptr_stack_offset => |off| { + break :result MCValue{ .ptr_stack_offset = off }; + }, + else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), + } + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result = try self.resolveInst(un_op); From 0c9c9117bacfcff2b11ab658b89f192e27fc7c3d Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sat, 29 Apr 2023 03:35:16 +0200 Subject: [PATCH 012/725] std.builtin.CallModifier: add missing word --- lib/std/builtin.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 3523c5ac12..4a5e8a28d6 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -646,7 +646,7 @@ pub const CallModifier = enum { /// If this is not possible, a compile error is emitted instead. always_tail, - /// Guarantees that the call will inlined at the callsite. + /// Guarantees that the call will be inlined at the callsite. /// If this is not possible, a compile error is emitted instead. always_inline, From bd8b5c25ece2fa158196381004db6e655226576c Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sat, 29 Apr 2023 04:19:58 +0200 Subject: [PATCH 013/725] Sema: emit error for always_inline call of noinline function Fixes #15489 This also lays the groundwork for exposing the whether or not a function is noinline in std.builtin.Fn as an `is_noinline: bool` field if we ever want to do that. --- src/Sema.zig | 10 ++++++++-- src/type.zig | 13 ++++++++++++ .../compile_errors/bad_usage_of_call.zig | 20 +++++++++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8b47f1877b..207ccb41a9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6565,7 +6565,10 @@ fn analyzeCall( }; if (modifier == .never_inline and func_ty_info.cc == .Inline) { - return sema.fail(block, call_src, "no-inline call of inline function", .{}); + return sema.fail(block, call_src, "'never_inline' call of inline function", .{}); + } + if (modifier == .always_inline and func_ty_info.is_noinline) { + return sema.fail(block, call_src, "'always_inline' call of noinline function", .{}); } const gpa = sema.gpa; @@ -8784,7 +8787,8 @@ fn funcCommon( if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and alignment.? == 0 and address_space.? == target_util.defaultAddressSpace(target, .function) and - section == .default) + section == .default and + !is_noinline) { if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) { break :fn_ty Type.initTag(.fn_noreturn_no_args); @@ -9002,6 +9006,7 @@ fn funcCommon( .addrspace_is_generic = address_space == null, .is_var_args = var_args, .is_generic = is_generic, + .is_noinline = is_noinline, .noalias_bits = noalias_bits, }); }; @@ -19217,6 +19222,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in .cc = cc, .is_var_args = is_var_args, .is_generic = false, + .is_noinline = false, .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, diff --git a/src/type.zig b/src/type.zig index c7b2844970..82896800ed 100644 --- a/src/type.zig +++ b/src/type.zig @@ -666,6 +666,9 @@ pub const Type = extern union { if (a_info.is_generic != b_info.is_generic) return false; + if (a_info.is_noinline != b_info.is_noinline) + return false; + if (a_info.noalias_bits != b_info.noalias_bits) return false; @@ -1074,6 +1077,7 @@ pub const Type = extern union { } std.hash.autoHash(hasher, fn_info.is_var_args); std.hash.autoHash(hasher, fn_info.is_generic); + std.hash.autoHash(hasher, fn_info.is_noinline); std.hash.autoHash(hasher, fn_info.noalias_bits); std.hash.autoHash(hasher, fn_info.param_types.len); @@ -1454,6 +1458,7 @@ pub const Type = extern union { .alignment = payload.alignment, .is_var_args = payload.is_var_args, .is_generic = payload.is_generic, + .is_noinline = payload.is_noinline, .comptime_params = comptime_params.ptr, .align_is_generic = payload.align_is_generic, .cc_is_generic = payload.cc_is_generic, @@ -2069,6 +2074,9 @@ pub const Type = extern union { .function => { const fn_info = ty.fnInfo(); + if (fn_info.is_noinline) { + try writer.writeAll("noinline "); + } try writer.writeAll("fn("); for (fn_info.param_types, 0..) |param_ty, i| { if (i != 0) try writer.writeAll(", "); @@ -4863,6 +4871,7 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .is_noinline = false, .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, @@ -4877,6 +4886,7 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .is_noinline = false, .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, @@ -4891,6 +4901,7 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .is_noinline = false, .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, @@ -4905,6 +4916,7 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .is_noinline = false, .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, @@ -6367,6 +6379,7 @@ pub const Type = extern union { cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, + is_noinline: bool, align_is_generic: bool, cc_is_generic: bool, section_is_generic: bool, diff --git a/test/cases/compile_errors/bad_usage_of_call.zig b/test/cases/compile_errors/bad_usage_of_call.zig index c0b632bef6..3669cda3bf 100644 --- a/test/cases/compile_errors/bad_usage_of_call.zig +++ b/test/cases/compile_errors/bad_usage_of_call.zig @@ -14,14 +14,25 @@ export fn entry5(c: bool) void { var baz = if (c) &baz1 else &baz2; @call(.compile_time, baz, .{}); } +export fn entry6() void { + _ = @call(.always_inline, dummy, .{}); +} +export fn entry7() void { + _ = @call(.always_inline, dummy2, .{}); +} pub export fn entry() void { var call_me: *const fn () void = undefined; @call(.always_inline, call_me, .{}); } + fn foo() void {} -fn bar() callconv(.Inline) void {} +inline fn bar() void {} fn baz1() void {} fn baz2() void {} +noinline fn dummy() u32 { + return 0; +} +noinline fn dummy2() void {} // error // backend=stage2 @@ -30,7 +41,8 @@ fn baz2() void {} // :2:23: error: expected a tuple, found 'void' // :5:21: error: unable to perform 'never_inline' call at compile-time // :8:21: error: unable to perform 'never_tail' call at compile-time -// :11:5: error: no-inline call of inline function +// :11:5: error: 'never_inline' call of inline function // :15:26: error: modifier 'compile_time' requires a comptime-known function -// :19:27: error: modifier 'always_inline' requires a comptime-known function - +// :18:9: error: 'always_inline' call of noinline function +// :21:9: error: 'always_inline' call of noinline function +// :25:27: error: modifier 'always_inline' requires a comptime-known function From 440b3df702f1c8bfddabc1c594a3f49cf0011a63 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sat, 29 Apr 2023 11:54:21 -0400 Subject: [PATCH 014/725] main: parse --dynamicbase linker arg --- src/main.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.zig b/src/main.zig index 66b207aa43..45b0cf30b8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2072,6 +2072,8 @@ fn buildOutputType( linker_tsaware = true; } else if (mem.eql(u8, arg, "--nxcompat")) { linker_nxcompat = true; + } else if (mem.eql(u8, arg, "--dynamicbase")) { + linker_dynamicbase = true; } else if (mem.eql(u8, arg, "--no-dynamicbase")) { linker_dynamicbase = false; } else if (mem.eql(u8, arg, "--high-entropy-va")) { From 4f248e1b519b001cee67e461068245c142d38e73 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 29 Apr 2023 22:44:32 +0100 Subject: [PATCH 015/725] std.c:complete further more netbsd's mmap flags --- lib/std/c/netbsd.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index f70549775c..11126e7115 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -603,6 +603,17 @@ pub const MAP = struct { pub const ANON = 0x1000; pub const ANONYMOUS = ANON; pub const STACK = 0x2000; + + pub const ALIGNMENT_SHIFT = 24; + pub fn ALIGNED(n: u32) u32 { + return n << ALIGNMENT_SHIFT; + } + pub const ALIGNMENT_64KB = MAP.ALIGNED(0xff); + pub const ALIGNMENT_16MB = MAP.ALIGNED(16); + pub const ALIGNMENT_4GB = MAP.ALIGNED(32); + pub const ALIGNMENT_1TB = MAP.ALIGNED(40); + pub const ALIGNMENT_256TB = MAP.ALIGNED(48); + pub const ALIGNMENT_64PB = MAP.ALIGNED(56); }; pub const MSF = struct { From 3fb93fc8f2d3e755492a495fa69f65ae6615cab6 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 26 Apr 2023 22:04:55 +0100 Subject: [PATCH 016/725] std.c: freebsd add procctl exclusive x86_64 flags --- lib/std/c/freebsd.zig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index d3fa8f51c7..e5cdc0e715 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -2172,6 +2172,30 @@ pub const PROC = struct { pub const WX_MAPPINGS_PERMIT = 0x0001; pub const WX_MAPPINGS_DISALLOW_EXEC = 0x0002; pub const WX_MAPPINGS_ENFORCE = 0x80000000; + pub const PROCCTL_MD_MIN = 0x10000000; + // x86_64-only constants + pub const KPTI = switch (builtin.cpu.arch) { + .x86_64 => struct { + pub const CTL = PROC.PROCCTL_MD_MIND; + pub const STATUS = PROC.PROCCTL_MD_MIND + 1; + pub const CTL_ENABLE_ON_EXEC = 1; + pub const CTL_DISABLE_ON_EXEC = 2; + pub const STATUS_ACTIVE = 0x80000000; + }, + else => void, + }; + pub const LA = switch (builtin.cpu.arch) { + .x86_64 => struct { + pub const CTL = PROC.PROCCTL_MD_MIND + 2; + pub const STATUS = PROC.PROCCTL_MD_MIND + 3; + pub const CTL_LA48_ON_EXEC = 1; + pub const CTL_LA57_ON_EXEC = 2; + pub const CTL_DEFAULT_ON_EXEC = 3; + pub const STATUS_LA48 = 0x01000000; + pub const STATUS_LA57 = 0x02000000; + }, + else => void, + }; }; pub const PPROT = struct { From ab086b62cf585aa7fa73f2df3332e87f60226f86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Apr 2023 00:09:51 -0700 Subject: [PATCH 017/725] update zig1.wasm Looks like I might have messed up the wasm kernel in my recent branch causing some sporadic failures on the CI. This file was built the following way: 1. check out d0311e28b397d173f0d60c403985047ec952a172 2. do the cmake bootstrap 3. check out 57ea6207d3cb2db706bdc06c14605e4b901736dd 4. `zig build update-zig1` and stash those modified files 5. check out 440b3df702f1c8bfddabc1c594a3f49cf0011a63 (master) 6. do the cmake bootstrap 7. `zig build update-zig1` to produce this commit --- stage1/zig1.wasm | Bin 2454149 -> 2455067 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index b00227e45d31187e03274df3b2c07a91832b35c7..600e273ec172ff7a813830f128187f5c04ecd69a 100644 GIT binary patch delta 222707 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

AU5yV|g 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 YUnVZZHlpe4OI1agj{KN@tW-4=03}}DsQ>@~ From 91b4729962ddec96d1ee60d742326da828dae94a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Apr 2023 12:14:02 -0700 Subject: [PATCH 018/725] fix compilation error on 32-bit LLVM-enabled compiler builds Fixes a regression introduced in 9295355985202c267b4326b5a6e2ad5158b48e5d that caused 32-bit builds with `-Denable-llvm` to fail to build from source due to that common u64/usize casting issue. --- src/value.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value.zig b/src/value.zig index 16ccc0c642..503797abba 100644 --- a/src/value.zig +++ b/src/value.zig @@ -5390,7 +5390,7 @@ pub const Value = extern union { /// have the same value, return that byte value, otherwise null. pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module, value_buffer: *Payload.U64) !?Value { const target = mod.getTarget(); - const abi_size = ty.abiSize(target); + const abi_size = std.math.cast(usize, ty.abiSize(target)) orelse return null; assert(abi_size >= 1); const byte_buffer = try mod.gpa.alloc(u8, abi_size); defer mod.gpa.free(byte_buffer); From 339cae7fd0af5730673fcba7d97568af24b5aa4f Mon Sep 17 00:00:00 2001 From: Eric Joldasov Date: Tue, 10 Jan 2023 13:20:40 +0600 Subject: [PATCH 019/725] cmake: install zig to 'build_dir/stage3' during building This commit installs Zig to "build_dir/stage3" during building so that distros' can easily find binary and use it for testing/etc. This commit also splits "add_custom_target(stage3 ALL" and command that it invokes, so that it won't retry it during installation, as target will be considered not out-of-date. See also https://www.github.com/ziglang/zig/issues/14240#issuecomment-1374642063 --- CMakeLists.txt | 8 ++++++-- build.zig | 3 --- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c63bff6e0..23222d73c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -860,8 +860,12 @@ set(ZIG_BUILD_ARGS ) add_custom_target(stage3 ALL - COMMAND zig2 build compile ${ZIG_BUILD_ARGS} - DEPENDS zig2 + DEPENDS "${CMAKE_BINARY_DIR}/stage3/bin/zig" +) + +add_custom_command( + OUTPUT "${CMAKE_BINARY_DIR}/stage3/bin/zig" + COMMAND zig2 build --prefix "${CMAKE_BINARY_DIR}/stage3" ${ZIG_BUILD_ARGS} COMMENT STATUS "Building stage3" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) diff --git a/build.zig b/build.zig index 6cb3e35488..78345ac9e4 100644 --- a/build.zig +++ b/build.zig @@ -179,9 +179,6 @@ pub fn build(b: *std.Build) !void { exe.entitlements = entitlements; b.installArtifact(exe); - const compile_step = b.step("compile", "Build the self-hosted compiler"); - compile_step.dependOn(&exe.step); - test_step.dependOn(&exe.step); exe.single_threaded = single_threaded; From 1b432072b5ae6a70a00464b48b7ebf8610293fc3 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Fri, 10 Mar 2023 13:03:40 +1100 Subject: [PATCH 020/725] std.Build: detect and disallow top-level step name clashes --- lib/std/Build.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index f23b3ba5aa..a2c8a22e32 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -926,7 +926,12 @@ pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { }), .description = self.dupe(description), }; - self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM"); + const gop = self.top_level_steps.getOrPut(self.allocator, name) catch @panic("OOM"); + if (gop.found_existing) std.debug.panic("A top-level step with name \"{s}\" already exists", .{name}); + + gop.key_ptr.* = step_info.step.name; + gop.value_ptr.* = step_info; + return &step_info.step; } From ec6ffaa1e47388a59035277dafbe3d9999a80fca Mon Sep 17 00:00:00 2001 From: kcbanner Date: Thu, 27 Apr 2023 21:42:32 -0400 Subject: [PATCH 021/725] sema: improve the error message when coercing to []anyopaque --- src/Sema.zig | 3 ++- .../double_pointer_to_anyopaque_pointer.zig | 7 +++++++ .../compile_errors/pointer_to_anyopaque_slice.zig | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/pointer_to_anyopaque_slice.zig diff --git a/src/Sema.zig b/src/Sema.zig index 8593edf6cc..71a1215dcd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25335,7 +25335,7 @@ fn coerceExtra( // cast from *T and [*]T to *anyopaque // but don't do it if the source type is a double pointer - if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer) { + if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer) to_anyopaque: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; const elem_ty = inst_ty.elemType2(); if (elem_ty.zigTypeTag() == .Pointer or elem_ty.isPtrLikeOptional()) { @@ -25345,6 +25345,7 @@ fn coerceExtra( } }; break :pointer; } + if (dest_ty.isSlice()) break :to_anyopaque; if (inst_ty.isSlice()) { in_memory_result = .{ .slice_to_anyopaque = .{ .actual = inst_ty, diff --git a/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig b/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig index c7e54738d1..c695b7393d 100644 --- a/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig +++ b/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig @@ -15,6 +15,11 @@ pub export fn entry3() void { const ptr: *const anyopaque = x; _ = ptr; } +export fn entry4() void { + var a: []*u32 = undefined; + var b: []anyopaque = undefined; + b = a; +} // error // backend=stage2 @@ -27,3 +32,5 @@ pub export fn entry3() void { // :11:12: note: parameter type declared here // :15:35: error: expected type '*const anyopaque', found '*?*usize' // :15:35: note: cannot implicitly cast double pointer '*?*usize' to anyopaque pointer '*const anyopaque' +// :21:9: error: expected type '[]anyopaque', found '[]*u32' +// :21:9: note: cannot implicitly cast double pointer '[]*u32' to anyopaque pointer '[]anyopaque' diff --git a/test/cases/compile_errors/pointer_to_anyopaque_slice.zig b/test/cases/compile_errors/pointer_to_anyopaque_slice.zig new file mode 100644 index 0000000000..2c7334bedb --- /dev/null +++ b/test/cases/compile_errors/pointer_to_anyopaque_slice.zig @@ -0,0 +1,11 @@ +export fn x() void { + var a: *u32 = undefined; + var b: []anyopaque = undefined; + b = a; +} + +// error +// backend=stage2 +// target=native +// +// :4:9: error: expected type '[]anyopaque', found '*u32' From 94e30a756edc4c2182168dabd97d481b8aec0ff2 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sun, 30 Apr 2023 18:02:08 +0100 Subject: [PATCH 022/725] std: fix a bunch of typos The majority of these are in comments, some in doc comments which might affect the generated documentation, and a few in parameter names - nothing that should be breaking, however. --- lib/std/Build/Cache/DepTokenizer.zig | 2 +- lib/std/Build/CheckObjectStep.zig | 2 +- lib/std/Build/RunStep.zig | 2 +- lib/std/Build/Step.zig | 2 +- lib/std/Thread.zig | 4 ++-- lib/std/Thread/Condition.zig | 2 +- lib/std/Thread/Futex.zig | 2 +- lib/std/atomic/Atomic.zig | 2 +- lib/std/builtin.zig | 2 +- lib/std/c/darwin.zig | 8 ++++---- lib/std/c/solaris.zig | 2 +- lib/std/child_process.zig | 2 +- lib/std/compress/deflate/huffman_code.zig | 2 +- lib/std/crypto/25519/edwards25519.zig | 4 ++-- lib/std/crypto/25519/field.zig | 2 +- lib/std/crypto/Certificate.zig | 4 ++-- lib/std/crypto/errors.zig | 2 +- lib/std/crypto/kyber_d00.zig | 4 ++-- lib/std/event/lock.zig | 4 ++-- lib/std/event/loop.zig | 6 +++--- lib/std/fmt/parse_float/convert_fast.zig | 2 +- lib/std/fs/file.zig | 4 ++-- lib/std/fs/path.zig | 4 ++-- lib/std/hash_map.zig | 2 +- lib/std/heap.zig | 2 +- lib/std/heap/arena_allocator.zig | 2 +- lib/std/heap/general_purpose_allocator.zig | 4 ++-- lib/std/heap/memory_pool.zig | 2 +- lib/std/http.zig | 2 +- lib/std/json.zig | 2 +- lib/std/linked_list.zig | 2 +- lib/std/macho.zig | 12 ++++++------ lib/std/math/big/int.zig | 20 ++++++++++---------- lib/std/math/complex/tan.zig | 2 +- lib/std/math/log10.zig | 2 +- lib/std/math/sqrt.zig | 2 +- lib/std/mem.zig | 10 +++++----- lib/std/os.zig | 6 +++--- lib/std/os/linux.zig | 6 +++--- lib/std/os/linux/bpf/btf.zig | 6 +++--- lib/std/os/linux/seccomp.zig | 2 +- lib/std/os/windows.zig | 2 +- lib/std/packed_int_array.zig | 4 ++-- lib/std/pdb.zig | 2 +- lib/std/simd.zig | 2 +- lib/std/target/csky.zig | 14 +++++++------- lib/std/time.zig | 2 +- lib/std/valgrind/memcheck.zig | 4 ++-- lib/std/zig/parser_test.zig | 6 +++--- lib/std/zig/render.zig | 2 +- 50 files changed, 97 insertions(+), 97 deletions(-) diff --git a/lib/std/Build/Cache/DepTokenizer.zig b/lib/std/Build/Cache/DepTokenizer.zig index 8f9f2f81cd..c640fa4adc 100644 --- a/lib/std/Build/Cache/DepTokenizer.zig +++ b/lib/std/Build/Cache/DepTokenizer.zig @@ -829,7 +829,7 @@ test "error illegal char at position - bad target escape" { ); } -test "error illegal char at position - execting dollar_sign" { +test "error illegal char at position - expecting dollar_sign" { try depTokenizer("$\t", \\ERROR: illegal char \x09 at position 1: expecting '$' ); diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index fbeb87baee..e79ce9d3df 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -68,7 +68,7 @@ const SearchPhrase = struct { } }; -/// There two types of actions currently suported: +/// There two types of actions currently supported: /// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}` /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index ce2dd0234a..5d530c7a25 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -90,7 +90,7 @@ pub const StdIo = union(enum) { /// certain conditions, and the step will succeed or fail based on these /// conditions. /// Note that an explicit check for exit code 0 needs to be added to this - /// list if such a check is desireable. + /// list if such a check is desirable. check: std.ArrayList(Check), /// This RunStep is running a zig unit test binary and will communicate /// extra metadata over the IPC protocol. diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 0c05a64b1c..bdb500d99c 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -37,7 +37,7 @@ result_duration_ns: ?u64, result_peak_rss: usize, test_results: TestResults, -/// The return addresss associated with creation of this step that can be useful +/// The return address associated with creation of this step that can be useful /// to print along with debugging messages. debug_stack_trace: [n_debug_stack_frames]usize, diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 119328b2a8..d213b167dd 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -469,8 +469,8 @@ const UnsupportedImpl = struct { return unsupported(self); } - fn unsupported(unusued: anytype) noreturn { - _ = unusued; + fn unsupported(unused: anytype) noreturn { + _ = unused; @compileError("Unsupported operating system " ++ @tagName(target.os.tag)); } }; diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index bc579878da..2f8ab02d5e 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -261,7 +261,7 @@ const FutexImpl = struct { const signals = (state & signal_mask) / one_signal; // Reserves which waiters to wake up by incrementing the signals count. - // Therefor, the signals count is always less than or equal to the waiters count. + // Therefore, the signals count is always less than or equal to the waiters count. // We don't need to Futex.wake if there's nothing to wake up or if other wake() threads have reserved to wake up the current waiters. const wakeable = waiters - signals; if (wakeable == 0) { diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index 7efdc69d3b..7fbe49fea2 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -772,7 +772,7 @@ const PosixImpl = struct { waiter.event.wait(timeout) catch { // If we fail to cancel after a timeout, it means a wake() thread dequeued us and will wake us up. - // We must wait until the event is set as that's a signal that the wake() thread wont access the waiter memory anymore. + // We must wait until the event is set as that's a signal that the wake() thread won't access the waiter memory anymore. // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF. defer if (!cancelled) waiter.event.wait(null) catch unreachable; diff --git a/lib/std/atomic/Atomic.zig b/lib/std/atomic/Atomic.zig index 51e61ca628..5bb25e9bde 100644 --- a/lib/std/atomic/Atomic.zig +++ b/lib/std/atomic/Atomic.zig @@ -31,7 +31,7 @@ pub fn Atomic(comptime T: type) type { /// // Release ensures code before unref() happens-before the count is decremented as dropFn could be called by then. /// if (self.count.fetchSub(1, .Release)) { /// // Acquire ensures count decrement and code before previous unrefs()s happens-before we call dropFn below. - /// // NOTE: another alterative is to use .AcqRel on the fetchSub count decrement but it's extra barrier in possibly hot path. + /// // NOTE: another alternative is to use .AcqRel on the fetchSub count decrement but it's extra barrier in possibly hot path. /// self.count.fence(.Acquire); /// (self.dropFn)(self); /// } diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 4a5e8a28d6..ea513ae87e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -749,7 +749,7 @@ pub const PrefetchOptions = struct { /// 3 means high temporal locality. That is, the data should be kept in /// the cache as it is likely to be accessed again soon. locality: u2 = 3, - /// The cache that the prefetch should be preformed on. + /// The cache that the prefetch should be performed on. cache: Cache = .data, pub const Rw = enum(u1) { diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index cd1fbb9d9b..6e4ef59d38 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -178,13 +178,13 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b const private = struct { extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; - /// On x86_64 Darwin, fstat has to be manully linked with $INODE64 suffix to + /// On x86_64 Darwin, fstat has to be manually linked with $INODE64 suffix to /// force 64bit version. /// Note that this is fixed on aarch64 and no longer necessary. extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int; - /// On x86_64 Darwin, fstatat has to be manully linked with $INODE64 suffix to + /// On x86_64 Darwin, fstatat has to be manually linked with $INODE64 suffix to /// force 64bit version. /// Note that this is fixed on aarch64 and no longer necessary. extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int; @@ -2643,7 +2643,7 @@ pub const F = struct { pub const ADDSIGS = 59; /// add signature from same file (used by dyld for shared libs) pub const ADDFILESIGS = 61; - /// used in conjunction with F.NOCACHE to indicate that DIRECT, synchonous writes + /// used in conjunction with F.NOCACHE to indicate that DIRECT, synchronous writes /// should not be used (i.e. its ok to temporaily create cached pages) pub const NODIRECT = 62; ///Get the protection class of a file from the EA, returns int @@ -3866,4 +3866,4 @@ pub const MIN = struct { pub const ANONYMOUS = 0x80; }; -pub extern "c" fn mincore(addr: *align(std.mem.page_size) const anyopaque, lengh: usize, vec: [*]u8) c_int; +pub extern "c" fn mincore(addr: *align(std.mem.page_size) const anyopaque, length: usize, vec: [*]u8) c_int; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index c2c38f46ce..b2225c6d00 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -1822,7 +1822,7 @@ pub const file_obj = extern struct { name: [*:0]u8, }; -// struct ifreq is marked obsolete, with struct lifreq prefered for interface requests. +// struct ifreq is marked obsolete, with struct lifreq preferred for interface requests. // Here we alias lifreq to ifreq to avoid chainging existing code in os and x.os.IPv6. pub const SIOCGLIFINDEX = IOWR('i', 133, lifreq); pub const SIOCGIFINDEX = SIOCGLIFINDEX; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index f5ca72ed39..daaa1689bc 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1174,7 +1174,7 @@ fn windowsCreateProcess(app_name: [*:0]u16, cmd_line: [*:0]u16, envp_ptr: ?[*]u1 ); } -/// Case-insenstive UTF-16 lookup +/// Case-insensitive UTF-16 lookup fn windowsCreateProcessSupportsExtension(ext: []const u16) bool { if (ext.len != 4) return false; const State = enum { diff --git a/lib/std/compress/deflate/huffman_code.zig b/lib/std/compress/deflate/huffman_code.zig index cf1dd71c75..4827feb245 100644 --- a/lib/std/compress/deflate/huffman_code.zig +++ b/lib/std/compress/deflate/huffman_code.zig @@ -421,7 +421,7 @@ test "generate a Huffman code from an array of frequencies" { try testing.expectEqual(@as(u16, 0x3f), enc.codes[16].code); } -test "generate a Huffman code for the fixed litteral table specific to Deflate" { +test "generate a Huffman code for the fixed literal table specific to Deflate" { var enc = try generateFixedLiteralEncoding(testing.allocator); defer enc.deinit(); } diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index a8ca8e2fb6..b9ce14bc92 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -137,7 +137,7 @@ pub const Edwards25519 = struct { }; } - /// Substract two Edwards25519 points. + /// Subtract two Edwards25519 points. pub fn sub(p: Edwards25519, q: Edwards25519) Edwards25519 { return p.add(q.neg()); } @@ -529,7 +529,7 @@ test "edwards25519 packing/unpacking" { } } -test "edwards25519 point addition/substraction" { +test "edwards25519 point addition/subtraction" { var s1: [32]u8 = undefined; var s2: [32]u8 = undefined; crypto.random.bytes(&s1); diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index 1885f9286e..f6265cba48 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -179,7 +179,7 @@ pub const Fe = struct { return fe; } - /// Substract a field element + /// Subtract a field element pub inline fn sub(a: Fe, b: Fe) Fe { var fe = b; comptime var i = 0; diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig index ec4766322c..113d169cbc 100644 --- a/lib/std/crypto/Certificate.zig +++ b/lib/std/crypto/Certificate.zig @@ -1134,9 +1134,9 @@ pub const rsa = struct { return res; } - fn setBytes(r: *BigInt, bytes: []const u8, allcator: std.mem.Allocator) !void { + fn setBytes(r: *BigInt, bytes: []const u8, allocator: std.mem.Allocator) !void { try r.set(0); - var tmp = try BigInt.init(allcator); + var tmp = try BigInt.init(allocator); defer tmp.deinit(); for (bytes) |b| { try r.shiftLeft(r, 8); diff --git a/lib/std/crypto/errors.zig b/lib/std/crypto/errors.zig index 4d79055919..bf37faaf29 100644 --- a/lib/std/crypto/errors.zig +++ b/lib/std/crypto/errors.zig @@ -10,7 +10,7 @@ pub const IdentityElementError = error{IdentityElement}; /// Encoded input cannot be decoded pub const EncodingError = error{InvalidEncoding}; -/// The signature does't verify for the given message and public key +/// The signature doesn't verify for the given message and public key pub const SignatureVerificationError = error{SignatureVerificationFailed}; /// Both a public and secret key have been provided, but they are incompatible diff --git a/lib/std/crypto/kyber_d00.zig b/lib/std/crypto/kyber_d00.zig index b52f9f475d..dca4ab7ea7 100644 --- a/lib/std/crypto/kyber_d00.zig +++ b/lib/std/crypto/kyber_d00.zig @@ -80,7 +80,7 @@ //! m = Compress(Decompress(c_2, d_v) - s^T Decompress(c_1, d_u), 1). //! //! It it not straight-forward to see that this formula is correct. In -//! fact, there is negligable but non-zero probability that a ciphertext +//! fact, there is negligible but non-zero probability that a ciphertext //! does not decrypt correctly given by the DFP column in Table 4. This //! failure probability can be computed by a careful automated analysis //! of the probabilities involved, see kyber_failure.py of [SecEst]. @@ -640,7 +640,7 @@ fn montReduce(x: i32) i16 { // we have int32(int64(a)*int64(b)) = int32(a*b) and so the result is ok. const m = @truncate(i16, @truncate(i32, x *% qInv)); - // Note that x - m q is divisable by R; indeed modulo R we have + // Note that x - m q is divisible by R; indeed modulo R we have // // x - m q ≡ x - x q' q ≡ x - x q⁻¹ q ≡ x - x = 0. // diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig index 6e095b1890..9cda7f2aba 100644 --- a/lib/std/event/lock.zig +++ b/lib/std/event/lock.zig @@ -36,7 +36,7 @@ pub const Lock = struct { // self.head transitions from multiple stages depending on the value: // UNLOCKED -> LOCKED: - // acquire Lock ownership when theres no waiters + // acquire Lock ownership when there are no waiters // LOCKED -> : // Lock is already owned, enqueue first Waiter // -> : @@ -87,7 +87,7 @@ pub const Lock = struct { // self.head goes through the reverse transition from acquire(): // -> : - // pop a waiter from the queue to give Lock ownership when theres still others pending + // pop a waiter from the queue to give Lock ownership when there are still others pending // -> LOCKED: // pop the laster waiter from the queue, while also giving it lock ownership when awaken // LOCKED -> UNLOCKED: diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 34f74e10d2..eea1ec75be 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -903,7 +903,7 @@ pub const Loop = struct { } } - // TODO: use a tickless heirarchical timer wheel: + // TODO: use a tickless hierarchical timer wheel: // https://github.com/wahern/timeout/ const Waiters = struct { entries: std.atomic.Queue(anyframe), @@ -947,7 +947,7 @@ pub const Loop = struct { // starting from the head var head = self.entries.head orelse return null; - // traverse the list of waiting entires to + // traverse the list of waiting entries to // find the Node with the smallest `expires` field var min = head; while (head.next) |node| { @@ -1756,7 +1756,7 @@ test "std.event.Loop - runDetached" { try loop.runDetached(std.testing.allocator, testRunDetached, .{}); // Now we can start the event loop. The function will return only - // after all tasks have been completed, allowing us to synchonize + // after all tasks have been completed, allowing us to synchronize // with the previous runDetached. loop.run(); diff --git a/lib/std/fmt/parse_float/convert_fast.zig b/lib/std/fmt/parse_float/convert_fast.zig index 75875156fd..74beb745de 100644 --- a/lib/std/fmt/parse_float/convert_fast.zig +++ b/lib/std/fmt/parse_float/convert_fast.zig @@ -1,4 +1,4 @@ -//! Representation of a float as the signficant digits and exponent. +//! Representation of a float as the significant digits and exponent. //! The fast path algorithm using machine-sized integers and floats. //! //! This only works if both the mantissa and the exponent can be exactly diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 3f8e7185d0..23021a26f5 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -92,7 +92,7 @@ pub const File = struct { /// processes from acquiring a exclusive lock, but does not prevent /// other process from getting their own shared locks. /// - /// The lock is advisory, except on Linux in very specific cirsumstances[1]. + /// The lock is advisory, except on Linux in very specific circumstances[1]. /// This means that a process that does not respect the locking API can still get access /// to the file, despite the lock. /// @@ -156,7 +156,7 @@ pub const File = struct { /// processes from acquiring a exclusive lock, but does not prevent /// other process from getting their own shared locks. /// - /// The lock is advisory, except on Linux in very specific cirsumstances[1]. + /// The lock is advisory, except on Linux in very specific circumstances[1]. /// This means that a process that does not respect the locking API can still get access /// to the file, despite the lock. /// diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 4f3e05cd59..4c320ae5cf 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -105,13 +105,13 @@ fn joinSepMaybeZ(allocator: Allocator, separator: u8, comptime sepPredicate: fn return buf; } -/// Naively combines a series of paths with the native path seperator. +/// Naively combines a series of paths with the native path separator. /// Allocates memory for the result, which must be freed by the caller. pub fn join(allocator: Allocator, paths: []const []const u8) ![]u8 { return joinSepMaybeZ(allocator, sep, isSep, paths, false); } -/// Naively combines a series of paths with the native path seperator and null terminator. +/// Naively combines a series of paths with the native path separator and null terminator. /// Allocates memory for the result, which must be freed by the caller. pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 { const out = try joinSepMaybeZ(allocator, sep, isSep, paths, true); diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 65b07b8e77..df3446a2c0 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -690,7 +690,7 @@ pub fn HashMap( /// It achieves good performance with quite high load factors (by default, /// grow is triggered at 80% full) and only one byte of overhead per element. /// The struct itself is only 16 bytes for a small footprint. This comes at -/// the price of handling size with u32, which should be reasonnable enough +/// the price of handling size with u32, which should be reasonable enough /// for almost all uses. /// Deletions are achieved with tombstones. pub fn HashMapUnmanaged( diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 111314a1d9..7d2a66df1e 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -79,7 +79,7 @@ const CAllocator = struct { } // Thin wrapper around regular malloc, overallocate to account for - // alignment padding and store the orignal malloc()'ed pointer before + // alignment padding and store the original malloc()'ed pointer before // the aligned address. var unaligned_ptr = @ptrCast([*]u8, c.malloc(len + alignment - 1 + @sizeOf(usize)) orelse return null); const unaligned_addr = @ptrToInt(unaligned_ptr); diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig index d07c45cb70..9489bbb449 100644 --- a/lib/std/heap/arena_allocator.zig +++ b/lib/std/heap/arena_allocator.zig @@ -136,7 +136,7 @@ pub const ArenaAllocator = struct { it = next_it; } else null; std.debug.assert(maybe_first_node == null or maybe_first_node.?.next == null); - // reset the state before we try resizing the buffers, so we definitly have reset the arena to 0. + // reset the state before we try resizing the buffers, so we definitely have reset the arena to 0. self.state.end_index = 0; if (maybe_first_node) |first_node| { // perfect, no need to invoke the child_allocator diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig index de7bc7c423..ef88787fc6 100644 --- a/lib/std/heap/general_purpose_allocator.zig +++ b/lib/std/heap/general_purpose_allocator.zig @@ -130,7 +130,7 @@ pub const Config = struct { thread_safe: bool = !builtin.single_threaded, /// What type of mutex you'd like to use, for thread safety. - /// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and + /// when specified, the mutex type must have the same shape as `std.Thread.Mutex` and /// `DummyMutex`, and have no required fields. Specifying this field causes /// the `thread_safe` field to be ignored. /// @@ -1241,7 +1241,7 @@ test "realloc large object to small object" { try std.testing.expect(slice[16] == 0x34); } -test "overrideable mutexes" { +test "overridable mutexes" { var gpa = GeneralPurposeAllocator(.{ .MutexType = std.Thread.Mutex }){ .backing_allocator = std.testing.allocator, .mutex = std.Thread.Mutex{}, diff --git a/lib/std/heap/memory_pool.zig b/lib/std/heap/memory_pool.zig index 6ae90a3894..ca6eb7f518 100644 --- a/lib/std/heap/memory_pool.zig +++ b/lib/std/heap/memory_pool.zig @@ -150,7 +150,7 @@ test "memory pool: basic" { pool.destroy(p2); const p4 = try pool.create(); - // Assert memory resuse + // Assert memory reuse try std.testing.expect(p2 == p4); } diff --git a/lib/std/http.zig b/lib/std/http.zig index bb06bb1c19..744615d7d7 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -12,7 +12,7 @@ pub const Version = enum { }; /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods -/// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definiton +/// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definition /// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH pub const Method = enum { GET, diff --git a/lib/std/json.zig b/lib/std/json.zig index 432f4e6911..af928bc564 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1603,7 +1603,7 @@ fn parseInternal( if (fields_seen[i]) { switch (options.duplicate_field_behavior) { .UseFirst => { - // unconditonally ignore value. for comptime fields, this skips check against default_value + // unconditionally ignore value. for comptime fields, this skips check against default_value parseFree(field.type, try parse(field.type, tokens, child_options), child_options); found = true; break; diff --git a/lib/std/linked_list.zig b/lib/std/linked_list.zig index 577bae3d38..f9084a55c4 100644 --- a/lib/std/linked_list.zig +++ b/lib/std/linked_list.zig @@ -406,7 +406,7 @@ test "TailQueue concatenation" { } } - // Swap them back, this verifies that concating to an empty list works. + // Swap them back, this verifies that concatenating to an empty list works. list2.concatByMoving(&list1); // Traverse forwards. diff --git a/lib/std/macho.zig b/lib/std/macho.zig index a25ffca4fa..8bddd67023 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -356,7 +356,7 @@ pub const dysymtab_command = extern struct { // All the local relocation entries are grouped together (they are not // grouped by their module since they are only used if the object is moved - // from it staticly link edited address). + // from its statically link edited address). /// offset to local relocation entries locreloff: u32 = 0, @@ -418,7 +418,7 @@ pub const dyld_info_command = extern struct { // // The opcodes are a compressed way to encode the table by only // encoding when a column changes. In addition simple patterns - // like for runs of pointers initialzed to the same value can be + // like for runs of pointers initialized to the same value can be // encoded in a few bytes. /// file offset to binding info @@ -1141,7 +1141,7 @@ pub const MH_NOUNDEFS = 0x1; /// the object file is the output of an incremental link against a base file and can't be link edited again pub const MH_INCRLINK = 0x2; -/// the object file is input for the dynamic linker and can't be staticly link edited again +/// the object file is input for the dynamic linker and can't be statically link edited again pub const MH_DYLDLINK = 0x4; /// the object file's undefined references are bound by the dynamic linker when loaded. @@ -1162,7 +1162,7 @@ pub const MH_TWOLEVEL = 0x80; /// the executable is forcing all images to use flat name space bindings pub const MH_FORCE_FLAT = 0x100; -/// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used. +/// this umbrella guarantees no multiple definitions of symbols in its sub-images so the two-level namespace hints can always be used. pub const MH_NOMULTIDEFS = 0x200; /// do not have dyld notify the prebinding agent about this executable @@ -1658,7 +1658,7 @@ pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08; pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10; // An indirect symbol table entry is simply a 32bit index into the symbol table -// to the symbol that the pointer or stub is refering to. Unless it is for a +// to the symbol that the pointer or stub is referring to. Unless it is for a // non-lazy symbol pointer section for a defined symbol which strip(1) as // removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the // symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. @@ -1741,7 +1741,7 @@ pub const CS_LINKER_SIGNED: u32 = 0x20000; pub const CS_EXECSEG_MAIN_BINARY: u32 = 0x1; -/// This CodeDirectory is tailored specfically at version 0x20400. +/// This CodeDirectory is tailored specifically at version 0x20400. pub const CodeDirectory = extern struct { /// Magic number (CSMAGIC_CODEDIRECTORY) magic: u32, diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 2406d669ec..b01d9b04ff 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -408,7 +408,7 @@ pub const Mutable = struct { } /// Base implementation for addition. Adds `max(a.limbs.len, b.limbs.len)` elements from a and b, - /// and returns whether any overflow occured. + /// and returns whether any overflow occurred. /// r, a and b may be aliases. /// /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`. @@ -467,7 +467,7 @@ pub const Mutable = struct { const req_limbs = calcTwosCompLimbCount(bit_count); // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine - // if an overflow occured. + // if an overflow occurred. const x = Const{ .positive = a.positive, .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)], @@ -512,7 +512,7 @@ pub const Mutable = struct { const req_limbs = calcTwosCompLimbCount(bit_count); // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine - // if an overflow occured. + // if an overflow occurred. const x = Const{ .positive = a.positive, .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)], @@ -544,7 +544,7 @@ pub const Mutable = struct { } /// Base implementation for subtraction. Subtracts `max(a.limbs.len, b.limbs.len)` elements from a and b, - /// and returns whether any overflow occured. + /// and returns whether any overflow occurred. /// r, a and b may be aliases. /// /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`. @@ -605,7 +605,7 @@ pub const Mutable = struct { r.add(a, b.negate()); } - /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured. + /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occurred. /// /// r, a and b may be aliases /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by @@ -1141,7 +1141,7 @@ pub const Mutable = struct { return; } - // Generate a mask with the bits to check in the most signficant limb. We'll need to check + // Generate a mask with the bits to check in the most significant limb. We'll need to check // all bits with equal or more significance than checkbit. // const msb = @truncate(Log2Limb, checkbit); // const checkmask = (@as(Limb, 1) << msb) -% 1; @@ -2037,7 +2037,7 @@ pub const Const = struct { add_res = ov[0]; carry = ov[1]; sum += @popCount(add_res); - remaining_bits -= limb_bits; // Asserted not to undeflow by fitsInTwosComp + remaining_bits -= limb_bits; // Asserted not to underflow by fitsInTwosComp } // The most significant limb may have fewer than @bitSizeOf(Limb) meaningful bits, @@ -2813,7 +2813,7 @@ pub const Managed = struct { r.setMetadata(m.positive, m.len); } - /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occured. + /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occurred. /// /// r, a and b may be aliases. /// @@ -2856,7 +2856,7 @@ pub const Managed = struct { r.setMetadata(m.positive, m.len); } - /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured. + /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occurred. /// /// r, a and b may be aliases. /// @@ -4010,7 +4010,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void { assert(r.len >= 2 * x_norm.len + 1); // Compute the square of a N-limb bigint with only (N^2 + N)/2 - // multiplications by exploting the symmetry of the coefficients around the + // multiplications by exploiting the symmetry of the coefficients around the // diagonal: // // a b c * diff --git a/lib/std/math/complex/tan.zig b/lib/std/math/complex/tan.zig index 9e1025f74f..a0ce5d18e0 100644 --- a/lib/std/math/complex/tan.zig +++ b/lib/std/math/complex/tan.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -/// Returns the tanget of z. +/// Returns the tangent of z. pub fn tan(z: anytype) Complex(@TypeOf(z.re)) { const T = @TypeOf(z.re); const q = Complex(T).init(-z.im, z.re); diff --git a/lib/std/math/log10.zig b/lib/std/math/log10.zig index b1ecb9ad2b..6b5758763c 100644 --- a/lib/std/math/log10.zig +++ b/lib/std/math/log10.zig @@ -58,7 +58,7 @@ pub fn log10_int(x: anytype) Log2Int(@TypeOf(x)) { var log: u32 = 0; inline for (0..11) |i| { - // Unnecesary branches should be removed by the compiler + // Unnecessary branches should be removed by the compiler if (bit_size > (1 << (11 - i)) * 5 * @log2(10.0) and val >= pow10((1 << (11 - i)) * 5)) { const num_digits = (1 << (11 - i)) * 5; val /= pow10(num_digits); diff --git a/lib/std/math/sqrt.zig b/lib/std/math/sqrt.zig index e642f8a309..926582034e 100644 --- a/lib/std/math/sqrt.zig +++ b/lib/std/math/sqrt.zig @@ -11,7 +11,7 @@ const maxInt = std.math.maxInt; /// - sqrt(+-0) = +-0 /// - sqrt(x) = nan if x < 0 /// - sqrt(nan) = nan -/// TODO Decide if all this logic should be implemented directly in the @sqrt bultin function. +/// TODO Decide if all this logic should be implemented directly in the @sqrt builtin function. pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) { const T = @TypeOf(x); switch (@typeInfo(T)) { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 557e39cdfc..6cc60ddad2 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -114,7 +114,7 @@ pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator) /// An allocator helper function. Adjusts an allocation length satisfy `len_align`. /// `full_len` should be the full capacity of the allocation which may be greater -/// than the `len` that was requsted. This function should only be used by allocators +/// than the `len` that was requested. This function should only be used by allocators /// that are unaffected by `len_align`. pub fn alignAllocLen(full_len: usize, alloc_len: usize, len_align: u29) usize { assert(alloc_len > 0); @@ -427,7 +427,7 @@ pub fn zeroInit(comptime T: type, init: anytype) T { .Struct => |init_info| { if (init_info.is_tuple) { if (init_info.fields.len > struct_info.fields.len) { - @compileError("Tuple initializer has more elments than there are fields in `" ++ @typeName(T) ++ "`"); + @compileError("Tuple initializer has more elements than there are fields in `" ++ @typeName(T) ++ "`"); } } else { inline for (init_info.fields) |field| { @@ -668,7 +668,7 @@ test "Span" { /// Takes a sentinel-terminated pointer and returns a slice, iterating over the /// memory to find the sentinel and determine the length. -/// Ponter attributes such as const are preserved. +/// Pointer attributes such as const are preserved. /// `[*c]` pointers are assumed to be non-null and 0-terminated. pub fn span(ptr: anytype) Span(@TypeOf(ptr)) { if (@typeInfo(@TypeOf(ptr)) == .Optional) { @@ -1835,7 +1835,7 @@ test "writeIntBig and writeIntLittle" { } /// Swap the byte order of all the members of the fields of a struct -/// (Changing their endianess) +/// (Changing their endianness) pub fn byteSwapAllFields(comptime S: type, ptr: *S) void { if (@typeInfo(S) != .Struct) @compileError("byteSwapAllFields expects a struct as the first argument"); inline for (std.meta.fields(S)) |f| { @@ -3168,7 +3168,7 @@ test "replace" { try testing.expectEqualStrings(expected, output[0..expected.len]); } -/// Replace all occurences of `needle` with `replacement`. +/// Replace all occurrences of `needle` with `replacement`. pub fn replaceScalar(comptime T: type, slice: []T, needle: T, replacement: T) void { for (slice, 0..) |e, i| { if (e == needle) { diff --git a/lib/std/os.zig b/lib/std/os.zig index 32c73916d4..7759f1403f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -659,7 +659,7 @@ pub fn exit(status: u8) noreturn { linux.exit_group(status); } if (builtin.os.tag == .uefi) { - // exit() is only avaliable if exitBootServices() has not been called yet. + // exit() is only available if exitBootServices() has not been called yet. // This call to exit should not fail, so we don't care about its return value. if (uefi.system_table.boot_services) |bs| { _ = bs.exit(uefi.handle, @intToEnum(uefi.Status, status), 0, null); @@ -2978,7 +2978,7 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { } } -/// Windows-only. Same as `chdir` except the paramter is WTF16 encoded. +/// Windows-only. Same as `chdir` except the parameter is WTF16 encoded. pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void { windows.SetCurrentDirectory(dir_path) catch |err| switch (err) { error.NoDevice => return error.FileSystem, @@ -6925,7 +6925,7 @@ pub const PrctlError = error{ /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT, /// or PR_MPX_DISABLE_MANAGEMENT UnsupportedFeature, - /// Can only occur wih PR_SET_FP_MODE + /// Can only occur with PR_SET_FP_MODE OperationNotSupported, PermissionDenied, } || UnexpectedError; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 901bb8dbb3..4ffaeb54a4 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -472,7 +472,7 @@ pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: i64) usize { @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, - // Kernel expects the offset is splitted into largest natural word-size. + // Kernel expects the offset is split into largest natural word-size. // See following link for detail: // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=601cc11d054ae4b5e9b5babec3d8e4667a2cb9b5 @truncate(usize, offset_u), @@ -3912,7 +3912,7 @@ pub const io_uring_cqe = extern struct { /// If set, the upper 16 bits are the buffer ID pub const IORING_CQE_F_BUFFER = 1 << 0; /// If set, parent SQE will generate more CQE entries. -/// Avaiable since Linux 5.13. +/// Available since Linux 5.13. pub const IORING_CQE_F_MORE = 1 << 1; /// If set, more data to read after socket recv pub const IORING_CQE_F_SOCK_NONEMPTY = 1 << 2; @@ -4234,7 +4234,7 @@ pub const tcp_fastopen_client_fail = enum { pub const TCPI_OPT_TIMESTAMPS = 1; pub const TCPI_OPT_SACK = 2; pub const TCPI_OPT_WSCALE = 4; -/// ECN was negociated at TCP session init +/// ECN was negotiated at TCP session init pub const TCPI_OPT_ECN = 8; /// we received at least one packet with ECT pub const TCPI_OPT_ECN_SEEN = 16; diff --git a/lib/std/os/linux/bpf/btf.zig b/lib/std/os/linux/bpf/btf.zig index 7b85a618b3..39d25014da 100644 --- a/lib/std/os/linux/bpf/btf.zig +++ b/lib/std/os/linux/bpf/btf.zig @@ -109,7 +109,7 @@ pub const Enum64 = extern struct { val_hi32: i32, }; -/// array kind is followd by this struct +/// array kind is followed by this struct pub const Array = extern struct { typ: u32, index_type: u32, @@ -149,13 +149,13 @@ pub const FuncLinkage = enum { external, }; -/// var kind is followd by a single Var struct to describe additional +/// var kind is followed by a single Var struct to describe additional /// information related to the variable such as its linkage pub const Var = extern struct { linkage: u32, }; -/// datasec kind is followed by multible VarSecInfo to describe all Var kind +/// datasec kind is followed by multiple VarSecInfo to describe all Var kind /// types it contains along with it's in-section offset as well as size. pub const VarSecInfo = extern struct { typ: u32, diff --git a/lib/std/os/linux/seccomp.zig b/lib/std/os/linux/seccomp.zig index 23dbb6ee38..f10cb84aa0 100644 --- a/lib/std/os/linux/seccomp.zig +++ b/lib/std/os/linux/seccomp.zig @@ -65,7 +65,7 @@ //! //! Unfortunately, there is no easy solution for issue 5. The most reliable //! strategy is to keep testing; test newer Zig versions, different libcs, -//! different distros, and design your filter to accomidate all of them. +//! different distros, and design your filter to accommodate all of them. //! Alternatively, you could inject a filter at runtime. Since filters are //! preserved across execve(2), a filter could be setup before executing your //! program, without your program having any knowledge of this happening. This diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 6d654f1c20..22ffc850e6 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -2114,7 +2114,7 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: /// and you get an unexpected error. pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { if (std.os.unexpected_error_tracing) { - // 614 is the length of the longest windows error desciption + // 614 is the length of the longest windows error description var buf_wstr: [614]WCHAR = undefined; var buf_utf8: [614]u8 = undefined; const len = kernel32.FormatMessageW( diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig index 8004d223f7..10d8af0575 100644 --- a/lib/std/packed_int_array.zig +++ b/lib/std/packed_int_array.zig @@ -33,7 +33,7 @@ pub fn PackedIntIo(comptime Int: type, comptime endian: Endian) type { // In the worst case, this is the number of bytes we need to touch // to read or write a value, as bits. To calculate for int_bits > 1, // set aside 2 bits to touch the first and last bytes, then divide - // by 8 to see how many bytes can be filled up inbetween. + // by 8 to see how many bytes can be filled up in between. const max_io_bits = switch (int_bits) { 0 => 0, 1 => 8, @@ -298,7 +298,7 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: Endian) type { } /// Initialize a packed slice using the memory at `bytes`, with `int_count` - /// elements. `bytes` must be large enough to accomodate the requested + /// elements. `bytes` must be large enough to accommodate the requested /// count. pub fn init(bytes: []u8, int_count: usize) Self { debug.assert(bytes.len >= bytesRequired(int_count)); diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index fdd162a34f..5bc836b08e 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -912,7 +912,7 @@ const Msf = struct { const stream_sizes = try allocator.alloc(u32, stream_count); defer allocator.free(stream_sizes); - // Microsoft's implementation uses @as(u32, -1) for inexistant streams. + // Microsoft's implementation uses @as(u32, -1) for inexistent streams. // These streams are not used, but still participate in the file // and must be taken into account when resolving stream indices. const Nil = 0xFFFFFFFF; diff --git a/lib/std/simd.zig b/lib/std/simd.zig index c2e0d9f972..905925fb78 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -159,7 +159,7 @@ pub fn interlace(vecs: anytype) @Vector(vectorLength(@TypeOf(vecs[0])) * vecs.le } /// The contents of `interlaced` is evenly split between vec_count vectors that are returned as an array. They "take turns", -/// recieving one element from `interlaced` at a time. +/// receiving one element from `interlaced` at a time. pub fn deinterlace( comptime vec_count: usize, interlaced: anytype, diff --git a/lib/std/target/csky.zig b/lib/std/target/csky.zig index 10386b31b8..0a2812c6d9 100644 --- a/lib/std/target/csky.zig +++ b/lib/std/target/csky.zig @@ -214,7 +214,7 @@ pub const all_features = blk: { }; result[@enumToInt(Feature.dsp_silan)] = .{ .llvm_name = "dsp_silan", - .description = "Enable DSP Silan instrutions", + .description = "Enable DSP Silan instructions", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.dspe60)] = .{ @@ -224,7 +224,7 @@ pub const all_features = blk: { }; result[@enumToInt(Feature.dspv2)] = .{ .llvm_name = "dspv2", - .description = "Enable DSP V2.0 instrutions", + .description = "Enable DSP V2.0 instructions", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.e1)] = .{ @@ -243,7 +243,7 @@ pub const all_features = blk: { }; result[@enumToInt(Feature.edsp)] = .{ .llvm_name = "edsp", - .description = "Enable DSP instrutions", + .description = "Enable DSP instructions", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.elrw)] = .{ @@ -333,12 +333,12 @@ pub const all_features = blk: { }; result[@enumToInt(Feature.hwdiv)] = .{ .llvm_name = "hwdiv", - .description = "Enable divide instrutions", + .description = "Enable divide instructions", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.istack)] = .{ .llvm_name = "istack", - .description = "Enable interrput attribute", + .description = "Enable interrupt attribute", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.java)] = .{ @@ -362,7 +362,7 @@ pub const all_features = blk: { }; result[@enumToInt(Feature.multiple_stld)] = .{ .llvm_name = "multiple_stld", - .description = "Enable multiple load/store instrutions", + .description = "Enable multiple load/store instructions", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.nvic)] = .{ @@ -372,7 +372,7 @@ pub const all_features = blk: { }; result[@enumToInt(Feature.pushpop)] = .{ .llvm_name = "pushpop", - .description = "Enable push/pop instrutions", + .description = "Enable push/pop instructions", .dependencies = featureSet(&[_]Feature{}), }; result[@enumToInt(Feature.smart)] = .{ diff --git a/lib/std/time.zig b/lib/std/time.zig index 5f86c25b8d..3eb342fa85 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -258,7 +258,7 @@ pub const Instant = struct { /// A monotonic, high performance timer. /// -/// Timer.start() is used to initalize the timer +/// Timer.start() is used to initialize the timer /// and gives the caller an opportunity to check for the existence of a supported clock. /// Once a supported clock is discovered, /// it is assumed that it will be available for the duration of the Timer's use. diff --git a/lib/std/valgrind/memcheck.zig b/lib/std/valgrind/memcheck.zig index 489abac2f6..25081510cd 100644 --- a/lib/std/valgrind/memcheck.zig +++ b/lib/std/valgrind/memcheck.zig @@ -77,7 +77,7 @@ pub fn discard(blkindex: usize) bool { } /// Check that memory at qzz.ptr is addressable for qzz.len bytes. -/// If suitable addressibility is not established, Valgrind prints an +/// If suitable addressability is not established, Valgrind prints an /// error message and returns the address of the first offending byte. /// Otherwise it returns zero. pub fn checkMemIsAddressable(qzz: []u8) usize { @@ -85,7 +85,7 @@ pub fn checkMemIsAddressable(qzz: []u8) usize { } /// Check that memory at qzz.ptr is addressable and defined for -/// qzz.len bytes. If suitable addressibility and definedness are not +/// qzz.len bytes. If suitable addressability and definedness are not /// established, Valgrind prints an error message and returns the /// address of the first offending byte. Otherwise it returns zero. pub fn checkMemIsDefined(qzz: []u8) usize { diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 5ad8f8a07e..9176e14480 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -239,7 +239,7 @@ test "zig fmt: file ends in comment after var decl" { ); } -test "zig fmt: if statment" { +test "zig fmt: if statement" { try testCanonical( \\test "" { \\ if (optional()) |some| @@ -529,7 +529,7 @@ test "zig fmt: remove empty lines at start/end of block" { ); } -test "zig fmt: allow empty line before commment at start of block" { +test "zig fmt: allow empty line before comment at start of block" { try testCanonical( \\test { \\ @@ -4371,7 +4371,7 @@ test "zig fmt: same line doc comment returns error" { \\const Foo = struct{ \\ bar: u32, /// comment \\ foo: u32, /// comment - \\ /// commment + \\ /// comment \\}; \\ \\const a = 42; /// comment diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index d8a8c8e9ed..367d06f7c6 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -681,7 +681,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, try renderToken(ais, tree, switch_token, .space); // switch keyword try renderToken(ais, tree, switch_token + 1, .none); // lparen - try renderExpression(gpa, ais, tree, condition, .none); // condtion expression + try renderExpression(gpa, ais, tree, condition, .none); // condition expression try renderToken(ais, tree, rparen, .space); // rparen ais.pushIndentNextLine(); From 6ae19fa48d7853430ba45f2de90550cd48cf5b4c Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 29 Apr 2023 17:44:56 +0100 Subject: [PATCH 023/725] std.c: add essential freebsd's capsicum api subset. --- lib/std/c/freebsd.zig | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index e5cdc0e715..28e95d0414 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -476,10 +476,43 @@ pub const sockaddr = extern struct { pub const CAP_RIGHTS_VERSION = 0; -pub const cap_rights = extern struct { - rights: [CAP_RIGHTS_VERSION + 2]u64, +pub const cap_rights_t = extern struct { + cr_rights: [CAP_RIGHTS_VERSION + 2]u64, }; +pub const CAP = struct { + pub fn RIGHT(idx: u6, bit: u64) u64 { + return (@intCast(u64, 1) << (57 + idx)) | bit; + } + pub const READ = CAP.RIGHT(0, 0x0000000000000001); + pub const WRITE = CAP.RIGHT(0, 0x0000000000000002); + pub const SEEK_TELL = CAP.RIGHT(0, 0x0000000000000004); + pub const SEEK = CAP.SEEK_TELL | 0x0000000000000008; + pub const PREAD = CAP.SEEK | CAP.READ; + pub const PWRITE = CAP.SEEK | CAP.WRITE; + pub const MMAP = CAP.RIGHT(0, 0x0000000000000010); + pub const MMAP_R = CAP.MMAP | CAP.SEEK | CAP.READ; + pub const MMAP_W = CAP.MMAP | CAP.SEEK | CAP.WRITE; + pub const MMAP_X = CAP.MMAP | CAP.SEEK | 0x0000000000000020; + pub const MMAP_RW = CAP.MMAP_R | CAP.MMAP_W; + pub const MMAP_RX = CAP.MMAP_R | CAP.MMAP_X; + pub const MMAP_WX = CAP.MMAP_W | CAP.MMAP_X; + pub const MMAP_RWX = CAP.MMAP_R | CAP.MMAP_W | CAP.MMAP_X; + pub const CREATE = CAP.RIGHT(0, 0x0000000000000040); + pub const FEXECVE = CAP.RIGHT(0, 0x0000000000000080); + pub const FSYNC = CAP.RIGHT(0, 0x0000000000000100); + pub const FTRUNCATE = CAP.RIGHT(0, 0x0000000000000200); +}; + +pub extern "c" fn __cap_rights_init(version: c_int, rights: ?*cap_rights_t, ...) ?*cap_rights_t; +pub extern "c" fn __cap_rights_set(rights: ?*cap_rights_t, ...) ?*cap_rights_t; +pub extern "c" fn __cap_rights_clear(rights: ?*cap_rights_t, ...) ?*cap_rights_t; +pub extern "c" fn __cap_rights_merge(dst: ?*cap_rights_t, src: ?*const cap_rights_t) ?*cap_rights_t; +pub extern "c" fn __cap_rights_remove(dst: ?*cap_rights_t, src: ?*const cap_rights_t) ?*cap_rights_t; +pub extern "c" fn __cap_rights_contains(dst: ?*const cap_rights_t, src: ?*const cap_rights_t) bool; +pub extern "c" fn __cap_rights_is_set(rights: ?*const cap_rights_t, ...) bool; +pub extern "c" fn __cap_rights_is_valid(rights: ?*const cap_rights_t) bool; + pub const kinfo_file = extern struct { /// Size of this record. /// A zero value is for the sentinel record at the end of an array. @@ -581,7 +614,7 @@ pub const kinfo_file = extern struct { // Reserved for future use. _spare: c_int, /// Capability rights. - cap_rights: cap_rights, + cap_rights: cap_rights_t, /// Reserved for future cap_rights _cap_spare: u64, /// Path to file, if any. From e963793e37f93d84f1e5295d309ebe0c738b663d Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 1 May 2023 01:03:46 -0400 Subject: [PATCH 024/725] Updates std.meta.intToEnum to support non-exhaustive enums (#15491) This was preventing `std.json` from deserializing non-exhaustive enums. --- lib/std/meta.zig | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 97c2ff4fb0..7be3b71347 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -965,18 +965,33 @@ test "intToEnum with error return" { A, B, }; + const E3 = enum(i8) { A, _ }; var zero: u8 = 0; var one: u16 = 1; try testing.expect(intToEnum(E1, zero) catch unreachable == E1.A); try testing.expect(intToEnum(E2, one) catch unreachable == E2.B); + try testing.expect(intToEnum(E3, zero) catch unreachable == E3.A); + try testing.expect(intToEnum(E3, 127) catch unreachable == @intToEnum(E3, 127)); + try testing.expect(intToEnum(E3, -128) catch unreachable == @intToEnum(E3, -128)); try testing.expectError(error.InvalidEnumTag, intToEnum(E1, one)); + try testing.expectError(error.InvalidEnumTag, intToEnum(E3, 128)); + try testing.expectError(error.InvalidEnumTag, intToEnum(E3, -129)); } pub const IntToEnumError = error{InvalidEnumTag}; pub fn intToEnum(comptime EnumTag: type, tag_int: anytype) IntToEnumError!EnumTag { - inline for (@typeInfo(EnumTag).Enum.fields) |f| { + const enum_info = @typeInfo(EnumTag).Enum; + + if (!enum_info.is_exhaustive) { + if (std.math.cast(enum_info.tag_type, tag_int)) |tag| { + return @intToEnum(EnumTag, tag); + } + return error.InvalidEnumTag; + } + + inline for (enum_info.fields) |f| { const this_tag_value = @field(EnumTag, f.name); if (tag_int == @enumToInt(this_tag_value)) { return this_tag_value; From 0f0f005e927be5e9d813e40c8c6c6580fd4f5697 Mon Sep 17 00:00:00 2001 From: Martin Wickham Date: Sat, 4 Jun 2022 17:34:21 -0500 Subject: [PATCH 025/725] std.windows: use posix semantics to delete files, if available Justification: When a file is deleted on Windows, it may not be immediately removed from the directory. This can cause problems with future scans of that directory, which will see the partially deleted file. Under some workloads and system configurations, Windows files may appear to be deleted immediately. This is the PR with requested fixup. Thanks to @SpexGuy for the original PR. --- lib/std/os/windows.zig | 75 +++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 22ffc850e6..37ec7f09a3 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -937,25 +937,50 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil .DELETE_PENDING => return, else => return unexpectedStatus(rc), } - var file_dispo = FILE_DISPOSITION_INFORMATION{ - .DeleteFile = TRUE, - }; - rc = ntdll.NtSetInformationFile( - tmp_handle, - &io, - &file_dispo, - @sizeOf(FILE_DISPOSITION_INFORMATION), - .FileDispositionInformation, - ); - CloseHandle(tmp_handle); - switch (rc) { - .SUCCESS => return, - .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, - .INVALID_PARAMETER => unreachable, - .CANNOT_DELETE => return error.AccessDenied, - .MEDIA_WRITE_PROTECTED => return error.AccessDenied, - .ACCESS_DENIED => return error.AccessDenied, - else => return unexpectedStatus(rc), + defer CloseHandle(tmp_handle); + if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) { + // Deletion with posix semantics. + var info = FILE_DISPOSITION_INFORMATION_EX{ + .Flags = FILE_DISPOSITION_DELETE | + FILE_DISPOSITION_POSIX_SEMANTICS | + FILE_DISPOSITION_ON_CLOSE | + FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, + }; + + rc = ntdll.NtSetInformationFile( + tmp_handle, + &io, + &info, + @sizeOf(FILE_DISPOSITION_INFORMATION_EX), + .FileDispositionInformationEx, + ); + switch (rc) { + .SUCCESS => {}, + .CANNOT_DELETE => return error.FileBusy, // file is currently mapped + else => return unexpectedStatus(rc), + } + } else { + // Deletion with file pending semantics, which requires waiting or moving + // files to get them removed (from here). + var file_dispo = FILE_DISPOSITION_INFORMATION{ + .DeleteFile = TRUE, + }; + rc = ntdll.NtSetInformationFile( + tmp_handle, + &io, + &file_dispo, + @sizeOf(FILE_DISPOSITION_INFORMATION), + .FileDispositionInformation, + ); + switch (rc) { + .SUCCESS => {}, + .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, + .INVALID_PARAMETER => unreachable, + .CANNOT_DELETE => return error.AccessDenied, + .MEDIA_WRITE_PROTECTED => return error.AccessDenied, + .ACCESS_DENIED => return error.AccessDenied, + else => return unexpectedStatus(rc), + } } } @@ -2397,6 +2422,18 @@ pub const FILE_NAME_INFORMATION = extern struct { FileName: [1]WCHAR, }; +pub const FILE_DISPOSITION_INFORMATION_EX = extern struct { + /// combination of FILE_DISPOSITION_* flags + Flags: ULONG, +}; + +const FILE_DISPOSITION_DO_NOT_DELETE: ULONG = 0x00000000; +const FILE_DISPOSITION_DELETE: ULONG = 0x00000001; +const FILE_DISPOSITION_POSIX_SEMANTICS: ULONG = 0x00000002; +const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004; +const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008; +const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010; + pub const FILE_RENAME_INFORMATION = extern struct { ReplaceIfExists: BOOLEAN, RootDirectory: ?HANDLE, From be50dbf1ce03f5dc5deef86882ab3505363b683a Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Mon, 1 May 2023 12:45:41 +0200 Subject: [PATCH 026/725] apply suggestion by user @xEgoist FILE_DISPOSITION_ON_CLOSE is used to set/clear the FILE_DELETE_ON_CLOSE, but we do not use that anymore and FILE_DISPOSITION_POSIX_SEMANTICS already implies unmapping of the handle and removal fo it on close. --- lib/std/os/windows.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 37ec7f09a3..e6ce95713c 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -943,7 +943,6 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil var info = FILE_DISPOSITION_INFORMATION_EX{ .Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS | - FILE_DISPOSITION_ON_CLOSE | FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, }; From 7594d2c0977497c81db0c394f775832689bad492 Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Mon, 1 May 2023 18:22:28 +0200 Subject: [PATCH 027/725] address review by user @squeek502 --- lib/std/fs/test.zig | 35 +++++++++++++++++++++++++++-------- lib/std/os/windows.zig | 25 +++++++++++-------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 15c8307f58..660ec0c381 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1416,23 +1416,42 @@ test "File.PermissionsUnix" { try testing.expect(!permissions_unix.unixHas(.other, .execute)); } -test "delete a read-only file on windows" { - if (builtin.os.tag != .windows) return error.SkipZigTest; +test "delete a read-only file on windows with file pending semantics" { + if (builtin.os.tag != .windows or builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) + return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + { + const file = try tmp.dir.createFile("test_file", .{ .read = true }); + defer file.close(); + // Create a file and make it read-only + const metadata = try file.metadata(); + var permissions = metadata.permissions(); + permissions.setReadOnly(true); + try file.setPermissions(permissions); + try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file")); + // Now make the file not read-only + permissions.setReadOnly(false); + try file.setPermissions(permissions); + } + try tmp.dir.deleteFile("test_file"); +} + +test "delete a read-only file on windows with posix semantis" { + if (builtin.os.tag != .windows or !builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) + return error.SkipZigTest; var tmp = tmpDir(.{}); defer tmp.cleanup(); const file = try tmp.dir.createFile("test_file", .{ .read = true }); + defer file.close(); // Create a file and make it read-only const metadata = try file.metadata(); var permissions = metadata.permissions(); permissions.setReadOnly(true); try file.setPermissions(permissions); - try testing.expectError(error.AccessDenied, tmp.dir.deleteFile("test_file")); - // Now make the file not read-only - permissions.setReadOnly(false); - try file.setPermissions(permissions); - file.close(); - try tmp.dir.deleteFile("test_file"); + try tmp.dir.deleteFile("test_file"); // file is unmapped and deleted once last handle closed } test "delete a setAsCwd directory on Windows" { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index e6ce95713c..ef2643792e 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -938,6 +938,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil else => return unexpectedStatus(rc), } defer CloseHandle(tmp_handle); + if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs1)) { // Deletion with posix semantics. var info = FILE_DISPOSITION_INFORMATION_EX{ @@ -953,17 +954,13 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil @sizeOf(FILE_DISPOSITION_INFORMATION_EX), .FileDispositionInformationEx, ); - switch (rc) { - .SUCCESS => {}, - .CANNOT_DELETE => return error.FileBusy, // file is currently mapped - else => return unexpectedStatus(rc), - } } else { // Deletion with file pending semantics, which requires waiting or moving // files to get them removed (from here). var file_dispo = FILE_DISPOSITION_INFORMATION{ .DeleteFile = TRUE, }; + rc = ntdll.NtSetInformationFile( tmp_handle, &io, @@ -971,15 +968,15 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil @sizeOf(FILE_DISPOSITION_INFORMATION), .FileDispositionInformation, ); - switch (rc) { - .SUCCESS => {}, - .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, - .INVALID_PARAMETER => unreachable, - .CANNOT_DELETE => return error.AccessDenied, - .MEDIA_WRITE_PROTECTED => return error.AccessDenied, - .ACCESS_DENIED => return error.AccessDenied, - else => return unexpectedStatus(rc), - } + } + switch (rc) { + .SUCCESS => {}, + .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, + .INVALID_PARAMETER => unreachable, + .CANNOT_DELETE => return error.AccessDenied, + .MEDIA_WRITE_PROTECTED => return error.AccessDenied, + .ACCESS_DENIED => return error.AccessDenied, + else => return unexpectedStatus(rc), } } From 28923474401051a9aa0bddd60904b9be64943dba Mon Sep 17 00:00:00 2001 From: jcalabro Date: Mon, 1 May 2023 12:57:05 -0400 Subject: [PATCH 028/725] Fix PBKDF2 docstring comment --- lib/std/crypto/pbkdf2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/crypto/pbkdf2.zig b/lib/std/crypto/pbkdf2.zig index 7c6df5444d..115fd38b3d 100644 --- a/lib/std/crypto/pbkdf2.zig +++ b/lib/std/crypto/pbkdf2.zig @@ -49,7 +49,7 @@ const WeakParametersError = std.crypto.errors.WeakParametersError; /// Larger iteration counts improve security by increasing the time required to compute /// the dk. It is common to tune this parameter to achieve approximately 100ms. /// -/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`. +/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.sha2.HmacSha256`. pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void { if (rounds < 1) return error.WeakParameters; From 7c9891d7b7d09060630693231702f27669dd0dc9 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 26 Apr 2023 22:51:35 -0400 Subject: [PATCH 029/725] x86_64: use std.log for debug logging --- src/arch/x86_64/CodeGen.zig | 137 +++++++++++++++++++++++++----------- src/print_air.zig | 12 +++- 2 files changed, 104 insertions(+), 45 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index be972d7aea..b862252561 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -7,6 +7,8 @@ const leb128 = std.leb; const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); const tracking_log = std.log.scoped(.tracking); +const verbose_tracking_log = std.log.scoped(.verbose_tracking); +const wip_mir_log = std.log.scoped(.wip_mir); const math = std.math; const mem = std.mem; const trace = @import("../../tracy.zig").trace; @@ -48,9 +50,6 @@ const sse = abi.RegisterClass.sse; const InnerError = CodeGenError || error{OutOfRegisters}; -const debug_wip_mir = false; -const debug_tracking = false; - gpa: Allocator, air: Air, liveness: Liveness, @@ -575,12 +574,6 @@ pub fn generate( assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; - if (debug_wip_mir) { - const stderr = std.io.getStdErr().writer(); - fn_owner_decl.renderFullyQualifiedName(mod, stderr) catch {}; - stderr.writeAll(":\n") catch {}; - } - const gpa = bin_file.allocator; var function = Self{ .gpa = gpa, @@ -614,6 +607,8 @@ pub fn generate( if (builtin.mode == .Debug) function.mir_to_air_map.deinit(gpa); } + wip_mir_log.debug("{}:", .{function.fmtDecl(module_fn.owner_decl)}); + try function.frame_allocs.resize(gpa, FrameIndex.named_count); function.frame_allocs.set( @enumToInt(FrameIndex.stack_frame), @@ -715,48 +710,104 @@ pub fn generate( } } -fn dumpWipMir(self: *Self, inst: Mir.Inst) !void { - if (!debug_wip_mir) return; - const stderr = std.io.getStdErr().writer(); +const FormatDeclData = struct { + mod: *Module, + decl_index: Module.Decl.Index, +}; +fn formatDecl( + data: FormatDeclData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + try data.mod.declPtr(data.decl_index).renderFullyQualifiedName(data.mod, writer); +} +fn fmtDecl(self: *Self, decl_index: Module.Decl.Index) std.fmt.Formatter(formatDecl) { + return .{ .data = .{ + .mod = self.bin_file.options.module.?, + .decl_index = decl_index, + } }; +} +const FormatAirData = struct { + self: *Self, + inst: Air.Inst.Index, +}; +fn formatAir( + data: FormatAirData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + @import("../../print_air.zig").dumpInst( + data.inst, + data.self.bin_file.options.module.?, + data.self.air, + data.self.liveness, + ); +} +fn fmtAir(self: *Self, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { + return .{ .data = .{ .self = self, .inst = inst } }; +} + +const FormatWipMirData = struct { + self: *Self, + inst: Mir.Inst.Index, +}; +fn formatWipMir( + data: FormatWipMirData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { var lower = Lower{ - .allocator = self.gpa, + .allocator = data.self.gpa, .mir = .{ - .instructions = self.mir_instructions.slice(), - .extra = self.mir_extra.items, + .instructions = data.self.mir_instructions.slice(), + .extra = data.self.mir_extra.items, .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(), }, - .target = self.target, - .src_loc = self.src_loc, + .target = data.self.target, + .src_loc = data.self.src_loc, }; - for (lower.lowerMir(inst) catch |err| switch (err) { + for (lower.lowerMir(data.self.mir_instructions.get(data.inst)) catch |err| switch (err) { error.LowerFail => { defer { - lower.err_msg.?.deinit(self.gpa); + lower.err_msg.?.deinit(data.self.gpa); lower.err_msg = null; } - try stderr.print("{s}\n", .{lower.err_msg.?.msg}); + try writer.writeAll(lower.err_msg.?.msg); return; }, - error.InvalidInstruction, error.CannotEncode => |e| { - try stderr.writeAll(switch (e) { - error.InvalidInstruction => "CodeGen failed to find a viable instruction.\n", - error.CannotEncode => "CodeGen failed to encode the instruction.\n", + error.OutOfMemory, error.InvalidInstruction, error.CannotEncode => |e| { + try writer.writeAll(switch (e) { + error.OutOfMemory => "Out of memory", + error.InvalidInstruction => "CodeGen failed to find a viable instruction.", + error.CannotEncode => "CodeGen failed to encode the instruction.", }); return; }, else => |e| return e, - }) |lower_inst| { - try stderr.print(" | {}\n", .{lower_inst}); - } + }) |lower_inst| try writer.print(" | {}", .{lower_inst}); +} +fn fmtWipMir(self: *Self, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { + return .{ .data = .{ .self = self, .inst = inst } }; } -fn dumpTracking(self: *Self) !void { - if (!debug_tracking) return; - const stderr = std.io.getStdErr().writer(); - - var it = self.inst_tracking.iterator(); - while (it.next()) |entry| try stderr.print("%{d} = {}\n", .{ entry.key_ptr.*, entry.value_ptr.* }); +const FormatTrackingData = struct { + self: *Self, +}; +fn formatTracking( + data: FormatTrackingData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + var it = data.self.inst_tracking.iterator(); + while (it.next()) |entry| try writer.print("\n%{d} = {}", .{ entry.key_ptr.*, entry.value_ptr.* }); +} +fn fmtTracking(self: *Self) std.fmt.Formatter(formatTracking) { + return .{ .data = .{ .self = self } }; } fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { @@ -764,7 +815,14 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { try self.mir_instructions.ensureUnusedCapacity(gpa, 1); const result_index = @intCast(Mir.Inst.Index, self.mir_instructions.len); self.mir_instructions.appendAssumeCapacity(inst); - self.dumpWipMir(inst) catch {}; + switch (inst.tag) { + else => wip_mir_log.debug("{}", .{self.fmtWipMir(result_index)}), + .dbg_line, + .dbg_prologue_end, + .dbg_epilogue_begin, + .dead, + => {}, + } return result_index; } @@ -1186,13 +1244,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } if (self.liveness.isUnused(inst) and !self.air.mustLower(inst)) continue; - if (debug_wip_mir) @import("../../print_air.zig").dumpInst( - inst, - self.bin_file.options.module.?, - self.air, - self.liveness, - ); - self.dumpTracking() catch {}; + wip_mir_log.debug("{}", .{self.fmtAir(inst)}); + verbose_tracking_log.debug("{}", .{self.fmtTracking()}); const old_air_bookkeeping = self.air_bookkeeping; try self.inst_tracking.ensureUnusedCapacity(self.gpa, 1); @@ -1453,7 +1506,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } } } - self.dumpTracking() catch {}; + verbose_tracking_log.debug("{}", .{self.fmtTracking()}); } fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void { diff --git a/src/print_air.zig b/src/print_air.zig index 2d7995842f..d90d31ec67 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -94,14 +94,20 @@ const Writer = struct { for (w.air.instructions.items(.tag), 0..) |tag, i| { const inst = @intCast(Air.Inst.Index, i); switch (tag) { - .constant, .const_ty => try w.writeInst(s, inst), + .constant, .const_ty => { + try w.writeInst(s, inst); + try s.writeByte('\n'); + }, else => continue, } } } fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void { - for (body) |inst| try w.writeInst(s, inst); + for (body) |inst| { + try w.writeInst(s, inst); + try s.writeByte('\n'); + } } fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { @@ -336,7 +342,7 @@ const Writer = struct { .work_group_id, => try w.writeWorkDimension(s, inst), } - try s.writeAll(")\n"); + try s.writeByte(')'); } fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { From 4ec49da5f6a6a8e77cdb66b8f814718bf11fffef Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 20:39:38 -0400 Subject: [PATCH 030/725] x86_64: implement a bunch of floating point stuff --- src/arch/x86_64/CodeGen.zig | 438 +++++++++++++++++++++++++++------- src/arch/x86_64/Encoding.zig | 27 ++- src/arch/x86_64/Lower.zig | 6 + src/arch/x86_64/Mir.zig | 12 + src/arch/x86_64/encoder.zig | 2 +- src/arch/x86_64/encodings.zig | 14 ++ src/codegen.zig | 103 ++++++-- 7 files changed, 486 insertions(+), 116 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b862252561..d9c7298c95 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1297,9 +1297,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, - .neg, => try self.airUnaryMath(inst), + .neg => try self.airNeg(inst), + .add_with_overflow => try self.airAddSubWithOverflow(inst), .sub_with_overflow => try self.airAddSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1881,7 +1882,7 @@ pub fn spillRegisters(self: *Self, registers: []const Register) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg: Register = try self.register_manager.allocReg(null, try self.regClassForType(ty)); + const reg = try self.register_manager.allocReg(null, try self.regClassForType(ty)); try self.genSetReg(reg, ty, mcv); return reg; } @@ -1924,16 +1925,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - _ = ty_op; - return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + const dst_ty = self.air.typeOfIndex(inst); + const src_ty = self.air.typeOf(ty_op.operand); + if (dst_ty.floatBits(self.target.*) != 32 or src_ty.floatBits(self.target.*) != 64 or + !Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + return self.fail("TODO implement airFptrunc from {} to {}", .{ + src_ty.fmt(self.bin_file.options.module.?), + dst_ty.fmt(self.bin_file.options.module.?), + }); + + const src_mcv = try self.resolveInst(ty_op.operand); + const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + src_mcv + else + try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); + const dst_lock = self.register_manager.lockReg(dst_mcv.register); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + try self.genBinOpMir(.cvtsd2ss, src_ty, dst_mcv, src_mcv); + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn airFpext(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - _ = ty_op; - return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + const dst_ty = self.air.typeOfIndex(inst); + const src_ty = self.air.typeOf(ty_op.operand); + if (dst_ty.floatBits(self.target.*) != 64 or src_ty.floatBits(self.target.*) != 32 or + !Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + return self.fail("TODO implement airFpext from {} to {}", .{ + src_ty.fmt(self.bin_file.options.module.?), + dst_ty.fmt(self.bin_file.options.module.?), + }); + + const src_mcv = try self.resolveInst(ty_op.operand); + const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + src_mcv + else + try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); + const dst_lock = self.register_manager.lockReg(dst_mcv.register); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + try self.genBinOpMir(.cvtss2sd, src_ty, dst_mcv, src_mcv); + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { @@ -3953,10 +3986,65 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } +fn airNeg(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const ty = self.air.typeOf(un_op); + const ty_bits = ty.floatBits(self.target.*); + + var arena = std.heap.ArenaAllocator.init(self.gpa); + defer arena.deinit(); + + const ExpectedContents = union { + f16: Value.Payload.Float_16, + f32: Value.Payload.Float_32, + f64: Value.Payload.Float_64, + f80: Value.Payload.Float_80, + f128: Value.Payload.Float_128, + }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); + + var vec_pl = Type.Payload.Array{ + .base = .{ .tag = .vector }, + .data = .{ + .len = @divExact(128, ty_bits), + .elem_type = ty, + }, + }; + const vec_ty = Type.initPayload(&vec_pl.base); + + var sign_pl = Value.Payload.SubValue{ + .base = .{ .tag = .repeated }, + .data = try Value.floatToValue(-0.0, stack.get(), ty, self.target.*), + }; + const sign_val = Value.initPayload(&sign_pl.base); + + const sign_mcv = try self.genTypedValue(.{ .ty = vec_ty, .val = sign_val }); + + const src_mcv = try self.resolveInst(un_op); + const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv)) + src_mcv + else + try self.copyToRegisterWithInstTracking(inst, ty, src_mcv); + const dst_lock = self.register_manager.lockReg(dst_mcv.register); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + try self.genBinOpMir(switch (ty_bits) { + 32 => .xorps, + 64 => .xorpd, + else => return self.fail("TODO implement airNeg for {}", .{ + ty.fmt(self.bin_file.options.module.?), + }), + }, vec_ty, dst_mcv, sign_mcv); + return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); +} + fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; _ = un_op; - return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.fail("TODO implement airUnaryMath for {}", .{ + self.air.instructions.items(.tag)[inst], + }); //return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4109,7 +4197,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const elem_ty = self.air.typeOfIndex(inst); - const elem_size = elem_ty.abiSize(self.target.*); const result: MCValue = result: { if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; @@ -4117,14 +4204,20 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); + const ptr_ty = self.air.typeOf(ty_op.operand); + const elem_size = elem_ty.abiSize(self.target.*); + + const elem_rc = try self.regClassForType(elem_ty); + const ptr_rc = try self.regClassForType(ptr_ty); + const ptr_mcv = try self.resolveInst(ty_op.operand); - const dst_mcv = if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv)) + const dst_mcv = if (elem_size <= 8 and elem_rc.supersetOf(ptr_rc) and + self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv)) // The MCValue that holds the pointer can be re-used as the value. ptr_mcv else try self.allocRegOrMem(inst, true); - const ptr_ty = self.air.typeOf(ty_op.operand); if (ptr_ty.ptrInfo().data.host_size > 0) { try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv); } else { @@ -4346,17 +4439,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { switch (src_mcv) { .load_frame => |frame_addr| { - const field_abi_size = @intCast(u32, field_ty.abiSize(self.target.*)); - const limb_abi_size = @min(field_abi_size, 8); - const limb_abi_bits = limb_abi_size * 8; - const field_byte_off = @intCast(i32, field_off / limb_abi_bits * limb_abi_size); - const field_bit_off = field_off % limb_abi_bits; - - if (field_bit_off == 0) { - const off_mcv = MCValue{ .load_frame = .{ - .index = frame_addr.index, - .off = frame_addr.off + field_byte_off, - } }; + if (field_off % 8 == 0) { + const off_mcv = + src_mcv.address().offset(@intCast(i32, @divExact(field_off, 8))).deref(); if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result off_mcv; const dst_mcv = try self.allocRegOrMem(inst, true); @@ -4364,6 +4449,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result dst_mcv; } + const field_abi_size = @intCast(u32, field_ty.abiSize(self.target.*)); + const limb_abi_size = @min(field_abi_size, 8); + const limb_abi_bits = limb_abi_size * 8; + const field_byte_off = @intCast(i32, field_off / limb_abi_bits * limb_abi_size); + const field_bit_off = field_off % limb_abi_bits; + if (field_abi_size > 8) { return self.fail("TODO implement struct_field_val with large packed field", .{}); } @@ -5181,24 +5272,69 @@ fn genBinOp( switch (tag) { .add, .addwrap, - => try self.genBinOpMir(switch (lhs_ty.tag()) { + => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { else => .add, - .f32 => .addss, - .f64 => .addsd, + .Float => switch (lhs_ty.floatBits(self.target.*)) { + 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse)) + .addss + else + return self.fail("TODO implement genBinOp for {s} {} without sse", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + .addsd + else + return self.fail("TODO implement genBinOp for {s} {} without sse2", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + else => return self.fail("TODO implement genBinOp for {s} {}", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + }, }, lhs_ty, dst_mcv, src_mcv), .sub, .subwrap, - => try self.genBinOpMir(switch (lhs_ty.tag()) { + => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { else => .sub, - .f32 => .subss, - .f64 => .subsd, + .Float => switch (lhs_ty.floatBits(self.target.*)) { + 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse)) + .subss + else + return self.fail("TODO implement genBinOp for {s} {} without sse", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + .subsd + else + return self.fail("TODO implement genBinOp for {s} {} without sse2", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + else => return self.fail("TODO implement genBinOp for {s} {}", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + }, }, lhs_ty, dst_mcv, src_mcv), - .mul => try self.genBinOpMir(switch (lhs_ty.tag()) { - .f32 => .mulss, - .f64 => .mulsd, + .mul => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }), + .Float => switch (lhs_ty.floatBits(self.target.*)) { + 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse)) + .mulss + else + return self.fail("TODO implement genBinOp for {s} {} without sse", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + .mulsd + else + return self.fail("TODO implement genBinOp for {s} {} without sse2", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + else => return self.fail("TODO implement genBinOp for {s} {}", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + }, }, lhs_ty, dst_mcv, src_mcv), .div_float, @@ -5206,12 +5342,27 @@ fn genBinOp( .div_trunc, .div_floor, => { - try self.genBinOpMir(switch (lhs_ty.tag()) { - .f32 => .divss, - .f64 => .divsd, + try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), }), + .Float => switch (lhs_ty.floatBits(self.target.*)) { + 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse)) + .divss + else + return self.fail("TODO implement genBinOp for {s} {} without sse", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + .divsd + else + return self.fail("TODO implement genBinOp for {s} {} without sse2", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + else => return self.fail("TODO implement genBinOp for {s} {}", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + }, }, lhs_ty, dst_mcv, src_mcv); switch (tag) { .div_float, @@ -5222,16 +5373,18 @@ fn genBinOp( => if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) { const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); const dst_alias = registerAlias(dst_mcv.register, abi_size); - try self.asmRegisterRegisterImmediate(switch (lhs_ty.tag()) { - .f32 => .roundss, - .f64 => .roundsd, + try self.asmRegisterRegisterImmediate(switch (lhs_ty.floatBits(self.target.*)) { + 32 => .roundss, + 64 => .roundsd, else => unreachable, }, dst_alias, dst_alias, Immediate.u(switch (tag) { .div_trunc => 0b1_0_11, .div_floor => 0b1_0_01, else => unreachable, })); - } else return self.fail("TODO implement round without sse4_1", .{}), + } else return self.fail("TODO implement genBinOp for {s} {} without sse4_1", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), else => unreachable, } }, @@ -5453,39 +5606,68 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s )), else => unreachable, }, - .register_offset, .eflags, + .register_offset, .memory, + .indirect, .load_direct, .lea_direct, .load_got, .lea_got, .load_tlv, .lea_tlv, + .load_frame, .lea_frame, => { - assert(abi_size <= 8); + blk: { + return self.asmRegisterMemory( + mir_tag, + registerAlias(dst_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) { + .memory => |addr| .{ + .base = .{ .reg = .ds }, + .disp = math.cast(i32, addr) orelse break :blk, + }, + .indirect => |reg_off| .{ + .base = .{ .reg = reg_off.reg }, + .disp = reg_off.off, + }, + .load_frame => |frame_addr| .{ + .base = .{ .frame = frame_addr.index }, + .disp = frame_addr.off, + }, + else => break :blk, + }), + ); + } + const dst_reg_lock = self.register_manager.lockReg(dst_reg); defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); - const reg = try self.copyToTmpRegister(ty, src_mcv); - return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); - }, - .indirect, .load_frame => try self.asmRegisterMemory( - mir_tag, - registerAlias(dst_reg, abi_size), - Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) { - .indirect => |reg_off| .{ - .base = .{ .reg = reg_off.reg }, - .disp = reg_off.off, + switch (src_mcv) { + .eflags, + .register_offset, + .lea_direct, + .lea_got, + .lea_tlv, + .lea_frame, + => { + const reg = try self.copyToTmpRegister(ty, src_mcv); + return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); }, - .load_frame => |frame_addr| .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, + .memory, + .load_direct, + .load_got, + .load_tlv, + => { + const addr_reg = try self.copyToTmpRegister(ty, src_mcv.address()); + return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ + .indirect = .{ .reg = addr_reg }, + }); }, else => unreachable, - }), - ), + } + }, } }, .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => { @@ -6175,10 +6357,25 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); const src_mcv = if (flipped) lhs_mcv else rhs_mcv; - try self.genBinOpMir(switch (ty.tag()) { + try self.genBinOpMir(switch (ty.zigTypeTag()) { else => .cmp, - .f32 => .ucomiss, - .f64 => .ucomisd, + .Float => switch (ty.floatBits(self.target.*)) { + 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse)) + .ucomiss + else + return self.fail("TODO implement airCmp for {} without sse", .{ + ty.fmt(self.bin_file.options.module.?), + }), + 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + .ucomisd + else + return self.fail("TODO implement airCmp for {} without sse2", .{ + ty.fmt(self.bin_file.options.module.?), + }), + else => return self.fail("TODO implement airCmp for {}", .{ + ty.fmt(self.bin_file.options.module.?), + }), + }, }, ty, dst_mcv, src_mcv); const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned; @@ -7608,7 +7805,8 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const dst_rc = try self.regClassForType(dst_ty); const src_rc = try self.regClassForType(src_ty); const operand = try self.resolveInst(ty_op.operand); - if (dst_rc.eql(src_rc) and self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; + if (dst_rc.supersetOf(src_rc) and self.reuseOperand(inst, ty_op.operand, 0, operand)) + break :result operand; const operand_lock = switch (operand) { .register => |reg| self.register_manager.lockReg(reg), @@ -7648,9 +7846,59 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - _ = ty_op; - return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch}); - //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + + const src_ty = self.air.typeOf(ty_op.operand); + const src_bits = @intCast(u32, src_ty.bitSize(self.target.*)); + const src_signedness = + if (src_ty.isAbiInt()) src_ty.intInfo(self.target.*).signedness else .unsigned; + const dst_ty = self.air.typeOfIndex(inst); + + const src_size = std.math.divCeil(u32, @max(switch (src_signedness) { + .signed => src_bits, + .unsigned => src_bits + 1, + }, 32), 8) catch unreachable; + if (src_size > 8) return self.fail("TODO implement airIntToFloat from {} to {}", .{ + src_ty.fmt(self.bin_file.options.module.?), + dst_ty.fmt(self.bin_file.options.module.?), + }); + + const src_mcv = try self.resolveInst(ty_op.operand); + const src_reg = switch (src_mcv) { + .register => |reg| reg, + else => try self.copyToTmpRegister(src_ty, src_mcv), + }; + const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); + defer self.register_manager.unlockReg(src_lock); + + if (src_bits < src_size * 8) try self.truncateRegister(src_ty, src_reg); + + const dst_reg = try self.register_manager.allocReg(inst, try self.regClassForType(dst_ty)); + const dst_mcv = MCValue{ .register = dst_reg }; + const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_lock); + + try self.asmRegisterRegister(switch (dst_ty.floatBits(self.target.*)) { + 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse)) + .cvtsi2ss + else + return self.fail("TODO implement airIntToFloat from {} to {} without sse", .{ + src_ty.fmt(self.bin_file.options.module.?), + dst_ty.fmt(self.bin_file.options.module.?), + }), + 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2)) + .cvtsi2sd + else + return self.fail("TODO implement airIntToFloat from {} to {} without sse2", .{ + src_ty.fmt(self.bin_file.options.module.?), + dst_ty.fmt(self.bin_file.options.module.?), + }), + else => return self.fail("TODO implement airIntToFloat from {} to {}", .{ + src_ty.fmt(self.bin_file.options.module.?), + dst_ty.fmt(self.bin_file.options.module.?), + }), + }, dst_reg.to128(), registerAlias(src_reg, src_size)); + + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { @@ -8717,6 +8965,7 @@ fn resolveCallingConventionValues( }, .C => { var param_reg_i: usize = 0; + var param_sse_reg_i: usize = 0; result.stack_align = 16; switch (self.target.os.tag) { @@ -8734,26 +8983,39 @@ fn resolveCallingConventionValues( // TODO: is this even possible for C calling convention? result.return_value = InstTracking.init(.none); } else { - const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0]; - const ret_ty_size = @intCast(u31, ret_ty.abiSize(self.target.*)); - if (ret_ty_size <= 8) { - const aliased_reg = registerAlias(ret_reg, ret_ty_size); - result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none }; - } else { - const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i]; - param_reg_i += 1; - result.return_value = .{ - .short = .{ .indirect = .{ .reg = ret_reg } }, - .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, - }; + const classes = switch (self.target.os.tag) { + .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, self.target.*)}, + else => mem.sliceTo(&abi.classifySystemV(ret_ty, self.target.*, .ret), .none), + }; + if (classes.len > 1) { + return self.fail("TODO handle multiple classes per type", .{}); } + const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0]; + result.return_value = switch (classes[0]) { + .integer => InstTracking.init(.{ .register = registerAlias( + ret_reg, + @intCast(u32, ret_ty.abiSize(self.target.*)), + ) }), + .float, .sse => InstTracking.init(.{ .register = .xmm0 }), + .memory => ret: { + const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i]; + param_reg_i += 1; + break :ret .{ + .short = .{ .indirect = .{ .reg = ret_reg } }, + .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, + }; + }, + else => |class| return self.fail("TODO handle calling convention class {s}", .{ + @tagName(class), + }), + }; } // Input params for (param_types, result.args) |ty, *arg| { assert(ty.hasRuntimeBitsIgnoreComptime()); - const classes: []const abi.Class = switch (self.target.os.tag) { + const classes = switch (self.target.os.tag) { .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*, .arg), .none), }; @@ -8761,13 +9023,29 @@ fn resolveCallingConventionValues( return self.fail("TODO handle multiple classes per type", .{}); } switch (classes[0]) { - .integer => blk: { - if (param_reg_i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; - const param_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i]; + .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) { + arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i] }; param_reg_i += 1; - arg.* = .{ .register = param_reg }; continue; }, + .float, .sse => switch (self.target.os.tag) { + .windows => if (param_reg_i < 4) { + arg.* = .{ .register = @intToEnum( + Register, + @enumToInt(Register.xmm0) + param_reg_i, + ) }; + param_reg_i += 1; + continue; + }, + else => if (param_sse_reg_i < 8) { + arg.* = .{ .register = @intToEnum( + Register, + @enumToInt(Register.xmm0) + param_sse_reg_i, + ) }; + param_sse_reg_i += 1; + continue; + }, + }, .memory => {}, // fallthrough else => |class| return self.fail("TODO handle calling convention class {s}", .{ @tagName(class), diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index a977af7842..5cb7f7a2d9 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -58,7 +58,7 @@ pub fn findByMnemonic( next: for (mnemonic_to_encodings_map[@enumToInt(mnemonic)]) |data| { switch (data.mode) { .rex => if (!rex_required) continue, - .long, .sse2_long => {}, + .long, .sse_long, .sse2_long => {}, else => if (rex_required) continue, } for (input_ops, data.ops) |input_op, data_op| @@ -90,7 +90,7 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct { if (prefixes.rex.w) { switch (data.mode) { .short, .fpu, .sse, .sse2, .sse4_1, .none => continue, - .long, .sse2_long, .rex => {}, + .long, .sse_long, .sse2_long, .rex => {}, } } else if (prefixes.rex.present and !prefixes.rex.isSet()) { switch (data.mode) { @@ -138,7 +138,7 @@ pub fn modRmExt(encoding: Encoding) u3 { pub fn operandBitSize(encoding: Encoding) u64 { switch (encoding.data.mode) { .short => return 16, - .long, .sse2_long => return 64, + .long, .sse_long, .sse2_long => return 64, else => {}, } const bit_size: u64 = switch (encoding.data.op_en) { @@ -163,7 +163,7 @@ pub fn format( _ = options; _ = fmt; switch (encoding.data.mode) { - .long, .sse2_long => try writer.writeAll("REX.W + "), + .long, .sse_long, .sse2_long => try writer.writeAll("REX.W + "), else => {}, } @@ -269,21 +269,25 @@ pub const Mnemonic = enum { // SSE addss, cmpss, + cvtsi2ss, divss, maxss, minss, movss, mulss, subss, ucomiss, + xorps, // SSE2 addsd, //cmpsd, + cvtsd2ss, cvtsi2sd, cvtss2sd, divsd, maxsd, minsd, movq, //movd, movsd, mulsd, subsd, ucomisd, + xorpd, // SSE4.1 roundss, roundsd, @@ -318,7 +322,7 @@ pub const Op = enum { m, moffs, sreg, - xmm, xmm_m32, xmm_m64, + xmm, xmm_m32, xmm_m64, xmm_m128, // zig fmt: on pub fn fromOperand(operand: Instruction.Operand) Op { @@ -400,7 +404,7 @@ pub const Op = enum { .imm32, .imm32s, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32, .imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64, .m80 => 80, - .m128, .xmm => 128, + .m128, .xmm, .xmm_m128 => 128, }; } @@ -423,8 +427,8 @@ pub const Op = enum { .al, .ax, .eax, .rax, .r8, .r16, .r32, .r64, .rm8, .rm16, .rm32, .rm64, - .xmm, .xmm_m32, .xmm_m64, - => true, + .xmm, .xmm_m32, .xmm_m64, .xmm_m128, + => true, else => false, }; // zig fmt: on @@ -449,7 +453,7 @@ pub const Op = enum { .rm8, .rm16, .rm32, .rm64, .m8, .m16, .m32, .m64, .m80, .m128, .m, - .xmm_m32, .xmm_m64, + .xmm_m32, .xmm_m64, .xmm_m128, => true, else => false, }; @@ -470,13 +474,13 @@ pub const Op = enum { .r8, .r16, .r32, .r64 => .general_purpose, .rm8, .rm16, .rm32, .rm64 => .general_purpose, .sreg => .segment, - .xmm, .xmm_m32, .xmm_m64 => .floating_point, + .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => .floating_point, }; } pub fn isFloatingPointRegister(op: Op) bool { return switch (op) { - .xmm, .xmm_m32, .xmm_m64 => true, + .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => true, else => false, }; } @@ -535,6 +539,7 @@ pub const Mode = enum { rex, long, sse, + sse_long, sse2, sse2_long, sse4_1, diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index af0146c6e1..a961100687 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -95,6 +95,7 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction { .addss, .cmpss, + .cvtsi2ss, .divss, .maxss, .minss, @@ -103,8 +104,12 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction { .roundss, .subss, .ucomiss, + .xorps, .addsd, .cmpsd, + .cvtsd2ss, + .cvtsi2sd, + .cvtss2sd, .divsd, .maxsd, .minsd, @@ -113,6 +118,7 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction { .roundsd, .subsd, .ucomisd, + .xorpd, => try lower.mirGeneric(inst), .cmps, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index c8703373d2..c14338b13d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -170,6 +170,8 @@ pub const Inst = struct { addss, /// Compare scalar single-precision floating-point values cmpss, + /// Convert doubleword integer to scalar single-precision floating-point value + cvtsi2ss, /// Divide scalar single-precision floating-point values divss, /// Return maximum single-precision floating-point value @@ -186,10 +188,18 @@ pub const Inst = struct { subss, /// Unordered compare scalar single-precision floating-point values ucomiss, + /// Bitwise logical xor of packed single precision floating-point values + xorps, /// Add double precision floating point values addsd, /// Compare scalar double-precision floating-point values cmpsd, + /// Convert scalar double-precision floating-point value to scalar single-precision floating-point value + cvtsd2ss, + /// Convert doubleword integer to scalar double-precision floating-point value + cvtsi2sd, + /// Convert scalar single-precision floating-point value to scalar double-precision floating-point value + cvtss2sd, /// Divide scalar double-precision floating-point values divsd, /// Return maximum double-precision floating-point value @@ -206,6 +216,8 @@ pub const Inst = struct { subsd, /// Unordered compare scalar double-precision floating-point values ucomisd, + /// Bitwise logical xor of packed double precision floating-point values + xorpd, /// Compare string operands cmps, diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 329dfca924..4c900697f5 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -323,7 +323,7 @@ pub const Instruction = struct { var rex = Rex{}; rex.present = inst.encoding.data.mode == .rex; switch (inst.encoding.data.mode) { - .long, .sse2_long => rex.w = true, + .long, .sse_long, .sse2_long => rex.w = true, else => {}, } diff --git a/src/arch/x86_64/encodings.zig b/src/arch/x86_64/encodings.zig index 333bdceea8..ac427c3633 100644 --- a/src/arch/x86_64/encodings.zig +++ b/src/arch/x86_64/encodings.zig @@ -834,6 +834,9 @@ pub const table = [_]Entry{ .{ .cmpss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0xf3, 0x0f, 0xc2 }, 0, .sse }, + .{ .cvtsi2ss, .rm, &.{ .xmm, .rm32 }, &.{ 0xf3, 0x0f, 0x2a }, 0, .sse }, + .{ .cvtsi2ss, .rm, &.{ .xmm, .rm64 }, &.{ 0xf3, 0x0f, 0x2a }, 0, .sse_long }, + .{ .divss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5e }, 0, .sse }, .{ .maxss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5f }, 0, .sse }, @@ -849,11 +852,20 @@ pub const table = [_]Entry{ .{ .ucomiss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x0f, 0x2e }, 0, .sse }, + .{ .xorps, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x0f, 0x57 }, 0, .sse }, + // SSE2 .{ .addsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x58 }, 0, .sse2 }, .{ .cmpsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0xf2, 0x0f, 0xc2 }, 0, .sse2 }, + .{ .cvtsd2ss, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5a }, 0, .sse2 }, + + .{ .cvtsi2sd, .rm, &.{ .xmm, .rm32 }, &.{ 0xf2, 0x0f, 0x2a }, 0, .sse2 }, + .{ .cvtsi2sd, .rm, &.{ .xmm, .rm64 }, &.{ 0xf2, 0x0f, 0x2a }, 0, .sse2_long }, + + .{ .cvtss2sd, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5a }, 0, .sse2 }, + .{ .divsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5e }, 0, .sse2 }, .{ .maxsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5f }, 0, .sse2 }, @@ -878,6 +890,8 @@ pub const table = [_]Entry{ .{ .ucomisd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x2e }, 0, .sse2 }, + .{ .xorpd, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x57 }, 0, .sse2 }, + // SSE4.1 .{ .roundss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0a }, 0, .sse4_1 }, .{ .roundsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0b }, 0, .sse4_1 }, diff --git a/src/codegen.zig b/src/codegen.zig index bf80a90cc3..0043b38a5b 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -291,6 +291,20 @@ pub fn generateSymbol( }, }, .Pointer => switch (typed_value.val.tag()) { + .null_value => { + switch (target.cpu.arch.ptrBitWidth()) { + 32 => { + mem.writeInt(u32, try code.addManyAsArray(4), 0, endian); + if (typed_value.ty.isSlice()) try code.appendNTimes(0xaa, 4); + }, + 64 => { + mem.writeInt(u64, try code.addManyAsArray(8), 0, endian); + if (typed_value.ty.isSlice()) try code.appendNTimes(0xaa, 8); + }, + else => unreachable, + } + return Result.ok; + }, .zero, .one, .int_u64, .int_big_positive => { switch (target.cpu.arch.ptrBitWidth()) { 32 => { @@ -397,30 +411,15 @@ pub fn generateSymbol( }, } }, - .elem_ptr => { - const elem_ptr = typed_value.val.castTag(.elem_ptr).?.data; - const elem_size = typed_value.ty.childType().abiSize(target); - const addend = @intCast(u32, elem_ptr.index * elem_size); - const array_ptr = elem_ptr.array_ptr; - - switch (array_ptr.tag()) { - .decl_ref => { - const decl_index = array_ptr.castTag(.decl_ref).?.data; - return lowerDeclRef(bin_file, src_loc, typed_value, decl_index, code, debug_output, .{ - .parent_atom_index = reloc_info.parent_atom_index, - .addend = (reloc_info.addend orelse 0) + addend, - }); - }, - else => return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, - src_loc, - "TODO implement generateSymbol for pointer type value: '{s}'", - .{@tagName(typed_value.val.tag())}, - ), - }, - } - }, + .elem_ptr => return lowerParentPtr( + bin_file, + src_loc, + typed_value, + typed_value.val, + code, + debug_output, + reloc_info, + ), else => return Result{ .fail = try ErrorMsg.create( bin_file.allocator, @@ -838,9 +837,62 @@ pub fn generateSymbol( } } +fn lowerParentPtr( + bin_file: *link.File, + src_loc: Module.SrcLoc, + typed_value: TypedValue, + parent_ptr: Value, + code: *std.ArrayList(u8), + debug_output: DebugInfoOutput, + reloc_info: RelocInfo, +) CodeGenError!Result { + const target = bin_file.options.target; + + switch (parent_ptr.tag()) { + .elem_ptr => { + const elem_ptr = parent_ptr.castTag(.elem_ptr).?.data; + return lowerParentPtr( + bin_file, + src_loc, + typed_value, + elem_ptr.array_ptr, + code, + debug_output, + reloc_info.offset(@intCast(u32, elem_ptr.index * elem_ptr.elem_ty.abiSize(target))), + ); + }, + .decl_ref => { + const decl_index = parent_ptr.castTag(.decl_ref).?.data; + return lowerDeclRef( + bin_file, + src_loc, + typed_value, + decl_index, + code, + debug_output, + reloc_info, + ); + }, + else => |t| { + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO implement lowerParentPtr for type '{s}'", + .{@tagName(t)}, + ), + }; + }, + } +} + const RelocInfo = struct { parent_atom_index: u32, addend: ?u32 = null, + + fn offset(ri: RelocInfo, addend: u32) RelocInfo { + return .{ .parent_atom_index = ri.parent_atom_index, .addend = (ri.addend orelse 0) + addend }; + } }; fn lowerDeclRef( @@ -1095,6 +1147,9 @@ pub fn genTypedValue( .Slice => {}, else => { switch (typed_value.val.tag()) { + .null_value => { + return GenResult.mcv(.{ .immediate = 0 }); + }, .int_u64 => { return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) }); }, From 6de457211fe4f261fab6c30f652f833ed2a144b5 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 27 Apr 2023 03:24:48 -0400 Subject: [PATCH 031/725] behavior: update affected tests for the x86_64 backend --- test/behavior/basic.zig | 12 ++++++++---- test/behavior/bugs/13069.zig | 2 ++ test/behavior/cast.zig | 7 +++++-- test/behavior/enum.zig | 2 ++ test/behavior/eval.zig | 2 ++ test/behavior/floatop.zig | 12 ++++++++---- test/behavior/fn.zig | 4 ++++ test/behavior/generics.zig | 4 ++++ test/behavior/math.zig | 2 ++ test/behavior/maximum_minimum.zig | 4 ++++ test/behavior/pointers.zig | 5 ++++- test/behavior/struct.zig | 3 ++- test/behavior/switch.zig | 2 ++ test/behavior/union.zig | 14 ++++++++++++++ test/behavior/vector.zig | 4 ++++ 15 files changed, 67 insertions(+), 12 deletions(-) diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 437b1b1373..86fc61c2c9 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -363,7 +363,8 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { } test "take address of parameter" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -391,7 +392,8 @@ test "array 2D const double ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_2d_vertexes = [_][1]f32{ @@ -405,7 +407,8 @@ test "array 2D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; const rect_2d_vertexes = [_][2]f32{ @@ -419,7 +422,8 @@ test "array 3D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_3d_vertexes = [_][2][2]f32{ diff --git a/test/behavior/bugs/13069.zig b/test/behavior/bugs/13069.zig index 41c5906ee6..1c2526ef2a 100644 --- a/test/behavior/bugs/13069.zig +++ b/test/behavior/bugs/13069.zig @@ -6,6 +6,8 @@ test { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO var opt_x: ?[3]f32 = [_]f32{0.0} ** 3; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index fc364da0b8..3c1d26f284 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -95,7 +95,9 @@ test "comptime_int @intToFloat" { test "@intToFloat" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -634,7 +636,8 @@ test "vector casts" { } test "@floatCast cast down" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 316275d6ac..de5c0efb8e 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -940,6 +940,8 @@ test "constant enum initialization with differing sizes" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO try test3_1(test3_foo); try test3_2(test3_bar); diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 3566bdacb0..3a10a288e3 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -535,6 +535,8 @@ test "static eval list init" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO try expect(static_vec3.data[2] == 1.0); try expect(vec3(0.0, 0.0, 3.0).data[2] == 3.0); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 3fc2249ccc..700b419fce 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -904,7 +904,8 @@ test "negation f16" { } test "negation f32" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -924,7 +925,8 @@ test "negation f32" { } test "negation f64" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1060,7 +1062,8 @@ test "nan negation f16" { } test "nan negation f32" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1080,7 +1083,8 @@ test "nan negation f32" { test "nan negation f64" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 4ff5e20378..71b7b36c21 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -338,6 +338,8 @@ test "function call with anon list literal" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -358,6 +360,8 @@ test "function call with anon list literal - 2D" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 0e002b2016..c5168e420b 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -59,6 +59,8 @@ test "fn with comptime args" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO try expect(gimmeTheBigOne(1234, 5678) == 5678); try expect(shouldCallSameInstance(34, 12) == 34); @@ -69,6 +71,8 @@ test "anytype params" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO try expect(max_i32(12, 34) == 34); try expect(max_f64(1.2, 3.4) == 3.4); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 846f72e301..b7dd6b9287 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -203,6 +203,8 @@ test "float equality" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const x: f64 = 0.012; const y: f64 = x + 1.0; diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index d538e2db65..9a4ae40eef 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -8,6 +8,8 @@ test "@max" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -54,6 +56,8 @@ test "@min" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 016b59b613..0bd8388660 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -206,7 +206,8 @@ test "allowzero pointer and slice" { } test "assign null directly to C pointer and test null equality" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -345,6 +346,8 @@ test "pointer sentinel with +inf" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index c3609e4a0a..e533e34cc3 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -744,7 +744,8 @@ var g_foo: S0 = S0.init(); test "packed struct with fp fields" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b69add6fdf..3924d1d6c1 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -231,6 +231,8 @@ test "switch prong with variable" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 025eb4f6bd..76c5b09a89 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -14,6 +14,8 @@ test "basic unions with floats" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO var foo = FooWithFloats{ .int = 1 }; try expect(foo.int == 1); @@ -29,6 +31,8 @@ test "init union with runtime value - floats" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO var foo: FooWithFloats = undefined; @@ -216,6 +220,8 @@ test "union with specified enum tag" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO try doTest(); comptime try doTest(); @@ -225,6 +231,8 @@ test "packed union generates correctly aligned type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const U = packed union { f1: *const fn () error{TestUnexpectedResult}!void, @@ -903,6 +911,8 @@ test "anonymous union literal syntax" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { const Number = union { @@ -1055,6 +1065,8 @@ test "containers with single-field enums" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const S = struct { const A = union(enum) { f1 }; @@ -1515,6 +1527,8 @@ test "reinterpreting enum value inside packed union" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and + comptime !std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sse, .sse2, .sse4_1 })) return error.SkipZigTest; // TODO const U = packed union { tag: enum { a, b }, diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index f6521f04c3..01c76310d7 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -321,6 +321,7 @@ test "load vector elements via comptime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -342,6 +343,7 @@ test "store vector elements via comptime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -369,6 +371,7 @@ test "load vector elements via runtime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -390,6 +393,7 @@ test "store vector elements via runtime index" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From 1fd48815c6e22b266b318f58c6b3c828b20ace80 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 01:56:18 -0400 Subject: [PATCH 032/725] x86_64: cleanup unneeded code --- src/arch/x86_64/CodeGen.zig | 136 ++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 77 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d9c7298c95..915b36f267 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6595,35 +6595,26 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst); } - const outer_state = try self.saveState(); - { - self.scope_generation += 1; - const inner_state = try self.saveState(); + self.scope_generation += 1; + const state = try self.saveState(); - for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand); - try self.genBody(then_body); - try self.restoreState(inner_state, &.{}, .{ - .emit_instructions = false, - .update_tracking = true, - .resurrect = true, - .close_scope = true, - }); - - try self.performReloc(reloc); - - for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand); - try self.genBody(else_body); - try self.restoreState(inner_state, &.{}, .{ - .emit_instructions = false, - .update_tracking = true, - .resurrect = true, - .close_scope = true, - }); - } - try self.restoreState(outer_state, &.{}, .{ + for (liveness_cond_br.then_deaths) |operand| self.processDeath(operand); + try self.genBody(then_body); + try self.restoreState(state, &.{}, .{ .emit_instructions = false, - .update_tracking = false, - .resurrect = false, + .update_tracking = true, + .resurrect = true, + .close_scope = true, + }); + + try self.performReloc(reloc); + + for (liveness_cond_br.else_deaths) |operand| self.processDeath(operand); + try self.genBody(else_body); + try self.restoreState(state, &.{}, .{ + .emit_instructions = false, + .update_tracking = true, + .resurrect = true, .close_scope = true, }); @@ -6996,64 +6987,55 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst); } - const outer_state = try self.saveState(); - { - self.scope_generation += 1; - const inner_state = try self.saveState(); + self.scope_generation += 1; + const state = try self.saveState(); - while (case_i < switch_br.data.cases_len) : (case_i += 1) { - const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @ptrCast( - []const Air.Inst.Ref, - self.air.extra[case.end..][0..case.data.items_len], - ); - const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; - extra_index = case.end + items.len + case_body.len; + while (case_i < switch_br.data.cases_len) : (case_i += 1) { + const case = self.air.extraData(Air.SwitchBr.Case, extra_index); + const items = @ptrCast( + []const Air.Inst.Ref, + self.air.extra[case.end..][0..case.data.items_len], + ); + const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; + extra_index = case.end + items.len + case_body.len; - var relocs = try self.gpa.alloc(u32, items.len); - defer self.gpa.free(relocs); + var relocs = try self.gpa.alloc(u32, items.len); + defer self.gpa.free(relocs); - for (items, relocs) |item, *reloc| { - try self.spillEflagsIfOccupied(); - const item_mcv = try self.resolveInst(item); - try self.genBinOpMir(.cmp, condition_ty, condition, item_mcv); - reloc.* = try self.asmJccReloc(undefined, .ne); - } - - for (liveness.deaths[case_i]) |operand| self.processDeath(operand); - - try self.genBody(case_body); - try self.restoreState(inner_state, &.{}, .{ - .emit_instructions = false, - .update_tracking = true, - .resurrect = true, - .close_scope = true, - }); - - for (relocs) |reloc| try self.performReloc(reloc); + for (items, relocs) |item, *reloc| { + try self.spillEflagsIfOccupied(); + const item_mcv = try self.resolveInst(item); + try self.genBinOpMir(.cmp, condition_ty, condition, item_mcv); + reloc.* = try self.asmJccReloc(undefined, .ne); } - if (switch_br.data.else_body_len > 0) { - const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; + for (liveness.deaths[case_i]) |operand| self.processDeath(operand); - const else_deaths = liveness.deaths.len - 1; - for (liveness.deaths[else_deaths]) |operand| self.processDeath(operand); + try self.genBody(case_body); + try self.restoreState(state, &.{}, .{ + .emit_instructions = false, + .update_tracking = true, + .resurrect = true, + .close_scope = true, + }); - try self.genBody(else_body); - try self.restoreState(inner_state, &.{}, .{ - .emit_instructions = false, - .update_tracking = true, - .resurrect = true, - .close_scope = true, - }); - } + for (relocs) |reloc| try self.performReloc(reloc); + } + + if (switch_br.data.else_body_len > 0) { + const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; + + const else_deaths = liveness.deaths.len - 1; + for (liveness.deaths[else_deaths]) |operand| self.processDeath(operand); + + try self.genBody(else_body); + try self.restoreState(state, &.{}, .{ + .emit_instructions = false, + .update_tracking = true, + .resurrect = true, + .close_scope = true, + }); } - try self.restoreState(outer_state, &.{}, .{ - .emit_instructions = false, - .update_tracking = false, - .resurrect = false, - .close_scope = true, - }); // We already took care of pl_op.operand earlier, so we're going to pass .none here return self.finishAir(inst, .unreach, .{ .none, .none, .none }); From db76ae82607b941373064b05a348950802a21217 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 01:56:45 -0400 Subject: [PATCH 033/725] x86_64: fix emitting f80 globals --- src/codegen.zig | 22 ++++++++-------------- test/behavior/floatop.zig | 1 - test/behavior/math.zig | 1 - 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 0043b38a5b..f967566034 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -91,12 +91,10 @@ pub fn generateFunction( fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian, code: []u8) void { _ = target; - const Int = @Type(.{ .Int = .{ - .signedness = .unsigned, - .bits = @typeInfo(F).Float.bits, - } }); + const bits = @typeInfo(F).Float.bits; + const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = bits } }); const int = @bitCast(Int, f); - mem.writeInt(Int, code[0..@sizeOf(Int)], int, endian); + mem.writeInt(Int, code[0..@divExact(bits, 8)], int, endian); } pub fn generateLazySymbol( @@ -187,18 +185,14 @@ pub fn generateSymbol( }; }, .Float => { - const float_bits = typed_value.ty.floatBits(target); - switch (float_bits) { + switch (typed_value.ty.floatBits(target)) { 16 => writeFloat(f16, typed_value.val.toFloat(f16), target, endian, try code.addManyAsArray(2)), 32 => writeFloat(f32, typed_value.val.toFloat(f32), target, endian, try code.addManyAsArray(4)), 64 => writeFloat(f64, typed_value.val.toFloat(f64), target, endian, try code.addManyAsArray(8)), - 80 => return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, - src_loc, - "TODO handle f80 in generateSymbol", - .{}, - ), + 80 => { + writeFloat(f80, typed_value.val.toFloat(f80), target, endian, try code.addManyAsArray(10)); + const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; + try code.appendNTimes(0, abi_size - 10); }, 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)), else => unreachable, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 700b419fce..f713cd035c 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -1023,7 +1023,6 @@ test "comptime fixed-width float zero divided by zero produces NaN" { test "comptime fixed-width float non-zero divided by zero produces signed Inf" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/math.zig b/test/behavior/math.zig index b7dd6b9287..03a92a83e9 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1535,7 +1535,6 @@ test "signed zeros are represented properly" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { From 50f96c2949bd7d396eaaf391ffd5fd55e59ebac1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 02:24:29 -0400 Subject: [PATCH 034/725] x86_64: fix stack realignment --- src/arch/x86_64/CodeGen.zig | 7 ++++--- test/behavior/align.zig | 5 ----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 915b36f267..955a0ba843 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1673,15 +1673,16 @@ fn allocFrameIndex(self: *Self, alloc: FrameAlloc) !FrameIndex { const frame_allocs_slice = self.frame_allocs.slice(); const frame_size = frame_allocs_slice.items(.abi_size); const frame_align = frame_allocs_slice.items(.abi_align); + + const stack_frame_align = &frame_align[@enumToInt(FrameIndex.stack_frame)]; + stack_frame_align.* = @max(stack_frame_align.*, alloc.abi_align); + for (self.free_frame_indices.keys(), 0..) |frame_index, free_i| { const abi_size = frame_size[@enumToInt(frame_index)]; if (abi_size != alloc.abi_size) continue; const abi_align = &frame_align[@enumToInt(frame_index)]; abi_align.* = @max(abi_align.*, alloc.abi_align); - const stack_frame_align = &frame_align[@enumToInt(FrameIndex.stack_frame)]; - stack_frame_align.* = @max(stack_frame_align.*, alloc.abi_align); - _ = self.free_frame_indices.swapRemoveAt(free_i); return frame_index; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 7755cdaa7d..bfc9997dd2 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -282,7 +282,6 @@ fn give() anyerror!u128 { test "page aligned array on stack" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -491,10 +490,6 @@ test "read 128-bit field from default aligned struct in global memory" { } test "struct field explicit alignment" { - if (builtin.zig_backend == .stage2_x86_64) { - // Careful enabling this test, fails randomly. - return error.SkipZigTest; - } if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From aaef5288f886c86e4257c5c67122a0be8a64c134 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 17:35:17 -0400 Subject: [PATCH 035/725] x86_64: fix 128-bit cmpxchg --- src/arch/x86_64/CodeGen.zig | 134 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 955a0ba843..99b3c913a0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -219,9 +219,9 @@ pub const MCValue = union(enum) { .dead, .undef, .immediate, + .eflags, .register, .register_offset, - .eflags, .register_overflow, .lea_direct, .lea_got, @@ -297,6 +297,41 @@ pub const MCValue = union(enum) { }; } + fn mem(mcv: MCValue, ptr_size: Memory.PtrSize) Memory { + return switch (mcv) { + .none, + .unreach, + .dead, + .undef, + .immediate, + .eflags, + .register, + .register_offset, + .register_overflow, + .load_direct, + .lea_direct, + .load_got, + .lea_got, + .load_tlv, + .lea_tlv, + .lea_frame, + .reserved_frame, + => unreachable, + .memory => |addr| if (math.cast(i32, @bitCast(i64, addr))) |small_addr| + Memory.sib(ptr_size, .{ .base = .{ .reg = .ds }, .disp = small_addr }) + else + Memory.moffs(.ds, addr), + .indirect => |reg_off| Memory.sib(ptr_size, .{ + .base = .{ .reg = reg_off.reg }, + .disp = reg_off.off, + }), + .load_frame => |frame_addr| Memory.sib(ptr_size, .{ + .base = .{ .frame = frame_addr.index }, + .disp = frame_addr.off, + }), + }; + } + pub fn format( mcv: MCValue, comptime _: []const u8, @@ -7936,70 +7971,50 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data; const ptr_ty = self.air.typeOf(extra.ptr); - const ptr_mcv = try self.resolveInst(extra.ptr); const val_ty = self.air.typeOf(extra.expected_value); const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx }); const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx }); - for (regs_lock) |lock| self.register_manager.unlockReg(lock); + defer for (regs_lock) |lock| self.register_manager.unlockReg(lock); const exp_mcv = try self.resolveInst(extra.expected_value); - if (val_abi_size > 8) switch (exp_mcv) { - .load_frame => |frame_addr| { - try self.genSetReg(.rax, Type.usize, .{ .load_frame = .{ - .index = frame_addr.index, - .off = frame_addr.off + 0, - } }); - try self.genSetReg(.rdx, Type.usize, .{ .load_frame = .{ - .index = frame_addr.index, - .off = frame_addr.off + 8, - } }); - }, - else => return self.fail("TODO implement cmpxchg for {s}", .{@tagName(exp_mcv)}), + if (val_abi_size > 8) { + try self.genSetReg(.rax, Type.usize, exp_mcv); + try self.genSetReg(.rdx, Type.usize, exp_mcv.address().offset(8).deref()); } else try self.genSetReg(.rax, val_ty, exp_mcv); - const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); - defer self.register_manager.unlockReg(rax_lock); const new_mcv = try self.resolveInst(extra.new_value); - const new_reg: Register = if (val_abi_size > 8) switch (new_mcv) { - .load_frame => |frame_addr| new: { - try self.genSetReg(.rbx, Type.usize, .{ .load_frame = .{ - .index = frame_addr.index, - .off = frame_addr.off + 0, - } }); - try self.genSetReg(.rcx, Type.usize, .{ .load_frame = .{ - .index = frame_addr.index, - .off = frame_addr.off + 8, - } }); - break :new undefined; - }, - else => return self.fail("TODO implement cmpxchg for {s}", .{@tagName(exp_mcv)}), + const new_reg = if (val_abi_size > 8) new: { + try self.genSetReg(.rbx, Type.usize, new_mcv); + try self.genSetReg(.rcx, Type.usize, new_mcv.address().offset(8).deref()); + break :new null; } else try self.copyToTmpRegister(val_ty, new_mcv); - const new_lock = self.register_manager.lockRegAssumeUnused(new_reg); - defer self.register_manager.unlockReg(new_lock); + const new_lock = if (new_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null; + defer if (new_lock) |lock| self.register_manager.unlockReg(lock); + const ptr_mcv = try self.resolveInst(extra.ptr); const ptr_size = Memory.PtrSize.fromSize(val_abi_size); const ptr_mem = switch (ptr_mcv) { - .register => |reg| Memory.sib(ptr_size, .{ .base = .{ .reg = reg } }), - .lea_frame => |frame_addr| Memory.sib(ptr_size, .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, + .immediate, .register, .register_offset, .lea_frame => ptr_mcv.deref().mem(ptr_size), + else => Memory.sib(ptr_size, .{ + .base = .{ .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }, }), - else => Memory.sib(ptr_size, .{ .base = .{ - .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv), - } }), }; - const mem_lock = switch (ptr_mem.base()) { + switch (ptr_mem) { + .sib, .rip => {}, + .moffs => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), + } + const ptr_lock = switch (ptr_mem.base()) { .none, .frame => null, .reg => |reg| self.register_manager.lockReg(reg), }; - defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); + defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); try self.spillEflagsIfOccupied(); if (val_abi_size <= 8) { _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{ - .r = registerAlias(new_reg, val_abi_size), + .r = registerAlias(new_reg.?, val_abi_size), .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), } } }); } else { @@ -8017,24 +8032,9 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { } const dst_mcv = try self.allocRegOrMem(inst, false); - try self.genSetMem( - .{ .frame = dst_mcv.load_frame.index }, - dst_mcv.load_frame.off + 16, - Type.bool, - .{ .eflags = .ne }, - ); - try self.genSetMem( - .{ .frame = dst_mcv.load_frame.index }, - dst_mcv.load_frame.off + 8, - Type.usize, - .{ .register = .rdx }, - ); - try self.genSetMem( - .{ .frame = dst_mcv.load_frame.index }, - dst_mcv.load_frame.off + 0, - Type.usize, - .{ .register = .rax }, - ); + try self.genCopy(Type.usize, dst_mcv, .{ .register = .rax }); + try self.genCopy(Type.usize, dst_mcv.address().offset(8).deref(), .{ .register = .rdx }); + try self.genCopy(Type.bool, dst_mcv.address().offset(16).deref(), .{ .eflags = .ne }); break :result dst_mcv; }; return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); @@ -8065,15 +8065,15 @@ fn atomicOp( const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); const ptr_size = Memory.PtrSize.fromSize(val_abi_size); const ptr_mem = switch (ptr_mcv) { - .register => |reg| Memory.sib(ptr_size, .{ .base = .{ .reg = reg } }), - .lea_frame => |frame_addr| Memory.sib(ptr_size, .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, + .immediate, .register, .register_offset, .lea_frame => ptr_mcv.deref().mem(ptr_size), + else => Memory.sib(ptr_size, .{ + .base = .{ .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }, }), - else => Memory.sib(ptr_size, .{ .base = .{ - .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv), - } }), }; + switch (ptr_mem) { + .sib, .rip => {}, + .moffs => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), + } const mem_lock = switch (ptr_mem.base()) { .none, .frame => null, .reg => |reg| self.register_manager.lockReg(reg), From 00ae3592e6d054276b61404808d016dc2ef7ced5 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 19:38:18 -0400 Subject: [PATCH 036/725] test_runner: use const to control verbose output --- lib/test_runner.zig | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/test_runner.zig b/lib/test_runner.zig index 20d7b02e26..f5bb0150a7 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -234,25 +234,33 @@ pub fn log( /// work-in-progress backends can handle it. pub fn mainSimple() anyerror!void { const enable_print = false; + const print_all = false; var passed: u64 = 0; var skipped: u64 = 0; var failed: u64 = 0; const stderr = if (enable_print) std.io.getStdErr() else {}; for (builtin.test_functions) |test_fn| { + if (enable_print and print_all) { + stderr.writeAll(test_fn.name) catch {}; + stderr.writeAll("... ") catch {}; + } test_fn.func() catch |err| { - if (enable_print) stderr.writeAll(test_fn.name) catch {}; + if (enable_print and !print_all) { + stderr.writeAll(test_fn.name) catch {}; + stderr.writeAll("... ") catch {}; + } if (err != error.SkipZigTest) { - if (enable_print) stderr.writeAll("... FAIL\n") catch {}; + if (enable_print) stderr.writeAll("FAIL\n") catch {}; failed += 1; if (!enable_print) return err; continue; } - if (enable_print) stderr.writeAll("... SKIP\n") catch {}; + if (enable_print) stderr.writeAll("SKIP\n") catch {}; skipped += 1; continue; }; - //if (enable_print) stderr.writeAll("... PASS\n") catch {}; + if (enable_print and print_all) stderr.writeAll("PASS\n") catch {}; passed += 1; } if (enable_print) { From c3889600424e3720dc07b22397129f82f2111d72 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 28 Apr 2023 20:02:28 -0400 Subject: [PATCH 037/725] x86_64: fix large not and atomicrmw --- src/arch/x86_64/CodeGen.zig | 200 ++++++++++++++++-------------------- test/behavior/atomics.zig | 1 - test/behavior/math.zig | 1 - 3 files changed, 86 insertions(+), 116 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 99b3c913a0..670dc6840c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2050,13 +2050,10 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { registerAlias(src_reg, min_abi_size), ); }, - .load_frame => |frame_addr| try self.asmRegisterMemory( + .memory, .indirect, .load_frame => try self.asmRegisterMemory( tag, dst_alias, - Memory.sib(Memory.PtrSize.fromSize(min_abi_size), .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, - }), + src_mcv.mem(Memory.PtrSize.fromSize(min_abi_size)), ), else => return self.fail("TODO airIntCast from {s} to {s}", .{ @tagName(src_mcv), @@ -2738,19 +2735,9 @@ fn genIntMulDivOpMir( }; switch (mat_rhs) { .register => |reg| try self.asmRegister(tag, registerAlias(reg, abi_size)), - .indirect, .load_frame => try self.asmMemory( + .memory, .indirect, .load_frame => try self.asmMemory( tag, - Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (mat_rhs) { - .indirect => |reg_off| .{ - .base = .{ .reg = reg_off.reg }, - .disp = reg_off.off, - }, - .load_frame => |frame_addr| .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, - }, - else => unreachable, - }), + mat_rhs.mem(Memory.PtrSize.fromSize(abi_size)), ), else => unreachable, } @@ -4628,9 +4615,6 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: if (src_ty.zigTypeTag() == .Vector) { return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); } - if (src_ty.abiSize(self.target.*) > 8) { - return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); - } switch (src_mcv) { .eflags => |cc| switch (tag) { @@ -4646,13 +4630,13 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: }; defer if (src_lock) |lock| self.register_manager.unlockReg(lock); - const dst_mcv: MCValue = if (maybe_inst) |inst| - if (self.reuseOperand(inst, src_air, 0, src_mcv)) - src_mcv - else - try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv) - else - .{ .register = try self.copyToTmpRegister(src_ty, src_mcv) }; + const dst_mcv: MCValue = dst: { + if (maybe_inst) |inst| if (self.reuseOperand(inst, src_air, 0, src_mcv)) break :dst src_mcv; + + const dst_mcv = try self.allocRegOrMemAdvanced(src_ty, maybe_inst, true); + try self.genCopy(src_ty, dst_mcv, src_mcv); + break :dst dst_mcv; + }; const dst_lock = switch (dst_mcv) { .register => |reg| self.register_manager.lockReg(reg), else => null, @@ -4661,19 +4645,33 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: switch (tag) { .not => { + const limb_abi_size = @intCast(u16, @min(src_ty.abiSize(self.target.*), 8)); const int_info = if (src_ty.tag() == .bool) std.builtin.Type.Int{ .signedness = .unsigned, .bits = 1 } else src_ty.intInfo(self.target.*); - const extra_bits = self.regExtraBits(src_ty); - if (int_info.signedness == .unsigned and extra_bits > 0) { - const mask = (@as(u64, 1) << @intCast(u6, src_ty.bitSize(self.target.*))) - 1; - try self.genBinOpMir(.xor, src_ty, dst_mcv, .{ .immediate = mask }); - } else try self.genUnOpMir(.not, src_ty, dst_mcv); + var byte_off: i32 = 0; + while (byte_off * 8 < int_info.bits) : (byte_off += limb_abi_size) { + var limb_pl = Type.Payload.Bits{ + .base = .{ .tag = switch (int_info.signedness) { + .signed => .int_signed, + .unsigned => .int_unsigned, + } }, + .data = @intCast(u16, @min(int_info.bits - byte_off * 8, limb_abi_size * 8)), + }; + const limb_ty = Type.initPayload(&limb_pl.base); + const limb_mcv = switch (byte_off) { + 0 => dst_mcv, + else => dst_mcv.address().offset(byte_off).deref(), + }; + + if (limb_pl.base.tag == .int_unsigned and self.regExtraBits(limb_ty) > 0) { + const mask = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - limb_pl.data); + try self.genBinOpMir(.xor, limb_ty, limb_mcv, .{ .immediate = mask }); + } else try self.genUnOpMir(.not, limb_ty, limb_mcv); + } }, - .neg => try self.genUnOpMir(.neg, src_ty, dst_mcv), - else => unreachable, } return dst_mcv; @@ -4714,17 +4712,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue }, .indirect, .load_frame => try self.asmMemory( mir_tag, - Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (dst_mcv) { - .indirect => |reg_off| .{ - .base = .{ .reg = reg_off.reg }, - .disp = reg_off.off, - }, - .load_frame => |frame_addr| .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, - }, - else => unreachable, - }), + dst_mcv.mem(Memory.PtrSize.fromSize(abi_size)), ), } } @@ -8179,12 +8167,9 @@ fn atomicOp( registerAlias(val_reg, cmov_abi_size), cc, ), - .load_frame => |frame_addr| try self.asmCmovccRegisterMemory( + .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory( registerAlias(tmp_reg, cmov_abi_size), - Memory.sib(Memory.PtrSize.fromSize(cmov_abi_size), .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off, - }), + val_mcv.mem(Memory.PtrSize.fromSize(cmov_abi_size)), cc, ), else => { @@ -8207,72 +8192,62 @@ fn atomicOp( } else { try self.asmRegisterMemory(.mov, .rax, Memory.sib(.qword, .{ .base = ptr_mem.sib.base, - .scale_index = ptr_mem.sib.scale_index, + .scale_index = ptr_mem.scaleIndex(), .disp = ptr_mem.sib.disp + 0, })); try self.asmRegisterMemory(.mov, .rdx, Memory.sib(.qword, .{ .base = ptr_mem.sib.base, - .scale_index = ptr_mem.sib.scale_index, + .scale_index = ptr_mem.scaleIndex(), .disp = ptr_mem.sib.disp + 8, })); const loop = @intCast(u32, self.mir_instructions.len); - switch (val_mcv) { - .load_frame => |frame_addr| { - const val_lo_mem = Memory.sib(.qword, .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off + 0, - }); - const val_hi_mem = Memory.sib(.qword, .{ - .base = .{ .frame = frame_addr.index }, - .disp = frame_addr.off + 8, - }); - - if (rmw_op != std.builtin.AtomicRmwOp.Xchg) { - try self.asmRegisterRegister(.mov, .rbx, .rax); - try self.asmRegisterRegister(.mov, .rcx, .rdx); - } - if (rmw_op) |op| switch (op) { - .Xchg => { - try self.asmRegisterMemory(.mov, .rbx, val_lo_mem); - try self.asmRegisterMemory(.mov, .rcx, val_hi_mem); - }, - .Add => { - try self.asmRegisterMemory(.add, .rbx, val_lo_mem); - try self.asmRegisterMemory(.adc, .rcx, val_hi_mem); - }, - .Sub => { - try self.asmRegisterMemory(.sub, .rbx, val_lo_mem); - try self.asmRegisterMemory(.sbb, .rcx, val_hi_mem); - }, - .And => { - try self.asmRegisterMemory(.@"and", .rbx, val_lo_mem); - try self.asmRegisterMemory(.@"and", .rcx, val_hi_mem); - }, - .Nand => { - try self.asmRegisterMemory(.@"and", .rbx, val_lo_mem); - try self.asmRegisterMemory(.@"and", .rcx, val_hi_mem); - try self.asmRegister(.not, .rbx); - try self.asmRegister(.not, .rcx); - }, - .Or => { - try self.asmRegisterMemory(.@"or", .rbx, val_lo_mem); - try self.asmRegisterMemory(.@"or", .rcx, val_hi_mem); - }, - .Xor => { - try self.asmRegisterMemory(.xor, .rbx, val_lo_mem); - try self.asmRegisterMemory(.xor, .rcx, val_hi_mem); - }, - else => return self.fail( - "TODO implement x86 atomic loop for large abi {s}", - .{@tagName(op)}, - ), - }; - }, - else => return self.fail( - "TODO implement x86 atomic loop for large abi {s}", - .{@tagName(val_mcv)}, - ), + const val_mem_mcv: MCValue = switch (val_mcv) { + .memory, .indirect, .load_frame => val_mcv, + else => .{ .indirect = .{ + .reg = try self.copyToTmpRegister(Type.usize, val_mcv.address()), + } }, + }; + const val_lo_mem = val_mem_mcv.mem(.qword); + const val_hi_mem = val_mem_mcv.address().offset(8).deref().mem(.qword); + if (rmw_op != std.builtin.AtomicRmwOp.Xchg) { + try self.asmRegisterRegister(.mov, .rbx, .rax); + try self.asmRegisterRegister(.mov, .rcx, .rdx); } + if (rmw_op) |op| switch (op) { + .Xchg => { + try self.asmRegisterMemory(.mov, .rbx, val_lo_mem); + try self.asmRegisterMemory(.mov, .rcx, val_hi_mem); + }, + .Add => { + try self.asmRegisterMemory(.add, .rbx, val_lo_mem); + try self.asmRegisterMemory(.adc, .rcx, val_hi_mem); + }, + .Sub => { + try self.asmRegisterMemory(.sub, .rbx, val_lo_mem); + try self.asmRegisterMemory(.sbb, .rcx, val_hi_mem); + }, + .And => { + try self.asmRegisterMemory(.@"and", .rbx, val_lo_mem); + try self.asmRegisterMemory(.@"and", .rcx, val_hi_mem); + }, + .Nand => { + try self.asmRegisterMemory(.@"and", .rbx, val_lo_mem); + try self.asmRegisterMemory(.@"and", .rcx, val_hi_mem); + try self.asmRegister(.not, .rbx); + try self.asmRegister(.not, .rcx); + }, + .Or => { + try self.asmRegisterMemory(.@"or", .rbx, val_lo_mem); + try self.asmRegisterMemory(.@"or", .rcx, val_hi_mem); + }, + .Xor => { + try self.asmRegisterMemory(.xor, .rbx, val_lo_mem); + try self.asmRegisterMemory(.xor, .rcx, val_hi_mem); + }, + else => return self.fail("TODO implement x86 atomic loop for {} {s}", .{ + val_ty.fmt(self.bin_file.options.module.?), @tagName(op), + }), + }; _ = try self.addInst(.{ .tag = .cmpxchgb, .ops = .lock_m_sib, .data = .{ .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), } }); @@ -9177,15 +9152,16 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { } fn regBitSize(self: *Self, ty: Type) u64 { + const abi_size = ty.abiSize(self.target.*); return switch (ty.zigTypeTag()) { - else => switch (ty.abiSize(self.target.*)) { + else => switch (abi_size) { 1 => 8, 2 => 16, 3...4 => 32, 5...8 => 64, else => unreachable, }, - .Float => switch (ty.abiSize(self.target.*)) { + .Float => switch (abi_size) { 1...16 => 128, 17...32 => 256, else => unreachable, @@ -9197,10 +9173,6 @@ fn regExtraBits(self: *Self, ty: Type) u64 { return self.regBitSize(ty) - ty.bitSize(self.target.*); } -fn hasAvxSupport(target: Target) bool { - return Target.x86.featureSetHasAny(target.cpu.features, .{ .avx, .avx2 }); -} - fn getSymbolIndexForDecl(self: *Self, decl_index: Module.Decl.Index) !u32 { if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(decl_index); diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 19afa79683..56854d43d8 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -112,7 +112,6 @@ test "128-bit cmpxchg" { if (!supports_128_bit_atomics) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 03a92a83e9..01b927b913 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -379,7 +379,6 @@ fn testBinaryNot(x: u16) !void { test "binary not 128-bit" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From 3c2636a83dbb964f80f93131b30222ad3889a7e9 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 29 Apr 2023 17:58:09 -0400 Subject: [PATCH 038/725] x86_64: implement more forms of wide mul with overflow --- src/arch/x86_64/CodeGen.zig | 263 +++++++++++++++++------------------- 1 file changed, 122 insertions(+), 141 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 670dc6840c..a09759951d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2434,12 +2434,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const frame_index = try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); - try self.genSetFrameTruncatedOverflowCompare( - tuple_ty, - frame_index, - partial_mcv.register, - cc, - ); + try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); break :result .{ .load_frame = .{ .index = frame_index } }; }, else => unreachable, @@ -2511,12 +2506,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const frame_index = try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); - try self.genSetFrameTruncatedOverflowCompare( - tuple_ty, - frame_index, - partial_mcv.register, - cc, - ); + try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); break :result .{ .load_frame = .{ .index = frame_index } }; }, else => unreachable, @@ -2529,173 +2519,164 @@ fn genSetFrameTruncatedOverflowCompare( self: *Self, tuple_ty: Type, frame_index: FrameIndex, - reg: Register, + src_mcv: MCValue, cc: Condition, ) !void { - const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); + const src_lock = switch (src_mcv) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (src_lock) |lock| self.register_manager.unlockReg(lock); const ty = tuple_ty.structFieldType(0); const int_info = ty.intInfo(self.target.*); - const extended_ty = switch (int_info.signedness) { - .signed => Type.isize, - .unsigned => ty, + + var hi_limb_pl = Type.Payload.Bits{ + .base = .{ .tag = switch (int_info.signedness) { + .signed => .int_signed, + .unsigned => .int_unsigned, + } }, + .data = (int_info.bits - 1) % 64 + 1, }; + const hi_limb_ty = Type.initPayload(&hi_limb_pl.base); + + var rest_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = int_info.bits - hi_limb_pl.data, + }; + const rest_ty = Type.initPayload(&rest_pl.base); const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, gp); - const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); - defer for (temp_regs_locks) |rreg| { - self.register_manager.unlockReg(rreg); - }; + const temp_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); + defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); const overflow_reg = temp_regs[0]; try self.asmSetccRegister(overflow_reg.to8(), cc); const scratch_reg = temp_regs[1]; - try self.genSetReg(scratch_reg, extended_ty, .{ .register = reg }); - try self.truncateRegister(ty, scratch_reg); - try self.genBinOpMir( - .cmp, - extended_ty, - .{ .register = reg }, - .{ .register = scratch_reg }, - ); + const hi_limb_off = if (int_info.bits <= 64) 0 else (int_info.bits - 1) / 64 * 8; + const hi_limb_mcv = if (hi_limb_off > 0) + src_mcv.address().offset(int_info.bits / 64 * 8).deref() + else + src_mcv; + try self.genSetReg(scratch_reg, hi_limb_ty, hi_limb_mcv); + try self.truncateRegister(hi_limb_ty, scratch_reg); + try self.genBinOpMir(.cmp, hi_limb_ty, .{ .register = scratch_reg }, hi_limb_mcv); const eq_reg = temp_regs[2]; try self.asmSetccRegister(eq_reg.to8(), .ne); - try self.genBinOpMir( - .@"or", - Type.u8, - .{ .register = overflow_reg }, - .{ .register = eq_reg }, - ); + try self.genBinOpMir(.@"or", Type.u8, .{ .register = overflow_reg }, .{ .register = eq_reg }); + const payload_off = @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)); + if (hi_limb_off > 0) try self.genSetMem(.{ .frame = frame_index }, payload_off, rest_ty, src_mcv); + try self.genSetMem( + .{ .frame = frame_index }, + payload_off + hi_limb_off, + hi_limb_ty, + .{ .register = scratch_reg }, + ); try self.genSetMem( .{ .frame = frame_index }, @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), tuple_ty.structFieldType(1), .{ .register = overflow_reg.to8() }, ); - try self.genSetMem( - .{ .frame = frame_index }, - @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)), - ty, - .{ .register = scratch_reg }, - ); } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = result: { - const dst_ty = self.air.typeOf(bin_op.lhs); - switch (dst_ty.zigTypeTag()) { - .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), - .Int => { - try self.spillEflagsIfOccupied(); + const dst_ty = self.air.typeOf(bin_op.lhs); + const result: MCValue = switch (dst_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), + .Int => result: { + try self.spillEflagsIfOccupied(); + try self.spillRegisters(&.{ .rax, .rdx }); - const dst_info = dst_ty.intInfo(self.target.*); - const cc: Condition = switch (dst_info.signedness) { - .unsigned => .c, - .signed => .o, + const dst_info = dst_ty.intInfo(self.target.*); + const cc: Condition = switch (dst_info.signedness) { + .unsigned => .c, + .signed => .o, + }; + + const lhs_active_bits = self.activeIntBits(bin_op.lhs); + const rhs_active_bits = self.activeIntBits(bin_op.rhs); + var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) { + .signed => .int_signed, + .unsigned => .int_unsigned, + } }, .data = math.max3(lhs_active_bits, rhs_active_bits, dst_info.bits / 2) }; + const src_ty = Type.initPayload(&src_pl.base); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const tuple_ty = self.air.typeOfIndex(inst); + const extra_bits = if (dst_info.bits <= 64) + self.regExtraBits(dst_ty) + else + dst_info.bits % 64; + const partial_mcv = if (dst_info.signedness == .signed and extra_bits > 0) dst: { + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - const tuple_ty = self.air.typeOfIndex(inst); - if (dst_info.bits >= 8 and math.isPowerOfTwo(dst_info.bits)) { - var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) { - .signed => .int_signed, - .unsigned => .int_unsigned, - } }, .data = math.max3( - self.activeIntBits(bin_op.lhs), - self.activeIntBits(bin_op.rhs), - dst_info.bits / 2, - ) }; - const src_ty = Type.initPayload(&src_pl.base); + const dst_reg: Register = blk: { + if (lhs.isRegister()) break :blk lhs.register; + break :blk try self.copyToTmpRegister(dst_ty, lhs); + }; + const dst_mcv = MCValue{ .register = dst_reg }; + const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_reg_lock); - try self.spillRegisters(&.{ .rax, .rdx }); - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); + const rhs_mcv: MCValue = blk: { + if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; + break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; + }; + const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); - const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); - switch (partial_mcv) { - .register => |reg| { - self.eflags_inst = inst; - break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; - }, - else => {}, - } + try self.genIntMulComplexOpMir(Type.isize, dst_mcv, rhs_mcv); + break :dst dst_mcv; + } else try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); - // For now, this is the only supported multiply that doesn't fit in a register. - assert(dst_info.bits == 128 and src_pl.data == 64); + switch (partial_mcv) { + .register => |reg| if (extra_bits == 0) { + self.eflags_inst = inst; + break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; + } else { const frame_index = try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); - try self.genSetMem( - .{ .frame = frame_index }, - @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), - tuple_ty.structFieldType(1), - .{ .immediate = 0 }, // overflow is impossible for 64-bit*64-bit -> 128-bit - ); - try self.genSetMem( - .{ .frame = frame_index }, - @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)), - tuple_ty.structFieldType(0), - partial_mcv, - ); + try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); break :result .{ .load_frame = .{ .index = frame_index } }; - } + }, + // For now, this is the only supported multiply that doesn't fit in a register. + else => assert(dst_info.bits <= 128 and src_pl.data == 64), + } - const dst_reg: Register = dst_reg: { - switch (dst_info.signedness) { - .signed => { - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - - const dst_reg: Register = blk: { - if (lhs.isRegister()) break :blk lhs.register; - break :blk try self.copyToTmpRegister(dst_ty, lhs); - }; - const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); - defer self.register_manager.unlockReg(dst_reg_lock); - - const rhs_mcv: MCValue = blk: { - if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; - break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; - }; - const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); - - break :dst_reg dst_reg; - }, - .unsigned => { - try self.spillRegisters(&.{ .rax, .rdx }); - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const dst_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, dst_ty, lhs, rhs); - break :dst_reg dst_mcv.register; - }, - } - }; - - const frame_index = - try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); - try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, dst_reg, cc); - break :result .{ .load_frame = .{ .index = frame_index } }; - }, - else => unreachable, - } + const frame_index = + try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); + if (dst_info.bits >= lhs_active_bits + rhs_active_bits) { + try self.genSetMem( + .{ .frame = frame_index }, + @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)), + tuple_ty.structFieldType(0), + partial_mcv, + ); + try self.genSetMem( + .{ .frame = frame_index }, + @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), + tuple_ty.structFieldType(1), + .{ .immediate = 0 }, // overflow is impossible for 64-bit*64-bit -> 128-bit + ); + } else try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); + break :result .{ .load_frame = .{ .index = frame_index } }; + }, + else => unreachable, }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } From c81878978a41f117818ac5b4918cd952f123bad7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 29 Apr 2023 17:56:48 -0400 Subject: [PATCH 039/725] x86_64: optimize wide mul with overflow --- src/arch/x86_64/CodeGen.zig | 61 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a09759951d..7f0d07cf9b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2520,7 +2520,7 @@ fn genSetFrameTruncatedOverflowCompare( tuple_ty: Type, frame_index: FrameIndex, src_mcv: MCValue, - cc: Condition, + overflow_cc: ?Condition, ) !void { const src_lock = switch (src_mcv) { .register => |reg| self.register_manager.lockReg(reg), @@ -2551,7 +2551,7 @@ fn genSetFrameTruncatedOverflowCompare( defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); const overflow_reg = temp_regs[0]; - try self.asmSetccRegister(overflow_reg.to8(), cc); + if (overflow_cc) |cc| try self.asmSetccRegister(overflow_reg.to8(), cc); const scratch_reg = temp_regs[1]; const hi_limb_off = if (int_info.bits <= 64) 0 else (int_info.bits - 1) / 64 * 8; @@ -2564,8 +2564,10 @@ fn genSetFrameTruncatedOverflowCompare( try self.genBinOpMir(.cmp, hi_limb_ty, .{ .register = scratch_reg }, hi_limb_mcv); const eq_reg = temp_regs[2]; - try self.asmSetccRegister(eq_reg.to8(), .ne); - try self.genBinOpMir(.@"or", Type.u8, .{ .register = overflow_reg }, .{ .register = eq_reg }); + if (overflow_cc) |_| { + try self.asmSetccRegister(eq_reg.to8(), .ne); + try self.genBinOpMir(.@"or", Type.u8, .{ .register = overflow_reg }, .{ .register = eq_reg }); + } const payload_off = @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)); if (hi_limb_off > 0) try self.genSetMem(.{ .frame = frame_index }, payload_off, rest_ty, src_mcv); @@ -2579,7 +2581,7 @@ fn genSetFrameTruncatedOverflowCompare( .{ .frame = frame_index }, @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), tuple_ty.structFieldType(1), - .{ .register = overflow_reg.to8() }, + if (overflow_cc) |_| .{ .register = overflow_reg.to8() } else .{ .eflags = .ne }, ); } @@ -2654,27 +2656,36 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); break :result .{ .load_frame = .{ .index = frame_index } }; }, - // For now, this is the only supported multiply that doesn't fit in a register. - else => assert(dst_info.bits <= 128 and src_pl.data == 64), - } + else => { + // For now, this is the only supported multiply that doesn't fit in a register, + // so cc being set is impossible. - const frame_index = - try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); - if (dst_info.bits >= lhs_active_bits + rhs_active_bits) { - try self.genSetMem( - .{ .frame = frame_index }, - @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)), - tuple_ty.structFieldType(0), - partial_mcv, - ); - try self.genSetMem( - .{ .frame = frame_index }, - @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), - tuple_ty.structFieldType(1), - .{ .immediate = 0 }, // overflow is impossible for 64-bit*64-bit -> 128-bit - ); - } else try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); - break :result .{ .load_frame = .{ .index = frame_index } }; + assert(dst_info.bits <= 128 and src_pl.data == 64); + + const frame_index = + try self.allocFrameIndex(FrameAlloc.initType(tuple_ty, self.target.*)); + if (dst_info.bits >= lhs_active_bits + rhs_active_bits) { + try self.genSetMem( + .{ .frame = frame_index }, + @intCast(i32, tuple_ty.structFieldOffset(0, self.target.*)), + tuple_ty.structFieldType(0), + partial_mcv, + ); + try self.genSetMem( + .{ .frame = frame_index }, + @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), + tuple_ty.structFieldType(1), + .{ .immediate = 0 }, + ); + } else try self.genSetFrameTruncatedOverflowCompare( + tuple_ty, + frame_index, + partial_mcv, + null, + ); + break :result .{ .load_frame = .{ .index = frame_index } }; + }, + } }, else => unreachable, }; From 10a4c2269d110d636e7817677fb50c6f418bff34 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 29 Apr 2023 17:57:23 -0400 Subject: [PATCH 040/725] x86_64: enable normal start/test_runner logic on more targets --- lib/std/start.zig | 2 +- lib/test_runner.zig | 1 - test/src/Cases.zig | 6 ++++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 45e2e7da43..4177c9ba01 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -18,7 +18,7 @@ const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start"; // Until then, we have simplified logic here for self-hosted. TODO remove this once // self-hosted is capable enough to handle all of the real start.zig logic. pub const simplified_logic = - (builtin.zig_backend == .stage2_x86_64 and (builtin.link_libc or builtin.os.tag == .plan9)) or + (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .plan9) or builtin.zig_backend == .stage2_x86 or builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or diff --git a/lib/test_runner.zig b/lib/test_runner.zig index f5bb0150a7..8e29d90433 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -13,7 +13,6 @@ var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); pub fn main() void { if (builtin.zig_backend == .stage2_wasm or - (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) or builtin.zig_backend == .stage2_aarch64) { return mainSimple() catch @panic("test failure"); diff --git a/test/src/Cases.zig b/test/src/Cases.zig index defe248fe6..4b023f45b0 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -396,6 +396,12 @@ fn addFromDirInner( // Other backends don't support new liveness format continue; } + if (backend == .stage2 and target.getOsTag() == .macos and + target.getCpuArch() == .x86_64 and builtin.cpu.arch == .aarch64) + { + // Rosetta has issues with ZLD + continue; + } const next = ctx.cases.items.len; try ctx.cases.append(.{ From f37ca3fa7370c501c630c53b370fecdeb313e3be Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 29 Apr 2023 19:31:34 -0400 Subject: [PATCH 041/725] link: cleanup lazy alignment This gets the alignment from the code that creates a lazy symbol instead of guessing it at every use. --- src/arch/x86_64/CodeGen.zig | 6 ------ src/codegen.zig | 8 ++++---- src/link/Coff.zig | 18 +++++------------- src/link/Elf.zig | 12 ++++-------- src/link/MachO.zig | 14 +++++--------- 5 files changed, 18 insertions(+), 40 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7f0d07cf9b..eee89e9ded 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6416,7 +6416,6 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForLazySymbol( .{ .kind = .const_data, .ty = Type.anyerror }, - 4, // dword alignment ); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); @@ -6429,14 +6428,12 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try coff_file.getOrCreateAtomForLazySymbol( .{ .kind = .const_data, .ty = Type.anyerror }, - 4, // dword alignment ); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForLazySymbol( .{ .kind = .const_data, .ty = Type.anyerror }, - 4, // dword alignment ); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); @@ -8504,7 +8501,6 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForLazySymbol( .{ .kind = .const_data, .ty = Type.anyerror }, - 4, // dword alignment ); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); @@ -8517,14 +8513,12 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try coff_file.getOrCreateAtomForLazySymbol( .{ .kind = .const_data, .ty = Type.anyerror }, - 4, // dword alignment ); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForLazySymbol( .{ .kind = .const_data, .ty = Type.anyerror }, - 4, // dword alignment ); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); diff --git a/src/codegen.zig b/src/codegen.zig index f967566034..690e96d25c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -104,7 +104,7 @@ pub fn generateLazySymbol( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, -) CodeGenError!Result { +) CodeGenError!struct { res: Result, alignment: u32 } { _ = debug_output; _ = reloc_info; @@ -133,13 +133,13 @@ pub fn generateLazySymbol( code.appendAssumeCapacity(0); } mem.writeInt(u32, code.items[offset..][0..4], @intCast(u32, code.items.len), endian); - return Result.ok; - } else return .{ .fail = try ErrorMsg.create( + return .{ .res = Result.ok, .alignment = 4 }; + } else return .{ .res = .{ .fail = try ErrorMsg.create( bin_file.allocator, src_loc, "TODO implement generateLazySymbol for {s} {}", .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(mod) }, - ) }; + ) }, .alignment = undefined }; } pub fn generateSymbol( diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 0af681bb5e..d20d17f2b1 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -145,7 +145,6 @@ const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, const LazySymbolMetadata = struct { text_atom: ?Atom.Index = null, rdata_atom: ?Atom.Index = null, - alignment: u32, }; const DeclMetadata = struct { @@ -1195,13 +1194,11 @@ fn updateLazySymbol(self: *Coff, decl: Module.Decl.OptionalIndex, metadata: Lazy link.File.LazySymbol.initDecl(.code, decl, mod), atom, self.text_section_index.?, - metadata.alignment, ); if (metadata.rdata_atom) |atom| try self.updateLazySymbolAtom( link.File.LazySymbol.initDecl(.const_data, decl, mod), atom, self.rdata_section_index.?, - metadata.alignment, ); } @@ -1210,7 +1207,6 @@ fn updateLazySymbolAtom( sym: link.File.LazySymbol, atom_index: Atom.Index, section_index: u16, - required_alignment: u32, ) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; @@ -1238,7 +1234,7 @@ fn updateLazySymbolAtom( const res = try codegen.generateLazySymbol(&self.base, src, sym, &code_buffer, .none, .{ .parent_atom_index = local_sym_index, }); - const code = switch (res) { + const code = switch (res.res) { .ok => code_buffer.items, .fail => |em| { log.err("{s}", .{em.msg}); @@ -1252,11 +1248,11 @@ fn updateLazySymbolAtom( symbol.section_number = @intToEnum(coff.SectionNumber, section_index + 1); symbol.type = .{ .complex_type = .NULL, .base_type = .NULL }; - const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); + const vaddr = try self.allocateAtom(atom_index, code_len, res.alignment); errdefer self.freeAtom(atom_index); log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr }); - log.debug(" (required alignment 0x{x})", .{required_alignment}); + log.debug(" (required alignment 0x{x})", .{res.alignment}); atom.size = code_len; symbol.value = vaddr; @@ -1265,14 +1261,10 @@ fn updateLazySymbolAtom( try self.writeAtom(atom_index, code); } -pub fn getOrCreateAtomForLazySymbol( - self: *Coff, - sym: link.File.LazySymbol, - alignment: u32, -) !Atom.Index { +pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Atom.Index { const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); errdefer _ = self.lazy_syms.pop(); - if (!gop.found_existing) gop.value_ptr.* = .{ .alignment = alignment }; + if (!gop.found_existing) gop.value_ptr.* = .{}; const atom = switch (sym.kind) { .code => &gop.value_ptr.text_atom, .const_data => &gop.value_ptr.rdata_atom, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 48d952b6cc..b9c113f834 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -67,7 +67,6 @@ const Section = struct { const LazySymbolMetadata = struct { text_atom: ?Atom.Index = null, rodata_atom: ?Atom.Index = null, - alignment: u32, }; const DeclMetadata = struct { @@ -2377,10 +2376,10 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { } } -pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol, alignment: u32) !Atom.Index { +pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Index { const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); errdefer _ = self.lazy_syms.pop(); - if (!gop.found_existing) gop.value_ptr.* = .{ .alignment = alignment }; + if (!gop.found_existing) gop.value_ptr.* = .{}; const atom = switch (sym.kind) { .code => &gop.value_ptr.text_atom, .const_data => &gop.value_ptr.rodata_atom, @@ -2663,13 +2662,11 @@ fn updateLazySymbol(self: *Elf, decl: Module.Decl.OptionalIndex, metadata: LazyS File.LazySymbol.initDecl(.code, decl, mod), atom, self.text_section_index.?, - metadata.alignment, ); if (metadata.rodata_atom) |atom| try self.updateLazySymbolAtom( File.LazySymbol.initDecl(.const_data, decl, mod), atom, self.rodata_section_index.?, - metadata.alignment, ); } @@ -2678,7 +2675,6 @@ fn updateLazySymbolAtom( sym: File.LazySymbol, atom_index: Atom.Index, shdr_index: u16, - required_alignment: u32, ) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; @@ -2710,7 +2706,7 @@ fn updateLazySymbolAtom( const res = try codegen.generateLazySymbol(&self.base, src, sym, &code_buffer, .none, .{ .parent_atom_index = local_sym_index, }); - const code = switch (res) { + const code = switch (res.res) { .ok => code_buffer.items, .fail => |em| { log.err("{s}", .{em.msg}); @@ -2728,7 +2724,7 @@ fn updateLazySymbolAtom( .st_value = 0, .st_size = 0, }; - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); + const vaddr = try self.allocateAtom(atom_index, code.len, res.alignment); errdefer self.freeAtom(atom_index); log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 21633dea64..a57742507d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -238,7 +238,6 @@ const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, const LazySymbolMetadata = struct { text_atom: ?Atom.Index = null, data_const_atom: ?Atom.Index = null, - alignment: u32, }; const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); @@ -2043,13 +2042,11 @@ fn updateLazySymbol(self: *MachO, decl: Module.Decl.OptionalIndex, metadata: Laz File.LazySymbol.initDecl(.code, decl, mod), atom, self.text_section_index.?, - metadata.alignment, ); if (metadata.data_const_atom) |atom| try self.updateLazySymbolAtom( File.LazySymbol.initDecl(.const_data, decl, mod), atom, self.data_const_section_index.?, - metadata.alignment, ); } @@ -2058,7 +2055,6 @@ fn updateLazySymbolAtom( sym: File.LazySymbol, atom_index: Atom.Index, section_index: u8, - required_alignment: u32, ) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; @@ -2090,7 +2086,7 @@ fn updateLazySymbolAtom( const res = try codegen.generateLazySymbol(&self.base, src, sym, &code_buffer, .none, .{ .parent_atom_index = local_sym_index, }); - const code = switch (res) { + const code = switch (res.res) { .ok => code_buffer.items, .fail => |em| { log.err("{s}", .{em.msg}); @@ -2104,11 +2100,11 @@ fn updateLazySymbolAtom( symbol.n_sect = section_index + 1; symbol.n_desc = 0; - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); + const vaddr = try self.allocateAtom(atom_index, code.len, res.alignment); errdefer self.freeAtom(atom_index); log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr }); - log.debug(" (required alignment 0x{x}", .{required_alignment}); + log.debug(" (required alignment 0x{x}", .{res.alignment}); atom.size = code.len; symbol.n_value = vaddr; @@ -2117,10 +2113,10 @@ fn updateLazySymbolAtom( try self.writeAtom(atom_index, code); } -pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol, alignment: u32) !Atom.Index { +pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index { const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); errdefer _ = self.lazy_syms.pop(); - if (!gop.found_existing) gop.value_ptr.* = .{ .alignment = alignment }; + if (!gop.found_existing) gop.value_ptr.* = .{}; const atom = switch (sym.kind) { .code => &gop.value_ptr.text_atom, .const_data => &gop.value_ptr.data_const_atom, From 372bc960b8315ea1ff17b369b0b33485d5e34cfb Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 29 Apr 2023 19:57:44 -0400 Subject: [PATCH 042/725] link: update decl-specific lazy symbols --- src/link.zig | 4 ++-- src/link/Coff.zig | 23 ++++++++++++++--------- src/link/Elf.zig | 23 ++++++++++++++--------- src/link/MachO.zig | 17 +++++++++-------- 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/link.zig b/src/link.zig index 672a53999f..74e3ca85fc 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1120,8 +1120,8 @@ pub const File = struct { kind: Kind, ty: Type, - pub fn initDecl(kind: Kind, decl: Module.Decl.OptionalIndex, mod: *Module) LazySymbol { - return .{ .kind = kind, .ty = if (decl.unwrap()) |decl_index| + pub fn initDecl(kind: Kind, decl: ?Module.Decl.Index, mod: *Module) LazySymbol { + return .{ .kind = kind, .ty = if (decl) |decl_index| mod.declPtr(decl_index).val.castTag(.ty).?.data else Type.anyerror }; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index d20d17f2b1..deac8d1fd6 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1136,7 +1136,11 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In return atom.getSymbolIndex().?; } -pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { +pub fn updateDecl( + self: *Coff, + module: *Module, + decl_index: Module.Decl.Index, +) link.File.UpdateDeclError!void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); } @@ -1146,6 +1150,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! const tracy = trace(@src()); defer tracy.end(); + try self.updateLazySymbol(decl_index); + const decl = module.declPtr(decl_index); if (decl.val.tag() == .extern_fn) { @@ -1188,7 +1194,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -fn updateLazySymbol(self: *Coff, decl: Module.Decl.OptionalIndex, metadata: LazySymbolMetadata) !void { +fn updateLazySymbol(self: *Coff, decl: ?Module.Decl.Index) !void { + const metadata = self.lazy_syms.get(Module.Decl.OptionalIndex.init(decl)) orelse return; const mod = self.base.options.module.?; if (metadata.text_atom) |atom| try self.updateLazySymbolAtom( link.File.LazySymbol.initDecl(.code, decl, mod), @@ -1402,7 +1409,7 @@ pub fn updateDeclExports( module: *Module, decl_index: Module.Decl.Index, exports: []const *Module.Export, -) !void { +) link.File.UpdateDeclExportsError!void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); } @@ -1599,12 +1606,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod // Most lazy symbols can be updated when the corresponding decl is, // so we only have to worry about the one without an associated decl. - if (self.lazy_syms.get(.none)) |metadata| { - self.updateLazySymbol(.none, metadata) catch |err| switch (err) { - error.CodegenFail => return error.FlushFailure, - else => |e| return e, - }; - } + self.updateLazySymbol(null) catch |err| switch (err) { + error.CodegenFail => return error.FlushFailure, + else => |e| return e, + }; const gpa = self.base.allocator; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b9c113f834..29ae97150e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1034,12 +1034,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Most lazy symbols can be updated when the corresponding decl is, // so we only have to worry about the one without an associated decl. - if (self.lazy_syms.get(.none)) |metadata| { - self.updateLazySymbol(.none, metadata) catch |err| switch (err) { - error.CodegenFail => return error.FlushFailure, - else => |e| return e, - }; - } + self.updateLazySymbol(null) catch |err| switch (err) { + error.CodegenFail => return error.FlushFailure, + else => |e| return e, + }; // TODO This linker code currently assumes there is only 1 compilation unit and it // corresponds to the Zig source code. @@ -2579,7 +2577,11 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !void { +pub fn updateDecl( + self: *Elf, + module: *Module, + decl_index: Module.Decl.Index, +) File.UpdateDeclError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } @@ -2590,6 +2592,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v const tracy = trace(@src()); defer tracy.end(); + try self.updateLazySymbol(decl_index); + const decl = module.declPtr(decl_index); if (decl.val.tag() == .extern_fn) { @@ -2656,7 +2660,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -fn updateLazySymbol(self: *Elf, decl: Module.Decl.OptionalIndex, metadata: LazySymbolMetadata) !void { +fn updateLazySymbol(self: *Elf, decl: ?Module.Decl.Index) !void { + const metadata = self.lazy_syms.get(Module.Decl.OptionalIndex.init(decl)) orelse return; const mod = self.base.options.module.?; if (metadata.text_atom) |atom| try self.updateLazySymbolAtom( File.LazySymbol.initDecl(.code, decl, mod), @@ -2810,7 +2815,7 @@ pub fn updateDeclExports( module: *Module, decl_index: Module.Decl.Index, exports: []const *Module.Export, -) !void { +) File.UpdateDeclExportsError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a57742507d..5136bd84a2 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -495,12 +495,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // Most lazy symbols can be updated when the corresponding decl is, // so we only have to worry about the one without an associated decl. - if (self.lazy_syms.get(.none)) |metadata| { - self.updateLazySymbol(.none, metadata) catch |err| switch (err) { - error.CodegenFail => return error.FlushFailure, - else => |e| return e, - }; - } + self.updateLazySymbol(null) catch |err| switch (err) { + error.CodegenFail => return error.FlushFailure, + else => |e| return e, + }; const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; @@ -1962,6 +1960,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) const tracy = trace(@src()); defer tracy.end(); + try self.updateLazySymbol(decl_index); + const decl = module.declPtr(decl_index); if (decl.val.tag() == .extern_fn) { @@ -2036,7 +2036,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -fn updateLazySymbol(self: *MachO, decl: Module.Decl.OptionalIndex, metadata: LazySymbolMetadata) !void { +fn updateLazySymbol(self: *MachO, decl: ?Module.Decl.Index) !void { + const metadata = self.lazy_syms.get(Module.Decl.OptionalIndex.init(decl)) orelse return; const mod = self.base.options.module.?; if (metadata.text_atom) |atom| try self.updateLazySymbolAtom( File.LazySymbol.initDecl(.code, decl, mod), @@ -2353,7 +2354,7 @@ pub fn updateDeclExports( module: *Module, decl_index: Module.Decl.Index, exports: []const *Module.Export, -) !void { +) File.UpdateDeclExportsError!void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } From 19bd7d12b0186f0e45c77c564251d6355966b2ef Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 30 Apr 2023 07:30:32 -0400 Subject: [PATCH 043/725] x86_64: factor out lazy_sym --- src/arch/x86_64/CodeGen.zig | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index eee89e9ded..99fb516b45 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6413,10 +6413,10 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_lock); + const mod = self.bin_file.options.module.?; + const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForLazySymbol( - .{ .kind = .const_data, .ty = Type.anyerror }, - ); + const atom_index = try elf_file.getOrCreateAtomForLazySymbol(lazy_sym); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = atom.getOffsetTableAddress(elf_file); @@ -6426,15 +6426,11 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), ); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try coff_file.getOrCreateAtomForLazySymbol( - .{ .kind = .const_data, .ty = Type.anyerror }, - ); + const atom_index = try coff_file.getOrCreateAtomForLazySymbol(lazy_sym); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = try macho_file.getOrCreateAtomForLazySymbol( - .{ .kind = .const_data, .ty = Type.anyerror }, - ); + const atom_index = try macho_file.getOrCreateAtomForLazySymbol(lazy_sym); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else { @@ -8498,10 +8494,10 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_lock); + const mod = self.bin_file.options.module.?; + const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForLazySymbol( - .{ .kind = .const_data, .ty = Type.anyerror }, - ); + const atom_index = try elf_file.getOrCreateAtomForLazySymbol(lazy_sym); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = atom.getOffsetTableAddress(elf_file); @@ -8511,15 +8507,11 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), ); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try coff_file.getOrCreateAtomForLazySymbol( - .{ .kind = .const_data, .ty = Type.anyerror }, - ); + const atom_index = try coff_file.getOrCreateAtomForLazySymbol(lazy_sym); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = try macho_file.getOrCreateAtomForLazySymbol( - .{ .kind = .const_data, .ty = Type.anyerror }, - ); + const atom_index = try macho_file.getOrCreateAtomForLazySymbol(lazy_sym); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else { From 0489a63a432b31ce0fd9d6ec6c8e2fb72a920573 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 30 Apr 2023 21:13:29 -0400 Subject: [PATCH 044/725] Sema: use trap for backends that don't support panic_fn Debuggers also catch trap, but the code is not allowed to continue. --- src/Sema.zig | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 71a1215dcd..79f2fd7fca 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23287,8 +23287,7 @@ fn panicWithMsg( const arena = sema.arena; if (!mod.backendSupportsFeature(.panic_fn)) { - _ = try block.addNoOp(.breakpoint); - _ = try block.addNoOp(.unreach); + _ = try block.addNoOp(.trap); return; } const panic_fn = try sema.getBuiltin("panic"); @@ -23336,8 +23335,7 @@ fn panicUnwrapError( { if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) { - _ = try fail_block.addNoOp(.breakpoint); - _ = try fail_block.addNoOp(.unreach); + _ = try fail_block.addNoOp(.trap); } else { const panic_fn = try sema.getBuiltin("panicUnwrapError"); const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); @@ -23462,8 +23460,7 @@ fn safetyCheckFormatted( defer fail_block.instructions.deinit(gpa); if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) { - _ = try fail_block.addNoOp(.breakpoint); - _ = try fail_block.addNoOp(.unreach); + _ = try fail_block.addNoOp(.trap); } else { const panic_fn = try sema.getBuiltin(func); _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null); From 47a34d038dd114533c3fcb130e03249ad846fe9c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 30 Apr 2023 21:47:19 -0400 Subject: [PATCH 045/725] x86_64: implement tagName --- src/arch/x86_64/CodeGen.zig | 292 ++++++++++++++++++++++++++++++++---- src/codegen.zig | 40 ++++- src/link/Coff.zig | 33 ++-- src/link/Elf.zig | 31 ++-- src/link/MachO.zig | 33 ++-- test/behavior/enum.zig | 5 - test/behavior/memset.zig | 2 - test/behavior/type.zig | 2 - test/behavior/union.zig | 1 - 9 files changed, 368 insertions(+), 71 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 99fb516b45..71dcbfa461 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -56,7 +56,10 @@ liveness: Liveness, bin_file: *link.File, debug_output: DebugInfoOutput, target: *const std.Target, -mod_fn: *const Module.Fn, +owner: union(enum) { + mod_fn: *const Module.Fn, + decl: Module.Decl.Index, +}, err_msg: ?*ErrorMsg, args: []MCValue, ret_mcv: InstTracking, @@ -617,7 +620,7 @@ pub fn generate( .target = &bin_file.options.target, .bin_file = bin_file, .debug_output = debug_output, - .mod_fn = module_fn, + .owner = .{ .mod_fn = module_fn }, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` @@ -745,6 +748,92 @@ pub fn generate( } } +pub fn generateLazy( + bin_file: *link.File, + src_loc: Module.SrcLoc, + lazy_sym: link.File.LazySymbol, + code: *std.ArrayList(u8), + debug_output: DebugInfoOutput, +) CodeGenError!Result { + const gpa = bin_file.allocator; + var function = Self{ + .gpa = gpa, + .air = undefined, + .liveness = undefined, + .target = &bin_file.options.target, + .bin_file = bin_file, + .debug_output = debug_output, + .owner = .{ .decl = lazy_sym.ty.getOwnerDecl() }, + .err_msg = null, + .args = undefined, + .ret_mcv = undefined, + .fn_type = undefined, + .arg_index = undefined, + .src_loc = src_loc, + .end_di_line = undefined, // no debug info yet + .end_di_column = undefined, // no debug info yet + }; + defer { + function.mir_instructions.deinit(gpa); + function.mir_extra.deinit(gpa); + } + + function.genLazy(lazy_sym) catch |err| switch (err) { + error.CodegenFail => return Result{ .fail = function.err_msg.? }, + error.OutOfRegisters => return Result{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }; + + var mir = Mir{ + .instructions = function.mir_instructions.toOwnedSlice(), + .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .frame_locs = function.frame_locs.toOwnedSlice(), + }; + defer mir.deinit(bin_file.allocator); + + var emit = Emit{ + .lower = .{ + .allocator = bin_file.allocator, + .mir = mir, + .target = &bin_file.options.target, + .src_loc = src_loc, + }, + .bin_file = bin_file, + .debug_output = debug_output, + .code = code, + .prev_di_pc = undefined, // no debug info yet + .prev_di_line = undefined, // no debug info yet + .prev_di_column = undefined, // no debug info yet + }; + defer emit.deinit(); + emit.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, + error.InvalidInstruction, error.CannotEncode => |e| { + const msg = switch (e) { + error.InvalidInstruction => "CodeGen failed to find a viable instruction.", + error.CannotEncode => "CodeGen failed to encode the instruction.", + }; + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "{s} This is a bug in the Zig compiler.", + .{msg}, + ), + }; + }, + else => |e| return e, + }; + + if (function.err_msg) |em| { + return Result{ .fail = em }; + } else { + return Result.ok; + } +} + const FormatDeclData = struct { mod: *Module, decl_index: Module.Decl.Index, @@ -1545,6 +1634,103 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { verbose_tracking_log.debug("{}", .{self.fmtTracking()}); } +fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { + switch (lazy_sym.ty.zigTypeTag()) { + .Enum => { + const enum_ty = lazy_sym.ty; + wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)}); + + const param_regs = abi.getCAbiIntParamRegs(self.target.*); + const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); + defer for (param_locks) |lock| self.register_manager.unlockReg(lock); + + const ret_reg = param_regs[0]; + const enum_mcv = MCValue{ .register = param_regs[1] }; + + var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount()); + defer self.gpa.free(exitlude_jump_relocs); + + const data_reg = try self.register_manager.allocReg(null, gp); + const data_lock = self.register_manager.lockRegAssumeUnused(data_reg); + defer self.register_manager.unlockReg(data_lock); + + const data_lazy_sym = link.File.LazySymbol{ .kind = .const_data, .ty = enum_ty }; + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = elf_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); + try self.asmRegisterMemory( + .mov, + data_reg.to64(), + Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), + ); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = coff_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = macho_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); + } else { + return self.fail("TODO implement {s} for {}", .{ + @tagName(lazy_sym.kind), + lazy_sym.ty.fmt(self.bin_file.options.module.?), + }); + } + + var data_off: i32 = 0; + for ( + exitlude_jump_relocs, + enum_ty.enumFields().keys(), + 0.., + ) |*exitlude_jump_reloc, tag_name, index| { + var tag_pl = Value.Payload.U32{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, index), + }; + const tag_val = Value.initPayload(&tag_pl.base); + const tag_mcv = try self.genTypedValue(.{ .ty = enum_ty, .val = tag_val }); + try self.genBinOpMir(.cmp, enum_ty, enum_mcv, tag_mcv); + const skip_reloc = try self.asmJccReloc(undefined, .ne); + + try self.genSetMem( + .{ .reg = ret_reg }, + 0, + Type.usize, + .{ .register_offset = .{ .reg = data_reg, .off = data_off } }, + ); + try self.genSetMem(.{ .reg = ret_reg }, 8, Type.usize, .{ .immediate = tag_name.len }); + + exitlude_jump_reloc.* = try self.asmJmpReloc(undefined); + try self.performReloc(skip_reloc); + + data_off += @intCast(i32, tag_name.len + 1); + } + + try self.airTrap(); + + for (exitlude_jump_relocs) |reloc| try self.performReloc(reloc); + try self.asmOpOnly(.ret); + }, + else => return self.fail( + "TODO implement {s} for {}", + .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(self.bin_file.options.module.?) }, + ), + } +} + +fn getOwnerDecl(self: *const Self) Module.Decl.Index { + return switch (self.owner) { + .mod_fn => |mod_fn| mod_fn.owner_decl, + .decl => |index| index, + }; +} + fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void { const reg = value.getReg() orelse return; if (self.register_manager.isRegFree(reg)) { @@ -6020,7 +6206,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOfIndex(inst); const src_index = self.air.instructions.items(.data)[inst].arg.src_index; - const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index); + const name = self.owner.mod_fn.getParamName(self.bin_file.options.module.?, src_index); try self.genArgDbgInfo(ty, name, dst_mcv); break :result dst_mcv; @@ -6044,7 +6230,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { //}, else => unreachable, // not a valid function parameter }; - try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, loc); + try dw.genArgDbgInfo(name, ty, self.getOwnerDecl(), loc); }, .plan9 => {}, .none => {}, @@ -6085,7 +6271,7 @@ fn genVarDbgInfo( break :blk .nop; }, }; - try dw.genVarDbgInfo(name, ty, self.mod_fn.owner_decl, is_ptr, loc); + try dw.genVarDbgInfo(name, ty, self.getOwnerDecl(), is_ptr, loc); }, .plan9 => {}, .none => {}, @@ -6243,7 +6429,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const decl_name = mem.sliceTo(mod.declPtr(extern_fn.owner_decl).name, 0); const lib_name = mem.sliceTo(extern_fn.lib_name, 0); if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); + const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name); _ = try self.addInst(.{ .tag = .mov_linker, @@ -6257,7 +6443,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name); - const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); + const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); _ = try self.addInst(.{ .tag = .call_extern, .ops = undefined, @@ -6416,7 +6602,8 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.options.module.?; const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForLazySymbol(lazy_sym); + const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = atom.getOffsetTableAddress(elf_file); @@ -6426,11 +6613,13 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), ); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try coff_file.getOrCreateAtomForLazySymbol(lazy_sym); + const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = try macho_file.getOrCreateAtomForLazySymbol(lazy_sym); + const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else { @@ -7530,7 +7719,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr }), ), .load_direct => |sym_index| if (try self.movMirTag(ty) == .mov) { - const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); + const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); _ = try self.addInst(.{ .tag = .mov_linker, .ops = .direct_reloc, @@ -7557,7 +7746,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr ); }, .lea_direct, .lea_got => |sym_index| { - const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); + const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); _ = try self.addInst(.{ .tag = switch (src_mcv) { .lea_direct => .lea_linker, @@ -7577,7 +7766,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr }); }, .lea_tlv => |sym_index| { - const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); + const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); if (self.bin_file.cast(link.File.MachO)) |_| { _ = try self.addInst(.{ .tag = .lea_linker, @@ -8475,10 +8664,64 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { fn airTagName(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; + const inst_ty = self.air.typeOfIndex(inst); + const enum_ty = self.air.typeOf(un_op); + + // We need a properly aligned and sized call frame to be able to call this function. + { + const needed_call_frame = FrameAlloc.init(.{ + .size = inst_ty.abiSize(self.target.*), + .alignment = inst_ty.abiAlignment(self.target.*), + }); + const frame_allocs_slice = self.frame_allocs.slice(); + const stack_frame_size = + &frame_allocs_slice.items(.abi_size)[@enumToInt(FrameIndex.call_frame)]; + stack_frame_size.* = @max(stack_frame_size.*, needed_call_frame.abi_size); + const stack_frame_align = + &frame_allocs_slice.items(.abi_align)[@enumToInt(FrameIndex.call_frame)]; + stack_frame_align.* = @max(stack_frame_align.*, needed_call_frame.abi_align); + } + + try self.spillEflagsIfOccupied(); + try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*)); + + const param_regs = abi.getCAbiIntParamRegs(self.target.*); + + const dst_mcv = try self.allocRegOrMem(inst, false); + try self.genSetReg(param_regs[0], Type.usize, dst_mcv.address()); + const operand = try self.resolveInst(un_op); - _ = operand; - return self.fail("TODO implement airTagName for x86_64", .{}); - //return self.finishAir(inst, result, .{ un_op, .none, .none }); + try self.genSetReg(param_regs[1], enum_ty, operand); + + const mod = self.bin_file.options.module.?; + const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); + try self.asmMemory( + .call, + Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), + ); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); + try self.asmRegister(.call, .rax); + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); + try self.asmRegister(.call, .rax); + } else { + return self.fail("TODO implement airTagName for x86_64 {s}", .{@tagName(self.bin_file.tag)}); + } + + return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); } fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { @@ -8497,7 +8740,8 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.options.module.?; const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForLazySymbol(lazy_sym); + const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = atom.getOffsetTableAddress(elf_file); @@ -8507,11 +8751,13 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), ); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try coff_file.getOrCreateAtomForLazySymbol(lazy_sym); + const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = try macho_file.getOrCreateAtomForLazySymbol(lazy_sym); + const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); } else { @@ -8833,12 +9079,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV } fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - const mcv: MCValue = switch (try codegen.genTypedValue( - self.bin_file, - self.src_loc, - arg_tv, - self.mod_fn.owner_decl, - )) { + return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.getOwnerDecl())) { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, @@ -8853,7 +9094,6 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { return error.CodegenFail; }, }; - return mcv; } const CallMCValues = struct { diff --git a/src/codegen.zig b/src/codegen.zig index 690e96d25c..078feb409d 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -7,6 +7,7 @@ const link = @import("link.zig"); const log = std.log.scoped(.codegen); const mem = std.mem; const math = std.math; +const target_util = @import("target.zig"); const trace = @import("tracy.zig").trace; const Air = @import("Air.zig"); @@ -89,6 +90,19 @@ pub fn generateFunction( } } +pub fn generateLazyFunction( + bin_file: *link.File, + src_loc: Module.SrcLoc, + lazy_sym: link.File.LazySymbol, + code: *std.ArrayList(u8), + debug_output: DebugInfoOutput, +) CodeGenError!Result { + switch (bin_file.options.target.cpu.arch) { + .x86_64 => return @import("arch/x86_64/CodeGen.zig").generateLazy(bin_file, src_loc, lazy_sym, code, debug_output), + else => unreachable, + } +} + fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian, code: []u8) void { _ = target; const bits = @typeInfo(F).Float.bits; @@ -101,11 +115,11 @@ pub fn generateLazySymbol( bin_file: *link.File, src_loc: Module.SrcLoc, lazy_sym: link.File.LazySymbol, + alignment: *u32, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, -) CodeGenError!struct { res: Result, alignment: u32 } { - _ = debug_output; +) CodeGenError!Result { _ = reloc_info; const tracy = trace(@src()); @@ -120,7 +134,13 @@ pub fn generateLazySymbol( lazy_sym.ty.fmt(mod), }); - if (lazy_sym.kind == .const_data and lazy_sym.ty.isAnyError()) { + if (lazy_sym.kind == .code) { + alignment.* = target_util.defaultFunctionAlignment(target); + return generateLazyFunction(bin_file, src_loc, lazy_sym, code, debug_output); + } + + if (lazy_sym.ty.isAnyError()) { + alignment.* = 4; const err_names = mod.error_name_list.items; mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, err_names.len), endian); var offset = code.items.len; @@ -133,13 +153,21 @@ pub fn generateLazySymbol( code.appendAssumeCapacity(0); } mem.writeInt(u32, code.items[offset..][0..4], @intCast(u32, code.items.len), endian); - return .{ .res = Result.ok, .alignment = 4 }; - } else return .{ .res = .{ .fail = try ErrorMsg.create( + return Result.ok; + } else if (lazy_sym.ty.zigTypeTag() == .Enum) { + alignment.* = 1; + for (lazy_sym.ty.enumFields().keys()) |tag_name| { + try code.ensureUnusedCapacity(tag_name.len + 1); + code.appendSliceAssumeCapacity(tag_name); + code.appendAssumeCapacity(0); + } + return Result.ok; + } else return .{ .fail = try ErrorMsg.create( bin_file.allocator, src_loc, "TODO implement generateLazySymbol for {s} {}", .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(mod) }, - ) }, .alignment = undefined }; + ) }; } pub fn generateSymbol( diff --git a/src/link/Coff.zig b/src/link/Coff.zig index deac8d1fd6..41d4617e53 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1218,6 +1218,7 @@ fn updateLazySymbolAtom( const gpa = self.base.allocator; const mod = self.base.options.module.?; + var required_alignment: u32 = undefined; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1238,10 +1239,16 @@ fn updateLazySymbolAtom( .parent_decl_node = undefined, .lazy = .unneeded, }; - const res = try codegen.generateLazySymbol(&self.base, src, sym, &code_buffer, .none, .{ - .parent_atom_index = local_sym_index, - }); - const code = switch (res.res) { + const res = try codegen.generateLazySymbol( + &self.base, + src, + sym, + &required_alignment, + &code_buffer, + .none, + .{ .parent_atom_index = local_sym_index }, + ); + const code = switch (res) { .ok => code_buffer.items, .fail => |em| { log.err("{s}", .{em.msg}); @@ -1255,11 +1262,11 @@ fn updateLazySymbolAtom( symbol.section_number = @intToEnum(coff.SectionNumber, section_index + 1); symbol.type = .{ .complex_type = .NULL, .base_type = .NULL }; - const vaddr = try self.allocateAtom(atom_index, code_len, res.alignment); + const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); errdefer self.freeAtom(atom_index); log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr }); - log.debug(" (required alignment 0x{x})", .{res.alignment}); + log.debug(" (required alignment 0x{x})", .{required_alignment}); atom.size = code_len; symbol.value = vaddr; @@ -1270,14 +1277,20 @@ fn updateLazySymbolAtom( pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Atom.Index { const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); - errdefer _ = self.lazy_syms.pop(); + errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const atom = switch (sym.kind) { + const atom_ptr = switch (sym.kind) { .code => &gop.value_ptr.text_atom, .const_data => &gop.value_ptr.rdata_atom, }; - if (atom.* == null) atom.* = try self.createAtom(); - return atom.*.?; + if (atom_ptr.*) |atom| return atom; + const atom = try self.createAtom(); + atom_ptr.* = atom; + try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + .code => self.text_section_index.?, + .const_data => self.rdata_section_index.?, + }); + return atom; } pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: Module.Decl.Index) !Atom.Index { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 29ae97150e..ec113e5c8a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2376,14 +2376,20 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Index { const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); - errdefer _ = self.lazy_syms.pop(); + errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const atom = switch (sym.kind) { + const atom_ptr = switch (sym.kind) { .code => &gop.value_ptr.text_atom, .const_data => &gop.value_ptr.rodata_atom, }; - if (atom.* == null) atom.* = try self.createAtom(); - return atom.*.?; + if (atom_ptr.*) |atom| return atom; + const atom = try self.createAtom(); + atom_ptr.* = atom; + try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + .code => self.text_section_index.?, + .const_data => self.rodata_section_index.?, + }); + return atom; } pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { @@ -2684,6 +2690,7 @@ fn updateLazySymbolAtom( const gpa = self.base.allocator; const mod = self.base.options.module.?; + var required_alignment: u32 = undefined; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -2708,10 +2715,16 @@ fn updateLazySymbolAtom( .parent_decl_node = undefined, .lazy = .unneeded, }; - const res = try codegen.generateLazySymbol(&self.base, src, sym, &code_buffer, .none, .{ - .parent_atom_index = local_sym_index, - }); - const code = switch (res.res) { + const res = try codegen.generateLazySymbol( + &self.base, + src, + sym, + &required_alignment, + &code_buffer, + .none, + .{ .parent_atom_index = local_sym_index }, + ); + const code = switch (res) { .ok => code_buffer.items, .fail => |em| { log.err("{s}", .{em.msg}); @@ -2729,7 +2742,7 @@ fn updateLazySymbolAtom( .st_value = 0, .st_size = 0, }; - const vaddr = try self.allocateAtom(atom_index, code.len, res.alignment); + const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); errdefer self.freeAtom(atom_index); log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5136bd84a2..c14168c66b 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2060,6 +2060,7 @@ fn updateLazySymbolAtom( const gpa = self.base.allocator; const mod = self.base.options.module.?; + var required_alignment: u32 = undefined; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -2084,10 +2085,16 @@ fn updateLazySymbolAtom( .parent_decl_node = undefined, .lazy = .unneeded, }; - const res = try codegen.generateLazySymbol(&self.base, src, sym, &code_buffer, .none, .{ - .parent_atom_index = local_sym_index, - }); - const code = switch (res.res) { + const res = try codegen.generateLazySymbol( + &self.base, + src, + sym, + &required_alignment, + &code_buffer, + .none, + .{ .parent_atom_index = local_sym_index }, + ); + const code = switch (res) { .ok => code_buffer.items, .fail => |em| { log.err("{s}", .{em.msg}); @@ -2101,11 +2108,11 @@ fn updateLazySymbolAtom( symbol.n_sect = section_index + 1; symbol.n_desc = 0; - const vaddr = try self.allocateAtom(atom_index, code.len, res.alignment); + const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); errdefer self.freeAtom(atom_index); log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr }); - log.debug(" (required alignment 0x{x}", .{res.alignment}); + log.debug(" (required alignment 0x{x}", .{required_alignment}); atom.size = code.len; symbol.n_value = vaddr; @@ -2116,14 +2123,20 @@ fn updateLazySymbolAtom( pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index { const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); - errdefer _ = self.lazy_syms.pop(); + errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const atom = switch (sym.kind) { + const atom_ptr = switch (sym.kind) { .code => &gop.value_ptr.text_atom, .const_data => &gop.value_ptr.data_const_atom, }; - if (atom.* == null) atom.* = try self.createAtom(); - return atom.*.?; + if (atom_ptr.*) |atom| return atom; + const atom = try self.createAtom(); + atom_ptr.* = atom; + try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + .code => self.text_section_index.?, + .const_data => self.data_const_section_index.?, + }); + return atom; } fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index de5c0efb8e..26b941bcdc 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -981,7 +981,6 @@ fn test3_2(f: Test3Foo) !void { } test "@tagName" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -997,7 +996,6 @@ fn testEnumTagNameBare(n: anytype) []const u8 { const BareNumber = enum { One, Two, Three }; test "@tagName non-exhaustive enum" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1008,7 +1006,6 @@ test "@tagName non-exhaustive enum" { const NonExhaustive = enum(u8) { A, B, _ }; test "@tagName is null-terminated" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1023,7 +1020,6 @@ test "@tagName is null-terminated" { } test "tag name with assigned enum values" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1113,7 +1109,6 @@ test "enum literal in array literal" { test "tag name functions are unique" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/memset.zig b/test/behavior/memset.zig index 2cc390a3c9..89e01a0e56 100644 --- a/test/behavior/memset.zig +++ b/test/behavior/memset.zig @@ -114,7 +114,6 @@ test "memset with large array element, runtime known" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const A = [128]u64; var buf: [5]A = undefined; @@ -132,7 +131,6 @@ test "memset with large array element, comptime known" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const A = [128]u64; var buf: [5]A = undefined; diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 0d309b9a6e..695a81b1a3 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -258,7 +258,6 @@ test "Type.ErrorSet" { test "Type.Struct" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -383,7 +382,6 @@ test "Type.Enum" { test "Type.Union" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 76c5b09a89..f90a336e76 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1094,7 +1094,6 @@ test "containers with single-field enums" { test "@unionInit on union with tag but no fields" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From 0bdfb288ccc58807355221d3e86451c33e390d01 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 1 May 2023 00:39:31 -0400 Subject: [PATCH 046/725] x86_64: workaround tagName linker issues Pass extra pointer param with a linker ref when calling the lazy tagName function to workaround not being able to lower linker refs during codegen of a lazy func. --- src/arch/x86_64/CodeGen.zig | 79 ++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 71dcbfa461..6f71e0b810 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1641,48 +1641,16 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)}); const param_regs = abi.getCAbiIntParamRegs(self.target.*); - const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); + const param_locks = self.register_manager.lockRegsAssumeUnused(3, param_regs[0..3].*); defer for (param_locks) |lock| self.register_manager.unlockReg(lock); const ret_reg = param_regs[0]; const enum_mcv = MCValue{ .register = param_regs[1] }; + const data_mcv = MCValue{ .register = param_regs[2] }; var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount()); defer self.gpa.free(exitlude_jump_relocs); - const data_reg = try self.register_manager.allocReg(null, gp); - const data_lock = self.register_manager.lockRegAssumeUnused(data_reg); - defer self.register_manager.unlockReg(data_lock); - - const data_lazy_sym = link.File.LazySymbol{ .kind = .const_data, .ty = enum_ty }; - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmRegisterMemory( - .mov, - data_reg.to64(), - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), - ); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); - } else { - return self.fail("TODO implement {s} for {}", .{ - @tagName(lazy_sym.kind), - lazy_sym.ty.fmt(self.bin_file.options.module.?), - }); - } - var data_off: i32 = 0; for ( exitlude_jump_relocs, @@ -1698,12 +1666,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { try self.genBinOpMir(.cmp, enum_ty, enum_mcv, tag_mcv); const skip_reloc = try self.asmJccReloc(undefined, .ne); - try self.genSetMem( - .{ .reg = ret_reg }, - 0, - Type.usize, - .{ .register_offset = .{ .reg = data_reg, .off = data_off } }, - ); + try self.genSetMem(.{ .reg = ret_reg }, 0, Type.usize, data_mcv.offset(data_off)); try self.genSetMem(.{ .reg = ret_reg }, 8, Type.usize, .{ .immediate = tag_name.len }); exitlude_jump_reloc.* = try self.asmJmpReloc(undefined); @@ -8663,6 +8626,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { } fn airTagName(self: *Self, inst: Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; const un_op = self.air.instructions.items(.data)[inst].un_op; const inst_ty = self.air.typeOfIndex(inst); const enum_ty = self.air.typeOf(un_op); @@ -8693,10 +8657,35 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.resolveInst(un_op); try self.genSetReg(param_regs[1], enum_ty, operand); - const mod = self.bin_file.options.module.?; - const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod); + const data_lazy_sym = link.File.LazySymbol.initDecl(.const_data, enum_ty.getOwnerDecl(), mod); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + const atom_index = elf_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); + try self.asmRegisterMemory( + .mov, + param_regs[2], + Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), + ); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = coff_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(param_regs[2], Type.usize, .{ .lea_got = sym_index }); + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = macho_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(param_regs[2], Type.usize, .{ .lea_got = sym_index }); + } else { + return self.fail("TODO implement airTagName for x86_64 {s}", .{@tagName(self.bin_file.tag)}); + } + + const code_lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = elf_file.getOrCreateAtomForLazySymbol(code_lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); @@ -8706,13 +8695,13 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), ); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + const atom_index = coff_file.getOrCreateAtomForLazySymbol(code_lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + const atom_index = macho_file.getOrCreateAtomForLazySymbol(code_lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); From 7064d7dbf0157aed9b497e1158243e472082633f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 1 May 2023 09:54:42 +0200 Subject: [PATCH 047/725] Revert "x86_64: workaround tagName linker issues" This reverts commit aac97b92532e7492b9145e1562e31c2e1fa66c15. --- src/arch/x86_64/CodeGen.zig | 79 +++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6f71e0b810..71dcbfa461 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1641,16 +1641,48 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)}); const param_regs = abi.getCAbiIntParamRegs(self.target.*); - const param_locks = self.register_manager.lockRegsAssumeUnused(3, param_regs[0..3].*); + const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); defer for (param_locks) |lock| self.register_manager.unlockReg(lock); const ret_reg = param_regs[0]; const enum_mcv = MCValue{ .register = param_regs[1] }; - const data_mcv = MCValue{ .register = param_regs[2] }; var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount()); defer self.gpa.free(exitlude_jump_relocs); + const data_reg = try self.register_manager.allocReg(null, gp); + const data_lock = self.register_manager.lockRegAssumeUnused(data_reg); + defer self.register_manager.unlockReg(data_lock); + + const data_lazy_sym = link.File.LazySymbol{ .kind = .const_data, .ty = enum_ty }; + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = elf_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); + try self.asmRegisterMemory( + .mov, + data_reg.to64(), + Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), + ); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = coff_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = macho_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); + } else { + return self.fail("TODO implement {s} for {}", .{ + @tagName(lazy_sym.kind), + lazy_sym.ty.fmt(self.bin_file.options.module.?), + }); + } + var data_off: i32 = 0; for ( exitlude_jump_relocs, @@ -1666,7 +1698,12 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { try self.genBinOpMir(.cmp, enum_ty, enum_mcv, tag_mcv); const skip_reloc = try self.asmJccReloc(undefined, .ne); - try self.genSetMem(.{ .reg = ret_reg }, 0, Type.usize, data_mcv.offset(data_off)); + try self.genSetMem( + .{ .reg = ret_reg }, + 0, + Type.usize, + .{ .register_offset = .{ .reg = data_reg, .off = data_off } }, + ); try self.genSetMem(.{ .reg = ret_reg }, 8, Type.usize, .{ .immediate = tag_name.len }); exitlude_jump_reloc.* = try self.asmJmpReloc(undefined); @@ -8626,7 +8663,6 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { } fn airTagName(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; const un_op = self.air.instructions.items(.data)[inst].un_op; const inst_ty = self.air.typeOfIndex(inst); const enum_ty = self.air.typeOf(un_op); @@ -8657,35 +8693,10 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.resolveInst(un_op); try self.genSetReg(param_regs[1], enum_ty, operand); - const data_lazy_sym = link.File.LazySymbol.initDecl(.const_data, enum_ty.getOwnerDecl(), mod); + const mod = self.bin_file.options.module.?; + const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmRegisterMemory( - .mov, - param_regs[2], - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), - ); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(param_regs[2], Type.usize, .{ .lea_got = sym_index }); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(param_regs[2], Type.usize, .{ .lea_got = sym_index }); - } else { - return self.fail("TODO implement airTagName for x86_64 {s}", .{@tagName(self.bin_file.tag)}); - } - - const code_lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(code_lazy_sym) catch |err| + const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const atom = elf_file.getAtom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); @@ -8695,13 +8706,13 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), ); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(code_lazy_sym) catch |err| + const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(code_lazy_sym) catch |err| + const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); From 565f8979cc38d46b8b905f3a8b7db03238779ffc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 1 May 2023 13:41:42 +0200 Subject: [PATCH 048/725] link: fix accessing source atom's symbol index in codegen Since the owner can either be a `Decl` or a `LazySymbol` we need to preserve this information at the codegen generate function level so that we can then correctly work out the corresponding `Atom` in the linker. --- src/arch/x86_64/CodeGen.zig | 94 +++++++++++++++++++++++-------------- src/link/MachO.zig | 7 +-- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 71dcbfa461..d5c5938ba0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -56,10 +56,7 @@ liveness: Liveness, bin_file: *link.File, debug_output: DebugInfoOutput, target: *const std.Target, -owner: union(enum) { - mod_fn: *const Module.Fn, - decl: Module.Decl.Index, -}, +owner: Owner, err_msg: ?*ErrorMsg, args: []MCValue, ret_mcv: InstTracking, @@ -111,6 +108,44 @@ const mir_to_air_map_init = if (builtin.mode == .Debug) std.AutoHashMapUnmanaged const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; const RegisterOffset = struct { reg: Register, off: i32 = 0 }; +const Owner = union(enum) { + mod_fn: *const Module.Fn, + lazy_sym: link.File.LazySymbol, + + fn getOwnerDecl(owner: Owner) Module.Decl.Index { + return switch (owner) { + .mod_fn => |mod_fn| mod_fn.owner_decl, + .lazy_sym => |lazy_sym| lazy_sym.ty.getOwnerDecl(), + }; + } + + fn getSymbolIndex(owner: Owner, ctx: *Self) !u32 { + switch (owner) { + .mod_fn => |mod_fn| { + const decl_index = mod_fn.owner_decl; + if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { + const atom = try macho_file.getOrCreateAtomForDecl(decl_index); + return macho_file.getAtom(atom).getSymbolIndex().?; + } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| { + const atom = try coff_file.getOrCreateAtomForDecl(decl_index); + return coff_file.getAtom(atom).getSymbolIndex().?; + } else unreachable; + }, + .lazy_sym => |lazy_sym| { + if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { + const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); + return macho_file.getAtom(atom).getSymbolIndex().?; + } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| { + const atom = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); + return coff_file.getAtom(atom).getSymbolIndex().?; + } else unreachable; + }, + } + } +}; + pub const MCValue = union(enum) { /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. /// TODO Look into deleting this tag and using `dead` instead, since every use @@ -763,7 +798,7 @@ pub fn generateLazy( .target = &bin_file.options.target, .bin_file = bin_file, .debug_output = debug_output, - .owner = .{ .decl = lazy_sym.ty.getOwnerDecl() }, + .owner = .{ .lazy_sym = lazy_sym }, .err_msg = null, .args = undefined, .ret_mcv = undefined, @@ -1724,13 +1759,6 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { } } -fn getOwnerDecl(self: *const Self) Module.Decl.Index { - return switch (self.owner) { - .mod_fn => |mod_fn| mod_fn.owner_decl, - .decl => |index| index, - }; -} - fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void { const reg = value.getReg() orelse return; if (self.register_manager.isRegFree(reg)) { @@ -6230,7 +6258,10 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { //}, else => unreachable, // not a valid function parameter }; - try dw.genArgDbgInfo(name, ty, self.getOwnerDecl(), loc); + // TODO: this might need adjusting like the linkers do. + // Instead of flattening the owner and passing Decl.Index here we may + // want to special case LazySymbol in DWARF linker too. + try dw.genArgDbgInfo(name, ty, self.owner.getOwnerDecl(), loc); }, .plan9 => {}, .none => {}, @@ -6271,7 +6302,10 @@ fn genVarDbgInfo( break :blk .nop; }, }; - try dw.genVarDbgInfo(name, ty, self.getOwnerDecl(), is_ptr, loc); + // TODO: this might need adjusting like the linkers do. + // Instead of flattening the owner and passing Decl.Index here we may + // want to special case LazySymbol in DWARF linker too. + try dw.genVarDbgInfo(name, ty, self.owner.getOwnerDecl(), is_ptr, loc); }, .plan9 => {}, .none => {}, @@ -6403,12 +6437,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr), })); - } else if (self.bin_file.cast(link.File.Coff)) |_| { - const sym_index = try self.getSymbolIndexForDecl(func.owner_decl); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl); + const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); try self.asmRegister(.call, .rax); - } else if (self.bin_file.cast(link.File.MachO)) |_| { - const sym_index = try self.getSymbolIndexForDecl(func.owner_decl); + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); + const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.Plan9)) |p9| { @@ -6429,7 +6465,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const decl_name = mem.sliceTo(mod.declPtr(extern_fn.owner_decl).name, 0); const lib_name = mem.sliceTo(extern_fn.lib_name, 0); if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); + const atom_index = try self.owner.getSymbolIndex(self); const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name); _ = try self.addInst(.{ .tag = .mov_linker, @@ -6442,8 +6478,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier }); try self.asmRegister(.call, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = try self.owner.getSymbolIndex(self); const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name); - const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); _ = try self.addInst(.{ .tag = .call_extern, .ops = undefined, @@ -7719,7 +7755,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr }), ), .load_direct => |sym_index| if (try self.movMirTag(ty) == .mov) { - const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); + const atom_index = try self.owner.getSymbolIndex(self); _ = try self.addInst(.{ .tag = .mov_linker, .ops = .direct_reloc, @@ -7746,7 +7782,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr ); }, .lea_direct, .lea_got => |sym_index| { - const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); + const atom_index = try self.owner.getSymbolIndex(self); _ = try self.addInst(.{ .tag = switch (src_mcv) { .lea_direct => .lea_linker, @@ -7766,7 +7802,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr }); }, .lea_tlv => |sym_index| { - const atom_index = try self.getSymbolIndexForDecl(self.getOwnerDecl()); + const atom_index = try self.owner.getSymbolIndex(self); if (self.bin_file.cast(link.File.MachO)) |_| { _ = try self.addInst(.{ .tag = .lea_linker, @@ -9079,7 +9115,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV } fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.getOwnerDecl())) { + return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.owner.getOwnerDecl())) { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, @@ -9390,13 +9426,3 @@ fn regBitSize(self: *Self, ty: Type) u64 { fn regExtraBits(self: *Self, ty: Type) u64 { return self.regBitSize(ty) - ty.bitSize(self.target.*); } - -fn getSymbolIndexForDecl(self: *Self, decl_index: Module.Decl.Index) !u32 { - if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = try macho_file.getOrCreateAtomForDecl(decl_index); - return macho_file.getAtom(atom).getSymbolIndex().?; - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom = try coff_file.getOrCreateAtomForDecl(decl_index); - return coff_file.getAtom(atom).getSymbolIndex().?; - } else unreachable; -} diff --git a/src/link/MachO.zig b/src/link/MachO.zig index c14168c66b..458b283d4f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2112,7 +2112,7 @@ fn updateLazySymbolAtom( errdefer self.freeAtom(atom_index); log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr }); - log.debug(" (required alignment 0x{x}", .{required_alignment}); + log.debug(" (required alignment 0x{x})", .{required_alignment}); atom.size = code.len; symbol.n_value = vaddr; @@ -4157,9 +4157,6 @@ pub fn logSymtab(self: *MachO) void { log.debug("stubs entries:", .{}); log.debug("{}", .{self.stub_table}); - - // log.debug("threadlocal entries:", .{}); - // log.debug("{}", .{self.tlv_table}); } pub fn logAtoms(self: *MachO) void { @@ -4199,6 +4196,6 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index) void { sym.n_value, atom.size, atom.file, - sym.n_sect, + sym.n_sect + 1, }); } From 5e7f3d5daaea1e6f5e835648ffd085797b667824 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 1 May 2023 19:34:55 +0200 Subject: [PATCH 049/725] x86_64: disable advanced memset tests on Windows --- test/behavior/memset.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/behavior/memset.zig b/test/behavior/memset.zig index 89e01a0e56..076e1f17d9 100644 --- a/test/behavior/memset.zig +++ b/test/behavior/memset.zig @@ -114,6 +114,7 @@ test "memset with large array element, runtime known" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; const A = [128]u64; var buf: [5]A = undefined; @@ -131,6 +132,7 @@ test "memset with large array element, comptime known" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; const A = [128]u64; var buf: [5]A = undefined; From db88b414722e698a392ec65a3ef46730341aea25 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 1 May 2023 16:58:55 -0400 Subject: [PATCH 050/725] x86_64: fix switch multi-prongs and mul/div flags clobber --- src/arch/x86_64/CodeGen.zig | 10 ++++++---- test/behavior/inline_switch.zig | 1 - test/behavior/switch.zig | 4 ---- test/behavior/union.zig | 1 - 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d5c5938ba0..8e61af1c9f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2435,6 +2435,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { } }; const src_ty = Type.initPayload(&src_pl.base); + try self.spillEflagsIfOccupied(); try self.spillRegisters(&.{ .rax, .rdx }); const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -7236,15 +7237,16 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { var relocs = try self.gpa.alloc(u32, items.len); defer self.gpa.free(relocs); - for (items, relocs) |item, *reloc| { - try self.spillEflagsIfOccupied(); + try self.spillEflagsIfOccupied(); + for (items, relocs, 0..) |item, *reloc, i| { const item_mcv = try self.resolveInst(item); try self.genBinOpMir(.cmp, condition_ty, condition, item_mcv); - reloc.* = try self.asmJccReloc(undefined, .ne); + reloc.* = try self.asmJccReloc(undefined, if (i < relocs.len - 1) .e else .ne); } for (liveness.deaths[case_i]) |operand| self.processDeath(operand); + for (relocs[0 .. relocs.len - 1]) |reloc| try self.performReloc(reloc); try self.genBody(case_body); try self.restoreState(state, &.{}, .{ .emit_instructions = false, @@ -7253,7 +7255,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { .close_scope = true, }); - for (relocs) |reloc| try self.performReloc(reloc); + try self.performReloc(relocs[relocs.len - 1]); } if (switch_br.data.else_body_len > 0) { diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig index dcd603c94f..6e5013d83b 100644 --- a/test/behavior/inline_switch.zig +++ b/test/behavior/inline_switch.zig @@ -94,7 +94,6 @@ test "inline else error" { test "inline else enum" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const E2 = enum(u8) { a = 2, b = 3, c = 4, d = 5 }; diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 3924d1d6c1..5e2d6d28c1 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -88,7 +88,6 @@ fn nonConstSwitch(foo: SwitchStatementFoo) !void { const SwitchStatementFoo = enum { A, B, C, D }; test "switch with multiple expressions" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const x = switch (returnsFive()) { @@ -275,7 +274,6 @@ fn testSwitchEnumPtrCapture() !void { } test "switch handles all cases of number" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testSwitchHandleAllCases(); @@ -455,7 +453,6 @@ test "else prong of switch on error set excludes other cases" { } test "switch prongs with error set cases make a new error set type for capture value" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -532,7 +529,6 @@ test "switch with null and T peer types and inferred result location type" { test "switch prongs with cases with identical payload types" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const Union = union(enum) { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index f90a336e76..3dd8919935 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1288,7 +1288,6 @@ test "return an extern union from C calling convention" { test "noreturn field in union" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const U = union(enum) { From 3b1ea390a301dbdc992043d97cf618a94e8801de Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 1 May 2023 19:18:49 -0400 Subject: [PATCH 051/725] x86_64: cleanup lazy symbols In theory fixes updating lazy symbols during incremental compilation. --- src/arch/x86_64/CodeGen.zig | 190 +++++++++++++++--------------------- src/link/Coff.zig | 77 ++++++++------- src/link/Elf.zig | 75 +++++++------- src/link/MachO.zig | 78 ++++++++------- 4 files changed, 207 insertions(+), 213 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8e61af1c9f..a658103c1a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -112,7 +112,7 @@ const Owner = union(enum) { mod_fn: *const Module.Fn, lazy_sym: link.File.LazySymbol, - fn getOwnerDecl(owner: Owner) Module.Decl.Index { + fn getDecl(owner: Owner) Module.Decl.Index { return switch (owner) { .mod_fn => |mod_fn| mod_fn.owner_decl, .lazy_sym => |lazy_sym| lazy_sym.ty.getOwnerDecl(), @@ -1688,35 +1688,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { const data_reg = try self.register_manager.allocReg(null, gp); const data_lock = self.register_manager.lockRegAssumeUnused(data_reg); defer self.register_manager.unlockReg(data_lock); - - const data_lazy_sym = link.File.LazySymbol{ .kind = .const_data, .ty = enum_ty }; - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmRegisterMemory( - .mov, - data_reg.to64(), - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), - ); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(data_lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(data_reg, Type.usize, .{ .lea_got = sym_index }); - } else { - return self.fail("TODO implement {s} for {}", .{ - @tagName(lazy_sym.kind), - lazy_sym.ty.fmt(self.bin_file.options.module.?), - }); - } + try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = enum_ty }); var data_off: i32 = 0; for ( @@ -6262,7 +6234,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { // TODO: this might need adjusting like the linkers do. // Instead of flattening the owner and passing Decl.Index here we may // want to special case LazySymbol in DWARF linker too. - try dw.genArgDbgInfo(name, ty, self.owner.getOwnerDecl(), loc); + try dw.genArgDbgInfo(name, ty, self.owner.getDecl(), loc); }, .plan9 => {}, .none => {}, @@ -6306,7 +6278,7 @@ fn genVarDbgInfo( // TODO: this might need adjusting like the linkers do. // Instead of flattening the owner and passing Decl.Index here we may // want to special case LazySymbol in DWARF linker too. - try dw.genVarDbgInfo(name, ty, self.owner.getOwnerDecl(), is_ptr, loc); + try dw.genVarDbgInfo(name, ty, self.owner.getDecl(), is_ptr, loc); }, .plan9 => {}, .none => {}, @@ -6630,38 +6602,13 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { } fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; const un_op = self.air.instructions.items(.data)[inst].un_op; const addr_reg = try self.register_manager.allocReg(null, gp); const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_lock); - - const mod = self.bin_file.options.module.?; - const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmRegisterMemory( - .mov, - addr_reg.to64(), - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), - ); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); - } else { - return self.fail("TODO implement airCmpLtErrorsLen for x86_64 {s}", .{@tagName(self.bin_file.tag)}); - } + try self.genLazySymbolRef(.lea, addr_reg, link.File.LazySymbol.initDecl(.const_data, null, mod)); try self.spillEflagsIfOccupied(); self.eflags_inst = inst; @@ -7999,6 +7946,67 @@ fn genInlineMemset(self: *Self, dst_ptr: MCValue, value: MCValue, len: MCValue) }); } +fn genLazySymbolRef( + self: *Self, + comptime tag: Mir.Inst.Tag, + reg: Register, + lazy_sym: link.File.LazySymbol, +) InnerError!void { + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const atom = elf_file.getAtom(atom_index); + _ = try atom.getOrCreateOffsetTableEntry(elf_file); + const got_addr = atom.getOffsetTableAddress(elf_file); + const got_mem = + Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }); + switch (tag) { + .lea, .mov => try self.asmRegisterMemory(.mov, reg.to64(), got_mem), + .call => try self.asmMemory(.call, got_mem), + else => unreachable, + } + switch (tag) { + .lea, .call => {}, + .mov => try self.asmRegisterMemory( + tag, + reg.to64(), + Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }), + ), + else => unreachable, + } + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + switch (tag) { + .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym_index }), + .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym_index }), + else => unreachable, + } + switch (tag) { + .lea, .mov => {}, + .call => try self.asmRegister(.call, reg), + else => unreachable, + } + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + switch (tag) { + .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym_index }), + .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym_index }), + else => unreachable, + } + switch (tag) { + .lea, .mov => {}, + .call => try self.asmRegister(.call, reg), + else => unreachable, + } + } else { + return self.fail("TODO implement genLazySymbol for x86_64 {s}", .{@tagName(self.bin_file.tag)}); + } +} + fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result = result: { @@ -8701,6 +8709,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { } fn airTagName(self: *Self, inst: Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; const un_op = self.air.instructions.items(.data)[inst].un_op; const inst_ty = self.air.typeOfIndex(inst); const enum_ty = self.air.typeOf(un_op); @@ -8731,38 +8740,17 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.resolveInst(un_op); try self.genSetReg(param_regs[1], enum_ty, operand); - const mod = self.bin_file.options.module.?; - const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmMemory( - .call, - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), - ); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); - try self.asmRegister(.call, .rax); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); - try self.asmRegister(.call, .rax); - } else { - return self.fail("TODO implement airTagName for x86_64 {s}", .{@tagName(self.bin_file.tag)}); - } + try self.genLazySymbolRef( + .call, + .rax, + link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod), + ); return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); } fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; const un_op = self.air.instructions.items(.data)[inst].un_op; const err_ty = self.air.typeOf(un_op); @@ -8774,33 +8762,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { const addr_reg = try self.register_manager.allocReg(null, gp); const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_lock); - - const mod = self.bin_file.options.module.?; - const lazy_sym = link.File.LazySymbol.initDecl(.const_data, null, mod); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); - try self.asmRegisterMemory( - .mov, - addr_reg.to64(), - Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(i32, got_addr) }), - ); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - try self.genSetReg(addr_reg, Type.usize, .{ .lea_got = sym_index }); - } else { - return self.fail("TODO implement airErrorName for x86_64 {s}", .{@tagName(self.bin_file.tag)}); - } + try self.genLazySymbolRef(.lea, addr_reg, link.File.LazySymbol.initDecl(.const_data, null, mod)); const start_reg = try self.register_manager.allocReg(null, gp); const start_lock = self.register_manager.lockRegAssumeUnused(start_reg); @@ -9117,7 +9079,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV } fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.owner.getOwnerDecl())) { + return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.owner.getDecl())) { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 41d4617e53..81e8c57bdd 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -143,8 +143,11 @@ const Section = struct { const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); const LazySymbolMetadata = struct { - text_atom: ?Atom.Index = null, - rdata_atom: ?Atom.Index = null, + const State = enum { unused, pending_flush, flushed }; + text_atom: Atom.Index = undefined, + rdata_atom: Atom.Index = undefined, + text_state: State = .unused, + rdata_state: State = .unused, }; const DeclMetadata = struct { @@ -1150,8 +1153,6 @@ pub fn updateDecl( const tracy = trace(@src()); defer tracy.end(); - try self.updateLazySymbol(decl_index); - const decl = module.declPtr(decl_index); if (decl.val.tag() == .extern_fn) { @@ -1194,21 +1195,6 @@ pub fn updateDecl( return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -fn updateLazySymbol(self: *Coff, decl: ?Module.Decl.Index) !void { - const metadata = self.lazy_syms.get(Module.Decl.OptionalIndex.init(decl)) orelse return; - const mod = self.base.options.module.?; - if (metadata.text_atom) |atom| try self.updateLazySymbolAtom( - link.File.LazySymbol.initDecl(.code, decl, mod), - atom, - self.text_section_index.?, - ); - if (metadata.rdata_atom) |atom| try self.updateLazySymbolAtom( - link.File.LazySymbol.initDecl(.const_data, decl, mod), - atom, - self.rdata_section_index.?, - ); -} - fn updateLazySymbolAtom( self: *Coff, sym: link.File.LazySymbol, @@ -1279,14 +1265,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Ato const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const atom_ptr = switch (sym.kind) { - .code => &gop.value_ptr.text_atom, - .const_data => &gop.value_ptr.rdata_atom, + const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { + .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, + .const_data => .{ .atom = &gop.value_ptr.rdata_atom, .state = &gop.value_ptr.rdata_state }, }; - if (atom_ptr.*) |atom| return atom; - const atom = try self.createAtom(); - atom_ptr.* = atom; - try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + switch (metadata.state.*) { + .unused => metadata.atom.* = try self.createAtom(), + .pending_flush => return metadata.atom.*, + .flushed => {}, + } + metadata.state.* = .pending_flush; + const atom = metadata.atom.*; + // anyerror needs to be deferred until flushModule + if (sym.getDecl() != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { .code => self.text_section_index.?, .const_data => self.rdata_section_index.?, }); @@ -1617,15 +1608,35 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod sub_prog_node.activate(); defer sub_prog_node.end(); - // Most lazy symbols can be updated when the corresponding decl is, - // so we only have to worry about the one without an associated decl. - self.updateLazySymbol(null) catch |err| switch (err) { - error.CodegenFail => return error.FlushFailure, - else => |e| return e, - }; - const gpa = self.base.allocator; + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + + if (self.lazy_syms.getPtr(.none)) |metadata| { + // Most lazy symbols can be updated on first use, but + // anyerror needs to wait for everything to be flushed. + if (metadata.text_state != .unused) self.updateLazySymbolAtom( + link.File.LazySymbol.initDecl(.code, null, module), + metadata.text_atom, + self.text_section_index.?, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + if (metadata.rdata_state != .unused) self.updateLazySymbolAtom( + link.File.LazySymbol.initDecl(.const_data, null, module), + metadata.rdata_atom, + self.rdata_section_index.?, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + } + for (self.lazy_syms.values()) |*metadata| { + if (metadata.text_state != .unused) metadata.text_state = .flushed; + if (metadata.rdata_state != .unused) metadata.rdata_state = .flushed; + } + while (self.unresolved.popOrNull()) |entry| { assert(entry.value); // We only expect imports generated by the incremental linker for now. const global = self.globals.items[entry.key]; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ec113e5c8a..724ec76500 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -65,8 +65,11 @@ const Section = struct { }; const LazySymbolMetadata = struct { - text_atom: ?Atom.Index = null, - rodata_atom: ?Atom.Index = null, + const State = enum { unused, pending_flush, flushed }; + text_atom: Atom.Index = undefined, + rodata_atom: Atom.Index = undefined, + text_state: State = .unused, + rodata_state: State = .unused, }; const DeclMetadata = struct { @@ -1032,17 +1035,35 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node sub_prog_node.activate(); defer sub_prog_node.end(); - // Most lazy symbols can be updated when the corresponding decl is, - // so we only have to worry about the one without an associated decl. - self.updateLazySymbol(null) catch |err| switch (err) { - error.CodegenFail => return error.FlushFailure, - else => |e| return e, - }; - // TODO This linker code currently assumes there is only 1 compilation unit and it // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + if (self.lazy_syms.getPtr(.none)) |metadata| { + // Most lazy symbols can be updated on first use, but + // anyerror needs to wait for everything to be flushed. + if (metadata.text_state != .unused) self.updateLazySymbolAtom( + File.LazySymbol.initDecl(.code, null, module), + metadata.text_atom, + self.text_section_index.?, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( + File.LazySymbol.initDecl(.const_data, null, module), + metadata.rodata_atom, + self.rodata_section_index.?, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + } + for (self.lazy_syms.values()) |*metadata| { + if (metadata.text_state != .unused) metadata.text_state = .flushed; + if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; + } + const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); @@ -2378,14 +2399,19 @@ pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Inde const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const atom_ptr = switch (sym.kind) { - .code => &gop.value_ptr.text_atom, - .const_data => &gop.value_ptr.rodata_atom, + const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { + .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, + .const_data => .{ .atom = &gop.value_ptr.rodata_atom, .state = &gop.value_ptr.rodata_state }, }; - if (atom_ptr.*) |atom| return atom; - const atom = try self.createAtom(); - atom_ptr.* = atom; - try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + switch (metadata.state.*) { + .unused => metadata.atom.* = try self.createAtom(), + .pending_flush => return metadata.atom.*, + .flushed => {}, + } + metadata.state.* = .pending_flush; + const atom = metadata.atom.*; + // anyerror needs to be deferred until flushModule + if (sym.getDecl() != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { .code => self.text_section_index.?, .const_data => self.rodata_section_index.?, }); @@ -2598,8 +2624,6 @@ pub fn updateDecl( const tracy = trace(@src()); defer tracy.end(); - try self.updateLazySymbol(decl_index); - const decl = module.declPtr(decl_index); if (decl.val.tag() == .extern_fn) { @@ -2666,21 +2690,6 @@ pub fn updateDecl( return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -fn updateLazySymbol(self: *Elf, decl: ?Module.Decl.Index) !void { - const metadata = self.lazy_syms.get(Module.Decl.OptionalIndex.init(decl)) orelse return; - const mod = self.base.options.module.?; - if (metadata.text_atom) |atom| try self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.code, decl, mod), - atom, - self.text_section_index.?, - ); - if (metadata.rodata_atom) |atom| try self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.const_data, decl, mod), - atom, - self.rodata_section_index.?, - ); -} - fn updateLazySymbolAtom( self: *Elf, sym: File.LazySymbol, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 458b283d4f..a346ec756f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -236,8 +236,11 @@ const is_hot_update_compatible = switch (builtin.target.os.tag) { const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); const LazySymbolMetadata = struct { - text_atom: ?Atom.Index = null, - data_const_atom: ?Atom.Index = null, + const State = enum { unused, pending_flush, flushed }; + text_atom: Atom.Index = undefined, + data_const_atom: Atom.Index = undefined, + text_state: State = .unused, + data_const_state: State = .unused, }; const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); @@ -493,15 +496,33 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No sub_prog_node.activate(); defer sub_prog_node.end(); - // Most lazy symbols can be updated when the corresponding decl is, - // so we only have to worry about the one without an associated decl. - self.updateLazySymbol(null) catch |err| switch (err) { - error.CodegenFail => return error.FlushFailure, - else => |e| return e, - }; - const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + if (self.lazy_syms.getPtr(.none)) |metadata| { + // Most lazy symbols can be updated on first use, but + // anyerror needs to wait for everything to be flushed. + if (metadata.text_state != .unused) self.updateLazySymbolAtom( + File.LazySymbol.initDecl(.code, null, module), + metadata.text_atom, + self.text_section_index.?, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + if (metadata.data_const_state != .unused) self.updateLazySymbolAtom( + File.LazySymbol.initDecl(.const_data, null, module), + metadata.data_const_atom, + self.data_const_section_index.?, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + } + for (self.lazy_syms.values()) |*metadata| { + if (metadata.text_state != .unused) metadata.text_state = .flushed; + if (metadata.data_const_state != .unused) metadata.data_const_state = .flushed; + } + if (self.d_sym) |*d_sym| { try d_sym.dwarf.flushModule(module); } @@ -1960,8 +1981,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) const tracy = trace(@src()); defer tracy.end(); - try self.updateLazySymbol(decl_index); - const decl = module.declPtr(decl_index); if (decl.val.tag() == .extern_fn) { @@ -2036,21 +2055,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); } -fn updateLazySymbol(self: *MachO, decl: ?Module.Decl.Index) !void { - const metadata = self.lazy_syms.get(Module.Decl.OptionalIndex.init(decl)) orelse return; - const mod = self.base.options.module.?; - if (metadata.text_atom) |atom| try self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.code, decl, mod), - atom, - self.text_section_index.?, - ); - if (metadata.data_const_atom) |atom| try self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.const_data, decl, mod), - atom, - self.data_const_section_index.?, - ); -} - fn updateLazySymbolAtom( self: *MachO, sym: File.LazySymbol, @@ -2125,14 +2129,22 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl()); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const atom_ptr = switch (sym.kind) { - .code => &gop.value_ptr.text_atom, - .const_data => &gop.value_ptr.data_const_atom, + const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { + .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, + .const_data => .{ + .atom = &gop.value_ptr.data_const_atom, + .state = &gop.value_ptr.data_const_state, + }, }; - if (atom_ptr.*) |atom| return atom; - const atom = try self.createAtom(); - atom_ptr.* = atom; - try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + switch (metadata.state.*) { + .unused => metadata.atom.* = try self.createAtom(), + .pending_flush => return metadata.atom.*, + .flushed => {}, + } + metadata.state.* = .pending_flush; + const atom = metadata.atom.*; + // anyerror needs to be deferred until flushModule + if (sym.getDecl() != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { .code => self.text_section_index.?, .const_data => self.data_const_section_index.?, }); From 6f1336a50c6c2d9498bb9b3fb291cb305688c1b9 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Tue, 2 May 2023 04:41:25 +0200 Subject: [PATCH 052/725] autodoc: make the help modal toggleable Now you can simply press "?" again to toggle the help modal instead of requiring Esc. Both Esc and "?" work. --- lib/docs/index.html | 2 +- lib/docs/main.js | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/docs/index.html b/lib/docs/index.html index 6b9f95a7d0..36cfcc55cc 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -875,7 +875,7 @@