| author | |
| committer | |
| log | 8e6c2b7a47c3c19269cf03075eaeddb7ee61c42d |
| tree | 6fca0ca2a2929fe9fbb66dc960451d77cefb04bc |
| parent | 38441b5eab3c9371f8412aa46a277f37fc026a79 |
| parent | 8b9434871ea437840d25f073b945466359f402f9 |
43 files changed, 620 insertions(+), 164 deletions(-)
doc/langref.html.in+22-8| ... | ... | @@ -310,7 +310,7 @@ pub fn main() !void { |
| 310 | 310 | <p> |
| 311 | 311 | The two arguments passed to the <code>stdout.print()</code> function, <code>"Hello, {s}!\n"</code> |
| 312 | 312 | and <code>.{"world"}</code>, are evaluated at {#link|compile-time|comptime#}. The code sample is |
| 313 | purposely written to show how to perform {#link|string|String Literals and Character Literals#} | |
| 313 | purposely written to show how to perform {#link|string|String Literals and Unicode Code Point Literals#} | |
| 314 | 314 | substitution in the <code>print</code> function. The curly-braces inside of the first argument |
| 315 | 315 | are substituted with the compile-time known value inside of the second argument |
| 316 | 316 | (known as an {#link|anonymous struct literal|Anonymous Struct Literals#}). The <code>\n</code> |
| ... | ... | @@ -682,18 +682,31 @@ pub fn main() void { |
| 682 | 682 | </div> |
| 683 | 683 | {#see_also|Optionals|undefined#} |
| 684 | 684 | {#header_close#} |
| 685 | {#header_open|String Literals and Character Literals#} | |
| 685 | {#header_open|String Literals and Unicode Code Point Literals#} | |
| 686 | 686 | <p> |
| 687 | String literals are single-item constant {#link|Pointers#} to null-terminated UTF-8 encoded byte arrays. | |
| 687 | String literals are single-item constant {#link|Pointers#} to null-terminated byte arrays. | |
| 688 | 688 | The type of string literals encodes both the length, and the fact that they are null-terminated, |
| 689 | 689 | and thus they can be {#link|coerced|Type Coercion#} to both {#link|Slices#} and |
| 690 | 690 | {#link|Null-Terminated Pointers|Sentinel-Terminated Pointers#}. |
| 691 | 691 | Dereferencing string literals converts them to {#link|Arrays#}. |
| 692 | 692 | </p> |
| 693 | 693 | <p> |
| 694 | Character literals have type {#syntax#}comptime_int{#endsyntax#}, the same as | |
| 694 | The encoding of a string in Zig is de-facto assumed to be UTF-8. | |
| 695 | Because Zig source code is {#link|UTF-8 encoded|Source Encoding#}, any non-ASCII bytes appearing within a string literal | |
| 696 | in source code carry their UTF-8 meaning into the content of the string in the Zig program; | |
| 697 | the bytes are not modified by the compiler. | |
| 698 | However, it is possible to embbed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation. | |
| 699 | </p> | |
| 700 | <p> | |
| 701 | Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as | |
| 695 | 702 | {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals |
| 696 | and character literals. | |
| 703 | and Unicode code point literals. | |
| 704 | </p> | |
| 705 | <p> | |
| 706 | In many other programming languages, a Unicode code point literal is called a "character literal". | |
| 707 | However, there is <a href="https://unicode.org/glossary">no precise technical definition of a "character"</a> | |
| 708 | in recent versions of the Unicode specification (as of Unicode 13.0). | |
| 709 | In Zig, a Unicode code point literal corresponds to the Unicode definition of a code point. | |
| 697 | 710 | </p> |
| 698 | 711 | {#code_begin|test#} |
| 699 | 712 | const expect = @import("std").testing.expect; |
| ... | ... | @@ -709,6 +722,7 @@ test "string literals" { |
| 709 | 722 | expect('\u{1f4a9}' == 128169); |
| 710 | 723 | expect('💯' == 128175); |
| 711 | 724 | expect(mem.eql(u8, "hello", "h\x65llo")); |
| 725 | expect("\xff"[0] == 0xff); // non-UTF-8 strings are possible with \xNN notation. | |
| 712 | 726 | } |
| 713 | 727 | {#code_end#} |
| 714 | 728 | {#see_also|Arrays|Zig Test|Source Encoding#} |
| ... | ... | @@ -749,11 +763,11 @@ test "string literals" { |
| 749 | 763 | </tr> |
| 750 | 764 | <tr> |
| 751 | 765 | <td><code>\xNN</code></td> |
| 752 | <td>hexadecimal 8-bit character code (2 digits)</td> | |
| 766 | <td>hexadecimal 8-bit byte value (2 digits)</td> | |
| 753 | 767 | </tr> |
| 754 | 768 | <tr> |
| 755 | 769 | <td><code>\u{NNNNNN}</code></td> |
| 756 | <td>hexadecimal Unicode character code UTF-8 encoded (1 or more digits)</td> | |
| 770 | <td>hexadecimal Unicode code point UTF-8 encoded (1 or more digits)</td> | |
| 757 | 771 | </tr> |
| 758 | 772 | </table> |
| 759 | 773 | </div> |
| ... | ... | @@ -7414,7 +7428,7 @@ test "main" { |
| 7414 | 7428 | This function returns a compile time constant pointer to null-terminated, |
| 7415 | 7429 | fixed-size array with length equal to the byte count of the file given by |
| 7416 | 7430 | {#syntax#}path{#endsyntax#}. The contents of the array are the contents of the file. |
| 7417 | This is equivalent to a {#link|string literal|String Literals and Character Literals#} | |
| 7431 | This is equivalent to a {#link|string literal|String Literals and Unicode Code Point Literals#} | |
| 7418 | 7432 | with the file contents. |
| 7419 | 7433 | </p> |
| 7420 | 7434 | <p> |
lib/std/Progress.zig+17-1| ... | ... | @@ -25,6 +25,13 @@ terminal: ?std.fs.File = undefined, |
| 25 | 25 | /// Whether the terminal supports ANSI escape codes. |
| 26 | 26 | supports_ansi_escape_codes: bool = false, |
| 27 | 27 | |
| 28 | /// If the terminal is "dumb", don't print output. | |
| 29 | /// This can be useful if you don't want to print all | |
| 30 | /// the stages of code generation if there are a lot. | |
| 31 | /// You should not use it if the user should see output | |
| 32 | /// for example showing the user what tests run. | |
| 33 | dont_print_on_dumb: bool = false, | |
| 34 | ||
| 28 | 35 | root: Node = undefined, |
| 29 | 36 | |
| 30 | 37 | /// Keeps track of how much time has passed since the beginning. |
| ... | ... | @@ -141,6 +148,9 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* |
| 141 | 148 | self.supports_ansi_escape_codes = true; |
| 142 | 149 | } else if (std.builtin.os.tag == .windows and stderr.isTty()) { |
| 143 | 150 | self.terminal = stderr; |
| 151 | } else if (std.builtin.os.tag != .windows) { | |
| 152 | // we are in a "dumb" terminal like in acme or writing to a file | |
| 153 | self.terminal = stderr; | |
| 144 | 154 | } |
| 145 | 155 | self.root = Node{ |
| 146 | 156 | .context = self, |
| ... | ... | @@ -178,6 +188,8 @@ pub fn refresh(self: *Progress) void { |
| 178 | 188 | } |
| 179 | 189 | |
| 180 | 190 | fn refreshWithHeldLock(self: *Progress) void { |
| 191 | const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows); | |
| 192 | if (is_dumb and self.dont_print_on_dumb) return; | |
| 181 | 193 | const file = self.terminal orelse return; |
| 182 | 194 | |
| 183 | 195 | const prev_columns_written = self.columns_written; |
| ... | ... | @@ -226,7 +238,11 @@ fn refreshWithHeldLock(self: *Progress) void { |
| 226 | 238 | |
| 227 | 239 | if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) |
| 228 | 240 | unreachable; |
| 229 | } else unreachable; | |
| 241 | } else { | |
| 242 | // we are in a "dumb" terminal like in acme or writing to a file | |
| 243 | self.output_buffer[end] = '\n'; | |
| 244 | end += 1; | |
| 245 | } | |
| 230 | 246 | |
| 231 | 247 | self.columns_written = 0; |
| 232 | 248 | } |
lib/std/Thread/Semaphore.zig+1-1| ... | ... | @@ -13,7 +13,7 @@ cond: Condition = .{}, |
| 13 | 13 | //! It is OK to initialize this field to any value. |
| 14 | 14 | permits: usize = 0, |
| 15 | 15 | |
| 16 | const RwLock = @This(); | |
| 16 | const Semaphore = @This(); | |
| 17 | 17 | const std = @import("../std.zig"); |
| 18 | 18 | const Mutex = std.Thread.Mutex; |
| 19 | 19 | const Condition = std.Thread.Condition; |
lib/std/build.zig+46-5| ... | ... | @@ -543,7 +543,7 @@ pub const Builder = struct { |
| 543 | 543 | .Scalar => |s| { |
| 544 | 544 | const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { |
| 545 | 545 | error.Overflow => { |
| 546 | warn("-D{s} value {} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); | |
| 546 | warn("-D{s} value {s} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); | |
| 547 | 547 | self.markInvalidUserInput(); |
| 548 | 548 | return null; |
| 549 | 549 | }, |
| ... | ... | @@ -1308,6 +1308,12 @@ const BuildOptionArtifactArg = struct { |
| 1308 | 1308 | artifact: *LibExeObjStep, |
| 1309 | 1309 | }; |
| 1310 | 1310 | |
| 1311 | const BuildOptionWriteFileArg = struct { | |
| 1312 | name: []const u8, | |
| 1313 | write_file: *WriteFileStep, | |
| 1314 | basename: []const u8, | |
| 1315 | }; | |
| 1316 | ||
| 1311 | 1317 | pub const LibExeObjStep = struct { |
| 1312 | 1318 | step: Step, |
| 1313 | 1319 | builder: *Builder, |
| ... | ... | @@ -1355,6 +1361,7 @@ pub const LibExeObjStep = struct { |
| 1355 | 1361 | packages: ArrayList(Pkg), |
| 1356 | 1362 | build_options_contents: std.ArrayList(u8), |
| 1357 | 1363 | build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg), |
| 1364 | build_options_write_file_args: std.ArrayList(BuildOptionWriteFileArg), | |
| 1358 | 1365 | |
| 1359 | 1366 | object_src: []const u8, |
| 1360 | 1367 | |
| ... | ... | @@ -1515,6 +1522,7 @@ pub const LibExeObjStep = struct { |
| 1515 | 1522 | .object_src = undefined, |
| 1516 | 1523 | .build_options_contents = std.ArrayList(u8).init(builder.allocator), |
| 1517 | 1524 | .build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator), |
| 1525 | .build_options_write_file_args = std.ArrayList(BuildOptionWriteFileArg).init(builder.allocator), | |
| 1518 | 1526 | .c_std = Builder.CStd.C99, |
| 1519 | 1527 | .override_lib_dir = null, |
| 1520 | 1528 | .main_pkg_path = null, |
| ... | ... | @@ -2008,6 +2016,23 @@ pub const LibExeObjStep = struct { |
| 2008 | 2016 | self.step.dependOn(&artifact.step); |
| 2009 | 2017 | } |
| 2010 | 2018 | |
| 2019 | /// The value is the path in the cache dir. | |
| 2020 | /// Adds a dependency automatically. | |
| 2021 | /// basename refers to the basename of the WriteFileStep | |
| 2022 | pub fn addBuildOptionWriteFile( | |
| 2023 | self: *LibExeObjStep, | |
| 2024 | name: []const u8, | |
| 2025 | write_file: *WriteFileStep, | |
| 2026 | basename: []const u8, | |
| 2027 | ) void { | |
| 2028 | self.build_options_write_file_args.append(.{ | |
| 2029 | .name = name, | |
| 2030 | .write_file = write_file, | |
| 2031 | .basename = basename, | |
| 2032 | }) catch unreachable; | |
| 2033 | self.step.dependOn(&write_file.step); | |
| 2034 | } | |
| 2035 | ||
| 2011 | 2036 | pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { |
| 2012 | 2037 | self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable; |
| 2013 | 2038 | } |
| ... | ... | @@ -2228,11 +2253,27 @@ pub const LibExeObjStep = struct { |
| 2228 | 2253 | } |
| 2229 | 2254 | } |
| 2230 | 2255 | |
| 2231 | if (self.build_options_contents.items.len > 0 or self.build_options_artifact_args.items.len > 0) { | |
| 2232 | // Render build artifact options at the last minute, now that the path is known. | |
| 2256 | if (self.build_options_contents.items.len > 0 or | |
| 2257 | self.build_options_artifact_args.items.len > 0 or | |
| 2258 | self.build_options_write_file_args.items.len > 0) | |
| 2259 | { | |
| 2260 | // Render build artifact and write file options at the last minute, now that the path is known. | |
| 2261 | // | |
| 2262 | // Note that pathFromRoot uses resolve path, so this will have | |
| 2263 | // correct behavior even if getOutputPath is already absolute. | |
| 2233 | 2264 | for (self.build_options_artifact_args.items) |item| { |
| 2234 | const out = self.build_options_contents.writer(); | |
| 2235 | out.print("pub const {s}: []const u8 = \"{}\";\n", .{ item.name, std.zig.fmtEscapes(item.artifact.getOutputPath()) }) catch unreachable; | |
| 2265 | self.addBuildOption( | |
| 2266 | []const u8, | |
| 2267 | item.name, | |
| 2268 | self.builder.pathFromRoot(item.artifact.getOutputPath()), | |
| 2269 | ); | |
| 2270 | } | |
| 2271 | for (self.build_options_write_file_args.items) |item| { | |
| 2272 | self.addBuildOption( | |
| 2273 | []const u8, | |
| 2274 | item.name, | |
| 2275 | self.builder.pathFromRoot(item.write_file.getOutputPath(item.basename)), | |
| 2276 | ); | |
| 2236 | 2277 | } |
| 2237 | 2278 | |
| 2238 | 2279 | const build_options_file = try fs.path.join( |
lib/std/c.zig+2| ... | ... | @@ -100,6 +100,8 @@ pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64) |
| 100 | 100 | pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void; |
| 101 | 101 | pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; |
| 102 | 102 | pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; |
| 103 | pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; | |
| 104 | pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int; | |
| 103 | 105 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; |
| 104 | 106 | pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int; |
| 105 | 107 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; |
lib/std/crypto/25519/ed25519.zig+5-5| ... | ... | @@ -207,7 +207,7 @@ pub const Ed25519 = struct { |
| 207 | 207 | |
| 208 | 208 | test "ed25519 key pair creation" { |
| 209 | 209 | var seed: [32]u8 = undefined; |
| 210 | try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 210 | _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 211 | 211 | const key_pair = try Ed25519.KeyPair.create(seed); |
| 212 | 212 | var buf: [256]u8 = undefined; |
| 213 | 213 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); |
| ... | ... | @@ -216,7 +216,7 @@ test "ed25519 key pair creation" { |
| 216 | 216 | |
| 217 | 217 | test "ed25519 signature" { |
| 218 | 218 | var seed: [32]u8 = undefined; |
| 219 | try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 219 | _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 220 | 220 | const key_pair = try Ed25519.KeyPair.create(seed); |
| 221 | 221 | |
| 222 | 222 | const sig = try Ed25519.sign("test", key_pair, null); |
| ... | ... | @@ -339,11 +339,11 @@ test "ed25519 test vectors" { |
| 339 | 339 | }; |
| 340 | 340 | for (entries) |entry, i| { |
| 341 | 341 | var msg: [entry.msg_hex.len / 2]u8 = undefined; |
| 342 | try fmt.hexToBytes(&msg, entry.msg_hex); | |
| 342 | _ = try fmt.hexToBytes(&msg, entry.msg_hex); | |
| 343 | 343 | var public_key: [32]u8 = undefined; |
| 344 | try fmt.hexToBytes(&public_key, entry.public_key_hex); | |
| 344 | _ = try fmt.hexToBytes(&public_key, entry.public_key_hex); | |
| 345 | 345 | var sig: [64]u8 = undefined; |
| 346 | try fmt.hexToBytes(&sig, entry.sig_hex); | |
| 346 | _ = try fmt.hexToBytes(&sig, entry.sig_hex); | |
| 347 | 347 | if (entry.expected) |error_type| { |
| 348 | 348 | std.testing.expectError(error_type, Ed25519.verify(sig, &msg, public_key)); |
| 349 | 349 | } else { |
lib/std/crypto/25519/ristretto255.zig+1-1| ... | ... | @@ -173,7 +173,7 @@ test "ristretto255" { |
| 173 | 173 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); |
| 174 | 174 | |
| 175 | 175 | var r: [Ristretto255.encoded_length]u8 = undefined; |
| 176 | try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); | |
| 176 | _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); | |
| 177 | 177 | var q = try Ristretto255.fromBytes(r); |
| 178 | 178 | q = q.dbl().add(p); |
| 179 | 179 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E"); |
lib/std/crypto/25519/x25519.zig+2-2| ... | ... | @@ -85,8 +85,8 @@ const htest = @import("../test.zig"); |
| 85 | 85 | test "x25519 public key calculation from secret key" { |
| 86 | 86 | var sk: [32]u8 = undefined; |
| 87 | 87 | var pk_expected: [32]u8 = undefined; |
| 88 | try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 89 | try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); | |
| 88 | _ = try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 89 | _ = try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); | |
| 90 | 90 | const pk_calculated = try X25519.recoverPublicKey(sk); |
| 91 | 91 | std.testing.expectEqual(pk_calculated, pk_expected); |
| 92 | 92 | } |
lib/std/crypto/aes.zig+4-4| ... | ... | @@ -122,11 +122,11 @@ test "expand 128-bit key" { |
| 122 | 122 | var exp: [16]u8 = undefined; |
| 123 | 123 | |
| 124 | 124 | for (enc.key_schedule.round_keys) |round_key, i| { |
| 125 | try std.fmt.hexToBytes(&exp, exp_enc[i]); | |
| 125 | _ = try std.fmt.hexToBytes(&exp, exp_enc[i]); | |
| 126 | 126 | testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); |
| 127 | 127 | } |
| 128 | 128 | for (enc.key_schedule.round_keys) |round_key, i| { |
| 129 | try std.fmt.hexToBytes(&exp, exp_dec[i]); | |
| 129 | _ = try std.fmt.hexToBytes(&exp, exp_dec[i]); | |
| 130 | 130 | testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); |
| 131 | 131 | } |
| 132 | 132 | } |
| ... | ... | @@ -144,11 +144,11 @@ test "expand 256-bit key" { |
| 144 | 144 | var exp: [16]u8 = undefined; |
| 145 | 145 | |
| 146 | 146 | for (enc.key_schedule.round_keys) |round_key, i| { |
| 147 | try std.fmt.hexToBytes(&exp, exp_enc[i]); | |
| 147 | _ = try std.fmt.hexToBytes(&exp, exp_enc[i]); | |
| 148 | 148 | testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); |
| 149 | 149 | } |
| 150 | 150 | for (dec.key_schedule.round_keys) |round_key, i| { |
| 151 | try std.fmt.hexToBytes(&exp, exp_dec[i]); | |
| 151 | _ = try std.fmt.hexToBytes(&exp, exp_dec[i]); | |
| 152 | 152 | testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); |
| 153 | 153 | } |
| 154 | 154 | } |
lib/std/crypto/blake3.zig+1-1| ... | ... | @@ -663,7 +663,7 @@ fn testBlake3(hasher: *Blake3, input_len: usize, expected_hex: [262]u8) void { |
| 663 | 663 | |
| 664 | 664 | // Compare to expected value |
| 665 | 665 | var expected_bytes: [expected_hex.len / 2]u8 = undefined; |
| 666 | fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable; | |
| 666 | _ = fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable; | |
| 667 | 667 | testing.expectEqual(actual_bytes, expected_bytes); |
| 668 | 668 | |
| 669 | 669 | // Restore initial state |
lib/std/crypto/gimli.zig+11-11| ... | ... | @@ -270,7 +270,7 @@ pub fn hash(out: []u8, in: []const u8, options: Hash.Options) void { |
| 270 | 270 | test "hash" { |
| 271 | 271 | // a test vector (30) from NIST KAT submission. |
| 272 | 272 | var msg: [58 / 2]u8 = undefined; |
| 273 | try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C"); | |
| 273 | _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C"); | |
| 274 | 274 | var md: [32]u8 = undefined; |
| 275 | 275 | hash(&md, &msg, .{}); |
| 276 | 276 | htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", &md); |
| ... | ... | @@ -278,7 +278,7 @@ test "hash" { |
| 278 | 278 | |
| 279 | 279 | test "hash test vector 17" { |
| 280 | 280 | var msg: [32 / 2]u8 = undefined; |
| 281 | try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F"); | |
| 281 | _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F"); | |
| 282 | 282 | var md: [32]u8 = undefined; |
| 283 | 283 | hash(&md, &msg, .{}); |
| 284 | 284 | htest.assertEqual("404C130AF1B9023A7908200919F690FFBB756D5176E056FFDE320016A37C7282", &md); |
| ... | ... | @@ -286,7 +286,7 @@ test "hash test vector 17" { |
| 286 | 286 | |
| 287 | 287 | test "hash test vector 33" { |
| 288 | 288 | var msg: [32]u8 = undefined; |
| 289 | try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); | |
| 289 | _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); | |
| 290 | 290 | var md: [32]u8 = undefined; |
| 291 | 291 | hash(&md, &msg, .{}); |
| 292 | 292 | htest.assertEqual("A8F4FA28708BDA7EFB4C1914CA4AFA9E475B82D588D36504F87DBB0ED9AB3C4B", &md); |
| ... | ... | @@ -436,9 +436,9 @@ pub const Aead = struct { |
| 436 | 436 | |
| 437 | 437 | test "cipher" { |
| 438 | 438 | var key: [32]u8 = undefined; |
| 439 | try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); | |
| 439 | _ = try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); | |
| 440 | 440 | var nonce: [16]u8 = undefined; |
| 441 | try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F"); | |
| 441 | _ = try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F"); | |
| 442 | 442 | { // test vector (1) from NIST KAT submission. |
| 443 | 443 | const ad: [0]u8 = undefined; |
| 444 | 444 | const pt: [0]u8 = undefined; |
| ... | ... | @@ -456,7 +456,7 @@ test "cipher" { |
| 456 | 456 | { // test vector (34) from NIST KAT submission. |
| 457 | 457 | const ad: [0]u8 = undefined; |
| 458 | 458 | var pt: [2 / 2]u8 = undefined; |
| 459 | try std.fmt.hexToBytes(&pt, "00"); | |
| 459 | _ = try std.fmt.hexToBytes(&pt, "00"); | |
| 460 | 460 | |
| 461 | 461 | var ct: [pt.len]u8 = undefined; |
| 462 | 462 | var tag: [16]u8 = undefined; |
| ... | ... | @@ -470,9 +470,9 @@ test "cipher" { |
| 470 | 470 | } |
| 471 | 471 | { // test vector (106) from NIST KAT submission. |
| 472 | 472 | var ad: [12 / 2]u8 = undefined; |
| 473 | try std.fmt.hexToBytes(&ad, "000102030405"); | |
| 473 | _ = try std.fmt.hexToBytes(&ad, "000102030405"); | |
| 474 | 474 | var pt: [6 / 2]u8 = undefined; |
| 475 | try std.fmt.hexToBytes(&pt, "000102"); | |
| 475 | _ = try std.fmt.hexToBytes(&pt, "000102"); | |
| 476 | 476 | |
| 477 | 477 | var ct: [pt.len]u8 = undefined; |
| 478 | 478 | var tag: [16]u8 = undefined; |
| ... | ... | @@ -486,9 +486,9 @@ test "cipher" { |
| 486 | 486 | } |
| 487 | 487 | { // test vector (790) from NIST KAT submission. |
| 488 | 488 | var ad: [60 / 2]u8 = undefined; |
| 489 | try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D"); | |
| 489 | _ = try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D"); | |
| 490 | 490 | var pt: [46 / 2]u8 = undefined; |
| 491 | try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516"); | |
| 491 | _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516"); | |
| 492 | 492 | |
| 493 | 493 | var ct: [pt.len]u8 = undefined; |
| 494 | 494 | var tag: [16]u8 = undefined; |
| ... | ... | @@ -503,7 +503,7 @@ test "cipher" { |
| 503 | 503 | { // test vector (1057) from NIST KAT submission. |
| 504 | 504 | const ad: [0]u8 = undefined; |
| 505 | 505 | var pt: [64 / 2]u8 = undefined; |
| 506 | try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); | |
| 506 | _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); | |
| 507 | 507 | |
| 508 | 508 | var ct: [pt.len]u8 = undefined; |
| 509 | 509 | var tag: [16]u8 = undefined; |
lib/std/event/loop.zig+4-5| ... | ... | @@ -440,13 +440,11 @@ pub const Loop = struct { |
| 440 | 440 | .overlapped = ResumeNode.overlapped_init, |
| 441 | 441 | }, |
| 442 | 442 | }; |
| 443 | var need_to_delete = false; | |
| 443 | var need_to_delete = true; | |
| 444 | 444 | defer if (need_to_delete) self.linuxRemoveFd(fd); |
| 445 | 445 | |
| 446 | 446 | suspend { |
| 447 | if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| { | |
| 448 | need_to_delete = true; | |
| 449 | } else |err| switch (err) { | |
| 447 | self.linuxAddFd(fd, &resume_node.base, flags) catch |err| switch (err) { | |
| 450 | 448 | error.FileDescriptorNotRegistered => unreachable, |
| 451 | 449 | error.OperationCausesCircularLoop => unreachable, |
| 452 | 450 | error.FileDescriptorIncompatibleWithEpoll => unreachable, |
| ... | ... | @@ -456,6 +454,7 @@ pub const Loop = struct { |
| 456 | 454 | error.UserResourceLimitReached, |
| 457 | 455 | error.Unexpected, |
| 458 | 456 | => { |
| 457 | need_to_delete = false; | |
| 459 | 458 | // Fall back to a blocking poll(). Ideally this codepath is never hit, since |
| 460 | 459 | // epoll should be just fine. But this is better than incorrect behavior. |
| 461 | 460 | var poll_flags: i16 = 0; |
| ... | ... | @@ -479,7 +478,7 @@ pub const Loop = struct { |
| 479 | 478 | }; |
| 480 | 479 | resume @frame(); |
| 481 | 480 | }, |
| 482 | } | |
| 481 | }; | |
| 483 | 482 | } |
| 484 | 483 | } |
| 485 | 484 |
lib/std/fifo.zig+7-3| ... | ... | @@ -44,6 +44,8 @@ pub fn LinearFifo( |
| 44 | 44 | count: usize, |
| 45 | 45 | |
| 46 | 46 | const Self = @This(); |
| 47 | pub const Reader = std.io.Reader(*Self, error{}, readFn); | |
| 48 | pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite); | |
| 47 | 49 | |
| 48 | 50 | // Type of Self argument for slice operations. |
| 49 | 51 | // If buffer is inline (Static) then we need to ensure we haven't |
| ... | ... | @@ -153,7 +155,7 @@ pub fn LinearFifo( |
| 153 | 155 | var start = self.head + offset; |
| 154 | 156 | if (start >= self.buf.len) { |
| 155 | 157 | start -= self.buf.len; |
| 156 | return self.buf[start .. self.count - offset]; | |
| 158 | return self.buf[start .. start + (self.count - offset)]; | |
| 157 | 159 | } else { |
| 158 | 160 | const end = math.min(self.head + self.count, self.buf.len); |
| 159 | 161 | return self.buf[start..end]; |
| ... | ... | @@ -228,7 +230,7 @@ pub fn LinearFifo( |
| 228 | 230 | return self.read(dest); |
| 229 | 231 | } |
| 230 | 232 | |
| 231 | pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) { | |
| 233 | pub fn reader(self: *Self) Reader { | |
| 232 | 234 | return .{ .context = self }; |
| 233 | 235 | } |
| 234 | 236 | |
| ... | ... | @@ -318,7 +320,7 @@ pub fn LinearFifo( |
| 318 | 320 | return bytes.len; |
| 319 | 321 | } |
| 320 | 322 | |
| 321 | pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) { | |
| 323 | pub fn writer(self: *Self) Writer { | |
| 322 | 324 | return .{ .context = self }; |
| 323 | 325 | } |
| 324 | 326 | |
| ... | ... | @@ -427,6 +429,8 @@ test "LinearFifo(u8, .Dynamic)" { |
| 427 | 429 | fifo.writeAssumeCapacity("6<chars<11"); |
| 428 | 430 | testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0)); |
| 429 | 431 | testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11)); |
| 432 | testing.expectEqualSlices(u8, "11", fifo.readableSlice(13)); | |
| 433 | testing.expectEqualSlices(u8, "", fifo.readableSlice(15)); | |
| 430 | 434 | fifo.discard(11); |
| 431 | 435 | testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0)); |
| 432 | 436 | fifo.discard(4); |
lib/std/fmt.zig+22-17| ... | ... | @@ -524,7 +524,7 @@ pub fn formatType( |
| 524 | 524 | if (actual_fmt.len == 0) |
| 525 | 525 | @compileError("cannot format array ref without a specifier (i.e. {s} or {*})"); |
| 526 | 526 | if (info.child == u8) { |
| 527 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { | |
| 527 | if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { | |
| 528 | 528 | return formatText(value, actual_fmt, options, writer); |
| 529 | 529 | } |
| 530 | 530 | } |
| ... | ... | @@ -542,7 +542,7 @@ pub fn formatType( |
| 542 | 542 | return formatType(mem.span(value), actual_fmt, options, writer, max_depth); |
| 543 | 543 | } |
| 544 | 544 | if (ptr_info.child == u8) { |
| 545 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { | |
| 545 | if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { | |
| 546 | 546 | return formatText(mem.span(value), actual_fmt, options, writer); |
| 547 | 547 | } |
| 548 | 548 | } |
| ... | ... | @@ -555,7 +555,7 @@ pub fn formatType( |
| 555 | 555 | return writer.writeAll("{ ... }"); |
| 556 | 556 | } |
| 557 | 557 | if (ptr_info.child == u8) { |
| 558 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { | |
| 558 | if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { | |
| 559 | 559 | return formatText(value, actual_fmt, options, writer); |
| 560 | 560 | } |
| 561 | 561 | } |
| ... | ... | @@ -576,7 +576,7 @@ pub fn formatType( |
| 576 | 576 | return writer.writeAll("{ ... }"); |
| 577 | 577 | } |
| 578 | 578 | if (info.child == u8) { |
| 579 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { | |
| 579 | if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { | |
| 580 | 580 | return formatText(&value, actual_fmt, options, writer); |
| 581 | 581 | } |
| 582 | 582 | } |
| ... | ... | @@ -658,8 +658,6 @@ pub fn formatIntValue( |
| 658 | 658 | } else { |
| 659 | 659 | @compileError("Cannot print integer that is larger than 8 bits as a ascii"); |
| 660 | 660 | } |
| 661 | } else if (comptime std.mem.eql(u8, fmt, "Z")) { | |
| 662 | @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead"); | |
| 663 | 661 | } else if (comptime std.mem.eql(u8, fmt, "u")) { |
| 664 | 662 | if (@typeInfo(@TypeOf(int_value)).Int.bits <= 21) { |
| 665 | 663 | return formatUnicodeCodepoint(@as(u21, int_value), options, writer); |
| ... | ... | @@ -735,10 +733,6 @@ pub fn formatText( |
| 735 | 733 | } |
| 736 | 734 | } |
| 737 | 735 | return; |
| 738 | } else if (comptime std.mem.eql(u8, fmt, "z")) { | |
| 739 | @compileError("specifier 'z' has been deprecated, wrap your argument in std.zig.fmtId instead"); | |
| 740 | } else if (comptime std.mem.eql(u8, fmt, "Z")) { | |
| 741 | @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead"); | |
| 742 | 736 | } else { |
| 743 | 737 | @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); |
| 744 | 738 | } |
| ... | ... | @@ -1988,23 +1982,34 @@ test "bytes.hex" { |
| 1988 | 1982 | pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead"); |
| 1989 | 1983 | pub const isWhiteSpace = @compileError("deprecated; use std.ascii.isSpace instead"); |
| 1990 | 1984 | |
| 1991 | pub fn hexToBytes(out: []u8, input: []const u8) !void { | |
| 1992 | if (out.len * 2 < input.len) | |
| 1985 | /// Decodes the sequence of bytes represented by the specified string of | |
| 1986 | /// hexadecimal characters. | |
| 1987 | /// Returns a slice of the output buffer containing the decoded bytes. | |
| 1988 | pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { | |
| 1989 | // Expect 0 or n pairs of hexadecimal digits. | |
| 1990 | if (input.len & 1 != 0) | |
| 1993 | 1991 | return error.InvalidLength; |
| 1992 | if (out.len * 2 < input.len) | |
| 1993 | return error.NoSpaceLeft; | |
| 1994 | 1994 | |
| 1995 | 1995 | var in_i: usize = 0; |
| 1996 | while (in_i != input.len) : (in_i += 2) { | |
| 1996 | while (in_i < input.len) : (in_i += 2) { | |
| 1997 | 1997 | const hi = try charToDigit(input[in_i], 16); |
| 1998 | 1998 | const lo = try charToDigit(input[in_i + 1], 16); |
| 1999 | 1999 | out[in_i / 2] = (hi << 4) | lo; |
| 2000 | 2000 | } |
| 2001 | ||
| 2002 | return out[0 .. in_i / 2]; | |
| 2001 | 2003 | } |
| 2002 | 2004 | |
| 2003 | 2005 | test "hexToBytes" { |
| 2004 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; | |
| 2005 | var pb: [32]u8 = undefined; | |
| 2006 | try hexToBytes(pb[0..], test_hex_str); | |
| 2007 | try expectFmt(test_hex_str, "{X}", .{pb}); | |
| 2006 | var buf: [32]u8 = undefined; | |
| 2007 | try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)}); | |
| 2008 | try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")}); | |
| 2009 | try expectFmt("", "{X}", .{try hexToBytes(&buf, "")}); | |
| 2010 | std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z")); | |
| 2011 | std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA")); | |
| 2012 | std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB")); | |
| 2008 | 2013 | } |
| 2009 | 2014 | |
| 2010 | 2015 | test "formatIntValue with comptime_int" { |
lib/std/fs.zig+1-1| ... | ... | @@ -2186,7 +2186,7 @@ pub const Walker = struct { |
| 2186 | 2186 | var top = &self.stack.items[self.stack.items.len - 1]; |
| 2187 | 2187 | const dirname_len = top.dirname_len; |
| 2188 | 2188 | if (try top.dir_it.next()) |base| { |
| 2189 | self.name_buffer.shrinkAndFree(dirname_len); | |
| 2189 | self.name_buffer.shrinkRetainingCapacity(dirname_len); | |
| 2190 | 2190 | try self.name_buffer.append(path.sep); |
| 2191 | 2191 | try self.name_buffer.appendSlice(base.name); |
| 2192 | 2192 | if (base.kind == .Directory) { |
lib/std/fs/file.zig+2| ... | ... | @@ -587,6 +587,7 @@ pub const File = struct { |
| 587 | 587 | } |
| 588 | 588 | |
| 589 | 589 | /// See https://github.com/ziglang/zig/issues/7699 |
| 590 | /// See equivalent function: `std.net.Stream.writev`. | |
| 590 | 591 | pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize { |
| 591 | 592 | if (is_windows) { |
| 592 | 593 | // TODO improve this to use WriteFileScatter |
| ... | ... | @@ -605,6 +606,7 @@ pub const File = struct { |
| 605 | 606 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 606 | 607 | /// order to handle partial writes from the underlying OS layer. |
| 607 | 608 | /// See https://github.com/ziglang/zig/issues/7699 |
| 609 | /// See equivalent function: `std.net.Stream.writevAll`. | |
| 608 | 610 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { |
| 609 | 611 | if (iovecs.len == 0) return; |
| 610 | 612 |
lib/std/io/reader.zig+1-1| ... | ... | @@ -111,7 +111,7 @@ pub fn Reader( |
| 111 | 111 | delimiter: u8, |
| 112 | 112 | max_size: usize, |
| 113 | 113 | ) !void { |
| 114 | array_list.shrinkAndFree(0); | |
| 114 | array_list.shrinkRetainingCapacity(0); | |
| 115 | 115 | while (true) { |
| 116 | 116 | var byte: u8 = try self.readByte(); |
| 117 | 117 |
lib/std/json.zig+1-1| ... | ... | @@ -2018,7 +2018,7 @@ pub const Parser = struct { |
| 2018 | 2018 | |
| 2019 | 2019 | pub fn reset(p: *Parser) void { |
| 2020 | 2020 | p.state = .Simple; |
| 2021 | p.stack.shrinkAndFree(0); | |
| 2021 | p.stack.shrinkRetainingCapacity(0); | |
| 2022 | 2022 | } |
| 2023 | 2023 | |
| 2024 | 2024 | pub fn parse(p: *Parser, input: []const u8) !ValueTree { |
lib/std/math/big/int.zig+1-1| ... | ... | @@ -607,7 +607,7 @@ pub const Mutable = struct { |
| 607 | 607 | /// it will have the same length as it had when the function was called. |
| 608 | 608 | pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void { |
| 609 | 609 | const prev_len = limbs_buffer.items.len; |
| 610 | defer limbs_buffer.shrinkAndFree(prev_len); | |
| 610 | defer limbs_buffer.shrinkRetainingCapacity(prev_len); | |
| 611 | 611 | const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: { |
| 612 | 612 | const start = limbs_buffer.items.len; |
| 613 | 613 | try limbs_buffer.appendSlice(x.limbs); |
lib/std/net.zig+39-2| ... | ... | @@ -1205,13 +1205,13 @@ fn linuxLookupNameFromDnsSearch( |
| 1205 | 1205 | |
| 1206 | 1206 | var tok_it = mem.tokenize(search, " \t"); |
| 1207 | 1207 | while (tok_it.next()) |tok| { |
| 1208 | canon.shrinkAndFree(canon_name.len + 1); | |
| 1208 | canon.shrinkRetainingCapacity(canon_name.len + 1); | |
| 1209 | 1209 | try canon.appendSlice(tok); |
| 1210 | 1210 | try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port); |
| 1211 | 1211 | if (addrs.items.len != 0) return; |
| 1212 | 1212 | } |
| 1213 | 1213 | |
| 1214 | canon.shrinkAndFree(canon_name.len); | |
| 1214 | canon.shrinkRetainingCapacity(canon_name.len); | |
| 1215 | 1215 | return linuxLookupNameFromDns(addrs, canon, name, family, rc, port); |
| 1216 | 1216 | } |
| 1217 | 1217 | |
| ... | ... | @@ -1621,6 +1621,9 @@ pub const Stream = struct { |
| 1621 | 1621 | } |
| 1622 | 1622 | } |
| 1623 | 1623 | |
| 1624 | /// TODO in evented I/O mode, this implementation incorrectly uses the event loop's | |
| 1625 | /// file system thread instead of non-blocking. It needs to be reworked to properly | |
| 1626 | /// use non-blocking I/O. | |
| 1624 | 1627 | pub fn write(self: Stream, buffer: []const u8) WriteError!usize { |
| 1625 | 1628 | if (std.Target.current.os.tag == .windows) { |
| 1626 | 1629 | return os.windows.WriteFile(self.handle, buffer, null, io.default_mode); |
| ... | ... | @@ -1632,6 +1635,40 @@ pub const Stream = struct { |
| 1632 | 1635 | return os.write(self.handle, buffer); |
| 1633 | 1636 | } |
| 1634 | 1637 | } |
| 1638 | ||
| 1639 | /// See https://github.com/ziglang/zig/issues/7699 | |
| 1640 | /// See equivalent function: `std.fs.File.writev`. | |
| 1641 | pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize { | |
| 1642 | if (std.io.is_async) { | |
| 1643 | // TODO improve to actually take advantage of writev syscall, if available. | |
| 1644 | if (iovecs.len == 0) return 0; | |
| 1645 | const first_buffer = iovecs[0].iov_base[0..iovecs[0].iov_len]; | |
| 1646 | try self.write(first_buffer); | |
| 1647 | return first_buffer.len; | |
| 1648 | } else { | |
| 1649 | return os.writev(self.handle, iovecs); | |
| 1650 | } | |
| 1651 | } | |
| 1652 | ||
| 1653 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | |
| 1654 | /// order to handle partial writes from the underlying OS layer. | |
| 1655 | /// See https://github.com/ziglang/zig/issues/7699 | |
| 1656 | /// See equivalent function: `std.fs.File.writevAll`. | |
| 1657 | pub fn writevAll(self: Stream, iovecs: []os.iovec_const) WriteError!void { | |
| 1658 | if (iovecs.len == 0) return; | |
| 1659 | ||
| 1660 | var i: usize = 0; | |
| 1661 | while (true) { | |
| 1662 | var amt = try self.writev(iovecs[i..]); | |
| 1663 | while (amt >= iovecs[i].iov_len) { | |
| 1664 | amt -= iovecs[i].iov_len; | |
| 1665 | i += 1; | |
| 1666 | if (i >= iovecs.len) return; | |
| 1667 | } | |
| 1668 | iovecs[i].iov_base += amt; | |
| 1669 | iovecs[i].iov_len -= amt; | |
| 1670 | } | |
| 1671 | } | |
| 1635 | 1672 | }; |
| 1636 | 1673 | |
| 1637 | 1674 | pub const StreamServer = struct { |
lib/std/os.zig+86| ... | ... | @@ -1634,6 +1634,92 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*: |
| 1634 | 1634 | } |
| 1635 | 1635 | } |
| 1636 | 1636 | |
| 1637 | pub const LinkError = UnexpectedError || error{ | |
| 1638 | AccessDenied, | |
| 1639 | DiskQuota, | |
| 1640 | PathAlreadyExists, | |
| 1641 | FileSystem, | |
| 1642 | SymLinkLoop, | |
| 1643 | LinkQuotaExceeded, | |
| 1644 | NameTooLong, | |
| 1645 | FileNotFound, | |
| 1646 | SystemResources, | |
| 1647 | NoSpaceLeft, | |
| 1648 | ReadOnlyFileSystem, | |
| 1649 | NotSameFileSystem, | |
| 1650 | }; | |
| 1651 | ||
| 1652 | pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void { | |
| 1653 | switch (errno(system.link(oldpath, newpath, flags))) { | |
| 1654 | 0 => return, | |
| 1655 | EACCES => return error.AccessDenied, | |
| 1656 | EDQUOT => return error.DiskQuota, | |
| 1657 | EEXIST => return error.PathAlreadyExists, | |
| 1658 | EFAULT => unreachable, | |
| 1659 | EIO => return error.FileSystem, | |
| 1660 | ELOOP => return error.SymLinkLoop, | |
| 1661 | EMLINK => return error.LinkQuotaExceeded, | |
| 1662 | ENAMETOOLONG => return error.NameTooLong, | |
| 1663 | ENOENT => return error.FileNotFound, | |
| 1664 | ENOMEM => return error.SystemResources, | |
| 1665 | ENOSPC => return error.NoSpaceLeft, | |
| 1666 | EPERM => return error.AccessDenied, | |
| 1667 | EROFS => return error.ReadOnlyFileSystem, | |
| 1668 | EXDEV => return error.NotSameFileSystem, | |
| 1669 | EINVAL => unreachable, | |
| 1670 | else => |err| return unexpectedErrno(err), | |
| 1671 | } | |
| 1672 | } | |
| 1673 | ||
| 1674 | pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void { | |
| 1675 | const old = try toPosixPath(oldpath); | |
| 1676 | const new = try toPosixPath(newpath); | |
| 1677 | return try linkZ(&old, &new, flags); | |
| 1678 | } | |
| 1679 | ||
| 1680 | pub const LinkatError = LinkError || error{NotDir}; | |
| 1681 | ||
| 1682 | pub fn linkatZ( | |
| 1683 | olddir: fd_t, | |
| 1684 | oldpath: [*:0]const u8, | |
| 1685 | newdir: fd_t, | |
| 1686 | newpath: [*:0]const u8, | |
| 1687 | flags: i32, | |
| 1688 | ) LinkatError!void { | |
| 1689 | switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) { | |
| 1690 | 0 => return, | |
| 1691 | EACCES => return error.AccessDenied, | |
| 1692 | EDQUOT => return error.DiskQuota, | |
| 1693 | EEXIST => return error.PathAlreadyExists, | |
| 1694 | EFAULT => unreachable, | |
| 1695 | EIO => return error.FileSystem, | |
| 1696 | ELOOP => return error.SymLinkLoop, | |
| 1697 | EMLINK => return error.LinkQuotaExceeded, | |
| 1698 | ENAMETOOLONG => return error.NameTooLong, | |
| 1699 | ENOENT => return error.FileNotFound, | |
| 1700 | ENOMEM => return error.SystemResources, | |
| 1701 | ENOSPC => return error.NoSpaceLeft, | |
| 1702 | ENOTDIR => return error.NotDir, | |
| 1703 | EPERM => return error.AccessDenied, | |
| 1704 | EROFS => return error.ReadOnlyFileSystem, | |
| 1705 | EXDEV => return error.NotSameFileSystem, | |
| 1706 | EINVAL => unreachable, | |
| 1707 | else => |err| return unexpectedErrno(err), | |
| 1708 | } | |
| 1709 | } | |
| 1710 | ||
| 1711 | pub fn linkat( | |
| 1712 | olddir: fd_t, | |
| 1713 | oldpath: []const u8, | |
| 1714 | newdir: fd_t, | |
| 1715 | newpath: []const u8, | |
| 1716 | flags: i32, | |
| 1717 | ) LinkatError!void { | |
| 1718 | const old = try toPosixPath(oldpath); | |
| 1719 | const new = try toPosixPath(newpath); | |
| 1720 | return try linkatZ(olddir, &old, newdir, &new, flags); | |
| 1721 | } | |
| 1722 | ||
| 1637 | 1723 | pub const UnlinkError = error{ |
| 1638 | 1724 | FileNotFound, |
| 1639 | 1725 |
lib/std/os/bits/linux/arm-eabi.zig+1| ... | ... | @@ -412,6 +412,7 @@ pub const SYS = extern enum(usize) { |
| 412 | 412 | pidfd_getfd = 438, |
| 413 | 413 | faccessat2 = 439, |
| 414 | 414 | process_madvise = 440, |
| 415 | epoll_pwait2 = 441, | |
| 415 | 416 | |
| 416 | 417 | breakpoint = 0x0f0001, |
| 417 | 418 | cacheflush = 0x0f0002, |
lib/std/os/bits/linux/arm64.zig+1| ... | ... | @@ -313,6 +313,7 @@ pub const SYS = extern enum(usize) { |
| 313 | 313 | pidfd_getfd = 438, |
| 314 | 314 | faccessat2 = 439, |
| 315 | 315 | process_madvise = 440, |
| 316 | epoll_pwait2 = 441, | |
| 316 | 317 | |
| 317 | 318 | _, |
| 318 | 319 | }; |
lib/std/os/bits/linux/i386.zig+1| ... | ... | @@ -448,6 +448,7 @@ pub const SYS = extern enum(usize) { |
| 448 | 448 | pidfd_getfd = 438, |
| 449 | 449 | faccessat2 = 439, |
| 450 | 450 | process_madvise = 440, |
| 451 | epoll_pwait2 = 441, | |
| 451 | 452 | |
| 452 | 453 | _, |
| 453 | 454 | }; |
lib/std/os/bits/linux/mips.zig+1| ... | ... | @@ -430,6 +430,7 @@ pub const SYS = extern enum(usize) { |
| 430 | 430 | pidfd_getfd = Linux + 438, |
| 431 | 431 | faccessat2 = Linux + 439, |
| 432 | 432 | process_madvise = Linux + 440, |
| 433 | epoll_pwait2 = Linux + 441, | |
| 433 | 434 | |
| 434 | 435 | _, |
| 435 | 436 | }; |
lib/std/os/bits/linux/powerpc64.zig+1| ... | ... | @@ -409,6 +409,7 @@ pub const SYS = extern enum(usize) { |
| 409 | 409 | pidfd_getfd = 438, |
| 410 | 410 | faccessat2 = 439, |
| 411 | 411 | process_madvise = 440, |
| 412 | epoll_pwait2 = 441, | |
| 412 | 413 | |
| 413 | 414 | _, |
| 414 | 415 | }; |
lib/std/os/bits/linux/riscv64.zig+1| ... | ... | @@ -310,6 +310,7 @@ pub const SYS = extern enum(usize) { |
| 310 | 310 | pidfd_getfd = 438, |
| 311 | 311 | faccessat2 = 439, |
| 312 | 312 | process_madvise = 440, |
| 313 | epoll_pwait2 = 441, | |
| 313 | 314 | |
| 314 | 315 | _, |
| 315 | 316 | }; |
lib/std/os/bits/linux/sparc64.zig+1| ... | ... | @@ -387,6 +387,7 @@ pub const SYS = extern enum(usize) { |
| 387 | 387 | pidfd_getfd = 438, |
| 388 | 388 | faccessat2 = 439, |
| 389 | 389 | process_madvise = 440, |
| 390 | epoll_pwait2 = 441, | |
| 390 | 391 | |
| 391 | 392 | _, |
| 392 | 393 | }; |
lib/std/os/bits/linux/x86_64.zig+1| ... | ... | @@ -375,6 +375,7 @@ pub const SYS = extern enum(usize) { |
| 375 | 375 | pidfd_getfd = 438, |
| 376 | 376 | faccessat2 = 439, |
| 377 | 377 | process_madvise = 440, |
| 378 | epoll_pwait2 = 441, | |
| 378 | 379 | |
| 379 | 380 | _, |
| 380 | 381 | }; |
lib/std/os/linux.zig+31| ... | ... | @@ -634,6 +634,37 @@ pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { |
| 634 | 634 | return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); |
| 635 | 635 | } |
| 636 | 636 | |
| 637 | pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize { | |
| 638 | if (@hasField(SYS, "link")) { | |
| 639 | return syscall3( | |
| 640 | .link, | |
| 641 | @ptrToInt(oldpath), | |
| 642 | @ptrToInt(newpath), | |
| 643 | @bitCast(usize, @as(isize, flags)), | |
| 644 | ); | |
| 645 | } else { | |
| 646 | return syscall5( | |
| 647 | .linkat, | |
| 648 | @bitCast(usize, @as(isize, AT_FDCWD)), | |
| 649 | @ptrToInt(oldpath), | |
| 650 | @bitCast(usize, @as(isize, AT_FDCWD)), | |
| 651 | @ptrToInt(newpath), | |
| 652 | @bitCast(usize, @as(isize, flags)), | |
| 653 | ); | |
| 654 | } | |
| 655 | } | |
| 656 | ||
| 657 | pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: i32) usize { | |
| 658 | return syscall5( | |
| 659 | .linkat, | |
| 660 | @bitCast(usize, @as(isize, oldfd)), | |
| 661 | @ptrToInt(oldpath), | |
| 662 | @bitCast(usize, @as(isize, newfd)), | |
| 663 | @ptrToInt(newpath), | |
| 664 | @bitCast(usize, @as(isize, flags)), | |
| 665 | ); | |
| 666 | } | |
| 667 | ||
| 637 | 668 | pub fn unlink(path: [*:0]const u8) usize { |
| 638 | 669 | if (@hasField(SYS, "unlink")) { |
| 639 | 670 | return syscall1(.unlink, @ptrToInt(path)); |
lib/std/os/test.zig+69| ... | ... | @@ -189,6 +189,75 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 189 | 189 | expect(mem.eql(u8, target_path, given)); |
| 190 | 190 | } |
| 191 | 191 | |
| 192 | test "link with relative paths" { | |
| 193 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 194 | var cwd = fs.cwd(); | |
| 195 | ||
| 196 | cwd.deleteFile("example.txt") catch {}; | |
| 197 | cwd.deleteFile("new.txt") catch {}; | |
| 198 | ||
| 199 | try cwd.writeFile("example.txt", "example"); | |
| 200 | try os.link("example.txt", "new.txt", 0); | |
| 201 | ||
| 202 | const efd = try cwd.openFile("example.txt", .{}); | |
| 203 | defer efd.close(); | |
| 204 | ||
| 205 | const nfd = try cwd.openFile("new.txt", .{}); | |
| 206 | defer nfd.close(); | |
| 207 | ||
| 208 | { | |
| 209 | const estat = try os.fstat(efd.handle); | |
| 210 | const nstat = try os.fstat(nfd.handle); | |
| 211 | ||
| 212 | testing.expectEqual(estat.ino, nstat.ino); | |
| 213 | testing.expectEqual(@as(usize, 2), nstat.nlink); | |
| 214 | } | |
| 215 | ||
| 216 | try os.unlink("new.txt"); | |
| 217 | ||
| 218 | { | |
| 219 | const estat = try os.fstat(efd.handle); | |
| 220 | testing.expectEqual(@as(usize, 1), estat.nlink); | |
| 221 | } | |
| 222 | ||
| 223 | try cwd.deleteFile("example.txt"); | |
| 224 | } | |
| 225 | ||
| 226 | test "linkat with different directories" { | |
| 227 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 228 | var cwd = fs.cwd(); | |
| 229 | var tmp = tmpDir(.{}); | |
| 230 | ||
| 231 | cwd.deleteFile("example.txt") catch {}; | |
| 232 | tmp.dir.deleteFile("new.txt") catch {}; | |
| 233 | ||
| 234 | try cwd.writeFile("example.txt", "example"); | |
| 235 | try os.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0); | |
| 236 | ||
| 237 | const efd = try cwd.openFile("example.txt", .{}); | |
| 238 | defer efd.close(); | |
| 239 | ||
| 240 | const nfd = try tmp.dir.openFile("new.txt", .{}); | |
| 241 | ||
| 242 | { | |
| 243 | defer nfd.close(); | |
| 244 | const estat = try os.fstat(efd.handle); | |
| 245 | const nstat = try os.fstat(nfd.handle); | |
| 246 | ||
| 247 | testing.expectEqual(estat.ino, nstat.ino); | |
| 248 | testing.expectEqual(@as(usize, 2), nstat.nlink); | |
| 249 | } | |
| 250 | ||
| 251 | try os.unlinkat(tmp.dir.fd, "new.txt", 0); | |
| 252 | ||
| 253 | { | |
| 254 | const estat = try os.fstat(efd.handle); | |
| 255 | testing.expectEqual(@as(usize, 1), estat.nlink); | |
| 256 | } | |
| 257 | ||
| 258 | try cwd.deleteFile("example.txt"); | |
| 259 | } | |
| 260 | ||
| 192 | 261 | test "fstatat" { |
| 193 | 262 | // enable when `fstat` and `fstatat` are implemented on Windows |
| 194 | 263 | if (builtin.os.tag == .windows) return error.SkipZigTest; |
lib/std/os/uefi.zig+14-3| ... | ... | @@ -3,6 +3,8 @@ |
| 3 | 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. |
| 4 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | const std = @import("../std.zig"); | |
| 7 | ||
| 6 | 8 | /// A protocol is an interface identified by a GUID. |
| 7 | 9 | pub const protocols = @import("uefi/protocols.zig"); |
| 8 | 10 | |
| ... | ... | @@ -33,10 +35,10 @@ pub const Guid = extern struct { |
| 33 | 35 | self: @This(), |
| 34 | 36 | comptime f: []const u8, |
| 35 | 37 | options: std.fmt.FormatOptions, |
| 36 | out_stream: anytype, | |
| 37 | ) Errors!void { | |
| 38 | writer: anytype, | |
| 39 | ) !void { | |
| 38 | 40 | if (f.len == 0) { |
| 39 | return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ | |
| 41 | return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ | |
| 40 | 42 | self.time_low, |
| 41 | 43 | self.time_mid, |
| 42 | 44 | self.time_high_and_version, |
| ... | ... | @@ -48,6 +50,15 @@ pub const Guid = extern struct { |
| 48 | 50 | @compileError("Unknown format character: '" ++ f ++ "'"); |
| 49 | 51 | } |
| 50 | 52 | } |
| 53 | ||
| 54 | pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool { | |
| 55 | return a.time_low == b.time_low and | |
| 56 | a.time_mid == b.time_mid and | |
| 57 | a.time_high_and_version == b.time_high_and_version and | |
| 58 | a.clock_seq_high_and_reserved == b.clock_seq_high_and_reserved and | |
| 59 | a.clock_seq_low == b.clock_seq_low and | |
| 60 | std.mem.eql(u8, &a.node, &b.node); | |
| 61 | } | |
| 51 | 62 | }; |
| 52 | 63 | |
| 53 | 64 | /// An EFI Handle represents a collection of related interfaces. |
lib/std/wasm.zig+14| ... | ... | @@ -253,6 +253,20 @@ pub fn section(val: Section) u8 { |
| 253 | 253 | return @enumToInt(val); |
| 254 | 254 | } |
| 255 | 255 | |
| 256 | /// The kind of the type when importing or exporting to/from the host environment | |
| 257 | /// https://webassembly.github.io/spec/core/syntax/modules.html | |
| 258 | pub const ExternalKind = enum(u8) { | |
| 259 | function, | |
| 260 | table, | |
| 261 | memory, | |
| 262 | global, | |
| 263 | }; | |
| 264 | ||
| 265 | /// Returns the integer value of a given `ExternalKind` | |
| 266 | pub fn externalKind(val: ExternalKind) u8 { | |
| 267 | return @enumToInt(val); | |
| 268 | } | |
| 269 | ||
| 256 | 270 | // types |
| 257 | 271 | pub const element_type: u8 = 0x70; |
| 258 | 272 | pub const function_type: u8 = 0x60; |
lib/std/zig/parser_test.zig+1-1| ... | ... | @@ -648,7 +648,7 @@ test "zig fmt: struct literal 1 element" { |
| 648 | 648 | ); |
| 649 | 649 | } |
| 650 | 650 | |
| 651 | test "zig fmt: struct literal 1 element comma" { | |
| 651 | test "zig fmt: Unicode code point literal larger than u8" { | |
| 652 | 652 | try testCanonical( |
| 653 | 653 | \\test { |
| 654 | 654 | \\ const x = X{ |
lib/std/zig/tokenizer.zig+3-3| ... | ... | @@ -1535,7 +1535,7 @@ test "tokenizer - unknown length pointer and then c pointer" { |
| 1535 | 1535 | }); |
| 1536 | 1536 | } |
| 1537 | 1537 | |
| 1538 | test "tokenizer - char literal with hex escape" { | |
| 1538 | test "tokenizer - code point literal with hex escape" { | |
| 1539 | 1539 | testTokenize( |
| 1540 | 1540 | \\'\x1b' |
| 1541 | 1541 | , &.{.char_literal}); |
| ... | ... | @@ -1544,7 +1544,7 @@ test "tokenizer - char literal with hex escape" { |
| 1544 | 1544 | , &.{ .invalid, .invalid }); |
| 1545 | 1545 | } |
| 1546 | 1546 | |
| 1547 | test "tokenizer - char literal with unicode escapes" { | |
| 1547 | test "tokenizer - code point literal with unicode escapes" { | |
| 1548 | 1548 | // Valid unicode escapes |
| 1549 | 1549 | testTokenize( |
| 1550 | 1550 | \\'\u{3}' |
| ... | ... | @@ -1594,7 +1594,7 @@ test "tokenizer - char literal with unicode escapes" { |
| 1594 | 1594 | , &.{ .invalid, .integer_literal, .invalid }); |
| 1595 | 1595 | } |
| 1596 | 1596 | |
| 1597 | test "tokenizer - char literal with unicode code point" { | |
| 1597 | test "tokenizer - code point literal with unicode code point" { | |
| 1598 | 1598 | testTokenize( |
| 1599 | 1599 | \\'💩' |
| 1600 | 1600 | , &.{.char_literal}); |
src/Cache.zig+1-1| ... | ... | @@ -317,7 +317,7 @@ pub const Manifest = struct { |
| 317 | 317 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; |
| 318 | 318 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; |
| 319 | 319 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; |
| 320 | std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | |
| 320 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | |
| 321 | 321 | |
| 322 | 322 | if (file_path.len == 0) { |
| 323 | 323 | return error.InvalidFormat; |
src/Compilation.zig+4-2| ... | ... | @@ -645,7 +645,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 645 | 645 | }; |
| 646 | 646 | |
| 647 | 647 | const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: { |
| 648 | const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: { | |
| 648 | const opts: DarwinOptions = if (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()) inner: { | |
| 649 | 649 | // TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out. |
| 650 | 650 | // See https://github.com/ziglang/zig/issues/6996 |
| 651 | 651 | const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11; |
| ... | ... | @@ -1538,7 +1538,9 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 { |
| 1538 | 1538 | } |
| 1539 | 1539 | |
| 1540 | 1540 | pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { |
| 1541 | var progress: std.Progress = .{}; | |
| 1541 | // If the terminal is dumb, we dont want to show the user all the | |
| 1542 | // output. | |
| 1543 | var progress: std.Progress = .{ .dont_print_on_dumb = true }; | |
| 1542 | 1544 | var main_progress_node = try progress.start("", 0); |
| 1543 | 1545 | defer main_progress_node.end(); |
| 1544 | 1546 | if (self.color == .off) progress.terminal = null; |
src/codegen.zig+61-58| ... | ... | @@ -944,7 +944,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 944 | 944 | /// Copies a value to a register without tracking the register. The register is not considered |
| 945 | 945 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 946 | 946 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 947 | fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register { | |
| 947 | fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register { | |
| 948 | 948 | const reg = self.findUnusedReg() orelse b: { |
| 949 | 949 | // We'll take over the first register. Move the instruction that was previously |
| 950 | 950 | // there to a stack allocation. |
| ... | ... | @@ -961,7 +961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 961 | 961 | |
| 962 | 962 | break :b reg; |
| 963 | 963 | }; |
| 964 | try self.genSetReg(src, reg, mcv); | |
| 964 | try self.genSetReg(src, ty, reg, mcv); | |
| 965 | 965 | return reg; |
| 966 | 966 | } |
| 967 | 967 | |
| ... | ... | @@ -988,7 +988,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 988 | 988 | |
| 989 | 989 | break :b reg; |
| 990 | 990 | }; |
| 991 | try self.genSetReg(reg_owner.src, reg, mcv); | |
| 991 | try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); | |
| 992 | 992 | return MCValue{ .register = reg }; |
| 993 | 993 | } |
| 994 | 994 | |
| ... | ... | @@ -1356,13 +1356,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1356 | 1356 | // Load immediate into register if it doesn't fit |
| 1357 | 1357 | // as an operand |
| 1358 | 1358 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse |
| 1359 | Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none); | |
| 1359 | Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none); | |
| 1360 | 1360 | }, |
| 1361 | 1361 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), |
| 1362 | 1362 | .stack_offset, |
| 1363 | 1363 | .embedded_in_code, |
| 1364 | 1364 | .memory, |
| 1365 | => Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none), | |
| 1365 | => Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none), | |
| 1366 | 1366 | }; |
| 1367 | 1367 | |
| 1368 | 1368 | switch (op) { |
| ... | ... | @@ -1448,7 +1448,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1448 | 1448 | switch (src_mcv) { |
| 1449 | 1449 | .immediate => |imm| { |
| 1450 | 1450 | if (imm > math.maxInt(u31)) { |
| 1451 | src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) }; | |
| 1451 | src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) }; | |
| 1452 | 1452 | } |
| 1453 | 1453 | }, |
| 1454 | 1454 | else => {}, |
| ... | ... | @@ -1479,7 +1479,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1479 | 1479 | .register => |dst_reg| { |
| 1480 | 1480 | switch (src_mcv) { |
| 1481 | 1481 | .none => unreachable, |
| 1482 | .undef => try self.genSetReg(src, dst_reg, .undef), | |
| 1482 | .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef), | |
| 1483 | 1483 | .dead, .unreach => unreachable, |
| 1484 | 1484 | .ptr_stack_offset => unreachable, |
| 1485 | 1485 | .ptr_embedded_in_code => unreachable, |
| ... | ... | @@ -1689,7 +1689,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1689 | 1689 | switch (mc_arg) { |
| 1690 | 1690 | .none => continue, |
| 1691 | 1691 | .register => |reg| { |
| 1692 | try self.genSetReg(arg.src, reg, arg_mcv); | |
| 1692 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); | |
| 1693 | 1693 | // TODO interact with the register allocator to mark the instruction as moved. |
| 1694 | 1694 | }, |
| 1695 | 1695 | .stack_offset => { |
| ... | ... | @@ -1758,7 +1758,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1758 | 1758 | else |
| 1759 | 1759 | unreachable; |
| 1760 | 1760 | |
| 1761 | try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr }); | |
| 1761 | try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr }); | |
| 1762 | 1762 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32()); |
| 1763 | 1763 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1764 | 1764 | return self.fail(inst.base.src, "TODO implement calling extern functions", .{}); |
| ... | ... | @@ -1831,7 +1831,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1831 | 1831 | .compare_flags_signed => unreachable, |
| 1832 | 1832 | .compare_flags_unsigned => unreachable, |
| 1833 | 1833 | .register => |reg| { |
| 1834 | try self.genSetReg(arg.src, reg, arg_mcv); | |
| 1834 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); | |
| 1835 | 1835 | // TODO interact with the register allocator to mark the instruction as moved. |
| 1836 | 1836 | }, |
| 1837 | 1837 | .stack_offset => { |
| ... | ... | @@ -1859,7 +1859,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1859 | 1859 | else |
| 1860 | 1860 | unreachable; |
| 1861 | 1861 | |
| 1862 | try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); | |
| 1862 | try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr }); | |
| 1863 | 1863 | |
| 1864 | 1864 | // TODO: add Instruction.supportedOn |
| 1865 | 1865 | // function for ARM |
| ... | ... | @@ -1894,7 +1894,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1894 | 1894 | .compare_flags_signed => unreachable, |
| 1895 | 1895 | .compare_flags_unsigned => unreachable, |
| 1896 | 1896 | .register => |reg| { |
| 1897 | try self.genSetReg(arg.src, reg, arg_mcv); | |
| 1897 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); | |
| 1898 | 1898 | // TODO interact with the register allocator to mark the instruction as moved. |
| 1899 | 1899 | }, |
| 1900 | 1900 | .stack_offset => { |
| ... | ... | @@ -1922,7 +1922,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1922 | 1922 | else |
| 1923 | 1923 | unreachable; |
| 1924 | 1924 | |
| 1925 | try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); | |
| 1925 | try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr }); | |
| 1926 | 1926 | |
| 1927 | 1927 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); |
| 1928 | 1928 | } else if (func_value.castTag(.extern_fn)) |_| { |
| ... | ... | @@ -1945,7 +1945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1945 | 1945 | switch (mc_arg) { |
| 1946 | 1946 | .none => continue, |
| 1947 | 1947 | .register => |reg| { |
| 1948 | try self.genSetReg(arg.src, reg, arg_mcv); | |
| 1948 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); | |
| 1949 | 1949 | // TODO interact with the register allocator to mark the instruction as moved. |
| 1950 | 1950 | }, |
| 1951 | 1951 | .stack_offset => { |
| ... | ... | @@ -1978,12 +1978,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1978 | 1978 | const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); |
| 1979 | 1979 | switch (arch) { |
| 1980 | 1980 | .x86_64 => { |
| 1981 | try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr }); | |
| 1981 | try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr }); | |
| 1982 | 1982 | // callq *%rax |
| 1983 | 1983 | self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); |
| 1984 | 1984 | }, |
| 1985 | 1985 | .aarch64 => { |
| 1986 | try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); | |
| 1986 | try self.genSetReg(inst.base.src, Type.initTag(.u32), .x30, .{ .memory = got_addr }); | |
| 1987 | 1987 | // blr x30 |
| 1988 | 1988 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); |
| 1989 | 1989 | }, |
| ... | ... | @@ -2584,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2584 | 2584 | const reg = parseRegName(reg_name) orelse |
| 2585 | 2585 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2586 | 2586 | const arg = try self.resolveInst(inst.args[i]); |
| 2587 | try self.genSetReg(inst.base.src, reg, arg); | |
| 2587 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2588 | 2588 | } |
| 2589 | 2589 | |
| 2590 | 2590 | if (mem.eql(u8, inst.asm_source, "svc #0")) { |
| ... | ... | @@ -2614,7 +2614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2614 | 2614 | const reg = parseRegName(reg_name) orelse |
| 2615 | 2615 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2616 | 2616 | const arg = try self.resolveInst(inst.args[i]); |
| 2617 | try self.genSetReg(inst.base.src, reg, arg); | |
| 2617 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2618 | 2618 | } |
| 2619 | 2619 | |
| 2620 | 2620 | if (mem.eql(u8, inst.asm_source, "svc #0")) { |
| ... | ... | @@ -2646,7 +2646,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2646 | 2646 | const reg = parseRegName(reg_name) orelse |
| 2647 | 2647 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2648 | 2648 | const arg = try self.resolveInst(inst.args[i]); |
| 2649 | try self.genSetReg(inst.base.src, reg, arg); | |
| 2649 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2650 | 2650 | } |
| 2651 | 2651 | |
| 2652 | 2652 | if (mem.eql(u8, inst.asm_source, "ecall")) { |
| ... | ... | @@ -2676,7 +2676,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2676 | 2676 | const reg = parseRegName(reg_name) orelse |
| 2677 | 2677 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2678 | 2678 | const arg = try self.resolveInst(inst.args[i]); |
| 2679 | try self.genSetReg(inst.base.src, reg, arg); | |
| 2679 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2680 | 2680 | } |
| 2681 | 2681 | |
| 2682 | 2682 | if (mem.eql(u8, inst.asm_source, "syscall")) { |
| ... | ... | @@ -2738,7 +2738,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2738 | 2738 | fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void { |
| 2739 | 2739 | switch (loc) { |
| 2740 | 2740 | .none => return, |
| 2741 | .register => |reg| return self.genSetReg(src, reg, val), | |
| 2741 | .register => |reg| return self.genSetReg(src, ty, reg, val), | |
| 2742 | 2742 | .stack_offset => |off| return self.genSetStack(src, ty, off, val), |
| 2743 | 2743 | .memory => { |
| 2744 | 2744 | return self.fail(src, "TODO implement setRegOrMem for memory", .{}); |
| ... | ... | @@ -2773,7 +2773,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2773 | 2773 | return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); |
| 2774 | 2774 | }, |
| 2775 | 2775 | .immediate => { |
| 2776 | const reg = try self.copyToTmpRegister(src, mcv); | |
| 2776 | const reg = try self.copyToTmpRegister(src, ty, mcv); | |
| 2777 | 2777 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); |
| 2778 | 2778 | }, |
| 2779 | 2779 | .embedded_in_code => |code_offset| { |
| ... | ... | @@ -2787,7 +2787,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2787 | 2787 | 1, 4 => { |
| 2788 | 2788 | const offset = if (math.cast(u12, adj_off)) |imm| blk: { |
| 2789 | 2789 | break :blk Instruction.Offset.imm(imm); |
| 2790 | } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0); | |
| 2790 | } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); | |
| 2791 | 2791 | const str = switch (abi_size) { |
| 2792 | 2792 | 1 => Instruction.strb, |
| 2793 | 2793 | 4 => Instruction.str, |
| ... | ... | @@ -2802,7 +2802,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2802 | 2802 | 2 => { |
| 2803 | 2803 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 2804 | 2804 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 2805 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); | |
| 2805 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off })); | |
| 2806 | 2806 | |
| 2807 | 2807 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{ |
| 2808 | 2808 | .offset = offset, |
| ... | ... | @@ -2819,7 +2819,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2819 | 2819 | if (stack_offset == off) |
| 2820 | 2820 | return; // Copy stack variable to itself; nothing to do. |
| 2821 | 2821 | |
| 2822 | const reg = try self.copyToTmpRegister(src, mcv); | |
| 2822 | const reg = try self.copyToTmpRegister(src, ty, mcv); | |
| 2823 | 2823 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); |
| 2824 | 2824 | }, |
| 2825 | 2825 | }, |
| ... | ... | @@ -2908,7 +2908,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2908 | 2908 | if (stack_offset == off) |
| 2909 | 2909 | return; // Copy stack variable to itself; nothing to do. |
| 2910 | 2910 | |
| 2911 | const reg = try self.copyToTmpRegister(src, mcv); | |
| 2911 | const reg = try self.copyToTmpRegister(src, ty, mcv); | |
| 2912 | 2912 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); |
| 2913 | 2913 | }, |
| 2914 | 2914 | }, |
| ... | ... | @@ -2936,7 +2936,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2936 | 2936 | return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); |
| 2937 | 2937 | }, |
| 2938 | 2938 | .immediate => { |
| 2939 | const reg = try self.copyToTmpRegister(src, mcv); | |
| 2939 | const reg = try self.copyToTmpRegister(src, ty, mcv); | |
| 2940 | 2940 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); |
| 2941 | 2941 | }, |
| 2942 | 2942 | .embedded_in_code => |code_offset| { |
| ... | ... | @@ -2951,7 +2951,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2951 | 2951 | const offset = if (math.cast(i9, adj_off)) |imm| |
| 2952 | 2952 | Instruction.LoadStoreOffset.imm_post_index(-imm) |
| 2953 | 2953 | else |_| |
| 2954 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); | |
| 2954 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off })); | |
| 2955 | 2955 | const rn: Register = switch (arch) { |
| 2956 | 2956 | .aarch64, .aarch64_be => .x29, |
| 2957 | 2957 | .aarch64_32 => .w29, |
| ... | ... | @@ -2972,7 +2972,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2972 | 2972 | if (stack_offset == off) |
| 2973 | 2973 | return; // Copy stack variable to itself; nothing to do. |
| 2974 | 2974 | |
| 2975 | const reg = try self.copyToTmpRegister(src, mcv); | |
| 2975 | const reg = try self.copyToTmpRegister(src, ty, mcv); | |
| 2976 | 2976 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); |
| 2977 | 2977 | }, |
| 2978 | 2978 | }, |
| ... | ... | @@ -2980,7 +2980,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2980 | 2980 | } |
| 2981 | 2981 | } |
| 2982 | 2982 | |
| 2983 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { | |
| 2983 | fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void { | |
| 2984 | 2984 | switch (arch) { |
| 2985 | 2985 | .arm, .armeb => switch (mcv) { |
| 2986 | 2986 | .dead => unreachable, |
| ... | ... | @@ -2991,7 +2991,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2991 | 2991 | if (!self.wantSafety()) |
| 2992 | 2992 | return; // The already existing value will do just fine. |
| 2993 | 2993 | // Write the debug undefined value. |
| 2994 | return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); | |
| 2994 | return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }); | |
| 2995 | 2995 | }, |
| 2996 | 2996 | .compare_flags_unsigned, |
| 2997 | 2997 | .compare_flags_signed, |
| ... | ... | @@ -3056,21 +3056,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3056 | 3056 | .memory => |addr| { |
| 3057 | 3057 | // The value is in memory at a hard-coded address. |
| 3058 | 3058 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 3059 | try self.genSetReg(src, reg, .{ .immediate = addr }); | |
| 3059 | try self.genSetReg(src, ty, reg, .{ .immediate = addr }); | |
| 3060 | 3060 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); |
| 3061 | 3061 | }, |
| 3062 | 3062 | .stack_offset => |unadjusted_off| { |
| 3063 | 3063 | // TODO: maybe addressing from sp instead of fp |
| 3064 | // TODO: supply type information to genSetReg as we do to genSetStack | |
| 3065 | // const abi_size = ty.abiSize(self.target.*); | |
| 3066 | const abi_size = 4; | |
| 3064 | const abi_size = ty.abiSize(self.target.*); | |
| 3067 | 3065 | const adj_off = unadjusted_off + abi_size; |
| 3068 | 3066 | |
| 3069 | 3067 | switch (abi_size) { |
| 3070 | 3068 | 1, 4 => { |
| 3071 | 3069 | const offset = if (adj_off <= math.maxInt(u12)) blk: { |
| 3072 | 3070 | break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); |
| 3073 | } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0); | |
| 3071 | } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); | |
| 3074 | 3072 | const ldr = switch (abi_size) { |
| 3075 | 3073 | 1 => Instruction.ldrb, |
| 3076 | 3074 | 4 => Instruction.ldr, |
| ... | ... | @@ -3085,7 +3083,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3085 | 3083 | 2 => { |
| 3086 | 3084 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 3087 | 3085 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 3088 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); | |
| 3086 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off })); | |
| 3089 | 3087 | |
| 3090 | 3088 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{ |
| 3091 | 3089 | .offset = offset, |
| ... | ... | @@ -3107,8 +3105,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3107 | 3105 | return; // The already existing value will do just fine. |
| 3108 | 3106 | // Write the debug undefined value. |
| 3109 | 3107 | switch (reg.size()) { |
| 3110 | 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), | |
| 3111 | 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), | |
| 3108 | 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }), | |
| 3109 | 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), | |
| 3112 | 3110 | else => unreachable, // unexpected register size |
| 3113 | 3111 | } |
| 3114 | 3112 | }, |
| ... | ... | @@ -3221,7 +3219,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3221 | 3219 | } else { |
| 3222 | 3220 | // The value is in memory at a hard-coded address. |
| 3223 | 3221 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 3224 | try self.genSetReg(src, reg, .{ .immediate = addr }); | |
| 3222 | try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr }); | |
| 3225 | 3223 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32()); |
| 3226 | 3224 | } |
| 3227 | 3225 | }, |
| ... | ... | @@ -3236,7 +3234,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3236 | 3234 | if (!self.wantSafety()) |
| 3237 | 3235 | return; // The already existing value will do just fine. |
| 3238 | 3236 | // Write the debug undefined value. |
| 3239 | return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); | |
| 3237 | return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); | |
| 3240 | 3238 | }, |
| 3241 | 3239 | .immediate => |unsigned_x| { |
| 3242 | 3240 | const x = @bitCast(i64, unsigned_x); |
| ... | ... | @@ -3261,7 +3259,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3261 | 3259 | .memory => |addr| { |
| 3262 | 3260 | // The value is in memory at a hard-coded address. |
| 3263 | 3261 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 3264 | try self.genSetReg(src, reg, .{ .immediate = addr }); | |
| 3262 | try self.genSetReg(src, ty, reg, .{ .immediate = addr }); | |
| 3265 | 3263 | |
| 3266 | 3264 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32()); |
| 3267 | 3265 | // LOAD imm=[i12 offset = 0], rs1 = |
| ... | ... | @@ -3280,10 +3278,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3280 | 3278 | return; // The already existing value will do just fine. |
| 3281 | 3279 | // Write the debug undefined value. |
| 3282 | 3280 | switch (reg.size()) { |
| 3283 | 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }), | |
| 3284 | 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }), | |
| 3285 | 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), | |
| 3286 | 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), | |
| 3281 | 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }), | |
| 3282 | 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }), | |
| 3283 | 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }), | |
| 3284 | 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), | |
| 3287 | 3285 | else => unreachable, |
| 3288 | 3286 | } |
| 3289 | 3287 | }, |
| ... | ... | @@ -3497,7 +3495,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3497 | 3495 | assert(id3 != 4 and id3 != 5); |
| 3498 | 3496 | |
| 3499 | 3497 | // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. |
| 3500 | try self.genSetReg(src, reg, MCValue{ .immediate = x }); | |
| 3498 | try self.genSetReg(src, ty, reg, MCValue{ .immediate = x }); | |
| 3501 | 3499 | |
| 3502 | 3500 | // Now, the register contains the address of the value to load into it |
| 3503 | 3501 | // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant. |
| ... | ... | @@ -3596,7 +3594,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3596 | 3594 | // This immediate is unsigned. |
| 3597 | 3595 | const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); |
| 3598 | 3596 | if (imm >= math.maxInt(U)) { |
| 3599 | return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) }; | |
| 3597 | return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) }; | |
| 3600 | 3598 | } |
| 3601 | 3599 | }, |
| 3602 | 3600 | else => {}, |
| ... | ... | @@ -3710,17 +3708,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3710 | 3708 | for (param_types) |ty, i| { |
| 3711 | 3709 | switch (ty.zigTypeTag()) { |
| 3712 | 3710 | .Bool, .Int => { |
| 3713 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 3714 | if (next_int_reg >= c_abi_int_param_regs.len) { | |
| 3715 | result.args[i] = .{ .stack_offset = next_stack_offset }; | |
| 3716 | next_stack_offset += param_size; | |
| 3711 | if (!ty.hasCodeGenBits()) { | |
| 3712 | assert(cc != .C); | |
| 3713 | result.args[i] = .{ .none = {} }; | |
| 3717 | 3714 | } else { |
| 3718 | const aliased_reg = registerAlias( | |
| 3719 | c_abi_int_param_regs[next_int_reg], | |
| 3720 | param_size, | |
| 3721 | ); | |
| 3722 | result.args[i] = .{ .register = aliased_reg }; | |
| 3723 | next_int_reg += 1; | |
| 3715 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 3716 | if (next_int_reg >= c_abi_int_param_regs.len) { | |
| 3717 | result.args[i] = .{ .stack_offset = next_stack_offset }; | |
| 3718 | next_stack_offset += param_size; | |
| 3719 | } else { | |
| 3720 | const aliased_reg = registerAlias( | |
| 3721 | c_abi_int_param_regs[next_int_reg], | |
| 3722 | param_size, | |
| 3723 | ); | |
| 3724 | result.args[i] = .{ .register = aliased_reg }; | |
| 3725 | next_int_reg += 1; | |
| 3726 | } | |
| 3724 | 3727 | } |
| 3725 | 3728 | }, |
| 3726 | 3729 | else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), |
src/codegen/wasm.zig+21-9| ... | ... | @@ -161,15 +161,19 @@ pub const Context = struct { |
| 161 | 161 | pub fn gen(self: *Context) InnerError!void { |
| 162 | 162 | assert(self.code.items.len == 0); |
| 163 | 163 | try self.genFunctype(); |
| 164 | const writer = self.code.writer(); | |
| 165 | ||
| 166 | // Reserve space to write the size after generating the code as well as space for locals count | |
| 167 | try self.code.resize(10); | |
| 168 | 164 | |
| 169 | 165 | // Write instructions |
| 170 | 166 | // TODO: check for and handle death of instructions |
| 171 | 167 | const tv = self.decl.typed_value.most_recent.typed_value; |
| 172 | const mod_fn = tv.val.castTag(.function).?.data; | |
| 168 | const mod_fn = blk: { | |
| 169 | if (tv.val.castTag(.function)) |func| break :blk func.data; | |
| 170 | if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions | |
| 171 | return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()}); | |
| 172 | }; | |
| 173 | ||
| 174 | // Reserve space to write the size after generating the code as well as space for locals count | |
| 175 | try self.code.resize(10); | |
| 176 | ||
| 173 | 177 | try self.genBody(mod_fn.body); |
| 174 | 178 | |
| 175 | 179 | // finally, write our local types at the 'offset' position |
| ... | ... | @@ -189,6 +193,7 @@ pub const Context = struct { |
| 189 | 193 | } |
| 190 | 194 | } |
| 191 | 195 | |
| 196 | const writer = self.code.writer(); | |
| 192 | 197 | try writer.writeByte(wasm.opcode(.end)); |
| 193 | 198 | |
| 194 | 199 | // Fill in the size of the generated code to the reserved space at the |
| ... | ... | @@ -239,9 +244,16 @@ pub const Context = struct { |
| 239 | 244 | |
| 240 | 245 | fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue { |
| 241 | 246 | const func_inst = inst.func.castTag(.constant).?; |
| 242 | const func = func_inst.val.castTag(.function).?.data; | |
| 243 | const target = func.owner_decl; | |
| 244 | const target_ty = target.typed_value.most_recent.typed_value.ty; | |
| 247 | const func_val = inst.func.value().?; | |
| 248 | ||
| 249 | const target = blk: { | |
| 250 | if (func_val.castTag(.function)) |func| { | |
| 251 | break :blk func.data.owner_decl; | |
| 252 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { | |
| 253 | break :blk ext_fn.data; | |
| 254 | } | |
| 255 | return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()}); | |
| 256 | }; | |
| 245 | 257 | |
| 246 | 258 | for (inst.args) |arg| { |
| 247 | 259 | const arg_val = self.resolveInst(arg); |
| ... | ... | @@ -495,7 +507,7 @@ pub const Context = struct { |
| 495 | 507 | } |
| 496 | 508 | |
| 497 | 509 | fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue { |
| 498 | // of operand has codegen bits we should break with a value | |
| 510 | // if operand has codegen bits we should break with a value | |
| 499 | 511 | if (br.operand.ty.hasCodeGenBits()) { |
| 500 | 512 | const operand = self.resolveInst(br.operand); |
| 501 | 513 | try self.emitWValue(operand); |
src/link/Wasm.zig+90-14| ... | ... | @@ -33,9 +33,18 @@ base: link.File, |
| 33 | 33 | |
| 34 | 34 | /// List of all function Decls to be written to the output file. The index of |
| 35 | 35 | /// each Decl in this list at the time of writing the binary is used as the |
| 36 | /// function index. | |
| 36 | /// function index. In the event where ext_funcs' size is not 0, the index of | |
| 37 | /// each function is added on top of the ext_funcs' length. | |
| 37 | 38 | /// TODO: can/should we access some data structure in Module directly? |
| 38 | 39 | funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 40 | /// List of all extern function Decls to be written to the `import` section of the | |
| 41 | /// wasm binary. The positin in the list defines the function index | |
| 42 | ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, | |
| 43 | /// When importing objects from the host environment, a name must be supplied. | |
| 44 | /// LLVM uses "env" by default when none is given. This would be a good default for Zig | |
| 45 | /// to support existing code. | |
| 46 | /// TODO: Allow setting this through a flag? | |
| 47 | host_name: []const u8 = "env", | |
| 39 | 48 | |
| 40 | 49 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { |
| 41 | 50 | assert(options.object_format == .wasm); |
| ... | ... | @@ -76,7 +85,13 @@ pub fn deinit(self: *Wasm) void { |
| 76 | 85 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); |
| 77 | 86 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); |
| 78 | 87 | } |
| 88 | for (self.ext_funcs.items) |decl| { | |
| 89 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | |
| 90 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | |
| 91 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | |
| 92 | } | |
| 79 | 93 | self.funcs.deinit(self.base.allocator); |
| 94 | self.ext_funcs.deinit(self.base.allocator); | |
| 80 | 95 | } |
| 81 | 96 | |
| 82 | 97 | // Generate code for the Decl, storing it in memory to be later written to |
| ... | ... | @@ -85,8 +100,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 85 | 100 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 86 | 101 | if (typed_value.ty.zigTypeTag() != .Fn) |
| 87 | 102 | return error.TODOImplementNonFnDeclsForWasm; |
| 88 | if (typed_value.val.tag() == .extern_fn) | |
| 89 | return error.TODOImplementExternFnDeclsForWasm; | |
| 90 | 103 | |
| 91 | 104 | if (decl.fn_link.wasm) |*fn_data| { |
| 92 | 105 | fn_data.functype.items.len = 0; |
| ... | ... | @@ -94,7 +107,12 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 94 | 107 | fn_data.idx_refs.items.len = 0; |
| 95 | 108 | } else { |
| 96 | 109 | decl.fn_link.wasm = .{}; |
| 97 | try self.funcs.append(self.base.allocator, decl); | |
| 110 | // dependent on function type, appends it to the correct list | |
| 111 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | |
| 112 | .function => try self.funcs.append(self.base.allocator, decl), | |
| 113 | .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), | |
| 114 | else => return error.TODOImplementNonFnDeclsForWasm, | |
| 115 | } | |
| 98 | 116 | } |
| 99 | 117 | const fn_data = &decl.fn_link.wasm.?; |
| 100 | 118 | |
| ... | ... | @@ -143,7 +161,12 @@ pub fn updateDeclExports( |
| 143 | 161 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 144 | 162 | // TODO: remove this assert when non-function Decls are implemented |
| 145 | 163 | assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); |
| 146 | _ = self.funcs.swapRemove(self.getFuncidx(decl).?); | |
| 164 | const func_idx = self.getFuncidx(decl).?; | |
| 165 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | |
| 166 | .function => _ = self.funcs.swapRemove(func_idx), | |
| 167 | .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), | |
| 168 | else => unreachable, | |
| 169 | } | |
| 147 | 170 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); |
| 148 | 171 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); |
| 149 | 172 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); |
| ... | ... | @@ -172,15 +195,46 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 172 | 195 | // Type section |
| 173 | 196 | { |
| 174 | 197 | const header_offset = try reserveVecSectionHeader(file); |
| 175 | for (self.funcs.items) |decl| { | |
| 176 | try file.writeAll(decl.fn_link.wasm.?.functype.items); | |
| 177 | } | |
| 198 | ||
| 199 | // extern functions are defined in the wasm binary first through the `import` | |
| 200 | // section, so define their func types first | |
| 201 | for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); | |
| 202 | for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); | |
| 203 | ||
| 178 | 204 | try writeVecSectionHeader( |
| 179 | 205 | file, |
| 180 | 206 | header_offset, |
| 181 | 207 | .type, |
| 182 | 208 | @intCast(u32, (try file.getPos()) - header_offset - header_size), |
| 183 | @intCast(u32, self.funcs.items.len), | |
| 209 | @intCast(u32, self.ext_funcs.items.len + self.funcs.items.len), | |
| 210 | ); | |
| 211 | } | |
| 212 | ||
| 213 | // Import section | |
| 214 | { | |
| 215 | // TODO: implement non-functions imports | |
| 216 | const header_offset = try reserveVecSectionHeader(file); | |
| 217 | const writer = file.writer(); | |
| 218 | for (self.ext_funcs.items) |decl, typeidx| { | |
| 219 | try leb.writeULEB128(writer, @intCast(u32, self.host_name.len)); | |
| 220 | try writer.writeAll(self.host_name); | |
| 221 | ||
| 222 | // wasm requires the length of the import name with no null-termination | |
| 223 | const decl_len = mem.len(decl.name); | |
| 224 | try leb.writeULEB128(writer, @intCast(u32, decl_len)); | |
| 225 | try writer.writeAll(decl.name[0..decl_len]); | |
| 226 | ||
| 227 | // emit kind and the function type | |
| 228 | try writer.writeByte(wasm.externalKind(.function)); | |
| 229 | try leb.writeULEB128(writer, @intCast(u32, typeidx)); | |
| 230 | } | |
| 231 | ||
| 232 | try writeVecSectionHeader( | |
| 233 | file, | |
| 234 | header_offset, | |
| 235 | .import, | |
| 236 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | |
| 237 | @intCast(u32, self.ext_funcs.items.len), | |
| 184 | 238 | ); |
| 185 | 239 | } |
| 186 | 240 | |
| ... | ... | @@ -188,7 +242,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 188 | 242 | { |
| 189 | 243 | const header_offset = try reserveVecSectionHeader(file); |
| 190 | 244 | const writer = file.writer(); |
| 191 | for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx)); | |
| 245 | for (self.funcs.items) |_, typeidx| { | |
| 246 | const func_idx = @intCast(u32, self.getFuncIdxOffset() + typeidx); | |
| 247 | try leb.writeULEB128(writer, func_idx); | |
| 248 | } | |
| 249 | ||
| 192 | 250 | try writeVecSectionHeader( |
| 193 | 251 | file, |
| 194 | 252 | header_offset, |
| ... | ... | @@ -212,7 +270,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 212 | 270 | switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { |
| 213 | 271 | .Fn => { |
| 214 | 272 | // Type of the export |
| 215 | try writer.writeByte(0x00); | |
| 273 | try writer.writeByte(wasm.externalKind(.function)); | |
| 216 | 274 | // Exported function index |
| 217 | 275 | try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); |
| 218 | 276 | }, |
| ... | ... | @@ -523,13 +581,31 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 523 | 581 | } |
| 524 | 582 | |
| 525 | 583 | /// Get the current index of a given Decl in the function list |
| 526 | /// TODO: we could maintain a hash map to potentially make this | |
| 584 | /// This will correctly provide the index, regardless whether the function is extern or not | |
| 585 | /// TODO: we could maintain a hash map to potentially make this simpler | |
| 527 | 586 | fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { |
| 528 | return for (self.funcs.items) |func, idx| { | |
| 529 | if (func == decl) break @intCast(u32, idx); | |
| 587 | var offset: u32 = 0; | |
| 588 | const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) { | |
| 589 | .function => blk: { | |
| 590 | // when the target is a regular function, we have to calculate | |
| 591 | // the offset of where the index starts | |
| 592 | offset += self.getFuncIdxOffset(); | |
| 593 | break :blk self.funcs.items; | |
| 594 | }, | |
| 595 | .extern_fn => self.ext_funcs.items, | |
| 596 | else => return null, | |
| 597 | }; | |
| 598 | return for (slice) |func, idx| { | |
| 599 | if (func == decl) break @intCast(u32, offset + idx); | |
| 530 | 600 | } else null; |
| 531 | 601 | } |
| 532 | 602 | |
| 603 | /// Based on the size of `ext_funcs` returns the | |
| 604 | /// offset of the function indices | |
| 605 | fn getFuncIdxOffset(self: Wasm) u32 { | |
| 606 | return @intCast(u32, self.ext_funcs.items.len); | |
| 607 | } | |
| 608 | ||
| 533 | 609 | fn reserveVecSectionHeader(file: fs.File) !u64 { |
| 534 | 610 | // section id + fixed leb contents size + fixed leb vector length |
| 535 | 611 | const header_size = 1 + 5 + 5; |
src/stage1.zig+3-1| ... | ... | @@ -278,7 +278,9 @@ export fn stage2_attach_segfault_handler() void { |
| 278 | 278 | // ABI warning |
| 279 | 279 | export fn stage2_progress_create() *std.Progress { |
| 280 | 280 | const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); |
| 281 | ptr.* = std.Progress{}; | |
| 281 | // If the terminal is dumb, we dont want to show the user all the | |
| 282 | // output. | |
| 283 | ptr.* = std.Progress{ .dont_print_on_dumb = true }; | |
| 282 | 284 | return ptr; |
| 283 | 285 | } |
| 284 | 286 |
src/stage1/tokenizer.cpp+2-2| ... | ... | @@ -1447,7 +1447,7 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 1447 | 1447 | tokenize_error(&t, "unterminated string"); |
| 1448 | 1448 | break; |
| 1449 | 1449 | } else if (t.cur_tok->id == TokenIdCharLiteral) { |
| 1450 | tokenize_error(&t, "unterminated character literal"); | |
| 1450 | tokenize_error(&t, "unterminated Unicode code point literal"); | |
| 1451 | 1451 | break; |
| 1452 | 1452 | } else { |
| 1453 | 1453 | zig_unreachable(); |
| ... | ... | @@ -1456,7 +1456,7 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 1456 | 1456 | case TokenizeStateCharLiteral: |
| 1457 | 1457 | case TokenizeStateCharLiteralEnd: |
| 1458 | 1458 | case TokenizeStateCharLiteralUnicode: |
| 1459 | tokenize_error(&t, "unterminated character literal"); | |
| 1459 | tokenize_error(&t, "unterminated Unicode code point literal"); | |
| 1460 | 1460 | break; |
| 1461 | 1461 | case TokenizeStateSymbol: |
| 1462 | 1462 | case TokenizeStateZero: |
test/stage2/test.zig+22| ... | ... | @@ -1397,4 +1397,26 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1397 | 1397 | "", |
| 1398 | 1398 | ); |
| 1399 | 1399 | } |
| 1400 | ||
| 1401 | { | |
| 1402 | var case = ctx.exe("passing u0 to function", linux_x64); | |
| 1403 | case.addCompareOutput( | |
| 1404 | \\export fn _start() noreturn { | |
| 1405 | \\ doNothing(0); | |
| 1406 | \\ exit(); | |
| 1407 | \\} | |
| 1408 | \\fn doNothing(arg: u0) void {} | |
| 1409 | \\fn exit() noreturn { | |
| 1410 | \\ asm volatile ("syscall" | |
| 1411 | \\ : | |
| 1412 | \\ : [number] "{rax}" (231), | |
| 1413 | \\ [arg1] "{rdi}" (0) | |
| 1414 | \\ : "rcx", "r11", "memory" | |
| 1415 | \\ ); | |
| 1416 | \\ unreachable; | |
| 1417 | \\} | |
| 1418 | , | |
| 1419 | "", | |
| 1420 | ); | |
| 1421 | } | |
| 1400 | 1422 | } |