authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-10 16:11:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-10 16:11:10-07:00
log6e6c68d88909f008afc706949effe38c470a055d
treeed4175263a31adf21dffe28af15717f5b54c2950
parent725dbf295e82624477aa4d95ba71ec02d9c94978
parent43fba5ea83849ec901bb2cd4f98bd0222f51f7f6

Merge remote-tracking branch 'origin/master' into wrangle-writer-buffering


368 files changed, 7860 insertions(+), 29114 deletions(-)

CMakeLists.txt-39
......@@ -537,45 +537,6 @@ set(ZIG_STAGE2_SOURCES
537537 src/Value.zig
538538 src/Zcu.zig
539539 src/Zcu/PerThread.zig
540 src/arch/aarch64/CodeGen.zig
541 src/arch/aarch64/Emit.zig
542 src/arch/aarch64/Mir.zig
543 src/arch/aarch64/abi.zig
544 src/arch/aarch64/bits.zig
545 src/arch/arm/CodeGen.zig
546 src/arch/arm/Emit.zig
547 src/arch/arm/Mir.zig
548 src/arch/arm/abi.zig
549 src/arch/arm/bits.zig
550 src/arch/powerpc/CodeGen.zig
551 src/arch/riscv64/abi.zig
552 src/arch/riscv64/bits.zig
553 src/arch/riscv64/CodeGen.zig
554 src/arch/riscv64/Emit.zig
555 src/arch/riscv64/encoding.zig
556 src/arch/riscv64/Lower.zig
557 src/arch/riscv64/Mir.zig
558 src/arch/riscv64/mnem.zig
559 src/arch/sparc64/CodeGen.zig
560 src/arch/sparc64/Emit.zig
561 src/arch/sparc64/Mir.zig
562 src/arch/sparc64/abi.zig
563 src/arch/sparc64/bits.zig
564 src/arch/wasm/CodeGen.zig
565 src/arch/wasm/Emit.zig
566 src/arch/wasm/Mir.zig
567 src/arch/wasm/abi.zig
568 src/arch/x86/bits.zig
569 src/arch/x86_64/CodeGen.zig
570 src/arch/x86_64/Disassembler.zig
571 src/arch/x86_64/Emit.zig
572 src/arch/x86_64/Encoding.zig
573 src/arch/x86_64/Lower.zig
574 src/arch/x86_64/Mir.zig
575 src/arch/x86_64/abi.zig
576 src/arch/x86_64/bits.zig
577 src/arch/x86_64/encoder.zig
578 src/arch/x86_64/encodings.zon
579540 src/clang.zig
580541 src/clang_options.zig
581542 src/clang_options_data.zig
build.zig+34-3
......@@ -415,7 +415,18 @@ pub fn build(b: *std.Build) !void {
415415 test_step.dependOn(check_fmt);
416416
417417 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
418 try tests.addCases(b, test_cases_step, test_filters, test_target_filters, target, .{
418 try tests.addCases(b, test_cases_step, target, .{
419 .test_filters = test_filters,
420 .test_target_filters = test_target_filters,
421 .skip_non_native = skip_non_native,
422 .skip_freebsd = skip_freebsd,
423 .skip_netbsd = skip_netbsd,
424 .skip_windows = skip_windows,
425 .skip_macos = skip_macos,
426 .skip_linux = skip_linux,
427 .skip_llvm = skip_llvm,
428 .skip_libc = skip_libc,
429 }, .{
419430 .skip_translate_c = skip_translate_c,
420431 .skip_run_translated_c = skip_run_translated_c,
421432 }, .{
......@@ -439,6 +450,7 @@ pub fn build(b: *std.Build) !void {
439450 .desc = "Run the behavior tests",
440451 .optimize_modes = optimization_modes,
441452 .include_paths = &.{},
453 .windows_libs = &.{},
442454 .skip_single_threaded = skip_single_threaded,
443455 .skip_non_native = skip_non_native,
444456 .skip_freebsd = skip_freebsd,
......@@ -448,8 +460,8 @@ pub fn build(b: *std.Build) !void {
448460 .skip_linux = skip_linux,
449461 .skip_llvm = skip_llvm,
450462 .skip_libc = skip_libc,
451 // 2923515904 was observed on an x86_64-linux-gnu host.
452 .max_rss = 3100000000,
463 // 3888779264 was observed on an x86_64-linux-gnu host.
464 .max_rss = 4000000000,
453465 }));
454466
455467 test_modules_step.dependOn(tests.addModuleTests(b, .{
......@@ -461,6 +473,7 @@ pub fn build(b: *std.Build) !void {
461473 .desc = "Run the @cImport tests",
462474 .optimize_modes = optimization_modes,
463475 .include_paths = &.{"test/c_import"},
476 .windows_libs = &.{},
464477 .skip_single_threaded = true,
465478 .skip_non_native = skip_non_native,
466479 .skip_freebsd = skip_freebsd,
......@@ -481,6 +494,7 @@ pub fn build(b: *std.Build) !void {
481494 .desc = "Run the compiler_rt tests",
482495 .optimize_modes = optimization_modes,
483496 .include_paths = &.{},
497 .windows_libs = &.{},
484498 .skip_single_threaded = true,
485499 .skip_non_native = skip_non_native,
486500 .skip_freebsd = skip_freebsd,
......@@ -502,6 +516,7 @@ pub fn build(b: *std.Build) !void {
502516 .desc = "Run the zigc tests",
503517 .optimize_modes = optimization_modes,
504518 .include_paths = &.{},
519 .windows_libs = &.{},
505520 .skip_single_threaded = true,
506521 .skip_non_native = skip_non_native,
507522 .skip_freebsd = skip_freebsd,
......@@ -523,6 +538,12 @@ pub fn build(b: *std.Build) !void {
523538 .desc = "Run the standard library tests",
524539 .optimize_modes = optimization_modes,
525540 .include_paths = &.{},
541 .windows_libs = &.{
542 "advapi32",
543 "crypt32",
544 "iphlpapi",
545 "ws2_32",
546 },
526547 .skip_single_threaded = skip_single_threaded,
527548 .skip_non_native = skip_non_native,
528549 .skip_freebsd = skip_freebsd,
......@@ -720,6 +741,12 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu
720741 compiler_mod.addImport("aro", aro_mod);
721742 compiler_mod.addImport("aro_translate_c", aro_translate_c_mod);
722743
744 if (options.target.result.os.tag == .windows) {
745 compiler_mod.linkSystemLibrary("advapi32", .{});
746 compiler_mod.linkSystemLibrary("crypt32", .{});
747 compiler_mod.linkSystemLibrary("ws2_32", .{});
748 }
749
723750 return compiler_mod;
724751}
725752
......@@ -1417,6 +1444,10 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
14171444 }),
14181445 });
14191446
1447 if (b.graph.host.result.os.tag == .windows) {
1448 doctest_exe.root_module.linkSystemLibrary("advapi32", .{});
1449 }
1450
14201451 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
14211452 std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
14221453 b.build_root, @errorName(err),
ci/x86_64-linux-debug-llvm.sh+1-1
......@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460"
1212PREFIX="$HOME/deps/$CACHE_BASENAME"
1313ZIG="$PREFIX/bin/zig"
1414
15export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH"
15export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH"
1616
1717# Make the `zig version` number consistent.
1818# This will affect the cmake command below.
ci/x86_64-linux-debug.sh+1-1
......@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460"
1212PREFIX="$HOME/deps/$CACHE_BASENAME"
1313ZIG="$PREFIX/bin/zig"
1414
15export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH"
15export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH"
1616
1717# Make the `zig version` number consistent.
1818# This will affect the cmake command below.
ci/x86_64-linux-release.sh+1-1
......@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460"
1212PREFIX="$HOME/deps/$CACHE_BASENAME"
1313ZIG="$PREFIX/bin/zig"
1414
15export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH"
15export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH"
1616
1717# Make the `zig version` number consistent.
1818# This will affect the cmake command below.
doc/langref.html.in+10-88
......@@ -374,7 +374,8 @@
374374 <p>
375375 Most of the time, it is more appropriate to write to stderr rather than stdout, and
376376 whether or not the message is successfully written to the stream is irrelevant.
377 For this common case, there is a simpler API:
377 Also, formatted printing often comes in handy. For this common case,
378 there is a simpler API:
378379 </p>
379380 {#code|hello_again.zig#}
380381
......@@ -3842,37 +3843,6 @@ void do_a_thing(struct Foo *foo) {
38423843 {#header_close#}
38433844 {#header_close#}
38443845
3845 {#header_open|usingnamespace#}
3846 <p>
3847 {#syntax#}usingnamespace{#endsyntax#} is a declaration that mixes all the public
3848 declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#},
3849 or {#link|opaque#}, into the namespace:
3850 </p>
3851 {#code|test_usingnamespace.zig#}
3852
3853 <p>
3854 {#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public
3855 API of a file or package. For example, one might have <code class="file">c.zig</code> with all of the
3856 {#link|C imports|Import from C Header File#}:
3857 </p>
3858 {#syntax_block|zig|c.zig#}
3859pub usingnamespace @cImport({
3860 @cInclude("epoxy/gl.h");
3861 @cInclude("GLFW/glfw3.h");
3862 @cDefine("STBI_ONLY_PNG", "");
3863 @cDefine("STBI_NO_STDIO", "");
3864 @cInclude("stb_image.h");
3865});
3866 {#end_syntax_block#}
3867 <p>
3868 The above example demonstrates using {#syntax#}pub{#endsyntax#} to qualify the
3869 {#syntax#}usingnamespace{#endsyntax#} additionally makes the imported declarations
3870 {#syntax#}pub{#endsyntax#}. This can be used to forward declarations, giving precise control
3871 over what declarations a given file exposes.
3872 </p>
3873 {#header_close#}
3874
3875
38763846 {#header_open|comptime#}
38773847 <p>
38783848 Zig places importance on the concept of whether an expression is known at compile-time.
......@@ -4279,16 +4249,9 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
42794249 {#header_close#}
42804250
42814251 {#header_open|Async Functions#}
4282 <p>Async functions regressed with the release of 0.11.0. Their future in
4283 the Zig language is unclear due to multiple unsolved problems:</p>
4284 <ul>
4285 <li>LLVM's lack of ability to optimize them.</li>
4286 <li>Third-party debuggers' lack of ability to debug them.</li>
4287 <li><a href="https://github.com/ziglang/zig/issues/5913">The cancellation problem</a>.</li>
4288 <li>Async function pointers preventing the stack size from being known.</li>
4289 </ul>
4290 <p>These problems are surmountable, but it will take time. The Zig team
4291 is currently focused on other priorities.</p>
4252 <p>Async functions regressed with the release of 0.11.0. The current plan is to
4253 reintroduce them as a lower level primitive that powers I/O implementations.</p>
4254 <p>Tracking issue: <a href="https://github.com/ziglang/zig/issues/23446">Proposal: stackless coroutines as low-level primitives</a></p>
42924255 {#header_close#}
42934256
42944257 {#header_open|Builtin Functions|2col#}
......@@ -6552,7 +6515,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
65526515 </p>
65536516 <ul>
65546517 <li>If a call to {#syntax#}@import{#endsyntax#} is analyzed, the file being imported is analyzed.</li>
6555 <li>If a type (including a file) is analyzed, all {#syntax#}comptime{#endsyntax#}, {#syntax#}usingnamespace{#endsyntax#}, and {#syntax#}export{#endsyntax#} declarations within it are analyzed.</li>
6518 <li>If a type (including a file) is analyzed, all {#syntax#}comptime{#endsyntax#} and {#syntax#}export{#endsyntax#} declarations within it are analyzed.</li>
65566519 <li>If a type (including a file) is analyzed, and the compilation is for a {#link|test|Zig Test#}, and the module the type is within is the root module of the compilation, then all {#syntax#}test{#endsyntax#} declarations within it are also analyzed.</li>
65576520 <li>If a reference to a named declaration (i.e. a usage of it) is analyzed, the declaration being referenced is analyzed. Declarations are order-independent, so this reference may be above or below the declaration being referenced, or even in another file entirely.</li>
65586521 </ul>
......@@ -7372,29 +7335,6 @@ fn readU32Be() u32 {}
73727335 </ul>
73737336 </td>
73747337 </tr>
7375 <tr>
7376 <th scope="row">
7377 <pre>{#syntax#}async{#endsyntax#}</pre>
7378 </th>
7379 <td>
7380 {#syntax#}async{#endsyntax#} can be used before a function call to get a pointer to the function's frame when it suspends.
7381 <ul>
7382 <li>See also {#link|Async Functions#}</li>
7383 </ul>
7384 </td>
7385 </tr>
7386 <tr>
7387 <th scope="row">
7388 <pre>{#syntax#}await{#endsyntax#}</pre>
7389 </th>
7390 <td>
7391 {#syntax#}await{#endsyntax#} can be used to suspend the current function until the frame provided after the {#syntax#}await{#endsyntax#} completes.
7392 {#syntax#}await{#endsyntax#} copies the value returned from the target function's frame to the caller.
7393 <ul>
7394 <li>See also {#link|Async Functions#}</li>
7395 </ul>
7396 </td>
7397 </tr>
73987338 <tr>
73997339 <th scope="row">
74007340 <pre>{#syntax#}break{#endsyntax#}</pre>
......@@ -7812,18 +7752,6 @@ fn readU32Be() u32 {}
78127752 </ul>
78137753 </td>
78147754 </tr>
7815 <tr>
7816 <th scope="row">
7817 <pre>{#syntax#}usingnamespace{#endsyntax#}</pre>
7818 </th>
7819 <td>
7820 {#syntax#}usingnamespace{#endsyntax#} is a top-level declaration that imports all the public declarations of the operand,
7821 which must be a struct, union, or enum, into the current scope.
7822 <ul>
7823 <li>See also {#link|usingnamespace#}</li>
7824 </ul>
7825 </td>
7826 </tr>
78277755 <tr>
78287756 <th scope="row">
78297757 <pre>{#syntax#}var{#endsyntax#}</pre>
......@@ -7893,7 +7821,6 @@ ComptimeDecl <- KEYWORD_comptime Block
78937821Decl
78947822 <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
78957823 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl
7896 / KEYWORD_usingnamespace Expr SEMICOLON
78977824
78987825FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
78997826
......@@ -8006,8 +7933,7 @@ TypeExpr <- PrefixTypeOp* ErrorUnionExpr
80067933ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
80077934
80087935SuffixExpr
8009 <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
8010 / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
7936 <- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
80117937
80127938PrimaryTypeExpr
80137939 <- BUILTINIDENTIFIER FnCallArguments
......@@ -8183,7 +8109,6 @@ PrefixOp
81838109 / MINUSPERCENT
81848110 / AMPERSAND
81858111 / KEYWORD_try
8186 / KEYWORD_await
81878112
81888113PrefixTypeOp
81898114 <- QUESTIONMARK
......@@ -8404,8 +8329,6 @@ KEYWORD_and <- 'and' end_of_word
84048329KEYWORD_anyframe <- 'anyframe' end_of_word
84058330KEYWORD_anytype <- 'anytype' end_of_word
84068331KEYWORD_asm <- 'asm' end_of_word
8407KEYWORD_async <- 'async' end_of_word
8408KEYWORD_await <- 'await' end_of_word
84098332KEYWORD_break <- 'break' end_of_word
84108333KEYWORD_callconv <- 'callconv' end_of_word
84118334KEYWORD_catch <- 'catch' end_of_word
......@@ -8442,14 +8365,13 @@ KEYWORD_threadlocal <- 'threadlocal' end_of_word
84428365KEYWORD_try <- 'try' end_of_word
84438366KEYWORD_union <- 'union' end_of_word
84448367KEYWORD_unreachable <- 'unreachable' end_of_word
8445KEYWORD_usingnamespace <- 'usingnamespace' end_of_word
84468368KEYWORD_var <- 'var' end_of_word
84478369KEYWORD_volatile <- 'volatile' end_of_word
84488370KEYWORD_while <- 'while' end_of_word
84498371
84508372keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
8451 / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async
8452 / KEYWORD_await / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
8373 / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm
8374 / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
84538375 / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer
84548376 / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
84558377 / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if
......@@ -8458,7 +8380,7 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
84588380 / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
84598381 / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test
84608382 / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable
8461 / KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
8383 / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
84628384 {#end_syntax_block#}
84638385 {#header_close#}
84648386 {#header_open|Zen#}
doc/langref/bad_default_value.zig+1-1
......@@ -17,7 +17,7 @@ pub fn main() !void {
1717 .maximum = 0.20,
1818 };
1919 const category = threshold.categorize(0.90);
20 try std.io.getStdOut().writeAll(@tagName(category));
20 try std.fs.File.stdout().writeAll(@tagName(category));
2121}
2222
2323const std = @import("std");
doc/langref/hello.zig+1-2
......@@ -1,8 +1,7 @@
11const std = @import("std");
22
33pub fn main() !void {
4 const stdout = std.io.getStdOut().writer();
5 try stdout.print("Hello, {s}!\n", .{"world"});
4 try std.fs.File.stdout().writeAll("Hello, World!\n");
65}
76
87// exe=succeed
doc/langref/hello_again.zig+1-1
......@@ -1,7 +1,7 @@
11const std = @import("std");
22
33pub fn main() void {
4 std.debug.print("Hello, world!\n", .{});
4 std.debug.print("Hello, {s}!\n", .{"World"});
55}
66
77// exe=succeed
doc/langref/test_usingnamespace.zig deleted-8
......@@ -1,8 +0,0 @@
1test "using std namespace" {
2 const S = struct {
3 usingnamespace @import("std");
4 };
5 try S.testing.expect(true);
6}
7
8// test
lib/compiler/aro/aro/Compilation.zig+1-1
......@@ -1432,7 +1432,7 @@ fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u
14321432 defer buf.deinit();
14331433
14341434 const max = limit orelse std.math.maxInt(u32);
1435 file.reader().readAllArrayList(&buf, max) catch |e| switch (e) {
1435 file.deprecatedReader().readAllArrayList(&buf, max) catch |e| switch (e) {
14361436 error.StreamTooLong => if (limit == null) return e,
14371437 else => return e,
14381438 };
lib/compiler/aro/aro/Diagnostics.zig+17-27
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const assert = std.debug.assert;
23const Allocator = mem.Allocator;
34const mem = std.mem;
45const Source = @import("Source.zig");
......@@ -323,12 +324,13 @@ pub fn addExtra(
323324
324325pub fn render(comp: *Compilation, config: std.io.tty.Config) void {
325326 if (comp.diagnostics.list.items.len == 0) return;
326 var m = defaultMsgWriter(config);
327 var buffer: [1000]u8 = undefined;
328 var m = defaultMsgWriter(config, &buffer);
327329 defer m.deinit();
328330 renderMessages(comp, &m);
329331}
330pub fn defaultMsgWriter(config: std.io.tty.Config) MsgWriter {
331 return MsgWriter.init(config);
332pub fn defaultMsgWriter(config: std.io.tty.Config, buffer: []u8) MsgWriter {
333 return MsgWriter.init(config, buffer);
332334}
333335
334336pub fn renderMessages(comp: *Compilation, m: anytype) void {
......@@ -449,12 +451,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
449451 },
450452 .normalized => {
451453 const f = struct {
452 pub fn f(
453 bytes: []const u8,
454 comptime _: []const u8,
455 _: std.fmt.FormatOptions,
456 writer: anytype,
457 ) !void {
454 pub fn f(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
458455 var it: std.unicode.Utf8Iterator = .{
459456 .bytes = bytes,
460457 .i = 0,
......@@ -464,22 +461,16 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
464461 try writer.writeByte(@intCast(codepoint));
465462 } else if (codepoint < 0xFFFF) {
466463 try writer.writeAll("\\u");
467 try std.fmt.formatInt(codepoint, 16, .upper, .{
468 .fill = '0',
469 .width = 4,
470 }, writer);
464 try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 4 });
471465 } else {
472466 try writer.writeAll("\\U");
473 try std.fmt.formatInt(codepoint, 16, .upper, .{
474 .fill = '0',
475 .width = 8,
476 }, writer);
467 try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 8 });
477468 }
478469 }
479470 }
480471 }.f;
481 printRt(m, prop.msg, .{"{s}"}, .{
482 std.fmt.Formatter(f){ .data = msg.extra.normalized },
472 printRt(m, prop.msg, .{"{f}"}, .{
473 std.fmt.Formatter([]const u8, f){ .data = msg.extra.normalized },
483474 });
484475 },
485476 .none, .offset => m.write(prop.msg),
......@@ -535,32 +526,31 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind {
535526}
536527
537528const MsgWriter = struct {
538 w: *std.fs.File.Writer,
529 writer: *std.io.Writer,
539530 config: std.io.tty.Config,
540531
541532 fn init(config: std.io.tty.Config, buffer: []u8) MsgWriter {
542 std.debug.lockStdErr();
543533 return .{
544 .w = std.fs.stderr().writer(buffer),
534 .writer = std.debug.lockStderrWriter(buffer),
545535 .config = config,
546536 };
547537 }
548538
549539 pub fn deinit(m: *MsgWriter) void {
550 m.w.flush() catch {};
551 std.debug.unlockStdErr();
540 std.debug.unlockStderrWriter();
541 m.* = undefined;
552542 }
553543
554544 pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {
555 m.w.writer().print(fmt, args) catch {};
545 m.writer.print(fmt, args) catch {};
556546 }
557547
558548 fn write(m: *MsgWriter, msg: []const u8) void {
559 m.w.writer().writeAll(msg) catch {};
549 m.writer.writeAll(msg) catch {};
560550 }
561551
562552 fn setColor(m: *MsgWriter, color: std.io.tty.Color) void {
563 m.config.setColor(m.w.writer(), color) catch {};
553 m.config.setColor(m.writer, color) catch {};
564554 }
565555
566556 fn location(m: *MsgWriter, path: []const u8, line: u32, col: u32) void {
lib/compiler/aro/aro/Driver.zig+11-11
......@@ -519,7 +519,7 @@ fn option(arg: []const u8, name: []const u8) ?[]const u8 {
519519
520520fn addSource(d: *Driver, path: []const u8) !Source {
521521 if (mem.eql(u8, "-", path)) {
522 const stdin = std.io.getStdIn().reader();
522 const stdin = std.fs.File.stdin().deprecatedReader();
523523 const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32));
524524 defer d.comp.gpa.free(input);
525525 return d.comp.addSourceFromBuffer("<stdin>", input);
......@@ -541,7 +541,7 @@ pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{ FatalEr
541541}
542542
543543pub fn renderErrors(d: *Driver) void {
544 Diagnostics.render(d.comp, d.detectConfig(std.io.getStdErr()));
544 Diagnostics.render(d.comp, d.detectConfig(std.fs.File.stderr()));
545545}
546546
547547pub fn detectConfig(d: *Driver, file: std.fs.File) std.io.tty.Config {
......@@ -591,7 +591,7 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8, comptime fast_
591591 var macro_buf = std.ArrayList(u8).init(d.comp.gpa);
592592 defer macro_buf.deinit();
593593
594 const std_out = std.io.getStdOut().writer();
594 const std_out = std.fs.File.stdout().deprecatedWriter();
595595 if (try parseArgs(d, std_out, macro_buf.writer(), args)) return;
596596
597597 const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile);
......@@ -686,10 +686,10 @@ fn processSource(
686686 std.fs.cwd().createFile(some, .{}) catch |er|
687687 return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) })
688688 else
689 std.io.getStdOut();
689 std.fs.File.stdout();
690690 defer if (d.output_name != null) file.close();
691691
692 var buf_w = std.io.bufferedWriter(file.writer());
692 var buf_w = std.io.bufferedWriter(file.deprecatedWriter());
693693
694694 pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er|
695695 return d.fatal("unable to write result: {s}", .{errorDescription(er)});
......@@ -704,8 +704,8 @@ fn processSource(
704704 defer tree.deinit();
705705
706706 if (d.verbose_ast) {
707 const stdout = std.io.getStdOut();
708 var buf_writer = std.io.bufferedWriter(stdout.writer());
707 const stdout = std.fs.File.stdout();
708 var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter());
709709 tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {};
710710 buf_writer.flush() catch {};
711711 }
......@@ -734,8 +734,8 @@ fn processSource(
734734 defer ir.deinit(d.comp.gpa);
735735
736736 if (d.verbose_ir) {
737 const stdout = std.io.getStdOut();
738 var buf_writer = std.io.bufferedWriter(stdout.writer());
737 const stdout = std.fs.File.stdout();
738 var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter());
739739 ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {};
740740 buf_writer.flush() catch {};
741741 }
......@@ -806,10 +806,10 @@ fn processSource(
806806}
807807
808808fn dumpLinkerArgs(items: []const []const u8) !void {
809 const stdout = std.io.getStdOut().writer();
809 const stdout = std.fs.File.stdout().deprecatedWriter();
810810 for (items, 0..) |item, i| {
811811 if (i > 0) try stdout.writeByte(' ');
812 try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)});
812 try stdout.print("\"{f}\"", .{std.zig.fmtString(item)});
813813 }
814814 try stdout.writeByte('\n');
815815}
lib/compiler/aro/aro/Parser.zig+5-5
......@@ -500,8 +500,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_
500500
501501 const w = p.strings.writer();
502502 const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes;
503 try w.print("call to '{s}' declared with attribute error: {}", .{
504 p.tokSlice(@"error".__name_tok), std.zig.fmtEscapes(msg_str),
503 try w.print("call to '{s}' declared with attribute error: {f}", .{
504 p.tokSlice(@"error".__name_tok), std.zig.fmtString(msg_str),
505505 });
506506 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
507507 try p.errStr(.error_attribute, usage_tok, str);
......@@ -512,8 +512,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_
512512
513513 const w = p.strings.writer();
514514 const msg_str = p.comp.interner.get(warning.msg.ref()).bytes;
515 try w.print("call to '{s}' declared with attribute warning: {}", .{
516 p.tokSlice(warning.__name_tok), std.zig.fmtEscapes(msg_str),
515 try w.print("call to '{s}' declared with attribute warning: {f}", .{
516 p.tokSlice(warning.__name_tok), std.zig.fmtString(msg_str),
517517 });
518518 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
519519 try p.errStr(.warning_attribute, usage_tok, str);
......@@ -542,7 +542,7 @@ fn errDeprecated(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, msg: ?Valu
542542 try w.writeAll(reason);
543543 if (msg) |m| {
544544 const str = p.comp.interner.get(m.ref()).bytes;
545 try w.print(": {}", .{std.zig.fmtEscapes(str)});
545 try w.print(": {f}", .{std.zig.fmtString(str)});
546546 }
547547 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
548548 return p.errStr(tag, tok_i, str);
lib/compiler/aro/aro/Preprocessor.zig+3-2
......@@ -811,7 +811,7 @@ fn verboseLog(pp: *Preprocessor, raw: RawToken, comptime fmt: []const u8, args:
811811 const source = pp.comp.getSource(raw.source);
812812 const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start });
813813
814 const stderr = std.io.getStdErr().writer();
814 const stderr = std.fs.File.stderr().deprecatedWriter();
815815 var buf_writer = std.io.bufferedWriter(stderr);
816816 const writer = buf_writer.writer();
817817 defer buf_writer.flush() catch {};
......@@ -3262,7 +3262,8 @@ fn printLinemarker(
32623262 // containing the same bytes as the input regardless of encoding.
32633263 else => {
32643264 try w.writeAll("\\x");
3265 try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, w);
3265 // TODO try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
3266 try w.print("{x:0>2}", .{byte});
32663267 },
32673268 };
32683269 try w.writeByte('"');
lib/compiler/aro/aro/Value.zig+2-2
......@@ -961,7 +961,7 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w
961961 switch (key) {
962962 .null => return w.writeAll("nullptr_t"),
963963 .int => |repr| switch (repr) {
964 inline else => |x| return w.print("{d}", .{x}),
964 inline .u64, .i64, .big_int => |x| return w.print("{d}", .{x}),
965965 },
966966 .float => |repr| switch (repr) {
967967 .f16 => |x| return w.print("{d}", .{@round(@as(f64, @floatCast(x)) * 1000) / 1000}),
......@@ -982,7 +982,7 @@ pub fn printString(bytes: []const u8, ty: Type, comp: *const Compilation, w: any
982982 const without_null = bytes[0 .. bytes.len - @intFromEnum(size)];
983983 try w.writeByte('"');
984984 switch (size) {
985 .@"1" => try w.print("{}", .{std.zig.fmtEscapes(without_null)}),
985 .@"1" => try w.print("{f}", .{std.zig.fmtString(without_null)}),
986986 .@"2" => {
987987 var items: [2]u16 = undefined;
988988 var i: usize = 0;
lib/compiler/aro/backend/Object/Elf.zig+1-1
......@@ -171,7 +171,7 @@ pub fn addRelocation(elf: *Elf, name: []const u8, section_kind: Object.Section,
171171/// strtab
172172/// section headers
173173pub fn finish(elf: *Elf, file: std.fs.File) !void {
174 var buf_writer = std.io.bufferedWriter(file.writer());
174 var buf_writer = std.io.bufferedWriter(file.deprecatedWriter());
175175 const w = buf_writer.writer();
176176
177177 var num_sections: std.elf.Elf64_Half = additional_sections;
lib/compiler/aro_translate_c.zig+3-2
......@@ -1781,7 +1781,8 @@ test "Macro matching" {
17811781fn renderErrorsAndExit(comp: *aro.Compilation) noreturn {
17821782 defer std.process.exit(1);
17831783
1784 var writer = aro.Diagnostics.defaultMsgWriter(std.io.tty.detectConfig(std.io.getStdErr()));
1784 var buffer: [1000]u8 = undefined;
1785 var writer = aro.Diagnostics.defaultMsgWriter(std.io.tty.detectConfig(std.fs.File.stderr()), &buffer);
17851786 defer writer.deinit(); // writer deinit must run *before* exit so that stderr is flushed
17861787
17871788 var saw_error = false;
......@@ -1824,6 +1825,6 @@ pub fn main() !void {
18241825 defer tree.deinit(gpa);
18251826
18261827 const formatted = try tree.render(arena);
1827 try std.io.getStdOut().writeAll(formatted);
1828 try std.fs.File.stdout().writeAll(formatted);
18281829 return std.process.cleanExit();
18291830}
lib/compiler/aro_translate_c/ast.zig+6-6
......@@ -849,7 +849,7 @@ const Context = struct {
849849 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
850850 if (std.zig.primitives.isPrimitive(bytes))
851851 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
852 return c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(bytes)});
852 return c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(bytes, .{ .allow_primitive = true })});
853853 }
854854
855855 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
......@@ -1201,7 +1201,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
12011201
12021202 const compile_error_tok = try c.addToken(.builtin, "@compileError");
12031203 _ = try c.addToken(.l_paren, "(");
1204 const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(payload.mangled)});
1204 const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
12051205 const err_msg = try c.addNode(.{
12061206 .tag = .string_literal,
12071207 .main_token = err_msg_tok,
......@@ -2116,7 +2116,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
21162116 defer c.gpa.free(members);
21172117
21182118 for (payload.fields, 0..) |field, i| {
2119 const name_tok = try c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(field.name)});
2119 const name_tok = try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true })});
21202120 _ = try c.addToken(.colon, ":");
21212121 const type_expr = try renderNode(c, field.type);
21222122
......@@ -2205,7 +2205,7 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI
22052205 .main_token = try c.addToken(.period, "."),
22062206 .data = .{ .node_and_token = .{
22072207 lhs,
2208 try c.addTokenFmt(.identifier, "{p}", .{std.zig.fmtId(field_name)}),
2208 try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true })}),
22092209 } },
22102210 });
22112211}
......@@ -2681,7 +2681,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
26812681 _ = try c.addToken(.l_paren, "(");
26822682 const res = try c.addNode(.{
26832683 .tag = .string_literal,
2684 .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}),
2684 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
26852685 .data = undefined,
26862686 });
26872687 _ = try c.addToken(.r_paren, ")");
......@@ -2765,7 +2765,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
27652765 _ = try c.addToken(.l_paren, "(");
27662766 const res = try c.addNode(.{
27672767 .tag = .string_literal,
2768 .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}),
2768 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
27692769 .data = undefined,
27702770 });
27712771 _ = try c.addToken(.r_paren, ")");
lib/compiler/build_runner.zig+3-1
......@@ -255,7 +255,7 @@ pub fn main() !void {
255255 builder.verbose_llvm_ir = "-";
256256 } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {
257257 builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..];
258 } else if (mem.eql(u8, arg, "--verbose-llvm-bc=")) {
258 } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) {
259259 builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];
260260 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
261261 builder.verbose_cimport = true;
......@@ -719,6 +719,8 @@ fn runStepNames(
719719 if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {};
720720 if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {};
721721
722 w.writeAll("\n") catch {};
723
722724 // Print a fancy tree with build results.
723725 var step_stack_copy = try step_stack.clone(gpa);
724726 defer step_stack_copy.deinit(gpa);
lib/compiler/libc.zig+3-3
......@@ -40,7 +40,7 @@ pub fn main() !void {
4040 const arg = args[i];
4141 if (mem.startsWith(u8, arg, "-")) {
4242 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
43 const stdout = std.io.getStdOut().writer();
43 const stdout = std.fs.File.stdout().deprecatedWriter();
4444 try stdout.writeAll(usage_libc);
4545 return std.process.cleanExit();
4646 } else if (mem.eql(u8, arg, "-target")) {
......@@ -97,7 +97,7 @@ pub fn main() !void {
9797 fatal("no include dirs detected for target {s}", .{zig_target});
9898 }
9999
100 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
100 var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
101101 var writer = bw.writer();
102102 for (libc_dirs.libc_include_dir_list) |include_dir| {
103103 try writer.writeAll(include_dir);
......@@ -125,7 +125,7 @@ pub fn main() !void {
125125 };
126126 defer libc.deinit(gpa);
127127
128 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
128 var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
129129 try libc.render(bw.writer());
130130 try bw.flush();
131131 }
lib/compiler/objcopy.zig+6-6
......@@ -54,7 +54,7 @@ fn cmdObjCopy(
5454 fatal("unexpected positional argument: '{s}'", .{arg});
5555 }
5656 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
57 return std.io.getStdOut().writeAll(usage);
57 return std.fs.File.stdout().writeAll(usage);
5858 } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) {
5959 i += 1;
6060 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
......@@ -227,8 +227,8 @@ fn cmdObjCopy(
227227 if (listen) {
228228 var server = try Server.init(.{
229229 .gpa = gpa,
230 .in = std.io.getStdIn(),
231 .out = std.io.getStdOut(),
230 .in = .stdin(),
231 .out = .stdout(),
232232 .zig_version = builtin.zig_version_string,
233233 });
234234 defer server.deinit();
......@@ -635,11 +635,11 @@ const HexWriter = struct {
635635 const payload_bytes = self.getPayloadBytes();
636636 assert(payload_bytes.len <= MAX_PAYLOAD_LEN);
637637
638 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3s}{4X:0>2}" ++ linesep, .{
638 const line = try std.fmt.bufPrint(&outbuf, ":{0X:0>2}{1X:0>4}{2X:0>2}{3X}{4X:0>2}" ++ linesep, .{
639639 @as(u8, @intCast(payload_bytes.len)),
640640 self.address,
641641 @intFromEnum(self.payload),
642 std.fmt.fmtSliceHexUpper(payload_bytes),
642 payload_bytes,
643643 self.checksum(),
644644 });
645645 try file.writeAll(line);
......@@ -1495,7 +1495,7 @@ const ElfFileHelper = struct {
14951495 if (size < prefix.len) return null;
14961496
14971497 try in_file.seekTo(offset);
1498 var section_reader = std.io.limitedReader(in_file.reader(), size);
1498 var section_reader = std.io.limitedReader(in_file.deprecatedReader(), size);
14991499
15001500 // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.
15011501 const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size));
lib/compiler/reduce.zig+1-1
......@@ -68,7 +68,7 @@ pub fn main() !void {
6868 const arg = args[i];
6969 if (mem.startsWith(u8, arg, "-")) {
7070 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
71 const stdout = std.io.getStdOut().writer();
71 const stdout = std.fs.File.stdout().deprecatedWriter();
7272 try stdout.writeAll(usage);
7373 return std.process.cleanExit();
7474 } else if (mem.eql(u8, arg, "--")) {
lib/compiler/reduce/Walk.zig-12
......@@ -160,12 +160,6 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
160160 try walkExpression(w, decl);
161161 },
162162
163 .@"usingnamespace" => {
164 try w.transformations.append(.{ .delete_node = decl });
165 const expr = ast.nodeData(decl).node;
166 try walkExpression(w, expr);
167 },
168
169163 .global_var_decl,
170164 .local_var_decl,
171165 .simple_var_decl,
......@@ -335,7 +329,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
335329 .address_of,
336330 .@"try",
337331 .@"resume",
338 .@"await",
339332 .deref,
340333 => {
341334 return walkExpression(w, ast.nodeData(node).node);
......@@ -379,12 +372,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
379372
380373 .call_one,
381374 .call_one_comma,
382 .async_call_one,
383 .async_call_one_comma,
384375 .call,
385376 .call_comma,
386 .async_call,
387 .async_call_comma,
388377 => {
389378 var buf: [1]Ast.Node.Index = undefined;
390379 return walkCall(w, ast.fullCall(&buf, node).?);
......@@ -525,7 +514,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
525514 .local_var_decl => unreachable,
526515 .simple_var_decl => unreachable,
527516 .aligned_var_decl => unreachable,
528 .@"usingnamespace" => unreachable,
529517 .test_decl => unreachable,
530518 .asm_output => unreachable,
531519 .asm_input => unreachable,
lib/compiler/resinator/cli.zig+13-14
......@@ -125,13 +125,12 @@ pub const Diagnostics = struct {
125125 }
126126
127127 pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {
128 std.debug.lockStdErr();
129 defer std.debug.unlockStdErr();
130 const stderr = std.io.getStdErr().writer();
128 const stderr = std.debug.lockStderrWriter(&.{});
129 defer std.debug.unlockStderrWriter();
131130 self.renderToWriter(args, stderr, config) catch return;
132131 }
133132
134 pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: anytype, config: std.io.tty.Config) !void {
133 pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: *std.io.Writer, config: std.io.tty.Config) !void {
135134 for (self.errors.items) |err_details| {
136135 try renderErrorMessage(writer, config, err_details, args);
137136 }
......@@ -1403,7 +1402,7 @@ test parsePercent {
14031402 try std.testing.expectError(error.InvalidFormat, parsePercent("~1"));
14041403}
14051404
1406pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void {
1405pub fn renderErrorMessage(writer: *std.io.Writer, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void {
14071406 try config.setColor(writer, .dim);
14081407 try writer.writeAll("<cli>");
14091408 try config.setColor(writer, .reset);
......@@ -1481,27 +1480,27 @@ pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_detail
14811480 try writer.writeByte('\n');
14821481
14831482 try config.setColor(writer, .green);
1484 try writer.writeByteNTimes(' ', prefix.len);
1483 try writer.splatByteAll(' ', prefix.len);
14851484 // Special case for when the option is *only* a prefix (e.g. invalid option: -)
14861485 if (err_details.arg_span.prefix_len == arg_with_name.len) {
1487 try writer.writeByteNTimes('^', err_details.arg_span.prefix_len);
1486 try writer.splatByteAll('^', err_details.arg_span.prefix_len);
14881487 } else {
1489 try writer.writeByteNTimes('~', err_details.arg_span.prefix_len);
1490 try writer.writeByteNTimes(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len);
1488 try writer.splatByteAll('~', err_details.arg_span.prefix_len);
1489 try writer.splatByteAll(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len);
14911490 if (!err_details.arg_span.point_at_next_arg and err_details.arg_span.value_offset == 0) {
14921491 try writer.writeByte('^');
1493 try writer.writeByteNTimes('~', name_slice.len - 1);
1492 try writer.splatByteAll('~', name_slice.len - 1);
14941493 } else if (err_details.arg_span.value_offset > 0) {
1495 try writer.writeByteNTimes('~', err_details.arg_span.value_offset - err_details.arg_span.name_offset);
1494 try writer.splatByteAll('~', err_details.arg_span.value_offset - err_details.arg_span.name_offset);
14961495 try writer.writeByte('^');
14971496 if (err_details.arg_span.value_offset < arg_with_name.len) {
1498 try writer.writeByteNTimes('~', arg_with_name.len - err_details.arg_span.value_offset - 1);
1497 try writer.splatByteAll('~', arg_with_name.len - err_details.arg_span.value_offset - 1);
14991498 }
15001499 } else if (err_details.arg_span.point_at_next_arg) {
1501 try writer.writeByteNTimes('~', arg_with_name.len - err_details.arg_span.name_offset + 1);
1500 try writer.splatByteAll('~', arg_with_name.len - err_details.arg_span.name_offset + 1);
15021501 try writer.writeByte('^');
15031502 if (next_arg_len > 0) {
1504 try writer.writeByteNTimes('~', next_arg_len - 1);
1503 try writer.splatByteAll('~', next_arg_len - 1);
15051504 }
15061505 }
15071506 }
lib/compiler/resinator/compile.zig+11-11
......@@ -570,7 +570,7 @@ pub const Compiler = struct {
570570 switch (predefined_type) {
571571 .GROUP_ICON, .GROUP_CURSOR => {
572572 // Check for animated icon first
573 if (ani.isAnimatedIcon(file.reader())) {
573 if (ani.isAnimatedIcon(file.deprecatedReader())) {
574574 // Animated icons are just put into the resource unmodified,
575575 // and the resource type changes to ANIICON/ANICURSOR
576576
......@@ -586,14 +586,14 @@ pub const Compiler = struct {
586586
587587 try header.write(writer, self.errContext(node.id));
588588 try file.seekTo(0);
589 try writeResourceData(writer, file.reader(), header.data_size);
589 try writeResourceData(writer, file.deprecatedReader(), header.data_size);
590590 return;
591591 }
592592
593593 // isAnimatedIcon moved the file cursor so reset to the start
594594 try file.seekTo(0);
595595
596 const icon_dir = ico.read(self.allocator, file.reader(), try file.getEndPos()) catch |err| switch (err) {
596 const icon_dir = ico.read(self.allocator, file.deprecatedReader(), try file.getEndPos()) catch |err| switch (err) {
597597 error.OutOfMemory => |e| return e,
598598 else => |e| {
599599 return self.iconReadError(
......@@ -672,7 +672,7 @@ pub const Compiler = struct {
672672 }
673673
674674 try file.seekTo(entry.data_offset_from_start_of_file);
675 var header_bytes = file.reader().readBytesNoEof(16) catch {
675 var header_bytes = file.deprecatedReader().readBytesNoEof(16) catch {
676676 return self.iconReadError(
677677 error.UnexpectedEOF,
678678 filename_utf8,
......@@ -803,7 +803,7 @@ pub const Compiler = struct {
803803 }
804804
805805 try file.seekTo(entry.data_offset_from_start_of_file);
806 try writeResourceDataNoPadding(writer, file.reader(), entry.data_size_in_bytes);
806 try writeResourceDataNoPadding(writer, file.deprecatedReader(), entry.data_size_in_bytes);
807807 try writeDataPadding(writer, full_data_size);
808808
809809 if (self.state.icon_id == std.math.maxInt(u16)) {
......@@ -859,7 +859,7 @@ pub const Compiler = struct {
859859 header.applyMemoryFlags(node.common_resource_attributes, self.source);
860860 const file_size = try file.getEndPos();
861861
862 const bitmap_info = bmp.read(file.reader(), file_size) catch |err| {
862 const bitmap_info = bmp.read(file.deprecatedReader(), file_size) catch |err| {
863863 const filename_string_index = try self.diagnostics.putString(filename_utf8);
864864 return self.addErrorDetailsAndFail(.{
865865 .err = .bmp_read_error,
......@@ -922,7 +922,7 @@ pub const Compiler = struct {
922922 header.data_size = bmp_bytes_to_write;
923923 try header.write(writer, self.errContext(node.id));
924924 try file.seekTo(bmp.file_header_len);
925 const file_reader = file.reader();
925 const file_reader = file.deprecatedReader();
926926 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size);
927927 if (bitmap_info.getBitmasksByteLen() > 0) {
928928 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen());
......@@ -968,7 +968,7 @@ pub const Compiler = struct {
968968 header.data_size = @intCast(file_size);
969969 try header.write(writer, self.errContext(node.id));
970970
971 var header_slurping_reader = headerSlurpingReader(148, file.reader());
971 var header_slurping_reader = headerSlurpingReader(148, file.deprecatedReader());
972972 try writeResourceData(writer, header_slurping_reader.reader(), header.data_size);
973973
974974 try self.state.font_dir.add(self.arena, FontDir.Font{
......@@ -1002,7 +1002,7 @@ pub const Compiler = struct {
10021002 // We now know that the data size will fit in a u32
10031003 header.data_size = @intCast(data_size);
10041004 try header.write(writer, self.errContext(node.id));
1005 try writeResourceData(writer, file.reader(), header.data_size);
1005 try writeResourceData(writer, file.deprecatedReader(), header.data_size);
10061006 }
10071007
10081008 fn iconReadError(
......@@ -2947,7 +2947,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype)
29472947 slurped_header: [size]u8 = [_]u8{0x00} ** size,
29482948
29492949 pub const Error = ReaderType.Error;
2950 pub const Reader = std.io.Reader(*@This(), Error, read);
2950 pub const Reader = std.io.GenericReader(*@This(), Error, read);
29512951
29522952 pub fn read(self: *@This(), buf: []u8) Error!usize {
29532953 const amt = try self.child_reader.read(buf);
......@@ -2981,7 +2981,7 @@ pub fn LimitedWriter(comptime WriterType: type) type {
29812981 bytes_left: u64,
29822982
29832983 pub const Error = error{NoSpaceLeft} || WriterType.Error;
2984 pub const Writer = std.io.Writer(*Self, Error, write);
2984 pub const Writer = std.io.GenericWriter(*Self, Error, write);
29852985
29862986 const Self = @This();
29872987
lib/compiler/resinator/errors.zig+27-31
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const assert = std.debug.assert;
23const Token = @import("lex.zig").Token;
34const SourceMappings = @import("source_mapping.zig").SourceMappings;
45const utils = @import("utils.zig");
......@@ -61,16 +62,15 @@ pub const Diagnostics = struct {
6162 }
6263
6364 pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void {
64 std.debug.lockStdErr();
65 defer std.debug.unlockStdErr();
66 const stderr = std.io.getStdErr().writer();
65 const stderr = std.debug.lockStderrWriter(&.{});
66 defer std.debug.unlockStderrWriter();
6767 for (self.errors.items) |err_details| {
6868 renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;
6969 }
7070 }
7171
7272 pub fn renderToStdErrDetectTTY(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void {
73 const tty_config = std.io.tty.detectConfig(std.io.getStdErr());
73 const tty_config = std.io.tty.detectConfig(std.fs.File.stderr());
7474 return self.renderToStdErr(cwd, source, tty_config, source_mappings);
7575 }
7676
......@@ -409,15 +409,7 @@ pub const ErrorDetails = struct {
409409 failed_to_open_cwd,
410410 };
411411
412 fn formatToken(
413 ctx: TokenFormatContext,
414 comptime fmt: []const u8,
415 options: std.fmt.FormatOptions,
416 writer: anytype,
417 ) !void {
418 _ = fmt;
419 _ = options;
420
412 fn formatToken(ctx: TokenFormatContext, writer: *std.io.Writer) std.io.Writer.Error!void {
421413 switch (ctx.token.id) {
422414 .eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()),
423415 else => {},
......@@ -441,7 +433,7 @@ pub const ErrorDetails = struct {
441433 code_page: SupportedCodePage,
442434 };
443435
444 fn fmtToken(self: ErrorDetails, source: []const u8) std.fmt.Formatter(formatToken) {
436 fn fmtToken(self: ErrorDetails, source: []const u8) std.fmt.Formatter(TokenFormatContext, formatToken) {
445437 return .{ .data = .{
446438 .token = self.token,
447439 .code_page = self.code_page,
......@@ -452,7 +444,7 @@ pub const ErrorDetails = struct {
452444 pub fn render(self: ErrorDetails, writer: anytype, source: []const u8, strings: []const []const u8) !void {
453445 switch (self.err) {
454446 .unfinished_string_literal => {
455 return writer.print("unfinished string literal at '{s}', expected closing '\"'", .{self.fmtToken(source)});
447 return writer.print("unfinished string literal at '{f}', expected closing '\"'", .{self.fmtToken(source)});
456448 },
457449 .string_literal_too_long => {
458450 return writer.print("string literal too long (max is currently {} characters)", .{self.extra.number});
......@@ -466,10 +458,14 @@ pub const ErrorDetails = struct {
466458 .hint => return,
467459 },
468460 .illegal_byte => {
469 return writer.print("character '{s}' is not allowed", .{std.fmt.fmtSliceEscapeUpper(self.token.slice(source))});
461 return writer.print("character '{f}' is not allowed", .{
462 std.ascii.hexEscape(self.token.slice(source), .upper),
463 });
470464 },
471465 .illegal_byte_outside_string_literals => {
472 return writer.print("character '{s}' is not allowed outside of string literals", .{std.fmt.fmtSliceEscapeUpper(self.token.slice(source))});
466 return writer.print("character '{f}' is not allowed outside of string literals", .{
467 std.ascii.hexEscape(self.token.slice(source), .upper),
468 });
473469 },
474470 .illegal_codepoint_outside_string_literals => {
475471 // This is somewhat hacky, but we know that:
......@@ -527,26 +523,26 @@ pub const ErrorDetails = struct {
527523 return writer.print("unsupported code page '{s} (id={})' in #pragma code_page", .{ @tagName(code_page), number });
528524 },
529525 .unfinished_raw_data_block => {
530 return writer.print("unfinished raw data block at '{s}', expected closing '}}' or 'END'", .{self.fmtToken(source)});
526 return writer.print("unfinished raw data block at '{f}', expected closing '}}' or 'END'", .{self.fmtToken(source)});
531527 },
532528 .unfinished_string_table_block => {
533 return writer.print("unfinished STRINGTABLE block at '{s}', expected closing '}}' or 'END'", .{self.fmtToken(source)});
529 return writer.print("unfinished STRINGTABLE block at '{f}', expected closing '}}' or 'END'", .{self.fmtToken(source)});
534530 },
535531 .expected_token => {
536 return writer.print("expected '{s}', got '{s}'", .{ self.extra.expected.nameForErrorDisplay(), self.fmtToken(source) });
532 return writer.print("expected '{s}', got '{f}'", .{ self.extra.expected.nameForErrorDisplay(), self.fmtToken(source) });
537533 },
538534 .expected_something_else => {
539535 try writer.writeAll("expected ");
540536 try self.extra.expected_types.writeCommaSeparated(writer);
541 return writer.print("; got '{s}'", .{self.fmtToken(source)});
537 return writer.print("; got '{f}'", .{self.fmtToken(source)});
542538 },
543539 .resource_type_cant_use_raw_data => switch (self.type) {
544 .err, .warning => try writer.print("expected '<filename>', found '{s}' (resource type '{s}' can't use raw data)", .{ self.fmtToken(source), self.extra.resource.nameForErrorDisplay() }),
545 .note => try writer.print("if '{s}' is intended to be a filename, it must be specified as a quoted string literal", .{self.fmtToken(source)}),
540 .err, .warning => try writer.print("expected '<filename>', found '{f}' (resource type '{s}' can't use raw data)", .{ self.fmtToken(source), self.extra.resource.nameForErrorDisplay() }),
541 .note => try writer.print("if '{f}' is intended to be a filename, it must be specified as a quoted string literal", .{self.fmtToken(source)}),
546542 .hint => return,
547543 },
548544 .id_must_be_ordinal => {
549 try writer.print("id of resource type '{s}' must be an ordinal (u16), got '{s}'", .{ self.extra.resource.nameForErrorDisplay(), self.fmtToken(source) });
545 try writer.print("id of resource type '{s}' must be an ordinal (u16), got '{f}'", .{ self.extra.resource.nameForErrorDisplay(), self.fmtToken(source) });
550546 },
551547 .name_or_id_not_allowed => {
552548 try writer.print("name or id is not allowed for resource type '{s}'", .{self.extra.resource.nameForErrorDisplay()});
......@@ -562,7 +558,7 @@ pub const ErrorDetails = struct {
562558 try writer.writeAll("ASCII character not equivalent to virtual key code");
563559 },
564560 .empty_menu_not_allowed => {
565 try writer.print("empty menu of type '{s}' not allowed", .{self.fmtToken(source)});
561 try writer.print("empty menu of type '{f}' not allowed", .{self.fmtToken(source)});
566562 },
567563 .rc_would_miscompile_version_value_padding => switch (self.type) {
568564 .err, .warning => return writer.print("the padding before this quoted string value would be miscompiled by the Win32 RC compiler", .{}),
......@@ -627,7 +623,7 @@ pub const ErrorDetails = struct {
627623 .string_already_defined => switch (self.type) {
628624 .err, .warning => {
629625 const language = self.extra.string_and_language.language;
630 return writer.print("string with id {d} (0x{X}) already defined for language {}", .{ self.extra.string_and_language.id, self.extra.string_and_language.id, language });
626 return writer.print("string with id {d} (0x{X}) already defined for language {f}", .{ self.extra.string_and_language.id, self.extra.string_and_language.id, language });
631627 },
632628 .note => return writer.print("previous definition of string with id {d} (0x{X}) here", .{ self.extra.string_and_language.id, self.extra.string_and_language.id }),
633629 .hint => return,
......@@ -642,7 +638,7 @@ pub const ErrorDetails = struct {
642638 try writer.print("unable to open file '{s}': {s}", .{ strings[self.extra.file_open_error.filename_string_index], @tagName(self.extra.file_open_error.err) });
643639 },
644640 .invalid_accelerator_key => {
645 try writer.print("invalid accelerator key '{s}': {s}", .{ self.fmtToken(source), @tagName(self.extra.accelerator_error.err) });
641 try writer.print("invalid accelerator key '{f}': {s}", .{ self.fmtToken(source), @tagName(self.extra.accelerator_error.err) });
646642 },
647643 .accelerator_type_required => {
648644 try writer.writeAll("accelerator type [ASCII or VIRTKEY] required when key is an integer");
......@@ -898,7 +894,7 @@ fn cellCount(code_page: SupportedCodePage, source: []const u8, start_index: usiz
898894
899895const truncated_str = "<...truncated...>";
900896
901pub fn renderErrorMessage(writer: anytype, tty_config: std.io.tty.Config, cwd: std.fs.Dir, err_details: ErrorDetails, source: []const u8, strings: []const []const u8, source_mappings: ?SourceMappings) !void {
897pub fn renderErrorMessage(writer: *std.io.Writer, tty_config: std.io.tty.Config, cwd: std.fs.Dir, err_details: ErrorDetails, source: []const u8, strings: []const []const u8, source_mappings: ?SourceMappings) !void {
902898 if (err_details.type == .hint) return;
903899
904900 const source_line_start = err_details.token.getLineStartForErrorDisplay(source);
......@@ -981,10 +977,10 @@ pub fn renderErrorMessage(writer: anytype, tty_config: std.io.tty.Config, cwd: s
981977
982978 try tty_config.setColor(writer, .green);
983979 const num_spaces = truncated_visual_info.point_offset - truncated_visual_info.before_len;
984 try writer.writeByteNTimes(' ', num_spaces);
985 try writer.writeByteNTimes('~', truncated_visual_info.before_len);
980 try writer.splatByteAll(' ', num_spaces);
981 try writer.splatByteAll('~', truncated_visual_info.before_len);
986982 try writer.writeByte('^');
987 try writer.writeByteNTimes('~', truncated_visual_info.after_len);
983 try writer.splatByteAll('~', truncated_visual_info.after_len);
988984 try writer.writeByte('\n');
989985 try tty_config.setColor(writer, .reset);
990986
lib/compiler/resinator/lex.zig+3-1
......@@ -237,7 +237,9 @@ pub const Lexer = struct {
237237 }
238238
239239 pub fn dump(self: *Self, token: *const Token) void {
240 std.debug.print("{s}:{d}: {s}\n", .{ @tagName(token.id), token.line_number, std.fmt.fmtSliceEscapeLower(token.slice(self.buffer)) });
240 std.debug.print("{s}:{d}: {f}\n", .{
241 @tagName(token.id), token.line_number, std.ascii.hexEscape(token.slice(self.buffer), .lower),
242 });
241243 }
242244
243245 pub const LexMethod = enum {
lib/compiler/resinator/main.zig+17-13
......@@ -22,14 +22,14 @@ pub fn main() !void {
2222 defer arena_state.deinit();
2323 const arena = arena_state.allocator();
2424
25 const stderr = std.io.getStdErr();
25 const stderr = std.fs.File.stderr();
2626 const stderr_config = std.io.tty.detectConfig(stderr);
2727
2828 const args = try std.process.argsAlloc(allocator);
2929 defer std.process.argsFree(allocator, args);
3030
3131 if (args.len < 2) {
32 try renderErrorMessage(stderr.writer(), stderr_config, .err, "expected zig lib dir as first argument", .{});
32 try renderErrorMessage(std.debug.lockStderrWriter(&.{}), stderr_config, .err, "expected zig lib dir as first argument", .{});
3333 std.process.exit(1);
3434 }
3535 const zig_lib_dir = args[1];
......@@ -44,7 +44,7 @@ pub fn main() !void {
4444 var error_handler: ErrorHandler = switch (zig_integration) {
4545 true => .{
4646 .server = .{
47 .out = std.io.getStdOut(),
47 .out = std.fs.File.stdout(),
4848 .in = undefined, // won't be receiving messages
4949 .receive_fifo = undefined, // won't be receiving messages
5050 },
......@@ -81,15 +81,15 @@ pub fn main() !void {
8181 defer options.deinit();
8282
8383 if (options.print_help_and_exit) {
84 const stdout = std.io.getStdOut();
85 try cli.writeUsage(stdout.writer(), "zig rc");
84 const stdout = std.fs.File.stdout();
85 try cli.writeUsage(stdout.deprecatedWriter(), "zig rc");
8686 return;
8787 }
8888
8989 // Don't allow verbose when integrating with Zig via stdout
9090 options.verbose = false;
9191
92 const stdout_writer = std.io.getStdOut().writer();
92 const stdout_writer = std.fs.File.stdout().deprecatedWriter();
9393 if (options.verbose) {
9494 try options.dumpVerbose(stdout_writer);
9595 try stdout_writer.writeByte('\n');
......@@ -290,7 +290,7 @@ pub fn main() !void {
290290 };
291291 defer depfile.close();
292292
293 const depfile_writer = depfile.writer();
293 const depfile_writer = depfile.deprecatedWriter();
294294 var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer);
295295 switch (options.depfile_fmt) {
296296 .json => {
......@@ -343,7 +343,7 @@ pub fn main() !void {
343343 switch (err) {
344344 error.DuplicateResource => {
345345 const duplicate_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];
346 try error_handler.emitMessage(allocator, .err, "duplicate resource [id: {}, type: {}, language: {}]", .{
346 try error_handler.emitMessage(allocator, .err, "duplicate resource [id: {f}, type: {f}, language: {f}]", .{
347347 duplicate_resource.name_value,
348348 fmtResourceType(duplicate_resource.type_value),
349349 duplicate_resource.language,
......@@ -352,7 +352,7 @@ pub fn main() !void {
352352 error.ResourceDataTooLong => {
353353 const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];
354354 try error_handler.emitMessage(allocator, .err, "resource has a data length that is too large to be written into a coff section", .{});
355 try error_handler.emitMessage(allocator, .note, "the resource with the invalid size is [id: {}, type: {}, language: {}]", .{
355 try error_handler.emitMessage(allocator, .note, "the resource with the invalid size is [id: {f}, type: {f}, language: {f}]", .{
356356 overflow_resource.name_value,
357357 fmtResourceType(overflow_resource.type_value),
358358 overflow_resource.language,
......@@ -361,7 +361,7 @@ pub fn main() !void {
361361 error.TotalResourceDataTooLong => {
362362 const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];
363363 try error_handler.emitMessage(allocator, .err, "total resource data exceeds the maximum of the coff 'size of raw data' field", .{});
364 try error_handler.emitMessage(allocator, .note, "size overflow occurred when attempting to write this resource: [id: {}, type: {}, language: {}]", .{
364 try error_handler.emitMessage(allocator, .note, "size overflow occurred when attempting to write this resource: [id: {f}, type: {f}, language: {f}]", .{
365365 overflow_resource.name_value,
366366 fmtResourceType(overflow_resource.type_value),
367367 overflow_resource.language,
......@@ -471,7 +471,7 @@ const IoStream = struct {
471471 allocator: std.mem.Allocator,
472472 };
473473 pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError;
474 pub const Writer = std.io.Writer(WriterContext, WriteError, write);
474 pub const Writer = std.io.GenericWriter(WriterContext, WriteError, write);
475475
476476 pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize {
477477 switch (ctx.self.*) {
......@@ -645,7 +645,9 @@ const ErrorHandler = union(enum) {
645645 },
646646 .tty => {
647647 // extra newline to separate this line from the aro errors
648 try renderErrorMessage(std.io.getStdErr().writer(), self.tty, .err, "{s}\n", .{fail_msg});
648 const stderr = std.debug.lockStderrWriter(&.{});
649 defer std.debug.unlockStderrWriter();
650 try renderErrorMessage(stderr, self.tty, .err, "{s}\n", .{fail_msg});
649651 aro.Diagnostics.render(comp, self.tty);
650652 },
651653 }
......@@ -690,7 +692,9 @@ const ErrorHandler = union(enum) {
690692 try server.serveErrorBundle(error_bundle);
691693 },
692694 .tty => {
693 try renderErrorMessage(std.io.getStdErr().writer(), self.tty, msg_type, format, args);
695 const stderr = std.debug.lockStderrWriter(&.{});
696 defer std.debug.unlockStderrWriter();
697 try renderErrorMessage(stderr, self.tty, msg_type, format, args);
694698 },
695699 }
696700 }
lib/compiler/resinator/res.zig+11-31
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const assert = std.debug.assert;
23const rc = @import("rc.zig");
34const ResourceType = rc.ResourceType;
45const CommonResourceAttributes = rc.CommonResourceAttributes;
......@@ -163,14 +164,7 @@ pub const Language = packed struct(u16) {
163164 return @bitCast(self);
164165 }
165166
166 pub fn format(
167 language: Language,
168 comptime fmt: []const u8,
169 options: std.fmt.FormatOptions,
170 out_stream: anytype,
171 ) !void {
172 _ = fmt;
173 _ = options;
167 pub fn format(language: Language, w: *std.io.Writer) std.io.Writer.Error!void {
174168 const language_id = language.asInt();
175169 const language_name = language_name: {
176170 if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {
......@@ -181,7 +175,7 @@ pub const Language = packed struct(u16) {
181175 }
182176 break :language_name "<UNKNOWN>";
183177 };
184 try out_stream.print("{s} (0x{X})", .{ language_name, language_id });
178 try w.print("{s} (0x{X})", .{ language_name, language_id });
185179 }
186180};
187181
......@@ -445,47 +439,33 @@ pub const NameOrOrdinal = union(enum) {
445439 }
446440 }
447441
448 pub fn format(
449 self: NameOrOrdinal,
450 comptime fmt: []const u8,
451 options: std.fmt.FormatOptions,
452 out_stream: anytype,
453 ) !void {
454 _ = fmt;
455 _ = options;
442 pub fn format(self: NameOrOrdinal, w: *std.io.Writer) !void {
456443 switch (self) {
457444 .name => |name| {
458 try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)});
445 try w.print("{f}", .{std.unicode.fmtUtf16Le(name)});
459446 },
460447 .ordinal => |ordinal| {
461 try out_stream.print("{d}", .{ordinal});
448 try w.print("{d}", .{ordinal});
462449 },
463450 }
464451 }
465452
466 fn formatResourceType(
467 self: NameOrOrdinal,
468 comptime fmt: []const u8,
469 options: std.fmt.FormatOptions,
470 out_stream: anytype,
471 ) !void {
472 _ = fmt;
473 _ = options;
453 fn formatResourceType(self: NameOrOrdinal, w: *std.io.Writer) std.io.Writer.Error!void {
474454 switch (self) {
475455 .name => |name| {
476 try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)});
456 try w.print("{f}", .{std.unicode.fmtUtf16Le(name)});
477457 },
478458 .ordinal => |ordinal| {
479459 if (std.enums.tagName(RT, @enumFromInt(ordinal))) |predefined_type_name| {
480 try out_stream.print("{s}", .{predefined_type_name});
460 try w.print("{s}", .{predefined_type_name});
481461 } else {
482 try out_stream.print("{d}", .{ordinal});
462 try w.print("{d}", .{ordinal});
483463 }
484464 },
485465 }
486466 }
487467
488 pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(formatResourceType) {
468 pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(NameOrOrdinal, formatResourceType) {
489469 return .{ .data = type_value };
490470 }
491471};
lib/compiler/resinator/utils.zig+1-1
......@@ -86,7 +86,7 @@ pub const ErrorMessageType = enum { err, warning, note };
8686
8787/// Used for generic colored errors/warnings/notes, more context-specific error messages
8888/// are handled elsewhere.
89pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void {
89pub fn renderErrorMessage(writer: *std.io.Writer, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void {
9090 switch (msg_type) {
9191 .err => {
9292 try config.setColor(writer, .bold);
lib/compiler/test_runner.zig+2-2
......@@ -303,7 +303,7 @@ pub fn mainSimple() anyerror!void {
303303 var failed: u64 = 0;
304304
305305 // we don't want to bring in File and Writer if the backend doesn't support it
306 const stderr = if (comptime enable_print) std.io.getStdErr() else {};
306 const stderr = if (comptime enable_print) std.fs.File.stderr() else {};
307307
308308 for (builtin.test_functions) |test_fn| {
309309 if (test_fn.func()) |_| {
......@@ -330,7 +330,7 @@ pub fn mainSimple() anyerror!void {
330330 passed += 1;
331331 }
332332 if (enable_print and print_summary) {
333 stderr.writer().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {};
333 stderr.deprecatedWriter().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {};
334334 }
335335 if (failed != 0) std.process.exit(1);
336336}
lib/compiler_rt/clear_cache.zig+20
......@@ -86,6 +86,26 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void {
8686 const result = std.os.linux.syscall3(.cacheflush, start, end - start, flags);
8787 std.debug.assert(result == 0);
8888 exportIt();
89 } else if (os == .netbsd and mips) {
90 // Replace with https://github.com/ziglang/zig/issues/23904 in the future.
91 const cfa: extern struct {
92 va: usize,
93 nbytes: usize,
94 whichcache: u32,
95 } = .{
96 .va = start,
97 .nbytes = end - start,
98 .whichcache = 3, // ICACHE | DCACHE
99 };
100 asm volatile (
101 \\ syscall
102 :
103 : [_] "{$2}" (165), // nr = SYS_sysarch
104 [_] "{$4}" (0), // op = MIPS_CACHEFLUSH
105 [_] "{$5}" (&cfa), // args = &cfa
106 : "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
107 );
108 exportIt();
89109 } else if (mips and os == .openbsd) {
90110 // TODO
91111 //cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE);
lib/compiler_rt/emutls.zig+1-1
......@@ -18,7 +18,7 @@ const gcc_word = usize;
1818pub const panic = common.panic;
1919
2020comptime {
21 if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.os.tag == .openbsd)) {
21 if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.abi.isOpenHarmony() or builtin.os.tag == .openbsd)) {
2222 @export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility });
2323 }
2424}
lib/docs/wasm/Walk.zig+1-12
......@@ -238,12 +238,8 @@ pub const File = struct {
238238
239239 .call_one,
240240 .call_one_comma,
241 .async_call_one,
242 .async_call_one_comma,
243241 .call,
244242 .call_comma,
245 .async_call,
246 .async_call_comma,
247243 => {
248244 var buf: [1]Ast.Node.Index = undefined;
249245 return categorize_call(file_index, node, ast.fullCall(&buf, node).?);
......@@ -450,7 +446,7 @@ fn parse(file_name: []const u8, source: []u8) Oom!Ast {
450446 error.WriteFailed => return error.OutOfMemory,
451447 };
452448 }
453 log.err("{s}:{}:{}: {s}", .{ file_name, err_loc.line + 1, err_loc.column + 1, rendered_err.items });
449 log.err("{s}:{d}:{d}: {s}", .{ file_name, err_loc.line + 1, err_loc.column + 1, rendered_err.items });
454450 }
455451 return Ast.parse(gpa, "", .zig);
456452 }
......@@ -577,7 +573,6 @@ fn struct_decl(
577573 },
578574
579575 .@"comptime",
580 .@"usingnamespace",
581576 => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node),
582577
583578 .test_decl => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).opt_token_and_node[1]),
......@@ -649,7 +644,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
649644 const ast = w.file.get_ast();
650645 switch (ast.nodeTag(node)) {
651646 .root => unreachable, // Top-level declaration.
652 .@"usingnamespace" => unreachable, // Top-level declaration.
653647 .test_decl => unreachable, // Top-level declaration.
654648 .container_field_init => unreachable, // Top-level declaration.
655649 .container_field_align => unreachable, // Top-level declaration.
......@@ -749,7 +743,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
749743 .@"comptime",
750744 .@"nosuspend",
751745 .@"suspend",
752 .@"await",
753746 .@"resume",
754747 .@"try",
755748 => try expr(w, scope, parent_decl, ast.nodeData(node).node),
......@@ -812,12 +805,8 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
812805
813806 .call_one,
814807 .call_one_comma,
815 .async_call_one,
816 .async_call_one_comma,
817808 .call,
818809 .call_comma,
819 .async_call,
820 .async_call_comma,
821810 => {
822811 var buf: [1]Ast.Node.Index = undefined;
823812 const full = ast.fullCall(&buf, node).?;
lib/docs/wasm/html_render.zig-3
......@@ -101,8 +101,6 @@ pub fn fileSourceHtml(
101101 .keyword_align,
102102 .keyword_and,
103103 .keyword_asm,
104 .keyword_async,
105 .keyword_await,
106104 .keyword_break,
107105 .keyword_catch,
108106 .keyword_comptime,
......@@ -139,7 +137,6 @@ pub fn fileSourceHtml(
139137 .keyword_try,
140138 .keyword_union,
141139 .keyword_unreachable,
142 .keyword_usingnamespace,
143140 .keyword_var,
144141 .keyword_volatile,
145142 .keyword_allowzero,
lib/docs/wasm/markdown.zig+2-2
......@@ -143,7 +143,7 @@ fn mainImpl() !void {
143143 var parser = try Parser.init(gpa);
144144 defer parser.deinit();
145145
146 var stdin_buf = std.io.bufferedReader(std.io.getStdIn().reader());
146 var stdin_buf = std.io.bufferedReader(std.fs.File.stdin().deprecatedReader());
147147 var line_buf = std.ArrayList(u8).init(gpa);
148148 defer line_buf.deinit();
149149 while (stdin_buf.reader().streamUntilDelimiter(line_buf.writer(), '\n', null)) {
......@@ -158,7 +158,7 @@ fn mainImpl() !void {
158158 var doc = try parser.endInput();
159159 defer doc.deinit(gpa);
160160
161 var stdout_buf = std.io.bufferedWriter(std.io.getStdOut().writer());
161 var stdout_buf = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
162162 try doc.render(stdout_buf.writer());
163163 try stdout_buf.flush();
164164}
lib/fuzzer.zig+12-9
......@@ -9,7 +9,8 @@ pub const std_options = std.Options{
99 .logFn = logOverride,
1010};
1111
12var log_file: ?std.fs.File = null;
12var log_file_buffer: [256]u8 = undefined;
13var log_file_writer: ?std.fs.File.Writer = null;
1314
1415fn logOverride(
1516 comptime level: std.log.Level,
......@@ -17,15 +18,17 @@ fn logOverride(
1718 comptime format: []const u8,
1819 args: anytype,
1920) void {
20 const f = if (log_file) |f| f else f: {
21 const fw = if (log_file_writer) |*f| f else f: {
2122 const f = fuzzer.cache_dir.createFile("tmp/libfuzzer.log", .{}) catch
2223 @panic("failed to open fuzzer log file");
23 log_file = f;
24 break :f f;
24 log_file_writer = f.writer(&log_file_buffer);
25 break :f &log_file_writer.?;
2526 };
2627 const prefix1 = comptime level.asText();
2728 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
28 f.writer().print(prefix1 ++ prefix2 ++ format ++ "\n", args) catch @panic("failed to write to fuzzer log");
29 fw.interface.print(prefix1 ++ prefix2 ++ format ++ "\n", args) catch
30 @panic("failed to write to fuzzer log");
31 fw.interface.flush() catch @panic("failed to flush fuzzer log");
2932}
3033
3134/// Helps determine run uniqueness in the face of recursion.
......@@ -226,18 +229,18 @@ const Fuzzer = struct {
226229 .read = true,
227230 }) catch |e| switch (e) {
228231 error.PathAlreadyExists => continue,
229 else => fatal("unable to create '{}{d}: {s}", .{ f.corpus_directory, i, @errorName(err) }),
232 else => fatal("unable to create '{f}{d}: {s}", .{ f.corpus_directory, i, @errorName(err) }),
230233 };
231234 errdefer input_file.close();
232235 // Initialize the mmap for the current input.
233236 f.input = MemoryMappedList.create(input_file, 0, std.heap.page_size_max) catch |e| {
234 fatal("unable to init memory map for input at '{}{d}': {s}", .{
237 fatal("unable to init memory map for input at '{f}{d}': {s}", .{
235238 f.corpus_directory, i, @errorName(e),
236239 });
237240 };
238241 break;
239242 },
240 else => fatal("unable to read '{}{d}': {s}", .{ f.corpus_directory, i, @errorName(err) }),
243 else => fatal("unable to read '{f}{d}': {s}", .{ f.corpus_directory, i, @errorName(err) }),
241244 };
242245 errdefer gpa.free(input);
243246 f.corpus.append(gpa, .{
......@@ -263,7 +266,7 @@ const Fuzzer = struct {
263266 const sub_path = try std.fmt.allocPrint(gpa, "f/{s}", .{f.unit_test_name});
264267 f.corpus_directory = .{
265268 .handle = f.cache_dir.makeOpenPath(sub_path, .{}) catch |err|
266 fatal("unable to open corpus directory 'f/{s}': {s}", .{ sub_path, @errorName(err) }),
269 fatal("unable to open corpus directory 'f/{s}': {t}", .{ sub_path, err }),
267270 .path = sub_path,
268271 };
269272 initNextInput(f);
lib/init/src/root.zig+1-1
......@@ -5,7 +5,7 @@ pub fn bufferedPrint() !void {
55 // Stdout is for the actual output of your application, for example if you
66 // are implementing gzip, then only the compressed bytes should be sent to
77 // stdout, not any debugging messages.
8 const stdout_file = std.io.getStdOut().writer();
8 const stdout_file = std.fs.File.stdout().deprecatedWriter();
99 // Buffering can improve performance significantly in print-heavy programs.
1010 var bw = std.io.bufferedWriter(stdout_file);
1111 const stdout = bw.writer();
lib/libc/mingw/math/x86/floorf.S deleted-51
......@@ -1,51 +0,0 @@
1/**
2 * This file has no copyright assigned and is placed in the Public Domain.
3 * This file is part of the mingw-w64 runtime package.
4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
5 */
6 #include <_mingw_mac.h>
7
8 .file "floorf.S"
9 .text
10 .p2align 4,,15
11 .globl __MINGW_USYMBOL(floorf)
12 .def __MINGW_USYMBOL(floorf); .scl 2; .type 32; .endef
13#ifdef __x86_64__
14 .seh_proc __MINGW_USYMBOL(floorf)
15#endif
16__MINGW_USYMBOL(floorf):
17#if defined(_AMD64_) || defined(__x86_64__)
18 subq $40, %rsp
19 .seh_stackalloc 40
20 .seh_endprologue
21 unpcklps %xmm0, %xmm0
22 cvtps2pd %xmm0, %xmm0
23 call floor
24 unpcklpd %xmm0, %xmm0
25 cvtpd2ps %xmm0, %xmm0
26 addq $40, %rsp
27 ret
28 .seh_endproc
29 .def __MINGW_USYMBOL(floor); .scl 2; .type 32; .endef
30#elif defined(_X86_) || defined(__i386__)
31 flds 4(%esp)
32 subl $8,%esp
33
34 fstcw 4(%esp) /* store fpu control word */
35
36 /* We use here %edx although only the low 1 bits are defined.
37 But none of the operations should care and they are faster
38 than the 16 bit operations. */
39 movl $0x400,%edx /* round towards -oo */
40 orl 4(%esp),%edx
41 andl $0xf7ff,%edx
42 movl %edx,(%esp)
43 fldcw (%esp) /* load modified control word */
44
45 frndint /* round */
46
47 fldcw 4(%esp) /* restore original control word */
48
49 addl $8,%esp
50 ret
51#endif
lib/libc/mingw/math/x86/floorl.S deleted-63
......@@ -1,63 +0,0 @@
1/**
2 * This file has no copyright assigned and is placed in the Public Domain.
3 * This file is part of the mingw-w64 runtime package.
4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
5 */
6#include <_mingw_mac.h>
7
8 .file "floorl.S"
9 .text
10#ifdef __x86_64__
11 .align 8
12#else
13 .align 4
14#endif
15 .globl __MINGW_USYMBOL(floorl)
16 .def __MINGW_USYMBOL(floorl); .scl 2; .type 32; .endef
17__MINGW_USYMBOL(floorl):
18#if defined(_AMD64_) || defined(__x86_64__)
19 fldt (%rdx)
20 subq $24,%rsp
21
22 fstcw 8(%rsp) /* store fpu control word */
23
24 /* We use here %edx although only the low 1 bits are defined.
25 But none of the operations should care and they are faster
26 than the 16 bit operations. */
27 movl $0x400,%edx /* round towards -oo */
28 orl 8(%rsp),%edx
29 andl $0xf7ff,%edx
30 movl %edx,(%rsp)
31 fldcw (%rsp) /* load modified control word */
32
33 frndint /* round */
34
35 fldcw 8(%rsp) /* restore original control word */
36
37 addq $24,%rsp
38 movq %rcx,%rax
39 movq $0,8(%rcx)
40 fstpt (%rcx)
41 ret
42#elif defined(_X86_) || defined(__i386__)
43 fldt 4(%esp)
44 subl $8,%esp
45
46 fstcw 4(%esp) /* store fpu control word */
47
48 /* We use here %edx although only the low 1 bits are defined.
49 But none of the operations should care and they are faster
50 than the 16 bit operations. */
51 movl $0x400,%edx /* round towards -oo */
52 orl 4(%esp),%edx
53 andl $0xf7ff,%edx
54 movl %edx,(%esp)
55 fldcw (%esp) /* load modified control word */
56
57 frndint /* round */
58
59 fldcw 4(%esp) /* restore original control word */
60
61 addl $8,%esp
62 ret
63#endif
lib/libc/musl/src/math/aarch64/floor.c deleted-7
......@@ -1,7 +0,0 @@
1#include <math.h>
2
3double floor(double x)
4{
5 __asm__ ("frintm %d0, %d1" : "=w"(x) : "w"(x));
6 return x;
7}
lib/libc/musl/src/math/aarch64/floorf.c deleted-7
......@@ -1,7 +0,0 @@
1#include <math.h>
2
3float floorf(float x)
4{
5 __asm__ ("frintm %s0, %s1" : "=w"(x) : "w"(x));
6 return x;
7}
lib/libc/musl/src/math/floor.c deleted-31
......@@ -1,31 +0,0 @@
1#include "libm.h"
2
3#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
4#define EPS DBL_EPSILON
5#elif FLT_EVAL_METHOD==2
6#define EPS LDBL_EPSILON
7#endif
8static const double_t toint = 1/EPS;
9
10double floor(double x)
11{
12 union {double f; uint64_t i;} u = {x};
13 int e = u.i >> 52 & 0x7ff;
14 double_t y;
15
16 if (e >= 0x3ff+52 || x == 0)
17 return x;
18 /* y = int(x) - x, where int(x) is an integer neighbor of x */
19 if (u.i >> 63)
20 y = x - toint + toint - x;
21 else
22 y = x + toint - toint - x;
23 /* special case because of non-nearest rounding modes */
24 if (e <= 0x3ff-1) {
25 FORCE_EVAL(y);
26 return u.i >> 63 ? -1 : 0;
27 }
28 if (y > 0)
29 return x + y - 1;
30 return x + y;
31}
lib/libc/musl/src/math/floorf.c deleted-27
......@@ -1,27 +0,0 @@
1#include "libm.h"
2
3float floorf(float x)
4{
5 union {float f; uint32_t i;} u = {x};
6 int e = (int)(u.i >> 23 & 0xff) - 0x7f;
7 uint32_t m;
8
9 if (e >= 23)
10 return x;
11 if (e >= 0) {
12 m = 0x007fffff >> e;
13 if ((u.i & m) == 0)
14 return x;
15 FORCE_EVAL(x + 0x1p120f);
16 if (u.i >> 31)
17 u.i += m;
18 u.i &= ~m;
19 } else {
20 FORCE_EVAL(x + 0x1p120f);
21 if (u.i >> 31 == 0)
22 u.i = 0;
23 else if (u.i << 1)
24 u.f = -1.0;
25 }
26 return u.f;
27}
lib/libc/musl/src/math/floorl.c deleted-34
......@@ -1,34 +0,0 @@
1#include "libm.h"
2
3#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
4long double floorl(long double x)
5{
6 return floor(x);
7}
8#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
9
10static const long double toint = 1/LDBL_EPSILON;
11
12long double floorl(long double x)
13{
14 union ldshape u = {x};
15 int e = u.i.se & 0x7fff;
16 long double y;
17
18 if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0)
19 return x;
20 /* y = int(x) - x, where int(x) is an integer neighbor of x */
21 if (u.i.se >> 15)
22 y = x - toint + toint - x;
23 else
24 y = x + toint - toint - x;
25 /* special case because of non-nearest rounding modes */
26 if (e <= 0x3fff-1) {
27 FORCE_EVAL(y);
28 return u.i.se >> 15 ? -1 : 0;
29 }
30 if (y > 0)
31 return x + y - 1;
32 return x + y;
33}
34#endif
lib/libc/musl/src/math/i386/floor.s+1-16
......@@ -1,20 +1,5 @@
1.global floorf
2.type floorf,@function
3floorf:
4 flds 4(%esp)
5 jmp 1f
6
7.global floorl
8.type floorl,@function
9floorl:
10 fldt 4(%esp)
11 jmp 1f
1/* zig patch: removed `floorl` and `floorf` in favor of using zig compiler_rt's implementations */
122
13.global floor
14.type floor,@function
15floor:
16 fldl 4(%esp)
171: mov $0x7,%al
1831: fstcw 4(%esp)
194 mov 5(%esp),%ah
205 mov %al,5(%esp)
lib/libc/musl/src/math/i386/floorf.s deleted-1
......@@ -1 +0,0 @@
1# see floor.s
lib/libc/musl/src/math/i386/floorl.s deleted-1
......@@ -1 +0,0 @@
1# see floor.s
lib/libc/musl/src/math/powerpc64/floor.c deleted-15
......@@ -1,15 +0,0 @@
1#include <math.h>
2
3#ifdef _ARCH_PWR5X
4
5double floor(double x)
6{
7 __asm__ ("frim %0, %1" : "=d"(x) : "d"(x));
8 return x;
9}
10
11#else
12
13#include "../floor.c"
14
15#endif
lib/libc/musl/src/math/powerpc64/floorf.c deleted-15
......@@ -1,15 +0,0 @@
1#include <math.h>
2
3#ifdef _ARCH_PWR5X
4
5float floorf(float x)
6{
7 __asm__ ("frim %0, %1" : "=f"(x) : "f"(x));
8 return x;
9}
10
11#else
12
13#include "../floorf.c"
14
15#endif
lib/libc/musl/src/math/s390x/floor.c deleted-15
......@@ -1,15 +0,0 @@
1#include <math.h>
2
3#if defined(__HTM__) || __ARCH__ >= 9
4
5double floor(double x)
6{
7 __asm__ ("fidbra %0, 7, %1, 4" : "=f"(x) : "f"(x));
8 return x;
9}
10
11#else
12
13#include "../floor.c"
14
15#endif
lib/libc/musl/src/math/s390x/floorf.c deleted-15
......@@ -1,15 +0,0 @@
1#include <math.h>
2
3#if defined(__HTM__) || __ARCH__ >= 9
4
5float floorf(float x)
6{
7 __asm__ ("fiebra %0, 7, %1, 4" : "=f"(x) : "f"(x));
8 return x;
9}
10
11#else
12
13#include "../floorf.c"
14
15#endif
lib/libc/musl/src/math/s390x/floorl.c deleted-15
......@@ -1,15 +0,0 @@
1#include <math.h>
2
3#if defined(__HTM__) || __ARCH__ >= 9
4
5long double floorl(long double x)
6{
7 __asm__ ("fixbra %0, 7, %1, 4" : "=f"(x) : "f"(x));
8 return x;
9}
10
11#else
12
13#include "../floorl.c"
14
15#endif
lib/std/Build.zig+25-35
......@@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct {
24662466
24672467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {
24682468 return gen.path orelse {
2469 std.debug.lockStdErr();
2470 const stderr = std.io.getStdErr();
2471 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
2472 std.debug.unlockStdErr();
2469 const w = debug.lockStderrWriter(&.{});
2470 dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2471 debug.unlockStderrWriter();
24732472 @panic("misconfigured build script");
24742473 };
24752474 }
......@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {
26762675 var file_path: Cache.Path = .{
26772676 .root_dir = Cache.Directory.cwd(),
26782677 .sub_path = gen.file.path orelse {
2679 std.debug.lockStdErr();
2680 const stderr: fs.File = .stderr();
2681 dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};
2682 std.debug.unlockStdErr();
2678 const w = debug.lockStderrWriter(&.{});
2679 dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2680 debug.unlockStderrWriter();
26832681 @panic("misconfigured build script");
26842682 },
26852683 };
......@@ -2769,17 +2767,16 @@ fn dumpBadDirnameHelp(
27692767 const w = debug.lockStderrWriter(&.{});
27702768 defer debug.unlockStderrWriter();
27712769
2772 const stderr: fs.File = .stderr();
27732770 try w.print(msg, args);
27742771
2775 const tty_config = std.io.tty.detectConfig(stderr);
2772 const tty_config = std.io.tty.detectConfig(.stderr());
27762773
27772774 if (fail_step) |s| {
27782775 tty_config.setColor(w, .red) catch {};
2779 try stderr.writeAll(" The step was created by this stack trace:\n");
2776 try w.writeAll(" The step was created by this stack trace:\n");
27802777 tty_config.setColor(w, .reset) catch {};
27812778
2782 s.dump(stderr);
2779 s.dump(w, tty_config);
27832780 }
27842781
27852782 if (asking_step) |as| {
......@@ -2787,24 +2784,23 @@ fn dumpBadDirnameHelp(
27872784 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
27882785 tty_config.setColor(w, .reset) catch {};
27892786
2790 as.dump(stderr);
2787 as.dump(w, tty_config);
27912788 }
27922789
27932790 tty_config.setColor(w, .red) catch {};
2794 try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");
2791 try w.writeAll(" Hope that helps. Proceeding to panic.\n");
27952792 tty_config.setColor(w, .reset) catch {};
27962793}
27972794
27982795/// In this function the stderr mutex has already been locked.
27992796pub fn dumpBadGetPathHelp(
28002797 s: *Step,
2801 stderr: fs.File,
2798 w: *std.io.Writer,
2799 tty_config: std.io.tty.Config,
28022800 src_builder: *Build,
28032801 asking_step: ?*Step,
28042802) anyerror!void {
2805 var fw = stderr.writer(&.{});
2806 const bw = &fw.interface;
2807 try bw.print(
2803 try w.print(
28082804 \\getPath() was called on a GeneratedFile that wasn't built yet.
28092805 \\ source package path: {s}
28102806 \\ Is there a missing Step dependency on step '{s}'?
......@@ -2814,22 +2810,21 @@ pub fn dumpBadGetPathHelp(
28142810 s.name,
28152811 });
28162812
2817 const tty_config = std.io.tty.detectConfig(stderr);
2818 tty_config.setColor(&bw, .red) catch {};
2819 try stderr.writeAll(" The step was created by this stack trace:\n");
2820 tty_config.setColor(&bw, .reset) catch {};
2813 tty_config.setColor(w, .red) catch {};
2814 try w.writeAll(" The step was created by this stack trace:\n");
2815 tty_config.setColor(w, .reset) catch {};
28212816
2822 s.dump(stderr);
2817 s.dump(w, tty_config);
28232818 if (asking_step) |as| {
2824 tty_config.setColor(&bw, .red) catch {};
2825 try bw.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2826 tty_config.setColor(&bw, .reset) catch {};
2819 tty_config.setColor(w, .red) catch {};
2820 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2821 tty_config.setColor(w, .reset) catch {};
28272822
2828 as.dump(stderr);
2823 as.dump(w, tty_config);
28292824 }
2830 tty_config.setColor(&bw, .red) catch {};
2831 try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");
2832 tty_config.setColor(&bw, .reset) catch {};
2825 tty_config.setColor(w, .red) catch {};
2826 try w.writeAll(" Hope that helps. Proceeding to panic.\n");
2827 tty_config.setColor(w, .reset) catch {};
28332828}
28342829
28352830pub const InstallDir = union(enum) {
......@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {
28662861 return result_path;
28672862}
28682863
2869/// Deprecated; use `std.fmt.hex` instead.
2870pub fn hex64(x: u64) [16]u8 {
2871 return std.fmt.hex(x);
2872}
2873
28742864/// A pair of target query and fully resolved target.
28752865/// This type is generally required by build system API that need to be given a
28762866/// target. The query is kept because the Zig toolchain needs to know which parts
lib/std/Build/Cache.zig+33-31
......@@ -2,6 +2,18 @@
22//! This is not a general-purpose cache. It is designed to be fast and simple,
33//! not to withstand attacks using specially-crafted input.
44
5const Cache = @This();
6const std = @import("std");
7const builtin = @import("builtin");
8const crypto = std.crypto;
9const fs = std.fs;
10const assert = std.debug.assert;
11const testing = std.testing;
12const mem = std.mem;
13const fmt = std.fmt;
14const Allocator = std.mem.Allocator;
15const log = std.log.scoped(.cache);
16
517gpa: Allocator,
618manifest_dir: fs.Dir,
719hash: HashHelper = .{},
......@@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig");
2133pub const Directory = @import("Cache/Directory.zig");
2234pub const DepTokenizer = @import("Cache/DepTokenizer.zig");
2335
24const Cache = @This();
25const std = @import("std");
26const builtin = @import("builtin");
27const crypto = std.crypto;
28const fs = std.fs;
29const assert = std.debug.assert;
30const testing = std.testing;
31const mem = std.mem;
32const fmt = std.fmt;
33const Allocator = std.mem.Allocator;
34const log = std.log.scoped(.cache);
35
3636pub fn addPrefix(cache: *Cache, directory: Directory) void {
3737 cache.prefixes_buffer[cache.prefixes_len] = directory;
3838 cache.prefixes_len += 1;
......@@ -1118,25 +1118,12 @@ pub const Manifest = struct {
11181118 if (self.manifest_dirty) {
11191119 self.manifest_dirty = false;
11201120
1121 const gpa = self.cache.gpa;
1122 var contents: std.ArrayListUnmanaged(u8) = .empty;
1123 defer contents.deinit(gpa);
1124
1125 try contents.appendSlice(gpa, manifest_header ++ "\n");
1126 for (self.files.keys()) |file| {
1127 try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{
1128 file.stat.size,
1129 file.stat.inode,
1130 file.stat.mtime,
1131 &file.bin_digest,
1132 file.prefixed_path.prefix,
1133 file.prefixed_path.sub_path,
1134 });
1135 }
1136
1137 try manifest_file.setEndPos(contents.items.len);
1138 var pos: usize = 0;
1139 while (pos < contents.items.len) pos += try manifest_file.pwrite(contents.items[pos..], pos);
1121 var buffer: [4000]u8 = undefined;
1122 var fw = manifest_file.writer(&buffer);
1123 writeDirtyManifestToStream(self, &fw) catch |err| switch (err) {
1124 error.WriteFailed => return fw.err.?,
1125 else => |e| return e,
1126 };
11401127 }
11411128
11421129 if (self.want_shared_lock) {
......@@ -1144,6 +1131,21 @@ pub const Manifest = struct {
11441131 }
11451132 }
11461133
1134 fn writeDirtyManifestToStream(self: *Manifest, fw: *fs.File.Writer) !void {
1135 try fw.interface.writeAll(manifest_header ++ "\n");
1136 for (self.files.keys()) |file| {
1137 try fw.interface.print("{d} {d} {d} {x} {d} {s}\n", .{
1138 file.stat.size,
1139 file.stat.inode,
1140 file.stat.mtime,
1141 &file.bin_digest,
1142 file.prefixed_path.prefix,
1143 file.prefixed_path.sub_path,
1144 });
1145 }
1146 try fw.end();
1147 }
1148
11471149 fn downgradeToSharedLock(self: *Manifest) !void {
11481150 if (!self.have_exclusive_lock) return;
11491151
lib/std/Build/Cache/Directory.zig+4-4
......@@ -1,5 +1,6 @@
11const Directory = @This();
22const std = @import("../../std.zig");
3const assert = std.debug.assert;
34const fs = std.fs;
45const fmt = std.fmt;
56const Allocator = std.mem.Allocator;
......@@ -55,11 +56,10 @@ pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
5556 self.* = undefined;
5657}
5758
58pub fn format(self: Directory, w: *std.io.Writer, comptime fmt_string: []const u8) !void {
59 if (fmt_string.len != 0) fmt.invalidFmtError(fmt_string, self);
59pub fn format(self: Directory, writer: *std.io.Writer) std.io.Writer.Error!void {
6060 if (self.path) |p| {
61 try w.writeAll(p);
62 try w.writeAll(fs.path.sep_str);
61 try writer.writeAll(p);
62 try writer.writeAll(fs.path.sep_str);
6363 }
6464}
6565
lib/std/Build/Cache/Path.zig+42-32
......@@ -1,3 +1,10 @@
1const Path = @This();
2const std = @import("../../std.zig");
3const assert = std.debug.assert;
4const fs = std.fs;
5const Allocator = std.mem.Allocator;
6const Cache = std.Build.Cache;
7
18root_dir: Cache.Directory,
29/// The path, relative to the root dir, that this `Path` represents.
310/// Empty string means the root_dir is the path.
......@@ -137,46 +144,55 @@ pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 {
137144}
138145
139146pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 {
140 return std.fmt.allocPrintZ(allocator, "{f}", .{p});
147 return std.fmt.allocPrintSentinel(allocator, "{f}", .{p}, 0);
141148}
142149
143pub fn format(self: Path, w: *std.io.Writer, comptime fmt_string: []const u8) !void {
144 if (fmt_string.len == 1) {
145 // Quote-escape the string.
146 const stringEscape = std.zig.stringEscape;
147 const f = switch (fmt_string[0]) {
148 'q' => "",
149 '\'' => "\'",
150 else => @compileError("unsupported format string: " ++ fmt_string),
151 };
152 if (self.root_dir.path) |p| {
153 try stringEscape(p, w, f);
154 if (self.sub_path.len > 0) try stringEscape(fs.path.sep_str, w, f);
155 }
156 if (self.sub_path.len > 0) {
157 try stringEscape(self.sub_path, w, f);
158 }
159 return;
150pub fn fmtEscapeString(path: Path) std.fmt.Formatter(Path, formatEscapeString) {
151 return .{ .data = path };
152}
153
154pub fn formatEscapeString(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void {
155 if (path.root_dir.path) |p| {
156 try std.zig.stringEscape(p, writer);
157 if (path.sub_path.len > 0) try std.zig.stringEscape(fs.path.sep_str, writer);
158 }
159 if (path.sub_path.len > 0) {
160 try std.zig.stringEscape(path.sub_path, writer);
160161 }
161 if (fmt_string.len > 0)
162 std.fmt.invalidFmtError(fmt_string, self);
162}
163
164pub fn fmtEscapeChar(path: Path) std.fmt.Formatter(Path, formatEscapeChar) {
165 return .{ .data = path };
166}
167
168pub fn formatEscapeChar(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void {
169 if (path.root_dir.path) |p| {
170 try std.zig.charEscape(p, writer);
171 if (path.sub_path.len > 0) try std.zig.charEscape(fs.path.sep_str, writer);
172 }
173 if (path.sub_path.len > 0) {
174 try std.zig.charEscape(path.sub_path, writer);
175 }
176}
177
178pub fn format(self: Path, writer: *std.io.Writer) std.io.Writer.Error!void {
163179 if (std.fs.path.isAbsolute(self.sub_path)) {
164 try w.writeAll(self.sub_path);
180 try writer.writeAll(self.sub_path);
165181 return;
166182 }
167183 if (self.root_dir.path) |p| {
168 try w.writeAll(p);
184 try writer.writeAll(p);
169185 if (self.sub_path.len > 0) {
170 try w.writeAll(fs.path.sep_str);
171 try w.writeAll(self.sub_path);
186 try writer.writeAll(fs.path.sep_str);
187 try writer.writeAll(self.sub_path);
172188 }
173189 return;
174190 }
175191 if (self.sub_path.len > 0) {
176 try w.writeAll(self.sub_path);
192 try writer.writeAll(self.sub_path);
177193 return;
178194 }
179 try w.writeByte('.');
195 try writer.writeByte('.');
180196}
181197
182198pub fn eql(self: Path, other: Path) bool {
......@@ -218,9 +234,3 @@ pub const TableAdapter = struct {
218234 return a.eql(b);
219235 }
220236};
221
222const Path = @This();
223const std = @import("../../std.zig");
224const fs = std.fs;
225const Allocator = std.mem.Allocator;
226const Cache = std.Build.Cache;
lib/std/Build/Fuzz.zig+6-4
......@@ -124,9 +124,10 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par
124124 const show_stderr = compile.step.result_stderr.len > 0;
125125
126126 if (show_error_msgs or show_compile_errors or show_stderr) {
127 const bw = std.debug.lockStderrWriter(&.{});
127 var buf: [256]u8 = undefined;
128 const w = std.debug.lockStderrWriter(&buf);
128129 defer std.debug.unlockStderrWriter();
129 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, bw, false) catch {};
130 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, false) catch {};
130131 }
131132
132133 const rebuilt_bin_path = result catch |err| switch (err) {
......@@ -151,9 +152,10 @@ fn fuzzWorkerRun(
151152
152153 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
153154 error.MakeFailed => {
154 const bw = std.debug.lockStderrWriter(&.{});
155 var buf: [256]u8 = undefined;
156 const w = std.debug.lockStderrWriter(&buf);
155157 defer std.debug.unlockStderrWriter();
156 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, bw, false) catch {};
158 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, w, false) catch {};
157159 return;
158160 },
159161 else => {
lib/std/Build/Fuzz/WebServer.zig+1-1
......@@ -176,7 +176,7 @@ fn serveFile(
176176 // We load the file with every request so that the user can make changes to the file
177177 // and refresh the HTML page without restarting this server.
178178 const file_contents = ws.zig_lib_directory.handle.readFileAlloc(name, gpa, .limited(10 * 1024 * 1024)) catch |err| {
179 log.err("failed to read '{f}{s}': {s}", .{ ws.zig_lib_directory, name, @errorName(err) });
179 log.err("failed to read '{f}{s}': {t}", .{ ws.zig_lib_directory, name, err });
180180 return error.AlreadyReported;
181181 };
182182 defer gpa.free(file_contents);
lib/std/Build/Module.zig+2-2
......@@ -186,7 +186,7 @@ pub const IncludeDir = union(enum) {
186186 .embed_path => |lazy_path| {
187187 // Special case: this is a single arg.
188188 const resolved = lazy_path.getPath3(b, asking_step);
189 const arg = b.fmt("--embed-dir={}", .{resolved});
189 const arg = b.fmt("--embed-dir={f}", .{resolved});
190190 return zig_args.append(arg);
191191 },
192192 };
......@@ -572,7 +572,7 @@ pub fn appendZigProcessFlags(
572572 try zig_args.append(switch (unwind_tables) {
573573 .none => "-fno-unwind-tables",
574574 .sync => "-funwind-tables",
575 .@"async" => "-fasync-unwind-tables",
575 .async => "-fasync-unwind-tables",
576576 });
577577 }
578578
lib/std/Build/Step.zig+10-13
......@@ -286,28 +286,25 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
286286}
287287
288288/// For debugging purposes, prints identifying information about this Step.
289pub fn dump(step: *Step, file: std.fs.File) void {
290 var fw = file.writer(&.{});
291 const bw = &fw.interface;
292 const tty_config = std.io.tty.detectConfig(file);
289pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void {
293290 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
294 bw.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
291 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
295292 @errorName(err),
296293 }) catch {};
297294 return;
298295 };
299296 if (step.getStackTrace()) |stack_trace| {
300 bw.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
301 std.debug.writeStackTrace(stack_trace, &bw, debug_info, tty_config) catch |err| {
302 bw.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};
297 w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
298 std.debug.writeStackTrace(stack_trace, w, debug_info, tty_config) catch |err| {
299 w.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};
303300 return;
304301 };
305302 } else {
306303 const field = "debug_stack_frames_count";
307304 comptime assert(@hasField(Build, field));
308 tty_config.setColor(&bw, .yellow) catch {};
309 bw.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {};
310 tty_config.setColor(&bw, .reset) catch {};
305 tty_config.setColor(w, .yellow) catch {};
306 w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {};
307 tty_config.setColor(w, .reset) catch {};
311308 }
312309}
313310
......@@ -483,9 +480,9 @@ pub fn evalZigProcess(
483480pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus {
484481 const b = s.owner;
485482 const src_path = src_lazy_path.getPath3(b, s);
486 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{}", .{src_path}), dest_path });
483 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
487484 return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| {
488 return s.fail("unable to update file from '{}' to '{s}': {s}", .{
485 return s.fail("unable to update file from '{f}' to '{s}': {s}", .{
489486 src_path, dest_path, @errorName(err),
490487 });
491488 };
lib/std/Build/Step/CheckObject.zig+79-97
......@@ -230,16 +230,11 @@ const ComputeCompareExpected = struct {
230230 literal: u64,
231231 },
232232
233 pub fn format(
234 value: ComputeCompareExpected,
235 bw: *Writer,
236 comptime fmt: []const u8,
237 ) !void {
238 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
239 try bw.print("{s} ", .{@tagName(value.op)});
233 pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void {
234 try w.print("{t} ", .{value.op});
240235 switch (value.value) {
241 .variable => |name| try bw.writeAll(name),
242 .literal => |x| try bw.print("{x}", .{x}),
236 .variable => |name| try w.writeAll(name),
237 .literal => |x| try w.print("{x}", .{x}),
243238 }
244239 }
245240};
......@@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
571566 null,
572567 .of(u64),
573568 null,
574 ) catch |err| return step.fail("unable to read '{f'}': {s}", .{ src_path, @errorName(err) });
569 ) catch |err| return step.fail("unable to read '{f}': {s}", .{
570 std.fmt.alt(src_path, .formatEscapeChar), @errorName(err),
571 });
575572
576573 var vars: std.StringHashMap(u64) = .init(gpa);
577574 for (check_object.checks.items) |chk| {
......@@ -606,7 +603,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
606603 // we either format message string with escaped codes, or not to aid debugging
607604 // the failed test.
608605 const fmtMessageString = struct {
609 fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) {
606 fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(Ctx, formatMessageString) {
610607 return .{ .data = .{
611608 .kind = kind,
612609 .msg = msg,
......@@ -618,15 +615,10 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
618615 msg: []const u8,
619616 };
620617
621 fn formatMessageString(
622 ctx: Ctx,
623 bw: *Writer,
624 comptime unused_fmt_string: []const u8,
625 ) !void {
626 _ = unused_fmt_string;
618 fn formatMessageString(ctx: Ctx, w: *Writer) !void {
627619 switch (ctx.kind) {
628 .dump_section => try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}),
629 else => try bw.writeAll(ctx.msg),
620 .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}),
621 else => try w.writeAll(ctx.msg),
630622 }
631623 }
632624 }.fmtMessageString;
......@@ -882,9 +874,9 @@ const MachODumper = struct {
882874 try bw.writeByte('\n');
883875 }
884876
885 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, bw: *Writer) !void {
877 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: *Writer) !void {
886878 // print header first
887 try bw.print(
879 try writer.print(
888880 \\LC {d}
889881 \\cmd {s}
890882 \\cmdsize {d}
......@@ -893,8 +885,8 @@ const MachODumper = struct {
893885 switch (lc.cmd()) {
894886 .SEGMENT_64 => {
895887 const seg = lc.cast(macho.segment_command_64).?;
896 try bw.writeByte('\n');
897 try bw.print(
888 try writer.writeByte('\n');
889 try writer.print(
898890 \\segname {s}
899891 \\vmaddr {x}
900892 \\vmsize {x}
......@@ -909,8 +901,8 @@ const MachODumper = struct {
909901 });
910902
911903 for (lc.getSections()) |sect| {
912 try bw.writeByte('\n');
913 try bw.print(
904 try writer.writeByte('\n');
905 try writer.print(
914906 \\sectname {s}
915907 \\addr {x}
916908 \\size {x}
......@@ -932,8 +924,8 @@ const MachODumper = struct {
932924 .REEXPORT_DYLIB,
933925 => {
934926 const dylib = lc.cast(macho.dylib_command).?;
935 try bw.writeByte('\n');
936 try bw.print(
927 try writer.writeByte('\n');
928 try writer.print(
937929 \\name {s}
938930 \\timestamp {d}
939931 \\current version {x}
......@@ -948,16 +940,16 @@ const MachODumper = struct {
948940
949941 .MAIN => {
950942 const main = lc.cast(macho.entry_point_command).?;
951 try bw.writeByte('\n');
952 try bw.print(
943 try writer.writeByte('\n');
944 try writer.print(
953945 \\entryoff {x}
954946 \\stacksize {x}
955947 , .{ main.entryoff, main.stacksize });
956948 },
957949
958950 .RPATH => {
959 try bw.writeByte('\n');
960 try bw.print(
951 try writer.writeByte('\n');
952 try writer.print(
961953 \\path {s}
962954 , .{
963955 lc.getRpathPathName(),
......@@ -966,8 +958,8 @@ const MachODumper = struct {
966958
967959 .UUID => {
968960 const uuid = lc.cast(macho.uuid_command).?;
969 try bw.writeByte('\n');
970 try bw.print("uuid {x}", .{&uuid.uuid});
961 try writer.writeByte('\n');
962 try writer.print("uuid {x}", .{&uuid.uuid});
971963 },
972964
973965 .DATA_IN_CODE,
......@@ -975,8 +967,8 @@ const MachODumper = struct {
975967 .CODE_SIGNATURE,
976968 => {
977969 const llc = lc.cast(macho.linkedit_data_command).?;
978 try bw.writeByte('\n');
979 try bw.print(
970 try writer.writeByte('\n');
971 try writer.print(
980972 \\dataoff {x}
981973 \\datasize {x}
982974 , .{ llc.dataoff, llc.datasize });
......@@ -984,8 +976,8 @@ const MachODumper = struct {
984976
985977 .DYLD_INFO_ONLY => {
986978 const dlc = lc.cast(macho.dyld_info_command).?;
987 try bw.writeByte('\n');
988 try bw.print(
979 try writer.writeByte('\n');
980 try writer.print(
989981 \\rebaseoff {x}
990982 \\rebasesize {x}
991983 \\bindoff {x}
......@@ -1012,8 +1004,8 @@ const MachODumper = struct {
10121004
10131005 .SYMTAB => {
10141006 const slc = lc.cast(macho.symtab_command).?;
1015 try bw.writeByte('\n');
1016 try bw.print(
1007 try writer.writeByte('\n');
1008 try writer.print(
10171009 \\symoff {x}
10181010 \\nsyms {x}
10191011 \\stroff {x}
......@@ -1028,8 +1020,8 @@ const MachODumper = struct {
10281020
10291021 .DYSYMTAB => {
10301022 const dlc = lc.cast(macho.dysymtab_command).?;
1031 try bw.writeByte('\n');
1032 try bw.print(
1023 try writer.writeByte('\n');
1024 try writer.print(
10331025 \\ilocalsym {x}
10341026 \\nlocalsym {x}
10351027 \\iextdefsym {x}
......@@ -1052,8 +1044,8 @@ const MachODumper = struct {
10521044
10531045 .BUILD_VERSION => {
10541046 const blc = lc.cast(macho.build_version_command).?;
1055 try bw.writeByte('\n');
1056 try bw.print(
1047 try writer.writeByte('\n');
1048 try writer.print(
10571049 \\platform {s}
10581050 \\minos {d}.{d}.{d}
10591051 \\sdk {d}.{d}.{d}
......@@ -1069,12 +1061,12 @@ const MachODumper = struct {
10691061 blc.ntools,
10701062 });
10711063 for (lc.getBuildVersionTools()) |tool| {
1072 try bw.writeByte('\n');
1064 try writer.writeByte('\n');
10731065 switch (tool.tool) {
1074 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try bw.print("tool {s}\n", .{@tagName(tool.tool)}),
1075 else => |x| try bw.print("tool {d}\n", .{@intFromEnum(x)}),
1066 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}),
1067 else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}),
10761068 }
1077 try bw.print(
1069 try writer.print(
10781070 \\version {d}.{d}.{d}
10791071 , .{
10801072 tool.version >> 16,
......@@ -1090,8 +1082,8 @@ const MachODumper = struct {
10901082 .VERSION_MIN_TVOS,
10911083 => {
10921084 const vlc = lc.cast(macho.version_min_command).?;
1093 try bw.writeByte('\n');
1094 try bw.print(
1085 try writer.writeByte('\n');
1086 try writer.print(
10951087 \\version {d}.{d}.{d}
10961088 \\sdk {d}.{d}.{d}
10971089 , .{
......@@ -1943,58 +1935,58 @@ const ElfDumper = struct {
19431935 try bw.print("entry {x}\n", .{ctx.hdr.e_entry});
19441936 }
19451937
1946 fn dumpPhdrs(ctx: ObjectContext, bw: *Writer) !void {
1938 fn dumpPhdrs(ctx: ObjectContext, writer: *Writer) !void {
19471939 if (ctx.phdrs.len == 0) return;
19481940
1949 try bw.writeAll("program headers\n");
1941 try writer.writeAll("program headers\n");
19501942
19511943 for (ctx.phdrs, 0..) |phdr, phndx| {
1952 try bw.print("phdr {d}\n", .{phndx});
1953 try bw.print("type {f}\n", .{fmtPhType(phdr.p_type)});
1954 try bw.print("vaddr {x}\n", .{phdr.p_vaddr});
1955 try bw.print("paddr {x}\n", .{phdr.p_paddr});
1956 try bw.print("offset {x}\n", .{phdr.p_offset});
1957 try bw.print("memsz {x}\n", .{phdr.p_memsz});
1958 try bw.print("filesz {x}\n", .{phdr.p_filesz});
1959 try bw.print("align {x}\n", .{phdr.p_align});
1944 try writer.print("phdr {d}\n", .{phndx});
1945 try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)});
1946 try writer.print("vaddr {x}\n", .{phdr.p_vaddr});
1947 try writer.print("paddr {x}\n", .{phdr.p_paddr});
1948 try writer.print("offset {x}\n", .{phdr.p_offset});
1949 try writer.print("memsz {x}\n", .{phdr.p_memsz});
1950 try writer.print("filesz {x}\n", .{phdr.p_filesz});
1951 try writer.print("align {x}\n", .{phdr.p_align});
19601952
19611953 {
19621954 const flags = phdr.p_flags;
1963 try bw.writeAll("flags");
1964 if (flags > 0) try bw.writeByte(' ');
1955 try writer.writeAll("flags");
1956 if (flags > 0) try writer.writeByte(' ');
19651957 if (flags & elf.PF_R != 0) {
1966 try bw.writeByte('R');
1958 try writer.writeByte('R');
19671959 }
19681960 if (flags & elf.PF_W != 0) {
1969 try bw.writeByte('W');
1961 try writer.writeByte('W');
19701962 }
19711963 if (flags & elf.PF_X != 0) {
1972 try bw.writeByte('E');
1964 try writer.writeByte('E');
19731965 }
19741966 if (flags & elf.PF_MASKOS != 0) {
1975 try bw.writeAll("OS");
1967 try writer.writeAll("OS");
19761968 }
19771969 if (flags & elf.PF_MASKPROC != 0) {
1978 try bw.writeAll("PROC");
1970 try writer.writeAll("PROC");
19791971 }
1980 try bw.writeByte('\n');
1972 try writer.writeByte('\n');
19811973 }
19821974 }
19831975 }
19841976
1985 fn dumpShdrs(ctx: ObjectContext, bw: *Writer) !void {
1977 fn dumpShdrs(ctx: ObjectContext, writer: *Writer) !void {
19861978 if (ctx.shdrs.len == 0) return;
19871979
1988 try bw.writeAll("section headers\n");
1980 try writer.writeAll("section headers\n");
19891981
19901982 for (ctx.shdrs, 0..) |shdr, shndx| {
1991 try bw.print("shdr {d}\n", .{shndx});
1992 try bw.print("name {s}\n", .{ctx.getSectionName(shndx)});
1993 try bw.print("type {f}\n", .{fmtShType(shdr.sh_type)});
1994 try bw.print("addr {x}\n", .{shdr.sh_addr});
1995 try bw.print("offset {x}\n", .{shdr.sh_offset});
1996 try bw.print("size {x}\n", .{shdr.sh_size});
1997 try bw.print("addralign {x}\n", .{shdr.sh_addralign});
1983 try writer.print("shdr {d}\n", .{shndx});
1984 try writer.print("name {s}\n", .{ctx.getSectionName(shndx)});
1985 try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)});
1986 try writer.print("addr {x}\n", .{shdr.sh_addr});
1987 try writer.print("offset {x}\n", .{shdr.sh_offset});
1988 try writer.print("size {x}\n", .{shdr.sh_size});
1989 try writer.print("addralign {x}\n", .{shdr.sh_addralign});
19981990 // TODO dump formatted sh_flags
19991991 }
20001992 }
......@@ -2263,16 +2255,11 @@ const ElfDumper = struct {
22632255 return str[0..std.mem.indexOfScalar(u8, str, 0).?];
22642256 }
22652257
2266 fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) {
2258 fn fmtShType(sh_type: u32) std.fmt.Formatter(u32, formatShType) {
22672259 return .{ .data = sh_type };
22682260 }
22692261
2270 fn formatShType(
2271 sh_type: u32,
2272 bw: *Writer,
2273 comptime unused_fmt_string: []const u8,
2274 ) !void {
2275 _ = unused_fmt_string;
2262 fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void {
22762263 const name = switch (sh_type) {
22772264 elf.SHT_NULL => "NULL",
22782265 elf.SHT_PROGBITS => "PROGBITS",
......@@ -2298,26 +2285,21 @@ const ElfDumper = struct {
22982285 elf.SHT_GNU_VERNEED => "VERNEED",
22992286 elf.SHT_GNU_VERSYM => "VERSYM",
23002287 else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) {
2301 return try bw.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS});
2288 return try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS});
23022289 } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) {
2303 return try bw.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC});
2290 return try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC});
23042291 } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) {
2305 return try bw.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER});
2292 return try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER});
23062293 } else "UNKNOWN",
23072294 };
2308 try bw.writeAll(name);
2295 try writer.writeAll(name);
23092296 }
23102297
2311 fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) {
2298 fn fmtPhType(ph_type: u32) std.fmt.Formatter(u32, formatPhType) {
23122299 return .{ .data = ph_type };
23132300 }
23142301
2315 fn formatPhType(
2316 ph_type: u32,
2317 bw: *Writer,
2318 comptime unused_fmt_string: []const u8,
2319 ) !void {
2320 _ = unused_fmt_string;
2302 fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void {
23212303 const p_type = switch (ph_type) {
23222304 elf.PT_NULL => "NULL",
23232305 elf.PT_LOAD => "LOAD",
......@@ -2332,12 +2314,12 @@ const ElfDumper = struct {
23322314 elf.PT_GNU_STACK => "GNU_STACK",
23332315 elf.PT_GNU_RELRO => "GNU_RELRO",
23342316 else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) {
2335 return try bw.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS});
2317 return try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS});
23362318 } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) {
2337 return try bw.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC});
2319 return try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC});
23382320 } else "UNKNOWN",
23392321 };
2340 try bw.writeAll(p_type);
2322 try writer.writeAll(p_type);
23412323 }
23422324};
23432325
lib/std/Build/Step/Compile.zig+6-10
......@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
10171017 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
10181018
10191019 const generated_file = maybe_path orelse {
1020 std.debug.lockStdErr();
1021 const stderr: fs.File = .stderr();
1022
1023 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1024
1020 const w = std.debug.lockStderrWriter(&.{});
1021 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
1022 std.debug.unlockStderrWriter();
10251023 @panic("missing emit option for " ++ tag_name);
10261024 };
10271025
10281026 const path = generated_file.path orelse {
1029 std.debug.lockStdErr();
1030 const stderr: fs.File = .stderr();
1031
1032 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1033
1027 const w = std.debug.lockStderrWriter(&.{});
1028 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
1029 std.debug.unlockStderrWriter();
10341030 @panic(tag_name ++ " is null. Is there a missing step dependency?");
10351031 };
10361032
lib/std/Build/Step/ConfigHeader.zig+8-8
......@@ -198,7 +198,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
198198
199199 var aw: std.io.Writer.Allocating = .init(gpa);
200200 defer aw.deinit();
201 const bw = &aw.interface;
201 const bw = &aw.writer;
202202
203203 const header_text = "This file was generated by ConfigHeader using the Zig Build System.";
204204 const c_generated_line = "/* " ++ header_text ++ " */\n";
......@@ -335,7 +335,7 @@ fn render_autoconf_at(
335335) !void {
336336 const build = step.owner;
337337 const allocator = build.allocator;
338 const bw = &aw.interface;
338 const bw = &aw.writer;
339339
340340 const used = allocator.alloc(bool, values.count()) catch @panic("OOM");
341341 for (used) |*u| u.* = false;
......@@ -553,7 +553,7 @@ fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void {
553553 .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }),
554554 .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }),
555555 // TODO: use C-specific escaping instead of zig string literals
556 .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtEscapes(string) }),
556 .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
557557 }
558558}
559559
......@@ -565,7 +565,7 @@ fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void {
565565 .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }),
566566 .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }),
567567 // TODO: use nasm-specific escaping instead of zig string literals
568 .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtEscapes(string) }),
568 .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
569569 }
570570}
571571
......@@ -753,17 +753,17 @@ fn testReplaceVariablesAutoconfAt(
753753 expected: []const u8,
754754 values: std.StringArrayHashMap(Value),
755755) !void {
756 var output: std.io.Writer.Allocating = .init(allocator);
757 defer output.deinit();
756 var aw: std.io.Writer.Allocating = .init(allocator);
757 defer aw.deinit();
758758
759759 const used = try allocator.alloc(bool, values.count());
760760 for (used) |*u| u.* = false;
761761 defer allocator.free(used);
762762
763 try expand_variables_autoconf_at(&output.interface, contents, values, used);
763 try expand_variables_autoconf_at(&aw.writer, contents, values, used);
764764
765765 for (used) |u| if (!u) return error.UnusedValue;
766 try std.testing.expectEqualStrings(expected, output.getWritten());
766 try std.testing.expectEqualStrings(expected, aw.getWritten());
767767}
768768
769769fn testReplaceVariablesCMake(
lib/std/Build/Step/Options.zig+33-22
......@@ -62,7 +62,7 @@ fn printType(
6262
6363 for (value) |slice| {
6464 try out.appendNTimes(gpa, ' ', indent);
65 try out.print(gpa, " \"{f}\",\n", .{std.zig.fmtEscapes(slice)});
65 try out.print(gpa, " \"{f}\",\n", .{std.zig.fmtString(slice)});
6666 }
6767
6868 if (name != null) {
......@@ -76,28 +76,28 @@ fn printType(
7676 []const u8 => {
7777 if (name) |some| {
7878 try out.print(gpa, "pub const {f}: []const u8 = \"{f}\";", .{
79 std.zig.fmtId(some), std.zig.fmtEscapes(value),
79 std.zig.fmtId(some), std.zig.fmtString(value),
8080 });
8181 } else {
82 try out.print(gpa, "\"{f}\",", .{std.zig.fmtEscapes(value)});
82 try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)});
8383 }
8484 return out.appendSlice(gpa, "\n");
8585 },
8686 [:0]const u8 => {
8787 if (name) |some| {
88 try out.print(gpa, "pub const {f}: [:0]const u8 = \"{f}\";", .{ std.zig.fmtId(some), std.zig.fmtEscapes(value) });
88 try out.print(gpa, "pub const {f}: [:0]const u8 = \"{f}\";", .{ std.zig.fmtId(some), std.zig.fmtString(value) });
8989 } else {
90 try out.print(gpa, "\"{f}\",", .{std.zig.fmtEscapes(value)});
90 try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)});
9191 }
9292 return out.appendSlice(gpa, "\n");
9393 },
9494 ?[]const u8 => {
9595 if (name) |some| {
96 try out.print(gpa, "pub const {}: ?[]const u8 = ", .{std.zig.fmtId(some)});
96 try out.print(gpa, "pub const {f}: ?[]const u8 = ", .{std.zig.fmtId(some)});
9797 }
9898
9999 if (value) |payload| {
100 try out.print(gpa, "\"{f}\"", .{std.zig.fmtEscapes(payload)});
100 try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)});
101101 } else {
102102 try out.appendSlice(gpa, "null");
103103 }
......@@ -111,11 +111,11 @@ fn printType(
111111 },
112112 ?[:0]const u8 => {
113113 if (name) |some| {
114 try out.print(gpa, "pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(some)});
114 try out.print(gpa, "pub const {f}: ?[:0]const u8 = ", .{std.zig.fmtId(some)});
115115 }
116116
117117 if (value) |payload| {
118 try out.print(gpa, "\"{f}\"", .{std.zig.fmtEscapes(payload)});
118 try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)});
119119 } else {
120120 try out.appendSlice(gpa, "null");
121121 }
......@@ -142,11 +142,11 @@ fn printType(
142142
143143 if (value.pre) |some| {
144144 try out.appendNTimes(gpa, ' ', indent);
145 try out.print(gpa, " .pre = \"{f}\",\n", .{std.zig.fmtEscapes(some)});
145 try out.print(gpa, " .pre = \"{f}\",\n", .{std.zig.fmtString(some)});
146146 }
147147 if (value.build) |some| {
148148 try out.appendNTimes(gpa, ' ', indent);
149 try out.print(gpa, " .build = \"{f}\",\n", .{std.zig.fmtEscapes(some)});
149 try out.print(gpa, " .build = \"{f}\",\n", .{std.zig.fmtString(some)});
150150 }
151151
152152 if (name != null) {
......@@ -162,7 +162,7 @@ fn printType(
162162 switch (@typeInfo(T)) {
163163 .array => {
164164 if (name) |some| {
165 try out.print(gpa, "pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
165 try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
166166 }
167167
168168 try out.print(gpa, "{s} {{\n", .{@typeName(T)});
......@@ -186,7 +186,7 @@ fn printType(
186186 }
187187
188188 if (name) |some| {
189 try out.print(gpa, "pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
189 try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
190190 }
191191
192192 try out.print(gpa, "&[_]{s} {{\n", .{@typeName(p.child)});
......@@ -206,7 +206,7 @@ fn printType(
206206 },
207207 .optional => {
208208 if (name) |some| {
209 try out.print(gpa, "pub const {}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
209 try out.print(gpa, "pub const {f}: {s} = ", .{ std.zig.fmtId(some), @typeName(T) });
210210 }
211211
212212 if (value) |inner| {
......@@ -243,10 +243,10 @@ fn printType(
243243 try printEnum(options, out, T, info, indent);
244244
245245 if (name) |some| {
246 try out.print(gpa, "pub const {f}: {f} = .{fp_};\n", .{
246 try out.print(gpa, "pub const {f}: {f} = .{f};\n", .{
247247 std.zig.fmtId(some),
248248 std.zig.fmtId(@typeName(T)),
249 std.zig.fmtId(@tagName(value)),
249 std.zig.fmtIdFlags(@tagName(value), .{ .allow_underscore = true, .allow_primitive = true }),
250250 });
251251 }
252252 return;
......@@ -295,7 +295,9 @@ fn printEnum(
295295
296296 inline for (val.fields) |field| {
297297 try out.appendNTimes(gpa, ' ', indent);
298 try out.print(gpa, " {fp} = {d},\n", .{ std.zig.fmtId(field.name), field.value });
298 try out.print(gpa, " {f} = {d},\n", .{
299 std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true }), field.value,
300 });
299301 }
300302
301303 if (!val.is_exhaustive) {
......@@ -313,7 +315,7 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T:
313315 if (gop.found_existing) return;
314316
315317 try out.appendNTimes(gpa, ' ', indent);
316 try out.print(gpa, "pub const {} = ", .{std.zig.fmtId(@typeName(T))});
318 try out.print(gpa, "pub const {f} = ", .{std.zig.fmtId(@typeName(T))});
317319
318320 switch (val.layout) {
319321 .@"extern" => try out.appendSlice(gpa, "extern struct"),
......@@ -330,9 +332,15 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T:
330332
331333 // If the type name doesn't contains a '.' the type is from zig builtins.
332334 if (std.mem.containsAtLeast(u8, type_name, 1, ".")) {
333 try out.print(gpa, " {p_}: {}", .{ std.zig.fmtId(field.name), std.zig.fmtId(type_name) });
335 try out.print(gpa, " {f}: {f}", .{
336 std.zig.fmtIdFlags(field.name, .{ .allow_underscore = true, .allow_primitive = true }),
337 std.zig.fmtId(type_name),
338 });
334339 } else {
335 try out.print(gpa, " {p_}: {s}", .{ std.zig.fmtId(field.name), type_name });
340 try out.print(gpa, " {f}: {s}", .{
341 std.zig.fmtIdFlags(field.name, .{ .allow_underscore = true, .allow_primitive = true }),
342 type_name,
343 });
336344 }
337345
338346 if (field.defaultValue()) |default_value| {
......@@ -377,7 +385,9 @@ fn printStructValue(
377385 } else {
378386 inline for (struct_val.fields) |field| {
379387 try out.appendNTimes(gpa, ' ', indent);
380 try out.print(gpa, " .{p_} = ", .{std.zig.fmtId(field.name)});
388 try out.print(gpa, " .{f} = ", .{
389 std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true, .allow_underscore = true }),
390 });
381391
382392 const field_name = @field(val, field.name);
383393 switch (@typeInfo(@TypeOf(field_name))) {
......@@ -405,7 +415,8 @@ pub fn addOptionPath(
405415 name: []const u8,
406416 path: LazyPath,
407417) void {
408 options.args.append(.{
418 const arena = options.step.owner.allocator;
419 options.args.append(arena, .{
409420 .name = options.step.owner.dupe(name),
410421 .path = path.dupe(options.step.owner),
411422 }) catch @panic("OOM");
lib/std/Build/Step/Run.zig+9-8
......@@ -1015,16 +1015,17 @@ fn populateGeneratedPaths(
10151015 }
10161016}
10171017
1018fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer, comptime fmt: []const u8) !void {
1019 comptime assert(fmt.len == 0);
1018fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer) std.io.Writer.Error!void {
10201019 if (term) |t| switch (t) {
1021 .Exited => |code| try w.print("exited with code {}", .{code}),
1022 .Signal => |sig| try w.print("terminated with signal {}", .{sig}),
1023 .Stopped => |sig| try w.print("stopped with signal {}", .{sig}),
1024 .Unknown => |code| try w.print("terminated for unknown reason with code {}", .{code}),
1025 } else try w.writeAll("exited with any code");
1020 .Exited => |code| try w.print("exited with code {d}", .{code}),
1021 .Signal => |sig| try w.print("terminated with signal {d}", .{sig}),
1022 .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
1023 .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
1024 } else {
1025 try w.writeAll("exited with any code");
1026 }
10261027}
1027fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) {
1028fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(?std.process.Child.Term, formatTerm) {
10281029 return .{ .data = term };
10291030}
10301031
lib/std/Build/Watch.zig+1-1
......@@ -659,7 +659,7 @@ const Os = switch (builtin.os.tag) {
659659 path.root_dir.handle.fd
660660 else
661661 posix.openat(path.root_dir.handle.fd, path.sub_path, dir_open_flags, 0) catch |err| {
662 fatal("failed to open directory {}: {s}", .{ path, @errorName(err) });
662 fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) });
663663 };
664664 // Empirically the dir has to stay open or else no events are triggered.
665665 errdefer if (!skip_open_dir) posix.close(dir_fd);
lib/std/SemanticVersion.zig+7-12
......@@ -150,15 +150,10 @@ fn parseNum(text: []const u8) error{ InvalidVersion, Overflow }!usize {
150150 };
151151}
152152
153pub fn format(
154 self: Version,
155 bw: *std.io.Writer,
156 comptime fmt: []const u8,
157) !void {
158 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
159 try bw.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch });
160 if (self.pre) |pre| try bw.print("-{s}", .{pre});
161 if (self.build) |build| try bw.print("+{s}", .{build});
153pub fn format(self: Version, w: *std.io.Writer) std.io.Writer.Error!void {
154 try w.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch });
155 if (self.pre) |pre| try w.print("-{s}", .{pre});
156 if (self.build) |build| try w.print("+{s}", .{build});
162157}
163158
164159const expect = std.testing.expect;
......@@ -200,7 +195,7 @@ test format {
200195 "1.0.0+0.build.1-rc.10000aaa-kk-0.1",
201196 "5.4.0-1018-raspi",
202197 "5.7.123",
203 }) |valid| try std.testing.expectFmt(valid, "{}", .{try parse(valid)});
198 }) |valid| try std.testing.expectFmt(valid, "{f}", .{try parse(valid)});
204199
205200 // Invalid version strings should be rejected.
206201 for ([_][]const u8{
......@@ -267,12 +262,12 @@ test format {
267262 // Valid version string that may overflow.
268263 const big_valid = "99999999999999999999999.999999999999999999.99999999999999999";
269264 if (parse(big_valid)) |ver| {
270 try std.testing.expectFmt(big_valid, "{}", .{ver});
265 try std.testing.expectFmt(big_valid, "{f}", .{ver});
271266 } else |err| try expect(err == error.Overflow);
272267
273268 // Invalid version string that may overflow.
274269 const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12";
275 if (parse(big_invalid)) |ver| std.debug.panic("expected error, found {}", .{ver}) else |_| {}
270 if (parse(big_invalid)) |ver| std.debug.panic("expected error, found {f}", .{ver}) else |_| {}
276271}
277272
278273test "precedence" {
lib/std/Target.zig+8-19
......@@ -301,24 +301,13 @@ pub const Os = struct {
301301
302302 /// This function is defined to serialize a Zig source code representation of this
303303 /// type, that, when parsed, will deserialize into the same data.
304 pub fn format(ver: WindowsVersion, bw: *std.io.Writer, comptime fmt_str: []const u8) std.io.Writer.Error!void {
305 const maybe_name = std.enums.tagName(WindowsVersion, ver);
306 if (comptime std.mem.eql(u8, fmt_str, "s")) {
307 if (maybe_name) |name|
308 try bw.print(".{s}", .{name})
309 else
310 try bw.print(".{d}", .{@intFromEnum(ver)});
311 } else if (comptime std.mem.eql(u8, fmt_str, "c")) {
312 if (maybe_name) |name|
313 try bw.print(".{s}", .{name})
314 else
315 try bw.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)});
316 } else if (fmt_str.len == 0) {
317 if (maybe_name) |name|
318 try bw.print("WindowsVersion.{s}", .{name})
319 else
320 try bw.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)});
321 } else std.fmt.invalidFmtError(fmt_str, ver);
304 pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void {
305 if (std.enums.tagName(WindowsVersion, wv)) |name| {
306 var vecs: [2][]const u8 = .{ ".", name };
307 return w.writeVecAll(&vecs);
308 } else {
309 return w.print("@enumFromInt(0x{X:0>8})", .{wv});
310 }
322311 }
323312 };
324313
......@@ -1686,7 +1675,7 @@ pub const Cpu = struct {
16861675 pub fn fromCallingConvention(cc: std.builtin.CallingConvention.Tag) []const Arch {
16871676 return switch (cc) {
16881677 .auto,
1689 .@"async",
1678 .async,
16901679 .naked,
16911680 .@"inline",
16921681 => unreachable,
lib/std/Thread.zig+21-1
......@@ -165,10 +165,18 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
165165 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
166166 const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only });
167167 defer file.close();
168<<<<<<< HEAD
168169 var fw = file.writer(&.{});
169170 fw.interface.writeAll(name) catch |err| switch (err) {
170171 error.WriteFailed => return fw.err.?,
171172 };
173||||||| edf785db0f
174
175 try file.writer().writeAll(name);
176=======
177
178 try file.deprecatedWriter().writeAll(name);
179>>>>>>> origin/master
172180 return;
173181 },
174182 .windows => {
......@@ -280,11 +288,23 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
280288 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
281289 const file = try std.fs.cwd().openFile(path, .{});
282290 defer file.close();
291<<<<<<< HEAD
283292 var fr = file.reader(&.{});
284293 const n = fr.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) {
285294 error.ReadFailed => return fr.err.?,
286295 };
287296 return if (n == 0) null else buffer[0 .. n - 1];
297||||||| edf785db0f
298
299 const data_len = try file.reader().readAll(buffer_ptr[0 .. max_name_len + 1]);
300
301 return if (data_len >= 1) buffer[0 .. data_len - 1] else null;
302=======
303
304 const data_len = try file.deprecatedReader().readAll(buffer_ptr[0 .. max_name_len + 1]);
305
306 return if (data_len >= 1) buffer[0 .. data_len - 1] else null;
307>>>>>>> origin/master
288308 },
289309 .windows => {
290310 const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);
......@@ -1164,7 +1184,7 @@ const LinuxThreadImpl = struct {
11641184
11651185 fn getCurrentId() Id {
11661186 return tls_thread_id orelse {
1167 const tid = @as(u32, @bitCast(linux.gettid()));
1187 const tid: u32 = @bitCast(linux.gettid());
11681188 tls_thread_id = tid;
11691189 return tid;
11701190 };
lib/std/Uri.zig+148-106
......@@ -3,12 +3,10 @@
33
44const std = @import("std.zig");
55const testing = std.testing;
6const Uri = @This();
67const Allocator = std.mem.Allocator;
7const assert = std.debug.assert;
88const Writer = std.io.Writer;
99
10const Uri = @This();
11
1210scheme: []const u8,
1311user: ?Component = null,
1412password: ?Component = null,
......@@ -65,7 +63,7 @@ pub const Component = union(enum) {
6563 return switch (component) {
6664 .raw => |raw| raw,
6765 .percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_|
68 try std.fmt.bufPrint(buffer, "{fraw}", .{component})
66 try std.fmt.bufPrint(buffer, "{f}", .{std.fmt.alt(component, .formatRaw)})
6967 else
7068 percent_encoded,
7169 };
......@@ -85,16 +83,9 @@ pub const Component = union(enum) {
8583 };
8684 }
8785
88 pub fn format(component: Component, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
89 if (fmt.len == 0) {
90 try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{
91 @tagName(component),
92 std.zig.fmtEscapes(switch (component) {
93 .raw, .percent_encoded => |string| string,
94 }),
95 });
96 } else if (comptime std.mem.eql(u8, fmt, "raw")) switch (component) {
97 .raw => |raw| try bw.writeAll(raw),
86 pub fn formatRaw(component: Component, w: *Writer) Writer.Error!void {
87 switch (component) {
88 .raw => |raw| try w.writeAll(raw),
9889 .percent_encoded => |percent_encoded| {
9990 var start: usize = 0;
10091 var index: usize = 0;
......@@ -103,51 +94,75 @@ pub const Component = union(enum) {
10394 if (percent_encoded.len - index < 2) continue;
10495 const percent_encoded_char =
10596 std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue;
106 try bw.print("{s}{c}", .{
97 try w.print("{s}{c}", .{
10798 percent_encoded[start..percent],
10899 percent_encoded_char,
109100 });
110101 start = percent + 3;
111102 index = percent + 3;
112103 }
113 try bw.writeAll(percent_encoded[start..]);
104 try w.writeAll(percent_encoded[start..]);
114105 },
115 } else if (comptime std.mem.eql(u8, fmt, "%")) switch (component) {
116 .raw => |raw| try percentEncode(bw, raw, isUnreserved),
117 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
118 } else if (comptime std.mem.eql(u8, fmt, "user")) switch (component) {
119 .raw => |raw| try percentEncode(bw, raw, isUserChar),
120 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
121 } else if (comptime std.mem.eql(u8, fmt, "password")) switch (component) {
122 .raw => |raw| try percentEncode(bw, raw, isPasswordChar),
123 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
124 } else if (comptime std.mem.eql(u8, fmt, "host")) switch (component) {
125 .raw => |raw| try percentEncode(bw, raw, isHostChar),
126 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
127 } else if (comptime std.mem.eql(u8, fmt, "path")) switch (component) {
128 .raw => |raw| try percentEncode(bw, raw, isPathChar),
129 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
130 } else if (comptime std.mem.eql(u8, fmt, "query")) switch (component) {
131 .raw => |raw| try percentEncode(bw, raw, isQueryChar),
132 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
133 } else if (comptime std.mem.eql(u8, fmt, "fragment")) switch (component) {
134 .raw => |raw| try percentEncode(bw, raw, isFragmentChar),
135 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),
136 } else @compileError("invalid format string '" ++ fmt ++ "'");
106 }
107 }
108
109 pub fn formatEscaped(component: Component, w: *Writer) Writer.Error!void {
110 switch (component) {
111 .raw => |raw| try percentEncode(w, raw, isUnreserved),
112 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
113 }
114 }
115
116 pub fn formatUser(component: Component, w: *Writer) Writer.Error!void {
117 switch (component) {
118 .raw => |raw| try percentEncode(w, raw, isUserChar),
119 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
120 }
121 }
122
123 pub fn formatPassword(component: Component, w: *Writer) Writer.Error!void {
124 switch (component) {
125 .raw => |raw| try percentEncode(w, raw, isPasswordChar),
126 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
127 }
128 }
129
130 pub fn formatHost(component: Component, w: *Writer) Writer.Error!void {
131 switch (component) {
132 .raw => |raw| try percentEncode(w, raw, isHostChar),
133 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
134 }
135 }
136
137 pub fn formatPath(component: Component, w: *Writer) Writer.Error!void {
138 switch (component) {
139 .raw => |raw| try percentEncode(w, raw, isPathChar),
140 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
141 }
137142 }
138143
139 pub fn percentEncode(
140 bw: *Writer,
141 raw: []const u8,
142 comptime isValidChar: fn (u8) bool,
143 ) Writer.Error!void {
144 pub fn formatQuery(component: Component, w: *Writer) Writer.Error!void {
145 switch (component) {
146 .raw => |raw| try percentEncode(w, raw, isQueryChar),
147 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
148 }
149 }
150
151 pub fn formatFragment(component: Component, w: *Writer) Writer.Error!void {
152 switch (component) {
153 .raw => |raw| try percentEncode(w, raw, isFragmentChar),
154 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
155 }
156 }
157
158 pub fn percentEncode(w: *Writer, raw: []const u8, comptime isValidChar: fn (u8) bool) Writer.Error!void {
144159 var start: usize = 0;
145160 for (raw, 0..) |char, index| {
146161 if (isValidChar(char)) continue;
147 try bw.print("{s}%{X:0>2}", .{ raw[start..index], char });
162 try w.print("{s}%{X:0>2}", .{ raw[start..index], char });
148163 start = index + 1;
149164 }
150 try bw.writeAll(raw[start..]);
165 try w.writeAll(raw[start..]);
151166 }
152167};
153168
......@@ -264,76 +279,91 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri {
264279 return uri;
265280}
266281
267pub const WriteToStreamOptions = struct {
268 /// When true, include the scheme part of the URI.
269 scheme: bool = false,
270 /// When true, include the user and password part of the URI. Ignored if `authority` is false.
271 authentication: bool = false,
272 /// When true, include the authority part of the URI.
273 authority: bool = false,
274 /// When true, include the path part of the URI.
275 path: bool = false,
276 /// When true, include the query part of the URI. Ignored when `path` is false.
277 query: bool = false,
278 /// When true, include the fragment part of the URI. Ignored when `path` is false.
279 fragment: bool = false,
280 /// When true, include the port part of the URI. Ignored when `port` is null.
281 port: bool = true,
282};
282pub fn format(uri: *const Uri, writer: *Writer) Writer.Error!void {
283 return writeToStream(uri, writer, .all);
284}
283285
284pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *Writer) Writer.Error!void {
285 if (options.scheme) {
286 try bw.print("{s}:", .{uri.scheme});
287 if (options.authority and uri.host != null) {
288 try bw.writeAll("//");
286pub fn writeToStream(uri: *const Uri, writer: *Writer, flags: Format.Flags) Writer.Error!void {
287 if (flags.scheme) {
288 try writer.print("{s}:", .{uri.scheme});
289 if (flags.authority and uri.host != null) {
290 try writer.writeAll("//");
289291 }
290292 }
291 if (options.authority) {
292 if (options.authentication and uri.host != null) {
293 if (flags.authority) {
294 if (flags.authentication and uri.host != null) {
293295 if (uri.user) |user| {
294 try bw.print("{fuser}", .{user});
296 try user.formatUser(writer);
295297 if (uri.password) |password| {
296 try bw.print(":{fpassword}", .{password});
298 try writer.writeByte(':');
299 try password.formatPassword(writer);
297300 }
298 try bw.writeByte('@');
301 try writer.writeByte('@');
299302 }
300303 }
301304 if (uri.host) |host| {
302 try bw.print("{fhost}", .{host});
303 if (options.port) {
304 if (uri.port) |port| try bw.print(":{d}", .{port});
305 try host.formatHost(writer);
306 if (flags.port) {
307 if (uri.port) |port| try writer.print(":{d}", .{port});
305308 }
306309 }
307310 }
308 if (options.path) {
309 try bw.print("{fpath}", .{
310 if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path,
311 });
312 if (options.query) {
313 if (uri.query) |query| try bw.print("?{fquery}", .{query});
311 if (flags.path) {
312 const uri_path: Component = if (uri.path.isEmpty()) .{ .percent_encoded = "/" } else uri.path;
313 try uri_path.formatPath(writer);
314 if (flags.query) {
315 if (uri.query) |query| {
316 try writer.writeByte('?');
317 try query.formatQuery(writer);
318 }
314319 }
315 if (options.fragment) {
316 if (uri.fragment) |fragment| try bw.print("#{ffragment}", .{fragment});
320 if (flags.fragment) {
321 if (uri.fragment) |fragment| {
322 try writer.writeByte('#');
323 try fragment.formatFragment(writer);
324 }
317325 }
318326 }
319327}
320328
321pub fn format(uri: Uri, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
322 const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0;
323 const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0;
324 const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0;
325 const path = comptime std.mem.indexOfScalar(u8, fmt, '/') != null or fmt.len == 0;
326 const query = comptime std.mem.indexOfScalar(u8, fmt, '?') != null or fmt.len == 0;
327 const fragment = comptime std.mem.indexOfScalar(u8, fmt, '#') != null or fmt.len == 0;
329pub const Format = struct {
330 uri: *const Uri,
331 flags: Flags = .{},
332
333 pub const Flags = struct {
334 /// When true, include the scheme part of the URI.
335 scheme: bool = false,
336 /// When true, include the user and password part of the URI. Ignored if `authority` is false.
337 authentication: bool = false,
338 /// When true, include the authority part of the URI.
339 authority: bool = false,
340 /// When true, include the path part of the URI.
341 path: bool = false,
342 /// When true, include the query part of the URI. Ignored when `path` is false.
343 query: bool = false,
344 /// When true, include the fragment part of the URI. Ignored when `path` is false.
345 fragment: bool = false,
346 /// When true, include the port part of the URI. Ignored when `port` is null.
347 port: bool = true,
348
349 pub const all: Flags = .{
350 .scheme = true,
351 .authentication = true,
352 .authority = true,
353 .path = true,
354 .query = true,
355 .fragment = true,
356 .port = true,
357 };
358 };
328359
329 return writeToStream(uri, .{
330 .scheme = scheme,
331 .authentication = authentication,
332 .authority = authority,
333 .path = path,
334 .query = query,
335 .fragment = fragment,
336 }, bw);
360 pub fn default(f: Format, writer: *Writer) Writer.Error!void {
361 return writeToStream(f.uri, writer, f.flags);
362 }
363};
364
365pub fn fmt(uri: *const Uri, flags: Format.Flags) std.fmt.Formatter(Format, Format.default) {
366 return .{ .data = .{ .uri = uri, .flags = flags } };
337367}
338368
339369/// The return value will contain strings pointing into the original `text`.
......@@ -464,9 +494,8 @@ test remove_dot_segments {
464494fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component {
465495 var aux: Writer = .fixed(aux_buf.*);
466496 if (!base.isEmpty()) {
467 aux.print("{fpath}", .{base}) catch return error.NoSpaceLeft;
468 aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse
469 return remove_dot_segments(new);
497 base.formatPath(&aux) catch return error.NoSpaceLeft;
498 aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse return remove_dot_segments(new);
470499 }
471500 aux.print("/{s}", .{new}) catch return error.NoSpaceLeft;
472501 const merged_path = remove_dot_segments(aux.buffered());
......@@ -745,8 +774,11 @@ test "Special test" {
745774test "URI percent encoding" {
746775 try std.testing.expectFmt(
747776 "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad",
748 "{%}",
749 .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }},
777 "{f}",
778 .{std.fmt.alt(
779 @as(Component, .{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }),
780 .formatEscaped,
781 )},
750782 );
751783}
752784
......@@ -755,7 +787,10 @@ test "URI percent decoding" {
755787 const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad";
756788 var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*;
757789
758 try std.testing.expectFmt(expected, "{fraw}", .{Component{ .percent_encoded = &input }});
790 try std.testing.expectFmt(expected, "{f}", .{std.fmt.alt(
791 @as(Component, .{ .percent_encoded = &input }),
792 .formatRaw,
793 )});
759794
760795 var output: [expected.len]u8 = undefined;
761796 try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);
......@@ -767,7 +802,10 @@ test "URI percent decoding" {
767802 const expected = "/abc%";
768803 var input = expected.*;
769804
770 try std.testing.expectFmt(expected, "{fraw}", .{Component{ .percent_encoded = &input }});
805 try std.testing.expectFmt(expected, "{f}", .{std.fmt.alt(
806 @as(Component, .{ .percent_encoded = &input }),
807 .formatRaw,
808 )});
771809
772810 var output: [expected.len]u8 = undefined;
773811 try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);
......@@ -781,7 +819,9 @@ test "URI query encoding" {
781819 const parsed = try Uri.parse(address);
782820
783821 // format the URI to percent encode it
784 try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{/?}", .{parsed});
822 try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{f}", .{
823 parsed.fmt(.{ .path = true, .query = true }),
824 });
785825}
786826
787827test "format" {
......@@ -795,7 +835,9 @@ test "format" {
795835 .query = null,
796836 .fragment = null,
797837 };
798 try std.testing.expectFmt("file:/foo/bar/baz", "{;/?#}", .{uri});
838 try std.testing.expectFmt("file:/foo/bar/baz", "{f}", .{
839 uri.fmt(.{ .scheme = true, .path = true, .query = true, .fragment = true }),
840 });
799841}
800842
801843test "URI malformed input" {
lib/std/array_list.zig+4-3
......@@ -339,9 +339,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
339339 }
340340
341341 pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
342 const gpa = self.allocator;
342343 var unmanaged = self.moveToUnmanaged();
343 try unmanaged.print(self.allocator, fmt, args);
344 self.* = unmanaged.toManaged(self.allocator);
344 defer self.* = unmanaged.toManaged(gpa);
345 try unmanaged.print(gpa, fmt, args);
345346 }
346347
347348 /// Append a value to the list `n` times.
......@@ -907,7 +908,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
907908 try self.ensureUnusedCapacity(gpa, fmt.len);
908909 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self);
909910 defer self.* = aw.toArrayList();
910 return aw.interface.print(fmt, args) catch |err| switch (err) {
911 return aw.writer.print(fmt, args) catch |err| switch (err) {
911912 error.WriteFailed => return error.OutOfMemory,
912913 };
913914 }
lib/std/ascii.zig+45
......@@ -10,6 +10,10 @@
1010
1111const std = @import("std");
1212
13pub const lowercase = "abcdefghijklmnopqrstuvwxyz";
14pub const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
15pub const letters = lowercase ++ uppercase;
16
1317/// The C0 control codes of the ASCII encoding.
1418///
1519/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes and `isControl`
......@@ -435,3 +439,44 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {
435439pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
436440 return orderIgnoreCase(lhs, rhs) == .lt;
437441}
442
443pub const HexEscape = struct {
444 bytes: []const u8,
445 charset: *const [16]u8,
446
447 pub const upper_charset = "0123456789ABCDEF";
448 pub const lower_charset = "0123456789abcdef";
449
450 pub fn format(se: HexEscape, w: *std.io.Writer) std.io.Writer.Error!void {
451 const charset = se.charset;
452
453 var buf: [4]u8 = undefined;
454 buf[0] = '\\';
455 buf[1] = 'x';
456
457 for (se.bytes) |c| {
458 if (std.ascii.isPrint(c)) {
459 try w.writeByte(c);
460 } else {
461 buf[2] = charset[c >> 4];
462 buf[3] = charset[c & 15];
463 try w.writeAll(&buf);
464 }
465 }
466 }
467};
468
469/// Replaces non-ASCII bytes with hex escapes.
470pub fn hexEscape(bytes: []const u8, case: std.fmt.Case) std.fmt.Formatter(HexEscape, HexEscape.format) {
471 return .{ .data = .{ .bytes = bytes, .charset = switch (case) {
472 .lower => HexEscape.lower_charset,
473 .upper => HexEscape.upper_charset,
474 } } };
475}
476
477test hexEscape {
478 try std.testing.expectFmt("abc 123", "{f}", .{hexEscape("abc 123", .lower)});
479 try std.testing.expectFmt("ab\\xffc", "{f}", .{hexEscape("ab\xffc", .lower)});
480 try std.testing.expectFmt("abc 123", "{f}", .{hexEscape("abc 123", .upper)});
481 try std.testing.expectFmt("ab\\xFFc", "{f}", .{hexEscape("ab\xffc", .upper)});
482}
lib/std/base64.zig+3-3
......@@ -108,7 +108,7 @@ pub const Base64Encoder = struct {
108108 }
109109 }
110110
111 // dest must be compatible with std.io.Writer's writeAll interface
111 // dest must be compatible with std.io.GenericWriter's writeAll interface
112112 pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void {
113113 var chunker = window(u8, source, 3, 3);
114114 while (chunker.next()) |chunk| {
......@@ -118,8 +118,8 @@ pub const Base64Encoder = struct {
118118 }
119119 }
120120
121 // destWriter must be compatible with std.io.Writer's writeAll interface
122 // sourceReader must be compatible with std.io.Reader's read interface
121 // destWriter must be compatible with std.io.GenericWriter's writeAll interface
122 // sourceReader must be compatible with `std.io.GenericReader` read interface
123123 pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void {
124124 while (true) {
125125 var tempSource: [3]u8 = undefined;
lib/std/bounded_array.zig+2-2
......@@ -277,7 +277,7 @@ pub fn BoundedArrayAligned(
277277 @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++
278278 "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)")
279279 else
280 std.io.Writer(*Self, error{Overflow}, appendWrite);
280 std.io.GenericWriter(*Self, error{Overflow}, appendWrite);
281281
282282 /// Initializes a writer which will write into the array.
283283 pub fn writer(self: *Self) Writer {
......@@ -285,7 +285,7 @@ pub fn BoundedArrayAligned(
285285 }
286286
287287 /// Same as `appendSlice` except it returns the number of bytes written, which is always the same
288 /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.
288 /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API.
289289 fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize {
290290 try self.appendSlice(m);
291291 return m.len;
lib/std/builtin.zig+9-22
......@@ -34,23 +34,21 @@ pub const StackTrace = struct {
3434 index: usize,
3535 instruction_addresses: []usize,
3636
37 pub fn format(st: StackTrace, bw: *std.io.Writer, comptime fmt: []const u8) !void {
38 comptime if (fmt.len != 0) unreachable;
39
37 pub fn format(self: StackTrace, writer: *std.io.Writer) std.io.Writer.Error!void {
4038 // TODO: re-evaluate whether to use format() methods at all.
4139 // Until then, avoid an error when using DebugAllocator with WebAssembly
4240 // where it tries to call detectTTYConfig here.
4341 if (builtin.os.tag == .freestanding) return 0;
4442
4543 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
46 return bw.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{
44 return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{
4745 @errorName(err),
4846 });
4947 };
50 const tty_config = std.io.tty.detectConfig(.stderr());
51 try bw.writeAll("\n");
52 std.debug.writeStackTrace(st, bw, debug_info, tty_config) catch |err| {
53 try bw.print("Unable to print stack trace: {s}\n", .{@errorName(err)});
48 const tty_config = std.io.tty.detectConfig(std.fs.File.stderr());
49 try writer.writeAll("\n");
50 std.debug.writeStackTrace(self, writer, debug_info, tty_config) catch |err| {
51 try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)});
5452 };
5553 }
5654};
......@@ -195,8 +193,6 @@ pub const CallingConvention = union(enum(u8)) {
195193 pub const C: CallingConvention = .c;
196194 /// Deprecated; use `.naked`.
197195 pub const Naked: CallingConvention = .naked;
198 /// Deprecated; use `.@"async"`.
199 pub const Async: CallingConvention = .@"async";
200196 /// Deprecated; use `.@"inline"`.
201197 pub const Inline: CallingConvention = .@"inline";
202198 /// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`.
......@@ -244,7 +240,7 @@ pub const CallingConvention = union(enum(u8)) {
244240 /// The calling convention of a function that can be called with `async` syntax. An `async` call
245241 /// of a runtime-known function must target a function with this calling convention.
246242 /// Comptime-known functions with other calling conventions may be coerced to this one.
247 @"async",
243 async,
248244
249245 /// Functions with this calling convention have no prologue or epilogue, making the function
250246 /// uncallable in regular Zig code. This can be useful when integrating with assembly.
......@@ -847,7 +843,7 @@ pub const LinkMode = enum {
847843pub const UnwindTables = enum {
848844 none,
849845 sync,
850 @"async",
846 async,
851847};
852848
853849/// This data structure is used by the Zig language code generation and
......@@ -862,32 +858,23 @@ pub const WasiExecModel = enum {
862858pub const CallModifier = enum {
863859 /// Equivalent to function call syntax.
864860 auto,
865
866 /// Equivalent to async keyword used with function call syntax.
867 async_kw,
868
869861 /// Prevents tail call optimization. This guarantees that the return
870862 /// address will point to the callsite, as opposed to the callsite's
871863 /// callsite. If the call is otherwise required to be tail-called
872864 /// or inlined, a compile error is emitted instead.
873865 never_tail,
874
875866 /// Guarantees that the call will not be inlined. If the call is
876867 /// otherwise required to be inlined, a compile error is emitted instead.
877868 never_inline,
878
879869 /// Asserts that the function call will not suspend. This allows a
880870 /// non-async function to call an async function.
881 no_async,
882
871 no_suspend,
883872 /// Guarantees that the call will be generated with tail call optimization.
884873 /// If this is not possible, a compile error is emitted instead.
885874 always_tail,
886
887875 /// Guarantees that the call will be inlined at the callsite.
888876 /// If this is not possible, a compile error is emitted instead.
889877 always_inline,
890
891878 /// Evaluates the call at compile-time. If the call cannot be completed at
892879 /// compile-time, a compile error is emitted instead.
893880 compile_time,
lib/std/c.zig+5-1
......@@ -10412,7 +10412,10 @@ pub const sigfillset = switch (native_os) {
1041210412};
1041310413
1041410414pub const sigaddset = private.sigaddset;
10415pub const sigemptyset = private.sigemptyset;
10415pub const sigemptyset = switch (native_os) {
10416 .netbsd => private.__sigemptyset14,
10417 else => private.sigemptyset,
10418};
1041610419pub const sigdelset = private.sigdelset;
1041710420pub const sigismember = private.sigismember;
1041810421
......@@ -11268,6 +11271,7 @@ const private = struct {
1126811271 extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
1126911272 extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int;
1127011273 extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
11274 extern "c" fn __sigemptyset14(set: ?*sigset_t) c_int;
1127111275 extern "c" fn __sigfillset14(set: ?*sigset_t) c_int;
1127211276 extern "c" fn __sigprocmask14(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
1127311277 extern "c" fn __socket30(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
lib/std/compress/xz.zig+1-1
......@@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type {
3434 const Self = @This();
3535
3636 pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error;
37 pub const Reader = std.io.Reader(*Self, Error, read);
37 pub const Reader = std.io.GenericReader(*Self, Error, read);
3838
3939 allocator: Allocator,
4040 block_decoder: block.Decoder(ReaderType),
lib/std/compress/xz/block.zig+1-1
......@@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type {
2727 ReaderType.Error ||
2828 DecodeError ||
2929 Allocator.Error;
30 pub const Reader = std.io.Reader(*Self, Error, read);
30 pub const Reader = std.io.GenericReader(*Self, Error, read);
3131
3232 allocator: Allocator,
3333 inner_reader: ReaderType,
lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig+1-1
......@@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void {
4545 self.data.ptr = begin;
4646}
4747
48pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize);
48pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize);
4949/// Warning: This writer writes backwards. `fn print` will NOT work as expected.
5050pub fn writer(self: *ArrayListReverse) Writer {
5151 return .{ .context = self };
lib/std/crypto/sha2.zig+16
......@@ -383,12 +383,28 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {
383383 for (&d.s, v) |*dv, vv| dv.* +%= vv;
384384 }
385385
386<<<<<<< HEAD
386387 pub fn writer(this: *@This(), buffer: []u8) Writer {
387388 return .{
388389 .context = this,
389390 .vtable = &.{ .drain = drain },
390391 .buffer = buffer,
391392 };
393||||||| edf785db0f
394 pub const Error = error{};
395 pub const Writer = std.io.Writer(*Self, Error, write);
396
397 fn write(self: *Self, bytes: []const u8) Error!usize {
398 self.update(bytes);
399 return bytes.len;
400=======
401 pub const Error = error{};
402 pub const Writer = std.io.GenericWriter(*Self, Error, write);
403
404 fn write(self: *Self, bytes: []const u8) Error!usize {
405 self.update(bytes);
406 return bytes.len;
407>>>>>>> origin/master
392408 }
393409
394410 fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
lib/std/debug.zig+38-31
......@@ -222,7 +222,8 @@ pub fn unlockStderrWriter() void {
222222/// Print to stderr, unbuffered, and silently returning on failure. Intended
223223/// for use in "printf debugging". Use `std.log` functions for proper logging.
224224pub fn print(comptime fmt: []const u8, args: anytype) void {
225 const bw = lockStderrWriter(&.{});
225 var buffer: [32]u8 = undefined;
226 const bw = lockStderrWriter(&buffer);
226227 defer unlockStderrWriter();
227228 nosuspend bw.print(fmt, args) catch return;
228229}
......@@ -307,7 +308,7 @@ test dumpHexFallible {
307308 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
308309 defer aw.deinit();
309310
310 try dumpHexFallible(&aw.interface, .no_color, bytes);
311 try dumpHexFallible(&aw.writer, .no_color, bytes);
311312 const expected = try std.fmt.allocPrint(std.testing.allocator,
312313 \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........
313314 \\{x:0>[2]} 01 12 13 ...
......@@ -1228,9 +1229,9 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi
12281229}
12291230
12301231test printLineFromFileAnyOs {
1231 var output = std.ArrayList(u8).init(std.testing.allocator);
1232 defer output.deinit();
1233 const output_stream = output.writer();
1232 var aw: Writer.Allocating = .init(std.testing.allocator);
1233 defer aw.deinit();
1234 const output_stream = &aw.writer;
12341235
12351236 const allocator = std.testing.allocator;
12361237 const join = std.fs.path.join;
......@@ -1252,8 +1253,8 @@ test printLineFromFileAnyOs {
12521253 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
12531254
12541255 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1255 try expectEqualStrings("no new lines in this file, but one is printed anyway\n", output.items);
1256 output.clearRetainingCapacity();
1256 try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.getWritten());
1257 aw.clearRetainingCapacity();
12571258 }
12581259 {
12591260 const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
......@@ -1268,12 +1269,12 @@ test printLineFromFileAnyOs {
12681269 });
12691270
12701271 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1271 try expectEqualStrings("1\n", output.items);
1272 output.clearRetainingCapacity();
1272 try expectEqualStrings("1\n", aw.getWritten());
1273 aw.clearRetainingCapacity();
12731274
12741275 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
1275 try expectEqualStrings("3\n", output.items);
1276 output.clearRetainingCapacity();
1276 try expectEqualStrings("3\n", aw.getWritten());
1277 aw.clearRetainingCapacity();
12771278 }
12781279 {
12791280 const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
......@@ -1282,14 +1283,17 @@ test printLineFromFileAnyOs {
12821283 defer allocator.free(path);
12831284
12841285 const overlap = 10;
1285 var writer = file.writer();
1286 var buf: [16]u8 = undefined;
1287 var file_writer = file.writer(&buf);
1288 const writer = &file_writer.interface;
12861289 try writer.splatByteAll('a', std.heap.page_size_min - overlap);
12871290 try writer.writeByte('\n');
12881291 try writer.splatByteAll('a', overlap);
1292 try writer.flush();
12891293
12901294 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
1291 try expectEqualStrings(("a" ** overlap) ++ "\n", output.items);
1292 output.clearRetainingCapacity();
1295 try expectEqualStrings(("a" ** overlap) ++ "\n", aw.getWritten());
1296 aw.clearRetainingCapacity();
12931297 }
12941298 {
12951299 const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
......@@ -1297,12 +1301,13 @@ test printLineFromFileAnyOs {
12971301 const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
12981302 defer allocator.free(path);
12991303
1300 var writer = file.writer();
1304 var file_writer = file.writer(&.{});
1305 const writer = &file_writer.interface;
13011306 try writer.splatByteAll('a', std.heap.page_size_max);
13021307
13031308 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1304 try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items);
1305 output.clearRetainingCapacity();
1309 try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.getWritten());
1310 aw.clearRetainingCapacity();
13061311 }
13071312 {
13081313 const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
......@@ -1310,24 +1315,25 @@ test printLineFromFileAnyOs {
13101315 const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
13111316 defer allocator.free(path);
13121317
1313 var writer = file.writer();
1318 var file_writer = file.writer(&.{});
1319 const writer = &file_writer.interface;
13141320 try writer.splatByteAll('a', 3 * std.heap.page_size_max);
13151321
13161322 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
13171323
13181324 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1319 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", output.items);
1320 output.clearRetainingCapacity();
1325 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.getWritten());
1326 aw.clearRetainingCapacity();
13211327
13221328 try writer.writeAll("a\na");
13231329
13241330 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1325 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", output.items);
1326 output.clearRetainingCapacity();
1331 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.getWritten());
1332 aw.clearRetainingCapacity();
13271333
13281334 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
1329 try expectEqualStrings("a\n", output.items);
1330 output.clearRetainingCapacity();
1335 try expectEqualStrings("a\n", aw.getWritten());
1336 aw.clearRetainingCapacity();
13311337 }
13321338 {
13331339 const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
......@@ -1335,18 +1341,19 @@ test printLineFromFileAnyOs {
13351341 const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
13361342 defer allocator.free(path);
13371343
1338 var writer = file.writer();
1344 var file_writer = file.writer(&.{});
1345 const writer = &file_writer.interface;
13391346 const real_file_start = 3 * std.heap.page_size_min;
13401347 try writer.splatByteAll('\n', real_file_start);
13411348 try writer.writeAll("abc\ndef");
13421349
13431350 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
1344 try expectEqualStrings("abc\n", output.items);
1345 output.clearRetainingCapacity();
1351 try expectEqualStrings("abc\n", aw.getWritten());
1352 aw.clearRetainingCapacity();
13461353
13471354 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
1348 try expectEqualStrings("def\n", output.items);
1349 output.clearRetainingCapacity();
1355 try expectEqualStrings("def\n", aw.getWritten());
1356 aw.clearRetainingCapacity();
13501357 }
13511358}
13521359
......@@ -1597,10 +1604,10 @@ test "manage resources correctly" {
15971604 // self-hosted debug info is still too buggy
15981605 if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest;
15991606
1600 const writer = std.io.null_writer;
1607 var discarding: std.io.Writer.Discarding = .init(&.{});
16011608 var di = try SelfInfo.open(testing.allocator);
16021609 defer di.deinit();
1603 try printSourceAtAddress(&di, writer, showMyTrace(), io.tty.detectConfig(.stderr()));
1610 try printSourceAtAddress(&di, &discarding.writer, showMyTrace(), io.tty.detectConfig(.stderr()));
16041611}
16051612
16061613noinline fn showMyTrace() usize {
lib/std/debug/Pdb.zig+3-3
......@@ -395,7 +395,7 @@ const Msf = struct {
395395 streams: []MsfStream,
396396
397397 fn init(allocator: Allocator, file: File) !Msf {
398 const in = file.reader();
398 const in = file.deprecatedReader();
399399
400400 const superblock = try in.takeStruct(pdb.SuperBlock);
401401
......@@ -514,7 +514,7 @@ const MsfStream = struct {
514514 var offset = self.pos % self.block_size;
515515
516516 try self.in_file.seekTo(block * self.block_size + offset);
517 const in = self.in_file.reader();
517 const in = self.in_file.deprecatedReader();
518518
519519 var size: usize = 0;
520520 var rem_buffer = buffer;
......@@ -562,7 +562,7 @@ const MsfStream = struct {
562562 return block * self.block_size + offset;
563563 }
564564
565 pub fn reader(self: *MsfStream) std.io.Reader(*MsfStream, Error, read) {
565 pub fn reader(self: *MsfStream) std.io.GenericReader(*MsfStream, Error, read) {
566566 return .{ .context = self };
567567 }
568568};
lib/std/elf.zig+164
......@@ -508,6 +508,7 @@ pub const Header = struct {
508508 };
509509 }
510510
511<<<<<<< HEAD
511512 pub const ReadError = std.io.Reader.Error || ParseError;
512513
513514 pub fn read(r: *std.io.Reader) ReadError!Header {
......@@ -515,6 +516,19 @@ pub const Header = struct {
515516 const result = try parse(@ptrCast(buf));
516517 r.toss(if (result.is_64) @sizeOf(Elf64_Ehdr) else @sizeOf(Elf32_Ehdr));
517518 return result;
519||||||| edf785db0f
520 pub fn read(parse_source: anytype) !Header {
521 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
522 try parse_source.seekableStream().seekTo(0);
523 try parse_source.reader().readNoEof(&hdr_buf);
524 return Header.parse(&hdr_buf);
525=======
526 pub fn read(parse_source: anytype) !Header {
527 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
528 try parse_source.seekableStream().seekTo(0);
529 try parse_source.deprecatedReader().readNoEof(&hdr_buf);
530 return Header.parse(&hdr_buf);
531>>>>>>> origin/master
518532 }
519533
520534 pub const ParseError = error{
......@@ -590,14 +604,92 @@ pub const ProgramHeaderIterator = struct {
590604 if (it.index >= it.elf_header.phnum) return null;
591605 defer it.index += 1;
592606
607<<<<<<< HEAD
593608 if (it.elf_header.is_64) {
594609 var phdr: Elf64_Phdr = undefined;
595610 const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index;
596611 try it.file_reader.seekTo(offset);
597612 try it.file_reader.interface.readSlice(@ptrCast(&phdr));
598613 if (it.elf_header.endian != native_endian)
614||||||| edf785db0f
615 if (self.elf_header.is_64) {
616 var phdr: Elf64_Phdr = undefined;
617 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
618 try self.parse_source.seekableStream().seekTo(offset);
619 try self.parse_source.reader().readNoEof(mem.asBytes(&phdr));
620
621 // ELF endianness matches native endianness.
622 if (self.elf_header.endian == native_endian) return phdr;
623
624 // Convert fields to native endianness.
625=======
626 if (self.elf_header.is_64) {
627 var phdr: Elf64_Phdr = undefined;
628 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
629 try self.parse_source.seekableStream().seekTo(offset);
630 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr));
631
632 // ELF endianness matches native endianness.
633 if (self.elf_header.endian == native_endian) return phdr;
634
635 // Convert fields to native endianness.
636>>>>>>> origin/master
599637 mem.byteSwapAllFields(Elf64_Phdr, &phdr);
638<<<<<<< HEAD
600639 return phdr;
640||||||| edf785db0f
641 return phdr;
642 }
643
644 var phdr: Elf32_Phdr = undefined;
645 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
646 try self.parse_source.seekableStream().seekTo(offset);
647 try self.parse_source.reader().readNoEof(mem.asBytes(&phdr));
648
649 // ELF endianness does NOT match native endianness.
650 if (self.elf_header.endian != native_endian) {
651 // Convert fields to native endianness.
652 mem.byteSwapAllFields(Elf32_Phdr, &phdr);
653 }
654
655 // Convert 32-bit header to 64-bit.
656 return Elf64_Phdr{
657 .p_type = phdr.p_type,
658 .p_offset = phdr.p_offset,
659 .p_vaddr = phdr.p_vaddr,
660 .p_paddr = phdr.p_paddr,
661 .p_filesz = phdr.p_filesz,
662 .p_memsz = phdr.p_memsz,
663 .p_flags = phdr.p_flags,
664 .p_align = phdr.p_align,
665 };
666=======
667 return phdr;
668 }
669
670 var phdr: Elf32_Phdr = undefined;
671 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
672 try self.parse_source.seekableStream().seekTo(offset);
673 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&phdr));
674
675 // ELF endianness does NOT match native endianness.
676 if (self.elf_header.endian != native_endian) {
677 // Convert fields to native endianness.
678 mem.byteSwapAllFields(Elf32_Phdr, &phdr);
679 }
680
681 // Convert 32-bit header to 64-bit.
682 return Elf64_Phdr{
683 .p_type = phdr.p_type,
684 .p_offset = phdr.p_offset,
685 .p_vaddr = phdr.p_vaddr,
686 .p_paddr = phdr.p_paddr,
687 .p_filesz = phdr.p_filesz,
688 .p_memsz = phdr.p_memsz,
689 .p_flags = phdr.p_flags,
690 .p_align = phdr.p_align,
691 };
692>>>>>>> origin/master
601693 }
602694
603695 var phdr: Elf32_Phdr = undefined;
......@@ -624,9 +716,23 @@ pub const SectionHeaderIterator = struct {
624716 file_reader: *std.fs.File.Reader,
625717 index: usize = 0,
626718
719<<<<<<< HEAD
627720 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {
628721 if (it.index >= it.elf_header.shnum) return null;
629722 defer it.index += 1;
723||||||| edf785db0f
724 if (self.elf_header.is_64) {
725 var shdr: Elf64_Shdr = undefined;
726 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
727 try self.parse_source.seekableStream().seekTo(offset);
728 try self.parse_source.reader().readNoEof(mem.asBytes(&shdr));
729=======
730 if (self.elf_header.is_64) {
731 var shdr: Elf64_Shdr = undefined;
732 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
733 try self.parse_source.seekableStream().seekTo(offset);
734 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr));
735>>>>>>> origin/master
630736
631737 if (it.elf_header.is_64) {
632738 var shdr: Elf64_Shdr = undefined;
......@@ -635,7 +741,65 @@ pub const SectionHeaderIterator = struct {
635741 try it.file_reader.interface.readSlice(@ptrCast(&shdr));
636742 if (it.elf_header.endian != native_endian)
637743 mem.byteSwapAllFields(Elf64_Shdr, &shdr);
744<<<<<<< HEAD
638745 return shdr;
746||||||| edf785db0f
747 return shdr;
748 }
749
750 var shdr: Elf32_Shdr = undefined;
751 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
752 try self.parse_source.seekableStream().seekTo(offset);
753 try self.parse_source.reader().readNoEof(mem.asBytes(&shdr));
754
755 // ELF endianness does NOT match native endianness.
756 if (self.elf_header.endian != native_endian) {
757 // Convert fields to native endianness.
758 mem.byteSwapAllFields(Elf32_Shdr, &shdr);
759 }
760
761 // Convert 32-bit header to 64-bit.
762 return Elf64_Shdr{
763 .sh_name = shdr.sh_name,
764 .sh_type = shdr.sh_type,
765 .sh_flags = shdr.sh_flags,
766 .sh_addr = shdr.sh_addr,
767 .sh_offset = shdr.sh_offset,
768 .sh_size = shdr.sh_size,
769 .sh_link = shdr.sh_link,
770 .sh_info = shdr.sh_info,
771 .sh_addralign = shdr.sh_addralign,
772 .sh_entsize = shdr.sh_entsize,
773 };
774=======
775 return shdr;
776 }
777
778 var shdr: Elf32_Shdr = undefined;
779 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
780 try self.parse_source.seekableStream().seekTo(offset);
781 try self.parse_source.deprecatedReader().readNoEof(mem.asBytes(&shdr));
782
783 // ELF endianness does NOT match native endianness.
784 if (self.elf_header.endian != native_endian) {
785 // Convert fields to native endianness.
786 mem.byteSwapAllFields(Elf32_Shdr, &shdr);
787 }
788
789 // Convert 32-bit header to 64-bit.
790 return Elf64_Shdr{
791 .sh_name = shdr.sh_name,
792 .sh_type = shdr.sh_type,
793 .sh_flags = shdr.sh_flags,
794 .sh_addr = shdr.sh_addr,
795 .sh_offset = shdr.sh_offset,
796 .sh_size = shdr.sh_size,
797 .sh_link = shdr.sh_link,
798 .sh_info = shdr.sh_info,
799 .sh_addralign = shdr.sh_addralign,
800 .sh_entsize = shdr.sh_entsize,
801 };
802>>>>>>> origin/master
639803 }
640804
641805 var shdr: Elf32_Shdr = undefined;
lib/std/fmt.zig+245-567
......@@ -7,7 +7,6 @@ const io = std.io;
77const math = std.math;
88const assert = std.debug.assert;
99const mem = std.mem;
10const unicode = std.unicode;
1110const meta = std.meta;
1211const lossyCast = math.lossyCast;
1312const expectFmt = std.testing.expectFmt;
......@@ -25,10 +24,12 @@ pub const Alignment = enum {
2524 right,
2625};
2726
27pub const Case = enum { lower, upper };
28
2829const default_alignment = .right;
2930const default_fill_char = ' ';
3031
31/// Deprecated; to be removed after 0.14.0 is tagged.
32/// Deprecated in favor of `Options`.
3233pub const FormatOptions = Options;
3334
3435pub const Options = struct {
......@@ -36,229 +37,78 @@ pub const Options = struct {
3637 width: ?usize = null,
3738 alignment: Alignment = default_alignment,
3839 fill: u8 = default_fill_char,
39};
40
41/// Renders fmt string with args, calling `writer` with slices of bytes.
42/// If `writer` returns an error, the error is returned from `format` and
43/// `writer` is not called again.
44///
45/// The format string must be comptime-known and may contain placeholders following
46/// this format:
47/// `{[argument][specifier]:[fill][alignment][width].[precision]}`
48///
49/// Above, each word including its surrounding [ and ] is a parameter which you have to replace with something:
50///
51/// - *argument* is either the numeric index or the field name of the argument that should be inserted
52/// - when using a field name, you are required to enclose the field name (an identifier) in square
53/// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...}
54/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below)
55/// - *fill* is a single byte which is used to pad the formatted text
56/// - *alignment* is one of the three bytes '<', '^', or '>' to make the text left-, center-, or right-aligned, respectively
57/// - *width* is the total width of the field in bytes. This is generally only
58/// useful for ASCII text, such as numbers.
59/// - *precision* specifies how many decimals a formatted number should have
60///
61/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when
62/// all parameters after the separator are omitted.
63/// Only exception is the *fill* parameter. If a non-zero *fill* character is required at the same time as *width* is specified,
64/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*.
65///
66/// The *specifier* has several options for types:
67/// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes
68/// - `s`:
69/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination
70/// - for slices of u8, print the entire slice as a string without zero-termination
71/// - `b64`: output string as standard base64
72/// - `e`: output floating point value in scientific notation
73/// - `d`: output numeric value in decimal notation
74/// - `b`: output integer value in binary notation
75/// - `o`: output integer value in octal notation
76/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
77/// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max.
78/// - `D`: output nanoseconds as duration
79/// - `B`: output bytes in SI units (decimal)
80/// - `Bi`: output bytes in IEC units (binary)
81/// - `?`: output optional value as either the unwrapped value, or `null`; may be followed by a format specifier for the underlying value.
82/// - `!`: output error union value as either the unwrapped value, or the formatted error value; may be followed by a format specifier for the underlying value.
83/// - `*`: output the address of the value instead of the value itself.
84/// - `any`: output a value of any type using its default format.
85///
86/// If a formatted user type contains a function of the type
87/// ```
88/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.Options, writer: anytype) !void
89/// ```
90/// with `?` being the type formatted, this function will be called instead of the default implementation.
91/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
92///
93/// A user type may be a `struct`, `vector`, `union` or `enum` type.
94///
95/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`.
96pub fn format(bw: *Writer, comptime fmt: []const u8, args: anytype) Writer.Error!void {
97 const ArgsType = @TypeOf(args);
98 const args_type_info = @typeInfo(ArgsType);
99 if (args_type_info != .@"struct") {
100 @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
101 }
102
103 const fields_info = args_type_info.@"struct".fields;
104 if (fields_info.len > max_format_args) {
105 @compileError("32 arguments max are supported per format call");
106 }
107
108 @setEvalBranchQuota(2000000);
109 comptime var arg_state: ArgState = .{ .args_len = fields_info.len };
110 comptime var i = 0;
111 comptime var literal: []const u8 = "";
112 inline while (true) {
113 const start_index = i;
114
115 inline while (i < fmt.len) : (i += 1) {
116 switch (fmt[i]) {
117 '{', '}' => break,
118 else => {},
119 }
120 }
12140
122 comptime var end_index = i;
123 comptime var unescape_brace = false;
124
125 // Handle {{ and }}, those are un-escaped as single braces
126 if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) {
127 unescape_brace = true;
128 // Make the first brace part of the literal...
129 end_index += 1;
130 // ...and skip both
131 i += 2;
132 }
133
134 literal = literal ++ fmt[start_index..end_index];
135
136 // We've already skipped the other brace, restart the loop
137 if (unescape_brace) continue;
138
139 // Write out the literal
140 if (literal.len != 0) {
141 try bw.writeAll(literal);
142 literal = "";
143 }
144
145 if (i >= fmt.len) break;
146
147 if (fmt[i] == '}') {
148 @compileError("missing opening {");
149 }
150
151 // Get past the {
152 comptime assert(fmt[i] == '{');
153 i += 1;
154
155 const fmt_begin = i;
156 // Find the closing brace
157 inline while (i < fmt.len and fmt[i] != '}') : (i += 1) {}
158 const fmt_end = i;
159
160 if (i >= fmt.len) {
161 @compileError("missing closing }");
162 }
163
164 // Get past the }
165 comptime assert(fmt[i] == '}');
166 i += 1;
167
168 const placeholder = comptime Placeholder.parse(fmt[fmt_begin..fmt_end].*);
169 const arg_pos = comptime switch (placeholder.arg) {
170 .none => null,
171 .number => |pos| pos,
172 .named => |arg_name| meta.fieldIndex(ArgsType, arg_name) orelse
173 @compileError("no argument with name '" ++ arg_name ++ "'"),
174 };
175
176 const width = switch (placeholder.width) {
177 .none => null,
178 .number => |v| v,
179 .named => |arg_name| blk: {
180 const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse
181 @compileError("no argument with name '" ++ arg_name ++ "'");
182 _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments");
183 break :blk @field(args, arg_name);
184 },
41 pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number {
42 return .{
43 .mode = mode,
44 .case = case,
45 .precision = o.precision,
46 .width = o.width,
47 .alignment = o.alignment,
48 .fill = o.fill,
18549 };
50 }
51};
18652
187 const precision = switch (placeholder.precision) {
188 .none => null,
189 .number => |v| v,
190 .named => |arg_name| blk: {
191 const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse
192 @compileError("no argument with name '" ++ arg_name ++ "'");
193 _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments");
194 break :blk @field(args, arg_name);
195 },
196 };
53pub const Number = struct {
54 mode: Mode = .decimal,
55 /// Affects hex digits as well as floating point "inf"/"INF".
56 case: Case = .lower,
57 precision: ?usize = null,
58 width: ?usize = null,
59 alignment: Alignment = default_alignment,
60 fill: u8 = default_fill_char,
19761
198 const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse
199 @compileError("too few arguments");
200
201 try bw.printValue(
202 placeholder.specifier_arg,
203 .{
204 .fill = placeholder.fill,
205 .alignment = placeholder.alignment,
206 .width = width,
207 .precision = precision,
208 },
209 @field(args, fields_info[arg_to_print].name),
210 std.options.fmt_max_depth,
211 );
212 }
213
214 if (comptime arg_state.hasUnusedArgs()) {
215 const missing_count = arg_state.args_len - @popCount(arg_state.used_args);
216 switch (missing_count) {
217 0 => unreachable,
218 1 => @compileError("unused argument in '" ++ fmt ++ "'"),
219 else => @compileError(comptimePrint("{d}", .{missing_count}) ++ " unused arguments in '" ++ fmt ++ "'"),
62 pub const Mode = enum {
63 decimal,
64 binary,
65 octal,
66 hex,
67 scientific,
68
69 pub fn base(mode: Mode) ?u8 {
70 return switch (mode) {
71 .decimal => 10,
72 .binary => 2,
73 .octal => 8,
74 .hex => 16,
75 .scientific => null,
76 };
22077 }
221 }
222}
78 };
79};
22380
224fn cacheString(str: anytype) []const u8 {
225 return &str;
81/// Deprecated in favor of `Writer.print`.
82pub fn format(writer: anytype, comptime fmt: []const u8, args: anytype) !void {
83 var adapter = writer.adaptToNewApi();
84 return adapter.new_interface.print(fmt, args) catch |err| switch (err) {
85 error.WriteFailed => return adapter.err.?,
86 };
22687}
22788
22889pub const Placeholder = struct {
22990 specifier_arg: []const u8,
230 fill: u21,
91 fill: u8,
23192 alignment: Alignment,
23293 arg: Specifier,
23394 width: Specifier,
23495 precision: Specifier,
23596
236 pub fn parse(comptime str: anytype) Placeholder {
237 const view = std.unicode.Utf8View.initComptime(&str);
238 comptime var parser = Parser{
239 .iter = view.iterator(),
240 };
241
242 // Parse the positional argument number
243 const arg = comptime parser.specifier() catch |err|
244 @compileError(@errorName(err));
245
246 // Parse the format specifier
247 const specifier_arg = comptime parser.until(':');
248
249 // Skip the colon, if present
250 if (comptime parser.char()) |ch| {
251 if (ch != ':') {
252 @compileError("expected : or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'");
253 }
97 pub fn parse(comptime bytes: []const u8) Placeholder {
98 var parser: Parser = .{ .bytes = bytes, .i = 0 };
99 const arg = parser.specifier() catch |err| @compileError(@errorName(err));
100 const specifier_arg = parser.until(':');
101 if (parser.char()) |b| {
102 if (b != ':') @compileError("expected : or }, found '" ++ &[1]u8{b} ++ "'");
254103 }
255104
256 // Parse the fill character, if present.
257 // When the width field is also specified, the fill character must
105 // Parse the fill byte, if present.
106 //
107 // When the width field is also specified, the fill byte must
258108 // be followed by an alignment specifier, unless it's '0' (zero)
259 // (in which case it's handled as part of the width specifier)
260 var fill: ?u21 = comptime if (parser.peek(1)) |ch|
261 switch (ch) {
109 // (in which case it's handled as part of the width specifier).
110 var fill: ?u8 = if (parser.peek(1)) |b|
111 switch (b) {
262112 '<', '^', '>' => parser.char(),
263113 else => null,
264114 }
......@@ -266,8 +116,8 @@ pub const Placeholder = struct {
266116 null;
267117
268118 // Parse the alignment parameter
269 const alignment: ?Alignment = comptime if (parser.peek(0)) |ch| init: {
270 switch (ch) {
119 const alignment: ?Alignment = if (parser.peek(0)) |b| init: {
120 switch (b) {
271121 '<', '^', '>' => {
272122 // consume the character
273123 break :init switch (parser.char().?) {
......@@ -283,30 +133,26 @@ pub const Placeholder = struct {
283133 // When none of the fill character and the alignment specifier have
284134 // been provided, check whether the width starts with a zero.
285135 if (fill == null and alignment == null) {
286 fill = comptime if (parser.peek(0) == '0') '0' else null;
136 fill = if (parser.peek(0) == '0') '0' else null;
287137 }
288138
289139 // Parse the width parameter
290 const width = comptime parser.specifier() catch |err|
291 @compileError(@errorName(err));
140 const width = parser.specifier() catch |err| @compileError(@errorName(err));
292141
293142 // Skip the dot, if present
294 if (comptime parser.char()) |ch| {
295 if (ch != '.') {
296 @compileError("expected . or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'");
297 }
143 if (parser.char()) |b| {
144 if (b != '.') @compileError("expected . or }, found '" ++ &[1]u8{b} ++ "'");
298145 }
299146
300147 // Parse the precision parameter
301 const precision = comptime parser.specifier() catch |err|
302 @compileError(@errorName(err));
148 const precision = parser.specifier() catch |err| @compileError(@errorName(err));
303149
304 if (comptime parser.char()) |ch| {
305 @compileError("extraneous trailing character '" ++ unicode.utf8EncodeComptime(ch) ++ "'");
306 }
150 if (parser.char()) |b| @compileError("extraneous trailing character '" ++ &[1]u8{b} ++ "'");
151
152 const specifier_array = specifier_arg[0..specifier_arg.len].*;
307153
308154 return .{
309 .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*),
155 .specifier_arg = &specifier_array,
310156 .fill = fill orelse default_fill_char,
311157 .alignment = alignment orelse default_alignment,
312158 .arg = arg,
......@@ -327,93 +173,64 @@ pub const Specifier = union(enum) {
327173/// Allows to implement formatters compatible with std.fmt without replicating
328174/// the standard library behavior.
329175pub const Parser = struct {
330 iter: std.unicode.Utf8Iterator,
176 bytes: []const u8,
177 i: usize,
331178
332 // Returns a decimal number or null if the current character is not a
333 // digit
334179 pub fn number(self: *@This()) ?usize {
335180 var r: ?usize = null;
336
337 while (self.peek(0)) |code_point| {
338 switch (code_point) {
181 while (self.peek(0)) |byte| {
182 switch (byte) {
339183 '0'...'9' => {
340184 if (r == null) r = 0;
341185 r.? *= 10;
342 r.? += code_point - '0';
186 r.? += byte - '0';
343187 },
344188 else => break,
345189 }
346 _ = self.iter.nextCodepoint();
190 self.i += 1;
347191 }
348
349192 return r;
350193 }
351194
352 // Returns a substring of the input starting from the current position
353 // and ending where `ch` is found or until the end if not found
354 pub fn until(self: *@This(), ch: u21) []const u8 {
355 const start = self.iter.i;
356 while (self.peek(0)) |code_point| {
357 if (code_point == ch)
358 break;
359 _ = self.iter.nextCodepoint();
360 }
361 return self.iter.bytes[start..self.iter.i];
195 pub fn until(self: *@This(), delimiter: u8) []const u8 {
196 const start = self.i;
197 self.i = std.mem.indexOfScalarPos(u8, self.bytes, self.i, delimiter) orelse self.bytes.len;
198 return self.bytes[start..self.i];
362199 }
363200
364 // Returns the character pointed to by the iterator if available, or
365 // null otherwise
366 pub fn char(self: *@This()) ?u21 {
367 if (self.iter.nextCodepoint()) |code_point| {
368 return code_point;
369 }
370 return null;
201 pub fn char(self: *@This()) ?u8 {
202 const i = self.i;
203 if (self.bytes.len - i == 0) return null;
204 self.i = i + 1;
205 return self.bytes[i];
371206 }
372207
373 // Returns true if the iterator points to an existing character and
374 // false otherwise
375 pub fn maybe(self: *@This(), val: u21) bool {
376 if (self.peek(0) == val) {
377 _ = self.iter.nextCodepoint();
208 pub fn maybe(self: *@This(), byte: u8) bool {
209 if (self.peek(0) == byte) {
210 self.i += 1;
378211 return true;
379212 }
380213 return false;
381214 }
382215
383 // Returns a decimal number or null if the current character is not a
384 // digit
385216 pub fn specifier(self: *@This()) !Specifier {
386217 if (self.maybe('[')) {
387218 const arg_name = self.until(']');
388
389 if (!self.maybe(']'))
390 return @field(anyerror, "Expected closing ]");
391
392 return Specifier{ .named = arg_name };
219 if (!self.maybe(']')) return error.@"Expected closing ]";
220 return .{ .named = arg_name };
393221 }
394 if (self.number()) |i|
395 return Specifier{ .number = i };
396
397 return Specifier{ .none = {} };
222 if (self.number()) |i| return .{ .number = i };
223 return .{ .none = {} };
398224 }
399225
400 // Returns the n-th next character or null if that's past the end
401 pub fn peek(self: *@This(), n: usize) ?u21 {
402 const original_i = self.iter.i;
403 defer self.iter.i = original_i;
404
405 var i: usize = 0;
406 var code_point: ?u21 = null;
407 while (i <= n) : (i += 1) {
408 code_point = self.iter.nextCodepoint();
409 if (code_point == null) return null;
410 }
411 return code_point;
226 pub fn peek(self: *@This(), i: usize) ?u8 {
227 const peek_index = self.i + i;
228 if (peek_index >= self.bytes.len) return null;
229 return self.bytes[peek_index];
412230 }
413231};
414232
415233pub const ArgSetType = u32;
416const max_format_args = @typeInfo(ArgSetType).int.bits;
417234
418235pub const ArgState = struct {
419236 next_arg: usize = 0,
......@@ -441,63 +258,12 @@ pub const ArgState = struct {
441258 }
442259};
443260
444test {
445 _ = float;
446}
447
448pub const Case = enum { lower, upper };
449
450fn SliceEscape(comptime case: Case) type {
451 const charset = "0123456789" ++ if (case == .upper) "ABCDEF" else "abcdef";
452
453 return struct {
454 pub fn format(
455 bytes: []const u8,
456 bw: *Writer,
457 comptime fmt: []const u8,
458 ) !void {
459 _ = fmt;
460 var buf: [4]u8 = undefined;
461
462 buf[0] = '\\';
463 buf[1] = 'x';
464
465 for (bytes) |c| {
466 if (std.ascii.isPrint(c)) {
467 try bw.writeByte(c);
468 } else {
469 buf[2] = charset[c >> 4];
470 buf[3] = charset[c & 15];
471 try bw.writeAll(&buf);
472 }
473 }
474 }
475 };
476}
477
478const formatSliceEscapeLower = SliceEscape(.lower).format;
479const formatSliceEscapeUpper = SliceEscape(.upper).format;
480
481/// Return a Formatter for a []const u8 where every non-printable ASCII
482/// character is escaped as \xNN, where NN is the character in lowercase
483/// hexadecimal notation.
484pub fn fmtSliceEscapeLower(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeLower) {
485 return .{ .data = bytes };
486}
487
488/// Return a Formatter for a []const u8 where every non-printable ASCII
489/// character is escaped as \xNN, where NN is the character in uppercase
490/// hexadecimal notation.
491pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeUpper) {
492 return .{ .data = bytes };
493}
494
495261/// Asserts the rendered integer value fits in `buffer`.
496262/// Returns the end index within `buffer`.
497263pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize {
498 var bw: Writer = .fixed(buffer);
499 bw.printIntOptions(value, base, case, options) catch unreachable;
500 return bw.end;
264 var w: Writer = .fixed(buffer);
265 w.printInt(value, base, case, options) catch unreachable;
266 return w.end;
501267}
502268
503269/// Converts values in the range [0, 100) to a base 10 string.
......@@ -509,35 +275,49 @@ pub fn digits2(value: u8) [2]u8 {
509275 }
510276}
511277
512pub const ParseIntError = error{
513 /// The result cannot fit in the type specified
514 Overflow,
515
516 /// The input was empty or contained an invalid character
517 InvalidCharacter,
518};
278/// Deprecated in favor of `Alt`.
279pub const Formatter = Alt;
519280
520/// Creates a Formatter type from a format function. Wrapping data in Formatter(func) causes
521/// the data to be formatted using the given function `func`. `func` must be of the following
522/// form:
523///
524/// fn formatExample(
525/// data: T,
526/// comptime fmt: []const u8,
527/// options: std.fmt.Options,
528/// writer: anytype,
529/// ) !void;
530///
531pub fn Formatter(comptime formatFn: anytype) type {
532 const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?;
281/// Creates a type suitable for instantiating and passing to a "{f}" placeholder.
282pub fn Alt(
283 comptime Data: type,
284 comptime formatFn: fn (data: Data, writer: *Writer) Writer.Error!void,
285) type {
533286 return struct {
534287 data: Data,
535 pub fn format(self: @This(), writer: *Writer, comptime fmt: []const u8) Writer.Error!void {
536 try formatFn(self.data, writer, fmt);
288 pub inline fn format(self: @This(), writer: *Writer) Writer.Error!void {
289 try formatFn(self.data, writer);
537290 }
538291 };
539292}
540293
294/// Helper for calling alternate format methods besides one named "format".
295pub fn alt(
296 context: anytype,
297 comptime func_name: @TypeOf(.enum_literal),
298) Formatter(@TypeOf(context), @field(@TypeOf(context), @tagName(func_name))) {
299 return .{ .data = context };
300}
301
302test alt {
303 const Example = struct {
304 number: u8,
305
306 pub fn other(ex: @This(), w: *Writer) Writer.Error!void {
307 try w.writeByte(ex.number);
308 }
309 };
310 const ex: Example = .{ .number = 'a' };
311 try expectFmt("a", "{f}", .{alt(ex, .other)});
312}
313
314pub const ParseIntError = error{
315 /// The result cannot fit in the type specified.
316 Overflow,
317 /// The input was empty or contained an invalid character.
318 InvalidCharacter,
319};
320
541321/// Parses the string `buf` as signed or unsigned representation in the
542322/// specified base of an integral value of type `T`.
543323///
......@@ -845,17 +625,17 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
845625/// Count the characters needed for format.
846626pub fn count(comptime fmt: []const u8, args: anytype) usize {
847627 var trash_buffer: [64]u8 = undefined;
848 var w: Writer = .discarding(&trash_buffer);
849 w.print(fmt, args) catch |err| switch (err) {
628 var dw: Writer.Discarding = .init(&trash_buffer);
629 dw.writer.print(fmt, args) catch |err| switch (err) {
850630 error.WriteFailed => unreachable,
851631 };
852 return w.count;
632 return @intCast(dw.count + dw.writer.end);
853633}
854634
855635pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 {
856 var aw = try std.io.Writer.Allocating.initCapacity(gpa, fmt.len);
636 var aw = try Writer.Allocating.initCapacity(gpa, fmt.len);
857637 defer aw.deinit();
858 aw.interface.print(fmt, args) catch |err| switch (err) {
638 aw.writer.print(fmt, args) catch |err| switch (err) {
859639 error.WriteFailed => return error.OutOfMemory,
860640 };
861641 return aw.toOwnedSlice();
......@@ -867,9 +647,9 @@ pub fn allocPrintSentinel(
867647 args: anytype,
868648 comptime sentinel: u8,
869649) Allocator.Error![:sentinel]u8 {
870 var aw = try std.io.Writer.Allocating.initCapacity(gpa, fmt.len);
650 var aw = try Writer.Allocating.initCapacity(gpa, fmt.len);
871651 defer aw.deinit();
872 aw.interface.print(fmt, args) catch |err| switch (err) {
652 aw.writer.print(fmt, args) catch |err| switch (err) {
873653 error.WriteFailed => return error.OutOfMemory,
874654 };
875655 return aw.toOwnedSliceSentinel(sentinel);
......@@ -1003,10 +783,6 @@ test "int.padded" {
1003783 try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
1004784 try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
1005785 try expectFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});
1006
1007 try expectFmt("UTF-8: 'ü '", "UTF-8: '{u:<4}'", .{'ü'});
1008 try expectFmt("UTF-8: ' ü'", "UTF-8: '{u:>4}'", .{'ü'});
1009 try expectFmt("UTF-8: ' ü '", "UTF-8: '{u:^4}'", .{'ü'});
1010786}
1011787
1012788test "buffer" {
......@@ -1036,36 +812,24 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime
1036812}
1037813
1038814test "array" {
1039 {
1040 const value: [3]u8 = "abc".*;
1041 try expectArrayFmt("array: abc\n", "array: {s}\n", value);
1042 try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {d}\n", value);
1043 try expectArrayFmt("array: { 61, 62, 63 }\n", "array: {x}\n", value);
1044 try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value);
815 const value: [3]u8 = "abc".*;
816 try expectArrayFmt("array: abc\n", "array: {s}\n", value);
817 try expectArrayFmt("array: 616263\n", "array: {x}\n", value);
818 try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value);
1045819
1046 var buf: [100]u8 = undefined;
1047 try expectFmt(
1048 try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}),
1049 "array: {*}\n",
1050 .{&value},
1051 );
1052 }
1053
1054 {
1055 const value = [2][3]u8{ "abc".*, "def".* };
1056
1057 try expectArrayFmt("array: { abc, def }\n", "array: {s}\n", value);
1058 try expectArrayFmt("array: { { 97, 98, 99 }, { 100, 101, 102 } }\n", "array: {d}\n", value);
1059 try expectArrayFmt("array: { { 61, 62, 63 }, { 64, 65, 66 } }\n", "array: {x}\n", value);
1060 }
820 var buf: [100]u8 = undefined;
821 try expectFmt(
822 try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}),
823 "array: {*}\n",
824 .{&value},
825 );
1061826}
1062827
1063828test "slice" {
1064829 {
1065830 const value: []const u8 = "abc";
1066831 try expectFmt("slice: abc\n", "slice: {s}\n", .{value});
1067 try expectFmt("slice: { 97, 98, 99 }\n", "slice: {d}\n", .{value});
1068 try expectFmt("slice: { 61, 62, 63 }\n", "slice: {x}\n", .{value});
832 try expectFmt("slice: 616263\n", "slice: {x}\n", .{value});
1069833 try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value});
1070834 }
1071835 {
......@@ -1079,45 +843,33 @@ test "slice" {
1079843 try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice});
1080844 }
1081845
1082 try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
1083846 try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
1084847
1085848 {
1086849 var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 };
1087 var runtime_zero: usize = 0;
1088 _ = &runtime_zero;
1089 try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]});
1090 try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]});
1091 try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]});
1092 try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]});
850 const input: []const u32 = &int_slice;
851 try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input});
1093852 }
1094853 {
1095854 const S1 = struct {
1096855 x: u8,
1097856 };
1098857 const struct_slice: []const S1 = &[_]S1{ S1{ .x = 8 }, S1{ .x = 42 } };
1099 try expectFmt("slice: { fmt.test.slice.S1{ .x = 8 }, fmt.test.slice.S1{ .x = 42 } }", "slice: {any}", .{struct_slice});
858 try expectFmt("slice: { .{ .x = 8 }, .{ .x = 42 } }", "slice: {any}", .{struct_slice});
1100859 }
1101860 {
1102861 const S2 = struct {
1103862 x: u8,
1104863
1105 pub fn format(s: @This(), comptime _: []const u8, _: std.fmt.Options, writer: anytype) !void {
864 pub fn format(s: @This(), writer: *Writer) Writer.Error!void {
1106865 try writer.print("S2({})", .{s.x});
1107866 }
1108867 };
1109868 const struct_slice: []const S2 = &[_]S2{ S2{ .x = 8 }, S2{ .x = 42 } };
1110 try expectFmt("slice: { S2(8), S2(42) }", "slice: {any}", .{struct_slice});
869 try expectFmt("slice: { .{ .x = 8 }, .{ .x = 42 } }", "slice: {any}", .{struct_slice});
1111870 }
1112871}
1113872
1114test "escape non-printable" {
1115 try expectFmt("abc 123", "{s}", .{fmtSliceEscapeLower("abc 123")});
1116 try expectFmt("ab\\xffc", "{s}", .{fmtSliceEscapeLower("ab\xffc")});
1117 try expectFmt("abc 123", "{s}", .{fmtSliceEscapeUpper("abc 123")});
1118 try expectFmt("ab\\xFFc", "{s}", .{fmtSliceEscapeUpper("ab\xffc")});
1119}
1120
1121873test "pointer" {
1122874 {
1123875 const value = @as(*align(1) i32, @ptrFromInt(0xdeadbeef));
......@@ -1141,11 +893,6 @@ test "cstr" {
1141893 "cstr: {s}\n",
1142894 .{@as([*c]const u8, @ptrCast("Test C"))},
1143895 );
1144 try expectFmt(
1145 "cstr: Test C\n",
1146 "cstr: {s:10}\n",
1147 .{@as([*c]const u8, @ptrCast("Test C"))},
1148 );
1149896}
1150897
1151898test "struct" {
......@@ -1154,8 +901,8 @@ test "struct" {
1154901 field: u8,
1155902 };
1156903 const value = Struct{ .field = 42 };
1157 try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{value});
1158 try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{&value});
904 try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{value});
905 try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{&value});
1159906 }
1160907 {
1161908 const Struct = struct {
......@@ -1163,7 +910,7 @@ test "struct" {
1163910 b: u1,
1164911 };
1165912 const value = Struct{ .a = 0, .b = 1 };
1166 try expectFmt("struct: fmt.test.struct.Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value});
913 try expectFmt("struct: .{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value});
1167914 }
1168915
1169916 const S = struct {
......@@ -1176,11 +923,11 @@ test "struct" {
1176923 .b = error.Unused,
1177924 };
1178925
1179 try expectFmt("fmt.test.struct.S{ .a = 456, .b = error.Unused }", "{}", .{inst});
926 try expectFmt(".{ .a = 456, .b = error.Unused }", "{}", .{inst});
1180927 // Tuples
1181 try expectFmt("{ }", "{}", .{.{}});
1182 try expectFmt("{ -1 }", "{}", .{.{-1}});
1183 try expectFmt("{ -1, 42, 2.5e4 }", "{}", .{.{ -1, 42, 0.25e5 }});
928 try expectFmt(".{ }", "{}", .{.{}});
929 try expectFmt(".{ -1 }", "{}", .{.{-1}});
930 try expectFmt(".{ -1, 42, 25000 }", "{}", .{.{ -1, 42, 0.25e5 }});
1184931}
1185932
1186933test "enum" {
......@@ -1189,15 +936,15 @@ test "enum" {
1189936 Two,
1190937 };
1191938 const value = Enum.Two;
1192 try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{value});
1193 try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{&value});
1194 try expectFmt("enum: fmt.test.enum.Enum.One\n", "enum: {}\n", .{Enum.One});
1195 try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{Enum.Two});
939 try expectFmt("enum: .Two\n", "enum: {}\n", .{value});
940 try expectFmt("enum: .Two\n", "enum: {}\n", .{&value});
941 try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One});
942 try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two});
1196943
1197944 // test very large enum to verify ct branch quota is large enough
1198945 // TODO: https://github.com/ziglang/zig/issues/15609
1199946 if (!((builtin.cpu.arch == .wasm32) and builtin.mode == .Debug)) {
1200 try expectFmt("enum: os.windows.win32error.Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
947 try expectFmt("enum: .INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
1201948 }
1202949
1203950 const E = enum {
......@@ -1208,7 +955,7 @@ test "enum" {
1208955
1209956 const inst = E.Two;
1210957
1211 try expectFmt("fmt.test.enum.E.Two", "{}", .{inst});
958 try expectFmt(".Two", "{}", .{inst});
1212959}
1213960
1214961test "non-exhaustive enum" {
......@@ -1217,13 +964,17 @@ test "non-exhaustive enum" {
1217964 Two = 0xbeef,
1218965 _,
1219966 };
1220 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {}\n", .{Enum.One});
1221 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {}\n", .{Enum.Two});
1222 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))});
1223 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {x}\n", .{Enum.One});
1224 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {x}\n", .{Enum.Two});
1225 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {X}\n", .{Enum.Two});
1226 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(1234)\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))});
967 try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One});
968 try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two});
969 try expectFmt("enum: @enumFromInt(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))});
970 try expectFmt("enum: f\n", "enum: {x}\n", .{Enum.One});
971 try expectFmt("enum: beef\n", "enum: {x}\n", .{Enum.Two});
972 try expectFmt("enum: BEEF\n", "enum: {X}\n", .{Enum.Two});
973 try expectFmt("enum: 1234\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))});
974
975 try expectFmt("enum: 15\n", "enum: {d}\n", .{Enum.One});
976 try expectFmt("enum: 48879\n", "enum: {d}\n", .{Enum.Two});
977 try expectFmt("enum: 4660\n", "enum: {d}\n", .{@as(Enum, @enumFromInt(0x1234))});
1227978}
1228979
1229980test "float.scientific" {
......@@ -1349,41 +1100,6 @@ test "float.libc.sanity" {
13491100 try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1518338049))))});
13501101}
13511102
1352test "custom" {
1353 const Vec2 = struct {
1354 const SelfType = @This();
1355 x: f32,
1356 y: f32,
1357
1358 pub fn format(
1359 self: SelfType,
1360 comptime fmt: []const u8,
1361 options: Options,
1362 writer: anytype,
1363 ) !void {
1364 _ = options;
1365 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
1366 return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
1367 } else if (comptime std.mem.eql(u8, fmt, "d")) {
1368 return std.fmt.format(writer, "{d:.3}x{d:.3}", .{ self.x, self.y });
1369 } else {
1370 @compileError("unknown format character: '" ++ fmt ++ "'");
1371 }
1372 }
1373 };
1374
1375 var value = Vec2{
1376 .x = 10.2,
1377 .y = 2.22,
1378 };
1379 try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value});
1380 try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value});
1381
1382 // same thing but not passing a pointer
1383 try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{value});
1384 try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value});
1385}
1386
13871103test "union" {
13881104 const TU = union(enum) {
13891105 float: f32,
......@@ -1400,18 +1116,13 @@ test "union" {
14001116 int: u32,
14011117 };
14021118
1403 const tu_inst = TU{ .int = 123 };
1404 const uu_inst = UU{ .int = 456 };
1405 const eu_inst = EU{ .float = 321.123 };
1406
1407 try expectFmt("fmt.test.union.TU{ .int = 123 }", "{}", .{tu_inst});
1119 const tu_inst: TU = .{ .int = 123 };
1120 const uu_inst: UU = .{ .int = 456 };
1121 const eu_inst: EU = .{ .float = 321.123 };
14081122
1409 var buf: [100]u8 = undefined;
1410 const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst});
1411 try std.testing.expectEqualStrings("fmt.test.union.UU@", uu_result[0..18]);
1412
1413 const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst});
1414 try std.testing.expectEqualStrings("fmt.test.union.EU@", eu_result[0..18]);
1123 try expectFmt(".{ .int = 123 }", "{}", .{tu_inst});
1124 try expectFmt(".{ ... }", "{}", .{uu_inst});
1125 try expectFmt(".{ .float = 321.123, .int = 1134596030 }", "{}", .{eu_inst});
14151126}
14161127
14171128test "struct.self-referential" {
......@@ -1425,7 +1136,7 @@ test "struct.self-referential" {
14251136 };
14261137 inst.a = &inst;
14271138
1428 try expectFmt("fmt.test.struct.self-referential.S{ .a = fmt.test.struct.self-referential.S{ .a = fmt.test.struct.self-referential.S{ .a = fmt.test.struct.self-referential.S{ ... } } } }", "{}", .{inst});
1139 try expectFmt(".{ .a = .{ .a = .{ .a = .{ ... } } } }", "{}", .{inst});
14291140}
14301141
14311142test "struct.zero-size" {
......@@ -1440,7 +1151,7 @@ test "struct.zero-size" {
14401151 const a = A{};
14411152 const b = B{ .a = a, .c = 0 };
14421153
1443 try expectFmt("fmt.test.struct.zero-size.B{ .a = fmt.test.struct.zero-size.A{ }, .c = 0 }", "{}", .{b});
1154 try expectFmt(".{ .a = .{ }, .c = 0 }", "{}", .{b});
14441155}
14451156
14461157/// Encodes a sequence of bytes as hexadecimal digits.
......@@ -1551,33 +1262,17 @@ test "enum-literal" {
15511262
15521263test "padding" {
15531264 try expectFmt("Simple", "{s}", .{"Simple"});
1554 try expectFmt(" true", "{:10}", .{true});
1555 try expectFmt(" true", "{:>10}", .{true});
1556 try expectFmt("======true", "{:=>10}", .{true});
1557 try expectFmt("true======", "{:=<10}", .{true});
1558 try expectFmt(" true ", "{:^10}", .{true});
1559 try expectFmt("===true===", "{:=^10}", .{true});
1560 try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"});
1561 try expectFmt("==================Filled", "{s:=>24}", .{"Filled"});
1562 try expectFmt(" Centered ", "{s:^24}", .{"Centered"});
1563 try expectFmt("-", "{s:-^1}", .{""});
1564 try expectFmt("==crêpe===", "{s:=^10}", .{"crêpe"});
1565 try expectFmt("=====crêpe", "{s:=>10}", .{"crêpe"});
1566 try expectFmt("crêpe=====", "{s:=<10}", .{"crêpe"});
1265 try expectFmt(" 1234", "{:10}", .{1234});
1266 try expectFmt(" 1234", "{:>10}", .{1234});
1267 try expectFmt("======1234", "{:=>10}", .{1234});
1268 try expectFmt("1234======", "{:=<10}", .{1234});
1269 try expectFmt(" 1234 ", "{:^10}", .{1234});
1270 try expectFmt("===1234===", "{:=^10}", .{1234});
15671271 try expectFmt("====a", "{c:=>5}", .{'a'});
15681272 try expectFmt("==a==", "{c:=^5}", .{'a'});
15691273 try expectFmt("a====", "{c:=<5}", .{'a'});
15701274}
15711275
1572test "padding fill char utf" {
1573 try expectFmt("──crêpe───", "{s:─^10}", .{"crêpe"});
1574 try expectFmt("─────crêpe", "{s:─>10}", .{"crêpe"});
1575 try expectFmt("crêpe─────", "{s:─<10}", .{"crêpe"});
1576 try expectFmt("────a", "{c:─>5}", .{'a'});
1577 try expectFmt("──a──", "{c:─^5}", .{'a'});
1578 try expectFmt("a────", "{c:─<5}", .{'a'});
1579}
1580
15811276test "decimal float padding" {
15821277 const number: f32 = 3.1415;
15831278 try expectFmt("left-pad: **3.142\n", "left-pad: {d:*>7.3}\n", .{number});
......@@ -1620,17 +1315,17 @@ test "named arguments" {
16201315
16211316test "runtime width specifier" {
16221317 const width: usize = 9;
1623 try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width });
1624 try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width });
1625 try expectFmt(" hello", "{s:[1]}", .{ "hello", width });
1626 try expectFmt("42 hello", "{d} {s:[2]}", .{ 42, "hello", width });
1318 try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width });
1319 try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width });
1320 try expectFmt(" 12345", "{d:[1]}", .{ 12345, width });
1321 try expectFmt("42 12345", "{d} {d:[2]}", .{ 42, 12345, width });
16271322}
16281323
16291324test "runtime precision specifier" {
16301325 const number: f32 = 3.1415;
16311326 const precision: usize = 2;
1632 try expectFmt("3.14e0", "{:1.[1]}", .{ number, precision });
1633 try expectFmt("3.14e0", "{:1.[precision]}", .{ .number = number, .precision = precision });
1327 try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision });
1328 try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision });
16341329}
16351330
16361331test "recursive format function" {
......@@ -1639,16 +1334,16 @@ test "recursive format function" {
16391334 Leaf: i32,
16401335 Branch: struct { left: *const R, right: *const R },
16411336
1642 pub fn format(self: R, comptime _: []const u8, _: std.fmt.Options, writer: anytype) !void {
1337 pub fn format(self: R, writer: *Writer) Writer.Error!void {
16431338 return switch (self) {
1644 .Leaf => |n| std.fmt.format(writer, "Leaf({})", .{n}),
1645 .Branch => |b| std.fmt.format(writer, "Branch({}, {})", .{ b.left, b.right }),
1339 .Leaf => |n| writer.print("Leaf({})", .{n}),
1340 .Branch => |b| writer.print("Branch({f}, {f})", .{ b.left, b.right }),
16461341 };
16471342 }
16481343 };
16491344
1650 var r = R{ .Leaf = 1 };
1651 try expectFmt("Leaf(1)\n", "{}\n", .{&r});
1345 var r: R = .{ .Leaf = 1 };
1346 try expectFmt("Leaf(1)\n", "{f}\n", .{&r});
16521347}
16531348
16541349pub const hex_charset = "0123456789abcdef";
......@@ -1682,54 +1377,39 @@ test hex {
16821377
16831378test "parser until" {
16841379 { // return substring till ':'
1685 var parser: Parser = .{
1686 .iter = .{ .bytes = "abc:1234", .i = 0 },
1687 };
1380 var parser: Parser = .{ .bytes = "abc:1234", .i = 0 };
16881381 try testing.expectEqualStrings("abc", parser.until(':'));
16891382 }
16901383
16911384 { // return the entire string - `ch` not found
1692 var parser: Parser = .{
1693 .iter = .{ .bytes = "abc1234", .i = 0 },
1694 };
1385 var parser: Parser = .{ .bytes = "abc1234", .i = 0 };
16951386 try testing.expectEqualStrings("abc1234", parser.until(':'));
16961387 }
16971388
16981389 { // substring is empty - `ch` is the only character
1699 var parser: Parser = .{
1700 .iter = .{ .bytes = ":", .i = 0 },
1701 };
1390 var parser: Parser = .{ .bytes = ":", .i = 0 };
17021391 try testing.expectEqualStrings("", parser.until(':'));
17031392 }
17041393
17051394 { // empty string and `ch` not found
1706 var parser: Parser = .{
1707 .iter = .{ .bytes = "", .i = 0 },
1708 };
1395 var parser: Parser = .{ .bytes = "", .i = 0 };
17091396 try testing.expectEqualStrings("", parser.until(':'));
17101397 }
17111398
17121399 { // substring starts at index 2 and goes upto `ch`
1713 var parser: Parser = .{
1714 .iter = .{ .bytes = "abc:1234", .i = 2 },
1715 };
1400 var parser: Parser = .{ .bytes = "abc:1234", .i = 2 };
17161401 try testing.expectEqualStrings("c", parser.until(':'));
17171402 }
17181403
17191404 { // substring starts at index 4 and goes upto the end - `ch` not found
1720 var parser: Parser = .{
1721 .iter = .{ .bytes = "abc1234", .i = 4 },
1722 };
1405 var parser: Parser = .{ .bytes = "abc1234", .i = 4 };
17231406 try testing.expectEqualStrings("234", parser.until(':'));
17241407 }
17251408}
17261409
17271410test "parser peek" {
17281411 { // start iteration from the first index
1729 var parser: Parser = .{
1730 .iter = .{ .bytes = "hello world", .i = 0 },
1731 };
1732
1412 var parser: Parser = .{ .bytes = "hello world", .i = 0 };
17331413 try testing.expectEqual('h', parser.peek(0));
17341414 try testing.expectEqual('e', parser.peek(1));
17351415 try testing.expectEqual(' ', parser.peek(5));
......@@ -1738,9 +1418,7 @@ test "parser peek" {
17381418 }
17391419
17401420 { // start iteration from the second last index
1741 var parser: Parser = .{
1742 .iter = .{ .bytes = "hello world!", .i = 10 },
1743 };
1421 var parser: Parser = .{ .bytes = "hello world!", .i = 10 };
17441422
17451423 try testing.expectEqual('d', parser.peek(0));
17461424 try testing.expectEqual('!', parser.peek(1));
......@@ -1748,18 +1426,14 @@ test "parser peek" {
17481426 }
17491427
17501428 { // start iteration beyond the length of the string
1751 var parser: Parser = .{
1752 .iter = .{ .bytes = "hello", .i = 5 },
1753 };
1429 var parser: Parser = .{ .bytes = "hello", .i = 5 };
17541430
17551431 try testing.expectEqual(null, parser.peek(0));
17561432 try testing.expectEqual(null, parser.peek(1));
17571433 }
17581434
17591435 { // empty string
1760 var parser: Parser = .{
1761 .iter = .{ .bytes = "", .i = 0 },
1762 };
1436 var parser: Parser = .{ .bytes = "", .i = 0 };
17631437
17641438 try testing.expectEqual(null, parser.peek(0));
17651439 try testing.expectEqual(null, parser.peek(2));
......@@ -1768,78 +1442,78 @@ test "parser peek" {
17681442
17691443test "parser char" {
17701444 // character exists - iterator at 0
1771 var parser: Parser = .{ .iter = .{ .bytes = "~~hello", .i = 0 } };
1445 var parser: Parser = .{ .bytes = "~~hello", .i = 0 };
17721446 try testing.expectEqual('~', parser.char());
17731447
17741448 // character exists - iterator in the middle
1775 parser = .{ .iter = .{ .bytes = "~~hello", .i = 3 } };
1449 parser = .{ .bytes = "~~hello", .i = 3 };
17761450 try testing.expectEqual('e', parser.char());
17771451
17781452 // character exists - iterator at the end
1779 parser = .{ .iter = .{ .bytes = "~~hello", .i = 6 } };
1453 parser = .{ .bytes = "~~hello", .i = 6 };
17801454 try testing.expectEqual('o', parser.char());
17811455
17821456 // character doesn't exist - iterator beyond the length of the string
1783 parser = .{ .iter = .{ .bytes = "~~hello", .i = 7 } };
1457 parser = .{ .bytes = "~~hello", .i = 7 };
17841458 try testing.expectEqual(null, parser.char());
17851459}
17861460
17871461test "parser maybe" {
17881462 // character exists - iterator at 0
1789 var parser: Parser = .{ .iter = .{ .bytes = "hello world", .i = 0 } };
1463 var parser: Parser = .{ .bytes = "hello world", .i = 0 };
17901464 try testing.expect(parser.maybe('h'));
17911465
17921466 // character exists - iterator at space
1793 parser = .{ .iter = .{ .bytes = "hello world", .i = 5 } };
1467 parser = .{ .bytes = "hello world", .i = 5 };
17941468 try testing.expect(parser.maybe(' '));
17951469
17961470 // character exists - iterator at the end
1797 parser = .{ .iter = .{ .bytes = "hello world", .i = 10 } };
1471 parser = .{ .bytes = "hello world", .i = 10 };
17981472 try testing.expect(parser.maybe('d'));
17991473
18001474 // character doesn't exist - iterator beyond the length of the string
1801 parser = .{ .iter = .{ .bytes = "hello world", .i = 11 } };
1475 parser = .{ .bytes = "hello world", .i = 11 };
18021476 try testing.expect(!parser.maybe('e'));
18031477}
18041478
18051479test "parser number" {
18061480 // input is a single digit natural number - iterator at 0
1807 var parser: Parser = .{ .iter = .{ .bytes = "7", .i = 0 } };
1481 var parser: Parser = .{ .bytes = "7", .i = 0 };
18081482 try testing.expect(7 == parser.number());
18091483
18101484 // input is a two digit natural number - iterator at 1
1811 parser = .{ .iter = .{ .bytes = "29", .i = 1 } };
1485 parser = .{ .bytes = "29", .i = 1 };
18121486 try testing.expect(9 == parser.number());
18131487
18141488 // input is a two digit natural number - iterator beyond the length of the string
1815 parser = .{ .iter = .{ .bytes = "32", .i = 2 } };
1489 parser = .{ .bytes = "32", .i = 2 };
18161490 try testing.expectEqual(null, parser.number());
18171491
18181492 // input is an integer
1819 parser = .{ .iter = .{ .bytes = "0", .i = 0 } };
1493 parser = .{ .bytes = "0", .i = 0 };
18201494 try testing.expect(0 == parser.number());
18211495
18221496 // input is a negative integer
1823 parser = .{ .iter = .{ .bytes = "-2", .i = 0 } };
1497 parser = .{ .bytes = "-2", .i = 0 };
18241498 try testing.expectEqual(null, parser.number());
18251499
18261500 // input is a string
1827 parser = .{ .iter = .{ .bytes = "no_number", .i = 2 } };
1501 parser = .{ .bytes = "no_number", .i = 2 };
18281502 try testing.expectEqual(null, parser.number());
18291503
18301504 // input is a single character string
1831 parser = .{ .iter = .{ .bytes = "n", .i = 0 } };
1505 parser = .{ .bytes = "n", .i = 0 };
18321506 try testing.expectEqual(null, parser.number());
18331507
18341508 // input is an empty string
1835 parser = .{ .iter = .{ .bytes = "", .i = 0 } };
1509 parser = .{ .bytes = "", .i = 0 };
18361510 try testing.expectEqual(null, parser.number());
18371511}
18381512
18391513test "parser specifier" {
18401514 { // input string is a digit; iterator at 0
18411515 const expected: Specifier = Specifier{ .number = 1 };
1842 var parser: Parser = .{ .iter = .{ .bytes = "1", .i = 0 } };
1516 var parser: Parser = .{ .bytes = "1", .i = 0 };
18431517
18441518 const result = try parser.specifier();
18451519 try testing.expect(expected.number == result.number);
......@@ -1847,7 +1521,7 @@ test "parser specifier" {
18471521
18481522 { // input string is a two digit number; iterator at 0
18491523 const digit: Specifier = Specifier{ .number = 42 };
1850 var parser: Parser = .{ .iter = .{ .bytes = "42", .i = 0 } };
1524 var parser: Parser = .{ .bytes = "42", .i = 0 };
18511525
18521526 const result = try parser.specifier();
18531527 try testing.expect(digit.number == result.number);
......@@ -1855,7 +1529,7 @@ test "parser specifier" {
18551529
18561530 { // input string is a two digit number digit; iterator at 1
18571531 const digit: Specifier = Specifier{ .number = 8 };
1858 var parser: Parser = .{ .iter = .{ .bytes = "28", .i = 1 } };
1532 var parser: Parser = .{ .bytes = "28", .i = 1 };
18591533
18601534 const result = try parser.specifier();
18611535 try testing.expect(digit.number == result.number);
......@@ -1863,7 +1537,7 @@ test "parser specifier" {
18631537
18641538 { // input string is a two digit number with square brackets; iterator at 0
18651539 const digit: Specifier = Specifier{ .named = "15" };
1866 var parser: Parser = .{ .iter = .{ .bytes = "[15]", .i = 0 } };
1540 var parser: Parser = .{ .bytes = "[15]", .i = 0 };
18671541
18681542 const result = try parser.specifier();
18691543 try testing.expectEqualStrings(digit.named, result.named);
......@@ -1871,21 +1545,21 @@ test "parser specifier" {
18711545
18721546 { // input string is not a number and contains square brackets; iterator at 0
18731547 const digit: Specifier = Specifier{ .named = "hello" };
1874 var parser: Parser = .{ .iter = .{ .bytes = "[hello]", .i = 0 } };
1548 var parser: Parser = .{ .bytes = "[hello]", .i = 0 };
18751549
18761550 const result = try parser.specifier();
18771551 try testing.expectEqualStrings(digit.named, result.named);
18781552 }
18791553
18801554 { // input string is not a number and doesn't contain closing square bracket; iterator at 0
1881 var parser: Parser = .{ .iter = .{ .bytes = "[hello", .i = 0 } };
1555 var parser: Parser = .{ .bytes = "[hello", .i = 0 };
18821556
18831557 const result = parser.specifier();
18841558 try testing.expectError(@field(anyerror, "Expected closing ]"), result);
18851559 }
18861560
18871561 { // input string is not a number and doesn't contain closing square bracket; iterator at 2
1888 var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello", .i = 2 } };
1562 var parser: Parser = .{ .bytes = "[[[[hello", .i = 2 };
18891563
18901564 const result = parser.specifier();
18911565 try testing.expectError(@field(anyerror, "Expected closing ]"), result);
......@@ -1893,7 +1567,7 @@ test "parser specifier" {
18931567
18941568 { // input string is not a number and contains unbalanced square brackets; iterator at 0
18951569 const digit: Specifier = Specifier{ .named = "[[hello" };
1896 var parser: Parser = .{ .iter = .{ .bytes = "[[[hello]", .i = 0 } };
1570 var parser: Parser = .{ .bytes = "[[[hello]", .i = 0 };
18971571
18981572 const result = try parser.specifier();
18991573 try testing.expectEqualStrings(digit.named, result.named);
......@@ -1901,7 +1575,7 @@ test "parser specifier" {
19011575
19021576 { // input string is not a number and contains unbalanced square brackets; iterator at 1
19031577 const digit: Specifier = Specifier{ .named = "[[hello" };
1904 var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]", .i = 1 } };
1578 var parser: Parser = .{ .bytes = "[[[[hello]]]]]", .i = 1 };
19051579
19061580 const result = try parser.specifier();
19071581 try testing.expectEqualStrings(digit.named, result.named);
......@@ -1909,9 +1583,13 @@ test "parser specifier" {
19091583
19101584 { // input string is neither a digit nor a named argument
19111585 const char: Specifier = Specifier{ .none = {} };
1912 var parser: Parser = .{ .iter = .{ .bytes = "hello", .i = 0 } };
1586 var parser: Parser = .{ .bytes = "hello", .i = 0 };
19131587
19141588 const result = try parser.specifier();
19151589 try testing.expectEqual(char.none, result.none);
19161590 }
19171591}
1592
1593test {
1594 _ = float;
1595}
lib/std/fs/File.zig+193-108
......@@ -1,3 +1,20 @@
1const builtin = @import("builtin");
2const Os = std.builtin.Os;
3const native_os = builtin.os.tag;
4const is_windows = native_os == .windows;
5
6const File = @This();
7const std = @import("../std.zig");
8const Allocator = std.mem.Allocator;
9const posix = std.posix;
10const io = std.io;
11const math = std.math;
12const assert = std.debug.assert;
13const linux = std.os.linux;
14const windows = std.os.windows;
15const maxInt = std.math.maxInt;
16const Alignment = std.mem.Alignment;
17
118/// The OS-specific file descriptor or file handle.
219handle: Handle,
320
......@@ -844,7 +861,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {
844861 return posix.write(self.handle, bytes);
845862}
846863
847/// One-shot alternative to `std.io.Writer.writeAll` via `writer`.
864/// Deprecated in favor of `Writer`.
848865pub fn writeAll(self: File, bytes: []const u8) WriteError!void {
849866 var index: usize = 0;
850867 while (index < bytes.len) {
......@@ -900,6 +917,8 @@ pub const Reader = struct {
900917 file: File,
901918 err: ?ReadError = null,
902919 mode: Reader.Mode = .positional,
920 /// Tracks the true seek position in the file. To obtain the logical
921 /// position, subtract the buffer size from this value.
903922 pos: u64 = 0,
904923 size: ?u64 = null,
905924 size_err: ?GetEndPosError = null,
......@@ -1008,7 +1027,7 @@ pub const Reader = struct {
10081027 };
10091028 var remaining = std.math.cast(u64, offset) orelse return seek_err;
10101029 while (remaining > 0) {
1011 const n = discard(&r.interface, .limited(remaining)) catch |err| {
1030 const n = discard(&r.interface, .limited64(remaining)) catch |err| {
10121031 r.seek_err = err;
10131032 return err;
10141033 };
......@@ -1043,7 +1062,7 @@ pub const Reader = struct {
10431062 const max_buffers_len = 16;
10441063
10451064 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
1046 const r: *Reader = @fieldParentPtr("interface", io_reader);
1065 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
10471066 switch (r.mode) {
10481067 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
10491068 error.Unimplemented => {
......@@ -1067,10 +1086,14 @@ pub const Reader = struct {
10671086 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
10681087 error.Unseekable => {
10691088 r.mode = r.mode.toStreaming();
1070 if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch {
1071 r.mode = .failure;
1072 return error.ReadFailed;
1073 };
1089 const pos = r.pos;
1090 if (pos != 0) {
1091 r.pos = 0;
1092 r.seekBy(@intCast(pos)) catch {
1093 r.mode = .failure;
1094 return error.ReadFailed;
1095 };
1096 }
10741097 return 0;
10751098 },
10761099 else => |e| {
......@@ -1113,7 +1136,7 @@ pub const Reader = struct {
11131136 }
11141137
11151138 fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize {
1116 const r: *Reader = @fieldParentPtr("interface", io_reader);
1139 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
11171140 const file = r.file;
11181141 const pos = r.pos;
11191142 switch (r.mode) {
......@@ -1195,10 +1218,14 @@ pub const Reader = struct {
11951218 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
11961219 error.Unseekable => {
11971220 r.mode = r.mode.toStreaming();
1198 if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch {
1199 r.mode = .failure;
1200 return error.ReadFailed;
1201 };
1221 const pos = r.pos;
1222 if (pos != 0) {
1223 r.pos = 0;
1224 r.seekBy(@intCast(pos)) catch {
1225 r.mode = .failure;
1226 return error.ReadFailed;
1227 };
1228 }
12021229 return 0;
12031230 },
12041231 else => |e| {
......@@ -1246,6 +1273,8 @@ pub const Writer = struct {
12461273 file: File,
12471274 err: ?WriteError = null,
12481275 mode: Writer.Mode = .positional,
1276 /// Tracks the true seek position in the file. To obtain the logical
1277 /// position, add the buffer size to this value.
12491278 pos: u64 = 0,
12501279 sendfile_err: ?SendfileError = null,
12511280 copy_file_range_err: ?CopyFileRangeError = null,
......@@ -1308,110 +1337,162 @@ pub const Writer = struct {
13081337 };
13091338 }
13101339
1311 pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1312 const w: *Writer = @fieldParentPtr("interface", io_writer);
1340 pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1341 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
13131342 const handle = w.file.handle;
1314 const buffered = io_writer.buffered();
1315 var splat_buffer: [256]u8 = undefined;
1316 if (is_windows) {
1317 var i: usize = 0;
1318 while (i < buffered.len) {
1319 const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {
1343 const buffered = io_w.buffered();
1344 if (is_windows) switch (w.mode) {
1345 .positional, .positional_reading => {
1346 if (buffered.len != 0) {
1347 const n = windows.WriteFile(handle, buffered, w.pos) catch |err| {
1348 w.err = err;
1349 return error.WriteFailed;
1350 };
1351 w.pos += n;
1352 return io_w.consume(n);
1353 }
1354 for (data[0 .. data.len - 1]) |buf| {
1355 if (buf.len == 0) continue;
1356 const n = windows.WriteFile(handle, buf, w.pos) catch |err| {
1357 w.err = err;
1358 return error.WriteFailed;
1359 };
1360 w.pos += n;
1361 return io_w.consume(n);
1362 }
1363 const pattern = data[data.len - 1];
1364 if (pattern.len == 0 or splat == 0) return 0;
1365 const n = windows.WriteFile(handle, pattern, w.pos) catch |err| {
13201366 w.err = err;
1321 w.pos += i;
1322 _ = io_writer.consume(i);
13231367 return error.WriteFailed;
13241368 };
1325 i += n;
1326 if (data.len > 0 and buffered.len - i < n) {
1327 w.pos += i;
1328 return io_writer.consume(i);
1369 w.pos += n;
1370 return io_w.consume(n);
1371 },
1372 .streaming, .streaming_reading => {
1373 if (buffered.len != 0) {
1374 const n = windows.WriteFile(handle, buffered, null) catch |err| {
1375 w.err = err;
1376 return error.WriteFailed;
1377 };
1378 w.pos += n;
1379 return io_w.consume(n);
13291380 }
1330 }
1331 if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) {
1332 w.pos += i;
1333 return io_writer.consume(i);
1334 }
1335 const n = windows.WriteFile(handle, data[0], null) catch |err| {
1336 w.err = err;
1337 return 0;
1338 };
1339 w.pos += n;
1340 return n;
1341 }
1342 if (data.len == 0) {
1343 var i: usize = 0;
1344 while (i < buffered.len) {
1345 i += std.posix.write(handle, buffered) catch |err| {
1381 for (data[0 .. data.len - 1]) |buf| {
1382 if (buf.len == 0) continue;
1383 const n = windows.WriteFile(handle, buf, null) catch |err| {
1384 w.err = err;
1385 return error.WriteFailed;
1386 };
1387 w.pos += n;
1388 return io_w.consume(n);
1389 }
1390 const pattern = data[data.len - 1];
1391 if (pattern.len == 0 or splat == 0) return 0;
1392 const n = windows.WriteFile(handle, pattern, null) catch |err| {
1393 std.debug.print("windows write file failed3: {t}\n", .{err});
13461394 w.err = err;
1347 w.pos += i;
1348 _ = io_writer.consume(i);
13491395 return error.WriteFailed;
13501396 };
1351 }
1352 w.pos += i;
1353 return io_writer.consumeAll();
1354 }
1397 w.pos += n;
1398 return io_w.consume(n);
1399 },
1400 .failure => return error.WriteFailed,
1401 };
13551402 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
13561403 var len: usize = 0;
13571404 if (buffered.len > 0) {
13581405 iovecs[len] = .{ .base = buffered.ptr, .len = buffered.len };
13591406 len += 1;
13601407 }
1361 for (data) |d| {
1408 for (data[0 .. data.len - 1]) |d| {
13621409 if (d.len == 0) continue;
1363 if (iovecs.len - len == 0) break;
13641410 iovecs[len] = .{ .base = d.ptr, .len = d.len };
13651411 len += 1;
1412 if (iovecs.len - len == 0) break;
13661413 }
1367 switch (splat) {
1368 0 => if (data[data.len - 1].len != 0) {
1369 len -= 1;
1414 const pattern = data[data.len - 1];
1415 if (iovecs.len - len != 0) switch (splat) {
1416 0 => {},
1417 1 => if (pattern.len != 0) {
1418 iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len };
1419 len += 1;
13701420 },
1371 1 => {},
1372 else => switch (data[data.len - 1].len) {
1421 else => switch (pattern.len) {
13731422 0 => {},
13741423 1 => {
1424 const splat_buffer_candidate = io_w.buffer[io_w.end..];
1425 var backup_buffer: [64]u8 = undefined;
1426 const splat_buffer = if (splat_buffer_candidate.len >= backup_buffer.len)
1427 splat_buffer_candidate
1428 else
1429 &backup_buffer;
13751430 const memset_len = @min(splat_buffer.len, splat);
13761431 const buf = splat_buffer[0..memset_len];
1377 @memset(buf, data[data.len - 1][0]);
1378 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1432 @memset(buf, pattern[0]);
1433 iovecs[len] = .{ .base = buf.ptr, .len = buf.len };
1434 len += 1;
13791435 var remaining_splat = splat - buf.len;
1380 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
1381 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };
1382 remaining_splat -= splat_buffer.len;
1436 while (remaining_splat > splat_buffer.len and iovecs.len - len != 0) {
1437 assert(buf.len == splat_buffer.len);
1438 iovecs[len] = .{ .base = splat_buffer.ptr, .len = splat_buffer.len };
13831439 len += 1;
1440 remaining_splat -= splat_buffer.len;
13841441 }
1385 if (remaining_splat > 0 and len < iovecs.len) {
1386 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };
1442 if (remaining_splat > 0 and iovecs.len - len != 0) {
1443 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
13871444 len += 1;
13881445 }
1389 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1390 w.err = err;
1391 return error.WriteFailed;
1392 };
13931446 },
1394 else => for (0..splat - 1) |_| {
1395 if (iovecs.len - len == 0) break;
1396 iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].len };
1447 else => for (0..splat) |_| {
1448 iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len };
13971449 len += 1;
1450 if (iovecs.len - len == 0) break;
13981451 },
13991452 },
1400 }
1401 const n = std.posix.writev(handle, iovecs[0..len]) catch |err| {
1402 w.err = err;
1403 return error.WriteFailed;
14041453 };
1405 w.pos += n;
1406 return io_writer.consume(n);
1454 if (len == 0) return 0;
1455 switch (w.mode) {
1456 .positional, .positional_reading => {
1457 const n = std.posix.pwritev(handle, iovecs[0..len], w.pos) catch |err| switch (err) {
1458 error.Unseekable => {
1459 w.mode = w.mode.toStreaming();
1460 const pos = w.pos;
1461 if (pos != 0) {
1462 w.pos = 0;
1463 w.seekTo(@intCast(pos)) catch {
1464 w.mode = .failure;
1465 return error.WriteFailed;
1466 };
1467 }
1468 return 0;
1469 },
1470 else => |e| {
1471 w.err = e;
1472 return error.WriteFailed;
1473 },
1474 };
1475 w.pos += n;
1476 return io_w.consume(n);
1477 },
1478 .streaming, .streaming_reading => {
1479 const n = std.posix.writev(handle, iovecs[0..len]) catch |err| {
1480 w.err = err;
1481 return error.WriteFailed;
1482 };
1483 w.pos += n;
1484 return io_w.consume(n);
1485 },
1486 .failure => return error.WriteFailed,
1487 }
14071488 }
14081489
14091490 pub fn sendFile(
1410 io_writer: *std.io.Writer,
1491 io_w: *std.io.Writer,
14111492 file_reader: *Reader,
14121493 limit: std.io.Limit,
14131494 ) std.io.Writer.FileError!usize {
1414 const w: *Writer = @fieldParentPtr("interface", io_writer);
1495 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
14151496 const out_fd = w.file.handle;
14161497 const in_fd = file_reader.file.handle;
14171498 // TODO try using copy_file_range on FreeBSD
......@@ -1422,7 +1503,7 @@ pub const Writer = struct {
14221503 if (w.sendfile_err != null) break :sf;
14231504 // Linux sendfile does not support headers.
14241505 const buffered = limit.slice(file_reader.interface.buffer);
1425 if (io_writer.end != 0 or buffered.len != 0) return drain(io_writer, &.{buffered}, 1);
1506 if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1);
14261507 const max_count = 0x7ffff000; // Avoid EINVAL.
14271508 var off: std.os.linux.off_t = undefined;
14281509 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
......@@ -1446,10 +1527,14 @@ pub const Writer = struct {
14461527 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) {
14471528 error.Unseekable => {
14481529 file_reader.mode = file_reader.mode.toStreaming();
1449 if (file_reader.pos != 0) file_reader.seekBy(@intCast(file_reader.pos)) catch {
1450 file_reader.mode = .failure;
1451 return error.ReadFailed;
1452 };
1530 const pos = file_reader.pos;
1531 if (pos != 0) {
1532 file_reader.pos = 0;
1533 file_reader.seekBy(@intCast(pos)) catch {
1534 file_reader.mode = .failure;
1535 return error.ReadFailed;
1536 };
1537 }
14531538 return 0;
14541539 },
14551540 else => |e| {
......@@ -1465,21 +1550,21 @@ pub const Writer = struct {
14651550 w.pos += n;
14661551 return n;
14671552 }
1468 const copy_file_range_fn = switch (native_os) {
1553 const copy_file_range = switch (native_os) {
14691554 .freebsd => std.os.freebsd.copy_file_range,
1470 .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else null,
1471 else => null,
1555 .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {},
1556 else => {},
14721557 };
1473 if (copy_file_range_fn) |copy_file_range| cfr: {
1558 if (@TypeOf(copy_file_range) != void) cfr: {
14741559 if (w.copy_file_range_err != null) break :cfr;
14751560 const buffered = limit.slice(file_reader.interface.buffer);
1476 if (io_writer.end != 0 or buffered.len != 0) return drain(io_writer, &.{buffered}, 1);
1561 if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1);
14771562 var off_in: i64 = undefined;
14781563 var off_out: i64 = undefined;
14791564 const off_in_ptr: ?*i64 = switch (file_reader.mode) {
14801565 .positional_reading, .streaming_reading => return error.Unimplemented,
14811566 .positional => p: {
1482 off_in = file_reader.pos;
1567 off_in = @intCast(file_reader.pos);
14831568 break :p &off_in;
14841569 },
14851570 .streaming => null,
......@@ -1488,7 +1573,7 @@ pub const Writer = struct {
14881573 const off_out_ptr: ?*i64 = switch (w.mode) {
14891574 .positional_reading, .streaming_reading => return error.Unimplemented,
14901575 .positional => p: {
1491 off_out = w.pos;
1576 off_out = @intCast(w.pos);
14921577 break :p &off_out;
14931578 },
14941579 .streaming => null,
......@@ -1542,19 +1627,35 @@ pub const Writer = struct {
15421627 }
15431628
15441629 pub fn seekTo(w: *Writer, offset: u64) SeekError!void {
1545 if (w.seek_err) |err| return err;
15461630 switch (w.mode) {
15471631 .positional, .positional_reading => {
15481632 w.pos = offset;
15491633 },
15501634 .streaming, .streaming_reading => {
1635 if (w.seek_err) |err| return err;
15511636 posix.lseek_SET(w.file.handle, offset) catch |err| {
15521637 w.seek_err = err;
15531638 return err;
15541639 };
1640 w.pos = offset;
15551641 },
1642 .failure => return w.seek_err.?,
15561643 }
15571644 }
1645
1646 pub const EndError = SetEndPosError || std.io.Writer.Error;
1647
1648 /// Flushes any buffered data and sets the end position of the file.
1649 ///
1650 /// If not overwriting existing contents, then calling `interface.flush`
1651 /// directly is sufficient.
1652 ///
1653 /// Flush failure is handled by setting `err` so that it can be handled
1654 /// along with other write failures.
1655 pub fn end(w: *Writer) EndError!void {
1656 try w.interface.flush();
1657 return w.file.setEndPos(w.pos);
1658 }
15581659};
15591660
15601661/// Defaults to positional reading; falls back to streaming.
......@@ -1568,9 +1669,10 @@ pub fn reader(file: File, buffer: []u8) Reader {
15681669/// Positional is more threadsafe, since the global seek position is not
15691670/// affected, but when such syscalls are not available, preemptively choosing
15701671/// `Reader.Mode.streaming` will skip a failed syscall.
1571pub fn readerStreaming(file: File) Reader {
1672pub fn readerStreaming(file: File, buffer: []u8) Reader {
15721673 return .{
15731674 .file = file,
1675 .interface = Reader.initInterface(buffer),
15741676 .mode = .streaming,
15751677 .seek_err = error.Unseekable,
15761678 };
......@@ -1753,20 +1855,3 @@ pub fn downgradeLock(file: File) LockError!void {
17531855 };
17541856 }
17551857}
1756
1757const builtin = @import("builtin");
1758const Os = std.builtin.Os;
1759const native_os = builtin.os.tag;
1760const is_windows = native_os == .windows;
1761
1762const File = @This();
1763const std = @import("../std.zig");
1764const Allocator = std.mem.Allocator;
1765const posix = std.posix;
1766const io = std.io;
1767const math = std.math;
1768const assert = std.debug.assert;
1769const linux = std.os.linux;
1770const windows = std.os.windows;
1771const maxInt = std.math.maxInt;
1772const Alignment = std.mem.Alignment;
lib/std/fs/path.zig+6-8
......@@ -146,30 +146,28 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {
146146 return out[0 .. out.len - 1 :0];
147147}
148148
149pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter(formatJoin) {
149pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter([]const []const u8, formatJoin) {
150150 return .{ .data = paths };
151151}
152152
153fn formatJoin(paths: []const []const u8, bw: *std.io.Writer, comptime fmt: []const u8) !void {
154 comptime assert(fmt.len == 0);
155
153fn formatJoin(paths: []const []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
156154 const first_path_idx = for (paths, 0..) |p, idx| {
157155 if (p.len != 0) break idx;
158156 } else return;
159157
160 try bw.writeAll(paths[first_path_idx]); // first component
158 try w.writeAll(paths[first_path_idx]); // first component
161159 var prev_path = paths[first_path_idx];
162160 for (paths[first_path_idx + 1 ..]) |this_path| {
163161 if (this_path.len == 0) continue; // skip empty components
164162 const prev_sep = isSep(prev_path[prev_path.len - 1]);
165163 const this_sep = isSep(this_path[0]);
166164 if (!prev_sep and !this_sep) {
167 try bw.writeByte(sep);
165 try w.writeByte(sep);
168166 }
169167 if (prev_sep and this_sep) {
170 try bw.writeAll(this_path[1..]); // skip redundant separator
168 try w.writeAll(this_path[1..]); // skip redundant separator
171169 } else {
172 try bw.writeAll(this_path);
170 try w.writeAll(this_path);
173171 }
174172 prev_path = this_path;
175173 }
lib/std/fs/test.zig+2-2
......@@ -1798,11 +1798,11 @@ test "walker" {
17981798 var num_walked: usize = 0;
17991799 while (try walker.next()) |entry| {
18001800 testing.expect(expected_basenames.has(entry.basename)) catch |err| {
1801 std.debug.print("found unexpected basename: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.basename)});
1801 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
18021802 return err;
18031803 };
18041804 testing.expect(expected_paths.has(entry.path)) catch |err| {
1805 std.debug.print("found unexpected path: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.path)});
1805 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
18061806 return err;
18071807 };
18081808 // make sure that the entry.dir is the containing dir
lib/std/heap.zig+1-1
......@@ -287,7 +287,7 @@ fn rawCAlloc(
287287) ?[*]u8 {
288288 _ = context;
289289 _ = return_address;
290 assert(alignment.compare(.lte, comptime .fromByteUnits(@alignOf(std.c.max_align_t))));
290 assert(alignment.compare(.lte, .of(std.c.max_align_t)));
291291 // Note that this pointer cannot be aligncasted to max_align_t because if
292292 // len is < max_align_t then the alignment can be smaller. For example, if
293293 // max_align_t is 16, but the user requests 8 bytes, there is no built-in
lib/std/heap/arena_allocator.zig+1-1
......@@ -42,7 +42,7 @@ pub const ArenaAllocator = struct {
4242 data: usize,
4343 node: std.SinglyLinkedList.Node = .{},
4444 };
45 const BufNode_alignment: Alignment = .fromByteUnits(@alignOf(BufNode));
45 const BufNode_alignment: Alignment = .of(BufNode);
4646
4747 pub fn init(child_allocator: Allocator) ArenaAllocator {
4848 return (State{}).promote(child_allocator);
lib/std/heap/debug_allocator.zig+1-1
......@@ -1054,7 +1054,7 @@ const TraceKind = enum {
10541054 free,
10551055};
10561056
1057const test_config = Config{};
1057const test_config: Config = .{};
10581058
10591059test "small allocations - free in same order" {
10601060 var gpa = DebugAllocator(test_config){};
lib/std/http.zig+2-2
......@@ -42,8 +42,8 @@ pub const Method = enum(u64) {
4242 return x;
4343 }
4444
45 pub fn write(self: Method, w: anytype) !void {
46 const bytes = std.mem.asBytes(&@intFromEnum(self));
45 pub fn format(self: Method, w: *std.io.Writer) std.io.Writer.Error!void {
46 const bytes: []const u8 = @ptrCast(&@intFromEnum(self));
4747 const str = std.mem.sliceTo(bytes, 0);
4848 try w.writeAll(str);
4949 }
lib/std/http/Client.zig+13-4
......@@ -920,7 +920,7 @@ pub const Request = struct {
920920 .authority = connection.proxied,
921921 .path = true,
922922 .query = true,
923 }, w);
923 });
924924 }
925925 try w.writeByte(' ');
926926 try w.writeAll(@tagName(r.version));
......@@ -1280,9 +1280,18 @@ pub const basic_authorization = struct {
12801280 }
12811281
12821282 pub fn valueLengthFromUri(uri: Uri) usize {
1283 // TODO don't abuse formatted printing to count percent encoded characters
1284 const user_len = std.fmt.count("{fuser}", .{uri.user orelse Uri.Component.empty});
1285 const password_len = std.fmt.count("{fpassword}", .{uri.password orelse Uri.Component.empty});
1283 const user: Uri.Component = uri.user orelse .empty;
1284 const password: Uri.Component = uri.password orelse .empty;
1285
1286 var dw: std.io.Writer.Discarding = .init(&.{});
1287 user.formatUser(&dw.writer) catch unreachable; // discarding
1288 const user_len = dw.count + dw.writer.end;
1289
1290 dw.count = 0;
1291 dw.writer.end = 0;
1292 password.formatPassword(&dw.writer) catch unreachable; // discarding
1293 const password_len = dw.count + dw.writer.end;
1294
12861295 return valueLength(@intCast(user_len), @intCast(password_len));
12871296 }
12881297
lib/std/http/test.zig+2-4
......@@ -405,10 +405,8 @@ test "general client/server API coverage" {
405405 fn handleRequest(request: *http.Server.Request, listen_port: u16) !void {
406406 const log = std.log.scoped(.server);
407407
408 log.info("{} {s} {s}", .{
409 request.head.method,
410 @tagName(request.head.version),
411 request.head.target,
408 log.info("{f} {s} {s}", .{
409 request.head.method, @tagName(request.head.version), request.head.target,
412410 });
413411
414412 const gpa = std.testing.allocator;
lib/std/io.zig+10
......@@ -19,6 +19,12 @@ pub const Limit = enum(usize) {
1919 return @enumFromInt(n);
2020 }
2121
22 /// Any value grater than `std.math.maxInt(usize)` is interpreted to mean
23 /// `.unlimited`.
24 pub fn limited64(n: u64) Limit {
25 return @enumFromInt(@min(n, std.math.maxInt(usize)));
26 }
27
2228 pub fn countVec(data: []const []const u8) Limit {
2329 var total: usize = 0;
2430 for (data) |d| total += d.len;
......@@ -33,6 +39,10 @@ pub const Limit = enum(usize) {
3339 return @min(n, @intFromEnum(l));
3440 }
3541
42 pub fn minInt64(l: Limit, n: u64) usize {
43 return @min(n, @intFromEnum(l));
44 }
45
3646 pub fn slice(l: Limit, s: []u8) []u8 {
3747 return s[0..l.minInt(s.len)];
3848 }
lib/std/io/DeprecatedReader.zig created+386
......@@ -0,0 +1,386 @@
1context: *const anyopaque,
2readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize,
3
4pub const Error = anyerror;
5
6/// Returns the number of bytes read. It may be less than buffer.len.
7/// If the number of bytes read is 0, it means end of stream.
8/// End of stream is not an error condition.
9pub fn read(self: Self, buffer: []u8) anyerror!usize {
10 return self.readFn(self.context, buffer);
11}
12
13/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
14/// means the stream reached the end. Reaching the end of a stream is not an error
15/// condition.
16pub fn readAll(self: Self, buffer: []u8) anyerror!usize {
17 return readAtLeast(self, buffer, buffer.len);
18}
19
20/// Returns the number of bytes read, calling the underlying read
21/// function the minimal number of times until the buffer has at least
22/// `len` bytes filled. If the number read is less than `len` it means
23/// the stream reached the end. Reaching the end of the stream is not
24/// an error condition.
25pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize {
26 assert(len <= buffer.len);
27 var index: usize = 0;
28 while (index < len) {
29 const amt = try self.read(buffer[index..]);
30 if (amt == 0) break;
31 index += amt;
32 }
33 return index;
34}
35
36/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
37pub fn readNoEof(self: Self, buf: []u8) anyerror!void {
38 const amt_read = try self.readAll(buf);
39 if (amt_read < buf.len) return error.EndOfStream;
40}
41
42/// Appends to the `std.ArrayList` contents by reading from the stream
43/// until end of stream is found.
44/// If the number of bytes appended would exceed `max_append_size`,
45/// `error.StreamTooLong` is returned
46/// and the `std.ArrayList` has exactly `max_append_size` bytes appended.
47pub fn readAllArrayList(
48 self: Self,
49 array_list: *std.ArrayList(u8),
50 max_append_size: usize,
51) anyerror!void {
52 return self.readAllArrayListAligned(null, array_list, max_append_size);
53}
54
55pub fn readAllArrayListAligned(
56 self: Self,
57 comptime alignment: ?Alignment,
58 array_list: *std.ArrayListAligned(u8, alignment),
59 max_append_size: usize,
60) anyerror!void {
61 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
62 const original_len = array_list.items.len;
63 var start_index: usize = original_len;
64 while (true) {
65 array_list.expandToCapacity();
66 const dest_slice = array_list.items[start_index..];
67 const bytes_read = try self.readAll(dest_slice);
68 start_index += bytes_read;
69
70 if (start_index - original_len > max_append_size) {
71 array_list.shrinkAndFree(original_len + max_append_size);
72 return error.StreamTooLong;
73 }
74
75 if (bytes_read != dest_slice.len) {
76 array_list.shrinkAndFree(start_index);
77 return;
78 }
79
80 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.
81 try array_list.ensureTotalCapacity(start_index + 1);
82 }
83}
84
85/// Allocates enough memory to hold all the contents of the stream. If the allocated
86/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
87/// Caller owns returned memory.
88/// If this function returns an error, the contents from the stream read so far are lost.
89pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 {
90 var array_list = std.ArrayList(u8).init(allocator);
91 defer array_list.deinit();
92 try self.readAllArrayList(&array_list, max_size);
93 return try array_list.toOwnedSlice();
94}
95
96/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
97/// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.
98/// Does not include the delimiter in the result.
99/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
100/// `std.ArrayList` is populated with `max_size` bytes from the stream.
101pub fn readUntilDelimiterArrayList(
102 self: Self,
103 array_list: *std.ArrayList(u8),
104 delimiter: u8,
105 max_size: usize,
106) anyerror!void {
107 array_list.shrinkRetainingCapacity(0);
108 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
109}
110
111/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
112/// Allocates enough memory to read until `delimiter`. If the allocated
113/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
114/// Caller owns returned memory.
115/// If this function returns an error, the contents from the stream read so far are lost.
116pub fn readUntilDelimiterAlloc(
117 self: Self,
118 allocator: mem.Allocator,
119 delimiter: u8,
120 max_size: usize,
121) anyerror![]u8 {
122 var array_list = std.ArrayList(u8).init(allocator);
123 defer array_list.deinit();
124 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
125 return try array_list.toOwnedSlice();
126}
127
128/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
129/// Reads from the stream until specified byte is found. If the buffer is not
130/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
131/// If end-of-stream is found, `error.EndOfStream` is returned.
132/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
133/// delimiter byte is written to the output buffer but is not included
134/// in the returned slice.
135pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 {
136 var fbs = std.io.fixedBufferStream(buf);
137 try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len);
138 const output = fbs.getWritten();
139 buf[output.len] = delimiter; // emulating old behaviour
140 return output;
141}
142
143/// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead.
144/// Allocates enough memory to read until `delimiter` or end-of-stream.
145/// If the allocated memory would be greater than `max_size`, returns
146/// `error.StreamTooLong`. If end-of-stream is found, returns the rest
147/// of the stream. If this function is called again after that, returns
148/// null.
149/// Caller owns returned memory.
150/// If this function returns an error, the contents from the stream read so far are lost.
151pub fn readUntilDelimiterOrEofAlloc(
152 self: Self,
153 allocator: mem.Allocator,
154 delimiter: u8,
155 max_size: usize,
156) anyerror!?[]u8 {
157 var array_list = std.ArrayList(u8).init(allocator);
158 defer array_list.deinit();
159 self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) {
160 error.EndOfStream => if (array_list.items.len == 0) {
161 return null;
162 },
163 else => |e| return e,
164 };
165 return try array_list.toOwnedSlice();
166}
167
168/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
169/// Reads from the stream until specified byte is found. If the buffer is not
170/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
171/// If end-of-stream is found, returns the rest of the stream. If this
172/// function is called again after that, returns null.
173/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
174/// delimiter byte is written to the output buffer but is not included
175/// in the returned slice.
176pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) anyerror!?[]u8 {
177 var fbs = std.io.fixedBufferStream(buf);
178 self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) {
179 error.EndOfStream => if (fbs.getWritten().len == 0) {
180 return null;
181 },
182
183 else => |e| return e,
184 };
185 const output = fbs.getWritten();
186 buf[output.len] = delimiter; // emulating old behaviour
187 return output;
188}
189
190/// Appends to the `writer` contents by reading from the stream until `delimiter` is found.
191/// Does not write the delimiter itself.
192/// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
193/// returns `error.StreamTooLong` and finishes appending.
194/// If `optional_max_size` is null, appending is unbounded.
195pub fn streamUntilDelimiter(
196 self: Self,
197 writer: anytype,
198 delimiter: u8,
199 optional_max_size: ?usize,
200) anyerror!void {
201 if (optional_max_size) |max_size| {
202 for (0..max_size) |_| {
203 const byte: u8 = try self.readByte();
204 if (byte == delimiter) return;
205 try writer.writeByte(byte);
206 }
207 return error.StreamTooLong;
208 } else {
209 while (true) {
210 const byte: u8 = try self.readByte();
211 if (byte == delimiter) return;
212 try writer.writeByte(byte);
213 }
214 // Can not throw `error.StreamTooLong` since there are no boundary.
215 }
216}
217
218/// Reads from the stream until specified byte is found, discarding all data,
219/// including the delimiter.
220/// If end-of-stream is found, this function succeeds.
221pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void {
222 while (true) {
223 const byte = self.readByte() catch |err| switch (err) {
224 error.EndOfStream => return,
225 else => |e| return e,
226 };
227 if (byte == delimiter) return;
228 }
229}
230
231/// Reads 1 byte from the stream or returns `error.EndOfStream`.
232pub fn readByte(self: Self) anyerror!u8 {
233 var result: [1]u8 = undefined;
234 const amt_read = try self.read(result[0..]);
235 if (amt_read < 1) return error.EndOfStream;
236 return result[0];
237}
238
239/// Same as `readByte` except the returned byte is signed.
240pub fn readByteSigned(self: Self) anyerror!i8 {
241 return @as(i8, @bitCast(try self.readByte()));
242}
243
244/// Reads exactly `num_bytes` bytes and returns as an array.
245/// `num_bytes` must be comptime-known
246pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 {
247 var bytes: [num_bytes]u8 = undefined;
248 try self.readNoEof(&bytes);
249 return bytes;
250}
251
252/// Reads bytes until `bounded.len` is equal to `num_bytes`,
253/// or the stream ends.
254///
255/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`
256pub fn readIntoBoundedBytes(
257 self: Self,
258 comptime num_bytes: usize,
259 bounded: *std.BoundedArray(u8, num_bytes),
260) anyerror!void {
261 while (bounded.len < num_bytes) {
262 // get at most the number of bytes free in the bounded array
263 const bytes_read = try self.read(bounded.unusedCapacitySlice());
264 if (bytes_read == 0) return;
265
266 // bytes_read will never be larger than @TypeOf(bounded.len)
267 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
268 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
269 }
270}
271
272/// Reads at most `num_bytes` and returns as a bounded array.
273pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) {
274 var result = std.BoundedArray(u8, num_bytes){};
275 try self.readIntoBoundedBytes(num_bytes, &result);
276 return result;
277}
278
279pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
280 const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8));
281 return mem.readInt(T, &bytes, endian);
282}
283
284pub fn readVarInt(
285 self: Self,
286 comptime ReturnType: type,
287 endian: std.builtin.Endian,
288 size: usize,
289) anyerror!ReturnType {
290 assert(size <= @sizeOf(ReturnType));
291 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
292 const bytes = bytes_buf[0..size];
293 try self.readNoEof(bytes);
294 return mem.readVarInt(ReturnType, bytes, endian);
295}
296
297/// Optional parameters for `skipBytes`
298pub const SkipBytesOptions = struct {
299 buf_size: usize = 512,
300};
301
302// `num_bytes` is a `u64` to match `off_t`
303/// Reads `num_bytes` bytes from the stream and discards them
304pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void {
305 var buf: [options.buf_size]u8 = undefined;
306 var remaining = num_bytes;
307
308 while (remaining > 0) {
309 const amt = @min(remaining, options.buf_size);
310 try self.readNoEof(buf[0..amt]);
311 remaining -= amt;
312 }
313}
314
315/// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice
316pub fn isBytes(self: Self, slice: []const u8) anyerror!bool {
317 var i: usize = 0;
318 var matches = true;
319 while (i < slice.len) : (i += 1) {
320 if (slice[i] != try self.readByte()) {
321 matches = false;
322 }
323 }
324 return matches;
325}
326
327pub fn readStruct(self: Self, comptime T: type) anyerror!T {
328 // Only extern and packed structs have defined in-memory layout.
329 comptime assert(@typeInfo(T).@"struct".layout != .auto);
330 var res: [1]T = undefined;
331 try self.readNoEof(mem.sliceAsBytes(res[0..]));
332 return res[0];
333}
334
335pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
336 var res = try self.readStruct(T);
337 if (native_endian != endian) {
338 mem.byteSwapAllFields(T, &res);
339 }
340 return res;
341}
342
343/// Reads an integer with the same size as the given enum's tag type. If the integer matches
344/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.
345/// TODO optimization taking advantage of most fields being in order
346pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {
347 const E = error{
348 /// An integer was read, but it did not match any of the tags in the supplied enum.
349 InvalidValue,
350 };
351 const type_info = @typeInfo(Enum).@"enum";
352 const tag = try self.readInt(type_info.tag_type, endian);
353
354 inline for (std.meta.fields(Enum)) |field| {
355 if (tag == field.value) {
356 return @field(Enum, field.name);
357 }
358 }
359
360 return E.InvalidValue;
361}
362
363/// Reads the stream until the end, ignoring all the data.
364/// Returns the number of bytes discarded.
365pub fn discard(self: Self) anyerror!u64 {
366 var trash: [4096]u8 = undefined;
367 var index: u64 = 0;
368 while (true) {
369 const n = try self.read(&trash);
370 if (n == 0) return index;
371 index += n;
372 }
373}
374
375const std = @import("../std.zig");
376const Self = @This();
377const math = std.math;
378const assert = std.debug.assert;
379const mem = std.mem;
380const testing = std.testing;
381const native_endian = @import("builtin").target.cpu.arch.endian();
382const Alignment = std.mem.Alignment;
383
384test {
385 _ = @import("Reader/test.zig");
386}
lib/std/io/DeprecatedWriter.zig created+109
......@@ -0,0 +1,109 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const native_endian = @import("builtin").target.cpu.arch.endian();
5
6context: *const anyopaque,
7writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize,
8
9const Self = @This();
10pub const Error = anyerror;
11
12pub fn write(self: Self, bytes: []const u8) anyerror!usize {
13 return self.writeFn(self.context, bytes);
14}
15
16pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {
17 var index: usize = 0;
18 while (index != bytes.len) {
19 index += try self.write(bytes[index..]);
20 }
21}
22
23pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void {
24 return std.fmt.format(self, format, args);
25}
26
27pub fn writeByte(self: Self, byte: u8) anyerror!void {
28 const array = [1]u8{byte};
29 return self.writeAll(&array);
30}
31
32pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void {
33 var bytes: [256]u8 = undefined;
34 @memset(bytes[0..], byte);
35
36 var remaining: usize = n;
37 while (remaining > 0) {
38 const to_write = @min(remaining, bytes.len);
39 try self.writeAll(bytes[0..to_write]);
40 remaining -= to_write;
41 }
42}
43
44pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) anyerror!void {
45 var i: usize = 0;
46 while (i < n) : (i += 1) {
47 try self.writeAll(bytes);
48 }
49}
50
51pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {
52 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;
53 mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
54 return self.writeAll(&bytes);
55}
56
57pub fn writeStruct(self: Self, value: anytype) anyerror!void {
58 // Only extern and packed structs have defined in-memory layout.
59 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
60 return self.writeAll(mem.asBytes(&value));
61}
62
63pub fn writeStructEndian(self: Self, value: anytype, endian: std.builtin.Endian) anyerror!void {
64 // TODO: make sure this value is not a reference type
65 if (native_endian == endian) {
66 return self.writeStruct(value);
67 } else {
68 var copy = value;
69 mem.byteSwapAllFields(@TypeOf(value), &copy);
70 return self.writeStruct(copy);
71 }
72}
73
74pub fn writeFile(self: Self, file: std.fs.File) anyerror!void {
75 // TODO: figure out how to adjust std lib abstractions so that this ends up
76 // doing sendfile or maybe even copy_file_range under the right conditions.
77 var buf: [4000]u8 = undefined;
78 while (true) {
79 const n = try file.readAll(&buf);
80 try self.writeAll(buf[0..n]);
81 if (n < buf.len) return;
82 }
83}
84
85/// Helper for bridging to the new `Writer` API while upgrading.
86pub fn adaptToNewApi(self: *const Self) Adapter {
87 return .{
88 .derp_writer = self.*,
89 .new_interface = .{
90 .buffer = &.{},
91 .vtable = &.{ .drain = Adapter.drain },
92 },
93 };
94}
95
96pub const Adapter = struct {
97 derp_writer: Self,
98 new_interface: std.io.Writer,
99 err: ?Error = null,
100
101 fn drain(w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
102 _ = splat;
103 const a: *@This() = @fieldParentPtr("new_interface", w);
104 return a.derp_writer.write(data[0]) catch |err| {
105 a.err = err;
106 return error.WriteFailed;
107 };
108 }
109};
lib/std/io/Reader.zig+440-171
......@@ -26,7 +26,8 @@ pub const VTable = struct {
2626 /// Returns the number of bytes written, which will be at minimum `0` and
2727 /// at most `limit`. The number returned, including zero, does not indicate
2828 /// end of stream. `limit` is guaranteed to be at least as large as the
29 /// buffer capacity of `w`.
29 /// buffer capacity of `w`, a value whose minimum size is determined by the
30 /// stream implementation.
3031 ///
3132 /// The reader's internal logical seek position moves forward in accordance
3233 /// with the number of bytes returned from this function.
......@@ -35,7 +36,15 @@ pub const VTable = struct {
3536 /// sizes combined with short reads (returning a value less than `limit`)
3637 /// in order to minimize complexity.
3738 ///
38 /// This function is always called when `buffer` is empty.
39 /// Although this function is usually called when `buffer` is empty, it is
40 /// also called when it needs to be filled more due to the API user
41 /// requesting contiguous memory. In either case, the existing buffer data
42 /// should be ignored; new data written to `w`.
43 ///
44 /// In addition to, or instead of writing to `w`, the implementation may
45 /// choose to store data in `buffer`, modifying `seek` and `end`
46 /// accordingly. Stream implementations are encouraged to take advantage of
47 /// this if simplifies the logic.
3948 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
4049
4150 /// Consumes bytes from the internally tracked stream position without
......@@ -55,6 +64,8 @@ pub const VTable = struct {
5564 /// The default implementation is is based on calling `stream`, borrowing
5665 /// `buffer` to construct a temporary `Writer` and ignoring the written
5766 /// data.
67 ///
68 /// This function is only called when `buffer` is empty.
5869 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
5970};
6071
......@@ -102,7 +113,7 @@ const ending_state: Reader = .fixed(&.{});
102113pub const ending: *Reader = @constCast(&ending_state);
103114
104115pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {
105 return Limited.init(r, limit, buffer);
116 return .init(r, limit, buffer);
106117}
107118
108119/// Constructs a `Reader` such that it will read from `buffer` and then end.
......@@ -128,10 +139,8 @@ pub fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
128139 r.seek += n;
129140 return n;
130141 }
131 const before = w.count;
132142 const n = try r.vtable.stream(r, w, limit);
133143 assert(n <= @intFromEnum(limit));
134 assert(w.count == before + n);
135144 return n;
136145}
137146
......@@ -154,19 +163,13 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {
154163pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
155164 assert(r.seek == 0);
156165 assert(r.end == 0);
157 var w: Writer = .discarding(r.buffer);
158 const n = r.stream(&w, limit) catch |err| switch (err) {
166 var dw: Writer.Discarding = .init(r.buffer);
167 const n = r.stream(&dw.writer, limit) catch |err| switch (err) {
159168 error.WriteFailed => unreachable,
160169 error.ReadFailed => return error.ReadFailed,
161170 error.EndOfStream => return error.EndOfStream,
162171 };
163 if (n > @intFromEnum(limit)) {
164 const over_amt = n - @intFromEnum(limit);
165 r.seek = w.end - over_amt;
166 r.end = w.end;
167 assert(r.end <= w.buffer.len); // limit may be exceeded only by an amount within buffer capacity.
168 return @intFromEnum(limit);
169 }
172 assert(n <= @intFromEnum(limit));
170173 return n;
171174}
172175
......@@ -193,7 +196,7 @@ pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize {
193196/// Consumes the stream until the end, ignoring all the data, returning the
194197/// number of bytes discarded.
195198pub fn discardRemaining(r: *Reader) ShortError!usize {
196 var offset: usize = r.end;
199 var offset: usize = r.end - r.seek;
197200 r.seek = 0;
198201 r.end = 0;
199202 while (true) {
......@@ -262,10 +265,9 @@ pub fn appendRemaining(
262265 error.EndOfStream => break,
263266 error.ReadFailed => return error.ReadFailed,
264267 };
265 if (n >= dest.len) {
268 if (n > dest.len) {
266269 r.end = n - dest.len;
267270 list.items.len += dest.len;
268 if (n == dest.len) return;
269271 return error.StreamTooLong;
270272 }
271273 list.items.len += n;
......@@ -320,22 +322,29 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
320322 },
321323 .writer = .{
322324 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
323 .vtable = &Writer.VectorWrapper.vtable,
325 .vtable = Writer.VectorWrapper.vtable,
324326 },
325327 };
326328 var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {
327329 error.WriteFailed => {
330 assert(!wrapper.used);
328331 if (wrapper.writer.buffer.ptr == first.ptr) {
329332 remaining -= wrapper.writer.end;
330333 } else {
334 assert(wrapper.writer.end <= r.buffer.len);
331335 r.end = wrapper.writer.end;
332336 }
333337 break;
334338 },
335339 else => |e| return e,
336340 };
337 if (wrapper.writer.buffer.ptr != first.ptr) {
338 r.end = n;
341 if (!wrapper.used) {
342 if (wrapper.writer.buffer.ptr == first.ptr) {
343 remaining -= n;
344 } else {
345 assert(n <= r.buffer.len);
346 r.end = n;
347 }
339348 break;
340349 }
341350 if (n < first.len) {
......@@ -352,6 +361,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
352361 remaining -= mid.len;
353362 n -= mid.len;
354363 }
364 assert(n <= r.buffer.len);
355365 r.end = n;
356366 break;
357367 }
......@@ -441,7 +451,7 @@ pub fn toss(r: *Reader, n: usize) void {
441451}
442452
443453/// Equivalent to `toss(r.bufferedLen())`.
444pub fn tossAll(r: *Reader) void {
454pub fn tossBuffered(r: *Reader) void {
445455 r.seek = 0;
446456 r.end = 0;
447457}
......@@ -553,7 +563,7 @@ pub fn discardShort(r: *Reader, n: usize) ShortError!usize {
553563/// See also:
554564/// * `peek`
555565/// * `readSliceShort`
556pub fn readSlice(r: *Reader, buffer: []u8) Error!void {
566pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
557567 const n = try readSliceShort(r, buffer);
558568 if (n != buffer.len) return error.EndOfStream;
559569}
......@@ -567,7 +577,7 @@ pub fn readSlice(r: *Reader, buffer: []u8) Error!void {
567577/// only if the stream reached the end.
568578///
569579/// See also:
570/// * `readSlice`
580/// * `readSliceAll`
571581pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
572582 const in_buffer = r.buffer[r.seek..r.end];
573583 const copy_len = @min(buffer.len, in_buffer.len);
......@@ -588,17 +598,16 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
588598 },
589599 .writer = .{
590600 .buffer = if (remaining.len >= r.buffer.len) remaining else r.buffer,
591 .vtable = &Writer.VectorWrapper.vtable,
601 .vtable = Writer.VectorWrapper.vtable,
592602 },
593603 };
594604 const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) {
595605 error.WriteFailed => {
596 if (wrapper.writer.buffer.ptr != remaining.ptr) {
606 if (!wrapper.used) {
597607 assert(r.seek == 0);
598608 r.seek = remaining.len;
599609 r.end = wrapper.writer.end;
600610 @memcpy(remaining, r.buffer[0..remaining.len]);
601 return buffer.len;
602611 }
603612 return buffer.len;
604613 },
......@@ -626,7 +635,7 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
626635/// comptime-known and matches host endianness.
627636///
628637/// See also:
629/// * `readSlice`
638/// * `readSliceAll`
630639/// * `readSliceEndianAlloc`
631640pub inline fn readSliceEndian(
632641 r: *Reader,
......@@ -634,7 +643,7 @@ pub inline fn readSliceEndian(
634643 buffer: []Elem,
635644 endian: std.builtin.Endian,
636645) Error!void {
637 try readSlice(r, @ptrCast(buffer));
646 try readSliceAll(r, @ptrCast(buffer));
638647 if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem);
639648}
640649
......@@ -651,15 +660,16 @@ pub inline fn readSliceEndianAlloc(
651660) ReadAllocError![]Elem {
652661 const dest = try allocator.alloc(Elem, len);
653662 errdefer allocator.free(dest);
654 try readSlice(r, @ptrCast(dest));
663 try readSliceAll(r, @ptrCast(dest));
655664 if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem);
656665 return dest;
657666}
658667
659pub fn readSliceAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 {
668/// Shortcut for calling `readSliceAll` with a buffer provided by `allocator`.
669pub fn readAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 {
660670 const dest = try allocator.alloc(u8, len);
661671 errdefer allocator.free(dest);
662 try readSlice(r, dest);
672 try readSliceAll(r, dest);
663673 return dest;
664674}
665675
......@@ -692,6 +702,17 @@ pub fn takeSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel
692702 return result;
693703}
694704
705/// Returns a slice of the next bytes of buffered data from the stream until
706/// `sentinel` is found, without advancing the seek position.
707///
708/// Returned slice has a sentinel; end of stream does not count as a delimiter.
709///
710/// Invalidates previously returned values from `peek`.
711///
712/// See also:
713/// * `takeSentinel`
714/// * `peekDelimiterExclusive`
715/// * `peekDelimiterInclusive`
695716pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {
696717 const result = try r.peekDelimiterInclusive(sentinel);
697718 return result[0 .. result.len - 1 :sentinel];
......@@ -732,26 +753,21 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
732753 @branchHint(.likely);
733754 return buffer[seek .. end + 1];
734755 }
735 if (seek > 0) {
736 const remainder = buffer[seek..];
737 @memmove(buffer[0..remainder.len], remainder);
738 r.end = remainder.len;
739 r.seek = 0;
756 if (r.vtable.stream == &endingStream) {
757 // Protect the `@constCast` of `fixed`.
758 return error.EndOfStream;
740759 }
741 var writer: Writer = .{
742 .buffer = r.buffer,
743 .vtable = &.{ .drain = Writer.fixedDrain },
744 };
745 while (r.end < r.buffer.len) {
746 writer.end = r.end;
747 const n = r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
760 r.rebase();
761 while (r.buffer.len - r.end != 0) {
762 const end_cap = r.buffer[r.end..];
763 var writer: Writer = .fixed(end_cap);
764 const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) {
748765 error.WriteFailed => unreachable,
749766 else => |e| return e,
750767 };
751 const prev_end = r.end;
752 r.end = prev_end + n;
753 if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], prev_end, delimiter)) |end| {
754 return r.buffer[0 .. end + 1];
768 r.end += n;
769 if (std.mem.indexOfScalarPos(u8, end_cap[0..n], 0, delimiter)) |end| {
770 return r.buffer[0 .. r.end - n + end + 1];
755771 }
756772 }
757773 return error.StreamTooLong;
......@@ -777,9 +793,10 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
777793pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
778794 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
779795 error.EndOfStream => {
780 if (r.end == 0) return error.EndOfStream;
781 r.toss(r.end);
782 return r.buffer[0..r.end];
796 const remaining = r.buffer[r.seek..r.end];
797 if (remaining.len == 0) return error.EndOfStream;
798 r.toss(remaining.len);
799 return remaining;
783800 },
784801 else => |e| return e,
785802 };
......@@ -807,8 +824,10 @@ pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
807824pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
808825 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
809826 error.EndOfStream => {
810 if (r.end == 0) return error.EndOfStream;
811 return r.buffer[0..r.end];
827 const remaining = r.buffer[r.seek..r.end];
828 if (remaining.len == 0) return error.EndOfStream;
829 r.toss(remaining.len);
830 return remaining;
812831 },
813832 else => |e| return e,
814833 };
......@@ -818,37 +837,50 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
818837/// Appends to `w` contents by reading from the stream until `delimiter` is
819838/// found. Does not write the delimiter itself.
820839///
821/// Returns number of bytes streamed.
822pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize {
823 const amount, const to = try r.readAny(w, delimiter, .unlimited);
824 return switch (to) {
825 .delimiter => amount,
826 .limit => unreachable,
827 .end => error.EndOfStream,
840/// Returns number of bytes streamed, which may be zero, or error.EndOfStream
841/// if the delimiter was not found.
842///
843/// Asserts buffer capacity of at least one. This function performs better with
844/// larger buffers.
845///
846/// See also:
847/// * `streamDelimiterEnding`
848/// * `streamDelimiterLimit`
849pub fn streamDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize {
850 const n = streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) {
851 error.StreamTooLong => unreachable, // unlimited is passed
852 else => |e| return e,
828853 };
854 if (r.seek == r.end) return error.EndOfStream;
855 return n;
829856}
830857
831858/// Appends to `w` contents by reading from the stream until `delimiter` is found.
832859/// Does not write the delimiter itself.
833860///
834/// Succeeds if stream ends before delimiter found.
861/// Returns number of bytes streamed, which may be zero. End of stream can be
862/// detected by checking if the next byte in the stream is the delimiter.
863///
864/// Asserts buffer capacity of at least one. This function performs better with
865/// larger buffers.
835866///
836/// Returns number of bytes streamed. The end is not signaled to the writer.
837pub fn readDelimiterEnding(
867/// See also:
868/// * `streamDelimiter`
869/// * `streamDelimiterLimit`
870pub fn streamDelimiterEnding(
838871 r: *Reader,
839872 w: *Writer,
840873 delimiter: u8,
841874) StreamRemainingError!usize {
842 const amount, const to = try r.readAny(w, delimiter, .unlimited);
843 return switch (to) {
844 .delimiter, .end => amount,
845 .limit => unreachable,
875 return streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) {
876 error.StreamTooLong => unreachable, // unlimited is passed
877 else => |e| return e,
846878 };
847879}
848880
849pub const StreamDelimiterLimitedError = StreamRemainingError || error{
850 /// Stream ended before the delimiter was found.
851 EndOfStream,
881pub const StreamDelimiterLimitError = error{
882 ReadFailed,
883 WriteFailed,
852884 /// The delimiter was not found within the limit.
853885 StreamTooLong,
854886};
......@@ -856,65 +888,103 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{
856888/// Appends to `w` contents by reading from the stream until `delimiter` is found.
857889/// Does not write the delimiter itself.
858890///
859/// Returns number of bytes streamed.
860pub fn readDelimiterLimit(
891/// Returns number of bytes streamed, which may be zero. End of stream can be
892/// detected by checking if the next byte in the stream is the delimiter.
893///
894/// Asserts buffer capacity of at least one. This function performs better with
895/// larger buffers.
896pub fn streamDelimiterLimit(
861897 r: *Reader,
862898 w: *Writer,
863899 delimiter: u8,
864900 limit: Limit,
865) StreamDelimiterLimitedError!usize {
866 const amount, const to = try r.readAny(w, delimiter, limit);
867 return switch (to) {
868 .delimiter => amount,
869 .limit => error.StreamTooLong,
870 .end => error.EndOfStream,
871 };
872}
873
874fn readAny(
875 r: *Reader,
876 w: *Writer,
877 delimiter: ?u8,
878 limit: Limit,
879) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } {
880 var amount: usize = 0;
881 var remaining = limit;
882 while (remaining.nonzero()) {
883 const available = remaining.slice(r.peekGreedy(1) catch |err| switch (err) {
884 error.ReadFailed => |e| return e,
885 error.EndOfStream => return .{ amount, .end },
901) StreamDelimiterLimitError!usize {
902 var remaining = @intFromEnum(limit);
903 while (remaining != 0) {
904 const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) {
905 error.ReadFailed => return error.ReadFailed,
906 error.EndOfStream => return @intFromEnum(limit) - remaining,
886907 });
887 if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| {
908 if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| {
888909 try w.writeAll(available[0..delimiter_index]);
889 r.toss(delimiter_index + 1);
890 return .{ amount + delimiter_index, .delimiter };
891 };
910 r.toss(delimiter_index);
911 remaining -= delimiter_index;
912 return @intFromEnum(limit) - remaining;
913 }
892914 try w.writeAll(available);
893915 r.toss(available.len);
894 amount += available.len;
895 remaining = remaining.subtract(available.len).?;
916 remaining -= available.len;
896917 }
897 return .{ amount, .limit };
918 return error.StreamTooLong;
898919}
899920
900921/// Reads from the stream until specified byte is found, discarding all data,
901922/// including the delimiter.
902923///
903/// If end of stream is found, this function succeeds.
904pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void {
905 _ = r;
906 _ = delimiter;
907 @panic("TODO");
924/// Returns number of bytes discarded, or `error.EndOfStream` if the delimiter
925/// is not found.
926///
927/// See also:
928/// * `discardDelimiterExclusive`
929/// * `discardDelimiterLimit`
930pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!usize {
931 const n = discardDelimiterLimit(r, delimiter, .unlimited) catch |err| switch (err) {
932 error.StreamTooLong => unreachable, // unlimited is passed
933 else => |e| return e,
934 };
935 if (r.seek == r.end) return error.EndOfStream;
936 assert(r.buffer[r.seek] == delimiter);
937 toss(r, 1);
938 return n + 1;
908939}
909940
910941/// Reads from the stream until specified byte is found, discarding all data,
911942/// excluding the delimiter.
912943///
913/// Succeeds if stream ends before delimiter found.
914pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!void {
915 _ = r;
916 _ = delimiter;
917 @panic("TODO");
944/// Returns the number of bytes discarded.
945///
946/// Succeeds if stream ends before delimiter found. End of stream can be
947/// detected by checking if the delimiter is buffered.
948///
949/// See also:
950/// * `discardDelimiterInclusive`
951/// * `discardDelimiterLimit`
952pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!usize {
953 return discardDelimiterLimit(r, delimiter, .unlimited) catch |err| switch (err) {
954 error.StreamTooLong => unreachable, // unlimited is passed
955 else => |e| return e,
956 };
957}
958
959pub const DiscardDelimiterLimitError = error{
960 ReadFailed,
961 /// The delimiter was not found within the limit.
962 StreamTooLong,
963};
964
965/// Reads from the stream until specified byte is found, discarding all data,
966/// excluding the delimiter.
967///
968/// Returns the number of bytes discarded.
969///
970/// Succeeds if stream ends before delimiter found. End of stream can be
971/// detected by checking if the delimiter is buffered.
972pub fn discardDelimiterLimit(r: *Reader, delimiter: u8, limit: Limit) DiscardDelimiterLimitError!usize {
973 var remaining = @intFromEnum(limit);
974 while (remaining != 0) {
975 const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) {
976 error.ReadFailed => return error.ReadFailed,
977 error.EndOfStream => return @intFromEnum(limit) - remaining,
978 });
979 if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| {
980 r.toss(delimiter_index);
981 remaining -= delimiter_index;
982 return @intFromEnum(limit) - remaining;
983 }
984 r.toss(available.len);
985 remaining -= available.len;
986 }
987 return error.StreamTooLong;
918988}
919989
920990/// Fills the buffer such that it contains at least `n` bytes, without
......@@ -930,6 +1000,19 @@ pub fn fill(r: *Reader, n: usize) Error!void {
9301000 @branchHint(.likely);
9311001 return;
9321002 }
1003 if (r.seek + n <= r.buffer.len) while (true) {
1004 const end_cap = r.buffer[r.end..];
1005 var writer: Writer = .fixed(end_cap);
1006 r.end += r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) {
1007 error.WriteFailed => unreachable,
1008 else => |e| return e,
1009 };
1010 if (r.seek + n <= r.end) return;
1011 };
1012 if (r.vtable.stream == &endingStream) {
1013 // Protect the `@constCast` of `fixed`.
1014 return error.EndOfStream;
1015 }
9331016 rebaseCapacity(r, n);
9341017 var writer: Writer = .{
9351018 .buffer = r.buffer,
......@@ -970,11 +1053,12 @@ pub fn fillMore(r: *Reader) Error!void {
9701053pub fn peekByte(r: *Reader) Error!u8 {
9711054 const buffer = r.buffer[0..r.end];
9721055 const seek = r.seek;
973 if (seek >= buffer.len) {
974 @branchHint(.unlikely);
975 try fill(r, 1);
1056 if (seek < buffer.len) {
1057 @branchHint(.likely);
1058 return buffer[seek];
9761059 }
977 return buffer[seek];
1060 try fill(r, 1);
1061 return r.buffer[r.seek];
9781062}
9791063
9801064/// Reads 1 byte from the stream or returns `error.EndOfStream`.
......@@ -1009,6 +1093,7 @@ pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n:
10091093///
10101094/// See also:
10111095/// * `peekStruct`
1096/// * `takeStructEndian`
10121097pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
10131098 // Only extern and packed structs have defined in-memory layout.
10141099 comptime assert(@typeInfo(T).@"struct".layout != .auto);
......@@ -1021,6 +1106,7 @@ pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
10211106///
10221107/// See also:
10231108/// * `takeStruct`
1109/// * `peekStructEndian`
10241110pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
10251111 // Only extern and packed structs have defined in-memory layout.
10261112 comptime assert(@typeInfo(T).@"struct".layout != .auto);
......@@ -1031,6 +1117,10 @@ pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
10311117///
10321118/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
10331119/// when `endian` is comptime-known and matches the host endianness.
1120///
1121/// See also:
1122/// * `takeStruct`
1123/// * `peekStructEndian`
10341124pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
10351125 var res = (try r.takeStruct(T)).*;
10361126 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
......@@ -1041,6 +1131,10 @@ pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin
10411131///
10421132/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
10431133/// when `endian` is comptime-known and matches the host endianness.
1134///
1135/// See also:
1136/// * `takeStructEndian`
1137/// * `peekStruct`
10441138pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
10451139 var res = (try r.peekStruct(T)).*;
10461140 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
......@@ -1218,146 +1312,295 @@ test fixed {
12181312}
12191313
12201314test peek {
1221 return error.Unimplemented;
1315 var r: Reader = .fixed("abc");
1316 try testing.expectEqualStrings("ab", try r.peek(2));
1317 try testing.expectEqualStrings("a", try r.peek(1));
12221318}
12231319
12241320test peekGreedy {
1225 return error.Unimplemented;
1321 var r: Reader = .fixed("abc");
1322 try testing.expectEqualStrings("abc", try r.peekGreedy(1));
12261323}
12271324
12281325test toss {
1229 return error.Unimplemented;
1326 var r: Reader = .fixed("abc");
1327 r.toss(1);
1328 try testing.expectEqualStrings("bc", r.buffered());
12301329}
12311330
12321331test take {
1233 return error.Unimplemented;
1332 var r: Reader = .fixed("abc");
1333 try testing.expectEqualStrings("ab", try r.take(2));
1334 try testing.expectEqualStrings("c", try r.take(1));
12341335}
12351336
12361337test takeArray {
1237 return error.Unimplemented;
1338 var r: Reader = .fixed("abc");
1339 try testing.expectEqualStrings("ab", try r.takeArray(2));
1340 try testing.expectEqualStrings("c", try r.takeArray(1));
12381341}
12391342
12401343test peekArray {
1241 return error.Unimplemented;
1344 var r: Reader = .fixed("abc");
1345 try testing.expectEqualStrings("ab", try r.peekArray(2));
1346 try testing.expectEqualStrings("a", try r.peekArray(1));
12421347}
12431348
12441349test discardAll {
12451350 var r: Reader = .fixed("foobar");
1246 try r.discard(3);
1351 try r.discardAll(3);
12471352 try testing.expectEqualStrings("bar", try r.take(3));
1248 try r.discard(0);
1249 try testing.expectError(error.EndOfStream, r.discard(1));
1353 try r.discardAll(0);
1354 try testing.expectError(error.EndOfStream, r.discardAll(1));
12501355}
12511356
12521357test discardRemaining {
1253 return error.Unimplemented;
1358 var r: Reader = .fixed("foobar");
1359 r.toss(1);
1360 try testing.expectEqual(5, try r.discardRemaining());
1361 try testing.expectEqual(0, try r.discardRemaining());
12541362}
12551363
12561364test stream {
1257 return error.Unimplemented;
1365 var out_buffer: [10]u8 = undefined;
1366 var r: Reader = .fixed("foobar");
1367 var w: Writer = .fixed(&out_buffer);
1368 // Short streams are possible with this function but not with fixed.
1369 try testing.expectEqual(2, try r.stream(&w, .limited(2)));
1370 try testing.expectEqualStrings("fo", w.buffered());
1371 try testing.expectEqual(4, try r.stream(&w, .unlimited));
1372 try testing.expectEqualStrings("foobar", w.buffered());
12581373}
12591374
12601375test takeSentinel {
1261 return error.Unimplemented;
1376 var r: Reader = .fixed("ab\nc");
1377 try testing.expectEqualStrings("ab", try r.takeSentinel('\n'));
1378 try testing.expectError(error.EndOfStream, r.takeSentinel('\n'));
1379 try testing.expectEqualStrings("c", try r.peek(1));
12621380}
12631381
12641382test peekSentinel {
1265 return error.Unimplemented;
1383 var r: Reader = .fixed("ab\nc");
1384 try testing.expectEqualStrings("ab", try r.peekSentinel('\n'));
1385 try testing.expectEqualStrings("ab", try r.peekSentinel('\n'));
12661386}
12671387
12681388test takeDelimiterInclusive {
1269 return error.Unimplemented;
1389 var r: Reader = .fixed("ab\nc");
1390 try testing.expectEqualStrings("ab\n", try r.takeDelimiterInclusive('\n'));
1391 try testing.expectError(error.EndOfStream, r.takeDelimiterInclusive('\n'));
12701392}
12711393
12721394test peekDelimiterInclusive {
1273 return error.Unimplemented;
1395 var r: Reader = .fixed("ab\nc");
1396 try testing.expectEqualStrings("ab\n", try r.peekDelimiterInclusive('\n'));
1397 try testing.expectEqualStrings("ab\n", try r.peekDelimiterInclusive('\n'));
1398 r.toss(3);
1399 try testing.expectError(error.EndOfStream, r.peekDelimiterInclusive('\n'));
12741400}
12751401
12761402test takeDelimiterExclusive {
1277 return error.Unimplemented;
1403 var r: Reader = .fixed("ab\nc");
1404 try testing.expectEqualStrings("ab", try r.takeDelimiterExclusive('\n'));
1405 try testing.expectEqualStrings("c", try r.takeDelimiterExclusive('\n'));
1406 try testing.expectError(error.EndOfStream, r.takeDelimiterExclusive('\n'));
12781407}
12791408
12801409test peekDelimiterExclusive {
1281 return error.Unimplemented;
1282}
1283
1284test readDelimiter {
1285 return error.Unimplemented;
1286}
1287
1288test readDelimiterEnding {
1289 return error.Unimplemented;
1290}
1291
1292test readDelimiterLimit {
1293 return error.Unimplemented;
1410 var r: Reader = .fixed("ab\nc");
1411 try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n'));
1412 try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n'));
1413 r.toss(3);
1414 try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n'));
1415}
1416
1417test streamDelimiter {
1418 var out_buffer: [10]u8 = undefined;
1419 var r: Reader = .fixed("foo\nbars");
1420 var w: Writer = .fixed(&out_buffer);
1421 try testing.expectEqual(3, try r.streamDelimiter(&w, '\n'));
1422 try testing.expectEqualStrings("foo", w.buffered());
1423 try testing.expectEqual(0, try r.streamDelimiter(&w, '\n'));
1424 r.toss(1);
1425 try testing.expectError(error.EndOfStream, r.streamDelimiter(&w, '\n'));
1426}
1427
1428test streamDelimiterEnding {
1429 var out_buffer: [10]u8 = undefined;
1430 var r: Reader = .fixed("foo\nbars");
1431 var w: Writer = .fixed(&out_buffer);
1432 try testing.expectEqual(3, try r.streamDelimiterEnding(&w, '\n'));
1433 try testing.expectEqualStrings("foo", w.buffered());
1434 r.toss(1);
1435 try testing.expectEqual(4, try r.streamDelimiterEnding(&w, '\n'));
1436 try testing.expectEqualStrings("foobars", w.buffered());
1437 try testing.expectEqual(0, try r.streamDelimiterEnding(&w, '\n'));
1438 try testing.expectEqual(0, try r.streamDelimiterEnding(&w, '\n'));
1439}
1440
1441test streamDelimiterLimit {
1442 var out_buffer: [10]u8 = undefined;
1443 var r: Reader = .fixed("foo\nbars");
1444 var w: Writer = .fixed(&out_buffer);
1445 try testing.expectError(error.StreamTooLong, r.streamDelimiterLimit(&w, '\n', .limited(2)));
1446 try testing.expectEqual(1, try r.streamDelimiterLimit(&w, '\n', .limited(3)));
1447 try testing.expectEqualStrings("\n", try r.take(1));
1448 try testing.expectEqual(4, try r.streamDelimiterLimit(&w, '\n', .unlimited));
1449 try testing.expectEqualStrings("foobars", w.buffered());
12941450}
12951451
12961452test discardDelimiterExclusive {
1297 return error.Unimplemented;
1453 var r: Reader = .fixed("foob\nar");
1454 try testing.expectEqual(4, try r.discardDelimiterExclusive('\n'));
1455 try testing.expectEqualStrings("\n", try r.take(1));
1456 try testing.expectEqual(2, try r.discardDelimiterExclusive('\n'));
1457 try testing.expectEqual(0, try r.discardDelimiterExclusive('\n'));
12981458}
12991459
13001460test discardDelimiterInclusive {
1301 return error.Unimplemented;
1461 var r: Reader = .fixed("foob\nar");
1462 try testing.expectEqual(5, try r.discardDelimiterInclusive('\n'));
1463 try testing.expectError(error.EndOfStream, r.discardDelimiterInclusive('\n'));
1464}
1465
1466test discardDelimiterLimit {
1467 var r: Reader = .fixed("foob\nar");
1468 try testing.expectError(error.StreamTooLong, r.discardDelimiterLimit('\n', .limited(4)));
1469 try testing.expectEqual(0, try r.discardDelimiterLimit('\n', .limited(2)));
1470 try testing.expectEqualStrings("\n", try r.take(1));
1471 try testing.expectEqual(2, try r.discardDelimiterLimit('\n', .unlimited));
1472 try testing.expectEqual(0, try r.discardDelimiterLimit('\n', .unlimited));
13021473}
13031474
13041475test fill {
1305 return error.Unimplemented;
1476 var r: Reader = .fixed("abc");
1477 try r.fill(1);
1478 try r.fill(3);
13061479}
13071480
13081481test takeByte {
1309 return error.Unimplemented;
1482 var r: Reader = .fixed("ab");
1483 try testing.expectEqual('a', try r.takeByte());
1484 try testing.expectEqual('b', try r.takeByte());
1485 try testing.expectError(error.EndOfStream, r.takeByte());
13101486}
13111487
13121488test takeByteSigned {
1313 return error.Unimplemented;
1489 var r: Reader = .fixed(&.{ 255, 5 });
1490 try testing.expectEqual(-1, try r.takeByteSigned());
1491 try testing.expectEqual(5, try r.takeByteSigned());
1492 try testing.expectError(error.EndOfStream, r.takeByteSigned());
13141493}
13151494
13161495test takeInt {
1317 return error.Unimplemented;
1496 var r: Reader = .fixed(&.{ 0x12, 0x34, 0x56 });
1497 try testing.expectEqual(0x1234, try r.takeInt(u16, .big));
1498 try testing.expectError(error.EndOfStream, r.takeInt(u16, .little));
13181499}
13191500
13201501test takeVarInt {
1321 return error.Unimplemented;
1502 var r: Reader = .fixed(&.{ 0x12, 0x34, 0x56 });
1503 try testing.expectEqual(0x123456, try r.takeVarInt(u64, .big, 3));
1504 try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1));
13221505}
13231506
13241507test takeStruct {
1325 return error.Unimplemented;
1508 var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
1509 const S = extern struct { a: u8, b: u16 };
1510 switch (native_endian) {
1511 .little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStruct(S)).*),
1512 .big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStruct(S)).*),
1513 }
1514 try testing.expectError(error.EndOfStream, r.takeStruct(S));
13261515}
13271516
13281517test peekStruct {
1329 return error.Unimplemented;
1518 var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
1519 const S = extern struct { a: u8, b: u16 };
1520 switch (native_endian) {
1521 .little => {
1522 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*);
1523 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStruct(S)).*);
1524 },
1525 .big => {
1526 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*);
1527 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStruct(S)).*);
1528 },
1529 }
13301530}
13311531
13321532test takeStructEndian {
1333 return error.Unimplemented;
1533 var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
1534 const S = extern struct { a: u8, b: u16 };
1535 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.takeStructEndian(S, .big));
1536 try testing.expectError(error.EndOfStream, r.takeStructEndian(S, .little));
13341537}
13351538
13361539test peekStructEndian {
1337 return error.Unimplemented;
1540 var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
1541 const S = extern struct { a: u8, b: u16 };
1542 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), try r.peekStructEndian(S, .big));
1543 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), try r.peekStructEndian(S, .little));
13381544}
13391545
13401546test takeEnum {
1341 return error.Unimplemented;
1547 var r: Reader = .fixed(&.{ 2, 0, 1 });
1548 const E1 = enum(u8) { a, b, c };
1549 const E2 = enum(u16) { _ };
1550 try testing.expectEqual(E1.c, try r.takeEnum(E1, .little));
1551 try testing.expectEqual(@as(E2, @enumFromInt(0x0001)), try r.takeEnum(E2, .big));
13421552}
13431553
13441554test takeLeb128 {
1345 return error.Unimplemented;
1555 var r: Reader = .fixed("\xc7\x9f\x7f\x80");
1556 try testing.expectEqual(-12345, try r.takeLeb128(i64));
1557 try testing.expectEqual(0x80, try r.peekByte());
1558 try testing.expectError(error.EndOfStream, r.takeLeb128(i64));
13461559}
13471560
13481561test readSliceShort {
1349 return error.Unimplemented;
1562 var r: Reader = .fixed("HelloFren");
1563 var buf: [5]u8 = undefined;
1564 try testing.expectEqual(5, try r.readSliceShort(&buf));
1565 try testing.expectEqualStrings("Hello", buf[0..5]);
1566 try testing.expectEqual(4, try r.readSliceShort(&buf));
1567 try testing.expectEqualStrings("Fren", buf[0..4]);
1568 try testing.expectEqual(0, try r.readSliceShort(&buf));
13501569}
13511570
13521571test readVec {
1353 return error.Unimplemented;
1572 var r: Reader = .fixed(std.ascii.letters);
1573 var flat_buffer: [52]u8 = undefined;
1574 var bufs: [2][]u8 = .{
1575 flat_buffer[0..26],
1576 flat_buffer[26..],
1577 };
1578 // Short reads are possible with this function but not with fixed.
1579 try testing.expectEqual(26 * 2, try r.readVec(&bufs));
1580 try testing.expectEqualStrings(std.ascii.letters[0..26], bufs[0]);
1581 try testing.expectEqualStrings(std.ascii.letters[26..], bufs[1]);
1582}
1583
1584test readVecLimit {
1585 var r: Reader = .fixed(std.ascii.letters);
1586 var flat_buffer: [52]u8 = undefined;
1587 var bufs: [2][]u8 = .{
1588 flat_buffer[0..26],
1589 flat_buffer[26..],
1590 };
1591 // Short reads are possible with this function but not with fixed.
1592 try testing.expectEqual(50, try r.readVecLimit(&bufs, .limited(50)));
1593 try testing.expectEqualStrings(std.ascii.letters[0..26], bufs[0]);
1594 try testing.expectEqualStrings(std.ascii.letters[26..50], bufs[1][0..24]);
13541595}
13551596
13561597test "expected error.EndOfStream" {
13571598 // Unit test inspired by https://github.com/ziglang/zig/issues/17733
1358 var r: std.io.Reader = .fixed("");
1359 try std.testing.expectError(error.EndOfStream, r.readEnum(enum(u8) { a, b }, .little));
1360 try std.testing.expectError(error.EndOfStream, r.isBytes("foo"));
1599 var buffer: [3]u8 = undefined;
1600 var r: std.io.Reader = .fixed(&buffer);
1601 r.end = 0; // capacity 3, but empty
1602 try std.testing.expectError(error.EndOfStream, r.takeEnum(enum(u8) { a, b }, .little));
1603 try std.testing.expectError(error.EndOfStream, r.take(3));
13611604}
13621605
13631606fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
......@@ -1389,25 +1632,51 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
13891632test "readAlloc when the backing reader provides one byte at a time" {
13901633 const OneByteReader = struct {
13911634 str: []const u8,
1392 curr: usize,
1393
1394 fn read(self: *@This(), dest: []u8) usize {
1395 if (self.str.len <= self.curr or dest.len == 0)
1396 return 0;
1397
1398 dest[0] = self.str[self.curr];
1399 self.curr += 1;
1635 i: usize,
1636 reader: Reader,
1637
1638 fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1639 assert(@intFromEnum(limit) >= 1);
1640 const self: *@This() = @fieldParentPtr("reader", r);
1641 if (self.str.len - self.i == 0) return error.EndOfStream;
1642 try w.writeByte(self.str[self.i]);
1643 self.i += 1;
14001644 return 1;
14011645 }
14021646 };
1403
14041647 const str = "This is a test";
1405 var one_byte_stream: OneByteReader = .init(str);
1406 const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1);
1648 var one_byte_stream: OneByteReader = .{
1649 .str = str,
1650 .i = 0,
1651 .reader = .{
1652 .buffer = &.{},
1653 .vtable = &.{ .stream = OneByteReader.stream },
1654 .seek = 0,
1655 .end = 0,
1656 },
1657 };
1658 const res = try one_byte_stream.reader.allocRemaining(std.testing.allocator, .unlimited);
14071659 defer std.testing.allocator.free(res);
14081660 try std.testing.expectEqualStrings(str, res);
14091661}
14101662
1663test "takeDelimiterInclusive when it rebases" {
1664 const written_line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n";
1665 var buffer: [128]u8 = undefined;
1666 var tr: std.testing.Reader = .init(&buffer, &.{
1667 .{ .buffer = written_line },
1668 .{ .buffer = written_line },
1669 .{ .buffer = written_line },
1670 .{ .buffer = written_line },
1671 .{ .buffer = written_line },
1672 .{ .buffer = written_line },
1673 });
1674 const r = &tr.interface;
1675 for (0..6) |_| {
1676 try std.testing.expectEqualStrings(written_line, try r.takeDelimiterInclusive('\n'));
1677 }
1678}
1679
14111680/// Provides a `Reader` implementation by passing data from an underlying
14121681/// reader through `Hasher.update`.
14131682///
lib/std/io/Writer.zig+819-534
......@@ -14,12 +14,6 @@ vtable: *const VTable,
1414buffer: []u8,
1515/// In `buffer` before this are buffered bytes, after this is `undefined`.
1616end: usize = 0,
17/// Tracks total number of bytes written to this `Writer`. This value
18/// only increases. In the case of fixed mode, this value always equals `end`.
19///
20/// This value is maintained by the interface; `VTable` function
21/// implementations need not modify it.
22count: usize = 0,
2317
2418pub const VTable = struct {
2519 /// Sends bytes to the logical sink. A write will only be sent here if it
......@@ -37,6 +31,10 @@ pub const VTable = struct {
3731 /// The last element of `data` is repeated as necessary so that it is
3832 /// written `splat` number of times, which may be zero.
3933 ///
34 /// This function may not be called if the data to be written could have
35 /// been stored in `buffer` instead, including when the amount of data to
36 /// be written is zero and the buffer capacity is zero.
37 ///
4038 /// Number of bytes consumed from `data` is returned, excluding bytes from
4139 /// `buffer`.
4240 ///
......@@ -113,8 +111,7 @@ pub const FileError = error{
113111 Unimplemented,
114112};
115113
116/// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless
117/// modified externally, `count` will always equal `end`.
114/// Writes to `buffer` and returns `error.WriteFailed` when it is full.
118115pub fn fixed(buffer: []u8) Writer {
119116 return .{
120117 .vtable = &.{ .drain = fixedDrain },
......@@ -122,8 +119,8 @@ pub fn fixed(buffer: []u8) Writer {
122119 };
123120}
124121
125pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) {
126 return .{ .out = w, .hasher = hasher };
122pub fn hashed(w: *Writer, hasher: anytype, buffer: []u8) Hashed(@TypeOf(hasher)) {
123 return .initHasher(w, hasher, buffer);
127124}
128125
129126pub const failing: Writer = .{
......@@ -133,16 +130,6 @@ pub const failing: Writer = .{
133130 },
134131};
135132
136pub fn discarding(buffer: []u8) Writer {
137 return .{
138 .vtable = &.{
139 .drain = discardingDrain,
140 .sendFile = discardingSendFile,
141 },
142 .buffer = buffer,
143 };
144}
145
146133/// Returns the contents not yet drained.
147134pub fn buffered(w: *const Writer) []u8 {
148135 return w.buffer[0..w.end];
......@@ -174,53 +161,26 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz
174161 assert(data.len > 0);
175162 const buffer = w.buffer;
176163 const count = countSplat(data, splat);
177 if (w.end + count > buffer.len) {
178 const n = try w.vtable.drain(w, data, splat);
179 w.count += n;
180 return n;
181 }
182 w.count += count;
183 for (data) |bytes| {
164 if (w.end + count > buffer.len) return w.vtable.drain(w, data, splat);
165 for (data[0 .. data.len - 1]) |bytes| {
184166 @memcpy(buffer[w.end..][0..bytes.len], bytes);
185167 w.end += bytes.len;
186168 }
187169 const pattern = data[data.len - 1];
188 if (splat == 0) {
189 @branchHint(.unlikely);
190 w.end -= pattern.len;
191 return count;
192 }
193 const remaining_splat = splat - 1;
194170 switch (pattern.len) {
195171 0 => {},
196172 1 => {
197 @memset(buffer[w.end..][0..remaining_splat], pattern[0]);
198 w.end += remaining_splat;
173 @memset(buffer[w.end..][0..splat], pattern[0]);
174 w.end += splat;
199175 },
200 else => {
201 const new_end = w.end + pattern.len * remaining_splat;
202 while (w.end < new_end) : (w.end += pattern.len) {
203 @memcpy(buffer[w.end..][0..pattern.len], pattern);
204 }
176 else => for (0..splat) |_| {
177 @memcpy(buffer[w.end..][0..pattern.len], pattern);
178 w.end += pattern.len;
205179 },
206180 }
207181 return count;
208182}
209183
210/// Equivalent to `writeSplat` but writes at most `limit` bytes.
211pub fn writeSplatLimit(
212 w: *Writer,
213 data: []const []const u8,
214 splat: usize,
215 limit: Limit,
216) Error!usize {
217 _ = w;
218 _ = data;
219 _ = splat;
220 _ = limit;
221 @panic("TODO");
222}
223
224184/// Returns how many bytes were consumed from `header` and `data`.
225185pub fn writeSplatHeader(
226186 w: *Writer,
......@@ -232,38 +192,40 @@ pub fn writeSplatHeader(
232192 if (new_end <= w.buffer.len) {
233193 @memcpy(w.buffer[w.end..][0..header.len], header);
234194 w.end = new_end;
235 w.count += header.len;
236195 return header.len + try writeSplat(w, data, splat);
237196 }
238197 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
239198 var i: usize = 1;
240199 vecs[0] = header;
241 for (data) |buf| {
200 for (data[0 .. data.len - 1]) |buf| {
242201 if (buf.len == 0) continue;
243202 vecs[i] = buf;
244203 i += 1;
245204 if (vecs.len - i == 0) break;
246205 }
247 const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1;
248 const n = try w.vtable.drain(w, vecs[0..i], new_splat);
249 w.count += n;
250 return n;
206 const pattern = data[data.len - 1];
207 const new_splat = s: {
208 if (pattern.len == 0 or vecs.len - i == 0) break :s 1;
209 vecs[i] = pattern;
210 i += 1;
211 break :s splat;
212 };
213 return w.vtable.drain(w, vecs[0..i], new_splat);
251214}
252215
253/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.
254pub fn writeSplatHeaderLimit(
255 w: *Writer,
256 header: []const u8,
257 data: []const []const u8,
258 splat: usize,
259 limit: Limit,
260) Error!usize {
261 _ = w;
262 _ = header;
263 _ = data;
264 _ = splat;
265 _ = limit;
266 @panic("TODO");
216test "writeSplatHeader splatting avoids buffer aliasing temptation" {
217 const initial_buf = try testing.allocator.alloc(u8, 8);
218 var aw: std.io.Writer.Allocating = .initOwnedSlice(testing.allocator, initial_buf);
219 defer aw.deinit();
220 // This test assumes 8 vector buffer in this function.
221 const n = try aw.writer.writeSplatHeader("header which is longer than buf ", &.{
222 "1", "2", "3", "4", "5", "6", "foo", "bar", "foo",
223 }, 3);
224 try testing.expectEqual(41, n);
225 try testing.expectEqualStrings(
226 "header which is longer than buf 123456foo",
227 aw.writer.buffered(),
228 );
267229}
268230
269231/// Drains all remaining buffered data.
......@@ -386,12 +348,18 @@ pub const WritableVectorIterator = struct {
386348pub const VectorWrapper = struct {
387349 writer: Writer,
388350 it: WritableVectorIterator,
389 pub const vtable: VTable = .{ .drain = fixedDrain };
351 /// Tracks whether the "writable vector" API was used.
352 used: bool = false,
353 pub const vtable: *const VTable = &unique_vtable_allocation;
354 /// This is intended to be constant but it must be a unique address for
355 /// `@fieldParentPtr` to work.
356 var unique_vtable_allocation: VTable = .{ .drain = fixedDrain };
390357};
391358
392359pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {
393 if (w.vtable == &VectorWrapper.vtable) {
360 if (w.vtable == VectorWrapper.vtable) {
394361 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
362 wrapper.used = true;
395363 return wrapper.it;
396364 }
397365 return .{ .first = try writableSliceGreedy(w, 1) };
......@@ -419,7 +387,6 @@ pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
419387
420388pub fn undo(w: *Writer, n: usize) void {
421389 w.end -= n;
422 w.count -= n;
423390}
424391
425392/// After calling `writableSliceGreedy`, this function tracks how many bytes
......@@ -430,13 +397,11 @@ pub fn advance(w: *Writer, n: usize) void {
430397 const new_end = w.end + n;
431398 assert(new_end <= w.buffer.len);
432399 w.end = new_end;
433 w.count += n;
434400}
435401
436402/// After calling `writableVector`, this function tracks how many bytes were
437403/// written to it.
438404pub fn advanceVector(w: *Writer, n: usize) usize {
439 w.count += n;
440405 return consume(w, n);
441406}
442407
......@@ -494,12 +459,9 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {
494459 @branchHint(.likely);
495460 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
496461 w.end += bytes.len;
497 w.count += bytes.len;
498462 return bytes.len;
499463 }
500 const n = try w.vtable.drain(w, &.{bytes}, 1);
501 w.count += n;
502 return n;
464 return w.vtable.drain(w, &.{bytes}, 1);
503465}
504466
505467/// Asserts `buffer` capacity exceeds `preserve_length`.
......@@ -509,7 +471,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro
509471 @branchHint(.likely);
510472 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
511473 w.end += bytes.len;
512 w.count += bytes.len;
513474 return bytes.len;
514475 }
515476 const temp_end = w.end -| preserve_length;
......@@ -517,7 +478,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro
517478 w.end = temp_end;
518479 defer w.end += preserved.len;
519480 const n = try w.vtable.drain(w, &.{bytes}, 1);
520 w.count += n;
521481 assert(w.end <= temp_end + preserved.len);
522482 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
523483 return n;
......@@ -542,23 +502,207 @@ pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) E
542502 while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]);
543503}
544504
545pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {
546 try std.fmt.format(w, format, args);
505/// Renders fmt string with args, calling `writer` with slices of bytes.
506/// If `writer` returns an error, the error is returned from `format` and
507/// `writer` is not called again.
508///
509/// The format string must be comptime-known and may contain placeholders following
510/// this format:
511/// `{[argument][specifier]:[fill][alignment][width].[precision]}`
512///
513/// Above, each word including its surrounding [ and ] is a parameter which you have to replace with something:
514///
515/// - *argument* is either the numeric index or the field name of the argument that should be inserted
516/// - when using a field name, you are required to enclose the field name (an identifier) in square
517/// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...}
518/// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below)
519/// - *fill* is a single byte which is used to pad formatted numbers.
520/// - *alignment* is one of the three bytes '<', '^', or '>' to make numbers
521/// left, center, or right-aligned, respectively.
522/// - Not all specifiers support alignment.
523/// - Alignment is not Unicode-aware; appropriate only when used with raw bytes or ASCII.
524/// - *width* is the total width of the field in bytes. This only applies to number formatting.
525/// - *precision* specifies how many decimals a formatted number should have.
526///
527/// Note that most of the parameters are optional and may be omitted. Also you
528/// can leave out separators like `:` and `.` when all parameters after the
529/// separator are omitted.
530///
531/// Only exception is the *fill* parameter. If a non-zero *fill* character is
532/// required at the same time as *width* is specified, one has to specify
533/// *alignment* as well, as otherwise the digit following `:` is interpreted as
534/// *width*, not *fill*.
535///
536/// The *specifier* has several options for types:
537/// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes
538/// - `s`:
539/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination
540/// - for slices of u8, print the entire slice as a string without zero-termination
541/// - `t`:
542/// - for enums and tagged unions: prints the tag name
543/// - for error sets: prints the error name
544/// - `b64`: output string as standard base64
545/// - `e`: output floating point value in scientific notation
546/// - `d`: output numeric value in decimal notation
547/// - `b`: output integer value in binary notation
548/// - `o`: output integer value in octal notation
549/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
550/// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max.
551/// - `D`: output nanoseconds as duration
552/// - `B`: output bytes in SI units (decimal)
553/// - `Bi`: output bytes in IEC units (binary)
554/// - `?`: output optional value as either the unwrapped value, or `null`; may be followed by a format specifier for the underlying value.
555/// - `!`: output error union value as either the unwrapped value, or the formatted error value; may be followed by a format specifier for the underlying value.
556/// - `*`: output the address of the value instead of the value itself.
557/// - `any`: output a value of any type using its default format.
558/// - `f`: delegates to a method on the type named "format" with the signature `fn (*Writer, args: anytype) Writer.Error!void`.
559///
560/// A user type may be a `struct`, `vector`, `union` or `enum` type.
561///
562/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`.
563///
564/// Asserts `buffer` capacity of at least 2 if a union is printed. This
565/// requirement could be lifted by adjusting the code, but if you trigger that
566/// assertion it is a clue that you should probably be using a buffer.
567pub fn print(w: *Writer, comptime fmt: []const u8, args: anytype) Error!void {
568 const ArgsType = @TypeOf(args);
569 const args_type_info = @typeInfo(ArgsType);
570 if (args_type_info != .@"struct") {
571 @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
572 }
573
574 const fields_info = args_type_info.@"struct".fields;
575 const max_format_args = @typeInfo(std.fmt.ArgSetType).int.bits;
576 if (fields_info.len > max_format_args) {
577 @compileError("32 arguments max are supported per format call");
578 }
579
580 @setEvalBranchQuota(fmt.len * 1000);
581 comptime var arg_state: std.fmt.ArgState = .{ .args_len = fields_info.len };
582 comptime var i = 0;
583 comptime var literal: []const u8 = "";
584 inline while (true) {
585 const start_index = i;
586
587 inline while (i < fmt.len) : (i += 1) {
588 switch (fmt[i]) {
589 '{', '}' => break,
590 else => {},
591 }
592 }
593
594 comptime var end_index = i;
595 comptime var unescape_brace = false;
596
597 // Handle {{ and }}, those are un-escaped as single braces
598 if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) {
599 unescape_brace = true;
600 // Make the first brace part of the literal...
601 end_index += 1;
602 // ...and skip both
603 i += 2;
604 }
605
606 literal = literal ++ fmt[start_index..end_index];
607
608 // We've already skipped the other brace, restart the loop
609 if (unescape_brace) continue;
610
611 // Write out the literal
612 if (literal.len != 0) {
613 try w.writeAll(literal);
614 literal = "";
615 }
616
617 if (i >= fmt.len) break;
618
619 if (fmt[i] == '}') {
620 @compileError("missing opening {");
621 }
622
623 // Get past the {
624 comptime assert(fmt[i] == '{');
625 i += 1;
626
627 const fmt_begin = i;
628 // Find the closing brace
629 inline while (i < fmt.len and fmt[i] != '}') : (i += 1) {}
630 const fmt_end = i;
631
632 if (i >= fmt.len) {
633 @compileError("missing closing }");
634 }
635
636 // Get past the }
637 comptime assert(fmt[i] == '}');
638 i += 1;
639
640 const placeholder_array = fmt[fmt_begin..fmt_end].*;
641 const placeholder = comptime std.fmt.Placeholder.parse(&placeholder_array);
642 const arg_pos = comptime switch (placeholder.arg) {
643 .none => null,
644 .number => |pos| pos,
645 .named => |arg_name| std.meta.fieldIndex(ArgsType, arg_name) orelse
646 @compileError("no argument with name '" ++ arg_name ++ "'"),
647 };
648
649 const width = switch (placeholder.width) {
650 .none => null,
651 .number => |v| v,
652 .named => |arg_name| blk: {
653 const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse
654 @compileError("no argument with name '" ++ arg_name ++ "'");
655 _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments");
656 break :blk @field(args, arg_name);
657 },
658 };
659
660 const precision = switch (placeholder.precision) {
661 .none => null,
662 .number => |v| v,
663 .named => |arg_name| blk: {
664 const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse
665 @compileError("no argument with name '" ++ arg_name ++ "'");
666 _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments");
667 break :blk @field(args, arg_name);
668 },
669 };
670
671 const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse
672 @compileError("too few arguments");
673
674 try w.printValue(
675 placeholder.specifier_arg,
676 .{
677 .fill = placeholder.fill,
678 .alignment = placeholder.alignment,
679 .width = width,
680 .precision = precision,
681 },
682 @field(args, fields_info[arg_to_print].name),
683 std.options.fmt_max_depth,
684 );
685 }
686
687 if (comptime arg_state.hasUnusedArgs()) {
688 const missing_count = arg_state.args_len - @popCount(arg_state.used_args);
689 switch (missing_count) {
690 0 => unreachable,
691 1 => @compileError("unused argument in '" ++ fmt ++ "'"),
692 else => @compileError(std.fmt.comptimePrint("{d}", .{missing_count}) ++ " unused arguments in '" ++ fmt ++ "'"),
693 }
694 }
547695}
548696
549697/// Calls `drain` as many times as necessary such that `byte` is transferred.
550698pub fn writeByte(w: *Writer, byte: u8) Error!void {
551699 while (w.buffer.len - w.end == 0) {
552700 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);
553 if (n > 0) {
554 w.count += 1;
555 return;
556 }
701 if (n > 0) return;
557702 } else {
558703 @branchHint(.likely);
559704 w.buffer[w.end] = byte;
560705 w.end += 1;
561 w.count += 1;
562706 }
563707}
564708
......@@ -571,7 +715,6 @@ pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!voi
571715 @branchHint(.likely);
572716 w.buffer[w.end] = byte;
573717 w.end += 1;
574 w.count += 1;
575718 }
576719}
577720
......@@ -625,12 +768,23 @@ pub fn writeStruct(w: *Writer, value: anytype) Error!void {
625768/// comptime-known and matches host endianness.
626769/// TODO: make sure this value is not a reference type
627770pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
628 if (native_endian == endian) {
629 return w.writeStruct(value);
630 } else {
631 var copy = value;
632 std.mem.byteSwapAllFields(@TypeOf(value), &copy);
633 return w.writeStruct(copy);
771 switch (@typeInfo(@TypeOf(value))) {
772 .@"struct" => |info| switch (info.layout) {
773 .auto => @compileError("ill-defined memory layout"),
774 .@"extern" => {
775 if (native_endian == endian) {
776 return w.writeStruct(value);
777 } else {
778 var copy = value;
779 std.mem.byteSwapAllFields(@TypeOf(value), &copy);
780 return w.writeStruct(copy);
781 }
782 },
783 .@"packed" => {
784 return writeInt(w, info.backing_integer.?, @bitCast(value), endian);
785 },
786 },
787 else => @compileError("not a struct"),
634788 }
635789}
636790
......@@ -647,14 +801,6 @@ pub inline fn writeSliceEndian(
647801 }
648802}
649803
650/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
651pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {
652 // copy to storage first, then swap in place
653 _ = w;
654 _ = slice;
655 @panic("TODO");
656}
657
658804/// Unlike `writeSplat` and `writeVec`, this function will call into `VTable`
659805/// even if there is enough buffer capacity for the file contents.
660806///
......@@ -680,12 +826,10 @@ pub fn sendFileHeader(
680826 if (new_end <= w.buffer.len) {
681827 @memcpy(w.buffer[w.end..][0..header.len], header);
682828 w.end = new_end;
683 w.count += header.len;
684829 return header.len + try w.vtable.sendFile(w, file_reader, limit);
685830 }
686831 const buffered_contents = limit.slice(file_reader.interface.buffered());
687832 const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1);
688 w.count += n;
689833 file_reader.interface.toss(n - header.len);
690834 return n;
691835}
......@@ -698,6 +842,8 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File
698842 return n;
699843}
700844
845/// Number of bytes logically written is returned. This excludes bytes from
846/// `buffer` because they have already been logically written.
701847pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {
702848 var remaining = @intFromEnum(limit);
703849 while (remaining > 0) {
......@@ -772,16 +918,13 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
772918 switch (@typeInfo(T)) {
773919 .pointer => |info| {
774920 try w.writeAll(@typeName(info.child) ++ "@");
775 if (info.size == .slice)
776 try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{})
777 else
778 try w.printIntOptions(@intFromPtr(value), 16, .lower, .{});
779 return;
921 const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value);
922 return w.printInt(int, 16, .lower, .{});
780923 },
781924 .optional => |info| {
782925 if (@typeInfo(info.child) == .pointer) {
783926 try w.writeAll(@typeName(info.child) ++ "@");
784 try w.printIntOptions(@intFromPtr(value), 16, .lower, .{});
927 try w.printInt(@intFromPtr(value), 16, .lower, .{});
785928 return;
786929 }
787930 },
......@@ -791,6 +934,7 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
791934 @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier");
792935}
793936
937/// Asserts `buffer` capacity of at least 2 if `value` is a union.
794938pub fn printValue(
795939 w: *Writer,
796940 comptime fmt: []const u8,
......@@ -800,26 +944,181 @@ pub fn printValue(
800944) Error!void {
801945 const T = @TypeOf(value);
802946
803 if (comptime std.mem.eql(u8, fmt, "*")) {
804 return w.printAddress(value);
947 switch (fmt.len) {
948 1 => switch (fmt[0]) {
949 '*' => return w.printAddress(value),
950 'f' => return value.format(w),
951 'd' => switch (@typeInfo(T)) {
952 .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)),
953 .int, .comptime_int => return printInt(w, value, 10, .lower, options),
954 .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)),
955 .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options),
956 .vector => return printVector(w, fmt, options, value, max_depth),
957 else => invalidFmtError(fmt, value),
958 },
959 'c' => return w.printAsciiChar(value, options),
960 'u' => return w.printUnicodeCodepoint(value),
961 'b' => switch (@typeInfo(T)) {
962 .int, .comptime_int => return printInt(w, value, 2, .lower, options),
963 .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options),
964 .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)),
965 .vector => return printVector(w, fmt, options, value, max_depth),
966 else => invalidFmtError(fmt, value),
967 },
968 'o' => switch (@typeInfo(T)) {
969 .int, .comptime_int => return printInt(w, value, 8, .lower, options),
970 .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options),
971 .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)),
972 .vector => return printVector(w, fmt, options, value, max_depth),
973 else => invalidFmtError(fmt, value),
974 },
975 'x' => switch (@typeInfo(T)) {
976 .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
977 .int, .comptime_int => return printInt(w, value, 16, .lower, options),
978 .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options),
979 .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)),
980 .pointer => |info| switch (info.size) {
981 .one, .slice => {
982 const slice: []const u8 = value;
983 optionsForbidden(options);
984 return printHex(w, slice, .lower);
985 },
986 .many, .c => {
987 const slice: [:0]const u8 = std.mem.span(value);
988 optionsForbidden(options);
989 return printHex(w, slice, .lower);
990 },
991 },
992 .array => {
993 const slice: []const u8 = &value;
994 optionsForbidden(options);
995 return printHex(w, slice, .lower);
996 },
997 .vector => return printVector(w, fmt, options, value, max_depth),
998 else => invalidFmtError(fmt, value),
999 },
1000 'X' => switch (@typeInfo(T)) {
1001 .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
1002 .int, .comptime_int => return printInt(w, value, 16, .upper, options),
1003 .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options),
1004 .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)),
1005 .pointer => |info| switch (info.size) {
1006 .one, .slice => {
1007 const slice: []const u8 = value;
1008 optionsForbidden(options);
1009 return printHex(w, slice, .upper);
1010 },
1011 .many, .c => {
1012 const slice: [:0]const u8 = std.mem.span(value);
1013 optionsForbidden(options);
1014 return printHex(w, slice, .upper);
1015 },
1016 },
1017 .array => {
1018 const slice: []const u8 = &value;
1019 optionsForbidden(options);
1020 return printHex(w, slice, .upper);
1021 },
1022 .vector => return printVector(w, fmt, options, value, max_depth),
1023 else => invalidFmtError(fmt, value),
1024 },
1025 's' => switch (@typeInfo(T)) {
1026 .pointer => |info| switch (info.size) {
1027 .one, .slice => {
1028 const slice: []const u8 = value;
1029 return w.alignBufferOptions(slice, options);
1030 },
1031 .many, .c => {
1032 const slice: [:0]const u8 = std.mem.span(value);
1033 return w.alignBufferOptions(slice, options);
1034 },
1035 },
1036 .array => {
1037 const slice: []const u8 = &value;
1038 return w.alignBufferOptions(slice, options);
1039 },
1040 else => invalidFmtError(fmt, value),
1041 },
1042 'B' => switch (@typeInfo(T)) {
1043 .int, .comptime_int => return w.printByteSize(value, .decimal, options),
1044 .@"struct" => return value.formatByteSize(w, .decimal),
1045 else => invalidFmtError(fmt, value),
1046 },
1047 'D' => switch (@typeInfo(T)) {
1048 .int, .comptime_int => return w.printDuration(value, options),
1049 .@"struct" => return value.formatDuration(w),
1050 else => invalidFmtError(fmt, value),
1051 },
1052 'e' => switch (@typeInfo(T)) {
1053 .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
1054 .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
1055 else => invalidFmtError(fmt, value),
1056 },
1057 'E' => switch (@typeInfo(T)) {
1058 .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)),
1059 .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)),
1060 else => invalidFmtError(fmt, value),
1061 },
1062 't' => switch (@typeInfo(T)) {
1063 .error_set => return w.writeAll(@errorName(value)),
1064 .@"enum", .@"union" => return w.writeAll(@tagName(value)),
1065 else => invalidFmtError(fmt, value),
1066 },
1067 else => {},
1068 },
1069 2 => switch (fmt[0]) {
1070 'B' => switch (fmt[1]) {
1071 'i' => switch (@typeInfo(T)) {
1072 .int, .comptime_int => return w.printByteSize(value, .binary, options),
1073 .@"struct" => return value.formatByteSize(w, .binary),
1074 else => invalidFmtError(fmt, value),
1075 },
1076 else => {},
1077 },
1078 else => {},
1079 },
1080 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) {
1081 .pointer => |info| switch (info.size) {
1082 .one, .slice => {
1083 const slice: []const u8 = value;
1084 optionsForbidden(options);
1085 return w.printBase64(slice);
1086 },
1087 .many, .c => {
1088 const slice: [:0]const u8 = std.mem.span(value);
1089 optionsForbidden(options);
1090 return w.printBase64(slice);
1091 },
1092 },
1093 .array => {
1094 const slice: []const u8 = &value;
1095 optionsForbidden(options);
1096 return w.printBase64(slice);
1097 },
1098 else => invalidFmtError(fmt, value),
1099 },
1100 else => {},
8051101 }
8061102
8071103 const is_any = comptime std.mem.eql(u8, fmt, ANY);
808 if (!is_any and std.meta.hasMethod(T, "format")) {
809 if (fmt.len > 0 and fmt[0] == 'f') {
810 return value.format(w, fmt[1..]);
811 } else if (fmt.len == 0) {
812 // after 0.15.0 is tagged, delete the hasMethod condition and this compile error
813 @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
814 }
1104 if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) {
1105 // after 0.15.0 is tagged, delete this compile error and its condition
1106 @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
8151107 }
8161108
8171109 switch (@typeInfo(T)) {
818 .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value),
819 .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value),
1110 .float, .comptime_float => {
1111 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1112 return printFloat(w, value, options.toNumber(.decimal, .lower));
1113 },
1114 .int, .comptime_int => {
1115 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1116 return printInt(w, value, 10, .lower, options);
1117 },
8201118 .bool => {
8211119 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
822 return w.alignBufferOptions(if (value) "true" else "false", options);
1120 const string: []const u8 = if (value) "true" else "false";
1121 return w.alignBufferOptions(string, options);
8231122 },
8241123 .void => {
8251124 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
......@@ -852,50 +1151,30 @@ pub fn printValue(
8521151 }
8531152 },
8541153 .error_set => {
855 if (fmt.len == 1 and fmt[0] == 's') return w.writeAll(@errorName(value));
8561154 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
857 try printErrorSet(w, value);
1155 optionsForbidden(options);
1156 return printErrorSet(w, value);
8581157 },
859 .@"enum" => {
860 if (fmt.len == 1 and fmt[0] == 's') {
861 try w.writeAll(@tagName(value));
862 return;
863 }
864 if (!is_any) {
865 if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth);
866 return printValue(w, ANY, options, value, max_depth);
867 }
868 const enum_info = @typeInfo(T).@"enum";
869 if (enum_info.is_exhaustive) {
870 var vecs: [3][]const u8 = .{ @typeName(T), ".", @tagName(value) };
871 try w.writeVecAll(&vecs);
872 return;
873 }
874 try w.writeAll(@typeName(T));
875 @setEvalBranchQuota(3 * enum_info.fields.len);
876 inline for (enum_info.fields) |field| {
877 if (@intFromEnum(value) == field.value) {
878 try w.writeAll(".");
879 try w.writeAll(@tagName(value));
880 return;
881 }
1158 .@"enum" => |info| {
1159 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1160 optionsForbidden(options);
1161 if (info.is_exhaustive) {
1162 return printEnumExhaustive(w, value);
1163 } else {
1164 return printEnumNonexhaustive(w, value);
8821165 }
883 try w.writeByte('(');
884 try w.printValue(ANY, options, @intFromEnum(value), max_depth);
885 try w.writeByte(')');
8861166 },
8871167 .@"union" => |info| {
8881168 if (!is_any) {
8891169 if (fmt.len != 0) invalidFmtError(fmt, value);
8901170 return printValue(w, ANY, options, value, max_depth);
8911171 }
892 try w.writeAll(@typeName(T));
8931172 if (max_depth == 0) {
894 try w.writeAll("{ ... }");
1173 try w.writeAll(".{ ... }");
8951174 return;
8961175 }
8971176 if (info.tag_type) |UnionTagType| {
898 try w.writeAll("{ .");
1177 try w.writeAll(".{ .");
8991178 try w.writeAll(@tagName(@as(UnionTagType, value)));
9001179 try w.writeAll(" = ");
9011180 inline for (info.fields) |u_field| {
......@@ -904,9 +1183,22 @@ pub fn printValue(
9041183 }
9051184 }
9061185 try w.writeAll(" }");
907 } else {
908 try w.writeByte('@');
909 try w.printIntOptions(@intFromPtr(&value), 16, .lower, options);
1186 } else switch (info.layout) {
1187 .auto => {
1188 return w.writeAll(".{ ... }");
1189 },
1190 .@"extern", .@"packed" => {
1191 if (info.fields.len == 0) return w.writeAll(".{}");
1192 try w.writeAll(".{ ");
1193 inline for (info.fields) |field| {
1194 try w.writeByte('.');
1195 try w.writeAll(field.name);
1196 try w.writeAll(" = ");
1197 try w.printValue(ANY, options, @field(value, field.name), max_depth - 1);
1198 (try w.writableArray(2)).* = ", ".*;
1199 }
1200 w.buffer[w.end - 2 ..][0..2].* = " }".*;
1201 },
9101202 }
9111203 },
9121204 .@"struct" => |info| {
......@@ -917,10 +1209,10 @@ pub fn printValue(
9171209 if (info.is_tuple) {
9181210 // Skip the type and field names when formatting tuples.
9191211 if (max_depth == 0) {
920 try w.writeAll("{ ... }");
1212 try w.writeAll(".{ ... }");
9211213 return;
9221214 }
923 try w.writeAll("{");
1215 try w.writeAll(".{");
9241216 inline for (info.fields, 0..) |f, i| {
9251217 if (i == 0) {
9261218 try w.writeAll(" ");
......@@ -932,12 +1224,11 @@ pub fn printValue(
9321224 try w.writeAll(" }");
9331225 return;
9341226 }
935 try w.writeAll(@typeName(T));
9361227 if (max_depth == 0) {
937 try w.writeAll("{ ... }");
1228 try w.writeAll(".{ ... }");
9381229 return;
9391230 }
940 try w.writeAll("{");
1231 try w.writeAll(".{");
9411232 inline for (info.fields, 0..) |f, i| {
9421233 if (i == 0) {
9431234 try w.writeAll(" .");
......@@ -952,44 +1243,24 @@ pub fn printValue(
9521243 },
9531244 .pointer => |ptr_info| switch (ptr_info.size) {
9541245 .one => switch (@typeInfo(ptr_info.child)) {
955 .array, .@"enum", .@"union", .@"struct" => {
956 return w.printValue(fmt, options, value.*, max_depth);
957 },
1246 .array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth),
1247 .@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth),
9581248 else => {
9591249 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
9601250 try w.writeVecAll(&buffers);
961 try w.printIntOptions(@intFromPtr(value), 16, .lower, options);
1251 try w.printInt(@intFromPtr(value), 16, .lower, options);
9621252 return;
9631253 },
9641254 },
9651255 .many, .c => {
966 if (ptr_info.sentinel() != null)
967 return w.printValue(fmt, options, std.mem.span(value), max_depth);
968 if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8)
969 return w.alignBufferOptions(std.mem.span(value), options);
970 if (!is_any and fmt.len == 0)
971 @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
972 if (!is_any and fmt.len != 0)
973 invalidFmtError(fmt, value);
1256 if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
1257 optionsForbidden(options);
9741258 try w.printAddress(value);
9751259 },
9761260 .slice => {
977 if (!is_any and fmt.len == 0)
1261 if (!is_any)
9781262 @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");
979 if (max_depth == 0)
980 return w.writeAll("{ ... }");
981 if (ptr_info.child == u8) switch (fmt.len) {
982 1 => switch (fmt[0]) {
983 's' => return w.alignBufferOptions(value, options),
984 'x' => return w.printHex(value, .lower),
985 'X' => return w.printHex(value, .upper),
986 else => {},
987 },
988 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') {
989 return w.printBase64(value);
990 },
991 else => {},
992 };
1263 if (max_depth == 0) return w.writeAll("{ ... }");
9931264 try w.writeAll("{ ");
9941265 for (value, 0..) |elem, i| {
9951266 try w.printValue(fmt, options, elem, max_depth - 1);
......@@ -1000,21 +1271,9 @@ pub fn printValue(
10001271 try w.writeAll(" }");
10011272 },
10021273 },
1003 .array => |info| {
1004 if (fmt.len == 0)
1005 @compileError("cannot format array without a specifier (i.e. {s} or {any})");
1006 if (max_depth == 0) {
1007 return w.writeAll("{ ... }");
1008 }
1009 if (info.child == u8) {
1010 if (fmt[0] == 's') {
1011 return w.alignBufferOptions(&value, options);
1012 } else if (fmt[0] == 'x') {
1013 return w.printHex(&value, .lower);
1014 } else if (fmt[0] == 'X') {
1015 return w.printHex(&value, .upper);
1016 }
1017 }
1274 .array => {
1275 if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})");
1276 if (max_depth == 0) return w.writeAll("{ ... }");
10181277 try w.writeAll("{ ");
10191278 for (value, 0..) |elem, i| {
10201279 try w.printValue(fmt, options, elem, max_depth - 1);
......@@ -1024,19 +1283,9 @@ pub fn printValue(
10241283 }
10251284 try w.writeAll(" }");
10261285 },
1027 .vector => |info| {
1028 if (max_depth == 0) {
1029 return w.writeAll("{ ... }");
1030 }
1031 try w.writeAll("{ ");
1032 var i: usize = 0;
1033 while (i < info.len) : (i += 1) {
1034 try w.printValue(fmt, options, value[i], max_depth - 1);
1035 if (i < info.len - 1) {
1036 try w.writeAll(", ");
1037 }
1038 }
1039 try w.writeAll(" }");
1286 .vector => {
1287 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1288 return printVector(w, fmt, options, value, max_depth);
10401289 },
10411290 .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),
10421291 .type => {
......@@ -1045,8 +1294,9 @@ pub fn printValue(
10451294 },
10461295 .enum_literal => {
10471296 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1048 const buffer = [_]u8{'.'} ++ @tagName(value);
1049 return w.alignBufferOptions(buffer, options);
1297 optionsForbidden(options);
1298 var vecs: [2][]const u8 = .{ ".", @tagName(value) };
1299 return w.writeVecAll(&vecs);
10501300 },
10511301 .null => {
10521302 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
......@@ -1056,75 +1306,78 @@ pub fn printValue(
10561306 }
10571307}
10581308
1309fn optionsForbidden(options: std.fmt.Options) void {
1310 assert(options.precision == null);
1311 assert(options.width == null);
1312}
1313
10591314fn printErrorSet(w: *Writer, error_set: anyerror) Error!void {
10601315 var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) };
10611316 try w.writeVecAll(&vecs);
10621317}
10631318
1064pub fn printInt(
1319fn printEnumExhaustive(w: *Writer, value: anytype) Error!void {
1320 var vecs: [2][]const u8 = .{ ".", @tagName(value) };
1321 try w.writeVecAll(&vecs);
1322}
1323
1324fn printEnumNonexhaustive(w: *Writer, value: anytype) Error!void {
1325 if (std.enums.tagName(@TypeOf(value), value)) |tag_name| {
1326 var vecs: [2][]const u8 = .{ ".", tag_name };
1327 try w.writeVecAll(&vecs);
1328 return;
1329 }
1330 try w.writeAll("@enumFromInt(");
1331 try w.printInt(@intFromEnum(value), 10, .lower, .{});
1332 try w.writeByte(')');
1333}
1334
1335pub fn printVector(
10651336 w: *Writer,
10661337 comptime fmt: []const u8,
10671338 options: std.fmt.Options,
10681339 value: anytype,
1340 max_depth: usize,
10691341) Error!void {
1070 const int_value = if (@TypeOf(value) == comptime_int) blk: {
1071 const Int = std.math.IntFittingRange(value, value);
1072 break :blk @as(Int, value);
1073 } else value;
1342 const len = @typeInfo(@TypeOf(value)).vector.len;
1343 if (max_depth == 0) return w.writeAll("{ ... }");
1344 try w.writeAll("{ ");
1345 inline for (0..len) |i| {
1346 try w.printValue(fmt, options, value[i], max_depth - 1);
1347 if (i < len - 1) try w.writeAll(", ");
1348 }
1349 try w.writeAll(" }");
1350}
10741351
1075 switch (fmt.len) {
1076 0 => return w.printIntOptions(int_value, 10, .lower, options),
1077 1 => switch (fmt[0]) {
1078 'd' => return w.printIntOptions(int_value, 10, .lower, options),
1079 'c' => {
1080 if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) {
1081 return w.printAsciiChar(@as(u8, int_value), options);
1082 } else {
1083 @compileError("cannot print integer that is larger than 8 bits as an ASCII character");
1084 }
1085 },
1086 'u' => {
1087 if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) {
1088 return w.printUnicodeCodepoint(@as(u21, int_value), options);
1089 } else {
1090 @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence");
1091 }
1092 },
1093 'b' => return w.printIntOptions(int_value, 2, .lower, options),
1094 'x' => return w.printIntOptions(int_value, 16, .lower, options),
1095 'X' => return w.printIntOptions(int_value, 16, .upper, options),
1096 'o' => return w.printIntOptions(int_value, 8, .lower, options),
1097 'B' => return w.printByteSize(int_value, .decimal, options),
1098 'D' => return w.printDuration(int_value, options),
1099 else => invalidFmtError(fmt, value),
1352// A wrapper around `printIntAny` to avoid the generic explosion of this
1353// function by funneling smaller integer types through `isize` and `usize`.
1354pub inline fn printInt(
1355 w: *Writer,
1356 value: anytype,
1357 base: u8,
1358 case: std.fmt.Case,
1359 options: std.fmt.Options,
1360) Error!void {
1361 switch (@TypeOf(value)) {
1362 isize, usize => {},
1363 comptime_int => {
1364 if (comptime std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options);
1365 if (comptime std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options);
1366 const Int = std.math.IntFittingRange(value, value);
1367 return printIntAny(w, @as(Int, value), base, case, options);
11001368 },
1101 2 => {
1102 if (fmt[0] == 'B' and fmt[1] == 'i') {
1103 return w.printByteSize(int_value, .binary, options);
1104 } else {
1105 invalidFmtError(fmt, value);
1106 }
1369 else => switch (@typeInfo(@TypeOf(value)).int.signedness) {
1370 .signed => if (std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options),
1371 .unsigned => if (std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options),
11071372 },
1108 else => invalidFmtError(fmt, value),
11091373 }
1110 comptime unreachable;
1111}
1112
1113pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void {
1114 return w.alignBufferOptions(@as(*const [1]u8, &c), options);
1374 return printIntAny(w, value, base, case, options);
11151375}
11161376
1117pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void {
1118 return w.alignBufferOptions(bytes, options);
1119}
1120
1121pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void {
1122 var buf: [4]u8 = undefined;
1123 const len = try std.unicode.utf8Encode(c, &buf);
1124 return w.alignBufferOptions(buf[0..len], options);
1125}
1126
1127pub fn printIntOptions(
1377/// In general, prefer `printInt` to avoid generic explosion. However this
1378/// function may be used when optimal codegen for a particular integer type is
1379/// desired.
1380pub fn printIntAny(
11281381 w: *Writer,
11291382 value: anytype,
11301383 base: u8,
......@@ -1132,20 +1385,14 @@ pub fn printIntOptions(
11321385 options: std.fmt.Options,
11331386) Error!void {
11341387 assert(base >= 2);
1135
1136 const int_value = if (@TypeOf(value) == comptime_int) blk: {
1137 const Int = std.math.IntFittingRange(value, value);
1138 break :blk @as(Int, value);
1139 } else value;
1140
1141 const value_info = @typeInfo(@TypeOf(int_value)).int;
1388 const value_info = @typeInfo(@TypeOf(value)).int;
11421389
11431390 // The type must have the same size as `base` or be wider in order for the
11441391 // division to work
11451392 const min_int_bits = comptime @max(value_info.bits, 8);
11461393 const MinInt = std.meta.Int(.unsigned, min_int_bits);
11471394
1148 const abs_value = @abs(int_value);
1395 const abs_value = @abs(value);
11491396 // The worst case in terms of space needed is base 2, plus 1 for the sign
11501397 var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined;
11511398
......@@ -1192,41 +1439,69 @@ pub fn printIntOptions(
11921439 return w.alignBufferOptions(buf[index..], options);
11931440}
11941441
1195pub fn printFloat(
1196 w: *Writer,
1197 comptime fmt: []const u8,
1198 options: std.fmt.Options,
1199 value: anytype,
1200) Error!void {
1201 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
1442pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void {
1443 return w.alignBufferOptions(@as(*const [1]u8, &c), options);
1444}
12021445
1203 if (fmt.len > 1) invalidFmtError(fmt, value);
1204 switch (if (fmt.len == 0) 'e' else fmt[0]) {
1205 'e' => {
1206 const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) {
1207 error.BufferTooSmall => "(float)",
1208 };
1209 return w.alignBufferOptions(s, options);
1210 },
1211 'd' => {
1212 const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) {
1213 error.BufferTooSmall => "(float)",
1214 };
1215 return w.alignBufferOptions(s, options);
1216 },
1217 'x' => {
1218 var sub_bw: Writer = .fixed(&buf);
1219 sub_bw.printFloatHexadecimal(value, options.precision) catch unreachable;
1220 return w.alignBufferOptions(sub_bw.buffered(), options);
1446pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void {
1447 return w.alignBufferOptions(bytes, options);
1448}
1449
1450pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void {
1451 var buf: [4]u8 = undefined;
1452 const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) {
1453 error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: {
1454 buf[0..3].* = std.unicode.replacement_character_utf8;
1455 break :l 3;
12211456 },
1222 else => invalidFmtError(fmt, value),
1457 };
1458 return w.writeAll(buf[0..len]);
1459}
1460
1461/// Uses a larger stack buffer; asserts mode is decimal or scientific.
1462pub fn printFloat(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
1463 const mode: std.fmt.float.Mode = switch (options.mode) {
1464 .decimal => .decimal,
1465 .scientific => .scientific,
1466 .binary, .octal, .hex => unreachable,
1467 };
1468 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
1469 const s = std.fmt.float.render(&buf, value, .{
1470 .mode = mode,
1471 .precision = options.precision,
1472 }) catch |err| switch (err) {
1473 error.BufferTooSmall => "(float)",
1474 };
1475 return w.alignBuffer(s, options.width orelse s.len, options.alignment, options.fill);
1476}
1477
1478/// Uses a smaller stack buffer; asserts mode is not decimal or scientific.
1479pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
1480 var buf: [50]u8 = undefined; // for aligning
1481 var sub_writer: Writer = .fixed(&buf);
1482 switch (options.mode) {
1483 .decimal => unreachable,
1484 .scientific => unreachable,
1485 .binary => @panic("TODO"),
1486 .octal => @panic("TODO"),
1487 .hex => {},
12231488 }
1489 printFloatHex(&sub_writer, value, options.case, options.precision) catch unreachable; // buf is large enough
1490
1491 const printed = sub_writer.buffered();
1492 return w.alignBuffer(printed, options.width orelse printed.len, options.alignment, options.fill);
12241493}
12251494
1226pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void {
1495pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void {
12271496 if (std.math.signbit(value)) try w.writeByte('-');
1228 if (std.math.isNan(value)) return w.writeAll("nan");
1229 if (std.math.isInf(value)) return w.writeAll("inf");
1497 if (std.math.isNan(value)) return w.writeAll(switch (case) {
1498 .lower => "nan",
1499 .upper => "NAN",
1500 });
1501 if (std.math.isInf(value)) return w.writeAll(switch (case) {
1502 .lower => "inf",
1503 .upper => "INF",
1504 });
12301505
12311506 const T = @TypeOf(value);
12321507 const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
......@@ -1302,7 +1577,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize)
13021577
13031578 // +1 for the decimal part.
13041579 var buf: [1 + mantissa_digits]u8 = undefined;
1305 assert(std.fmt.printInt(&buf, mantissa, 16, .lower, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len);
1580 assert(std.fmt.printInt(&buf, mantissa, 16, case, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len);
13061581
13071582 try w.writeAll("0x");
13081583 try w.writeByte(buf[0]);
......@@ -1319,7 +1594,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize)
13191594 try w.splatByteAll('0', precision - trimmed.len);
13201595 };
13211596 try w.writeAll("p");
1322 try w.printIntOptions(exponent - exponent_bias, 10, .lower, .{});
1597 try w.printInt(exponent - exponent_bias, 10, case, .{});
13231598}
13241599
13251600pub const ByteSizeUnits = enum {
......@@ -1415,7 +1690,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
14151690 }) |unit| {
14161691 if (ns_remaining >= unit.ns) {
14171692 const units = ns_remaining / unit.ns;
1418 try w.printIntOptions(units, 10, .lower, .{});
1693 try w.printInt(units, 10, .lower, .{});
14191694 try w.writeByte(unit.sep);
14201695 ns_remaining -= units * unit.ns;
14211696 if (ns_remaining == 0) return;
......@@ -1429,13 +1704,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
14291704 }) |unit| {
14301705 const kunits = ns_remaining * 1000 / unit.ns;
14311706 if (kunits >= 1000) {
1432 try w.printIntOptions(kunits / 1000, 10, .lower, .{});
1707 try w.printInt(kunits / 1000, 10, .lower, .{});
14331708 const frac = kunits % 1000;
14341709 if (frac > 0) {
14351710 // Write up to 3 decimal places
14361711 var decimal_buf = [_]u8{ '.', 0, 0, 0 };
14371712 var inner: Writer = .fixed(decimal_buf[1..]);
1438 inner.printIntOptions(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable;
1713 inner.printInt(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable;
14391714 var end: usize = 4;
14401715 while (end > 1) : (end -= 1) {
14411716 if (decimal_buf[end - 1] != '0') break;
......@@ -1446,7 +1721,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
14461721 }
14471722 }
14481723
1449 try w.printIntOptions(ns_remaining, 10, .lower, .{});
1724 try w.printInt(ns_remaining, 10, .lower, .{});
14501725 try w.writeAll("ns");
14511726}
14521727
......@@ -1456,12 +1731,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
14561731pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void {
14571732 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24
14581733 var buf: [24]u8 = undefined;
1459 var sub_bw: Writer = .fixed(&buf);
1460 switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) {
1461 .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable,
1462 .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable,
1734 var sub_writer: Writer = .fixed(&buf);
1735 if (@TypeOf(nanoseconds) == comptime_int) {
1736 if (nanoseconds >= 0) {
1737 sub_writer.printDurationUnsigned(nanoseconds) catch unreachable;
1738 } else {
1739 sub_writer.printDurationSigned(nanoseconds) catch unreachable;
1740 }
1741 } else switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) {
1742 .signed => sub_writer.printDurationSigned(nanoseconds) catch unreachable,
1743 .unsigned => sub_writer.printDurationUnsigned(nanoseconds) catch unreachable,
14631744 }
1464 return w.alignBufferOptions(sub_bw.buffered(), options);
1745 return w.alignBufferOptions(sub_writer.buffered(), options);
14651746}
14661747
14671748pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void {
......@@ -1547,24 +1828,14 @@ fn writeMultipleOf7Leb128(w: *Writer, value: anytype) Error!void {
15471828 }
15481829}
15491830
1550test "formatValue max_depth" {
1831test "printValue max_depth" {
15511832 const Vec2 = struct {
15521833 const SelfType = @This();
15531834 x: f32,
15541835 y: f32,
15551836
1556 pub fn format(
1557 self: SelfType,
1558 comptime fmt: []const u8,
1559 options: std.fmt.Options,
1560 w: *Writer,
1561 ) Error!void {
1562 _ = options;
1563 if (fmt.len == 0) {
1564 return w.print("({d:.3},{d:.3})", .{ self.x, self.y });
1565 } else {
1566 @compileError("unknown format string: '" ++ fmt ++ "'");
1567 }
1837 pub fn format(self: SelfType, w: *Writer) Error!void {
1838 return w.print("({d:.3},{d:.3})", .{ self.x, self.y });
15681839 }
15691840 };
15701841 const E = enum {
......@@ -1598,133 +1869,133 @@ test "formatValue max_depth" {
15981869 var buf: [1000]u8 = undefined;
15991870 var w: Writer = .fixed(&buf);
16001871 try w.printValue("", .{}, inst, 0);
1601 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ ... }", w.buffered());
1872 try testing.expectEqualStrings(".{ ... }", w.buffered());
16021873
1603 w.reset();
1874 w = .fixed(&buf);
16041875 try w.printValue("", .{}, inst, 1);
1605 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ ... }, .tu = io.Writer.test.printValue max_depth.TU{ ... }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }", w.buffered());
1876 try testing.expectEqualStrings(".{ .a = .{ ... }, .tu = .{ ... }, .e = .Two, .vec = .{ ... } }", w.buffered());
16061877
1607 w.reset();
1878 w = .fixed(&buf);
16081879 try w.printValue("", .{}, inst, 2);
1609 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ ... }, .tu = io.Writer.test.printValue max_depth.TU{ ... }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }, .tu = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ ... } }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }", w.buffered());
1880 try testing.expectEqualStrings(".{ .a = .{ .a = .{ ... }, .tu = .{ ... }, .e = .Two, .vec = .{ ... } }, .tu = .{ .ptr = .{ ... } }, .e = .Two, .vec = .{ .x = 10.2, .y = 2.22 } }", w.buffered());
16101881
1611 w.reset();
1882 w = .fixed(&buf);
16121883 try w.printValue("", .{}, inst, 3);
1613 try testing.expectEqualStrings("io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ .a = io.Writer.test.printValue max_depth.S{ ... }, .tu = io.Writer.test.printValue max_depth.TU{ ... }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }, .tu = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ ... } }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }, .tu = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ .ptr = io.Writer.test.printValue max_depth.TU{ ... } } }, .e = io.Writer.test.printValue max_depth.E.Two, .vec = (10.200,2.220) }", w.buffered());
1884 try testing.expectEqualStrings(".{ .a = .{ .a = .{ .a = .{ ... }, .tu = .{ ... }, .e = .Two, .vec = .{ ... } }, .tu = .{ .ptr = .{ ... } }, .e = .Two, .vec = .{ .x = 10.2, .y = 2.22 } }, .tu = .{ .ptr = .{ .ptr = .{ ... } } }, .e = .Two, .vec = .{ .x = 10.2, .y = 2.22 } }", w.buffered());
16141885
16151886 const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 };
1616 w.reset();
1887 w = .fixed(&buf);
16171888 try w.printValue("", .{}, vec, 0);
16181889 try testing.expectEqualStrings("{ ... }", w.buffered());
16191890
1620 w.reset();
1891 w = .fixed(&buf);
16211892 try w.printValue("", .{}, vec, 1);
16221893 try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());
16231894}
16241895
16251896test printDuration {
1626 testDurationCase("0ns", 0);
1627 testDurationCase("1ns", 1);
1628 testDurationCase("999ns", std.time.ns_per_us - 1);
1629 testDurationCase("1us", std.time.ns_per_us);
1630 testDurationCase("1.45us", 1450);
1631 testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);
1632 testDurationCase("14.5us", 14500);
1633 testDurationCase("145us", 145000);
1634 testDurationCase("999.999us", std.time.ns_per_ms - 1);
1635 testDurationCase("1ms", std.time.ns_per_ms + 1);
1636 testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);
1637 testDurationCase("1.11ms", 1110000);
1638 testDurationCase("1.111ms", 1111000);
1639 testDurationCase("1.111ms", 1111100);
1640 testDurationCase("999.999ms", std.time.ns_per_s - 1);
1641 testDurationCase("1s", std.time.ns_per_s);
1642 testDurationCase("59.999s", std.time.ns_per_min - 1);
1643 testDurationCase("1m", std.time.ns_per_min);
1644 testDurationCase("1h", std.time.ns_per_hour);
1645 testDurationCase("1d", std.time.ns_per_day);
1646 testDurationCase("1w", std.time.ns_per_week);
1647 testDurationCase("1y", 365 * std.time.ns_per_day);
1648 testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1
1649 testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1650 testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1651 testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1652 testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1653 testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1654 testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1655 testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));
1656
1657 testing.expectFmt("=======0ns", "{D:=>10}", .{0});
1658 testing.expectFmt("1ns=======", "{D:=<10}", .{1});
1659 testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});
1897 try testDurationCase("0ns", 0);
1898 try testDurationCase("1ns", 1);
1899 try testDurationCase("999ns", std.time.ns_per_us - 1);
1900 try testDurationCase("1us", std.time.ns_per_us);
1901 try testDurationCase("1.45us", 1450);
1902 try testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);
1903 try testDurationCase("14.5us", 14500);
1904 try testDurationCase("145us", 145000);
1905 try testDurationCase("999.999us", std.time.ns_per_ms - 1);
1906 try testDurationCase("1ms", std.time.ns_per_ms + 1);
1907 try testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);
1908 try testDurationCase("1.11ms", 1110000);
1909 try testDurationCase("1.111ms", 1111000);
1910 try testDurationCase("1.111ms", 1111100);
1911 try testDurationCase("999.999ms", std.time.ns_per_s - 1);
1912 try testDurationCase("1s", std.time.ns_per_s);
1913 try testDurationCase("59.999s", std.time.ns_per_min - 1);
1914 try testDurationCase("1m", std.time.ns_per_min);
1915 try testDurationCase("1h", std.time.ns_per_hour);
1916 try testDurationCase("1d", std.time.ns_per_day);
1917 try testDurationCase("1w", std.time.ns_per_week);
1918 try testDurationCase("1y", 365 * std.time.ns_per_day);
1919 try testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1
1920 try testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1921 try testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1922 try testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1923 try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1924 try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1925 try testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1926 try testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));
1927
1928 try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
1929 try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
1930 try testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});
16601931}
16611932
16621933test printDurationSigned {
1663 testDurationCaseSigned("0ns", 0);
1664 testDurationCaseSigned("1ns", 1);
1665 testDurationCaseSigned("-1ns", -(1));
1666 testDurationCaseSigned("999ns", std.time.ns_per_us - 1);
1667 testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));
1668 testDurationCaseSigned("1us", std.time.ns_per_us);
1669 testDurationCaseSigned("-1us", -(std.time.ns_per_us));
1670 testDurationCaseSigned("1.45us", 1450);
1671 testDurationCaseSigned("-1.45us", -(1450));
1672 testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);
1673 testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));
1674 testDurationCaseSigned("14.5us", 14500);
1675 testDurationCaseSigned("-14.5us", -(14500));
1676 testDurationCaseSigned("145us", 145000);
1677 testDurationCaseSigned("-145us", -(145000));
1678 testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);
1679 testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));
1680 testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);
1681 testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));
1682 testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);
1683 testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));
1684 testDurationCaseSigned("1.11ms", 1110000);
1685 testDurationCaseSigned("-1.11ms", -(1110000));
1686 testDurationCaseSigned("1.111ms", 1111000);
1687 testDurationCaseSigned("-1.111ms", -(1111000));
1688 testDurationCaseSigned("1.111ms", 1111100);
1689 testDurationCaseSigned("-1.111ms", -(1111100));
1690 testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);
1691 testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));
1692 testDurationCaseSigned("1s", std.time.ns_per_s);
1693 testDurationCaseSigned("-1s", -(std.time.ns_per_s));
1694 testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);
1695 testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));
1696 testDurationCaseSigned("1m", std.time.ns_per_min);
1697 testDurationCaseSigned("-1m", -(std.time.ns_per_min));
1698 testDurationCaseSigned("1h", std.time.ns_per_hour);
1699 testDurationCaseSigned("-1h", -(std.time.ns_per_hour));
1700 testDurationCaseSigned("1d", std.time.ns_per_day);
1701 testDurationCaseSigned("-1d", -(std.time.ns_per_day));
1702 testDurationCaseSigned("1w", std.time.ns_per_week);
1703 testDurationCaseSigned("-1w", -(std.time.ns_per_week));
1704 testDurationCaseSigned("1y", 365 * std.time.ns_per_day);
1705 testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));
1706 testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
1707 testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d
1708 testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1709 testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms));
1710 testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1711 testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us));
1712 testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1713 testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1));
1714 testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1715 testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms));
1716 testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1717 testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1));
1718 testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1719 testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
1720 testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));
1721 testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
1722 testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));
1723
1724 testing.expectFmt("=======0ns", "{s:=>10}", .{0});
1725 testing.expectFmt("1ns=======", "{s:=<10}", .{1});
1726 testing.expectFmt("-1ns======", "{s:=<10}", .{-(1)});
1727 testing.expectFmt(" -999ns ", "{s:^10}", .{-(std.time.ns_per_us - 1)});
1934 try testDurationCaseSigned("0ns", 0);
1935 try testDurationCaseSigned("1ns", 1);
1936 try testDurationCaseSigned("-1ns", -(1));
1937 try testDurationCaseSigned("999ns", std.time.ns_per_us - 1);
1938 try testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));
1939 try testDurationCaseSigned("1us", std.time.ns_per_us);
1940 try testDurationCaseSigned("-1us", -(std.time.ns_per_us));
1941 try testDurationCaseSigned("1.45us", 1450);
1942 try testDurationCaseSigned("-1.45us", -(1450));
1943 try testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);
1944 try testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));
1945 try testDurationCaseSigned("14.5us", 14500);
1946 try testDurationCaseSigned("-14.5us", -(14500));
1947 try testDurationCaseSigned("145us", 145000);
1948 try testDurationCaseSigned("-145us", -(145000));
1949 try testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);
1950 try testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));
1951 try testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);
1952 try testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));
1953 try testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);
1954 try testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));
1955 try testDurationCaseSigned("1.11ms", 1110000);
1956 try testDurationCaseSigned("-1.11ms", -(1110000));
1957 try testDurationCaseSigned("1.111ms", 1111000);
1958 try testDurationCaseSigned("-1.111ms", -(1111000));
1959 try testDurationCaseSigned("1.111ms", 1111100);
1960 try testDurationCaseSigned("-1.111ms", -(1111100));
1961 try testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);
1962 try testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));
1963 try testDurationCaseSigned("1s", std.time.ns_per_s);
1964 try testDurationCaseSigned("-1s", -(std.time.ns_per_s));
1965 try testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);
1966 try testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));
1967 try testDurationCaseSigned("1m", std.time.ns_per_min);
1968 try testDurationCaseSigned("-1m", -(std.time.ns_per_min));
1969 try testDurationCaseSigned("1h", std.time.ns_per_hour);
1970 try testDurationCaseSigned("-1h", -(std.time.ns_per_hour));
1971 try testDurationCaseSigned("1d", std.time.ns_per_day);
1972 try testDurationCaseSigned("-1d", -(std.time.ns_per_day));
1973 try testDurationCaseSigned("1w", std.time.ns_per_week);
1974 try testDurationCaseSigned("-1w", -(std.time.ns_per_week));
1975 try testDurationCaseSigned("1y", 365 * std.time.ns_per_day);
1976 try testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));
1977 try testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
1978 try testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d
1979 try testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1980 try testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms));
1981 try testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1982 try testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us));
1983 try testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1984 try testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1));
1985 try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1986 try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms));
1987 try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1988 try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1));
1989 try testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1990 try testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
1991 try testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));
1992 try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
1993 try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));
1994
1995 try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
1996 try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
1997 try testing.expectFmt("-1ns======", "{D:=<10}", .{-(1)});
1998 try testing.expectFmt(" -999ns ", "{D:^10}", .{-(std.time.ns_per_us - 1)});
17281999}
17292000
17302001fn testDurationCase(expected: []const u8, input: u64) !void {
......@@ -1741,7 +2012,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void {
17412012 try testing.expectEqualStrings(expected, w.buffered());
17422013}
17432014
1744test printIntOptions {
2015test printInt {
17452016 try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{});
17462017
17472018 try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{});
......@@ -1757,27 +2028,22 @@ test printIntOptions {
17572028
17582029 try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 });
17592030 try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 });
1760}
17612031
1762test "printInt with comptime_int" {
1763 var buf: [20]u8 = undefined;
1764 var w: Writer = .fixed(&buf);
1765 try w.printInt(@as(comptime_int, 123456789123456789), "", .{});
1766 try std.testing.expectEqualStrings("123456789123456789", w.buffered());
2032 try testPrintIntCase("123456789123456789", @as(comptime_int, 123456789123456789), 10, .lower, .{});
17672033}
17682034
17692035test "printFloat with comptime_float" {
17702036 var buf: [20]u8 = undefined;
17712037 var w: Writer = .fixed(&buf);
1772 try w.printFloat("", .{}, @as(comptime_float, 1.0));
1773 try std.testing.expectEqualStrings(w.buffered(), "1e0");
1774 try std.testing.expectFmt("1e0", "{}", .{1.0});
2038 try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
2039 try testing.expectEqualStrings(w.buffered(), "1e0");
2040 try testing.expectFmt("1", "{}", .{1.0});
17752041}
17762042
17772043fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {
17782044 var buffer: [100]u8 = undefined;
17792045 var w: Writer = .fixed(&buffer);
1780 w.printIntOptions(value, base, case, options);
2046 try w.printInt(value, base, case, options);
17812047 try testing.expectEqualStrings(expected, w.buffered());
17822048}
17832049
......@@ -1798,12 +2064,12 @@ test printByteSize {
17982064
17992065test "bytes.hex" {
18002066 const some_bytes = "\xCA\xFE\xBA\xBE";
1801 try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
1802 try std.testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
1803 try std.testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
1804 try std.testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
2067 try testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
2068 try testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
2069 try testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
2070 try testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
18052071 const bytes_with_zeros = "\x00\x0E\xBA\xBE";
1806 try std.testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
2072 try testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
18072073}
18082074
18092075test fixed {
......@@ -1832,17 +2098,22 @@ test "fixed output" {
18322098 try w.writeAll("world");
18332099 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
18342100
1835 try testing.expectError(error.WriteStreamEnd, w.writeAll("!"));
2101 try testing.expectError(error.WriteFailed, w.writeAll("!"));
18362102 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
18372103
1838 w.reset();
2104 w = .fixed(&buffer);
2105
18392106 try testing.expect(w.buffered().len == 0);
18402107
1841 try testing.expectError(error.WriteStreamEnd, w.writeAll("Hello world!"));
2108 try testing.expectError(error.WriteFailed, w.writeAll("Hello world!"));
18422109 try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl"));
2110}
18432111
1844 try w.seekTo((try w.getEndPos()) + 1);
1845 try testing.expectError(error.WriteStreamEnd, w.writeAll("H"));
2112test "writeSplat 0 len splat larger than capacity" {
2113 var buf: [8]u8 = undefined;
2114 var w: std.io.Writer = .fixed(&buf);
2115 const n = try w.writeSplat(&.{"something that overflows buf"}, 0);
2116 try testing.expectEqual(0, n);
18462117}
18472118
18482119pub fn failingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
......@@ -1859,29 +2130,52 @@ pub fn failingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) File
18592130 return error.WriteFailed;
18602131}
18612132
1862pub fn discardingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1863 const slice = data[0 .. data.len - 1];
1864 const pattern = data[slice.len..];
1865 var written: usize = pattern.len * splat;
1866 for (slice) |bytes| written += bytes.len;
1867 w.end = 0;
1868 return written;
1869}
2133pub const Discarding = struct {
2134 count: u64,
2135 writer: Writer,
18702136
1871pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
1872 if (File.Handle == void) return error.Unimplemented;
1873 w.end = 0;
1874 if (file_reader.getSize()) |size| {
1875 const n = limit.minInt(size - file_reader.pos);
1876 file_reader.seekBy(@intCast(n)) catch return error.Unimplemented;
2137 pub fn init(buffer: []u8) Discarding {
2138 return .{
2139 .count = 0,
2140 .writer = .{
2141 .vtable = &.{
2142 .drain = Discarding.drain,
2143 .sendFile = Discarding.sendFile,
2144 },
2145 .buffer = buffer,
2146 },
2147 };
2148 }
2149
2150 pub fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
2151 const d: *Discarding = @alignCast(@fieldParentPtr("writer", w));
2152 const slice = data[0 .. data.len - 1];
2153 const pattern = data[slice.len..];
2154 var written: usize = pattern.len * splat;
2155 for (slice) |bytes| written += bytes.len;
2156 d.count += w.end + written;
18772157 w.end = 0;
1878 return n;
1879 } else |_| {
1880 // Error is observable on `file_reader` instance, and it is better to
1881 // treat the file as a pipe.
1882 return error.Unimplemented;
2158 return written;
18832159 }
1884}
2160
2161 pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
2162 if (File.Handle == void) return error.Unimplemented;
2163 const d: *Discarding = @alignCast(@fieldParentPtr("writer", w));
2164 d.count += w.end;
2165 w.end = 0;
2166 if (file_reader.getSize()) |size| {
2167 const n = limit.minInt64(size - file_reader.pos);
2168 file_reader.seekBy(@intCast(n)) catch return error.Unimplemented;
2169 w.end = 0;
2170 d.count += n;
2171 return n;
2172 } else |_| {
2173 // Error is observable on `file_reader` instance, and it is better to
2174 // treat the file as a pipe.
2175 return error.Unimplemented;
2176 }
2177 }
2178};
18852179
18862180/// Removes the first `n` bytes from `buffer` by shifting buffer contents,
18872181/// returning how many bytes are left after consuming the entire buffer, or
......@@ -1966,28 +2260,27 @@ pub fn Hashed(comptime Hasher: type) type {
19662260 return struct {
19672261 out: *Writer,
19682262 hasher: Hasher,
1969 interface: Writer,
2263 writer: Writer,
19702264
1971 pub fn init(out: *Writer) @This() {
2265 pub fn init(out: *Writer, buffer: []u8) @This() {
2266 return .initHasher(out, .{}, buffer);
2267 }
2268
2269 pub fn initHasher(out: *Writer, hasher: Hasher, buffer: []u8) @This() {
19722270 return .{
19732271 .out = out,
1974 .hasher = .{},
1975 .interface = .{
1976 .vtable = &.{@This().drain},
2272 .hasher = hasher,
2273 .writer = .{
2274 .buffer = buffer,
2275 .vtable = &.{ .drain = @This().drain },
19772276 },
19782277 };
19792278 }
19802279
19812280 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1982 const this: *@This() = @alignCast(@fieldParentPtr("interface", w));
1983 if (data.len == 0) {
1984 const buf = w.buffered();
1985 try this.out.writeAll(buf);
1986 this.hasher.update(buf);
1987 w.end = 0;
1988 return buf.len;
1989 }
1990 const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat);
2281 const this: *@This() = @alignCast(@fieldParentPtr("writer", w));
2282 const aux = w.buffered();
2283 const aux_n = try this.out.writeSplatHeader(aux, data, splat);
19912284 if (aux_n < w.end) {
19922285 this.hasher.update(w.buffer[0..aux_n]);
19932286 const remaining = w.buffer[aux_n..w.end];
......@@ -1995,29 +2288,20 @@ pub fn Hashed(comptime Hasher: type) type {
19952288 w.end = remaining.len;
19962289 return 0;
19972290 }
1998 this.hasher.update(w.buffered());
2291 this.hasher.update(aux);
19992292 const n = aux_n - w.end;
20002293 w.end = 0;
20012294 var remaining: usize = n;
2002 const short_data = data[0 .. data.len - @intFromBool(splat == 0)];
2003 for (short_data) |slice| {
2004 if (remaining < slice.len) {
2295 for (data[0 .. data.len - 1]) |slice| {
2296 if (remaining <= slice.len) {
20052297 this.hasher.update(slice[0..remaining]);
20062298 return n;
2007 } else {
2008 remaining -= slice.len;
2009 this.hasher.update(slice);
20102299 }
2300 remaining -= slice.len;
2301 this.hasher.update(slice);
20112302 }
2012 const remaining_splat = switch (splat) {
2013 0, 1 => {
2014 assert(remaining == 0);
2015 return n;
2016 },
2017 else => splat - 1,
2018 };
20192303 const pattern = data[data.len - 1];
2020 assert(remaining == remaining_splat * pattern.len);
2304 assert(remaining == splat * pattern.len);
20212305 switch (pattern.len) {
20222306 0 => {
20232307 assert(remaining == 0);
......@@ -2053,12 +2337,12 @@ pub fn Hashed(comptime Hasher: type) type {
20532337/// When using this API, it is not necessary to call `flush`.
20542338pub const Allocating = struct {
20552339 allocator: Allocator,
2056 interface: Writer,
2340 writer: Writer,
20572341
20582342 pub fn init(allocator: Allocator) Allocating {
20592343 return .{
20602344 .allocator = allocator,
2061 .interface = .{
2345 .writer = .{
20622346 .buffer = &.{},
20632347 .vtable = &vtable,
20642348 },
......@@ -2068,7 +2352,7 @@ pub const Allocating = struct {
20682352 pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating {
20692353 return .{
20702354 .allocator = allocator,
2071 .interface = .{
2355 .writer = .{
20722356 .buffer = try allocator.alloc(u8, capacity),
20732357 .vtable = &vtable,
20742358 },
......@@ -2078,7 +2362,7 @@ pub const Allocating = struct {
20782362 pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating {
20792363 return .{
20802364 .allocator = allocator,
2081 .interface = .{
2365 .writer = .{
20822366 .buffer = slice,
20832367 .vtable = &vtable,
20842368 },
......@@ -2090,7 +2374,7 @@ pub const Allocating = struct {
20902374 defer array_list.* = .empty;
20912375 return .{
20922376 .allocator = allocator,
2093 .interface = .{
2377 .writer = .{
20942378 .vtable = &vtable,
20952379 .buffer = array_list.allocatedSlice(),
20962380 .end = array_list.items.len,
......@@ -2105,14 +2389,14 @@ pub const Allocating = struct {
21052389 };
21062390
21072391 pub fn deinit(a: *Allocating) void {
2108 a.allocator.free(a.interface.buffer);
2392 a.allocator.free(a.writer.buffer);
21092393 a.* = undefined;
21102394 }
21112395
21122396 /// Returns an array list that takes ownership of the allocated memory.
21132397 /// Resets the `Allocating` to an empty state.
21142398 pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) {
2115 const w = &a.interface;
2399 const w = &a.writer;
21162400 const result: std.ArrayListUnmanaged(u8) = .{
21172401 .items = w.buffer[0..w.end],
21182402 .capacity = w.buffer.len,
......@@ -2134,13 +2418,11 @@ pub const Allocating = struct {
21342418 }
21352419
21362420 pub fn getWritten(a: *Allocating) []u8 {
2137 return a.interface.buffered();
2421 return a.writer.buffered();
21382422 }
21392423
21402424 pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void {
2141 const shrink_by = a.interface.end - new_len;
2142 a.interface.end = new_len;
2143 a.interface.count -= shrink_by;
2425 a.writer.end = new_len;
21442426 }
21452427
21462428 pub fn clearRetainingCapacity(a: *Allocating) void {
......@@ -2148,15 +2430,18 @@ pub const Allocating = struct {
21482430 }
21492431
21502432 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
2151 const a: *Allocating = @fieldParentPtr("interface", w);
2433 const a: *Allocating = @fieldParentPtr("writer", w);
21522434 const gpa = a.allocator;
21532435 const pattern = data[data.len - 1];
21542436 const splat_len = pattern.len * splat;
21552437 var list = a.toArrayList();
21562438 defer setArrayList(a, list);
21572439 const start_len = list.items.len;
2440 // Even if we append no data, this function needs to ensure there is more
2441 // capacity in the buffer to avoid infinite loop, hence the +1 in this loop.
2442 assert(data.len != 0);
21582443 for (data) |bytes| {
2159 list.ensureUnusedCapacity(gpa, bytes.len + splat_len) catch return error.WriteFailed;
2444 list.ensureUnusedCapacity(gpa, bytes.len + splat_len + 1) catch return error.WriteFailed;
21602445 list.appendSliceAssumeCapacity(bytes);
21612446 }
21622447 if (splat == 0) {
......@@ -2171,13 +2456,13 @@ pub const Allocating = struct {
21712456
21722457 fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize {
21732458 if (File.Handle == void) return error.Unimplemented;
2174 const a: *Allocating = @fieldParentPtr("interface", w);
2459 const a: *Allocating = @fieldParentPtr("writer", w);
21752460 const gpa = a.allocator;
21762461 var list = a.toArrayList();
21772462 defer setArrayList(a, list);
21782463 const pos = file_reader.pos;
21792464 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
2180 list.ensureUnusedCapacity(gpa, limit.minInt(additional)) catch return error.WriteFailed;
2465 list.ensureUnusedCapacity(gpa, limit.minInt64(additional)) catch return error.WriteFailed;
21812466 const dest = limit.slice(list.unusedCapacitySlice());
21822467 const n = file_reader.read(dest) catch |err| switch (err) {
21832468 error.ReadFailed => return error.ReadFailed,
......@@ -2188,14 +2473,14 @@ pub const Allocating = struct {
21882473 }
21892474
21902475 fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void {
2191 a.interface.buffer = list.allocatedSlice();
2192 a.interface.end = list.items.len;
2476 a.writer.buffer = list.allocatedSlice();
2477 a.writer.end = list.items.len;
21932478 }
21942479
21952480 test Allocating {
2196 var a: Allocating = .init(std.testing.allocator);
2481 var a: Allocating = .init(testing.allocator);
21972482 defer a.deinit();
2198 const w = &a.interface;
2483 const w = &a.writer;
21992484
22002485 const x: i32 = 42;
22012486 const y: i32 = 1234;
lib/std/io/change_detection_stream.zig+1-1
......@@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type {
88 return struct {
99 const Self = @This();
1010 pub const Error = WriterType.Error;
11 pub const Writer = io.Writer(*Self, Error, write);
11 pub const Writer = io.GenericWriter(*Self, Error, write);
1212
1313 anything_changed: bool,
1414 underlying_writer: WriterType,
lib/std/io/find_byte_writer.zig+1-1
......@@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type {
88 return struct {
99 const Self = @This();
1010 pub const Error = UnderlyingWriter.Error;
11 pub const Writer = io.Writer(*Self, Error, write);
11 pub const Writer = io.GenericWriter(*Self, Error, write);
1212
1313 underlying_writer: UnderlyingWriter,
1414 byte_found: bool,
lib/std/io/test.zig+4-4
......@@ -24,7 +24,7 @@ test "write a file, read it, then delete it" {
2424 var file = try tmp.dir.createFile(tmp_file_name, .{});
2525 defer file.close();
2626
27 var buf_stream = io.bufferedWriter(file.writer());
27 var buf_stream = io.bufferedWriter(file.deprecatedWriter());
2828 const st = buf_stream.writer();
2929 try st.print("begin", .{});
3030 try st.writeAll(data[0..]);
......@@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {
4545 const expected_file_size: u64 = "begin".len + data.len + "end".len;
4646 try expectEqual(expected_file_size, file_size);
4747
48 var buf_stream = io.bufferedReader(file.reader());
48 var buf_stream = io.bufferedReader(file.deprecatedReader());
4949 const st = buf_stream.reader();
5050 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
5151 defer std.testing.allocator.free(contents);
......@@ -66,7 +66,7 @@ test "BitStreams with File Stream" {
6666 var file = try tmp.dir.createFile(tmp_file_name, .{});
6767 defer file.close();
6868
69 var bit_stream = io.bitWriter(native_endian, file.writer());
69 var bit_stream = io.bitWriter(native_endian, file.deprecatedWriter());
7070
7171 try bit_stream.writeBits(@as(u2, 1), 1);
7272 try bit_stream.writeBits(@as(u5, 2), 2);
......@@ -80,7 +80,7 @@ test "BitStreams with File Stream" {
8080 var file = try tmp.dir.openFile(tmp_file_name, .{});
8181 defer file.close();
8282
83 var bit_stream = io.bitReader(native_endian, file.reader());
83 var bit_stream = io.bitReader(native_endian, file.deprecatedReader());
8484
8585 var out_bits: u16 = undefined;
8686
lib/std/io/tty.zig+35-29
......@@ -5,36 +5,9 @@ const process = std.process;
55const windows = std.os.windows;
66const native_os = builtin.os.tag;
77
8/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
9/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
10/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
11/// Will attempt to enable ANSI escape code support if necessary/possible.
8/// Deprecated in favor of `Config.detect`.
129pub fn detectConfig(file: File) Config {
13 const force_color: ?bool = if (builtin.os.tag == .wasi)
14 null // wasi does not support environment variables
15 else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
16 false
17 else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
18 true
19 else
20 null;
21
22 if (force_color == false) return .no_color;
23
24 if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
25
26 if (native_os == .windows and file.isTty()) {
27 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
28 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
29 return if (force_color == true) .escape_codes else .no_color;
30 }
31 return .{ .windows_api = .{
32 .handle = file.handle,
33 .reset_attributes = info.wAttributes,
34 } };
35 }
36
37 return if (force_color == true) .escape_codes else .no_color;
10 return .detect(file);
3811}
3912
4013pub const Color = enum {
......@@ -66,6 +39,38 @@ pub const Config = union(enum) {
6639 escape_codes,
6740 windows_api: if (native_os == .windows) WindowsContext else void,
6841
42 /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
43 /// This includes feature checks for ANSI escape codes and the Windows console API, as well as
44 /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
45 /// Will attempt to enable ANSI escape code support if necessary/possible.
46 pub fn detect(file: File) Config {
47 const force_color: ?bool = if (builtin.os.tag == .wasi)
48 null // wasi does not support environment variables
49 else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
50 false
51 else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
52 true
53 else
54 null;
55
56 if (force_color == false) return .no_color;
57
58 if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
59
60 if (native_os == .windows and file.isTty()) {
61 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
62 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
63 return if (force_color == true) .escape_codes else .no_color;
64 }
65 return .{ .windows_api = .{
66 .handle = file.handle,
67 .reset_attributes = info.wAttributes,
68 } };
69 }
70
71 return if (force_color == true) .escape_codes else .no_color;
72 }
73
6974 pub const WindowsContext = struct {
7075 handle: File.Handle,
7176 reset_attributes: u16,
......@@ -123,6 +128,7 @@ pub const Config = union(enum) {
123128 .dim => windows.FOREGROUND_INTENSITY,
124129 .reset => ctx.reset_attributes,
125130 };
131 try w.flush();
126132 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
127133 } else {
128134 unreachable;
lib/std/json.zig+2-2
......@@ -1,12 +1,12 @@
11//! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259
22//!
33//! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs,
4//! The `Reader` API connects a `std.io.Reader` to a `Scanner`.
4//! The `Reader` API connects a `std.io.GenericReader` to a `Scanner`.
55//!
66//! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type.
77//! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection.
88//!
9//! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.Writer`.
9//! The low-level `writeStream` emits syntax-conformant JSON tokens to a `std.io.GenericWriter`.
1010//! The high-level `stringify` serializes a Zig or `Value` type into JSON.
1111
1212const builtin = @import("builtin");
lib/std/json/dynamic.zig+2-2
......@@ -51,10 +51,10 @@ pub const Value = union(enum) {
5151 }
5252
5353 pub fn dump(v: Value) void {
54 const bw = std.debug.lockStderrWriter(&.{});
54 const w = std.debug.lockStderrWriter(&.{});
5555 defer std.debug.unlockStderrWriter();
5656
57 json.Stringify.value(v, .{}, bw) catch return;
57 json.Stringify.value(v, .{}, w) catch return;
5858 }
5959
6060 pub fn jsonStringify(value: @This(), jws: anytype) !void {
lib/std/json/dynamic_test.zig+1-1
......@@ -251,7 +251,7 @@ test "Value.jsonStringify" {
251251 \\ true,
252252 \\ 42,
253253 \\ 43,
254 \\ 4.2e1,
254 \\ 42,
255255 \\ "weeee",
256256 \\ [
257257 \\ 1,
lib/std/json/scanner.zig+1-1
......@@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always };
219219/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.
220220pub const default_max_value_len = 4 * 1024 * 1024;
221221
222/// Connects a `std.io.Reader` to a `std.json.Scanner`.
222/// Connects a `std.io.GenericReader` to a `std.json.Scanner`.
223223/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.
224224pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {
225225 return struct {
lib/std/log.zig+6-8
......@@ -45,9 +45,8 @@
4545//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix;
4646//!
4747//! // Print the message to stderr, silently ignoring any errors
48//! std.debug.lockStdErr();
49//! defer std.debug.unlockStdErr();
50//! const stderr = std.fs.File.stderr().writer();
48//! const stderr = std.debug.lockStderrWriter(&.{});
49//! defer std.debug.unlockStderrWriter();
5150//! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
5251//! }
5352//!
......@@ -101,8 +100,7 @@ pub const Level = enum {
101100/// The default log level is based on build mode.
102101pub const default_level: Level = switch (builtin.mode) {
103102 .Debug => .debug,
104 .ReleaseSafe => .info,
105 .ReleaseFast, .ReleaseSmall => .err,
103 .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info,
106104};
107105
108106const level = std.options.log_level;
......@@ -148,10 +146,10 @@ pub fn defaultLog(
148146) void {
149147 const level_txt = comptime message_level.asText();
150148 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
151 var buffer: [1024]u8 = undefined;
152 const bw = std.debug.lockStderrWriter(&buffer);
149 var buffer: [32]u8 = undefined;
150 const stderr = std.debug.lockStderrWriter(&buffer);
153151 defer std.debug.unlockStderrWriter();
154 bw.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
152 nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
155153}
156154
157155/// Returns a scoped logging namespace that logs all messages using the scope
lib/std/math/big/int.zig+28-42
......@@ -2028,6 +2028,14 @@ pub const Mutable = struct {
20282028 pub fn normalize(r: *Mutable, length: usize) void {
20292029 r.len = llnormalize(r.limbs[0..length]);
20302030 }
2031
2032 pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void {
2033 return formatNumber(self, w, .{});
2034 }
2035
2036 pub fn formatNumber(self: Const, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
2037 return self.toConst().formatNumber(w, n);
2038 }
20312039};
20322040
20332041/// A arbitrary-precision big integer, with a fixed set of immutable limbs.
......@@ -2317,46 +2325,25 @@ pub const Const = struct {
23172325 return .{ normalized_res.reconstruct(if (self.positive) .positive else .negative), exactness };
23182326 }
23192327
2320 /// To allow `std.fmt.format` to work with this type.
23212328 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
23222329 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
23232330 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
23242331 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
2325 pub fn format(self: Const, bw: *std.io.Writer, comptime fmt: []const u8) !void {
2326 comptime var base = 10;
2327 comptime var case: std.fmt.Case = .lower;
2328
2329 if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) {
2330 base = 10;
2331 case = .lower;
2332 } else if (comptime mem.eql(u8, fmt, "b")) {
2333 base = 2;
2334 case = .lower;
2335 } else if (comptime mem.eql(u8, fmt, "x")) {
2336 base = 16;
2337 case = .lower;
2338 } else if (comptime mem.eql(u8, fmt, "X")) {
2339 base = 16;
2340 case = .upper;
2341 } else {
2342 std.fmt.invalidFmtError(fmt, self);
2343 }
2332 pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void {
2333 const available_len = 64;
2334 if (self.limbs.len > available_len)
2335 return w.writeAll("(BigInt)");
23442336
2345 const max_str_len = self.sizeInBaseUpperBound(base);
2346 const limbs_len = calcToStringLimbsBufferLen(self.limbs.len, base);
2347 if (bw.writableSliceGreedy(max_str_len + @alignOf(Limb) - 1 + @sizeOf(Limb) * limbs_len)) |buf| {
2348 const limbs: [*]Limb = @alignCast(@ptrCast(std.mem.alignPointer(buf[max_str_len..].ptr, @alignOf(Limb))));
2349 bw.advance(self.toString(buf[0..max_str_len], base, case, limbs[0..limbs_len]));
2350 return;
2351 } else |_| if (bw.writableSliceGreedy(max_str_len)) |buf| {
2352 const available_len = 64;
2353 var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined;
2354 if (limbs.len >= limbs_len) {
2355 bw.advance(self.toString(buf, base, case, &limbs));
2356 return;
2357 }
2358 } else |_| {}
2359 try bw.writeAll("(BigInt)");
2337 var limbs: [calcToStringLimbsBufferLen(available_len, 10)]Limb = undefined;
2338
2339 const biggest: Const = .{
2340 .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len),
2341 .positive = false,
2342 };
2343 var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined;
2344 const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form");
2345 const len = self.toString(&buf, base, number.case, &limbs);
2346 return w.writeAll(buf[0..len]);
23602347 }
23612348
23622349 /// Converts self to a string in the requested base.
......@@ -2926,17 +2913,16 @@ pub const Managed = struct {
29262913 }
29272914
29282915 /// To allow `std.fmt.format` to work with `Managed`.
2916 pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void {
2917 return formatNumber(self, w, .{});
2918 }
2919
29292920 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
29302921 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
29312922 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
29322923 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
2933 pub fn format(
2934 self: Managed,
2935 comptime fmt: []const u8,
2936 options: std.fmt.FormatOptions,
2937 out_stream: anytype,
2938 ) !void {
2939 return self.toConst().format(fmt, options, out_stream);
2924 pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
2925 return self.toConst().formatNumber(w, n);
29402926 }
29412927
29422928 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
lib/std/math/big/int_test.zig+4-7
......@@ -3813,13 +3813,10 @@ test "(BigInt) positive" {
38133813 try a.pow(&a, 64 * @sizeOf(Limb) * 8);
38143814 try b.sub(&a, &c);
38153815
3816 const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a});
3817 defer testing.allocator.free(a_fmt);
3816 try testing.expectFmt("(BigInt)", "{d}", .{a});
38183817
3819 const b_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{b});
3818 const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b});
38203819 defer testing.allocator.free(b_fmt);
3821
3822 try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
38233820 try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));
38243821}
38253822
......@@ -3838,10 +3835,10 @@ test "(BigInt) negative" {
38383835 a.negate();
38393836 try b.add(&a, &c);
38403837
3841 const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a});
3838 const a_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{a});
38423839 defer testing.allocator.free(a_fmt);
38433840
3844 const b_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{b});
3841 const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b});
38453842 defer testing.allocator.free(b_fmt);
38463843
38473844 try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
lib/std/mem.zig+1-1
......@@ -1714,7 +1714,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian)
17141714 }
17151715 },
17161716 }
1717 return @as(ReturnType, @truncate(result));
1717 return @truncate(result);
17181718}
17191719
17201720test readVarInt {
lib/std/mem/Allocator.zig+1-1
......@@ -253,7 +253,7 @@ pub inline fn allocAdvancedWithRetAddr(
253253 n: usize,
254254 return_address: usize,
255255) Error![]align(if (alignment) |a| a.toByteUnits() else @alignOf(T)) T {
256 const a = comptime (alignment orelse Alignment.fromByteUnits(@alignOf(T)));
256 const a = comptime (alignment orelse Alignment.of(T));
257257 const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address));
258258 return ptr[0..n];
259259}
lib/std/multi_array_list.zig+1
......@@ -991,6 +991,7 @@ test "0 sized struct" {
991991test "struct with many fields" {
992992 const ManyFields = struct {
993993 fn Type(count: comptime_int) type {
994 @setEvalBranchQuota(50000);
994995 var fields: [count]std.builtin.Type.StructField = undefined;
995996 for (0..count) |i| {
996997 fields[i] = .{
lib/std/net.zig+16-45
......@@ -164,22 +164,13 @@ pub const Address = extern union {
164164 }
165165 }
166166
167 pub fn format(
168 self: Address,
169 comptime fmt: []const u8,
170 options: std.fmt.FormatOptions,
171 out_stream: anytype,
172 ) !void {
173 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
167 pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void {
174168 switch (self.any.family) {
175 posix.AF.INET => try self.in.format(fmt, options, out_stream),
176 posix.AF.INET6 => try self.in6.format(fmt, options, out_stream),
169 posix.AF.INET => try self.in.format(w),
170 posix.AF.INET6 => try self.in6.format(w),
177171 posix.AF.UNIX => {
178 if (!has_unix_sockets) {
179 unreachable;
180 }
181
182 try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)});
172 if (!has_unix_sockets) unreachable;
173 try w.writeAll(std.mem.sliceTo(&self.un.path, 0));
183174 },
184175 else => unreachable,
185176 }
......@@ -352,22 +343,9 @@ pub const Ip4Address = extern struct {
352343 self.sa.port = mem.nativeToBig(u16, port);
353344 }
354345
355 pub fn format(
356 self: Ip4Address,
357 comptime fmt: []const u8,
358 options: std.fmt.FormatOptions,
359 out_stream: anytype,
360 ) !void {
361 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
362 _ = options;
363 const bytes = @as(*const [4]u8, @ptrCast(&self.sa.addr));
364 try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
365 bytes[0],
366 bytes[1],
367 bytes[2],
368 bytes[3],
369 self.getPort(),
370 });
346 pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void {
347 const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);
348 try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });
371349 }
372350
373351 pub fn getOsSockLen(self: Ip4Address) posix.socklen_t {
......@@ -656,17 +634,10 @@ pub const Ip6Address = extern struct {
656634 self.sa.port = mem.nativeToBig(u16, port);
657635 }
658636
659 pub fn format(
660 self: Ip6Address,
661 comptime fmt: []const u8,
662 options: std.fmt.FormatOptions,
663 out_stream: anytype,
664 ) !void {
665 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
666 _ = options;
637 pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void {
667638 const port = mem.bigToNative(u16, self.sa.port);
668639 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
669 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
640 try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
670641 self.sa.addr[12],
671642 self.sa.addr[13],
672643 self.sa.addr[14],
......@@ -714,14 +685,14 @@ pub const Ip6Address = extern struct {
714685 longest_len = 0;
715686 }
716687
717 try out_stream.writeAll("[");
688 try w.writeAll("[");
718689 var i: usize = 0;
719690 var abbrv = false;
720691 while (i < native_endian_parts.len) : (i += 1) {
721692 if (i == longest_start) {
722693 // Emit "::" for the longest zero run
723694 if (!abbrv) {
724 try out_stream.writeAll(if (i == 0) "::" else ":");
695 try w.writeAll(if (i == 0) "::" else ":");
725696 abbrv = true;
726697 }
727698 i += longest_len - 1; // Skip the compressed range
......@@ -730,12 +701,12 @@ pub const Ip6Address = extern struct {
730701 if (abbrv) {
731702 abbrv = false;
732703 }
733 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
704 try w.print("{x}", .{native_endian_parts[i]});
734705 if (i != native_endian_parts.len - 1) {
735 try out_stream.writeAll(":");
706 try w.writeAll(":");
736707 }
737708 }
738 try std.fmt.format(out_stream, "]:{}", .{port});
709 try w.print("]:{}", .{port});
739710 }
740711
741712 pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {
......@@ -898,7 +869,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis
898869 const name_c = try gpa.dupeZ(u8, name);
899870 defer gpa.free(name_c);
900871
901 const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port});
872 const port_c = try std.fmt.allocPrintSentinel(gpa, "{}", .{port}, 0);
902873 defer gpa.free(port_c);
903874
904875 const ws2_32 = windows.ws2_32;
lib/std/net/test.zig+16-53
......@@ -5,20 +5,13 @@ const mem = std.mem;
55const testing = std.testing;
66
77test "parse and render IP addresses at comptime" {
8 if (builtin.os.tag == .wasi) return error.SkipZigTest;
98 comptime {
10 var ipAddrBuffer: [16]u8 = undefined;
11 // Parses IPv6 at comptime
129 const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable;
13 var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable;
14 try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3]));
10 try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr});
1511
16 // Parses IPv4 at comptime
1712 const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable;
18 var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable;
19 try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2]));
13 try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr});
2014
21 // Returns error for invalid IP addresses at comptime
2215 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0));
2316 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0));
2417 try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("::123.123.123.123", 0));
......@@ -27,47 +20,23 @@ test "parse and render IP addresses at comptime" {
2720}
2821
2922test "format IPv6 address with no zero runs" {
30 if (builtin.os.tag == .wasi) return error.SkipZigTest;
31
3223 const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0);
33
34 var buffer: [50]u8 = undefined;
35 const result = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;
36
37 try std.testing.expectEqualStrings("[2001:db8:1:2:3:4:5:6]:0", result);
24 try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr});
3825}
3926
4027test "parse IPv6 addresses and check compressed form" {
41 if (builtin.os.tag == .wasi) return error.SkipZigTest;
42
43 const alloc = testing.allocator;
44
45 // 1) Parse an IPv6 address that should compress to [2001:db8::1:0:0:2]:0
46 const addr1 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0);
47
48 // 2) Parse an IPv6 address that should compress to [2001:db8::1:2]:0
49 const addr2 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0);
50
51 // 3) Parse an IPv6 address that should compress to [2001:db8:1:0:1::2]:0
52 const addr3 = try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0);
53
54 // Print each address in Zig's default "[ipv6]:port" form.
55 const printed1 = try std.fmt.allocPrint(alloc, "{any}", .{addr1});
56 defer testing.allocator.free(printed1);
57 const printed2 = try std.fmt.allocPrint(alloc, "{any}", .{addr2});
58 defer testing.allocator.free(printed2);
59 const printed3 = try std.fmt.allocPrint(alloc, "{any}", .{addr3});
60 defer testing.allocator.free(printed3);
61
62 // Check the exact compressed forms we expect.
63 try std.testing.expectEqualStrings("[2001:db8::1:0:0:2]:0", printed1);
64 try std.testing.expectEqualStrings("[2001:db8::1:2]:0", printed2);
65 try std.testing.expectEqualStrings("[2001:db8:1:0:1::2]:0", printed3);
28 try std.testing.expectFmt("[2001:db8::1:0:0:2]:0", "{f}", .{
29 try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0),
30 });
31 try std.testing.expectFmt("[2001:db8::1:2]:0", "{f}", .{
32 try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0),
33 });
34 try std.testing.expectFmt("[2001:db8:1:0:1::2]:0", "{f}", .{
35 try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0),
36 });
6637}
6738
6839test "parse IPv6 address, check raw bytes" {
69 if (builtin.os.tag == .wasi) return error.SkipZigTest;
70
7140 const expected_raw: [16]u8 = .{
7241 0x20, 0x01, 0x0d, 0xb8, // 2001:db8
7342 0x00, 0x00, 0x00, 0x00, // :0000:0000
......@@ -82,8 +51,6 @@ test "parse IPv6 address, check raw bytes" {
8251}
8352
8453test "parse and render IPv6 addresses" {
85 if (builtin.os.tag == .wasi) return error.SkipZigTest;
86
8754 var buffer: [100]u8 = undefined;
8855 const ips = [_][]const u8{
8956 "FF01:0:0:0:0:0:0:FB",
......@@ -111,12 +78,12 @@ test "parse and render IPv6 addresses" {
11178 };
11279 for (ips, 0..) |ip, i| {
11380 const addr = net.Address.parseIp6(ip, 0) catch unreachable;
114 var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;
81 var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
11582 try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
11683
11784 if (builtin.os.tag == .linux) {
11885 const addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable;
119 var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr_via_resolve}) catch unreachable;
86 var newResolvedIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr_via_resolve}) catch unreachable;
12087 try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3]));
12188 }
12289 }
......@@ -148,8 +115,6 @@ test "invalid but parseable IPv6 scope ids" {
148115}
149116
150117test "parse and render IPv4 addresses" {
151 if (builtin.os.tag == .wasi) return error.SkipZigTest;
152
153118 var buffer: [18]u8 = undefined;
154119 for ([_][]const u8{
155120 "0.0.0.0",
......@@ -159,7 +124,7 @@ test "parse and render IPv4 addresses" {
159124 "127.0.0.1",
160125 }) |ip| {
161126 const addr = net.Address.parseIp4(ip, 0) catch unreachable;
162 var newIp = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;
127 var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
163128 try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
164129 }
165130
......@@ -175,10 +140,8 @@ test "parse and render UNIX addresses" {
175140 if (builtin.os.tag == .wasi) return error.SkipZigTest;
176141 if (!net.has_unix_sockets) return error.SkipZigTest;
177142
178 var buffer: [14]u8 = undefined;
179143 const addr = net.Address.initUnix("/tmp/testpath") catch unreachable;
180 const fmt_addr = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;
181 try std.testing.expectEqualSlices(u8, "/tmp/testpath", fmt_addr);
144 try std.testing.expectFmt("/tmp/testpath", "{f}", .{addr});
182145
183146 const too_long = [_]u8{'a'} ** 200;
184147 try testing.expectError(error.NameTooLong, net.Address.initUnix(too_long[0..]));
lib/std/os/freebsd.zig+1-1
......@@ -4,7 +4,7 @@ const off_t = std.c.off_t;
44const unexpectedErrno = std.posix.unexpectedErrno;
55const errno = std.posix.errno;
66
7pub const CopyFileRangeError = error{
7pub const CopyFileRangeError = std.posix.UnexpectedError || error{
88 /// If infd is not open for reading or outfd is not open for writing, or
99 /// opened for writing with O_APPEND, or if infd and outfd refer to the
1010 /// same file.
lib/std/os/uefi.zig+14-23
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const assert = std.debug.assert;
23
34/// A protocol is an interface identified by a GUID.
45pub const protocol = @import("uefi/protocol.zig");
......@@ -59,29 +60,19 @@ pub const Guid = extern struct {
5960 node: [6]u8,
6061
6162 /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
62 pub fn format(
63 self: @This(),
64 comptime f: []const u8,
65 options: std.fmt.FormatOptions,
66 writer: anytype,
67 ) !void {
68 _ = options;
69 if (f.len == 0) {
70 const time_low = @byteSwap(self.time_low);
71 const time_mid = @byteSwap(self.time_mid);
72 const time_high_and_version = @byteSwap(self.time_high_and_version);
73
74 return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
75 std.mem.asBytes(&time_low),
76 std.mem.asBytes(&time_mid),
77 std.mem.asBytes(&time_high_and_version),
78 std.mem.asBytes(&self.clock_seq_high_and_reserved),
79 std.mem.asBytes(&self.clock_seq_low),
80 std.mem.asBytes(&self.node),
81 });
82 } else {
83 std.fmt.invalidFmtError(f, self);
84 }
63 pub fn format(self: @This(), writer: *std.io.Writer) std.io.Writer.Error!void {
64 const time_low = @byteSwap(self.time_low);
65 const time_mid = @byteSwap(self.time_mid);
66 const time_high_and_version = @byteSwap(self.time_high_and_version);
67
68 return writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
69 std.mem.asBytes(&time_low),
70 std.mem.asBytes(&time_mid),
71 std.mem.asBytes(&time_high_and_version),
72 std.mem.asBytes(&self.clock_seq_high_and_reserved),
73 std.mem.asBytes(&self.clock_seq_low),
74 std.mem.asBytes(&self.node),
75 });
8576 }
8677
8778 pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool {
lib/std/os/windows.zig+2-3
......@@ -2812,9 +2812,8 @@ pub fn unexpectedError(err: Win32Error) UnexpectedError {
28122812 buf_wstr.len,
28132813 null,
28142814 );
2815 std.debug.print("error.Unexpected: GetLastError({}): {}\n", .{
2816 @intFromEnum(err),
2817 std.unicode.fmtUtf16Le(buf_wstr[0..len]),
2815 std.debug.print("error.Unexpected: GetLastError({d}): {f}\n", .{
2816 err, std.unicode.fmtUtf16Le(buf_wstr[0..len]),
28182817 });
28192818 std.debug.dumpCurrentStackTrace(@returnAddress());
28202819 }
lib/std/os/windows/test.zig+2-2
......@@ -30,7 +30,7 @@ fn testToPrefixedFileNoOracle(comptime path: []const u8, comptime expected_path:
3030 const expected_path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(expected_path);
3131 const actual_path = try windows.wToPrefixedFileW(null, path_utf16);
3232 std.testing.expectEqualSlices(u16, expected_path_utf16, actual_path.span()) catch |e| {
33 std.debug.print("got '{s}', expected '{s}'\n", .{ std.unicode.fmtUtf16Le(actual_path.span()), std.unicode.fmtUtf16Le(expected_path_utf16) });
33 std.debug.print("got '{f}', expected '{f}'\n", .{ std.unicode.fmtUtf16Le(actual_path.span()), std.unicode.fmtUtf16Le(expected_path_utf16) });
3434 return e;
3535 };
3636}
......@@ -48,7 +48,7 @@ fn testToPrefixedFileOnlyOracle(comptime path: []const u8) !void {
4848 const zig_result = try windows.wToPrefixedFileW(null, path_utf16);
4949 const win32_api_result = try RtlDosPathNameToNtPathName_U(path_utf16);
5050 std.testing.expectEqualSlices(u16, win32_api_result.span(), zig_result.span()) catch |e| {
51 std.debug.print("got '{s}', expected '{s}'\n", .{ std.unicode.fmtUtf16Le(zig_result.span()), std.unicode.fmtUtf16Le(win32_api_result.span()) });
51 std.debug.print("got '{f}', expected '{f}'\n", .{ std.unicode.fmtUtf16Le(zig_result.span()), std.unicode.fmtUtf16Le(win32_api_result.span()) });
5252 return e;
5353 };
5454}
lib/std/posix.zig+3-2
......@@ -651,7 +651,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
651651 }
652652
653653 const file: fs.File = .{ .handle = fd };
654 const stream = file.reader();
654 const stream = file.deprecatedReader();
655655 stream.readNoEof(buf) catch return error.Unexpected;
656656}
657657
......@@ -3936,6 +3936,7 @@ pub fn accept(
39363936 .WSANOTINITIALISED => unreachable, // not initialized WSA
39373937 .WSAECONNRESET => return error.ConnectionResetByPeer,
39383938 .WSAEFAULT => unreachable,
3939 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
39393940 .WSAEINVAL => return error.SocketNotListening,
39403941 .WSAEMFILE => return error.ProcessFdQuotaExceeded,
39413942 .WSAENETDOWN => return error.NetworkSubsystemFailed,
......@@ -4335,7 +4336,7 @@ pub const GetSockOptError = error{
43354336} || UnexpectedError;
43364337
43374338pub fn getsockopt(fd: socket_t, level: i32, optname: u32, opt: []u8) GetSockOptError!void {
4338 var len: socklen_t = undefined;
4339 var len: socklen_t = @intCast(opt.len);
43394340 switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) {
43404341 .SUCCESS => {
43414342 std.debug.assert(len == opt.len);
lib/std/posix/test.zig+1-1
......@@ -667,7 +667,7 @@ test "mmap" {
667667 const file = try tmp.dir.createFile(test_out_file, .{});
668668 defer file.close();
669669
670 const stream = file.writer();
670 const stream = file.deprecatedWriter();
671671
672672 var i: u32 = 0;
673673 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
lib/std/process.zig+1-1
......@@ -1553,7 +1553,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
15531553 const file = try std.fs.openFileAbsolute("/etc/passwd", .{});
15541554 defer file.close();
15551555
1556 const reader = file.reader();
1556 const reader = file.deprecatedReader();
15571557
15581558 const State = enum {
15591559 Start,
lib/std/start.zig+2-2
......@@ -486,7 +486,7 @@ fn _start() callconv(.naked) noreturn {
486486
487487fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
488488 // Switch from the x87 fpu state set by windows to the state expected by the gnu abi.
489 if (builtin.abi == .gnu) asm volatile ("fninit");
489 if (builtin.cpu.arch.isX86() and builtin.abi == .gnu) asm volatile ("fninit");
490490
491491 if (!builtin.single_threaded and !builtin.link_libc) {
492492 _ = @import("os/windows/tls.zig");
......@@ -499,7 +499,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
499499
500500fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {
501501 // Switch from the x87 fpu state set by windows to the state expected by the gnu abi.
502 if (builtin.abi == .gnu) asm volatile ("fninit");
502 if (builtin.cpu.arch.isX86() and builtin.abi == .gnu) asm volatile ("fninit");
503503
504504 if (!builtin.single_threaded and !builtin.link_libc) {
505505 _ = @import("os/windows/tls.zig");
lib/std/testing.zig+101-48
......@@ -103,7 +103,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
103103 .error_set,
104104 => {
105105 if (actual != expected) {
106 print("expected {}, found {}\n", .{ expected, actual });
106 print("expected {any}, found {any}\n", .{ expected, actual });
107107 return error.TestExpectedEqual;
108108 }
109109 },
......@@ -265,9 +265,13 @@ test "expectEqual null" {
265265
266266/// This function is intended to be used only in tests. When the formatted result of the template
267267/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how
268/// they are not equal, then returns an error. It depends on `expectEqualStrings()` for printing
268/// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing
269269/// diagnostics.
270270pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
271 if (@inComptime()) {
272 var buffer: [std.fmt.count(template, args)]u8 = undefined;
273 return expectEqualStrings(expected, try std.fmt.bufPrint(&buffer, template, args));
274 }
271275 const actual = try std.fmt.allocPrint(allocator, template, args);
272276 defer allocator.free(actual);
273277 return expectEqualStrings(expected, actual);
......@@ -354,9 +358,6 @@ test expectApproxEqRel {
354358/// The colorized output is optional and controlled by the return of `std.io.tty.detectConfig()`.
355359/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
356360pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {
357 if (expected.ptr == actual.ptr and expected.len == actual.len) {
358 return;
359 }
360361 const diff_index: usize = diff_index: {
361362 const shortest = @min(expected.len, actual.len);
362363 var index: usize = 0;
......@@ -365,12 +366,21 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
365366 }
366367 break :diff_index if (expected.len == actual.len) return else shortest;
367368 };
369 if (!backend_can_print) return error.TestExpectedEqual;
370 const stderr_w = std.debug.lockStderrWriter(&.{});
371 defer std.debug.unlockStderrWriter();
372 failEqualSlices(T, expected, actual, diff_index, stderr_w) catch {};
373 return error.TestExpectedEqual;
374}
368375
369 if (!backend_can_print) {
370 return error.TestExpectedEqual;
371 }
372
373 print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });
376fn failEqualSlices(
377 comptime T: type,
378 expected: []const T,
379 actual: []const T,
380 diff_index: usize,
381 w: *std.io.Writer,
382) !void {
383 try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });
374384
375385 // TODO: Should this be configurable by the caller?
376386 const max_lines: usize = 16;
......@@ -388,8 +398,6 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
388398 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
389399 const actual_truncated = window_start + actual_window.len < actual.len;
390400
391 const bw = std.debug.lockStderrWriter(&.{});
392 defer std.debug.unlockStderrWriter();
393401 const ttyconf = std.io.tty.detectConfig(.stderr());
394402 var differ = if (T == u8) BytesDiffer{
395403 .expected = expected_window,
......@@ -406,47 +414,47 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
406414 // that is usually useful.
407415 const index_fmt = if (T == u8) "0x{X}" else "{}";
408416
409 print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len });
417 try w.print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len });
410418 if (window_start > 0) {
411419 if (T == u8) {
412 print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
420 try w.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
413421 } else {
414 print("... truncated ...\n", .{});
422 try w.print("... truncated ...\n", .{});
415423 }
416424 }
417 differ.write(bw) catch {};
425 differ.write(w) catch {};
418426 if (expected_truncated) {
419427 const end_offset = window_start + expected_window.len;
420428 const num_missing_items = expected.len - (window_start + expected_window.len);
421429 if (T == u8) {
422 print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
430 try w.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
423431 } else {
424 print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
432 try w.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
425433 }
426434 }
427435
428436 // now reverse expected/actual and print again
429437 differ.expected = actual_window;
430438 differ.actual = expected_window;
431 print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len });
439 try w.print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len });
432440 if (window_start > 0) {
433441 if (T == u8) {
434 print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
442 try w.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
435443 } else {
436 print("... truncated ...\n", .{});
444 try w.print("... truncated ...\n", .{});
437445 }
438446 }
439 differ.write(bw) catch {};
447 differ.write(w) catch {};
440448 if (actual_truncated) {
441449 const end_offset = window_start + actual_window.len;
442450 const num_missing_items = actual.len - (window_start + actual_window.len);
443451 if (T == u8) {
444 print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
452 try w.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
445453 } else {
446 print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
454 try w.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
447455 }
448456 }
449 print("\n================================================\n\n", .{});
457 try w.print("\n================================================\n\n", .{});
450458
451459 return error.TestExpectedEqual;
452460}
......@@ -460,17 +468,17 @@ fn SliceDiffer(comptime T: type) type {
460468
461469 const Self = @This();
462470
463 pub fn write(self: Self, bw: *Writer) !void {
471 pub fn write(self: Self, writer: *std.io.Writer) !void {
464472 for (self.expected, 0..) |value, i| {
465473 const full_index = self.start_index + i;
466474 const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true;
467 if (diff) try self.ttyconf.setColor(bw, .red);
475 if (diff) try self.ttyconf.setColor(writer, .red);
468476 if (@typeInfo(T) == .pointer) {
469 try bw.print("[{}]{*}: {any}\n", .{ full_index, value, value });
477 try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value });
470478 } else {
471 try bw.print("[{}]: {any}\n", .{ full_index, value });
479 try writer.print("[{}]: {any}\n", .{ full_index, value });
472480 }
473 if (diff) try self.ttyconf.setColor(bw, .reset);
481 if (diff) try self.ttyconf.setColor(writer, .reset);
474482 }
475483 }
476484 };
......@@ -481,7 +489,7 @@ const BytesDiffer = struct {
481489 actual: []const u8,
482490 ttyconf: std.io.tty.Config,
483491
484 pub fn write(self: BytesDiffer, bw: *Writer) !void {
492 pub fn write(self: BytesDiffer, writer: *std.io.Writer) !void {
485493 var expected_iterator = std.mem.window(u8, self.expected, 16, 16);
486494 var row: usize = 0;
487495 while (expected_iterator.next()) |chunk| {
......@@ -491,23 +499,23 @@ const BytesDiffer = struct {
491499 const absolute_byte_index = col + row * 16;
492500 const diff = if (absolute_byte_index < self.actual.len) self.actual[absolute_byte_index] != byte else true;
493501 if (diff) diffs.set(col);
494 try self.writeDiff(bw, "{X:0>2} ", .{byte}, diff);
495 if (col == 7) try bw.writeByte(' ');
502 try self.writeDiff(writer, "{X:0>2} ", .{byte}, diff);
503 if (col == 7) try writer.writeByte(' ');
496504 }
497 try bw.writeByte(' ');
505 try writer.writeByte(' ');
498506 if (chunk.len < 16) {
499507 var missing_columns = (16 - chunk.len) * 3;
500508 if (chunk.len < 8) missing_columns += 1;
501 try bw.splatByteAll(' ', missing_columns);
509 try writer.splatByteAll(' ', missing_columns);
502510 }
503511 for (chunk, 0..) |byte, col| {
504512 const diff = diffs.isSet(col);
505513 if (std.ascii.isPrint(byte)) {
506 try self.writeDiff(bw, "{c}", .{byte}, diff);
514 try self.writeDiff(writer, "{c}", .{byte}, diff);
507515 } else {
508516 // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed
509517 if (self.ttyconf == .windows_api) {
510 try self.writeDiff(bw, ".", .{}, diff);
518 try self.writeDiff(writer, ".", .{}, diff);
511519 continue;
512520 }
513521
......@@ -515,22 +523,22 @@ const BytesDiffer = struct {
515523 // We don't want to do this for all control codes because most control codes apart from
516524 // the ones that Zig has escape sequences for are likely not very useful to print as symbols.
517525 switch (byte) {
518 '\n' => try self.writeDiff(bw, "␊", .{}, diff),
519 '\r' => try self.writeDiff(bw, "␍", .{}, diff),
520 '\t' => try self.writeDiff(bw, "␉", .{}, diff),
521 else => try self.writeDiff(bw, ".", .{}, diff),
526 '\n' => try self.writeDiff(writer, "␊", .{}, diff),
527 '\r' => try self.writeDiff(writer, "␍", .{}, diff),
528 '\t' => try self.writeDiff(writer, "␉", .{}, diff),
529 else => try self.writeDiff(writer, ".", .{}, diff),
522530 }
523531 }
524532 }
525 try bw.writeByte('\n');
533 try writer.writeByte('\n');
526534 row += 1;
527535 }
528536 }
529537
530 fn writeDiff(self: BytesDiffer, bw: *Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void {
531 if (diff) try self.ttyconf.setColor(bw, .red);
532 try bw.print(fmt, args);
533 if (diff) try self.ttyconf.setColor(bw, .reset);
538 fn writeDiff(self: BytesDiffer, writer: *std.io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void {
539 if (diff) try self.ttyconf.setColor(writer, .red);
540 try writer.print(fmt, args);
541 if (diff) try self.ttyconf.setColor(writer, .reset);
534542 }
535543};
536544
......@@ -641,6 +649,11 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir {
641649
642650pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
643651 if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {
652 if (@inComptime()) {
653 @compileError(std.fmt.comptimePrint("\nexpected:\n{s}\nfound:\n{s}\ndifference starts at index {d}", .{
654 expected, actual, diff_index,
655 }));
656 }
644657 print("\n====== expected this output: =========\n", .{});
645658 printWithVisibleNewlines(expected);
646659 print("\n======== instead found this: =========\n", .{});
......@@ -1112,7 +1125,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
11121125 const arg_i_str = comptime str: {
11131126 var str_buf: [100]u8 = undefined;
11141127 const args_i = i + 1;
1115 const str_len = std.fmt.formatIntBuf(&str_buf, args_i, 10, .lower, .{});
1128 const str_len = std.fmt.printInt(&str_buf, args_i, 10, .lower, .{});
11161129 break :str str_buf[0..str_len];
11171130 };
11181131 @field(args, arg_i_str) = @field(extra_args, field.name);
......@@ -1142,7 +1155,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
11421155 error.OutOfMemory => {
11431156 if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {
11441157 print(
1145 "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {}",
1158 "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}",
11461159 .{
11471160 fail_index,
11481161 needed_alloc_count,
......@@ -1196,3 +1209,43 @@ pub inline fn fuzz(
11961209) anyerror!void {
11971210 return @import("root").fuzz(context, testOne, options);
11981211}
1212
1213/// A `std.io.Reader` that writes a predetermined list of buffers during `stream`.
1214pub const Reader = struct {
1215 calls: []const Call,
1216 interface: std.io.Reader,
1217 next_call_index: usize,
1218 next_offset: usize,
1219
1220 pub const Call = struct {
1221 buffer: []const u8,
1222 };
1223
1224 pub fn init(buffer: []u8, calls: []const Call) Reader {
1225 return .{
1226 .next_call_index = 0,
1227 .next_offset = 0,
1228 .interface = .{
1229 .vtable = &.{ .stream = stream },
1230 .buffer = buffer,
1231 .seek = 0,
1232 .end = 0,
1233 },
1234 .calls = calls,
1235 };
1236 }
1237
1238 fn stream(io_r: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
1239 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r));
1240 if (r.calls.len - r.next_call_index == 0) return error.EndOfStream;
1241 const call = r.calls[r.next_call_index];
1242 const buffer = limit.sliceConst(call.buffer[r.next_offset..]);
1243 const n = try w.write(buffer);
1244 r.next_offset += n;
1245 if (call.buffer.len - r.next_offset == 0) {
1246 r.next_call_index += 1;
1247 r.next_offset = 0;
1248 }
1249 return n;
1250 }
1251};
lib/std/unicode.zig+23-36
......@@ -9,6 +9,7 @@ const native_endian = builtin.cpu.arch.endian();
99///
1010/// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character
1111pub const replacement_character: u21 = 0xFFFD;
12pub const replacement_character_utf8: [3]u8 = utf8EncodeComptime(replacement_character);
1213
1314/// Returns how many bytes the UTF-8 representation would require
1415/// for the given codepoint.
......@@ -802,14 +803,7 @@ fn testDecode(bytes: []const u8) !u21 {
802803/// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD)
803804/// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of
804805/// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder
805fn formatUtf8(
806 utf8: []const u8,
807 comptime fmt: []const u8,
808 options: std.fmt.FormatOptions,
809 writer: anytype,
810) !void {
811 _ = fmt;
812 _ = options;
806fn formatUtf8(utf8: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
813807 var buf: [300]u8 = undefined; // just an arbitrary size
814808 var u8len: usize = 0;
815809
......@@ -898,27 +892,27 @@ fn formatUtf8(
898892/// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD)
899893/// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of
900894/// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder
901pub fn fmtUtf8(utf8: []const u8) std.fmt.Formatter(formatUtf8) {
895pub fn fmtUtf8(utf8: []const u8) std.fmt.Formatter([]const u8, formatUtf8) {
902896 return .{ .data = utf8 };
903897}
904898
905899test fmtUtf8 {
906900 const expectFmt = testing.expectFmt;
907 try expectFmt("", "{}", .{fmtUtf8("")});
908 try expectFmt("foo", "{}", .{fmtUtf8("foo")});
909 try expectFmt("𐐷", "{}", .{fmtUtf8("𐐷")});
901 try expectFmt("", "{f}", .{fmtUtf8("")});
902 try expectFmt("foo", "{f}", .{fmtUtf8("foo")});
903 try expectFmt("𐐷", "{f}", .{fmtUtf8("𐐷")});
910904
911905 // Table 3-8. U+FFFD for Non-Shortest Form Sequences
912 try expectFmt("��������A", "{}", .{fmtUtf8("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82A")});
906 try expectFmt("��������A", "{f}", .{fmtUtf8("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82A")});
913907
914908 // Table 3-9. U+FFFD for Ill-Formed Sequences for Surrogates
915 try expectFmt("��������A", "{}", .{fmtUtf8("\xED\xA0\x80\xED\xBF\xBF\xED\xAFA")});
909 try expectFmt("��������A", "{f}", .{fmtUtf8("\xED\xA0\x80\xED\xBF\xBF\xED\xAFA")});
916910
917911 // Table 3-10. U+FFFD for Other Ill-Formed Sequences
918 try expectFmt("�����A��B", "{}", .{fmtUtf8("\xF4\x91\x92\x93\xFFA\x80\xBFB")});
912 try expectFmt("�����A��B", "{f}", .{fmtUtf8("\xF4\x91\x92\x93\xFFA\x80\xBFB")});
919913
920914 // Table 3-11. U+FFFD for Truncated Sequences
921 try expectFmt("����A", "{}", .{fmtUtf8("\xE1\x80\xE2\xF0\x91\x92\xF1\xBFA")});
915 try expectFmt("����A", "{f}", .{fmtUtf8("\xE1\x80\xE2\xF0\x91\x92\xF1\xBFA")});
922916}
923917
924918fn utf16LeToUtf8ArrayListImpl(
......@@ -1477,14 +1471,7 @@ test calcWtf16LeLen {
14771471
14781472/// Print the given `utf16le` string, encoded as UTF-8 bytes.
14791473/// Unpaired surrogates are replaced by the replacement character (U+FFFD).
1480fn formatUtf16Le(
1481 utf16le: []const u16,
1482 comptime fmt: []const u8,
1483 options: std.fmt.FormatOptions,
1484 writer: anytype,
1485) !void {
1486 _ = fmt;
1487 _ = options;
1474fn formatUtf16Le(utf16le: []const u16, writer: *std.io.Writer) std.io.Writer.Error!void {
14881475 var buf: [300]u8 = undefined; // just an arbitrary size
14891476 var it = Utf16LeIterator.init(utf16le);
14901477 var u8len: usize = 0;
......@@ -1505,23 +1492,23 @@ pub const fmtUtf16le = @compileError("deprecated; renamed to fmtUtf16Le");
15051492/// Return a Formatter for a (potentially ill-formed) UTF-16 LE string,
15061493/// which will be converted to UTF-8 during formatting.
15071494/// Unpaired surrogates are replaced by the replacement character (U+FFFD).
1508pub fn fmtUtf16Le(utf16le: []const u16) std.fmt.Formatter(formatUtf16Le) {
1495pub fn fmtUtf16Le(utf16le: []const u16) std.fmt.Formatter([]const u16, formatUtf16Le) {
15091496 return .{ .data = utf16le };
15101497}
15111498
15121499test fmtUtf16Le {
15131500 const expectFmt = testing.expectFmt;
1514 try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});
1515 try expectFmt("", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))});
1516 try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});
1517 try expectFmt("foo", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))});
1518 try expectFmt("𐐷", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))});
1519 try expectFmt("퟿", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})});
1520 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})});
1521 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})});
1522 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})});
1523 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})});
1524 try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})});
1501 try expectFmt("", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});
1502 try expectFmt("", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))});
1503 try expectFmt("foo", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});
1504 try expectFmt("foo", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))});
1505 try expectFmt("𐐷", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))});
1506 try expectFmt("퟿", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})});
1507 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})});
1508 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})});
1509 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})});
1510 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})});
1511 try expectFmt("", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})});
15251512}
15261513
15271514fn testUtf8ToUtf16LeStringLiteral(utf8ToUtf16LeStringLiteral_: anytype) !void {
lib/std/zig.zig+108-110
......@@ -54,7 +54,7 @@ pub const Color = enum {
5454
5555 pub fn get_tty_conf(color: Color) std.io.tty.Config {
5656 return switch (color) {
57 .auto => std.io.tty.detectConfig(.stderr()),
57 .auto => std.io.tty.detectConfig(std.fs.File.stderr()),
5858 .on => .escape_codes,
5959 .off => .no_color,
6060 };
......@@ -364,138 +364,136 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![
364364
365365/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
366366///
367/// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives
368/// and the reserved `_` identifier.
369/// - Add `p` to the specifier to render identifiers that shadow primitives unescaped.
370/// - Add `_` to the specifier to render the reserved `_` identifier unescaped.
371/// - `p` and `_` can be combined, e.g. `{p_}`.
367/// See also `fmtIdFlags`.
368pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
369 return .{ .data = .{ .bytes = bytes, .flags = .{} } };
370}
371
372/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
372373///
373pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {
374 return .{ .data = bytes };
374/// See also `fmtId`.
375pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) std.fmt.Formatter(FormatId, FormatId.render) {
376 return .{ .data = .{ .bytes = bytes, .flags = flags } };
377}
378
379pub fn fmtIdPU(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
380 return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true, .allow_underscore = true } } };
381}
382
383pub fn fmtIdP(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
384 return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true } } };
375385}
376386
377387test fmtId {
378388 const expectFmt = std.testing.expectFmt;
379 try expectFmt("@\"while\"", "{}", .{fmtId("while")});
380 try expectFmt("@\"while\"", "{p}", .{fmtId("while")});
381 try expectFmt("@\"while\"", "{_}", .{fmtId("while")});
382 try expectFmt("@\"while\"", "{p_}", .{fmtId("while")});
383 try expectFmt("@\"while\"", "{_p}", .{fmtId("while")});
384
385 try expectFmt("hello", "{}", .{fmtId("hello")});
386 try expectFmt("hello", "{p}", .{fmtId("hello")});
387 try expectFmt("hello", "{_}", .{fmtId("hello")});
388 try expectFmt("hello", "{p_}", .{fmtId("hello")});
389 try expectFmt("hello", "{_p}", .{fmtId("hello")});
390
391 try expectFmt("@\"type\"", "{}", .{fmtId("type")});
392 try expectFmt("type", "{p}", .{fmtId("type")});
393 try expectFmt("@\"type\"", "{_}", .{fmtId("type")});
394 try expectFmt("type", "{p_}", .{fmtId("type")});
395 try expectFmt("type", "{_p}", .{fmtId("type")});
396
397 try expectFmt("@\"_\"", "{}", .{fmtId("_")});
398 try expectFmt("@\"_\"", "{p}", .{fmtId("_")});
399 try expectFmt("_", "{_}", .{fmtId("_")});
400 try expectFmt("_", "{p_}", .{fmtId("_")});
401 try expectFmt("_", "{_p}", .{fmtId("_")});
402
403 try expectFmt("@\"i123\"", "{}", .{fmtId("i123")});
404 try expectFmt("i123", "{p}", .{fmtId("i123")});
405 try expectFmt("@\"4four\"", "{}", .{fmtId("4four")});
406 try expectFmt("_underscore", "{}", .{fmtId("_underscore")});
407 try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")});
408 try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")});
389 try expectFmt("@\"while\"", "{f}", .{fmtId("while")});
390 try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true })});
391 try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_underscore = true })});
392 try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true, .allow_underscore = true })});
393
394 try expectFmt("hello", "{f}", .{fmtId("hello")});
395 try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true })});
396 try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_underscore = true })});
397 try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true, .allow_underscore = true })});
398
399 try expectFmt("@\"type\"", "{f}", .{fmtId("type")});
400 try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true })});
401 try expectFmt("@\"type\"", "{f}", .{fmtIdFlags("type", .{ .allow_underscore = true })});
402 try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true, .allow_underscore = true })});
403
404 try expectFmt("@\"_\"", "{f}", .{fmtId("_")});
405 try expectFmt("@\"_\"", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true })});
406 try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_underscore = true })});
407 try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true, .allow_underscore = true })});
408
409 try expectFmt("@\"i123\"", "{f}", .{fmtId("i123")});
410 try expectFmt("i123", "{f}", .{fmtIdFlags("i123", .{ .allow_primitive = true })});
411 try expectFmt("@\"4four\"", "{f}", .{fmtId("4four")});
412 try expectFmt("_underscore", "{f}", .{fmtId("_underscore")});
413 try expectFmt("@\"11\\\"23\"", "{f}", .{fmtId("11\"23")});
414 try expectFmt("@\"11\\x0f23\"", "{f}", .{fmtId("11\x0F23")});
409415
410416 // These are technically not currently legal in Zig.
411 try expectFmt("@\"\"", "{}", .{fmtId("")});
412 try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")});
417 try expectFmt("@\"\"", "{f}", .{fmtId("")});
418 try expectFmt("@\"\\x00\"", "{f}", .{fmtId("\x00")});
413419}
414420
415/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
416fn formatId(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void {
417 const allow_primitive, const allow_underscore = comptime parse_fmt: {
418 var allow_primitive = false;
419 var allow_underscore = false;
420 for (fmt) |char| {
421 switch (char) {
422 'p' => if (!allow_primitive) {
423 allow_primitive = true;
424 continue;
425 },
426 '_' => if (!allow_underscore) {
427 allow_underscore = true;
428 continue;
429 },
430 else => {},
431 }
432 @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}");
433 }
434 break :parse_fmt .{ allow_primitive, allow_underscore };
421pub const FormatId = struct {
422 bytes: []const u8,
423 flags: Flags,
424 pub const Flags = struct {
425 allow_primitive: bool = false,
426 allow_underscore: bool = false,
435427 };
436428
437 if (isValidId(bytes) and
438 (allow_primitive or !std.zig.isPrimitive(bytes)) and
439 (allow_underscore or !isUnderscore(bytes)))
440 {
441 return bw.writeAll(bytes);
429 /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
430 fn render(ctx: FormatId, writer: *std.io.Writer) std.io.Writer.Error!void {
431 const bytes = ctx.bytes;
432 if (isValidId(bytes) and
433 (ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and
434 (ctx.flags.allow_underscore or !isUnderscore(bytes)))
435 {
436 return writer.writeAll(bytes);
437 }
438 try writer.writeAll("@\"");
439 try stringEscape(bytes, writer);
440 try writer.writeByte('"');
442441 }
443 try bw.writeAll("@\"");
444 try stringEscape(bytes, bw, "");
445 try bw.writeByte('"');
442};
443
444/// Return a formatter for escaping a double quoted Zig string.
445pub fn fmtString(bytes: []const u8) std.fmt.Formatter([]const u8, stringEscape) {
446 return .{ .data = bytes };
446447}
447448
448/// Return a Formatter for Zig Escapes of a double quoted string.
449/// The format specifier must be one of:
450/// * `{}` treats contents as a double-quoted string.
451/// * `{'}` treats contents as a single-quoted string.
452pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) {
449/// Return a formatter for escaping a single quoted Zig string.
450pub fn fmtChar(bytes: []const u8) std.fmt.Formatter([]const u8, charEscape) {
453451 return .{ .data = bytes };
454452}
455453
456test fmtEscapes {
457 const expectFmt = std.testing.expectFmt;
458 try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});
459 try expectFmt(
460 \\" \\ hi \x07 \x11 " derp \'"
461 , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
462 try expectFmt(
454test fmtString {
455 try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")});
456 try std.testing.expectFmt(
463457 \\" \\ hi \x07 \x11 \" derp '"
464 , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
458 , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")});
465459}
466460
467/// Print the string as escaped contents of a double quoted or single-quoted string.
468/// Format `{}` treats contents as a double-quoted string.
469/// Format `{'}` treats contents as a single-quoted string.
470pub fn stringEscape(bytes: []const u8, bw: *Writer, comptime f: []const u8) !void {
461test fmtChar {
462 try std.testing.expectFmt(
463 \\" \\ hi \x07 \x11 " derp \'"
464 , "\"{f}\"", .{fmtChar(" \\ hi \x07 \x11 \" derp '")});
465}
466
467/// Print the string as escaped contents of a double quoted string.
468pub fn stringEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
471469 for (bytes) |byte| switch (byte) {
472 '\n' => try bw.writeAll("\\n"),
473 '\r' => try bw.writeAll("\\r"),
474 '\t' => try bw.writeAll("\\t"),
475 '\\' => try bw.writeAll("\\\\"),
476 '"' => {
477 if (f.len == 1 and f[0] == '\'') {
478 try bw.writeByte('"');
479 } else if (f.len == 0) {
480 try bw.writeAll("\\\"");
481 } else {
482 @compileError("expected {} or {'}, found {" ++ f ++ "}");
483 }
484 },
485 '\'' => {
486 if (f.len == 1 and f[0] == '\'') {
487 try bw.writeAll("\\'");
488 } else if (f.len == 0) {
489 try bw.writeByte('\'');
490 } else {
491 @compileError("expected {} or {'}, found {" ++ f ++ "}");
492 }
470 '\n' => try w.writeAll("\\n"),
471 '\r' => try w.writeAll("\\r"),
472 '\t' => try w.writeAll("\\t"),
473 '\\' => try w.writeAll("\\\\"),
474 '"' => try w.writeAll("\\\""),
475 '\'' => try w.writeByte('\''),
476 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte),
477 else => {
478 try w.writeAll("\\x");
479 try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
493480 },
494 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte),
495 // Use hex escapes for rest any unprintable characters.
481 };
482}
483
484/// Print the string as escaped contents of a single-quoted string.
485pub fn charEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
486 for (bytes) |byte| switch (byte) {
487 '\n' => try w.writeAll("\\n"),
488 '\r' => try w.writeAll("\\r"),
489 '\t' => try w.writeAll("\\t"),
490 '\\' => try w.writeAll("\\\\"),
491 '"' => try w.writeByte('"'),
492 '\'' => try w.writeAll("\\'"),
493 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte),
496494 else => {
497 try bw.writeAll("\\x");
498 try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' });
495 try w.writeAll("\\x");
496 try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
499497 },
500498 };
501499}
lib/std/zig/Ast.zig+75-146
......@@ -320,261 +320,261 @@ pub fn rootDecls(tree: Ast) []const Node.Index {
320320 }
321321}
322322
323pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void {
323pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void {
324324 switch (parse_error.tag) {
325325 .asterisk_after_ptr_deref => {
326326 // Note that the token will point at the `.*` but ideally the source
327327 // location would point to the `*` after the `.*`.
328 return bw.writeAll("'.*' cannot be followed by '*'; are you missing a space?");
328 return w.writeAll("'.*' cannot be followed by '*'; are you missing a space?");
329329 },
330330 .chained_comparison_operators => {
331 return bw.writeAll("comparison operators cannot be chained");
331 return w.writeAll("comparison operators cannot be chained");
332332 },
333333 .decl_between_fields => {
334 return bw.writeAll("declarations are not allowed between container fields");
334 return w.writeAll("declarations are not allowed between container fields");
335335 },
336336 .expected_block => {
337 return bw.print("expected block, found '{s}'", .{
337 return w.print("expected block, found '{s}'", .{
338338 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
339339 });
340340 },
341341 .expected_block_or_assignment => {
342 return bw.print("expected block or assignment, found '{s}'", .{
342 return w.print("expected block or assignment, found '{s}'", .{
343343 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
344344 });
345345 },
346346 .expected_block_or_expr => {
347 return bw.print("expected block or expression, found '{s}'", .{
347 return w.print("expected block or expression, found '{s}'", .{
348348 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
349349 });
350350 },
351351 .expected_block_or_field => {
352 return bw.print("expected block or field, found '{s}'", .{
352 return w.print("expected block or field, found '{s}'", .{
353353 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
354354 });
355355 },
356356 .expected_container_members => {
357 return bw.print("expected test, comptime, var decl, or container field, found '{s}'", .{
357 return w.print("expected test, comptime, var decl, or container field, found '{s}'", .{
358358 tree.tokenTag(parse_error.token).symbol(),
359359 });
360360 },
361361 .expected_expr => {
362 return bw.print("expected expression, found '{s}'", .{
362 return w.print("expected expression, found '{s}'", .{
363363 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
364364 });
365365 },
366366 .expected_expr_or_assignment => {
367 return bw.print("expected expression or assignment, found '{s}'", .{
367 return w.print("expected expression or assignment, found '{s}'", .{
368368 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
369369 });
370370 },
371371 .expected_expr_or_var_decl => {
372 return bw.print("expected expression or var decl, found '{s}'", .{
372 return w.print("expected expression or var decl, found '{s}'", .{
373373 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
374374 });
375375 },
376376 .expected_fn => {
377 return bw.print("expected function, found '{s}'", .{
377 return w.print("expected function, found '{s}'", .{
378378 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
379379 });
380380 },
381381 .expected_inlinable => {
382 return bw.print("expected 'while' or 'for', found '{s}'", .{
382 return w.print("expected 'while' or 'for', found '{s}'", .{
383383 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
384384 });
385385 },
386386 .expected_labelable => {
387 return bw.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{
387 return w.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{
388388 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
389389 });
390390 },
391391 .expected_param_list => {
392 return bw.print("expected parameter list, found '{s}'", .{
392 return w.print("expected parameter list, found '{s}'", .{
393393 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
394394 });
395395 },
396396 .expected_prefix_expr => {
397 return bw.print("expected prefix expression, found '{s}'", .{
397 return w.print("expected prefix expression, found '{s}'", .{
398398 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
399399 });
400400 },
401401 .expected_primary_type_expr => {
402 return bw.print("expected primary type expression, found '{s}'", .{
402 return w.print("expected primary type expression, found '{s}'", .{
403403 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
404404 });
405405 },
406406 .expected_pub_item => {
407 return bw.writeAll("expected function or variable declaration after pub");
407 return w.writeAll("expected function or variable declaration after pub");
408408 },
409409 .expected_return_type => {
410 return bw.print("expected return type expression, found '{s}'", .{
410 return w.print("expected return type expression, found '{s}'", .{
411411 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
412412 });
413413 },
414414 .expected_semi_or_else => {
415 return bw.writeAll("expected ';' or 'else' after statement");
415 return w.writeAll("expected ';' or 'else' after statement");
416416 },
417417 .expected_semi_or_lbrace => {
418 return bw.writeAll("expected ';' or block after function prototype");
418 return w.writeAll("expected ';' or block after function prototype");
419419 },
420420 .expected_statement => {
421 return bw.print("expected statement, found '{s}'", .{
421 return w.print("expected statement, found '{s}'", .{
422422 tree.tokenTag(parse_error.token).symbol(),
423423 });
424424 },
425425 .expected_suffix_op => {
426 return bw.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
426 return w.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
427427 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
428428 });
429429 },
430430 .expected_type_expr => {
431 return bw.print("expected type expression, found '{s}'", .{
431 return w.print("expected type expression, found '{s}'", .{
432432 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
433433 });
434434 },
435435 .expected_var_decl => {
436 return bw.print("expected variable declaration, found '{s}'", .{
436 return w.print("expected variable declaration, found '{s}'", .{
437437 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
438438 });
439439 },
440440 .expected_var_decl_or_fn => {
441 return bw.print("expected variable declaration or function, found '{s}'", .{
441 return w.print("expected variable declaration or function, found '{s}'", .{
442442 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
443443 });
444444 },
445445 .expected_loop_payload => {
446 return bw.print("expected loop payload, found '{s}'", .{
446 return w.print("expected loop payload, found '{s}'", .{
447447 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
448448 });
449449 },
450450 .expected_container => {
451 return bw.print("expected a struct, enum or union, found '{s}'", .{
451 return w.print("expected a struct, enum or union, found '{s}'", .{
452452 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
453453 });
454454 },
455455 .extern_fn_body => {
456 return bw.writeAll("extern functions have no body");
456 return w.writeAll("extern functions have no body");
457457 },
458458 .extra_addrspace_qualifier => {
459 return bw.writeAll("extra addrspace qualifier");
459 return w.writeAll("extra addrspace qualifier");
460460 },
461461 .extra_align_qualifier => {
462 return bw.writeAll("extra align qualifier");
462 return w.writeAll("extra align qualifier");
463463 },
464464 .extra_allowzero_qualifier => {
465 return bw.writeAll("extra allowzero qualifier");
465 return w.writeAll("extra allowzero qualifier");
466466 },
467467 .extra_const_qualifier => {
468 return bw.writeAll("extra const qualifier");
468 return w.writeAll("extra const qualifier");
469469 },
470470 .extra_volatile_qualifier => {
471 return bw.writeAll("extra volatile qualifier");
471 return w.writeAll("extra volatile qualifier");
472472 },
473473 .ptr_mod_on_array_child_type => {
474 return bw.print("pointer modifier '{s}' not allowed on array child type", .{
474 return w.print("pointer modifier '{s}' not allowed on array child type", .{
475475 tree.tokenTag(parse_error.token).symbol(),
476476 });
477477 },
478478 .invalid_bit_range => {
479 return bw.writeAll("bit range not allowed on slices and arrays");
479 return w.writeAll("bit range not allowed on slices and arrays");
480480 },
481481 .same_line_doc_comment => {
482 return bw.writeAll("same line documentation comment");
482 return w.writeAll("same line documentation comment");
483483 },
484484 .unattached_doc_comment => {
485 return bw.writeAll("unattached documentation comment");
485 return w.writeAll("unattached documentation comment");
486486 },
487487 .test_doc_comment => {
488 return bw.writeAll("documentation comments cannot be attached to tests");
488 return w.writeAll("documentation comments cannot be attached to tests");
489489 },
490490 .comptime_doc_comment => {
491 return bw.writeAll("documentation comments cannot be attached to comptime blocks");
491 return w.writeAll("documentation comments cannot be attached to comptime blocks");
492492 },
493493 .varargs_nonfinal => {
494 return bw.writeAll("function prototype has parameter after varargs");
494 return w.writeAll("function prototype has parameter after varargs");
495495 },
496496 .expected_continue_expr => {
497 return bw.writeAll("expected ':' before while continue expression");
497 return w.writeAll("expected ':' before while continue expression");
498498 },
499499
500500 .expected_semi_after_decl => {
501 return bw.writeAll("expected ';' after declaration");
501 return w.writeAll("expected ';' after declaration");
502502 },
503503 .expected_semi_after_stmt => {
504 return bw.writeAll("expected ';' after statement");
504 return w.writeAll("expected ';' after statement");
505505 },
506506 .expected_comma_after_field => {
507 return bw.writeAll("expected ',' after field");
507 return w.writeAll("expected ',' after field");
508508 },
509509 .expected_comma_after_arg => {
510 return bw.writeAll("expected ',' after argument");
510 return w.writeAll("expected ',' after argument");
511511 },
512512 .expected_comma_after_param => {
513 return bw.writeAll("expected ',' after parameter");
513 return w.writeAll("expected ',' after parameter");
514514 },
515515 .expected_comma_after_initializer => {
516 return bw.writeAll("expected ',' after initializer");
516 return w.writeAll("expected ',' after initializer");
517517 },
518518 .expected_comma_after_switch_prong => {
519 return bw.writeAll("expected ',' after switch prong");
519 return w.writeAll("expected ',' after switch prong");
520520 },
521521 .expected_comma_after_for_operand => {
522 return bw.writeAll("expected ',' after for operand");
522 return w.writeAll("expected ',' after for operand");
523523 },
524524 .expected_comma_after_capture => {
525 return bw.writeAll("expected ',' after for capture");
525 return w.writeAll("expected ',' after for capture");
526526 },
527527 .expected_initializer => {
528 return bw.writeAll("expected field initializer");
528 return w.writeAll("expected field initializer");
529529 },
530530 .mismatched_binary_op_whitespace => {
531 return bw.print("binary operator '{s}' has whitespace on one side, but not the other", .{tree.tokenTag(parse_error.token).lexeme().?});
531 return w.print("binary operator '{s}' has whitespace on one side, but not the other", .{tree.tokenTag(parse_error.token).lexeme().?});
532532 },
533533 .invalid_ampersand_ampersand => {
534 return bw.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND");
534 return w.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND");
535535 },
536536 .c_style_container => {
537 return bw.print("'{s} {s}' is invalid", .{
537 return w.print("'{s} {s}' is invalid", .{
538538 parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token),
539539 });
540540 },
541541 .zig_style_container => {
542 return bw.print("to declare a container do 'const {s} = {s}'", .{
542 return w.print("to declare a container do 'const {s} = {s}'", .{
543543 tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(),
544544 });
545545 },
546546 .previous_field => {
547 return bw.writeAll("field before declarations here");
547 return w.writeAll("field before declarations here");
548548 },
549549 .next_field => {
550 return bw.writeAll("field after declarations here");
550 return w.writeAll("field after declarations here");
551551 },
552552 .expected_var_const => {
553 return bw.writeAll("expected 'var' or 'const' before variable declaration");
553 return w.writeAll("expected 'var' or 'const' before variable declaration");
554554 },
555555 .wrong_equal_var_decl => {
556 return bw.writeAll("variable initialized with '==' instead of '='");
556 return w.writeAll("variable initialized with '==' instead of '='");
557557 },
558558 .var_const_decl => {
559 return bw.writeAll("use 'var' or 'const' to declare variable");
559 return w.writeAll("use 'var' or 'const' to declare variable");
560560 },
561561 .extra_for_capture => {
562 return bw.writeAll("extra capture in for loop");
562 return w.writeAll("extra capture in for loop");
563563 },
564564 .for_input_not_captured => {
565 return bw.writeAll("for input is not captured");
565 return w.writeAll("for input is not captured");
566566 },
567567
568568 .invalid_byte => {
569569 const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..];
570 return bw.print("{s} contains invalid byte: '{f'}'", .{
570 return w.print("{s} contains invalid byte: '{f}'", .{
571571 switch (tok_slice[0]) {
572572 '\'' => "character literal",
573573 '"', '\\' => "string literal",
574574 '/' => "comment",
575575 else => unreachable,
576576 },
577 std.zig.fmtEscapes(tok_slice[parse_error.extra.offset..][0..1]),
577 std.zig.fmtChar(tok_slice[parse_error.extra.offset..][0..1]),
578578 });
579579 },
580580
......@@ -582,10 +582,10 @@ pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void
582582 const found_tag = tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev));
583583 const expected_symbol = parse_error.extra.expected_tag.symbol();
584584 switch (found_tag) {
585 .invalid => return bw.print("expected '{s}', found invalid bytes", .{
585 .invalid => return w.print("expected '{s}', found invalid bytes", .{
586586 expected_symbol,
587587 }),
588 else => return bw.print("expected '{s}', found '{s}'", .{
588 else => return w.print("expected '{s}', found '{s}'", .{
589589 expected_symbol, found_tag.symbol(),
590590 }),
591591 }
......@@ -608,7 +608,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
608608 .negation_wrap,
609609 .address_of,
610610 .@"try",
611 .@"await",
612611 .optional_type,
613612 .@"switch",
614613 .switch_comma,
......@@ -758,27 +757,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
758757 return i - end_offset;
759758 },
760759
761 .@"usingnamespace" => {
762 const main_token: TokenIndex = tree.nodeMainToken(n);
763 const has_visib_token = tree.isTokenPrecededByTags(main_token, &.{.keyword_pub});
764 end_offset += @intFromBool(has_visib_token);
765 return main_token - end_offset;
766 },
767
768 .async_call_one,
769 .async_call_one_comma,
770 => {
771 end_offset += 1; // async token
772 n = tree.nodeData(n).node_and_opt_node[0];
773 },
774
775 .async_call,
776 .async_call_comma,
777 => {
778 end_offset += 1; // async token
779 n = tree.nodeData(n).node_and_extra[0];
780 },
781
782760 .container_field_init,
783761 .container_field_align,
784762 .container_field,
......@@ -898,14 +876,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
898876 while (true) switch (tree.nodeTag(n)) {
899877 .root => return @intCast(tree.tokens.len - 1),
900878
901 .@"usingnamespace",
902879 .bool_not,
903880 .negation,
904881 .bit_not,
905882 .negation_wrap,
906883 .address_of,
907884 .@"try",
908 .@"await",
909885 .optional_type,
910886 .@"suspend",
911887 .@"resume",
......@@ -1024,7 +1000,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
10241000 };
10251001 },
10261002
1027 .call, .async_call => {
1003 .call => {
10281004 _, const extra_index = tree.nodeData(n).node_and_extra;
10291005 const params = tree.extraData(extra_index, Node.SubRange);
10301006 assert(params.start != params.end);
......@@ -1043,7 +1019,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
10431019 }
10441020 },
10451021 .call_comma,
1046 .async_call_comma,
10471022 .tagged_union_enum_tag_trailing,
10481023 => {
10491024 _, const extra_index = tree.nodeData(n).node_and_extra;
......@@ -1124,7 +1099,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
11241099 n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member
11251100 },
11261101 .call_one,
1127 .async_call_one,
11281102 => {
11291103 _, const first_param = tree.nodeData(n).node_and_opt_node;
11301104 end_offset += 1; // for the rparen
......@@ -1273,7 +1247,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
12731247 n = first_element;
12741248 },
12751249 .call_one_comma,
1276 .async_call_one_comma,
12771250 .struct_init_one_comma,
12781251 => {
12791252 _, const first_field = tree.nodeData(n).node_and_opt_node;
......@@ -1990,21 +1963,21 @@ pub fn forFull(tree: Ast, node: Node.Index) full.For {
19901963pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call {
19911964 const fn_expr, const first_param = tree.nodeData(node).node_and_opt_node;
19921965 const params = loadOptionalNodesIntoBuffer(1, buffer, .{first_param});
1993 return tree.fullCallComponents(.{
1966 return .{ .ast = .{
19941967 .lparen = tree.nodeMainToken(node),
19951968 .fn_expr = fn_expr,
19961969 .params = params,
1997 });
1970 } };
19981971}
19991972
20001973pub fn callFull(tree: Ast, node: Node.Index) full.Call {
20011974 const fn_expr, const extra_index = tree.nodeData(node).node_and_extra;
20021975 const params = tree.extraDataSlice(tree.extraData(extra_index, Node.SubRange), Node.Index);
2003 return tree.fullCallComponents(.{
1976 return .{ .ast = .{
20041977 .lparen = tree.nodeMainToken(node),
20051978 .fn_expr = fn_expr,
20061979 .params = params,
2007 });
1980 } };
20081981}
20091982
20101983fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl {
......@@ -2338,18 +2311,6 @@ fn fullForComponents(tree: Ast, info: full.For.Components) full.For {
23382311 return result;
23392312}
23402313
2341fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call {
2342 var result: full.Call = .{
2343 .ast = info,
2344 .async_token = null,
2345 };
2346 const first_token = tree.firstToken(info.fn_expr);
2347 if (tree.isTokenPrecededByTags(first_token, &.{.keyword_async})) {
2348 result.async_token = first_token - 1;
2349 }
2350 return result;
2351}
2352
23532314pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl {
23542315 return switch (tree.nodeTag(node)) {
23552316 .global_var_decl => tree.globalVarDecl(node),
......@@ -2490,8 +2451,8 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm {
24902451
24912452pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call {
24922453 return switch (tree.nodeTag(node)) {
2493 .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),
2494 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node),
2454 .call, .call_comma => tree.callFull(node),
2455 .call_one, .call_one_comma => tree.callOne(buffer, node),
24952456 else => null,
24962457 };
24972458}
......@@ -2884,7 +2845,6 @@ pub const full = struct {
28842845
28852846 pub const Call = struct {
28862847 ast: Components,
2887 async_token: ?TokenIndex,
28882848
28892849 pub const Components = struct {
28902850 lparen: TokenIndex,
......@@ -3067,12 +3027,6 @@ pub const Node = struct {
30673027 ///
30683028 /// The `main_token` field is the first token for the source file.
30693029 root,
3070 /// `usingnamespace expr;`.
3071 ///
3072 /// The `data` field is a `.node` to expr.
3073 ///
3074 /// The `main_token` field is the `usingnamespace` token.
3075 @"usingnamespace",
30763030 /// `test {}`,
30773031 /// `test "name" {}`,
30783032 /// `test identifier {}`.
......@@ -3303,8 +3257,6 @@ pub const Node = struct {
33033257 address_of,
33043258 /// `try expr`. The `main_token` field is the `try` token.
33053259 @"try",
3306 /// `await expr`. The `main_token` field is the `await` token.
3307 @"await",
33083260 /// `?expr`. The `main_token` field is the `?` token.
33093261 optional_type,
33103262 /// `[lhs]rhs`. The `main_token` field is the `[` token.
......@@ -3500,17 +3452,6 @@ pub const Node = struct {
35003452 /// Same as `call_one` except there is known to be a trailing comma
35013453 /// before the final rparen.
35023454 call_one_comma,
3503 /// `async a(b)`, `async a()`.
3504 ///
3505 /// The `data` field is a `.node_and_opt_node`:
3506 /// 1. a `Node.Index` to the function expression.
3507 /// 2. a `Node.OptionalIndex` to the first argument, if any.
3508 ///
3509 /// The `main_token` field is the `(` token.
3510 async_call_one,
3511 /// Same as `async_call_one` except there is known to be a trailing
3512 /// comma before the final rparen.
3513 async_call_one_comma,
35143455 /// `a(b, c, d)`.
35153456 ///
35163457 /// The `data` field is a `.node_and_extra`:
......@@ -3523,18 +3464,6 @@ pub const Node = struct {
35233464 /// Same as `call` except there is known to be a trailing comma before
35243465 /// the final rparen.
35253466 call_comma,
3526 /// `async a(b, c, d)`.
3527 ///
3528 /// The `data` field is a `.node_and_extra`:
3529 /// 1. a `Node.Index` to the function expression.
3530 /// 2. a `ExtraIndex` to a `SubRange` that stores a `Node.Index` for
3531 /// each argument.
3532 ///
3533 /// The `main_token` field is the `(` token.
3534 async_call,
3535 /// Same as `async_call` except there is known to be a trailing comma
3536 /// before the final rparen.
3537 async_call_comma,
35383467 /// `switch(a) {}`.
35393468 ///
35403469 /// The `data` field is a `.node_and_extra`:
lib/std/zig/Ast/Render.zig+3-23
......@@ -265,17 +265,6 @@ fn renderMember(
265265 return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon
266266 },
267267
268 .@"usingnamespace" => {
269 const main_token = tree.nodeMainToken(decl);
270 const expr = tree.nodeData(decl).node;
271 if (tree.isTokenPrecededByTags(main_token, &.{.keyword_pub})) {
272 try renderToken(r, main_token - 1, .space); // pub
273 }
274 try renderToken(r, main_token, .space); // usingnamespace
275 try renderExpression(r, expr, .none);
276 return renderToken(r, tree.lastToken(expr) + 1, space); // ;
277 },
278
279268 .global_var_decl,
280269 .local_var_decl,
281270 .simple_var_decl,
......@@ -594,7 +583,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
594583
595584 .@"try",
596585 .@"resume",
597 .@"await",
598586 => {
599587 try renderToken(r, tree.nodeMainToken(node), .space);
600588 return renderExpression(r, tree.nodeData(node).node, space);
......@@ -638,12 +626,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
638626
639627 .call_one,
640628 .call_one_comma,
641 .async_call_one,
642 .async_call_one_comma,
643629 .call,
644630 .call_comma,
645 .async_call,
646 .async_call_comma,
647631 => {
648632 var buf: [1]Ast.Node.Index = undefined;
649633 return renderCall(r, tree.fullCall(&buf, node).?, space);
......@@ -885,7 +869,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
885869 .local_var_decl => unreachable,
886870 .simple_var_decl => unreachable,
887871 .aligned_var_decl => unreachable,
888 .@"usingnamespace" => unreachable,
889872 .test_decl => unreachable,
890873 .asm_output => unreachable,
891874 .asm_input => unreachable,
......@@ -1584,7 +1567,7 @@ fn renderBuiltinCall(
15841567 defer r.gpa.free(new_string);
15851568
15861569 try renderToken(r, builtin_token + 1, .none); // (
1587 try ais.print("\"{f}\"", .{std.zig.fmtEscapes(new_string)});
1570 try ais.print("\"{f}\"", .{std.zig.fmtString(new_string)});
15881571 return renderToken(r, str_lit_token + 1, space); // )
15891572 }
15901573 }
......@@ -2556,9 +2539,6 @@ fn renderCall(
25562539 call: Ast.full.Call,
25572540 space: Space,
25582541) Error!void {
2559 if (call.async_token) |async_token| {
2560 try renderToken(r, async_token, .space);
2561 }
25622542 try renderExpression(r, call.ast.fn_expr, .none);
25632543 try renderParamList(r, call.ast.lparen, call.ast.params, space);
25642544}
......@@ -2897,7 +2877,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
28972877 .success => |codepoint| {
28982878 if (codepoint <= 0x7f) {
28992879 const buf = [1]u8{@as(u8, @intCast(codepoint))};
2900 try ais.print("{f}", .{std.zig.fmtEscapes(&buf)});
2880 try ais.print("{f}", .{std.zig.fmtString(&buf)});
29012881 } else {
29022882 try ais.writeAll(escape_sequence);
29032883 }
......@@ -2909,7 +2889,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
29092889 },
29102890 0x00...('\\' - 1), ('\\' + 1)...0x7f => {
29112891 const buf = [1]u8{byte};
2912 try ais.print("{f}", .{std.zig.fmtEscapes(&buf)});
2892 try ais.print("{f}", .{std.zig.fmtString(&buf)});
29132893 pos += 1;
29142894 },
29152895 0x80...0xff => {
lib/std/zig/AstGen.zig+3-164
......@@ -442,7 +442,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
442442 const tree = astgen.tree;
443443 switch (tree.nodeTag(node)) {
444444 .root => unreachable,
445 .@"usingnamespace" => unreachable,
446445 .test_decl => unreachable,
447446 .global_var_decl => unreachable,
448447 .local_var_decl => unreachable,
......@@ -510,12 +509,8 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
510509 .number_literal,
511510 .call,
512511 .call_comma,
513 .async_call,
514 .async_call_comma,
515512 .call_one,
516513 .call_one_comma,
517 .async_call_one,
518 .async_call_one_comma,
519514 .unreachable_literal,
520515 .@"return",
521516 .@"if",
......@@ -547,7 +542,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
547542 .merge_error_sets,
548543 .switch_range,
549544 .for_range,
550 .@"await",
551545 .bit_not,
552546 .negation,
553547 .negation_wrap,
......@@ -642,7 +636,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
642636
643637 switch (tree.nodeTag(node)) {
644638 .root => unreachable, // Top-level declaration.
645 .@"usingnamespace" => unreachable, // Top-level declaration.
646639 .test_decl => unreachable, // Top-level declaration.
647640 .container_field_init => unreachable, // Top-level declaration.
648641 .container_field_align => unreachable, // Top-level declaration.
......@@ -836,12 +829,8 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
836829
837830 .call_one,
838831 .call_one_comma,
839 .async_call_one,
840 .async_call_one_comma,
841832 .call,
842833 .call_comma,
843 .async_call,
844 .async_call_comma,
845834 => {
846835 var buf: [1]Ast.Node.Index = undefined;
847836 return callExpr(gz, scope, ri, .none, node, tree.fullCall(&buf, node).?);
......@@ -1114,7 +1103,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
11141103
11151104 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
11161105 .@"suspend" => return suspendExpr(gz, scope, node),
1117 .@"await" => return awaitExpr(gz, scope, ri, node),
11181106 .@"resume" => return resumeExpr(gz, scope, ri, node),
11191107
11201108 .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node),
......@@ -1259,33 +1247,6 @@ fn suspendExpr(
12591247 return suspend_inst.toRef();
12601248}
12611249
1262fn awaitExpr(
1263 gz: *GenZir,
1264 scope: *Scope,
1265 ri: ResultInfo,
1266 node: Ast.Node.Index,
1267) InnerError!Zir.Inst.Ref {
1268 const astgen = gz.astgen;
1269 const tree = astgen.tree;
1270 const rhs_node = tree.nodeData(node).node;
1271
1272 if (gz.suspend_node.unwrap()) |suspend_node| {
1273 return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
1274 try astgen.errNoteNode(suspend_node, "suspend block here", .{}),
1275 });
1276 }
1277 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
1278 const result = if (gz.nosuspend_node != .none)
1279 try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
1280 .node = gz.nodeIndexToRelative(node),
1281 .operand = operand,
1282 })
1283 else
1284 try gz.addUnNode(.@"await", operand, node);
1285
1286 return rvalue(gz, ri, result, node);
1287}
1288
12891250fn resumeExpr(
12901251 gz: *GenZir,
12911252 scope: *Scope,
......@@ -2853,7 +2814,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28532814 .tag_name,
28542815 .type_name,
28552816 .frame_type,
2856 .frame_size,
28572817 .int_from_float,
28582818 .float_from_int,
28592819 .ptr_from_int,
......@@ -2887,7 +2847,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28872847 .min,
28882848 .c_import,
28892849 .@"resume",
2890 .@"await",
28912850 .ret_err_value_code,
28922851 .ret_ptr,
28932852 .ret_type,
......@@ -4739,69 +4698,6 @@ fn comptimeDecl(
47394698 });
47404699}
47414700
4742fn usingnamespaceDecl(
4743 astgen: *AstGen,
4744 gz: *GenZir,
4745 scope: *Scope,
4746 wip_members: *WipMembers,
4747 node: Ast.Node.Index,
4748) InnerError!void {
4749 const tree = astgen.tree;
4750
4751 const old_hasher = astgen.src_hasher;
4752 defer astgen.src_hasher = old_hasher;
4753 astgen.src_hasher = std.zig.SrcHasher.init(.{});
4754 astgen.src_hasher.update(tree.getNodeSource(node));
4755 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
4756
4757 const type_expr = tree.nodeData(node).node;
4758 const is_pub = tree.isTokenPrecededByTags(tree.nodeMainToken(node), &.{.keyword_pub});
4759
4760 // Up top so the ZIR instruction index marks the start range of this
4761 // top-level declaration.
4762 const decl_inst = try gz.makeDeclaration(node);
4763 wip_members.nextDecl(decl_inst);
4764 astgen.advanceSourceCursorToNode(node);
4765
4766 // This is just needed for the `setDeclaration` call.
4767 var dummy_gz = gz.makeSubBlock(scope);
4768 defer dummy_gz.unstack();
4769
4770 var usingnamespace_gz: GenZir = .{
4771 .is_comptime = true,
4772 .decl_node_index = node,
4773 .decl_line = astgen.source_line,
4774 .parent = scope,
4775 .astgen = astgen,
4776 .instructions = gz.instructions,
4777 .instructions_top = gz.instructions.items.len,
4778 };
4779 defer usingnamespace_gz.unstack();
4780
4781 const decl_column = astgen.source_column;
4782
4783 const namespace_inst = try typeExpr(&usingnamespace_gz, &usingnamespace_gz.base, type_expr);
4784 _ = try usingnamespace_gz.addBreak(.break_inline, decl_inst, namespace_inst);
4785
4786 var hash: std.zig.SrcHash = undefined;
4787 astgen.src_hasher.final(&hash);
4788 try setDeclaration(decl_inst, .{
4789 .src_hash = hash,
4790 .src_line = usingnamespace_gz.decl_line,
4791 .src_column = decl_column,
4792 .kind = .@"usingnamespace",
4793 .name = .empty,
4794 .is_pub = is_pub,
4795 .is_threadlocal = false,
4796 .linkage = .normal,
4797 .type_gz = &dummy_gz,
4798 .align_gz = &dummy_gz,
4799 .linksection_gz = &dummy_gz,
4800 .addrspace_gz = &dummy_gz,
4801 .value_gz = &usingnamespace_gz,
4802 });
4803}
4804
48054701fn testDecl(
48064702 astgen: *AstGen,
48074703 gz: *GenZir,
......@@ -5971,23 +5867,6 @@ fn containerMember(
59715867 },
59725868 };
59735869 },
5974 .@"usingnamespace" => {
5975 const prev_decl_index = wip_members.decl_index;
5976 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5977 error.OutOfMemory => return error.OutOfMemory,
5978 error.AnalysisFail => {
5979 wip_members.decl_index = prev_decl_index;
5980 try addFailedDeclaration(
5981 wip_members,
5982 gz,
5983 .@"usingnamespace",
5984 .empty,
5985 member_node,
5986 tree.isTokenPrecededByTags(tree.nodeMainToken(member_node), &.{.keyword_pub}),
5987 );
5988 },
5989 };
5990 },
59915870 .test_decl => {
59925871 const prev_decl_index = wip_members.decl_index;
59935872 // We need to have *some* decl here so that the decl count matches what's expected.
......@@ -9501,7 +9380,6 @@ fn builtinCall(
95019380 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
95029381 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
95039382 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
9504 .frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size),
95059383
95069384 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
95079385 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
......@@ -9767,16 +9645,6 @@ fn builtinCall(
97679645 });
97689646 return rvalue(gz, ri, result, node);
97699647 },
9770 .async_call => {
9771 const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
9772 .node = gz.nodeIndexToRelative(node),
9773 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
9774 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9775 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
9776 .args = try expr(gz, scope, .{ .rl = .none }, params[3]),
9777 });
9778 return rvalue(gz, ri, result, node);
9779 },
97809648 .Vector => {
97819649 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
97829650 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type),
......@@ -10175,11 +10043,8 @@ fn callExpr(
1017510043
1017610044 const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr);
1017710045 const modifier: std.builtin.CallModifier = blk: {
10178 if (call.async_token != null) {
10179 break :blk .async_kw;
10180 }
1018110046 if (gz.nosuspend_node != .none) {
10182 break :blk .no_async;
10047 break :blk .no_suspend;
1018310048 }
1018410049 break :blk .auto;
1018510050 };
......@@ -10451,7 +10316,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
1045110316 while (true) {
1045210317 switch (tree.nodeTag(node)) {
1045310318 .root,
10454 .@"usingnamespace",
1045510319 .test_decl,
1045610320 .switch_case,
1045710321 .switch_case_inline,
......@@ -10483,12 +10347,8 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
1048310347 .switch_comma,
1048410348 .call_one,
1048510349 .call_one_comma,
10486 .async_call_one,
10487 .async_call_one_comma,
1048810350 .call,
1048910351 .call_comma,
10490 .async_call,
10491 .async_call_comma,
1049210352 => return .maybe,
1049310353
1049410354 .@"return",
......@@ -10613,7 +10473,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
1061310473
1061410474 // Forward the question to the LHS sub-expression.
1061510475 .@"try",
10616 .@"await",
1061710476 .@"comptime",
1061810477 .@"nosuspend",
1061910478 => node = tree.nodeData(node).node,
......@@ -10664,7 +10523,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
1066410523 while (true) {
1066510524 switch (tree.nodeTag(node)) {
1066610525 .root,
10667 .@"usingnamespace",
1066810526 .test_decl,
1066910527 .switch_case,
1067010528 .switch_case_inline,
......@@ -10803,12 +10661,8 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
1080310661 .switch_comma,
1080410662 .call_one,
1080510663 .call_one_comma,
10806 .async_call_one,
10807 .async_call_one_comma,
1080810664 .call,
1080910665 .call_comma,
10810 .async_call,
10811 .async_call_comma,
1081210666 .block_two,
1081310667 .block_two_semicolon,
1081410668 .block,
......@@ -10826,7 +10680,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
1082610680
1082710681 // Forward the question to the LHS sub-expression.
1082810682 .@"try",
10829 .@"await",
1083010683 .@"comptime",
1083110684 .@"nosuspend",
1083210685 => node = tree.nodeData(node).node,
......@@ -10908,7 +10761,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
1090810761 while (true) {
1090910762 switch (tree.nodeTag(node)) {
1091010763 .root,
10911 .@"usingnamespace",
1091210764 .test_decl,
1091310765 .switch_case,
1091410766 .switch_case_inline,
......@@ -11047,12 +10899,8 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
1104710899 .switch_comma,
1104810900 .call_one,
1104910901 .call_one_comma,
11050 .async_call_one,
11051 .async_call_one_comma,
1105210902 .call,
1105310903 .call_comma,
11054 .async_call,
11055 .async_call_comma,
1105610904 .block_two,
1105710905 .block_two_semicolon,
1105810906 .block,
......@@ -11079,7 +10927,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
1107910927
1108010928 // Forward the question to the LHS sub-expression.
1108110929 .@"try",
11082 .@"await",
1108310930 .@"comptime",
1108410931 .@"nosuspend",
1108510932 => node = tree.nodeData(node).node,
......@@ -11462,13 +11309,7 @@ fn failWithStrLitError(
1146211309 offset: u32,
1146311310) InnerError {
1146411311 const raw_string = bytes[offset..];
11465 return failOff(
11466 astgen,
11467 token,
11468 @intCast(offset + err.offset()),
11469 "{f}",
11470 .{err.fmt(raw_string)},
11471 );
11312 return failOff(astgen, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)});
1147211313}
1147311314
1147411315fn failNode(
......@@ -13591,7 +13432,7 @@ fn scanContainer(
1359113432 break :blk .{ .decl, ident };
1359213433 },
1359313434
13594 .@"comptime", .@"usingnamespace" => {
13435 .@"comptime" => {
1359513436 decl_count += 1;
1359613437 continue;
1359713438 },
......@@ -13970,7 +13811,6 @@ const DeclarationName = union(enum) {
1397013811 decltest: Ast.TokenIndex,
1397113812 unnamed_test,
1397213813 @"comptime",
13973 @"usingnamespace",
1397413814};
1397513815
1397613816fn addFailedDeclaration(
......@@ -14060,7 +13900,6 @@ fn setDeclaration(
1406013900 .@"test" => .@"test",
1406113901 .decltest => .decltest,
1406213902 .@"comptime" => .@"comptime",
14063 .@"usingnamespace" => if (args.is_pub) .pub_usingnamespace else .@"usingnamespace",
1406413903 .@"const" => switch (args.linkage) {
1406513904 .normal => if (args.is_pub) id: {
1406613905 if (has_special_body) break :id .pub_const;
lib/std/zig/AstRlAnnotate.zig-22
......@@ -165,10 +165,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
165165 }
166166 return false;
167167 },
168 .@"usingnamespace" => {
169 _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.type_only);
170 return false;
171 },
172168 .test_decl => {
173169 _ = try astrl.expr(tree.nodeData(node).opt_token_and_node[1], block, ResultInfo.none);
174170 return false;
......@@ -334,12 +330,8 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
334330
335331 .call_one,
336332 .call_one_comma,
337 .async_call_one,
338 .async_call_one_comma,
339333 .call,
340334 .call_comma,
341 .async_call,
342 .async_call_comma,
343335 => {
344336 var buf: [1]Ast.Node.Index = undefined;
345337 const full = tree.fullCall(&buf, node).?;
......@@ -353,11 +345,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
353345 .call,
354346 .call_comma,
355347 => false, // TODO: once function calls are passed result locations this will change
356 .async_call_one,
357 .async_call_one_comma,
358 .async_call,
359 .async_call_comma,
360 => ri.have_ptr, // always use result ptr for frames
361348 else => unreachable,
362349 };
363350 },
......@@ -503,7 +490,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
503490 return false;
504491 },
505492 .@"try",
506 .@"await",
507493 .@"nosuspend",
508494 => return astrl.expr(tree.nodeData(node).node, block, ri),
509495 .grouped_expression,
......@@ -948,7 +934,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
948934 .tag_name,
949935 .type_name,
950936 .Frame,
951 .frame_size,
952937 .int_from_float,
953938 .float_from_int,
954939 .ptr_from_int,
......@@ -1079,13 +1064,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
10791064 _ = try astrl.expr(args[3], block, ResultInfo.none);
10801065 return false;
10811066 },
1082 .async_call => {
1083 _ = try astrl.expr(args[0], block, ResultInfo.none);
1084 _ = try astrl.expr(args[1], block, ResultInfo.none);
1085 _ = try astrl.expr(args[2], block, ResultInfo.none);
1086 _ = try astrl.expr(args[3], block, ResultInfo.none);
1087 return false; // buffer passed as arg for frame data
1088 },
10891067 .Vector => {
10901068 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
10911069 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig-16
......@@ -4,7 +4,6 @@ pub const Tag = enum {
44 align_cast,
55 align_of,
66 as,
7 async_call,
87 atomic_load,
98 atomic_rmw,
109 atomic_store,
......@@ -55,7 +54,6 @@ pub const Tag = enum {
5554 frame,
5655 Frame,
5756 frame_address,
58 frame_size,
5957 has_decl,
6058 has_field,
6159 import,
......@@ -184,13 +182,6 @@ pub const list = list: {
184182 .param_count = 2,
185183 },
186184 },
187 .{
188 "@asyncCall",
189 .{
190 .tag = .async_call,
191 .param_count = 4,
192 },
193 },
194185 .{
195186 "@atomicLoad",
196187 .{
......@@ -550,13 +541,6 @@ pub const list = list: {
550541 .illegal_outside_function = true,
551542 },
552543 },
553 .{
554 "@frameSize",
555 .{
556 .tag = .frame_size,
557 .param_count = 1,
558 },
559 },
560544 .{
561545 "@hasDecl",
562546 .{
lib/std/zig/ErrorBundle.zig+64-56
......@@ -164,22 +164,22 @@ pub const RenderOptions = struct {
164164
165165pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
166166 var buffer: [256]u8 = undefined;
167 const bw = std.debug.lockStderrWriter(&buffer);
167 const w = std.debug.lockStderrWriter(&buffer);
168168 defer std.debug.unlockStderrWriter();
169 renderToWriter(eb, options, bw) catch return;
169 renderToWriter(eb, options, w) catch return;
170170}
171171
172pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *Writer) (Writer.Error || std.posix.UnexpectedError)!void {
172pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) (Writer.Error || std.posix.UnexpectedError)!void {
173173 if (eb.extra.len == 0) return;
174174 for (eb.getMessages()) |err_msg| {
175 try renderErrorMessageToWriter(eb, options, err_msg, bw, "error", .red, 0);
175 try renderErrorMessageToWriter(eb, options, err_msg, w, "error", .red, 0);
176176 }
177177
178178 if (options.include_log_text) {
179179 const log_text = eb.getCompileLogOutput();
180180 if (log_text.len != 0) {
181 try bw.writeAll("\nCompile Log Output:\n");
182 try bw.writeAll(log_text);
181 try w.writeAll("\nCompile Log Output:\n");
182 try w.writeAll(log_text);
183183 }
184184 }
185185}
......@@ -188,73 +188,81 @@ fn renderErrorMessageToWriter(
188188 eb: ErrorBundle,
189189 options: RenderOptions,
190190 err_msg_index: MessageIndex,
191 bw: *Writer,
191 w: *Writer,
192192 kind: []const u8,
193193 color: std.io.tty.Color,
194194 indent: usize,
195195) (Writer.Error || std.posix.UnexpectedError)!void {
196196 const ttyconf = options.ttyconf;
197197 const err_msg = eb.getErrorMessage(err_msg_index);
198 const prefix_start = bw.count;
199198 if (err_msg.src_loc != .none) {
200199 const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));
201 try bw.splatByteAll(' ', indent);
202 try ttyconf.setColor(bw, .bold);
203 try bw.print("{s}:{d}:{d}: ", .{
200 var prefix: std.io.Writer.Discarding = .init(&.{});
201 try w.splatByteAll(' ', indent);
202 prefix.count += indent;
203 try ttyconf.setColor(w, .bold);
204 try w.print("{s}:{d}:{d}: ", .{
204205 eb.nullTerminatedString(src.data.src_path),
205206 src.data.line + 1,
206207 src.data.column + 1,
207208 });
208 try ttyconf.setColor(bw, color);
209 try bw.writeAll(kind);
210 try bw.writeAll(": ");
209 try prefix.writer.print("{s}:{d}:{d}: ", .{
210 eb.nullTerminatedString(src.data.src_path),
211 src.data.line + 1,
212 src.data.column + 1,
213 });
214 try ttyconf.setColor(w, color);
215 try w.writeAll(kind);
216 prefix.count += kind.len;
217 try w.writeAll(": ");
218 prefix.count += 2;
211219 // This is the length of the part before the error message:
212220 // e.g. "file.zig:4:5: error: "
213 const prefix_len = bw.count - prefix_start;
214 try ttyconf.setColor(bw, .reset);
215 try ttyconf.setColor(bw, .bold);
221 const prefix_len: usize = @intCast(prefix.count);
222 try ttyconf.setColor(w, .reset);
223 try ttyconf.setColor(w, .bold);
216224 if (err_msg.count == 1) {
217 try writeMsg(eb, err_msg, bw, prefix_len);
218 try bw.writeByte('\n');
225 try writeMsg(eb, err_msg, w, prefix_len);
226 try w.writeByte('\n');
219227 } else {
220 try writeMsg(eb, err_msg, bw, prefix_len);
221 try ttyconf.setColor(bw, .dim);
222 try bw.print(" ({d} times)\n", .{err_msg.count});
228 try writeMsg(eb, err_msg, w, prefix_len);
229 try ttyconf.setColor(w, .dim);
230 try w.print(" ({d} times)\n", .{err_msg.count});
223231 }
224 try ttyconf.setColor(bw, .reset);
232 try ttyconf.setColor(w, .reset);
225233 if (src.data.source_line != 0 and options.include_source_line) {
226234 const line = eb.nullTerminatedString(src.data.source_line);
227235 for (line) |b| switch (b) {
228 '\t' => try bw.writeByte(' '),
229 else => try bw.writeByte(b),
236 '\t' => try w.writeByte(' '),
237 else => try w.writeByte(b),
230238 };
231 try bw.writeByte('\n');
239 try w.writeByte('\n');
232240 // TODO basic unicode code point monospace width
233241 const before_caret = src.data.span_main - src.data.span_start;
234242 // -1 since span.main includes the caret
235243 const after_caret = src.data.span_end -| src.data.span_main -| 1;
236 try bw.splatByteAll(' ', src.data.column - before_caret);
237 try ttyconf.setColor(bw, .green);
238 try bw.splatByteAll('~', before_caret);
239 try bw.writeByte('^');
240 try bw.splatByteAll('~', after_caret);
241 try bw.writeByte('\n');
242 try ttyconf.setColor(bw, .reset);
244 try w.splatByteAll(' ', src.data.column - before_caret);
245 try ttyconf.setColor(w, .green);
246 try w.splatByteAll('~', before_caret);
247 try w.writeByte('^');
248 try w.splatByteAll('~', after_caret);
249 try w.writeByte('\n');
250 try ttyconf.setColor(w, .reset);
243251 }
244252 for (eb.getNotes(err_msg_index)) |note| {
245 try renderErrorMessageToWriter(eb, options, note, bw, "note", .cyan, indent);
253 try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent);
246254 }
247255 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
248 try ttyconf.setColor(bw, .reset);
249 try ttyconf.setColor(bw, .dim);
250 try bw.print("referenced by:\n", .{});
256 try ttyconf.setColor(w, .reset);
257 try ttyconf.setColor(w, .dim);
258 try w.print("referenced by:\n", .{});
251259 var ref_index = src.end;
252260 for (0..src.data.reference_trace_len) |_| {
253261 const ref_trace = eb.extraData(ReferenceTrace, ref_index);
254262 ref_index = ref_trace.end;
255263 if (ref_trace.data.src_loc != .none) {
256264 const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);
257 try bw.print(" {s}: {s}:{d}:{d}\n", .{
265 try w.print(" {s}: {s}:{d}:{d}\n", .{
258266 eb.nullTerminatedString(ref_trace.data.decl_name),
259267 eb.nullTerminatedString(ref_src.src_path),
260268 ref_src.line + 1,
......@@ -262,36 +270,36 @@ fn renderErrorMessageToWriter(
262270 });
263271 } else if (ref_trace.data.decl_name != 0) {
264272 const count = ref_trace.data.decl_name;
265 try bw.print(
273 try w.print(
266274 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
267275 .{ count, count + src.data.reference_trace_len - 1 },
268276 );
269277 } else {
270 try bw.print(
278 try w.print(
271279 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
272280 .{},
273281 );
274282 }
275283 }
276 try ttyconf.setColor(bw, .reset);
284 try ttyconf.setColor(w, .reset);
277285 }
278286 } else {
279 try ttyconf.setColor(bw, color);
280 try bw.splatByteAll(' ', indent);
281 try bw.writeAll(kind);
282 try bw.writeAll(": ");
283 try ttyconf.setColor(bw, .reset);
287 try ttyconf.setColor(w, color);
288 try w.splatByteAll(' ', indent);
289 try w.writeAll(kind);
290 try w.writeAll(": ");
291 try ttyconf.setColor(w, .reset);
284292 const msg = eb.nullTerminatedString(err_msg.msg);
285293 if (err_msg.count == 1) {
286 try bw.print("{s}\n", .{msg});
294 try w.print("{s}\n", .{msg});
287295 } else {
288 try bw.print("{s}", .{msg});
289 try ttyconf.setColor(bw, .dim);
290 try bw.print(" ({d} times)\n", .{err_msg.count});
296 try w.print("{s}", .{msg});
297 try ttyconf.setColor(w, .dim);
298 try w.print(" ({d} times)\n", .{err_msg.count});
291299 }
292 try ttyconf.setColor(bw, .reset);
300 try ttyconf.setColor(w, .reset);
293301 for (eb.getNotes(err_msg_index)) |note| {
294 try renderErrorMessageToWriter(eb, options, note, bw, "note", .cyan, indent + 4);
302 try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent + 4);
295303 }
296304 }
297305}
......@@ -300,13 +308,13 @@ fn renderErrorMessageToWriter(
300308/// to allow for long, good-looking error messages.
301309///
302310/// This is used to split the message in `@compileError("hello\nworld")` for example.
303fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, bw: *Writer, indent: usize) !void {
311fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, w: *Writer, indent: usize) !void {
304312 var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');
305313 while (lines.next()) |line| {
306 try bw.writeAll(line);
314 try w.writeAll(line);
307315 if (lines.index == null) break;
308 try bw.writeByte('\n');
309 try bw.splatByteAll(' ', indent);
316 try w.writeByte('\n');
317 try w.splatByteAll(' ', indent);
310318 }
311319}
312320
lib/std/zig/Parse.zig+2-90
......@@ -359,16 +359,6 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members {
359359 }
360360 trailing = p.tokenTag(p.tok_i - 1) == .semicolon;
361361 },
362 .keyword_usingnamespace => {
363 const opt_node = try p.expectUsingNamespaceRecoverable();
364 if (opt_node) |node| {
365 if (field_state == .seen) {
366 field_state = .{ .end = node };
367 }
368 try p.scratch.append(p.gpa, node);
369 }
370 trailing = p.tokenTag(p.tok_i - 1) == .semicolon;
371 },
372362 .keyword_const,
373363 .keyword_var,
374364 .keyword_threadlocal,
......@@ -496,7 +486,6 @@ fn findNextContainerMember(p: *Parse) void {
496486 .keyword_extern,
497487 .keyword_inline,
498488 .keyword_noinline,
499 .keyword_usingnamespace,
500489 .keyword_threadlocal,
501490 .keyword_const,
502491 .keyword_var,
......@@ -601,7 +590,6 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
601590/// Decl
602591/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
603592/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
604/// / KEYWORD_usingnamespace Expr SEMICOLON
605593fn expectTopLevelDecl(p: *Parse) !?Node.Index {
606594 const extern_export_inline_token = p.nextToken();
607595 var is_extern: bool = false;
......@@ -664,10 +652,7 @@ fn expectTopLevelDecl(p: *Parse) !?Node.Index {
664652 if (expect_var_or_fn) {
665653 return p.fail(.expected_var_decl_or_fn);
666654 }
667 if (p.tokenTag(p.tok_i) != .keyword_usingnamespace) {
668 return p.fail(.expected_pub_item);
669 }
670 return try p.expectUsingNamespace();
655 return p.fail(.expected_pub_item);
671656}
672657
673658fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
......@@ -680,27 +665,6 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
680665 };
681666}
682667
683fn expectUsingNamespace(p: *Parse) !Node.Index {
684 const usingnamespace_token = p.assertToken(.keyword_usingnamespace);
685 const expr = try p.expectExpr();
686 try p.expectSemicolon(.expected_semi_after_decl, false);
687 return p.addNode(.{
688 .tag = .@"usingnamespace",
689 .main_token = usingnamespace_token,
690 .data = .{ .node = expr },
691 });
692}
693
694fn expectUsingNamespaceRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
695 return p.expectUsingNamespace() catch |err| switch (err) {
696 error.OutOfMemory => return error.OutOfMemory,
697 error.ParseError => {
698 p.findNextContainerMember();
699 return null;
700 },
701 };
702}
703
704668/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
705669fn parseFnProto(p: *Parse) !?Node.Index {
706670 const fn_token = p.eatToken(.keyword_fn) orelse return null;
......@@ -1688,7 +1652,6 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index {
16881652/// / MINUSPERCENT
16891653/// / AMPERSAND
16901654/// / KEYWORD_try
1691/// / KEYWORD_await
16921655fn parsePrefixExpr(p: *Parse) Error!?Node.Index {
16931656 const tag: Node.Tag = switch (p.tokenTag(p.tok_i)) {
16941657 .bang => .bool_not,
......@@ -1697,7 +1660,6 @@ fn parsePrefixExpr(p: *Parse) Error!?Node.Index {
16971660 .minus_percent => .negation_wrap,
16981661 .ampersand => .address_of,
16991662 .keyword_try => .@"try",
1700 .keyword_await => .@"await",
17011663 else => return p.parsePrimaryExpr(),
17021664 };
17031665 return try p.addNode(.{
......@@ -2385,62 +2347,12 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index {
23852347}
23862348
23872349/// SuffixExpr
2388/// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
2389/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
2350/// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
23902351///
23912352/// FnCallArguments <- LPAREN ExprList RPAREN
23922353///
23932354/// ExprList <- (Expr COMMA)* Expr?
23942355fn parseSuffixExpr(p: *Parse) !?Node.Index {
2395 if (p.eatToken(.keyword_async)) |_| {
2396 var res = try p.expectPrimaryTypeExpr();
2397 while (true) {
2398 res = try p.parseSuffixOp(res) orelse break;
2399 }
2400 const lparen = p.eatToken(.l_paren) orelse {
2401 try p.warn(.expected_param_list);
2402 return res;
2403 };
2404 const scratch_top = p.scratch.items.len;
2405 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2406 while (true) {
2407 if (p.eatToken(.r_paren)) |_| break;
2408 const param = try p.expectExpr();
2409 try p.scratch.append(p.gpa, param);
2410 switch (p.tokenTag(p.tok_i)) {
2411 .comma => p.tok_i += 1,
2412 .r_paren => {
2413 p.tok_i += 1;
2414 break;
2415 },
2416 .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
2417 // Likely just a missing comma; give error but continue parsing.
2418 else => try p.warn(.expected_comma_after_arg),
2419 }
2420 }
2421 const comma = (p.tokenTag(p.tok_i - 2)) == .comma;
2422 const params = p.scratch.items[scratch_top..];
2423 if (params.len <= 1) {
2424 return try p.addNode(.{
2425 .tag = if (comma) .async_call_one_comma else .async_call_one,
2426 .main_token = lparen,
2427 .data = .{ .node_and_opt_node = .{
2428 res,
2429 if (params.len >= 1) params[0].toOptional() else .none,
2430 } },
2431 });
2432 } else {
2433 return try p.addNode(.{
2434 .tag = if (comma) .async_call_comma else .async_call,
2435 .main_token = lparen,
2436 .data = .{ .node_and_extra = .{
2437 res,
2438 try p.addExtra(try p.listToSpan(params)),
2439 } },
2440 });
2441 }
2442 }
2443
24442356 var res = try p.parsePrimaryTypeExpr() orelse return null;
24452357 while (true) {
24462358 const opt_suffix_op = try p.parseSuffixOp(res);
lib/std/zig/Zir.zig+4-43
......@@ -899,8 +899,6 @@ pub const Inst = struct {
899899 type_name,
900900 /// Implement builtin `@Frame`. Uses `un_node`.
901901 frame_type,
902 /// Implement builtin `@frameSize`. Uses `un_node`.
903 frame_size,
904902
905903 /// Implements the `@intFromFloat` builtin.
906904 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
......@@ -1044,7 +1042,6 @@ pub const Inst = struct {
10441042
10451043 /// Implements `resume` syntax. Uses `un_node` field.
10461044 @"resume",
1047 @"await",
10481045
10491046 /// A defer statement.
10501047 /// Uses the `defer` union field.
......@@ -1241,7 +1238,6 @@ pub const Inst = struct {
12411238 .tag_name,
12421239 .type_name,
12431240 .frame_type,
1244 .frame_size,
12451241 .int_from_float,
12461242 .float_from_int,
12471243 .ptr_from_int,
......@@ -1279,7 +1275,6 @@ pub const Inst = struct {
12791275 .min,
12801276 .c_import,
12811277 .@"resume",
1282 .@"await",
12831278 .ret_err_value_code,
12841279 .extended,
12851280 .ret_ptr,
......@@ -1526,7 +1521,6 @@ pub const Inst = struct {
15261521 .tag_name,
15271522 .type_name,
15281523 .frame_type,
1529 .frame_size,
15301524 .int_from_float,
15311525 .float_from_int,
15321526 .ptr_from_int,
......@@ -1560,7 +1554,6 @@ pub const Inst = struct {
15601554 .min,
15611555 .c_import,
15621556 .@"resume",
1563 .@"await",
15641557 .ret_err_value_code,
15651558 .@"break",
15661559 .break_inline,
......@@ -1791,7 +1784,6 @@ pub const Inst = struct {
17911784 .tag_name = .un_node,
17921785 .type_name = .un_node,
17931786 .frame_type = .un_node,
1794 .frame_size = .un_node,
17951787
17961788 .int_from_float = .pl_node,
17971789 .float_from_int = .pl_node,
......@@ -1852,7 +1844,6 @@ pub const Inst = struct {
18521844 .make_ptr_const = .un_node,
18531845
18541846 .@"resume" = .un_node,
1855 .@"await" = .un_node,
18561847
18571848 .@"defer" = .@"defer",
18581849 .defer_err_code = .defer_err_code,
......@@ -2016,8 +2007,6 @@ pub const Inst = struct {
20162007 /// Implements the `@errorCast` builtin.
20172008 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
20182009 error_cast,
2019 /// `operand` is payload index to `UnNode`.
2020 await_nosuspend,
20212010 /// Implements `@breakpoint`.
20222011 /// `operand` is `src_node: Ast.Node.Offset`.
20232012 breakpoint,
......@@ -2038,9 +2027,6 @@ pub const Inst = struct {
20382027 /// `operand` is payload index to `Reify`.
20392028 /// `small` contains `NameStrategy`.
20402029 reify,
2041 /// Implements the `@asyncCall` builtin.
2042 /// `operand` is payload index to `AsyncCall`.
2043 builtin_async_call,
20442030 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
20452031 /// `small` 0=>weak 1=>strong
20462032 /// `operand` is payload index to `Cmpxchg`.
......@@ -2689,7 +2675,6 @@ pub const Inst = struct {
26892675 @"test",
26902676 decltest,
26912677 @"comptime",
2692 @"usingnamespace",
26932678 @"const",
26942679 @"var",
26952680 };
......@@ -2706,7 +2691,7 @@ pub const Inst = struct {
27062691 src_column: u32,
27072692
27082693 kind: Kind,
2709 /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`.
2694 /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`
27102695 name: NullTerminatedString,
27112696 /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`.
27122697 is_pub: bool,
......@@ -2737,9 +2722,6 @@ pub const Inst = struct {
27372722 decltest,
27382723 @"comptime",
27392724
2740 @"usingnamespace",
2741 pub_usingnamespace,
2742
27432725 const_simple,
27442726 const_typed,
27452727 @"const",
......@@ -2776,8 +2758,6 @@ pub const Inst = struct {
27762758 return switch (id) {
27772759 .unnamed_test,
27782760 .@"comptime",
2779 .@"usingnamespace",
2780 .pub_usingnamespace,
27812761 => false,
27822762 else => true,
27832763 };
......@@ -2802,8 +2782,6 @@ pub const Inst = struct {
28022782 .@"test",
28032783 .decltest,
28042784 .@"comptime",
2805 .@"usingnamespace",
2806 .pub_usingnamespace,
28072785 => false, // these constructs are untyped
28082786 .const_simple,
28092787 .pub_const_simple,
......@@ -2835,8 +2813,6 @@ pub const Inst = struct {
28352813 .@"test",
28362814 .decltest,
28372815 .@"comptime",
2838 .@"usingnamespace",
2839 .pub_usingnamespace,
28402816 => false, // these constructs are untyped
28412817 .const_simple,
28422818 .const_typed,
......@@ -2879,7 +2855,6 @@ pub const Inst = struct {
28792855 .@"test" => .@"test",
28802856 .decltest => .decltest,
28812857 .@"comptime" => .@"comptime",
2882 .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace",
28832858 .const_simple,
28842859 .const_typed,
28852860 .@"const",
......@@ -2913,7 +2888,6 @@ pub const Inst = struct {
29132888
29142889 pub fn isPub(id: Id) bool {
29152890 return switch (id) {
2916 .pub_usingnamespace,
29172891 .pub_const_simple,
29182892 .pub_const_typed,
29192893 .pub_const,
......@@ -2949,8 +2923,7 @@ pub const Inst = struct {
29492923
29502924 pub const Name = enum(u32) {
29512925 @"comptime" = std.math.maxInt(u32),
2952 @"usingnamespace" = std.math.maxInt(u32) - 1,
2953 unnamed_test = std.math.maxInt(u32) - 2,
2926 unnamed_test = std.math.maxInt(u32) - 1,
29542927 /// Other values are `NullTerminatedString` values, i.e. index into
29552928 /// `string_bytes`. If the byte referenced is 0, the decl is a named
29562929 /// test, and the actual name begins at the following byte.
......@@ -2958,13 +2931,13 @@ pub const Inst = struct {
29582931
29592932 pub fn isNamedTest(name: Name, zir: Zir) bool {
29602933 return switch (name) {
2961 .@"comptime", .@"usingnamespace", .unnamed_test => false,
2934 .@"comptime", .unnamed_test => false,
29622935 _ => zir.string_bytes[@intFromEnum(name)] == 0,
29632936 };
29642937 }
29652938 pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {
29662939 switch (name) {
2967 .@"comptime", .@"usingnamespace", .unnamed_test => return null,
2940 .@"comptime", .unnamed_test => return null,
29682941 _ => {},
29692942 }
29702943 const idx: u32 = @intFromEnum(name);
......@@ -3771,14 +3744,6 @@ pub const Inst = struct {
37713744 b: Ref,
37723745 };
37733746
3774 pub const AsyncCall = struct {
3775 node: Ast.Node.Offset,
3776 frame_buffer: Ref,
3777 result_ptr: Ref,
3778 fn_ptr: Ref,
3779 args: Ref,
3780 };
3781
37823747 /// Trailing: inst: Index // for every body_len
37833748 pub const Param = struct {
37843749 /// Null-terminated string index.
......@@ -4297,7 +4262,6 @@ fn findTrackableInner(
42974262 .tag_name,
42984263 .type_name,
42994264 .frame_type,
4300 .frame_size,
43014265 .int_from_float,
43024266 .float_from_int,
43034267 .ptr_from_int,
......@@ -4337,7 +4301,6 @@ fn findTrackableInner(
43374301 .resolve_inferred_alloc,
43384302 .make_ptr_const,
43394303 .@"resume",
4340 .@"await",
43414304 .save_err_ret_index,
43424305 .restore_err_ret_index_unconditional,
43434306 .restore_err_ret_index_fn_entry,
......@@ -4380,14 +4343,12 @@ fn findTrackableInner(
43804343 .prefetch,
43814344 .set_float_mode,
43824345 .error_cast,
4383 .await_nosuspend,
43844346 .breakpoint,
43854347 .disable_instrumentation,
43864348 .disable_intrinsics,
43874349 .select,
43884350 .int_from_error,
43894351 .error_from_int,
4390 .builtin_async_call,
43914352 .cmpxchg,
43924353 .c_va_arg,
43934354 .c_va_copy,
lib/std/zig/ZonGen.zig+1-13
......@@ -100,7 +100,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
100100
101101 switch (tree.nodeTag(node)) {
102102 .root => unreachable,
103 .@"usingnamespace" => unreachable,
104103 .test_decl => unreachable,
105104 .container_field_init => unreachable,
106105 .container_field_align => unreachable,
......@@ -204,12 +203,8 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
204203
205204 .call_one,
206205 .call_one_comma,
207 .async_call_one,
208 .async_call_one_comma,
209206 .call,
210207 .call_comma,
211 .async_call,
212 .async_call_comma,
213208 .@"return",
214209 .if_simple,
215210 .@"if",
......@@ -226,7 +221,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
226221 .switch_comma,
227222 .@"nosuspend",
228223 .@"suspend",
229 .@"await",
230224 .@"resume",
231225 .@"try",
232226 .unreachable_literal,
......@@ -776,13 +770,7 @@ fn lowerStrLitError(
776770 raw_string: []const u8,
777771 offset: u32,
778772) Allocator.Error!void {
779 return ZonGen.addErrorTokOff(
780 zg,
781 token,
782 @intCast(offset + err.offset()),
783 "{f}",
784 .{err.fmt(raw_string)},
785 );
773 return ZonGen.addErrorTokOff(zg, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)});
786774}
787775
788776fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void {
lib/std/zig/llvm/Builder.zig+710-557
......@@ -1,3 +1,14 @@
1const std = @import("../../std.zig");
2const Allocator = std.mem.Allocator;
3const assert = std.debug.assert;
4const bitcode_writer = @import("bitcode_writer.zig");
5const Builder = @This();
6const builtin = @import("builtin");
7const DW = std.dwarf;
8const ir = @import("ir.zig");
9const log = std.log.scoped(.llvm);
10const Writer = std.io.Writer;
11
112gpa: Allocator,
213strip: bool,
314
......@@ -90,26 +101,38 @@ pub const String = enum(u32) {
90101 const FormatData = struct {
91102 string: String,
92103 builder: *const Builder,
104 quote_behavior: ?QuoteBehavior,
93105 };
94 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
95 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
96 @compileError("invalid format string: '" ++ fmt_str ++ "'");
106 fn format(data: FormatData, w: *Writer) Writer.Error!void {
97107 assert(data.string != .none);
98108 const string_slice = data.string.slice(data.builder) orelse
99 return bw.print("{d}", .{@intFromEnum(data.string)});
100 if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_|
101 return bw.writeAll(string_slice);
102 try printEscapedString(
103 string_slice,
104 if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_|
105 .always_quote
106 else
107 .quote_unless_valid_identifier,
108 bw,
109 );
109 return w.print("{d}", .{@intFromEnum(data.string)});
110 const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice);
111 return printEscapedString(string_slice, quote_behavior, w);
112 }
113
114 pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) {
115 return .{ .data = .{
116 .string = self,
117 .builder = builder,
118 .quote_behavior = .quote_unless_valid_identifier,
119 } };
110120 }
111 pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) {
112 return .{ .data = .{ .string = self, .builder = builder } };
121
122 pub fn fmtQ(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) {
123 return .{ .data = .{
124 .string = self,
125 .builder = builder,
126 .quote_behavior = .always_quote,
127 } };
128 }
129
130 pub fn fmtRaw(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) {
131 return .{ .data = .{
132 .string = self,
133 .builder = builder,
134 .quote_behavior = null,
135 } };
113136 }
114137
115138 fn fromIndex(index: ?usize) String {
......@@ -223,7 +246,7 @@ pub const Type = enum(u32) {
223246 _,
224247
225248 pub const ptr_amdgpu_constant =
226 @field(Type, std.fmt.comptimePrint("ptr{f }", .{AddrSpace.amdgpu.constant}));
249 @field(Type, std.fmt.comptimePrint("ptr{f}", .{AddrSpace.amdgpu.constant.fmt(" ")}));
227250
228251 pub const Tag = enum(u4) {
229252 simple,
......@@ -648,13 +671,16 @@ pub const Type = enum(u32) {
648671 const FormatData = struct {
649672 type: Type,
650673 builder: *const Builder,
674 mode: Mode,
675
676 const Mode = enum { default, m, lt, gt, percent };
651677 };
652 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
678 fn format(data: FormatData, w: *Writer) Writer.Error!void {
653679 assert(data.type != .none);
654 if (comptime std.mem.eql(u8, fmt_str, "m")) {
680 if (data.mode == .m) {
655681 const item = data.builder.type_items.items[@intFromEnum(data.type)];
656682 switch (item.tag) {
657 .simple => try bw.writeAll(switch (@as(Simple, @enumFromInt(item.data))) {
683 .simple => try w.writeAll(switch (@as(Simple, @enumFromInt(item.data))) {
658684 .void => "isVoid",
659685 .half => "f16",
660686 .bfloat => "bf16",
......@@ -671,36 +697,36 @@ pub const Type = enum(u32) {
671697 .function, .vararg_function => |kind| {
672698 var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);
673699 const params = extra.trail.next(extra.data.params_len, Type, data.builder);
674 try bw.print("f_{fm}", .{extra.data.ret.fmt(data.builder)});
675 for (params) |param| try bw.print("{fm}", .{param.fmt(data.builder)});
700 try w.print("f_{f}", .{extra.data.ret.fmt(data.builder, .m)});
701 for (params) |param| try w.print("{f}", .{param.fmt(data.builder, .m)});
676702 switch (kind) {
677703 .function => {},
678 .vararg_function => try bw.writeAll("vararg"),
704 .vararg_function => try w.writeAll("vararg"),
679705 else => unreachable,
680706 }
681 try bw.writeByte('f');
707 try w.writeByte('f');
682708 },
683 .integer => try bw.print("i{d}", .{item.data}),
684 .pointer => try bw.print("p{d}", .{item.data}),
709 .integer => try w.print("i{d}", .{item.data}),
710 .pointer => try w.print("p{d}", .{item.data}),
685711 .target => {
686712 var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
687713 const types = extra.trail.next(extra.data.types_len, Type, data.builder);
688714 const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
689 try bw.print("t{s}", .{extra.data.name.slice(data.builder).?});
690 for (types) |ty| try bw.print("_{fm}", .{ty.fmt(data.builder)});
691 for (ints) |int| try bw.print("_{d}", .{int});
692 try bw.writeByte('t');
715 try w.print("t{s}", .{extra.data.name.slice(data.builder).?});
716 for (types) |ty| try w.print("_{f}", .{ty.fmt(data.builder, .m)});
717 for (ints) |int| try w.print("_{d}", .{int});
718 try w.writeByte('t');
693719 },
694720 .vector, .scalable_vector => |kind| {
695721 const extra = data.builder.typeExtraData(Type.Vector, item.data);
696 try bw.print("{s}v{d}{fm}", .{
722 try w.print("{s}v{d}{f}", .{
697723 switch (kind) {
698724 .vector => "",
699725 .scalable_vector => "nx",
700726 else => unreachable,
701727 },
702728 extra.len,
703 extra.child.fmt(data.builder),
729 extra.child.fmt(data.builder, .m),
704730 });
705731 },
706732 inline .small_array, .array => |kind| {
......@@ -709,72 +735,72 @@ pub const Type = enum(u32) {
709735 .array => Type.Array,
710736 else => unreachable,
711737 }, item.data);
712 try bw.print("a{d}{fm}", .{ extra.length(), extra.child.fmt(data.builder) });
738 try w.print("a{d}{f}", .{ extra.length(), extra.child.fmt(data.builder, .m) });
713739 },
714740 .structure, .packed_structure => {
715741 var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);
716742 const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);
717 try bw.writeAll("sl_");
718 for (fields) |field| try bw.print("{fm}", .{field.fmt(data.builder)});
719 try bw.writeByte('s');
743 try w.writeAll("sl_");
744 for (fields) |field| try w.print("{f}", .{field.fmt(data.builder, .m)});
745 try w.writeByte('s');
720746 },
721747 .named_structure => {
722748 const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);
723 try bw.writeAll("s_");
724 if (extra.id.slice(data.builder)) |id| try bw.writeAll(id);
749 try w.writeAll("s_");
750 if (extra.id.slice(data.builder)) |id| try w.writeAll(id);
725751 },
726752 }
727753 return;
728754 }
729 if (std.enums.tagName(Type, data.type)) |name| return bw.writeAll(name);
755 if (std.enums.tagName(Type, data.type)) |name| return w.writeAll(name);
730756 const item = data.builder.type_items.items[@intFromEnum(data.type)];
731757 switch (item.tag) {
732758 .simple => unreachable,
733759 .function, .vararg_function => |kind| {
734760 var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);
735761 const params = extra.trail.next(extra.data.params_len, Type, data.builder);
736 if (!comptime std.mem.eql(u8, fmt_str, ">"))
737 try bw.print("{f%} ", .{extra.data.ret.fmt(data.builder)});
738 if (!comptime std.mem.eql(u8, fmt_str, "<")) {
739 try bw.writeByte('(');
762 if (data.mode != .gt)
763 try w.print("{f} ", .{extra.data.ret.fmt(data.builder, .percent)});
764 if (data.mode != .lt) {
765 try w.writeByte('(');
740766 for (params, 0..) |param, index| {
741 if (index > 0) try bw.writeAll(", ");
742 try bw.print("{f%}", .{param.fmt(data.builder)});
767 if (index > 0) try w.writeAll(", ");
768 try w.print("{f}", .{param.fmt(data.builder, .percent)});
743769 }
744770 switch (kind) {
745771 .function => {},
746772 .vararg_function => {
747 if (params.len > 0) try bw.writeAll(", ");
748 try bw.writeAll("...");
773 if (params.len > 0) try w.writeAll(", ");
774 try w.writeAll("...");
749775 },
750776 else => unreachable,
751777 }
752 try bw.writeByte(')');
778 try w.writeByte(')');
753779 }
754780 },
755 .integer => try bw.print("i{d}", .{item.data}),
756 .pointer => try bw.print("ptr{f }", .{@as(AddrSpace, @enumFromInt(item.data))}),
781 .integer => try w.print("i{d}", .{item.data}),
782 .pointer => try w.print("ptr{f}", .{@as(AddrSpace, @enumFromInt(item.data)).fmt(" ")}),
757783 .target => {
758784 var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
759785 const types = extra.trail.next(extra.data.types_len, Type, data.builder);
760786 const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
761 try bw.print(
762 \\target({f"}
763 , .{extra.data.name.fmt(data.builder)});
764 for (types) |ty| try bw.print(", {f%}", .{ty.fmt(data.builder)});
765 for (ints) |int| try bw.print(", {d}", .{int});
766 try bw.writeByte(')');
787 try w.print(
788 \\target({f}
789 , .{extra.data.name.fmtQ(data.builder)});
790 for (types) |ty| try w.print(", {f}", .{ty.fmt(data.builder, .percent)});
791 for (ints) |int| try w.print(", {d}", .{int});
792 try w.writeByte(')');
767793 },
768794 .vector, .scalable_vector => |kind| {
769795 const extra = data.builder.typeExtraData(Type.Vector, item.data);
770 try bw.print("<{s}{d} x {f%}>", .{
796 try w.print("<{s}{d} x {f}>", .{
771797 switch (kind) {
772798 .vector => "",
773799 .scalable_vector => "vscale x ",
774800 else => unreachable,
775801 },
776802 extra.len,
777 extra.child.fmt(data.builder),
803 extra.child.fmt(data.builder, .percent),
778804 });
779805 },
780806 inline .small_array, .array => |kind| {
......@@ -783,44 +809,45 @@ pub const Type = enum(u32) {
783809 .array => Type.Array,
784810 else => unreachable,
785811 }, item.data);
786 try bw.print("[{d} x {f%}]", .{ extra.length(), extra.child.fmt(data.builder) });
812 try w.print("[{d} x {f}]", .{ extra.length(), extra.child.fmt(data.builder, .percent) });
787813 },
788814 .structure, .packed_structure => |kind| {
789815 var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);
790816 const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);
791817 switch (kind) {
792818 .structure => {},
793 .packed_structure => try bw.writeByte('<'),
819 .packed_structure => try w.writeByte('<'),
794820 else => unreachable,
795821 }
796 try bw.writeAll("{ ");
822 try w.writeAll("{ ");
797823 for (fields, 0..) |field, index| {
798 if (index > 0) try bw.writeAll(", ");
799 try bw.print("{f%}", .{field.fmt(data.builder)});
824 if (index > 0) try w.writeAll(", ");
825 try w.print("{f}", .{field.fmt(data.builder, .percent)});
800826 }
801 try bw.writeAll(" }");
827 try w.writeAll(" }");
802828 switch (kind) {
803829 .structure => {},
804 .packed_structure => try bw.writeByte('>'),
830 .packed_structure => try w.writeByte('>'),
805831 else => unreachable,
806832 }
807833 },
808834 .named_structure => {
809835 const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);
810 if (comptime std.mem.eql(u8, fmt_str, "%")) try bw.print("%{f}", .{
836 if (data.mode == .percent) try w.print("%{f}", .{
811837 extra.id.fmt(data.builder),
812838 }) else switch (extra.body) {
813 .none => try bw.writeAll("opaque"),
839 .none => try w.writeAll("opaque"),
814840 else => try format(.{
815841 .type = extra.body,
816842 .builder = data.builder,
817 }, bw, fmt_str),
843 .mode = data.mode,
844 }, w),
818845 }
819846 },
820847 }
821848 }
822 pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) {
823 return .{ .data = .{ .type = self, .builder = builder } };
849 pub fn fmt(self: Type, builder: *const Builder, mode: FormatData.Mode) std.fmt.Formatter(FormatData, format) {
850 return .{ .data = .{ .type = self, .builder = builder, .mode = mode } };
824851 }
825852
826853 const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void);
......@@ -1128,10 +1155,13 @@ pub const Attribute = union(Kind) {
11281155 const FormatData = struct {
11291156 attribute_index: Index,
11301157 builder: *const Builder,
1158 flags: Flags = .{},
1159 const Flags = struct {
1160 pound: bool = false,
1161 quote: bool = false,
1162 };
11311163 };
1132 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
1133 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_|
1134 @compileError("invalid format string: '" ++ fmt_str ++ "'");
1164 fn format(data: FormatData, w: *Writer) Writer.Error!void {
11351165 const attribute = data.attribute_index.toAttribute(data.builder);
11361166 switch (attribute) {
11371167 .zeroext,
......@@ -1204,97 +1234,99 @@ pub const Attribute = union(Kind) {
12041234 .no_sanitize_address,
12051235 .no_sanitize_hwaddress,
12061236 .sanitize_address_dyninit,
1207 => try bw.print(" {s}", .{@tagName(attribute)}),
1237 => try w.print(" {s}", .{@tagName(attribute)}),
12081238 .byval,
12091239 .byref,
12101240 .preallocated,
12111241 .inalloca,
12121242 .sret,
12131243 .elementtype,
1214 => |ty| try bw.print(" {s}({f%})", .{ @tagName(attribute), ty.fmt(data.builder) }),
1215 .@"align" => |alignment| try bw.print("{f }", .{alignment}),
1244 => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }),
1245 .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}),
12161246 .dereferenceable,
12171247 .dereferenceable_or_null,
1218 => |size| try bw.print(" {s}({d})", .{ @tagName(attribute), size }),
1248 => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }),
12191249 .nofpclass => |fpclass| {
12201250 const Int = @typeInfo(FpClass).@"struct".backing_integer.?;
1221 try bw.print(" {s}(", .{@tagName(attribute)});
1251 try w.print(" {s}(", .{@tagName(attribute)});
12221252 var any = false;
12231253 var remaining: Int = @bitCast(fpclass);
12241254 inline for (@typeInfo(FpClass).@"struct".decls) |decl| {
12251255 const pattern: Int = @bitCast(@field(FpClass, decl.name));
12261256 if (remaining & pattern == pattern) {
12271257 if (!any) {
1228 try bw.writeByte(' ');
1258 try w.writeByte(' ');
12291259 any = true;
12301260 }
1231 try bw.writeAll(decl.name);
1261 try w.writeAll(decl.name);
12321262 remaining &= ~pattern;
12331263 }
12341264 }
1235 try bw.writeByte(')');
1265 try w.writeByte(')');
1266 },
1267 .alignstack => |alignment| {
1268 try w.print(" {t}", .{attribute});
1269 const alignment_bytes = alignment.toByteUnits() orelse return;
1270 if (data.flags.pound) {
1271 try w.print("={d}", .{alignment_bytes});
1272 } else {
1273 try w.print("({d})", .{alignment_bytes});
1274 }
12361275 },
1237 .alignstack => |alignment| try bw.print(
1238 if (comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null)
1239 " {s}={d}"
1240 else
1241 " {s}({d})",
1242 .{ @tagName(attribute), alignment.toByteUnits() orelse return },
1243 ),
12441276 .allockind => |allockind| {
1245 try bw.print(" {s}(\"", .{@tagName(attribute)});
1277 try w.print(" {t}(\"", .{attribute});
12461278 var any = false;
12471279 inline for (@typeInfo(AllocKind).@"struct".fields) |field| {
12481280 if (comptime std.mem.eql(u8, field.name, "_")) continue;
12491281 if (@field(allockind, field.name)) {
12501282 if (!any) {
1251 try bw.writeByte(',');
1283 try w.writeByte(',');
12521284 any = true;
12531285 }
1254 try bw.writeAll(field.name);
1286 try w.writeAll(field.name);
12551287 }
12561288 }
1257 try bw.writeAll("\")");
1289 try w.writeAll("\")");
12581290 },
12591291 .allocsize => |allocsize| {
1260 try bw.print(" {s}({d}", .{ @tagName(attribute), allocsize.elem_size });
1292 try w.print(" {t}({d}", .{ attribute, allocsize.elem_size });
12611293 if (allocsize.num_elems != AllocSize.none)
1262 try bw.print(",{d}", .{allocsize.num_elems});
1263 try bw.writeByte(')');
1294 try w.print(",{d}", .{allocsize.num_elems});
1295 try w.writeByte(')');
12641296 },
12651297 .memory => |memory| {
1266 try bw.print(" {s}(", .{@tagName(attribute)});
1298 try w.print(" {t}(", .{attribute});
12671299 var any = memory.other != .none or
12681300 (memory.argmem == .none and memory.inaccessiblemem == .none);
1269 if (any) try bw.writeAll(@tagName(memory.other));
1301 if (any) try w.writeAll(@tagName(memory.other));
12701302 inline for (.{ "argmem", "inaccessiblemem" }) |kind| {
12711303 if (@field(memory, kind) != memory.other) {
1272 if (any) try bw.writeAll(", ");
1273 try bw.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) });
1304 if (any) try w.writeAll(", ");
1305 try w.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) });
12741306 any = true;
12751307 }
12761308 }
1277 try bw.writeByte(')');
1309 try w.writeByte(')');
12781310 },
12791311 .uwtable => |uwtable| if (uwtable != .none) {
1280 try bw.print(" {s}", .{@tagName(attribute)});
1281 if (uwtable != UwTable.default) try bw.print("({s})", .{@tagName(uwtable)});
1312 try w.print(" {s}", .{@tagName(attribute)});
1313 if (uwtable != UwTable.default) try w.print("({s})", .{@tagName(uwtable)});
12821314 },
1283 .vscale_range => |vscale_range| try bw.print(" {s}({d},{d})", .{
1315 .vscale_range => |vscale_range| try w.print(" {s}({d},{d})", .{
12841316 @tagName(attribute),
12851317 vscale_range.min.toByteUnits().?,
12861318 vscale_range.max.toByteUnits() orelse 0,
12871319 }),
1288 .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) {
1289 try bw.print(" {f\"}", .{string_attr.kind.fmt(data.builder)});
1320 .string => |string_attr| if (data.flags.quote) {
1321 try w.print(" {f}", .{string_attr.kind.fmtQ(data.builder)});
12901322 if (string_attr.value != .empty)
1291 try bw.print("={f\"}", .{string_attr.value.fmt(data.builder)});
1323 try w.print("={f}", .{string_attr.value.fmtQ(data.builder)});
12921324 },
12931325 .none => unreachable,
12941326 }
12951327 }
1296 pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) {
1297 return .{ .data = .{ .attribute_index = self, .builder = builder } };
1328 pub fn fmt(self: Index, builder: *const Builder, mode: FormatData.mode) std.fmt.Formatter(FormatData, format) {
1329 return .{ .data = .{ .attribute_index = self, .builder = builder, .mode = mode } };
12981330 }
12991331
13001332 fn toStorage(self: Index, builder: *const Builder) Storage {
......@@ -1506,9 +1538,9 @@ pub const Attribute = union(Kind) {
15061538 pub const UwTable = enum(u32) {
15071539 none,
15081540 sync,
1509 @"async",
1541 async,
15101542
1511 pub const default = UwTable.@"async";
1543 pub const default = UwTable.async;
15121544 };
15131545
15141546 pub const VScaleRange = packed struct(u32) {
......@@ -1567,15 +1599,18 @@ pub const Attributes = enum(u32) {
15671599 const FormatData = struct {
15681600 attributes: Attributes,
15691601 builder: *const Builder,
1602 flags: Flags = .{},
1603 const Flags = Attribute.Index.FormatData.Flags;
15701604 };
1571 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
1605 fn format(data: FormatData, w: *Writer) Writer.Error!void {
15721606 for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{
15731607 .attribute_index = attribute_index,
15741608 .builder = data.builder,
1575 }, bw, fmt_str);
1609 .flags = data.flags,
1610 }, w);
15761611 }
1577 pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) {
1578 return .{ .data = .{ .attributes = self, .builder = builder } };
1612 pub fn fmt(self: Attributes, builder: *const Builder, flags: FormatData.Flags) std.fmt.Formatter(FormatData, format) {
1613 return .{ .data = .{ .attributes = self, .builder = builder, .flags = flags } };
15791614 }
15801615};
15811616
......@@ -1761,14 +1796,14 @@ pub const Linkage = enum(u4) {
17611796 extern_weak = 7,
17621797 external = 0,
17631798
1764 pub fn format(self: Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1765 if (self != .external) try bw.print(" {s}", .{@tagName(self)});
1799 pub fn format(self: Linkage, w: *Writer) Writer.Error!void {
1800 if (self != .external) try w.print(" {s}", .{@tagName(self)});
17661801 }
17671802
1768 fn formatOptional(data: ?Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1769 if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)});
1803 fn formatOptional(data: ?Linkage, w: *Writer) Writer.Error!void {
1804 if (data) |linkage| try w.print(" {s}", .{@tagName(linkage)});
17701805 }
1771 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) {
1806 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(?Linkage, formatOptional) {
17721807 return .{ .data = self };
17731808 }
17741809};
......@@ -1778,8 +1813,8 @@ pub const Preemption = enum {
17781813 dso_local,
17791814 implicit_dso_local,
17801815
1781 pub fn format(self: Preemption, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1782 if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)});
1816 pub fn format(self: Preemption, w: *Writer) Writer.Error!void {
1817 if (self == .dso_local) try w.print(" {s}", .{@tagName(self)});
17831818 }
17841819};
17851820
......@@ -1796,8 +1831,7 @@ pub const Visibility = enum(u2) {
17961831 };
17971832 }
17981833
1799 pub fn format(self: Visibility, comptime format_string: []const u8, writer: *Writer) Writer.Error!void {
1800 comptime assert(format_string.len == 0);
1834 pub fn format(self: Visibility, writer: *Writer) Writer.Error!void {
18011835 if (self != .default) try writer.print(" {s}", .{@tagName(self)});
18021836 }
18031837};
......@@ -1807,8 +1841,8 @@ pub const DllStorageClass = enum(u2) {
18071841 dllimport = 1,
18081842 dllexport = 2,
18091843
1810 pub fn format(self: DllStorageClass, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1811 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
1844 pub fn format(self: DllStorageClass, w: *Writer) Writer.Error!void {
1845 if (self != .default) try w.print(" {s}", .{@tagName(self)});
18121846 }
18131847};
18141848
......@@ -1819,10 +1853,31 @@ pub const ThreadLocal = enum(u3) {
18191853 initialexec = 3,
18201854 localexec = 4,
18211855
1822 pub fn format(self: ThreadLocal, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {
1823 if (self == .default) return;
1824 try bw.print("{s}thread_local", .{prefix});
1825 if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)});
1856 pub fn format(tl: ThreadLocal, w: *Writer) Writer.Error!void {
1857 return Prefixed.format(.{ .thread_local = tl, .prefix = "" }, w);
1858 }
1859
1860 pub const Prefixed = struct {
1861 thread_local: ThreadLocal,
1862 prefix: []const u8,
1863
1864 pub fn format(p: Prefixed, w: *Writer) Writer.Error!void {
1865 switch (p.thread_local) {
1866 .default => return,
1867 .generaldynamic => {
1868 var vecs: [2][]const u8 = .{ p.prefix, "thread_local" };
1869 return w.writeVecAll(&vecs);
1870 },
1871 else => {
1872 var vecs: [4][]const u8 = .{ p.prefix, "thread_local(", @tagName(p.thread_local), ")" };
1873 return w.writeVecAll(&vecs);
1874 },
1875 }
1876 }
1877 };
1878
1879 pub fn fmt(tl: ThreadLocal, prefix: []const u8) Prefixed {
1880 return .{ .thread_local = tl, .prefix = prefix };
18261881 }
18271882};
18281883
......@@ -1833,8 +1888,8 @@ pub const UnnamedAddr = enum(u2) {
18331888 unnamed_addr = 1,
18341889 local_unnamed_addr = 2,
18351890
1836 pub fn format(self: UnnamedAddr, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1837 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
1891 pub fn format(self: UnnamedAddr, w: *Writer) Writer.Error!void {
1892 if (self != .default) try w.print(" {s}", .{@tagName(self)});
18381893 }
18391894};
18401895
......@@ -1927,8 +1982,24 @@ pub const AddrSpace = enum(u24) {
19271982 pub const funcref: AddrSpace = @enumFromInt(20);
19281983 };
19291984
1930 pub fn format(self: AddrSpace, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {
1931 if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) });
1985 pub fn format(addr_space: AddrSpace, w: *Writer) Writer.Error!void {
1986 return Prefixed.format(.{ .addr_space = addr_space, .prefix = "" }, w);
1987 }
1988
1989 pub const Prefixed = struct {
1990 addr_space: AddrSpace,
1991 prefix: []const u8,
1992
1993 pub fn format(p: Prefixed, w: *Writer) Writer.Error!void {
1994 switch (p.addr_space) {
1995 .default => return,
1996 else => return w.print("{s}addrspace({d})", .{ p.prefix, p.addr_space }),
1997 }
1998 }
1999 };
2000
2001 pub fn fmt(addr_space: AddrSpace, prefix: []const u8) Prefixed {
2002 return .{ .addr_space = addr_space, .prefix = prefix };
19322003 }
19332004};
19342005
......@@ -1936,8 +2007,8 @@ pub const ExternallyInitialized = enum {
19362007 default,
19372008 externally_initialized,
19382009
1939 pub fn format(self: ExternallyInitialized, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1940 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
2010 pub fn format(self: ExternallyInitialized, w: *Writer) Writer.Error!void {
2011 if (self != .default) try w.print(" {s}", .{@tagName(self)});
19412012 }
19422013};
19432014
......@@ -1960,8 +2031,18 @@ pub const Alignment = enum(u6) {
19602031 return if (self == .default) 0 else (@intFromEnum(self) + 1);
19612032 }
19622033
1963 pub fn format(self: Alignment, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {
1964 try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return });
2034 pub const Prefixed = struct {
2035 alignment: Alignment,
2036 prefix: []const u8,
2037
2038 pub fn format(p: Prefixed, w: *Writer) Writer.Error!void {
2039 const byte_units = p.alignment.toByteUnits() orelse return;
2040 return w.print("{s}align ({d})", .{ p.prefix, byte_units });
2041 }
2042 };
2043
2044 pub fn fmt(alignment: Alignment, prefix: []const u8) Prefixed {
2045 return .{ .alignment = alignment, .prefix = prefix };
19652046 }
19662047};
19672048
......@@ -2034,7 +2115,7 @@ pub const CallConv = enum(u10) {
20342115
20352116 pub const default = CallConv.ccc;
20362117
2037 pub fn format(self: CallConv, bw: *Writer, comptime _: []const u8) Writer.Error!void {
2118 pub fn format(self: CallConv, w: *Writer) Writer.Error!void {
20382119 switch (self) {
20392120 default => {},
20402121 .fastcc,
......@@ -2088,8 +2169,8 @@ pub const CallConv = enum(u10) {
20882169 .aarch64_sme_preservemost_from_x2,
20892170 .m68k_rtdcc,
20902171 .riscv_vectorcallcc,
2091 => try bw.print(" {s}", .{@tagName(self)}),
2092 _ => try bw.print(" cc{d}", .{@intFromEnum(self)}),
2172 => try w.print(" {s}", .{@tagName(self)}),
2173 _ => try w.print(" cc{d}", .{@intFromEnum(self)}),
20932174 }
20942175 }
20952176};
......@@ -2114,26 +2195,25 @@ pub const StrtabString = enum(u32) {
21142195 const FormatData = struct {
21152196 string: StrtabString,
21162197 builder: *const Builder,
2198 quote_behavior: ?QuoteBehavior,
21172199 };
2118 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
2119 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
2120 @compileError("invalid format string: '" ++ fmt_str ++ "'");
2200 fn format(data: FormatData, w: *Writer) Writer.Error!void {
21212201 assert(data.string != .none);
21222202 const string_slice = data.string.slice(data.builder) orelse
2123 return bw.print("{d}", .{@intFromEnum(data.string)});
2124 if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_|
2125 return bw.writeAll(string_slice);
2126 try printEscapedString(
2127 string_slice,
2128 if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_|
2129 .always_quote
2130 else
2131 .quote_unless_valid_identifier,
2132 bw,
2133 );
2203 return w.print("{d}", .{@intFromEnum(data.string)});
2204 const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice);
2205 return printEscapedString(string_slice, quote_behavior, w);
21342206 }
2135 pub fn fmt(self: StrtabString, builder: *const Builder) std.fmt.Formatter(format) {
2136 return .{ .data = .{ .string = self, .builder = builder } };
2207 pub fn fmt(
2208 self: StrtabString,
2209 builder: *const Builder,
2210 quote_behavior: ?QuoteBehavior,
2211 ) std.fmt.Formatter(FormatData, format) {
2212 return .{ .data = .{
2213 .string = self,
2214 .builder = builder,
2215 .quote_behavior = quote_behavior,
2216 } };
21372217 }
21382218
21392219 fn fromIndex(index: ?usize) StrtabString {
......@@ -2302,12 +2382,12 @@ pub const Global = struct {
23022382 global: Index,
23032383 builder: *const Builder,
23042384 };
2305 fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void {
2306 try bw.print("@{f}", .{
2307 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder),
2385 fn format(data: FormatData, w: *Writer) Writer.Error!void {
2386 try w.print("@{f}", .{
2387 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder, null),
23082388 });
23092389 }
2310 pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) {
2390 pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(FormatData, format) {
23112391 return .{ .data = .{ .global = self, .builder = builder } };
23122392 }
23132393
......@@ -4747,24 +4827,23 @@ pub const Function = struct {
47474827 instruction: Instruction.Index,
47484828 function: Function.Index,
47494829 builder: *Builder,
4830 flags: FormatFlags,
47504831 };
4751 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
4752 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|
4753 @compileError("invalid format string: '" ++ fmt_str ++ "'");
4754 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
4832 fn format(data: FormatData, w: *Writer) Writer.Error!void {
4833 if (data.flags.comma) {
47554834 if (data.instruction == .none) return;
4756 try bw.writeByte(',');
4835 try w.writeByte(',');
47574836 }
4758 if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) {
4837 if (data.flags.space) {
47594838 if (data.instruction == .none) return;
4760 try bw.writeByte(' ');
4839 try w.writeByte(' ');
47614840 }
4762 if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) try bw.print(
4763 "{f%} ",
4764 .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder)},
4841 if (data.flags.percent) try w.print(
4842 "{f} ",
4843 .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder, .percent)},
47654844 );
47664845 assert(data.instruction != .none);
4767 try bw.print("%{f}", .{
4846 try w.print("%{f}", .{
47684847 data.instruction.name(data.function.ptrConst(data.builder)).fmt(data.builder),
47694848 });
47704849 }
......@@ -4772,8 +4851,14 @@ pub const Function = struct {
47724851 self: Instruction.Index,
47734852 function: Function.Index,
47744853 builder: *Builder,
4775 ) std.fmt.Formatter(format) {
4776 return .{ .data = .{ .instruction = self, .function = function, .builder = builder } };
4854 flags: FormatFlags,
4855 ) std.fmt.Formatter(FormatData, format) {
4856 return .{ .data = .{
4857 .instruction = self,
4858 .function = function,
4859 .builder = builder,
4860 .flags = flags,
4861 } };
47774862 }
47784863 };
47794864
......@@ -6270,10 +6355,10 @@ pub const WipFunction = struct {
62706355
62716356 while (true) {
62726357 gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1);
6273 const unique_name = try wip_name.builder.fmt("{fr}{s}{fr}", .{
6274 name.fmt(wip_name.builder),
6358 const unique_name = try wip_name.builder.fmt("{f}{s}{f}", .{
6359 name.fmtRaw(wip_name.builder),
62756360 sep,
6276 gop.value_ptr.fmt(wip_name.builder),
6361 gop.value_ptr.fmtRaw(wip_name.builder),
62776362 });
62786363 const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name);
62796364 if (!unique_gop.found_existing) {
......@@ -6940,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) {
69407025 normal,
69417026 @"volatile",
69427027
6943 pub fn format(self: MemoryAccessKind, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {
6944 if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) });
7028 pub fn format(memory_access_kind: MemoryAccessKind, w: *Writer) Writer.Error!void {
7029 return Prefixed.format(.{ .memory_access_kind = memory_access_kind, .prefix = "" }, w);
7030 }
7031
7032 pub const Prefixed = struct {
7033 memory_access_kind: MemoryAccessKind,
7034 prefix: []const u8,
7035
7036 pub fn format(p: Prefixed, w: *Writer) Writer.Error!void {
7037 switch (p.memory_access_kind) {
7038 .normal => return,
7039 .@"volatile" => {
7040 var vecs: [2][]const u8 = .{ p.prefix, "volatile" };
7041 return w.writeVecAll(&vecs);
7042 },
7043 }
7044 }
7045 };
7046
7047 pub fn fmt(memory_access_kind: MemoryAccessKind, prefix: []const u8) Prefixed {
7048 return .{ .memory_access_kind = memory_access_kind, .prefix = prefix };
69457049 }
69467050};
69477051
......@@ -6949,10 +7053,27 @@ pub const SyncScope = enum(u1) {
69497053 singlethread,
69507054 system,
69517055
6952 pub fn format(self: SyncScope, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {
6953 if (self != .system) try bw.print(
6954 \\{s}syncscope("{s}")
6955 , .{ prefix, @tagName(self) });
7056 pub fn format(sync_scope: SyncScope, w: *Writer) Writer.Error!void {
7057 return Prefixed.format(.{ .sync_scope = sync_scope, .prefix = "" }, w);
7058 }
7059
7060 pub const Prefixed = struct {
7061 sync_scope: SyncScope,
7062 prefix: []const u8,
7063
7064 pub fn format(p: Prefixed, w: *Writer) Writer.Error!void {
7065 switch (p.sync_scope) {
7066 .system => return,
7067 .singlethread => {
7068 var vecs: [2][]const u8 = .{ p.prefix, "syncscope(\"singlethread\")" };
7069 return w.writeVecAll(&vecs);
7070 },
7071 }
7072 }
7073 };
7074
7075 pub fn fmt(sync_scope: SyncScope, prefix: []const u8) Prefixed {
7076 return .{ .sync_scope = sync_scope, .prefix = prefix };
69567077 }
69577078};
69587079
......@@ -6965,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) {
69657086 acq_rel = 5,
69667087 seq_cst = 6,
69677088
6968 pub fn format(self: AtomicOrdering, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {
6969 if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) });
7089 pub fn format(atomic_ordering: AtomicOrdering, w: *Writer) Writer.Error!void {
7090 return Prefixed.format(.{ .atomic_ordering = atomic_ordering, .prefix = "" }, w);
7091 }
7092
7093 pub const Prefixed = struct {
7094 atomic_ordering: AtomicOrdering,
7095 prefix: []const u8,
7096
7097 pub fn format(p: Prefixed, w: *Writer) Writer.Error!void {
7098 switch (p.atomic_ordering) {
7099 .none => return,
7100 else => {
7101 var vecs: [2][]const u8 = .{ p.prefix, @tagName(p.atomic_ordering) };
7102 return w.writeVecAll(&vecs);
7103 },
7104 }
7105 }
7106 };
7107
7108 pub fn fmt(atomic_ordering: AtomicOrdering, prefix: []const u8) Prefixed {
7109 return .{ .atomic_ordering = atomic_ordering, .prefix = prefix };
69707110 }
69717111};
69727112
......@@ -7380,22 +7520,21 @@ pub const Constant = enum(u32) {
73807520 const FormatData = struct {
73817521 constant: Constant,
73827522 builder: *Builder,
7523 flags: FormatFlags,
73837524 };
7384 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
7385 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|
7386 @compileError("invalid format string: '" ++ fmt_str ++ "'");
7387 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
7525 fn format(data: FormatData, w: *Writer) Writer.Error!void {
7526 if (data.flags.comma) {
73887527 if (data.constant == .no_init) return;
7389 try bw.writeByte(',');
7528 try w.writeByte(',');
73907529 }
7391 if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) {
7530 if (data.flags.space) {
73927531 if (data.constant == .no_init) return;
7393 try bw.writeByte(' ');
7532 try w.writeByte(' ');
73947533 }
7395 if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null)
7396 try bw.print("{f%} ", .{data.constant.typeOf(data.builder).fmt(data.builder)});
7534 if (data.flags.percent)
7535 try w.print("{f} ", .{data.constant.typeOf(data.builder).fmt(data.builder, .percent)});
73977536 assert(data.constant != .no_init);
7398 if (std.enums.tagName(Constant, data.constant)) |name| return bw.writeAll(name);
7537 if (std.enums.tagName(Constant, data.constant)) |name| return w.writeAll(name);
73997538 switch (data.constant.unwrap()) {
74007539 .constant => |constant| {
74017540 const item = data.builder.constant_items.get(constant);
......@@ -7432,13 +7571,13 @@ pub const Constant = enum(u32) {
74327571 var stack align(@alignOf(ExpectedContents)) =
74337572 std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);
74347573 const allocator = stack.get();
7435 const str = try bigint.toStringAlloc(allocator, 10, undefined);
7574 const str = bigint.toStringAlloc(allocator, 10, undefined) catch return error.WriteFailed;
74367575 defer allocator.free(str);
7437 try bw.writeAll(str);
7576 try w.writeAll(str);
74387577 },
74397578 .half,
74407579 .bfloat,
7441 => |tag| try bw.print("0x{c}{X:0>4}", .{ @as(u8, switch (tag) {
7580 => |tag| try w.print("0x{c}{X:0>4}", .{ @as(u8, switch (tag) {
74427581 .half => 'H',
74437582 .bfloat => 'R',
74447583 else => unreachable,
......@@ -7469,7 +7608,7 @@ pub const Constant = enum(u32) {
74697608 ) + 1,
74707609 else => 0,
74717610 };
7472 try bw.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){
7611 try w.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){
74737612 .mantissa = std.math.shl(
74747613 Mantissa64,
74757614 repr.mantissa,
......@@ -7491,13 +7630,13 @@ pub const Constant = enum(u32) {
74917630 },
74927631 .double => {
74937632 const extra = data.builder.constantExtraData(Double, item.data);
7494 try bw.print("0x{X:0>8}{X:0>8}", .{ extra.hi, extra.lo });
7633 try w.print("0x{X:0>8}{X:0>8}", .{ extra.hi, extra.lo });
74957634 },
74967635 .fp128,
74977636 .ppc_fp128,
74987637 => |tag| {
74997638 const extra = data.builder.constantExtraData(Fp128, item.data);
7500 try bw.print("0x{c}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{
7639 try w.print("0x{c}{X:0>8}{X:0>8}{X:0>8}{X:0>8}", .{
75017640 @as(u8, switch (tag) {
75027641 .fp128 => 'L',
75037642 .ppc_fp128 => 'M',
......@@ -7511,7 +7650,7 @@ pub const Constant = enum(u32) {
75117650 },
75127651 .x86_fp80 => {
75137652 const extra = data.builder.constantExtraData(Fp80, item.data);
7514 try bw.print("0xK{X:0>4}{X:0>8}{X:0>8}", .{
7653 try w.print("0xK{X:0>4}{X:0>8}{X:0>8}", .{
75157654 extra.hi, extra.lo_hi, extra.lo_lo,
75167655 });
75177656 },
......@@ -7520,7 +7659,7 @@ pub const Constant = enum(u32) {
75207659 .zeroinitializer,
75217660 .undef,
75227661 .poison,
7523 => |tag| try bw.writeAll(@tagName(tag)),
7662 => |tag| try w.writeAll(@tagName(tag)),
75247663 .structure,
75257664 .packed_structure,
75267665 .array,
......@@ -7529,7 +7668,7 @@ pub const Constant = enum(u32) {
75297668 var extra = data.builder.constantExtraDataTrail(Aggregate, item.data);
75307669 const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder));
75317670 const vals = extra.trail.next(len, Constant, data.builder);
7532 try bw.writeAll(switch (tag) {
7671 try w.writeAll(switch (tag) {
75337672 .structure => "{ ",
75347673 .packed_structure => "<{ ",
75357674 .array => "[",
......@@ -7537,10 +7676,10 @@ pub const Constant = enum(u32) {
75377676 else => unreachable,
75387677 });
75397678 for (vals, 0..) |val, index| {
7540 if (index > 0) try bw.writeAll(", ");
7541 try bw.print("{f%}", .{val.fmt(data.builder)});
7679 if (index > 0) try w.writeAll(", ");
7680 try w.print("{f}", .{val.fmt(data.builder, .{ .percent = true })});
75427681 }
7543 try bw.writeAll(switch (tag) {
7682 try w.writeAll(switch (tag) {
75447683 .structure => " }",
75457684 .packed_structure => " }>",
75467685 .array => "]",
......@@ -7551,30 +7690,30 @@ pub const Constant = enum(u32) {
75517690 .splat => {
75527691 const extra = data.builder.constantExtraData(Splat, item.data);
75537692 const len = extra.type.vectorLen(data.builder);
7554 try bw.writeByte('<');
7693 try w.writeByte('<');
75557694 for (0..len) |index| {
7556 if (index > 0) try bw.writeAll(", ");
7557 try bw.print("{f%}", .{extra.value.fmt(data.builder)});
7695 if (index > 0) try w.writeAll(", ");
7696 try w.print("{f}", .{extra.value.fmt(data.builder, .{ .percent = true })});
75587697 }
7559 try bw.writeByte('>');
7698 try w.writeByte('>');
75607699 },
7561 .string => try bw.print("c{f\"}", .{
7562 @as(String, @enumFromInt(item.data)).fmt(data.builder),
7700 .string => try w.print("c{f}", .{
7701 @as(String, @enumFromInt(item.data)).fmtQ(data.builder),
75637702 }),
75647703 .blockaddress => |tag| {
75657704 const extra = data.builder.constantExtraData(BlockAddress, item.data);
75667705 const function = extra.function.ptrConst(data.builder);
7567 try bw.print("{s}({f}, {f})", .{
7706 try w.print("{s}({f}, {f})", .{
75687707 @tagName(tag),
75697708 function.global.fmt(data.builder),
7570 extra.block.toInst(function).fmt(extra.function, data.builder),
7709 extra.block.toInst(function).fmt(extra.function, data.builder, .{}),
75717710 });
75727711 },
75737712 .dso_local_equivalent,
75747713 .no_cfi,
75757714 => |tag| {
75767715 const function: Function.Index = @enumFromInt(item.data);
7577 try bw.print("{s} {f}", .{
7716 try w.print("{s} {f}", .{
75787717 @tagName(tag),
75797718 function.ptrConst(data.builder).global.fmt(data.builder),
75807719 });
......@@ -7586,10 +7725,10 @@ pub const Constant = enum(u32) {
75867725 .addrspacecast,
75877726 => |tag| {
75887727 const extra = data.builder.constantExtraData(Cast, item.data);
7589 try bw.print("{s} ({f%} to {f%})", .{
7728 try w.print("{s} ({f} to {f})", .{
75907729 @tagName(tag),
7591 extra.val.fmt(data.builder),
7592 extra.type.fmt(data.builder),
7730 extra.val.fmt(data.builder, .{ .percent = true }),
7731 extra.type.fmt(data.builder, .percent),
75937732 });
75947733 },
75957734 .getelementptr,
......@@ -7598,13 +7737,13 @@ pub const Constant = enum(u32) {
75987737 var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data);
75997738 const indices =
76007739 extra.trail.next(extra.data.info.indices_len, Constant, data.builder);
7601 try bw.print("{s} ({f%}, {f%}", .{
7740 try w.print("{s} ({f}, {f}", .{
76027741 @tagName(tag),
7603 extra.data.type.fmt(data.builder),
7604 extra.data.base.fmt(data.builder),
7742 extra.data.type.fmt(data.builder, .percent),
7743 extra.data.base.fmt(data.builder, .{ .percent = true }),
76057744 });
7606 for (indices) |index| try bw.print(", {f%}", .{index.fmt(data.builder)});
7607 try bw.writeByte(')');
7745 for (indices) |index| try w.print(", {f}", .{index.fmt(data.builder, .{ .percent = true })});
7746 try w.writeByte(')');
76087747 },
76097748 .add,
76107749 .@"add nsw",
......@@ -7616,10 +7755,10 @@ pub const Constant = enum(u32) {
76167755 .xor,
76177756 => |tag| {
76187757 const extra = data.builder.constantExtraData(Binary, item.data);
7619 try bw.print("{s} ({f%}, {f%})", .{
7758 try w.print("{s} ({f}, {f})", .{
76207759 @tagName(tag),
7621 extra.lhs.fmt(data.builder),
7622 extra.rhs.fmt(data.builder),
7760 extra.lhs.fmt(data.builder, .{ .percent = true }),
7761 extra.rhs.fmt(data.builder, .{ .percent = true }),
76237762 });
76247763 },
76257764 .@"asm",
......@@ -7640,19 +7779,23 @@ pub const Constant = enum(u32) {
76407779 .@"asm sideeffect alignstack inteldialect unwind",
76417780 => |tag| {
76427781 const extra = data.builder.constantExtraData(Assembly, item.data);
7643 try bw.print("{s} {f\"}, {f\"}", .{
7782 try w.print("{s} {f}, {f}", .{
76447783 @tagName(tag),
7645 extra.assembly.fmt(data.builder),
7646 extra.constraints.fmt(data.builder),
7784 extra.assembly.fmtQ(data.builder),
7785 extra.constraints.fmtQ(data.builder),
76477786 });
76487787 },
76497788 }
76507789 },
7651 .global => |global| try bw.print("{f}", .{global.fmt(data.builder)}),
7790 .global => |global| try w.print("{f}", .{global.fmt(data.builder)}),
76527791 }
76537792 }
7654 pub fn fmt(self: Constant, builder: *Builder) std.fmt.Formatter(format) {
7655 return .{ .data = .{ .constant = self, .builder = builder } };
7793 pub fn fmt(self: Constant, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) {
7794 return .{ .data = .{
7795 .constant = self,
7796 .builder = builder,
7797 .flags = flags,
7798 } };
76567799 }
76577800};
76587801
......@@ -7707,23 +7850,26 @@ pub const Value = enum(u32) {
77077850 value: Value,
77087851 function: Function.Index,
77097852 builder: *Builder,
7853 flags: FormatFlags,
77107854 };
7711 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
7855 fn format(data: FormatData, w: *Writer) Writer.Error!void {
77127856 switch (data.value.unwrap()) {
77137857 .instruction => |instruction| try Function.Instruction.Index.format(.{
77147858 .instruction = instruction,
77157859 .function = data.function,
77167860 .builder = data.builder,
7717 }, bw, fmt_str),
7861 .flags = data.flags,
7862 }, w),
77187863 .constant => |constant| try Constant.format(.{
77197864 .constant = constant,
77207865 .builder = data.builder,
7721 }, bw, fmt_str),
7866 .flags = data.flags,
7867 }, w),
77227868 .metadata => unreachable,
77237869 }
77247870 }
7725 pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) {
7726 return .{ .data = .{ .value = self, .function = function, .builder = builder } };
7871 pub fn fmt(self: Value, function: Function.Index, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) {
7872 return .{ .data = .{ .value = self, .function = function, .builder = builder, .flags = flags } };
77277873 }
77287874};
77297875
......@@ -7753,10 +7899,10 @@ pub const MetadataString = enum(u32) {
77537899 metadata_string: MetadataString,
77547900 builder: *const Builder,
77557901 };
7756 fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void {
7757 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw);
7902 fn format(data: FormatData, w: *Writer) Writer.Error!void {
7903 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, w);
77587904 }
7759 fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) {
7905 fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(FormatData, format) {
77607906 return .{ .data = .{ .metadata_string = self, .builder = builder } };
77617907 }
77627908};
......@@ -7918,24 +8064,24 @@ pub const Metadata = enum(u32) {
79188064 AllCallsDescribed: bool = false,
79198065 Unused: u2 = 0,
79208066
7921 pub fn format(self: DIFlags, bw: *Writer, comptime _: []const u8) Writer.Error!void {
8067 pub fn format(self: DIFlags, w: *Writer) Writer.Error!void {
79228068 var need_pipe = false;
79238069 inline for (@typeInfo(DIFlags).@"struct".fields) |field| {
79248070 switch (@typeInfo(field.type)) {
79258071 .bool => if (@field(self, field.name)) {
7926 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;
7927 try bw.print("DIFlag{s}", .{field.name});
8072 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
8073 try w.print("DIFlag{s}", .{field.name});
79288074 },
79298075 .@"enum" => if (@field(self, field.name) != .Zero) {
7930 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;
7931 try bw.print("DIFlag{s}", .{@tagName(@field(self, field.name))});
8076 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
8077 try w.print("DIFlag{s}", .{@tagName(@field(self, field.name))});
79328078 },
79338079 .int => assert(@field(self, field.name) == 0),
79348080 else => @compileError("bad field type: " ++ field.name ++ ": " ++
79358081 @typeName(field.type)),
79368082 }
79378083 }
7938 if (!need_pipe) try bw.writeByte('0');
8084 if (!need_pipe) try w.writeByte('0');
79398085 }
79408086 };
79418087
......@@ -7975,24 +8121,24 @@ pub const Metadata = enum(u32) {
79758121 ObjCDirect: bool = false,
79768122 Unused: u20 = 0,
79778123
7978 pub fn format(self: DISPFlags, bw: *Writer, comptime _: []const u8) Writer.Error!void {
8124 pub fn format(self: DISPFlags, w: *Writer) Writer.Error!void {
79798125 var need_pipe = false;
79808126 inline for (@typeInfo(DISPFlags).@"struct".fields) |field| {
79818127 switch (@typeInfo(field.type)) {
79828128 .bool => if (@field(self, field.name)) {
7983 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;
7984 try bw.print("DISPFlag{s}", .{field.name});
8129 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
8130 try w.print("DISPFlag{s}", .{field.name});
79858131 },
79868132 .@"enum" => if (@field(self, field.name) != .Zero) {
7987 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;
7988 try bw.print("DISPFlag{s}", .{@tagName(@field(self, field.name))});
8133 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
8134 try w.print("DISPFlag{s}", .{@tagName(@field(self, field.name))});
79898135 },
79908136 .int => assert(@field(self, field.name) == 0),
79918137 else => @compileError("bad field type: " ++ field.name ++ ": " ++
79928138 @typeName(field.type)),
79938139 }
79948140 }
7995 if (!need_pipe) try bw.writeByte('0');
8141 if (!need_pipe) try w.writeByte('0');
79968142 }
79978143 };
79988144
......@@ -8167,6 +8313,7 @@ pub const Metadata = enum(u32) {
81678313 formatter: *Formatter,
81688314 prefix: []const u8 = "",
81698315 node: Node,
8316 specialized: ?FormatFlags,
81708317
81718318 const Node = union(enum) {
81728319 none,
......@@ -8192,15 +8339,14 @@ pub const Metadata = enum(u32) {
81928339 };
81938340 };
81948341 };
8195 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {
8342 fn format(data: FormatData, w: *Writer) Writer.Error!void {
81968343 if (data.node == .none) return;
81978344
8198 const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S';
8199 const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str;
8345 const is_specialized = data.specialized != null;
82008346
8201 if (data.formatter.need_comma) try bw.writeAll(", ");
8347 if (data.formatter.need_comma) try w.writeAll(", ");
82028348 defer data.formatter.need_comma = true;
8203 try bw.writeAll(data.prefix);
8349 try w.writeAll(data.prefix);
82048350
82058351 const builder = data.formatter.builder;
82068352 switch (data.node) {
......@@ -8215,50 +8361,57 @@ pub const Metadata = enum(u32) {
82158361 .expression => {
82168362 var extra = builder.metadataExtraDataTrail(Expression, item.data);
82178363 const elements = extra.trail.next(extra.data.elements_len, u32, builder);
8218 try bw.writeAll("!DIExpression(");
8364 try w.writeAll("!DIExpression(");
82198365 for (elements) |element| try format(.{
82208366 .formatter = data.formatter,
82218367 .node = .{ .u64 = element },
8222 }, bw, "%");
8223 try bw.writeByte(')');
8368 .specialized = .{ .percent = true },
8369 }, w);
8370 try w.writeByte(')');
82248371 },
82258372 .constant => try Constant.format(.{
82268373 .constant = @enumFromInt(item.data),
82278374 .builder = builder,
8228 }, bw, recurse_fmt_str),
8375 .flags = data.specialized orelse .{},
8376 }, w),
82298377 else => unreachable,
82308378 }
82318379 },
8232 .index => |node| try bw.print("!{d}", .{node}),
8380 .index => |node| try w.print("!{d}", .{node}),
82338381 inline .local_value, .local_metadata => |node, tag| try Value.format(.{
82348382 .value = node.value,
82358383 .function = node.function,
82368384 .builder = builder,
8237 }, bw, switch (tag) {
8238 .local_value => recurse_fmt_str,
8239 .local_metadata => "%",
8240 else => unreachable,
8241 }),
8385 .flags = switch (tag) {
8386 .local_value => data.specialized orelse .{},
8387 .local_metadata => .{ .percent = true },
8388 else => unreachable,
8389 },
8390 }, w),
82428391 inline .local_inline, .local_index => |node, tag| {
8243 if (comptime std.mem.eql(u8, recurse_fmt_str, "%"))
8244 try bw.print("{f%} ", .{Type.metadata.fmt(builder)});
8392 if (data.specialized) |flags| {
8393 if (flags.onlyPercent()) {
8394 try w.print("{f} ", .{Type.metadata.fmt(builder, .percent)});
8395 }
8396 }
82458397 try format(.{
82468398 .formatter = data.formatter,
82478399 .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node),
8248 }, bw, "%");
8400 .specialized = .{ .percent = true },
8401 }, w);
82498402 },
8250 .string => |node| try bw.print((if (is_specialized) "" else "!") ++ "{f}", .{
8251 node.fmt(builder),
8403 .string => |node| try w.print("{s}{f}", .{
8404 @as([]const u8, if (is_specialized) "" else "!"), node.fmt(builder),
82528405 }),
8253 inline .bool, .u32, .u64 => |node| try bw.print("{}", .{node}),
8254 inline .di_flags, .sp_flags => |node| try bw.print("{f}", .{node}),
8255 .raw => |node| try bw.writeAll(node),
8406 inline .bool, .u32, .u64 => |node| try w.print("{}", .{node}),
8407 inline .di_flags, .sp_flags => |node| try w.print("{f}", .{node}),
8408 .raw => |node| try w.writeAll(node),
82568409 }
82578410 }
8258 inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype) switch (@TypeOf(node)) {
8411 inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype, special: ?FormatFlags) switch (@TypeOf(node)) {
82598412 Metadata => Allocator.Error,
82608413 else => error{},
8261 }!std.fmt.Formatter(format) {
8414 }!std.fmt.Formatter(FormatData, format) {
82628415 const Node = @TypeOf(node);
82638416 const MaybeNode = switch (@typeInfo(Node)) {
82648417 .optional => Node,
......@@ -8295,6 +8448,7 @@ pub const Metadata = enum(u32) {
82958448 .optional, .null => .none,
82968449 else => unreachable,
82978450 },
8451 .specialized = special,
82988452 } };
82998453 }
83008454 inline fn fmtLocal(
......@@ -8302,7 +8456,7 @@ pub const Metadata = enum(u32) {
83028456 prefix: []const u8,
83038457 value: Value,
83048458 function: Function.Index,
8305 ) Allocator.Error!std.fmt.Formatter(format) {
8459 ) Allocator.Error!std.fmt.Formatter(FormatData, format) {
83068460 return .{ .data = .{
83078461 .formatter = formatter,
83088462 .prefix = prefix,
......@@ -8327,6 +8481,7 @@ pub const Metadata = enum(u32) {
83278481 };
83288482 },
83298483 },
8484 .specialized = null,
83308485 } };
83318486 }
83328487 fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node {
......@@ -8366,7 +8521,7 @@ pub const Metadata = enum(u32) {
83668521 DIGlobalVariableExpression,
83678522 },
83688523 nodes: anytype,
8369 bw: *Writer,
8524 w: *Writer,
83708525 ) !void {
83718526 comptime var fmt_str: []const u8 = "";
83728527 const names = comptime std.meta.fieldNames(@TypeOf(nodes));
......@@ -8383,10 +8538,10 @@ pub const Metadata = enum(u32) {
83838538 }
83848539 fmt_str = fmt_str ++ "(";
83858540 inline for (fields[2..], names) |*field, name| {
8386 fmt_str = fmt_str ++ "{[" ++ name ++ "]fS}";
8541 fmt_str = fmt_str ++ "{[" ++ name ++ "]f}";
83878542 field.* = .{
83888543 .name = name,
8389 .type = std.fmt.Formatter(format),
8544 .type = std.fmt.Formatter(FormatData, format),
83908545 .default_value_ptr = null,
83918546 .is_comptime = false,
83928547 .alignment = 0,
......@@ -8405,8 +8560,9 @@ pub const Metadata = enum(u32) {
84058560 inline for (names) |name| @field(fmt_args, name) = try formatter.fmt(
84068561 name ++ ": ",
84078562 @field(nodes, name),
8563 null,
84088564 );
8409 try bw.print(fmt_str, fmt_args);
8565 try w.print(fmt_str, fmt_args);
84108566 }
84118567 };
84128568};
......@@ -8496,7 +8652,7 @@ pub fn init(options: Options) Allocator.Error!Builder {
84968652 inline for (.{ 0, 4 }) |addr_space_index| {
84978653 const addr_space: AddrSpace = @enumFromInt(addr_space_index);
84988654 assert(self.ptrTypeAssumeCapacity(addr_space) ==
8499 @field(Type, std.fmt.comptimePrint("ptr{f }", .{addr_space})));
8655 @field(Type, std.fmt.comptimePrint("ptr{f}", .{addr_space.fmt(" ")})));
85008656 }
85018657 }
85028658
......@@ -8619,7 +8775,7 @@ pub fn deinit(self: *Builder) void {
86198775 self.* = undefined;
86208776}
86218777
8622pub fn finishModuleAsm(self: *Builder, aw: *std.io.Writer.Allocating) Allocator.Error!void {
8778pub fn finishModuleAsm(self: *Builder, aw: *Writer.Allocating) Allocator.Error!void {
86238779 self.module_asm = aw.toArrayList();
86248780 if (self.module_asm.getLastOrNull()) |last| if (last != '\n')
86258781 try self.module_asm.append(self.gpa, '\n');
......@@ -8929,11 +9085,11 @@ pub fn getIntrinsic(
89299085
89309086 const name = name: {
89319087 {
8932 var aw: std.io.Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes);
8933 const bw = &aw.interface;
9088 var aw: Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes);
9089 const w = &aw.writer;
89349090 defer self.strtab_string_bytes = aw.toArrayList();
8935 bw.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory;
8936 for (overload) |ty| bw.print(".{fm}", .{ty.fmt(self)}) catch return error.OutOfMemory;
9091 w.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory;
9092 for (overload) |ty| w.print(".{f}", .{ty.fmt(self, .m)}) catch return error.OutOfMemory;
89379093 }
89389094 break :name try self.trailingStrtabString();
89399095 };
......@@ -9348,110 +9504,105 @@ pub fn asmValue(
93489504 return (try self.asmConst(ty, info, assembly, constraints)).toValue();
93499505}
93509506
9351pub fn dump(self: *Builder) void {
9507pub fn dump(b: *Builder) void {
9508 var buffer: [4000]u8 = undefined;
93529509 const stderr: std.fs.File = .stderr();
9353 self.printBuffered(stderr.writer()) catch {};
9510 b.printToFile(stderr, &buffer) catch {};
93549511}
93559512
9356pub fn printToFile(self: *Builder, path: []const u8) bool {
9357 var file = std.fs.cwd().createFile(path, .{}) catch |err| {
9358 log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) });
9359 return false;
9360 };
9513pub fn printToFilePath(b: *Builder, dir: std.fs.Dir, path: []const u8) !void {
9514 var buffer: [4000]u8 = undefined;
9515 const file = try dir.createFile(path, .{});
93619516 defer file.close();
9362 self.printBuffered(file.writer()) catch |err| {
9363 log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) });
9364 return false;
9365 };
9366 return true;
9517 try b.printToFile(file, &buffer);
93679518}
93689519
9369pub fn printBuffered(self: *Builder, writer: Writer) Writer.Error!void {
9370 var buffer: [4096]u8 = undefined;
9371 var bw = writer.buffered(&buffer);
9372 try self.print(&bw);
9373 try bw.flush();
9520pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void {
9521 var fw = file.writer(buffer);
9522 try print(b, &fw.interface);
9523 try fw.interface.flush();
93749524}
93759525
9376pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9526pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void {
93779527 var need_newline = false;
93789528 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
93799529 defer metadata_formatter.map.deinit(self.gpa);
93809530
93819531 if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) {
9382 if (need_newline) try bw.writeByte('\n') else need_newline = true;
9383 if (self.source_filename != .none) try bw.print(
9532 if (need_newline) try w.writeByte('\n') else need_newline = true;
9533 if (self.source_filename != .none) try w.print(
93849534 \\; ModuleID = '{s}'
9385 \\source_filename = {f"}
9535 \\source_filename = {f}
93869536 \\
9387 , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) });
9388 if (self.data_layout != .none) try bw.print(
9389 \\target datalayout = {f"}
9537 , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) });
9538 if (self.data_layout != .none) try w.print(
9539 \\target datalayout = {f}
93909540 \\
9391 , .{self.data_layout.fmt(self)});
9392 if (self.target_triple != .none) try bw.print(
9393 \\target triple = {f"}
9541 , .{self.data_layout.fmtQ(self)});
9542 if (self.target_triple != .none) try w.print(
9543 \\target triple = {f}
93949544 \\
9395 , .{self.target_triple.fmt(self)});
9545 , .{self.target_triple.fmtQ(self)});
93969546 }
93979547
93989548 if (self.module_asm.items.len > 0) {
9399 if (need_newline) try bw.writeByte('\n') else need_newline = true;
9549 if (need_newline) try w.writeByte('\n') else need_newline = true;
94009550 var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n');
94019551 while (line_it.next()) |line| {
9402 try bw.writeAll("module asm ");
9403 try printEscapedString(line, .always_quote, bw);
9404 try bw.writeByte('\n');
9552 try w.writeAll("module asm ");
9553 try printEscapedString(line, .always_quote, w);
9554 try w.writeByte('\n');
94059555 }
94069556 }
94079557
94089558 if (self.types.count() > 0) {
9409 if (need_newline) try bw.writeByte('\n') else need_newline = true;
9410 for (self.types.keys(), self.types.values()) |id, ty| try bw.print(
9559 if (need_newline) try w.writeByte('\n') else need_newline = true;
9560 for (self.types.keys(), self.types.values()) |id, ty| try w.print(
94119561 \\%{f} = type {f}
94129562 \\
9413 , .{ id.fmt(self), ty.fmt(self) });
9563 , .{ id.fmt(self), ty.fmt(self, .default) });
94149564 }
94159565
94169566 if (self.variables.items.len > 0) {
9417 if (need_newline) try bw.writeByte('\n') else need_newline = true;
9567 if (need_newline) try w.writeByte('\n') else need_newline = true;
94189568 for (self.variables.items) |variable| {
94199569 if (variable.global.getReplacement(self) != .none) continue;
94209570 const global = variable.global.ptrConst(self);
94219571 metadata_formatter.need_comma = true;
94229572 defer metadata_formatter.need_comma = undefined;
9423 try bw.print(
9424 \\{f} ={f}{f}{f}{f}{f }{f}{f }{f} {s} {f%}{f }{f, }{f}
9573 try w.print(
9574 \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f}
94259575 \\
94269576 , .{
94279577 variable.global.fmt(self),
9428 Linkage.fmtOptional(if (global.linkage == .external and
9429 variable.init != .no_init) null else global.linkage),
9578 Linkage.fmtOptional(
9579 if (global.linkage == .external and variable.init != .no_init) null else global.linkage,
9580 ),
94309581 global.preemption,
94319582 global.visibility,
94329583 global.dll_storage_class,
9433 variable.thread_local,
9584 variable.thread_local.fmt(" "),
94349585 global.unnamed_addr,
9435 global.addr_space,
9586 global.addr_space.fmt(" "),
94369587 global.externally_initialized,
94379588 @tagName(variable.mutability),
9438 global.type.fmt(self),
9439 variable.init.fmt(self),
9440 variable.alignment,
9441 try metadata_formatter.fmt("!dbg ", global.dbg),
9589 global.type.fmt(self, .percent),
9590 variable.init.fmt(self, .{ .space = true }),
9591 variable.alignment.fmt(", "),
9592 try metadata_formatter.fmt("!dbg ", global.dbg, null),
94429593 });
94439594 }
94449595 }
94459596
94469597 if (self.aliases.items.len > 0) {
9447 if (need_newline) try bw.writeByte('\n') else need_newline = true;
9598 if (need_newline) try w.writeByte('\n') else need_newline = true;
94489599 for (self.aliases.items) |alias| {
94499600 if (alias.global.getReplacement(self) != .none) continue;
94509601 const global = alias.global.ptrConst(self);
94519602 metadata_formatter.need_comma = true;
94529603 defer metadata_formatter.need_comma = undefined;
9453 try bw.print(
9454 \\{f} ={f}{f}{f}{f}{f }{f} alias {f%}, {f%}{f}
9604 try w.print(
9605 \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f}
94559606 \\
94569607 , .{
94579608 alias.global.fmt(self),
......@@ -9459,11 +9610,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
94599610 global.preemption,
94609611 global.visibility,
94619612 global.dll_storage_class,
9462 alias.thread_local,
9613 alias.thread_local.fmt(" "),
94639614 global.unnamed_addr,
9464 global.type.fmt(self),
9465 alias.aliasee.fmt(self),
9466 try metadata_formatter.fmt("!dbg ", global.dbg),
9615 global.type.fmt(self, .percent),
9616 alias.aliasee.fmt(self, .{ .percent = true }),
9617 try metadata_formatter.fmt("!dbg ", global.dbg, null),
94679618 });
94689619 }
94699620 }
......@@ -9473,17 +9624,17 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
94739624
94749625 for (0.., self.functions.items) |function_i, function| {
94759626 if (function.global.getReplacement(self) != .none) continue;
9476 if (need_newline) try bw.writeByte('\n') else need_newline = true;
9627 if (need_newline) try w.writeByte('\n') else need_newline = true;
94779628 const function_index: Function.Index = @enumFromInt(function_i);
94789629 const global = function.global.ptrConst(self);
94799630 const params_len = global.type.functionParameters(self).len;
94809631 const function_attributes = function.attributes.func(self);
9481 if (function_attributes != .none) try bw.print(
9632 if (function_attributes != .none) try w.print(
94829633 \\; Function Attrs:{f}
94839634 \\
9484 , .{function_attributes.fmt(self)});
9485 try bw.print(
9486 \\{s}{f}{f}{f}{f}{f}{f"} {f%} {f}(
9635 , .{function_attributes.fmt(self, .{})});
9636 try w.print(
9637 \\{s}{f}{f}{f}{f}{f}{f} {f} {f}(
94879638 , .{
94889639 if (function.instructions.len > 0) "define" else "declare",
94899640 global.linkage,
......@@ -9491,45 +9642,45 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
94919642 global.visibility,
94929643 global.dll_storage_class,
94939644 function.call_conv,
9494 function.attributes.ret(self).fmt(self),
9495 global.type.functionReturn(self).fmt(self),
9645 function.attributes.ret(self).fmt(self, .{}),
9646 global.type.functionReturn(self).fmt(self, .percent),
94969647 function.global.fmt(self),
94979648 });
94989649 for (0..params_len) |arg| {
9499 if (arg > 0) try bw.writeAll(", ");
9500 try bw.print(
9501 \\{f%}{f"}
9650 if (arg > 0) try w.writeAll(", ");
9651 try w.print(
9652 \\{f}{f}
95029653 , .{
9503 global.type.functionParameters(self)[arg].fmt(self),
9504 function.attributes.param(arg, self).fmt(self),
9654 global.type.functionParameters(self)[arg].fmt(self, .percent),
9655 function.attributes.param(arg, self).fmt(self, .{}),
95059656 });
95069657 if (function.instructions.len > 0)
9507 try bw.print(" {f}", .{function.arg(@intCast(arg)).fmt(function_index, self)})
9658 try w.print(" {f}", .{function.arg(@intCast(arg)).fmt(function_index, self, .{})})
95089659 else
9509 try bw.print(" %{d}", .{arg});
9660 try w.print(" %{d}", .{arg});
95109661 }
95119662 switch (global.type.functionKind(self)) {
95129663 .normal => {},
95139664 .vararg => {
9514 if (params_len > 0) try bw.writeAll(", ");
9515 try bw.writeAll("...");
9665 if (params_len > 0) try w.writeAll(", ");
9666 try w.writeAll("...");
95169667 },
95179668 }
9518 try bw.print("){f}{f }", .{ global.unnamed_addr, global.addr_space });
9519 if (function_attributes != .none) try bw.print(" #{d}", .{
9669 try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") });
9670 if (function_attributes != .none) try w.print(" #{d}", .{
95209671 (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index,
95219672 });
95229673 {
95239674 metadata_formatter.need_comma = false;
95249675 defer metadata_formatter.need_comma = undefined;
9525 try bw.print("{f }{f}", .{
9526 function.alignment,
9527 try metadata_formatter.fmt(" !dbg ", global.dbg),
9676 try w.print("{f}{f}", .{
9677 function.alignment.fmt(" "),
9678 try metadata_formatter.fmt(" !dbg ", global.dbg, null),
95289679 });
95299680 }
95309681 if (function.instructions.len > 0) {
95319682 var block_incoming_len: u32 = undefined;
9532 try bw.writeAll(" {\n");
9683 try w.writeAll(" {\n");
95339684 var maybe_dbg_index: ?u32 = null;
95349685 for (params_len..function.instructions.len) |instruction_i| {
95359686 const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i);
......@@ -9627,11 +9778,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
96279778 .xor,
96289779 => |tag| {
96299780 const extra = function.extraData(Function.Instruction.Binary, instruction.data);
9630 try bw.print(" %{f} = {s} {f%}, {f}", .{
9781 try w.print(" %{f} = {s} {f}, {f}", .{
96319782 instruction_index.name(&function).fmt(self),
96329783 @tagName(tag),
9633 extra.lhs.fmt(function_index, self),
9634 extra.rhs.fmt(function_index, self),
9784 extra.lhs.fmt(function_index, self, .{ .percent = true }),
9785 extra.rhs.fmt(function_index, self, .{ .percent = true }),
96359786 });
96369787 },
96379788 .addrspacecast,
......@@ -9649,73 +9800,76 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
96499800 .zext,
96509801 => |tag| {
96519802 const extra = function.extraData(Function.Instruction.Cast, instruction.data);
9652 try bw.print(" %{f} = {s} {f%} to {f%}", .{
9803 try w.print(" %{f} = {s} {f} to {f}", .{
96539804 instruction_index.name(&function).fmt(self),
96549805 @tagName(tag),
9655 extra.val.fmt(function_index, self),
9656 extra.type.fmt(self),
9806 extra.val.fmt(function_index, self, .{ .percent = true }),
9807 extra.type.fmt(self, .percent),
96579808 });
96589809 },
96599810 .alloca,
96609811 .@"alloca inalloca",
96619812 => |tag| {
96629813 const extra = function.extraData(Function.Instruction.Alloca, instruction.data);
9663 try bw.print(" %{f} = {s} {f%}{f,%}{f, }{f, }", .{
9814 try w.print(" %{f} = {s} {f}{f}{f}{f}", .{
96649815 instruction_index.name(&function).fmt(self),
96659816 @tagName(tag),
9666 extra.type.fmt(self),
9817 extra.type.fmt(self, .percent),
96679818 Value.fmt(switch (extra.len) {
96689819 .@"1" => .none,
96699820 else => extra.len,
9670 }, function_index, self),
9671 extra.info.alignment,
9672 extra.info.addr_space,
9821 }, function_index, self, .{
9822 .comma = true,
9823 .percent = true,
9824 }),
9825 extra.info.alignment.fmt(", "),
9826 extra.info.addr_space.fmt(", "),
96739827 });
96749828 },
96759829 .arg => unreachable,
96769830 .atomicrmw => |tag| {
96779831 const extra =
96789832 function.extraData(Function.Instruction.AtomicRmw, instruction.data);
9679 try bw.print(" %{f} = {s}{f } {s} {f%}, {f%}{f }{f }{f, }", .{
9833 try w.print(" %{f} = {t}{f} {t} {f}, {f}{f}{f}{f}", .{
96809834 instruction_index.name(&function).fmt(self),
9681 @tagName(tag),
9682 extra.info.access_kind,
9683 @tagName(extra.info.atomic_rmw_operation),
9684 extra.ptr.fmt(function_index, self),
9685 extra.val.fmt(function_index, self),
9686 extra.info.sync_scope,
9687 extra.info.success_ordering,
9688 extra.info.alignment,
9835 tag,
9836 extra.info.access_kind.fmt(" "),
9837 extra.info.atomic_rmw_operation,
9838 extra.ptr.fmt(function_index, self, .{ .percent = true }),
9839 extra.val.fmt(function_index, self, .{ .percent = true }),
9840 extra.info.sync_scope.fmt(" "),
9841 extra.info.success_ordering.fmt(" "),
9842 extra.info.alignment.fmt(", "),
96899843 });
96909844 },
96919845 .block => {
96929846 block_incoming_len = instruction.data;
96939847 const name = instruction_index.name(&function);
96949848 if (@intFromEnum(instruction_index) > params_len)
9695 try bw.writeByte('\n');
9696 try bw.print("{f}:\n", .{name.fmt(self)});
9849 try w.writeByte('\n');
9850 try w.print("{f}:\n", .{name.fmt(self)});
96979851 continue;
96989852 },
96999853 .br => |tag| {
97009854 const target: Function.Block.Index = @enumFromInt(instruction.data);
9701 try bw.print(" {s} {f%}", .{
9702 @tagName(tag), target.toInst(&function).fmt(function_index, self),
9855 try w.print(" {s} {f}", .{
9856 @tagName(tag), target.toInst(&function).fmt(function_index, self, .{ .percent = true }),
97039857 });
97049858 },
97059859 .br_cond => {
97069860 const extra = function.extraData(Function.Instruction.BrCond, instruction.data);
9707 try bw.print(" br {f%}, {f%}, {f%}", .{
9708 extra.cond.fmt(function_index, self),
9709 extra.then.toInst(&function).fmt(function_index, self),
9710 extra.@"else".toInst(&function).fmt(function_index, self),
9861 try w.print(" br {f}, {f}, {f}", .{
9862 extra.cond.fmt(function_index, self, .{ .percent = true }),
9863 extra.then.toInst(&function).fmt(function_index, self, .{ .percent = true }),
9864 extra.@"else".toInst(&function).fmt(function_index, self, .{ .percent = true }),
97119865 });
97129866 metadata_formatter.need_comma = true;
97139867 defer metadata_formatter.need_comma = undefined;
97149868 switch (extra.weights) {
97159869 .none => {},
9716 .unpredictable => try bw.writeAll("!unpredictable !{}"),
9717 _ => try bw.print("{f}", .{
9718 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))),
9870 .unpredictable => try w.writeAll("!unpredictable !{}"),
9871 _ => try w.print("{f}", .{
9872 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights))), null),
97199873 }),
97209874 }
97219875 },
......@@ -9731,42 +9885,42 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
97319885 var extra =
97329886 function.extraDataTrail(Function.Instruction.Call, instruction.data);
97339887 const args = extra.trail.next(extra.data.args_len, Value, &function);
9734 try bw.writeAll(" ");
9888 try w.writeAll(" ");
97359889 const ret_ty = extra.data.ty.functionReturn(self);
97369890 switch (ret_ty) {
97379891 .void => {},
9738 else => try bw.print("%{f} = ", .{
9892 else => try w.print("%{f} = ", .{
97399893 instruction_index.name(&function).fmt(self),
97409894 }),
97419895 .none => unreachable,
97429896 }
9743 try bw.print("{s}{f}{f}{f} {f%} {f}(", .{
9744 @tagName(tag),
9897 try w.print("{t}{f}{f}{f} {f} {f}(", .{
9898 tag,
97459899 extra.data.info.call_conv,
9746 extra.data.attributes.ret(self).fmt(self),
9900 extra.data.attributes.ret(self).fmt(self, .{}),
97479901 extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self),
97489902 switch (extra.data.ty.functionKind(self)) {
97499903 .normal => ret_ty,
97509904 .vararg => extra.data.ty,
9751 }.fmt(self),
9752 extra.data.callee.fmt(function_index, self),
9905 }.fmt(self, .percent),
9906 extra.data.callee.fmt(function_index, self, .{}),
97539907 });
97549908 for (0.., args) |arg_index, arg| {
9755 if (arg_index > 0) try bw.writeAll(", ");
9909 if (arg_index > 0) try w.writeAll(", ");
97569910 metadata_formatter.need_comma = false;
97579911 defer metadata_formatter.need_comma = undefined;
9758 try bw.print("{f%}{f}{f}", .{
9759 arg.typeOf(function_index, self).fmt(self),
9760 extra.data.attributes.param(arg_index, self).fmt(self),
9912 try w.print("{f}{f}{f}", .{
9913 arg.typeOf(function_index, self).fmt(self, .percent),
9914 extra.data.attributes.param(arg_index, self).fmt(self, .{}),
97619915 try metadata_formatter.fmtLocal(" ", arg, function_index),
97629916 });
97639917 }
9764 try bw.writeByte(')');
9918 try w.writeByte(')');
97659919 if (extra.data.info.has_op_bundle_cold) {
9766 try bw.writeAll(" [ \"cold\"() ]");
9920 try w.writeAll(" [ \"cold\"() ]");
97679921 }
97689922 const call_function_attributes = extra.data.attributes.func(self);
9769 if (call_function_attributes != .none) try bw.print(" #{d}", .{
9923 if (call_function_attributes != .none) try w.print(" #{d}", .{
97709924 (try attribute_groups.getOrPutValue(
97719925 self.gpa,
97729926 call_function_attributes,
......@@ -9779,27 +9933,27 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
97799933 => |tag| {
97809934 const extra =
97819935 function.extraData(Function.Instruction.CmpXchg, instruction.data);
9782 try bw.print(" %{f} = {s}{f } {f%}, {f%}, {f%}{f }{f }{f }{f, }", .{
9936 try w.print(" %{f} = {t}{f} {f}, {f}, {f}{f}{f}{f}{f}", .{
97839937 instruction_index.name(&function).fmt(self),
9784 @tagName(tag),
9785 extra.info.access_kind,
9786 extra.ptr.fmt(function_index, self),
9787 extra.cmp.fmt(function_index, self),
9788 extra.new.fmt(function_index, self),
9789 extra.info.sync_scope,
9790 extra.info.success_ordering,
9791 extra.info.failure_ordering,
9792 extra.info.alignment,
9938 tag,
9939 extra.info.access_kind.fmt(" "),
9940 extra.ptr.fmt(function_index, self, .{ .percent = true }),
9941 extra.cmp.fmt(function_index, self, .{ .percent = true }),
9942 extra.new.fmt(function_index, self, .{ .percent = true }),
9943 extra.info.sync_scope.fmt(" "),
9944 extra.info.success_ordering.fmt(" "),
9945 extra.info.failure_ordering.fmt(" "),
9946 extra.info.alignment.fmt(", "),
97939947 });
97949948 },
97959949 .extractelement => |tag| {
97969950 const extra =
97979951 function.extraData(Function.Instruction.ExtractElement, instruction.data);
9798 try bw.print(" %{f} = {s} {f%}, {f%}", .{
9952 try w.print(" %{f} = {s} {f}, {f}", .{
97999953 instruction_index.name(&function).fmt(self),
98009954 @tagName(tag),
9801 extra.val.fmt(function_index, self),
9802 extra.index.fmt(function_index, self),
9955 extra.val.fmt(function_index, self, .{ .percent = true }),
9956 extra.index.fmt(function_index, self, .{ .percent = true }),
98039957 });
98049958 },
98059959 .extractvalue => |tag| {
......@@ -9808,29 +9962,29 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
98089962 instruction.data,
98099963 );
98109964 const indices = extra.trail.next(extra.data.indices_len, u32, &function);
9811 try bw.print(" %{f} = {s} {f%}", .{
9965 try w.print(" %{f} = {s} {f}", .{
98129966 instruction_index.name(&function).fmt(self),
98139967 @tagName(tag),
9814 extra.data.val.fmt(function_index, self),
9968 extra.data.val.fmt(function_index, self, .{ .percent = true }),
98159969 });
9816 for (indices) |index| try bw.print(", {d}", .{index});
9970 for (indices) |index| try w.print(", {d}", .{index});
98179971 },
98189972 .fence => |tag| {
98199973 const info: MemoryAccessInfo = @bitCast(instruction.data);
9820 try bw.print(" {s}{f }{f }", .{
9821 @tagName(tag),
9822 info.sync_scope,
9823 info.success_ordering,
9974 try w.print(" {t}{f}{f}", .{
9975 tag,
9976 info.sync_scope.fmt(" "),
9977 info.success_ordering.fmt(" "),
98249978 });
98259979 },
98269980 .fneg,
98279981 .@"fneg fast",
98289982 => |tag| {
98299983 const val: Value = @enumFromInt(instruction.data);
9830 try bw.print(" %{f} = {s} {f%}", .{
9984 try w.print(" %{f} = {s} {f}", .{
98319985 instruction_index.name(&function).fmt(self),
98329986 @tagName(tag),
9833 val.fmt(function_index, self),
9987 val.fmt(function_index, self, .{ .percent = true }),
98349988 });
98359989 },
98369990 .getelementptr,
......@@ -9841,14 +9995,14 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
98419995 instruction.data,
98429996 );
98439997 const indices = extra.trail.next(extra.data.indices_len, Value, &function);
9844 try bw.print(" %{f} = {s} {f%}, {f%}", .{
9998 try w.print(" %{f} = {s} {f}, {f}", .{
98459999 instruction_index.name(&function).fmt(self),
984610000 @tagName(tag),
9847 extra.data.type.fmt(self),
9848 extra.data.base.fmt(function_index, self),
10001 extra.data.type.fmt(self, .percent),
10002 extra.data.base.fmt(function_index, self, .{ .percent = true }),
984910003 });
9850 for (indices) |index| try bw.print(", {f%}", .{
9851 index.fmt(function_index, self),
10004 for (indices) |index| try w.print(", {f}", .{
10005 index.fmt(function_index, self, .{ .percent = true }),
985210006 });
985310007 },
985410008 .indirectbr => |tag| {
......@@ -9856,54 +10010,54 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
985610010 function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data);
985710011 const targets =
985810012 extra.trail.next(extra.data.targets_len, Function.Block.Index, &function);
9859 try bw.print(" {s} {f%}, [", .{
10013 try w.print(" {s} {f}, [", .{
986010014 @tagName(tag),
9861 extra.data.addr.fmt(function_index, self),
10015 extra.data.addr.fmt(function_index, self, .{ .percent = true }),
986210016 });
986310017 for (0.., targets) |target_index, target| {
9864 if (target_index > 0) try bw.writeAll(", ");
9865 try bw.print("{f%}", .{
9866 target.toInst(&function).fmt(function_index, self),
10018 if (target_index > 0) try w.writeAll(", ");
10019 try w.print("{f}", .{
10020 target.toInst(&function).fmt(function_index, self, .{ .percent = true }),
986710021 });
986810022 }
9869 try bw.writeByte(']');
10023 try w.writeByte(']');
987010024 },
987110025 .insertelement => |tag| {
987210026 const extra =
987310027 function.extraData(Function.Instruction.InsertElement, instruction.data);
9874 try bw.print(" %{f} = {s} {f%}, {f%}, {f%}", .{
10028 try w.print(" %{f} = {s} {f}, {f}, {f}", .{
987510029 instruction_index.name(&function).fmt(self),
987610030 @tagName(tag),
9877 extra.val.fmt(function_index, self),
9878 extra.elem.fmt(function_index, self),
9879 extra.index.fmt(function_index, self),
10031 extra.val.fmt(function_index, self, .{ .percent = true }),
10032 extra.elem.fmt(function_index, self, .{ .percent = true }),
10033 extra.index.fmt(function_index, self, .{ .percent = true }),
988010034 });
988110035 },
988210036 .insertvalue => |tag| {
988310037 var extra =
988410038 function.extraDataTrail(Function.Instruction.InsertValue, instruction.data);
988510039 const indices = extra.trail.next(extra.data.indices_len, u32, &function);
9886 try bw.print(" %{f} = {s} {f%}, {f%}", .{
10040 try w.print(" %{f} = {s} {f}, {f}", .{
988710041 instruction_index.name(&function).fmt(self),
988810042 @tagName(tag),
9889 extra.data.val.fmt(function_index, self),
9890 extra.data.elem.fmt(function_index, self),
10043 extra.data.val.fmt(function_index, self, .{ .percent = true }),
10044 extra.data.elem.fmt(function_index, self, .{ .percent = true }),
989110045 });
9892 for (indices) |index| try bw.print(", {d}", .{index});
10046 for (indices) |index| try w.print(", {d}", .{index});
989310047 },
989410048 .load,
989510049 .@"load atomic",
989610050 => |tag| {
989710051 const extra = function.extraData(Function.Instruction.Load, instruction.data);
9898 try bw.print(" %{f} = {s}{f } {f%}, {f%}{f }{f }{f, }", .{
10052 try w.print(" %{f} = {t}{f} {f}, {f}{f}{f}{f}", .{
989910053 instruction_index.name(&function).fmt(self),
9900 @tagName(tag),
9901 extra.info.access_kind,
9902 extra.type.fmt(self),
9903 extra.ptr.fmt(function_index, self),
9904 extra.info.sync_scope,
9905 extra.info.success_ordering,
9906 extra.info.alignment,
10054 tag,
10055 extra.info.access_kind.fmt(" "),
10056 extra.type.fmt(self, .percent),
10057 extra.ptr.fmt(function_index, self, .{ .percent = true }),
10058 extra.info.sync_scope.fmt(" "),
10059 extra.info.success_ordering.fmt(" "),
10060 extra.info.alignment.fmt(", "),
990710061 });
990810062 },
990910063 .phi,
......@@ -9913,64 +10067,64 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
991310067 const vals = extra.trail.next(block_incoming_len, Value, &function);
991410068 const blocks =
991510069 extra.trail.next(block_incoming_len, Function.Block.Index, &function);
9916 try bw.print(" %{f} = {s} {f%} ", .{
10070 try w.print(" %{f} = {s} {f} ", .{
991710071 instruction_index.name(&function).fmt(self),
991810072 @tagName(tag),
9919 vals[0].typeOf(function_index, self).fmt(self),
10073 vals[0].typeOf(function_index, self).fmt(self, .percent),
992010074 });
992110075 for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| {
9922 if (incoming_index > 0) try bw.writeAll(", ");
9923 try bw.print("[ {f}, {f} ]", .{
9924 incoming_val.fmt(function_index, self),
9925 incoming_block.toInst(&function).fmt(function_index, self),
10076 if (incoming_index > 0) try w.writeAll(", ");
10077 try w.print("[ {f}, {f} ]", .{
10078 incoming_val.fmt(function_index, self, .{}),
10079 incoming_block.toInst(&function).fmt(function_index, self, .{}),
992610080 });
992710081 }
992810082 },
992910083 .ret => |tag| {
993010084 const val: Value = @enumFromInt(instruction.data);
9931 try bw.print(" {s} {f%}", .{
10085 try w.print(" {s} {f}", .{
993210086 @tagName(tag),
9933 val.fmt(function_index, self),
10087 val.fmt(function_index, self, .{ .percent = true }),
993410088 });
993510089 },
993610090 .@"ret void",
993710091 .@"unreachable",
9938 => |tag| try bw.print(" {s}", .{@tagName(tag)}),
10092 => |tag| try w.print(" {s}", .{@tagName(tag)}),
993910093 .select,
994010094 .@"select fast",
994110095 => |tag| {
994210096 const extra = function.extraData(Function.Instruction.Select, instruction.data);
9943 try bw.print(" %{f} = {s} {f%}, {f%}, {f%}", .{
10097 try w.print(" %{f} = {s} {f}, {f}, {f}", .{
994410098 instruction_index.name(&function).fmt(self),
994510099 @tagName(tag),
9946 extra.cond.fmt(function_index, self),
9947 extra.lhs.fmt(function_index, self),
9948 extra.rhs.fmt(function_index, self),
10100 extra.cond.fmt(function_index, self, .{ .percent = true }),
10101 extra.lhs.fmt(function_index, self, .{ .percent = true }),
10102 extra.rhs.fmt(function_index, self, .{ .percent = true }),
994910103 });
995010104 },
995110105 .shufflevector => |tag| {
995210106 const extra =
995310107 function.extraData(Function.Instruction.ShuffleVector, instruction.data);
9954 try bw.print(" %{f} = {s} {f%}, {f%}, {f%}", .{
10108 try w.print(" %{f} = {s} {f}, {f}, {f}", .{
995510109 instruction_index.name(&function).fmt(self),
995610110 @tagName(tag),
9957 extra.lhs.fmt(function_index, self),
9958 extra.rhs.fmt(function_index, self),
9959 extra.mask.fmt(function_index, self),
10111 extra.lhs.fmt(function_index, self, .{ .percent = true }),
10112 extra.rhs.fmt(function_index, self, .{ .percent = true }),
10113 extra.mask.fmt(function_index, self, .{ .percent = true }),
996010114 });
996110115 },
996210116 .store,
996310117 .@"store atomic",
996410118 => |tag| {
996510119 const extra = function.extraData(Function.Instruction.Store, instruction.data);
9966 try bw.print(" {s}{f } {f%}, {f%}{f }{f }{f, }", .{
9967 @tagName(tag),
9968 extra.info.access_kind,
9969 extra.val.fmt(function_index, self),
9970 extra.ptr.fmt(function_index, self),
9971 extra.info.sync_scope,
9972 extra.info.success_ordering,
9973 extra.info.alignment,
10120 try w.print(" {t}{f} {f}, {f}{f}{f}{f}", .{
10121 tag,
10122 extra.info.access_kind.fmt(" "),
10123 extra.val.fmt(function_index, self, .{ .percent = true }),
10124 extra.ptr.fmt(function_index, self, .{ .percent = true }),
10125 extra.info.sync_scope.fmt(" "),
10126 extra.info.success_ordering.fmt(" "),
10127 extra.info.alignment.fmt(", "),
997410128 });
997510129 },
997610130 .@"switch" => |tag| {
......@@ -9979,80 +10133,80 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
997910133 const vals = extra.trail.next(extra.data.cases_len, Constant, &function);
998010134 const blocks =
998110135 extra.trail.next(extra.data.cases_len, Function.Block.Index, &function);
9982 try bw.print(" {s} {f%}, {f%} [\n", .{
10136 try w.print(" {s} {f}, {f} [\n", .{
998310137 @tagName(tag),
9984 extra.data.val.fmt(function_index, self),
9985 extra.data.default.toInst(&function).fmt(function_index, self),
10138 extra.data.val.fmt(function_index, self, .{ .percent = true }),
10139 extra.data.default.toInst(&function).fmt(function_index, self, .{ .percent = true }),
998610140 });
9987 for (vals, blocks) |case_val, case_block| try bw.print(
9988 " {f%}, {f%}\n",
10141 for (vals, blocks) |case_val, case_block| try w.print(
10142 " {f}, {f}\n",
998910143 .{
9990 case_val.fmt(self),
9991 case_block.toInst(&function).fmt(function_index, self),
10144 case_val.fmt(self, .{ .percent = true }),
10145 case_block.toInst(&function).fmt(function_index, self, .{ .percent = true }),
999210146 },
999310147 );
9994 try bw.writeAll(" ]");
10148 try w.writeAll(" ]");
999510149 metadata_formatter.need_comma = true;
999610150 defer metadata_formatter.need_comma = undefined;
999710151 switch (extra.data.weights) {
999810152 .none => {},
9999 .unpredictable => try bw.writeAll("!unpredictable !{}"),
10000 _ => try bw.print("{f}", .{
10001 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))),
10153 .unpredictable => try w.writeAll("!unpredictable !{}"),
10154 _ => try w.print("{f}", .{
10155 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights))), null),
1000210156 }),
1000310157 }
1000410158 },
1000510159 .va_arg => |tag| {
1000610160 const extra = function.extraData(Function.Instruction.VaArg, instruction.data);
10007 try bw.print(" %{f} = {s} {f%}, {f%}", .{
10161 try w.print(" %{f} = {s} {f}, {f}", .{
1000810162 instruction_index.name(&function).fmt(self),
1000910163 @tagName(tag),
10010 extra.list.fmt(function_index, self),
10011 extra.type.fmt(self),
10164 extra.list.fmt(function_index, self, .{ .percent = true }),
10165 extra.type.fmt(self, .percent),
1001210166 });
1001310167 },
1001410168 }
1001510169
1001610170 if (maybe_dbg_index) |dbg_index| {
10017 try bw.print(", !dbg !{d}", .{dbg_index});
10171 try w.print(", !dbg !{d}", .{dbg_index});
1001810172 }
10019 try bw.writeByte('\n');
10173 try w.writeByte('\n');
1002010174 }
10021 try bw.writeByte('}');
10175 try w.writeByte('}');
1002210176 }
10023 try bw.writeByte('\n');
10177 try w.writeByte('\n');
1002410178 }
1002510179
1002610180 if (attribute_groups.count() > 0) {
10027 if (need_newline) try bw.writeByte('\n') else need_newline = true;
10181 if (need_newline) try w.writeByte('\n') else need_newline = true;
1002810182 for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group|
10029 try bw.print(
10030 \\attributes #{d} = {{{f#"} }}
10183 try w.print(
10184 \\attributes #{d} = {{{f} }}
1003110185 \\
10032 , .{ attribute_group_index, attribute_group.fmt(self) });
10186 , .{ attribute_group_index, attribute_group.fmt(self, .{ .pound = true, .quote = true }) });
1003310187 }
1003410188
1003510189 if (self.metadata_named.count() > 0) {
10036 if (need_newline) try bw.writeByte('\n') else need_newline = true;
10190 if (need_newline) try w.writeByte('\n') else need_newline = true;
1003710191 for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| {
1003810192 const elements: []const Metadata =
1003910193 @ptrCast(self.metadata_extra.items[data.index..][0..data.len]);
10040 try bw.writeByte('!');
10041 try printEscapedString(name.slice(self), .quote_unless_valid_identifier, bw);
10042 try bw.writeAll(" = !{");
10194 try w.writeByte('!');
10195 try printEscapedString(name.slice(self), .quote_unless_valid_identifier, w);
10196 try w.writeAll(" = !{");
1004310197 metadata_formatter.need_comma = false;
1004410198 defer metadata_formatter.need_comma = undefined;
10045 for (elements) |element| try bw.print("{f}", .{try metadata_formatter.fmt("", element)});
10046 try bw.writeAll("}\n");
10199 for (elements) |element| try w.print("{f}", .{try metadata_formatter.fmt("", element, null)});
10200 try w.writeAll("}\n");
1004710201 }
1004810202 }
1004910203
1005010204 if (metadata_formatter.map.count() > 0) {
10051 if (need_newline) try bw.writeByte('\n') else need_newline = true;
10205 if (need_newline) try w.writeByte('\n') else need_newline = true;
1005210206 var metadata_index: usize = 0;
1005310207 while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) {
1005410208 @setEvalBranchQuota(10_000);
10055 try bw.print("!{d} = ", .{metadata_index});
10209 try w.print("!{d} = ", .{metadata_index});
1005610210 metadata_formatter.need_comma = false;
1005710211 defer metadata_formatter.need_comma = undefined;
1005810212
......@@ -10065,7 +10219,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1006510219 .scope = location.scope,
1006610220 .inlinedAt = location.inlined_at,
1006710221 .isImplicitCode = false,
10068 }, bw);
10222 }, w);
1006910223 continue;
1007010224 },
1007110225 .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)),
......@@ -10081,7 +10235,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1008110235 .checksumkind = null,
1008210236 .checksum = null,
1008310237 .source = null,
10084 }, bw);
10238 }, w);
1008510239 },
1008610240 .compile_unit,
1008710241 .@"compile_unit optimized",
......@@ -10112,7 +10266,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1011210266 .rangesBaseAddress = null,
1011310267 .sysroot = null,
1011410268 .sdk = null,
10115 }, bw);
10269 }, w);
1011610270 },
1011710271 .subprogram,
1011810272 .@"subprogram local",
......@@ -10146,7 +10300,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1014610300 .thrownTypes = null,
1014710301 .annotations = null,
1014810302 .targetFuncName = null,
10149 }, bw);
10303 }, w);
1015010304 },
1015110305 .lexical_block => {
1015210306 const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data);
......@@ -10155,7 +10309,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1015510309 .file = extra.file,
1015610310 .line = extra.line,
1015710311 .column = extra.column,
10158 }, bw);
10312 }, w);
1015910313 },
1016010314 .location => {
1016110315 const extra = self.metadataExtraData(Metadata.Location, metadata_item.data);
......@@ -10165,7 +10319,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1016510319 .scope = extra.scope,
1016610320 .inlinedAt = extra.inlined_at,
1016710321 .isImplicitCode = false,
10168 }, bw);
10322 }, w);
1016910323 },
1017010324 .basic_bool_type,
1017110325 .basic_unsigned_type,
......@@ -10194,7 +10348,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1019410348 else => unreachable,
1019510349 }),
1019610350 .flags = null,
10197 }, bw);
10351 }, w);
1019810352 },
1019910353 .composite_struct_type,
1020010354 .composite_union_type,
......@@ -10239,7 +10393,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1023910393 .allocated = null,
1024010394 .rank = null,
1024110395 .annotations = null,
10242 }, bw);
10396 }, w);
1024310397 },
1024410398 .derived_pointer_type,
1024510399 .derived_member_type,
......@@ -10272,7 +10426,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1027210426 .extraData = null,
1027310427 .dwarfAddressSpace = null,
1027410428 .annotations = null,
10275 }, bw);
10429 }, w);
1027610430 },
1027710431 .subroutine_type => {
1027810432 const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data);
......@@ -10280,7 +10434,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1028010434 .flags = null,
1028110435 .cc = null,
1028210436 .types = extra.types_tuple,
10283 }, bw);
10437 }, w);
1028410438 },
1028510439 .enumerator_unsigned,
1028610440 .enumerator_signed_positive,
......@@ -10330,7 +10484,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1033010484 => false,
1033110485 else => unreachable,
1033210486 },
10333 }, bw);
10487 }, w);
1033410488 },
1033510489 .subrange => {
1033610490 const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data);
......@@ -10339,34 +10493,34 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1033910493 .lowerBound = extra.lower_bound,
1034010494 .upperBound = null,
1034110495 .stride = null,
10342 }, bw);
10496 }, w);
1034310497 },
1034410498 .tuple => {
1034510499 var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data);
1034610500 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
10347 try bw.writeAll("!{");
10348 for (elements) |element| try bw.print("{[element]f%}", .{
10349 .element = try metadata_formatter.fmt("", element),
10501 try w.writeAll("!{");
10502 for (elements) |element| try w.print("{[element]f}", .{
10503 .element = try metadata_formatter.fmt("", element, .{ .percent = true }),
1035010504 });
10351 try bw.writeAll("}\n");
10505 try w.writeAll("}\n");
1035210506 },
1035310507 .str_tuple => {
1035410508 var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data);
1035510509 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
10356 try bw.print("!{{{[str]f%}", .{
10357 .str = try metadata_formatter.fmt("", extra.data.str),
10510 try w.print("!{{{[str]f}", .{
10511 .str = try metadata_formatter.fmt("", extra.data.str, .{ .percent = true }),
1035810512 });
10359 for (elements) |element| try bw.print("{[element]f%}", .{
10360 .element = try metadata_formatter.fmt("", element),
10513 for (elements) |element| try w.print("{[element]f}", .{
10514 .element = try metadata_formatter.fmt("", element, .{ .percent = true }),
1036110515 });
10362 try bw.writeAll("}\n");
10516 try w.writeAll("}\n");
1036310517 },
1036410518 .module_flag => {
1036510519 const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data);
10366 try bw.print("!{{{[behavior]f%}{[name]f%}{[constant]f%}}}\n", .{
10367 .behavior = try metadata_formatter.fmt("", extra.behavior),
10368 .name = try metadata_formatter.fmt("", extra.name),
10369 .constant = try metadata_formatter.fmt("", extra.constant),
10520 try w.print("!{{{[behavior]f}{[name]f}{[constant]f}}}\n", .{
10521 .behavior = try metadata_formatter.fmt("", extra.behavior, .{ .percent = true }),
10522 .name = try metadata_formatter.fmt("", extra.name, .{ .percent = true }),
10523 .constant = try metadata_formatter.fmt("", extra.constant, .{ .percent = true }),
1037010524 });
1037110525 },
1037210526 .local_var => {
......@@ -10381,7 +10535,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1038110535 .flags = null,
1038210536 .@"align" = null,
1038310537 .annotations = null,
10384 }, bw);
10538 }, w);
1038510539 },
1038610540 .parameter => {
1038710541 const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data);
......@@ -10395,7 +10549,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1039510549 .flags = null,
1039610550 .@"align" = null,
1039710551 .annotations = null,
10398 }, bw);
10552 }, w);
1039910553 },
1040010554 .global_var,
1040110555 .@"global_var local",
......@@ -10418,7 +10572,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1041810572 .templateParams = null,
1041910573 .@"align" = null,
1042010574 .annotations = null,
10421 }, bw);
10575 }, w);
1042210576 },
1042310577 .global_var_expression => {
1042410578 const extra =
......@@ -10426,7 +10580,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
1042610580 try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{
1042710581 .@"var" = extra.variable,
1042810582 .expr = extra.expression,
10429 }, bw);
10583 }, w);
1043010584 },
1043110585 }
1043210586 }
......@@ -10445,18 +10599,18 @@ fn isValidIdentifier(id: []const u8) bool {
1044510599}
1044610600
1044710601const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier };
10448fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *Writer) Writer.Error!void {
10602fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, w: *Writer) Writer.Error!void {
1044910603 const need_quotes = switch (quotes) {
1045010604 .always_quote => true,
1045110605 .quote_unless_valid_identifier => !isValidIdentifier(slice),
1045210606 };
10453 if (need_quotes) try bw.writeByte('"');
10607 if (need_quotes) try w.writeByte('"');
1045410608 for (slice) |byte| switch (byte) {
10455 '\\' => try bw.writeAll("\\\\"),
10456 ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try bw.writeByte(byte),
10457 else => try bw.print("\\{X:0>2}", .{byte}),
10609 '\\' => try w.writeAll("\\\\"),
10610 ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try w.writeByte(byte),
10611 else => try w.print("\\{X:0>2}", .{byte}),
1045810612 };
10459 if (need_quotes) try bw.writeByte('"');
10613 if (need_quotes) try w.writeByte('"');
1046010614}
1046110615
1046210616fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void {
......@@ -15084,13 +15238,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco
1508415238 return bitcode.toOwnedSlice();
1508515239}
1508615240
15087const std = @import("../../std.zig");
15088const Allocator = std.mem.Allocator;
15089const assert = std.debug.assert;
15090const bitcode_writer = @import("bitcode_writer.zig");
15091const Builder = @This();
15092const builtin = @import("builtin");
15093const DW = std.dwarf;
15094const ir = @import("ir.zig");
15095const log = std.log.scoped(.llvm);
15096const Writer = std.io.Writer;
15241const FormatFlags = struct {
15242 comma: bool = false,
15243 space: bool = false,
15244 percent: bool = false,
15245
15246 fn onlyPercent(f: FormatFlags) bool {
15247 return !f.comma and !f.space and f.percent;
15248 }
15249};
lib/std/zig/parser_test.zig+10-149
......@@ -1,3 +1,9 @@
1const std = @import("std");
2const mem = std.mem;
3const print = std.debug.print;
4const io = std.io;
5const maxInt = std.math.maxInt;
6
17test "zig fmt: remove extra whitespace at start and end of file with comment between" {
28 try testTransform(
39 \\
......@@ -341,15 +347,6 @@ test "zig fmt: nosuspend block" {
341347 );
342348}
343349
344test "zig fmt: nosuspend await" {
345 try testCanonical(
346 \\fn foo() void {
347 \\ x = nosuspend await y;
348 \\}
349 \\
350 );
351}
352
353350test "zig fmt: container declaration, single line" {
354351 try testCanonical(
355352 \\const X = struct { foo: i32 };
......@@ -1093,18 +1090,6 @@ test "zig fmt: block in slice expression" {
10931090 );
10941091}
10951092
1096test "zig fmt: async function" {
1097 try testCanonical(
1098 \\pub const Server = struct {
1099 \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.@"async") void,
1100 \\};
1101 \\test "hi" {
1102 \\ var ptr: fn (i32) callconv(.@"async") void = @ptrCast(other);
1103 \\}
1104 \\
1105 );
1106}
1107
11081093test "zig fmt: whitespace fixes" {
11091094 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
11101095 \\test "" {
......@@ -1549,17 +1534,6 @@ test "zig fmt: spaces around slice operator" {
15491534 );
15501535}
15511536
1552test "zig fmt: async call in if condition" {
1553 try testCanonical(
1554 \\comptime {
1555 \\ if (async b()) {
1556 \\ a();
1557 \\ }
1558 \\}
1559 \\
1560 );
1561}
1562
15631537test "zig fmt: 2nd arg multiline string" {
15641538 try testCanonical(
15651539 \\comptime {
......@@ -2770,11 +2744,11 @@ test "zig fmt: preserve spacing" {
27702744 \\const std = @import("std");
27712745 \\
27722746 \\pub fn main() !void {
2773 \\ var stdout_file = std.io.getStdOut;
2774 \\ var stdout_file = std.io.getStdOut;
2747 \\ var stdout_file = std.lol.abcd;
2748 \\ var stdout_file = std.lol.abcd;
27752749 \\
2776 \\ var stdout_file = std.io.getStdOut;
2777 \\ var stdout_file = std.io.getStdOut;
2750 \\ var stdout_file = std.lol.abcd;
2751 \\ var stdout_file = std.lol.abcd;
27782752 \\}
27792753 \\
27802754 );
......@@ -3946,27 +3920,6 @@ test "zig fmt: inline asm" {
39463920 );
39473921}
39483922
3949test "zig fmt: async functions" {
3950 try testCanonical(
3951 \\fn simpleAsyncFn() void {
3952 \\ const a = async a.b();
3953 \\ x += 1;
3954 \\ suspend {}
3955 \\ x += 1;
3956 \\ suspend {}
3957 \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable;
3958 \\ await p;
3959 \\}
3960 \\
3961 \\test "suspend, resume, await" {
3962 \\ const p: anyframe = async testAsyncSeq();
3963 \\ resume p;
3964 \\ await p;
3965 \\}
3966 \\
3967 );
3968}
3969
39703923test "zig fmt: nosuspend" {
39713924 try testCanonical(
39723925 \\const a = nosuspend foo();
......@@ -3989,14 +3942,6 @@ test "zig fmt: Block after if" {
39893942 );
39903943}
39913944
3992test "zig fmt: usingnamespace" {
3993 try testCanonical(
3994 \\usingnamespace @import("std");
3995 \\pub usingnamespace @import("std");
3996 \\
3997 );
3998}
3999
40003945test "zig fmt: string identifier" {
40013946 try testCanonical(
40023947 \\const @"a b" = @"c d".@"e f";
......@@ -5140,17 +5085,6 @@ test "zig fmt: line comment after multiline single expr if statement with multil
51405085 );
51415086}
51425087
5143test "zig fmt: respect extra newline between fn and pub usingnamespace" {
5144 try testCanonical(
5145 \\fn foo() void {
5146 \\ bar();
5147 \\}
5148 \\
5149 \\pub usingnamespace baz;
5150 \\
5151 );
5152}
5153
51545088test "zig fmt: respect extra newline between switch items" {
51555089 try testCanonical(
51565090 \\const a = switch (b) {
......@@ -5719,34 +5653,6 @@ test "zig fmt: canonicalize symbols (primitive types)" {
57195653 );
57205654}
57215655
5722// Never unescape names spelled like keywords.
5723test "zig fmt: canonicalize symbols (keywords)" {
5724 try testCanonical(
5725 \\const @"enum" = struct {
5726 \\ @"error": @"struct" = true,
5727 \\ const @"struct" = bool;
5728 \\};
5729 \\
5730 \\fn @"usingnamespace"(@"union": @"enum") error{@"try"}!void {
5731 \\ var @"struct" = @"union";
5732 \\ @"struct".@"error" = false;
5733 \\ if (@"struct".@"error") {
5734 \\ return @"usingnamespace"(.{ .@"error" = false });
5735 \\ } else {
5736 \\ return error.@"try";
5737 \\ }
5738 \\}
5739 \\
5740 \\test @"usingnamespace" {
5741 \\ try @"usingnamespace"(.{});
5742 \\ _ = @"return": {
5743 \\ break :@"return" 4;
5744 \\ };
5745 \\}
5746 \\
5747 );
5748}
5749
57505656test "zig fmt: no space before newline before multiline string" {
57515657 try testCanonical(
57525658 \\const S = struct {
......@@ -6181,29 +6087,6 @@ test "recovery: missing return type" {
61816087 });
61826088}
61836089
6184test "recovery: continue after invalid decl" {
6185 try testError(
6186 \\fn foo {
6187 \\ inline;
6188 \\}
6189 \\pub test "" {
6190 \\ async a & b;
6191 \\}
6192 , &[_]Error{
6193 .expected_token,
6194 .expected_pub_item,
6195 .expected_param_list,
6196 });
6197 try testError(
6198 \\threadlocal test "" {
6199 \\ @a & b;
6200 \\}
6201 , &[_]Error{
6202 .expected_var_decl,
6203 .expected_param_list,
6204 });
6205}
6206
62076090test "recovery: invalid extern/inline" {
62086091 try testError(
62096092 \\inline test "" { a & b; }
......@@ -6232,22 +6115,6 @@ test "recovery: missing semicolon" {
62326115 });
62336116}
62346117
6235test "recovery: invalid container members" {
6236 try testError(
6237 \\usingnamespace;
6238 \\@foo()+
6239 \\@bar()@,
6240 \\while (a == 2) { test "" {}}
6241 \\test "" {
6242 \\ a & b
6243 \\}
6244 , &[_]Error{
6245 .expected_expr,
6246 .expected_comma_after_field,
6247 .expected_semi_after_stmt,
6248 });
6249}
6250
62516118// TODO after https://github.com/ziglang/zig/issues/35 is implemented,
62526119// we should be able to recover from this *at any indentation level*,
62536120// reporting a parse error and yet also parsing all the decls even
......@@ -6454,12 +6321,6 @@ test "ampersand" {
64546321 , &.{});
64556322}
64566323
6457const std = @import("std");
6458const mem = std.mem;
6459const print = std.debug.print;
6460const io = std.io;
6461const maxInt = std.math.maxInt;
6462
64636324var fixed_buffer_mem: [100 * 1024]u8 = undefined;
64646325
64656326fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {
lib/std/zig/string_literal.zig+13-13
......@@ -45,50 +45,49 @@ pub const Error = union(enum) {
4545 raw_string: []const u8,
4646 };
4747
48 fn formatMessage(self: FormatMessage, bw: *Writer, comptime f: []const u8) !void {
49 _ = f;
48 fn formatMessage(self: FormatMessage, writer: *std.io.Writer) std.io.Writer.Error!void {
5049 switch (self.err) {
51 .invalid_escape_character => |bad_index| try bw.print(
50 .invalid_escape_character => |bad_index| try writer.print(
5251 "invalid escape character: '{c}'",
5352 .{self.raw_string[bad_index]},
5453 ),
55 .expected_hex_digit => |bad_index| try bw.print(
54 .expected_hex_digit => |bad_index| try writer.print(
5655 "expected hex digit, found '{c}'",
5756 .{self.raw_string[bad_index]},
5857 ),
59 .empty_unicode_escape_sequence => try bw.writeAll(
58 .empty_unicode_escape_sequence => try writer.writeAll(
6059 "empty unicode escape sequence",
6160 ),
62 .expected_hex_digit_or_rbrace => |bad_index| try bw.print(
61 .expected_hex_digit_or_rbrace => |bad_index| try writer.print(
6362 "expected hex digit or '}}', found '{c}'",
6463 .{self.raw_string[bad_index]},
6564 ),
66 .invalid_unicode_codepoint => try bw.writeAll(
65 .invalid_unicode_codepoint => try writer.writeAll(
6766 "unicode escape does not correspond to a valid unicode scalar value",
6867 ),
69 .expected_lbrace => |bad_index| try bw.print(
68 .expected_lbrace => |bad_index| try writer.print(
7069 "expected '{{', found '{c}'",
7170 .{self.raw_string[bad_index]},
7271 ),
73 .expected_rbrace => |bad_index| try bw.print(
72 .expected_rbrace => |bad_index| try writer.print(
7473 "expected '}}', found '{c}'",
7574 .{self.raw_string[bad_index]},
7675 ),
77 .expected_single_quote => |bad_index| try bw.print(
76 .expected_single_quote => |bad_index| try writer.print(
7877 "expected single quote ('), found '{c}'",
7978 .{self.raw_string[bad_index]},
8079 ),
81 .invalid_character => |bad_index| try bw.print(
80 .invalid_character => |bad_index| try writer.print(
8281 "invalid byte in string or character literal: '{c}'",
8382 .{self.raw_string[bad_index]},
8483 ),
85 .empty_char_literal => try bw.writeAll(
84 .empty_char_literal => try writer.writeAll(
8685 "empty character literal",
8786 ),
8887 }
8988 }
9089
91 pub fn fmt(self: @This(), raw_string: []const u8) std.fmt.Formatter(formatMessage) {
90 pub fn fmt(self: @This(), raw_string: []const u8) std.fmt.Formatter(FormatMessage, formatMessage) {
9291 return .{ .data = .{
9392 .err = self,
9493 .raw_string = raw_string,
......@@ -318,6 +317,7 @@ test parseCharLiteral {
318317}
319318
320319/// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type.
320///
321321/// Asserts `bytes` has '"' at beginning and end.
322322pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result {
323323 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
lib/std/zig/system/linux.zig+4-4
......@@ -388,7 +388,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
388388 const current_arch = builtin.cpu.arch;
389389 switch (current_arch) {
390390 .arm, .armeb, .thumb, .thumbeb => {
391 return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null;
391 return ArmCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
392392 },
393393 .aarch64, .aarch64_be => {
394394 const registers = [12]u64{
......@@ -410,13 +410,13 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
410410 return core;
411411 },
412412 .sparc64 => {
413 return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;
413 return SparcCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
414414 },
415415 .powerpc, .powerpcle, .powerpc64, .powerpc64le => {
416 return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null;
416 return PowerpcCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
417417 },
418418 .riscv64, .riscv32 => {
419 return RiscvCpuinfoParser.parse(current_arch, f.reader()) catch null;
419 return RiscvCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
420420 },
421421 else => {},
422422 }
lib/std/zig/tokenizer.zig-9
......@@ -17,8 +17,6 @@ pub const Token = struct {
1717 .{ "anyframe", .keyword_anyframe },
1818 .{ "anytype", .keyword_anytype },
1919 .{ "asm", .keyword_asm },
20 .{ "async", .keyword_async },
21 .{ "await", .keyword_await },
2220 .{ "break", .keyword_break },
2321 .{ "callconv", .keyword_callconv },
2422 .{ "catch", .keyword_catch },
......@@ -55,7 +53,6 @@ pub const Token = struct {
5553 .{ "try", .keyword_try },
5654 .{ "union", .keyword_union },
5755 .{ "unreachable", .keyword_unreachable },
58 .{ "usingnamespace", .keyword_usingnamespace },
5956 .{ "var", .keyword_var },
6057 .{ "volatile", .keyword_volatile },
6158 .{ "while", .keyword_while },
......@@ -146,8 +143,6 @@ pub const Token = struct {
146143 keyword_anyframe,
147144 keyword_anytype,
148145 keyword_asm,
149 keyword_async,
150 keyword_await,
151146 keyword_break,
152147 keyword_callconv,
153148 keyword_catch,
......@@ -184,7 +179,6 @@ pub const Token = struct {
184179 keyword_try,
185180 keyword_union,
186181 keyword_unreachable,
187 keyword_usingnamespace,
188182 keyword_var,
189183 keyword_volatile,
190184 keyword_while,
......@@ -273,8 +267,6 @@ pub const Token = struct {
273267 .keyword_anyframe => "anyframe",
274268 .keyword_anytype => "anytype",
275269 .keyword_asm => "asm",
276 .keyword_async => "async",
277 .keyword_await => "await",
278270 .keyword_break => "break",
279271 .keyword_callconv => "callconv",
280272 .keyword_catch => "catch",
......@@ -311,7 +303,6 @@ pub const Token = struct {
311303 .keyword_try => "try",
312304 .keyword_union => "union",
313305 .keyword_unreachable => "unreachable",
314 .keyword_usingnamespace => "usingnamespace",
315306 .keyword_var => "var",
316307 .keyword_volatile => "volatile",
317308 .keyword_while => "while",
lib/std/zon/parse.zig+113-130
......@@ -64,22 +64,14 @@ pub const Error = union(enum) {
6464 }
6565 };
6666
67 fn formatMessage(
68 self: []const u8,
69 comptime f: []const u8,
70 options: std.fmt.FormatOptions,
71 writer: anytype,
72 ) !void {
73 _ = f;
74 _ = options;
75
67 fn formatMessage(self: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
7668 // Just writes the string for now, but we're keeping this behind a formatter so we have
7769 // the option to extend it in the future to print more advanced messages (like `Error`
7870 // does) without breaking the API.
79 try writer.writeAll(self);
71 try w.writeAll(self);
8072 }
8173
82 pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Formatter(Note.formatMessage) {
74 pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Formatter([]const u8, Note.formatMessage) {
8375 return .{ .data = switch (self) {
8476 .zoir => |note| note.msg.get(diag.zoir),
8577 .type_check => |note| note.msg,
......@@ -155,21 +147,14 @@ pub const Error = union(enum) {
155147 diag: *const Diagnostics,
156148 };
157149
158 fn formatMessage(
159 self: FormatMessage,
160 comptime f: []const u8,
161 options: std.fmt.FormatOptions,
162 writer: anytype,
163 ) !void {
164 _ = f;
165 _ = options;
150 fn formatMessage(self: FormatMessage, w: *std.io.Writer) std.io.Writer.Error!void {
166151 switch (self.err) {
167 .zoir => |err| try writer.writeAll(err.msg.get(self.diag.zoir)),
168 .type_check => |tc| try writer.writeAll(tc.message),
152 .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)),
153 .type_check => |tc| try w.writeAll(tc.message),
169154 }
170155 }
171156
172 pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Formatter(formatMessage) {
157 pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Formatter(FormatMessage, formatMessage) {
173158 return .{ .data = .{
174159 .err = self,
175160 .diag = diag,
......@@ -241,25 +226,18 @@ pub const Diagnostics = struct {
241226 return .{ .diag = self };
242227 }
243228
244 pub fn format(
245 self: *const @This(),
246 comptime fmt: []const u8,
247 options: std.fmt.FormatOptions,
248 writer: anytype,
249 ) !void {
250 _ = fmt;
251 _ = options;
229 pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void {
252230 var errors = self.iterateErrors();
253231 while (errors.next()) |err| {
254232 const loc = err.getLocation(self);
255233 const msg = err.fmtMessage(self);
256 try writer.print("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, msg });
234 try w.print("{d}:{d}: error: {f}\n", .{ loc.line + 1, loc.column + 1, msg });
257235
258236 var notes = err.iterateNotes(self);
259237 while (notes.next()) |note| {
260238 const note_loc = note.getLocation(self);
261239 const note_msg = note.fmtMessage(self);
262 try writer.print("{}:{}: note: {s}\n", .{
240 try w.print("{d}:{d}: note: {f}\n", .{
263241 note_loc.line + 1,
264242 note_loc.column + 1,
265243 note_msg,
......@@ -648,7 +626,7 @@ const Parser = struct {
648626 .failure => |err| {
649627 const token = self.ast.nodeMainToken(ast_node);
650628 const raw_string = self.ast.tokenSlice(token);
651 return self.failTokenFmt(token, @intCast(err.offset()), "{s}", .{err.fmt(raw_string)});
629 return self.failTokenFmt(token, @intCast(err.offset()), "{f}", .{err.fmt(raw_string)});
652630 },
653631 }
654632
......@@ -1089,7 +1067,10 @@ const Parser = struct {
10891067 try buf.appendSlice(gpa, msg);
10901068 inline for (info.fields, 0..) |field_info, i| {
10911069 if (i != 0) try buf.appendSlice(gpa, ", ");
1092 try buf.print(gpa, "'{p_}'", .{std.zig.fmtId(field_info.name)});
1070 try buf.print(gpa, "'{f}'", .{std.zig.fmtIdFlags(field_info.name, .{
1071 .allow_primitive = true,
1072 .allow_underscore = true,
1073 })});
10931074 }
10941075 break :b .{
10951076 .token = token,
......@@ -1300,7 +1281,7 @@ test "std.zon ast errors" {
13001281 error.ParseZon,
13011282 fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &diag, .{}),
13021283 );
1303 try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{}", .{diag});
1284 try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{diag});
13041285}
13051286
13061287test "std.zon comments" {
......@@ -1322,7 +1303,7 @@ test "std.zon comments" {
13221303 , &diag, .{}));
13231304 try std.testing.expectFmt(
13241305 "1:1: error: expected expression, found 'a document comment'\n",
1325 "{}",
1306 "{f}",
13261307 .{diag},
13271308 );
13281309 }
......@@ -1343,7 +1324,7 @@ test "std.zon failure/oom formatting" {
13431324 &diag,
13441325 .{},
13451326 ));
1346 try std.testing.expectFmt("", "{}", .{diag});
1327 try std.testing.expectFmt("", "{f}", .{diag});
13471328}
13481329
13491330test "std.zon fromSlice syntax error" {
......@@ -1423,7 +1404,7 @@ test "std.zon unions" {
14231404 \\1:4: note: supported: 'x', 'y'
14241405 \\
14251406 ,
1426 "{}",
1407 "{f}",
14271408 .{diag},
14281409 );
14291410 }
......@@ -1437,7 +1418,7 @@ test "std.zon unions" {
14371418 error.ParseZon,
14381419 fromSlice(Union, gpa, ".{.x=1}", &diag, .{}),
14391420 );
1440 try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{}", .{diag});
1421 try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag});
14411422 }
14421423
14431424 // Extra field
......@@ -1449,7 +1430,7 @@ test "std.zon unions" {
14491430 error.ParseZon,
14501431 fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}),
14511432 );
1452 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag});
1433 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
14531434 }
14541435
14551436 // No fields
......@@ -1461,7 +1442,7 @@ test "std.zon unions" {
14611442 error.ParseZon,
14621443 fromSlice(Union, gpa, ".{}", &diag, .{}),
14631444 );
1464 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag});
1445 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
14651446 }
14661447
14671448 // Enum literals cannot coerce into untagged unions
......@@ -1470,7 +1451,7 @@ test "std.zon unions" {
14701451 var diag: Diagnostics = .{};
14711452 defer diag.deinit(gpa);
14721453 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{}));
1473 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag});
1454 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
14741455 }
14751456
14761457 // Unknown field for enum literal coercion
......@@ -1484,7 +1465,7 @@ test "std.zon unions" {
14841465 \\1:2: note: supported: 'x'
14851466 \\
14861467 ,
1487 "{}",
1468 "{f}",
14881469 .{diag},
14891470 );
14901471 }
......@@ -1495,7 +1476,7 @@ test "std.zon unions" {
14951476 var diag: Diagnostics = .{};
14961477 defer diag.deinit(gpa);
14971478 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{}));
1498 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{diag});
1479 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
14991480 }
15001481}
15011482
......@@ -1551,7 +1532,7 @@ test "std.zon structs" {
15511532 \\1:12: note: supported: 'x', 'y'
15521533 \\
15531534 ,
1554 "{}",
1535 "{f}",
15551536 .{diag},
15561537 );
15571538 }
......@@ -1569,7 +1550,7 @@ test "std.zon structs" {
15691550 \\1:4: error: duplicate struct field name
15701551 \\1:12: note: duplicate name here
15711552 \\
1572 , "{}", .{diag});
1553 , "{f}", .{diag});
15731554 }
15741555
15751556 // Ignore unknown fields
......@@ -1594,7 +1575,7 @@ test "std.zon structs" {
15941575 \\1:4: error: unexpected field 'x'
15951576 \\1:4: note: none expected
15961577 \\
1597 , "{}", .{diag});
1578 , "{f}", .{diag});
15981579 }
15991580
16001581 // Missing field
......@@ -1606,7 +1587,7 @@ test "std.zon structs" {
16061587 error.ParseZon,
16071588 fromSlice(Vec2, gpa, ".{.x=1.5}", &diag, .{}),
16081589 );
1609 try std.testing.expectFmt("1:2: error: missing required field y\n", "{}", .{diag});
1590 try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{diag});
16101591 }
16111592
16121593 // Default field
......@@ -1633,7 +1614,7 @@ test "std.zon structs" {
16331614 try std.testing.expectFmt(
16341615 \\1:18: error: cannot initialize comptime field
16351616 \\
1636 , "{}", .{diag});
1617 , "{f}", .{diag});
16371618 }
16381619
16391620 // Enum field (regression test, we were previously getting the field name in an
......@@ -1663,7 +1644,7 @@ test "std.zon structs" {
16631644 \\1:1: error: types are not available in ZON
16641645 \\1:1: note: replace the type with '.'
16651646 \\
1666 , "{}", .{diag});
1647 , "{f}", .{diag});
16671648 }
16681649
16691650 // Arrays
......@@ -1676,7 +1657,7 @@ test "std.zon structs" {
16761657 \\1:1: error: types are not available in ZON
16771658 \\1:1: note: replace the type with '.'
16781659 \\
1679 , "{}", .{diag});
1660 , "{f}", .{diag});
16801661 }
16811662
16821663 // Slices
......@@ -1689,7 +1670,7 @@ test "std.zon structs" {
16891670 \\1:1: error: types are not available in ZON
16901671 \\1:1: note: replace the type with '.'
16911672 \\
1692 , "{}", .{diag});
1673 , "{f}", .{diag});
16931674 }
16941675
16951676 // Tuples
......@@ -1708,7 +1689,7 @@ test "std.zon structs" {
17081689 \\1:1: error: types are not available in ZON
17091690 \\1:1: note: replace the type with '.'
17101691 \\
1711 , "{}", .{diag});
1692 , "{f}", .{diag});
17121693 }
17131694
17141695 // Nested
......@@ -1721,7 +1702,7 @@ test "std.zon structs" {
17211702 \\1:9: error: types are not available in ZON
17221703 \\1:9: note: replace the type with '.'
17231704 \\
1724 , "{}", .{diag});
1705 , "{f}", .{diag});
17251706 }
17261707 }
17271708}
......@@ -1766,7 +1747,7 @@ test "std.zon tuples" {
17661747 error.ParseZon,
17671748 fromSlice(Tuple, gpa, ".{0.5, true, 123}", &diag, .{}),
17681749 );
1769 try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{}", .{diag});
1750 try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{diag});
17701751 }
17711752
17721753 // Extra field
......@@ -1780,7 +1761,7 @@ test "std.zon tuples" {
17801761 );
17811762 try std.testing.expectFmt(
17821763 "1:2: error: missing tuple field with index 1\n",
1783 "{}",
1764 "{f}",
17841765 .{diag},
17851766 );
17861767 }
......@@ -1794,7 +1775,7 @@ test "std.zon tuples" {
17941775 error.ParseZon,
17951776 fromSlice(Tuple, gpa, ".{.foo = 10.0}", &diag, .{}),
17961777 );
1797 try std.testing.expectFmt("1:2: error: expected tuple\n", "{}", .{diag});
1778 try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{diag});
17981779 }
17991780
18001781 // Struct with missing field names
......@@ -1806,7 +1787,7 @@ test "std.zon tuples" {
18061787 error.ParseZon,
18071788 fromSlice(Struct, gpa, ".{10.0}", &diag, .{}),
18081789 );
1809 try std.testing.expectFmt("1:2: error: expected struct\n", "{}", .{diag});
1790 try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{diag});
18101791 }
18111792
18121793 // Comptime field
......@@ -1826,7 +1807,7 @@ test "std.zon tuples" {
18261807 try std.testing.expectFmt(
18271808 \\1:9: error: cannot initialize comptime field
18281809 \\
1829 , "{}", .{diag});
1810 , "{f}", .{diag});
18301811 }
18311812}
18321813
......@@ -1938,7 +1919,7 @@ test "std.zon arrays and slices" {
19381919 );
19391920 try std.testing.expectFmt(
19401921 "1:3: error: index 0 outside of array of length 0\n",
1941 "{}",
1922 "{f}",
19421923 .{diag},
19431924 );
19441925 }
......@@ -1953,7 +1934,7 @@ test "std.zon arrays and slices" {
19531934 );
19541935 try std.testing.expectFmt(
19551936 "1:8: error: index 1 outside of array of length 1\n",
1956 "{}",
1937 "{f}",
19571938 .{diag},
19581939 );
19591940 }
......@@ -1968,7 +1949,7 @@ test "std.zon arrays and slices" {
19681949 );
19691950 try std.testing.expectFmt(
19701951 "1:2: error: expected 2 array elements; found 1\n",
1971 "{}",
1952 "{f}",
19721953 .{diag},
19731954 );
19741955 }
......@@ -1983,7 +1964,7 @@ test "std.zon arrays and slices" {
19831964 );
19841965 try std.testing.expectFmt(
19851966 "1:2: error: expected 3 array elements; found 0\n",
1986 "{}",
1967 "{f}",
19871968 .{diag},
19881969 );
19891970 }
......@@ -1998,7 +1979,7 @@ test "std.zon arrays and slices" {
19981979 error.ParseZon,
19991980 fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
20001981 );
2001 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{diag});
1982 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag});
20021983 }
20031984
20041985 // Slice
......@@ -2009,7 +1990,7 @@ test "std.zon arrays and slices" {
20091990 error.ParseZon,
20101991 fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
20111992 );
2012 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{diag});
1993 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag});
20131994 }
20141995 }
20151996
......@@ -2023,7 +2004,7 @@ test "std.zon arrays and slices" {
20232004 error.ParseZon,
20242005 fromSlice([3]u8, gpa, "'a'", &diag, .{}),
20252006 );
2026 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2007 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
20272008 }
20282009
20292010 // Slice
......@@ -2034,7 +2015,7 @@ test "std.zon arrays and slices" {
20342015 error.ParseZon,
20352016 fromSlice([]u8, gpa, "'a'", &diag, .{}),
20362017 );
2037 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2018 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
20382019 }
20392020 }
20402021
......@@ -2048,7 +2029,7 @@ test "std.zon arrays and slices" {
20482029 );
20492030 try std.testing.expectFmt(
20502031 "1:3: error: pointers are not available in ZON\n",
2051 "{}",
2032 "{f}",
20522033 .{diag},
20532034 );
20542035 }
......@@ -2087,7 +2068,7 @@ test "std.zon string literal" {
20872068 error.ParseZon,
20882069 fromSlice([]u8, gpa, "\"abcd\"", &diag, .{}),
20892070 );
2090 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2071 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
20912072 }
20922073
20932074 {
......@@ -2097,7 +2078,7 @@ test "std.zon string literal" {
20972078 error.ParseZon,
20982079 fromSlice([]u8, gpa, "\\\\abcd", &diag, .{}),
20992080 );
2100 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2081 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
21012082 }
21022083 }
21032084
......@@ -2114,7 +2095,7 @@ test "std.zon string literal" {
21142095 error.ParseZon,
21152096 fromSlice([4:0]u8, gpa, "\"abcd\"", &diag, .{}),
21162097 );
2117 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2098 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
21182099 }
21192100
21202101 {
......@@ -2124,7 +2105,7 @@ test "std.zon string literal" {
21242105 error.ParseZon,
21252106 fromSlice([4:0]u8, gpa, "\\\\abcd", &diag, .{}),
21262107 );
2127 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2108 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
21282109 }
21292110 }
21302111
......@@ -2166,7 +2147,7 @@ test "std.zon string literal" {
21662147 error.ParseZon,
21672148 fromSlice([:1]const u8, gpa, "\"foo\"", &diag, .{}),
21682149 );
2169 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2150 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
21702151 }
21712152
21722153 {
......@@ -2176,7 +2157,7 @@ test "std.zon string literal" {
21762157 error.ParseZon,
21772158 fromSlice([:1]const u8, gpa, "\\\\foo", &diag, .{}),
21782159 );
2179 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2160 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
21802161 }
21812162 }
21822163
......@@ -2188,7 +2169,7 @@ test "std.zon string literal" {
21882169 error.ParseZon,
21892170 fromSlice([]const u8, gpa, "true", &diag, .{}),
21902171 );
2191 try std.testing.expectFmt("1:1: error: expected string\n", "{}", .{diag});
2172 try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag});
21922173 }
21932174
21942175 // Expecting string literal, getting an incompatible tuple
......@@ -2199,7 +2180,7 @@ test "std.zon string literal" {
21992180 error.ParseZon,
22002181 fromSlice([]const u8, gpa, ".{false}", &diag, .{}),
22012182 );
2202 try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{}", .{diag});
2183 try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag});
22032184 }
22042185
22052186 // Invalid string literal
......@@ -2210,7 +2191,7 @@ test "std.zon string literal" {
22102191 error.ParseZon,
22112192 fromSlice([]const i8, gpa, "\"\\a\"", &diag, .{}),
22122193 );
2213 try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{}", .{diag});
2194 try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag});
22142195 }
22152196
22162197 // Slice wrong child type
......@@ -2222,7 +2203,7 @@ test "std.zon string literal" {
22222203 error.ParseZon,
22232204 fromSlice([]const i8, gpa, "\"a\"", &diag, .{}),
22242205 );
2225 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2206 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
22262207 }
22272208
22282209 {
......@@ -2232,7 +2213,7 @@ test "std.zon string literal" {
22322213 error.ParseZon,
22332214 fromSlice([]const i8, gpa, "\\\\a", &diag, .{}),
22342215 );
2235 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2216 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
22362217 }
22372218 }
22382219
......@@ -2245,7 +2226,7 @@ test "std.zon string literal" {
22452226 error.ParseZon,
22462227 fromSlice([]align(2) const u8, gpa, "\"abc\"", &diag, .{}),
22472228 );
2248 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2229 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
22492230 }
22502231
22512232 {
......@@ -2255,7 +2236,7 @@ test "std.zon string literal" {
22552236 error.ParseZon,
22562237 fromSlice([]align(2) const u8, gpa, "\\\\abc", &diag, .{}),
22572238 );
2258 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{diag});
2239 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
22592240 }
22602241 }
22612242
......@@ -2329,7 +2310,7 @@ test "std.zon enum literals" {
23292310 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
23302311 \\
23312312 ,
2332 "{}",
2313 "{f}",
23332314 .{diag},
23342315 );
23352316 }
......@@ -2347,7 +2328,7 @@ test "std.zon enum literals" {
23472328 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
23482329 \\
23492330 ,
2350 "{}",
2331 "{f}",
23512332 .{diag},
23522333 );
23532334 }
......@@ -2360,7 +2341,7 @@ test "std.zon enum literals" {
23602341 error.ParseZon,
23612342 fromSlice(Enum, gpa, "true", &diag, .{}),
23622343 );
2363 try std.testing.expectFmt("1:1: error: expected enum literal\n", "{}", .{diag});
2344 try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{diag});
23642345 }
23652346
23662347 // Test embedded nulls in an identifier
......@@ -2373,7 +2354,7 @@ test "std.zon enum literals" {
23732354 );
23742355 try std.testing.expectFmt(
23752356 "1:2: error: identifier cannot contain null bytes\n",
2376 "{}",
2357 "{f}",
23772358 .{diag},
23782359 );
23792360 }
......@@ -2399,13 +2380,13 @@ test "std.zon parse bool" {
23992380 \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
24002381 \\1:2: note: precede identifier with '.' for an enum literal
24012382 \\
2402 , "{}", .{diag});
2383 , "{f}", .{diag});
24032384 }
24042385 {
24052386 var diag: Diagnostics = .{};
24062387 defer diag.deinit(gpa);
24072388 try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &diag, .{}));
2408 try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{}", .{diag});
2389 try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{diag});
24092390 }
24102391}
24112392
......@@ -2478,7 +2459,7 @@ test "std.zon parse int" {
24782459 ));
24792460 try std.testing.expectFmt(
24802461 "1:1: error: type 'i66' cannot represent value\n",
2481 "{}",
2462 "{f}",
24822463 .{diag},
24832464 );
24842465 }
......@@ -2494,7 +2475,7 @@ test "std.zon parse int" {
24942475 ));
24952476 try std.testing.expectFmt(
24962477 "1:1: error: type 'i66' cannot represent value\n",
2497 "{}",
2478 "{f}",
24982479 .{diag},
24992480 );
25002481 }
......@@ -2583,7 +2564,7 @@ test "std.zon parse int" {
25832564 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{}));
25842565 try std.testing.expectFmt(
25852566 "1:3: error: invalid digit 'a' for decimal base\n",
2586 "{}",
2567 "{f}",
25872568 .{diag},
25882569 );
25892570 }
......@@ -2593,7 +2574,7 @@ test "std.zon parse int" {
25932574 var diag: Diagnostics = .{};
25942575 defer diag.deinit(gpa);
25952576 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &diag, .{}));
2596 try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{}", .{diag});
2577 try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{diag});
25972578 }
25982579
25992580 // Failing because an int is out of range
......@@ -2603,7 +2584,7 @@ test "std.zon parse int" {
26032584 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{}));
26042585 try std.testing.expectFmt(
26052586 "1:1: error: type 'u8' cannot represent value\n",
2606 "{}",
2587 "{f}",
26072588 .{diag},
26082589 );
26092590 }
......@@ -2615,7 +2596,7 @@ test "std.zon parse int" {
26152596 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{}));
26162597 try std.testing.expectFmt(
26172598 "1:1: error: type 'i8' cannot represent value\n",
2618 "{}",
2599 "{f}",
26192600 .{diag},
26202601 );
26212602 }
......@@ -2627,7 +2608,7 @@ test "std.zon parse int" {
26272608 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{}));
26282609 try std.testing.expectFmt(
26292610 "1:1: error: type 'u8' cannot represent value\n",
2630 "{}",
2611 "{f}",
26312612 .{diag},
26322613 );
26332614 }
......@@ -2639,7 +2620,7 @@ test "std.zon parse int" {
26392620 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{}));
26402621 try std.testing.expectFmt(
26412622 "1:1: error: type 'u8' cannot represent value\n",
2642 "{}",
2623 "{f}",
26432624 .{diag},
26442625 );
26452626 }
......@@ -2651,7 +2632,7 @@ test "std.zon parse int" {
26512632 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{}));
26522633 try std.testing.expectFmt(
26532634 "1:1: error: type 'u8' cannot represent value\n",
2654 "{}",
2635 "{f}",
26552636 .{diag},
26562637 );
26572638 }
......@@ -2666,7 +2647,7 @@ test "std.zon parse int" {
26662647 \\1:2: note: use '0' for an integer zero
26672648 \\1:2: note: use '-0.0' for a floating-point signed zero
26682649 \\
2669 , "{}", .{diag});
2650 , "{f}", .{diag});
26702651 }
26712652
26722653 // Negative integer zero casted to float
......@@ -2679,7 +2660,7 @@ test "std.zon parse int" {
26792660 \\1:2: note: use '0' for an integer zero
26802661 \\1:2: note: use '-0.0' for a floating-point signed zero
26812662 \\
2682 , "{}", .{diag});
2663 , "{f}", .{diag});
26832664 }
26842665
26852666 // Negative float 0 is allowed
......@@ -2695,7 +2676,7 @@ test "std.zon parse int" {
26952676 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{}));
26962677 try std.testing.expectFmt(
26972678 "1:1: error: expected number or 'inf' after '-'\n",
2698 "{}",
2679 "{f}",
26992680 .{diag},
27002681 );
27012682 }
......@@ -2709,7 +2690,7 @@ test "std.zon parse int" {
27092690 );
27102691 try std.testing.expectFmt(
27112692 "1:1: error: expected number or 'inf' after '-'\n",
2712 "{}",
2693 "{f}",
27132694 .{diag},
27142695 );
27152696 }
......@@ -2719,7 +2700,7 @@ test "std.zon parse int" {
27192700 var diag: Diagnostics = .{};
27202701 defer diag.deinit(gpa);
27212702 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &diag, .{}));
2722 try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{}", .{diag});
2703 try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{diag});
27232704 }
27242705
27252706 // Notes on invalid int literal
......@@ -2731,7 +2712,7 @@ test "std.zon parse int" {
27312712 \\1:1: error: number '0123' has leading zero
27322713 \\1:1: note: use '0o' prefix for octal literals
27332714 \\
2734 , "{}", .{diag});
2715 , "{f}", .{diag});
27352716 }
27362717}
27372718
......@@ -2744,7 +2725,7 @@ test "std.zon negative char" {
27442725 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{}));
27452726 try std.testing.expectFmt(
27462727 "1:1: error: expected number or 'inf' after '-'\n",
2747 "{}",
2728 "{f}",
27482729 .{diag},
27492730 );
27502731 }
......@@ -2754,13 +2735,15 @@ test "std.zon negative char" {
27542735 try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{}));
27552736 try std.testing.expectFmt(
27562737 "1:1: error: expected number or 'inf' after '-'\n",
2757 "{}",
2738 "{f}",
27582739 .{diag},
27592740 );
27602741 }
27612742}
27622743
27632744test "std.zon parse float" {
2745 if (builtin.cpu.arch == .x86 and builtin.abi == .musl and builtin.link_mode == .dynamic) return error.SkipZigTest;
2746
27642747 const gpa = std.testing.allocator;
27652748
27662749 // Test decimals
......@@ -2841,7 +2824,7 @@ test "std.zon parse float" {
28412824 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{}));
28422825 try std.testing.expectFmt(
28432826 "1:1: error: expected number or 'inf' after '-'\n",
2844 "{}",
2827 "{f}",
28452828 .{diag},
28462829 );
28472830 }
......@@ -2851,7 +2834,7 @@ test "std.zon parse float" {
28512834 var diag: Diagnostics = .{};
28522835 defer diag.deinit(gpa);
28532836 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{}));
2854 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag});
2837 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
28552838 }
28562839
28572840 // nan as int not allowed
......@@ -2859,7 +2842,7 @@ test "std.zon parse float" {
28592842 var diag: Diagnostics = .{};
28602843 defer diag.deinit(gpa);
28612844 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{}));
2862 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag});
2845 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
28632846 }
28642847
28652848 // inf as int not allowed
......@@ -2867,7 +2850,7 @@ test "std.zon parse float" {
28672850 var diag: Diagnostics = .{};
28682851 defer diag.deinit(gpa);
28692852 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &diag, .{}));
2870 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag});
2853 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
28712854 }
28722855
28732856 // -inf as int not allowed
......@@ -2875,7 +2858,7 @@ test "std.zon parse float" {
28752858 var diag: Diagnostics = .{};
28762859 defer diag.deinit(gpa);
28772860 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &diag, .{}));
2878 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{diag});
2861 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
28792862 }
28802863
28812864 // Bad identifier as float
......@@ -2888,7 +2871,7 @@ test "std.zon parse float" {
28882871 \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
28892872 \\1:1: note: precede identifier with '.' for an enum literal
28902873 \\
2891 , "{}", .{diag});
2874 , "{f}", .{diag});
28922875 }
28932876
28942877 {
......@@ -2897,7 +2880,7 @@ test "std.zon parse float" {
28972880 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{}));
28982881 try std.testing.expectFmt(
28992882 "1:1: error: expected number or 'inf' after '-'\n",
2900 "{}",
2883 "{f}",
29012884 .{diag},
29022885 );
29032886 }
......@@ -2910,7 +2893,7 @@ test "std.zon parse float" {
29102893 error.ParseZon,
29112894 fromSlice(f32, gpa, "\"foo\"", &diag, .{}),
29122895 );
2913 try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{}", .{diag});
2896 try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{diag});
29142897 }
29152898}
29162899
......@@ -3154,7 +3137,7 @@ test "std.zon vector" {
31543137 );
31553138 try std.testing.expectFmt(
31563139 "1:2: error: expected 2 vector elements; found 1\n",
3157 "{}",
3140 "{f}",
31583141 .{diag},
31593142 );
31603143 }
......@@ -3169,7 +3152,7 @@ test "std.zon vector" {
31693152 );
31703153 try std.testing.expectFmt(
31713154 "1:2: error: expected 2 vector elements; found 3\n",
3172 "{}",
3155 "{f}",
31733156 .{diag},
31743157 );
31753158 }
......@@ -3184,7 +3167,7 @@ test "std.zon vector" {
31843167 );
31853168 try std.testing.expectFmt(
31863169 "1:8: error: expected type 'f32'\n",
3187 "{}",
3170 "{f}",
31883171 .{diag},
31893172 );
31903173 }
......@@ -3197,7 +3180,7 @@ test "std.zon vector" {
31973180 error.ParseZon,
31983181 fromSlice(@Vector(3, u8), gpa, "true", &diag, .{}),
31993182 );
3200 try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{}", .{diag});
3183 try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{diag});
32013184 }
32023185
32033186 // Elements should get freed on error
......@@ -3208,7 +3191,7 @@ test "std.zon vector" {
32083191 error.ParseZon,
32093192 fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}),
32103193 );
3211 try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{}", .{diag});
3194 try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{diag});
32123195 }
32133196}
32143197
......@@ -3332,7 +3315,7 @@ test "std.zon add pointers" {
33323315 error.ParseZon,
33333316 fromSlice(*const ?*const u8, gpa, "true", &diag, .{}),
33343317 );
3335 try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{}", .{diag});
3318 try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag});
33363319 }
33373320
33383321 {
......@@ -3342,7 +3325,7 @@ test "std.zon add pointers" {
33423325 error.ParseZon,
33433326 fromSlice(*const ?*const f32, gpa, "true", &diag, .{}),
33443327 );
3345 try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{}", .{diag});
3328 try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag});
33463329 }
33473330
33483331 {
......@@ -3352,7 +3335,7 @@ test "std.zon add pointers" {
33523335 error.ParseZon,
33533336 fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}),
33543337 );
3355 try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{}", .{diag});
3338 try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{diag});
33563339 }
33573340
33583341 {
......@@ -3362,7 +3345,7 @@ test "std.zon add pointers" {
33623345 error.ParseZon,
33633346 fromSlice(*const ?*const bool, gpa, "10", &diag, .{}),
33643347 );
3365 try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{}", .{diag});
3348 try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag});
33663349 }
33673350
33683351 {
......@@ -3372,7 +3355,7 @@ test "std.zon add pointers" {
33723355 error.ParseZon,
33733356 fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}),
33743357 );
3375 try std.testing.expectFmt("1:1: error: expected optional struct\n", "{}", .{diag});
3358 try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag});
33763359 }
33773360
33783361 {
......@@ -3382,7 +3365,7 @@ test "std.zon add pointers" {
33823365 error.ParseZon,
33833366 fromSlice(*const ?*const struct { i32 }, gpa, "true", &diag, .{}),
33843367 );
3385 try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{}", .{diag});
3368 try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag});
33863369 }
33873370
33883371 {
......@@ -3392,7 +3375,7 @@ test "std.zon add pointers" {
33923375 error.ParseZon,
33933376 fromSlice(*const ?*const union { x: void }, gpa, "true", &diag, .{}),
33943377 );
3395 try std.testing.expectFmt("1:1: error: expected optional union\n", "{}", .{diag});
3378 try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag});
33963379 }
33973380
33983381 {
......@@ -3402,7 +3385,7 @@ test "std.zon add pointers" {
34023385 error.ParseZon,
34033386 fromSlice(*const ?*const [3]u8, gpa, "true", &diag, .{}),
34043387 );
3405 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag});
3388 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
34063389 }
34073390
34083391 {
......@@ -3412,7 +3395,7 @@ test "std.zon add pointers" {
34123395 error.ParseZon,
34133396 fromSlice(?[3]u8, gpa, "true", &diag, .{}),
34143397 );
3415 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag});
3398 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
34163399 }
34173400
34183401 {
......@@ -3422,7 +3405,7 @@ test "std.zon add pointers" {
34223405 error.ParseZon,
34233406 fromSlice(*const ?*const []u8, gpa, "true", &diag, .{}),
34243407 );
3425 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag});
3408 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
34263409 }
34273410
34283411 {
......@@ -3432,7 +3415,7 @@ test "std.zon add pointers" {
34323415 error.ParseZon,
34333416 fromSlice(?[]u8, gpa, "true", &diag, .{}),
34343417 );
3435 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{diag});
3418 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
34363419 }
34373420
34383421 {
......@@ -3442,7 +3425,7 @@ test "std.zon add pointers" {
34423425 error.ParseZon,
34433426 fromSlice(*const ?*const []const u8, gpa, "true", &diag, .{}),
34443427 );
3445 try std.testing.expectFmt("1:1: error: expected optional string\n", "{}", .{diag});
3428 try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag});
34463429 }
34473430
34483431 {
......@@ -3452,7 +3435,7 @@ test "std.zon add pointers" {
34523435 error.ParseZon,
34533436 fromSlice(*const ?*const enum { foo }, gpa, "true", &diag, .{}),
34543437 );
3455 try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{}", .{diag});
3438 try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag});
34563439 }
34573440}
34583441
lib/ubsan_rt.zig+26-44
......@@ -119,24 +119,22 @@ const Value = extern struct {
119119 }
120120 }
121121
122 pub fn format(value: Value, bw: *std.io.Writer, comptime fmt: []const u8) !void {
123 comptime assert(fmt.len == 0);
124
122 pub fn format(value: Value, writer: *std.io.Writer) std.io.Writer.Error!void {
125123 // Work around x86_64 backend limitation.
126124 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) {
127 return bw.writeAll("(unknown)");
125 return writer.writeAll("(unknown)");
128126 }
129127
130128 switch (value.td.kind) {
131129 .integer => {
132130 if (value.td.isSigned()) {
133 return bw.print("{d}", .{value.getSignedInteger()});
131 try writer.print("{d}", .{value.getSignedInteger()});
134132 } else {
135 return bw.print("{d}", .{value.getUnsignedInteger()});
133 try writer.print("{d}", .{value.getUnsignedInteger()});
136134 }
137135 },
138 .float => return bw.print("{d}", .{value.getFloat()}),
139 .unknown => return bw.writeAll("(unknown)"),
136 .float => try writer.print("{d}", .{value.getFloat()}),
137 .unknown => try writer.writeAll("(unknown)"),
140138 }
141139 }
142140};
......@@ -166,17 +164,12 @@ fn overflowHandler(
166164 ) callconv(.c) noreturn {
167165 const lhs: Value = .{ .handle = lhs_handle, .td = data.td };
168166 const rhs: Value = .{ .handle = rhs_handle, .td = data.td };
169
170 const is_signed = data.td.isSigned();
171 const fmt = "{s} integer overflow: " ++ "{f} " ++
172 operator ++ " {f} cannot be represented in type {s}";
173
174 panic(@returnAddress(), fmt, .{
175 if (is_signed) "signed" else "unsigned",
176 lhs,
177 rhs,
178 data.td.getName(),
179 });
167 const signed_str = if (data.td.isSigned()) "signed" else "unsigned";
168 panic(
169 @returnAddress(),
170 "{s} integer overflow: {f} " ++ operator ++ " {f} cannot be represented in type {s}",
171 .{ signed_str, lhs, rhs, data.td.getName() },
172 );
180173 }
181174 };
182175
......@@ -195,11 +188,9 @@ fn negationHandler(
195188 value_handle: ValueHandle,
196189) callconv(.c) noreturn {
197190 const value: Value = .{ .handle = value_handle, .td = data.td };
198 panic(
199 @returnAddress(),
200 "negation of {f} cannot be represented in type {s}",
201 .{ value, data.td.getName() },
202 );
191 panic(@returnAddress(), "negation of {f} cannot be represented in type {s}", .{
192 value, data.td.getName(),
193 });
203194}
204195
205196fn divRemHandlerAbort(
......@@ -219,11 +210,9 @@ fn divRemHandler(
219210 const rhs: Value = .{ .handle = rhs_handle, .td = data.td };
220211
221212 if (rhs.isMinusOne()) {
222 panic(
223 @returnAddress(),
224 "division of {f} by -1 cannot be represented in type {s}",
225 .{ lhs, data.td.getName() },
226 );
213 panic(@returnAddress(), "division of {f} by -1 cannot be represented in type {s}", .{
214 lhs, data.td.getName(),
215 });
227216 } else panic(@returnAddress(), "division by zero", .{});
228217}
229218
......@@ -353,11 +342,10 @@ fn outOfBounds(
353342 index_handle: ValueHandle,
354343) callconv(.c) noreturn {
355344 const index: Value = .{ .handle = index_handle, .td = data.index_type };
356 panic(
357 @returnAddress(),
358 "index {f} out of bounds for type {s}",
359 .{ index, data.array_type.getName() },
360 );
345 panic(@returnAddress(), "index {f} out of bounds for type {s}", .{
346 index,
347 data.array_type.getName(),
348 });
361349}
362350
363351const PointerOverflowData = extern struct {
......@@ -547,11 +535,9 @@ fn loadInvalidValue(
547535 value_handle: ValueHandle,
548536) callconv(.c) noreturn {
549537 const value: Value = .{ .handle = value_handle, .td = data.td };
550 panic(
551 @returnAddress(),
552 "load of value {f}, which is not valid for type {s}",
553 .{ value, data.td.getName() },
554 );
538 panic(@returnAddress(), "load of value {f}, which is not valid for type {s}", .{
539 value, data.td.getName(),
540 });
555541}
556542
557543const InvalidBuiltinData = extern struct {
......@@ -590,11 +576,7 @@ fn vlaBoundNotPositive(
590576 bound_handle: ValueHandle,
591577) callconv(.c) noreturn {
592578 const bound: Value = .{ .handle = bound_handle, .td = data.td };
593 panic(
594 @returnAddress(),
595 "variable length array bound evaluates to non-positive value {f}",
596 .{bound},
597 );
579 panic(@returnAddress(), "variable length array bound evaluates to non-positive value {f}", .{bound});
598580}
599581
600582const FloatCastOverflowData = extern struct {
src/Air.zig+8-5
......@@ -747,7 +747,9 @@ pub const Inst = struct {
747747 /// Dest slice may have any alignment; source pointer may have any alignment.
748748 /// The two memory regions must not overlap.
749749 /// Result type is always void.
750 ///
750751 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
752 ///
751753 /// If the length is compile-time known (due to the destination or
752754 /// source being a pointer-to-array), then it is guaranteed to be
753755 /// greater than zero.
......@@ -759,7 +761,9 @@ pub const Inst = struct {
759761 /// Dest slice may have any alignment; source pointer may have any alignment.
760762 /// The two memory regions may overlap.
761763 /// Result type is always void.
764 ///
762765 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
766 ///
763767 /// If the length is compile-time known (due to the destination or
764768 /// source being a pointer-to-array), then it is guaranteed to be
765769 /// greater than zero.
......@@ -958,14 +962,13 @@ pub const Inst = struct {
958962 return index.unwrap().target;
959963 }
960964
961 pub fn format(index: Index, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
962 comptime assert(fmt.len == 0);
963 try bw.writeByte('%');
965 pub fn format(index: Index, w: *std.io.Writer) std.io.Writer.Error!void {
966 try w.writeByte('%');
964967 switch (index.unwrap()) {
965968 .ref => {},
966 .target => try bw.writeByte('t'),
969 .target => try w.writeByte('t'),
967970 }
968 try bw.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))});
971 try w.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))});
969972 }
970973 };
971974
src/Air/Liveness.zig+11-12
......@@ -1300,10 +1300,10 @@ fn analyzeOperands(
13001300
13011301 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
13021302 const immediate_death = if (data.live_set.remove(inst)) blk: {
1303 log.debug("[{}] %{}: removed from live set", .{ pass, @intFromEnum(inst) });
1303 log.debug("[{}] %{d}: removed from live set", .{ pass, @intFromEnum(inst) });
13041304 break :blk false;
13051305 } else blk: {
1306 log.debug("[{}] %{}: immediate death", .{ pass, @intFromEnum(inst) });
1306 log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) });
13071307 break :blk true;
13081308 };
13091309
......@@ -1324,7 +1324,7 @@ fn analyzeOperands(
13241324 const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i));
13251325
13261326 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
1327 log.debug("[{}] %{}: added %{f} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand });
1327 log.debug("[{}] %{d}: added %{d} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand });
13281328 tomb_bits |= mask;
13291329 }
13301330 }
......@@ -2037,15 +2037,15 @@ fn fmtInstSet(set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void)) FmtIns
20372037const FmtInstSet = struct {
20382038 set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),
20392039
2040 pub fn format(val: FmtInstSet, bw: *Writer, comptime _: []const u8) !void {
2040 pub fn format(val: FmtInstSet, w: *std.io.Writer) std.io.Writer.Error!void {
20412041 if (val.set.count() == 0) {
2042 try bw.writeAll("[no instructions]");
2042 try w.writeAll("[no instructions]");
20432043 return;
20442044 }
20452045 var it = val.set.keyIterator();
2046 try bw.print("%{f}", .{it.next().?.*});
2046 try w.print("%{f}", .{it.next().?.*});
20472047 while (it.next()) |key| {
2048 try bw.print(" %{f}", .{key.*});
2048 try w.print(" %{f}", .{key.*});
20492049 }
20502050 }
20512051};
......@@ -2057,15 +2057,14 @@ fn fmtInstList(list: []const Air.Inst.Index) FmtInstList {
20572057const FmtInstList = struct {
20582058 list: []const Air.Inst.Index,
20592059
2060 pub fn format(val: FmtInstList, bw: *Writer, comptime fmt: []const u8) !void {
2061 comptime assert(fmt.len == 0);
2060 pub fn format(val: FmtInstList, w: *std.io.Writer) std.io.Writer.Error!void {
20622061 if (val.list.len == 0) {
2063 try bw.writeAll("[no instructions]");
2062 try w.writeAll("[no instructions]");
20642063 return;
20652064 }
2066 try bw.print("%{f}", .{val.list[0]});
2065 try w.print("%{f}", .{val.list[0]});
20672066 for (val.list[1..]) |inst| {
2068 try bw.print(" %{f}", .{inst});
2067 try w.print(" %{f}", .{inst});
20692068 }
20702069 }
20712070};
src/Air/Liveness/Verify.zig+5-3
......@@ -511,7 +511,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
511511
512512 // The same stuff should be alive after the loop as before it.
513513 const gop = try self.loops.getOrPut(self.gpa, inst);
514 if (gop.found_existing) return invalid("%{}: loop already exists", .{@intFromEnum(inst)});
514 if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)});
515515 defer {
516516 var live = self.loops.fetchRemove(inst).?;
517517 live.value.deinit(self.gpa);
......@@ -560,7 +560,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
560560 // after the loop as before it.
561561 {
562562 const gop = try self.loops.getOrPut(self.gpa, inst);
563 if (gop.found_existing) return invalid("%{}: loop already exists", .{@intFromEnum(inst)});
563 if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)});
564564 gop.value_ptr.* = self.live.move();
565565 }
566566 defer {
......@@ -601,7 +601,9 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies
601601 return;
602602 };
603603 if (dies) {
604 if (!self.live.remove(operand)) return invalid("%{f}: dead operand %{f} reused and killed again", .{ inst, operand });
604 if (!self.live.remove(operand)) return invalid("%{f}: dead operand %{f} reused and killed again", .{
605 inst, operand,
606 });
605607 } else {
606608 if (!self.live.contains(operand)) return invalid("%{f}: dead operand %{f} reused", .{ inst, operand });
607609 }
src/Air/print.zig+6-6
......@@ -518,13 +518,13 @@ const Writer = struct {
518518 if (mask_idx > 0) try s.writeAll(", ");
519519 switch (mask_elem.unwrap()) {
520520 .elem => |idx| try s.print("elem {d}", .{idx}),
521 .value => |val| try s.print("val {}", .{Value.fromInterned(val).fmtValue(w.pt)}),
521 .value => |val| try s.print("val {f}", .{Value.fromInterned(val).fmtValue(w.pt)}),
522522 }
523523 }
524524 try s.writeByte(']');
525525 }
526526
527 fn writeShuffleTwo(w: *Writer, s: anytype, inst: Air.Inst.Index) Error!void {
527 fn writeShuffleTwo(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
528528 const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst);
529529 try w.writeType(s, unwrapped.result_ty);
530530 try s.writeAll(", ");
......@@ -590,7 +590,7 @@ const Writer = struct {
590590 const ip = &w.pt.zcu.intern_pool;
591591 const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
592592 try w.writeType(s, .fromInterned(ty_nav.ty));
593 try s.print(", '{}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)});
593 try s.print(", '{f}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)});
594594 }
595595
596596 fn writeAtomicLoad(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
......@@ -710,7 +710,7 @@ const Writer = struct {
710710 }
711711 }
712712 const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len];
713 try s.print(", \"{f}\"", .{std.zig.fmtEscapes(asm_source)});
713 try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)});
714714 }
715715
716716 fn writeDbgStmt(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
......@@ -722,7 +722,7 @@ const Writer = struct {
722722 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
723723 try w.writeOperand(s, inst, 0, pl_op.operand);
724724 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
725 try s.print(", \"{f}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))});
725 try s.print(", \"{f}\"", .{std.zig.fmtString(name.toSlice(w.air))});
726726 }
727727
728728 fn writeCall(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
......@@ -1010,7 +1010,7 @@ const Writer = struct {
10101010
10111011 fn writeInstRef(
10121012 w: *Writer,
1013 s: anytype,
1013 s: *std.io.Writer,
10141014 operand: Air.Inst.Ref,
10151015 dies: bool,
10161016 ) Error!void {
src/Builtin.zig+31-31
......@@ -57,49 +57,49 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
5757 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
5858 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
5959 \\pub const zig_version_string = "{s}";
60 \\pub const zig_backend = std.builtin.CompilerBackend.{fp_};
60 \\pub const zig_backend = std.builtin.CompilerBackend.{f};
6161 \\
62 \\pub const output_mode: std.builtin.OutputMode = .{fp_};
63 \\pub const link_mode: std.builtin.LinkMode = .{fp_};
64 \\pub const unwind_tables: std.builtin.UnwindTables = .{fp_};
62 \\pub const output_mode: std.builtin.OutputMode = .{f};
63 \\pub const link_mode: std.builtin.LinkMode = .{f};
64 \\pub const unwind_tables: std.builtin.UnwindTables = .{f};
6565 \\pub const is_test = {};
6666 \\pub const single_threaded = {};
67 \\pub const abi: std.Target.Abi = .{fp_};
67 \\pub const abi: std.Target.Abi = .{f};
6868 \\pub const cpu: std.Target.Cpu = .{{
69 \\ .arch = .{fp_},
70 \\ .model = &std.Target.{fp_}.cpu.{fp_},
71 \\ .features = std.Target.{fp_}.featureSet(&.{{
69 \\ .arch = .{f},
70 \\ .model = &std.Target.{f}.cpu.{f},
71 \\ .features = std.Target.{f}.featureSet(&.{{
7272 \\
7373 , .{
7474 build_options.version,
75 std.zig.fmtId(@tagName(zig_backend)),
76 std.zig.fmtId(@tagName(opts.output_mode)),
77 std.zig.fmtId(@tagName(opts.link_mode)),
78 std.zig.fmtId(@tagName(opts.unwind_tables)),
75 std.zig.fmtIdPU(@tagName(zig_backend)),
76 std.zig.fmtIdPU(@tagName(opts.output_mode)),
77 std.zig.fmtIdPU(@tagName(opts.link_mode)),
78 std.zig.fmtIdPU(@tagName(opts.unwind_tables)),
7979 opts.is_test,
8080 opts.single_threaded,
81 std.zig.fmtId(@tagName(target.abi)),
82 std.zig.fmtId(@tagName(target.cpu.arch)),
83 std.zig.fmtId(arch_family_name),
84 std.zig.fmtId(target.cpu.model.name),
85 std.zig.fmtId(arch_family_name),
81 std.zig.fmtIdPU(@tagName(target.abi)),
82 std.zig.fmtIdPU(@tagName(target.cpu.arch)),
83 std.zig.fmtIdPU(arch_family_name),
84 std.zig.fmtIdPU(target.cpu.model.name),
85 std.zig.fmtIdPU(arch_family_name),
8686 });
8787
8888 for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
8989 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
9090 const is_enabled = target.cpu.features.isEnabled(index);
9191 if (is_enabled) {
92 try buffer.print(" .{fp_},\n", .{std.zig.fmtId(feature.name)});
92 try buffer.print(" .{f},\n", .{std.zig.fmtIdPU(feature.name)});
9393 }
9494 }
9595 try buffer.print(
9696 \\ }}),
9797 \\}};
9898 \\pub const os: std.Target.Os = .{{
99 \\ .tag = .{fp_},
99 \\ .tag = .{f},
100100 \\ .version_range = .{{
101101 ,
102 .{std.zig.fmtId(@tagName(target.os.tag))},
102 .{std.zig.fmtIdPU(@tagName(target.os.tag))},
103103 );
104104
105105 switch (target.os.versionRange()) {
......@@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
200200 }),
201201 .windows => |windows| try buffer.print(
202202 \\ .windows = .{{
203 \\ .min = {fc},
204 \\ .max = {fc},
203 \\ .min = {f},
204 \\ .max = {f},
205205 \\ }}}},
206206 \\
207207 , .{ windows.min, windows.max }),
......@@ -238,8 +238,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
238238 const link_libc = opts.link_libc;
239239
240240 try buffer.print(
241 \\pub const object_format: std.Target.ObjectFormat = .{fp_};
242 \\pub const mode: std.builtin.OptimizeMode = .{fp_};
241 \\pub const object_format: std.Target.ObjectFormat = .{f};
242 \\pub const mode: std.builtin.OptimizeMode = .{f};
243243 \\pub const link_libc = {};
244244 \\pub const link_libcpp = {};
245245 \\pub const have_error_return_tracing = {};
......@@ -249,12 +249,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
249249 \\pub const position_independent_code = {};
250250 \\pub const position_independent_executable = {};
251251 \\pub const strip_debug_info = {};
252 \\pub const code_model: std.builtin.CodeModel = .{fp_};
252 \\pub const code_model: std.builtin.CodeModel = .{f};
253253 \\pub const omit_frame_pointer = {};
254254 \\
255255 , .{
256 std.zig.fmtId(@tagName(target.ofmt)),
257 std.zig.fmtId(@tagName(opts.optimize_mode)),
256 std.zig.fmtIdPU(@tagName(target.ofmt)),
257 std.zig.fmtIdPU(@tagName(opts.optimize_mode)),
258258 link_libc,
259259 opts.link_libcpp,
260260 opts.error_tracing,
......@@ -264,15 +264,15 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
264264 opts.pic,
265265 opts.pie,
266266 opts.strip,
267 std.zig.fmtId(@tagName(opts.code_model)),
267 std.zig.fmtIdPU(@tagName(opts.code_model)),
268268 opts.omit_frame_pointer,
269269 });
270270
271271 if (target.os.tag == .wasi) {
272272 try buffer.print(
273 \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{fp_};
273 \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{f};
274274 \\
275 , .{std.zig.fmtId(@tagName(opts.wasi_exec_model))});
275 , .{std.zig.fmtIdPU(@tagName(opts.wasi_exec_model))});
276276 }
277277
278278 if (opts.is_test) {
......@@ -317,7 +317,7 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
317317 if (root_dir.statFile(sub_path)) |stat| {
318318 if (stat.size != file.source.?.len) {
319319 std.log.warn(
320 "the cached file '{f}{s}' had the wrong size. Expected {d}, found {d}. " ++
320 "the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++
321321 "Overwriting with correct file contents now",
322322 .{ file.path.fmt(comp), file.source.?.len, stat.size },
323323 );
src/Compilation.zig+38-74
......@@ -237,7 +237,6 @@ fuzzer_lib: ?CrtFile = null,
237237glibc_so_files: ?glibc.BuiltSharedObjects = null,
238238freebsd_so_files: ?freebsd.BuiltSharedObjects = null,
239239netbsd_so_files: ?netbsd.BuiltSharedObjects = null,
240wasi_emulated_libs: []const wasi_libc.CrtFile,
241240
242241/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
243242/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
......@@ -403,9 +402,7 @@ pub const Path = struct {
403402 const Formatter = struct {
404403 p: Path,
405404 comp: *Compilation,
406 pub fn format(f: Formatter, comptime unused_fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void {
407 comptime assert(unused_fmt.len == 0);
408 _ = options;
405 pub fn format(f: Formatter, w: *std.io.Writer) std.io.Writer.Error!void {
409406 const root_path: []const u8 = switch (f.p.root) {
410407 .zig_lib => f.comp.dirs.zig_lib.path orelse ".",
411408 .global_cache => f.comp.dirs.global_cache.path orelse ".",
......@@ -734,10 +731,10 @@ pub const Directories = struct {
734731 };
735732
736733 if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) {
737 fatal("zig lib directory '{}' cannot be equal to global cache directory '{}'", .{ zig_lib, global_cache });
734 fatal("zig lib directory '{f}' cannot be equal to global cache directory '{f}'", .{ zig_lib, global_cache });
738735 }
739736 if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) {
740 fatal("zig lib directory '{}' cannot be equal to local cache directory '{}'", .{ zig_lib, local_cache });
737 fatal("zig lib directory '{f}' cannot be equal to local cache directory '{f}'", .{ zig_lib, local_cache });
741738 }
742739
743740 return .{
......@@ -1570,12 +1567,6 @@ pub const CreateOptions = struct {
15701567 framework_dirs: []const []const u8 = &[0][]const u8{},
15711568 frameworks: []const Framework = &.{},
15721569 windows_lib_names: []const []const u8 = &.{},
1573 /// These correspond to the WASI libc emulated subcomponents including:
1574 /// * process clocks
1575 /// * getpid
1576 /// * mman
1577 /// * signal
1578 wasi_emulated_libs: []const wasi_libc.CrtFile = &.{},
15791570 /// This means that if the output mode is an executable it will be a
15801571 /// Position Independent Executable. If the output mode is not an
15811572 /// executable this field is ignored.
......@@ -2055,7 +2046,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
20552046 .function_sections = options.function_sections,
20562047 .data_sections = options.data_sections,
20572048 .native_system_include_paths = options.native_system_include_paths,
2058 .wasi_emulated_libs = options.wasi_emulated_libs,
20592049 .force_undefined_symbols = options.force_undefined_symbols,
20602050 .link_eh_frame_hdr = link_eh_frame_hdr,
20612051 .global_cc_argv = options.global_cc_argv,
......@@ -2070,12 +2060,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
20702060 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
20712061 };
20722062
2073 errdefer {
2074 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
2075 comp.windows_libs.deinit(gpa);
2076 }
2077 try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len);
2078 for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {});
2063 comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
2064 errdefer comp.windows_libs.deinit(gpa);
20792065
20802066 // Prevent some footguns by making the "any" fields of config reflect
20812067 // the default Module settings.
......@@ -2306,6 +2292,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23062292
23072293 if (comp.emit_bin != null and target.ofmt != .c) {
23082294 if (!comp.skip_linker_dependencies) {
2295 // These DLLs are always loaded into every Windows process.
2296 if (target.os.tag == .windows and is_exe_or_dyn_lib) {
2297 try comp.windows_libs.ensureUnusedCapacity(gpa, 2);
2298 comp.windows_libs.putAssumeCapacity("kernel32", {});
2299 comp.windows_libs.putAssumeCapacity("ntdll", {});
2300 }
2301
23092302 // If we need to build libc for the target, add work items for it.
23102303 // We go through the work queue so that building can be done in parallel.
23112304 // If linking against host libc installation, instead queue up jobs
......@@ -2381,11 +2374,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23812374 } else if (target.isWasiLibC()) {
23822375 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
23832376
2384 for (comp.wasi_emulated_libs) |crt_file| {
2385 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = true;
2386 }
2387 comp.link_task_queue.pending_prelink_tasks += @intCast(comp.wasi_emulated_libs.len);
2388
23892377 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;
23902378 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true;
23912379 comp.link_task_queue.pending_prelink_tasks += 2;
......@@ -2399,7 +2387,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23992387
24002388 // When linking mingw-w64 there are some import libs we always need.
24012389 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
2402 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {});
2390 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
24032391 } else {
24042392 return error.LibCUnavailable;
24052393 }
......@@ -2497,7 +2485,6 @@ pub fn destroy(comp: *Compilation) void {
24972485 comp.c_object_work_queue.deinit();
24982486 comp.win32_resource_work_queue.deinit();
24992487
2500 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
25012488 comp.windows_libs.deinit(gpa);
25022489
25032490 {
......@@ -2994,7 +2981,7 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat
29942981 break @intCast(i);
29952982 }
29962983 } else std.debug.panic(
2997 "missing prefix directory '{s}' ('{}') for '{s}'",
2984 "missing prefix directory '{s}' ('{f}') for '{s}'",
29982985 .{ @tagName(path.root), want_prefix_dir, path.sub_path },
29992986 );
30002987
......@@ -3333,7 +3320,7 @@ fn emitFromCObject(
33333320 emit_path.root_dir.handle,
33343321 emit_path.sub_path,
33353322 .{},
3336 ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{
3323 ) catch |err| log.err("unable to copy '{f}' to '{f}': {s}", .{
33373324 src_path,
33383325 emit_path,
33393326 @errorName(err),
......@@ -3681,7 +3668,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
36813668 .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"),
36823669 },
36833670 .src_loc = try bundle.addSourceLocation(.{
3684 .src_path = try bundle.printString("{}", .{file.path.fmt(comp)}),
3671 .src_path = try bundle.printString("{f}", .{file.path.fmt(comp)}),
36853672 .span_start = start,
36863673 .span_main = start,
36873674 .span_end = @intCast(end),
......@@ -3728,7 +3715,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
37283715 assert(!is_retryable);
37293716 // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors.
37303717 _ = try file.getTree(zcu); // Tree must be loaded.
3731 const path = try std.fmt.allocPrint(gpa, "{}", .{file.path.fmt(comp)});
3718 const path = try std.fmt.allocPrint(gpa, "{f}", .{file.path.fmt(comp)});
37323719 defer gpa.free(path);
37333720 if (file.zir != null) {
37343721 try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path);
......@@ -3784,8 +3771,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
37843771 }
37853772
37863773 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{
3787 error_msg.msg,
3788 zcu.fmtAnalUnit(anal_unit),
3774 error_msg.msg, zcu.fmtAnalUnit(anal_unit),
37893775 });
37903776
37913777 try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error);
......@@ -4047,7 +4033,7 @@ pub fn addModuleErrorMsg(
40474033 const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
40484034 const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {
40494035 try eb.addRootErrorMessage(.{
4050 .msg = try eb.printString("unable to load '{}': {s}", .{
4036 .msg = try eb.printString("unable to load '{f}': {s}", .{
40514037 err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err),
40524038 }),
40534039 });
......@@ -4110,7 +4096,7 @@ pub fn addModuleErrorMsg(
41104096 }
41114097
41124098 const src_loc = try eb.addSourceLocation(.{
4113 .src_path = try eb.printString("{}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}),
4099 .src_path = try eb.printString("{f}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}),
41144100 .span_start = err_span.start,
41154101 .span_main = err_span.main,
41164102 .span_end = err_span.end,
......@@ -4142,7 +4128,7 @@ pub fn addModuleErrorMsg(
41424128 const gop = try notes.getOrPutContext(gpa, .{
41434129 .msg = try eb.addString(module_note.msg),
41444130 .src_loc = try eb.addSourceLocation(.{
4145 .src_path = try eb.printString("{}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}),
4131 .src_path = try eb.printString("{f}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}),
41464132 .span_start = span.start,
41474133 .span_main = span.main,
41484134 .span_end = span.end,
......@@ -4187,7 +4173,7 @@ fn addReferenceTraceFrame(
41874173 try ref_traces.append(gpa, .{
41884174 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
41894175 .src_loc = try eb.addSourceLocation(.{
4190 .src_path = try eb.printString("{}", .{src.file_scope.path.fmt(zcu.comp)}),
4176 .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}),
41914177 .span_start = span.start,
41924178 .span_main = span.main,
41934179 .span_end = span.end,
......@@ -4906,7 +4892,7 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,
49064892 var walker = try mod_dir.walk(comp.gpa);
49074893 defer walker.deinit();
49084894
4909 var archiver = std.tar.writer(tar_file.writer().any());
4895 var archiver = std.tar.writer(tar_file.deprecatedWriter().any());
49104896 archiver.prefix = name;
49114897
49124898 while (try walker.next()) |entry| {
......@@ -4919,13 +4905,13 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,
49194905 else => continue,
49204906 }
49214907 var file = mod_dir.openFile(entry.path, .{}) catch |err| {
4922 return comp.lockAndSetMiscFailure(.docs_copy, "unable to open '{}{s}': {s}", .{
4908 return comp.lockAndSetMiscFailure(.docs_copy, "unable to open '{f}{s}': {s}", .{
49234909 root.fmt(comp), entry.path, @errorName(err),
49244910 });
49254911 };
49264912 defer file.close();
49274913 archiver.writeFile(entry.path, file) catch |err| {
4928 return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive '{}{s}': {s}", .{
4914 return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive '{f}{s}': {s}", .{
49294915 root.fmt(comp), entry.path, @errorName(err),
49304916 });
49314917 };
......@@ -5055,7 +5041,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
50555041 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
50565042 return comp.lockAndSetMiscFailure(
50575043 .docs_copy,
5058 "unable to create output directory '{}': {s}",
5044 "unable to create output directory '{f}': {s}",
50595045 .{ docs_path, @errorName(err) },
50605046 );
50615047 };
......@@ -5067,10 +5053,8 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
50675053 "main.wasm",
50685054 .{},
50695055 ) catch |err| {
5070 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{
5071 crt_file.full_object_path,
5072 docs_path,
5073 @errorName(err),
5056 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {s}", .{
5057 crt_file.full_object_path, docs_path, @errorName(err),
50745058 });
50755059 };
50765060}
......@@ -6024,16 +6008,15 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
60246008
60256009 // In .rc files, a " within a quoted string is escaped as ""
60266010 const fmtRcEscape = struct {
6027 fn formatRcEscape(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void {
6028 comptime assert(fmt.len == 0);
6011 fn formatRcEscape(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
60296012 for (bytes) |byte| switch (byte) {
6030 '"' => try bw.writeAll("\"\""),
6031 '\\' => try bw.writeAll("\\\\"),
6032 else => try bw.writeByte(byte),
6013 '"' => try writer.writeAll("\"\""),
6014 '\\' => try writer.writeAll("\\\\"),
6015 else => try writer.writeByte(byte),
60336016 };
60346017 }
60356018
6036 pub fn fmtRcEscape(bytes: []const u8) std.fmt.Formatter(formatRcEscape) {
6019 pub fn fmtRcEscape(bytes: []const u8) std.fmt.Formatter([]const u8, formatRcEscape) {
60376020 return .{ .data = bytes };
60386021 }
60396022 }.fmtRcEscape;
......@@ -6047,7 +6030,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
60476030 // 24 is RT_MANIFEST
60486031 const resource_type = 24;
60496032
6050 const input = try std.fmt.allocPrint(arena, "{} {} \"{f}\"", .{ resource_id, resource_type, fmtRcEscape(src_path) });
6033 const input = try std.fmt.allocPrint(arena, "{d} {d} \"{f}\"", .{
6034 resource_id, resource_type, fmtRcEscape(src_path),
6035 });
60516036
60526037 try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input });
60536038
......@@ -6259,7 +6244,7 @@ fn spawnZigRc(
62596244 }
62606245
62616246 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
6262 const stderr_reader = child.stderr.?.reader();
6247 const stderr_reader = child.stderr.?.deprecatedReader();
62636248 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
62646249
62656250 const term = child.wait() catch |err| {
......@@ -6474,7 +6459,7 @@ pub fn addCCArgs(
64746459 try argv.append("-fno-asynchronous-unwind-tables");
64756460 try argv.append("-funwind-tables");
64766461 },
6477 .@"async" => try argv.append("-fasynchronous-unwind-tables"),
6462 .async => try argv.append("-fasynchronous-unwind-tables"),
64786463 }
64796464
64806465 try argv.append("-nostdinc");
......@@ -7597,27 +7582,6 @@ fn getCrtPathsInner(
75977582 };
75987583}
75997584
7600pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
7601 // Avoid deadlocking on building import libs such as kernel32.lib
7602 // This can happen when the user uses `build-exe foo.obj -lkernel32` and
7603 // then when we create a sub-Compilation for zig libc, it also tries to
7604 // build kernel32.lib.
7605 if (comp.skip_linker_dependencies) return;
7606 const target = &comp.root_mod.resolved_target.result;
7607 if (target.os.tag != .windows or target.ofmt == .c) return;
7608
7609 // This happens when an `extern "foo"` function is referenced.
7610 // If we haven't seen this library yet and we're targeting Windows, we need
7611 // to queue up a work item to produce the DLL import library for this.
7612 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
7613 if (gop.found_existing) return;
7614 {
7615 errdefer _ = comp.windows_libs.pop();
7616 gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name);
7617 }
7618 try comp.queueJob(.{ .windows_import_lib = gop.index });
7619}
7620
76217585/// This decides the optimization mode for all zig-provided libraries, including
76227586/// compiler-rt, libcxx, libc, libunwind, etc.
76237587pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode {
src/IncrementalDebugServer.zig+5-5
......@@ -142,8 +142,8 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons
142142 const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index");
143143 const nav = ip.getNav(nav_index);
144144 try w.print(
145 \\name: '{}'
146 \\fqn: '{}'
145 \\name: '{f}'
146 \\fqn: '{f}'
147147 \\status: {s}
148148 \\created on generation: {d}
149149 \\
......@@ -234,7 +234,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons
234234 for (unit_info.deps.items, 0..) |dependee, i| {
235235 try w.print("[{d}] ", .{i});
236236 switch (dependee) {
237 .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{}", .{zcu.fmtDependee(dependee)}),
237 .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}),
238238 .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }),
239239 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
240240 .struct_type, .union_type, .enum_type => try w.print("type {d}", .{@intFromEnum(ip_index)}),
......@@ -260,7 +260,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons
260260 const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"));
261261 const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type");
262262 try w.print(
263 \\name: '{}'
263 \\name: '{f}'
264264 \\created on generation: {d}
265265 \\
266266 , .{
......@@ -365,7 +365,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void {
365365 .union_type,
366366 .enum_type,
367367 .opaque_type,
368 => try w.print("{}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @intFromEnum(ty.toIntern()) }),
368 => try w.print("{f}[{d}]", .{ ty.containerTypeName(ip).fmt(ip), @intFromEnum(ty.toIntern()) }),
369369
370370 else => unreachable,
371371 }
src/InternPool.zig+1-13
......@@ -518,8 +518,6 @@ pub const Nav = struct {
518518 namespace: NamespaceIndex,
519519 zir_index: TrackedInst.Index,
520520 },
521 /// TODO: this is a hack! If #20663 isn't accepted, let's figure out something a bit better.
522 is_usingnamespace: bool,
523521 status: union(enum) {
524522 /// This `Nav` is pending semantic analysis.
525523 unresolved,
......@@ -735,7 +733,7 @@ pub const Nav = struct {
735733 @"addrspace": std.builtin.AddressSpace,
736734 /// Populated only if `bits.status == .type_resolved`.
737735 is_threadlocal: bool,
738 is_usingnamespace: bool,
736 _: u1 = 0,
739737 };
740738
741739 fn unpack(repr: Repr) Nav {
......@@ -749,7 +747,6 @@ pub const Nav = struct {
749747 assert(repr.analysis_zir_index == .none);
750748 break :a null;
751749 },
752 .is_usingnamespace = repr.bits.is_usingnamespace,
753750 .status = switch (repr.bits.status) {
754751 .unresolved => .unresolved,
755752 .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{
......@@ -797,7 +794,6 @@ pub const Nav = struct {
797794 .is_const = false,
798795 .alignment = .none,
799796 .@"addrspace" = .generic,
800 .is_usingnamespace = nav.is_usingnamespace,
801797 .is_threadlocal = false,
802798 },
803799 .type_resolved => |r| .{
......@@ -805,7 +801,6 @@ pub const Nav = struct {
805801 .is_const = r.is_const,
806802 .alignment = r.alignment,
807803 .@"addrspace" = r.@"addrspace",
808 .is_usingnamespace = nav.is_usingnamespace,
809804 .is_threadlocal = r.is_threadlocal,
810805 },
811806 .fully_resolved => |r| .{
......@@ -813,7 +808,6 @@ pub const Nav = struct {
813808 .is_const = r.is_const,
814809 .alignment = r.alignment,
815810 .@"addrspace" = r.@"addrspace",
816 .is_usingnamespace = nav.is_usingnamespace,
817811 .is_threadlocal = false,
818812 },
819813 },
......@@ -6865,8 +6859,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
68656859 {
68666860 namespace.pub_decls.deinit(gpa);
68676861 namespace.priv_decls.deinit(gpa);
6868 namespace.pub_usingnamespace.deinit(gpa);
6869 namespace.priv_usingnamespace.deinit(gpa);
68706862 namespace.comptime_decls.deinit(gpa);
68716863 namespace.test_decls.deinit(gpa);
68726864 }
......@@ -11502,7 +11494,6 @@ pub fn createNav(
1150211494 .@"linksection" = opts.@"linksection",
1150311495 .@"addrspace" = opts.@"addrspace",
1150411496 } },
11505 .is_usingnamespace = false,
1150611497 }));
1150711498 return index_unwrapped.wrap(ip);
1150811499}
......@@ -11517,8 +11508,6 @@ pub fn createDeclNav(
1151711508 fqn: NullTerminatedString,
1151811509 zir_index: TrackedInst.Index,
1151911510 namespace: NamespaceIndex,
11520 /// TODO: this is hacky! See `Nav.is_usingnamespace`.
11521 is_usingnamespace: bool,
1152211511) Allocator.Error!Nav.Index {
1152311512 const navs = ip.getLocal(tid).getMutableNavs(gpa);
1152411513
......@@ -11537,7 +11526,6 @@ pub fn createDeclNav(
1153711526 .zir_index = zir_index,
1153811527 },
1153911528 .status = .unresolved,
11540 .is_usingnamespace = is_usingnamespace,
1154111529 }));
1154211530
1154311531 return nav;
src/Package/Fetch.zig+47-44
......@@ -27,6 +27,22 @@
2727//! All of this must be done with only referring to the state inside this struct
2828//! because this work will be done in a dedicated thread.
2929
30const builtin = @import("builtin");
31const std = @import("std");
32const fs = std.fs;
33const assert = std.debug.assert;
34const ascii = std.ascii;
35const Allocator = std.mem.Allocator;
36const Cache = std.Build.Cache;
37const ThreadPool = std.Thread.Pool;
38const WaitGroup = std.Thread.WaitGroup;
39const Fetch = @This();
40const git = @import("Fetch/git.zig");
41const Package = @import("../Package.zig");
42const Manifest = Package.Manifest;
43const ErrorBundle = std.zig.ErrorBundle;
44const native_os = builtin.os.tag;
45
3046arena: std.heap.ArenaAllocator,
3147location: Location,
3248location_tok: std.zig.Ast.TokenIndex,
......@@ -184,7 +200,7 @@ pub const JobQueue = struct {
184200
185201 const hash_slice = hash.toSlice();
186202
187 try buf.print(
203 try buf.writer().print(
188204 \\ pub const {f} = struct {{
189205 \\
190206 , .{std.zig.fmtId(hash_slice)});
......@@ -211,15 +227,15 @@ pub const JobQueue = struct {
211227 }
212228
213229 try buf.print(
214 \\ pub const build_root = "{fq}";
230 \\ pub const build_root = "{f}";
215231 \\
216 , .{fetch.package_root});
232 , .{std.fmt.alt(fetch.package_root, .formatEscapeString)});
217233
218234 if (fetch.has_build_zig) {
219235 try buf.print(
220236 \\ pub const build_zig = @import("{f}");
221237 \\
222 , .{std.zig.fmtEscapes(hash_slice)});
238 , .{std.zig.fmtString(hash_slice)});
223239 }
224240
225241 if (fetch.manifest) |*manifest| {
......@@ -231,7 +247,7 @@ pub const JobQueue = struct {
231247 const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue;
232248 try buf.print(
233249 " .{{ \"{f}\", \"{f}\" }},\n",
234 .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) },
250 .{ std.zig.fmtEscapes(name), std.zig.fmtString(h.toSlice()) },
235251 );
236252 }
237253
......@@ -263,7 +279,7 @@ pub const JobQueue = struct {
263279 const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue;
264280 try buf.print(
265281 " .{{ \"{f}\", \"{f}\" }},\n",
266 .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) },
282 .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) },
267283 );
268284 }
269285 try buf.appendSlice("};\n");
......@@ -420,14 +436,14 @@ pub fn run(f: *Fetch) RunError!void {
420436 }
421437 if (f.job_queue.read_only) return f.fail(
422438 f.name_tok,
423 try eb.printString("package not found at '{}{s}'", .{
439 try eb.printString("package not found at '{f}{s}'", .{
424440 cache_root, pkg_sub_path,
425441 }),
426442 );
427443 },
428444 else => |e| {
429445 try eb.addRootErrorMessage(.{
430 .msg = try eb.printString("unable to open global package cache directory '{}{s}': {s}", .{
446 .msg = try eb.printString("unable to open global package cache directory '{f}{s}': {s}", .{
431447 cache_root, pkg_sub_path, @errorName(e),
432448 }),
433449 });
......@@ -961,7 +977,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
961977 if (ascii.eqlIgnoreCase(uri.scheme, "file")) {
962978 const path = try uri.path.toRawMaybeAlloc(arena);
963979 return .{ .file = f.parent_package_root.openFile(path, .{}) catch |err| {
964 return f.fail(f.location_tok, try eb.printString("unable to open '{}{s}': {s}", .{
980 return f.fail(f.location_tok, try eb.printString("unable to open '{f}{s}': {s}", .{
965981 f.parent_package_root, path, @errorName(err),
966982 }));
967983 } };
......@@ -1063,13 +1079,16 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
10631079 });
10641080 const notes_start = try eb.reserveNotes(notes_len);
10651081 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{
1066 .msg = try eb.printString("try .url = \"{;+/}#{}\",", .{ uri, want_oid }),
1082 .msg = try eb.printString("try .url = \"{f}#{f}\",", .{
1083 uri.fmt(.{ .scheme = true, .authority = true, .path = true }),
1084 want_oid,
1085 }),
10671086 }));
10681087 return error.FetchFailed;
10691088 }
10701089
10711090 var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined;
1072 _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{want_oid}) catch unreachable;
1091 _ = std.fmt.bufPrint(&want_oid_buf, "{f}", .{want_oid}) catch unreachable;
10731092 var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| {
10741093 return f.fail(f.location_tok, try eb.printString(
10751094 "unable to create fetch stream: {s}",
......@@ -1305,7 +1324,7 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult {
13051324 .{@errorName(err)},
13061325 ));
13071326 if (len == 0) break;
1308 zip_file.writer().writeAll(buf[0..len]) catch |err| return f.fail(f.location_tok, try eb.printString(
1327 zip_file.deprecatedWriter().writeAll(buf[0..len]) catch |err| return f.fail(f.location_tok, try eb.printString(
13091328 "write temporary zip file failed: {s}",
13101329 .{@errorName(err)},
13111330 ));
......@@ -1813,28 +1832,6 @@ pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifes
18131832 }
18141833}
18151834
1816const builtin = @import("builtin");
1817const std = @import("std");
1818const fs = std.fs;
1819const assert = std.debug.assert;
1820const ascii = std.ascii;
1821const Allocator = std.mem.Allocator;
1822const Cache = std.Build.Cache;
1823const ThreadPool = std.Thread.Pool;
1824const WaitGroup = std.Thread.WaitGroup;
1825const Fetch = @This();
1826const git = @import("Fetch/git.zig");
1827const Package = @import("../Package.zig");
1828const Manifest = Package.Manifest;
1829const ErrorBundle = std.zig.ErrorBundle;
1830const native_os = builtin.os.tag;
1831
1832test {
1833 _ = Filter;
1834 _ = FileType;
1835 _ = UnpackResult;
1836}
1837
18381835// Detects executable header: ELF or Macho-O magic header or shebang line.
18391836const FileHeader = struct {
18401837 header: [4]u8 = undefined,
......@@ -2052,15 +2049,15 @@ const UnpackResult = struct {
20522049 // output errors to string
20532050 var errors = try fetch.error_bundle.toOwnedBundle("");
20542051 defer errors.deinit(gpa);
2055 var out = std.ArrayList(u8).init(gpa);
2056 defer out.deinit();
2057 try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer());
2052 var aw: std.io.Writer.Allocating = .init(gpa);
2053 defer aw.deinit();
2054 try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer);
20582055 try std.testing.expectEqualStrings(
20592056 \\error: unable to unpack
20602057 \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError
20612058 \\ note: file 'dir2/file4' has unsupported type 'x'
20622059 \\
2063 , out.items);
2060 , aw.getWritten());
20642061 }
20652062};
20662063
......@@ -2076,7 +2073,7 @@ test "zip" {
20762073 {
20772074 var zip_file = try tmp.dir.createFile("test.zip", .{});
20782075 defer zip_file.close();
2079 var bw = std.io.bufferedWriter(zip_file.writer());
2076 var bw = std.io.bufferedWriter(zip_file.deprecatedWriter());
20802077 var store: [test_files.len]std.zip.testutil.FileStore = undefined;
20812078 try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{});
20822079 try bw.flush();
......@@ -2109,7 +2106,7 @@ test "zip with one root folder" {
21092106 {
21102107 var zip_file = try tmp.dir.createFile("test.zip", .{});
21112108 defer zip_file.close();
2112 var bw = std.io.bufferedWriter(zip_file.writer());
2109 var bw = std.io.bufferedWriter(zip_file.deprecatedWriter());
21132110 var store: [test_files.len]std.zip.testutil.FileStore = undefined;
21142111 try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{});
21152112 try bw.flush();
......@@ -2427,9 +2424,15 @@ const TestFetchBuilder = struct {
24272424 if (notes_len > 0) {
24282425 try std.testing.expectEqual(notes_len, em.notes_len);
24292426 }
2430 var al = std.ArrayList(u8).init(std.testing.allocator);
2431 defer al.deinit();
2432 try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer());
2433 try std.testing.expectEqualStrings(msg, al.items);
2427 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
2428 defer aw.deinit();
2429 try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer);
2430 try std.testing.expectEqualStrings(msg, aw.getWritten());
24342431 }
24352432};
2433
2434test {
2435 _ = Filter;
2436 _ = FileType;
2437 _ = UnpackResult;
2438}
src/Package/Fetch/git.zig+27-12
......@@ -135,9 +135,8 @@ pub const Oid = union(Format) {
135135 } else error.InvalidOid;
136136 }
137137
138 pub fn format(oid: Oid, w: *Writer, comptime fmt: []const u8) Writer.Error!void {
139 comptime assert(fmt.len == 0);
140 try w.print("{x}", .{oid.slice()});
138 pub fn format(oid: Oid, writer: *std.io.Writer) std.io.Writer.Error!void {
139 try writer.print("{x}", .{oid.slice()});
141140 }
142141
143142 pub fn slice(oid: *const Oid) []const u8 {
......@@ -697,13 +696,21 @@ pub const Session = struct {
697696 fn init(allocator: Allocator, uri: std.Uri) !Location {
698697 const scheme = try allocator.dupe(u8, uri.scheme);
699698 errdefer allocator.free(scheme);
700 const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{user}", .{user}) else null;
699 const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{f}", .{
700 std.fmt.alt(user, .formatUser),
701 }) else null;
701702 errdefer if (user) |s| allocator.free(s);
702 const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{password}", .{password}) else null;
703 const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{f}", .{
704 std.fmt.alt(password, .formatPassword),
705 }) else null;
703706 errdefer if (password) |s| allocator.free(s);
704 const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{host}", .{host}) else null;
707 const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{f}", .{
708 std.fmt.alt(host, .formatHost),
709 }) else null;
705710 errdefer if (host) |s| allocator.free(s);
706 const path = try std.fmt.allocPrint(allocator, "{path}", .{uri.path});
711 const path = try std.fmt.allocPrint(allocator, "{f}", .{
712 std.fmt.alt(uri.path, .formatPath),
713 });
707714 errdefer allocator.free(path);
708715 // The query and fragment are not used as part of the base server URI.
709716 return .{
......@@ -734,7 +741,9 @@ pub const Session = struct {
734741 fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator {
735742 var info_refs_uri = session.location.uri;
736743 {
737 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{session.location.uri.path});
744 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
745 std.fmt.alt(session.location.uri.path, .formatPath),
746 });
738747 defer session.allocator.free(session_uri_path);
739748 info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) };
740749 }
......@@ -758,7 +767,9 @@ pub const Session = struct {
758767 if (request.response.status != .ok) return error.ProtocolError;
759768 const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects;
760769 if (any_redirects_occurred) {
761 const request_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{request.uri.path});
770 const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
771 std.fmt.alt(request.uri.path, .formatPath),
772 });
762773 defer session.allocator.free(request_uri_path);
763774 if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect;
764775 var new_uri = request.uri;
......@@ -845,7 +856,9 @@ pub const Session = struct {
845856 pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator {
846857 var upload_pack_uri = session.location.uri;
847858 {
848 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{session.location.uri.path});
859 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
860 std.fmt.alt(session.location.uri.path, .formatPath),
861 });
849862 defer session.allocator.free(session_uri_path);
850863 upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) };
851864 }
......@@ -962,7 +975,9 @@ pub const Session = struct {
962975 ) !FetchStream {
963976 var upload_pack_uri = session.location.uri;
964977 {
965 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{path}", .{session.location.uri.path});
978 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
979 std.fmt.alt(session.location.uri.path, .formatPath),
980 });
966981 defer session.allocator.free(session_uri_path);
967982 upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) };
968983 }
......@@ -1058,7 +1073,7 @@ pub const Session = struct {
10581073 ProtocolError,
10591074 UnexpectedPacket,
10601075 };
1061 pub const Reader = std.io.Reader(*FetchStream, ReadError, read);
1076 pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read);
10621077
10631078 const StreamCode = enum(u8) {
10641079 pack_data = 1,
src/Sema.zig+108-258
......@@ -5,6 +5,39 @@
55//! Does type checking, comptime control flow, and safety-check generation.
66//! This is the the heart of the Zig compiler.
77
8const std = @import("std");
9const math = std.math;
10const mem = std.mem;
11const Allocator = mem.Allocator;
12const assert = std.debug.assert;
13const log = std.log.scoped(.sema);
14
15const Sema = @This();
16const Value = @import("Value.zig");
17const MutableValue = @import("mutable_value.zig").MutableValue;
18const Type = @import("Type.zig");
19const Air = @import("Air.zig");
20const Zir = std.zig.Zir;
21const Zcu = @import("Zcu.zig");
22const trace = @import("tracy.zig").trace;
23const Namespace = Zcu.Namespace;
24const CompileError = Zcu.CompileError;
25const SemaError = Zcu.SemaError;
26const LazySrcLoc = Zcu.LazySrcLoc;
27const RangeSet = @import("RangeSet.zig");
28const target_util = @import("target.zig");
29const Package = @import("Package.zig");
30const crash_report = @import("crash_report.zig");
31const build_options = @import("build_options");
32const Compilation = @import("Compilation.zig");
33const InternPool = @import("InternPool.zig");
34const Alignment = InternPool.Alignment;
35const AnalUnit = InternPool.AnalUnit;
36const ComptimeAllocIndex = InternPool.ComptimeAllocIndex;
37const Cache = std.Build.Cache;
38const LowerZon = @import("Sema/LowerZon.zig");
39const arith = @import("Sema/arith.zig");
40
841pt: Zcu.PerThread,
942/// Alias to `zcu.gpa`.
1043gpa: Allocator,
......@@ -157,39 +190,6 @@ pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc {
157190 return &sema.comptime_allocs.items[@intFromEnum(idx)];
158191}
159192
160const std = @import("std");
161const math = std.math;
162const mem = std.mem;
163const Allocator = mem.Allocator;
164const assert = std.debug.assert;
165const log = std.log.scoped(.sema);
166
167const Sema = @This();
168const Value = @import("Value.zig");
169const MutableValue = @import("mutable_value.zig").MutableValue;
170const Type = @import("Type.zig");
171const Air = @import("Air.zig");
172const Zir = std.zig.Zir;
173const Zcu = @import("Zcu.zig");
174const trace = @import("tracy.zig").trace;
175const Namespace = Zcu.Namespace;
176const CompileError = Zcu.CompileError;
177const SemaError = Zcu.SemaError;
178const LazySrcLoc = Zcu.LazySrcLoc;
179const RangeSet = @import("RangeSet.zig");
180const target_util = @import("target.zig");
181const Package = @import("Package.zig");
182const crash_report = @import("crash_report.zig");
183const build_options = @import("build_options");
184const Compilation = @import("Compilation.zig");
185const InternPool = @import("InternPool.zig");
186const Alignment = InternPool.Alignment;
187const AnalUnit = InternPool.AnalUnit;
188const ComptimeAllocIndex = InternPool.ComptimeAllocIndex;
189const Cache = std.Build.Cache;
190const LowerZon = @import("Sema/LowerZon.zig");
191const arith = @import("Sema/arith.zig");
192
193193pub const default_branch_quota = 1000;
194194
195195pub const InferredErrorSet = struct {
......@@ -1144,7 +1144,7 @@ fn analyzeBodyInner(
11441144
11451145 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.
11461146 if (build_options.enable_logging) {
1147 std.log.scoped(.sema_zir).debug("sema ZIR {} %{d}", .{ path: {
1147 std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: {
11481148 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool);
11491149 const file = zcu.fileByIndex(file_index);
11501150 break :path file.path.fmt(zcu.comp);
......@@ -1280,7 +1280,6 @@ fn analyzeBodyInner(
12801280 .tag_name => try sema.zirTagName(block, inst),
12811281 .type_name => try sema.zirTypeName(block, inst),
12821282 .frame_type => try sema.zirFrameType(block, inst),
1283 .frame_size => try sema.zirFrameSize(block, inst),
12841283 .int_from_float => try sema.zirIntFromFloat(block, inst),
12851284 .float_from_int => try sema.zirFloatFromInt(block, inst),
12861285 .ptr_from_int => try sema.zirPtrFromInt(block, inst),
......@@ -1302,7 +1301,6 @@ fn analyzeBodyInner(
13021301 .mul_add => try sema.zirMulAdd(block, inst),
13031302 .builtin_call => try sema.zirBuiltinCall(block, inst),
13041303 .@"resume" => try sema.zirResume(block, inst),
1305 .@"await" => try sema.zirAwait(block, inst),
13061304 .for_len => try sema.zirForLen(block, inst),
13071305 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),
13081306 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),
......@@ -1410,12 +1408,10 @@ fn analyzeBodyInner(
14101408 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
14111409 .prefetch => try sema.zirPrefetch( block, extended),
14121410 .error_cast => try sema.zirErrorCast( block, extended),
1413 .await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
14141411 .select => try sema.zirSelect( block, extended),
14151412 .int_from_error => try sema.zirIntFromError( block, extended),
14161413 .error_from_int => try sema.zirErrorFromInt( block, extended),
14171414 .reify => try sema.zirReify( block, extended, inst),
1418 .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
14191415 .cmpxchg => try sema.zirCmpxchg( block, extended),
14201416 .c_va_arg => try sema.zirCVaArg( block, extended),
14211417 .c_va_copy => try sema.zirCVaCopy( block, extended),
......@@ -2767,7 +2763,7 @@ fn zirTupleDecl(
27672763 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);
27682764 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });
27692765 if (field_init_val.canMutateComptimeVarState(zcu)) {
2770 const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{}", .{field_index}, .no_embedded_nulls);
2766 const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
27712767 return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);
27722768 }
27732769 break :init field_init_val.toIntern();
......@@ -2864,7 +2860,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
28642860 sema.code.nullTerminatedString(str),
28652861 .no_embedded_nulls,
28662862 );
2867 const nav = try sema.lookupIdentifier(block, LazySrcLoc.unneeded, decl_name); // TODO: could we need this src loc?
2863 const nav = try sema.lookupIdentifier(block, decl_name);
28682864 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav });
28692865 },
28702866 .decl_ref => |str| capture: {
......@@ -2874,7 +2870,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
28742870 sema.code.nullTerminatedString(str),
28752871 .no_embedded_nulls,
28762872 );
2877 const nav = try sema.lookupIdentifier(block, LazySrcLoc.unneeded, decl_name); // TODO: could we need this src loc?
2873 const nav = try sema.lookupIdentifier(block, decl_name);
28782874 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav });
28792875 },
28802876 };
......@@ -3030,8 +3026,8 @@ pub fn createTypeName(
30303026
30313027 var aw: std.io.Writer.Allocating = .init(gpa);
30323028 defer aw.deinit();
3033 const bw = &aw.interface;
3034 bw.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
3029 const w = &aw.writer;
3030 w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
30353031
30363032 var arg_i: usize = 0;
30373033 for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
......@@ -3044,13 +3040,13 @@ pub fn createTypeName(
30443040 // result in a compile error.
30453041 const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat
30463042
3047 if (arg_i != 0) bw.writeByte(',') catch return error.OutOfMemory;
3043 if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory;
30483044
30493045 // Limiting the depth here helps avoid type names getting too long, which
30503046 // in turn helps to avoid unreasonably long symbol names for namespaced
30513047 // symbols. Such names should ideally be human-readable, and additionally,
30523048 // some tooling may not support very long symbol names.
3053 bw.print("{f}", .{Value.fmtValueSemaFull(.{
3049 w.print("{f}", .{Value.fmtValueSemaFull(.{
30543050 .val = arg_val,
30553051 .pt = pt,
30563052 .opt_sema = sema,
......@@ -3063,7 +3059,7 @@ pub fn createTypeName(
30633059 else => continue,
30643060 };
30653061
3066 try bw.writeByte(')');
3062 w.writeByte(')') catch return error.OutOfMemory;
30673063 return .{
30683064 .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls),
30693065 .nav = .none,
......@@ -5578,9 +5574,8 @@ fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
55785574
55795575 if (operand_ty.arrayLen(zcu) != extra.expect_len) {
55805576 return sema.failWithOwnedErrorMsg(block, msg: {
5581 const msg = try sema.errMsg(src, "expected {} elements for destructure, found {}", .{
5582 extra.expect_len,
5583 operand_ty.arrayLen(zcu),
5577 const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{
5578 extra.expect_len, operand_ty.arrayLen(zcu),
55845579 });
55855580 errdefer msg.destroy(sema.gpa);
55865581 try sema.errNote(destructure_src, msg, "result destructured here", .{});
......@@ -5912,26 +5907,25 @@ fn zirCompileLog(
59125907
59135908 var aw: std.io.Writer.Allocating = .init(gpa);
59145909 defer aw.deinit();
5915 const bw = &aw.interface;
5910 const writer = &aw.writer;
59165911
59175912 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
59185913 const src_node = extra.data.src_node;
59195914 const args = sema.code.refSlice(extra.end, extended.small);
59205915
59215916 for (args, 0..) |arg_ref, i| {
5922 if (i != 0) bw.writeAll(", ") catch return error.OutOfMemory;
5917 if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory;
59235918
59245919 const arg = try sema.resolveInst(arg_ref);
59255920 const arg_ty = sema.typeOf(arg);
59265921 if (try sema.resolveValueResolveLazy(arg)) |val| {
5927 bw.print("@as({f}, {f})", .{
5922 writer.print("@as({f}, {f})", .{
59285923 arg_ty.fmt(pt), val.fmtValueSema(pt, sema),
59295924 }) catch return error.OutOfMemory;
59305925 } else {
5931 bw.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory;
5926 writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory;
59325927 }
59335928 }
5934 bw.writeByte('\n') catch return error.OutOfMemory;
59355929
59365930 const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls);
59375931
......@@ -6928,7 +6922,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
69286922 inst_data.get(sema.code),
69296923 .no_embedded_nulls,
69306924 );
6931 const nav_index = try sema.lookupIdentifier(block, src, decl_name);
6925 const nav_index = try sema.lookupIdentifier(block, decl_name);
69326926 return sema.analyzeNavRef(block, src, nav_index);
69336927}
69346928
......@@ -6943,16 +6937,16 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
69436937 inst_data.get(sema.code),
69446938 .no_embedded_nulls,
69456939 );
6946 const nav = try sema.lookupIdentifier(block, src, decl_name);
6940 const nav = try sema.lookupIdentifier(block, decl_name);
69476941 return sema.analyzeNavVal(block, src, nav);
69486942}
69496943
6950fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !InternPool.Nav.Index {
6944fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index {
69516945 const pt = sema.pt;
69526946 const zcu = pt.zcu;
69536947 var namespace = block.namespace;
69546948 while (true) {
6955 if (try sema.lookupInNamespace(block, src, namespace, name, false)) |lookup| {
6949 if (try sema.lookupInNamespace(block, namespace, name)) |lookup| {
69566950 assert(lookup.accessible);
69576951 return lookup.nav;
69586952 }
......@@ -6961,15 +6955,12 @@ fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPoo
69616955 unreachable; // AstGen detects use of undeclared identifiers.
69626956}
69636957
6964/// This looks up a member of a specific namespace. It is affected by `usingnamespace` but
6965/// only for ones in the specified namespace.
6958/// This looks up a member of a specific namespace.
69666959fn lookupInNamespace(
69676960 sema: *Sema,
69686961 block: *Block,
6969 src: LazySrcLoc,
69706962 namespace_index: InternPool.NamespaceIndex,
69716963 ident_name: InternPool.NullTerminatedString,
6972 observe_usingnamespace: bool,
69736964) CompileError!?struct {
69746965 nav: InternPool.Nav.Index,
69756966 /// If `false`, the declaration is in a different file and is not `pub`.
......@@ -6978,7 +6969,6 @@ fn lookupInNamespace(
69786969} {
69796970 const pt = sema.pt;
69806971 const zcu = pt.zcu;
6981 const ip = &zcu.intern_pool;
69826972
69836973 try pt.ensureNamespaceUpToDate(namespace_index);
69846974
......@@ -6995,75 +6985,7 @@ fn lookupInNamespace(
69956985 } });
69966986 }
69976987
6998 if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) {
6999 const gpa = sema.gpa;
7000 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .empty;
7001 defer checked_namespaces.deinit(gpa);
7002
7003 // Keep track of name conflicts for error notes.
7004 var candidates: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty;
7005 defer candidates.deinit(gpa);
7006
7007 try checked_namespaces.put(gpa, namespace, {});
7008 var check_i: usize = 0;
7009
7010 while (check_i < checked_namespaces.count()) : (check_i += 1) {
7011 const check_ns = checked_namespaces.keys()[check_i];
7012 const Pass = enum { @"pub", priv };
7013 for ([2]Pass{ .@"pub", .priv }) |pass| {
7014 if (pass == .priv and src_file != check_ns.file_scope) {
7015 continue;
7016 }
7017
7018 const decls, const usingnamespaces = switch (pass) {
7019 .@"pub" => .{ &check_ns.pub_decls, &check_ns.pub_usingnamespace },
7020 .priv => .{ &check_ns.priv_decls, &check_ns.priv_usingnamespace },
7021 };
7022
7023 if (decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
7024 try candidates.append(gpa, nav_index);
7025 }
7026
7027 for (usingnamespaces.items) |sub_ns_nav| {
7028 try sema.ensureNavResolved(block, src, sub_ns_nav, .fully);
7029 const sub_ns_ty: Type = .fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val);
7030 const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu));
7031 try checked_namespaces.put(gpa, sub_ns, {});
7032 }
7033 }
7034 }
7035
7036 ignore_self: {
7037 const skip_nav = switch (sema.owner.unwrap()) {
7038 .@"comptime", .type, .func, .memoized_state => break :ignore_self,
7039 .nav_ty, .nav_val => |nav| nav,
7040 };
7041 var i: usize = 0;
7042 while (i < candidates.items.len) {
7043 if (candidates.items[i] == skip_nav) {
7044 _ = candidates.orderedRemove(i);
7045 } else {
7046 i += 1;
7047 }
7048 }
7049 }
7050
7051 switch (candidates.items.len) {
7052 0 => {},
7053 1 => return .{
7054 .nav = candidates.items[0],
7055 .accessible = true,
7056 },
7057 else => return sema.failWithOwnedErrorMsg(block, msg: {
7058 const msg = try sema.errMsg(src, "ambiguous reference", .{});
7059 errdefer msg.destroy(gpa);
7060 for (candidates.items) |candidate| {
7061 try sema.errNote(zcu.navSrcLoc(candidate), msg, "declared here", .{});
7062 }
7063 break :msg msg;
7064 }),
7065 }
7066 } else if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
6988 if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
70676989 return .{
70686990 .nav = nav_index,
70696991 .accessible = true,
......@@ -7652,10 +7574,6 @@ fn analyzeCall(
76527574 const ip = &zcu.intern_pool;
76537575 const arena = sema.arena;
76547576
7655 if (modifier == .async_kw) {
7656 return sema.failWithUseOfAsync(block, call_src);
7657 }
7658
76597577 const maybe_func_inst = try sema.funcDeclSrcInst(callee);
76607578 const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{
76617579 .base_node_inst = fn_decl_inst,
......@@ -8047,14 +7965,13 @@ fn analyzeCall(
80477965 }
80487966
80497967 const call_tag: Air.Inst.Tag = switch (modifier) {
8050 .auto, .no_async => .call,
7968 .auto, .no_suspend => .call,
80517969 .never_tail => .call_never_tail,
80527970 .never_inline => .call_never_inline,
80537971 .always_tail => .call_always_tail,
80547972
80557973 .always_inline,
80567974 .compile_time,
8057 .async_kw,
80587975 => unreachable,
80597976 };
80607977
......@@ -9417,14 +9334,6 @@ fn resolveGenericBody(
94179334 return sema.resolveConstDefinedValue(block, src, result, reason);
94189335}
94199336
9420/// Given a library name, examines if the library name should end up in
9421/// `link.File.Options.windows_libs` table (for example, libc is always
9422/// specified via dedicated flag `link_libc` instead),
9423/// and puts it there if it doesn't exist.
9424/// It also dupes the library name which can then be saved as part of the
9425/// respective `Decl` (either `ExternFn` or `Var`).
9426/// The liveness of the duped library name is tied to liveness of `Zcu`.
9427/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
94289337pub fn handleExternLibName(
94299338 sema: *Sema,
94309339 block: *Block,
......@@ -9474,11 +9383,6 @@ pub fn handleExternLibName(
94749383 .{ lib_name, lib_name },
94759384 );
94769385 }
9477 comp.addLinkLib(lib_name) catch |err| {
9478 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
9479 lib_name, @errorName(err),
9480 });
9481 };
94829386 }
94839387}
94849388
......@@ -9543,18 +9447,17 @@ fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool {
95439447fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void {
95449448 const CallingConventionsSupportingVarArgsList = struct {
95459449 arch: std.Target.Cpu.Arch,
9546 pub fn format(ctx: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void {
9547 comptime assert(fmt.len == 0);
9450 pub fn format(ctx: @This(), w: *std.io.Writer) std.io.Writer.Error!void {
95489451 var first = true;
95499452 for (calling_conventions_supporting_var_args) |cc_inner| {
95509453 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| {
95519454 if (supported_arch == ctx.arch) break;
95529455 } else continue; // callconv not supported by this arch
95539456 if (!first) {
9554 try bw.writeAll(", ");
9457 try w.writeAll(", ");
95559458 }
95569459 first = false;
9557 try bw.print("'{s}'", .{@tagName(cc_inner)});
9460 try w.print("'{s}'", .{@tagName(cc_inner)});
95589461 }
95599462 }
95609463 };
......@@ -9989,12 +9892,11 @@ fn finishFunc(
99899892 .bad_arch => |allowed_archs| {
99909893 const ArchListFormatter = struct {
99919894 archs: []const std.Target.Cpu.Arch,
9992 pub fn format(formatter: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void {
9993 comptime assert(fmt.len == 0);
9895 pub fn format(formatter: @This(), w: *std.io.Writer) std.io.Writer.Error!void {
99949896 for (formatter.archs, 0..) |arch, i| {
99959897 if (i != 0)
9996 try bw.writeAll(", ");
9997 try bw.print("'{s}'", .{@tagName(arch)});
9898 try w.writeAll(", ");
9899 try w.print("'{s}'", .{@tagName(arch)});
99989900 }
99999901 }
100009902 };
......@@ -13965,7 +13867,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1396513867 const zcu = pt.zcu;
1396613868 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
1396713869 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
13968 const src = block.nodeOffset(inst_data.src_node);
1396913870 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
1397013871 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
1397113872 const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
......@@ -13974,7 +13875,7 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1397413875 try sema.checkNamespaceType(block, lhs_src, container_type);
1397513876
1397613877 const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false;
13977 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| {
13878 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
1397813879 if (lookup.accessible) {
1397913880 return .bool_true;
1398013881 }
......@@ -14173,7 +14074,7 @@ fn zirShl(
1417314074 });
1417414075 }
1417514076 } else if (scalar_rhs_ty.isSignedInt(zcu)) {
14176 return sema.fail(block, rhs_src, "shift by signed type '{}'", .{rhs_ty.fmt(pt)});
14077 return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)});
1417714078 }
1417814079
1417914080 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
......@@ -14478,7 +14379,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1447814379 const scalar_tag = scalar_ty.zigTypeTag(zcu);
1447914380
1448014381 if (scalar_tag != .int and scalar_tag != .bool)
14481 return sema.fail(block, operand_src, "bitwise not operation on type '{}'", .{operand_ty.fmt(pt)});
14382 return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)});
1448214383
1448314384 return analyzeBitNot(sema, block, operand, src);
1448414385}
......@@ -17094,7 +16995,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
1709416995 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
1709516996 const tree = file.getTree(zcu) catch |err| {
1709616997 // In this case we emit a warning + a less precise source location.
17097 log.warn("unable to load {}: {s}", .{
16998 log.warn("unable to load {f}: {s}", .{
1709816999 file.path.fmt(zcu.comp), @errorName(err),
1709917000 });
1710017001 break :name null;
......@@ -17122,7 +17023,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
1712217023 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
1712317024 const tree = file.getTree(zcu) catch |err| {
1712417025 // In this case we emit a warning + a less precise source location.
17125 log.warn("unable to load {}: {s}", .{
17026 log.warn("unable to load {f}: {s}", .{
1712617027 file.path.fmt(zcu.comp), @errorName(err),
1712717028 });
1712817029 break :name null;
......@@ -17755,7 +17656,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1775517656 } });
1775617657 };
1775717658
17758 const decls_val = try sema.typeInfoDecls(block, src, ip.loadEnumType(ty.toIntern()).namespace.toOptional());
17659 const decls_val = try sema.typeInfoDecls(src, ip.loadEnumType(ty.toIntern()).namespace.toOptional());
1775917660
1776017661 const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum");
1776117662
......@@ -17868,7 +17769,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1786817769 } });
1786917770 };
1787017771
17871 const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespaceIndex(zcu).toOptional());
17772 const decls_val = try sema.typeInfoDecls(src, ty.getNamespaceIndex(zcu).toOptional());
1787217773
1787317774 const enum_tag_ty_val = try pt.intern(.{ .opt = .{
1787417775 .ty = (try pt.optionalType(.type_type)).toIntern(),
......@@ -18063,7 +17964,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1806317964 } });
1806417965 };
1806517966
18066 const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu));
17967 const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
1806717968
1806817969 const backing_integer_val = try pt.intern(.{ .opt = .{
1806917970 .ty = (try pt.optionalType(.type_type)).toIntern(),
......@@ -18102,7 +18003,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1810218003 const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque");
1810318004
1810418005 try ty.resolveFields(pt);
18105 const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu));
18006 const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
1810618007
1810718008 const field_values = .{
1810818009 // decls: []const Declaration,
......@@ -18124,7 +18025,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1812418025
1812518026fn typeInfoDecls(
1812618027 sema: *Sema,
18127 block: *Block,
1812818028 src: LazySrcLoc,
1812918029 opt_namespace: InternPool.OptionalNamespaceIndex,
1813018030) CompileError!InternPool.Index {
......@@ -18140,7 +18040,7 @@ fn typeInfoDecls(
1814018040 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
1814118041 defer seen_namespaces.deinit();
1814218042
18143 try sema.typeInfoNamespaceDecls(block, src, opt_namespace, declaration_ty, &decl_vals, &seen_namespaces);
18043 try sema.typeInfoNamespaceDecls(opt_namespace, declaration_ty, &decl_vals, &seen_namespaces);
1814418044
1814518045 const array_decl_ty = try pt.arrayType(.{
1814618046 .len = decl_vals.items.len,
......@@ -18174,8 +18074,6 @@ fn typeInfoDecls(
1817418074
1817518075fn typeInfoNamespaceDecls(
1817618076 sema: *Sema,
18177 block: *Block,
18178 src: LazySrcLoc,
1817918077 opt_namespace_index: InternPool.OptionalNamespaceIndex,
1818018078 declaration_ty: Type,
1818118079 decl_vals: *std.ArrayList(InternPool.Index),
......@@ -18231,15 +18129,6 @@ fn typeInfoNamespaceDecls(
1823118129 .storage = .{ .elems = &fields },
1823218130 } }));
1823318131 }
18234
18235 for (namespace.pub_usingnamespace.items) |nav| {
18236 if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) {
18237 continue;
18238 }
18239 try sema.ensureNavResolved(block, src, nav, .fully);
18240 const namespace_ty: Type = .fromInterned(ip.getNav(nav).status.fully_resolved.val);
18241 try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces);
18242 }
1824318132}
1824418133
1824518134fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -18375,7 +18264,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1837518264 const uncasted_ty = sema.typeOf(uncasted_operand);
1837618265 if (uncasted_ty.isVector(zcu)) {
1837718266 if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) {
18378 return sema.fail(block, operand_src, "boolean not operation on type '{}'", .{
18267 return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{
1837918268 uncasted_ty.fmt(pt),
1838018269 });
1838118270 }
......@@ -19406,13 +19295,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1940619295
1940719296 if (host_size != 0) {
1940819297 if (bit_offset >= host_size * 8) {
19409 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {} starts {} bits after the end of a {} byte host integer", .{
19298 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} starts {d} bits after the end of a {d} byte host integer", .{
1941019299 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
1941119300 });
1941219301 }
1941319302 const elem_bit_size = try elem_ty.bitSizeSema(pt);
1941419303 if (elem_bit_size > host_size * 8 - bit_offset) {
19415 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {} ends {} bits after the end of a {} byte host integer", .{
19304 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{
1941619305 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
1941719306 });
1941819307 }
......@@ -20573,7 +20462,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
2057320462 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
2057420463 const operand_scalar_ty = operand_ty.scalarType(zcu);
2057520464 if (operand_scalar_ty.toIntern() != .bool_type) {
20576 return sema.fail(block, src, "expected 'bool', found '{}'", .{operand_scalar_ty.zigTypeTag(zcu)});
20465 return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)});
2057720466 }
2057820467 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
2057920468 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1;
......@@ -20856,7 +20745,7 @@ fn zirReify(
2085620745 64 => .f64,
2085720746 80 => .f80,
2085820747 128 => .f128,
20859 else => return sema.fail(block, src, "{}-bit float unsupported", .{float.bits}),
20748 else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}),
2086020749 };
2086120750 return Air.internedToRef(ty.toIntern());
2086220751 },
......@@ -21747,7 +21636,7 @@ fn reifyTuple(
2174721636 return sema.fail(
2174821637 block,
2174921638 src,
21750 "tuple field name '{}' does not match field index {}",
21639 "tuple field name '{d}' does not match field index {d}",
2175121640 .{ field_name_index, field_idx },
2175221641 );
2175321642 }
......@@ -22143,12 +22032,6 @@ fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
2214322032 return sema.failWithUseOfAsync(block, src);
2214422033}
2214522034
22146fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22147 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
22148 const src = block.nodeOffset(inst_data.src_node);
22149 return sema.failWithUseOfAsync(block, src);
22150}
22151
2215222035fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
2215322036 const pt = sema.pt;
2215422037 const zcu = pt.zcu;
......@@ -22771,7 +22654,7 @@ fn ptrCastFull(
2277122654
2277222655 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
2277322656 return sema.failWithOwnedErrorMsg(block, msg: {
22774 const msg = try sema.errMsg(src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{
22657 const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{
2277522658 src_info.packed_offset.host_size,
2277622659 dest_info.packed_offset.host_size,
2277722660 });
......@@ -22783,7 +22666,7 @@ fn ptrCastFull(
2278322666
2278422667 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
2278522668 return sema.failWithOwnedErrorMsg(block, msg: {
22786 const msg = try sema.errMsg(src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{
22669 const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{
2278722670 src_info.packed_offset.bit_offset,
2278822671 dest_info.packed_offset.bit_offset,
2278922672 });
......@@ -23353,7 +23236,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
2335323236 return sema.fail(
2335423237 block,
2335523238 operand_src,
23356 "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {} bits",
23239 "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits",
2335723240 .{ scalar_ty.fmt(pt), bits },
2335823241 );
2335923242 }
......@@ -23690,7 +23573,7 @@ fn checkNumericType(
2369023573 .comptime_float, .float, .comptime_int, .int => {},
2369123574 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) {
2369223575 .comptime_float, .float, .comptime_int, .int => {},
23693 else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
23576 else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}),
2369423577 },
2369523578 else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}),
2369623579 }
......@@ -24367,7 +24250,7 @@ fn analyzeShuffle(
2436724250 if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: {
2436824251 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
2436924252 errdefer msg.destroy(sema.gpa);
24370 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{}' given here", .{ idx, b_ty.fmt(pt) });
24253 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) });
2437124254 break :msg msg;
2437224255 });
2437324256 }
......@@ -24795,14 +24678,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
2479524678 var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier);
2479624679 switch (modifier) {
2479724680 // These can be upgraded to comptime or nosuspend calls.
24798 .auto, .never_tail, .no_async => {
24681 .auto, .never_tail, .no_suspend => {
2479924682 if (block.isComptime()) {
2480024683 if (modifier == .never_tail) {
2480124684 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
2480224685 }
2480324686 modifier = .compile_time;
2480424687 } else if (extra.flags.is_nosuspend) {
24805 modifier = .no_async;
24688 modifier = .no_suspend;
2480624689 }
2480724690 },
2480824691 // These can be upgraded to comptime. nosuspend bit can be safely ignored.
......@@ -24820,14 +24703,6 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
2482024703 modifier = .compile_time;
2482124704 }
2482224705 },
24823 .async_kw => {
24824 if (extra.flags.is_nosuspend) {
24825 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
24826 }
24827 if (block.isComptime()) {
24828 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
24829 }
24830 },
2483124706 .never_inline => {
2483224707 if (block.isComptime()) {
2483324708 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
......@@ -25160,7 +25035,7 @@ fn analyzeMinMax(
2516025035 try sema.checkNumericType(block, operand_src, operand_ty);
2516125036 if (operand_ty.zigTypeTag(zcu) != .vector) {
2516225037 return sema.failWithOwnedErrorMsg(block, msg: {
25163 const msg = try sema.errMsg(operand_src, "expected vector, found '{}'", .{operand_ty.fmt(pt)});
25038 const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
2516425039 errdefer msg.destroy(zcu.gpa);
2516525040 try sema.errNote(operand_srcs[0], msg, "vector operand here", .{});
2516625041 break :msg msg;
......@@ -25168,7 +25043,7 @@ fn analyzeMinMax(
2516825043 }
2516925044 if (operand_ty.vectorLen(zcu) != vec_len) {
2517025045 return sema.failWithOwnedErrorMsg(block, msg: {
25171 const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{}'", .{ vec_len, operand_ty.fmt(pt) });
25046 const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) });
2517225047 errdefer msg.destroy(zcu.gpa);
2517325048 try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len});
2517425049 break :msg msg;
......@@ -25181,7 +25056,7 @@ fn analyzeMinMax(
2518125056 const operand_ty = sema.typeOf(operand);
2518225057 if (operand_ty.zigTypeTag(zcu) == .vector) {
2518325058 return sema.failWithOwnedErrorMsg(block, msg: {
25184 const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{}'", .{first_operand_ty.fmt(pt)});
25059 const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)});
2518525060 errdefer msg.destroy(zcu.gpa);
2518625061 try sema.errNote(operand_src, msg, "vector operand here", .{});
2518725062 break :msg msg;
......@@ -25816,40 +25691,12 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
2581625691 });
2581725692}
2581825693
25819fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
25820 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25821 const src = block.nodeOffset(extra.node);
25822 return sema.failWithUseOfAsync(block, src);
25823}
25824
2582525694fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
2582625695 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
2582725696 const src = block.nodeOffset(inst_data.src_node);
2582825697 return sema.failWithUseOfAsync(block, src);
2582925698}
2583025699
25831fn zirAwait(
25832 sema: *Sema,
25833 block: *Block,
25834 inst: Zir.Inst.Index,
25835) CompileError!Air.Inst.Ref {
25836 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
25837 const src = block.nodeOffset(inst_data.src_node);
25838
25839 return sema.failWithUseOfAsync(block, src);
25840}
25841
25842fn zirAwaitNosuspend(
25843 sema: *Sema,
25844 block: *Block,
25845 extended: Zir.Inst.Extended.InstData,
25846) CompileError!Air.Inst.Ref {
25847 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25848 const src = block.nodeOffset(extra.node);
25849
25850 return sema.failWithUseOfAsync(block, src);
25851}
25852
2585325700fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
2585425701 const tracy = trace(@src());
2585525702 defer tracy.end();
......@@ -26757,7 +26604,7 @@ fn explainWhyTypeIsNotExtern(
2675726604 }
2675826605 switch (ty.fnCallingConvention(zcu)) {
2675926606 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),
26760 .@"async" => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}),
26607 .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}),
2676126608 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),
2676226609 else => return,
2676326610 }
......@@ -27779,7 +27626,7 @@ fn namespaceLookup(
2777927626 const pt = sema.pt;
2778027627 const zcu = pt.zcu;
2778127628 const gpa = sema.gpa;
27782 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |lookup| {
27629 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
2778327630 if (!lookup.accessible) {
2778427631 return sema.failWithOwnedErrorMsg(block, msg: {
2778527632 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{
......@@ -29312,7 +29159,7 @@ fn coerceExtra(
2931229159 // return sema.fail(
2931329160 // block,
2931429161 // inst_src,
29315 // "type '{f}' cannot represent integer value '{}'",
29162 // "type '{f}' cannot represent integer value '{f}'",
2931629163 // .{ dest_ty.fmt(pt), val },
2931729164 // );
2931829165 //}
......@@ -29519,7 +29366,7 @@ fn coerceExtra(
2951929366 try sema.errNote(param_src, msg, "parameter type declared here", .{});
2952029367 }
2952129368
29522 // TODO maybe add "cannot store an error in type '{}'" note
29369 // TODO maybe add "cannot store an error in type '{f}'" note
2952329370
2952429371 break :msg msg;
2952529372 };
......@@ -29867,12 +29714,12 @@ const InMemoryCoercionResult = union(enum) {
2986729714 },
2986829715 .ptr_bit_range => |bit_range| {
2986929716 if (bit_range.actual_host != bit_range.wanted_host) {
29870 try sema.errNote(src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{
29717 try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{
2987129718 bit_range.actual_host, bit_range.wanted_host,
2987229719 });
2987329720 }
2987429721 if (bit_range.actual_offset != bit_range.wanted_offset) {
29875 try sema.errNote(src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{
29722 try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{
2987629723 bit_range.actual_offset, bit_range.wanted_offset,
2987729724 });
2987829725 }
......@@ -34989,7 +34836,7 @@ fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_
3498934836 return sema.fail(
3499034837 block,
3499134838 src,
34992 "backing integer type '{f}' has bit size {} but the struct fields have a total bit size of {}",
34839 "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}",
3499334840 .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum },
3499434841 );
3499534842 }
......@@ -35332,11 +35179,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load
3533235179 switch (union_type.flagsUnordered(ip).status) {
3533335180 .none => {},
3533435181 .field_types_wip => {
35335 const msg = try sema.errMsg(
35336 ty.srcLoc(zcu),
35337 "union '{f}' depends on itself",
35338 .{ty.fmt(pt)},
35339 );
35182 const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)});
3534035183 return sema.failWithOwnedErrorMsg(null, msg);
3534135184 },
3534235185 .have_field_types,
......@@ -37330,7 +37173,14 @@ fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg
3733037173 }
3733137174}
3733237175
37333fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, val: Value, intermediate_value_count: u32, start_value_name: InternPool.NullTerminatedString) Allocator.Error!union(enum) {
37176fn notePathToComptimeAllocPtr(
37177 sema: *Sema,
37178 msg: *Zcu.ErrorMsg,
37179 src: LazySrcLoc,
37180 val: Value,
37181 intermediate_value_count: u32,
37182 start_value_name: InternPool.NullTerminatedString,
37183) Allocator.Error!union(enum) {
3733437184 done,
3733537185 new_val: Value,
3733637186} {
......@@ -37341,9 +37191,9 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc,
3734137191
3734237192 var first_path: std.ArrayListUnmanaged(u8) = .empty;
3734337193 if (intermediate_value_count == 0) {
37344 try first_path.print(arena, "{fi}", .{start_value_name.fmt(ip)});
37194 try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)});
3734537195 } else {
37346 try first_path.print(arena, "v{}", .{intermediate_value_count - 1});
37196 try first_path.print(arena, "v{d}", .{intermediate_value_count - 1});
3734737197 }
3734837198
3734937199 const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path);
......@@ -37373,7 +37223,7 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc,
3737337223 const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count});
3737437224 const deriv_start = @import("print_value.zig").printPtrDerivation(
3737537225 derivation,
37376 &second_path_aw.interface,
37226 &second_path_aw.writer,
3737737227 pt,
3737837228 .lvalue,
3737937229 .{ .str = inter_name },
......@@ -37437,7 +37287,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList
3743737287 const backing_enum = union_ty.unionTagTypeHypothetical(zcu);
3743837288 const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
3743937289 const field_name = backing_enum.enumFieldName(field_idx, zcu);
37440 try path.print(arena, ".{fi}", .{field_name.fmt(ip)});
37290 try path.print(arena, ".{f}", .{field_name.fmt(ip)});
3744137291 return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path);
3744237292 },
3744337293 .aggregate => |agg| {
......@@ -37462,7 +37312,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList
3746237312 try path.print(arena, "[{d}]", .{elem_idx});
3746337313 } else {
3746437314 const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?;
37465 try path.print(arena, ".{fi}", .{name.fmt(ip)});
37315 try path.print(arena, ".{f}", .{name.fmt(ip)});
3746637316 },
3746737317 else => unreachable,
3746837318 }
src/Sema/LowerZon.zig+8-12
......@@ -360,11 +360,7 @@ fn fail(
360360fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
361361 const pt = self.sema.pt;
362362 return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) {
363 error.WrongType => return self.fail(
364 node,
365 "expected type '{f}'",
366 .{res_ty.fmt(pt)},
367 ),
363 error.WrongType => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(pt)}),
368364 else => |e| return e,
369365 };
370366}
......@@ -458,7 +454,7 @@ fn lowerInt(
458454 // If lhs is unsigned and rhs is less than 0, we're out of bounds
459455 if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(
460456 node,
461 "type '{f}' cannot represent integer value '{}'",
457 "type '{f}' cannot represent integer value '{d}'",
462458 .{ res_ty.fmt(self.sema.pt), rhs },
463459 );
464460
......@@ -478,7 +474,7 @@ fn lowerInt(
478474 if (rhs < min_int or rhs > max_int) {
479475 return self.fail(
480476 node,
481 "type '{f}' cannot represent integer value '{}'",
477 "type '{f}' cannot represent integer value '{d}'",
482478 .{ res_ty.fmt(self.sema.pt), rhs },
483479 );
484480 }
......@@ -496,7 +492,7 @@ fn lowerInt(
496492 if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {
497493 return self.fail(
498494 node,
499 "type '{f}' cannot represent integer value '{f}'",
495 "type '{f}' cannot represent integer value '{d}'",
500496 .{ res_ty.fmt(self.sema.pt), val },
501497 );
502498 }
......@@ -517,7 +513,7 @@ fn lowerInt(
517513 switch (big_int.setFloat(val, .trunc)) {
518514 .inexact => return self.fail(
519515 node,
520 "fractional component prevents float value '{}' from coercion to type '{f}'",
516 "fractional component prevents float value '{d}' from coercion to type '{f}'",
521517 .{ val, res_ty.fmt(self.sema.pt) },
522518 ),
523519 .exact => {},
......@@ -528,8 +524,8 @@ fn lowerInt(
528524 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
529525 return self.fail(
530526 node,
531 "type '{}' cannot represent integer value '{f}'",
532 .{ val, res_ty.fmt(self.sema.pt) },
527 "type '{f}' cannot represent integer value '{d}'",
528 .{ res_ty.fmt(self.sema.pt), val },
533529 );
534530 }
535531
......@@ -550,7 +546,7 @@ fn lowerInt(
550546 if (val >= out_of_range) {
551547 return self.fail(
552548 node,
553 "type '{f}' cannot represent integer value '{}'",
549 "type '{f}' cannot represent integer value '{d}'",
554550 .{ res_ty.fmt(self.sema.pt), val },
555551 );
556552 }
src/Type.zig+81-82
......@@ -122,14 +122,13 @@ pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool {
122122 return a.toIntern() == b.toIntern();
123123}
124124
125pub fn format(ty: Type, bw: *Writer, comptime f: []const u8) !usize {
125pub fn format(ty: Type, writer: *std.io.Writer) !void {
126126 _ = ty;
127 _ = f;
128 _ = bw;
127 _ = writer;
129128 @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
130129}
131130
132pub const Formatter = std.fmt.Formatter(format2);
131pub const Formatter = std.fmt.Formatter(Format, Format.default);
133132
134133pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {
135134 return .{ .data = .{
......@@ -138,30 +137,28 @@ pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {
138137 } };
139138}
140139
141const FormatContext = struct {
140const Format = struct {
142141 ty: Type,
143142 pt: Zcu.PerThread,
144};
145143
146fn format2(ctx: FormatContext, bw: *Writer, comptime f: []const u8) !void {
147 comptime assert(f.len == 0);
148 try print(ctx.ty, bw, ctx.pt);
149}
144 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
145 return print(f.ty, writer, f.pt);
146 }
147};
150148
151pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) {
149pub fn fmtDebug(ty: Type) std.fmt.Formatter(Type, dump) {
152150 return .{ .data = ty };
153151}
154152
155153/// This is a debug function. In order to print types in a meaningful way
156154/// we also need access to the module.
157pub fn dump(start_type: Type, bw: *Writer, comptime unused_format_string: []const u8) !void {
158 comptime assert(unused_format_string.len == 0);
159 return bw.print("{any}", .{start_type.ip_index});
155pub fn dump(start_type: Type, writer: *std.io.Writer) std.io.Writer.Error!void {
156 return writer.print("{any}", .{start_type.ip_index});
160157}
161158
162159/// Prints a name suitable for `@typeName`.
163160/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.
164pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
161pub fn print(ty: Type, writer: *std.io.Writer, pt: Zcu.PerThread) std.io.Writer.Error!void {
165162 const zcu = pt.zcu;
166163 const ip = &zcu.intern_pool;
167164 switch (ip.indexToKey(ty.toIntern())) {
......@@ -171,22 +168,22 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
171168 .signed => 'i',
172169 .unsigned => 'u',
173170 };
174 try bw.print("{c}{d}", .{ sign_char, int_type.bits });
171 try writer.print("{c}{d}", .{ sign_char, int_type.bits });
175172 },
176173 .ptr_type => {
177174 const info = ty.ptrInfo(zcu);
178175
179176 if (info.sentinel != .none) switch (info.flags.size) {
180177 .one, .c => unreachable,
181 .many => try bw.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
182 .slice => try bw.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
178 .many => try writer.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
179 .slice => try writer.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
183180 } else switch (info.flags.size) {
184 .one => try bw.writeAll("*"),
185 .many => try bw.writeAll("[*]"),
186 .c => try bw.writeAll("[*c]"),
187 .slice => try bw.writeAll("[]"),
181 .one => try writer.writeAll("*"),
182 .many => try writer.writeAll("[*]"),
183 .c => try writer.writeAll("[*c]"),
184 .slice => try writer.writeAll("[]"),
188185 }
189 if (info.flags.is_allowzero and info.flags.size != .c) try bw.writeAll("allowzero ");
186 if (info.flags.is_allowzero and info.flags.size != .c) try writer.writeAll("allowzero ");
190187 if (info.flags.alignment != .none or
191188 info.packed_offset.host_size != 0 or
192189 info.flags.vector_index != .none)
......@@ -195,72 +192,72 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
195192 info.flags.alignment
196193 else
197194 Type.fromInterned(info.child).abiAlignment(pt.zcu);
198 try bw.print("align({d}", .{alignment.toByteUnits() orelse 0});
195 try writer.print("align({d}", .{alignment.toByteUnits() orelse 0});
199196
200197 if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) {
201 try bw.print(":{d}:{d}", .{
198 try writer.print(":{d}:{d}", .{
202199 info.packed_offset.bit_offset, info.packed_offset.host_size,
203200 });
204201 }
205202 if (info.flags.vector_index == .runtime) {
206 try bw.writeAll(":?");
203 try writer.writeAll(":?");
207204 } else if (info.flags.vector_index != .none) {
208 try bw.print(":{d}", .{@intFromEnum(info.flags.vector_index)});
205 try writer.print(":{d}", .{@intFromEnum(info.flags.vector_index)});
209206 }
210 try bw.writeAll(") ");
207 try writer.writeAll(") ");
211208 }
212209 if (info.flags.address_space != .generic) {
213 try bw.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)});
210 try writer.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)});
214211 }
215 if (info.flags.is_const) try bw.writeAll("const ");
216 if (info.flags.is_volatile) try bw.writeAll("volatile ");
212 if (info.flags.is_const) try writer.writeAll("const ");
213 if (info.flags.is_volatile) try writer.writeAll("volatile ");
217214
218 try print(Type.fromInterned(info.child), bw, pt);
215 try print(Type.fromInterned(info.child), writer, pt);
219216 },
220217 .array_type => |array_type| {
221218 if (array_type.sentinel == .none) {
222 try bw.print("[{d}]", .{array_type.len});
223 try print(Type.fromInterned(array_type.child), bw, pt);
219 try writer.print("[{d}]", .{array_type.len});
220 try print(Type.fromInterned(array_type.child), writer, pt);
224221 } else {
225 try bw.print("[{d}:{f}]", .{
222 try writer.print("[{d}:{f}]", .{
226223 array_type.len,
227224 Value.fromInterned(array_type.sentinel).fmtValue(pt),
228225 });
229 try print(Type.fromInterned(array_type.child), bw, pt);
226 try print(Type.fromInterned(array_type.child), writer, pt);
230227 }
231228 },
232229 .vector_type => |vector_type| {
233 try bw.print("@Vector({d}, ", .{vector_type.len});
234 try print(Type.fromInterned(vector_type.child), bw, pt);
235 try bw.writeAll(")");
230 try writer.print("@Vector({d}, ", .{vector_type.len});
231 try print(Type.fromInterned(vector_type.child), writer, pt);
232 try writer.writeAll(")");
236233 },
237234 .opt_type => |child| {
238 try bw.writeByte('?');
239 try print(Type.fromInterned(child), bw, pt);
235 try writer.writeByte('?');
236 try print(Type.fromInterned(child), writer, pt);
240237 },
241238 .error_union_type => |error_union_type| {
242 try print(Type.fromInterned(error_union_type.error_set_type), bw, pt);
243 try bw.writeByte('!');
239 try print(Type.fromInterned(error_union_type.error_set_type), writer, pt);
240 try writer.writeByte('!');
244241 if (error_union_type.payload_type == .generic_poison_type) {
245 try bw.writeAll("anytype");
242 try writer.writeAll("anytype");
246243 } else {
247 try print(Type.fromInterned(error_union_type.payload_type), bw, pt);
244 try print(Type.fromInterned(error_union_type.payload_type), writer, pt);
248245 }
249246 },
250247 .inferred_error_set_type => |func_index| {
251248 const func_nav = ip.getNav(zcu.funcInfo(func_index).owner_nav);
252 return bw.print("@typeInfo(@typeInfo(@TypeOf({f})).@\"fn\".return_type.?).error_union.error_set", .{
249 try writer.print("@typeInfo(@typeInfo(@TypeOf({f})).@\"fn\".return_type.?).error_union.error_set", .{
253250 func_nav.fqn.fmt(ip),
254251 });
255252 },
256253 .error_set_type => |error_set_type| {
257254 const names = error_set_type.names;
258 try bw.writeAll("error{");
255 try writer.writeAll("error{");
259256 for (names.get(ip), 0..) |name, i| {
260 if (i != 0) try bw.writeByte(',');
261 try bw.print("{f}", .{name.fmt(ip)});
257 if (i != 0) try writer.writeByte(',');
258 try writer.print("{f}", .{name.fmt(ip)});
262259 }
263 try bw.writeAll("}");
260 try writer.writeAll("}");
264261 },
265262 .simple_type => |s| switch (s) {
266263 .f16,
......@@ -289,97 +286,99 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
289286 .comptime_float,
290287 .noreturn,
291288 .adhoc_inferred_error_set,
292 => return bw.writeAll(@tagName(s)),
289 => return writer.writeAll(@tagName(s)),
293290
294291 .null,
295292 .undefined,
296 => return bw.print("@TypeOf({s})", .{@tagName(s)}),
293 => return writer.print("@TypeOf({s})", .{@tagName(s)}),
297294
298 .enum_literal => return bw.writeAll("@Type(.enum_literal)"),
295 .enum_literal => return writer.writeAll("@Type(.enum_literal)"),
299296
300297 .generic_poison => unreachable,
301298 },
302299 .struct_type => {
303300 const name = ip.loadStructType(ty.toIntern()).name;
304 return bw.print("{f}", .{name.fmt(ip)});
301 try writer.print("{f}", .{name.fmt(ip)});
305302 },
306303 .tuple_type => |tuple| {
307304 if (tuple.types.len == 0) {
308 return bw.writeAll("@TypeOf(.{})");
305 return writer.writeAll("@TypeOf(.{})");
309306 }
310 try bw.writeAll("struct {");
307 try writer.writeAll("struct {");
311308 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {
312 try bw.writeAll(if (i == 0) " " else ", ");
313 if (val != .none) try bw.writeAll("comptime ");
314 try print(Type.fromInterned(field_ty), bw, pt);
315 if (val != .none) try bw.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
309 try writer.writeAll(if (i == 0) " " else ", ");
310 if (val != .none) try writer.writeAll("comptime ");
311 try print(Type.fromInterned(field_ty), writer, pt);
312 if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
316313 }
317 try bw.writeAll(" }");
314 try writer.writeAll(" }");
318315 },
319316
320317 .union_type => {
321318 const name = ip.loadUnionType(ty.toIntern()).name;
322 return bw.print("{f}", .{name.fmt(ip)});
319 try writer.print("{f}", .{name.fmt(ip)});
323320 },
324321 .opaque_type => {
325322 const name = ip.loadOpaqueType(ty.toIntern()).name;
326 return bw.print("{f}", .{name.fmt(ip)});
323 try writer.print("{f}", .{name.fmt(ip)});
327324 },
328325 .enum_type => {
329326 const name = ip.loadEnumType(ty.toIntern()).name;
330 return bw.print("{f}", .{name.fmt(ip)});
327 try writer.print("{f}", .{name.fmt(ip)});
331328 },
332329 .func_type => |fn_info| {
333330 if (fn_info.is_noinline) {
334 try bw.writeAll("noinline ");
331 try writer.writeAll("noinline ");
335332 }
336 try bw.writeAll("fn (");
333 try writer.writeAll("fn (");
337334 const param_types = fn_info.param_types.get(&zcu.intern_pool);
338335 for (param_types, 0..) |param_ty, i| {
339 if (i != 0) try bw.writeAll(", ");
336 if (i != 0) try writer.writeAll(", ");
340337 if (std.math.cast(u5, i)) |index| {
341338 if (fn_info.paramIsComptime(index)) {
342 try bw.writeAll("comptime ");
339 try writer.writeAll("comptime ");
343340 }
344341 if (fn_info.paramIsNoalias(index)) {
345 try bw.writeAll("noalias ");
342 try writer.writeAll("noalias ");
346343 }
347344 }
348345 if (param_ty == .generic_poison_type) {
349 try bw.writeAll("anytype");
346 try writer.writeAll("anytype");
350347 } else {
351 try print(Type.fromInterned(param_ty), bw, pt);
348 try print(Type.fromInterned(param_ty), writer, pt);
352349 }
353350 }
354351 if (fn_info.is_var_args) {
355352 if (param_types.len != 0) {
356 try bw.writeAll(", ");
353 try writer.writeAll(", ");
357354 }
358 try bw.writeAll("...");
355 try writer.writeAll("...");
359356 }
360 try bw.writeAll(") ");
357 try writer.writeAll(") ");
361358 if (fn_info.cc != .auto) print_cc: {
362359 if (zcu.getTarget().cCallingConvention()) |ccc| {
363360 if (fn_info.cc.eql(ccc)) {
364 try bw.writeAll("callconv(.c) ");
361 try writer.writeAll("callconv(.c) ");
365362 break :print_cc;
366363 }
367364 }
368365 switch (fn_info.cc) {
369 .auto, .@"async", .naked, .@"inline" => try bw.print("callconv(.{f}) ", .{std.zig.fmtId(@tagName(fn_info.cc))}),
370 else => try bw.print("callconv({any}) ", .{fn_info.cc}),
366 .auto, .async, .naked, .@"inline" => try writer.print("callconv(.{f}) ", .{
367 std.zig.fmtId(@tagName(fn_info.cc)),
368 }),
369 else => try writer.print("callconv({any}) ", .{fn_info.cc}),
371370 }
372371 }
373372 if (fn_info.return_type == .generic_poison_type) {
374 try bw.writeAll("anytype");
373 try writer.writeAll("anytype");
375374 } else {
376 try print(Type.fromInterned(fn_info.return_type), bw, pt);
375 try print(Type.fromInterned(fn_info.return_type), writer, pt);
377376 }
378377 },
379378 .anyframe_type => |child| {
380 if (child == .none) return bw.writeAll("anyframe");
381 try bw.writeAll("anyframe->");
382 try print(Type.fromInterned(child), bw, pt);
379 if (child == .none) return writer.writeAll("anyframe");
380 try writer.writeAll("anyframe->");
381 try print(Type.fromInterned(child), writer, pt);
383382 },
384383
385384 // values, not types
src/Value.zig+7-15
......@@ -15,31 +15,23 @@ const Value = @This();
1515
1616ip_index: InternPool.Index,
1717
18pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
18pub fn format(val: Value, writer: *std.io.Writer) !void {
1919 _ = val;
20 _ = fmt;
21 _ = options;
2220 _ = writer;
2321 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
2422}
2523
2624/// This is a debug function. In order to print values in a meaningful way
2725/// we also need access to the type.
28pub fn dump(
29 start_val: Value,
30 comptime fmt: []const u8,
31 _: std.fmt.FormatOptions,
32 out_stream: anytype,
33) !void {
34 comptime assert(fmt.len == 0);
35 try out_stream.print("(interned: {})", .{start_val.toIntern()});
26pub fn dump(start_val: Value, w: std.io.Writer) std.io.Writer.Error!void {
27 try w.print("(interned: {})", .{start_val.toIntern()});
3628}
3729
38pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) {
30pub fn fmtDebug(val: Value) std.fmt.Formatter(Value, dump) {
3931 return .{ .data = val };
4032}
4133
42pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.format) {
34pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.FormatContext, print_value.format) {
4335 return .{ .data = .{
4436 .val = val,
4537 .pt = pt,
......@@ -48,7 +40,7 @@ pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.for
4840 } };
4941}
5042
51pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatter(print_value.formatSema) {
43pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatter(print_value.FormatContext, print_value.formatSema) {
5244 return .{ .data = .{
5345 .val = val,
5446 .pt = pt,
......@@ -57,7 +49,7 @@ pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatte
5749 } };
5850}
5951
60pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Formatter(print_value.formatSema) {
52pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Formatter(print_value.FormatContext, print_value.formatSema) {
6153 return .{ .data = ctx };
6254}
6355
src/Zcu.zig+18-51
......@@ -793,10 +793,6 @@ pub const Namespace = struct {
793793 pub_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty,
794794 /// Members of the namespace which are *not* marked `pub`.
795795 priv_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty,
796 /// All `usingnamespace` declarations in this namespace which are marked `pub`.
797 pub_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
798 /// All `usingnamespace` declarations in this namespace which are *not* marked `pub`.
799 priv_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
800796 /// All `comptime` declarations in this namespace. We store these purely so that incremental
801797 /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.
802798 comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty,
......@@ -1116,7 +1112,7 @@ pub const File = struct {
11161112 eb: *std.zig.ErrorBundle.Wip,
11171113 ) !std.zig.ErrorBundle.SourceLocationIndex {
11181114 return eb.addSourceLocation(.{
1119 .src_path = try eb.printString("{}", .{file.path.fmt(zcu.comp)}),
1115 .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}),
11201116 .span_start = 0,
11211117 .span_main = 0,
11221118 .span_end = 0,
......@@ -1137,7 +1133,7 @@ pub const File = struct {
11371133 const end = start + tree.tokenSlice(tok).len;
11381134 const loc = std.zig.findLineColumn(source.bytes, start);
11391135 return eb.addSourceLocation(.{
1140 .src_path = try eb.printString("{}", .{file.path.fmt(zcu.comp)}),
1136 .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}),
11411137 .span_start = start,
11421138 .span_main = start,
11431139 .span_end = @intCast(end),
......@@ -1298,9 +1294,6 @@ pub const SrcLoc = struct {
12981294 .simple_var_decl,
12991295 .aligned_var_decl,
13001296 => tree.fullVarDecl(node).?,
1301 .@"usingnamespace" => {
1302 return tree.nodeToSpan(tree.nodeData(node).node);
1303 },
13041297 else => unreachable,
13051298 };
13061299 if (full.ast.type_node.unwrap()) |type_node| {
......@@ -1438,12 +1431,8 @@ pub const SrcLoc = struct {
14381431 .field_access => tree.nodeData(node).node_and_token[1],
14391432 .call_one,
14401433 .call_one_comma,
1441 .async_call_one,
1442 .async_call_one_comma,
14431434 .call,
14441435 .call_comma,
1445 .async_call,
1446 .async_call_comma,
14471436 => blk: {
14481437 const full = tree.fullCall(&buf, node).?;
14491438 break :blk tree.lastToken(full.ast.fn_expr);
......@@ -3306,9 +3295,6 @@ pub fn mapOldZirToNew(
33063295 // All comptime declarations, in order, for a best-effort match.
33073296 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
33083297 defer comptime_decls.deinit(gpa);
3309 // All usingnamespace declarations, in order, for a best-effort match.
3310 var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
3311 defer usingnamespace_decls.deinit(gpa);
33123298
33133299 {
33143300 var old_decl_it = old_zir.declIterator(match_item.old_inst);
......@@ -3316,7 +3302,6 @@ pub fn mapOldZirToNew(
33163302 const old_decl = old_zir.getDeclaration(old_decl_inst);
33173303 switch (old_decl.kind) {
33183304 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
3319 .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
33203305 .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
33213306 .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
33223307 .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
......@@ -3327,7 +3312,6 @@ pub fn mapOldZirToNew(
33273312
33283313 var unnamed_test_idx: u32 = 0;
33293314 var comptime_decl_idx: u32 = 0;
3330 var usingnamespace_decl_idx: u32 = 0;
33313315
33323316 var new_decl_it = new_zir.declIterator(match_item.new_inst);
33333317 while (new_decl_it.next()) |new_decl_inst| {
......@@ -3337,7 +3321,6 @@ pub fn mapOldZirToNew(
33373321 // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name.
33383322 // * For unnamed tests, we match based on order.
33393323 // * For comptime blocks, we match based on order.
3340 // * For usingnamespace decls, we match based on order.
33413324 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
33423325 const old_decl_inst = switch (new_decl.kind) {
33433326 .@"comptime" => inst: {
......@@ -3345,11 +3328,6 @@ pub fn mapOldZirToNew(
33453328 defer comptime_decl_idx += 1;
33463329 break :inst comptime_decls.items[comptime_decl_idx];
33473330 },
3348 .@"usingnamespace" => inst: {
3349 if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue;
3350 defer usingnamespace_decl_idx += 1;
3351 break :inst usingnamespace_decls.items[usingnamespace_decl_idx];
3352 },
33533331 .unnamed_test => inst: {
33543332 if (unnamed_test_idx == unnamed_tests.items.len) continue;
33553333 defer unnamed_test_idx += 1;
......@@ -4058,7 +4036,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
40584036 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
40594037
40604038 const want_analysis = switch (decl.kind) {
4061 .@"usingnamespace" => unreachable,
40624039 .@"const", .@"var" => unreachable,
40634040 .@"comptime" => unreachable,
40644041 .unnamed_test => true,
......@@ -4116,16 +4093,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
41164093 }
41174094 }
41184095 }
4119 // Incremental compilation does not support `usingnamespace`.
4120 // These are only included to keep good reference traces in non-incremental updates.
4121 for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| {
4122 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
4123 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
4124 }
4125 for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| {
4126 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
4127 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
4128 }
41294096 continue;
41304097 }
41314098 if (unit_queue.pop()) |kv| {
......@@ -4271,17 +4238,17 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *std.io.Writer) std.io.Writer.Er
42714238 const cu = ip.getComptimeUnit(cu_id);
42724239 if (cu.zir_index.resolveFull(ip)) |resolved| {
42734240 const file_path = zcu.fileByIndex(resolved.file).path;
4274 return writer.print("comptime(inst=('{}', %{}) [{}])", .{ file_path.fmt(zcu.comp), @intFromEnum(resolved.inst), @intFromEnum(cu_id) });
4241 return writer.print("comptime(inst=('{f}', %{}) [{}])", .{ file_path.fmt(zcu.comp), @intFromEnum(resolved.inst), @intFromEnum(cu_id) });
42754242 } else {
42764243 return writer.print("comptime(inst=<lost> [{}])", .{@intFromEnum(cu_id)});
42774244 }
42784245 },
4279 .nav_val => |nav| return writer.print("nav_val('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
4280 .nav_ty => |nav| return writer.print("nav_ty('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
4281 .type => |ty| return writer.print("ty('{}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),
4246 .nav_val => |nav| return writer.print("nav_val('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
4247 .nav_ty => |nav| return writer.print("nav_ty('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
4248 .type => |ty| return writer.print("ty('{f}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),
42824249 .func => |func| {
42834250 const nav = zcu.funcInfo(func).owner_nav;
4284 return writer.print("func('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) });
4251 return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) });
42854252 },
42864253 .memoized_state => return writer.writeAll("memoized_state"),
42874254 }
......@@ -4298,42 +4265,42 @@ fn formatDependee(data: FormatDependee, writer: *std.io.Writer) std.io.Writer.Er
42984265 return writer.writeAll("inst(<lost>)");
42994266 };
43004267 const file_path = zcu.fileByIndex(info.file).path;
4301 return writer.print("inst('{}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) });
4268 return writer.print("inst('{f}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) });
43024269 },
43034270 .nav_val => |nav| {
43044271 const fqn = ip.getNav(nav).fqn;
4305 return writer.print("nav_val('{}')", .{fqn.fmt(ip)});
4272 return writer.print("nav_val('{f}')", .{fqn.fmt(ip)});
43064273 },
43074274 .nav_ty => |nav| {
43084275 const fqn = ip.getNav(nav).fqn;
4309 return writer.print("nav_ty('{}')", .{fqn.fmt(ip)});
4276 return writer.print("nav_ty('{f}')", .{fqn.fmt(ip)});
43104277 },
43114278 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
4312 .struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
4313 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
4279 .struct_type, .union_type, .enum_type => return writer.print("type('{f}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
4280 .func => |f| return writer.print("ies('{f}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
43144281 else => unreachable,
43154282 },
43164283 .zon_file => |file| {
43174284 const file_path = zcu.fileByIndex(file).path;
4318 return writer.print("zon_file('{}')", .{file_path.fmt(zcu.comp)});
4285 return writer.print("zon_file('{f}')", .{file_path.fmt(zcu.comp)});
43194286 },
43204287 .embed_file => |ef_idx| {
43214288 const ef = ef_idx.get(zcu);
4322 return writer.print("embed_file('{}')", .{ef.path.fmt(zcu.comp)});
4289 return writer.print("embed_file('{f}')", .{ef.path.fmt(zcu.comp)});
43234290 },
43244291 .namespace => |ti| {
43254292 const info = ti.resolveFull(ip) orelse {
43264293 return writer.writeAll("namespace(<lost>)");
43274294 };
43284295 const file_path = zcu.fileByIndex(info.file).path;
4329 return writer.print("namespace('{}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) });
4296 return writer.print("namespace('{f}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) });
43304297 },
43314298 .namespace_name => |k| {
43324299 const info = k.namespace.resolveFull(ip) orelse {
4333 return writer.print("namespace(<lost>, '{}')", .{k.name.fmt(ip)});
4300 return writer.print("namespace(<lost>, '{f}')", .{k.name.fmt(ip)});
43344301 };
43354302 const file_path = zcu.fileByIndex(info.file).path;
4336 return writer.print("namespace('{}', %{d}, '{}')", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst), k.name.fmt(ip) });
4303 return writer.print("namespace('{f}', %{d}, '{f}')", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst), k.name.fmt(ip) });
43374304 },
43384305 .memoized_state => return writer.writeAll("memoized_state"),
43394306 }
......@@ -4374,7 +4341,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
43744341 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
43754342 switch (cc) {
43764343 .auto, .@"inline" => return .ok,
4377 .@"async" => return .{ .bad_backend = backend }, // nothing supports async currently
4344 .async => return .{ .bad_backend = backend }, // nothing supports async currently
43784345 .naked => {}, // depends only on backend
43794346 else => for (cc.archs()) |allowed_arch| {
43804347 if (allowed_arch == target.cpu.arch) break;
src/Zcu/PerThread.zig+18-69
......@@ -53,7 +53,7 @@ fn deinitFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
5353 const zcu = pt.zcu;
5454 const gpa = zcu.gpa;
5555 const file = zcu.fileByIndex(file_index);
56 log.debug("deinit File {}", .{file.path.fmt(zcu.comp)});
56 log.debug("deinit File {f}", .{file.path.fmt(zcu.comp)});
5757 file.path.deinit(gpa);
5858 file.unload(gpa);
5959 if (file.prev_zir) |prev_zir| {
......@@ -117,7 +117,7 @@ pub fn updateFile(
117117 var lock: std.fs.File.Lock = switch (file.status) {
118118 .never_loaded, .retryable_failure => lock: {
119119 // First, load the cached ZIR code, if any.
120 log.debug("AstGen checking cache: {} (local={}, digest={s})", .{
120 log.debug("AstGen checking cache: {f} (local={}, digest={s})", .{
121121 file.path.fmt(comp), want_local_cache, &hex_digest,
122122 });
123123
......@@ -130,11 +130,11 @@ pub fn updateFile(
130130 stat.inode == file.stat.inode;
131131
132132 if (unchanged_metadata) {
133 log.debug("unmodified metadata of file: {}", .{file.path.fmt(comp)});
133 log.debug("unmodified metadata of file: {f}", .{file.path.fmt(comp)});
134134 return;
135135 }
136136
137 log.debug("metadata changed: {}", .{file.path.fmt(comp)});
137 log.debug("metadata changed: {f}", .{file.path.fmt(comp)});
138138
139139 break :lock .exclusive;
140140 },
......@@ -221,12 +221,12 @@ pub fn updateFile(
221221 };
222222 switch (result) {
223223 .success => {
224 log.debug("AstGen cached success: {}", .{file.path.fmt(comp)});
224 log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)});
225225 break false;
226226 },
227227 .invalid => {},
228 .truncated => log.warn("unexpected EOF reading cached ZIR for {}", .{file.path.fmt(comp)}),
229 .stale => log.debug("AstGen cache stale: {}", .{file.path.fmt(comp)}),
228 .truncated => log.warn("unexpected EOF reading cached ZIR for {f}", .{file.path.fmt(comp)}),
229 .stale => log.debug("AstGen cache stale: {f}", .{file.path.fmt(comp)}),
230230 }
231231
232232 // If we already have the exclusive lock then it is our job to update.
......@@ -283,7 +283,7 @@ pub fn updateFile(
283283 },
284284 }
285285
286 log.debug("AstGen fresh success: {}", .{file.path.fmt(comp)});
286 log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)});
287287 }
288288
289289 file.stat = .{
......@@ -343,8 +343,9 @@ fn loadZirZoirCache(
343343 .zon => Zoir.Header,
344344 };
345345
346 var buffer: [@sizeOf(Header)]u8 = undefined;
346 var buffer: [2000]u8 = undefined;
347347 var cache_fr = cache_file.reader(&buffer);
348 cache_fr.size = stat.size;
348349 const cache_br = &cache_fr.interface;
349350
350351 // First we read the header to determine the lengths of arrays.
......@@ -1114,7 +1115,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
11141115 defer block.instructions.deinit(gpa);
11151116
11161117 const zir_decl = zir.getDeclaration(inst_resolved.inst);
1117 assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
11181118
11191119 const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero });
11201120 const init_src = block.src(.{ .node_offset_var_decl_init = .zero });
......@@ -1163,7 +1163,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
11631163 assert(nav_ty.zigTypeTag(zcu) == .@"fn");
11641164 break :is_const true;
11651165 },
1166 .@"usingnamespace", .@"const" => true,
1166 .@"const" => true,
11671167 .@"var" => {
11681168 try sema.validateVarType(
11691169 &block,
......@@ -1243,26 +1243,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
12431243 // this resolves the type `type` (which needs no resolution), not the struct itself.
12441244 try nav_ty.resolveLayout(pt);
12451245
1246 // TODO: this is jank. If #20663 is rejected, let's think about how to better model `usingnamespace`.
1247 if (zir_decl.kind == .@"usingnamespace") {
1248 if (nav_ty.toIntern() != .type_type) {
1249 return sema.fail(&block, ty_src, "expected type, found {f}", .{nav_ty.fmt(pt)});
1250 }
1251 if (nav_val.toType().getNamespace(zcu) == .none) {
1252 return sema.fail(&block, ty_src, "type {f} has no namespace", .{nav_val.toType().fmt(pt)});
1253 }
1254 ip.resolveNavValue(nav_id, .{
1255 .val = nav_val.toIntern(),
1256 .is_const = is_const,
1257 .alignment = .none,
1258 .@"linksection" = .none,
1259 .@"addrspace" = .generic,
1260 });
1261 // TODO: usingnamespace cannot participate in incremental compilation
1262 assert(zcu.analysis_in_progress.swapRemove(anal_unit));
1263 return .{ .val_changed = true };
1264 }
1265
12661246 const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
12671247 .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
12681248 .variable => |v| .{ v.owner_nav == nav_id, false },
......@@ -1467,7 +1447,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
14671447 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
14681448
14691449 const zir_decl = zir.getDeclaration(inst_resolved.inst);
1470 assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
14711450 const type_body = zir_decl.type_body.?;
14721451
14731452 var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
......@@ -1530,7 +1509,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
15301509
15311510 const is_const = switch (zir_decl.kind) {
15321511 .@"comptime" => unreachable,
1533 .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true,
1512 .unnamed_test, .@"test", .decltest, .@"const" => true,
15341513 .@"var" => false,
15351514 };
15361515
......@@ -2324,7 +2303,7 @@ pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!voi
23242303
23252304 Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure(
23262305 .write_builtin_zig,
2327 "unable to write '{}': {s}",
2306 "unable to write '{f}': {s}",
23282307 .{ file.path.fmt(comp), @errorName(err) },
23292308 );
23302309}
......@@ -2548,7 +2527,6 @@ pub fn scanNamespace(
25482527
25492528 try existing_by_inst.ensureTotalCapacity(gpa, @intCast(
25502529 namespace.pub_decls.count() + namespace.priv_decls.count() +
2551 namespace.pub_usingnamespace.items.len + namespace.priv_usingnamespace.items.len +
25522530 namespace.comptime_decls.items.len +
25532531 namespace.test_decls.items.len,
25542532 ));
......@@ -2561,14 +2539,6 @@ pub fn scanNamespace(
25612539 const zir_index = ip.getNav(nav).analysis.?.zir_index;
25622540 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
25632541 }
2564 for (namespace.pub_usingnamespace.items) |nav| {
2565 const zir_index = ip.getNav(nav).analysis.?.zir_index;
2566 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
2567 }
2568 for (namespace.priv_usingnamespace.items) |nav| {
2569 const zir_index = ip.getNav(nav).analysis.?.zir_index;
2570 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
2571 }
25722542 for (namespace.comptime_decls.items) |cu| {
25732543 const zir_index = ip.getComptimeUnit(cu).zir_index;
25742544 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu }));
......@@ -2585,8 +2555,6 @@ pub fn scanNamespace(
25852555
25862556 namespace.pub_decls.clearRetainingCapacity();
25872557 namespace.priv_decls.clearRetainingCapacity();
2588 namespace.pub_usingnamespace.clearRetainingCapacity();
2589 namespace.priv_usingnamespace.clearRetainingCapacity();
25902558 namespace.comptime_decls.clearRetainingCapacity();
25912559 namespace.test_decls.clearRetainingCapacity();
25922560
......@@ -2614,7 +2582,6 @@ const ScanDeclIter = struct {
26142582 /// Decl scanning is run in two passes, so that we can detect when a generated
26152583 /// name would clash with an explicit name and use a different one.
26162584 pass: enum { named, unnamed },
2617 usingnamespace_index: usize = 0,
26182585 unnamed_test_index: usize = 0,
26192586
26202587 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {
......@@ -2653,12 +2620,6 @@ const ScanDeclIter = struct {
26532620 if (iter.pass != .unnamed) return;
26542621 break :name .none;
26552622 },
2656 .@"usingnamespace" => name: {
2657 if (iter.pass != .unnamed) return;
2658 const i = iter.usingnamespace_index;
2659 iter.usingnamespace_index += 1;
2660 break :name (try iter.avoidNameConflict("usingnamespace_{d}", .{i})).toOptional();
2661 },
26622623 .unnamed_test => name: {
26632624 if (iter.pass != .unnamed) return;
26642625 const i = iter.unnamed_test_index;
......@@ -2717,7 +2678,7 @@ const ScanDeclIter = struct {
27172678 const name = maybe_name.unwrap().?;
27182679 const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name);
27192680 const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: {
2720 const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace");
2681 const nav = try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index);
27212682 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
27222683 break :nav nav;
27232684 };
......@@ -2729,17 +2690,6 @@ const ScanDeclIter = struct {
27292690
27302691 const want_analysis = switch (decl.kind) {
27312692 .@"comptime" => unreachable,
2732 .@"usingnamespace" => a: {
2733 if (comp.incremental) {
2734 @panic("'usingnamespace' is not supported by incremental compilation");
2735 }
2736 if (decl.is_pub) {
2737 try namespace.pub_usingnamespace.append(gpa, nav);
2738 } else {
2739 try namespace.priv_usingnamespace.append(gpa, nav);
2740 }
2741 break :a true;
2742 },
27432693 .unnamed_test, .@"test", .decltest => a: {
27442694 const is_named = decl.kind != .unnamed_test;
27452695 try namespace.test_decls.append(gpa, nav);
......@@ -4434,12 +4384,11 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e
44344384 defer liveness.deinit(gpa);
44354385
44364386 if (build_options.enable_debug_extensions and comp.verbose_air) {
4437 std.debug.lockStdErr();
4438 defer std.debug.unlockStdErr();
4439 const stderr = std.io.getStdErr().writer();
4440 stderr.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}) catch {};
4387 const stderr = std.debug.lockStderrWriter(&.{});
4388 defer std.debug.unlockStderrWriter();
4389 stderr.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}) catch {};
44414390 air.write(stderr, pt, liveness);
4442 stderr.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}) catch {};
4391 stderr.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)}) catch {};
44434392 }
44444393
44454394 if (std.debug.runtime_safety) {
src/arch/aarch64/CodeGen.zig deleted-6401
......@@ -1,6401 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const math = std.math;
5const assert = std.debug.assert;
6const codegen = @import("../../codegen.zig");
7const Air = @import("../../Air.zig");
8const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");
10const Type = @import("../../Type.zig");
11const Value = @import("../../Value.zig");
12const link = @import("../../link.zig");
13const Zcu = @import("../../Zcu.zig");
14const InternPool = @import("../../InternPool.zig");
15const Compilation = @import("../../Compilation.zig");
16const ErrorMsg = Zcu.ErrorMsg;
17const Target = std.Target;
18const Allocator = mem.Allocator;
19const trace = @import("../../tracy.zig").trace;
20const leb128 = std.leb;
21const log = std.log.scoped(.codegen);
22const build_options = @import("build_options");
23const Alignment = InternPool.Alignment;
24
25const CodeGenError = codegen.CodeGenError;
26
27const bits = @import("bits.zig");
28const abi = @import("abi.zig");
29const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
30const errUnionErrorOffset = codegen.errUnionErrorOffset;
31const RegisterManager = abi.RegisterManager;
32const RegisterLock = RegisterManager.RegisterLock;
33const Register = bits.Register;
34const Instruction = bits.Instruction;
35const Condition = bits.Instruction.Condition;
36const callee_preserved_regs = abi.callee_preserved_regs;
37const c_abi_int_param_regs = abi.c_abi_int_param_regs;
38const c_abi_int_return_regs = abi.c_abi_int_return_regs;
39const gp = abi.RegisterClass.gp;
40
41const InnerError = CodeGenError || error{OutOfRegisters};
42
43pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
44 return null;
45}
46
47gpa: Allocator,
48pt: Zcu.PerThread,
49air: Air,
50liveness: Air.Liveness,
51bin_file: *link.File,
52target: *const std.Target,
53func_index: InternPool.Index,
54owner_nav: InternPool.Nav.Index,
55args: []MCValue,
56ret_mcv: MCValue,
57fn_type: Type,
58arg_index: u32,
59src_loc: Zcu.LazySrcLoc,
60stack_align: u32,
61
62/// MIR Instructions
63mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
64/// MIR extra data
65mir_extra: std.ArrayListUnmanaged(u32) = .empty,
66
67/// Byte offset within the source file of the ending curly.
68end_di_line: u32,
69end_di_column: u32,
70
71/// The value is an offset into the `Function` `code` from the beginning.
72/// To perform the reloc, write 32-bit signed little-endian integer
73/// which is a relative jump, based on the address following the reloc.
74exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
75
76reused_operands: std.StaticBitSet(Air.Liveness.bpi - 1) = undefined,
77
78/// We postpone the creation of debug info for function args and locals
79/// until after all Mir instructions have been generated. Only then we
80/// will know saved_regs_stack_space which is necessary in order to
81/// calculate the right stack offsest with respect to the `.fp` register.
82dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .empty,
83
84/// Whenever there is a runtime branch, we push a Branch onto this stack,
85/// and pop it off when the runtime branch joins. This provides an "overlay"
86/// of the table of mappings from instructions to `MCValue` from within the branch.
87/// This way we can modify the `MCValue` for an instruction in different ways
88/// within different branches. Special consideration is needed when a branch
89/// joins with its parent, to make sure all instructions have the same MCValue
90/// across each runtime branch upon joining.
91branch_stack: *std.ArrayList(Branch),
92
93// Key is the block instruction
94blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
95
96register_manager: RegisterManager = .{},
97/// Maps offset to what is stored there.
98stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .empty,
99/// Tracks the current instruction allocated to the compare flags
100compare_flags_inst: ?Air.Inst.Index = null,
101
102/// Offset from the stack base, representing the end of the stack frame.
103max_end_stack: u32 = 0,
104/// Represents the current end stack offset. If there is no existing slot
105/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
106next_stack_offset: u32 = 0,
107
108saved_regs_stack_space: u32 = 0,
109
110/// Debug field, used to find bugs in the compiler.
111air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
112
113const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
114
115const MCValue = union(enum) {
116 /// No runtime bits. `void` types, empty structs, u0, enums with 1
117 /// tag, etc.
118 ///
119 /// TODO Look into deleting this tag and using `dead` instead,
120 /// since every use of MCValue.none should be instead looking at
121 /// the type and noticing it is 0 bits.
122 none,
123 /// Control flow will not allow this value to be observed.
124 unreach,
125 /// No more references to this value remain.
126 dead,
127 /// The value is undefined.
128 undef,
129 /// A pointer-sized integer that fits in a register.
130 ///
131 /// If the type is a pointer, this is the pointer address in
132 /// virtual address space.
133 immediate: u64,
134 /// The value is in a target-specific register.
135 register: Register,
136 /// The value is a tuple { wrapped: u32, overflow: u1 } where
137 /// wrapped is stored in the register and the overflow bit is
138 /// stored in the C (signed) or V (unsigned) flag of the CPSR.
139 ///
140 /// This MCValue is only generated by a add_with_overflow or
141 /// sub_with_overflow instruction operating on 32- or 64-bit values.
142 register_with_overflow: struct { reg: Register, flag: bits.Instruction.Condition },
143 /// The value is in memory at a hard-coded address.
144 ///
145 /// If the type is a pointer, it means the pointer address is at
146 /// this memory location.
147 memory: u64,
148 /// The value is in memory but requires a linker relocation fixup.
149 linker_load: codegen.LinkerLoad,
150 /// The value is one of the stack variables.
151 ///
152 /// If the type is a pointer, it means the pointer address is in
153 /// the stack at this offset.
154 stack_offset: u32,
155 /// The value is a pointer to one of the stack variables (payload
156 /// is stack offset).
157 ptr_stack_offset: u32,
158 /// The value resides in the N, Z, C, V flags. The value is 1 (if
159 /// the type is u1) or true (if the type in bool) iff the
160 /// specified condition is true.
161 compare_flags: Condition,
162 /// The value is a function argument passed via the stack.
163 stack_argument_offset: u32,
164};
165
166const DbgInfoReloc = struct {
167 tag: Air.Inst.Tag,
168 ty: Type,
169 name: [:0]const u8,
170 mcv: MCValue,
171
172 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
173 switch (reloc.tag) {
174 .arg,
175 .dbg_arg_inline,
176 => try reloc.genArgDbgInfo(function),
177
178 .dbg_var_ptr,
179 .dbg_var_val,
180 => try reloc.genVarDbgInfo(function),
181
182 else => unreachable,
183 }
184 }
185
186 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
187 // TODO: Add a pseudo-instruction or something to defer this work until Emit.
188 // We aren't allowed to interact with linker state here.
189 if (true) return;
190 switch (function.debug_output) {
191 .dwarf => |dw| {
192 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
193 .register => |reg| .{ .reg = reg.dwarfNum() },
194 .stack_offset,
195 .stack_argument_offset,
196 => |offset| blk: {
197 const adjusted_offset = switch (reloc.mcv) {
198 .stack_offset => -@as(i32, @intCast(offset)),
199 .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)),
200 else => unreachable,
201 };
202 break :blk .{ .plus = .{
203 &.{ .breg = Register.x29.dwarfNum() },
204 &.{ .consts = adjusted_offset },
205 } };
206 },
207 else => unreachable, // not a possible argument
208
209 };
210 try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
211 },
212 .plan9 => {},
213 .none => {},
214 }
215 }
216
217 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
218 // TODO: Add a pseudo-instruction or something to defer this work until Emit.
219 // We aren't allowed to interact with linker state here.
220 if (true) return;
221 switch (function.debug_output) {
222 .dwarf => |dwarf| {
223 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
224 .register => |reg| .{ .reg = reg.dwarfNum() },
225 .ptr_stack_offset,
226 .stack_offset,
227 .stack_argument_offset,
228 => |offset| blk: {
229 const adjusted_offset = switch (reloc.mcv) {
230 .ptr_stack_offset,
231 .stack_offset,
232 => -@as(i32, @intCast(offset)),
233 .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)),
234 else => unreachable,
235 };
236 break :blk .{ .plus = .{
237 &.{ .reg = Register.x29.dwarfNum() },
238 &.{ .consts = adjusted_offset },
239 } };
240 },
241 .memory => |address| .{ .constu = address },
242 .immediate => |x| .{ .constu = x },
243 .none => .empty,
244 else => blk: {
245 log.debug("TODO generate debug info for {}", .{reloc.mcv});
246 break :blk .empty;
247 },
248 };
249 try dwarf.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc);
250 },
251 .plan9 => {},
252 .none => {},
253 }
254 }
255};
256
257const Branch = struct {
258 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .empty,
259
260 fn deinit(self: *Branch, gpa: Allocator) void {
261 self.inst_table.deinit(gpa);
262 self.* = undefined;
263 }
264};
265
266const StackAllocation = struct {
267 inst: Air.Inst.Index,
268 /// TODO do we need size? should be determined by inst.ty.abiSize()
269 size: u32,
270};
271
272const BlockData = struct {
273 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
274 /// The first break instruction encounters `null` here and chooses a
275 /// machine code value for the block result, populating this field.
276 /// Following break instructions encounter that value and use it for
277 /// the location to store their block results.
278 mcv: MCValue,
279};
280
281const BigTomb = struct {
282 function: *Self,
283 inst: Air.Inst.Index,
284 lbt: Air.Liveness.BigTomb,
285
286 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
287 const dies = bt.lbt.feed();
288 const op_index = op_ref.toIndex() orelse return;
289 if (!dies) return;
290 bt.function.processDeath(op_index);
291 }
292
293 fn finishAir(bt: *BigTomb, result: MCValue) void {
294 const is_used = !bt.function.liveness.isUnused(bt.inst);
295 if (is_used) {
296 log.debug("%{d} => {}", .{ bt.inst, result });
297 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
298 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
299
300 switch (result) {
301 .register => |reg| {
302 // In some cases (such as bitcast), an operand
303 // may be the same MCValue as the result. If
304 // that operand died and was a register, it
305 // was freed by processDeath. We have to
306 // "re-allocate" the register.
307 if (bt.function.register_manager.isRegFree(reg)) {
308 bt.function.register_manager.getRegAssumeFree(reg, bt.inst);
309 }
310 },
311 .register_with_overflow => |rwo| {
312 if (bt.function.register_manager.isRegFree(rwo.reg)) {
313 bt.function.register_manager.getRegAssumeFree(rwo.reg, bt.inst);
314 }
315 bt.function.compare_flags_inst = bt.inst;
316 },
317 .compare_flags => |_| {
318 bt.function.compare_flags_inst = bt.inst;
319 },
320 else => {},
321 }
322 }
323 bt.function.finishAirBookkeeping();
324 }
325};
326
327const Self = @This();
328
329pub fn generate(
330 lf: *link.File,
331 pt: Zcu.PerThread,
332 src_loc: Zcu.LazySrcLoc,
333 func_index: InternPool.Index,
334 air: *const Air,
335 liveness: *const Air.Liveness,
336) CodeGenError!Mir {
337 const zcu = pt.zcu;
338 const gpa = zcu.gpa;
339 const func = zcu.funcInfo(func_index);
340 const fn_type = Type.fromInterned(func.ty);
341 const file_scope = zcu.navFileScope(func.owner_nav);
342 const target = &file_scope.mod.?.resolved_target.result;
343
344 var branch_stack = std.ArrayList(Branch).init(gpa);
345 defer {
346 assert(branch_stack.items.len == 1);
347 branch_stack.items[0].deinit(gpa);
348 branch_stack.deinit();
349 }
350 try branch_stack.append(.{});
351
352 var function: Self = .{
353 .gpa = gpa,
354 .pt = pt,
355 .air = air.*,
356 .liveness = liveness.*,
357 .target = target,
358 .bin_file = lf,
359 .func_index = func_index,
360 .owner_nav = func.owner_nav,
361 .args = undefined, // populated after `resolveCallingConventionValues`
362 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
363 .fn_type = fn_type,
364 .arg_index = 0,
365 .branch_stack = &branch_stack,
366 .src_loc = src_loc,
367 .stack_align = undefined,
368 .end_di_line = func.rbrace_line,
369 .end_di_column = func.rbrace_column,
370 };
371 defer function.stack.deinit(gpa);
372 defer function.blocks.deinit(gpa);
373 defer function.exitlude_jump_relocs.deinit(gpa);
374 defer function.dbg_info_relocs.deinit(gpa);
375
376 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
377 error.CodegenFail => return error.CodegenFail,
378 else => |e| return e,
379 };
380 defer call_info.deinit(&function);
381
382 function.args = call_info.args;
383 function.ret_mcv = call_info.return_value;
384 function.stack_align = call_info.stack_align;
385 function.max_end_stack = call_info.stack_byte_count;
386
387 function.gen() catch |err| switch (err) {
388 error.CodegenFail => return error.CodegenFail,
389 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
390 else => |e| return e,
391 };
392
393 for (function.dbg_info_relocs.items) |reloc| {
394 reloc.genDbgInfo(function) catch |err|
395 return function.fail("failed to generate debug info: {s}", .{@errorName(err)});
396 }
397
398 var mir: Mir = .{
399 .instructions = function.mir_instructions.toOwnedSlice(),
400 .extra = &.{}, // fallible, so assign after errdefer
401 .max_end_stack = function.max_end_stack,
402 .saved_regs_stack_space = function.saved_regs_stack_space,
403 };
404 errdefer mir.deinit(gpa);
405 mir.extra = try function.mir_extra.toOwnedSlice(gpa);
406 return mir;
407}
408
409fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
410 const gpa = self.gpa;
411
412 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
413
414 const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
415 self.mir_instructions.appendAssumeCapacity(inst);
416 return result_index;
417}
418
419fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
420 return try self.addInst(.{
421 .tag = .nop,
422 .data = .{ .nop = {} },
423 });
424}
425
426pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
427 const fields = std.meta.fields(@TypeOf(extra));
428 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
429 return self.addExtraAssumeCapacity(extra);
430}
431
432pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
433 const fields = std.meta.fields(@TypeOf(extra));
434 const result = @as(u32, @intCast(self.mir_extra.items.len));
435 inline for (fields) |field| {
436 self.mir_extra.appendAssumeCapacity(switch (field.type) {
437 u32 => @field(extra, field.name),
438 i32 => @as(u32, @bitCast(@field(extra, field.name))),
439 else => @compileError("bad field type"),
440 });
441 }
442 return result;
443}
444
445fn gen(self: *Self) !void {
446 const pt = self.pt;
447 const zcu = pt.zcu;
448 const cc = self.fn_type.fnCallingConvention(zcu);
449 if (cc != .naked) {
450 // stp fp, lr, [sp, #-16]!
451 _ = try self.addInst(.{
452 .tag = .stp,
453 .data = .{ .load_store_register_pair = .{
454 .rt = .x29,
455 .rt2 = .x30,
456 .rn = .sp,
457 .offset = Instruction.LoadStorePairOffset.pre_index(-16),
458 } },
459 });
460
461 // <store other registers>
462 const backpatch_save_registers = try self.addNop();
463
464 // mov fp, sp
465 _ = try self.addInst(.{
466 .tag = .mov_to_from_sp,
467 .data = .{ .rr = .{ .rd = .x29, .rn = .sp } },
468 });
469
470 // sub sp, sp, #reloc
471 const backpatch_reloc = try self.addNop();
472
473 if (self.ret_mcv == .stack_offset) {
474 // The address of where to store the return value is in x0
475 // (or w0 when pointer size is 32 bits). As this register
476 // might get overwritten along the way, save the address
477 // to the stack.
478 const ret_ptr_reg = self.registerAlias(.x0, Type.usize);
479
480 const stack_offset = try self.allocMem(8, .@"8", null);
481
482 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg });
483 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
484 }
485
486 for (self.args, 0..) |*arg, arg_index| {
487 // Copy register arguments to the stack
488 switch (arg.*) {
489 .register => |reg| {
490 // The first AIR instructions of the main body are guaranteed
491 // to be the functions arguments
492 const inst = self.air.getMainBody()[arg_index];
493 assert(self.air.instructions.items(.tag)[@intFromEnum(inst)] == .arg);
494
495 const ty = self.typeOfIndex(inst);
496
497 const abi_size = @as(u32, @intCast(ty.abiSize(zcu)));
498 const abi_align = ty.abiAlignment(zcu);
499 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
500 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
501
502 arg.* = MCValue{ .stack_offset = stack_offset };
503 },
504 else => {},
505 }
506 }
507
508 _ = try self.addInst(.{
509 .tag = .dbg_prologue_end,
510 .data = .{ .nop = {} },
511 });
512
513 try self.genBody(self.air.getMainBody());
514
515 // Backpatch push callee saved regs
516 var saved_regs: u32 = 0;
517 self.saved_regs_stack_space = 16;
518 inline for (callee_preserved_regs) |reg| {
519 if (self.register_manager.isRegAllocated(reg)) {
520 saved_regs |= @as(u32, 1) << @as(u5, @intCast(reg.id()));
521 self.saved_regs_stack_space += 8;
522 }
523 }
524
525 // Emit.mirPopPushRegs automatically adds extra empty space so
526 // that sp is always aligned to 16
527 if (!std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)) {
528 self.saved_regs_stack_space += 8;
529 }
530 assert(std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16));
531
532 self.mir_instructions.set(backpatch_save_registers, .{
533 .tag = .push_regs,
534 .data = .{ .reg_list = saved_regs },
535 });
536
537 // Backpatch stack offset
538 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
539 const aligned_total_stack_end = mem.alignForward(u32, total_stack_size, self.stack_align);
540 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
541 self.max_end_stack = stack_size;
542 if (math.cast(u12, stack_size)) |size| {
543 self.mir_instructions.set(backpatch_reloc, .{
544 .tag = .sub_immediate,
545 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } },
546 });
547 } else {
548 @panic("TODO AArch64: allow larger stacks");
549 }
550
551 _ = try self.addInst(.{
552 .tag = .dbg_epilogue_begin,
553 .data = .{ .nop = {} },
554 });
555
556 // exitlude jumps
557 if (self.exitlude_jump_relocs.items.len > 0 and
558 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
559 {
560 // If the last Mir instruction (apart from the
561 // dbg_epilogue_begin) is the last exitlude jump
562 // relocation (which would just jump one instruction
563 // further), it can be safely removed
564 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop().?);
565 }
566
567 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
568 self.mir_instructions.set(jmp_reloc, .{
569 .tag = .b,
570 .data = .{ .inst = @as(u32, @intCast(self.mir_instructions.len)) },
571 });
572 }
573
574 // add sp, sp, #stack_size
575 _ = try self.addInst(.{
576 .tag = .add_immediate,
577 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = @as(u12, @intCast(stack_size)) } },
578 });
579
580 // <load other registers>
581 _ = try self.addInst(.{
582 .tag = .pop_regs,
583 .data = .{ .reg_list = saved_regs },
584 });
585
586 // ldp fp, lr, [sp], #16
587 _ = try self.addInst(.{
588 .tag = .ldp,
589 .data = .{ .load_store_register_pair = .{
590 .rt = .x29,
591 .rt2 = .x30,
592 .rn = .sp,
593 .offset = Instruction.LoadStorePairOffset.post_index(16),
594 } },
595 });
596
597 // ret lr
598 _ = try self.addInst(.{
599 .tag = .ret,
600 .data = .{ .reg = .x30 },
601 });
602 } else {
603 _ = try self.addInst(.{
604 .tag = .dbg_prologue_end,
605 .data = .{ .nop = {} },
606 });
607
608 try self.genBody(self.air.getMainBody());
609
610 _ = try self.addInst(.{
611 .tag = .dbg_epilogue_begin,
612 .data = .{ .nop = {} },
613 });
614 }
615
616 // Drop them off at the rbrace.
617 _ = try self.addInst(.{
618 .tag = .dbg_line,
619 .data = .{ .dbg_line_column = .{
620 .line = self.end_di_line,
621 .column = self.end_di_column,
622 } },
623 });
624}
625
626fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
627 const pt = self.pt;
628 const zcu = pt.zcu;
629 const ip = &zcu.intern_pool;
630 const air_tags = self.air.instructions.items(.tag);
631
632 for (body) |inst| {
633 // TODO: remove now-redundant isUnused calls from AIR handler functions
634 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip))
635 continue;
636
637 const old_air_bookkeeping = self.air_bookkeeping;
638 try self.ensureProcessDeathCapacity(Air.Liveness.bpi);
639
640 self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
641 switch (air_tags[@intFromEnum(inst)]) {
642 // zig fmt: off
643 .add => try self.airBinOp(inst, .add),
644 .add_wrap => try self.airBinOp(inst, .add_wrap),
645 .sub => try self.airBinOp(inst, .sub),
646 .sub_wrap => try self.airBinOp(inst, .sub_wrap),
647 .mul => try self.airBinOp(inst, .mul),
648 .mul_wrap => try self.airBinOp(inst, .mul_wrap),
649 .shl => try self.airBinOp(inst, .shl),
650 .shl_exact => try self.airBinOp(inst, .shl_exact),
651 .bool_and => try self.airBinOp(inst, .bool_and),
652 .bool_or => try self.airBinOp(inst, .bool_or),
653 .bit_and => try self.airBinOp(inst, .bit_and),
654 .bit_or => try self.airBinOp(inst, .bit_or),
655 .xor => try self.airBinOp(inst, .xor),
656 .shr => try self.airBinOp(inst, .shr),
657 .shr_exact => try self.airBinOp(inst, .shr_exact),
658 .div_float => try self.airBinOp(inst, .div_float),
659 .div_trunc => try self.airBinOp(inst, .div_trunc),
660 .div_floor => try self.airBinOp(inst, .div_floor),
661 .div_exact => try self.airBinOp(inst, .div_exact),
662 .rem => try self.airBinOp(inst, .rem),
663 .mod => try self.airBinOp(inst, .mod),
664
665 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
666 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
667
668 .min => try self.airMinMax(inst),
669 .max => try self.airMinMax(inst),
670
671 .add_sat => try self.airAddSat(inst),
672 .sub_sat => try self.airSubSat(inst),
673 .mul_sat => try self.airMulSat(inst),
674 .shl_sat => try self.airShlSat(inst),
675 .slice => try self.airSlice(inst),
676
677 .sqrt,
678 .sin,
679 .cos,
680 .tan,
681 .exp,
682 .exp2,
683 .log,
684 .log2,
685 .log10,
686 .floor,
687 .ceil,
688 .round,
689 .trunc_float,
690 .neg,
691 => try self.airUnaryMath(inst),
692
693 .add_with_overflow => try self.airOverflow(inst),
694 .sub_with_overflow => try self.airOverflow(inst),
695 .mul_with_overflow => try self.airMulWithOverflow(inst),
696 .shl_with_overflow => try self.airShlWithOverflow(inst),
697
698 .cmp_lt => try self.airCmp(inst, .lt),
699 .cmp_lte => try self.airCmp(inst, .lte),
700 .cmp_eq => try self.airCmp(inst, .eq),
701 .cmp_gte => try self.airCmp(inst, .gte),
702 .cmp_gt => try self.airCmp(inst, .gt),
703 .cmp_neq => try self.airCmp(inst, .neq),
704
705 .cmp_vector => try self.airCmpVector(inst),
706 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
707
708 .alloc => try self.airAlloc(inst),
709 .ret_ptr => try self.airRetPtr(inst),
710 .arg => try self.airArg(inst),
711 .assembly => try self.airAsm(inst),
712 .bitcast => try self.airBitCast(inst),
713 .block => try self.airBlock(inst),
714 .br => try self.airBr(inst),
715 .repeat => return self.fail("TODO implement `repeat`", .{}),
716 .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}),
717 .trap => try self.airTrap(),
718 .breakpoint => try self.airBreakpoint(),
719 .ret_addr => try self.airRetAddr(inst),
720 .frame_addr => try self.airFrameAddress(inst),
721 .cond_br => try self.airCondBr(inst),
722 .fptrunc => try self.airFptrunc(inst),
723 .fpext => try self.airFpext(inst),
724 .intcast => try self.airIntCast(inst),
725 .trunc => try self.airTrunc(inst),
726 .is_non_null => try self.airIsNonNull(inst),
727 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
728 .is_null => try self.airIsNull(inst),
729 .is_null_ptr => try self.airIsNullPtr(inst),
730 .is_non_err => try self.airIsNonErr(inst),
731 .is_non_err_ptr => try self.airIsNonErrPtr(inst),
732 .is_err => try self.airIsErr(inst),
733 .is_err_ptr => try self.airIsErrPtr(inst),
734 .load => try self.airLoad(inst),
735 .loop => try self.airLoop(inst),
736 .not => try self.airNot(inst),
737 .ret => try self.airRet(inst),
738 .ret_safe => try self.airRet(inst), // TODO
739 .ret_load => try self.airRetLoad(inst),
740 .store => try self.airStore(inst, false),
741 .store_safe => try self.airStore(inst, true),
742 .struct_field_ptr=> try self.airStructFieldPtr(inst),
743 .struct_field_val=> try self.airStructFieldVal(inst),
744 .array_to_slice => try self.airArrayToSlice(inst),
745 .float_from_int => try self.airFloatFromInt(inst),
746 .int_from_float => try self.airIntFromFloat(inst),
747 .cmpxchg_strong => try self.airCmpxchg(inst),
748 .cmpxchg_weak => try self.airCmpxchg(inst),
749 .atomic_rmw => try self.airAtomicRmw(inst),
750 .atomic_load => try self.airAtomicLoad(inst),
751 .memcpy => try self.airMemcpy(inst),
752 .memmove => try self.airMemmove(inst),
753 .memset => try self.airMemset(inst, false),
754 .memset_safe => try self.airMemset(inst, true),
755 .set_union_tag => try self.airSetUnionTag(inst),
756 .get_union_tag => try self.airGetUnionTag(inst),
757 .clz => try self.airClz(inst),
758 .ctz => try self.airCtz(inst),
759 .popcount => try self.airPopcount(inst),
760 .abs => try self.airAbs(inst),
761 .byte_swap => try self.airByteSwap(inst),
762 .bit_reverse => try self.airBitReverse(inst),
763 .tag_name => try self.airTagName(inst),
764 .error_name => try self.airErrorName(inst),
765 .splat => try self.airSplat(inst),
766 .select => try self.airSelect(inst),
767 .shuffle_one => try self.airShuffleOne(inst),
768 .shuffle_two => try self.airShuffleTwo(inst),
769 .reduce => try self.airReduce(inst),
770 .aggregate_init => try self.airAggregateInit(inst),
771 .union_init => try self.airUnionInit(inst),
772 .prefetch => try self.airPrefetch(inst),
773 .mul_add => try self.airMulAdd(inst),
774 .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}),
775
776 .@"try" => try self.airTry(inst),
777 .try_cold => try self.airTry(inst),
778 .try_ptr => try self.airTryPtr(inst),
779 .try_ptr_cold => try self.airTryPtr(inst),
780
781 .dbg_stmt => try self.airDbgStmt(inst),
782 .dbg_empty_stmt => self.finishAirBookkeeping(),
783 .dbg_inline_block => try self.airDbgInlineBlock(inst),
784 .dbg_var_ptr,
785 .dbg_var_val,
786 .dbg_arg_inline,
787 => try self.airDbgVar(inst),
788
789 .call => try self.airCall(inst, .auto),
790 .call_always_tail => try self.airCall(inst, .always_tail),
791 .call_never_tail => try self.airCall(inst, .never_tail),
792 .call_never_inline => try self.airCall(inst, .never_inline),
793
794 .atomic_store_unordered => try self.airAtomicStore(inst, .unordered),
795 .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic),
796 .atomic_store_release => try self.airAtomicStore(inst, .release),
797 .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst),
798
799 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
800 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
801 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
802 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
803
804 .field_parent_ptr => try self.airFieldParentPtr(inst),
805
806 .switch_br => try self.airSwitch(inst),
807 .loop_switch_br => return self.fail("TODO implement `loop_switch_br`", .{}),
808 .slice_ptr => try self.airSlicePtr(inst),
809 .slice_len => try self.airSliceLen(inst),
810
811 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
812 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
813
814 .array_elem_val => try self.airArrayElemVal(inst),
815 .slice_elem_val => try self.airSliceElemVal(inst),
816 .slice_elem_ptr => try self.airSliceElemPtr(inst),
817 .ptr_elem_val => try self.airPtrElemVal(inst),
818 .ptr_elem_ptr => try self.airPtrElemPtr(inst),
819
820 .inferred_alloc, .inferred_alloc_comptime => unreachable,
821 .unreach => self.finishAirBookkeeping(),
822
823 .optional_payload => try self.airOptionalPayload(inst),
824 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
825 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst),
826 .unwrap_errunion_err => try self.airUnwrapErrErr(inst),
827 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
828 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
829 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
830 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
831 .err_return_trace => try self.airErrReturnTrace(inst),
832 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
833 .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
834
835 .wrap_optional => try self.airWrapOptional(inst),
836 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
837 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
838
839 .add_optimized,
840 .sub_optimized,
841 .mul_optimized,
842 .div_float_optimized,
843 .div_trunc_optimized,
844 .div_floor_optimized,
845 .div_exact_optimized,
846 .rem_optimized,
847 .mod_optimized,
848 .neg_optimized,
849 .cmp_lt_optimized,
850 .cmp_lte_optimized,
851 .cmp_eq_optimized,
852 .cmp_gte_optimized,
853 .cmp_gt_optimized,
854 .cmp_neq_optimized,
855 .cmp_vector_optimized,
856 .reduce_optimized,
857 .int_from_float_optimized,
858 => return self.fail("TODO implement optimized float mode", .{}),
859
860 .add_safe,
861 .sub_safe,
862 .mul_safe,
863 .intcast_safe,
864 .int_from_float_safe,
865 .int_from_float_optimized_safe,
866 => return self.fail("TODO implement safety_checked_instructions", .{}),
867
868 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
869 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
870 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
871 .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}),
872
873 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
874 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
875 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
876 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
877
878 .wasm_memory_size => unreachable,
879 .wasm_memory_grow => unreachable,
880
881 .work_item_id => unreachable,
882 .work_group_size => unreachable,
883 .work_group_id => unreachable,
884 // zig fmt: on
885 }
886
887 assert(!self.register_manager.lockedRegsExist());
888
889 if (std.debug.runtime_safety) {
890 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
891 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
892 }
893 }
894 }
895}
896
897/// Asserts there is already capacity to insert into top branch inst_table.
898fn processDeath(self: *Self, inst: Air.Inst.Index) void {
899 // When editing this function, note that the logic must synchronize with `reuseOperand`.
900 const prev_value = self.getResolvedInstValue(inst);
901 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
902 branch.inst_table.putAssumeCapacity(inst, .dead);
903 switch (prev_value) {
904 .register => |reg| {
905 self.register_manager.freeReg(reg);
906 },
907 .register_with_overflow => |rwo| {
908 self.register_manager.freeReg(rwo.reg);
909 self.compare_flags_inst = null;
910 },
911 .compare_flags => {
912 self.compare_flags_inst = null;
913 },
914 else => {}, // TODO process stack allocation death
915 }
916}
917
918/// Called when there are no operands, and the instruction is always unreferenced.
919fn finishAirBookkeeping(self: *Self) void {
920 if (std.debug.runtime_safety) {
921 self.air_bookkeeping += 1;
922 }
923}
924
925fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Air.Liveness.bpi - 1]Air.Inst.Ref) void {
926 const tomb_bits = self.liveness.getTombBits(inst);
927 for (0.., operands) |op_index, op| {
928 if (tomb_bits & @as(Air.Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
929 if (self.reused_operands.isSet(op_index)) continue;
930 self.processDeath(op.toIndexAllowNone() orelse continue);
931 }
932 if (tomb_bits & 1 << (Air.Liveness.bpi - 1) == 0) {
933 log.debug("%{d} => {}", .{ inst, result });
934 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
935 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
936
937 switch (result) {
938 .register => |reg| {
939 // In some cases (such as bitcast), an operand
940 // may be the same MCValue as the result. If
941 // that operand died and was a register, it
942 // was freed by processDeath. We have to
943 // "re-allocate" the register.
944 if (self.register_manager.isRegFree(reg)) {
945 self.register_manager.getRegAssumeFree(reg, inst);
946 }
947 },
948 .register_with_overflow => |rwo| {
949 if (self.register_manager.isRegFree(rwo.reg)) {
950 self.register_manager.getRegAssumeFree(rwo.reg, inst);
951 }
952 self.compare_flags_inst = inst;
953 },
954 .compare_flags => |_| {
955 self.compare_flags_inst = inst;
956 },
957 else => {},
958 }
959 }
960 self.finishAirBookkeeping();
961}
962
963fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
964 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
965 try table.ensureUnusedCapacity(self.gpa, additional_count);
966}
967
968fn allocMem(
969 self: *Self,
970 abi_size: u32,
971 abi_align: Alignment,
972 maybe_inst: ?Air.Inst.Index,
973) !u32 {
974 assert(abi_size > 0);
975 assert(abi_align != .none);
976
977 // In order to efficiently load and store stack items that fit
978 // into registers, we bump up the alignment to the next power of
979 // two.
980 const adjusted_align = if (abi_size > 8)
981 abi_align
982 else
983 Alignment.fromNonzeroByteUnits(std.math.ceilPowerOfTwoAssert(u64, abi_size));
984
985 // TODO find a free slot instead of always appending
986 const offset: u32 = @intCast(adjusted_align.forward(self.next_stack_offset) + abi_size);
987 self.next_stack_offset = offset;
988 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
989
990 if (maybe_inst) |inst| {
991 try self.stack.putNoClobber(self.gpa, offset, .{
992 .inst = inst,
993 .size = abi_size,
994 });
995 }
996
997 return offset;
998}
999
1000/// Use a pointer instruction as the basis for allocating stack memory.
1001fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
1002 const pt = self.pt;
1003 const zcu = pt.zcu;
1004 const elem_ty = self.typeOfIndex(inst).childType(zcu);
1005
1006 if (!elem_ty.hasRuntimeBits(zcu)) {
1007 // return the stack offset 0. Stack offset 0 will be where all
1008 // zero-sized stack allocations live as non-zero-sized
1009 // allocations will always have an offset > 0.
1010 return @as(u32, 0);
1011 }
1012
1013 const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse {
1014 return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
1015 };
1016 // TODO swap this for inst.ty.ptrAlign
1017 const abi_align = elem_ty.abiAlignment(zcu);
1018
1019 return self.allocMem(abi_size, abi_align, inst);
1020}
1021
1022fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
1023 const pt = self.pt;
1024 const abi_size = math.cast(u32, elem_ty.abiSize(pt.zcu)) orelse {
1025 return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
1026 };
1027 const abi_align = elem_ty.abiAlignment(pt.zcu);
1028
1029 if (reg_ok) {
1030 // Make sure the type can fit in a register before we try to allocate one.
1031 if (abi_size <= 8) {
1032 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
1033 return MCValue{ .register = self.registerAlias(reg, elem_ty) };
1034 }
1035 }
1036 }
1037
1038 const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst);
1039 return MCValue{ .stack_offset = stack_offset };
1040}
1041
1042pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1043 const stack_mcv = try self.allocRegOrMem(self.typeOfIndex(inst), false, inst);
1044 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
1045
1046 const reg_mcv = self.getResolvedInstValue(inst);
1047 switch (reg_mcv) {
1048 .register => |r| assert(reg.id() == r.id()),
1049 .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()),
1050 else => unreachable, // not a register
1051 }
1052
1053 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1054 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1055 try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1056}
1057
1058/// Save the current instruction stored in the compare flags if
1059/// occupied
1060fn spillCompareFlagsIfOccupied(self: *Self) !void {
1061 if (self.compare_flags_inst) |inst_to_save| {
1062 const ty = self.typeOfIndex(inst_to_save);
1063 const mcv = self.getResolvedInstValue(inst_to_save);
1064 const new_mcv = switch (mcv) {
1065 .compare_flags => try self.allocRegOrMem(ty, true, inst_to_save),
1066 .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save),
1067 else => unreachable, // mcv doesn't occupy the compare flags
1068 };
1069
1070 try self.setRegOrMem(self.typeOfIndex(inst_to_save), new_mcv, mcv);
1071 log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
1072
1073 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1074 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
1075
1076 self.compare_flags_inst = null;
1077
1078 // TODO consolidate with register manager and spillInstruction
1079 // this call should really belong in the register manager!
1080 switch (mcv) {
1081 .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg),
1082 else => {},
1083 }
1084 }
1085}
1086
1087/// Copies a value to a register without tracking the register. The register is not considered
1088/// allocated. A second call to `copyToTmpRegister` may return the same register.
1089/// This can have a side effect of spilling instructions to the stack to free up a register.
1090fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) InnerError!Register {
1091 const raw_reg = try self.register_manager.allocReg(null, gp);
1092 const reg = self.registerAlias(raw_reg, ty);
1093 try self.genSetReg(ty, reg, mcv);
1094 return reg;
1095}
1096
1097/// Allocates a new register and copies `mcv` into it.
1098/// `reg_owner` is the instruction that gets associated with the register in the register table.
1099/// This can have a side effect of spilling instructions to the stack to free up a register.
1100fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
1101 const raw_reg = try self.register_manager.allocReg(reg_owner, gp);
1102 const ty = self.typeOfIndex(reg_owner);
1103 const reg = self.registerAlias(raw_reg, ty);
1104 try self.genSetReg(self.typeOfIndex(reg_owner), reg, mcv);
1105 return MCValue{ .register = reg };
1106}
1107
1108fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1109 const stack_offset = try self.allocMemPtr(inst);
1110 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1111}
1112
1113fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
1114 const pt = self.pt;
1115 const zcu = pt.zcu;
1116 const result: MCValue = switch (self.ret_mcv) {
1117 .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) },
1118 .stack_offset => blk: {
1119 // self.ret_mcv is an address to where this function
1120 // should store its result into
1121 const ret_ty = self.fn_type.fnReturnType(zcu);
1122 const ptr_ty = try pt.singleMutPtrType(ret_ty);
1123
1124 // addr_reg will contain the address of where to store the
1125 // result into
1126 const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv);
1127 break :blk .{ .register = addr_reg };
1128 },
1129 else => unreachable, // invalid return result
1130 };
1131
1132 return self.finishAir(inst, result, .{ .none, .none, .none });
1133}
1134
1135fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1136 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1137 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
1138 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1139}
1140
1141fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void {
1142 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1143 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
1144 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1145}
1146
1147fn airIntCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
1148 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1149 if (self.liveness.isUnused(inst))
1150 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1151
1152 const pt = self.pt;
1153 const zcu = pt.zcu;
1154 const operand = ty_op.operand;
1155 const operand_mcv = try self.resolveInst(operand);
1156 const operand_ty = self.typeOf(operand);
1157 const operand_info = operand_ty.intInfo(zcu);
1158
1159 const dest_ty = self.typeOfIndex(inst);
1160 const dest_info = dest_ty.intInfo(zcu);
1161
1162 const result: MCValue = result: {
1163 const operand_lock: ?RegisterLock = switch (operand_mcv) {
1164 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1165 else => null,
1166 };
1167 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
1168
1169 const truncated: MCValue = switch (operand_mcv) {
1170 .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) },
1171 else => operand_mcv,
1172 };
1173
1174 if (dest_info.bits > operand_info.bits) {
1175 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst);
1176 try self.setRegOrMem(self.typeOfIndex(inst), dest_mcv, truncated);
1177 break :result dest_mcv;
1178 } else {
1179 if (self.reuseOperand(inst, operand, 0, truncated)) {
1180 break :result truncated;
1181 } else {
1182 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst);
1183 try self.setRegOrMem(self.typeOfIndex(inst), dest_mcv, truncated);
1184 break :result dest_mcv;
1185 }
1186 }
1187 };
1188
1189 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1190}
1191
1192fn truncRegister(
1193 self: *Self,
1194 operand_reg: Register,
1195 dest_reg: Register,
1196 int_signedness: std.builtin.Signedness,
1197 int_bits: u16,
1198) !void {
1199 switch (int_bits) {
1200 1...31, 33...63 => {
1201 _ = try self.addInst(.{
1202 .tag = switch (int_signedness) {
1203 .signed => .sbfx,
1204 .unsigned => .ubfx,
1205 },
1206 .data = .{ .rr_lsb_width = .{
1207 .rd = dest_reg,
1208 .rn = operand_reg,
1209 .lsb = 0,
1210 .width = @as(u6, @intCast(int_bits)),
1211 } },
1212 });
1213 },
1214 32, 64 => {
1215 _ = try self.addInst(.{
1216 .tag = .mov_register,
1217 .data = .{ .rr = .{
1218 .rd = if (int_bits == 32) dest_reg.toW() else dest_reg.toX(),
1219 .rn = if (int_bits == 32) operand_reg.toW() else operand_reg.toX(),
1220 } },
1221 });
1222 },
1223 else => unreachable,
1224 }
1225}
1226
1227fn trunc(
1228 self: *Self,
1229 maybe_inst: ?Air.Inst.Index,
1230 operand: MCValue,
1231 operand_ty: Type,
1232 dest_ty: Type,
1233) !MCValue {
1234 const pt = self.pt;
1235 const zcu = pt.zcu;
1236 const info_a = operand_ty.intInfo(zcu);
1237 const info_b = dest_ty.intInfo(zcu);
1238
1239 if (info_b.bits <= 64) {
1240 const operand_reg = switch (operand) {
1241 .register => |r| r,
1242 else => operand_reg: {
1243 if (info_a.bits <= 64) {
1244 const raw_reg = try self.copyToTmpRegister(operand_ty, operand);
1245 break :operand_reg self.registerAlias(raw_reg, operand_ty);
1246 } else {
1247 return self.fail("TODO load least significant word into register", .{});
1248 }
1249 },
1250 };
1251 const lock = self.register_manager.lockReg(operand_reg);
1252 defer if (lock) |reg| self.register_manager.unlockReg(reg);
1253
1254 const dest_reg = if (maybe_inst) |inst| blk: {
1255 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1256
1257 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1258 break :blk self.registerAlias(operand_reg, dest_ty);
1259 } else {
1260 const raw_reg = try self.register_manager.allocReg(inst, gp);
1261 break :blk self.registerAlias(raw_reg, dest_ty);
1262 }
1263 } else blk: {
1264 const raw_reg = try self.register_manager.allocReg(null, gp);
1265 break :blk self.registerAlias(raw_reg, dest_ty);
1266 };
1267
1268 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
1269
1270 return MCValue{ .register = dest_reg };
1271 } else {
1272 return self.fail("TODO: truncate to ints > 64 bits", .{});
1273 }
1274}
1275
1276fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
1277 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1278 const operand = try self.resolveInst(ty_op.operand);
1279 const operand_ty = self.typeOf(ty_op.operand);
1280 const dest_ty = self.typeOfIndex(inst);
1281
1282 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
1283 break :blk try self.trunc(inst, operand, operand_ty, dest_ty);
1284 };
1285
1286 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1287}
1288
1289fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
1290 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1291 const pt = self.pt;
1292 const zcu = pt.zcu;
1293 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1294 const operand = try self.resolveInst(ty_op.operand);
1295 const operand_ty = self.typeOf(ty_op.operand);
1296 switch (operand) {
1297 .dead => unreachable,
1298 .unreach => unreachable,
1299 .compare_flags => |cond| break :result MCValue{ .compare_flags = cond.negate() },
1300 else => {
1301 switch (operand_ty.zigTypeTag(zcu)) {
1302 .bool => {
1303 // TODO convert this to mvn + and
1304 const op_reg = switch (operand) {
1305 .register => |r| r,
1306 else => try self.copyToTmpRegister(operand_ty, operand),
1307 };
1308 const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
1309 defer self.register_manager.unlockReg(reg_lock);
1310
1311 const dest_reg = blk: {
1312 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1313 break :blk op_reg;
1314 }
1315
1316 const raw_reg = try self.register_manager.allocReg(null, gp);
1317 break :blk self.registerAlias(raw_reg, operand_ty);
1318 };
1319
1320 _ = try self.addInst(.{
1321 .tag = .eor_immediate,
1322 .data = .{ .rr_bitmask = .{
1323 .rd = dest_reg,
1324 .rn = op_reg,
1325 .imms = 0b000000,
1326 .immr = 0b000000,
1327 .n = 0b0,
1328 } },
1329 });
1330
1331 break :result MCValue{ .register = dest_reg };
1332 },
1333 .vector => return self.fail("TODO bitwise not for vectors", .{}),
1334 .int => {
1335 const int_info = operand_ty.intInfo(zcu);
1336 if (int_info.bits <= 64) {
1337 const op_reg = switch (operand) {
1338 .register => |r| r,
1339 else => try self.copyToTmpRegister(operand_ty, operand),
1340 };
1341 const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
1342 defer self.register_manager.unlockReg(reg_lock);
1343
1344 const dest_reg = blk: {
1345 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1346 break :blk op_reg;
1347 }
1348
1349 const raw_reg = try self.register_manager.allocReg(null, gp);
1350 break :blk self.registerAlias(raw_reg, operand_ty);
1351 };
1352
1353 _ = try self.addInst(.{
1354 .tag = .mvn,
1355 .data = .{ .rr_imm6_logical_shift = .{
1356 .rd = dest_reg,
1357 .rm = op_reg,
1358 .imm6 = 0,
1359 .shift = .lsl,
1360 } },
1361 });
1362
1363 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
1364
1365 break :result MCValue{ .register = dest_reg };
1366 } else {
1367 return self.fail("TODO AArch64 not on integers > u64/i64", .{});
1368 }
1369 },
1370 else => unreachable,
1371 }
1372 },
1373 }
1374 };
1375 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1376}
1377
1378fn minMax(
1379 self: *Self,
1380 tag: Air.Inst.Tag,
1381 lhs_bind: ReadArg.Bind,
1382 rhs_bind: ReadArg.Bind,
1383 lhs_ty: Type,
1384 rhs_ty: Type,
1385 maybe_inst: ?Air.Inst.Index,
1386) !MCValue {
1387 const pt = self.pt;
1388 const zcu = pt.zcu;
1389 switch (lhs_ty.zigTypeTag(zcu)) {
1390 .float => return self.fail("TODO ARM min/max on floats", .{}),
1391 .vector => return self.fail("TODO ARM min/max on vectors", .{}),
1392 .int => {
1393 assert(lhs_ty.eql(rhs_ty, zcu));
1394 const int_info = lhs_ty.intInfo(zcu);
1395 if (int_info.bits <= 64) {
1396 var lhs_reg: Register = undefined;
1397 var rhs_reg: Register = undefined;
1398 var dest_reg: Register = undefined;
1399
1400 const read_args = [_]ReadArg{
1401 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1402 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1403 };
1404 const write_args = [_]WriteArg{
1405 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1406 };
1407 try self.allocRegs(
1408 &read_args,
1409 &write_args,
1410 if (maybe_inst) |inst| .{
1411 .corresponding_inst = inst,
1412 .operand_mapping = &.{ 0, 1 },
1413 } else null,
1414 );
1415
1416 // lhs == reg should have been checked by airMinMax
1417 assert(lhs_reg != rhs_reg); // see note above
1418
1419 _ = try self.addInst(.{
1420 .tag = .cmp_shifted_register,
1421 .data = .{ .rr_imm6_shift = .{
1422 .rn = lhs_reg,
1423 .rm = rhs_reg,
1424 .imm6 = 0,
1425 .shift = .lsl,
1426 } },
1427 });
1428
1429 const cond_choose_lhs: Condition = switch (tag) {
1430 .max => switch (int_info.signedness) {
1431 .signed => Condition.gt,
1432 .unsigned => Condition.hi,
1433 },
1434 .min => switch (int_info.signedness) {
1435 .signed => Condition.lt,
1436 .unsigned => Condition.cc,
1437 },
1438 else => unreachable,
1439 };
1440
1441 _ = try self.addInst(.{
1442 .tag = .csel,
1443 .data = .{ .rrr_cond = .{
1444 .rd = dest_reg,
1445 .rn = lhs_reg,
1446 .rm = rhs_reg,
1447 .cond = cond_choose_lhs,
1448 } },
1449 });
1450
1451 return MCValue{ .register = dest_reg };
1452 } else {
1453 return self.fail("TODO ARM min/max on integers > u32/i32", .{});
1454 }
1455 },
1456 else => unreachable,
1457 }
1458}
1459
1460fn airMinMax(self: *Self, inst: Air.Inst.Index) InnerError!void {
1461 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
1462 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1463 const lhs_ty = self.typeOf(bin_op.lhs);
1464 const rhs_ty = self.typeOf(bin_op.rhs);
1465
1466 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1467 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1468 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1469
1470 const lhs = try self.resolveInst(bin_op.lhs);
1471 if (bin_op.lhs == bin_op.rhs) break :result lhs;
1472
1473 break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
1474 };
1475 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1476}
1477
1478fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
1479 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1480 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1481 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1482 const ptr = try self.resolveInst(bin_op.lhs);
1483 const ptr_ty = self.typeOf(bin_op.lhs);
1484 const len = try self.resolveInst(bin_op.rhs);
1485 const len_ty = self.typeOf(bin_op.rhs);
1486
1487 const stack_offset = try self.allocMem(16, .@"8", inst);
1488 try self.genSetStack(ptr_ty, stack_offset, ptr);
1489 try self.genSetStack(len_ty, stack_offset - 8, len);
1490 break :result MCValue{ .stack_offset = stack_offset };
1491 };
1492 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1493}
1494
1495/// An argument to a Mir instruction which is read (and possibly also
1496/// written to) by the respective instruction
1497const ReadArg = struct {
1498 ty: Type,
1499 bind: Bind,
1500 class: RegisterManager.RegisterBitSet,
1501 reg: *Register,
1502
1503 const Bind = union(enum) {
1504 inst: Air.Inst.Ref,
1505 mcv: MCValue,
1506
1507 fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue {
1508 return switch (bind) {
1509 .inst => |inst| try function.resolveInst(inst),
1510 .mcv => |mcv| mcv,
1511 };
1512 }
1513
1514 fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u64 {
1515 switch (bind) {
1516 .inst => |inst| {
1517 // TODO resolve independently of inst_table
1518 const mcv = try function.resolveInst(inst);
1519 switch (mcv) {
1520 .immediate => |imm| return imm,
1521 else => return null,
1522 }
1523 },
1524 .mcv => |mcv| {
1525 switch (mcv) {
1526 .immediate => |imm| return imm,
1527 else => return null,
1528 }
1529 },
1530 }
1531 }
1532 };
1533};
1534
1535/// An argument to a Mir instruction which is written to (but not read
1536/// from) by the respective instruction
1537const WriteArg = struct {
1538 ty: Type,
1539 bind: Bind,
1540 class: RegisterManager.RegisterBitSet,
1541 reg: *Register,
1542
1543 const Bind = union(enum) {
1544 reg: Register,
1545 none: void,
1546 };
1547};
1548
1549/// Holds all data necessary for enabling the potential reuse of
1550/// operand registers as destinations
1551const ReuseMetadata = struct {
1552 corresponding_inst: Air.Inst.Index,
1553
1554 /// Maps every element index of read_args to the corresponding
1555 /// index in the Air instruction
1556 ///
1557 /// When the order of read_args corresponds exactly to the order
1558 /// of the inputs of the Air instruction, this would be e.g.
1559 /// &.{ 0, 1 }. However, when the order is not the same or some
1560 /// inputs to the Air instruction are omitted (e.g. when they can
1561 /// be represented as immediates to the Mir instruction),
1562 /// operand_mapping should reflect that fact.
1563 operand_mapping: []const Air.Liveness.OperandInt,
1564};
1565
1566/// Allocate a set of registers for use as arguments for a Mir
1567/// instruction
1568///
1569/// If the Mir instruction these registers are allocated for
1570/// corresponds exactly to a single Air instruction, populate
1571/// reuse_metadata in order to enable potential reuse of an operand as
1572/// the destination (provided that that operand dies in this
1573/// instruction).
1574///
1575/// Reusing an operand register as destination is the only time two
1576/// arguments may share the same register. In all other cases,
1577/// allocRegs guarantees that a register will never be allocated to
1578/// more than one argument.
1579///
1580/// Furthermore, allocReg guarantees that all arguments which are
1581/// already bound to registers before calling allocRegs will not
1582/// change their register binding. This is done by locking these
1583/// registers.
1584fn allocRegs(
1585 self: *Self,
1586 read_args: []const ReadArg,
1587 write_args: []const WriteArg,
1588 reuse_metadata: ?ReuseMetadata,
1589) InnerError!void {
1590 // Air instructions have exactly one output
1591 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
1592
1593 // The operand mapping is a 1:1 mapping of read args to their
1594 // corresponding operand index in the Air instruction
1595 assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above
1596
1597 const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len);
1598 defer self.gpa.free(locks);
1599 const read_locks = locks[0..read_args.len];
1600 const write_locks = locks[read_args.len..];
1601
1602 @memset(locks, null);
1603 defer for (locks) |lock| {
1604 if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
1605 };
1606
1607 // When we reuse a read_arg as a destination, the corresponding
1608 // MCValue of the read_arg will be set to .dead. In that case, we
1609 // skip allocating this read_arg.
1610 var reused_read_arg: ?usize = null;
1611
1612 // Lock all args which are already allocated to registers
1613 for (read_args, 0..) |arg, i| {
1614 const mcv = try arg.bind.resolveToMcv(self);
1615 if (mcv == .register) {
1616 read_locks[i] = self.register_manager.lockReg(mcv.register);
1617 }
1618 }
1619
1620 for (write_args, 0..) |arg, i| {
1621 if (arg.bind == .reg) {
1622 write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
1623 }
1624 }
1625
1626 // Allocate registers for all args which aren't allocated to
1627 // registers yet
1628 for (read_args, 0..) |arg, i| {
1629 const mcv = try arg.bind.resolveToMcv(self);
1630 if (mcv == .register) {
1631 const raw_reg = mcv.register;
1632 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1633 } else {
1634 const track_inst: ?Air.Inst.Index = switch (arg.bind) {
1635 .inst => |inst| inst.toIndex().?,
1636 else => null,
1637 };
1638 const raw_reg = try self.register_manager.allocReg(track_inst, gp);
1639 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1640 read_locks[i] = self.register_manager.lockRegAssumeUnused(arg.reg.*);
1641 }
1642 }
1643
1644 if (reuse_metadata != null) {
1645 const inst = reuse_metadata.?.corresponding_inst;
1646 const operand_mapping = reuse_metadata.?.operand_mapping;
1647 const arg = write_args[0];
1648 if (arg.bind == .reg) {
1649 const raw_reg = arg.bind.reg;
1650 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1651 } else {
1652 reuse_operand: for (read_args, 0..) |read_arg, i| {
1653 if (read_arg.bind == .inst) {
1654 const operand = read_arg.bind.inst;
1655 const mcv = try self.resolveInst(operand);
1656 if (mcv == .register and
1657 std.meta.eql(arg.class, read_arg.class) and
1658 self.reuseOperand(inst, operand, operand_mapping[i], mcv))
1659 {
1660 const raw_reg = mcv.register;
1661 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1662 write_locks[0] = null;
1663 reused_read_arg = i;
1664 break :reuse_operand;
1665 }
1666 }
1667 } else {
1668 const raw_reg = try self.register_manager.allocReg(inst, arg.class);
1669 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1670 write_locks[0] = self.register_manager.lockReg(arg.reg.*);
1671 }
1672 }
1673 } else {
1674 for (write_args, 0..) |arg, i| {
1675 if (arg.bind == .reg) {
1676 const raw_reg = arg.bind.reg;
1677 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1678 } else {
1679 const raw_reg = try self.register_manager.allocReg(null, arg.class);
1680 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1681 write_locks[i] = self.register_manager.lockReg(arg.reg.*);
1682 }
1683 }
1684 }
1685
1686 // For all read_args which need to be moved from non-register to
1687 // register, perform the move
1688 for (read_args, 0..) |arg, i| {
1689 if (reused_read_arg) |j| {
1690 // Check whether this read_arg was reused
1691 if (i == j) continue;
1692 }
1693
1694 const mcv = try arg.bind.resolveToMcv(self);
1695 if (mcv != .register) {
1696 if (arg.bind == .inst) {
1697 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1698 const inst = arg.bind.inst.toIndex().?;
1699
1700 // Overwrite the MCValue associated with this inst
1701 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* });
1702
1703 // If the previous MCValue occupied some space we track, we
1704 // need to make sure it is marked as free now.
1705 switch (mcv) {
1706 .compare_flags => {
1707 assert(self.compare_flags_inst.? == inst);
1708 self.compare_flags_inst = null;
1709 },
1710 .register => |prev_reg| {
1711 assert(!self.register_manager.isRegFree(prev_reg));
1712 self.register_manager.freeReg(prev_reg);
1713 },
1714 else => {},
1715 }
1716 }
1717
1718 try self.genSetReg(arg.ty, arg.reg.*, mcv);
1719 }
1720 }
1721}
1722
1723/// Wrapper around allocRegs and addInst tailored for specific Mir
1724/// instructions which are binary operations acting on two registers
1725///
1726/// Returns the destination register
1727fn binOpRegister(
1728 self: *Self,
1729 mir_tag: Mir.Inst.Tag,
1730 lhs_bind: ReadArg.Bind,
1731 rhs_bind: ReadArg.Bind,
1732 lhs_ty: Type,
1733 rhs_ty: Type,
1734 maybe_inst: ?Air.Inst.Index,
1735) !MCValue {
1736 var lhs_reg: Register = undefined;
1737 var rhs_reg: Register = undefined;
1738 var dest_reg: Register = undefined;
1739
1740 const read_args = [_]ReadArg{
1741 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1742 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1743 };
1744 const write_args = [_]WriteArg{
1745 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1746 };
1747 try self.allocRegs(
1748 &read_args,
1749 &write_args,
1750 if (maybe_inst) |inst| .{
1751 .corresponding_inst = inst,
1752 .operand_mapping = &.{ 0, 1 },
1753 } else null,
1754 );
1755
1756 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1757 .add_shifted_register,
1758 .adds_shifted_register,
1759 .sub_shifted_register,
1760 .subs_shifted_register,
1761 => .{ .rrr_imm6_shift = .{
1762 .rd = dest_reg,
1763 .rn = lhs_reg,
1764 .rm = rhs_reg,
1765 .imm6 = 0,
1766 .shift = .lsl,
1767 } },
1768 .mul,
1769 .lsl_register,
1770 .asr_register,
1771 .lsr_register,
1772 .sdiv,
1773 .udiv,
1774 => .{ .rrr = .{
1775 .rd = dest_reg,
1776 .rn = lhs_reg,
1777 .rm = rhs_reg,
1778 } },
1779 .smull,
1780 .umull,
1781 => .{ .rrr = .{
1782 .rd = dest_reg.toX(),
1783 .rn = lhs_reg,
1784 .rm = rhs_reg,
1785 } },
1786 .and_shifted_register,
1787 .orr_shifted_register,
1788 .eor_shifted_register,
1789 => .{ .rrr_imm6_logical_shift = .{
1790 .rd = dest_reg,
1791 .rn = lhs_reg,
1792 .rm = rhs_reg,
1793 .imm6 = 0,
1794 .shift = .lsl,
1795 } },
1796 else => unreachable,
1797 };
1798
1799 _ = try self.addInst(.{
1800 .tag = mir_tag,
1801 .data = mir_data,
1802 });
1803
1804 return MCValue{ .register = dest_reg };
1805}
1806
1807/// Wrapper around allocRegs and addInst tailored for specific Mir
1808/// instructions which are binary operations acting on a register and
1809/// an immediate
1810///
1811/// Returns the destination register
1812fn binOpImmediate(
1813 self: *Self,
1814 mir_tag: Mir.Inst.Tag,
1815 lhs_bind: ReadArg.Bind,
1816 rhs_immediate: u64,
1817 lhs_ty: Type,
1818 lhs_and_rhs_swapped: bool,
1819 maybe_inst: ?Air.Inst.Index,
1820) !MCValue {
1821 var lhs_reg: Register = undefined;
1822 var dest_reg: Register = undefined;
1823
1824 const read_args = [_]ReadArg{
1825 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1826 };
1827 const write_args = [_]WriteArg{
1828 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1829 };
1830 const operand_mapping: []const Air.Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
1831 try self.allocRegs(
1832 &read_args,
1833 &write_args,
1834 if (maybe_inst) |inst| .{
1835 .corresponding_inst = inst,
1836 .operand_mapping = operand_mapping,
1837 } else null,
1838 );
1839
1840 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1841 .add_immediate,
1842 .adds_immediate,
1843 .sub_immediate,
1844 .subs_immediate,
1845 => .{ .rr_imm12_sh = .{
1846 .rd = dest_reg,
1847 .rn = lhs_reg,
1848 .imm12 = @as(u12, @intCast(rhs_immediate)),
1849 } },
1850 .lsl_immediate,
1851 .asr_immediate,
1852 .lsr_immediate,
1853 => .{ .rr_shift = .{
1854 .rd = dest_reg,
1855 .rn = lhs_reg,
1856 .shift = @as(u6, @intCast(rhs_immediate)),
1857 } },
1858 else => unreachable,
1859 };
1860
1861 _ = try self.addInst(.{
1862 .tag = mir_tag,
1863 .data = mir_data,
1864 });
1865
1866 return MCValue{ .register = dest_reg };
1867}
1868
1869fn addSub(
1870 self: *Self,
1871 tag: Air.Inst.Tag,
1872 lhs_bind: ReadArg.Bind,
1873 rhs_bind: ReadArg.Bind,
1874 lhs_ty: Type,
1875 rhs_ty: Type,
1876 maybe_inst: ?Air.Inst.Index,
1877) InnerError!MCValue {
1878 const pt = self.pt;
1879 const zcu = pt.zcu;
1880 switch (lhs_ty.zigTypeTag(zcu)) {
1881 .float => return self.fail("TODO binary operations on floats", .{}),
1882 .vector => return self.fail("TODO binary operations on vectors", .{}),
1883 .int => {
1884 assert(lhs_ty.eql(rhs_ty, zcu));
1885 const int_info = lhs_ty.intInfo(zcu);
1886 if (int_info.bits <= 64) {
1887 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
1888 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
1889
1890 // Only say yes if the operation is
1891 // commutative, i.e. we can swap both of the
1892 // operands
1893 const lhs_immediate_ok = switch (tag) {
1894 .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
1895 .sub => false,
1896 else => unreachable,
1897 };
1898 const rhs_immediate_ok = switch (tag) {
1899 .add,
1900 .sub,
1901 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
1902 else => unreachable,
1903 };
1904
1905 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1906 .add => .add_shifted_register,
1907 .sub => .sub_shifted_register,
1908 else => unreachable,
1909 };
1910 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
1911 .add => .add_immediate,
1912 .sub => .sub_immediate,
1913 else => unreachable,
1914 };
1915
1916 if (rhs_immediate_ok) {
1917 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
1918 } else if (lhs_immediate_ok) {
1919 // swap lhs and rhs
1920 return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
1921 } else {
1922 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1923 }
1924 } else {
1925 return self.fail("TODO binary operations on int with bits > 64", .{});
1926 }
1927 },
1928 else => unreachable,
1929 }
1930}
1931
1932fn mul(
1933 self: *Self,
1934 lhs_bind: ReadArg.Bind,
1935 rhs_bind: ReadArg.Bind,
1936 lhs_ty: Type,
1937 rhs_ty: Type,
1938 maybe_inst: ?Air.Inst.Index,
1939) InnerError!MCValue {
1940 const pt = self.pt;
1941 const zcu = pt.zcu;
1942 switch (lhs_ty.zigTypeTag(zcu)) {
1943 .vector => return self.fail("TODO binary operations on vectors", .{}),
1944 .int => {
1945 assert(lhs_ty.eql(rhs_ty, zcu));
1946 const int_info = lhs_ty.intInfo(zcu);
1947 if (int_info.bits <= 64) {
1948 // TODO add optimisations for multiplication
1949 // with immediates, for example a * 2 can be
1950 // lowered to a << 1
1951 return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1952 } else {
1953 return self.fail("TODO binary operations on int with bits > 64", .{});
1954 }
1955 },
1956 else => unreachable,
1957 }
1958}
1959
1960fn divFloat(
1961 self: *Self,
1962 lhs_bind: ReadArg.Bind,
1963 rhs_bind: ReadArg.Bind,
1964 lhs_ty: Type,
1965 rhs_ty: Type,
1966 maybe_inst: ?Air.Inst.Index,
1967) InnerError!MCValue {
1968 _ = lhs_bind;
1969 _ = rhs_bind;
1970 _ = rhs_ty;
1971 _ = maybe_inst;
1972
1973 const pt = self.pt;
1974 const zcu = pt.zcu;
1975 switch (lhs_ty.zigTypeTag(zcu)) {
1976 .float => return self.fail("TODO div_float", .{}),
1977 .vector => return self.fail("TODO div_float on vectors", .{}),
1978 else => unreachable,
1979 }
1980}
1981
1982fn divTrunc(
1983 self: *Self,
1984 lhs_bind: ReadArg.Bind,
1985 rhs_bind: ReadArg.Bind,
1986 lhs_ty: Type,
1987 rhs_ty: Type,
1988 maybe_inst: ?Air.Inst.Index,
1989) InnerError!MCValue {
1990 const pt = self.pt;
1991 const zcu = pt.zcu;
1992 switch (lhs_ty.zigTypeTag(zcu)) {
1993 .float => return self.fail("TODO div on floats", .{}),
1994 .vector => return self.fail("TODO div on vectors", .{}),
1995 .int => {
1996 assert(lhs_ty.eql(rhs_ty, zcu));
1997 const int_info = lhs_ty.intInfo(zcu);
1998 if (int_info.bits <= 64) {
1999 switch (int_info.signedness) {
2000 .signed => {
2001 // TODO optimize integer division by constants
2002 return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2003 },
2004 .unsigned => {
2005 // TODO optimize integer division by constants
2006 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2007 },
2008 }
2009 } else {
2010 return self.fail("TODO integer division for ints with bits > 64", .{});
2011 }
2012 },
2013 else => unreachable,
2014 }
2015}
2016
2017fn divFloor(
2018 self: *Self,
2019 lhs_bind: ReadArg.Bind,
2020 rhs_bind: ReadArg.Bind,
2021 lhs_ty: Type,
2022 rhs_ty: Type,
2023 maybe_inst: ?Air.Inst.Index,
2024) InnerError!MCValue {
2025 const pt = self.pt;
2026 const zcu = pt.zcu;
2027 switch (lhs_ty.zigTypeTag(zcu)) {
2028 .float => return self.fail("TODO div on floats", .{}),
2029 .vector => return self.fail("TODO div on vectors", .{}),
2030 .int => {
2031 assert(lhs_ty.eql(rhs_ty, zcu));
2032 const int_info = lhs_ty.intInfo(zcu);
2033 if (int_info.bits <= 64) {
2034 switch (int_info.signedness) {
2035 .signed => {
2036 return self.fail("TODO div_floor on signed integers", .{});
2037 },
2038 .unsigned => {
2039 // TODO optimize integer division by constants
2040 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2041 },
2042 }
2043 } else {
2044 return self.fail("TODO integer division for ints with bits > 64", .{});
2045 }
2046 },
2047 else => unreachable,
2048 }
2049}
2050
2051fn divExact(
2052 self: *Self,
2053 lhs_bind: ReadArg.Bind,
2054 rhs_bind: ReadArg.Bind,
2055 lhs_ty: Type,
2056 rhs_ty: Type,
2057 maybe_inst: ?Air.Inst.Index,
2058) InnerError!MCValue {
2059 const pt = self.pt;
2060 const zcu = pt.zcu;
2061 switch (lhs_ty.zigTypeTag(zcu)) {
2062 .float => return self.fail("TODO div on floats", .{}),
2063 .vector => return self.fail("TODO div on vectors", .{}),
2064 .int => {
2065 assert(lhs_ty.eql(rhs_ty, zcu));
2066 const int_info = lhs_ty.intInfo(zcu);
2067 if (int_info.bits <= 64) {
2068 switch (int_info.signedness) {
2069 .signed => {
2070 // TODO optimize integer division by constants
2071 return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2072 },
2073 .unsigned => {
2074 // TODO optimize integer division by constants
2075 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2076 },
2077 }
2078 } else {
2079 return self.fail("TODO integer division for ints with bits > 64", .{});
2080 }
2081 },
2082 else => unreachable,
2083 }
2084}
2085
2086fn rem(
2087 self: *Self,
2088 lhs_bind: ReadArg.Bind,
2089 rhs_bind: ReadArg.Bind,
2090 lhs_ty: Type,
2091 rhs_ty: Type,
2092 maybe_inst: ?Air.Inst.Index,
2093) InnerError!MCValue {
2094 _ = maybe_inst;
2095
2096 const pt = self.pt;
2097 const zcu = pt.zcu;
2098 switch (lhs_ty.zigTypeTag(zcu)) {
2099 .float => return self.fail("TODO rem/zcu on floats", .{}),
2100 .vector => return self.fail("TODO rem/zcu on vectors", .{}),
2101 .int => {
2102 assert(lhs_ty.eql(rhs_ty, zcu));
2103 const int_info = lhs_ty.intInfo(zcu);
2104 if (int_info.bits <= 64) {
2105 var lhs_reg: Register = undefined;
2106 var rhs_reg: Register = undefined;
2107 var quotient_reg: Register = undefined;
2108 var remainder_reg: Register = undefined;
2109
2110 const read_args = [_]ReadArg{
2111 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2112 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
2113 };
2114 const write_args = [_]WriteArg{
2115 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &quotient_reg },
2116 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &remainder_reg },
2117 };
2118 try self.allocRegs(
2119 &read_args,
2120 &write_args,
2121 null,
2122 );
2123
2124 _ = try self.addInst(.{
2125 .tag = switch (int_info.signedness) {
2126 .signed => .sdiv,
2127 .unsigned => .udiv,
2128 },
2129 .data = .{ .rrr = .{
2130 .rd = quotient_reg,
2131 .rn = lhs_reg,
2132 .rm = rhs_reg,
2133 } },
2134 });
2135
2136 _ = try self.addInst(.{
2137 .tag = .msub,
2138 .data = .{ .rrrr = .{
2139 .rd = remainder_reg,
2140 .rn = quotient_reg,
2141 .rm = rhs_reg,
2142 .ra = lhs_reg,
2143 } },
2144 });
2145
2146 return MCValue{ .register = remainder_reg };
2147 } else {
2148 return self.fail("TODO rem/zcu for integers with bits > 64", .{});
2149 }
2150 },
2151 else => unreachable,
2152 }
2153}
2154
2155fn modulo(
2156 self: *Self,
2157 lhs_bind: ReadArg.Bind,
2158 rhs_bind: ReadArg.Bind,
2159 lhs_ty: Type,
2160 rhs_ty: Type,
2161 maybe_inst: ?Air.Inst.Index,
2162) InnerError!MCValue {
2163 _ = lhs_bind;
2164 _ = rhs_bind;
2165 _ = rhs_ty;
2166 _ = maybe_inst;
2167
2168 const pt = self.pt;
2169 const zcu = pt.zcu;
2170 switch (lhs_ty.zigTypeTag(zcu)) {
2171 .float => return self.fail("TODO zcu on floats", .{}),
2172 .vector => return self.fail("TODO zcu on vectors", .{}),
2173 .int => return self.fail("TODO zcu on ints", .{}),
2174 else => unreachable,
2175 }
2176}
2177
2178fn wrappingArithmetic(
2179 self: *Self,
2180 tag: Air.Inst.Tag,
2181 lhs_bind: ReadArg.Bind,
2182 rhs_bind: ReadArg.Bind,
2183 lhs_ty: Type,
2184 rhs_ty: Type,
2185 maybe_inst: ?Air.Inst.Index,
2186) InnerError!MCValue {
2187 const pt = self.pt;
2188 const zcu = pt.zcu;
2189 switch (lhs_ty.zigTypeTag(zcu)) {
2190 .vector => return self.fail("TODO binary operations on vectors", .{}),
2191 .int => {
2192 const int_info = lhs_ty.intInfo(zcu);
2193 if (int_info.bits <= 64) {
2194 // Generate an add/sub/mul
2195 const result: MCValue = switch (tag) {
2196 .add_wrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2197 .sub_wrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2198 .mul_wrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2199 else => unreachable,
2200 };
2201
2202 // Truncate if necessary
2203 const result_reg = result.register;
2204 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
2205 return result;
2206 } else {
2207 return self.fail("TODO binary operations on integers > u64/i64", .{});
2208 }
2209 },
2210 else => unreachable,
2211 }
2212}
2213
2214fn bitwise(
2215 self: *Self,
2216 tag: Air.Inst.Tag,
2217 lhs_bind: ReadArg.Bind,
2218 rhs_bind: ReadArg.Bind,
2219 lhs_ty: Type,
2220 rhs_ty: Type,
2221 maybe_inst: ?Air.Inst.Index,
2222) InnerError!MCValue {
2223 const pt = self.pt;
2224 const zcu = pt.zcu;
2225 switch (lhs_ty.zigTypeTag(zcu)) {
2226 .vector => return self.fail("TODO binary operations on vectors", .{}),
2227 .int => {
2228 assert(lhs_ty.eql(rhs_ty, zcu));
2229 const int_info = lhs_ty.intInfo(zcu);
2230 if (int_info.bits <= 64) {
2231 // TODO implement bitwise operations with immediates
2232 const mir_tag: Mir.Inst.Tag = switch (tag) {
2233 .bit_and => .and_shifted_register,
2234 .bit_or => .orr_shifted_register,
2235 .xor => .eor_shifted_register,
2236 else => unreachable,
2237 };
2238
2239 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2240 } else {
2241 return self.fail("TODO binary operations on int with bits > 64", .{});
2242 }
2243 },
2244 else => unreachable,
2245 }
2246}
2247
2248fn shiftExact(
2249 self: *Self,
2250 tag: Air.Inst.Tag,
2251 lhs_bind: ReadArg.Bind,
2252 rhs_bind: ReadArg.Bind,
2253 lhs_ty: Type,
2254 rhs_ty: Type,
2255 maybe_inst: ?Air.Inst.Index,
2256) InnerError!MCValue {
2257 const pt = self.pt;
2258 const zcu = pt.zcu;
2259 switch (lhs_ty.zigTypeTag(zcu)) {
2260 .vector => if (!rhs_ty.isVector(zcu))
2261 return self.fail("TODO vector shift with scalar rhs", .{})
2262 else
2263 return self.fail("TODO binary operations on vectors", .{}),
2264 .int => {
2265 const int_info = lhs_ty.intInfo(zcu);
2266 if (int_info.bits <= 64) {
2267 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2268
2269 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
2270 .shl_exact => .lsl_register,
2271 .shr_exact => switch (int_info.signedness) {
2272 .signed => Mir.Inst.Tag.asr_register,
2273 .unsigned => Mir.Inst.Tag.lsr_register,
2274 },
2275 else => unreachable,
2276 };
2277 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
2278 .shl_exact => .lsl_immediate,
2279 .shr_exact => switch (int_info.signedness) {
2280 .signed => Mir.Inst.Tag.asr_immediate,
2281 .unsigned => Mir.Inst.Tag.lsr_immediate,
2282 },
2283 else => unreachable,
2284 };
2285
2286 if (rhs_immediate) |imm| {
2287 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst);
2288 } else {
2289 // We intentionally pass lhs_ty here in order to
2290 // prevent using the 32-bit register alias when
2291 // lhs_ty is > 32 bits.
2292 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst);
2293 }
2294 } else {
2295 return self.fail("TODO binary operations on int with bits > 64", .{});
2296 }
2297 },
2298 else => unreachable,
2299 }
2300}
2301
2302fn shiftNormal(
2303 self: *Self,
2304 tag: Air.Inst.Tag,
2305 lhs_bind: ReadArg.Bind,
2306 rhs_bind: ReadArg.Bind,
2307 lhs_ty: Type,
2308 rhs_ty: Type,
2309 maybe_inst: ?Air.Inst.Index,
2310) InnerError!MCValue {
2311 const pt = self.pt;
2312 const zcu = pt.zcu;
2313 switch (lhs_ty.zigTypeTag(zcu)) {
2314 .vector => if (!rhs_ty.isVector(zcu))
2315 return self.fail("TODO vector shift with scalar rhs", .{})
2316 else
2317 return self.fail("TODO binary operations on vectors", .{}),
2318 .int => {
2319 const int_info = lhs_ty.intInfo(zcu);
2320 if (int_info.bits <= 64) {
2321 // Generate a shl_exact/shr_exact
2322 const result: MCValue = switch (tag) {
2323 .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2324 .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2325 else => unreachable,
2326 };
2327
2328 // Truncate if necessary
2329 switch (tag) {
2330 .shr => return result,
2331 .shl => {
2332 const result_reg = result.register;
2333 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
2334 return result;
2335 },
2336 else => unreachable,
2337 }
2338 } else {
2339 return self.fail("TODO binary operations on integers > u64/i64", .{});
2340 }
2341 },
2342 else => unreachable,
2343 }
2344}
2345
2346fn booleanOp(
2347 self: *Self,
2348 tag: Air.Inst.Tag,
2349 lhs_bind: ReadArg.Bind,
2350 rhs_bind: ReadArg.Bind,
2351 lhs_ty: Type,
2352 rhs_ty: Type,
2353 maybe_inst: ?Air.Inst.Index,
2354) InnerError!MCValue {
2355 const pt = self.pt;
2356 const zcu = pt.zcu;
2357 switch (lhs_ty.zigTypeTag(zcu)) {
2358 .bool => {
2359 assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema
2360 assert((try rhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema
2361
2362 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
2363 .bool_and => .and_shifted_register,
2364 .bool_or => .orr_shifted_register,
2365 else => unreachable,
2366 };
2367
2368 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2369 },
2370 else => unreachable,
2371 }
2372}
2373
2374fn ptrArithmetic(
2375 self: *Self,
2376 tag: Air.Inst.Tag,
2377 lhs_bind: ReadArg.Bind,
2378 rhs_bind: ReadArg.Bind,
2379 lhs_ty: Type,
2380 rhs_ty: Type,
2381 maybe_inst: ?Air.Inst.Index,
2382) InnerError!MCValue {
2383 const pt = self.pt;
2384 const zcu = pt.zcu;
2385 switch (lhs_ty.zigTypeTag(zcu)) {
2386 .pointer => {
2387 assert(rhs_ty.eql(Type.usize, zcu));
2388
2389 const ptr_ty = lhs_ty;
2390 const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
2391 .one => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type
2392 else => ptr_ty.childType(zcu),
2393 };
2394 const elem_size = elem_ty.abiSize(zcu);
2395
2396 const base_tag: Air.Inst.Tag = switch (tag) {
2397 .ptr_add => .add,
2398 .ptr_sub => .sub,
2399 else => unreachable,
2400 };
2401
2402 if (elem_size == 1) {
2403 return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst);
2404 } else {
2405 // convert the offset into a byte offset by
2406 // multiplying it with elem_size
2407 const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
2408
2409 const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
2410 const offset_bind = ReadArg.Bind{ .mcv = offset };
2411
2412 const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null);
2413 return addr;
2414 }
2415 },
2416 else => unreachable,
2417 }
2418}
2419
2420fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void {
2421 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2422 const lhs_ty = self.typeOf(bin_op.lhs);
2423 const rhs_ty = self.typeOf(bin_op.rhs);
2424
2425 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2426 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2427 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2428
2429 break :result switch (tag) {
2430 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2431 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2432
2433 .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2434
2435 .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2436
2437 .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2438
2439 .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2440
2441 .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2442
2443 .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2444
2445 .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2446
2447 .add_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2448 .sub_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2449 .mul_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2450
2451 .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2452 .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2453 .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2454
2455 .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2456 .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2457
2458 .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2459 .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2460
2461 .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2462 .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2463
2464 else => unreachable,
2465 };
2466 };
2467 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2468}
2469
2470fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) InnerError!void {
2471 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2472 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2473 const lhs_ty = self.typeOf(bin_op.lhs);
2474 const rhs_ty = self.typeOf(bin_op.rhs);
2475
2476 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2477 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2478 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2479
2480 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
2481 };
2482 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2483}
2484
2485fn airAddSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2486 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2487 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
2488 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2489}
2490
2491fn airSubSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2492 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2493 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
2494 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2495}
2496
2497fn airMulSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2498 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2499 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
2500 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2501}
2502
2503fn airOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2504 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
2505 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2506 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2507 const pt = self.pt;
2508 const zcu = pt.zcu;
2509 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2510 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2511 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2512 const lhs_ty = self.typeOf(extra.lhs);
2513 const rhs_ty = self.typeOf(extra.rhs);
2514
2515 const tuple_ty = self.typeOfIndex(inst);
2516 const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
2517 const tuple_align = tuple_ty.abiAlignment(zcu);
2518 const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
2519
2520 switch (lhs_ty.zigTypeTag(zcu)) {
2521 .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
2522 .int => {
2523 assert(lhs_ty.eql(rhs_ty, zcu));
2524 const int_info = lhs_ty.intInfo(zcu);
2525 switch (int_info.bits) {
2526 1...31, 33...63 => {
2527 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
2528
2529 try self.spillCompareFlagsIfOccupied();
2530 self.compare_flags_inst = null;
2531
2532 const base_tag: Air.Inst.Tag = switch (tag) {
2533 .add_with_overflow => .add,
2534 .sub_with_overflow => .sub,
2535 else => unreachable,
2536 };
2537 const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
2538 const dest_reg = dest.register;
2539 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
2540 defer self.register_manager.unlockReg(dest_reg_lock);
2541
2542 const raw_truncated_reg = try self.register_manager.allocReg(null, gp);
2543 const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty);
2544 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
2545 defer self.register_manager.unlockReg(truncated_reg_lock);
2546
2547 // sbfx/ubfx truncated, dest, #0, #bits
2548 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
2549
2550 // cmp dest, truncated
2551 _ = try self.addInst(.{
2552 .tag = .cmp_shifted_register,
2553 .data = .{ .rr_imm6_shift = .{
2554 .rn = dest_reg,
2555 .rm = truncated_reg,
2556 .imm6 = 0,
2557 .shift = .lsl,
2558 } },
2559 });
2560
2561 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2562 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
2563
2564 break :result MCValue{ .stack_offset = stack_offset };
2565 },
2566 32, 64 => {
2567 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
2568 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2569
2570 // Only say yes if the operation is
2571 // commutative, i.e. we can swap both of the
2572 // operands
2573 const lhs_immediate_ok = switch (tag) {
2574 .add_with_overflow => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
2575 .sub_with_overflow => false,
2576 else => unreachable,
2577 };
2578 const rhs_immediate_ok = switch (tag) {
2579 .add_with_overflow,
2580 .sub_with_overflow,
2581 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
2582 else => unreachable,
2583 };
2584
2585 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
2586 .add_with_overflow => .adds_shifted_register,
2587 .sub_with_overflow => .subs_shifted_register,
2588 else => unreachable,
2589 };
2590 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
2591 .add_with_overflow => .adds_immediate,
2592 .sub_with_overflow => .subs_immediate,
2593 else => unreachable,
2594 };
2595
2596 try self.spillCompareFlagsIfOccupied();
2597 self.compare_flags_inst = inst;
2598
2599 const dest = blk: {
2600 if (rhs_immediate_ok) {
2601 break :blk try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null);
2602 } else if (lhs_immediate_ok) {
2603 // swap lhs and rhs
2604 break :blk try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null);
2605 } else {
2606 break :blk try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
2607 }
2608 };
2609
2610 const flag: bits.Instruction.Condition = switch (int_info.signedness) {
2611 .unsigned => switch (tag) {
2612 .add_with_overflow => bits.Instruction.Condition.cs,
2613 .sub_with_overflow => bits.Instruction.Condition.cc,
2614 else => unreachable,
2615 },
2616 .signed => .vs,
2617 };
2618 break :result MCValue{ .register_with_overflow = .{
2619 .reg = dest.register,
2620 .flag = flag,
2621 } };
2622 },
2623 else => return self.fail("TODO overflow operations on integers > u32/i32", .{}),
2624 }
2625 },
2626 else => unreachable,
2627 }
2628 };
2629 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2630}
2631
2632fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2633 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2634 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2635 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
2636 const zcu = self.pt.zcu;
2637 const result: MCValue = result: {
2638 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2639 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2640 const lhs_ty = self.typeOf(extra.lhs);
2641 const rhs_ty = self.typeOf(extra.rhs);
2642
2643 const tuple_ty = self.typeOfIndex(inst);
2644 const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu)));
2645 const tuple_align = tuple_ty.abiAlignment(zcu);
2646 const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu)));
2647
2648 switch (lhs_ty.zigTypeTag(zcu)) {
2649 .vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
2650 .int => {
2651 assert(lhs_ty.eql(rhs_ty, zcu));
2652 const int_info = lhs_ty.intInfo(zcu);
2653 if (int_info.bits <= 32) {
2654 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
2655
2656 try self.spillCompareFlagsIfOccupied();
2657
2658 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
2659 .signed => .smull,
2660 .unsigned => .umull,
2661 };
2662
2663 const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
2664 const dest_reg = dest.register;
2665 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
2666 defer self.register_manager.unlockReg(dest_reg_lock);
2667
2668 const truncated_reg = try self.register_manager.allocReg(null, gp);
2669 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
2670 defer self.register_manager.unlockReg(truncated_reg_lock);
2671
2672 try self.truncRegister(
2673 dest_reg.toW(),
2674 truncated_reg.toW(),
2675 int_info.signedness,
2676 int_info.bits,
2677 );
2678
2679 switch (int_info.signedness) {
2680 .signed => {
2681 _ = try self.addInst(.{
2682 .tag = .cmp_extended_register,
2683 .data = .{ .rr_extend_shift = .{
2684 .rn = dest_reg.toX(),
2685 .rm = truncated_reg.toW(),
2686 .ext_type = .sxtw,
2687 .imm3 = 0,
2688 } },
2689 });
2690 },
2691 .unsigned => {
2692 _ = try self.addInst(.{
2693 .tag = .cmp_extended_register,
2694 .data = .{ .rr_extend_shift = .{
2695 .rn = dest_reg.toX(),
2696 .rm = truncated_reg.toW(),
2697 .ext_type = .uxtw,
2698 .imm3 = 0,
2699 } },
2700 });
2701 },
2702 }
2703
2704 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2705 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
2706
2707 break :result MCValue{ .stack_offset = stack_offset };
2708 } else if (int_info.bits <= 64) {
2709 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
2710
2711 try self.spillCompareFlagsIfOccupied();
2712
2713 var lhs_reg: Register = undefined;
2714 var rhs_reg: Register = undefined;
2715 var dest_reg: Register = undefined;
2716 var dest_high_reg: Register = undefined;
2717 var truncated_reg: Register = undefined;
2718
2719 const read_args = [_]ReadArg{
2720 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2721 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
2722 };
2723 const write_args = [_]WriteArg{
2724 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2725 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_high_reg },
2726 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg },
2727 };
2728 try self.allocRegs(
2729 &read_args,
2730 &write_args,
2731 null,
2732 );
2733
2734 switch (int_info.signedness) {
2735 .signed => {
2736 // mul dest, lhs, rhs
2737 _ = try self.addInst(.{
2738 .tag = .mul,
2739 .data = .{ .rrr = .{
2740 .rd = dest_reg,
2741 .rn = lhs_reg,
2742 .rm = rhs_reg,
2743 } },
2744 });
2745
2746 // smulh dest_high, lhs, rhs
2747 _ = try self.addInst(.{
2748 .tag = .smulh,
2749 .data = .{ .rrr = .{
2750 .rd = dest_high_reg,
2751 .rn = lhs_reg,
2752 .rm = rhs_reg,
2753 } },
2754 });
2755
2756 // cmp dest_high, dest, asr #63
2757 _ = try self.addInst(.{
2758 .tag = .cmp_shifted_register,
2759 .data = .{ .rr_imm6_shift = .{
2760 .rn = dest_high_reg,
2761 .rm = dest_reg,
2762 .imm6 = 63,
2763 .shift = .asr,
2764 } },
2765 });
2766
2767 const shift: u6 = @as(u6, @intCast(@as(u7, 64) - @as(u7, @intCast(int_info.bits))));
2768 if (shift > 0) {
2769 // lsl dest_high, dest, #shift
2770 _ = try self.addInst(.{
2771 .tag = .lsl_immediate,
2772 .data = .{ .rr_shift = .{
2773 .rd = dest_high_reg,
2774 .rn = dest_reg,
2775 .shift = shift,
2776 } },
2777 });
2778
2779 // cmp dest, dest_high, #shift
2780 _ = try self.addInst(.{
2781 .tag = .cmp_shifted_register,
2782 .data = .{ .rr_imm6_shift = .{
2783 .rn = dest_reg,
2784 .rm = dest_high_reg,
2785 .imm6 = shift,
2786 .shift = .asr,
2787 } },
2788 });
2789 }
2790 },
2791 .unsigned => {
2792 // umulh dest_high, lhs, rhs
2793 _ = try self.addInst(.{
2794 .tag = .umulh,
2795 .data = .{ .rrr = .{
2796 .rd = dest_high_reg,
2797 .rn = lhs_reg,
2798 .rm = rhs_reg,
2799 } },
2800 });
2801
2802 // mul dest, lhs, rhs
2803 _ = try self.addInst(.{
2804 .tag = .mul,
2805 .data = .{ .rrr = .{
2806 .rd = dest_reg,
2807 .rn = lhs_reg,
2808 .rm = rhs_reg,
2809 } },
2810 });
2811
2812 _ = try self.addInst(.{
2813 .tag = .cmp_immediate,
2814 .data = .{ .r_imm12_sh = .{
2815 .rn = dest_high_reg,
2816 .imm12 = 0,
2817 } },
2818 });
2819
2820 if (int_info.bits < 64) {
2821 // lsr dest_high, dest, #shift
2822 _ = try self.addInst(.{
2823 .tag = .lsr_immediate,
2824 .data = .{ .rr_shift = .{
2825 .rd = dest_high_reg,
2826 .rn = dest_reg,
2827 .shift = @as(u6, @intCast(int_info.bits)),
2828 } },
2829 });
2830
2831 _ = try self.addInst(.{
2832 .tag = .cmp_immediate,
2833 .data = .{ .r_imm12_sh = .{
2834 .rn = dest_high_reg,
2835 .imm12 = 0,
2836 } },
2837 });
2838 }
2839 },
2840 }
2841
2842 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
2843
2844 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2845 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
2846
2847 break :result MCValue{ .stack_offset = stack_offset };
2848 } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{});
2849 },
2850 else => unreachable,
2851 }
2852 };
2853 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2854}
2855
2856fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
2857 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2858 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2859 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
2860 const pt = self.pt;
2861 const zcu = pt.zcu;
2862 const result: MCValue = result: {
2863 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2864 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2865 const lhs_ty = self.typeOf(extra.lhs);
2866 const rhs_ty = self.typeOf(extra.rhs);
2867
2868 const tuple_ty = self.typeOfIndex(inst);
2869 const tuple_size = @as(u32, @intCast(tuple_ty.abiSize(zcu)));
2870 const tuple_align = tuple_ty.abiAlignment(zcu);
2871 const overflow_bit_offset = @as(u32, @intCast(tuple_ty.structFieldOffset(1, zcu)));
2872
2873 switch (lhs_ty.zigTypeTag(zcu)) {
2874 .vector => if (!rhs_ty.isVector(zcu))
2875 return self.fail("TODO implement vector shl_with_overflow with scalar rhs", .{})
2876 else
2877 return self.fail("TODO implement shl_with_overflow for vectors", .{}),
2878 .int => {
2879 const int_info = lhs_ty.intInfo(zcu);
2880 if (int_info.bits <= 64) {
2881 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
2882
2883 try self.spillCompareFlagsIfOccupied();
2884
2885 var lhs_reg: Register = undefined;
2886 var rhs_reg: Register = undefined;
2887 var dest_reg: Register = undefined;
2888 var reconstructed_reg: Register = undefined;
2889
2890 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2891 if (rhs_immediate) |imm| {
2892 const read_args = [_]ReadArg{
2893 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2894 };
2895 const write_args = [_]WriteArg{
2896 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2897 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
2898 };
2899 try self.allocRegs(
2900 &read_args,
2901 &write_args,
2902 null,
2903 );
2904
2905 // lsl dest, lhs, rhs
2906 _ = try self.addInst(.{
2907 .tag = .lsl_immediate,
2908 .data = .{ .rr_shift = .{
2909 .rd = dest_reg,
2910 .rn = lhs_reg,
2911 .shift = @as(u6, @intCast(imm)),
2912 } },
2913 });
2914
2915 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
2916
2917 // asr/lsr reconstructed, dest, rhs
2918 _ = try self.addInst(.{
2919 .tag = switch (int_info.signedness) {
2920 .signed => Mir.Inst.Tag.asr_immediate,
2921 .unsigned => Mir.Inst.Tag.lsr_immediate,
2922 },
2923 .data = .{ .rr_shift = .{
2924 .rd = reconstructed_reg,
2925 .rn = dest_reg,
2926 .shift = @as(u6, @intCast(imm)),
2927 } },
2928 });
2929 } else {
2930 const read_args = [_]ReadArg{
2931 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2932 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
2933 };
2934 const write_args = [_]WriteArg{
2935 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2936 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
2937 };
2938 try self.allocRegs(
2939 &read_args,
2940 &write_args,
2941 null,
2942 );
2943
2944 // lsl dest, lhs, rhs
2945 _ = try self.addInst(.{
2946 .tag = .lsl_register,
2947 .data = .{ .rrr = .{
2948 .rd = dest_reg,
2949 .rn = lhs_reg,
2950 .rm = rhs_reg,
2951 } },
2952 });
2953
2954 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
2955
2956 // asr/lsr reconstructed, dest, rhs
2957 _ = try self.addInst(.{
2958 .tag = switch (int_info.signedness) {
2959 .signed => Mir.Inst.Tag.asr_register,
2960 .unsigned => Mir.Inst.Tag.lsr_register,
2961 },
2962 .data = .{ .rrr = .{
2963 .rd = reconstructed_reg,
2964 .rn = dest_reg,
2965 .rm = rhs_reg,
2966 } },
2967 });
2968 }
2969
2970 // cmp lhs, reconstructed
2971 _ = try self.addInst(.{
2972 .tag = .cmp_shifted_register,
2973 .data = .{ .rr_imm6_shift = .{
2974 .rn = lhs_reg,
2975 .rm = reconstructed_reg,
2976 .imm6 = 0,
2977 .shift = .lsl,
2978 } },
2979 });
2980
2981 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });
2982 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
2983
2984 break :result MCValue{ .stack_offset = stack_offset };
2985 } else {
2986 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
2987 }
2988 },
2989 else => unreachable,
2990 }
2991 };
2992 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2993}
2994
2995fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
2996 const zcu = self.pt.zcu;
2997 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2998 const result: MCValue = if (self.liveness.isUnused(inst))
2999 .dead
3000 else if (self.typeOf(bin_op.lhs).isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu))
3001 return self.fail("TODO implement vector shl_sat with scalar rhs for {}", .{self.target.cpu.arch})
3002 else
3003 return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
3004 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3005}
3006
3007fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3008 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3009 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3010 const optional_ty = self.typeOf(ty_op.operand);
3011 const mcv = try self.resolveInst(ty_op.operand);
3012 break :result try self.optionalPayload(inst, mcv, optional_ty);
3013 };
3014 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3015}
3016
3017fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: Type) !MCValue {
3018 const pt = self.pt;
3019 const zcu = pt.zcu;
3020 const payload_ty = optional_ty.optionalChild(zcu);
3021 if (!payload_ty.hasRuntimeBits(zcu)) return MCValue.none;
3022 if (optional_ty.isPtrLikeOptional(zcu)) {
3023 // TODO should we reuse the operand here?
3024 const raw_reg = try self.register_manager.allocReg(inst, gp);
3025 const reg = self.registerAlias(raw_reg, payload_ty);
3026 try self.genSetReg(payload_ty, reg, mcv);
3027 return MCValue{ .register = reg };
3028 }
3029
3030 switch (mcv) {
3031 .register => {
3032 // TODO should we reuse the operand here?
3033 const raw_reg = try self.register_manager.allocReg(inst, gp);
3034 const dest_reg = raw_reg.toX();
3035
3036 try self.genSetReg(payload_ty, dest_reg, mcv);
3037 return MCValue{ .register = self.registerAlias(dest_reg, payload_ty) };
3038 },
3039 .stack_argument_offset, .stack_offset, .memory => return mcv,
3040 else => unreachable, // invalid MCValue for an error union
3041 }
3042}
3043
3044fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3045 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
3047 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3048}
3049
3050fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3051 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
3053 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3054}
3055
3056/// Given an error union, returns the error
3057fn errUnionErr(
3058 self: *Self,
3059 error_union_bind: ReadArg.Bind,
3060 error_union_ty: Type,
3061 maybe_inst: ?Air.Inst.Index,
3062) !MCValue {
3063 const pt = self.pt;
3064 const zcu = pt.zcu;
3065 const err_ty = error_union_ty.errorUnionSet(zcu);
3066 const payload_ty = error_union_ty.errorUnionPayload(zcu);
3067 if (err_ty.errorSetIsEmpty(zcu)) {
3068 return MCValue{ .immediate = 0 };
3069 }
3070 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
3071 return try error_union_bind.resolveToMcv(self);
3072 }
3073
3074 const err_offset: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu));
3075 switch (try error_union_bind.resolveToMcv(self)) {
3076 .register => {
3077 var operand_reg: Register = undefined;
3078 var dest_reg: Register = undefined;
3079
3080 const read_args = [_]ReadArg{
3081 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
3082 };
3083 const write_args = [_]WriteArg{
3084 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3085 };
3086 try self.allocRegs(
3087 &read_args,
3088 &write_args,
3089 if (maybe_inst) |inst| .{
3090 .corresponding_inst = inst,
3091 .operand_mapping = &.{0},
3092 } else null,
3093 );
3094
3095 const err_bit_offset = err_offset * 8;
3096 const err_bit_size = @as(u32, @intCast(err_ty.abiSize(zcu))) * 8;
3097
3098 _ = try self.addInst(.{
3099 .tag = .ubfx, // errors are unsigned integers
3100 .data = .{
3101 .rr_lsb_width = .{
3102 // Set both registers to the X variant to get the full width
3103 .rd = dest_reg.toX(),
3104 .rn = operand_reg.toX(),
3105 .lsb = @as(u6, @intCast(err_bit_offset)),
3106 .width = @as(u7, @intCast(err_bit_size)),
3107 },
3108 },
3109 });
3110
3111 return MCValue{ .register = dest_reg };
3112 },
3113 .stack_argument_offset => |off| {
3114 return MCValue{ .stack_argument_offset = off + err_offset };
3115 },
3116 .stack_offset => |off| {
3117 return MCValue{ .stack_offset = off - err_offset };
3118 },
3119 .memory => |addr| {
3120 return MCValue{ .memory = addr + err_offset };
3121 },
3122 else => unreachable, // invalid MCValue for an error union
3123 }
3124}
3125
3126fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3127 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3128 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3129 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
3130 const error_union_ty = self.typeOf(ty_op.operand);
3131
3132 break :result try self.errUnionErr(error_union_bind, error_union_ty, inst);
3133 };
3134 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3135}
3136
3137/// Given an error union, returns the payload
3138fn errUnionPayload(
3139 self: *Self,
3140 error_union_bind: ReadArg.Bind,
3141 error_union_ty: Type,
3142 maybe_inst: ?Air.Inst.Index,
3143) !MCValue {
3144 const pt = self.pt;
3145 const zcu = pt.zcu;
3146 const err_ty = error_union_ty.errorUnionSet(zcu);
3147 const payload_ty = error_union_ty.errorUnionPayload(zcu);
3148 if (err_ty.errorSetIsEmpty(zcu)) {
3149 return try error_union_bind.resolveToMcv(self);
3150 }
3151 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
3152 return MCValue.none;
3153 }
3154
3155 const payload_offset = @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu)));
3156 switch (try error_union_bind.resolveToMcv(self)) {
3157 .register => {
3158 var operand_reg: Register = undefined;
3159 var dest_reg: Register = undefined;
3160
3161 const read_args = [_]ReadArg{
3162 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
3163 };
3164 const write_args = [_]WriteArg{
3165 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3166 };
3167 try self.allocRegs(
3168 &read_args,
3169 &write_args,
3170 if (maybe_inst) |inst| .{
3171 .corresponding_inst = inst,
3172 .operand_mapping = &.{0},
3173 } else null,
3174 );
3175
3176 const payload_bit_offset = payload_offset * 8;
3177 const payload_bit_size = @as(u32, @intCast(payload_ty.abiSize(zcu))) * 8;
3178
3179 _ = try self.addInst(.{
3180 .tag = if (payload_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx,
3181 .data = .{
3182 .rr_lsb_width = .{
3183 // Set both registers to the X variant to get the full width
3184 .rd = dest_reg.toX(),
3185 .rn = operand_reg.toX(),
3186 .lsb = @as(u5, @intCast(payload_bit_offset)),
3187 .width = @as(u6, @intCast(payload_bit_size)),
3188 },
3189 },
3190 });
3191
3192 return MCValue{ .register = dest_reg };
3193 },
3194 .stack_argument_offset => |off| {
3195 return MCValue{ .stack_argument_offset = off + payload_offset };
3196 },
3197 .stack_offset => |off| {
3198 return MCValue{ .stack_offset = off - payload_offset };
3199 },
3200 .memory => |addr| {
3201 return MCValue{ .memory = addr + payload_offset };
3202 },
3203 else => unreachable, // invalid MCValue for an error union
3204 }
3205}
3206
3207fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3208 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3209 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3210 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
3211 const error_union_ty = self.typeOf(ty_op.operand);
3212
3213 break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst);
3214 };
3215 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3216}
3217
3218// *(E!T) -> E
3219fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3220 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3221 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
3222 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3223}
3224
3225// *(E!T) -> *T
3226fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3227 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3228 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
3229 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3230}
3231
3232fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3233 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3234 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
3235 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3236}
3237
3238fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
3239 const result: MCValue = if (self.liveness.isUnused(inst))
3240 .dead
3241 else
3242 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
3243 return self.finishAir(inst, result, .{ .none, .none, .none });
3244}
3245
3246fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) InnerError!void {
3247 _ = inst;
3248 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
3249}
3250
3251fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) InnerError!void {
3252 _ = inst;
3253 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
3254}
3255
3256fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void {
3257 const pt = self.pt;
3258 const zcu = pt.zcu;
3259 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3260
3261 if (self.liveness.isUnused(inst)) {
3262 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
3263 }
3264
3265 const result: MCValue = result: {
3266 const payload_ty = self.typeOf(ty_op.operand);
3267 if (!payload_ty.hasRuntimeBits(zcu)) {
3268 break :result MCValue{ .immediate = 1 };
3269 }
3270
3271 const optional_ty = self.typeOfIndex(inst);
3272 const operand = try self.resolveInst(ty_op.operand);
3273 const operand_lock: ?RegisterLock = switch (operand) {
3274 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
3275 else => null,
3276 };
3277 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
3278
3279 if (optional_ty.isPtrLikeOptional(zcu)) {
3280 // TODO should we check if we can reuse the operand?
3281 const raw_reg = try self.register_manager.allocReg(inst, gp);
3282 const reg = self.registerAlias(raw_reg, payload_ty);
3283 try self.genSetReg(payload_ty, raw_reg, operand);
3284 break :result MCValue{ .register = reg };
3285 }
3286
3287 const optional_abi_size: u32 = @intCast(optional_ty.abiSize(zcu));
3288 const optional_abi_align = optional_ty.abiAlignment(zcu);
3289 const offset: u32 = @intCast(payload_ty.abiSize(zcu));
3290
3291 const stack_offset = try self.allocMem(optional_abi_size, optional_abi_align, inst);
3292 try self.genSetStack(payload_ty, stack_offset, operand);
3293 try self.genSetStack(Type.bool, stack_offset - offset, .{ .immediate = 1 });
3294
3295 break :result MCValue{ .stack_offset = stack_offset };
3296 };
3297
3298 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3299}
3300
3301/// T to E!T
3302fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3303 const pt = self.pt;
3304 const zcu = pt.zcu;
3305 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3306 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3307 const error_union_ty = ty_op.ty.toType();
3308 const error_ty = error_union_ty.errorUnionSet(zcu);
3309 const payload_ty = error_union_ty.errorUnionPayload(zcu);
3310 const operand = try self.resolveInst(ty_op.operand);
3311 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand;
3312
3313 const abi_size = @as(u32, @intCast(error_union_ty.abiSize(zcu)));
3314 const abi_align = error_union_ty.abiAlignment(zcu);
3315 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
3316 const payload_off = errUnionPayloadOffset(payload_ty, zcu);
3317 const err_off = errUnionErrorOffset(payload_ty, zcu);
3318 try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), operand);
3319 try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), .{ .immediate = 0 });
3320
3321 break :result MCValue{ .stack_offset = stack_offset };
3322 };
3323 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3324}
3325
3326/// E to E!T
3327fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3328 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3329 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3330 const pt = self.pt;
3331 const zcu = pt.zcu;
3332 const error_union_ty = ty_op.ty.toType();
3333 const error_ty = error_union_ty.errorUnionSet(zcu);
3334 const payload_ty = error_union_ty.errorUnionPayload(zcu);
3335 const operand = try self.resolveInst(ty_op.operand);
3336 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand;
3337
3338 const abi_size = @as(u32, @intCast(error_union_ty.abiSize(zcu)));
3339 const abi_align = error_union_ty.abiAlignment(zcu);
3340 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
3341 const payload_off = errUnionPayloadOffset(payload_ty, zcu);
3342 const err_off = errUnionErrorOffset(payload_ty, zcu);
3343 try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), operand);
3344 try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), .undef);
3345
3346 break :result MCValue{ .stack_offset = stack_offset };
3347 };
3348 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3349}
3350
3351fn slicePtr(mcv: MCValue) MCValue {
3352 switch (mcv) {
3353 .dead, .unreach, .none => unreachable,
3354 .register => unreachable, // a slice doesn't fit in one register
3355 .stack_argument_offset => |off| {
3356 return MCValue{ .stack_argument_offset = off };
3357 },
3358 .stack_offset => |off| {
3359 return MCValue{ .stack_offset = off };
3360 },
3361 .memory => |addr| {
3362 return MCValue{ .memory = addr };
3363 },
3364 else => unreachable, // invalid MCValue for a slice
3365 }
3366}
3367
3368fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3369 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3370 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3371 const mcv = try self.resolveInst(ty_op.operand);
3372 break :result slicePtr(mcv);
3373 };
3374 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3375}
3376
3377fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
3378 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3379 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3380 const ptr_bits = 64;
3381 const ptr_bytes = @divExact(ptr_bits, 8);
3382 const mcv = try self.resolveInst(ty_op.operand);
3383 switch (mcv) {
3384 .dead, .unreach, .none => unreachable,
3385 .register => unreachable, // a slice doesn't fit in one register
3386 .stack_argument_offset => |off| {
3387 break :result MCValue{ .stack_argument_offset = off + ptr_bytes };
3388 },
3389 .stack_offset => |off| {
3390 break :result MCValue{ .stack_offset = off - ptr_bytes };
3391 },
3392 .memory => |addr| {
3393 break :result MCValue{ .memory = addr + ptr_bytes };
3394 },
3395 else => return self.fail("TODO implement slice_len for {}", .{mcv}),
3396 }
3397 };
3398 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3399}
3400
3401fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3402 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3403 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3404 const ptr_bits = 64;
3405 const ptr_bytes = @divExact(ptr_bits, 8);
3406 const mcv = try self.resolveInst(ty_op.operand);
3407 switch (mcv) {
3408 .dead, .unreach, .none => unreachable,
3409 .ptr_stack_offset => |off| {
3410 break :result MCValue{ .ptr_stack_offset = off - ptr_bytes };
3411 },
3412 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}),
3413 }
3414 };
3415 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3416}
3417
3418fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3419 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3420 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3421 const mcv = try self.resolveInst(ty_op.operand);
3422 switch (mcv) {
3423 .dead, .unreach, .none => unreachable,
3424 .ptr_stack_offset => |off| {
3425 break :result MCValue{ .ptr_stack_offset = off };
3426 },
3427 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}),
3428 }
3429 };
3430 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3431}
3432
3433fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3434 const pt = self.pt;
3435 const zcu = pt.zcu;
3436 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3437 const slice_ty = self.typeOf(bin_op.lhs);
3438 const result: MCValue = if (!slice_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: {
3439 const ptr_ty = slice_ty.slicePtrFieldType(zcu);
3440
3441 const slice_mcv = try self.resolveInst(bin_op.lhs);
3442 const base_mcv = slicePtr(slice_mcv);
3443
3444 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
3445 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
3446
3447 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
3448 };
3449 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3450}
3451
3452fn ptrElemVal(
3453 self: *Self,
3454 ptr_bind: ReadArg.Bind,
3455 index_bind: ReadArg.Bind,
3456 ptr_ty: Type,
3457 maybe_inst: ?Air.Inst.Index,
3458) !MCValue {
3459 const pt = self.pt;
3460 const zcu = pt.zcu;
3461 const elem_ty = ptr_ty.childType(zcu);
3462 const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
3463
3464 // TODO optimize for elem_sizes of 1, 2, 4, 8
3465 switch (elem_size) {
3466 else => {
3467 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null);
3468
3469 const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst);
3470 try self.load(dest, addr, ptr_ty);
3471 return dest;
3472 },
3473 }
3474}
3475
3476fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3477 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3478 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3479 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3480 const slice_mcv = try self.resolveInst(extra.lhs);
3481 const base_mcv = slicePtr(slice_mcv);
3482
3483 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
3484 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
3485
3486 const slice_ty = self.typeOf(extra.lhs);
3487 const index_ty = self.typeOf(extra.rhs);
3488
3489 const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null);
3490 break :result addr;
3491 };
3492 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3493}
3494
3495fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3496 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3497 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
3498 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3499}
3500
3501fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3502 const pt = self.pt;
3503 const zcu = pt.zcu;
3504 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3505 const ptr_ty = self.typeOf(bin_op.lhs);
3506 const result: MCValue = if (!ptr_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: {
3507 const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
3508 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
3509
3510 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
3511 };
3512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3513}
3514
3515fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3516 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3517 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3518 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3519 const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
3520 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
3521
3522 const ptr_ty = self.typeOf(extra.lhs);
3523 const index_ty = self.typeOf(extra.rhs);
3524
3525 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null);
3526 break :result addr;
3527 };
3528 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
3529}
3530
3531fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
3532 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3533 _ = bin_op;
3534 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
3535}
3536
3537fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
3538 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3539 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
3540 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3541}
3542
3543fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
3544 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3545 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
3546 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3547}
3548
3549fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
3550 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3551 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
3552 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3553}
3554
3555fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
3556 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3557 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
3558 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3559}
3560
3561fn airAbs(self: *Self, inst: Air.Inst.Index) InnerError!void {
3562 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3563 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
3564 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3565}
3566
3567fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void {
3568 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3569 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
3570 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3571}
3572
3573fn airBitReverse(self: *Self, inst: Air.Inst.Index) InnerError!void {
3574 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3575 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
3576 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3577}
3578
3579fn airUnaryMath(self: *Self, inst: Air.Inst.Index) InnerError!void {
3580 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3581 const result: MCValue = if (self.liveness.isUnused(inst))
3582 .dead
3583 else
3584 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
3585 return self.finishAir(inst, result, .{ un_op, .none, .none });
3586}
3587
3588fn reuseOperand(
3589 self: *Self,
3590 inst: Air.Inst.Index,
3591 operand: Air.Inst.Ref,
3592 op_index: Air.Liveness.OperandInt,
3593 mcv: MCValue,
3594) bool {
3595 if (!self.liveness.operandDies(inst, op_index))
3596 return false;
3597
3598 switch (mcv) {
3599 .register => |reg| {
3600 // If it's in the registers table, need to associate the register with the
3601 // new instruction.
3602 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
3603 if (!self.register_manager.isRegFree(reg)) {
3604 self.register_manager.registers[index] = inst;
3605 }
3606 }
3607 log.debug("%{d} => {} (reused)", .{ inst, reg });
3608 },
3609 .stack_offset => |off| {
3610 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
3611 },
3612 else => return false,
3613 }
3614
3615 // Prevent the operand deaths processing code from deallocating it.
3616 self.reused_operands.set(op_index);
3617
3618 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
3619 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
3620 branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead);
3621
3622 return true;
3623}
3624
3625fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
3626 const pt = self.pt;
3627 const zcu = pt.zcu;
3628 const elem_ty = ptr_ty.childType(zcu);
3629 const elem_size = elem_ty.abiSize(zcu);
3630
3631 switch (ptr) {
3632 .none => unreachable,
3633 .undef => unreachable,
3634 .unreach => unreachable,
3635 .dead => unreachable,
3636 .compare_flags,
3637 .register_with_overflow,
3638 => unreachable, // cannot hold an address
3639 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
3640 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
3641 .register => |addr_reg| {
3642 const addr_reg_lock = self.register_manager.lockReg(addr_reg);
3643 defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
3644
3645 switch (dst_mcv) {
3646 .dead => unreachable,
3647 .undef => unreachable,
3648 .compare_flags => unreachable,
3649 .register => |dst_reg| {
3650 try self.genLdrRegister(dst_reg, addr_reg, elem_ty);
3651 },
3652 .stack_offset => |off| {
3653 if (elem_size <= 8) {
3654 const raw_tmp_reg = try self.register_manager.allocReg(null, gp);
3655 const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty);
3656 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3657 defer self.register_manager.unlockReg(tmp_reg_lock);
3658
3659 try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
3660 try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg });
3661 } else {
3662 // TODO optimize the register allocation
3663 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
3664 const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
3665 defer for (regs_locks) |reg| {
3666 self.register_manager.unlockReg(reg);
3667 };
3668
3669 const src_reg = addr_reg;
3670 const dst_reg = regs[0];
3671 const len_reg = regs[1];
3672 const count_reg = regs[2];
3673 const tmp_reg = regs[3];
3674
3675 // sub dst_reg, fp, #off
3676 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
3677
3678 // mov len, #elem_size
3679 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
3680
3681 // memcpy(src, dst, len)
3682 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
3683 }
3684 },
3685 else => return self.fail("TODO load from register into {}", .{dst_mcv}),
3686 }
3687 },
3688 .memory,
3689 .stack_offset,
3690 .stack_argument_offset,
3691 .linker_load,
3692 => {
3693 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr);
3694 try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty);
3695 },
3696 }
3697}
3698
3699fn genInlineMemcpy(
3700 self: *Self,
3701 src: Register,
3702 dst: Register,
3703 len: Register,
3704 count: Register,
3705 tmp: Register,
3706) !void {
3707 // movz count, #0
3708 _ = try self.addInst(.{
3709 .tag = .movz,
3710 .data = .{ .r_imm16_sh = .{
3711 .rd = count,
3712 .imm16 = 0,
3713 } },
3714 });
3715
3716 // loop:
3717 // cmp count, len
3718 _ = try self.addInst(.{
3719 .tag = .cmp_shifted_register,
3720 .data = .{ .rr_imm6_shift = .{
3721 .rn = count,
3722 .rm = len,
3723 .imm6 = 0,
3724 .shift = .lsl,
3725 } },
3726 });
3727
3728 // bge end
3729 _ = try self.addInst(.{
3730 .tag = .b_cond,
3731 .data = .{ .inst_cond = .{
3732 .inst = @as(u32, @intCast(self.mir_instructions.len + 5)),
3733 .cond = .ge,
3734 } },
3735 });
3736
3737 // ldrb tmp, [src, count]
3738 _ = try self.addInst(.{
3739 .tag = .ldrb_register,
3740 .data = .{ .load_store_register_register = .{
3741 .rt = tmp,
3742 .rn = src,
3743 .offset = Instruction.LoadStoreOffset.reg(count).register,
3744 } },
3745 });
3746
3747 // strb tmp, [dest, count]
3748 _ = try self.addInst(.{
3749 .tag = .strb_register,
3750 .data = .{ .load_store_register_register = .{
3751 .rt = tmp,
3752 .rn = dst,
3753 .offset = Instruction.LoadStoreOffset.reg(count).register,
3754 } },
3755 });
3756
3757 // add count, count, #1
3758 _ = try self.addInst(.{
3759 .tag = .add_immediate,
3760 .data = .{ .rr_imm12_sh = .{
3761 .rd = count,
3762 .rn = count,
3763 .imm12 = 1,
3764 } },
3765 });
3766
3767 // b loop
3768 _ = try self.addInst(.{
3769 .tag = .b,
3770 .data = .{ .inst = @as(u32, @intCast(self.mir_instructions.len - 5)) },
3771 });
3772
3773 // end:
3774}
3775
3776fn genInlineMemset(
3777 self: *Self,
3778 dst: MCValue,
3779 val: MCValue,
3780 len: MCValue,
3781) !void {
3782 const dst_reg = switch (dst) {
3783 .register => |r| r,
3784 else => try self.copyToTmpRegister(Type.manyptr_u8, dst),
3785 };
3786 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
3787 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
3788
3789 const val_reg = switch (val) {
3790 .register => |r| r,
3791 else => try self.copyToTmpRegister(Type.u8, val),
3792 };
3793 const val_reg_lock = self.register_manager.lockReg(val_reg);
3794 defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock);
3795
3796 const len_reg = switch (len) {
3797 .register => |r| r,
3798 else => try self.copyToTmpRegister(Type.usize, len),
3799 };
3800 const len_reg_lock = self.register_manager.lockReg(len_reg);
3801 defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock);
3802
3803 const count_reg = try self.register_manager.allocReg(null, gp);
3804
3805 try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg);
3806}
3807
3808fn genInlineMemsetCode(
3809 self: *Self,
3810 dst: Register,
3811 val: Register,
3812 len: Register,
3813 count: Register,
3814) !void {
3815 // mov count, #0
3816 _ = try self.addInst(.{
3817 .tag = .movz,
3818 .data = .{ .r_imm16_sh = .{
3819 .rd = count,
3820 .imm16 = 0,
3821 } },
3822 });
3823
3824 // loop:
3825 // cmp count, len
3826 _ = try self.addInst(.{
3827 .tag = .cmp_shifted_register,
3828 .data = .{ .rr_imm6_shift = .{
3829 .rn = count,
3830 .rm = len,
3831 .imm6 = 0,
3832 .shift = .lsl,
3833 } },
3834 });
3835
3836 // bge end
3837 _ = try self.addInst(.{
3838 .tag = .b_cond,
3839 .data = .{ .inst_cond = .{
3840 .inst = @as(u32, @intCast(self.mir_instructions.len + 4)),
3841 .cond = .ge,
3842 } },
3843 });
3844
3845 // strb val, [src, count]
3846 _ = try self.addInst(.{
3847 .tag = .strb_register,
3848 .data = .{ .load_store_register_register = .{
3849 .rt = val,
3850 .rn = dst,
3851 .offset = Instruction.LoadStoreOffset.reg(count).register,
3852 } },
3853 });
3854
3855 // add count, count, #1
3856 _ = try self.addInst(.{
3857 .tag = .add_immediate,
3858 .data = .{ .rr_imm12_sh = .{
3859 .rd = count,
3860 .rn = count,
3861 .imm12 = 1,
3862 } },
3863 });
3864
3865 // b loop
3866 _ = try self.addInst(.{
3867 .tag = .b,
3868 .data = .{ .inst = @as(u32, @intCast(self.mir_instructions.len - 4)) },
3869 });
3870
3871 // end:
3872}
3873
3874fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
3875 const pt = self.pt;
3876 const zcu = pt.zcu;
3877 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3878 const elem_ty = self.typeOfIndex(inst);
3879 const elem_size = elem_ty.abiSize(zcu);
3880 const result: MCValue = result: {
3881 if (!elem_ty.hasRuntimeBits(zcu))
3882 break :result MCValue.none;
3883
3884 const ptr = try self.resolveInst(ty_op.operand);
3885 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(zcu);
3886 if (self.liveness.isUnused(inst) and !is_volatile)
3887 break :result MCValue.dead;
3888
3889 const dst_mcv: MCValue = blk: {
3890 if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
3891 // The MCValue that holds the pointer can be re-used as the value.
3892 break :blk switch (ptr) {
3893 .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) },
3894 else => ptr,
3895 };
3896 } else {
3897 break :blk try self.allocRegOrMem(elem_ty, true, inst);
3898 }
3899 };
3900 try self.load(dst_mcv, ptr, self.typeOf(ty_op.operand));
3901 break :result dst_mcv;
3902 };
3903 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3904}
3905
3906fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
3907 const pt = self.pt;
3908 const zcu = pt.zcu;
3909 const abi_size = ty.abiSize(zcu);
3910
3911 const tag: Mir.Inst.Tag = switch (abi_size) {
3912 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate,
3913 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate,
3914 4 => .ldr_immediate,
3915 8 => .ldr_immediate,
3916 3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}),
3917 else => unreachable,
3918 };
3919
3920 _ = try self.addInst(.{
3921 .tag = tag,
3922 .data = .{ .load_store_register_immediate = .{
3923 .rt = value_reg,
3924 .rn = addr_reg,
3925 .offset = Instruction.LoadStoreOffset.none.immediate,
3926 } },
3927 });
3928}
3929
3930fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
3931 const pt = self.pt;
3932 const abi_size = ty.abiSize(pt.zcu);
3933
3934 const tag: Mir.Inst.Tag = switch (abi_size) {
3935 1 => .strb_immediate,
3936 2 => .strh_immediate,
3937 4, 8 => .str_immediate,
3938 3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}),
3939 else => unreachable,
3940 };
3941
3942 _ = try self.addInst(.{
3943 .tag = tag,
3944 .data = .{ .load_store_register_immediate = .{
3945 .rt = value_reg,
3946 .rn = addr_reg,
3947 .offset = Instruction.LoadStoreOffset.none.immediate,
3948 } },
3949 });
3950}
3951
3952fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
3953 const pt = self.pt;
3954 log.debug("store: storing {} to {}", .{ value, ptr });
3955 const abi_size = value_ty.abiSize(pt.zcu);
3956
3957 switch (ptr) {
3958 .none => unreachable,
3959 .undef => unreachable,
3960 .unreach => unreachable,
3961 .dead => unreachable,
3962 .compare_flags,
3963 .register_with_overflow,
3964 => unreachable, // cannot hold an address
3965 .immediate => |imm| {
3966 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
3967 },
3968 .ptr_stack_offset => |off| {
3969 try self.genSetStack(value_ty, off, value);
3970 },
3971 .register => |addr_reg| {
3972 const addr_reg_lock = self.register_manager.lockReg(addr_reg);
3973 defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
3974
3975 switch (value) {
3976 .dead => unreachable,
3977 .undef => {
3978 try self.genSetReg(value_ty, addr_reg, value);
3979 },
3980 .register => |value_reg| {
3981 log.debug("store: register {} to {}", .{ value_reg, addr_reg });
3982 try self.genStrRegister(value_reg, addr_reg, value_ty);
3983 },
3984 else => {
3985 if (abi_size <= 8) {
3986 const raw_tmp_reg = try self.register_manager.allocReg(null, gp);
3987 const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty);
3988 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3989 defer self.register_manager.unlockReg(tmp_reg_lock);
3990
3991 try self.genSetReg(value_ty, tmp_reg, value);
3992 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
3993 } else {
3994 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
3995 const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
3996 defer for (regs_locks) |reg| {
3997 self.register_manager.unlockReg(reg);
3998 };
3999
4000 const src_reg = regs[0];
4001 const dst_reg = addr_reg;
4002 const len_reg = regs[1];
4003 const count_reg = regs[2];
4004 const tmp_reg = regs[3];
4005
4006 switch (value) {
4007 .stack_offset => |off| {
4008 // sub src_reg, fp, #off
4009 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
4010 },
4011 .stack_argument_offset => |off| {
4012 _ = try self.addInst(.{
4013 .tag = .ldr_ptr_stack_argument,
4014 .data = .{ .load_store_stack = .{
4015 .rt = src_reg,
4016 .offset = off,
4017 } },
4018 });
4019 },
4020 .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @as(u32, @intCast(addr)) }),
4021 .linker_load => |load_struct| {
4022 const tag: Mir.Inst.Tag = switch (load_struct.type) {
4023 .got => .load_memory_ptr_got,
4024 .direct => .load_memory_ptr_direct,
4025 .import => unreachable,
4026 };
4027 const atom_index = switch (self.bin_file.tag) {
4028 .macho => {
4029 // const macho_file = self.bin_file.cast(link.File.MachO).?;
4030 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
4031 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
4032 @panic("TODO store");
4033 },
4034 .coff => blk: {
4035 const coff_file = self.bin_file.cast(.coff).?;
4036 const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav);
4037 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
4038 },
4039 else => unreachable, // unsupported target format
4040 };
4041 _ = try self.addInst(.{
4042 .tag = tag,
4043 .data = .{
4044 .payload = try self.addExtra(Mir.LoadMemoryPie{
4045 .register = @intFromEnum(src_reg),
4046 .atom_index = atom_index,
4047 .sym_index = load_struct.sym_index,
4048 }),
4049 },
4050 });
4051 },
4052 else => return self.fail("TODO store {} to register", .{value}),
4053 }
4054
4055 // mov len, #abi_size
4056 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
4057
4058 // memcpy(src, dst, len)
4059 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
4060 }
4061 },
4062 }
4063 },
4064 .memory,
4065 .stack_offset,
4066 .stack_argument_offset,
4067 .linker_load,
4068 => {
4069 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr);
4070 try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty);
4071 },
4072 }
4073}
4074
4075fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
4076 if (safety) {
4077 // TODO if the value is undef, write 0xaa bytes to dest
4078 } else {
4079 // TODO if the value is undef, don't lower this instruction
4080 }
4081 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4082 const ptr = try self.resolveInst(bin_op.lhs);
4083 const value = try self.resolveInst(bin_op.rhs);
4084 const ptr_ty = self.typeOf(bin_op.lhs);
4085 const value_ty = self.typeOf(bin_op.rhs);
4086
4087 try self.store(ptr, value, ptr_ty, value_ty);
4088
4089 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
4090}
4091
4092fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4093 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4094 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4095 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
4096 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
4097}
4098
4099fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) InnerError!void {
4100 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4101 const result = try self.structFieldPtr(inst, ty_op.operand, index);
4102 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4103}
4104
4105fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
4106 return if (self.liveness.isUnused(inst)) .dead else result: {
4107 const pt = self.pt;
4108 const zcu = pt.zcu;
4109 const mcv = try self.resolveInst(operand);
4110 const ptr_ty = self.typeOf(operand);
4111 const struct_ty = ptr_ty.childType(zcu);
4112 const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(index, zcu)));
4113 switch (mcv) {
4114 .ptr_stack_offset => |off| {
4115 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
4116 },
4117 else => {
4118 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
4119 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
4120
4121 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
4122 },
4123 }
4124 };
4125}
4126
4127fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
4128 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4129 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
4130 const operand = extra.struct_operand;
4131 const index = extra.field_index;
4132 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4133 const pt = self.pt;
4134 const zcu = pt.zcu;
4135 const mcv = try self.resolveInst(operand);
4136 const struct_ty = self.typeOf(operand);
4137 const struct_field_ty = struct_ty.fieldType(index, zcu);
4138 const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(index, zcu)));
4139
4140 switch (mcv) {
4141 .dead, .unreach => unreachable,
4142 .stack_argument_offset => |off| {
4143 break :result MCValue{ .stack_argument_offset = off + struct_field_offset };
4144 },
4145 .stack_offset => |off| {
4146 break :result MCValue{ .stack_offset = off - struct_field_offset };
4147 },
4148 .memory => |addr| {
4149 break :result MCValue{ .memory = addr + struct_field_offset };
4150 },
4151 .register_with_overflow => |rwo| {
4152 const reg_lock = self.register_manager.lockRegAssumeUnused(rwo.reg);
4153 defer self.register_manager.unlockReg(reg_lock);
4154
4155 const field: MCValue = switch (index) {
4156 // get wrapped value: return register
4157 0 => MCValue{ .register = rwo.reg },
4158
4159 // get overflow bit: return C or V flag
4160 1 => MCValue{ .compare_flags = rwo.flag },
4161
4162 else => unreachable,
4163 };
4164
4165 if (self.reuseOperand(inst, operand, 0, field)) {
4166 break :result field;
4167 } else {
4168 // Copy to new register
4169 const raw_dest_reg = try self.register_manager.allocReg(null, gp);
4170 const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty);
4171 try self.genSetReg(struct_field_ty, dest_reg, field);
4172
4173 break :result MCValue{ .register = dest_reg };
4174 }
4175 },
4176 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
4177 }
4178 };
4179
4180 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
4181}
4182
4183fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4184 const pt = self.pt;
4185 const zcu = pt.zcu;
4186 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4187 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
4188 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4189 const field_ptr = try self.resolveInst(extra.field_ptr);
4190 const struct_ty = ty_pl.ty.toType().childType(zcu);
4191 const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(extra.field_index, zcu)));
4192 switch (field_ptr) {
4193 .ptr_stack_offset => |off| {
4194 break :result MCValue{ .ptr_stack_offset = off + struct_field_offset };
4195 },
4196 else => {
4197 const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr };
4198 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
4199
4200 break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
4201 },
4202 }
4203 };
4204 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
4205}
4206
4207fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
4208 // skip zero-bit arguments as they don't have a corresponding arg instruction
4209 var arg_index = self.arg_index;
4210 while (self.args[arg_index] == .none) arg_index += 1;
4211 self.arg_index = arg_index + 1;
4212
4213 const zcu = self.pt.zcu;
4214 const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?;
4215 const file = zcu.fileByIndex(func_zir.file);
4216 if (!file.mod.?.strip) {
4217 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
4218 const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
4219 const ty = self.typeOfIndex(inst);
4220 const zir = &file.zir.?;
4221 const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
4222 try self.dbg_info_relocs.append(self.gpa, .{
4223 .tag = tag,
4224 .ty = ty,
4225 .name = name,
4226 .mcv = self.args[arg_index],
4227 });
4228 }
4229
4230 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
4231 return self.finishAir(inst, result, .{ .none, .none, .none });
4232}
4233
4234fn airTrap(self: *Self) InnerError!void {
4235 _ = try self.addInst(.{
4236 .tag = .brk,
4237 .data = .{ .imm16 = 0x0001 },
4238 });
4239 return self.finishAirBookkeeping();
4240}
4241
4242fn airBreakpoint(self: *Self) InnerError!void {
4243 _ = try self.addInst(.{
4244 .tag = .brk,
4245 .data = .{ .imm16 = 0xf000 },
4246 });
4247 return self.finishAirBookkeeping();
4248}
4249
4250fn airRetAddr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4251 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});
4252 return self.finishAir(inst, result, .{ .none, .none, .none });
4253}
4254
4255fn airFrameAddress(self: *Self, inst: Air.Inst.Index) InnerError!void {
4256 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});
4257 return self.finishAir(inst, result, .{ .none, .none, .none });
4258}
4259
4260fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
4261 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});
4262 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4263 const callee = pl_op.operand;
4264 const extra = self.air.extraData(Air.Call, pl_op.payload);
4265 const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]);
4266 const ty = self.typeOf(callee);
4267 const pt = self.pt;
4268 const zcu = pt.zcu;
4269 const ip = &zcu.intern_pool;
4270
4271 const fn_ty = switch (ty.zigTypeTag(zcu)) {
4272 .@"fn" => ty,
4273 .pointer => ty.childType(zcu),
4274 else => unreachable,
4275 };
4276
4277 var info = try self.resolveCallingConventionValues(fn_ty);
4278 defer info.deinit(self);
4279
4280 // According to the Procedure Call Standard for the ARM
4281 // Architecture, compare flags are not preserved across
4282 // calls. Therefore, if some value is currently stored there, we
4283 // need to save it.
4284 //
4285 // TODO once caller-saved registers are implemented, save them
4286 // here too, but crucially *after* we save the compare flags as
4287 // saving compare flags may require a new caller-saved register
4288 try self.spillCompareFlagsIfOccupied();
4289
4290 if (info.return_value == .stack_offset) {
4291 log.debug("airCall: return by reference", .{});
4292 const ret_ty = fn_ty.fnReturnType(zcu);
4293 const ret_abi_size: u32 = @intCast(ret_ty.abiSize(zcu));
4294 const ret_abi_align = ret_ty.abiAlignment(zcu);
4295 const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst);
4296
4297 const ret_ptr_reg = self.registerAlias(.x0, Type.usize);
4298
4299 const ptr_ty = try pt.singleMutPtrType(ret_ty);
4300 try self.register_manager.getReg(ret_ptr_reg, null);
4301 try self.genSetReg(ptr_ty, ret_ptr_reg, .{ .ptr_stack_offset = stack_offset });
4302
4303 info.return_value = .{ .stack_offset = stack_offset };
4304 }
4305
4306 // Make space for the arguments passed via the stack
4307 self.max_end_stack += info.stack_byte_count;
4308
4309 for (info.args, 0..) |mc_arg, arg_i| {
4310 const arg = args[arg_i];
4311 const arg_ty = self.typeOf(arg);
4312 const arg_mcv = try self.resolveInst(args[arg_i]);
4313
4314 switch (mc_arg) {
4315 .none => continue,
4316 .register => |reg| {
4317 try self.register_manager.getReg(reg, null);
4318 try self.genSetReg(arg_ty, reg, arg_mcv);
4319 },
4320 .stack_offset => unreachable,
4321 .stack_argument_offset => |offset| try self.genSetStackArgument(
4322 arg_ty,
4323 offset,
4324 arg_mcv,
4325 ),
4326 else => unreachable,
4327 }
4328 }
4329
4330 // Due to incremental compilation, how function calls are generated depends
4331 // on linking.
4332 if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
4333 .func => |func| {
4334 if (self.bin_file.cast(.elf)) |_| {
4335 return self.fail("TODO implement calling functions for Elf", .{});
4336 } else if (self.bin_file.cast(.macho)) |_| {
4337 return self.fail("TODO implement calling functions for MachO", .{});
4338 } else if (self.bin_file.cast(.coff)) |coff_file| {
4339 const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
4340 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
4341 try self.genSetReg(Type.u64, .x30, .{
4342 .linker_load = .{
4343 .type = .got,
4344 .sym_index = sym_index,
4345 },
4346 });
4347 } else if (self.bin_file.cast(.plan9)) |p9| {
4348 const atom_index = try p9.seeNav(pt, func.owner_nav);
4349 const atom = p9.getAtom(atom_index);
4350 try self.genSetReg(Type.usize, .x30, .{ .memory = atom.getOffsetTableAddress(p9) });
4351 } else unreachable;
4352
4353 _ = try self.addInst(.{
4354 .tag = .blr,
4355 .data = .{ .reg = .x30 },
4356 });
4357 },
4358 .@"extern" => |@"extern"| {
4359 const nav_name = ip.getNav(@"extern".owner_nav).name.toSlice(ip);
4360 const lib_name = @"extern".lib_name.toSlice(ip);
4361 if (self.bin_file.cast(.macho)) |_| {
4362 return self.fail("TODO implement calling extern functions for MachO", .{});
4363 } else if (self.bin_file.cast(.coff)) |coff_file| {
4364 const sym_index = try coff_file.getGlobalSymbol(nav_name, lib_name);
4365 try self.genSetReg(Type.u64, .x30, .{
4366 .linker_load = .{
4367 .type = .import,
4368 .sym_index = sym_index,
4369 },
4370 });
4371 _ = try self.addInst(.{
4372 .tag = .blr,
4373 .data = .{ .reg = .x30 },
4374 });
4375 } else {
4376 return self.fail("TODO implement calling extern functions", .{});
4377 }
4378 },
4379 else => return self.fail("TODO implement calling bitcasted functions", .{}),
4380 } else {
4381 assert(ty.zigTypeTag(zcu) == .pointer);
4382 const mcv = try self.resolveInst(callee);
4383 try self.genSetReg(ty, .x30, mcv);
4384
4385 _ = try self.addInst(.{
4386 .tag = .blr,
4387 .data = .{ .reg = .x30 },
4388 });
4389 }
4390
4391 const result: MCValue = result: {
4392 switch (info.return_value) {
4393 .register => |reg| {
4394 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
4395 // Save function return value in a callee saved register
4396 break :result try self.copyToNewRegister(inst, info.return_value);
4397 }
4398 },
4399 else => {},
4400 }
4401 break :result info.return_value;
4402 };
4403
4404 if (args.len + 1 <= Air.Liveness.bpi - 1) {
4405 var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1);
4406 buf[0] = callee;
4407 @memcpy(buf[1..][0..args.len], args);
4408 return self.finishAir(inst, result, buf);
4409 }
4410 var bt = try self.iterateBigTomb(inst, 1 + args.len);
4411 bt.feed(callee);
4412 for (args) |arg| {
4413 bt.feed(arg);
4414 }
4415 return bt.finishAir(result);
4416}
4417
4418fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void {
4419 const pt = self.pt;
4420 const zcu = pt.zcu;
4421 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4422 const operand = try self.resolveInst(un_op);
4423 const ret_ty = self.fn_type.fnReturnType(zcu);
4424
4425 switch (self.ret_mcv) {
4426 .none => {},
4427 .immediate => {
4428 assert(ret_ty.isError(zcu));
4429 },
4430 .register => |reg| {
4431 // Return result by value
4432 try self.genSetReg(ret_ty, reg, operand);
4433 },
4434 .stack_offset => {
4435 // Return result by reference
4436 //
4437 // self.ret_mcv is an address to where this function
4438 // should store its result into
4439 const ptr_ty = try pt.singleMutPtrType(ret_ty);
4440 try self.store(self.ret_mcv, operand, ptr_ty, ret_ty);
4441 },
4442 else => unreachable,
4443 }
4444
4445 // Just add space for an instruction, patch this later
4446 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
4447
4448 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4449}
4450
4451fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
4452 const pt = self.pt;
4453 const zcu = pt.zcu;
4454 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4455 const ptr = try self.resolveInst(un_op);
4456 const ptr_ty = self.typeOf(un_op);
4457 const ret_ty = self.fn_type.fnReturnType(zcu);
4458
4459 switch (self.ret_mcv) {
4460 .none => {},
4461 .register => {
4462 // Return result by value
4463 try self.load(self.ret_mcv, ptr, ptr_ty);
4464 },
4465 .stack_offset => {
4466 // Return result by reference
4467 //
4468 // self.ret_mcv is an address to where this function
4469 // should store its result into
4470 //
4471 // If the operand is a ret_ptr instruction, we are done
4472 // here. Else we need to load the result from the location
4473 // pointed to by the operand and store it to the result
4474 // location.
4475 const op_inst = un_op.toIndex().?;
4476 if (self.air.instructions.items(.tag)[@intFromEnum(op_inst)] != .ret_ptr) {
4477 const abi_size = @as(u32, @intCast(ret_ty.abiSize(zcu)));
4478 const abi_align = ret_ty.abiAlignment(zcu);
4479
4480 const offset = try self.allocMem(abi_size, abi_align, null);
4481
4482 const tmp_mcv = MCValue{ .stack_offset = offset };
4483 try self.load(tmp_mcv, ptr, ptr_ty);
4484 try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty);
4485 }
4486 },
4487 else => unreachable, // invalid return result
4488 }
4489
4490 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
4491
4492 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4493}
4494
4495fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) InnerError!void {
4496 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4497 const lhs_ty = self.typeOf(bin_op.lhs);
4498
4499 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
4500 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
4501 };
4502
4503 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
4504}
4505
4506fn cmp(
4507 self: *Self,
4508 lhs: ReadArg.Bind,
4509 rhs: ReadArg.Bind,
4510 lhs_ty: Type,
4511 op: math.CompareOperator,
4512) !MCValue {
4513 const pt = self.pt;
4514 const zcu = pt.zcu;
4515 const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
4516 .optional => blk: {
4517 const payload_ty = lhs_ty.optionalChild(zcu);
4518 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4519 break :blk Type.u1;
4520 } else if (lhs_ty.isPtrLikeOptional(zcu)) {
4521 break :blk Type.usize;
4522 } else {
4523 return self.fail("TODO ARM cmp non-pointer optionals", .{});
4524 }
4525 },
4526 .float => return self.fail("TODO ARM cmp floats", .{}),
4527 .@"enum" => lhs_ty.intTagType(zcu),
4528 .int => lhs_ty,
4529 .bool => Type.u1,
4530 .pointer => Type.usize,
4531 .error_set => Type.u16,
4532 else => unreachable,
4533 };
4534
4535 const int_info = int_ty.intInfo(zcu);
4536 if (int_info.bits <= 64) {
4537 try self.spillCompareFlagsIfOccupied();
4538
4539 var lhs_reg: Register = undefined;
4540 var rhs_reg: Register = undefined;
4541
4542 const rhs_immediate = try rhs.resolveToImmediate(self);
4543 const rhs_immediate_ok = if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false;
4544
4545 if (rhs_immediate_ok) {
4546 const read_args = [_]ReadArg{
4547 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
4548 };
4549 try self.allocRegs(
4550 &read_args,
4551 &.{},
4552 null, // we won't be able to reuse a register as there are no write_regs
4553 );
4554
4555 _ = try self.addInst(.{
4556 .tag = .cmp_immediate,
4557 .data = .{ .r_imm12_sh = .{
4558 .rn = lhs_reg,
4559 .imm12 = @as(u12, @intCast(rhs_immediate.?)),
4560 } },
4561 });
4562 } else {
4563 const read_args = [_]ReadArg{
4564 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
4565 .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg },
4566 };
4567 try self.allocRegs(
4568 &read_args,
4569 &.{},
4570 null, // we won't be able to reuse a register as there are no write_regs
4571 );
4572
4573 _ = try self.addInst(.{
4574 .tag = .cmp_shifted_register,
4575 .data = .{ .rr_imm6_shift = .{
4576 .rn = lhs_reg,
4577 .rm = rhs_reg,
4578 .imm6 = 0,
4579 .shift = .lsl,
4580 } },
4581 });
4582 }
4583
4584 return switch (int_info.signedness) {
4585 .signed => MCValue{ .compare_flags = Condition.fromCompareOperatorSigned(op) },
4586 .unsigned => MCValue{ .compare_flags = Condition.fromCompareOperatorUnsigned(op) },
4587 };
4588 } else {
4589 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{});
4590 }
4591}
4592
4593fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void {
4594 _ = inst;
4595 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
4596}
4597
4598fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
4599 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4600 const operand = try self.resolveInst(un_op);
4601 _ = operand;
4602 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
4603 return self.finishAir(inst, result, .{ un_op, .none, .none });
4604}
4605
4606fn airDbgStmt(self: *Self, inst: Air.Inst.Index) InnerError!void {
4607 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
4608
4609 _ = try self.addInst(.{
4610 .tag = .dbg_line,
4611 .data = .{ .dbg_line_column = .{
4612 .line = dbg_stmt.line,
4613 .column = dbg_stmt.column,
4614 } },
4615 });
4616
4617 return self.finishAirBookkeeping();
4618}
4619
4620fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
4621 const pt = self.pt;
4622 const zcu = pt.zcu;
4623 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4624 const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
4625 const func = zcu.funcInfo(extra.data.func);
4626 // TODO emit debug info for function change
4627 _ = func;
4628 try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]));
4629}
4630
4631fn airDbgVar(self: *Self, inst: Air.Inst.Index) InnerError!void {
4632 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4633 const operand = pl_op.operand;
4634 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
4635 const ty = self.typeOf(operand);
4636 const mcv = try self.resolveInst(operand);
4637 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
4638
4639 log.debug("airDbgVar: %{f}: {f}, {}", .{ inst, ty.fmtDebug(), mcv });
4640
4641 try self.dbg_info_relocs.append(self.gpa, .{
4642 .tag = tag,
4643 .ty = ty,
4644 .name = name.toSlice(self.air),
4645 .mcv = mcv,
4646 });
4647
4648 return self.finishAir(inst, .dead, .{ operand, .none, .none });
4649}
4650
4651fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
4652 switch (condition) {
4653 .compare_flags => |cond| return try self.addInst(.{
4654 .tag = .b_cond,
4655 .data = .{
4656 .inst_cond = .{
4657 .inst = undefined, // populated later through performReloc
4658 // Here we map to the opposite condition because the jump is to the false branch.
4659 .cond = cond.negate(),
4660 },
4661 },
4662 }),
4663 else => {
4664 const reg = switch (condition) {
4665 .register => |r| r,
4666 else => try self.copyToTmpRegister(Type.bool, condition),
4667 };
4668
4669 return try self.addInst(.{
4670 .tag = .cbz,
4671 .data = .{
4672 .r_inst = .{
4673 .rt = reg,
4674 .inst = undefined, // populated later through performReloc
4675 },
4676 },
4677 });
4678 },
4679 }
4680}
4681
4682fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4683 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4684 const cond = try self.resolveInst(pl_op.operand);
4685 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
4686 const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]);
4687 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
4688 const liveness_condbr = self.liveness.getCondBr(inst);
4689
4690 const reloc = try self.condBr(cond);
4691
4692 // If the condition dies here in this condbr instruction, process
4693 // that death now instead of later as this has an effect on
4694 // whether it needs to be spilled in the branches
4695 if (self.liveness.operandDies(inst, 0)) {
4696 if (pl_op.operand.toIndex()) |op_index| {
4697 self.processDeath(op_index);
4698 }
4699 }
4700
4701 // Capture the state of register and stack allocation state so that we can revert to it.
4702 const parent_next_stack_offset = self.next_stack_offset;
4703 const parent_free_registers = self.register_manager.free_registers;
4704 var parent_stack = try self.stack.clone(self.gpa);
4705 defer parent_stack.deinit(self.gpa);
4706 const parent_registers = self.register_manager.registers;
4707 const parent_compare_flags_inst = self.compare_flags_inst;
4708
4709 try self.branch_stack.append(.{});
4710 errdefer {
4711 _ = self.branch_stack.pop().?;
4712 }
4713
4714 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
4715 for (liveness_condbr.then_deaths) |operand| {
4716 self.processDeath(operand);
4717 }
4718 try self.genBody(then_body);
4719
4720 // Revert to the previous register and stack allocation state.
4721
4722 var saved_then_branch = self.branch_stack.pop().?;
4723 defer saved_then_branch.deinit(self.gpa);
4724
4725 self.register_manager.registers = parent_registers;
4726 self.compare_flags_inst = parent_compare_flags_inst;
4727
4728 self.stack.deinit(self.gpa);
4729 self.stack = parent_stack;
4730 parent_stack = .{};
4731
4732 self.next_stack_offset = parent_next_stack_offset;
4733 self.register_manager.free_registers = parent_free_registers;
4734
4735 try self.performReloc(reloc);
4736 const else_branch = self.branch_stack.addOneAssumeCapacity();
4737 else_branch.* = .{};
4738
4739 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
4740 for (liveness_condbr.else_deaths) |operand| {
4741 self.processDeath(operand);
4742 }
4743 try self.genBody(else_body);
4744
4745 // At this point, each branch will possibly have conflicting values for where
4746 // each instruction is stored. They agree, however, on which instructions are alive/dead.
4747 // We use the first ("then") branch as canonical, and here emit
4748 // instructions into the second ("else") branch to make it conform.
4749 // We continue respect the data structure semantic guarantees of the else_branch so
4750 // that we can use all the code emitting abstractions. This is why at the bottom we
4751 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
4752 // rather than assigning it.
4753 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
4754 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
4755
4756 const else_slice = else_branch.inst_table.entries.slice();
4757 const else_keys = else_slice.items(.key);
4758 const else_values = else_slice.items(.value);
4759 for (else_keys, 0..) |else_key, else_idx| {
4760 const else_value = else_values[else_idx];
4761 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
4762 // The instruction's MCValue is overridden in both branches.
4763 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
4764 if (else_value == .dead) {
4765 assert(then_entry.value == .dead);
4766 continue;
4767 }
4768 break :blk then_entry.value;
4769 } else blk: {
4770 if (else_value == .dead)
4771 continue;
4772 // The instruction is only overridden in the else branch.
4773 var i: usize = self.branch_stack.items.len - 1;
4774 while (true) {
4775 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
4776 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
4777 assert(mcv != .dead);
4778 break :blk mcv;
4779 }
4780 }
4781 };
4782 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
4783 // TODO make sure the destination stack offset / register does not already have something
4784 // going on there.
4785 try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value);
4786 // TODO track the new register / stack allocation
4787 }
4788 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
4789 const then_slice = saved_then_branch.inst_table.entries.slice();
4790 const then_keys = then_slice.items(.key);
4791 const then_values = then_slice.items(.value);
4792 for (then_keys, 0..) |then_key, then_idx| {
4793 const then_value = then_values[then_idx];
4794 // We already deleted the items from this table that matched the else_branch.
4795 // So these are all instructions that are only overridden in the then branch.
4796 parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
4797 if (then_value == .dead)
4798 continue;
4799 const parent_mcv = blk: {
4800 var i: usize = self.branch_stack.items.len - 1;
4801 while (true) {
4802 i -= 1;
4803 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
4804 assert(mcv != .dead);
4805 break :blk mcv;
4806 }
4807 }
4808 };
4809 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
4810 // TODO make sure the destination stack offset / register does not already have something
4811 // going on there.
4812 try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value);
4813 // TODO track the new register / stack allocation
4814 }
4815
4816 {
4817 var item = self.branch_stack.pop().?;
4818 item.deinit(self.gpa);
4819 }
4820
4821 // We already took care of pl_op.operand earlier, so we're going
4822 // to pass .none here
4823 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
4824}
4825
4826fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue {
4827 const pt = self.pt;
4828 const zcu = pt.zcu;
4829 const sentinel: struct { ty: Type, bind: ReadArg.Bind } = if (!operand_ty.isPtrLikeOptional(zcu)) blk: {
4830 const payload_ty = operand_ty.optionalChild(zcu);
4831 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu))
4832 break :blk .{ .ty = operand_ty, .bind = operand_bind };
4833
4834 const offset = @as(u32, @intCast(payload_ty.abiSize(zcu)));
4835 const operand_mcv = try operand_bind.resolveToMcv(self);
4836 const new_mcv: MCValue = switch (operand_mcv) {
4837 .register => |source_reg| new: {
4838 // TODO should we reuse the operand here?
4839 const raw_reg = try self.register_manager.allocReg(null, gp);
4840 const dest_reg = raw_reg.toX();
4841
4842 const shift = @as(u6, @intCast(offset * 8));
4843 if (shift == 0) {
4844 try self.genSetReg(payload_ty, dest_reg, operand_mcv);
4845 } else {
4846 _ = try self.addInst(.{
4847 .tag = if (payload_ty.isSignedInt(zcu))
4848 Mir.Inst.Tag.asr_immediate
4849 else
4850 Mir.Inst.Tag.lsr_immediate,
4851 .data = .{ .rr_shift = .{
4852 .rd = dest_reg,
4853 .rn = source_reg.toX(),
4854 .shift = shift,
4855 } },
4856 });
4857 }
4858
4859 break :new .{ .register = self.registerAlias(dest_reg, payload_ty) };
4860 },
4861 .stack_argument_offset => |off| .{ .stack_argument_offset = off + offset },
4862 .stack_offset => |off| .{ .stack_offset = off - offset },
4863 .memory => |addr| .{ .memory = addr + offset },
4864 else => unreachable, // invalid MCValue for an optional
4865 };
4866
4867 break :blk .{ .ty = Type.bool, .bind = .{ .mcv = new_mcv } };
4868 } else .{ .ty = operand_ty, .bind = operand_bind };
4869 const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } };
4870 return self.cmp(sentinel.bind, imm_bind, sentinel.ty, .eq);
4871}
4872
4873fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue {
4874 const is_null_res = try self.isNull(operand_bind, operand_ty);
4875 assert(is_null_res.compare_flags == .eq);
4876 return MCValue{ .compare_flags = is_null_res.compare_flags.negate() };
4877}
4878
4879fn isErr(
4880 self: *Self,
4881 error_union_bind: ReadArg.Bind,
4882 error_union_ty: Type,
4883) !MCValue {
4884 const pt = self.pt;
4885 const zcu = pt.zcu;
4886 const error_type = error_union_ty.errorUnionSet(zcu);
4887
4888 if (error_type.errorSetIsEmpty(zcu)) {
4889 return MCValue{ .immediate = 0 }; // always false
4890 }
4891
4892 const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null);
4893 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
4894}
4895
4896fn isNonErr(
4897 self: *Self,
4898 error_union_bind: ReadArg.Bind,
4899 error_union_ty: Type,
4900) !MCValue {
4901 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
4902 switch (is_err_result) {
4903 .compare_flags => |cond| {
4904 assert(cond == .hi);
4905 return MCValue{ .compare_flags = cond.negate() };
4906 },
4907 .immediate => |imm| {
4908 assert(imm == 0);
4909 return MCValue{ .immediate = 1 };
4910 },
4911 else => unreachable,
4912 }
4913}
4914
4915fn airIsNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
4916 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4917 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4918 const operand = try self.resolveInst(un_op);
4919 const operand_ty = self.typeOf(un_op);
4920
4921 break :result try self.isNull(.{ .mcv = operand }, operand_ty);
4922 };
4923 return self.finishAir(inst, result, .{ un_op, .none, .none });
4924}
4925
4926fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4927 const pt = self.pt;
4928 const zcu = pt.zcu;
4929 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4930 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4931 const operand_ptr = try self.resolveInst(un_op);
4932 const ptr_ty = self.typeOf(un_op);
4933 const elem_ty = ptr_ty.childType(zcu);
4934
4935 const operand = try self.allocRegOrMem(elem_ty, true, null);
4936 try self.load(operand, operand_ptr, ptr_ty);
4937
4938 break :result try self.isNull(.{ .mcv = operand }, elem_ty);
4939 };
4940 return self.finishAir(inst, result, .{ un_op, .none, .none });
4941}
4942
4943fn airIsNonNull(self: *Self, inst: Air.Inst.Index) InnerError!void {
4944 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4945 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4946 const operand = try self.resolveInst(un_op);
4947 const operand_ty = self.typeOf(un_op);
4948
4949 break :result try self.isNonNull(.{ .mcv = operand }, operand_ty);
4950 };
4951 return self.finishAir(inst, result, .{ un_op, .none, .none });
4952}
4953
4954fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4955 const pt = self.pt;
4956 const zcu = pt.zcu;
4957 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4958 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4959 const operand_ptr = try self.resolveInst(un_op);
4960 const ptr_ty = self.typeOf(un_op);
4961 const elem_ty = ptr_ty.childType(zcu);
4962
4963 const operand = try self.allocRegOrMem(elem_ty, true, null);
4964 try self.load(operand, operand_ptr, ptr_ty);
4965
4966 break :result try self.isNonNull(.{ .mcv = operand }, elem_ty);
4967 };
4968 return self.finishAir(inst, result, .{ un_op, .none, .none });
4969}
4970
4971fn airIsErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4972 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4973 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4974 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4975 const error_union_ty = self.typeOf(un_op);
4976
4977 break :result try self.isErr(error_union_bind, error_union_ty);
4978 };
4979 return self.finishAir(inst, result, .{ un_op, .none, .none });
4980}
4981
4982fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4983 const pt = self.pt;
4984 const zcu = pt.zcu;
4985 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4986 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4987 const operand_ptr = try self.resolveInst(un_op);
4988 const ptr_ty = self.typeOf(un_op);
4989 const elem_ty = ptr_ty.childType(zcu);
4990
4991 const operand = try self.allocRegOrMem(elem_ty, true, null);
4992 try self.load(operand, operand_ptr, ptr_ty);
4993
4994 break :result try self.isErr(.{ .mcv = operand }, elem_ty);
4995 };
4996 return self.finishAir(inst, result, .{ un_op, .none, .none });
4997}
4998
4999fn airIsNonErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5000 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5001 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5002 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
5003 const error_union_ty = self.typeOf(un_op);
5004
5005 break :result try self.isNonErr(error_union_bind, error_union_ty);
5006 };
5007 return self.finishAir(inst, result, .{ un_op, .none, .none });
5008}
5009
5010fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5011 const pt = self.pt;
5012 const zcu = pt.zcu;
5013 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5014 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5015 const operand_ptr = try self.resolveInst(un_op);
5016 const ptr_ty = self.typeOf(un_op);
5017 const elem_ty = ptr_ty.childType(zcu);
5018
5019 const operand = try self.allocRegOrMem(elem_ty, true, null);
5020 try self.load(operand, operand_ptr, ptr_ty);
5021
5022 break :result try self.isNonErr(.{ .mcv = operand }, elem_ty);
5023 };
5024 return self.finishAir(inst, result, .{ un_op, .none, .none });
5025}
5026
5027fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void {
5028 // A loop is a setup to be able to jump back to the beginning.
5029 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5030 const loop = self.air.extraData(Air.Block, ty_pl.payload);
5031 const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[loop.end..][0..loop.data.body_len]);
5032 const start_index = @as(u32, @intCast(self.mir_instructions.len));
5033
5034 try self.genBody(body);
5035 try self.jump(start_index);
5036
5037 return self.finishAirBookkeeping();
5038}
5039
5040/// Send control flow to `inst`.
5041fn jump(self: *Self, inst: Mir.Inst.Index) !void {
5042 _ = try self.addInst(.{
5043 .tag = .b,
5044 .data = .{ .inst = inst },
5045 });
5046}
5047
5048fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
5049 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5050 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5051 try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]));
5052}
5053
5054fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
5055 try self.blocks.putNoClobber(self.gpa, inst, .{
5056 // A block is a setup to be able to jump to the end.
5057 .relocs = .{},
5058 // It also acts as a receptacle for break operands.
5059 // Here we use `MCValue.none` to represent a null value so that the first
5060 // break instruction will choose a MCValue for the block result and overwrite
5061 // this field. Following break instructions will use that MCValue to put their
5062 // block results.
5063 .mcv = MCValue{ .none = {} },
5064 });
5065 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
5066
5067 // TODO emit debug info lexical block
5068 try self.genBody(body);
5069
5070 // relocations for `br` instructions
5071 const relocs = &self.blocks.getPtr(inst).?.relocs;
5072 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
5073 // If the last Mir instruction is the last relocation (which
5074 // would just jump one instruction further), it can be safely
5075 // removed
5076 self.mir_instructions.orderedRemove(relocs.pop().?);
5077 }
5078 for (relocs.items) |reloc| {
5079 try self.performReloc(reloc);
5080 }
5081
5082 const result = self.blocks.getPtr(inst).?.mcv;
5083 return self.finishAir(inst, result, .{ .none, .none, .none });
5084}
5085
5086fn airSwitch(self: *Self, inst: Air.Inst.Index) InnerError!void {
5087 const switch_br = self.air.unwrapSwitch(inst);
5088 const condition_ty = self.typeOf(switch_br.operand);
5089 const liveness = try self.liveness.getSwitchBr(
5090 self.gpa,
5091 inst,
5092 switch_br.cases_len + 1,
5093 );
5094 defer self.gpa.free(liveness.deaths);
5095
5096 var it = switch_br.iterateCases();
5097 while (it.next()) |case| {
5098 if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{});
5099
5100 // For every item, we compare it to condition and branch into
5101 // the prong if they are equal. After we compared to all
5102 // items, we branch into the next prong (or if no other prongs
5103 // exist out of the switch statement).
5104 //
5105 // cmp condition, item1
5106 // beq prong
5107 // cmp condition, item2
5108 // beq prong
5109 // cmp condition, item3
5110 // beq prong
5111 // b out
5112 // prong: ...
5113 // ...
5114 // out: ...
5115 const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len);
5116 defer self.gpa.free(branch_into_prong_relocs);
5117
5118 for (case.items, 0..) |item, idx| {
5119 const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq);
5120 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
5121 }
5122
5123 const branch_away_from_prong_reloc = try self.addInst(.{
5124 .tag = .b,
5125 .data = .{ .inst = undefined }, // populated later through performReloc
5126 });
5127
5128 for (branch_into_prong_relocs) |reloc| {
5129 try self.performReloc(reloc);
5130 }
5131
5132 // Capture the state of register and stack allocation state so that we can revert to it.
5133 const parent_next_stack_offset = self.next_stack_offset;
5134 const parent_free_registers = self.register_manager.free_registers;
5135 const parent_compare_flags_inst = self.compare_flags_inst;
5136 var parent_stack = try self.stack.clone(self.gpa);
5137 defer parent_stack.deinit(self.gpa);
5138 const parent_registers = self.register_manager.registers;
5139
5140 try self.branch_stack.append(.{});
5141 errdefer {
5142 _ = self.branch_stack.pop().?;
5143 }
5144
5145 try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len);
5146 for (liveness.deaths[case.idx]) |operand| {
5147 self.processDeath(operand);
5148 }
5149 try self.genBody(case.body);
5150
5151 // Revert to the previous register and stack allocation state.
5152 var saved_case_branch = self.branch_stack.pop().?;
5153 defer saved_case_branch.deinit(self.gpa);
5154
5155 self.register_manager.registers = parent_registers;
5156 self.compare_flags_inst = parent_compare_flags_inst;
5157 self.stack.deinit(self.gpa);
5158 self.stack = parent_stack;
5159 parent_stack = .{};
5160
5161 self.next_stack_offset = parent_next_stack_offset;
5162 self.register_manager.free_registers = parent_free_registers;
5163
5164 try self.performReloc(branch_away_from_prong_reloc);
5165 }
5166
5167 if (switch_br.else_body_len > 0) {
5168 const else_body = it.elseBody();
5169
5170 // Capture the state of register and stack allocation state so that we can revert to it.
5171 const parent_next_stack_offset = self.next_stack_offset;
5172 const parent_free_registers = self.register_manager.free_registers;
5173 const parent_compare_flags_inst = self.compare_flags_inst;
5174 var parent_stack = try self.stack.clone(self.gpa);
5175 defer parent_stack.deinit(self.gpa);
5176 const parent_registers = self.register_manager.registers;
5177
5178 try self.branch_stack.append(.{});
5179 errdefer {
5180 _ = self.branch_stack.pop().?;
5181 }
5182
5183 const else_deaths = liveness.deaths.len - 1;
5184 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
5185 for (liveness.deaths[else_deaths]) |operand| {
5186 self.processDeath(operand);
5187 }
5188 try self.genBody(else_body);
5189
5190 // Revert to the previous register and stack allocation state.
5191 var saved_case_branch = self.branch_stack.pop().?;
5192 defer saved_case_branch.deinit(self.gpa);
5193
5194 self.register_manager.registers = parent_registers;
5195 self.compare_flags_inst = parent_compare_flags_inst;
5196 self.stack.deinit(self.gpa);
5197 self.stack = parent_stack;
5198 parent_stack = .{};
5199
5200 self.next_stack_offset = parent_next_stack_offset;
5201 self.register_manager.free_registers = parent_free_registers;
5202
5203 // TODO consolidate returned MCValues between prongs and else branch like we do
5204 // in airCondBr.
5205 }
5206
5207 return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none });
5208}
5209
5210fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
5211 const tag = self.mir_instructions.items(.tag)[inst];
5212 switch (tag) {
5213 .cbz => self.mir_instructions.items(.data)[inst].r_inst.inst = @intCast(self.mir_instructions.len),
5214 .b_cond => self.mir_instructions.items(.data)[inst].inst_cond.inst = @intCast(self.mir_instructions.len),
5215 .b => self.mir_instructions.items(.data)[inst].inst = @intCast(self.mir_instructions.len),
5216 else => unreachable,
5217 }
5218}
5219
5220fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5221 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
5222 try self.br(branch.block_inst, branch.operand);
5223 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
5224}
5225
5226fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
5227 const pt = self.pt;
5228 const zcu = pt.zcu;
5229 const block_data = self.blocks.getPtr(block).?;
5230
5231 if (self.typeOf(operand).hasRuntimeBits(zcu)) {
5232 const operand_mcv = try self.resolveInst(operand);
5233 const block_mcv = block_data.mcv;
5234 if (block_mcv == .none) {
5235 block_data.mcv = switch (operand_mcv) {
5236 .none, .dead, .unreach => unreachable,
5237 .register, .stack_offset, .memory => operand_mcv,
5238 .immediate, .stack_argument_offset, .compare_flags => blk: {
5239 const new_mcv = try self.allocRegOrMem(self.typeOfIndex(block), true, block);
5240 try self.setRegOrMem(self.typeOfIndex(block), new_mcv, operand_mcv);
5241 break :blk new_mcv;
5242 },
5243 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
5244 };
5245 } else {
5246 try self.setRegOrMem(self.typeOfIndex(block), block_mcv, operand_mcv);
5247 }
5248 }
5249 return self.brVoid(block);
5250}
5251
5252fn brVoid(self: *Self, block: Air.Inst.Index) !void {
5253 const block_data = self.blocks.getPtr(block).?;
5254
5255 // Emit a jump with a relocation. It will be patched up after the block ends.
5256 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
5257
5258 block_data.relocs.appendAssumeCapacity(try self.addInst(.{
5259 .tag = .b,
5260 .data = .{ .inst = undefined }, // populated later through performReloc
5261 }));
5262}
5263
5264fn airAsm(self: *Self, inst: Air.Inst.Index) InnerError!void {
5265 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5266 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
5267 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
5268 const clobbers_len = @as(u31, @truncate(extra.data.flags));
5269 var extra_i: usize = extra.end;
5270 const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]);
5271 extra_i += outputs.len;
5272 const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]);
5273 extra_i += inputs.len;
5274
5275 const dead = !is_volatile and self.liveness.isUnused(inst);
5276 const result: MCValue = if (dead) .dead else result: {
5277 if (outputs.len > 1) {
5278 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
5279 }
5280
5281 const output_constraint: ?[]const u8 = for (outputs) |output| {
5282 if (output != .none) {
5283 return self.fail("TODO implement codegen for non-expr asm", .{});
5284 }
5285 const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]);
5286 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0);
5287 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
5288 // This equation accounts for the fact that even if we have exactly 4 bytes
5289 // for the string, we still use the next u32 for the null terminator.
5290 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
5291
5292 break constraint;
5293 } else null;
5294
5295 for (inputs) |input| {
5296 const input_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]);
5297 const constraint = std.mem.sliceTo(input_bytes, 0);
5298 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
5299 // This equation accounts for the fact that even if we have exactly 4 bytes
5300 // for the string, we still use the next u32 for the null terminator.
5301 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
5302
5303 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
5304 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
5305 }
5306 const reg_name = constraint[1 .. constraint.len - 1];
5307 const reg = parseRegName(reg_name) orelse
5308 return self.fail("unrecognized register: '{s}'", .{reg_name});
5309
5310 const arg_mcv = try self.resolveInst(input);
5311 try self.register_manager.getReg(reg, null);
5312 try self.genSetReg(self.typeOf(input), reg, arg_mcv);
5313 }
5314
5315 {
5316 var clobber_i: u32 = 0;
5317 while (clobber_i < clobbers_len) : (clobber_i += 1) {
5318 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0);
5319 // This equation accounts for the fact that even if we have exactly 4 bytes
5320 // for the string, we still use the next u32 for the null terminator.
5321 extra_i += clobber.len / 4 + 1;
5322
5323 // TODO honor these
5324 }
5325 }
5326
5327 const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len];
5328
5329 if (mem.eql(u8, asm_source, "svc #0")) {
5330 _ = try self.addInst(.{
5331 .tag = .svc,
5332 .data = .{ .imm16 = 0x0 },
5333 });
5334 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
5335 _ = try self.addInst(.{
5336 .tag = .svc,
5337 .data = .{ .imm16 = 0x80 },
5338 });
5339 } else {
5340 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
5341 }
5342
5343 if (output_constraint) |output| {
5344 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
5345 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
5346 }
5347 const reg_name = output[2 .. output.len - 1];
5348 const reg = parseRegName(reg_name) orelse
5349 return self.fail("unrecognized register: '{s}'", .{reg_name});
5350 break :result MCValue{ .register = reg };
5351 } else {
5352 break :result MCValue{ .none = {} };
5353 }
5354 };
5355
5356 simple: {
5357 var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1);
5358 var buf_index: usize = 0;
5359 for (outputs) |output| {
5360 if (output == .none) continue;
5361
5362 if (buf_index >= buf.len) break :simple;
5363 buf[buf_index] = output;
5364 buf_index += 1;
5365 }
5366 if (buf_index + inputs.len > buf.len) break :simple;
5367 @memcpy(buf[buf_index..][0..inputs.len], inputs);
5368 return self.finishAir(inst, result, buf);
5369 }
5370 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
5371 for (outputs) |output| {
5372 if (output == .none) continue;
5373
5374 bt.feed(output);
5375 }
5376 for (inputs) |input| {
5377 bt.feed(input);
5378 }
5379 return bt.finishAir(result);
5380}
5381
5382fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
5383 try self.ensureProcessDeathCapacity(operand_count + 1);
5384 return BigTomb{
5385 .function = self,
5386 .inst = inst,
5387 .lbt = self.liveness.iterateBigTomb(inst),
5388 };
5389}
5390
5391/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
5392fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
5393 switch (loc) {
5394 .none => return,
5395 .register => |reg| return self.genSetReg(ty, reg, val),
5396 .stack_offset => |off| return self.genSetStack(ty, off, val),
5397 .memory => {
5398 return self.fail("TODO implement setRegOrMem for memory", .{});
5399 },
5400 else => unreachable,
5401 }
5402}
5403
5404fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
5405 const pt = self.pt;
5406 const zcu = pt.zcu;
5407 const abi_size = @as(u32, @intCast(ty.abiSize(zcu)));
5408 switch (mcv) {
5409 .dead => unreachable,
5410 .unreach, .none => return, // Nothing to do.
5411 .undef => {
5412 if (!self.wantSafety())
5413 return; // The already existing value will do just fine.
5414 // TODO Upgrade this to a memset call when we have that available.
5415 switch (abi_size) {
5416 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
5417 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
5418 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
5419 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
5420 else => try self.genInlineMemset(
5421 .{ .ptr_stack_offset = stack_offset },
5422 .{ .immediate = 0xaa },
5423 .{ .immediate = abi_size },
5424 ),
5425 }
5426 },
5427 .compare_flags,
5428 .immediate,
5429 .ptr_stack_offset,
5430 => {
5431 const reg = try self.copyToTmpRegister(ty, mcv);
5432 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
5433 },
5434 .register => |reg| {
5435 switch (abi_size) {
5436 1, 2, 4, 8 => {
5437 assert(std.mem.isAlignedGeneric(u32, stack_offset, abi_size));
5438
5439 const tag: Mir.Inst.Tag = switch (abi_size) {
5440 1 => .strb_stack,
5441 2 => .strh_stack,
5442 4, 8 => .str_stack,
5443 else => unreachable, // unexpected abi size
5444 };
5445 const rt = self.registerAlias(reg, ty);
5446
5447 _ = try self.addInst(.{
5448 .tag = tag,
5449 .data = .{ .load_store_stack = .{
5450 .rt = rt,
5451 .offset = stack_offset,
5452 } },
5453 });
5454 },
5455 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
5456 }
5457 },
5458 .register_with_overflow => |rwo| {
5459 const reg_lock = self.register_manager.lockReg(rwo.reg);
5460 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
5461
5462 const wrapped_ty = ty.fieldType(0, zcu);
5463 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg });
5464
5465 const overflow_bit_ty = ty.fieldType(1, zcu);
5466 const overflow_bit_offset = @as(u32, @intCast(ty.structFieldOffset(1, zcu)));
5467 const raw_cond_reg = try self.register_manager.allocReg(null, gp);
5468 const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty);
5469
5470 _ = try self.addInst(.{
5471 .tag = .cset,
5472 .data = .{ .r_cond = .{
5473 .rd = cond_reg,
5474 .cond = rwo.flag,
5475 } },
5476 });
5477
5478 try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
5479 .register = cond_reg,
5480 });
5481 },
5482 .linker_load,
5483 .memory,
5484 .stack_argument_offset,
5485 .stack_offset,
5486 => {
5487 switch (mcv) {
5488 .stack_offset => |off| {
5489 if (stack_offset == off)
5490 return; // Copy stack variable to itself; nothing to do.
5491 },
5492 else => {},
5493 }
5494
5495 if (abi_size <= 8) {
5496 const reg = try self.copyToTmpRegister(ty, mcv);
5497 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
5498 } else {
5499 const ptr_ty = try pt.singleMutPtrType(ty);
5500
5501 // TODO call extern memcpy
5502 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
5503 const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs);
5504 defer for (regs_locks) |reg| {
5505 self.register_manager.unlockReg(reg);
5506 };
5507
5508 const src_reg = regs[0];
5509 const dst_reg = regs[1];
5510 const len_reg = regs[2];
5511 const count_reg = regs[3];
5512 const tmp_reg = regs[4];
5513
5514 switch (mcv) {
5515 .stack_offset => |off| {
5516 // sub src_reg, fp, #off
5517 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
5518 },
5519 .stack_argument_offset => |off| {
5520 _ = try self.addInst(.{
5521 .tag = .ldr_ptr_stack_argument,
5522 .data = .{ .load_store_stack = .{
5523 .rt = src_reg,
5524 .offset = off,
5525 } },
5526 });
5527 },
5528 .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }),
5529 .linker_load => |load_struct| {
5530 const tag: Mir.Inst.Tag = switch (load_struct.type) {
5531 .got => .load_memory_ptr_got,
5532 .direct => .load_memory_ptr_direct,
5533 .import => unreachable,
5534 };
5535 const atom_index = switch (self.bin_file.tag) {
5536 .macho => {
5537 // const macho_file = self.bin_file.cast(link.File.MachO).?;
5538 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
5539 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5540 @panic("TODO genSetStack");
5541 },
5542 .coff => blk: {
5543 const coff_file = self.bin_file.cast(.coff).?;
5544 const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav);
5545 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5546 },
5547 else => unreachable, // unsupported target format
5548 };
5549 _ = try self.addInst(.{
5550 .tag = tag,
5551 .data = .{
5552 .payload = try self.addExtra(Mir.LoadMemoryPie{
5553 .register = @intFromEnum(src_reg),
5554 .atom_index = atom_index,
5555 .sym_index = load_struct.sym_index,
5556 }),
5557 },
5558 });
5559 },
5560 else => unreachable,
5561 }
5562
5563 // sub dst_reg, fp, #stack_offset
5564 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });
5565
5566 // mov len, #abi_size
5567 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
5568
5569 // memcpy(src, dst, len)
5570 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
5571 }
5572 },
5573 }
5574}
5575
5576fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
5577 const pt = self.pt;
5578 const zcu = pt.zcu;
5579 switch (mcv) {
5580 .dead => unreachable,
5581 .unreach, .none => return, // Nothing to do.
5582 .undef => {
5583 if (!self.wantSafety())
5584 return; // The already existing value will do just fine.
5585 // Write the debug undefined value.
5586 switch (reg.size()) {
5587 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
5588 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
5589 else => unreachable, // unexpected register size
5590 }
5591 },
5592 .ptr_stack_offset => |off| {
5593 _ = try self.addInst(.{
5594 .tag = .ldr_ptr_stack,
5595 .data = .{ .load_store_stack = .{
5596 .rt = reg,
5597 .offset = @intCast(off),
5598 } },
5599 });
5600 },
5601 .compare_flags => |condition| {
5602 _ = try self.addInst(.{
5603 .tag = .cset,
5604 .data = .{ .r_cond = .{
5605 .rd = reg,
5606 .cond = condition,
5607 } },
5608 });
5609 },
5610 .immediate => |x| {
5611 _ = try self.addInst(.{
5612 .tag = .movz,
5613 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x) } },
5614 });
5615
5616 if (x & 0x0000_0000_ffff_0000 != 0) {
5617 _ = try self.addInst(.{
5618 .tag = .movk,
5619 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 16), .hw = 1 } },
5620 });
5621 }
5622
5623 if (reg.size() == 64) {
5624 if (x & 0x0000_ffff_0000_0000 != 0) {
5625 _ = try self.addInst(.{
5626 .tag = .movk,
5627 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 32), .hw = 2 } },
5628 });
5629 }
5630 if (x & 0xffff_0000_0000_0000 != 0) {
5631 _ = try self.addInst(.{
5632 .tag = .movk,
5633 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(x >> 48), .hw = 3 } },
5634 });
5635 }
5636 }
5637 },
5638 .register => |src_reg| {
5639 assert(src_reg.size() == reg.size());
5640
5641 // If the registers are the same, nothing to do.
5642 if (src_reg.id() == reg.id())
5643 return;
5644
5645 // mov reg, src_reg
5646 _ = try self.addInst(.{
5647 .tag = .mov_register,
5648 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } },
5649 });
5650 },
5651 .register_with_overflow => unreachable, // doesn't fit into a register
5652 .linker_load => |load_struct| {
5653 const tag: Mir.Inst.Tag = switch (load_struct.type) {
5654 .got => .load_memory_got,
5655 .direct => .load_memory_direct,
5656 .import => .load_memory_import,
5657 };
5658 const atom_index = switch (self.bin_file.tag) {
5659 .macho => {
5660 @panic("TODO genSetReg");
5661 // const macho_file = self.bin_file.cast(link.File.MachO).?;
5662 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
5663 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5664 },
5665 .coff => blk: {
5666 const coff_file = self.bin_file.cast(.coff).?;
5667 const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav);
5668 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5669 },
5670 else => unreachable, // unsupported target format
5671 };
5672 _ = try self.addInst(.{
5673 .tag = tag,
5674 .data = .{
5675 .payload = try self.addExtra(Mir.LoadMemoryPie{
5676 .register = @intFromEnum(reg),
5677 .atom_index = atom_index,
5678 .sym_index = load_struct.sym_index,
5679 }),
5680 },
5681 });
5682 },
5683 .memory => |addr| {
5684 // The value is in memory at a hard-coded address.
5685 // If the type is a pointer, it means the pointer address is at this memory location.
5686 try self.genSetReg(ty, reg.toX(), .{ .immediate = addr });
5687 try self.genLdrRegister(reg, reg.toX(), ty);
5688 },
5689 .stack_offset => |off| {
5690 const abi_size = ty.abiSize(zcu);
5691
5692 switch (abi_size) {
5693 1, 2, 4, 8 => {
5694 const tag: Mir.Inst.Tag = switch (abi_size) {
5695 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack,
5696 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack,
5697 4, 8 => .ldr_stack,
5698 else => unreachable, // unexpected abi size
5699 };
5700
5701 _ = try self.addInst(.{
5702 .tag = tag,
5703 .data = .{ .load_store_stack = .{
5704 .rt = reg,
5705 .offset = @intCast(off),
5706 } },
5707 });
5708 },
5709 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}),
5710 else => unreachable,
5711 }
5712 },
5713 .stack_argument_offset => |off| {
5714 const abi_size = ty.abiSize(zcu);
5715
5716 switch (abi_size) {
5717 1, 2, 4, 8 => {
5718 const tag: Mir.Inst.Tag = switch (abi_size) {
5719 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument,
5720 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument,
5721 4, 8 => .ldr_stack_argument,
5722 else => unreachable, // unexpected abi size
5723 };
5724
5725 _ = try self.addInst(.{
5726 .tag = tag,
5727 .data = .{ .load_store_stack = .{
5728 .rt = reg,
5729 .offset = @intCast(off),
5730 } },
5731 });
5732 },
5733 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}),
5734 else => unreachable,
5735 }
5736 },
5737 }
5738}
5739
5740fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
5741 const pt = self.pt;
5742 const zcu = pt.zcu;
5743 const abi_size = @as(u32, @intCast(ty.abiSize(zcu)));
5744 switch (mcv) {
5745 .dead => unreachable,
5746 .none, .unreach => return,
5747 .undef => {
5748 if (!self.wantSafety())
5749 return; // The already existing value will do just fine.
5750 // TODO Upgrade this to a memset call when we have that available.
5751 switch (ty.abiSize(pt.zcu)) {
5752 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
5753 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
5754 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
5755 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
5756 else => return self.fail("TODO implement memset", .{}),
5757 }
5758 },
5759 .register => |reg| {
5760 switch (abi_size) {
5761 1, 2, 4, 8 => {
5762 const tag: Mir.Inst.Tag = switch (abi_size) {
5763 1 => .strb_immediate,
5764 2 => .strh_immediate,
5765 4, 8 => .str_immediate,
5766 else => unreachable, // unexpected abi size
5767 };
5768 const rt = self.registerAlias(reg, ty);
5769 const offset = switch (abi_size) {
5770 1 => blk: {
5771 if (math.cast(u12, stack_offset)) |imm| {
5772 break :blk Instruction.LoadStoreOffset.imm(imm);
5773 } else {
5774 return self.fail("TODO genSetStackArgument byte with larger offset", .{});
5775 }
5776 },
5777 2 => blk: {
5778 assert(std.mem.isAlignedGeneric(u32, stack_offset, 2)); // misaligned stack entry
5779 if (math.cast(u12, @divExact(stack_offset, 2))) |imm| {
5780 break :blk Instruction.LoadStoreOffset.imm(imm);
5781 } else {
5782 return self.fail("TODO getSetStackArgument halfword with larger offset", .{});
5783 }
5784 },
5785 4, 8 => blk: {
5786 const alignment = abi_size;
5787 assert(std.mem.isAlignedGeneric(u32, stack_offset, alignment)); // misaligned stack entry
5788 if (math.cast(u12, @divExact(stack_offset, alignment))) |imm| {
5789 break :blk Instruction.LoadStoreOffset.imm(imm);
5790 } else {
5791 return self.fail("TODO genSetStackArgument with larger offset", .{});
5792 }
5793 },
5794 else => unreachable,
5795 };
5796
5797 _ = try self.addInst(.{
5798 .tag = tag,
5799 .data = .{ .load_store_register_immediate = .{
5800 .rt = rt,
5801 .rn = .sp,
5802 .offset = offset.immediate,
5803 } },
5804 });
5805 },
5806 else => return self.fail("TODO genSetStackArgument other types abi_size={}", .{abi_size}),
5807 }
5808 },
5809 .register_with_overflow => {
5810 return self.fail("TODO implement genSetStackArgument {}", .{mcv});
5811 },
5812 .linker_load,
5813 .memory,
5814 .stack_argument_offset,
5815 .stack_offset,
5816 => {
5817 if (abi_size <= 4) {
5818 const reg = try self.copyToTmpRegister(ty, mcv);
5819 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
5820 } else {
5821 const ptr_ty = try pt.singleMutPtrType(ty);
5822
5823 // TODO call extern memcpy
5824 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
5825 const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs);
5826 defer for (regs_locks) |reg| {
5827 self.register_manager.unlockReg(reg);
5828 };
5829
5830 const src_reg = regs[0];
5831 const dst_reg = regs[1];
5832 const len_reg = regs[2];
5833 const count_reg = regs[3];
5834 const tmp_reg = regs[4];
5835
5836 switch (mcv) {
5837 .stack_offset => |off| {
5838 // sub src_reg, fp, #off
5839 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
5840 },
5841 .stack_argument_offset => |off| {
5842 _ = try self.addInst(.{
5843 .tag = .ldr_ptr_stack_argument,
5844 .data = .{ .load_store_stack = .{
5845 .rt = src_reg,
5846 .offset = off,
5847 } },
5848 });
5849 },
5850 .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @as(u32, @intCast(addr)) }),
5851 .linker_load => |load_struct| {
5852 const tag: Mir.Inst.Tag = switch (load_struct.type) {
5853 .got => .load_memory_ptr_got,
5854 .direct => .load_memory_ptr_direct,
5855 .import => unreachable,
5856 };
5857 const atom_index = switch (self.bin_file.tag) {
5858 .macho => {
5859 @panic("TODO genSetStackArgument");
5860 // const macho_file = self.bin_file.cast(link.File.MachO).?;
5861 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
5862 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5863 },
5864 .coff => blk: {
5865 const coff_file = self.bin_file.cast(.coff).?;
5866 const atom = try coff_file.getOrCreateAtomForNav(self.owner_nav);
5867 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5868 },
5869 else => unreachable, // unsupported target format
5870 };
5871 _ = try self.addInst(.{
5872 .tag = tag,
5873 .data = .{
5874 .payload = try self.addExtra(Mir.LoadMemoryPie{
5875 .register = @intFromEnum(src_reg),
5876 .atom_index = atom_index,
5877 .sym_index = load_struct.sym_index,
5878 }),
5879 },
5880 });
5881 },
5882 else => unreachable,
5883 }
5884
5885 // add dst_reg, sp, #stack_offset
5886 _ = try self.addInst(.{
5887 .tag = .add_immediate,
5888 .data = .{ .rr_imm12_sh = .{
5889 .rd = dst_reg,
5890 .rn = .sp,
5891 .imm12 = math.cast(u12, stack_offset) orelse {
5892 return self.fail("TODO load: set reg to stack offset with all possible offsets", .{});
5893 },
5894 } },
5895 });
5896
5897 // mov len, #abi_size
5898 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
5899
5900 // memcpy(src, dst, len)
5901 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
5902 }
5903 },
5904 .compare_flags,
5905 .immediate,
5906 .ptr_stack_offset,
5907 => {
5908 const reg = try self.copyToTmpRegister(ty, mcv);
5909 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
5910 },
5911 }
5912}
5913
5914fn airBitCast(self: *Self, inst: Air.Inst.Index) InnerError!void {
5915 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5916 const result = if (self.liveness.isUnused(inst)) .dead else result: {
5917 const operand = try self.resolveInst(ty_op.operand);
5918 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
5919
5920 const operand_lock = switch (operand) {
5921 .register => |reg| self.register_manager.lockReg(reg),
5922 .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg),
5923 else => null,
5924 };
5925 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
5926
5927 const dest_ty = self.typeOfIndex(inst);
5928 const dest = try self.allocRegOrMem(dest_ty, true, inst);
5929 try self.setRegOrMem(dest_ty, dest, operand);
5930 break :result dest;
5931 };
5932 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5933}
5934
5935fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
5936 const pt = self.pt;
5937 const zcu = pt.zcu;
5938 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5939 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5940 const ptr_ty = self.typeOf(ty_op.operand);
5941 const ptr = try self.resolveInst(ty_op.operand);
5942 const array_ty = ptr_ty.childType(zcu);
5943 const array_len = @as(u32, @intCast(array_ty.arrayLen(zcu)));
5944 const ptr_bytes = 8;
5945 const stack_offset = try self.allocMem(ptr_bytes * 2, .@"8", inst);
5946 try self.genSetStack(ptr_ty, stack_offset, ptr);
5947 try self.genSetStack(Type.usize, stack_offset - ptr_bytes, .{ .immediate = array_len });
5948 break :result MCValue{ .stack_offset = stack_offset };
5949 };
5950 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5951}
5952
5953fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
5954 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5955 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
5956 self.target.cpu.arch,
5957 });
5958 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5959}
5960
5961fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
5962 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5963 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
5964 self.target.cpu.arch,
5965 });
5966 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5967}
5968
5969fn airCmpxchg(self: *Self, inst: Air.Inst.Index) InnerError!void {
5970 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5971 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5972 _ = extra;
5973
5974 return self.fail("TODO implement airCmpxchg for {}", .{
5975 self.target.cpu.arch,
5976 });
5977}
5978
5979fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) InnerError!void {
5980 _ = inst;
5981 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
5982}
5983
5984fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
5985 _ = inst;
5986 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
5987}
5988
5989fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) InnerError!void {
5990 _ = inst;
5991 _ = order;
5992 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
5993}
5994
5995fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) InnerError!void {
5996 _ = inst;
5997 if (safety) {
5998 // TODO if the value is undef, write 0xaa bytes to dest
5999 } else {
6000 // TODO if the value is undef, don't lower this instruction
6001 }
6002 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
6003}
6004
6005fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
6006 _ = inst;
6007 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
6008}
6009
6010fn airMemmove(self: *Self, inst: Air.Inst.Index) InnerError!void {
6011 _ = inst;
6012 return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
6013}
6014
6015fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void {
6016 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
6017 const operand = try self.resolveInst(un_op);
6018 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
6019 _ = operand;
6020 return self.fail("TODO implement airTagName for aarch64", .{});
6021 };
6022 return self.finishAir(inst, result, .{ un_op, .none, .none });
6023}
6024
6025fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
6026 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
6027 const operand = try self.resolveInst(un_op);
6028 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
6029 _ = operand;
6030 return self.fail("TODO implement airErrorName for aarch64", .{});
6031 };
6032 return self.finishAir(inst, result, .{ un_op, .none, .none });
6033}
6034
6035fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void {
6036 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6037 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});
6038 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
6039}
6040
6041fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void {
6042 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6043 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6044 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch});
6045 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
6046}
6047
6048fn airShuffleOne(self: *Self, inst: Air.Inst.Index) InnerError!void {
6049 _ = inst;
6050 return self.fail("TODO implement airShuffleOne for {}", .{self.target.cpu.arch});
6051}
6052
6053fn airShuffleTwo(self: *Self, inst: Air.Inst.Index) InnerError!void {
6054 _ = inst;
6055 return self.fail("TODO implement airShuffleTwo for {}", .{self.target.cpu.arch});
6056}
6057
6058fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void {
6059 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
6060 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{});
6061 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
6062}
6063
6064fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
6065 const pt = self.pt;
6066 const zcu = pt.zcu;
6067 const vector_ty = self.typeOfIndex(inst);
6068 const len = vector_ty.vectorLen(zcu);
6069 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6070 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]);
6071 const result: MCValue = res: {
6072 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
6073 return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch});
6074 };
6075
6076 if (elements.len <= Air.Liveness.bpi - 1) {
6077 var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1);
6078 @memcpy(buf[0..elements.len], elements);
6079 return self.finishAir(inst, result, buf);
6080 }
6081 var bt = try self.iterateBigTomb(inst, elements.len);
6082 for (elements) |elem| {
6083 bt.feed(elem);
6084 }
6085 return bt.finishAir(result);
6086}
6087
6088fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
6089 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6090 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
6091 _ = extra;
6092 return self.fail("TODO implement airUnionInit for aarch64", .{});
6093}
6094
6095fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void {
6096 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
6097 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
6098}
6099
6100fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void {
6101 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6102 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6103 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
6104 return self.fail("TODO implement airMulAdd for aarch64", .{});
6105 };
6106 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
6107}
6108
6109fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void {
6110 const pt = self.pt;
6111 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6112 const extra = self.air.extraData(Air.Try, pl_op.payload);
6113 const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]);
6114 const result: MCValue = result: {
6115 const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand };
6116 const error_union_ty = self.typeOf(pl_op.operand);
6117 const error_union_size = @as(u32, @intCast(error_union_ty.abiSize(pt.zcu)));
6118 const error_union_align = error_union_ty.abiAlignment(pt.zcu);
6119
6120 // The error union will die in the body. However, we need the
6121 // error union after the body in order to extract the payload
6122 // of the error union, so we create a copy of it
6123 const error_union_copy = try self.allocMem(error_union_size, error_union_align, null);
6124 try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self));
6125
6126 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
6127 const reloc = try self.condBr(is_err_result);
6128
6129 try self.genBody(body);
6130 try self.performReloc(reloc);
6131
6132 break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null);
6133 };
6134 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
6135}
6136
6137fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
6138 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6139 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
6140 const body = self.air.extra.items[extra.end..][0..extra.data.body_len];
6141 _ = body;
6142 return self.fail("TODO implement airTryPtr for arm", .{});
6143 // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
6144}
6145
6146fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
6147 const pt = self.pt;
6148 const zcu = pt.zcu;
6149
6150 // If the type has no codegen bits, no need to store it.
6151 const inst_ty = self.typeOf(inst);
6152 if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !inst_ty.isError(zcu))
6153 return MCValue{ .none = {} };
6154
6155 const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, pt)).?);
6156
6157 return self.getResolvedInstValue(inst_index);
6158}
6159
6160fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
6161 // Treat each stack item as a "layer" on top of the previous one.
6162 var i: usize = self.branch_stack.items.len;
6163 while (true) {
6164 i -= 1;
6165 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
6166 assert(mcv != .dead);
6167 return mcv;
6168 }
6169 }
6170}
6171
6172fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
6173 const mcv: MCValue = switch (try codegen.genTypedValue(
6174 self.bin_file,
6175 self.pt,
6176 self.src_loc,
6177 val,
6178 self.target,
6179 )) {
6180 .mcv => |mcv| switch (mcv) {
6181 .none => .none,
6182 .undef => .undef,
6183 .immediate => |imm| .{ .immediate = imm },
6184 .memory => |addr| .{ .memory = addr },
6185 .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
6186 .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
6187 .load_symbol, .lea_symbol, .lea_direct => unreachable, // TODO
6188 },
6189 .fail => |msg| return self.failMsg(msg),
6190 };
6191 return mcv;
6192}
6193
6194const CallMCValues = struct {
6195 args: []MCValue,
6196 return_value: MCValue,
6197 stack_byte_count: u32,
6198 stack_align: u32,
6199
6200 fn deinit(self: *CallMCValues, func: *Self) void {
6201 func.gpa.free(self.args);
6202 self.* = undefined;
6203 }
6204};
6205
6206/// Caller must call `CallMCValues.deinit`.
6207fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
6208 const pt = self.pt;
6209 const zcu = pt.zcu;
6210 const ip = &zcu.intern_pool;
6211 const fn_info = zcu.typeToFunc(fn_ty).?;
6212 const cc = fn_info.cc;
6213 var result: CallMCValues = .{
6214 .args = try self.gpa.alloc(MCValue, fn_info.param_types.len),
6215 // These undefined values must be populated before returning from this function.
6216 .return_value = undefined,
6217 .stack_byte_count = undefined,
6218 .stack_align = undefined,
6219 };
6220 errdefer self.gpa.free(result.args);
6221
6222 const ret_ty = fn_ty.fnReturnType(zcu);
6223
6224 switch (cc) {
6225 .naked => {
6226 assert(result.args.len == 0);
6227 result.return_value = .{ .unreach = {} };
6228 result.stack_byte_count = 0;
6229 result.stack_align = 1;
6230 return result;
6231 },
6232 .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => {
6233 // ARM64 Procedure Call Standard
6234 var ncrn: usize = 0; // Next Core Register Number
6235 var nsaa: u32 = 0; // Next stacked argument address
6236
6237 if (ret_ty.zigTypeTag(zcu) == .noreturn) {
6238 result.return_value = .{ .unreach = {} };
6239 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
6240 result.return_value = .{ .none = {} };
6241 } else {
6242 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu));
6243 if (ret_ty_size == 0) {
6244 assert(ret_ty.isError(zcu));
6245 result.return_value = .{ .immediate = 0 };
6246 } else if (ret_ty_size <= 8) {
6247 result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) };
6248 } else {
6249 return self.fail("TODO support more return types for ARM backend", .{});
6250 }
6251 }
6252
6253 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
6254 const param_size = @as(u32, @intCast(Type.fromInterned(ty).abiSize(zcu)));
6255 if (param_size == 0) {
6256 result_arg.* = .{ .none = {} };
6257 continue;
6258 }
6259
6260 // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned
6261 // values to spread across odd-numbered registers.
6262 if (Type.fromInterned(ty).abiAlignment(zcu) == .@"16" and cc != .aarch64_aapcs_darwin) {
6263 // Round up NCRN to the next even number
6264 ncrn += ncrn % 2;
6265 }
6266
6267 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
6268 if (param_size <= 8) {
6269 result_arg.* = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], Type.fromInterned(ty)) };
6270 ncrn += 1;
6271 } else {
6272 return self.fail("TODO MCValues with multiple registers", .{});
6273 }
6274 } else if (ncrn < 8 and nsaa == 0) {
6275 return self.fail("TODO MCValues split between registers and stack", .{});
6276 } else {
6277 ncrn = 8;
6278 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided
6279 // that the entire stack space consumed by the arguments is 8-byte aligned.
6280 if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8") {
6281 if (nsaa % 8 != 0) {
6282 nsaa += 8 - (nsaa % 8);
6283 }
6284 }
6285
6286 result_arg.* = .{ .stack_argument_offset = nsaa };
6287 nsaa += param_size;
6288 }
6289 }
6290
6291 result.stack_byte_count = nsaa;
6292 result.stack_align = 16;
6293 },
6294 .auto => {
6295 if (ret_ty.zigTypeTag(zcu) == .noreturn) {
6296 result.return_value = .{ .unreach = {} };
6297 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
6298 result.return_value = .{ .none = {} };
6299 } else {
6300 const ret_ty_size = @as(u32, @intCast(ret_ty.abiSize(zcu)));
6301 if (ret_ty_size == 0) {
6302 assert(ret_ty.isError(zcu));
6303 result.return_value = .{ .immediate = 0 };
6304 } else if (ret_ty_size <= 8) {
6305 result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) };
6306 } else {
6307 // The result is returned by reference, not by
6308 // value. This means that x0 (or w0 when pointer
6309 // size is 32 bits) will contain the address of
6310 // where this function should write the result
6311 // into.
6312 result.return_value = .{ .stack_offset = 0 };
6313 }
6314 }
6315
6316 var stack_offset: u32 = 0;
6317
6318 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
6319 if (Type.fromInterned(ty).abiSize(zcu) > 0) {
6320 const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu));
6321 const param_alignment = Type.fromInterned(ty).abiAlignment(zcu);
6322
6323 stack_offset = @intCast(param_alignment.forward(stack_offset));
6324 result_arg.* = .{ .stack_argument_offset = stack_offset };
6325 stack_offset += param_size;
6326 } else {
6327 result_arg.* = .{ .none = {} };
6328 }
6329 }
6330
6331 result.stack_byte_count = stack_offset;
6332 result.stack_align = 16;
6333 },
6334 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
6335 }
6336
6337 return result;
6338}
6339
6340/// TODO support scope overrides. Also note this logic is duplicated with `Zcu.wantSafety`.
6341fn wantSafety(self: *Self) bool {
6342 return switch (self.bin_file.comp.root_mod.optimize_mode) {
6343 .Debug => true,
6344 .ReleaseSafe => true,
6345 .ReleaseFast => false,
6346 .ReleaseSmall => false,
6347 };
6348}
6349
6350fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
6351 @branchHint(.cold);
6352 return self.pt.zcu.codegenFail(self.owner_nav, format, args);
6353}
6354
6355fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
6356 @branchHint(.cold);
6357 return self.pt.zcu.codegenFailMsg(self.owner_nav, msg);
6358}
6359
6360fn parseRegName(name: []const u8) ?Register {
6361 if (@hasDecl(Register, "parseRegName")) {
6362 return Register.parseRegName(name);
6363 }
6364 return std.meta.stringToEnum(Register, name);
6365}
6366
6367fn registerAlias(self: *Self, reg: Register, ty: Type) Register {
6368 const abi_size = ty.abiSize(self.pt.zcu);
6369
6370 switch (reg.class()) {
6371 .general_purpose => {
6372 if (abi_size == 0) {
6373 unreachable; // should be comptime-known
6374 } else if (abi_size <= 4) {
6375 return reg.toW();
6376 } else if (abi_size <= 8) {
6377 return reg.toX();
6378 } else unreachable;
6379 },
6380 .stack_pointer => unreachable, // we can't store/load the sp
6381 .floating_point => {
6382 return switch (ty.floatBits(self.target)) {
6383 16 => reg.toH(),
6384 32 => reg.toS(),
6385 64 => reg.toD(),
6386 128 => reg.toQ(),
6387
6388 80 => unreachable, // f80 registers don't exist
6389 else => unreachable,
6390 };
6391 },
6392 }
6393}
6394
6395fn typeOf(self: *Self, inst: Air.Inst.Ref) Type {
6396 return self.air.typeOf(inst, &self.pt.zcu.intern_pool);
6397}
6398
6399fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {
6400 return self.air.typeOfIndex(inst, &self.pt.zcu.intern_pool);
6401}
src/arch/aarch64/Emit.zig deleted-1349
......@@ -1,1349 +0,0 @@
1//! This file contains the functionality for lowering AArch64 MIR into
2//! machine code
3
4const Emit = @This();
5const std = @import("std");
6const math = std.math;
7const Mir = @import("Mir.zig");
8const bits = @import("bits.zig");
9const link = @import("../../link.zig");
10const Zcu = @import("../../Zcu.zig");
11const ErrorMsg = Zcu.ErrorMsg;
12const assert = std.debug.assert;
13const Instruction = bits.Instruction;
14const Register = bits.Register;
15const log = std.log.scoped(.aarch64_emit);
16
17mir: Mir,
18bin_file: *link.File,
19debug_output: link.File.DebugInfoOutput,
20target: *const std.Target,
21err_msg: ?*ErrorMsg = null,
22src_loc: Zcu.LazySrcLoc,
23code: *std.ArrayListUnmanaged(u8),
24
25prev_di_line: u32,
26prev_di_column: u32,
27
28/// Relative to the beginning of `code`.
29prev_di_pc: usize,
30
31/// The amount of stack space consumed by the saved callee-saved
32/// registers in bytes
33saved_regs_stack_space: u32,
34
35/// The branch type of every branch
36branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .empty,
37
38/// For every forward branch, maps the target instruction to a list of
39/// branches which branch to this target instruction
40branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .empty,
41
42/// For backward branches: stores the code offset of the target
43/// instruction
44///
45/// For forward branches: stores the code offset of the branch
46/// instruction
47code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
48
49/// The final stack frame size of the function (already aligned to the
50/// respective stack alignment). Does not include prologue stack space.
51stack_size: u32,
52
53const InnerError = error{
54 OutOfMemory,
55 EmitFail,
56};
57
58const BranchType = enum {
59 cbz,
60 b_cond,
61 unconditional_branch_immediate,
62
63 fn default(tag: Mir.Inst.Tag) BranchType {
64 return switch (tag) {
65 .cbz => .cbz,
66 .b, .bl => .unconditional_branch_immediate,
67 .b_cond => .b_cond,
68 else => unreachable,
69 };
70 }
71};
72
73pub fn emitMir(emit: *Emit) InnerError!void {
74 const mir_tags = emit.mir.instructions.items(.tag);
75
76 // Find smallest lowerings for branch instructions
77 try emit.lowerBranches();
78
79 // Emit machine code
80 for (mir_tags, 0..) |tag, index| {
81 const inst = @as(u32, @intCast(index));
82 switch (tag) {
83 .add_immediate => try emit.mirAddSubtractImmediate(inst),
84 .adds_immediate => try emit.mirAddSubtractImmediate(inst),
85 .cmp_immediate => try emit.mirAddSubtractImmediate(inst),
86 .sub_immediate => try emit.mirAddSubtractImmediate(inst),
87 .subs_immediate => try emit.mirAddSubtractImmediate(inst),
88
89 .asr_register => try emit.mirDataProcessing2Source(inst),
90 .lsl_register => try emit.mirDataProcessing2Source(inst),
91 .lsr_register => try emit.mirDataProcessing2Source(inst),
92 .sdiv => try emit.mirDataProcessing2Source(inst),
93 .udiv => try emit.mirDataProcessing2Source(inst),
94
95 .asr_immediate => try emit.mirShiftImmediate(inst),
96 .lsl_immediate => try emit.mirShiftImmediate(inst),
97 .lsr_immediate => try emit.mirShiftImmediate(inst),
98
99 .b_cond => try emit.mirConditionalBranchImmediate(inst),
100
101 .b => try emit.mirBranch(inst),
102 .bl => try emit.mirBranch(inst),
103
104 .cbz => try emit.mirCompareAndBranch(inst),
105
106 .blr => try emit.mirUnconditionalBranchRegister(inst),
107 .ret => try emit.mirUnconditionalBranchRegister(inst),
108
109 .brk => try emit.mirExceptionGeneration(inst),
110 .svc => try emit.mirExceptionGeneration(inst),
111
112 .call_extern => try emit.mirCallExtern(inst),
113
114 .eor_immediate => try emit.mirLogicalImmediate(inst),
115 .tst_immediate => try emit.mirLogicalImmediate(inst),
116
117 .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
118 .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
119 .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
120 .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
121 .subs_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
122
123 .add_extended_register => try emit.mirAddSubtractExtendedRegister(inst),
124 .adds_extended_register => try emit.mirAddSubtractExtendedRegister(inst),
125 .sub_extended_register => try emit.mirAddSubtractExtendedRegister(inst),
126 .subs_extended_register => try emit.mirAddSubtractExtendedRegister(inst),
127 .cmp_extended_register => try emit.mirAddSubtractExtendedRegister(inst),
128
129 .csel => try emit.mirConditionalSelect(inst),
130 .cset => try emit.mirConditionalSelect(inst),
131
132 .dbg_line => try emit.mirDbgLine(inst),
133
134 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
135 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
136
137 .and_shifted_register => try emit.mirLogicalShiftedRegister(inst),
138 .eor_shifted_register => try emit.mirLogicalShiftedRegister(inst),
139 .orr_shifted_register => try emit.mirLogicalShiftedRegister(inst),
140
141 .load_memory_got => try emit.mirLoadMemoryPie(inst),
142 .load_memory_direct => try emit.mirLoadMemoryPie(inst),
143 .load_memory_import => try emit.mirLoadMemoryPie(inst),
144 .load_memory_ptr_got => try emit.mirLoadMemoryPie(inst),
145 .load_memory_ptr_direct => try emit.mirLoadMemoryPie(inst),
146
147 .ldp => try emit.mirLoadStoreRegisterPair(inst),
148 .stp => try emit.mirLoadStoreRegisterPair(inst),
149
150 .ldr_ptr_stack => try emit.mirLoadStoreStack(inst),
151 .ldr_stack => try emit.mirLoadStoreStack(inst),
152 .ldrb_stack => try emit.mirLoadStoreStack(inst),
153 .ldrh_stack => try emit.mirLoadStoreStack(inst),
154 .ldrsb_stack => try emit.mirLoadStoreStack(inst),
155 .ldrsh_stack => try emit.mirLoadStoreStack(inst),
156 .str_stack => try emit.mirLoadStoreStack(inst),
157 .strb_stack => try emit.mirLoadStoreStack(inst),
158 .strh_stack => try emit.mirLoadStoreStack(inst),
159
160 .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst),
161 .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
162 .ldrb_stack_argument => try emit.mirLoadStackArgument(inst),
163 .ldrh_stack_argument => try emit.mirLoadStackArgument(inst),
164 .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst),
165 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),
166
167 .ldr_register => try emit.mirLoadStoreRegisterRegister(inst),
168 .ldrb_register => try emit.mirLoadStoreRegisterRegister(inst),
169 .ldrh_register => try emit.mirLoadStoreRegisterRegister(inst),
170 .str_register => try emit.mirLoadStoreRegisterRegister(inst),
171 .strb_register => try emit.mirLoadStoreRegisterRegister(inst),
172 .strh_register => try emit.mirLoadStoreRegisterRegister(inst),
173
174 .ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
175 .ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
176 .ldrh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
177 .ldrsb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
178 .ldrsh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
179 .ldrsw_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
180 .str_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
181 .strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
182 .strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
183
184 .mov_register => try emit.mirMoveRegister(inst),
185 .mov_to_from_sp => try emit.mirMoveRegister(inst),
186 .mvn => try emit.mirMoveRegister(inst),
187
188 .movk => try emit.mirMoveWideImmediate(inst),
189 .movz => try emit.mirMoveWideImmediate(inst),
190
191 .msub => try emit.mirDataProcessing3Source(inst),
192 .mul => try emit.mirDataProcessing3Source(inst),
193 .smulh => try emit.mirDataProcessing3Source(inst),
194 .smull => try emit.mirDataProcessing3Source(inst),
195 .umulh => try emit.mirDataProcessing3Source(inst),
196 .umull => try emit.mirDataProcessing3Source(inst),
197
198 .nop => try emit.mirNop(),
199
200 .push_regs => try emit.mirPushPopRegs(inst),
201 .pop_regs => try emit.mirPushPopRegs(inst),
202
203 .sbfx,
204 .ubfx,
205 => try emit.mirBitfieldExtract(inst),
206
207 .sxtb,
208 .sxth,
209 .sxtw,
210 .uxtb,
211 .uxth,
212 => try emit.mirExtend(inst),
213 }
214 }
215}
216
217pub fn deinit(emit: *Emit) void {
218 const comp = emit.bin_file.comp;
219 const gpa = comp.gpa;
220 var iter = emit.branch_forward_origins.valueIterator();
221 while (iter.next()) |origin_list| {
222 origin_list.deinit(gpa);
223 }
224
225 emit.branch_types.deinit(gpa);
226 emit.branch_forward_origins.deinit(gpa);
227 emit.code_offset_mapping.deinit(gpa);
228 emit.* = undefined;
229}
230
231fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
232 assert(offset & 0b11 == 0);
233
234 switch (tag) {
235 .cbz => {
236 if (std.math.cast(i19, @shrExact(offset, 2))) |_| {
237 return BranchType.cbz;
238 } else {
239 return emit.fail("TODO support cbz branches larger than +-1 MiB", .{});
240 }
241 },
242 .b, .bl => {
243 if (std.math.cast(i26, @shrExact(offset, 2))) |_| {
244 return BranchType.unconditional_branch_immediate;
245 } else {
246 return emit.fail("TODO support unconditional branches larger than +-128 MiB", .{});
247 }
248 },
249 .b_cond => {
250 if (std.math.cast(i19, @shrExact(offset, 2))) |_| {
251 return BranchType.b_cond;
252 } else {
253 return emit.fail("TODO support conditional branches larger than +-1 MiB", .{});
254 }
255 },
256 else => unreachable,
257 }
258}
259
260fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
261 const tag = emit.mir.instructions.items(.tag)[inst];
262
263 if (isBranch(tag)) {
264 switch (emit.branch_types.get(inst).?) {
265 .cbz,
266 .unconditional_branch_immediate,
267 .b_cond,
268 => return 4,
269 }
270 }
271
272 switch (tag) {
273 .load_memory_direct => return 3 * 4,
274 .load_memory_got,
275 .load_memory_ptr_got,
276 .load_memory_ptr_direct,
277 => return 2 * 4,
278 .pop_regs, .push_regs => {
279 const reg_list = emit.mir.instructions.items(.data)[inst].reg_list;
280 const number_of_regs = @popCount(reg_list);
281 const number_of_insts = std.math.divCeil(u6, number_of_regs, 2) catch unreachable;
282 return number_of_insts * 4;
283 },
284 .call_extern => return 4,
285 .dbg_line,
286 .dbg_epilogue_begin,
287 .dbg_prologue_end,
288 => return 0,
289 else => return 4,
290 }
291}
292
293fn isBranch(tag: Mir.Inst.Tag) bool {
294 return switch (tag) {
295 .cbz,
296 .b,
297 .bl,
298 .b_cond,
299 => true,
300 else => false,
301 };
302}
303
304fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
305 const tag = emit.mir.instructions.items(.tag)[inst];
306
307 switch (tag) {
308 .cbz => return emit.mir.instructions.items(.data)[inst].r_inst.inst,
309 .b, .bl => return emit.mir.instructions.items(.data)[inst].inst,
310 .b_cond => return emit.mir.instructions.items(.data)[inst].inst_cond.inst,
311 else => unreachable,
312 }
313}
314
315fn lowerBranches(emit: *Emit) !void {
316 const comp = emit.bin_file.comp;
317 const gpa = comp.gpa;
318 const mir_tags = emit.mir.instructions.items(.tag);
319
320 // First pass: Note down all branches and their target
321 // instructions, i.e. populate branch_types,
322 // branch_forward_origins, and code_offset_mapping
323 //
324 // TODO optimization opportunity: do this in codegen while
325 // generating MIR
326 for (mir_tags, 0..) |tag, index| {
327 const inst = @as(u32, @intCast(index));
328 if (isBranch(tag)) {
329 const target_inst = emit.branchTarget(inst);
330
331 // Remember this branch instruction
332 try emit.branch_types.put(gpa, inst, BranchType.default(tag));
333
334 // Forward branches require some extra stuff: We only
335 // know their offset once we arrive at the target
336 // instruction. Therefore, we need to be able to
337 // access the branch instruction when we visit the
338 // target instruction in order to manipulate its type
339 // etc.
340 if (target_inst > inst) {
341 // Remember the branch instruction index
342 try emit.code_offset_mapping.put(gpa, inst, 0);
343
344 if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| {
345 try origin_list.append(gpa, inst);
346 } else {
347 var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty;
348 try origin_list.append(gpa, inst);
349 try emit.branch_forward_origins.put(gpa, target_inst, origin_list);
350 }
351 }
352
353 // Remember the target instruction index so that we
354 // update the real code offset in all future passes
355 //
356 // putNoClobber may not be used as the put operation
357 // may clobber the entry when multiple branches branch
358 // to the same target instruction
359 try emit.code_offset_mapping.put(gpa, target_inst, 0);
360 }
361 }
362
363 // Further passes: Until all branches are lowered, interate
364 // through all instructions and calculate new offsets and
365 // potentially new branch types
366 var all_branches_lowered = false;
367 while (!all_branches_lowered) {
368 all_branches_lowered = true;
369 var current_code_offset: usize = 0;
370
371 for (mir_tags, 0..) |tag, index| {
372 const inst = @as(u32, @intCast(index));
373
374 // If this instruction contained in the code offset
375 // mapping (when it is a target of a branch or if it is a
376 // forward branch), update the code offset
377 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
378 offset.* = current_code_offset;
379 }
380
381 // If this instruction is a backward branch, calculate the
382 // offset, which may potentially update the branch type
383 if (isBranch(tag)) {
384 const target_inst = emit.branchTarget(inst);
385 if (target_inst < inst) {
386 const target_offset = emit.code_offset_mapping.get(target_inst).?;
387 const offset = @as(i64, @intCast(target_offset)) - @as(i64, @intCast(current_code_offset));
388 const branch_type = emit.branch_types.getPtr(inst).?;
389 const optimal_branch_type = try emit.optimalBranchType(tag, offset);
390 if (branch_type.* != optimal_branch_type) {
391 branch_type.* = optimal_branch_type;
392 all_branches_lowered = false;
393 }
394
395 log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset });
396 }
397 }
398
399 // If this instruction is the target of one or more
400 // forward branches, calculate the offset, which may
401 // potentially update the branch type
402 if (emit.branch_forward_origins.get(inst)) |origin_list| {
403 for (origin_list.items) |forward_branch_inst| {
404 const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst];
405 const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?;
406 const offset = @as(i64, @intCast(current_code_offset)) - @as(i64, @intCast(forward_branch_inst_offset));
407 const branch_type = emit.branch_types.getPtr(forward_branch_inst).?;
408 const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset);
409 if (branch_type.* != optimal_branch_type) {
410 branch_type.* = optimal_branch_type;
411 all_branches_lowered = false;
412 }
413
414 log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset });
415 }
416 }
417
418 // Increment code offset
419 current_code_offset += emit.instructionSize(inst);
420 }
421 }
422}
423
424fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
425 const comp = emit.bin_file.comp;
426 const gpa = comp.gpa;
427 const endian = emit.target.cpu.arch.endian();
428 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
429}
430
431fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
432 @branchHint(.cold);
433 assert(emit.err_msg == null);
434 const comp = emit.bin_file.comp;
435 const gpa = comp.gpa;
436 emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args);
437 return error.EmitFail;
438}
439
440fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
441 const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
442 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
443 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
444 switch (emit.debug_output) {
445 .dwarf => |dw| {
446 if (column != emit.prev_di_column) try dw.setColumn(column);
447 try dw.advancePCAndLine(delta_line, delta_pc);
448 emit.prev_di_line = line;
449 emit.prev_di_column = column;
450 emit.prev_di_pc = emit.code.items.len;
451 },
452 .plan9 => |dbg_out| {
453 if (delta_pc <= 0) return; // only do this when the pc changes
454
455 var aw: std.io.Writer.Allocating = .fromArrayList(emit.bin_file.comp.gpa, &dbg_out.dbg_line);
456 const bw = &aw.interface;
457 defer dbg_out.dbg_line = aw.toArrayList();
458
459 // increasing the line number
460 try link.File.Plan9.changeLine(bw, @intCast(delta_line));
461 // increasing the pc
462 const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
463 if (d_pc_p9 > 0) {
464 // minus one because if its the last one, we want to leave space to change the line which is one pc quanta
465 try bw.writeByte(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta);
466 const dbg_line = aw.getWritten();
467 if (dbg_out.pcop_change_index) |pci| dbg_line[pci] += 1;
468 dbg_out.pcop_change_index = @intCast(dbg_line.len - 1);
469 } else if (d_pc_p9 == 0) {
470 // we don't need to do anything, because adding the pc quanta does it for us
471 } else unreachable;
472 if (dbg_out.start_line == null)
473 dbg_out.start_line = emit.prev_di_line;
474 dbg_out.end_line = line;
475 // only do this if the pc changed
476 emit.prev_di_line = line;
477 emit.prev_di_column = column;
478 emit.prev_di_pc = emit.code.items.len;
479 },
480 .none => {},
481 }
482}
483
484fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
485 const tag = emit.mir.instructions.items(.tag)[inst];
486 switch (tag) {
487 .add_immediate,
488 .adds_immediate,
489 .sub_immediate,
490 .subs_immediate,
491 => {
492 const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh;
493 const rd = rr_imm12_sh.rd;
494 const rn = rr_imm12_sh.rn;
495 const imm12 = rr_imm12_sh.imm12;
496 const sh = rr_imm12_sh.sh == 1;
497
498 switch (tag) {
499 .add_immediate => try emit.writeInstruction(Instruction.add(rd, rn, imm12, sh)),
500 .adds_immediate => try emit.writeInstruction(Instruction.adds(rd, rn, imm12, sh)),
501 .sub_immediate => try emit.writeInstruction(Instruction.sub(rd, rn, imm12, sh)),
502 .subs_immediate => try emit.writeInstruction(Instruction.subs(rd, rn, imm12, sh)),
503 else => unreachable,
504 }
505 },
506 .cmp_immediate => {
507 const r_imm12_sh = emit.mir.instructions.items(.data)[inst].r_imm12_sh;
508 const rn = r_imm12_sh.rn;
509 const imm12 = r_imm12_sh.imm12;
510 const sh = r_imm12_sh.sh == 1;
511 const zr: Register = switch (rn.size()) {
512 32 => .wzr,
513 64 => .xzr,
514 else => unreachable,
515 };
516
517 try emit.writeInstruction(Instruction.subs(zr, rn, imm12, sh));
518 },
519 else => unreachable,
520 }
521}
522
523fn mirDataProcessing2Source(emit: *Emit, inst: Mir.Inst.Index) !void {
524 const tag = emit.mir.instructions.items(.tag)[inst];
525 const rrr = emit.mir.instructions.items(.data)[inst].rrr;
526 const rd = rrr.rd;
527 const rn = rrr.rn;
528 const rm = rrr.rm;
529
530 switch (tag) {
531 .asr_register => try emit.writeInstruction(Instruction.asrRegister(rd, rn, rm)),
532 .lsl_register => try emit.writeInstruction(Instruction.lslRegister(rd, rn, rm)),
533 .lsr_register => try emit.writeInstruction(Instruction.lsrRegister(rd, rn, rm)),
534 .sdiv => try emit.writeInstruction(Instruction.sdiv(rd, rn, rm)),
535 .udiv => try emit.writeInstruction(Instruction.udiv(rd, rn, rm)),
536 else => unreachable,
537 }
538}
539
540fn mirShiftImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
541 const tag = emit.mir.instructions.items(.tag)[inst];
542 const rr_shift = emit.mir.instructions.items(.data)[inst].rr_shift;
543 const rd = rr_shift.rd;
544 const rn = rr_shift.rn;
545 const shift = rr_shift.shift;
546
547 switch (tag) {
548 .asr_immediate => try emit.writeInstruction(Instruction.asrImmediate(rd, rn, shift)),
549 .lsl_immediate => try emit.writeInstruction(Instruction.lslImmediate(rd, rn, shift)),
550 .lsr_immediate => try emit.writeInstruction(Instruction.lsrImmediate(rd, rn, shift)),
551 else => unreachable,
552 }
553}
554
555fn mirConditionalBranchImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
556 const tag = emit.mir.instructions.items(.tag)[inst];
557 const inst_cond = emit.mir.instructions.items(.data)[inst].inst_cond;
558
559 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(inst_cond.inst).?)) - @as(i64, @intCast(emit.code.items.len));
560 const branch_type = emit.branch_types.get(inst).?;
561 log.debug("mirConditionalBranchImmediate: {} offset={}", .{ inst, offset });
562
563 switch (branch_type) {
564 .b_cond => switch (tag) {
565 .b_cond => try emit.writeInstruction(Instruction.bCond(inst_cond.cond, @as(i21, @intCast(offset)))),
566 else => unreachable,
567 },
568 else => unreachable,
569 }
570}
571
572fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
573 const tag = emit.mir.instructions.items(.tag)[inst];
574 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
575
576 log.debug("branch {}(tag: {}) -> {}(tag: {})", .{
577 inst,
578 tag,
579 target_inst,
580 emit.mir.instructions.items(.tag)[target_inst],
581 });
582
583 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(target_inst).?)) - @as(i64, @intCast(emit.code.items.len));
584 const branch_type = emit.branch_types.get(inst).?;
585 log.debug("mirBranch: {} offset={}", .{ inst, offset });
586
587 switch (branch_type) {
588 .unconditional_branch_immediate => switch (tag) {
589 .b => try emit.writeInstruction(Instruction.b(@as(i28, @intCast(offset)))),
590 .bl => try emit.writeInstruction(Instruction.bl(@as(i28, @intCast(offset)))),
591 else => unreachable,
592 },
593 else => unreachable,
594 }
595}
596
597fn mirCompareAndBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
598 const tag = emit.mir.instructions.items(.tag)[inst];
599 const r_inst = emit.mir.instructions.items(.data)[inst].r_inst;
600
601 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(r_inst.inst).?)) - @as(i64, @intCast(emit.code.items.len));
602 const branch_type = emit.branch_types.get(inst).?;
603 log.debug("mirCompareAndBranch: {} offset={}", .{ inst, offset });
604
605 switch (branch_type) {
606 .cbz => switch (tag) {
607 .cbz => try emit.writeInstruction(Instruction.cbz(r_inst.rt, @as(i21, @intCast(offset)))),
608 else => unreachable,
609 },
610 else => unreachable,
611 }
612}
613
614fn mirUnconditionalBranchRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
615 const tag = emit.mir.instructions.items(.tag)[inst];
616 const reg = emit.mir.instructions.items(.data)[inst].reg;
617
618 switch (tag) {
619 .blr => try emit.writeInstruction(Instruction.blr(reg)),
620 .ret => try emit.writeInstruction(Instruction.ret(reg)),
621 else => unreachable,
622 }
623}
624
625fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
626 const tag = emit.mir.instructions.items(.tag)[inst];
627 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
628
629 switch (tag) {
630 .brk => try emit.writeInstruction(Instruction.brk(imm16)),
631 .svc => try emit.writeInstruction(Instruction.svc(imm16)),
632 else => unreachable,
633 }
634}
635
636fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
637 const tag = emit.mir.instructions.items(.tag)[inst];
638 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
639
640 switch (tag) {
641 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
642 else => unreachable,
643 }
644}
645
646fn mirDebugPrologueEnd(emit: *Emit) !void {
647 switch (emit.debug_output) {
648 .dwarf => |dw| {
649 try dw.setPrologueEnd();
650 log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{
651 emit.prev_di_line, emit.prev_di_column,
652 });
653 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
654 },
655 .plan9 => {},
656 .none => {},
657 }
658}
659
660fn mirDebugEpilogueBegin(emit: *Emit) !void {
661 switch (emit.debug_output) {
662 .dwarf => |dw| {
663 try dw.setEpilogueBegin();
664 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
665 },
666 .plan9 => {},
667 .none => {},
668 }
669}
670
671fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
672 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);
673 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
674 _ = relocation;
675
676 const offset = blk: {
677 const offset = @as(u32, @intCast(emit.code.items.len));
678 // bl
679 try emit.writeInstruction(Instruction.bl(0));
680 break :blk offset;
681 };
682 _ = offset;
683
684 if (emit.bin_file.cast(.macho)) |macho_file| {
685 _ = macho_file;
686 @panic("TODO mirCallExtern");
687 // // Add relocation to the decl.
688 // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?;
689 // const target = macho_file.getGlobalByIndex(relocation.sym_index);
690 // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
691 // .type = .branch,
692 // .target = target,
693 // .offset = offset,
694 // .addend = 0,
695 // .pcrel = true,
696 // .length = 2,
697 // });
698 } else if (emit.bin_file.cast(.coff)) |_| {
699 unreachable; // Calling imports is handled via `.load_memory_import`
700 } else {
701 return emit.fail("Implement call_extern for linking backends != {{ COFF, MachO }}", .{});
702 }
703}
704
705fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
706 const tag = emit.mir.instructions.items(.tag)[inst];
707 const rr_bitmask = emit.mir.instructions.items(.data)[inst].rr_bitmask;
708 const rd = rr_bitmask.rd;
709 const rn = rr_bitmask.rn;
710 const imms = rr_bitmask.imms;
711 const immr = rr_bitmask.immr;
712 const n = rr_bitmask.n;
713
714 switch (tag) {
715 .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)),
716 .tst_immediate => {
717 const zr: Register = switch (rd.size()) {
718 32 => .wzr,
719 64 => .xzr,
720 else => unreachable,
721 };
722 try emit.writeInstruction(Instruction.andsImmediate(zr, rn, imms, immr, n));
723 },
724 else => unreachable,
725 }
726}
727
728fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
729 const tag = emit.mir.instructions.items(.tag)[inst];
730 switch (tag) {
731 .add_shifted_register,
732 .adds_shifted_register,
733 .sub_shifted_register,
734 .subs_shifted_register,
735 => {
736 const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift;
737 const rd = rrr_imm6_shift.rd;
738 const rn = rrr_imm6_shift.rn;
739 const rm = rrr_imm6_shift.rm;
740 const shift = rrr_imm6_shift.shift;
741 const imm6 = rrr_imm6_shift.imm6;
742
743 switch (tag) {
744 .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)),
745 .adds_shifted_register => try emit.writeInstruction(Instruction.addsShiftedRegister(rd, rn, rm, shift, imm6)),
746 .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)),
747 .subs_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)),
748 else => unreachable,
749 }
750 },
751 .cmp_shifted_register => {
752 const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift;
753 const rn = rr_imm6_shift.rn;
754 const rm = rr_imm6_shift.rm;
755 const shift = rr_imm6_shift.shift;
756 const imm6 = rr_imm6_shift.imm6;
757 const zr: Register = switch (rn.size()) {
758 32 => .wzr,
759 64 => .xzr,
760 else => unreachable,
761 };
762
763 try emit.writeInstruction(Instruction.subsShiftedRegister(zr, rn, rm, shift, imm6));
764 },
765 else => unreachable,
766 }
767}
768
769fn mirAddSubtractExtendedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
770 const tag = emit.mir.instructions.items(.tag)[inst];
771 switch (tag) {
772 .add_extended_register,
773 .adds_extended_register,
774 .sub_extended_register,
775 .subs_extended_register,
776 => {
777 const rrr_extend_shift = emit.mir.instructions.items(.data)[inst].rrr_extend_shift;
778 const rd = rrr_extend_shift.rd;
779 const rn = rrr_extend_shift.rn;
780 const rm = rrr_extend_shift.rm;
781 const ext_type = rrr_extend_shift.ext_type;
782 const imm3 = rrr_extend_shift.imm3;
783
784 switch (tag) {
785 .add_extended_register => try emit.writeInstruction(Instruction.addExtendedRegister(rd, rn, rm, ext_type, imm3)),
786 .adds_extended_register => try emit.writeInstruction(Instruction.addsExtendedRegister(rd, rn, rm, ext_type, imm3)),
787 .sub_extended_register => try emit.writeInstruction(Instruction.subExtendedRegister(rd, rn, rm, ext_type, imm3)),
788 .subs_extended_register => try emit.writeInstruction(Instruction.subsExtendedRegister(rd, rn, rm, ext_type, imm3)),
789 else => unreachable,
790 }
791 },
792 .cmp_extended_register => {
793 const rr_extend_shift = emit.mir.instructions.items(.data)[inst].rr_extend_shift;
794 const rn = rr_extend_shift.rn;
795 const rm = rr_extend_shift.rm;
796 const ext_type = rr_extend_shift.ext_type;
797 const imm3 = rr_extend_shift.imm3;
798 const zr: Register = switch (rn.size()) {
799 32 => .wzr,
800 64 => .xzr,
801 else => unreachable,
802 };
803
804 try emit.writeInstruction(Instruction.subsExtendedRegister(zr, rn, rm, ext_type, imm3));
805 },
806 else => unreachable,
807 }
808}
809
810fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void {
811 const tag = emit.mir.instructions.items(.tag)[inst];
812 switch (tag) {
813 .csel => {
814 const rrr_cond = emit.mir.instructions.items(.data)[inst].rrr_cond;
815 const rd = rrr_cond.rd;
816 const rn = rrr_cond.rn;
817 const rm = rrr_cond.rm;
818 const cond = rrr_cond.cond;
819 try emit.writeInstruction(Instruction.csel(rd, rn, rm, cond));
820 },
821 .cset => {
822 const r_cond = emit.mir.instructions.items(.data)[inst].r_cond;
823 const zr: Register = switch (r_cond.rd.size()) {
824 32 => .wzr,
825 64 => .xzr,
826 else => unreachable,
827 };
828 try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond.negate()));
829 },
830 else => unreachable,
831 }
832}
833
834fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
835 const tag = emit.mir.instructions.items(.tag)[inst];
836 const rrr_imm6_logical_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_logical_shift;
837 const rd = rrr_imm6_logical_shift.rd;
838 const rn = rrr_imm6_logical_shift.rn;
839 const rm = rrr_imm6_logical_shift.rm;
840 const shift = rrr_imm6_logical_shift.shift;
841 const imm6 = rrr_imm6_logical_shift.imm6;
842
843 switch (tag) {
844 .and_shifted_register => try emit.writeInstruction(Instruction.andShiftedRegister(rd, rn, rm, shift, imm6)),
845 .eor_shifted_register => try emit.writeInstruction(Instruction.eorShiftedRegister(rd, rn, rm, shift, imm6)),
846 .orr_shifted_register => try emit.writeInstruction(Instruction.orrShiftedRegister(rd, rn, rm, shift, imm6)),
847 else => unreachable,
848 }
849}
850
851fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
852 const tag = emit.mir.instructions.items(.tag)[inst];
853 const payload = emit.mir.instructions.items(.data)[inst].payload;
854 const data = emit.mir.extraData(Mir.LoadMemoryPie, payload).data;
855 const reg = @as(Register, @enumFromInt(data.register));
856
857 // PC-relative displacement to the entry in memory.
858 // adrp
859 const offset = @as(u32, @intCast(emit.code.items.len));
860 try emit.writeInstruction(Instruction.adrp(reg.toX(), 0));
861
862 switch (tag) {
863 .load_memory_got,
864 .load_memory_import,
865 => {
866 // ldr reg, reg, offset
867 try emit.writeInstruction(Instruction.ldr(
868 reg,
869 reg.toX(),
870 Instruction.LoadStoreOffset.imm(0),
871 ));
872 },
873 .load_memory_direct => {
874 // We cannot load the offset directly as it may not be aligned properly.
875 // For example, load for 64bit register will require the target address offset
876 // to be 8-byte aligned, while the value might have non-8-byte natural alignment,
877 // meaning the linker might have put it at a non-8-byte aligned address. To circumvent
878 // this, we use `adrp, add` to form the address value which we then dereference with
879 // `ldr`.
880 // Note that this can potentially be optimised out by the codegen/linker if the
881 // target address is appropriately aligned.
882 // add reg, reg, offset
883 try emit.writeInstruction(Instruction.add(reg.toX(), reg.toX(), 0, false));
884 // ldr reg, reg, offset
885 try emit.writeInstruction(Instruction.ldr(
886 reg,
887 reg.toX(),
888 Instruction.LoadStoreOffset.imm(0),
889 ));
890 },
891 .load_memory_ptr_direct,
892 .load_memory_ptr_got,
893 => {
894 // add reg, reg, offset
895 try emit.writeInstruction(Instruction.add(reg, reg, 0, false));
896 },
897 else => unreachable,
898 }
899
900 if (emit.bin_file.cast(.macho)) |macho_file| {
901 _ = macho_file;
902 @panic("TODO mirLoadMemoryPie");
903 // const Atom = link.File.MachO.Atom;
904 // const Relocation = Atom.Relocation;
905 // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?;
906 // try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{
907 // .target = .{ .sym_index = data.sym_index },
908 // .offset = offset,
909 // .addend = 0,
910 // .pcrel = true,
911 // .length = 2,
912 // .type = switch (tag) {
913 // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_page,
914 // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.page,
915 // else => unreachable,
916 // },
917 // }, .{
918 // .target = .{ .sym_index = data.sym_index },
919 // .offset = offset + 4,
920 // .addend = 0,
921 // .pcrel = false,
922 // .length = 2,
923 // .type = switch (tag) {
924 // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_pageoff,
925 // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.pageoff,
926 // else => unreachable,
927 // },
928 // } });
929 } else if (emit.bin_file.cast(.coff)) |coff_file| {
930 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
931 const target = switch (tag) {
932 .load_memory_got,
933 .load_memory_ptr_got,
934 .load_memory_direct,
935 .load_memory_ptr_direct,
936 => link.File.Coff.SymbolWithLoc{ .sym_index = data.sym_index, .file = null },
937 .load_memory_import => coff_file.getGlobalByIndex(data.sym_index),
938 else => unreachable,
939 };
940 try coff_file.addRelocation(atom_index, .{
941 .target = target,
942 .offset = offset,
943 .addend = 0,
944 .pcrel = true,
945 .length = 2,
946 .type = switch (tag) {
947 .load_memory_got,
948 .load_memory_ptr_got,
949 => .got_page,
950 .load_memory_direct,
951 .load_memory_ptr_direct,
952 => .page,
953 .load_memory_import => .import_page,
954 else => unreachable,
955 },
956 });
957 try coff_file.addRelocation(atom_index, .{
958 .target = target,
959 .offset = offset + 4,
960 .addend = 0,
961 .pcrel = false,
962 .length = 2,
963 .type = switch (tag) {
964 .load_memory_got,
965 .load_memory_ptr_got,
966 => .got_pageoff,
967 .load_memory_direct,
968 .load_memory_ptr_direct,
969 => .pageoff,
970 .load_memory_import => .import_pageoff,
971 else => unreachable,
972 },
973 });
974 } else {
975 return emit.fail("TODO implement load_memory for PIE GOT indirection on this platform", .{});
976 }
977}
978
979fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void {
980 const tag = emit.mir.instructions.items(.tag)[inst];
981 const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair;
982 const rt = load_store_register_pair.rt;
983 const rt2 = load_store_register_pair.rt2;
984 const rn = load_store_register_pair.rn;
985 const offset = load_store_register_pair.offset;
986
987 switch (tag) {
988 .stp => try emit.writeInstruction(Instruction.stp(rt, rt2, rn, offset)),
989 .ldp => try emit.writeInstruction(Instruction.ldp(rt, rt2, rn, offset)),
990 else => unreachable,
991 }
992}
993
994fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void {
995 const tag = emit.mir.instructions.items(.tag)[inst];
996 const load_store_stack = emit.mir.instructions.items(.data)[inst].load_store_stack;
997 const rt = load_store_stack.rt;
998
999 const raw_offset = emit.stack_size + emit.saved_regs_stack_space + load_store_stack.offset;
1000 switch (tag) {
1001 .ldr_ptr_stack_argument => {
1002 const offset = if (math.cast(u12, raw_offset)) |imm| imm else {
1003 return emit.fail("TODO load stack argument ptr with larger offset", .{});
1004 };
1005
1006 switch (tag) {
1007 .ldr_ptr_stack_argument => try emit.writeInstruction(Instruction.add(rt, .sp, offset, false)),
1008 else => unreachable,
1009 }
1010 },
1011 .ldrb_stack_argument, .ldrsb_stack_argument => {
1012 const offset = if (math.cast(u12, raw_offset)) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1013 return emit.fail("TODO load stack argument byte with larger offset", .{});
1014 };
1015
1016 switch (tag) {
1017 .ldrb_stack_argument => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
1018 .ldrsb_stack_argument => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)),
1019 else => unreachable,
1020 }
1021 },
1022 .ldrh_stack_argument, .ldrsh_stack_argument => {
1023 assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
1024 const offset = if (math.cast(u12, @divExact(raw_offset, 2))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1025 return emit.fail("TODO load stack argument halfword with larger offset", .{});
1026 };
1027
1028 switch (tag) {
1029 .ldrh_stack_argument => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)),
1030 .ldrsh_stack_argument => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)),
1031 else => unreachable,
1032 }
1033 },
1034 .ldr_stack_argument => {
1035 const alignment: u32 = switch (rt.size()) {
1036 32 => 4,
1037 64 => 8,
1038 else => unreachable,
1039 };
1040
1041 assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry
1042 const offset = if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1043 return emit.fail("TODO load stack argument with larger offset", .{});
1044 };
1045
1046 switch (tag) {
1047 .ldr_stack_argument => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
1048 else => unreachable,
1049 }
1050 },
1051 else => unreachable,
1052 }
1053}
1054
1055fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
1056 const tag = emit.mir.instructions.items(.tag)[inst];
1057 const load_store_stack = emit.mir.instructions.items(.data)[inst].load_store_stack;
1058 const rt = load_store_stack.rt;
1059
1060 const raw_offset = emit.stack_size - load_store_stack.offset;
1061 switch (tag) {
1062 .ldr_ptr_stack => {
1063 const offset = if (math.cast(u12, raw_offset)) |imm| imm else {
1064 return emit.fail("TODO load stack argument ptr with larger offset", .{});
1065 };
1066
1067 switch (tag) {
1068 .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(rt, .sp, offset, false)),
1069 else => unreachable,
1070 }
1071 },
1072 .ldrb_stack, .ldrsb_stack, .strb_stack => {
1073 const offset = if (math.cast(u12, raw_offset)) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1074 return emit.fail("TODO load/store stack byte with larger offset", .{});
1075 };
1076
1077 switch (tag) {
1078 .ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
1079 .ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)),
1080 .strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
1081 else => unreachable,
1082 }
1083 },
1084 .ldrh_stack, .ldrsh_stack, .strh_stack => {
1085 assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
1086 const offset = if (math.cast(u12, @divExact(raw_offset, 2))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1087 return emit.fail("TODO load/store stack halfword with larger offset", .{});
1088 };
1089
1090 switch (tag) {
1091 .ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)),
1092 .ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)),
1093 .strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
1094 else => unreachable,
1095 }
1096 },
1097 .ldr_stack, .str_stack => {
1098 const alignment: u32 = switch (rt.size()) {
1099 32 => 4,
1100 64 => 8,
1101 else => unreachable,
1102 };
1103
1104 assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry
1105 const offset = if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1106 return emit.fail("TODO load/store stack with larger offset", .{});
1107 };
1108
1109 switch (tag) {
1110 .ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
1111 .str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)),
1112 else => unreachable,
1113 }
1114 },
1115 else => unreachable,
1116 }
1117}
1118
1119fn mirLoadStoreRegisterImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
1120 const tag = emit.mir.instructions.items(.tag)[inst];
1121 const load_store_register_immediate = emit.mir.instructions.items(.data)[inst].load_store_register_immediate;
1122 const rt = load_store_register_immediate.rt;
1123 const rn = load_store_register_immediate.rn;
1124 const offset = Instruction.LoadStoreOffset{ .immediate = load_store_register_immediate.offset };
1125
1126 switch (tag) {
1127 .ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)),
1128 .ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)),
1129 .ldrh_immediate => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)),
1130 .ldrsb_immediate => try emit.writeInstruction(Instruction.ldrsb(rt, rn, offset)),
1131 .ldrsh_immediate => try emit.writeInstruction(Instruction.ldrsh(rt, rn, offset)),
1132 .ldrsw_immediate => try emit.writeInstruction(Instruction.ldrsw(rt, rn, offset)),
1133 .str_immediate => try emit.writeInstruction(Instruction.str(rt, rn, offset)),
1134 .strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)),
1135 .strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)),
1136 else => unreachable,
1137 }
1138}
1139
1140fn mirLoadStoreRegisterRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
1141 const tag = emit.mir.instructions.items(.tag)[inst];
1142 const load_store_register_register = emit.mir.instructions.items(.data)[inst].load_store_register_register;
1143 const rt = load_store_register_register.rt;
1144 const rn = load_store_register_register.rn;
1145 const offset = Instruction.LoadStoreOffset{ .register = load_store_register_register.offset };
1146
1147 switch (tag) {
1148 .ldr_register => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)),
1149 .ldrb_register => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)),
1150 .ldrh_register => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)),
1151 .str_register => try emit.writeInstruction(Instruction.str(rt, rn, offset)),
1152 .strb_register => try emit.writeInstruction(Instruction.strb(rt, rn, offset)),
1153 .strh_register => try emit.writeInstruction(Instruction.strh(rt, rn, offset)),
1154 else => unreachable,
1155 }
1156}
1157
1158fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
1159 const tag = emit.mir.instructions.items(.tag)[inst];
1160 switch (tag) {
1161 .mov_register => {
1162 const rr = emit.mir.instructions.items(.data)[inst].rr;
1163 const zr: Register = switch (rr.rd.size()) {
1164 32 => .wzr,
1165 64 => .xzr,
1166 else => unreachable,
1167 };
1168
1169 try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, zr, rr.rn, .lsl, 0));
1170 },
1171 .mov_to_from_sp => {
1172 const rr = emit.mir.instructions.items(.data)[inst].rr;
1173 try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false));
1174 },
1175 .mvn => {
1176 const rr_imm6_logical_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_logical_shift;
1177 const rd = rr_imm6_logical_shift.rd;
1178 const rm = rr_imm6_logical_shift.rm;
1179 const shift = rr_imm6_logical_shift.shift;
1180 const imm6 = rr_imm6_logical_shift.imm6;
1181 const zr: Register = switch (rd.size()) {
1182 32 => .wzr,
1183 64 => .xzr,
1184 else => unreachable,
1185 };
1186
1187 try emit.writeInstruction(Instruction.ornShiftedRegister(rd, zr, rm, shift, imm6));
1188 },
1189 else => unreachable,
1190 }
1191}
1192
1193fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
1194 const tag = emit.mir.instructions.items(.tag)[inst];
1195 const r_imm16_sh = emit.mir.instructions.items(.data)[inst].r_imm16_sh;
1196
1197 switch (tag) {
1198 .movz => try emit.writeInstruction(Instruction.movz(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)),
1199 .movk => try emit.writeInstruction(Instruction.movk(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)),
1200 else => unreachable,
1201 }
1202}
1203
1204fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void {
1205 const tag = emit.mir.instructions.items(.tag)[inst];
1206
1207 switch (tag) {
1208 .mul,
1209 .smulh,
1210 .smull,
1211 .umulh,
1212 .umull,
1213 => {
1214 const rrr = emit.mir.instructions.items(.data)[inst].rrr;
1215 switch (tag) {
1216 .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)),
1217 .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)),
1218 .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)),
1219 .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)),
1220 .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)),
1221 else => unreachable,
1222 }
1223 },
1224 .msub => {
1225 const rrrr = emit.mir.instructions.items(.data)[inst].rrrr;
1226 switch (tag) {
1227 .msub => try emit.writeInstruction(Instruction.msub(rrrr.rd, rrrr.rn, rrrr.rm, rrrr.ra)),
1228 else => unreachable,
1229 }
1230 },
1231 else => unreachable,
1232 }
1233}
1234
1235fn mirNop(emit: *Emit) !void {
1236 try emit.writeInstruction(Instruction.nop());
1237}
1238
1239fn regListIsSet(reg_list: u32, reg: Register) bool {
1240 return reg_list & @as(u32, 1) << @as(u5, @intCast(reg.id())) != 0;
1241}
1242
1243fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
1244 const tag = emit.mir.instructions.items(.tag)[inst];
1245 const reg_list = emit.mir.instructions.items(.data)[inst].reg_list;
1246
1247 if (regListIsSet(reg_list, .xzr)) return emit.fail("xzr is not a valid register for {}", .{tag});
1248
1249 // sp must be aligned at all times, so we only use stp and ldp
1250 // instructions for minimal instruction count.
1251 //
1252 // However, if we have an odd number of registers, for pop_regs we
1253 // use one ldr instruction followed by zero or more ldp
1254 // instructions; for push_regs we use zero or more stp
1255 // instructions followed by one str instruction.
1256 const number_of_regs = @popCount(reg_list);
1257 const odd_number_of_regs = number_of_regs % 2 != 0;
1258
1259 switch (tag) {
1260 .pop_regs => {
1261 var i: u6 = 32;
1262 var count: u6 = 0;
1263 var other_reg: ?Register = null;
1264 while (i > 0) : (i -= 1) {
1265 const reg = @as(Register, @enumFromInt(i - 1));
1266 if (regListIsSet(reg_list, reg)) {
1267 if (count == 0 and odd_number_of_regs) {
1268 try emit.writeInstruction(Instruction.ldr(
1269 reg,
1270 .sp,
1271 Instruction.LoadStoreOffset.imm_post_index(16),
1272 ));
1273 } else if (other_reg) |r| {
1274 try emit.writeInstruction(Instruction.ldp(
1275 reg,
1276 r,
1277 .sp,
1278 Instruction.LoadStorePairOffset.post_index(16),
1279 ));
1280 other_reg = null;
1281 } else {
1282 other_reg = reg;
1283 }
1284 count += 1;
1285 }
1286 }
1287 assert(count == number_of_regs);
1288 },
1289 .push_regs => {
1290 var i: u6 = 0;
1291 var count: u6 = 0;
1292 var other_reg: ?Register = null;
1293 while (i < 32) : (i += 1) {
1294 const reg = @as(Register, @enumFromInt(i));
1295 if (regListIsSet(reg_list, reg)) {
1296 if (count == number_of_regs - 1 and odd_number_of_regs) {
1297 try emit.writeInstruction(Instruction.str(
1298 reg,
1299 .sp,
1300 Instruction.LoadStoreOffset.imm_pre_index(-16),
1301 ));
1302 } else if (other_reg) |r| {
1303 try emit.writeInstruction(Instruction.stp(
1304 r,
1305 reg,
1306 .sp,
1307 Instruction.LoadStorePairOffset.pre_index(-16),
1308 ));
1309 other_reg = null;
1310 } else {
1311 other_reg = reg;
1312 }
1313 count += 1;
1314 }
1315 }
1316 assert(count == number_of_regs);
1317 },
1318 else => unreachable,
1319 }
1320}
1321
1322fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void {
1323 const tag = emit.mir.instructions.items(.tag)[inst];
1324 const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width;
1325 const rd = rr_lsb_width.rd;
1326 const rn = rr_lsb_width.rn;
1327 const lsb = rr_lsb_width.lsb;
1328 const width = rr_lsb_width.width;
1329
1330 switch (tag) {
1331 .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)),
1332 .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)),
1333 else => unreachable,
1334 }
1335}
1336
1337fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void {
1338 const tag = emit.mir.instructions.items(.tag)[inst];
1339 const rr = emit.mir.instructions.items(.data)[inst].rr;
1340
1341 switch (tag) {
1342 .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)),
1343 .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)),
1344 .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)),
1345 .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)),
1346 .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)),
1347 else => unreachable,
1348 }
1349}
src/arch/aarch64/Mir.zig deleted-568
......@@ -1,568 +0,0 @@
1//! Machine Intermediate Representation.
2//! This data is produced by AArch64 Codegen or AArch64 assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const Mir = @This();
10const std = @import("std");
11const builtin = @import("builtin");
12const assert = std.debug.assert;
13
14const bits = @import("bits.zig");
15const Register = bits.Register;
16const InternPool = @import("../../InternPool.zig");
17const Emit = @import("Emit.zig");
18const codegen = @import("../../codegen.zig");
19const link = @import("../../link.zig");
20const Zcu = @import("../../Zcu.zig");
21
22max_end_stack: u32,
23saved_regs_stack_space: u32,
24
25instructions: std.MultiArrayList(Inst).Slice,
26/// The meaning of this data is determined by `Inst.Tag` value.
27extra: []const u32,
28
29pub const Inst = struct {
30 tag: Tag,
31 /// The meaning of this depends on `tag`.
32 data: Data,
33
34 pub const Tag = enum(u16) {
35 /// Add (immediate)
36 add_immediate,
37 /// Add, update condition flags (immediate)
38 adds_immediate,
39 /// Add (shifted register)
40 add_shifted_register,
41 /// Add, update condition flags (shifted register)
42 adds_shifted_register,
43 /// Add (extended register)
44 add_extended_register,
45 /// Add, update condition flags (extended register)
46 adds_extended_register,
47 /// Bitwise AND (shifted register)
48 and_shifted_register,
49 /// Arithmetic Shift Right (immediate)
50 asr_immediate,
51 /// Arithmetic Shift Right (register)
52 asr_register,
53 /// Branch conditionally
54 b_cond,
55 /// Branch
56 b,
57 /// Branch with Link
58 bl,
59 /// Branch with Link to Register
60 blr,
61 /// Breakpoint
62 brk,
63 /// Pseudo-instruction: Call extern
64 call_extern,
65 /// Compare and Branch on Zero
66 cbz,
67 /// Compare (immediate)
68 cmp_immediate,
69 /// Compare (shifted register)
70 cmp_shifted_register,
71 /// Compare (extended register)
72 cmp_extended_register,
73 /// Conditional Select
74 csel,
75 /// Conditional set
76 cset,
77 /// Pseudo-instruction: End of prologue
78 dbg_prologue_end,
79 /// Pseudo-instruction: Beginning of epilogue
80 dbg_epilogue_begin,
81 /// Pseudo-instruction: Update debug line
82 dbg_line,
83 /// Bitwise Exclusive OR (immediate)
84 eor_immediate,
85 /// Bitwise Exclusive OR (shifted register)
86 eor_shifted_register,
87 /// Loads the contents into a register
88 ///
89 /// Payload is `LoadMemoryPie`
90 load_memory_got,
91 /// Loads the contents into a register
92 ///
93 /// Payload is `LoadMemoryPie`
94 load_memory_direct,
95 /// Loads the contents into a register
96 ///
97 /// Payload is `LoadMemoryPie`
98 load_memory_import,
99 /// Loads the address into a register
100 ///
101 /// Payload is `LoadMemoryPie`
102 load_memory_ptr_got,
103 /// Loads the address into a register
104 ///
105 /// Payload is `LoadMemoryPie`
106 load_memory_ptr_direct,
107 /// Load Pair of Registers
108 ldp,
109 /// Pseudo-instruction: Load pointer to stack item
110 ldr_ptr_stack,
111 /// Pseudo-instruction: Load pointer to stack argument
112 ldr_ptr_stack_argument,
113 /// Pseudo-instruction: Load from stack
114 ldr_stack,
115 /// Pseudo-instruction: Load from stack argument
116 ldr_stack_argument,
117 /// Load Register (immediate)
118 ldr_immediate,
119 /// Load Register (register)
120 ldr_register,
121 /// Pseudo-instruction: Load byte from stack
122 ldrb_stack,
123 /// Pseudo-instruction: Load byte from stack argument
124 ldrb_stack_argument,
125 /// Load Register Byte (immediate)
126 ldrb_immediate,
127 /// Load Register Byte (register)
128 ldrb_register,
129 /// Pseudo-instruction: Load halfword from stack
130 ldrh_stack,
131 /// Pseudo-instruction: Load halfword from stack argument
132 ldrh_stack_argument,
133 /// Load Register Halfword (immediate)
134 ldrh_immediate,
135 /// Load Register Halfword (register)
136 ldrh_register,
137 /// Load Register Signed Byte (immediate)
138 ldrsb_immediate,
139 /// Pseudo-instruction: Load signed byte from stack
140 ldrsb_stack,
141 /// Pseudo-instruction: Load signed byte from stack argument
142 ldrsb_stack_argument,
143 /// Load Register Signed Halfword (immediate)
144 ldrsh_immediate,
145 /// Pseudo-instruction: Load signed halfword from stack
146 ldrsh_stack,
147 /// Pseudo-instruction: Load signed halfword from stack argument
148 ldrsh_stack_argument,
149 /// Load Register Signed Word (immediate)
150 ldrsw_immediate,
151 /// Logical Shift Left (immediate)
152 lsl_immediate,
153 /// Logical Shift Left (register)
154 lsl_register,
155 /// Logical Shift Right (immediate)
156 lsr_immediate,
157 /// Logical Shift Right (register)
158 lsr_register,
159 /// Move (to/from SP)
160 mov_to_from_sp,
161 /// Move (register)
162 mov_register,
163 /// Move wide with keep
164 movk,
165 /// Move wide with zero
166 movz,
167 /// Multiply-subtract
168 msub,
169 /// Multiply
170 mul,
171 /// Bitwise NOT
172 mvn,
173 /// No Operation
174 nop,
175 /// Bitwise inclusive OR (shifted register)
176 orr_shifted_register,
177 /// Pseudo-instruction: Pop multiple registers
178 pop_regs,
179 /// Pseudo-instruction: Push multiple registers
180 push_regs,
181 /// Return from subroutine
182 ret,
183 /// Signed bitfield extract
184 sbfx,
185 /// Signed divide
186 sdiv,
187 /// Signed multiply high
188 smulh,
189 /// Signed multiply long
190 smull,
191 /// Signed extend byte
192 sxtb,
193 /// Signed extend halfword
194 sxth,
195 /// Signed extend word
196 sxtw,
197 /// Store Pair of Registers
198 stp,
199 /// Pseudo-instruction: Store to stack
200 str_stack,
201 /// Store Register (immediate)
202 str_immediate,
203 /// Store Register (register)
204 str_register,
205 /// Pseudo-instruction: Store byte to stack
206 strb_stack,
207 /// Store Register Byte (immediate)
208 strb_immediate,
209 /// Store Register Byte (register)
210 strb_register,
211 /// Pseudo-instruction: Store halfword to stack
212 strh_stack,
213 /// Store Register Halfword (immediate)
214 strh_immediate,
215 /// Store Register Halfword (register)
216 strh_register,
217 /// Subtract (immediate)
218 sub_immediate,
219 /// Subtract, update condition flags (immediate)
220 subs_immediate,
221 /// Subtract (shifted register)
222 sub_shifted_register,
223 /// Subtract, update condition flags (shifted register)
224 subs_shifted_register,
225 /// Subtract (extended register)
226 sub_extended_register,
227 /// Subtract, update condition flags (extended register)
228 subs_extended_register,
229 /// Supervisor Call
230 svc,
231 /// Test bits (immediate)
232 tst_immediate,
233 /// Unsigned bitfield extract
234 ubfx,
235 /// Unsigned divide
236 udiv,
237 /// Unsigned multiply high
238 umulh,
239 /// Unsigned multiply long
240 umull,
241 /// Unsigned extend byte
242 uxtb,
243 /// Unsigned extend halfword
244 uxth,
245 };
246
247 /// The position of an MIR instruction within the `Mir` instructions array.
248 pub const Index = u32;
249
250 /// All instructions have a 4-byte payload, which is contained within
251 /// this union. `Tag` determines which union field is active, as well as
252 /// how to interpret the data within.
253 pub const Data = union {
254 /// No additional data
255 ///
256 /// Used by e.g. nop
257 nop: void,
258 /// Another instruction
259 ///
260 /// Used by e.g. b
261 inst: Index,
262 /// Relocation for the linker where:
263 /// * `atom_index` is the index of the source
264 /// * `sym_index` is the index of the target
265 ///
266 /// Used by e.g. call_extern
267 relocation: struct {
268 /// Index of the containing atom.
269 atom_index: u32,
270 /// Index into the linker's string table.
271 sym_index: u32,
272 },
273 /// A 16-bit immediate value.
274 ///
275 /// Used by e.g. svc
276 imm16: u16,
277 /// Index into `extra`. Meaning of what can be found there is context-dependent.
278 payload: u32,
279 /// A register
280 ///
281 /// Used by e.g. blr
282 reg: Register,
283 /// Multiple registers
284 ///
285 /// Used by e.g. pop_regs
286 reg_list: u32,
287 /// Another instruction and a condition
288 ///
289 /// Used by e.g. b_cond
290 inst_cond: struct {
291 inst: Index,
292 cond: bits.Instruction.Condition,
293 },
294 /// A register, an unsigned 16-bit immediate, and an optional shift
295 ///
296 /// Used by e.g. movz
297 r_imm16_sh: struct {
298 rd: Register,
299 imm16: u16,
300 hw: u2 = 0,
301 },
302 /// A register and a condition
303 ///
304 /// Used by e.g. cset
305 r_cond: struct {
306 rd: Register,
307 cond: bits.Instruction.Condition,
308 },
309 /// A register and another instruction
310 ///
311 /// Used by e.g. cbz
312 r_inst: struct {
313 rt: Register,
314 inst: Index,
315 },
316 /// A register, an unsigned 12-bit immediate, and an optional shift
317 ///
318 /// Used by e.g. cmp_immediate
319 r_imm12_sh: struct {
320 rn: Register,
321 imm12: u12,
322 sh: u1 = 0,
323 },
324 /// Two registers
325 ///
326 /// Used by e.g. mov_register
327 rr: struct {
328 rd: Register,
329 rn: Register,
330 },
331 /// Two registers, an unsigned 12-bit immediate, and an optional shift
332 ///
333 /// Used by e.g. sub_immediate
334 rr_imm12_sh: struct {
335 rd: Register,
336 rn: Register,
337 imm12: u12,
338 sh: u1 = 0,
339 },
340 /// Two registers and a shift (shift type and 6-bit amount)
341 ///
342 /// Used by e.g. cmp_shifted_register
343 rr_imm6_shift: struct {
344 rn: Register,
345 rm: Register,
346 imm6: u6,
347 shift: bits.Instruction.AddSubtractShiftedRegisterShift,
348 },
349 /// Two registers with sign-extension (extension type and 3-bit shift amount)
350 ///
351 /// Used by e.g. cmp_extended_register
352 rr_extend_shift: struct {
353 rn: Register,
354 rm: Register,
355 ext_type: bits.Instruction.AddSubtractExtendedRegisterOption,
356 imm3: u3,
357 },
358 /// Two registers and a shift (logical instruction version)
359 /// (shift type and 6-bit amount)
360 ///
361 /// Used by e.g. mvn
362 rr_imm6_logical_shift: struct {
363 rd: Register,
364 rm: Register,
365 imm6: u6,
366 shift: bits.Instruction.LogicalShiftedRegisterShift,
367 },
368 /// Two registers and a lsb (range 0-63) and a width (range
369 /// 1-64)
370 ///
371 /// Used by e.g. ubfx
372 rr_lsb_width: struct {
373 rd: Register,
374 rn: Register,
375 lsb: u6,
376 width: u7,
377 },
378 /// Two registers and a bitmask immediate
379 ///
380 /// Used by e.g. eor_immediate
381 rr_bitmask: struct {
382 rd: Register,
383 rn: Register,
384 imms: u6,
385 immr: u6,
386 n: u1,
387 },
388 /// Two registers and a 6-bit unsigned shift
389 ///
390 /// Used by e.g. lsl_immediate
391 rr_shift: struct {
392 rd: Register,
393 rn: Register,
394 shift: u6,
395 },
396 /// Three registers
397 ///
398 /// Used by e.g. mul
399 rrr: struct {
400 rd: Register,
401 rn: Register,
402 rm: Register,
403 },
404 /// Three registers and a condition
405 ///
406 /// Used by e.g. csel
407 rrr_cond: struct {
408 rd: Register,
409 rn: Register,
410 rm: Register,
411 cond: bits.Instruction.Condition,
412 },
413 /// Three registers and a shift (shift type and 6-bit amount)
414 ///
415 /// Used by e.g. add_shifted_register
416 rrr_imm6_shift: struct {
417 rd: Register,
418 rn: Register,
419 rm: Register,
420 imm6: u6,
421 shift: bits.Instruction.AddSubtractShiftedRegisterShift,
422 },
423 /// Three registers with sign-extension (extension type and 3-bit shift amount)
424 ///
425 /// Used by e.g. add_extended_register
426 rrr_extend_shift: struct {
427 rd: Register,
428 rn: Register,
429 rm: Register,
430 ext_type: bits.Instruction.AddSubtractExtendedRegisterOption,
431 imm3: u3,
432 },
433 /// Three registers and a shift (logical instruction version)
434 /// (shift type and 6-bit amount)
435 ///
436 /// Used by e.g. eor_shifted_register
437 rrr_imm6_logical_shift: struct {
438 rd: Register,
439 rn: Register,
440 rm: Register,
441 imm6: u6,
442 shift: bits.Instruction.LogicalShiftedRegisterShift,
443 },
444 /// Two registers and a LoadStoreOffsetImmediate
445 ///
446 /// Used by e.g. str_immediate
447 load_store_register_immediate: struct {
448 rt: Register,
449 rn: Register,
450 offset: bits.Instruction.LoadStoreOffsetImmediate,
451 },
452 /// Two registers and a LoadStoreOffsetRegister
453 ///
454 /// Used by e.g. str_register
455 load_store_register_register: struct {
456 rt: Register,
457 rn: Register,
458 offset: bits.Instruction.LoadStoreOffsetRegister,
459 },
460 /// A register and a stack offset
461 ///
462 /// Used by e.g. str_stack
463 load_store_stack: struct {
464 rt: Register,
465 offset: u32,
466 },
467 /// Three registers and a LoadStorePairOffset
468 ///
469 /// Used by e.g. stp
470 load_store_register_pair: struct {
471 rt: Register,
472 rt2: Register,
473 rn: Register,
474 offset: bits.Instruction.LoadStorePairOffset,
475 },
476 /// Four registers
477 ///
478 /// Used by e.g. msub
479 rrrr: struct {
480 rd: Register,
481 rn: Register,
482 rm: Register,
483 ra: Register,
484 },
485 /// Debug info: line and column
486 ///
487 /// Used by e.g. dbg_line
488 dbg_line_column: struct {
489 line: u32,
490 column: u32,
491 },
492 };
493
494 // Make sure we don't accidentally make instructions bigger than expected.
495 // Note that in safety builds, Zig is allowed to insert a secret field for safety checks.
496 comptime {
497 if (!std.debug.runtime_safety) {
498 assert(@sizeOf(Data) == 8);
499 }
500 }
501};
502
503pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
504 mir.instructions.deinit(gpa);
505 gpa.free(mir.extra);
506 mir.* = undefined;
507}
508
509pub fn emit(
510 mir: Mir,
511 lf: *link.File,
512 pt: Zcu.PerThread,
513 src_loc: Zcu.LazySrcLoc,
514 func_index: InternPool.Index,
515 code: *std.ArrayListUnmanaged(u8),
516 debug_output: link.File.DebugInfoOutput,
517) codegen.CodeGenError!void {
518 const zcu = pt.zcu;
519 const func = zcu.funcInfo(func_index);
520 const nav = func.owner_nav;
521 const mod = zcu.navFileScope(nav).mod.?;
522 var e: Emit = .{
523 .mir = mir,
524 .bin_file = lf,
525 .debug_output = debug_output,
526 .target = &mod.resolved_target.result,
527 .src_loc = src_loc,
528 .code = code,
529 .prev_di_pc = 0,
530 .prev_di_line = func.lbrace_line,
531 .prev_di_column = func.lbrace_column,
532 .stack_size = mir.max_end_stack,
533 .saved_regs_stack_space = mir.saved_regs_stack_space,
534 };
535 defer e.deinit();
536 e.emitMir() catch |err| switch (err) {
537 error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?),
538 else => |e1| return e1,
539 };
540}
541
542/// Returns the requested data, as well as the new index which is at the start of the
543/// trailers for the object.
544pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
545 const fields = std.meta.fields(T);
546 var i: usize = index;
547 var result: T = undefined;
548 inline for (fields) |field| {
549 @field(result, field.name) = switch (field.type) {
550 u32 => mir.extra[i],
551 i32 => @as(i32, @bitCast(mir.extra[i])),
552 else => @compileError("bad field type"),
553 };
554 i += 1;
555 }
556 return .{
557 .data = result,
558 .end = i,
559 };
560}
561
562pub const LoadMemoryPie = struct {
563 register: u32,
564 /// Index of the containing atom.
565 atom_index: u32,
566 /// Index into the linker's symbol table.
567 sym_index: u32,
568};
src/arch/aarch64/abi.zig deleted-165
......@@ -1,165 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const bits = @import("bits.zig");
4const Register = bits.Register;
5const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
6const Type = @import("../../Type.zig");
7const Zcu = @import("../../Zcu.zig");
8
9pub const Class = union(enum) {
10 memory,
11 byval,
12 integer,
13 double_integer,
14 float_array: u8,
15};
16
17/// For `float_array` the second element will be the amount of floats.
18pub fn classifyType(ty: Type, zcu: *Zcu) Class {
19 std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
20
21 var maybe_float_bits: ?u16 = null;
22 switch (ty.zigTypeTag(zcu)) {
23 .@"struct" => {
24 if (ty.containerLayout(zcu) == .@"packed") return .byval;
25 const float_count = countFloats(ty, zcu, &maybe_float_bits);
26 if (float_count <= sret_float_count) return .{ .float_array = float_count };
27
28 const bit_size = ty.bitSize(zcu);
29 if (bit_size > 128) return .memory;
30 if (bit_size > 64) return .double_integer;
31 return .integer;
32 },
33 .@"union" => {
34 if (ty.containerLayout(zcu) == .@"packed") return .byval;
35 const float_count = countFloats(ty, zcu, &maybe_float_bits);
36 if (float_count <= sret_float_count) return .{ .float_array = float_count };
37
38 const bit_size = ty.bitSize(zcu);
39 if (bit_size > 128) return .memory;
40 if (bit_size > 64) return .double_integer;
41 return .integer;
42 },
43 .int, .@"enum", .error_set, .float, .bool => return .byval,
44 .vector => {
45 const bit_size = ty.bitSize(zcu);
46 // TODO is this controlled by a cpu feature?
47 if (bit_size > 128) return .memory;
48 return .byval;
49 },
50 .optional => {
51 std.debug.assert(ty.isPtrLikeOptional(zcu));
52 return .byval;
53 },
54 .pointer => {
55 std.debug.assert(!ty.isSlice(zcu));
56 return .byval;
57 },
58 .error_union,
59 .frame,
60 .@"anyframe",
61 .noreturn,
62 .void,
63 .type,
64 .comptime_float,
65 .comptime_int,
66 .undefined,
67 .null,
68 .@"fn",
69 .@"opaque",
70 .enum_literal,
71 .array,
72 => unreachable,
73 }
74}
75
76const sret_float_count = 4;
77fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u8 {
78 const ip = &zcu.intern_pool;
79 const target = zcu.getTarget();
80 const invalid = std.math.maxInt(u8);
81 switch (ty.zigTypeTag(zcu)) {
82 .@"union" => {
83 const union_obj = zcu.typeToUnion(ty).?;
84 var max_count: u8 = 0;
85 for (union_obj.field_types.get(ip)) |field_ty| {
86 const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits);
87 if (field_count == invalid) return invalid;
88 if (field_count > max_count) max_count = field_count;
89 if (max_count > sret_float_count) return invalid;
90 }
91 return max_count;
92 },
93 .@"struct" => {
94 const fields_len = ty.structFieldCount(zcu);
95 var count: u8 = 0;
96 var i: u32 = 0;
97 while (i < fields_len) : (i += 1) {
98 const field_ty = ty.fieldType(i, zcu);
99 const field_count = countFloats(field_ty, zcu, maybe_float_bits);
100 if (field_count == invalid) return invalid;
101 count += field_count;
102 if (count > sret_float_count) return invalid;
103 }
104 return count;
105 },
106 .float => {
107 const float_bits = maybe_float_bits.* orelse {
108 maybe_float_bits.* = ty.floatBits(target);
109 return 1;
110 };
111 if (ty.floatBits(target) == float_bits) return 1;
112 return invalid;
113 },
114 .void => return 0,
115 else => return invalid,
116 }
117}
118
119pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type {
120 const ip = &zcu.intern_pool;
121 switch (ty.zigTypeTag(zcu)) {
122 .@"union" => {
123 const union_obj = zcu.typeToUnion(ty).?;
124 for (union_obj.field_types.get(ip)) |field_ty| {
125 if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some;
126 }
127 return null;
128 },
129 .@"struct" => {
130 const fields_len = ty.structFieldCount(zcu);
131 var i: u32 = 0;
132 while (i < fields_len) : (i += 1) {
133 const field_ty = ty.fieldType(i, zcu);
134 if (getFloatArrayType(field_ty, zcu)) |some| return some;
135 }
136 return null;
137 },
138 .float => return ty,
139 else => return null,
140 }
141}
142
143pub const callee_preserved_regs = [_]Register{
144 .x19, .x20, .x21, .x22, .x23,
145 .x24, .x25, .x26, .x27, .x28,
146};
147
148pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
149pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
150
151const allocatable_registers = callee_preserved_regs;
152pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
153
154// Register classes
155const RegisterBitSet = RegisterManager.RegisterBitSet;
156pub const RegisterClass = struct {
157 pub const gp: RegisterBitSet = blk: {
158 var set = RegisterBitSet.initEmpty();
159 for (callee_preserved_regs) |reg| {
160 const index = RegisterManager.indexOfRegIntoTracked(reg).?;
161 set.set(index);
162 }
163 break :blk set;
164 };
165};
src/arch/arm/CodeGen.zig deleted-6340
......@@ -1,6340 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const math = std.math;
5const assert = std.debug.assert;
6const codegen = @import("../../codegen.zig");
7const Air = @import("../../Air.zig");
8const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");
10const Type = @import("../../Type.zig");
11const Value = @import("../../Value.zig");
12const link = @import("../../link.zig");
13const Zcu = @import("../../Zcu.zig");
14const InternPool = @import("../../InternPool.zig");
15const Compilation = @import("../../Compilation.zig");
16const ErrorMsg = Zcu.ErrorMsg;
17const Target = std.Target;
18const Allocator = mem.Allocator;
19const trace = @import("../../tracy.zig").trace;
20const leb128 = std.leb;
21const log = std.log.scoped(.codegen);
22const build_options = @import("build_options");
23const Alignment = InternPool.Alignment;
24
25const CodeGenError = codegen.CodeGenError;
26
27const bits = @import("bits.zig");
28const abi = @import("abi.zig");
29const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
30const errUnionErrorOffset = codegen.errUnionErrorOffset;
31const RegisterManager = abi.RegisterManager;
32const RegisterLock = RegisterManager.RegisterLock;
33const Register = bits.Register;
34const Instruction = bits.Instruction;
35const Condition = bits.Condition;
36const callee_preserved_regs = abi.callee_preserved_regs;
37const caller_preserved_regs = abi.caller_preserved_regs;
38const c_abi_int_param_regs = abi.c_abi_int_param_regs;
39const c_abi_int_return_regs = abi.c_abi_int_return_regs;
40const gp = abi.RegisterClass.gp;
41
42const InnerError = CodeGenError || error{OutOfRegisters};
43
44pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
45 return null;
46}
47
48gpa: Allocator,
49pt: Zcu.PerThread,
50air: Air,
51liveness: Air.Liveness,
52bin_file: *link.File,
53target: *const std.Target,
54func_index: InternPool.Index,
55err_msg: ?*ErrorMsg,
56args: []MCValue,
57ret_mcv: MCValue,
58fn_type: Type,
59arg_index: u32,
60src_loc: Zcu.LazySrcLoc,
61stack_align: u32,
62
63/// MIR Instructions
64mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
65/// MIR extra data
66mir_extra: std.ArrayListUnmanaged(u32) = .empty,
67
68/// Byte offset within the source file of the ending curly.
69end_di_line: u32,
70end_di_column: u32,
71
72/// The value is an offset into the `Function` `code` from the beginning.
73/// To perform the reloc, write 32-bit signed little-endian integer
74/// which is a relative jump, based on the address following the reloc.
75exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty,
76
77reused_operands: std.StaticBitSet(Air.Liveness.bpi - 1) = undefined,
78
79/// We postpone the creation of debug info for function args and locals
80/// until after all Mir instructions have been generated. Only then we
81/// will know saved_regs_stack_space which is necessary in order to
82/// calculate the right stack offsest with respect to the `.fp` register.
83dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .empty,
84
85/// Whenever there is a runtime branch, we push a Branch onto this stack,
86/// and pop it off when the runtime branch joins. This provides an "overlay"
87/// of the table of mappings from instructions to `MCValue` from within the branch.
88/// This way we can modify the `MCValue` for an instruction in different ways
89/// within different branches. Special consideration is needed when a branch
90/// joins with its parent, to make sure all instructions have the same MCValue
91/// across each runtime branch upon joining.
92branch_stack: *std.ArrayList(Branch),
93
94// Key is the block instruction
95blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
96
97register_manager: RegisterManager = .{},
98/// Maps offset to what is stored there.
99stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .empty,
100/// Tracks the current instruction allocated to the compare flags
101cpsr_flags_inst: ?Air.Inst.Index = null,
102
103/// Offset from the stack base, representing the end of the stack frame.
104max_end_stack: u32 = 0,
105/// Represents the current end stack offset. If there is no existing slot
106/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
107next_stack_offset: u32 = 0,
108
109saved_regs_stack_space: u32 = 0,
110
111/// Debug field, used to find bugs in the compiler.
112air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
113
114const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
115
116const MCValue = union(enum) {
117 /// No runtime bits. `void` types, empty structs, u0, enums with 1
118 /// tag, etc.
119 ///
120 /// TODO Look into deleting this tag and using `dead` instead,
121 /// since every use of MCValue.none should be instead looking at
122 /// the type and noticing it is 0 bits.
123 none,
124 /// Control flow will not allow this value to be observed.
125 unreach,
126 /// No more references to this value remain.
127 dead,
128 /// The value is undefined.
129 undef,
130 /// A pointer-sized integer that fits in a register.
131 ///
132 /// If the type is a pointer, this is the pointer address in
133 /// virtual address space.
134 immediate: u32,
135 /// The value is in a target-specific register.
136 register: Register,
137 /// The value is a tuple { wrapped: u32, overflow: u1 } where
138 /// wrapped is stored in the register and the overflow bit is
139 /// stored in the C flag of the CPSR.
140 ///
141 /// This MCValue is only generated by a add_with_overflow or
142 /// sub_with_overflow instruction operating on u32.
143 register_c_flag: Register,
144 /// The value is a tuple { wrapped: i32, overflow: u1 } where
145 /// wrapped is stored in the register and the overflow bit is
146 /// stored in the V flag of the CPSR.
147 ///
148 /// This MCValue is only generated by a add_with_overflow or
149 /// sub_with_overflow instruction operating on i32.
150 register_v_flag: Register,
151 /// The value is in memory at a hard-coded address.
152 ///
153 /// If the type is a pointer, it means the pointer address is at
154 /// this memory location.
155 memory: u64,
156 /// The value is one of the stack variables.
157 ///
158 /// If the type is a pointer, it means the pointer address is in
159 /// the stack at this offset.
160 stack_offset: u32,
161 /// The value is a pointer to one of the stack variables (payload
162 /// is stack offset).
163 ptr_stack_offset: u32,
164 /// The value resides in the N, Z, C, V flags of the Current
165 /// Program Status Register (CPSR). The value is 1 (if the type is
166 /// u1) or true (if the type in bool) iff the specified condition
167 /// is true.
168 cpsr_flags: Condition,
169 /// The value is a function argument passed via the stack.
170 stack_argument_offset: u32,
171};
172
173const Branch = struct {
174 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .empty,
175
176 fn deinit(self: *Branch, gpa: Allocator) void {
177 self.inst_table.deinit(gpa);
178 self.* = undefined;
179 }
180};
181
182const StackAllocation = struct {
183 inst: Air.Inst.Index,
184 /// TODO do we need size? should be determined by inst.ty.abiSize()
185 size: u32,
186};
187
188const BlockData = struct {
189 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
190 /// The first break instruction encounters `null` here and chooses a
191 /// machine code value for the block result, populating this field.
192 /// Following break instructions encounter that value and use it for
193 /// the location to store their block results.
194 mcv: MCValue,
195};
196
197const BigTomb = struct {
198 function: *Self,
199 inst: Air.Inst.Index,
200 lbt: Air.Liveness.BigTomb,
201
202 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
203 const dies = bt.lbt.feed();
204 const op_index = op_ref.toIndex() orelse return;
205 if (!dies) return;
206 bt.function.processDeath(op_index);
207 }
208
209 fn finishAir(bt: *BigTomb, result: MCValue) void {
210 const is_used = !bt.function.liveness.isUnused(bt.inst);
211 if (is_used) {
212 log.debug("%{d} => {}", .{ bt.inst, result });
213 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
214 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
215
216 switch (result) {
217 .register => |reg| {
218 // In some cases (such as bitcast), an operand
219 // may be the same MCValue as the result. If
220 // that operand died and was a register, it
221 // was freed by processDeath. We have to
222 // "re-allocate" the register.
223 if (bt.function.register_manager.isRegFree(reg)) {
224 bt.function.register_manager.getRegAssumeFree(reg, bt.inst);
225 }
226 },
227 .register_c_flag,
228 .register_v_flag,
229 => |reg| {
230 if (bt.function.register_manager.isRegFree(reg)) {
231 bt.function.register_manager.getRegAssumeFree(reg, bt.inst);
232 }
233 bt.function.cpsr_flags_inst = bt.inst;
234 },
235 .cpsr_flags => {
236 bt.function.cpsr_flags_inst = bt.inst;
237 },
238 else => {},
239 }
240 }
241 bt.function.finishAirBookkeeping();
242 }
243};
244
245const DbgInfoReloc = struct {
246 tag: Air.Inst.Tag,
247 ty: Type,
248 name: [:0]const u8,
249 mcv: MCValue,
250
251 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
252 switch (reloc.tag) {
253 .arg,
254 .dbg_arg_inline,
255 => try reloc.genArgDbgInfo(function),
256
257 .dbg_var_ptr,
258 .dbg_var_val,
259 => try reloc.genVarDbgInfo(function),
260
261 else => unreachable,
262 }
263 }
264
265 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
266 // TODO: Add a pseudo-instruction or something to defer this work until Emit.
267 // We aren't allowed to interact with linker state here.
268 if (true) return;
269 switch (function.debug_output) {
270 .dwarf => |dw| {
271 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
272 .register => |reg| .{ .reg = reg.dwarfNum() },
273 .stack_offset,
274 .stack_argument_offset,
275 => blk: {
276 const adjusted_stack_offset = switch (reloc.mcv) {
277 .stack_offset => |offset| -@as(i32, @intCast(offset)),
278 .stack_argument_offset => |offset| @as(i32, @intCast(function.saved_regs_stack_space + offset)),
279 else => unreachable,
280 };
281 break :blk .{ .plus = .{
282 &.{ .reg = 11 },
283 &.{ .consts = adjusted_stack_offset },
284 } };
285 },
286 else => unreachable, // not a possible argument
287 };
288
289 try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
290 },
291 .plan9 => {},
292 .none => {},
293 }
294 }
295
296 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
297 // TODO: Add a pseudo-instruction or something to defer this work until Emit.
298 // We aren't allowed to interact with linker state here.
299 if (true) return;
300 switch (function.debug_output) {
301 .dwarf => |dw| {
302 const loc: link.File.Dwarf.Loc = switch (reloc.mcv) {
303 .register => |reg| .{ .reg = reg.dwarfNum() },
304 .ptr_stack_offset,
305 .stack_offset,
306 .stack_argument_offset,
307 => |offset| blk: {
308 const adjusted_offset = switch (reloc.mcv) {
309 .ptr_stack_offset,
310 .stack_offset,
311 => -@as(i32, @intCast(offset)),
312 .stack_argument_offset => @as(i32, @intCast(function.saved_regs_stack_space + offset)),
313 else => unreachable,
314 };
315 break :blk .{ .plus = .{
316 &.{ .reg = 11 },
317 &.{ .consts = adjusted_offset },
318 } };
319 },
320 .memory => |address| .{ .constu = address },
321 .immediate => |x| .{ .constu = x },
322 .none => .empty,
323 else => blk: {
324 log.debug("TODO generate debug info for {}", .{reloc.mcv});
325 break :blk .empty;
326 },
327 };
328 try dw.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc);
329 },
330 .plan9 => {},
331 .none => {},
332 }
333 }
334};
335
336const Self = @This();
337
338pub fn generate(
339 lf: *link.File,
340 pt: Zcu.PerThread,
341 src_loc: Zcu.LazySrcLoc,
342 func_index: InternPool.Index,
343 air: *const Air,
344 liveness: *const Air.Liveness,
345) CodeGenError!Mir {
346 const zcu = pt.zcu;
347 const gpa = zcu.gpa;
348 const func = zcu.funcInfo(func_index);
349 const func_ty = Type.fromInterned(func.ty);
350 const file_scope = zcu.navFileScope(func.owner_nav);
351 const target = &file_scope.mod.?.resolved_target.result;
352
353 var branch_stack = std.ArrayList(Branch).init(gpa);
354 defer {
355 assert(branch_stack.items.len == 1);
356 branch_stack.items[0].deinit(gpa);
357 branch_stack.deinit();
358 }
359 try branch_stack.append(.{});
360
361 var function: Self = .{
362 .gpa = gpa,
363 .pt = pt,
364 .air = air.*,
365 .liveness = liveness.*,
366 .target = target,
367 .bin_file = lf,
368 .func_index = func_index,
369 .err_msg = null,
370 .args = undefined, // populated after `resolveCallingConventionValues`
371 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
372 .fn_type = func_ty,
373 .arg_index = 0,
374 .branch_stack = &branch_stack,
375 .src_loc = src_loc,
376 .stack_align = undefined,
377 .end_di_line = func.rbrace_line,
378 .end_di_column = func.rbrace_column,
379 };
380 defer function.stack.deinit(gpa);
381 defer function.blocks.deinit(gpa);
382 defer function.exitlude_jump_relocs.deinit(gpa);
383 defer function.dbg_info_relocs.deinit(gpa);
384
385 var call_info = function.resolveCallingConventionValues(func_ty) catch |err| switch (err) {
386 error.CodegenFail => return error.CodegenFail,
387 else => |e| return e,
388 };
389 defer call_info.deinit(&function);
390
391 function.args = call_info.args;
392 function.ret_mcv = call_info.return_value;
393 function.stack_align = call_info.stack_align;
394 function.max_end_stack = call_info.stack_byte_count;
395
396 function.gen() catch |err| switch (err) {
397 error.CodegenFail => return error.CodegenFail,
398 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
399 else => |e| return e,
400 };
401
402 for (function.dbg_info_relocs.items) |reloc| {
403 reloc.genDbgInfo(function) catch |err|
404 return function.fail("failed to generate debug info: {s}", .{@errorName(err)});
405 }
406
407 var mir: Mir = .{
408 .instructions = function.mir_instructions.toOwnedSlice(),
409 .extra = &.{}, // fallible, so assign after errdefer
410 .max_end_stack = function.max_end_stack,
411 .saved_regs_stack_space = function.saved_regs_stack_space,
412 };
413 errdefer mir.deinit(gpa);
414 mir.extra = try function.mir_extra.toOwnedSlice(gpa);
415 return mir;
416}
417
418fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
419 const gpa = self.gpa;
420
421 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
422
423 const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
424 self.mir_instructions.appendAssumeCapacity(inst);
425 return result_index;
426}
427
428fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
429 return try self.addInst(.{
430 .tag = .nop,
431 .data = .{ .nop = {} },
432 });
433}
434
435pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
436 const fields = std.meta.fields(@TypeOf(extra));
437 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
438 return self.addExtraAssumeCapacity(extra);
439}
440
441pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
442 const fields = std.meta.fields(@TypeOf(extra));
443 const result: u32 = @intCast(self.mir_extra.items.len);
444 inline for (fields) |field| {
445 self.mir_extra.appendAssumeCapacity(switch (field.type) {
446 u32 => @field(extra, field.name),
447 i32 => @bitCast(@field(extra, field.name)),
448 else => @compileError("bad field type"),
449 });
450 }
451 return result;
452}
453
454fn gen(self: *Self) !void {
455 const pt = self.pt;
456 const zcu = pt.zcu;
457 const cc = self.fn_type.fnCallingConvention(zcu);
458 if (cc != .naked) {
459 // push {fp, lr}
460 const push_reloc = try self.addNop();
461
462 // mov fp, sp
463 _ = try self.addInst(.{
464 .tag = .mov,
465 .data = .{ .r_op_mov = .{
466 .rd = .fp,
467 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),
468 } },
469 });
470
471 // sub sp, sp, #reloc
472 const sub_reloc = try self.addNop();
473
474 // The sub_sp_scratch_r4 instruction may use r4, so we mark r4
475 // as allocated by this function.
476 const index = RegisterManager.indexOfRegIntoTracked(.r4).?;
477 self.register_manager.allocated_registers.set(index);
478
479 if (self.ret_mcv == .stack_offset) {
480 // The address of where to store the return value is in
481 // r0. As this register might get overwritten along the
482 // way, save the address to the stack.
483 const stack_offset = try self.allocMem(4, .@"4", null);
484
485 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });
486 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
487 }
488
489 for (self.args, 0..) |*arg, arg_index| {
490 // Copy register arguments to the stack
491 switch (arg.*) {
492 .register => |reg| {
493 // The first AIR instructions of the main body are guaranteed
494 // to be the functions arguments
495 const inst = self.air.getMainBody()[arg_index];
496 assert(self.air.instructions.items(.tag)[@intFromEnum(inst)] == .arg);
497
498 const ty = self.typeOfIndex(inst);
499
500 const abi_size: u32 = @intCast(ty.abiSize(zcu));
501 const abi_align = ty.abiAlignment(zcu);
502 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
503 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
504
505 arg.* = MCValue{ .stack_offset = stack_offset };
506 },
507 else => {},
508 }
509 }
510
511 _ = try self.addInst(.{
512 .tag = .dbg_prologue_end,
513 .cond = undefined,
514 .data = .{ .nop = {} },
515 });
516
517 try self.genBody(self.air.getMainBody());
518
519 // Backpatch push callee saved regs
520 var saved_regs = Instruction.RegisterList{
521 .r11 = true, // fp
522 .r14 = true, // lr
523 };
524 self.saved_regs_stack_space = 8;
525 inline for (callee_preserved_regs) |reg| {
526 if (self.register_manager.isRegAllocated(reg)) {
527 @field(saved_regs, @tagName(reg)) = true;
528 self.saved_regs_stack_space += 4;
529 }
530 }
531 self.mir_instructions.set(push_reloc, .{
532 .tag = .push,
533 .data = .{ .register_list = saved_regs },
534 });
535
536 // Backpatch stack offset
537 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space;
538 const aligned_total_stack_end = mem.alignForward(u32, total_stack_size, self.stack_align);
539 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
540 self.max_end_stack = stack_size;
541 self.mir_instructions.set(sub_reloc, .{
542 .tag = .sub_sp_scratch_r4,
543 .data = .{ .imm32 = stack_size },
544 });
545
546 _ = try self.addInst(.{
547 .tag = .dbg_epilogue_begin,
548 .cond = undefined,
549 .data = .{ .nop = {} },
550 });
551
552 // exitlude jumps
553 if (self.exitlude_jump_relocs.items.len > 0 and
554 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
555 {
556 // If the last Mir instruction (apart from the
557 // dbg_epilogue_begin) is the last exitlude jump
558 // relocation (which would just jump one instruction
559 // further), it can be safely removed
560 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop().?);
561 }
562
563 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
564 self.mir_instructions.set(jmp_reloc, .{
565 .tag = .b,
566 .data = .{ .inst = @intCast(self.mir_instructions.len) },
567 });
568 }
569
570 // Epilogue: pop callee saved registers (swap lr with pc in saved_regs)
571 saved_regs.r14 = false; // lr
572 saved_regs.r15 = true; // pc
573
574 // mov sp, fp
575 _ = try self.addInst(.{
576 .tag = .mov,
577 .data = .{ .r_op_mov = .{
578 .rd = .sp,
579 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),
580 } },
581 });
582
583 // pop {fp, pc}
584 _ = try self.addInst(.{
585 .tag = .pop,
586 .data = .{ .register_list = saved_regs },
587 });
588 } else {
589 _ = try self.addInst(.{
590 .tag = .dbg_prologue_end,
591 .cond = undefined,
592 .data = .{ .nop = {} },
593 });
594
595 try self.genBody(self.air.getMainBody());
596
597 _ = try self.addInst(.{
598 .tag = .dbg_epilogue_begin,
599 .cond = undefined,
600 .data = .{ .nop = {} },
601 });
602 }
603
604 // Drop them off at the rbrace.
605 _ = try self.addInst(.{
606 .tag = .dbg_line,
607 .cond = undefined,
608 .data = .{ .dbg_line_column = .{
609 .line = self.end_di_line,
610 .column = self.end_di_column,
611 } },
612 });
613}
614
615fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
616 const pt = self.pt;
617 const zcu = pt.zcu;
618 const ip = &zcu.intern_pool;
619 const air_tags = self.air.instructions.items(.tag);
620
621 for (body) |inst| {
622 // TODO: remove now-redundant isUnused calls from AIR handler functions
623 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip))
624 continue;
625
626 const old_air_bookkeeping = self.air_bookkeeping;
627 try self.ensureProcessDeathCapacity(Air.Liveness.bpi);
628
629 self.reused_operands = @TypeOf(self.reused_operands).initEmpty();
630 switch (air_tags[@intFromEnum(inst)]) {
631 // zig fmt: off
632 .add, => try self.airBinOp(inst, .add),
633 .add_wrap => try self.airBinOp(inst, .add_wrap),
634 .sub, => try self.airBinOp(inst, .sub),
635 .sub_wrap => try self.airBinOp(inst, .sub_wrap),
636 .mul => try self.airBinOp(inst, .mul),
637 .mul_wrap => try self.airBinOp(inst, .mul_wrap),
638 .shl => try self.airBinOp(inst, .shl),
639 .shl_exact => try self.airBinOp(inst, .shl_exact),
640 .bool_and => try self.airBinOp(inst, .bool_and),
641 .bool_or => try self.airBinOp(inst, .bool_or),
642 .bit_and => try self.airBinOp(inst, .bit_and),
643 .bit_or => try self.airBinOp(inst, .bit_or),
644 .xor => try self.airBinOp(inst, .xor),
645 .shr => try self.airBinOp(inst, .shr),
646 .shr_exact => try self.airBinOp(inst, .shr_exact),
647 .div_float => try self.airBinOp(inst, .div_float),
648 .div_trunc => try self.airBinOp(inst, .div_trunc),
649 .div_floor => try self.airBinOp(inst, .div_floor),
650 .div_exact => try self.airBinOp(inst, .div_exact),
651 .rem => try self.airBinOp(inst, .rem),
652 .mod => try self.airBinOp(inst, .mod),
653
654 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
655 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
656
657 .min => try self.airMinMax(inst),
658 .max => try self.airMinMax(inst),
659
660 .add_sat => try self.airAddSat(inst),
661 .sub_sat => try self.airSubSat(inst),
662 .mul_sat => try self.airMulSat(inst),
663 .shl_sat => try self.airShlSat(inst),
664 .slice => try self.airSlice(inst),
665
666 .sqrt,
667 .sin,
668 .cos,
669 .tan,
670 .exp,
671 .exp2,
672 .log,
673 .log2,
674 .log10,
675 .floor,
676 .ceil,
677 .round,
678 .trunc_float,
679 .neg,
680 => try self.airUnaryMath(inst),
681
682 .add_with_overflow => try self.airOverflow(inst),
683 .sub_with_overflow => try self.airOverflow(inst),
684 .mul_with_overflow => try self.airMulWithOverflow(inst),
685 .shl_with_overflow => try self.airShlWithOverflow(inst),
686
687 .cmp_lt => try self.airCmp(inst, .lt),
688 .cmp_lte => try self.airCmp(inst, .lte),
689 .cmp_eq => try self.airCmp(inst, .eq),
690 .cmp_gte => try self.airCmp(inst, .gte),
691 .cmp_gt => try self.airCmp(inst, .gt),
692 .cmp_neq => try self.airCmp(inst, .neq),
693
694 .cmp_vector => try self.airCmpVector(inst),
695 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
696
697 .alloc => try self.airAlloc(inst),
698 .ret_ptr => try self.airRetPtr(inst),
699 .arg => try self.airArg(inst),
700 .assembly => try self.airAsm(inst),
701 .bitcast => try self.airBitCast(inst),
702 .block => try self.airBlock(inst),
703 .br => try self.airBr(inst),
704 .repeat => return self.fail("TODO implement `repeat`", .{}),
705 .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}),
706 .trap => try self.airTrap(),
707 .breakpoint => try self.airBreakpoint(),
708 .ret_addr => try self.airRetAddr(inst),
709 .frame_addr => try self.airFrameAddress(inst),
710 .cond_br => try self.airCondBr(inst),
711 .fptrunc => try self.airFptrunc(inst),
712 .fpext => try self.airFpext(inst),
713 .intcast => try self.airIntCast(inst),
714 .trunc => try self.airTrunc(inst),
715 .is_non_null => try self.airIsNonNull(inst),
716 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
717 .is_null => try self.airIsNull(inst),
718 .is_null_ptr => try self.airIsNullPtr(inst),
719 .is_non_err => try self.airIsNonErr(inst),
720 .is_non_err_ptr => try self.airIsNonErrPtr(inst),
721 .is_err => try self.airIsErr(inst),
722 .is_err_ptr => try self.airIsErrPtr(inst),
723 .load => try self.airLoad(inst),
724 .loop => try self.airLoop(inst),
725 .not => try self.airNot(inst),
726 .ret => try self.airRet(inst),
727 .ret_safe => try self.airRet(inst), // TODO
728 .ret_load => try self.airRetLoad(inst),
729 .store => try self.airStore(inst, false),
730 .store_safe => try self.airStore(inst, true),
731 .struct_field_ptr=> try self.airStructFieldPtr(inst),
732 .struct_field_val=> try self.airStructFieldVal(inst),
733 .array_to_slice => try self.airArrayToSlice(inst),
734 .float_from_int => try self.airFloatFromInt(inst),
735 .int_from_float => try self.airIntFromFloat(inst),
736 .cmpxchg_strong => try self.airCmpxchg(inst),
737 .cmpxchg_weak => try self.airCmpxchg(inst),
738 .atomic_rmw => try self.airAtomicRmw(inst),
739 .atomic_load => try self.airAtomicLoad(inst),
740 .memcpy => try self.airMemcpy(inst),
741 .memmove => try self.airMemmove(inst),
742 .memset => try self.airMemset(inst, false),
743 .memset_safe => try self.airMemset(inst, true),
744 .set_union_tag => try self.airSetUnionTag(inst),
745 .get_union_tag => try self.airGetUnionTag(inst),
746 .clz => try self.airClz(inst),
747 .ctz => try self.airCtz(inst),
748 .popcount => try self.airPopcount(inst),
749 .abs => try self.airAbs(inst),
750 .byte_swap => try self.airByteSwap(inst),
751 .bit_reverse => try self.airBitReverse(inst),
752 .tag_name => try self.airTagName(inst),
753 .error_name => try self.airErrorName(inst),
754 .splat => try self.airSplat(inst),
755 .select => try self.airSelect(inst),
756 .shuffle_one => try self.airShuffleOne(inst),
757 .shuffle_two => try self.airShuffleTwo(inst),
758 .reduce => try self.airReduce(inst),
759 .aggregate_init => try self.airAggregateInit(inst),
760 .union_init => try self.airUnionInit(inst),
761 .prefetch => try self.airPrefetch(inst),
762 .mul_add => try self.airMulAdd(inst),
763 .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}),
764
765 .@"try" => try self.airTry(inst),
766 .try_cold => try self.airTry(inst),
767 .try_ptr => try self.airTryPtr(inst),
768 .try_ptr_cold => try self.airTryPtr(inst),
769
770 .dbg_stmt => try self.airDbgStmt(inst),
771 .dbg_empty_stmt => self.finishAirBookkeeping(),
772 .dbg_inline_block => try self.airDbgInlineBlock(inst),
773 .dbg_var_ptr,
774 .dbg_var_val,
775 .dbg_arg_inline,
776 => try self.airDbgVar(inst),
777
778 .call => try self.airCall(inst, .auto),
779 .call_always_tail => try self.airCall(inst, .always_tail),
780 .call_never_tail => try self.airCall(inst, .never_tail),
781 .call_never_inline => try self.airCall(inst, .never_inline),
782
783 .atomic_store_unordered => try self.airAtomicStore(inst, .unordered),
784 .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic),
785 .atomic_store_release => try self.airAtomicStore(inst, .release),
786 .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst),
787
788 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
789 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
790 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
791 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
792
793 .field_parent_ptr => try self.airFieldParentPtr(inst),
794
795 .switch_br => try self.airSwitch(inst),
796 .loop_switch_br => return self.fail("TODO implement `loop_switch_br`", .{}),
797 .slice_ptr => try self.airSlicePtr(inst),
798 .slice_len => try self.airSliceLen(inst),
799
800 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
801 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
802
803 .array_elem_val => try self.airArrayElemVal(inst),
804 .slice_elem_val => try self.airSliceElemVal(inst),
805 .slice_elem_ptr => try self.airSliceElemPtr(inst),
806 .ptr_elem_val => try self.airPtrElemVal(inst),
807 .ptr_elem_ptr => try self.airPtrElemPtr(inst),
808
809 .inferred_alloc, .inferred_alloc_comptime => unreachable,
810 .unreach => self.finishAirBookkeeping(),
811
812 .optional_payload => try self.airOptionalPayload(inst),
813 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
814 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst),
815 .unwrap_errunion_err => try self.airUnwrapErrErr(inst),
816 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
817 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
818 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
819 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
820 .err_return_trace => try self.airErrReturnTrace(inst),
821 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
822 .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
823
824 .wrap_optional => try self.airWrapOptional(inst),
825 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
826 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
827
828 .add_optimized,
829 .sub_optimized,
830 .mul_optimized,
831 .div_float_optimized,
832 .div_trunc_optimized,
833 .div_floor_optimized,
834 .div_exact_optimized,
835 .rem_optimized,
836 .mod_optimized,
837 .neg_optimized,
838 .cmp_lt_optimized,
839 .cmp_lte_optimized,
840 .cmp_eq_optimized,
841 .cmp_gte_optimized,
842 .cmp_gt_optimized,
843 .cmp_neq_optimized,
844 .cmp_vector_optimized,
845 .reduce_optimized,
846 .int_from_float_optimized,
847 => return self.fail("TODO implement optimized float mode", .{}),
848
849 .add_safe,
850 .sub_safe,
851 .mul_safe,
852 .intcast_safe,
853 .int_from_float_safe,
854 .int_from_float_optimized_safe,
855 => return self.fail("TODO implement safety_checked_instructions", .{}),
856
857 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
858 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
859 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
860 .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}),
861
862 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
863 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
864 .c_va_end => return self.fail("TODO implement c_va_end", .{}),
865 .c_va_start => return self.fail("TODO implement c_va_start", .{}),
866
867 .wasm_memory_size => unreachable,
868 .wasm_memory_grow => unreachable,
869
870 .work_item_id => unreachable,
871 .work_group_size => unreachable,
872 .work_group_id => unreachable,
873 // zig fmt: on
874 }
875
876 assert(!self.register_manager.lockedRegsExist());
877
878 if (std.debug.runtime_safety) {
879 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
880 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
881 }
882 }
883 }
884}
885
886/// Asserts there is already capacity to insert into top branch inst_table.
887fn processDeath(self: *Self, inst: Air.Inst.Index) void {
888 // When editing this function, note that the logic must synchronize with `reuseOperand`.
889 const prev_value = self.getResolvedInstValue(inst);
890 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
891 branch.inst_table.putAssumeCapacity(inst, .dead);
892 switch (prev_value) {
893 .register => |reg| {
894 self.register_manager.freeReg(reg);
895 },
896 .register_c_flag,
897 .register_v_flag,
898 => |reg| {
899 self.register_manager.freeReg(reg);
900 self.cpsr_flags_inst = null;
901 },
902 .cpsr_flags => {
903 self.cpsr_flags_inst = null;
904 },
905 else => {}, // TODO process stack allocation death
906 }
907}
908
909/// Called when there are no operands, and the instruction is always unreferenced.
910fn finishAirBookkeeping(self: *Self) void {
911 if (std.debug.runtime_safety) {
912 self.air_bookkeeping += 1;
913 }
914}
915
916fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Air.Liveness.bpi - 1]Air.Inst.Ref) void {
917 const tomb_bits = self.liveness.getTombBits(inst);
918 for (0.., operands) |op_index, op| {
919 if (tomb_bits & @as(Air.Liveness.Bpi, 1) << @intCast(op_index) == 0) continue;
920 if (self.reused_operands.isSet(op_index)) continue;
921 self.processDeath(op.toIndexAllowNone() orelse continue);
922 }
923 if (tomb_bits & 1 << (Air.Liveness.bpi - 1) == 0) {
924 log.debug("%{d} => {}", .{ inst, result });
925 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
926 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
927
928 switch (result) {
929 .register => |reg| {
930 // In some cases (such as bitcast), an operand
931 // may be the same MCValue as the result. If
932 // that operand died and was a register, it
933 // was freed by processDeath. We have to
934 // "re-allocate" the register.
935 if (self.register_manager.isRegFree(reg)) {
936 self.register_manager.getRegAssumeFree(reg, inst);
937 }
938 },
939 .register_c_flag,
940 .register_v_flag,
941 => |reg| {
942 if (self.register_manager.isRegFree(reg)) {
943 self.register_manager.getRegAssumeFree(reg, inst);
944 }
945 self.cpsr_flags_inst = inst;
946 },
947 .cpsr_flags => {
948 self.cpsr_flags_inst = inst;
949 },
950 else => {},
951 }
952 }
953 self.finishAirBookkeeping();
954}
955
956fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
957 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
958 try table.ensureUnusedCapacity(self.gpa, additional_count);
959}
960
961fn allocMem(
962 self: *Self,
963 abi_size: u32,
964 abi_align: Alignment,
965 maybe_inst: ?Air.Inst.Index,
966) !u32 {
967 assert(abi_size > 0);
968 assert(abi_align != .none);
969
970 // TODO find a free slot instead of always appending
971 const offset: u32 = @intCast(abi_align.forward(self.next_stack_offset) + abi_size);
972 self.next_stack_offset = offset;
973 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
974
975 if (maybe_inst) |inst| {
976 try self.stack.putNoClobber(self.gpa, offset, .{
977 .inst = inst,
978 .size = abi_size,
979 });
980 }
981
982 return offset;
983}
984
985/// Use a pointer instruction as the basis for allocating stack memory.
986fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
987 const pt = self.pt;
988 const zcu = pt.zcu;
989 const elem_ty = self.typeOfIndex(inst).childType(zcu);
990
991 if (!elem_ty.hasRuntimeBits(zcu)) {
992 // As this stack item will never be dereferenced at runtime,
993 // return the stack offset 0. Stack offset 0 will be where all
994 // zero-sized stack allocations live as non-zero-sized
995 // allocations will always have an offset > 0.
996 return 0;
997 }
998
999 const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse {
1000 return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
1001 };
1002 // TODO swap this for inst.ty.ptrAlign
1003 const abi_align = elem_ty.abiAlignment(zcu);
1004
1005 return self.allocMem(abi_size, abi_align, inst);
1006}
1007
1008fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
1009 const pt = self.pt;
1010 const abi_size = math.cast(u32, elem_ty.abiSize(pt.zcu)) orelse {
1011 return self.fail("type '{f}' too big to fit into stack frame", .{elem_ty.fmt(pt)});
1012 };
1013 const abi_align = elem_ty.abiAlignment(pt.zcu);
1014
1015 if (reg_ok) {
1016 // Make sure the type can fit in a register before we try to allocate one.
1017 const ptr_bits = self.target.ptrBitWidth();
1018 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1019 if (abi_size <= ptr_bytes) {
1020 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
1021 return MCValue{ .register = reg };
1022 }
1023 }
1024 }
1025
1026 const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst);
1027 return MCValue{ .stack_offset = stack_offset };
1028}
1029
1030pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1031 const stack_mcv = try self.allocRegOrMem(self.typeOfIndex(inst), false, inst);
1032 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
1033
1034 const reg_mcv = self.getResolvedInstValue(inst);
1035 switch (reg_mcv) {
1036 .register,
1037 .register_c_flag,
1038 .register_v_flag,
1039 => |r| assert(r == reg),
1040 else => unreachable, // not a register
1041 }
1042
1043 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1044 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1045 try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1046}
1047
1048/// Save the current instruction stored in the compare flags if
1049/// occupied
1050fn spillCompareFlagsIfOccupied(self: *Self) !void {
1051 if (self.cpsr_flags_inst) |inst_to_save| {
1052 const ty = self.typeOfIndex(inst_to_save);
1053 const mcv = self.getResolvedInstValue(inst_to_save);
1054 const new_mcv = switch (mcv) {
1055 .cpsr_flags => try self.allocRegOrMem(ty, true, inst_to_save),
1056 .register_c_flag,
1057 .register_v_flag,
1058 => try self.allocRegOrMem(ty, false, inst_to_save),
1059 else => unreachable, // mcv doesn't occupy the compare flags
1060 };
1061
1062 try self.setRegOrMem(self.typeOfIndex(inst_to_save), new_mcv, mcv);
1063 log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
1064
1065 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1066 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
1067
1068 self.cpsr_flags_inst = null;
1069
1070 // TODO consolidate with register manager and spillInstruction
1071 // this call should really belong in the register manager!
1072 switch (mcv) {
1073 .register_c_flag,
1074 .register_v_flag,
1075 => |reg| self.register_manager.freeReg(reg),
1076 else => {},
1077 }
1078 }
1079}
1080
1081/// Copies a value to a register without tracking the register. The register is not considered
1082/// allocated. A second call to `copyToTmpRegister` may return the same register.
1083/// This can have a side effect of spilling instructions to the stack to free up a register.
1084fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
1085 const reg = try self.register_manager.allocReg(null, gp);
1086 try self.genSetReg(ty, reg, mcv);
1087 return reg;
1088}
1089
1090fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1091 const stack_offset = try self.allocMemPtr(inst);
1092 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1093}
1094
1095fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1096 const pt = self.pt;
1097 const zcu = pt.zcu;
1098 const result: MCValue = switch (self.ret_mcv) {
1099 .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) },
1100 .stack_offset => blk: {
1101 // self.ret_mcv is an address to where this function
1102 // should store its result into
1103 const ret_ty = self.fn_type.fnReturnType(zcu);
1104 const ptr_ty = try pt.singleMutPtrType(ret_ty);
1105
1106 // addr_reg will contain the address of where to store the
1107 // result into
1108 const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv);
1109 break :blk .{ .register = addr_reg };
1110 },
1111 else => unreachable, // invalid return result
1112 };
1113
1114 return self.finishAir(inst, result, .{ .none, .none, .none });
1115}
1116
1117fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
1118 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1119 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
1120 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1121}
1122
1123fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
1124 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1125 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
1126 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1127}
1128
1129fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1130 const pt = self.pt;
1131 const zcu = pt.zcu;
1132 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1133 if (self.liveness.isUnused(inst))
1134 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1135
1136 const operand = try self.resolveInst(ty_op.operand);
1137 const operand_ty = self.typeOf(ty_op.operand);
1138 const dest_ty = self.typeOfIndex(inst);
1139
1140 const operand_abi_size = operand_ty.abiSize(zcu);
1141 const dest_abi_size = dest_ty.abiSize(zcu);
1142 const info_a = operand_ty.intInfo(zcu);
1143 const info_b = dest_ty.intInfo(zcu);
1144
1145 const dst_mcv: MCValue = blk: {
1146 if (info_a.bits == info_b.bits) {
1147 break :blk operand;
1148 }
1149 if (operand_abi_size > 4 or dest_abi_size > 4) {
1150 return self.fail("TODO implement intCast for abi sizes larger than 4", .{});
1151 }
1152
1153 const operand_lock: ?RegisterLock = switch (operand) {
1154 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1155 else => null,
1156 };
1157 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
1158
1159 const reg = try self.register_manager.allocReg(inst, gp);
1160 try self.genSetReg(dest_ty, reg, operand);
1161 break :blk MCValue{ .register = reg };
1162 };
1163
1164 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
1165}
1166
1167fn truncRegister(
1168 self: *Self,
1169 operand_reg: Register,
1170 dest_reg: Register,
1171 int_signedness: std.builtin.Signedness,
1172 int_bits: u16,
1173) !void {
1174 // TODO check if sxtb/uxtb/sxth/uxth are more efficient
1175 _ = try self.addInst(.{
1176 .tag = switch (int_signedness) {
1177 .signed => .sbfx,
1178 .unsigned => .ubfx,
1179 },
1180 .data = .{ .rr_lsb_width = .{
1181 .rd = dest_reg,
1182 .rn = operand_reg,
1183 .lsb = 0,
1184 .width = @intCast(int_bits),
1185 } },
1186 });
1187}
1188
1189/// Asserts that both operand_ty and dest_ty are integer types
1190fn trunc(
1191 self: *Self,
1192 maybe_inst: ?Air.Inst.Index,
1193 operand_bind: ReadArg.Bind,
1194 operand_ty: Type,
1195 dest_ty: Type,
1196) !MCValue {
1197 const pt = self.pt;
1198 const zcu = pt.zcu;
1199 const info_a = operand_ty.intInfo(zcu);
1200 const info_b = dest_ty.intInfo(zcu);
1201
1202 if (info_b.bits <= 32) {
1203 if (info_a.bits > 32) {
1204 return self.fail("TODO load least significant word into register", .{});
1205 }
1206
1207 var operand_reg: Register = undefined;
1208 var dest_reg: Register = undefined;
1209
1210 const read_args = [_]ReadArg{
1211 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg },
1212 };
1213 const write_args = [_]WriteArg{
1214 .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1215 };
1216 try self.allocRegs(
1217 &read_args,
1218 &write_args,
1219 if (maybe_inst) |inst| .{
1220 .corresponding_inst = inst,
1221 .operand_mapping = &.{0},
1222 } else null,
1223 );
1224
1225 switch (info_b.bits) {
1226 32 => {
1227 try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
1228 },
1229 else => {
1230 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
1231 },
1232 }
1233
1234 return MCValue{ .register = dest_reg };
1235 } else {
1236 return self.fail("TODO: truncate to ints > 32 bits", .{});
1237 }
1238}
1239
1240fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1241 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1242 const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
1243 const operand_ty = self.typeOf(ty_op.operand);
1244 const dest_ty = self.typeOfIndex(inst);
1245
1246 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
1247 break :blk try self.trunc(inst, operand_bind, operand_ty, dest_ty);
1248 };
1249
1250 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1251}
1252
1253fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1254 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1255 const pt = self.pt;
1256 const zcu = pt.zcu;
1257 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1258 const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
1259 const operand_ty = self.typeOf(ty_op.operand);
1260 switch (try operand_bind.resolveToMcv(self)) {
1261 .dead => unreachable,
1262 .unreach => unreachable,
1263 .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() },
1264 else => {
1265 switch (operand_ty.zigTypeTag(zcu)) {
1266 .bool => {
1267 var op_reg: Register = undefined;
1268 var dest_reg: Register = undefined;
1269
1270 const read_args = [_]ReadArg{
1271 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
1272 };
1273 const write_args = [_]WriteArg{
1274 .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1275 };
1276 try self.allocRegs(
1277 &read_args,
1278 &write_args,
1279 ReuseMetadata{
1280 .corresponding_inst = inst,
1281 .operand_mapping = &.{0},
1282 },
1283 );
1284
1285 _ = try self.addInst(.{
1286 .tag = .eor,
1287 .data = .{ .rr_op = .{
1288 .rd = dest_reg,
1289 .rn = op_reg,
1290 .op = Instruction.Operand.fromU32(1).?,
1291 } },
1292 });
1293
1294 break :result MCValue{ .register = dest_reg };
1295 },
1296 .vector => return self.fail("TODO bitwise not for vectors", .{}),
1297 .int => {
1298 const int_info = operand_ty.intInfo(zcu);
1299 if (int_info.bits <= 32) {
1300 var op_reg: Register = undefined;
1301 var dest_reg: Register = undefined;
1302
1303 const read_args = [_]ReadArg{
1304 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
1305 };
1306 const write_args = [_]WriteArg{
1307 .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1308 };
1309 try self.allocRegs(
1310 &read_args,
1311 &write_args,
1312 ReuseMetadata{
1313 .corresponding_inst = inst,
1314 .operand_mapping = &.{0},
1315 },
1316 );
1317
1318 _ = try self.addInst(.{
1319 .tag = .mvn,
1320 .data = .{ .r_op_mov = .{
1321 .rd = dest_reg,
1322 .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),
1323 } },
1324 });
1325
1326 if (int_info.bits < 32) {
1327 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
1328 }
1329
1330 break :result MCValue{ .register = dest_reg };
1331 } else {
1332 return self.fail("TODO ARM not on integers > u32/i32", .{});
1333 }
1334 },
1335 else => unreachable,
1336 }
1337 },
1338 }
1339 };
1340 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1341}
1342
1343fn minMax(
1344 self: *Self,
1345 tag: Air.Inst.Tag,
1346 lhs_bind: ReadArg.Bind,
1347 rhs_bind: ReadArg.Bind,
1348 lhs_ty: Type,
1349 rhs_ty: Type,
1350 maybe_inst: ?Air.Inst.Index,
1351) !MCValue {
1352 const pt = self.pt;
1353 const zcu = pt.zcu;
1354 switch (lhs_ty.zigTypeTag(zcu)) {
1355 .float => return self.fail("TODO ARM min/max on floats", .{}),
1356 .vector => return self.fail("TODO ARM min/max on vectors", .{}),
1357 .int => {
1358 assert(lhs_ty.eql(rhs_ty, zcu));
1359 const int_info = lhs_ty.intInfo(zcu);
1360 if (int_info.bits <= 32) {
1361 var lhs_reg: Register = undefined;
1362 var rhs_reg: Register = undefined;
1363 var dest_reg: Register = undefined;
1364
1365 const read_args = [_]ReadArg{
1366 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1367 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1368 };
1369 const write_args = [_]WriteArg{
1370 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1371 };
1372 try self.allocRegs(
1373 &read_args,
1374 &write_args,
1375 if (maybe_inst) |inst| .{
1376 .corresponding_inst = inst,
1377 .operand_mapping = &.{ 0, 1 },
1378 } else null,
1379 );
1380
1381 // lhs == reg should have been checked by airMinMax
1382 //
1383 // By guaranteeing lhs != rhs, we guarantee (dst !=
1384 // lhs) or (dst != rhs), which is a property we use to
1385 // omit generating one instruction when we reuse a
1386 // register.
1387 assert(lhs_reg != rhs_reg); // see note above
1388
1389 _ = try self.addInst(.{
1390 .tag = .cmp,
1391 .data = .{ .r_op_cmp = .{
1392 .rn = lhs_reg,
1393 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
1394 } },
1395 });
1396
1397 const cond_choose_lhs: Condition = switch (tag) {
1398 .max => switch (int_info.signedness) {
1399 .signed => Condition.gt,
1400 .unsigned => Condition.hi,
1401 },
1402 .min => switch (int_info.signedness) {
1403 .signed => Condition.lt,
1404 .unsigned => Condition.cc,
1405 },
1406 else => unreachable,
1407 };
1408 const cond_choose_rhs = cond_choose_lhs.negate();
1409
1410 if (dest_reg != lhs_reg) {
1411 _ = try self.addInst(.{
1412 .tag = .mov,
1413 .cond = cond_choose_lhs,
1414 .data = .{ .r_op_mov = .{
1415 .rd = dest_reg,
1416 .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none),
1417 } },
1418 });
1419 }
1420 if (dest_reg != rhs_reg) {
1421 _ = try self.addInst(.{
1422 .tag = .mov,
1423 .cond = cond_choose_rhs,
1424 .data = .{ .r_op_mov = .{
1425 .rd = dest_reg,
1426 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
1427 } },
1428 });
1429 }
1430
1431 return MCValue{ .register = dest_reg };
1432 } else {
1433 return self.fail("TODO ARM min/max on integers > u32/i32", .{});
1434 }
1435 },
1436 else => unreachable,
1437 }
1438}
1439
1440fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
1441 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
1442 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1443 const lhs_ty = self.typeOf(bin_op.lhs);
1444 const rhs_ty = self.typeOf(bin_op.rhs);
1445
1446 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1447 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1448 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1449
1450 const lhs = try self.resolveInst(bin_op.lhs);
1451 if (bin_op.lhs == bin_op.rhs) break :result lhs;
1452
1453 break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
1454 };
1455 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1456}
1457
1458fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1459 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1460 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1461 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1462 const ptr = try self.resolveInst(bin_op.lhs);
1463 const ptr_ty = self.typeOf(bin_op.lhs);
1464 const len = try self.resolveInst(bin_op.rhs);
1465 const len_ty = self.typeOf(bin_op.rhs);
1466
1467 const stack_offset = try self.allocMem(8, .@"4", inst);
1468 try self.genSetStack(ptr_ty, stack_offset, ptr);
1469 try self.genSetStack(len_ty, stack_offset - 4, len);
1470 break :result MCValue{ .stack_offset = stack_offset };
1471 };
1472 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1473}
1474
1475fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1476 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1477 const lhs_ty = self.typeOf(bin_op.lhs);
1478 const rhs_ty = self.typeOf(bin_op.rhs);
1479
1480 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1481 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1482 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1483
1484 break :result switch (tag) {
1485 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1486 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1487
1488 .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1489
1490 .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1491
1492 .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1493
1494 .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1495
1496 .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1497
1498 .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1499
1500 .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1501
1502 .add_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1503 .sub_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1504 .mul_wrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1505
1506 .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1507 .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1508 .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1509
1510 .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1511 .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1512
1513 .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1514 .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1515
1516 .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1517 .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1518
1519 else => unreachable,
1520 };
1521 };
1522 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1523}
1524
1525fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1526 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1527 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1528 const lhs_ty = self.typeOf(bin_op.lhs);
1529 const rhs_ty = self.typeOf(bin_op.rhs);
1530
1531 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1532 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1533 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1534
1535 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
1536 };
1537 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1538}
1539
1540fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
1541 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1542 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
1543 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1544}
1545
1546fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
1547 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1548 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
1549 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1550}
1551
1552fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1553 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1554 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
1555 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1556}
1557
1558fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1559 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
1560 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1561 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1562 const pt = self.pt;
1563 const zcu = pt.zcu;
1564 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1565 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1566 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
1567 const lhs_ty = self.typeOf(extra.lhs);
1568 const rhs_ty = self.typeOf(extra.rhs);
1569
1570 const tuple_ty = self.typeOfIndex(inst);
1571 const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
1572 const tuple_align = tuple_ty.abiAlignment(zcu);
1573 const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
1574
1575 switch (lhs_ty.zigTypeTag(zcu)) {
1576 .vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
1577 .int => {
1578 assert(lhs_ty.eql(rhs_ty, zcu));
1579 const int_info = lhs_ty.intInfo(zcu);
1580 if (int_info.bits < 32) {
1581 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
1582
1583 try self.spillCompareFlagsIfOccupied();
1584
1585 const base_tag: Air.Inst.Tag = switch (tag) {
1586 .add_with_overflow => .add,
1587 .sub_with_overflow => .sub,
1588 else => unreachable,
1589 };
1590 const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
1591 const dest_reg = dest.register;
1592 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
1593 defer self.register_manager.unlockReg(dest_reg_lock);
1594
1595 const truncated_reg = try self.register_manager.allocReg(null, gp);
1596 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
1597 defer self.register_manager.unlockReg(truncated_reg_lock);
1598
1599 // sbfx/ubfx truncated, dest, #0, #bits
1600 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
1601
1602 // cmp dest, truncated
1603 _ = try self.addInst(.{
1604 .tag = .cmp,
1605 .data = .{ .r_op_cmp = .{
1606 .rn = dest_reg,
1607 .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none),
1608 } },
1609 });
1610
1611 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1612 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
1613
1614 break :result MCValue{ .stack_offset = stack_offset };
1615 } else if (int_info.bits == 32) {
1616 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
1617 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
1618
1619 // Only say yes if the operation is
1620 // commutative, i.e. we can swap both of the
1621 // operands
1622 const lhs_immediate_ok = switch (tag) {
1623 .add_with_overflow => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
1624 .sub_with_overflow => false,
1625 else => unreachable,
1626 };
1627 const rhs_immediate_ok = switch (tag) {
1628 .add_with_overflow,
1629 .sub_with_overflow,
1630 => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
1631 else => unreachable,
1632 };
1633
1634 const mir_tag: Mir.Inst.Tag = switch (tag) {
1635 .add_with_overflow => .adds,
1636 .sub_with_overflow => .subs,
1637 else => unreachable,
1638 };
1639
1640 try self.spillCompareFlagsIfOccupied();
1641 self.cpsr_flags_inst = inst;
1642
1643 const dest = blk: {
1644 if (rhs_immediate_ok) {
1645 break :blk try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, null);
1646 } else if (lhs_immediate_ok) {
1647 // swap lhs and rhs
1648 break :blk try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, null);
1649 } else {
1650 break :blk try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
1651 }
1652 };
1653
1654 if (tag == .sub_with_overflow) {
1655 break :result MCValue{ .register_v_flag = dest.register };
1656 }
1657
1658 switch (int_info.signedness) {
1659 .unsigned => break :result MCValue{ .register_c_flag = dest.register },
1660 .signed => break :result MCValue{ .register_v_flag = dest.register },
1661 }
1662 } else {
1663 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
1664 }
1665 },
1666 else => unreachable,
1667 }
1668 };
1669 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1670}
1671
1672fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1673 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1674 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1675 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1676 const pt = self.pt;
1677 const zcu = pt.zcu;
1678 const result: MCValue = result: {
1679 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1680 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
1681 const lhs_ty = self.typeOf(extra.lhs);
1682 const rhs_ty = self.typeOf(extra.rhs);
1683
1684 const tuple_ty = self.typeOfIndex(inst);
1685 const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
1686 const tuple_align = tuple_ty.abiAlignment(zcu);
1687 const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
1688
1689 switch (lhs_ty.zigTypeTag(zcu)) {
1690 .vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
1691 .int => {
1692 assert(lhs_ty.eql(rhs_ty, zcu));
1693 const int_info = lhs_ty.intInfo(zcu);
1694 if (int_info.bits <= 16) {
1695 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
1696
1697 try self.spillCompareFlagsIfOccupied();
1698
1699 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1700 .signed => .smulbb,
1701 .unsigned => .mul,
1702 };
1703
1704 const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
1705 const dest_reg = dest.register;
1706 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
1707 defer self.register_manager.unlockReg(dest_reg_lock);
1708
1709 const truncated_reg = try self.register_manager.allocReg(null, gp);
1710 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
1711 defer self.register_manager.unlockReg(truncated_reg_lock);
1712
1713 // sbfx/ubfx truncated, dest, #0, #bits
1714 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
1715
1716 // cmp dest, truncated
1717 _ = try self.addInst(.{
1718 .tag = .cmp,
1719 .data = .{ .r_op_cmp = .{
1720 .rn = dest_reg,
1721 .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none),
1722 } },
1723 });
1724
1725 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1726 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
1727
1728 break :result MCValue{ .stack_offset = stack_offset };
1729 } else if (int_info.bits <= 32) {
1730 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
1731
1732 try self.spillCompareFlagsIfOccupied();
1733
1734 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1735 .signed => .smull,
1736 .unsigned => .umull,
1737 };
1738
1739 var lhs_reg: Register = undefined;
1740 var rhs_reg: Register = undefined;
1741 var rdhi: Register = undefined;
1742 var rdlo: Register = undefined;
1743 var truncated_reg: Register = undefined;
1744
1745 const read_args = [_]ReadArg{
1746 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1747 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1748 };
1749 const write_args = [_]WriteArg{
1750 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdhi },
1751 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdlo },
1752 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg },
1753 };
1754 try self.allocRegs(
1755 &read_args,
1756 &write_args,
1757 null,
1758 );
1759
1760 _ = try self.addInst(.{
1761 .tag = base_tag,
1762 .data = .{ .rrrr = .{
1763 .rdlo = rdlo,
1764 .rdhi = rdhi,
1765 .rn = lhs_reg,
1766 .rm = rhs_reg,
1767 } },
1768 });
1769
1770 // sbfx/ubfx truncated, rdlo, #0, #bits
1771 try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits);
1772
1773 // str truncated, [...]
1774 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1775
1776 // cmp truncated, rdlo
1777 _ = try self.addInst(.{
1778 .tag = .cmp,
1779 .data = .{ .r_op_cmp = .{
1780 .rn = truncated_reg,
1781 .op = Instruction.Operand.reg(rdlo, Instruction.Operand.Shift.none),
1782 } },
1783 });
1784
1785 // mov rdlo, #0
1786 _ = try self.addInst(.{
1787 .tag = .mov,
1788 .data = .{ .r_op_mov = .{
1789 .rd = rdlo,
1790 .op = Instruction.Operand.fromU32(0).?,
1791 } },
1792 });
1793
1794 // movne rdlo, #1
1795 _ = try self.addInst(.{
1796 .tag = .mov,
1797 .cond = .ne,
1798 .data = .{ .r_op_mov = .{
1799 .rd = rdlo,
1800 .op = Instruction.Operand.fromU32(1).?,
1801 } },
1802 });
1803
1804 // cmp rdhi, #0
1805 _ = try self.addInst(.{
1806 .tag = .cmp,
1807 .data = .{ .r_op_cmp = .{
1808 .rn = rdhi,
1809 .op = Instruction.Operand.fromU32(0).?,
1810 } },
1811 });
1812
1813 // movne rdlo, #1
1814 _ = try self.addInst(.{
1815 .tag = .mov,
1816 .cond = .ne,
1817 .data = .{ .r_op_mov = .{
1818 .rd = rdlo,
1819 .op = Instruction.Operand.fromU32(1).?,
1820 } },
1821 });
1822
1823 // strb rdlo, [...]
1824 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .register = rdlo });
1825
1826 break :result MCValue{ .stack_offset = stack_offset };
1827 } else {
1828 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
1829 }
1830 },
1831 else => unreachable,
1832 }
1833 };
1834 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1835}
1836
1837fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1838 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
1839 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1840 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1841 const pt = self.pt;
1842 const zcu = pt.zcu;
1843 const result: MCValue = result: {
1844 const lhs_ty = self.typeOf(extra.lhs);
1845 const rhs_ty = self.typeOf(extra.rhs);
1846
1847 const tuple_ty = self.typeOfIndex(inst);
1848 const tuple_size: u32 = @intCast(tuple_ty.abiSize(zcu));
1849 const tuple_align = tuple_ty.abiAlignment(zcu);
1850 const overflow_bit_offset: u32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
1851
1852 switch (lhs_ty.zigTypeTag(zcu)) {
1853 .vector => if (!rhs_ty.isVector(zcu))
1854 return self.fail("TODO implement vector shl_with_overflow with scalar rhs", .{})
1855 else
1856 return self.fail("TODO implement shl_with_overflow for vectors", .{}),
1857 .int => {
1858 const int_info = lhs_ty.intInfo(zcu);
1859 if (int_info.bits <= 32) {
1860 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
1861
1862 try self.spillCompareFlagsIfOccupied();
1863
1864 const shr_mir_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1865 .signed => Mir.Inst.Tag.asr,
1866 .unsigned => Mir.Inst.Tag.lsr,
1867 };
1868
1869 var lhs_reg: Register = undefined;
1870 var rhs_reg: Register = undefined;
1871 var dest_reg: Register = undefined;
1872 var reconstructed_reg: Register = undefined;
1873
1874 const rhs_mcv = try self.resolveInst(extra.rhs);
1875 const rhs_immediate_ok = rhs_mcv == .immediate and Instruction.Operand.fromU32(rhs_mcv.immediate) != null;
1876
1877 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1878 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
1879
1880 if (rhs_immediate_ok) {
1881 const read_args = [_]ReadArg{
1882 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1883 };
1884 const write_args = [_]WriteArg{
1885 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1886 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
1887 };
1888 try self.allocRegs(
1889 &read_args,
1890 &write_args,
1891 null,
1892 );
1893
1894 // lsl dest, lhs, rhs
1895 _ = try self.addInst(.{
1896 .tag = .lsl,
1897 .data = .{ .rr_shift = .{
1898 .rd = dest_reg,
1899 .rm = lhs_reg,
1900 .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_mcv.immediate)),
1901 } },
1902 });
1903
1904 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
1905
1906 // asr/lsr reconstructed, dest, rhs
1907 _ = try self.addInst(.{
1908 .tag = shr_mir_tag,
1909 .data = .{ .rr_shift = .{
1910 .rd = reconstructed_reg,
1911 .rm = dest_reg,
1912 .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_mcv.immediate)),
1913 } },
1914 });
1915 } else {
1916 const read_args = [_]ReadArg{
1917 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1918 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1919 };
1920 const write_args = [_]WriteArg{
1921 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1922 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
1923 };
1924 try self.allocRegs(
1925 &read_args,
1926 &write_args,
1927 null,
1928 );
1929
1930 // lsl dest, lhs, rhs
1931 _ = try self.addInst(.{
1932 .tag = .lsl,
1933 .data = .{ .rr_shift = .{
1934 .rd = dest_reg,
1935 .rm = lhs_reg,
1936 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
1937 } },
1938 });
1939
1940 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
1941
1942 // asr/lsr reconstructed, dest, rhs
1943 _ = try self.addInst(.{
1944 .tag = shr_mir_tag,
1945 .data = .{ .rr_shift = .{
1946 .rd = reconstructed_reg,
1947 .rm = dest_reg,
1948 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
1949 } },
1950 });
1951 }
1952
1953 // cmp lhs, reconstructed
1954 _ = try self.addInst(.{
1955 .tag = .cmp,
1956 .data = .{ .r_op_cmp = .{
1957 .rn = lhs_reg,
1958 .op = Instruction.Operand.reg(reconstructed_reg, Instruction.Operand.Shift.none),
1959 } },
1960 });
1961
1962 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });
1963 try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
1964
1965 break :result MCValue{ .stack_offset = stack_offset };
1966 } else {
1967 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
1968 }
1969 },
1970 else => unreachable,
1971 }
1972 };
1973 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1974}
1975
1976fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
1977 const zcu = self.pt.zcu;
1978 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1979 const result: MCValue = if (self.liveness.isUnused(inst))
1980 .dead
1981 else if (self.typeOf(bin_op.lhs).isVector(zcu) and !self.typeOf(bin_op.rhs).isVector(zcu))
1982 return self.fail("TODO implement vector shl_sat with scalar rhs for {}", .{self.target.cpu.arch})
1983 else
1984 return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
1985 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1986}
1987
1988fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1989 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1990 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
1991 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1992}
1993
1994fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1995 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1996 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
1997 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1998}
1999
2000fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2001 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2002 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
2003 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2004}
2005
2006fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
2007 const pt = self.pt;
2008 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2009 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2010 const optional_ty = self.typeOfIndex(inst);
2011 const abi_size: u32 = @intCast(optional_ty.abiSize(pt.zcu));
2012
2013 // Optional with a zero-bit payload type is just a boolean true
2014 if (abi_size == 1) {
2015 break :result MCValue{ .immediate = 1 };
2016 } else {
2017 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
2018 }
2019 };
2020 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2021}
2022
2023/// Given an error union, returns the error
2024fn errUnionErr(
2025 self: *Self,
2026 error_union_bind: ReadArg.Bind,
2027 error_union_ty: Type,
2028 maybe_inst: ?Air.Inst.Index,
2029) !MCValue {
2030 const pt = self.pt;
2031 const zcu = pt.zcu;
2032 const err_ty = error_union_ty.errorUnionSet(zcu);
2033 const payload_ty = error_union_ty.errorUnionPayload(zcu);
2034 if (err_ty.errorSetIsEmpty(zcu)) {
2035 return MCValue{ .immediate = 0 };
2036 }
2037 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2038 return try error_union_bind.resolveToMcv(self);
2039 }
2040
2041 const err_offset: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu));
2042 switch (try error_union_bind.resolveToMcv(self)) {
2043 .register => {
2044 var operand_reg: Register = undefined;
2045 var dest_reg: Register = undefined;
2046
2047 const read_args = [_]ReadArg{
2048 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
2049 };
2050 const write_args = [_]WriteArg{
2051 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2052 };
2053 try self.allocRegs(
2054 &read_args,
2055 &write_args,
2056 if (maybe_inst) |inst| .{
2057 .corresponding_inst = inst,
2058 .operand_mapping = &.{0},
2059 } else null,
2060 );
2061
2062 const err_bit_offset = err_offset * 8;
2063 const err_bit_size: u32 = @intCast(err_ty.abiSize(zcu) * 8);
2064
2065 _ = try self.addInst(.{
2066 .tag = .ubfx, // errors are unsigned integers
2067 .data = .{ .rr_lsb_width = .{
2068 .rd = dest_reg,
2069 .rn = operand_reg,
2070 .lsb = @intCast(err_bit_offset),
2071 .width = @intCast(err_bit_size),
2072 } },
2073 });
2074
2075 return MCValue{ .register = dest_reg };
2076 },
2077 .stack_argument_offset => |off| {
2078 return MCValue{ .stack_argument_offset = off + err_offset };
2079 },
2080 .stack_offset => |off| {
2081 return MCValue{ .stack_offset = off - err_offset };
2082 },
2083 .memory => |addr| {
2084 return MCValue{ .memory = addr + err_offset };
2085 },
2086 else => unreachable, // invalid MCValue for an error union
2087 }
2088}
2089
2090fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
2091 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2092 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2093 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
2094 const error_union_ty = self.typeOf(ty_op.operand);
2095
2096 break :result try self.errUnionErr(error_union_bind, error_union_ty, inst);
2097 };
2098 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2099}
2100
2101/// Given an error union, returns the payload
2102fn errUnionPayload(
2103 self: *Self,
2104 error_union_bind: ReadArg.Bind,
2105 error_union_ty: Type,
2106 maybe_inst: ?Air.Inst.Index,
2107) !MCValue {
2108 const pt = self.pt;
2109 const zcu = pt.zcu;
2110 const err_ty = error_union_ty.errorUnionSet(zcu);
2111 const payload_ty = error_union_ty.errorUnionPayload(zcu);
2112 if (err_ty.errorSetIsEmpty(zcu)) {
2113 return try error_union_bind.resolveToMcv(self);
2114 }
2115 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2116 return MCValue.none;
2117 }
2118
2119 const payload_offset: u32 = @intCast(errUnionPayloadOffset(payload_ty, zcu));
2120 switch (try error_union_bind.resolveToMcv(self)) {
2121 .register => {
2122 var operand_reg: Register = undefined;
2123 var dest_reg: Register = undefined;
2124
2125 const read_args = [_]ReadArg{
2126 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
2127 };
2128 const write_args = [_]WriteArg{
2129 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2130 };
2131 try self.allocRegs(
2132 &read_args,
2133 &write_args,
2134 if (maybe_inst) |inst| .{
2135 .corresponding_inst = inst,
2136 .operand_mapping = &.{0},
2137 } else null,
2138 );
2139
2140 const payload_bit_offset = payload_offset * 8;
2141 const payload_bit_size: u32 = @intCast(payload_ty.abiSize(zcu) * 8);
2142
2143 _ = try self.addInst(.{
2144 .tag = if (payload_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx,
2145 .data = .{ .rr_lsb_width = .{
2146 .rd = dest_reg,
2147 .rn = operand_reg,
2148 .lsb = @intCast(payload_bit_offset),
2149 .width = @intCast(payload_bit_size),
2150 } },
2151 });
2152
2153 return MCValue{ .register = dest_reg };
2154 },
2155 .stack_argument_offset => |off| {
2156 return MCValue{ .stack_argument_offset = off + payload_offset };
2157 },
2158 .stack_offset => |off| {
2159 return MCValue{ .stack_offset = off - payload_offset };
2160 },
2161 .memory => |addr| {
2162 return MCValue{ .memory = addr + payload_offset };
2163 },
2164 else => unreachable, // invalid MCValue for an error union
2165 }
2166}
2167
2168fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
2169 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2170 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2171 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
2172 const error_union_ty = self.typeOf(ty_op.operand);
2173
2174 break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst);
2175 };
2176 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2177}
2178
2179// *(E!T) -> E
2180fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
2181 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2182 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
2183 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2184}
2185
2186// *(E!T) -> *T
2187fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
2188 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2189 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
2190 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2191}
2192
2193fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2194 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2195 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
2196 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2197}
2198
2199fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2200 const result: MCValue = if (self.liveness.isUnused(inst))
2201 .dead
2202 else
2203 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2204 return self.finishAir(inst, result, .{ .none, .none, .none });
2205}
2206
2207fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2208 _ = inst;
2209 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
2210}
2211
2212fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
2213 _ = inst;
2214 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
2215}
2216
2217/// T to E!T
2218fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
2219 const pt = self.pt;
2220 const zcu = pt.zcu;
2221 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2222 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2223 const error_union_ty = ty_op.ty.toType();
2224 const error_ty = error_union_ty.errorUnionSet(zcu);
2225 const payload_ty = error_union_ty.errorUnionPayload(zcu);
2226 const operand = try self.resolveInst(ty_op.operand);
2227 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand;
2228
2229 const abi_size: u32 = @intCast(error_union_ty.abiSize(zcu));
2230 const abi_align = error_union_ty.abiAlignment(zcu);
2231 const stack_offset: u32 = @intCast(try self.allocMem(abi_size, abi_align, inst));
2232 const payload_off = errUnionPayloadOffset(payload_ty, zcu);
2233 const err_off = errUnionErrorOffset(payload_ty, zcu);
2234 try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), operand);
2235 try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), .{ .immediate = 0 });
2236
2237 break :result MCValue{ .stack_offset = stack_offset };
2238 };
2239 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2240}
2241
2242/// E to E!T
2243fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
2244 const pt = self.pt;
2245 const zcu = pt.zcu;
2246 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2247 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2248 const error_union_ty = ty_op.ty.toType();
2249 const error_ty = error_union_ty.errorUnionSet(zcu);
2250 const payload_ty = error_union_ty.errorUnionPayload(zcu);
2251 const operand = try self.resolveInst(ty_op.operand);
2252 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result operand;
2253
2254 const abi_size: u32 = @intCast(error_union_ty.abiSize(zcu));
2255 const abi_align = error_union_ty.abiAlignment(zcu);
2256 const stack_offset: u32 = @intCast(try self.allocMem(abi_size, abi_align, inst));
2257 const payload_off = errUnionPayloadOffset(payload_ty, zcu);
2258 const err_off = errUnionErrorOffset(payload_ty, zcu);
2259 try self.genSetStack(error_ty, stack_offset - @as(u32, @intCast(err_off)), operand);
2260 try self.genSetStack(payload_ty, stack_offset - @as(u32, @intCast(payload_off)), .undef);
2261
2262 break :result MCValue{ .stack_offset = stack_offset };
2263 };
2264 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2265}
2266
2267/// Given a slice, returns the length
2268fn slicePtr(mcv: MCValue) MCValue {
2269 switch (mcv) {
2270 .register => unreachable, // a slice doesn't fit in one register
2271 .stack_argument_offset => |off| {
2272 return MCValue{ .stack_argument_offset = off };
2273 },
2274 .stack_offset => |off| {
2275 return MCValue{ .stack_offset = off };
2276 },
2277 .memory => |addr| {
2278 return MCValue{ .memory = addr };
2279 },
2280 else => unreachable, // invalid MCValue for a slice
2281 }
2282}
2283
2284fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
2285 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2286 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2287 const mcv = try self.resolveInst(ty_op.operand);
2288 break :result slicePtr(mcv);
2289 };
2290 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2291}
2292
2293fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
2294 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2295 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2296 const mcv = try self.resolveInst(ty_op.operand);
2297 switch (mcv) {
2298 .register => unreachable, // a slice doesn't fit in one register
2299 .stack_argument_offset => |off| {
2300 break :result MCValue{ .stack_argument_offset = off + 4 };
2301 },
2302 .stack_offset => |off| {
2303 break :result MCValue{ .stack_offset = off - 4 };
2304 },
2305 .memory => |addr| {
2306 break :result MCValue{ .memory = addr + 4 };
2307 },
2308 else => unreachable, // invalid MCValue for a slice
2309 }
2310 };
2311 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2312}
2313
2314fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
2315 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2316 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2317 const mcv = try self.resolveInst(ty_op.operand);
2318 switch (mcv) {
2319 .dead, .unreach => unreachable,
2320 .ptr_stack_offset => |off| {
2321 break :result MCValue{ .ptr_stack_offset = off - 4 };
2322 },
2323 else => {
2324 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
2325 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 4 } };
2326
2327 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
2328 },
2329 }
2330 };
2331 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2332}
2333
2334fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
2335 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2336 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2337 const mcv = try self.resolveInst(ty_op.operand);
2338 switch (mcv) {
2339 .dead, .unreach => unreachable,
2340 .ptr_stack_offset => |off| {
2341 break :result MCValue{ .ptr_stack_offset = off };
2342 },
2343 else => {
2344 if (self.reuseOperand(inst, ty_op.operand, 0, mcv)) {
2345 break :result mcv;
2346 } else {
2347 break :result MCValue{ .register = try self.copyToTmpRegister(Type.usize, mcv) };
2348 }
2349 },
2350 }
2351 };
2352 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2353}
2354
2355fn ptrElemVal(
2356 self: *Self,
2357 ptr_bind: ReadArg.Bind,
2358 index_bind: ReadArg.Bind,
2359 ptr_ty: Type,
2360 maybe_inst: ?Air.Inst.Index,
2361) !MCValue {
2362 const pt = self.pt;
2363 const zcu = pt.zcu;
2364 const elem_ty = ptr_ty.childType(zcu);
2365 const elem_size: u32 = @intCast(elem_ty.abiSize(zcu));
2366
2367 switch (elem_size) {
2368 1, 4 => {
2369 var base_reg: Register = undefined;
2370 var index_reg: Register = undefined;
2371 var dest_reg: Register = undefined;
2372
2373 const read_args = [_]ReadArg{
2374 .{ .ty = ptr_ty, .bind = ptr_bind, .class = gp, .reg = &base_reg },
2375 .{ .ty = Type.usize, .bind = index_bind, .class = gp, .reg = &index_reg },
2376 };
2377 const write_args = [_]WriteArg{
2378 .{ .ty = elem_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2379 };
2380 try self.allocRegs(
2381 &read_args,
2382 &write_args,
2383 if (maybe_inst) |inst| .{
2384 .corresponding_inst = inst,
2385 .operand_mapping = &.{ 0, 1 },
2386 } else null,
2387 );
2388
2389 const tag: Mir.Inst.Tag = switch (elem_size) {
2390 1 => .ldrb,
2391 4 => .ldr,
2392 else => unreachable,
2393 };
2394 const shift: u5 = switch (elem_size) {
2395 1 => 0,
2396 4 => 2,
2397 else => unreachable,
2398 };
2399
2400 _ = try self.addInst(.{
2401 .tag = tag,
2402 .data = .{ .rr_offset = .{
2403 .rt = dest_reg,
2404 .rn = base_reg,
2405 .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
2406 } },
2407 });
2408
2409 return MCValue{ .register = dest_reg };
2410 },
2411 else => {
2412 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null);
2413
2414 const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst);
2415 try self.load(dest, addr, ptr_ty);
2416 return dest;
2417 },
2418 }
2419}
2420
2421fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2422 const pt = self.pt;
2423 const zcu = pt.zcu;
2424 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2425 const slice_ty = self.typeOf(bin_op.lhs);
2426 const result: MCValue = if (!slice_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: {
2427 const ptr_ty = slice_ty.slicePtrFieldType(zcu);
2428
2429 const slice_mcv = try self.resolveInst(bin_op.lhs);
2430 const base_mcv = slicePtr(slice_mcv);
2431
2432 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2433 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2434
2435 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
2436 };
2437 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2438}
2439
2440fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2441 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2442 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2443 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2444 const slice_mcv = try self.resolveInst(extra.lhs);
2445 const base_mcv = slicePtr(slice_mcv);
2446
2447 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2448 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2449
2450 const slice_ty = self.typeOf(extra.lhs);
2451 const index_ty = self.typeOf(extra.rhs);
2452
2453 const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null);
2454 break :result addr;
2455 };
2456 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2457}
2458
2459fn arrayElemVal(
2460 self: *Self,
2461 array_bind: ReadArg.Bind,
2462 index_bind: ReadArg.Bind,
2463 array_ty: Type,
2464 maybe_inst: ?Air.Inst.Index,
2465) InnerError!MCValue {
2466 const pt = self.pt;
2467 const zcu = pt.zcu;
2468 const elem_ty = array_ty.childType(zcu);
2469
2470 const mcv = try array_bind.resolveToMcv(self);
2471 switch (mcv) {
2472 .stack_offset,
2473 .memory,
2474 .stack_argument_offset,
2475 => {
2476 const ptr_to_mcv = switch (mcv) {
2477 .stack_offset => |off| MCValue{ .ptr_stack_offset = off },
2478 .memory => |addr| MCValue{ .immediate = @intCast(addr) },
2479 .stack_argument_offset => |off| blk: {
2480 const reg = try self.register_manager.allocReg(null, gp);
2481
2482 _ = try self.addInst(.{
2483 .tag = .ldr_ptr_stack_argument,
2484 .data = .{ .r_stack_offset = .{
2485 .rt = reg,
2486 .stack_offset = off,
2487 } },
2488 });
2489
2490 break :blk MCValue{ .register = reg };
2491 },
2492 else => unreachable,
2493 };
2494 const ptr_to_mcv_lock: ?RegisterLock = switch (ptr_to_mcv) {
2495 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2496 else => null,
2497 };
2498 defer if (ptr_to_mcv_lock) |lock| self.register_manager.unlockReg(lock);
2499
2500 const base_bind: ReadArg.Bind = .{ .mcv = ptr_to_mcv };
2501
2502 const ptr_ty = try pt.singleMutPtrType(elem_ty);
2503
2504 return try self.ptrElemVal(base_bind, index_bind, ptr_ty, maybe_inst);
2505 },
2506 else => return self.fail("TODO implement array_elem_val for {}", .{mcv}),
2507 }
2508}
2509
2510fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2511 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2512 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2513 const array_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2514 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2515 const array_ty = self.typeOf(bin_op.lhs);
2516
2517 break :result try self.arrayElemVal(array_bind, index_bind, array_ty, inst);
2518 };
2519 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2520}
2521
2522fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
2523 const pt = self.pt;
2524 const zcu = pt.zcu;
2525 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2526 const ptr_ty = self.typeOf(bin_op.lhs);
2527 const result: MCValue = if (!ptr_ty.isVolatilePtr(zcu) and self.liveness.isUnused(inst)) .dead else result: {
2528 const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2529 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2530
2531 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
2532 };
2533 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2534}
2535
2536fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2537 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2538 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2539 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2540 const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2541 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2542
2543 const ptr_ty = self.typeOf(extra.lhs);
2544 const index_ty = self.typeOf(extra.rhs);
2545
2546 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null);
2547 break :result addr;
2548 };
2549 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2550}
2551
2552fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
2553 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2554 _ = bin_op;
2555 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
2556 // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2557}
2558
2559fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
2560 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2561 _ = ty_op;
2562 return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
2563 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2564}
2565
2566fn airClz(self: *Self, inst: Air.Inst.Index) !void {
2567 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2568 _ = ty_op;
2569 return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
2570 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2571}
2572
2573fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
2574 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2575 _ = ty_op;
2576 return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
2577 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2578}
2579
2580fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
2581 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2582 _ = ty_op;
2583 return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
2584 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2585}
2586
2587fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
2588 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2589 _ = ty_op;
2590 return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
2591 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2592}
2593
2594fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
2595 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2596 _ = ty_op;
2597 return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
2598 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2599}
2600
2601fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
2602 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2603 _ = ty_op;
2604 return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch});
2605 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2606}
2607
2608fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
2609 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2610 const result: MCValue = if (self.liveness.isUnused(inst))
2611 .dead
2612 else
2613 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
2614 return self.finishAir(inst, result, .{ un_op, .none, .none });
2615}
2616
2617fn reuseOperand(
2618 self: *Self,
2619 inst: Air.Inst.Index,
2620 operand: Air.Inst.Ref,
2621 op_index: Air.Liveness.OperandInt,
2622 mcv: MCValue,
2623) bool {
2624 if (!self.liveness.operandDies(inst, op_index))
2625 return false;
2626
2627 switch (mcv) {
2628 .register => |reg| {
2629 // We assert that this register is allocatable by asking
2630 // for its index
2631 const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above
2632 if (!self.register_manager.isRegFree(reg)) {
2633 self.register_manager.registers[index] = inst;
2634 }
2635
2636 log.debug("%{d} => {} (reused)", .{ inst, reg });
2637 },
2638 .stack_offset => |off| {
2639 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
2640 },
2641 .cpsr_flags => {
2642 log.debug("%{d} => cpsr_flags (reused)", .{inst});
2643 },
2644 else => return false,
2645 }
2646
2647 // Prevent the operand deaths processing code from deallocating it.
2648 self.reused_operands.set(op_index);
2649
2650 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
2651 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2652 branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead);
2653
2654 return true;
2655}
2656
2657fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
2658 const pt = self.pt;
2659 const zcu = pt.zcu;
2660 const elem_ty = ptr_ty.childType(zcu);
2661 const elem_size: u32 = @intCast(elem_ty.abiSize(zcu));
2662
2663 switch (ptr) {
2664 .none => unreachable,
2665 .undef => unreachable,
2666 .unreach => unreachable,
2667 .dead => unreachable,
2668 .cpsr_flags,
2669 .register_c_flag,
2670 .register_v_flag,
2671 => unreachable, // cannot hold an address
2672 .immediate => |imm| {
2673 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
2674 },
2675 .ptr_stack_offset => |off| {
2676 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
2677 },
2678 .register => |reg| {
2679 const reg_lock = self.register_manager.lockReg(reg);
2680 defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked);
2681
2682 switch (dst_mcv) {
2683 .register => |dst_reg| {
2684 try self.genLdrRegister(dst_reg, reg, elem_ty);
2685 },
2686 .stack_offset => |off| {
2687 if (elem_size <= 4) {
2688 const tmp_reg = try self.register_manager.allocReg(null, gp);
2689 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
2690 defer self.register_manager.unlockReg(tmp_reg_lock);
2691
2692 try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
2693 try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg });
2694 } else {
2695 // TODO optimize the register allocation
2696 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
2697 const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
2698 defer for (regs_locks) |reg_locked| {
2699 self.register_manager.unlockReg(reg_locked);
2700 };
2701
2702 const src_reg = reg;
2703 const dst_reg = regs[0];
2704 const len_reg = regs[1];
2705 const count_reg = regs[2];
2706 const tmp_reg = regs[3];
2707
2708 // sub dst_reg, fp, #off
2709 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
2710
2711 // mov len, #elem_size
2712 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
2713
2714 // memcpy(src, dst, len)
2715 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
2716 }
2717 },
2718 else => unreachable, // attempting to load into non-register or non-stack MCValue
2719 }
2720 },
2721 .memory,
2722 .stack_offset,
2723 .stack_argument_offset,
2724 => {
2725 const reg = try self.register_manager.allocReg(null, gp);
2726 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
2727 defer self.register_manager.unlockReg(reg_lock);
2728
2729 try self.genSetReg(ptr_ty, reg, ptr);
2730 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
2731 },
2732 }
2733}
2734
2735fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
2736 const pt = self.pt;
2737 const zcu = pt.zcu;
2738 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2739 const elem_ty = self.typeOfIndex(inst);
2740 const result: MCValue = result: {
2741 if (!elem_ty.hasRuntimeBits(zcu))
2742 break :result MCValue.none;
2743
2744 const ptr = try self.resolveInst(ty_op.operand);
2745 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(zcu);
2746 if (self.liveness.isUnused(inst) and !is_volatile)
2747 break :result MCValue.dead;
2748
2749 const dest_mcv: MCValue = blk: {
2750 const ptr_fits_dest = elem_ty.abiSize(zcu) <= 4;
2751 if (ptr_fits_dest and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
2752 // The MCValue that holds the pointer can be re-used as the value.
2753 break :blk ptr;
2754 } else {
2755 break :blk try self.allocRegOrMem(elem_ty, true, inst);
2756 }
2757 };
2758 try self.load(dest_mcv, ptr, self.typeOf(ty_op.operand));
2759
2760 break :result dest_mcv;
2761 };
2762 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2763}
2764
2765fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
2766 const pt = self.pt;
2767 const elem_size: u32 = @intCast(value_ty.abiSize(pt.zcu));
2768
2769 switch (ptr) {
2770 .none => unreachable,
2771 .undef => unreachable,
2772 .unreach => unreachable,
2773 .dead => unreachable,
2774 .cpsr_flags,
2775 .register_c_flag,
2776 .register_v_flag,
2777 => unreachable, // cannot hold an address
2778 .immediate => |imm| {
2779 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
2780 },
2781 .ptr_stack_offset => |off| {
2782 try self.genSetStack(value_ty, off, value);
2783 },
2784 .register => |addr_reg| {
2785 const addr_reg_lock = self.register_manager.lockReg(addr_reg);
2786 defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
2787
2788 switch (value) {
2789 .dead => unreachable,
2790 .undef => {
2791 try self.genSetReg(value_ty, addr_reg, value);
2792 },
2793 .register => |value_reg| {
2794 try self.genStrRegister(value_reg, addr_reg, value_ty);
2795 },
2796 else => {
2797 if (elem_size <= 4) {
2798 const tmp_reg = try self.register_manager.allocReg(null, gp);
2799 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
2800 defer self.register_manager.unlockReg(tmp_reg_lock);
2801
2802 try self.genSetReg(value_ty, tmp_reg, value);
2803 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
2804 } else {
2805 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
2806 const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
2807 defer for (regs_locks) |reg| {
2808 self.register_manager.unlockReg(reg);
2809 };
2810
2811 const src_reg = regs[0];
2812 const dst_reg = addr_reg;
2813 const len_reg = regs[1];
2814 const count_reg = regs[2];
2815 const tmp_reg = regs[3];
2816
2817 switch (value) {
2818 .stack_offset => |off| {
2819 // sub src_reg, fp, #off
2820 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
2821 },
2822 .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }),
2823 .stack_argument_offset => |off| {
2824 _ = try self.addInst(.{
2825 .tag = .ldr_ptr_stack_argument,
2826 .data = .{ .r_stack_offset = .{
2827 .rt = src_reg,
2828 .stack_offset = off,
2829 } },
2830 });
2831 },
2832 else => return self.fail("TODO store {} to register", .{value}),
2833 }
2834
2835 // mov len, #elem_size
2836 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
2837
2838 // memcpy(src, dst, len)
2839 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
2840 }
2841 },
2842 }
2843 },
2844 .memory,
2845 .stack_offset,
2846 .stack_argument_offset,
2847 => {
2848 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr);
2849 try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty);
2850 },
2851 }
2852}
2853
2854fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2855 if (safety) {
2856 // TODO if the value is undef, write 0xaa bytes to dest
2857 } else {
2858 // TODO if the value is undef, don't lower this instruction
2859 }
2860 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2861 const ptr = try self.resolveInst(bin_op.lhs);
2862 const value = try self.resolveInst(bin_op.rhs);
2863 const ptr_ty = self.typeOf(bin_op.lhs);
2864 const value_ty = self.typeOf(bin_op.rhs);
2865
2866 try self.store(ptr, value, ptr_ty, value_ty);
2867
2868 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2869}
2870
2871fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
2872 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2873 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
2874 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index);
2875 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
2876}
2877
2878fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
2879 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2880 const result = try self.structFieldPtr(inst, ty_op.operand, index);
2881 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2882}
2883
2884fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
2885 return if (self.liveness.isUnused(inst)) .dead else result: {
2886 const pt = self.pt;
2887 const zcu = pt.zcu;
2888 const mcv = try self.resolveInst(operand);
2889 const ptr_ty = self.typeOf(operand);
2890 const struct_ty = ptr_ty.childType(zcu);
2891 const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(index, zcu));
2892 switch (mcv) {
2893 .ptr_stack_offset => |off| {
2894 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
2895 },
2896 else => {
2897 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
2898 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
2899
2900 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
2901 },
2902 }
2903 };
2904}
2905
2906fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2907 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2908 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
2909 const operand = extra.struct_operand;
2910 const index = extra.field_index;
2911 const pt = self.pt;
2912 const zcu = pt.zcu;
2913 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2914 const mcv = try self.resolveInst(operand);
2915 const struct_ty = self.typeOf(operand);
2916 const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(index, zcu));
2917 const struct_field_ty = struct_ty.fieldType(index, zcu);
2918
2919 switch (mcv) {
2920 .dead, .unreach => unreachable,
2921 .stack_argument_offset => |off| {
2922 break :result MCValue{ .stack_argument_offset = off + struct_field_offset };
2923 },
2924 .stack_offset => |off| {
2925 break :result MCValue{ .stack_offset = off - struct_field_offset };
2926 },
2927 .memory => |addr| {
2928 break :result MCValue{ .memory = addr + struct_field_offset };
2929 },
2930 .register_c_flag,
2931 .register_v_flag,
2932 => |reg| {
2933 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
2934 defer self.register_manager.unlockReg(reg_lock);
2935
2936 const field: MCValue = switch (index) {
2937 // get wrapped value: return register
2938 0 => MCValue{ .register = reg },
2939
2940 // get overflow bit: return C or V flag
2941 1 => MCValue{ .cpsr_flags = switch (mcv) {
2942 .register_c_flag => .cs,
2943 .register_v_flag => .vs,
2944 else => unreachable,
2945 } },
2946
2947 else => unreachable,
2948 };
2949
2950 if (self.reuseOperand(inst, operand, 0, field)) {
2951 break :result field;
2952 } else {
2953 // Copy to new register
2954 const dest_reg = try self.register_manager.allocReg(null, gp);
2955 try self.genSetReg(struct_field_ty, dest_reg, field);
2956
2957 break :result MCValue{ .register = dest_reg };
2958 }
2959 },
2960 .register => {
2961 var operand_reg: Register = undefined;
2962 var dest_reg: Register = undefined;
2963
2964 const read_args = [_]ReadArg{
2965 .{ .ty = struct_ty, .bind = .{ .mcv = mcv }, .class = gp, .reg = &operand_reg },
2966 };
2967 const write_args = [_]WriteArg{
2968 .{ .ty = struct_field_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2969 };
2970 try self.allocRegs(
2971 &read_args,
2972 &write_args,
2973 ReuseMetadata{
2974 .corresponding_inst = inst,
2975 .operand_mapping = &.{0},
2976 },
2977 );
2978
2979 const field_bit_offset = struct_field_offset * 8;
2980 const field_bit_size: u32 = @intCast(struct_field_ty.abiSize(zcu) * 8);
2981
2982 _ = try self.addInst(.{
2983 .tag = if (struct_field_ty.isSignedInt(zcu)) Mir.Inst.Tag.sbfx else .ubfx,
2984 .data = .{ .rr_lsb_width = .{
2985 .rd = dest_reg,
2986 .rn = operand_reg,
2987 .lsb = @intCast(field_bit_offset),
2988 .width = @intCast(field_bit_size),
2989 } },
2990 });
2991
2992 break :result MCValue{ .register = dest_reg };
2993 },
2994 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
2995 }
2996 };
2997
2998 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
2999}
3000
3001fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
3002 const pt = self.pt;
3003 const zcu = pt.zcu;
3004 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3005 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
3006 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3007 const field_ptr = try self.resolveInst(extra.field_ptr);
3008 const struct_ty = ty_pl.ty.toType().childType(zcu);
3009
3010 if (struct_ty.zigTypeTag(zcu) == .@"union") {
3011 return self.fail("TODO implement @fieldParentPtr codegen for unions", .{});
3012 }
3013
3014 const struct_field_offset: u32 = @intCast(struct_ty.structFieldOffset(extra.field_index, zcu));
3015 switch (field_ptr) {
3016 .ptr_stack_offset => |off| {
3017 break :result MCValue{ .ptr_stack_offset = off + struct_field_offset };
3018 },
3019 else => {
3020 const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr };
3021 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
3022
3023 break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
3024 },
3025 }
3026 };
3027 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
3028}
3029
3030/// An argument to a Mir instruction which is read (and possibly also
3031/// written to) by the respective instruction
3032const ReadArg = struct {
3033 ty: Type,
3034 bind: Bind,
3035 class: RegisterManager.RegisterBitSet,
3036 reg: *Register,
3037
3038 const Bind = union(enum) {
3039 inst: Air.Inst.Ref,
3040 mcv: MCValue,
3041
3042 fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue {
3043 return switch (bind) {
3044 .inst => |inst| try function.resolveInst(inst),
3045 .mcv => |mcv| mcv,
3046 };
3047 }
3048
3049 fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u32 {
3050 switch (bind) {
3051 .inst => |inst| {
3052 // TODO resolve independently of inst_table
3053 const mcv = try function.resolveInst(inst);
3054 switch (mcv) {
3055 .immediate => |imm| return imm,
3056 else => return null,
3057 }
3058 },
3059 .mcv => |mcv| {
3060 switch (mcv) {
3061 .immediate => |imm| return imm,
3062 else => return null,
3063 }
3064 },
3065 }
3066 }
3067 };
3068};
3069
3070/// An argument to a Mir instruction which is written to (but not read
3071/// from) by the respective instruction
3072const WriteArg = struct {
3073 ty: Type,
3074 bind: Bind,
3075 class: RegisterManager.RegisterBitSet,
3076 reg: *Register,
3077
3078 const Bind = union(enum) {
3079 reg: Register,
3080 none: void,
3081 };
3082};
3083
3084/// Holds all data necessary for enabling the potential reuse of
3085/// operand registers as destinations
3086const ReuseMetadata = struct {
3087 corresponding_inst: Air.Inst.Index,
3088
3089 /// Maps every element index of read_args to the corresponding
3090 /// index in the Air instruction
3091 ///
3092 /// When the order of read_args corresponds exactly to the order
3093 /// of the inputs of the Air instruction, this would be e.g.
3094 /// &.{ 0, 1 }. However, when the order is not the same or some
3095 /// inputs to the Air instruction are omitted (e.g. when they can
3096 /// be represented as immediates to the Mir instruction),
3097 /// operand_mapping should reflect that fact.
3098 operand_mapping: []const Air.Liveness.OperandInt,
3099};
3100
3101/// Allocate a set of registers for use as arguments for a Mir
3102/// instruction
3103///
3104/// If the Mir instruction these registers are allocated for
3105/// corresponds exactly to a single Air instruction, populate
3106/// reuse_metadata in order to enable potential reuse of an operand as
3107/// the destination (provided that that operand dies in this
3108/// instruction).
3109///
3110/// Reusing an operand register as destination is the only time two
3111/// arguments may share the same register. In all other cases,
3112/// allocRegs guarantees that a register will never be allocated to
3113/// more than one argument.
3114///
3115/// Furthermore, allocReg guarantees that all arguments which are
3116/// already bound to registers before calling allocRegs will not
3117/// change their register binding. This is done by locking these
3118/// registers.
3119fn allocRegs(
3120 self: *Self,
3121 read_args: []const ReadArg,
3122 write_args: []const WriteArg,
3123 reuse_metadata: ?ReuseMetadata,
3124) InnerError!void {
3125 // Air instructions have exactly one output
3126 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
3127
3128 // The operand mapping is a 1:1 mapping of read args to their
3129 // corresponding operand index in the Air instruction
3130 assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above
3131
3132 const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len);
3133 defer self.gpa.free(locks);
3134 const read_locks = locks[0..read_args.len];
3135 const write_locks = locks[read_args.len..];
3136
3137 @memset(locks, null);
3138 defer for (locks) |lock| {
3139 if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
3140 };
3141
3142 // When we reuse a read_arg as a destination, the corresponding
3143 // MCValue of the read_arg will be set to .dead. In that case, we
3144 // skip allocating this read_arg.
3145 var reused_read_arg: ?usize = null;
3146
3147 // Lock all args which are already allocated to registers
3148 for (read_args, 0..) |arg, i| {
3149 const mcv = try arg.bind.resolveToMcv(self);
3150 if (mcv == .register) {
3151 read_locks[i] = self.register_manager.lockReg(mcv.register);
3152 }
3153 }
3154
3155 for (write_args, 0..) |arg, i| {
3156 if (arg.bind == .reg) {
3157 write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
3158 }
3159 }
3160
3161 // Allocate registers for all args which aren't allocated to
3162 // registers yet
3163 for (read_args, 0..) |arg, i| {
3164 const mcv = try arg.bind.resolveToMcv(self);
3165 if (mcv == .register) {
3166 arg.reg.* = mcv.register;
3167 } else {
3168 const track_inst: ?Air.Inst.Index = switch (arg.bind) {
3169 .inst => |inst| inst.toIndex().?,
3170 else => null,
3171 };
3172 arg.reg.* = try self.register_manager.allocReg(track_inst, arg.class);
3173 read_locks[i] = self.register_manager.lockReg(arg.reg.*);
3174 }
3175 }
3176
3177 if (reuse_metadata != null) {
3178 const inst = reuse_metadata.?.corresponding_inst;
3179 const operand_mapping = reuse_metadata.?.operand_mapping;
3180 const arg = write_args[0];
3181 if (arg.bind == .reg) {
3182 arg.reg.* = arg.bind.reg;
3183 } else {
3184 reuse_operand: for (read_args, 0..) |read_arg, i| {
3185 if (read_arg.bind == .inst) {
3186 const operand = read_arg.bind.inst;
3187 const mcv = try self.resolveInst(operand);
3188 if (mcv == .register and
3189 std.meta.eql(arg.class, read_arg.class) and
3190 self.reuseOperand(inst, operand, operand_mapping[i], mcv))
3191 {
3192 arg.reg.* = mcv.register;
3193 write_locks[0] = null;
3194 reused_read_arg = i;
3195 break :reuse_operand;
3196 }
3197 }
3198 } else {
3199 arg.reg.* = try self.register_manager.allocReg(inst, arg.class);
3200 write_locks[0] = self.register_manager.lockReg(arg.reg.*);
3201 }
3202 }
3203 } else {
3204 for (write_args, 0..) |arg, i| {
3205 if (arg.bind == .reg) {
3206 arg.reg.* = arg.bind.reg;
3207 } else {
3208 arg.reg.* = try self.register_manager.allocReg(null, arg.class);
3209 write_locks[i] = self.register_manager.lockReg(arg.reg.*);
3210 }
3211 }
3212 }
3213
3214 // For all read_args which need to be moved from non-register to
3215 // register, perform the move
3216 for (read_args, 0..) |arg, i| {
3217 if (reused_read_arg) |j| {
3218 // Check whether this read_arg was reused
3219 if (i == j) continue;
3220 }
3221
3222 const mcv = try arg.bind.resolveToMcv(self);
3223 if (mcv != .register) {
3224 if (arg.bind == .inst) {
3225 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
3226 const inst = arg.bind.inst.toIndex().?;
3227
3228 // Overwrite the MCValue associated with this inst
3229 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* });
3230
3231 // If the previous MCValue occupied some space we track, we
3232 // need to make sure it is marked as free now.
3233 switch (mcv) {
3234 .cpsr_flags => {
3235 assert(self.cpsr_flags_inst.? == inst);
3236 self.cpsr_flags_inst = null;
3237 },
3238 .register => |prev_reg| {
3239 assert(!self.register_manager.isRegFree(prev_reg));
3240 self.register_manager.freeReg(prev_reg);
3241 },
3242 else => {},
3243 }
3244 }
3245
3246 try self.genSetReg(arg.ty, arg.reg.*, mcv);
3247 }
3248 }
3249}
3250
3251/// Wrapper around allocRegs and addInst tailored for specific Mir
3252/// instructions which are binary operations acting on two registers
3253///
3254/// Returns the destination register
3255fn binOpRegister(
3256 self: *Self,
3257 mir_tag: Mir.Inst.Tag,
3258 lhs_bind: ReadArg.Bind,
3259 rhs_bind: ReadArg.Bind,
3260 lhs_ty: Type,
3261 rhs_ty: Type,
3262 maybe_inst: ?Air.Inst.Index,
3263) !MCValue {
3264 var lhs_reg: Register = undefined;
3265 var rhs_reg: Register = undefined;
3266 var dest_reg: Register = undefined;
3267
3268 const read_args = [_]ReadArg{
3269 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
3270 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
3271 };
3272 const write_args = [_]WriteArg{
3273 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3274 };
3275 try self.allocRegs(
3276 &read_args,
3277 &write_args,
3278 if (maybe_inst) |inst| .{
3279 .corresponding_inst = inst,
3280 .operand_mapping = &.{ 0, 1 },
3281 } else null,
3282 );
3283
3284 const mir_data: Mir.Inst.Data = switch (mir_tag) {
3285 .add,
3286 .adds,
3287 .sub,
3288 .subs,
3289 .@"and",
3290 .orr,
3291 .eor,
3292 => .{ .rr_op = .{
3293 .rd = dest_reg,
3294 .rn = lhs_reg,
3295 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
3296 } },
3297 .lsl,
3298 .asr,
3299 .lsr,
3300 => .{ .rr_shift = .{
3301 .rd = dest_reg,
3302 .rm = lhs_reg,
3303 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
3304 } },
3305 .mul,
3306 .smulbb,
3307 => .{ .rrr = .{
3308 .rd = dest_reg,
3309 .rn = lhs_reg,
3310 .rm = rhs_reg,
3311 } },
3312 else => unreachable,
3313 };
3314
3315 _ = try self.addInst(.{
3316 .tag = mir_tag,
3317 .data = mir_data,
3318 });
3319
3320 return MCValue{ .register = dest_reg };
3321}
3322
3323/// Wrapper around allocRegs and addInst tailored for specific Mir
3324/// instructions which are binary operations acting on a register and
3325/// an immediate
3326///
3327/// Returns the destination register
3328fn binOpImmediate(
3329 self: *Self,
3330 mir_tag: Mir.Inst.Tag,
3331 lhs_bind: ReadArg.Bind,
3332 rhs_immediate: u32,
3333 lhs_ty: Type,
3334 lhs_and_rhs_swapped: bool,
3335 maybe_inst: ?Air.Inst.Index,
3336) !MCValue {
3337 var lhs_reg: Register = undefined;
3338 var dest_reg: Register = undefined;
3339
3340 const read_args = [_]ReadArg{
3341 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
3342 };
3343 const write_args = [_]WriteArg{
3344 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3345 };
3346 const operand_mapping: []const Air.Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
3347 try self.allocRegs(
3348 &read_args,
3349 &write_args,
3350 if (maybe_inst) |inst| .{
3351 .corresponding_inst = inst,
3352 .operand_mapping = operand_mapping,
3353 } else null,
3354 );
3355
3356 const mir_data: Mir.Inst.Data = switch (mir_tag) {
3357 .add,
3358 .adds,
3359 .sub,
3360 .subs,
3361 .@"and",
3362 .orr,
3363 .eor,
3364 => .{ .rr_op = .{
3365 .rd = dest_reg,
3366 .rn = lhs_reg,
3367 .op = Instruction.Operand.fromU32(rhs_immediate).?,
3368 } },
3369 .lsl,
3370 .asr,
3371 .lsr,
3372 => .{ .rr_shift = .{
3373 .rd = dest_reg,
3374 .rm = lhs_reg,
3375 .shift_amount = Instruction.ShiftAmount.imm(@intCast(rhs_immediate)),
3376 } },
3377 else => unreachable,
3378 };
3379
3380 _ = try self.addInst(.{
3381 .tag = mir_tag,
3382 .data = mir_data,
3383 });
3384
3385 return MCValue{ .register = dest_reg };
3386}
3387
3388fn addSub(
3389 self: *Self,
3390 tag: Air.Inst.Tag,
3391 lhs_bind: ReadArg.Bind,
3392 rhs_bind: ReadArg.Bind,
3393 lhs_ty: Type,
3394 rhs_ty: Type,
3395 maybe_inst: ?Air.Inst.Index,
3396) InnerError!MCValue {
3397 const pt = self.pt;
3398 const zcu = pt.zcu;
3399 switch (lhs_ty.zigTypeTag(zcu)) {
3400 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3401 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3402 .int => {
3403 assert(lhs_ty.eql(rhs_ty, zcu));
3404 const int_info = lhs_ty.intInfo(zcu);
3405 if (int_info.bits <= 32) {
3406 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
3407 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3408
3409 // Only say yes if the operation is
3410 // commutative, i.e. we can swap both of the
3411 // operands
3412 const lhs_immediate_ok = switch (tag) {
3413 .add => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
3414 .sub => false,
3415 else => unreachable,
3416 };
3417 const rhs_immediate_ok = switch (tag) {
3418 .add,
3419 .sub,
3420 => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
3421 else => unreachable,
3422 };
3423
3424 const mir_tag: Mir.Inst.Tag = switch (tag) {
3425 .add => .add,
3426 .sub => .sub,
3427 else => unreachable,
3428 };
3429
3430 if (rhs_immediate_ok) {
3431 return try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
3432 } else if (lhs_immediate_ok) {
3433 // swap lhs and rhs
3434 return try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
3435 } else {
3436 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3437 }
3438 } else {
3439 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3440 }
3441 },
3442 else => unreachable,
3443 }
3444}
3445
3446fn mul(
3447 self: *Self,
3448 lhs_bind: ReadArg.Bind,
3449 rhs_bind: ReadArg.Bind,
3450 lhs_ty: Type,
3451 rhs_ty: Type,
3452 maybe_inst: ?Air.Inst.Index,
3453) InnerError!MCValue {
3454 const pt = self.pt;
3455 const zcu = pt.zcu;
3456 switch (lhs_ty.zigTypeTag(zcu)) {
3457 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3458 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3459 .int => {
3460 assert(lhs_ty.eql(rhs_ty, zcu));
3461 const int_info = lhs_ty.intInfo(zcu);
3462 if (int_info.bits <= 32) {
3463 // TODO add optimisations for multiplication
3464 // with immediates, for example a * 2 can be
3465 // lowered to a << 1
3466 return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3467 } else {
3468 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3469 }
3470 },
3471 else => unreachable,
3472 }
3473}
3474
3475fn divFloat(
3476 self: *Self,
3477 lhs_bind: ReadArg.Bind,
3478 rhs_bind: ReadArg.Bind,
3479 lhs_ty: Type,
3480 rhs_ty: Type,
3481 maybe_inst: ?Air.Inst.Index,
3482) InnerError!MCValue {
3483 _ = lhs_bind;
3484 _ = rhs_bind;
3485 _ = rhs_ty;
3486 _ = maybe_inst;
3487
3488 const pt = self.pt;
3489 const zcu = pt.zcu;
3490 switch (lhs_ty.zigTypeTag(zcu)) {
3491 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3492 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3493 else => unreachable,
3494 }
3495}
3496
3497fn divTrunc(
3498 self: *Self,
3499 lhs_bind: ReadArg.Bind,
3500 rhs_bind: ReadArg.Bind,
3501 lhs_ty: Type,
3502 rhs_ty: Type,
3503 maybe_inst: ?Air.Inst.Index,
3504) InnerError!MCValue {
3505 const pt = self.pt;
3506 const zcu = pt.zcu;
3507 switch (lhs_ty.zigTypeTag(zcu)) {
3508 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3509 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3510 .int => {
3511 assert(lhs_ty.eql(rhs_ty, zcu));
3512 const int_info = lhs_ty.intInfo(zcu);
3513 if (int_info.bits <= 32) {
3514 switch (int_info.signedness) {
3515 .signed => {
3516 return self.fail("TODO ARM signed integer division", .{});
3517 },
3518 .unsigned => {
3519 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3520
3521 if (rhs_immediate) |imm| {
3522 if (std.math.isPowerOfTwo(imm)) {
3523 const shift = std.math.log2_int(u32, imm);
3524 return try self.binOpImmediate(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst);
3525 } else {
3526 return self.fail("TODO ARM integer division by constants", .{});
3527 }
3528 } else {
3529 return self.fail("TODO ARM integer division", .{});
3530 }
3531 },
3532 }
3533 } else {
3534 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
3535 }
3536 },
3537 else => unreachable,
3538 }
3539}
3540
3541fn divFloor(
3542 self: *Self,
3543 lhs_bind: ReadArg.Bind,
3544 rhs_bind: ReadArg.Bind,
3545 lhs_ty: Type,
3546 rhs_ty: Type,
3547 maybe_inst: ?Air.Inst.Index,
3548) InnerError!MCValue {
3549 const pt = self.pt;
3550 const zcu = pt.zcu;
3551 switch (lhs_ty.zigTypeTag(zcu)) {
3552 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3553 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3554 .int => {
3555 assert(lhs_ty.eql(rhs_ty, zcu));
3556 const int_info = lhs_ty.intInfo(zcu);
3557 if (int_info.bits <= 32) {
3558 switch (int_info.signedness) {
3559 .signed => {
3560 return self.fail("TODO ARM signed integer division", .{});
3561 },
3562 .unsigned => {
3563 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3564
3565 if (rhs_immediate) |imm| {
3566 if (std.math.isPowerOfTwo(imm)) {
3567 const shift = std.math.log2_int(u32, imm);
3568 return try self.binOpImmediate(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst);
3569 } else {
3570 return self.fail("TODO ARM integer division by constants", .{});
3571 }
3572 } else {
3573 return self.fail("TODO ARM integer division", .{});
3574 }
3575 },
3576 }
3577 } else {
3578 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
3579 }
3580 },
3581 else => unreachable,
3582 }
3583}
3584
3585fn divExact(
3586 self: *Self,
3587 lhs_bind: ReadArg.Bind,
3588 rhs_bind: ReadArg.Bind,
3589 lhs_ty: Type,
3590 rhs_ty: Type,
3591 maybe_inst: ?Air.Inst.Index,
3592) InnerError!MCValue {
3593 _ = lhs_bind;
3594 _ = rhs_bind;
3595 _ = rhs_ty;
3596 _ = maybe_inst;
3597
3598 const pt = self.pt;
3599 const zcu = pt.zcu;
3600 switch (lhs_ty.zigTypeTag(zcu)) {
3601 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3602 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3603 .int => return self.fail("TODO ARM div_exact", .{}),
3604 else => unreachable,
3605 }
3606}
3607
3608fn rem(
3609 self: *Self,
3610 lhs_bind: ReadArg.Bind,
3611 rhs_bind: ReadArg.Bind,
3612 lhs_ty: Type,
3613 rhs_ty: Type,
3614 maybe_inst: ?Air.Inst.Index,
3615) InnerError!MCValue {
3616 const pt = self.pt;
3617 const zcu = pt.zcu;
3618 switch (lhs_ty.zigTypeTag(zcu)) {
3619 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3620 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3621 .int => {
3622 assert(lhs_ty.eql(rhs_ty, zcu));
3623 const int_info = lhs_ty.intInfo(zcu);
3624 if (int_info.bits <= 32) {
3625 switch (int_info.signedness) {
3626 .signed => {
3627 return self.fail("TODO ARM signed integer zcu", .{});
3628 },
3629 .unsigned => {
3630 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3631
3632 if (rhs_immediate) |imm| {
3633 if (std.math.isPowerOfTwo(imm)) {
3634 const log2 = std.math.log2_int(u32, imm);
3635
3636 var lhs_reg: Register = undefined;
3637 var dest_reg: Register = undefined;
3638
3639 const read_args = [_]ReadArg{
3640 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
3641 };
3642 const write_args = [_]WriteArg{
3643 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3644 };
3645 try self.allocRegs(
3646 &read_args,
3647 &write_args,
3648 if (maybe_inst) |inst| .{
3649 .corresponding_inst = inst,
3650 .operand_mapping = &.{0},
3651 } else null,
3652 );
3653
3654 try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2);
3655
3656 return MCValue{ .register = dest_reg };
3657 } else {
3658 return self.fail("TODO ARM integer zcu by constants", .{});
3659 }
3660 } else {
3661 return self.fail("TODO ARM integer zcu", .{});
3662 }
3663 },
3664 }
3665 } else {
3666 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
3667 }
3668 },
3669 else => unreachable,
3670 }
3671}
3672
3673fn modulo(
3674 self: *Self,
3675 lhs_bind: ReadArg.Bind,
3676 rhs_bind: ReadArg.Bind,
3677 lhs_ty: Type,
3678 rhs_ty: Type,
3679 maybe_inst: ?Air.Inst.Index,
3680) InnerError!MCValue {
3681 _ = lhs_bind;
3682 _ = rhs_bind;
3683 _ = rhs_ty;
3684 _ = maybe_inst;
3685
3686 const pt = self.pt;
3687 const zcu = pt.zcu;
3688 switch (lhs_ty.zigTypeTag(zcu)) {
3689 .float => return self.fail("TODO ARM binary operations on floats", .{}),
3690 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3691 .int => return self.fail("TODO ARM zcu", .{}),
3692 else => unreachable,
3693 }
3694}
3695
3696fn wrappingArithmetic(
3697 self: *Self,
3698 tag: Air.Inst.Tag,
3699 lhs_bind: ReadArg.Bind,
3700 rhs_bind: ReadArg.Bind,
3701 lhs_ty: Type,
3702 rhs_ty: Type,
3703 maybe_inst: ?Air.Inst.Index,
3704) InnerError!MCValue {
3705 const pt = self.pt;
3706 const zcu = pt.zcu;
3707 switch (lhs_ty.zigTypeTag(zcu)) {
3708 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3709 .int => {
3710 const int_info = lhs_ty.intInfo(zcu);
3711 if (int_info.bits <= 32) {
3712 // Generate an add/sub/mul
3713 const result: MCValue = switch (tag) {
3714 .add_wrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
3715 .sub_wrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
3716 .mul_wrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
3717 else => unreachable,
3718 };
3719
3720 // Truncate if necessary
3721 const result_reg = result.register;
3722 if (int_info.bits < 32) {
3723 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3724 }
3725
3726 return result;
3727 } else {
3728 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3729 }
3730 },
3731 else => unreachable,
3732 }
3733}
3734
3735fn bitwise(
3736 self: *Self,
3737 tag: Air.Inst.Tag,
3738 lhs_bind: ReadArg.Bind,
3739 rhs_bind: ReadArg.Bind,
3740 lhs_ty: Type,
3741 rhs_ty: Type,
3742 maybe_inst: ?Air.Inst.Index,
3743) InnerError!MCValue {
3744 const pt = self.pt;
3745 const zcu = pt.zcu;
3746 switch (lhs_ty.zigTypeTag(zcu)) {
3747 .vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3748 .int => {
3749 assert(lhs_ty.eql(rhs_ty, zcu));
3750 const int_info = lhs_ty.intInfo(zcu);
3751 if (int_info.bits <= 32) {
3752 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
3753 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3754
3755 const lhs_immediate_ok = if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
3756 const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
3757
3758 const mir_tag: Mir.Inst.Tag = switch (tag) {
3759 .bit_and => .@"and",
3760 .bit_or => .orr,
3761 .xor => .eor,
3762 else => unreachable,
3763 };
3764
3765 if (rhs_immediate_ok) {
3766 return try self.binOpImmediate(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
3767 } else if (lhs_immediate_ok) {
3768 // swap lhs and rhs
3769 return try self.binOpImmediate(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
3770 } else {
3771 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3772 }
3773 } else {
3774 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3775 }
3776 },
3777 else => unreachable,
3778 }
3779}
3780
3781fn shiftExact(
3782 self: *Self,
3783 tag: Air.Inst.Tag,
3784 lhs_bind: ReadArg.Bind,
3785 rhs_bind: ReadArg.Bind,
3786 lhs_ty: Type,
3787 rhs_ty: Type,
3788 maybe_inst: ?Air.Inst.Index,
3789) InnerError!MCValue {
3790 const pt = self.pt;
3791 const zcu = pt.zcu;
3792 switch (lhs_ty.zigTypeTag(zcu)) {
3793 .vector => if (!rhs_ty.isVector(zcu))
3794 return self.fail("TODO ARM vector shift with scalar rhs", .{})
3795 else
3796 return self.fail("TODO ARM binary operations on vectors", .{}),
3797 .int => {
3798 const int_info = lhs_ty.intInfo(zcu);
3799 if (int_info.bits <= 32) {
3800 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3801
3802 const mir_tag: Mir.Inst.Tag = switch (tag) {
3803 .shl_exact => .lsl,
3804 .shr_exact => switch (lhs_ty.intInfo(zcu).signedness) {
3805 .signed => Mir.Inst.Tag.asr,
3806 .unsigned => Mir.Inst.Tag.lsr,
3807 },
3808 else => unreachable,
3809 };
3810
3811 if (rhs_immediate) |imm| {
3812 return try self.binOpImmediate(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
3813 } else {
3814 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3815 }
3816 } else {
3817 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3818 }
3819 },
3820 else => unreachable,
3821 }
3822}
3823
3824fn shiftNormal(
3825 self: *Self,
3826 tag: Air.Inst.Tag,
3827 lhs_bind: ReadArg.Bind,
3828 rhs_bind: ReadArg.Bind,
3829 lhs_ty: Type,
3830 rhs_ty: Type,
3831 maybe_inst: ?Air.Inst.Index,
3832) InnerError!MCValue {
3833 const pt = self.pt;
3834 const zcu = pt.zcu;
3835 switch (lhs_ty.zigTypeTag(zcu)) {
3836 .vector => if (!rhs_ty.isVector(zcu))
3837 return self.fail("TODO ARM vector shift with scalar rhs", .{})
3838 else
3839 return self.fail("TODO ARM binary operations on vectors", .{}),
3840 .int => {
3841 const int_info = lhs_ty.intInfo(zcu);
3842 if (int_info.bits <= 32) {
3843 // Generate a shl_exact/shr_exact
3844 const result: MCValue = switch (tag) {
3845 .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
3846 .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
3847 else => unreachable,
3848 };
3849
3850 // Truncate if necessary
3851 switch (tag) {
3852 .shr => return result,
3853 .shl => {
3854 const result_reg = result.register;
3855 if (int_info.bits < 32) {
3856 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3857 }
3858
3859 return result;
3860 },
3861 else => unreachable,
3862 }
3863 } else {
3864 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3865 }
3866 },
3867 else => unreachable,
3868 }
3869}
3870
3871fn booleanOp(
3872 self: *Self,
3873 tag: Air.Inst.Tag,
3874 lhs_bind: ReadArg.Bind,
3875 rhs_bind: ReadArg.Bind,
3876 lhs_ty: Type,
3877 rhs_ty: Type,
3878 maybe_inst: ?Air.Inst.Index,
3879) InnerError!MCValue {
3880 const pt = self.pt;
3881 const zcu = pt.zcu;
3882 switch (lhs_ty.zigTypeTag(zcu)) {
3883 .bool => {
3884 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
3885 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3886
3887 const mir_tag: Mir.Inst.Tag = switch (tag) {
3888 .bool_and => .@"and",
3889 .bool_or => .orr,
3890 else => unreachable,
3891 };
3892
3893 if (rhs_immediate) |imm| {
3894 return try self.binOpImmediate(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
3895 } else if (lhs_immediate) |imm| {
3896 // swap lhs and rhs
3897 return try self.binOpImmediate(mir_tag, rhs_bind, imm, rhs_ty, true, maybe_inst);
3898 } else {
3899 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3900 }
3901 },
3902 else => unreachable,
3903 }
3904}
3905
3906fn ptrArithmetic(
3907 self: *Self,
3908 tag: Air.Inst.Tag,
3909 lhs_bind: ReadArg.Bind,
3910 rhs_bind: ReadArg.Bind,
3911 lhs_ty: Type,
3912 rhs_ty: Type,
3913 maybe_inst: ?Air.Inst.Index,
3914) InnerError!MCValue {
3915 const pt = self.pt;
3916 const zcu = pt.zcu;
3917 switch (lhs_ty.zigTypeTag(zcu)) {
3918 .pointer => {
3919 assert(rhs_ty.eql(Type.usize, zcu));
3920
3921 const ptr_ty = lhs_ty;
3922 const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
3923 .one => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type
3924 else => ptr_ty.childType(zcu),
3925 };
3926 const elem_size: u32 = @intCast(elem_ty.abiSize(zcu));
3927
3928 const base_tag: Air.Inst.Tag = switch (tag) {
3929 .ptr_add => .add,
3930 .ptr_sub => .sub,
3931 else => unreachable,
3932 };
3933
3934 if (elem_size == 1) {
3935 return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst);
3936 } else {
3937 // convert the offset into a byte offset by
3938 // multiplying it with elem_size
3939 const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
3940
3941 const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
3942 const offset_bind = ReadArg.Bind{ .mcv = offset };
3943
3944 const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null);
3945 return addr;
3946 }
3947 },
3948 else => unreachable,
3949 }
3950}
3951
3952fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void {
3953 const pt = self.pt;
3954 const zcu = pt.zcu;
3955 const abi_size = ty.abiSize(zcu);
3956
3957 const tag: Mir.Inst.Tag = switch (abi_size) {
3958 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb else .ldrb,
3959 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh else .ldrh,
3960 3, 4 => .ldr,
3961 else => unreachable,
3962 };
3963
3964 const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
3965 .rt = dest_reg,
3966 .rn = addr_reg,
3967 .offset = .{ .offset = Instruction.Offset.none },
3968 } };
3969 const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
3970 .rt = dest_reg,
3971 .rn = addr_reg,
3972 .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
3973 } };
3974
3975 const data: Mir.Inst.Data = switch (abi_size) {
3976 1 => if (ty.isSignedInt(zcu)) rr_extra_offset else rr_offset,
3977 2 => rr_extra_offset,
3978 3, 4 => rr_offset,
3979 else => unreachable,
3980 };
3981
3982 _ = try self.addInst(.{
3983 .tag = tag,
3984 .data = data,
3985 });
3986}
3987
3988fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void {
3989 const pt = self.pt;
3990 const abi_size = ty.abiSize(pt.zcu);
3991
3992 const tag: Mir.Inst.Tag = switch (abi_size) {
3993 1 => .strb,
3994 2 => .strh,
3995 4 => .str,
3996 3 => return self.fail("TODO: genStrRegister for abi_size={}", .{abi_size}),
3997 else => unreachable,
3998 };
3999
4000 const rr_offset: Mir.Inst.Data = .{ .rr_offset = .{
4001 .rt = source_reg,
4002 .rn = addr_reg,
4003 .offset = .{ .offset = Instruction.Offset.none },
4004 } };
4005 const rr_extra_offset: Mir.Inst.Data = .{ .rr_extra_offset = .{
4006 .rt = source_reg,
4007 .rn = addr_reg,
4008 .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none },
4009 } };
4010
4011 const data: Mir.Inst.Data = switch (abi_size) {
4012 1, 4 => rr_offset,
4013 2 => rr_extra_offset,
4014 else => unreachable,
4015 };
4016
4017 _ = try self.addInst(.{
4018 .tag = tag,
4019 .data = data,
4020 });
4021}
4022
4023fn genInlineMemcpy(
4024 self: *Self,
4025 src: Register,
4026 dst: Register,
4027 len: Register,
4028 count: Register,
4029 tmp: Register,
4030) !void {
4031 // mov count, #0
4032 _ = try self.addInst(.{
4033 .tag = .mov,
4034 .data = .{ .r_op_mov = .{
4035 .rd = count,
4036 .op = Instruction.Operand.imm(0, 0),
4037 } },
4038 });
4039
4040 // loop:
4041 // cmp count, len
4042 _ = try self.addInst(.{
4043 .tag = .cmp,
4044 .data = .{ .r_op_cmp = .{
4045 .rn = count,
4046 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
4047 } },
4048 });
4049
4050 // bge end
4051 _ = try self.addInst(.{
4052 .tag = .b,
4053 .cond = .ge,
4054 .data = .{ .inst = @intCast(self.mir_instructions.len + 5) },
4055 });
4056
4057 // ldrb tmp, [src, count]
4058 _ = try self.addInst(.{
4059 .tag = .ldrb,
4060 .data = .{ .rr_offset = .{
4061 .rt = tmp,
4062 .rn = src,
4063 .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
4064 } },
4065 });
4066
4067 // strb tmp, [src, count]
4068 _ = try self.addInst(.{
4069 .tag = .strb,
4070 .data = .{ .rr_offset = .{
4071 .rt = tmp,
4072 .rn = dst,
4073 .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
4074 } },
4075 });
4076
4077 // add count, count, #1
4078 _ = try self.addInst(.{
4079 .tag = .add,
4080 .data = .{ .rr_op = .{
4081 .rd = count,
4082 .rn = count,
4083 .op = Instruction.Operand.imm(1, 0),
4084 } },
4085 });
4086
4087 // b loop
4088 _ = try self.addInst(.{
4089 .tag = .b,
4090 .data = .{ .inst = @intCast(self.mir_instructions.len - 5) },
4091 });
4092
4093 // end:
4094}
4095
4096fn genInlineMemset(
4097 self: *Self,
4098 dst: MCValue,
4099 val: MCValue,
4100 len: MCValue,
4101) !void {
4102 const dst_reg = switch (dst) {
4103 .register => |r| r,
4104 else => try self.copyToTmpRegister(Type.manyptr_u8, dst),
4105 };
4106 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
4107 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
4108
4109 const val_reg = switch (val) {
4110 .register => |r| r,
4111 else => try self.copyToTmpRegister(Type.u8, val),
4112 };
4113 const val_reg_lock = self.register_manager.lockReg(val_reg);
4114 defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock);
4115
4116 const len_reg = switch (len) {
4117 .register => |r| r,
4118 else => try self.copyToTmpRegister(Type.usize, len),
4119 };
4120 const len_reg_lock = self.register_manager.lockReg(len_reg);
4121 defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock);
4122
4123 const count_reg = try self.register_manager.allocReg(null, gp);
4124
4125 try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg);
4126}
4127
4128fn genInlineMemsetCode(
4129 self: *Self,
4130 dst: Register,
4131 val: Register,
4132 len: Register,
4133 count: Register,
4134) !void {
4135 // mov count, #0
4136 _ = try self.addInst(.{
4137 .tag = .mov,
4138 .data = .{ .r_op_mov = .{
4139 .rd = count,
4140 .op = Instruction.Operand.imm(0, 0),
4141 } },
4142 });
4143
4144 // loop:
4145 // cmp count, len
4146 _ = try self.addInst(.{
4147 .tag = .cmp,
4148 .data = .{ .r_op_cmp = .{
4149 .rn = count,
4150 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
4151 } },
4152 });
4153
4154 // bge end
4155 _ = try self.addInst(.{
4156 .tag = .b,
4157 .cond = .ge,
4158 .data = .{ .inst = @intCast(self.mir_instructions.len + 4) },
4159 });
4160
4161 // strb val, [src, count]
4162 _ = try self.addInst(.{
4163 .tag = .strb,
4164 .data = .{ .rr_offset = .{
4165 .rt = val,
4166 .rn = dst,
4167 .offset = .{ .offset = Instruction.Offset.reg(count, .none) },
4168 } },
4169 });
4170
4171 // add count, count, #1
4172 _ = try self.addInst(.{
4173 .tag = .add,
4174 .data = .{ .rr_op = .{
4175 .rd = count,
4176 .rn = count,
4177 .op = Instruction.Operand.imm(1, 0),
4178 } },
4179 });
4180
4181 // b loop
4182 _ = try self.addInst(.{
4183 .tag = .b,
4184 .data = .{ .inst = @intCast(self.mir_instructions.len - 4) },
4185 });
4186
4187 // end:
4188}
4189
4190fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4191 // skip zero-bit arguments as they don't have a corresponding arg instruction
4192 var arg_index = self.arg_index;
4193 while (self.args[arg_index] == .none) arg_index += 1;
4194 self.arg_index = arg_index + 1;
4195
4196 const zcu = self.pt.zcu;
4197 const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?;
4198 const file = zcu.fileByIndex(func_zir.file);
4199 if (!file.mod.?.strip) {
4200 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
4201 const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
4202 const ty = self.typeOfIndex(inst);
4203 const zir = &file.zir.?;
4204 const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
4205 try self.dbg_info_relocs.append(self.gpa, .{
4206 .tag = tag,
4207 .ty = ty,
4208 .name = name,
4209 .mcv = self.args[arg_index],
4210 });
4211 }
4212
4213 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
4214 return self.finishAir(inst, result, .{ .none, .none, .none });
4215}
4216
4217fn airTrap(self: *Self) !void {
4218 _ = try self.addInst(.{
4219 .tag = .undefined_instruction,
4220 .data = .{ .nop = {} },
4221 });
4222 return self.finishAirBookkeeping();
4223}
4224
4225fn airBreakpoint(self: *Self) !void {
4226 _ = try self.addInst(.{
4227 .tag = .bkpt,
4228 .data = .{ .imm16 = 0 },
4229 });
4230 return self.finishAirBookkeeping();
4231}
4232
4233fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
4234 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for arm", .{});
4235 return self.finishAir(inst, result, .{ .none, .none, .none });
4236}
4237
4238fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
4239 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for arm", .{});
4240 return self.finishAir(inst, result, .{ .none, .none, .none });
4241}
4242
4243fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
4244 if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{});
4245 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4246 const callee = pl_op.operand;
4247 const extra = self.air.extraData(Air.Call, pl_op.payload);
4248 const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]);
4249 const ty = self.typeOf(callee);
4250 const pt = self.pt;
4251 const zcu = pt.zcu;
4252 const ip = &zcu.intern_pool;
4253
4254 const fn_ty = switch (ty.zigTypeTag(zcu)) {
4255 .@"fn" => ty,
4256 .pointer => ty.childType(zcu),
4257 else => unreachable,
4258 };
4259
4260 var info = try self.resolveCallingConventionValues(fn_ty);
4261 defer info.deinit(self);
4262
4263 // According to the Procedure Call Standard for the ARM
4264 // Architecture, compare flags are not preserved across
4265 // calls. Therefore, if some value is currently stored there, we
4266 // need to save it.
4267 try self.spillCompareFlagsIfOccupied();
4268
4269 // Save caller-saved registers, but crucially *after* we save the
4270 // compare flags as saving compare flags may require a new
4271 // caller-saved register
4272 for (caller_preserved_regs) |reg| {
4273 try self.register_manager.getReg(reg, null);
4274 }
4275
4276 // If returning by reference, r0 will contain the address of where
4277 // to put the result into. In that case, make sure that r0 remains
4278 // untouched by the parameter passing code
4279 const r0_lock: ?RegisterLock = if (info.return_value == .stack_offset) blk: {
4280 log.debug("airCall: return by reference", .{});
4281 const ret_ty = fn_ty.fnReturnType(zcu);
4282 const ret_abi_size: u32 = @intCast(ret_ty.abiSize(zcu));
4283 const ret_abi_align = ret_ty.abiAlignment(zcu);
4284 const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst);
4285
4286 const ptr_ty = try pt.singleMutPtrType(ret_ty);
4287 try self.register_manager.getReg(.r0, null);
4288 try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });
4289
4290 info.return_value = .{ .stack_offset = stack_offset };
4291
4292 break :blk self.register_manager.lockRegAssumeUnused(.r0);
4293 } else null;
4294 defer if (r0_lock) |reg| self.register_manager.unlockReg(reg);
4295
4296 // Make space for the arguments passed via the stack
4297 self.max_end_stack += info.stack_byte_count;
4298
4299 for (info.args, 0..) |mc_arg, arg_i| {
4300 const arg = args[arg_i];
4301 const arg_ty = self.typeOf(arg);
4302 const arg_mcv = try self.resolveInst(args[arg_i]);
4303
4304 switch (mc_arg) {
4305 .none => continue,
4306 .register => |reg| {
4307 try self.register_manager.getReg(reg, null);
4308 try self.genSetReg(arg_ty, reg, arg_mcv);
4309 },
4310 .stack_offset => unreachable,
4311 .stack_argument_offset => |offset| try self.genSetStackArgument(
4312 arg_ty,
4313 offset,
4314 arg_mcv,
4315 ),
4316 else => unreachable,
4317 }
4318 }
4319
4320 // Due to incremental compilation, how function calls are generated depends
4321 // on linking.
4322 if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
4323 .func => {
4324 return self.fail("TODO implement calling functions", .{});
4325 },
4326 .@"extern" => {
4327 return self.fail("TODO implement calling extern functions", .{});
4328 },
4329 else => {
4330 return self.fail("TODO implement calling bitcasted functions", .{});
4331 },
4332 } else {
4333 assert(ty.zigTypeTag(zcu) == .pointer);
4334 const mcv = try self.resolveInst(callee);
4335
4336 try self.genSetReg(Type.usize, .lr, mcv);
4337 }
4338
4339 // TODO: add Instruction.supportedOn
4340 // function for ARM
4341 if (self.target.cpu.has(.arm, .has_v5t)) {
4342 _ = try self.addInst(.{
4343 .tag = .blx,
4344 .data = .{ .reg = .lr },
4345 });
4346 } else {
4347 return self.fail("TODO fix blx emulation for ARM <v5", .{});
4348 // _ = try self.addInst(.{
4349 // .tag = .mov,
4350 // .data = .{ .rr_op = .{
4351 // .rd = .lr,
4352 // .rn = .r0,
4353 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
4354 // } },
4355 // });
4356 // _ = try self.addInst(.{
4357 // .tag = .bx,
4358 // .data = .{ .reg = .lr },
4359 // });
4360 }
4361
4362 const result: MCValue = result: {
4363 switch (info.return_value) {
4364 .register => |reg| {
4365 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
4366 // Save function return value into a tracked register
4367 log.debug("airCall: copying {} as it is not tracked", .{reg});
4368 const new_reg = try self.copyToTmpRegister(fn_ty.fnReturnType(zcu), info.return_value);
4369 break :result MCValue{ .register = new_reg };
4370 }
4371 },
4372 else => {},
4373 }
4374 break :result info.return_value;
4375 };
4376
4377 if (args.len <= Air.Liveness.bpi - 2) {
4378 var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1);
4379 buf[0] = callee;
4380 @memcpy(buf[1..][0..args.len], args);
4381 return self.finishAir(inst, result, buf);
4382 }
4383 var bt = try self.iterateBigTomb(inst, 1 + args.len);
4384 bt.feed(callee);
4385 for (args) |arg| {
4386 bt.feed(arg);
4387 }
4388 return bt.finishAir(result);
4389}
4390
4391fn airRet(self: *Self, inst: Air.Inst.Index) !void {
4392 const pt = self.pt;
4393 const zcu = pt.zcu;
4394 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4395 const operand = try self.resolveInst(un_op);
4396 const ret_ty = self.fn_type.fnReturnType(zcu);
4397
4398 switch (self.ret_mcv) {
4399 .none => {},
4400 .immediate => {
4401 assert(ret_ty.isError(zcu));
4402 },
4403 .register => |reg| {
4404 // Return result by value
4405 try self.genSetReg(ret_ty, reg, operand);
4406 },
4407 .stack_offset => {
4408 // Return result by reference
4409 //
4410 // self.ret_mcv is an address to where this function
4411 // should store its result into
4412 const ptr_ty = try pt.singleMutPtrType(ret_ty);
4413 try self.store(self.ret_mcv, operand, ptr_ty, ret_ty);
4414 },
4415 else => unreachable, // invalid return result
4416 }
4417
4418 // Just add space for an instruction, patch this later
4419 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
4420
4421 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4422}
4423
4424fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
4425 const pt = self.pt;
4426 const zcu = pt.zcu;
4427 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4428 const ptr = try self.resolveInst(un_op);
4429 const ptr_ty = self.typeOf(un_op);
4430 const ret_ty = self.fn_type.fnReturnType(zcu);
4431
4432 switch (self.ret_mcv) {
4433 .none => {},
4434 .register => {
4435 // Return result by value
4436 try self.load(self.ret_mcv, ptr, ptr_ty);
4437 },
4438 .stack_offset => {
4439 // Return result by reference
4440 //
4441 // self.ret_mcv is an address to where this function
4442 // should store its result into
4443 //
4444 // If the operand is a ret_ptr instruction, we are done
4445 // here. Else we need to load the result from the location
4446 // pointed to by the operand and store it to the result
4447 // location.
4448 const op_inst = un_op.toIndex().?;
4449 if (self.air.instructions.items(.tag)[@intFromEnum(op_inst)] != .ret_ptr) {
4450 const abi_size: u32 = @intCast(ret_ty.abiSize(zcu));
4451 const abi_align = ret_ty.abiAlignment(zcu);
4452
4453 const offset = try self.allocMem(abi_size, abi_align, null);
4454
4455 const tmp_mcv = MCValue{ .stack_offset = offset };
4456 try self.load(tmp_mcv, ptr, ptr_ty);
4457 try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty);
4458 }
4459 },
4460 else => unreachable, // invalid return result
4461 }
4462
4463 // Just add space for an instruction, patch this later
4464 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
4465
4466 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
4467}
4468
4469fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
4470 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4471 const lhs_ty = self.typeOf(bin_op.lhs);
4472
4473 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
4474 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
4475 };
4476
4477 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
4478}
4479
4480fn cmp(
4481 self: *Self,
4482 lhs: ReadArg.Bind,
4483 rhs: ReadArg.Bind,
4484 lhs_ty: Type,
4485 op: math.CompareOperator,
4486) !MCValue {
4487 const pt = self.pt;
4488 const zcu = pt.zcu;
4489 const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
4490 .optional => blk: {
4491 const payload_ty = lhs_ty.optionalChild(zcu);
4492 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4493 break :blk Type.u1;
4494 } else if (lhs_ty.isPtrLikeOptional(zcu)) {
4495 break :blk Type.usize;
4496 } else {
4497 return self.fail("TODO ARM cmp non-pointer optionals", .{});
4498 }
4499 },
4500 .float => return self.fail("TODO ARM cmp floats", .{}),
4501 .@"enum" => lhs_ty.intTagType(zcu),
4502 .int => lhs_ty,
4503 .bool => Type.u1,
4504 .pointer => Type.usize,
4505 .error_set => Type.u16,
4506 else => unreachable,
4507 };
4508
4509 const int_info = int_ty.intInfo(zcu);
4510 if (int_info.bits <= 32) {
4511 try self.spillCompareFlagsIfOccupied();
4512
4513 var lhs_reg: Register = undefined;
4514 var rhs_reg: Register = undefined;
4515
4516 const rhs_immediate = try rhs.resolveToImmediate(self);
4517 const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
4518
4519 if (rhs_immediate_ok) {
4520 const read_args = [_]ReadArg{
4521 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
4522 };
4523 try self.allocRegs(
4524 &read_args,
4525 &.{},
4526 null, // we won't be able to reuse a register as there are no write_regs
4527 );
4528
4529 _ = try self.addInst(.{
4530 .tag = .cmp,
4531 .data = .{ .r_op_cmp = .{
4532 .rn = lhs_reg,
4533 .op = Instruction.Operand.fromU32(rhs_immediate.?).?,
4534 } },
4535 });
4536 } else {
4537 const read_args = [_]ReadArg{
4538 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
4539 .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg },
4540 };
4541 try self.allocRegs(
4542 &read_args,
4543 &.{},
4544 null, // we won't be able to reuse a register as there are no write_regs
4545 );
4546
4547 _ = try self.addInst(.{
4548 .tag = .cmp,
4549 .data = .{ .r_op_cmp = .{
4550 .rn = lhs_reg,
4551 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
4552 } },
4553 });
4554 }
4555
4556 return switch (int_info.signedness) {
4557 .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) },
4558 .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) },
4559 };
4560 } else {
4561 return self.fail("TODO ARM cmp for ints > 32 bits", .{});
4562 }
4563}
4564
4565fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
4566 _ = inst;
4567 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
4568}
4569
4570fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
4571 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4572 const operand = try self.resolveInst(un_op);
4573 _ = operand;
4574 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
4575 return self.finishAir(inst, result, .{ un_op, .none, .none });
4576}
4577
4578fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
4579 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
4580
4581 _ = try self.addInst(.{
4582 .tag = .dbg_line,
4583 .cond = undefined,
4584 .data = .{ .dbg_line_column = .{
4585 .line = dbg_stmt.line,
4586 .column = dbg_stmt.column,
4587 } },
4588 });
4589
4590 return self.finishAirBookkeeping();
4591}
4592
4593fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
4594 const pt = self.pt;
4595 const zcu = pt.zcu;
4596 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4597 const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
4598 const func = zcu.funcInfo(extra.data.func);
4599 // TODO emit debug info for function change
4600 _ = func;
4601 try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]));
4602}
4603
4604fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
4605 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4606 const operand = pl_op.operand;
4607 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
4608 const ty = self.typeOf(operand);
4609 const mcv = try self.resolveInst(operand);
4610 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
4611
4612 log.debug("airDbgVar: %{f}: {f}, {}", .{ inst, ty.fmtDebug(), mcv });
4613
4614 try self.dbg_info_relocs.append(self.gpa, .{
4615 .tag = tag,
4616 .ty = ty,
4617 .name = name.toSlice(self.air),
4618 .mcv = mcv,
4619 });
4620
4621 return self.finishAir(inst, .dead, .{ operand, .none, .none });
4622}
4623
4624/// Given a boolean condition, emit a jump that is taken when that
4625/// condition is false.
4626fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
4627 const condition_code: Condition = switch (condition) {
4628 .cpsr_flags => |cond| cond.negate(),
4629 else => blk: {
4630 const reg = switch (condition) {
4631 .register => |r| r,
4632 else => try self.copyToTmpRegister(Type.bool, condition),
4633 };
4634
4635 try self.spillCompareFlagsIfOccupied();
4636
4637 // cmp reg, 1
4638 // bne ...
4639 _ = try self.addInst(.{
4640 .tag = .cmp,
4641 .data = .{ .r_op_cmp = .{
4642 .rn = reg,
4643 .op = Instruction.Operand.imm(1, 0),
4644 } },
4645 });
4646
4647 break :blk .ne;
4648 },
4649 };
4650
4651 return try self.addInst(.{
4652 .tag = .b,
4653 .cond = condition_code,
4654 .data = .{ .inst = undefined }, // populated later through performReloc
4655 });
4656}
4657
4658fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4659 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4660 const cond_inst = try self.resolveInst(pl_op.operand);
4661 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
4662 const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]);
4663 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
4664 const liveness_condbr = self.liveness.getCondBr(inst);
4665
4666 const reloc: Mir.Inst.Index = try self.condBr(cond_inst);
4667
4668 // If the condition dies here in this condbr instruction, process
4669 // that death now instead of later as this has an effect on
4670 // whether it needs to be spilled in the branches
4671 if (self.liveness.operandDies(inst, 0)) {
4672 if (pl_op.operand.toIndex()) |op_index| {
4673 self.processDeath(op_index);
4674 }
4675 }
4676
4677 // Capture the state of register and stack allocation state so that we can revert to it.
4678 const parent_next_stack_offset = self.next_stack_offset;
4679 const parent_free_registers = self.register_manager.free_registers;
4680 var parent_stack = try self.stack.clone(self.gpa);
4681 defer parent_stack.deinit(self.gpa);
4682 const parent_registers = self.register_manager.registers;
4683 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
4684
4685 try self.branch_stack.append(.{});
4686 errdefer {
4687 _ = self.branch_stack.pop().?;
4688 }
4689
4690 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
4691 for (liveness_condbr.then_deaths) |operand| {
4692 self.processDeath(operand);
4693 }
4694 try self.genBody(then_body);
4695
4696 // Revert to the previous register and stack allocation state.
4697
4698 var saved_then_branch = self.branch_stack.pop().?;
4699 defer saved_then_branch.deinit(self.gpa);
4700
4701 self.register_manager.registers = parent_registers;
4702 self.cpsr_flags_inst = parent_cpsr_flags_inst;
4703
4704 self.stack.deinit(self.gpa);
4705 self.stack = parent_stack;
4706 parent_stack = .{};
4707
4708 self.next_stack_offset = parent_next_stack_offset;
4709 self.register_manager.free_registers = parent_free_registers;
4710
4711 try self.performReloc(reloc);
4712 const else_branch = self.branch_stack.addOneAssumeCapacity();
4713 else_branch.* = .{};
4714
4715 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
4716 for (liveness_condbr.else_deaths) |operand| {
4717 self.processDeath(operand);
4718 }
4719 try self.genBody(else_body);
4720
4721 // At this point, each branch will possibly have conflicting values for where
4722 // each instruction is stored. They agree, however, on which instructions are alive/dead.
4723 // We use the first ("then") branch as canonical, and here emit
4724 // instructions into the second ("else") branch to make it conform.
4725 // We continue respect the data structure semantic guarantees of the else_branch so
4726 // that we can use all the code emitting abstractions. This is why at the bottom we
4727 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
4728 // rather than assigning it.
4729 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
4730 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
4731
4732 const else_slice = else_branch.inst_table.entries.slice();
4733 const else_keys = else_slice.items(.key);
4734 const else_values = else_slice.items(.value);
4735 for (else_keys, 0..) |else_key, else_idx| {
4736 const else_value = else_values[else_idx];
4737 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
4738 // The instruction's MCValue is overridden in both branches.
4739 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
4740 if (else_value == .dead) {
4741 assert(then_entry.value == .dead);
4742 continue;
4743 }
4744 break :blk then_entry.value;
4745 } else blk: {
4746 if (else_value == .dead)
4747 continue;
4748 // The instruction is only overridden in the else branch.
4749 var i: usize = self.branch_stack.items.len - 1;
4750 while (true) {
4751 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
4752 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
4753 assert(mcv != .dead);
4754 break :blk mcv;
4755 }
4756 }
4757 };
4758 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
4759 // TODO make sure the destination stack offset / register does not already have something
4760 // going on there.
4761 try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value);
4762 // TODO track the new register / stack allocation
4763 }
4764 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
4765 const then_slice = saved_then_branch.inst_table.entries.slice();
4766 const then_keys = then_slice.items(.key);
4767 const then_values = then_slice.items(.value);
4768 for (then_keys, 0..) |then_key, then_idx| {
4769 const then_value = then_values[then_idx];
4770 // We already deleted the items from this table that matched the else_branch.
4771 // So these are all instructions that are only overridden in the then branch.
4772 parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
4773 if (then_value == .dead)
4774 continue;
4775 const parent_mcv = blk: {
4776 var i: usize = self.branch_stack.items.len - 1;
4777 while (true) {
4778 i -= 1;
4779 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
4780 assert(mcv != .dead);
4781 break :blk mcv;
4782 }
4783 }
4784 };
4785 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
4786 // TODO make sure the destination stack offset / register does not already have something
4787 // going on there.
4788 try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value);
4789 // TODO track the new register / stack allocation
4790 }
4791
4792 {
4793 var item = self.branch_stack.pop().?;
4794 item.deinit(self.gpa);
4795 }
4796
4797 // We already took care of pl_op.operand earlier, so we're going
4798 // to pass .none here
4799 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
4800}
4801
4802fn isNull(
4803 self: *Self,
4804 operand_bind: ReadArg.Bind,
4805 operand_ty: Type,
4806) !MCValue {
4807 const pt = self.pt;
4808 const zcu = pt.zcu;
4809 if (operand_ty.isPtrLikeOptional(zcu)) {
4810 assert(operand_ty.abiSize(zcu) == 4);
4811
4812 const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } };
4813 return self.cmp(operand_bind, imm_bind, Type.usize, .eq);
4814 } else {
4815 return self.fail("TODO implement non-pointer optionals", .{});
4816 }
4817}
4818
4819fn isNonNull(
4820 self: *Self,
4821 operand_bind: ReadArg.Bind,
4822 operand_ty: Type,
4823) !MCValue {
4824 const is_null_result = try self.isNull(operand_bind, operand_ty);
4825 assert(is_null_result.cpsr_flags == .eq);
4826
4827 return MCValue{ .cpsr_flags = .ne };
4828}
4829
4830fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
4831 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4832 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4833 const operand_bind: ReadArg.Bind = .{ .inst = un_op };
4834 const operand_ty = self.typeOf(un_op);
4835
4836 break :result try self.isNull(operand_bind, operand_ty);
4837 };
4838 return self.finishAir(inst, result, .{ un_op, .none, .none });
4839}
4840
4841fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4842 const pt = self.pt;
4843 const zcu = pt.zcu;
4844 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4845 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4846 const operand_ptr = try self.resolveInst(un_op);
4847 const ptr_ty = self.typeOf(un_op);
4848 const elem_ty = ptr_ty.childType(zcu);
4849
4850 const operand = try self.allocRegOrMem(elem_ty, true, null);
4851 try self.load(operand, operand_ptr, ptr_ty);
4852
4853 break :result try self.isNull(.{ .mcv = operand }, elem_ty);
4854 };
4855 return self.finishAir(inst, result, .{ un_op, .none, .none });
4856}
4857
4858fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
4859 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4860 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4861 const operand_bind: ReadArg.Bind = .{ .inst = un_op };
4862 const operand_ty = self.typeOf(un_op);
4863
4864 break :result try self.isNonNull(operand_bind, operand_ty);
4865 };
4866 return self.finishAir(inst, result, .{ un_op, .none, .none });
4867}
4868
4869fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4870 const pt = self.pt;
4871 const zcu = pt.zcu;
4872 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4873 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4874 const operand_ptr = try self.resolveInst(un_op);
4875 const ptr_ty = self.typeOf(un_op);
4876 const elem_ty = ptr_ty.childType(zcu);
4877
4878 const operand = try self.allocRegOrMem(elem_ty, true, null);
4879 try self.load(operand, operand_ptr, ptr_ty);
4880
4881 break :result try self.isNonNull(.{ .mcv = operand }, elem_ty);
4882 };
4883 return self.finishAir(inst, result, .{ un_op, .none, .none });
4884}
4885
4886fn isErr(
4887 self: *Self,
4888 error_union_bind: ReadArg.Bind,
4889 error_union_ty: Type,
4890) !MCValue {
4891 const pt = self.pt;
4892 const zcu = pt.zcu;
4893 const error_type = error_union_ty.errorUnionSet(zcu);
4894
4895 if (error_type.errorSetIsEmpty(zcu)) {
4896 return MCValue{ .immediate = 0 }; // always false
4897 }
4898
4899 const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null);
4900 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
4901}
4902
4903fn isNonErr(
4904 self: *Self,
4905 error_union_bind: ReadArg.Bind,
4906 error_union_ty: Type,
4907) !MCValue {
4908 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
4909 switch (is_err_result) {
4910 .cpsr_flags => |cond| {
4911 assert(cond == .hi);
4912 return MCValue{ .cpsr_flags = cond.negate() };
4913 },
4914 .immediate => |imm| {
4915 assert(imm == 0);
4916 return MCValue{ .immediate = 1 };
4917 },
4918 else => unreachable,
4919 }
4920}
4921
4922fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
4923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4925 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4926 const error_union_ty = self.typeOf(un_op);
4927
4928 break :result try self.isErr(error_union_bind, error_union_ty);
4929 };
4930 return self.finishAir(inst, result, .{ un_op, .none, .none });
4931}
4932
4933fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4934 const pt = self.pt;
4935 const zcu = pt.zcu;
4936 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4937 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4938 const operand_ptr = try self.resolveInst(un_op);
4939 const ptr_ty = self.typeOf(un_op);
4940 const elem_ty = ptr_ty.childType(zcu);
4941
4942 const operand = try self.allocRegOrMem(elem_ty, true, null);
4943 try self.load(operand, operand_ptr, ptr_ty);
4944
4945 break :result try self.isErr(.{ .mcv = operand }, elem_ty);
4946 };
4947 return self.finishAir(inst, result, .{ un_op, .none, .none });
4948}
4949
4950fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
4951 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4952 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4953 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4954 const error_union_ty = self.typeOf(un_op);
4955
4956 break :result try self.isNonErr(error_union_bind, error_union_ty);
4957 };
4958 return self.finishAir(inst, result, .{ un_op, .none, .none });
4959}
4960
4961fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4962 const pt = self.pt;
4963 const zcu = pt.zcu;
4964 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4965 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4966 const operand_ptr = try self.resolveInst(un_op);
4967 const ptr_ty = self.typeOf(un_op);
4968 const elem_ty = ptr_ty.childType(zcu);
4969
4970 const operand = try self.allocRegOrMem(elem_ty, true, null);
4971 try self.load(operand, operand_ptr, ptr_ty);
4972
4973 break :result try self.isNonErr(.{ .mcv = operand }, elem_ty);
4974 };
4975 return self.finishAir(inst, result, .{ un_op, .none, .none });
4976}
4977
4978fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
4979 // A loop is a setup to be able to jump back to the beginning.
4980 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4981 const loop = self.air.extraData(Air.Block, ty_pl.payload);
4982 const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[loop.end..][0..loop.data.body_len]);
4983 const start_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
4984
4985 try self.genBody(body);
4986 try self.jump(start_index);
4987
4988 return self.finishAirBookkeeping();
4989}
4990
4991/// Send control flow to `inst`.
4992fn jump(self: *Self, inst: Mir.Inst.Index) !void {
4993 _ = try self.addInst(.{
4994 .tag = .b,
4995 .data = .{ .inst = inst },
4996 });
4997}
4998
4999fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
5000 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5001 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5002 try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]));
5003}
5004
5005fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
5006 try self.blocks.putNoClobber(self.gpa, inst, .{
5007 // A block is a setup to be able to jump to the end.
5008 .relocs = .{},
5009 // It also acts as a receptacle for break operands.
5010 // Here we use `MCValue.none` to represent a null value so that the first
5011 // break instruction will choose a MCValue for the block result and overwrite
5012 // this field. Following break instructions will use that MCValue to put their
5013 // block results.
5014 .mcv = MCValue{ .none = {} },
5015 });
5016 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
5017
5018 // TODO emit debug info lexical block
5019 try self.genBody(body);
5020
5021 // relocations for `br` instructions
5022 const relocs = &self.blocks.getPtr(inst).?.relocs;
5023 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
5024 // If the last Mir instruction is the last relocation (which
5025 // would just jump one instruction further), it can be safely
5026 // removed
5027 self.mir_instructions.orderedRemove(relocs.pop().?);
5028 }
5029 for (relocs.items) |reloc| {
5030 try self.performReloc(reloc);
5031 }
5032
5033 const result = self.blocks.getPtr(inst).?.mcv;
5034 return self.finishAir(inst, result, .{ .none, .none, .none });
5035}
5036
5037fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5038 const switch_br = self.air.unwrapSwitch(inst);
5039 const condition_ty = self.typeOf(switch_br.operand);
5040 const liveness = try self.liveness.getSwitchBr(
5041 self.gpa,
5042 inst,
5043 switch_br.cases_len + 1,
5044 );
5045 defer self.gpa.free(liveness.deaths);
5046
5047 var it = switch_br.iterateCases();
5048 while (it.next()) |case| {
5049 if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{});
5050 // For every item, we compare it to condition and branch into
5051 // the prong if they are equal. After we compared to all
5052 // items, we branch into the next prong (or if no other prongs
5053 // exist out of the switch statement).
5054 //
5055 // cmp condition, item1
5056 // beq prong
5057 // cmp condition, item2
5058 // beq prong
5059 // cmp condition, item3
5060 // beq prong
5061 // b out
5062 // prong: ...
5063 // ...
5064 // out: ...
5065 const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len);
5066 defer self.gpa.free(branch_into_prong_relocs);
5067
5068 for (case.items, 0..) |item, idx| {
5069 const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq);
5070 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
5071 }
5072
5073 const branch_away_from_prong_reloc = try self.addInst(.{
5074 .tag = .b,
5075 .data = .{ .inst = undefined }, // populated later through performReloc
5076 });
5077
5078 for (branch_into_prong_relocs) |reloc| {
5079 try self.performReloc(reloc);
5080 }
5081
5082 // Capture the state of register and stack allocation state so that we can revert to it.
5083 const parent_next_stack_offset = self.next_stack_offset;
5084 const parent_free_registers = self.register_manager.free_registers;
5085 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
5086 var parent_stack = try self.stack.clone(self.gpa);
5087 defer parent_stack.deinit(self.gpa);
5088 const parent_registers = self.register_manager.registers;
5089
5090 try self.branch_stack.append(.{});
5091 errdefer {
5092 _ = self.branch_stack.pop().?;
5093 }
5094
5095 try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len);
5096 for (liveness.deaths[case.idx]) |operand| {
5097 self.processDeath(operand);
5098 }
5099 try self.genBody(case.body);
5100
5101 // Revert to the previous register and stack allocation state.
5102 var saved_case_branch = self.branch_stack.pop().?;
5103 defer saved_case_branch.deinit(self.gpa);
5104
5105 self.register_manager.registers = parent_registers;
5106 self.cpsr_flags_inst = parent_cpsr_flags_inst;
5107 self.stack.deinit(self.gpa);
5108 self.stack = parent_stack;
5109 parent_stack = .{};
5110
5111 self.next_stack_offset = parent_next_stack_offset;
5112 self.register_manager.free_registers = parent_free_registers;
5113
5114 try self.performReloc(branch_away_from_prong_reloc);
5115 }
5116
5117 if (switch_br.else_body_len > 0) {
5118 const else_body = it.elseBody();
5119
5120 // Capture the state of register and stack allocation state so that we can revert to it.
5121 const parent_next_stack_offset = self.next_stack_offset;
5122 const parent_free_registers = self.register_manager.free_registers;
5123 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
5124 var parent_stack = try self.stack.clone(self.gpa);
5125 defer parent_stack.deinit(self.gpa);
5126 const parent_registers = self.register_manager.registers;
5127
5128 try self.branch_stack.append(.{});
5129 errdefer {
5130 _ = self.branch_stack.pop().?;
5131 }
5132
5133 const else_deaths = liveness.deaths.len - 1;
5134 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
5135 for (liveness.deaths[else_deaths]) |operand| {
5136 self.processDeath(operand);
5137 }
5138 try self.genBody(else_body);
5139
5140 // Revert to the previous register and stack allocation state.
5141 var saved_case_branch = self.branch_stack.pop().?;
5142 defer saved_case_branch.deinit(self.gpa);
5143
5144 self.register_manager.registers = parent_registers;
5145 self.cpsr_flags_inst = parent_cpsr_flags_inst;
5146 self.stack.deinit(self.gpa);
5147 self.stack = parent_stack;
5148 parent_stack = .{};
5149
5150 self.next_stack_offset = parent_next_stack_offset;
5151 self.register_manager.free_registers = parent_free_registers;
5152
5153 // TODO consolidate returned MCValues between prongs and else branch like we do
5154 // in airCondBr.
5155 }
5156
5157 return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none });
5158}
5159
5160fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
5161 const tag = self.mir_instructions.items(.tag)[inst];
5162 switch (tag) {
5163 .b => self.mir_instructions.items(.data)[inst].inst = @intCast(self.mir_instructions.len),
5164 else => unreachable,
5165 }
5166}
5167
5168fn airBr(self: *Self, inst: Air.Inst.Index) !void {
5169 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br;
5170 try self.br(branch.block_inst, branch.operand);
5171 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
5172}
5173
5174fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
5175 const zcu = self.pt.zcu;
5176 const block_data = self.blocks.getPtr(block).?;
5177
5178 if (self.typeOf(operand).hasRuntimeBits(zcu)) {
5179 const operand_mcv = try self.resolveInst(operand);
5180 const block_mcv = block_data.mcv;
5181 if (block_mcv == .none) {
5182 block_data.mcv = switch (operand_mcv) {
5183 .none, .dead, .unreach => unreachable,
5184 .register, .stack_offset, .memory => operand_mcv,
5185 .immediate, .stack_argument_offset, .cpsr_flags => blk: {
5186 const new_mcv = try self.allocRegOrMem(self.typeOfIndex(block), true, block);
5187 try self.setRegOrMem(self.typeOfIndex(block), new_mcv, operand_mcv);
5188 break :blk new_mcv;
5189 },
5190 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}),
5191 };
5192 } else {
5193 try self.setRegOrMem(self.typeOfIndex(block), block_mcv, operand_mcv);
5194 }
5195 }
5196 return self.brVoid(block);
5197}
5198
5199fn brVoid(self: *Self, block: Air.Inst.Index) !void {
5200 const block_data = self.blocks.getPtr(block).?;
5201
5202 // Emit a jump with a relocation. It will be patched up after the block ends.
5203 try block_data.relocs.append(self.gpa, try self.addInst(.{
5204 .tag = .b,
5205 .data = .{ .inst = undefined }, // populated later through performReloc
5206 }));
5207}
5208
5209fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5210 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5211 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
5212 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
5213 const clobbers_len: u31 = @truncate(extra.data.flags);
5214 var extra_i: usize = extra.end;
5215 const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]);
5216 extra_i += outputs.len;
5217 const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]);
5218 extra_i += inputs.len;
5219
5220 const dead = !is_volatile and self.liveness.isUnused(inst);
5221 const result: MCValue = if (dead) .dead else result: {
5222 if (outputs.len > 1) {
5223 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
5224 }
5225
5226 const output_constraint: ?[]const u8 = for (outputs) |output| {
5227 if (output != .none) {
5228 return self.fail("TODO implement codegen for non-expr asm", .{});
5229 }
5230 const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]);
5231 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0);
5232 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
5233 // This equation accounts for the fact that even if we have exactly 4 bytes
5234 // for the string, we still use the next u32 for the null terminator.
5235 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
5236
5237 break constraint;
5238 } else null;
5239
5240 for (inputs) |input| {
5241 const input_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]);
5242 const constraint = std.mem.sliceTo(input_bytes, 0);
5243 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
5244 // This equation accounts for the fact that even if we have exactly 4 bytes
5245 // for the string, we still use the next u32 for the null terminator.
5246 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
5247
5248 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
5249 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
5250 }
5251 const reg_name = constraint[1 .. constraint.len - 1];
5252 const reg = parseRegName(reg_name) orelse
5253 return self.fail("unrecognized register: '{s}'", .{reg_name});
5254
5255 const arg_mcv = try self.resolveInst(input);
5256 try self.register_manager.getReg(reg, null);
5257 try self.genSetReg(self.typeOf(input), reg, arg_mcv);
5258 }
5259
5260 {
5261 var clobber_i: u32 = 0;
5262 while (clobber_i < clobbers_len) : (clobber_i += 1) {
5263 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0);
5264 // This equation accounts for the fact that even if we have exactly 4 bytes
5265 // for the string, we still use the next u32 for the null terminator.
5266 extra_i += clobber.len / 4 + 1;
5267
5268 // TODO honor these
5269 }
5270 }
5271
5272 const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len];
5273
5274 if (mem.eql(u8, asm_source, "svc #0")) {
5275 _ = try self.addInst(.{
5276 .tag = .svc,
5277 .data = .{ .imm24 = 0 },
5278 });
5279 } else {
5280 return self.fail("TODO implement support for more arm assembly instructions", .{});
5281 }
5282
5283 if (output_constraint) |output| {
5284 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
5285 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
5286 }
5287 const reg_name = output[2 .. output.len - 1];
5288 const reg = parseRegName(reg_name) orelse
5289 return self.fail("unrecognized register: '{s}'", .{reg_name});
5290
5291 break :result MCValue{ .register = reg };
5292 } else {
5293 break :result MCValue{ .none = {} };
5294 }
5295 };
5296
5297 simple: {
5298 var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1);
5299 var buf_index: usize = 0;
5300 for (outputs) |output| {
5301 if (output == .none) continue;
5302
5303 if (buf_index >= buf.len) break :simple;
5304 buf[buf_index] = output;
5305 buf_index += 1;
5306 }
5307 if (buf_index + inputs.len > buf.len) break :simple;
5308 @memcpy(buf[buf_index..][0..inputs.len], inputs);
5309 return self.finishAir(inst, result, buf);
5310 }
5311 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
5312 for (outputs) |output| {
5313 if (output == .none) continue;
5314
5315 bt.feed(output);
5316 }
5317 for (inputs) |input| {
5318 bt.feed(input);
5319 }
5320 return bt.finishAir(result);
5321}
5322
5323fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
5324 try self.ensureProcessDeathCapacity(operand_count + 1);
5325 return BigTomb{
5326 .function = self,
5327 .inst = inst,
5328 .lbt = self.liveness.iterateBigTomb(inst),
5329 };
5330}
5331
5332/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
5333fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
5334 switch (loc) {
5335 .none => return,
5336 .register => |reg| return self.genSetReg(ty, reg, val),
5337 .stack_offset => |off| return self.genSetStack(ty, off, val),
5338 .memory => {
5339 return self.fail("TODO implement setRegOrMem for memory", .{});
5340 },
5341 else => unreachable,
5342 }
5343}
5344
5345fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
5346 const pt = self.pt;
5347 const zcu = pt.zcu;
5348 const abi_size: u32 = @intCast(ty.abiSize(zcu));
5349 switch (mcv) {
5350 .dead => unreachable,
5351 .unreach, .none => return, // Nothing to do.
5352 .undef => {
5353 if (!self.wantSafety())
5354 return; // The already existing value will do just fine.
5355 // TODO Upgrade this to a memset call when we have that available.
5356 switch (abi_size) {
5357 1 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
5358 2 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
5359 4 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
5360 else => try self.genInlineMemset(
5361 .{ .ptr_stack_offset = stack_offset },
5362 .{ .immediate = 0xaa },
5363 .{ .immediate = abi_size },
5364 ),
5365 }
5366 },
5367 .cpsr_flags,
5368 .immediate,
5369 .ptr_stack_offset,
5370 => {
5371 const reg = try self.copyToTmpRegister(ty, mcv);
5372 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
5373 },
5374 .register => |reg| {
5375 switch (abi_size) {
5376 1, 4 => {
5377 const offset = if (math.cast(u12, stack_offset)) |imm| blk: {
5378 break :blk Instruction.Offset.imm(imm);
5379 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }), .none);
5380
5381 const tag: Mir.Inst.Tag = switch (abi_size) {
5382 1 => .strb,
5383 4 => .str,
5384 else => unreachable,
5385 };
5386
5387 _ = try self.addInst(.{
5388 .tag = tag,
5389 .data = .{ .rr_offset = .{
5390 .rt = reg,
5391 .rn = .fp,
5392 .offset = .{
5393 .offset = offset,
5394 .positive = false,
5395 },
5396 } },
5397 });
5398 },
5399 2 => {
5400 const offset = if (stack_offset <= math.maxInt(u8)) blk: {
5401 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(stack_offset));
5402 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }));
5403
5404 _ = try self.addInst(.{
5405 .tag = .strh,
5406 .data = .{ .rr_extra_offset = .{
5407 .rt = reg,
5408 .rn = .fp,
5409 .offset = .{
5410 .offset = offset,
5411 .positive = false,
5412 },
5413 } },
5414 });
5415 },
5416 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
5417 }
5418 },
5419 .register_c_flag,
5420 .register_v_flag,
5421 => |reg| {
5422 const reg_lock = self.register_manager.lockReg(reg);
5423 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
5424
5425 const wrapped_ty = ty.fieldType(0, zcu);
5426 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg });
5427
5428 const overflow_bit_ty = ty.fieldType(1, zcu);
5429 const overflow_bit_offset: u32 = @intCast(ty.structFieldOffset(1, zcu));
5430 const cond_reg = try self.register_manager.allocReg(null, gp);
5431
5432 // C flag: movcs reg, #1
5433 // V flag: movvs reg, #1
5434 _ = try self.addInst(.{
5435 .tag = .mov,
5436 .cond = switch (mcv) {
5437 .register_c_flag => .cs,
5438 .register_v_flag => .vs,
5439 else => unreachable,
5440 },
5441 .data = .{ .r_op_mov = .{
5442 .rd = cond_reg,
5443 .op = Instruction.Operand.fromU32(1).?,
5444 } },
5445 });
5446
5447 try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
5448 .register = cond_reg,
5449 });
5450 },
5451 .memory,
5452 .stack_argument_offset,
5453 .stack_offset,
5454 => {
5455 switch (mcv) {
5456 .stack_offset => |off| {
5457 if (stack_offset == off)
5458 return; // Copy stack variable to itself; nothing to do.
5459 },
5460 else => {},
5461 }
5462
5463 if (abi_size <= 4) {
5464 const reg = try self.copyToTmpRegister(ty, mcv);
5465 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
5466 } else {
5467 const ptr_ty = try pt.singleMutPtrType(ty);
5468
5469 // TODO call extern memcpy
5470 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
5471 const src_reg = regs[0];
5472 const dst_reg = regs[1];
5473 const len_reg = regs[2];
5474 const count_reg = regs[3];
5475 const tmp_reg = regs[4];
5476
5477 switch (mcv) {
5478 .stack_offset => |off| {
5479 // sub src_reg, fp, #off
5480 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
5481 },
5482 .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }),
5483 .stack_argument_offset => |off| {
5484 _ = try self.addInst(.{
5485 .tag = .ldr_ptr_stack_argument,
5486 .data = .{ .r_stack_offset = .{
5487 .rt = src_reg,
5488 .stack_offset = off,
5489 } },
5490 });
5491 },
5492 else => unreachable,
5493 }
5494
5495 // sub dst_reg, fp, #stack_offset
5496 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });
5497
5498 // mov len, #abi_size
5499 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
5500
5501 // memcpy(src, dst, len)
5502 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
5503 }
5504 },
5505 }
5506}
5507
5508fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
5509 const pt = self.pt;
5510 const zcu = pt.zcu;
5511 switch (mcv) {
5512 .dead => unreachable,
5513 .unreach, .none => return, // Nothing to do.
5514 .undef => {
5515 if (!self.wantSafety())
5516 return; // The already existing value will do just fine.
5517 // Write the debug undefined value.
5518 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa });
5519 },
5520 .ptr_stack_offset => |off| {
5521 // TODO: maybe addressing from sp instead of fp
5522 const op = Instruction.Operand.fromU32(off) orelse
5523 return self.fail("TODO larger stack offsets", .{});
5524
5525 _ = try self.addInst(.{
5526 .tag = .sub,
5527 .data = .{ .rr_op = .{
5528 .rd = reg,
5529 .rn = .fp,
5530 .op = op,
5531 } },
5532 });
5533 },
5534 .cpsr_flags => |condition| {
5535 const zero = Instruction.Operand.imm(0, 0);
5536 const one = Instruction.Operand.imm(1, 0);
5537
5538 // mov reg, 0
5539 _ = try self.addInst(.{
5540 .tag = .mov,
5541 .data = .{ .r_op_mov = .{
5542 .rd = reg,
5543 .op = zero,
5544 } },
5545 });
5546
5547 // moveq reg, 1
5548 _ = try self.addInst(.{
5549 .tag = .mov,
5550 .cond = condition,
5551 .data = .{ .r_op_mov = .{
5552 .rd = reg,
5553 .op = one,
5554 } },
5555 });
5556 },
5557 .immediate => |x| {
5558 if (Instruction.Operand.fromU32(x)) |op| {
5559 _ = try self.addInst(.{
5560 .tag = .mov,
5561 .data = .{ .r_op_mov = .{
5562 .rd = reg,
5563 .op = op,
5564 } },
5565 });
5566 } else if (Instruction.Operand.fromU32(~x)) |op| {
5567 _ = try self.addInst(.{
5568 .tag = .mvn,
5569 .data = .{ .r_op_mov = .{
5570 .rd = reg,
5571 .op = op,
5572 } },
5573 });
5574 } else if (x <= math.maxInt(u16)) {
5575 if (self.target.cpu.has(.arm, .has_v7)) {
5576 _ = try self.addInst(.{
5577 .tag = .movw,
5578 .data = .{ .r_imm16 = .{
5579 .rd = reg,
5580 .imm16 = @intCast(x),
5581 } },
5582 });
5583 } else {
5584 _ = try self.addInst(.{
5585 .tag = .mov,
5586 .data = .{ .r_op_mov = .{
5587 .rd = reg,
5588 .op = Instruction.Operand.imm(@truncate(x), 0),
5589 } },
5590 });
5591 _ = try self.addInst(.{
5592 .tag = .orr,
5593 .data = .{ .rr_op = .{
5594 .rd = reg,
5595 .rn = reg,
5596 .op = Instruction.Operand.imm(@truncate(x >> 8), 12),
5597 } },
5598 });
5599 }
5600 } else {
5601 // TODO write constant to code and load
5602 // relative to pc
5603 if (self.target.cpu.has(.arm, .has_v7)) {
5604 // immediate: 0xaaaabbbb
5605 // movw reg, #0xbbbb
5606 // movt reg, #0xaaaa
5607 _ = try self.addInst(.{
5608 .tag = .movw,
5609 .data = .{ .r_imm16 = .{
5610 .rd = reg,
5611 .imm16 = @truncate(x),
5612 } },
5613 });
5614 _ = try self.addInst(.{
5615 .tag = .movt,
5616 .data = .{ .r_imm16 = .{
5617 .rd = reg,
5618 .imm16 = @truncate(x >> 16),
5619 } },
5620 });
5621 } else {
5622 // immediate: 0xaabbccdd
5623 // mov reg, #0xaa
5624 // orr reg, reg, #0xbb, 24
5625 // orr reg, reg, #0xcc, 16
5626 // orr reg, reg, #0xdd, 8
5627 _ = try self.addInst(.{
5628 .tag = .mov,
5629 .data = .{ .r_op_mov = .{
5630 .rd = reg,
5631 .op = Instruction.Operand.imm(@truncate(x), 0),
5632 } },
5633 });
5634 _ = try self.addInst(.{
5635 .tag = .orr,
5636 .data = .{ .rr_op = .{
5637 .rd = reg,
5638 .rn = reg,
5639 .op = Instruction.Operand.imm(@truncate(x >> 8), 12),
5640 } },
5641 });
5642 _ = try self.addInst(.{
5643 .tag = .orr,
5644 .data = .{ .rr_op = .{
5645 .rd = reg,
5646 .rn = reg,
5647 .op = Instruction.Operand.imm(@truncate(x >> 16), 8),
5648 } },
5649 });
5650 _ = try self.addInst(.{
5651 .tag = .orr,
5652 .data = .{ .rr_op = .{
5653 .rd = reg,
5654 .rn = reg,
5655 .op = Instruction.Operand.imm(@truncate(x >> 24), 4),
5656 } },
5657 });
5658 }
5659 }
5660 },
5661 .register => |src_reg| {
5662 // If the registers are the same, nothing to do.
5663 if (src_reg.id() == reg.id())
5664 return;
5665
5666 // mov reg, src_reg
5667 _ = try self.addInst(.{
5668 .tag = .mov,
5669 .data = .{ .r_op_mov = .{
5670 .rd = reg,
5671 .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),
5672 } },
5673 });
5674 },
5675 .register_c_flag => unreachable, // doesn't fit into a register
5676 .register_v_flag => unreachable, // doesn't fit into a register
5677 .memory => |addr| {
5678 // The value is in memory at a hard-coded address.
5679 // If the type is a pointer, it means the pointer address is at this memory location.
5680 try self.genSetReg(ty, reg, .{ .immediate = @intCast(addr) });
5681 try self.genLdrRegister(reg, reg, ty);
5682 },
5683 .stack_offset => |off| {
5684 // TODO: maybe addressing from sp instead of fp
5685 const abi_size: u32 = @intCast(ty.abiSize(zcu));
5686
5687 const tag: Mir.Inst.Tag = switch (abi_size) {
5688 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb else .ldrb,
5689 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh else .ldrh,
5690 3, 4 => .ldr,
5691 else => unreachable,
5692 };
5693
5694 const extra_offset = switch (abi_size) {
5695 1 => ty.isSignedInt(zcu),
5696 2 => true,
5697 3, 4 => false,
5698 else => unreachable,
5699 };
5700
5701 if (extra_offset) {
5702 const offset = if (off <= math.maxInt(u8)) blk: {
5703 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(off));
5704 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.usize, MCValue{ .immediate = off }));
5705
5706 _ = try self.addInst(.{
5707 .tag = tag,
5708 .data = .{ .rr_extra_offset = .{
5709 .rt = reg,
5710 .rn = .fp,
5711 .offset = .{
5712 .offset = offset,
5713 .positive = false,
5714 },
5715 } },
5716 });
5717 } else {
5718 const offset = if (off <= math.maxInt(u12)) blk: {
5719 break :blk Instruction.Offset.imm(@intCast(off));
5720 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.usize, MCValue{ .immediate = off }), .none);
5721
5722 _ = try self.addInst(.{
5723 .tag = tag,
5724 .data = .{ .rr_offset = .{
5725 .rt = reg,
5726 .rn = .fp,
5727 .offset = .{
5728 .offset = offset,
5729 .positive = false,
5730 },
5731 } },
5732 });
5733 }
5734 },
5735 .stack_argument_offset => |off| {
5736 const abi_size = ty.abiSize(zcu);
5737
5738 const tag: Mir.Inst.Tag = switch (abi_size) {
5739 1 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument,
5740 2 => if (ty.isSignedInt(zcu)) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument,
5741 3, 4 => .ldr_stack_argument,
5742 else => unreachable,
5743 };
5744
5745 _ = try self.addInst(.{
5746 .tag = tag,
5747 .data = .{ .r_stack_offset = .{
5748 .rt = reg,
5749 .stack_offset = off,
5750 } },
5751 });
5752 },
5753 }
5754}
5755
5756fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
5757 const pt = self.pt;
5758 const abi_size: u32 = @intCast(ty.abiSize(pt.zcu));
5759 switch (mcv) {
5760 .dead => unreachable,
5761 .none, .unreach => return,
5762 .undef => {
5763 if (!self.wantSafety())
5764 return; // The already existing value will do just fine.
5765 // TODO Upgrade this to a memset call when we have that available.
5766 switch (abi_size) {
5767 1 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }),
5768 2 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }),
5769 4 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
5770 else => return self.fail("TODO implement memset", .{}),
5771 }
5772 },
5773 .register => |reg| {
5774 switch (abi_size) {
5775 1, 4 => {
5776 const offset = if (math.cast(u12, stack_offset)) |imm| blk: {
5777 break :blk Instruction.Offset.imm(imm);
5778 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }), .none);
5779
5780 const tag: Mir.Inst.Tag = switch (abi_size) {
5781 1 => .strb,
5782 4 => .str,
5783 else => unreachable,
5784 };
5785
5786 _ = try self.addInst(.{
5787 .tag = tag,
5788 .data = .{ .rr_offset = .{
5789 .rt = reg,
5790 .rn = .sp,
5791 .offset = .{ .offset = offset },
5792 } },
5793 });
5794 },
5795 2 => {
5796 const offset = if (stack_offset <= math.maxInt(u8)) blk: {
5797 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(stack_offset));
5798 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.u32, MCValue{ .immediate = stack_offset }));
5799
5800 _ = try self.addInst(.{
5801 .tag = .strh,
5802 .data = .{ .rr_extra_offset = .{
5803 .rt = reg,
5804 .rn = .sp,
5805 .offset = .{ .offset = offset },
5806 } },
5807 });
5808 },
5809 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
5810 }
5811 },
5812 .register_c_flag,
5813 .register_v_flag,
5814 => {
5815 return self.fail("TODO implement genSetStack {}", .{mcv});
5816 },
5817 .stack_offset,
5818 .memory,
5819 .stack_argument_offset,
5820 => {
5821 if (abi_size <= 4) {
5822 const reg = try self.copyToTmpRegister(ty, mcv);
5823 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
5824 } else {
5825 const ptr_ty = try pt.singleMutPtrType(ty);
5826
5827 // TODO call extern memcpy
5828 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
5829 const src_reg = regs[0];
5830 const dst_reg = regs[1];
5831 const len_reg = regs[2];
5832 const count_reg = regs[3];
5833 const tmp_reg = regs[4];
5834
5835 switch (mcv) {
5836 .stack_offset => |off| {
5837 // sub src_reg, fp, #off
5838 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off });
5839 },
5840 .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(addr) }),
5841 .stack_argument_offset => |off| {
5842 _ = try self.addInst(.{
5843 .tag = .ldr_ptr_stack_argument,
5844 .data = .{ .r_stack_offset = .{
5845 .rt = src_reg,
5846 .stack_offset = off,
5847 } },
5848 });
5849 },
5850 else => unreachable,
5851 }
5852
5853 // add dst_reg, sp, #stack_offset
5854 const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(stack_offset)) |x| x else {
5855 return self.fail("TODO load: set reg to stack offset with all possible offsets", .{});
5856 };
5857 _ = try self.addInst(.{
5858 .tag = .add,
5859 .data = .{ .rr_op = .{
5860 .rd = dst_reg,
5861 .rn = .sp,
5862 .op = dst_offset_op,
5863 } },
5864 });
5865
5866 // mov len, #abi_size
5867 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
5868
5869 // memcpy(src, dst, len)
5870 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
5871 }
5872 },
5873 .cpsr_flags,
5874 .immediate,
5875 .ptr_stack_offset,
5876 => {
5877 const reg = try self.copyToTmpRegister(ty, mcv);
5878 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg });
5879 },
5880 }
5881}
5882
5883fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
5884 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5885 const result = if (self.liveness.isUnused(inst)) .dead else result: {
5886 const operand = try self.resolveInst(ty_op.operand);
5887 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
5888
5889 const operand_lock = switch (operand) {
5890 .register,
5891 .register_c_flag,
5892 .register_v_flag,
5893 => |reg| self.register_manager.lockReg(reg),
5894 else => null,
5895 };
5896 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
5897
5898 const dest_ty = self.typeOfIndex(inst);
5899 const dest = try self.allocRegOrMem(dest_ty, true, inst);
5900 try self.setRegOrMem(dest_ty, dest, operand);
5901 break :result dest;
5902 };
5903 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5904}
5905
5906fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
5907 const pt = self.pt;
5908 const zcu = pt.zcu;
5909 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5910 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
5911 const ptr_ty = self.typeOf(ty_op.operand);
5912 const ptr = try self.resolveInst(ty_op.operand);
5913 const array_ty = ptr_ty.childType(zcu);
5914 const array_len: u32 = @intCast(array_ty.arrayLen(zcu));
5915
5916 const stack_offset = try self.allocMem(8, .@"8", inst);
5917 try self.genSetStack(ptr_ty, stack_offset, ptr);
5918 try self.genSetStack(Type.usize, stack_offset - 4, .{ .immediate = array_len });
5919 break :result MCValue{ .stack_offset = stack_offset };
5920 };
5921 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5922}
5923
5924fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void {
5925 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5926 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{
5927 self.target.cpu.arch,
5928 });
5929 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5930}
5931
5932fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void {
5933 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5934 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{
5935 self.target.cpu.arch,
5936 });
5937 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
5938}
5939
5940fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
5941 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5942 const extra = self.air.extraData(Air.Block, ty_pl.payload);
5943 _ = extra;
5944
5945 return self.fail("TODO implement airCmpxchg for {}", .{
5946 self.target.cpu.arch,
5947 });
5948}
5949
5950fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
5951 _ = inst;
5952 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
5953}
5954
5955fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
5956 _ = inst;
5957 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
5958}
5959
5960fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
5961 _ = inst;
5962 _ = order;
5963 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
5964}
5965
5966fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
5967 if (safety) {
5968 // TODO if the value is undef, write 0xaa bytes to dest
5969 } else {
5970 // TODO if the value is undef, don't lower this instruction
5971 }
5972 _ = inst;
5973 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
5974}
5975
5976fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
5977 _ = inst;
5978 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
5979}
5980
5981fn airMemmove(self: *Self, inst: Air.Inst.Index) !void {
5982 _ = inst;
5983 return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
5984}
5985
5986fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
5987 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5988 const operand = try self.resolveInst(un_op);
5989 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
5990 _ = operand;
5991 return self.fail("TODO implement airTagName for arm", .{});
5992 };
5993 return self.finishAir(inst, result, .{ un_op, .none, .none });
5994}
5995
5996fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
5997 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5998 const operand = try self.resolveInst(un_op);
5999 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
6000 _ = operand;
6001 return self.fail("TODO implement airErrorName for arm", .{});
6002 };
6003 return self.finishAir(inst, result, .{ un_op, .none, .none });
6004}
6005
6006fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
6007 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for arm", .{});
6009 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
6010}
6011
6012fn airSelect(self: *Self, inst: Air.Inst.Index) !void {
6013 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6014 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6015 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for arm", .{});
6016 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs });
6017}
6018
6019fn airShuffleOne(self: *Self, inst: Air.Inst.Index) !void {
6020 _ = inst;
6021 return self.fail("TODO implement airShuffleOne for arm", .{});
6022}
6023
6024fn airShuffleTwo(self: *Self, inst: Air.Inst.Index) !void {
6025 _ = inst;
6026 return self.fail("TODO implement airShuffleTwo for arm", .{});
6027}
6028
6029fn airReduce(self: *Self, inst: Air.Inst.Index) !void {
6030 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
6031 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for arm", .{});
6032 return self.finishAir(inst, result, .{ reduce.operand, .none, .none });
6033}
6034
6035fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
6036 const pt = self.pt;
6037 const zcu = pt.zcu;
6038 const vector_ty = self.typeOfIndex(inst);
6039 const len = vector_ty.vectorLen(zcu);
6040 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6041 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]);
6042 const result: MCValue = res: {
6043 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
6044 return self.fail("TODO implement airAggregateInit for arm", .{});
6045 };
6046
6047 if (elements.len <= Air.Liveness.bpi - 1) {
6048 var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1);
6049 @memcpy(buf[0..elements.len], elements);
6050 return self.finishAir(inst, result, buf);
6051 }
6052 var bt = try self.iterateBigTomb(inst, elements.len);
6053 for (elements) |elem| {
6054 bt.feed(elem);
6055 }
6056 return bt.finishAir(result);
6057}
6058
6059fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
6060 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6061 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
6062 _ = extra;
6063
6064 return self.fail("TODO implement airUnionInit for arm", .{});
6065}
6066
6067fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
6068 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
6069 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
6070}
6071
6072fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
6073 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6074 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
6075 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
6076 return self.fail("TODO implement airMulAdd for arm", .{});
6077 };
6078 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
6079}
6080
6081fn airTry(self: *Self, inst: Air.Inst.Index) !void {
6082 const pt = self.pt;
6083 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6084 const extra = self.air.extraData(Air.Try, pl_op.payload);
6085 const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]);
6086 const result: MCValue = result: {
6087 const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand };
6088 const error_union_ty = self.typeOf(pl_op.operand);
6089 const error_union_size: u32 = @intCast(error_union_ty.abiSize(pt.zcu));
6090 const error_union_align = error_union_ty.abiAlignment(pt.zcu);
6091
6092 // The error union will die in the body. However, we need the
6093 // error union after the body in order to extract the payload
6094 // of the error union, so we create a copy of it
6095 const error_union_copy = try self.allocMem(error_union_size, error_union_align, null);
6096 try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self));
6097
6098 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
6099 const reloc = try self.condBr(is_err_result);
6100
6101 try self.genBody(body);
6102 try self.performReloc(reloc);
6103
6104 break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null);
6105 };
6106 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
6107}
6108
6109fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
6110 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6111 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
6112 const body = self.air.extra.items[extra.end..][0..extra.data.body_len];
6113 _ = body;
6114 return self.fail("TODO implement airTryPtr for arm", .{});
6115 // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
6116}
6117
6118fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
6119 const pt = self.pt;
6120 const zcu = pt.zcu;
6121
6122 // If the type has no codegen bits, no need to store it.
6123 const inst_ty = self.typeOf(inst);
6124 if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !inst_ty.isError(zcu))
6125 return MCValue{ .none = {} };
6126
6127 const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, pt)).?);
6128
6129 return self.getResolvedInstValue(inst_index);
6130}
6131
6132fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
6133 // Treat each stack item as a "layer" on top of the previous one.
6134 var i: usize = self.branch_stack.items.len;
6135 while (true) {
6136 i -= 1;
6137 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
6138 assert(mcv != .dead);
6139 return mcv;
6140 }
6141 }
6142}
6143
6144fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
6145 const pt = self.pt;
6146 const mcv: MCValue = switch (try codegen.genTypedValue(
6147 self.bin_file,
6148 pt,
6149 self.src_loc,
6150 val,
6151 self.target,
6152 )) {
6153 .mcv => |mcv| switch (mcv) {
6154 .none => .none,
6155 .undef => .undef,
6156 .load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO
6157 .immediate => |imm| .{ .immediate = @truncate(imm) },
6158 .memory => |addr| .{ .memory = addr },
6159 },
6160 .fail => |msg| {
6161 self.err_msg = msg;
6162 return error.CodegenFail;
6163 },
6164 };
6165 return mcv;
6166}
6167
6168const CallMCValues = struct {
6169 args: []MCValue,
6170 return_value: MCValue,
6171 stack_byte_count: u32,
6172 stack_align: u32,
6173
6174 fn deinit(self: *CallMCValues, func: *Self) void {
6175 func.gpa.free(self.args);
6176 self.* = undefined;
6177 }
6178};
6179
6180/// Caller must call `CallMCValues.deinit`.
6181fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
6182 const pt = self.pt;
6183 const zcu = pt.zcu;
6184 const ip = &zcu.intern_pool;
6185 const fn_info = zcu.typeToFunc(fn_ty).?;
6186 const cc = fn_info.cc;
6187 var result: CallMCValues = .{
6188 .args = try self.gpa.alloc(MCValue, fn_info.param_types.len),
6189 // These undefined values must be populated before returning from this function.
6190 .return_value = undefined,
6191 .stack_byte_count = undefined,
6192 .stack_align = undefined,
6193 };
6194 errdefer self.gpa.free(result.args);
6195
6196 const ret_ty = fn_ty.fnReturnType(zcu);
6197
6198 switch (cc) {
6199 .naked => {
6200 assert(result.args.len == 0);
6201 result.return_value = .{ .unreach = {} };
6202 result.stack_byte_count = 0;
6203 result.stack_align = 1;
6204 return result;
6205 },
6206 .arm_aapcs => {
6207 // ARM Procedure Call Standard, Chapter 6.5
6208 var ncrn: usize = 0; // Next Core Register Number
6209 var nsaa: u32 = 0; // Next stacked argument address
6210
6211 if (ret_ty.zigTypeTag(zcu) == .noreturn) {
6212 result.return_value = .{ .unreach = {} };
6213 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
6214 result.return_value = .{ .none = {} };
6215 } else {
6216 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu));
6217 // TODO handle cases where multiple registers are used
6218 if (ret_ty_size <= 4) {
6219 result.return_value = .{ .register = c_abi_int_return_regs[0] };
6220 } else {
6221 // The result is returned by reference, not by
6222 // value. This means that r0 will contain the
6223 // address of where this function should write the
6224 // result into.
6225 result.return_value = .{ .stack_offset = 0 };
6226 ncrn = 1;
6227 }
6228 }
6229
6230 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
6231 if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8")
6232 ncrn = std.mem.alignForward(usize, ncrn, 2);
6233
6234 const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu));
6235 if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
6236 if (param_size <= 4) {
6237 result_arg.* = .{ .register = c_abi_int_param_regs[ncrn] };
6238 ncrn += 1;
6239 } else {
6240 return self.fail("TODO MCValues with multiple registers", .{});
6241 }
6242 } else if (ncrn < 4 and nsaa == 0) {
6243 return self.fail("TODO MCValues split between registers and stack", .{});
6244 } else {
6245 ncrn = 4;
6246 if (Type.fromInterned(ty).abiAlignment(zcu) == .@"8")
6247 nsaa = std.mem.alignForward(u32, nsaa, 8);
6248
6249 result_arg.* = .{ .stack_argument_offset = nsaa };
6250 nsaa += param_size;
6251 }
6252 }
6253
6254 result.stack_byte_count = nsaa;
6255 result.stack_align = 8;
6256 },
6257 .auto => {
6258 if (ret_ty.zigTypeTag(zcu) == .noreturn) {
6259 result.return_value = .{ .unreach = {} };
6260 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) {
6261 result.return_value = .{ .none = {} };
6262 } else {
6263 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(zcu));
6264 if (ret_ty_size == 0) {
6265 assert(ret_ty.isError(zcu));
6266 result.return_value = .{ .immediate = 0 };
6267 } else if (ret_ty_size <= 4) {
6268 result.return_value = .{ .register = .r0 };
6269 } else {
6270 // The result is returned by reference, not by
6271 // value. This means that r0 will contain the
6272 // address of where this function should write the
6273 // result into.
6274 result.return_value = .{ .stack_offset = 0 };
6275 }
6276 }
6277
6278 var stack_offset: u32 = 0;
6279
6280 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
6281 if (Type.fromInterned(ty).abiSize(zcu) > 0) {
6282 const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(zcu));
6283 const param_alignment = Type.fromInterned(ty).abiAlignment(zcu);
6284
6285 stack_offset = @intCast(param_alignment.forward(stack_offset));
6286 result_arg.* = .{ .stack_argument_offset = stack_offset };
6287 stack_offset += param_size;
6288 } else {
6289 result_arg.* = .{ .none = {} };
6290 }
6291 }
6292
6293 result.stack_byte_count = stack_offset;
6294 result.stack_align = 8;
6295 },
6296 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
6297 }
6298
6299 return result;
6300}
6301
6302/// TODO support scope overrides. Also note this logic is duplicated with `Zcu.wantSafety`.
6303fn wantSafety(self: *Self) bool {
6304 return switch (self.bin_file.comp.root_mod.optimize_mode) {
6305 .Debug => true,
6306 .ReleaseSafe => true,
6307 .ReleaseFast => false,
6308 .ReleaseSmall => false,
6309 };
6310}
6311
6312fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
6313 @branchHint(.cold);
6314 const zcu = self.pt.zcu;
6315 const func = zcu.funcInfo(self.func_index);
6316 const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args);
6317 return zcu.codegenFailMsg(func.owner_nav, msg);
6318}
6319
6320fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } {
6321 @branchHint(.cold);
6322 const zcu = self.pt.zcu;
6323 const func = zcu.funcInfo(self.func_index);
6324 return zcu.codegenFailMsg(func.owner_nav, msg);
6325}
6326
6327fn parseRegName(name: []const u8) ?Register {
6328 if (@hasDecl(Register, "parseRegName")) {
6329 return Register.parseRegName(name);
6330 }
6331 return std.meta.stringToEnum(Register, name);
6332}
6333
6334fn typeOf(self: *Self, inst: Air.Inst.Ref) Type {
6335 return self.air.typeOf(inst, &self.pt.zcu.intern_pool);
6336}
6337
6338fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type {
6339 return self.air.typeOfIndex(inst, &self.pt.zcu.intern_pool);
6340}
src/arch/arm/Emit.zig deleted-714
......@@ -1,714 +0,0 @@
1//! This file contains the functionality for lowering AArch32 MIR into
2//! machine code
3
4const Emit = @This();
5const builtin = @import("builtin");
6const std = @import("std");
7const math = std.math;
8const Mir = @import("Mir.zig");
9const bits = @import("bits.zig");
10const link = @import("../../link.zig");
11const Zcu = @import("../../Zcu.zig");
12const Type = @import("../../Type.zig");
13const ErrorMsg = Zcu.ErrorMsg;
14const Target = std.Target;
15const assert = std.debug.assert;
16const Instruction = bits.Instruction;
17const Register = bits.Register;
18const log = std.log.scoped(.aarch32_emit);
19const CodeGen = @import("CodeGen.zig");
20
21mir: Mir,
22bin_file: *link.File,
23debug_output: link.File.DebugInfoOutput,
24target: *const std.Target,
25err_msg: ?*ErrorMsg = null,
26src_loc: Zcu.LazySrcLoc,
27code: *std.ArrayListUnmanaged(u8),
28
29prev_di_line: u32,
30prev_di_column: u32,
31/// Relative to the beginning of `code`.
32prev_di_pc: usize,
33
34/// The amount of stack space consumed by the saved callee-saved
35/// registers in bytes
36saved_regs_stack_space: u32,
37
38/// The final stack frame size of the function (already aligned to the
39/// respective stack alignment). Does not include prologue stack space.
40stack_size: u32,
41
42/// The branch type of every branch
43branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .empty,
44/// For every forward branch, maps the target instruction to a list of
45/// branches which branch to this target instruction
46branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .empty,
47/// For backward branches: stores the code offset of the target
48/// instruction
49///
50/// For forward branches: stores the code offset of the branch
51/// instruction
52code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
53
54const InnerError = error{
55 OutOfMemory,
56 EmitFail,
57};
58
59const BranchType = enum {
60 b,
61
62 fn default(tag: Mir.Inst.Tag) BranchType {
63 return switch (tag) {
64 .b => .b,
65 else => unreachable,
66 };
67 }
68};
69
70pub fn emitMir(emit: *Emit) InnerError!void {
71 const mir_tags = emit.mir.instructions.items(.tag);
72
73 // Find smallest lowerings for branch instructions
74 try emit.lowerBranches();
75
76 // Emit machine code
77 for (mir_tags, 0..) |tag, index| {
78 const inst = @as(u32, @intCast(index));
79 switch (tag) {
80 .add => try emit.mirDataProcessing(inst),
81 .adds => try emit.mirDataProcessing(inst),
82 .@"and" => try emit.mirDataProcessing(inst),
83 .cmp => try emit.mirDataProcessing(inst),
84 .eor => try emit.mirDataProcessing(inst),
85 .mov => try emit.mirDataProcessing(inst),
86 .mvn => try emit.mirDataProcessing(inst),
87 .orr => try emit.mirDataProcessing(inst),
88 .rsb => try emit.mirDataProcessing(inst),
89 .sub => try emit.mirDataProcessing(inst),
90 .subs => try emit.mirDataProcessing(inst),
91
92 .sub_sp_scratch_r4 => try emit.mirSubStackPointer(inst),
93
94 .asr => try emit.mirShift(inst),
95 .lsl => try emit.mirShift(inst),
96 .lsr => try emit.mirShift(inst),
97
98 .b => try emit.mirBranch(inst),
99
100 .undefined_instruction => try emit.mirUndefinedInstruction(),
101 .bkpt => try emit.mirExceptionGeneration(inst),
102
103 .blx => try emit.mirBranchExchange(inst),
104 .bx => try emit.mirBranchExchange(inst),
105
106 .dbg_line => try emit.mirDbgLine(inst),
107
108 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
109
110 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
111
112 .ldr => try emit.mirLoadStore(inst),
113 .ldrb => try emit.mirLoadStore(inst),
114 .str => try emit.mirLoadStore(inst),
115 .strb => try emit.mirLoadStore(inst),
116
117 .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst),
118 .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
119 .ldrb_stack_argument => try emit.mirLoadStackArgument(inst),
120 .ldrh_stack_argument => try emit.mirLoadStackArgument(inst),
121 .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst),
122 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),
123
124 .ldrh => try emit.mirLoadStoreExtra(inst),
125 .ldrsb => try emit.mirLoadStoreExtra(inst),
126 .ldrsh => try emit.mirLoadStoreExtra(inst),
127 .strh => try emit.mirLoadStoreExtra(inst),
128
129 .movw => try emit.mirSpecialMove(inst),
130 .movt => try emit.mirSpecialMove(inst),
131
132 .mul => try emit.mirMultiply(inst),
133 .smulbb => try emit.mirMultiply(inst),
134
135 .smull => try emit.mirMultiplyLong(inst),
136 .umull => try emit.mirMultiplyLong(inst),
137
138 .nop => try emit.mirNop(),
139
140 .pop => try emit.mirBlockDataTransfer(inst),
141 .push => try emit.mirBlockDataTransfer(inst),
142
143 .svc => try emit.mirSupervisorCall(inst),
144
145 .sbfx => try emit.mirBitFieldExtract(inst),
146 .ubfx => try emit.mirBitFieldExtract(inst),
147 }
148 }
149}
150
151pub fn deinit(emit: *Emit) void {
152 const comp = emit.bin_file.comp;
153 const gpa = comp.gpa;
154
155 var iter = emit.branch_forward_origins.valueIterator();
156 while (iter.next()) |origin_list| {
157 origin_list.deinit(gpa);
158 }
159
160 emit.branch_types.deinit(gpa);
161 emit.branch_forward_origins.deinit(gpa);
162 emit.code_offset_mapping.deinit(gpa);
163 emit.* = undefined;
164}
165
166fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
167 assert(std.mem.isAlignedGeneric(i64, offset, 4)); // misaligned offset
168
169 switch (tag) {
170 .b => {
171 if (std.math.cast(i24, @divExact(offset, 4))) |_| {
172 return BranchType.b;
173 } else {
174 return emit.fail("TODO support larger branches", .{});
175 }
176 },
177 else => unreachable,
178 }
179}
180
181fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
182 const tag = emit.mir.instructions.items(.tag)[inst];
183
184 if (isBranch(tag)) {
185 switch (emit.branch_types.get(inst).?) {
186 .b => return 4,
187 }
188 }
189
190 switch (tag) {
191 .dbg_line,
192 .dbg_epilogue_begin,
193 .dbg_prologue_end,
194 => return 0,
195
196 .sub_sp_scratch_r4 => {
197 const imm32 = emit.mir.instructions.items(.data)[inst].imm32;
198
199 if (imm32 == 0) {
200 return 0 * 4;
201 } else if (Instruction.Operand.fromU32(imm32) != null) {
202 // sub
203 return 1 * 4;
204 } else if (emit.target.cpu.has(.arm, .has_v7)) {
205 // movw; movt; sub
206 return 3 * 4;
207 } else {
208 // mov; orr; orr; orr; sub
209 return 5 * 4;
210 }
211 },
212
213 else => return 4,
214 }
215}
216
217fn isBranch(tag: Mir.Inst.Tag) bool {
218 return switch (tag) {
219 .b => true,
220 else => false,
221 };
222}
223
224fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
225 const tag = emit.mir.instructions.items(.tag)[inst];
226
227 switch (tag) {
228 .b => return emit.mir.instructions.items(.data)[inst].inst,
229 else => unreachable,
230 }
231}
232
233fn lowerBranches(emit: *Emit) !void {
234 const comp = emit.bin_file.comp;
235 const gpa = comp.gpa;
236 const mir_tags = emit.mir.instructions.items(.tag);
237
238 // First pass: Note down all branches and their target
239 // instructions, i.e. populate branch_types,
240 // branch_forward_origins, and code_offset_mapping
241 //
242 // TODO optimization opportunity: do this in codegen while
243 // generating MIR
244 for (mir_tags, 0..) |tag, index| {
245 const inst = @as(u32, @intCast(index));
246 if (isBranch(tag)) {
247 const target_inst = emit.branchTarget(inst);
248
249 // Remember this branch instruction
250 try emit.branch_types.put(gpa, inst, BranchType.default(tag));
251
252 // Forward branches require some extra stuff: We only
253 // know their offset once we arrive at the target
254 // instruction. Therefore, we need to be able to
255 // access the branch instruction when we visit the
256 // target instruction in order to manipulate its type
257 // etc.
258 if (target_inst > inst) {
259 // Remember the branch instruction index
260 try emit.code_offset_mapping.put(gpa, inst, 0);
261
262 if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| {
263 try origin_list.append(gpa, inst);
264 } else {
265 var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty;
266 try origin_list.append(gpa, inst);
267 try emit.branch_forward_origins.put(gpa, target_inst, origin_list);
268 }
269 }
270
271 // Remember the target instruction index so that we
272 // update the real code offset in all future passes
273 //
274 // putNoClobber may not be used as the put operation
275 // may clobber the entry when multiple branches branch
276 // to the same target instruction
277 try emit.code_offset_mapping.put(gpa, target_inst, 0);
278 }
279 }
280
281 // Further passes: Until all branches are lowered, interate
282 // through all instructions and calculate new offsets and
283 // potentially new branch types
284 var all_branches_lowered = false;
285 while (!all_branches_lowered) {
286 all_branches_lowered = true;
287 var current_code_offset: usize = 0;
288
289 for (mir_tags, 0..) |tag, index| {
290 const inst = @as(u32, @intCast(index));
291
292 // If this instruction contained in the code offset
293 // mapping (when it is a target of a branch or if it is a
294 // forward branch), update the code offset
295 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
296 offset.* = current_code_offset;
297 }
298
299 // If this instruction is a backward branch, calculate the
300 // offset, which may potentially update the branch type
301 if (isBranch(tag)) {
302 const target_inst = emit.branchTarget(inst);
303 if (target_inst < inst) {
304 const target_offset = emit.code_offset_mapping.get(target_inst).?;
305 const offset = @as(i64, @intCast(target_offset)) - @as(i64, @intCast(current_code_offset + 8));
306 const branch_type = emit.branch_types.getPtr(inst).?;
307 const optimal_branch_type = try emit.optimalBranchType(tag, offset);
308 if (branch_type.* != optimal_branch_type) {
309 branch_type.* = optimal_branch_type;
310 all_branches_lowered = false;
311 }
312
313 log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset });
314 }
315 }
316
317 // If this instruction is the target of one or more
318 // forward branches, calculate the offset, which may
319 // potentially update the branch type
320 if (emit.branch_forward_origins.get(inst)) |origin_list| {
321 for (origin_list.items) |forward_branch_inst| {
322 const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst];
323 const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?;
324 const offset = @as(i64, @intCast(current_code_offset)) - @as(i64, @intCast(forward_branch_inst_offset + 8));
325 const branch_type = emit.branch_types.getPtr(forward_branch_inst).?;
326 const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset);
327 if (branch_type.* != optimal_branch_type) {
328 branch_type.* = optimal_branch_type;
329 all_branches_lowered = false;
330 }
331
332 log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset });
333 }
334 }
335
336 // Increment code offset
337 current_code_offset += emit.instructionSize(inst);
338 }
339 }
340}
341
342fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
343 const comp = emit.bin_file.comp;
344 const gpa = comp.gpa;
345 const endian = emit.target.cpu.arch.endian();
346 std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
347}
348
349fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
350 @branchHint(.cold);
351 assert(emit.err_msg == null);
352 const comp = emit.bin_file.comp;
353 const gpa = comp.gpa;
354 emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args);
355 return error.EmitFail;
356}
357
358fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
359 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(self.prev_di_line));
360 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
361 switch (self.debug_output) {
362 .dwarf => |dw| {
363 try dw.advancePCAndLine(delta_line, delta_pc);
364 self.prev_di_line = line;
365 self.prev_di_column = column;
366 self.prev_di_pc = self.code.items.len;
367 },
368 .plan9 => |dbg_out| {
369 if (delta_pc <= 0) return; // only do this when the pc changes
370
371 var aw: std.io.Writer.Allocating = .fromArrayList(self.bin_file.comp.gpa, &dbg_out.dbg_line);
372 const bw = &aw.interface;
373 defer dbg_out.dbg_line = aw.toArrayList();
374
375 // increasing the line number
376 try link.File.Plan9.changeLine(bw, delta_line);
377 // increasing the pc
378 const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
379 if (d_pc_p9 > 0) {
380 // minus one because if its the last one, we want to leave space to change the line which is one pc quanta
381 try bw.writeByte(@as(u8, @intCast(@divExact(d_pc_p9, dbg_out.pc_quanta) + 128)) - dbg_out.pc_quanta);
382 const dbg_line = aw.getWritten();
383 if (dbg_out.pcop_change_index) |pci| dbg_line[pci] += 1;
384 dbg_out.pcop_change_index = @intCast(dbg_line.len - 1);
385 } else if (d_pc_p9 == 0) {
386 // we don't need to do anything, because adding the pc quanta does it for us
387 } else unreachable;
388 if (dbg_out.start_line == null)
389 dbg_out.start_line = self.prev_di_line;
390 dbg_out.end_line = line;
391 // only do this if the pc changed
392 self.prev_di_line = line;
393 self.prev_di_column = column;
394 self.prev_di_pc = self.code.items.len;
395 },
396 .none => {},
397 }
398}
399
400fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
401 const tag = emit.mir.instructions.items(.tag)[inst];
402 const cond = emit.mir.instructions.items(.cond)[inst];
403
404 switch (tag) {
405 .add,
406 .adds,
407 .@"and",
408 .eor,
409 .orr,
410 .rsb,
411 .sub,
412 .subs,
413 => {
414 const rr_op = emit.mir.instructions.items(.data)[inst].rr_op;
415 switch (tag) {
416 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),
417 .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)),
418 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),
419 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),
420 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),
421 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),
422 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),
423 .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)),
424 else => unreachable,
425 }
426 },
427 .cmp => {
428 const r_op_cmp = emit.mir.instructions.items(.data)[inst].r_op_cmp;
429 try emit.writeInstruction(Instruction.cmp(cond, r_op_cmp.rn, r_op_cmp.op));
430 },
431 .mov,
432 .mvn,
433 => {
434 const r_op_mov = emit.mir.instructions.items(.data)[inst].r_op_mov;
435 switch (tag) {
436 .mov => try emit.writeInstruction(Instruction.mov(cond, r_op_mov.rd, r_op_mov.op)),
437 .mvn => try emit.writeInstruction(Instruction.mvn(cond, r_op_mov.rd, r_op_mov.op)),
438 else => unreachable,
439 }
440 },
441 else => unreachable,
442 }
443}
444
445fn mirSubStackPointer(emit: *Emit, inst: Mir.Inst.Index) !void {
446 const tag = emit.mir.instructions.items(.tag)[inst];
447 const cond = emit.mir.instructions.items(.cond)[inst];
448 const imm32 = emit.mir.instructions.items(.data)[inst].imm32;
449
450 switch (tag) {
451 .sub_sp_scratch_r4 => {
452 if (imm32 == 0) return;
453
454 const operand = Instruction.Operand.fromU32(imm32) orelse blk: {
455 const scratch: Register = .r4;
456
457 if (emit.target.cpu.has(.arm, .has_v7)) {
458 try emit.writeInstruction(Instruction.movw(cond, scratch, @as(u16, @truncate(imm32))));
459 try emit.writeInstruction(Instruction.movt(cond, scratch, @as(u16, @truncate(imm32 >> 16))));
460 } else {
461 try emit.writeInstruction(Instruction.mov(cond, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32)), 0)));
462 try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32 >> 8)), 12)));
463 try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32 >> 16)), 8)));
464 try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@as(u8, @truncate(imm32 >> 24)), 4)));
465 }
466
467 break :blk Instruction.Operand.reg(scratch, Instruction.Operand.Shift.none);
468 };
469
470 try emit.writeInstruction(Instruction.sub(cond, .sp, .sp, operand));
471 },
472 else => unreachable,
473 }
474}
475
476fn mirShift(emit: *Emit, inst: Mir.Inst.Index) !void {
477 const tag = emit.mir.instructions.items(.tag)[inst];
478 const cond = emit.mir.instructions.items(.cond)[inst];
479 const rr_shift = emit.mir.instructions.items(.data)[inst].rr_shift;
480
481 switch (tag) {
482 .asr => try emit.writeInstruction(Instruction.asr(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)),
483 .lsl => try emit.writeInstruction(Instruction.lsl(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)),
484 .lsr => try emit.writeInstruction(Instruction.lsr(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)),
485 else => unreachable,
486 }
487}
488
489fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
490 const tag = emit.mir.instructions.items(.tag)[inst];
491 const cond = emit.mir.instructions.items(.cond)[inst];
492 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
493
494 const offset = @as(i64, @intCast(emit.code_offset_mapping.get(target_inst).?)) - @as(i64, @intCast(emit.code.items.len + 8));
495 const branch_type = emit.branch_types.get(inst).?;
496
497 switch (branch_type) {
498 .b => switch (tag) {
499 .b => try emit.writeInstruction(Instruction.b(cond, @as(i26, @intCast(offset)))),
500 else => unreachable,
501 },
502 }
503}
504
505fn mirUndefinedInstruction(emit: *Emit) !void {
506 try emit.writeInstruction(Instruction.undefinedInstruction());
507}
508
509fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
510 const tag = emit.mir.instructions.items(.tag)[inst];
511 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
512
513 switch (tag) {
514 .bkpt => try emit.writeInstruction(Instruction.bkpt(imm16)),
515 else => unreachable,
516 }
517}
518
519fn mirBranchExchange(emit: *Emit, inst: Mir.Inst.Index) !void {
520 const tag = emit.mir.instructions.items(.tag)[inst];
521 const cond = emit.mir.instructions.items(.cond)[inst];
522 const reg = emit.mir.instructions.items(.data)[inst].reg;
523
524 switch (tag) {
525 .blx => try emit.writeInstruction(Instruction.blx(cond, reg)),
526 .bx => try emit.writeInstruction(Instruction.bx(cond, reg)),
527 else => unreachable,
528 }
529}
530
531fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
532 const tag = emit.mir.instructions.items(.tag)[inst];
533 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
534
535 switch (tag) {
536 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
537 else => unreachable,
538 }
539}
540
541fn mirDebugPrologueEnd(emit: *Emit) !void {
542 switch (emit.debug_output) {
543 .dwarf => |dw| {
544 try dw.setPrologueEnd();
545 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
546 },
547 .plan9 => {},
548 .none => {},
549 }
550}
551
552fn mirDebugEpilogueBegin(emit: *Emit) !void {
553 switch (emit.debug_output) {
554 .dwarf => |dw| {
555 try dw.setEpilogueBegin();
556 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
557 },
558 .plan9 => {},
559 .none => {},
560 }
561}
562
563fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void {
564 const tag = emit.mir.instructions.items(.tag)[inst];
565 const cond = emit.mir.instructions.items(.cond)[inst];
566 const rr_offset = emit.mir.instructions.items(.data)[inst].rr_offset;
567
568 switch (tag) {
569 .ldr => try emit.writeInstruction(Instruction.ldr(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
570 .ldrb => try emit.writeInstruction(Instruction.ldrb(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
571 .str => try emit.writeInstruction(Instruction.str(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
572 .strb => try emit.writeInstruction(Instruction.strb(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
573 else => unreachable,
574 }
575}
576
577fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void {
578 const tag = emit.mir.instructions.items(.tag)[inst];
579 const cond = emit.mir.instructions.items(.cond)[inst];
580 const r_stack_offset = emit.mir.instructions.items(.data)[inst].r_stack_offset;
581 const rt = r_stack_offset.rt;
582
583 const raw_offset = emit.stack_size + emit.saved_regs_stack_space + r_stack_offset.stack_offset;
584 switch (tag) {
585 .ldr_ptr_stack_argument => {
586 const operand = Instruction.Operand.fromU32(raw_offset) orelse
587 return emit.fail("TODO mirLoadStack larger offsets", .{});
588
589 try emit.writeInstruction(Instruction.add(cond, rt, .sp, operand));
590 },
591 .ldr_stack_argument,
592 .ldrb_stack_argument,
593 => {
594 const offset = if (raw_offset <= math.maxInt(u12)) blk: {
595 break :blk Instruction.Offset.imm(@as(u12, @intCast(raw_offset)));
596 } else return emit.fail("TODO mirLoadStack larger offsets", .{});
597
598 switch (tag) {
599 .ldr_stack_argument => try emit.writeInstruction(Instruction.ldr(cond, rt, .sp, .{ .offset = offset })),
600 .ldrb_stack_argument => try emit.writeInstruction(Instruction.ldrb(cond, rt, .sp, .{ .offset = offset })),
601 else => unreachable,
602 }
603 },
604 .ldrh_stack_argument,
605 .ldrsb_stack_argument,
606 .ldrsh_stack_argument,
607 => {
608 const offset = if (raw_offset <= math.maxInt(u8)) blk: {
609 break :blk Instruction.ExtraLoadStoreOffset.imm(@as(u8, @intCast(raw_offset)));
610 } else return emit.fail("TODO mirLoadStack larger offsets", .{});
611
612 switch (tag) {
613 .ldrh_stack_argument => try emit.writeInstruction(Instruction.ldrh(cond, rt, .sp, .{ .offset = offset })),
614 .ldrsb_stack_argument => try emit.writeInstruction(Instruction.ldrsb(cond, rt, .sp, .{ .offset = offset })),
615 .ldrsh_stack_argument => try emit.writeInstruction(Instruction.ldrsh(cond, rt, .sp, .{ .offset = offset })),
616 else => unreachable,
617 }
618 },
619 else => unreachable,
620 }
621}
622
623fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void {
624 const tag = emit.mir.instructions.items(.tag)[inst];
625 const cond = emit.mir.instructions.items(.cond)[inst];
626 const rr_extra_offset = emit.mir.instructions.items(.data)[inst].rr_extra_offset;
627
628 switch (tag) {
629 .ldrh => try emit.writeInstruction(Instruction.ldrh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
630 .ldrsb => try emit.writeInstruction(Instruction.ldrsb(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
631 .ldrsh => try emit.writeInstruction(Instruction.ldrsh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
632 .strh => try emit.writeInstruction(Instruction.strh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
633 else => unreachable,
634 }
635}
636
637fn mirSpecialMove(emit: *Emit, inst: Mir.Inst.Index) !void {
638 const tag = emit.mir.instructions.items(.tag)[inst];
639 const cond = emit.mir.instructions.items(.cond)[inst];
640 const r_imm16 = emit.mir.instructions.items(.data)[inst].r_imm16;
641
642 switch (tag) {
643 .movw => try emit.writeInstruction(Instruction.movw(cond, r_imm16.rd, r_imm16.imm16)),
644 .movt => try emit.writeInstruction(Instruction.movt(cond, r_imm16.rd, r_imm16.imm16)),
645 else => unreachable,
646 }
647}
648
649fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {
650 const tag = emit.mir.instructions.items(.tag)[inst];
651 const cond = emit.mir.instructions.items(.cond)[inst];
652 const rrr = emit.mir.instructions.items(.data)[inst].rrr;
653
654 switch (tag) {
655 .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)),
656 .smulbb => try emit.writeInstruction(Instruction.smulbb(cond, rrr.rd, rrr.rn, rrr.rm)),
657 else => unreachable,
658 }
659}
660
661fn mirMultiplyLong(emit: *Emit, inst: Mir.Inst.Index) !void {
662 const tag = emit.mir.instructions.items(.tag)[inst];
663 const cond = emit.mir.instructions.items(.cond)[inst];
664 const rrrr = emit.mir.instructions.items(.data)[inst].rrrr;
665
666 switch (tag) {
667 .smull => try emit.writeInstruction(Instruction.smull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)),
668 .umull => try emit.writeInstruction(Instruction.umull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)),
669 else => unreachable,
670 }
671}
672
673fn mirNop(emit: *Emit) !void {
674 try emit.writeInstruction(Instruction.nop());
675}
676
677fn mirBlockDataTransfer(emit: *Emit, inst: Mir.Inst.Index) !void {
678 const tag = emit.mir.instructions.items(.tag)[inst];
679 const cond = emit.mir.instructions.items(.cond)[inst];
680 const register_list = emit.mir.instructions.items(.data)[inst].register_list;
681
682 switch (tag) {
683 .pop => try emit.writeInstruction(Instruction.ldm(cond, .sp, true, register_list)),
684 .push => try emit.writeInstruction(Instruction.stmdb(cond, .sp, true, register_list)),
685 else => unreachable,
686 }
687}
688
689fn mirSupervisorCall(emit: *Emit, inst: Mir.Inst.Index) !void {
690 const tag = emit.mir.instructions.items(.tag)[inst];
691 const cond = emit.mir.instructions.items(.cond)[inst];
692 const imm24 = emit.mir.instructions.items(.data)[inst].imm24;
693
694 switch (tag) {
695 .svc => try emit.writeInstruction(Instruction.svc(cond, imm24)),
696 else => unreachable,
697 }
698}
699
700fn mirBitFieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void {
701 const tag = emit.mir.instructions.items(.tag)[inst];
702 const cond = emit.mir.instructions.items(.cond)[inst];
703 const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width;
704 const rd = rr_lsb_width.rd;
705 const rn = rr_lsb_width.rn;
706 const lsb = rr_lsb_width.lsb;
707 const width = rr_lsb_width.width;
708
709 switch (tag) {
710 .sbfx => try emit.writeInstruction(Instruction.sbfx(cond, rd, rn, lsb, width)),
711 .ubfx => try emit.writeInstruction(Instruction.ubfx(cond, rd, rn, lsb, width)),
712 else => unreachable,
713 }
714}
src/arch/arm/Mir.zig deleted-340
......@@ -1,340 +0,0 @@
1//! Machine Intermediate Representation.
2//! This data is produced by ARM Codegen or ARM assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const Mir = @This();
10const std = @import("std");
11const builtin = @import("builtin");
12const assert = std.debug.assert;
13
14const bits = @import("bits.zig");
15const Register = bits.Register;
16const InternPool = @import("../../InternPool.zig");
17const Emit = @import("Emit.zig");
18const codegen = @import("../../codegen.zig");
19const link = @import("../../link.zig");
20const Zcu = @import("../../Zcu.zig");
21
22max_end_stack: u32,
23saved_regs_stack_space: u32,
24
25instructions: std.MultiArrayList(Inst).Slice,
26/// The meaning of this data is determined by `Inst.Tag` value.
27extra: []const u32,
28
29pub const Inst = struct {
30 tag: Tag,
31 cond: bits.Condition = .al,
32 /// The meaning of this depends on `tag`.
33 data: Data,
34
35 pub const Tag = enum(u16) {
36 /// Add
37 add,
38 /// Add, update condition flags
39 adds,
40 /// Bitwise AND
41 @"and",
42 /// Arithmetic Shift Right
43 asr,
44 /// Branch
45 b,
46 /// Undefined instruction
47 undefined_instruction,
48 /// Breakpoint
49 bkpt,
50 /// Branch with Link and Exchange
51 blx,
52 /// Branch and Exchange
53 bx,
54 /// Compare
55 cmp,
56 /// Pseudo-instruction: End of prologue
57 dbg_prologue_end,
58 /// Pseudo-instruction: Beginning of epilogue
59 dbg_epilogue_begin,
60 /// Pseudo-instruction: Update debug line
61 dbg_line,
62 /// Bitwise Exclusive OR
63 eor,
64 /// Load Register
65 ldr,
66 /// Pseudo-instruction: Load pointer to stack argument offset
67 ldr_ptr_stack_argument,
68 /// Load Register
69 ldr_stack_argument,
70 /// Load Register Byte
71 ldrb,
72 /// Load Register Byte
73 ldrb_stack_argument,
74 /// Load Register Halfword
75 ldrh,
76 /// Load Register Halfword
77 ldrh_stack_argument,
78 /// Load Register Signed Byte
79 ldrsb,
80 /// Load Register Signed Byte
81 ldrsb_stack_argument,
82 /// Load Register Signed Halfword
83 ldrsh,
84 /// Load Register Signed Halfword
85 ldrsh_stack_argument,
86 /// Logical Shift Left
87 lsl,
88 /// Logical Shift Right
89 lsr,
90 /// Move
91 mov,
92 /// Move
93 movw,
94 /// Move Top
95 movt,
96 /// Multiply
97 mul,
98 /// Bitwise NOT
99 mvn,
100 /// No Operation
101 nop,
102 /// Bitwise OR
103 orr,
104 /// Pop multiple registers from Stack
105 pop,
106 /// Push multiple registers to Stack
107 push,
108 /// Reverse Subtract
109 rsb,
110 /// Signed Bit Field Extract
111 sbfx,
112 /// Signed Multiply (halfwords), bottom half, bottom half
113 smulbb,
114 /// Signed Multiply Long
115 smull,
116 /// Store Register
117 str,
118 /// Store Register Byte
119 strb,
120 /// Store Register Halfword
121 strh,
122 /// Subtract
123 sub,
124 /// Pseudo-instruction: Subtract 32-bit immediate from stack
125 ///
126 /// r4 can be used by Emit as a scratch register for loading
127 /// the immediate
128 sub_sp_scratch_r4,
129 /// Subtract, update condition flags
130 subs,
131 /// Supervisor Call
132 svc,
133 /// Unsigned Bit Field Extract
134 ubfx,
135 /// Unsigned Multiply Long
136 umull,
137 };
138
139 /// The position of an MIR instruction within the `Mir` instructions array.
140 pub const Index = u32;
141
142 /// All instructions have a 8-byte payload, which is contained within
143 /// this union. `Tag` determines which union field is active, as well as
144 /// how to interpret the data within.
145 pub const Data = union {
146 /// No additional data
147 ///
148 /// Used by e.g. nop
149 nop: void,
150 /// Another instruction
151 ///
152 /// Used by e.g. b
153 inst: Index,
154 /// A 16-bit immediate value.
155 ///
156 /// Used by e.g. bkpt
157 imm16: u16,
158 /// A 24-bit immediate value.
159 ///
160 /// Used by e.g. svc
161 imm24: u24,
162 /// A 32-bit immediate value.
163 ///
164 /// Used by e.g. sub_sp_scratch_r0
165 imm32: u32,
166 /// Index into `extra`. Meaning of what can be found there is context-dependent.
167 ///
168 /// Used by e.g. load_memory
169 payload: u32,
170 /// A register
171 ///
172 /// Used by e.g. blx
173 reg: Register,
174 /// A register and a stack offset
175 ///
176 /// Used by e.g. ldr_stack_argument
177 r_stack_offset: struct {
178 rt: Register,
179 stack_offset: u32,
180 },
181 /// A register and a 16-bit unsigned immediate
182 ///
183 /// Used by e.g. movw
184 r_imm16: struct {
185 rd: Register,
186 imm16: u16,
187 },
188 /// A register and an operand
189 ///
190 /// Used by mov and mvn
191 r_op_mov: struct {
192 rd: Register,
193 op: bits.Instruction.Operand,
194 },
195 /// A register and an operand
196 ///
197 /// Used by cmp
198 r_op_cmp: struct {
199 rn: Register,
200 op: bits.Instruction.Operand,
201 },
202 /// Two registers and a shift amount
203 ///
204 /// Used by e.g. lsl
205 rr_shift: struct {
206 rd: Register,
207 rm: Register,
208 shift_amount: bits.Instruction.ShiftAmount,
209 },
210 /// Two registers and an operand
211 ///
212 /// Used by e.g. sub
213 rr_op: struct {
214 rd: Register,
215 rn: Register,
216 op: bits.Instruction.Operand,
217 },
218 /// Two registers and an offset
219 ///
220 /// Used by e.g. ldr
221 rr_offset: struct {
222 rt: Register,
223 rn: Register,
224 offset: bits.Instruction.OffsetArgs,
225 },
226 /// Two registers and an extra load/store offset
227 ///
228 /// Used by e.g. ldrh
229 rr_extra_offset: struct {
230 rt: Register,
231 rn: Register,
232 offset: bits.Instruction.ExtraLoadStoreOffsetArgs,
233 },
234 /// Two registers and a lsb (range 0-31) and a width (range
235 /// 1-32)
236 ///
237 /// Used by e.g. sbfx
238 rr_lsb_width: struct {
239 rd: Register,
240 rn: Register,
241 lsb: u5,
242 width: u6,
243 },
244 /// Three registers
245 ///
246 /// Used by e.g. mul
247 rrr: struct {
248 rd: Register,
249 rn: Register,
250 rm: Register,
251 },
252 /// Four registers
253 ///
254 /// Used by e.g. smull
255 rrrr: struct {
256 rdlo: Register,
257 rdhi: Register,
258 rn: Register,
259 rm: Register,
260 },
261 /// An unordered list of registers
262 ///
263 /// Used by e.g. push
264 register_list: bits.Instruction.RegisterList,
265 /// Debug info: line and column
266 ///
267 /// Used by e.g. dbg_line
268 dbg_line_column: struct {
269 line: u32,
270 column: u32,
271 },
272 };
273
274 // Make sure we don't accidentally make instructions bigger than expected.
275 // Note that in safety builds, Zig is allowed to insert a secret field for safety checks.
276 comptime {
277 if (!std.debug.runtime_safety) {
278 assert(@sizeOf(Data) == 8);
279 }
280 }
281};
282
283pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
284 mir.instructions.deinit(gpa);
285 gpa.free(mir.extra);
286 mir.* = undefined;
287}
288
289pub fn emit(
290 mir: Mir,
291 lf: *link.File,
292 pt: Zcu.PerThread,
293 src_loc: Zcu.LazySrcLoc,
294 func_index: InternPool.Index,
295 code: *std.ArrayListUnmanaged(u8),
296 debug_output: link.File.DebugInfoOutput,
297) codegen.CodeGenError!void {
298 const zcu = pt.zcu;
299 const func = zcu.funcInfo(func_index);
300 const nav = func.owner_nav;
301 const mod = zcu.navFileScope(nav).mod.?;
302 var e: Emit = .{
303 .mir = mir,
304 .bin_file = lf,
305 .debug_output = debug_output,
306 .target = &mod.resolved_target.result,
307 .src_loc = src_loc,
308 .code = code,
309 .prev_di_pc = 0,
310 .prev_di_line = func.lbrace_line,
311 .prev_di_column = func.lbrace_column,
312 .stack_size = mir.max_end_stack,
313 .saved_regs_stack_space = mir.saved_regs_stack_space,
314 };
315 defer e.deinit();
316 e.emitMir() catch |err| switch (err) {
317 error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?),
318 else => |e1| return e1,
319 };
320}
321
322/// Returns the requested data, as well as the new index which is at the start of the
323/// trailers for the object.
324pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
325 const fields = std.meta.fields(T);
326 var i: usize = index;
327 var result: T = undefined;
328 inline for (fields) |field| {
329 @field(result, field.name) = switch (field.type) {
330 u32 => mir.extra[i],
331 i32 => @as(i32, @bitCast(mir.extra[i])),
332 else => @compileError("bad field type"),
333 };
334 i += 1;
335 }
336 return .{
337 .data = result,
338 .end = i,
339 };
340}
src/arch/arm/abi.zig deleted-187
......@@ -1,187 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const bits = @import("bits.zig");
4const Register = bits.Register;
5const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
6const Type = @import("../../Type.zig");
7const Zcu = @import("../../Zcu.zig");
8
9pub const Class = union(enum) {
10 memory,
11 byval,
12 i32_array: u8,
13 i64_array: u8,
14
15 fn arrSize(total_size: u64, arr_size: u64) Class {
16 const count = @as(u8, @intCast(std.mem.alignForward(u64, total_size, arr_size) / arr_size));
17 if (arr_size == 32) {
18 return .{ .i32_array = count };
19 } else {
20 return .{ .i64_array = count };
21 }
22 }
23};
24
25pub const Context = enum { ret, arg };
26
27pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
28 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
29
30 var maybe_float_bits: ?u16 = null;
31 const max_byval_size = 512;
32 const ip = &zcu.intern_pool;
33 switch (ty.zigTypeTag(zcu)) {
34 .@"struct" => {
35 const bit_size = ty.bitSize(zcu);
36 if (ty.containerLayout(zcu) == .@"packed") {
37 if (bit_size > 64) return .memory;
38 return .byval;
39 }
40 if (bit_size > max_byval_size) return .memory;
41 const float_count = countFloats(ty, zcu, &maybe_float_bits);
42 if (float_count <= byval_float_count) return .byval;
43
44 const fields = ty.structFieldCount(zcu);
45 var i: u32 = 0;
46 while (i < fields) : (i += 1) {
47 const field_ty = ty.fieldType(i, zcu);
48 const field_alignment = ty.fieldAlignment(i, zcu);
49 const field_size = field_ty.bitSize(zcu);
50 if (field_size > 32 or field_alignment.compare(.gt, .@"32")) {
51 return Class.arrSize(bit_size, 64);
52 }
53 }
54 return Class.arrSize(bit_size, 32);
55 },
56 .@"union" => {
57 const bit_size = ty.bitSize(zcu);
58 const union_obj = zcu.typeToUnion(ty).?;
59 if (union_obj.flagsUnordered(ip).layout == .@"packed") {
60 if (bit_size > 64) return .memory;
61 return .byval;
62 }
63 if (bit_size > max_byval_size) return .memory;
64 const float_count = countFloats(ty, zcu, &maybe_float_bits);
65 if (float_count <= byval_float_count) return .byval;
66
67 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
68 if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or
69 ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32"))
70 {
71 return Class.arrSize(bit_size, 64);
72 }
73 }
74 return Class.arrSize(bit_size, 32);
75 },
76 .bool, .float => return .byval,
77 .int => {
78 // TODO this is incorrect for _BitInt(128) but implementing
79 // this correctly makes implementing compiler-rt impossible.
80 // const bit_size = ty.bitSize(zcu);
81 // if (bit_size > 64) return .memory;
82 return .byval;
83 },
84 .@"enum", .error_set => {
85 const bit_size = ty.bitSize(zcu);
86 if (bit_size > 64) return .memory;
87 return .byval;
88 },
89 .vector => {
90 const bit_size = ty.bitSize(zcu);
91 // TODO is this controlled by a cpu feature?
92 if (ctx == .ret and bit_size > 128) return .memory;
93 if (bit_size > 512) return .memory;
94 return .byval;
95 },
96 .optional => {
97 assert(ty.isPtrLikeOptional(zcu));
98 return .byval;
99 },
100 .pointer => {
101 assert(!ty.isSlice(zcu));
102 return .byval;
103 },
104 .error_union,
105 .frame,
106 .@"anyframe",
107 .noreturn,
108 .void,
109 .type,
110 .comptime_float,
111 .comptime_int,
112 .undefined,
113 .null,
114 .@"fn",
115 .@"opaque",
116 .enum_literal,
117 .array,
118 => unreachable,
119 }
120}
121
122const byval_float_count = 4;
123fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 {
124 const ip = &zcu.intern_pool;
125 const target = zcu.getTarget();
126 const invalid = std.math.maxInt(u32);
127 switch (ty.zigTypeTag(zcu)) {
128 .@"union" => {
129 const union_obj = zcu.typeToUnion(ty).?;
130 var max_count: u32 = 0;
131 for (union_obj.field_types.get(ip)) |field_ty| {
132 const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits);
133 if (field_count == invalid) return invalid;
134 if (field_count > max_count) max_count = field_count;
135 if (max_count > byval_float_count) return invalid;
136 }
137 return max_count;
138 },
139 .@"struct" => {
140 const fields_len = ty.structFieldCount(zcu);
141 var count: u32 = 0;
142 var i: u32 = 0;
143 while (i < fields_len) : (i += 1) {
144 const field_ty = ty.fieldType(i, zcu);
145 const field_count = countFloats(field_ty, zcu, maybe_float_bits);
146 if (field_count == invalid) return invalid;
147 count += field_count;
148 if (count > byval_float_count) return invalid;
149 }
150 return count;
151 },
152 .float => {
153 const float_bits = maybe_float_bits.* orelse {
154 const float_bits = ty.floatBits(target);
155 if (float_bits != 32 and float_bits != 64) return invalid;
156 maybe_float_bits.* = float_bits;
157 return 1;
158 };
159 if (ty.floatBits(target) == float_bits) return 1;
160 return invalid;
161 },
162 .void => return 0,
163 else => return invalid,
164 }
165}
166
167pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
168pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 };
169
170pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
171pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
172
173const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
174pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
175
176// Register classes
177const RegisterBitSet = RegisterManager.RegisterBitSet;
178pub const RegisterClass = struct {
179 pub const gp: RegisterBitSet = blk: {
180 var set = RegisterBitSet.initEmpty();
181 set.setRangeValue(.{
182 .start = 0,
183 .end = caller_preserved_regs.len + callee_preserved_regs.len,
184 }, true);
185 break :blk set;
186 };
187};
src/arch/arm/bits.zig deleted-1566
......@@ -1,1566 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const testing = std.testing;
4
5/// The condition field specifies the flags necessary for an
6/// Instruction to be executed
7pub const Condition = enum(u4) {
8 /// equal
9 eq,
10 /// not equal
11 ne,
12 /// unsigned higher or same
13 cs,
14 /// unsigned lower
15 cc,
16 /// negative
17 mi,
18 /// positive or zero
19 pl,
20 /// overflow
21 vs,
22 /// no overflow
23 vc,
24 /// unsigned higer
25 hi,
26 /// unsigned lower or same
27 ls,
28 /// greater or equal
29 ge,
30 /// less than
31 lt,
32 /// greater than
33 gt,
34 /// less than or equal
35 le,
36 /// always
37 al,
38
39 /// Converts a std.math.CompareOperator into a condition flag,
40 /// i.e. returns the condition that is true iff the result of the
41 /// comparison is true. Assumes signed comparison
42 pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
43 return switch (op) {
44 .gte => .ge,
45 .gt => .gt,
46 .neq => .ne,
47 .lt => .lt,
48 .lte => .le,
49 .eq => .eq,
50 };
51 }
52
53 /// Converts a std.math.CompareOperator into a condition flag,
54 /// i.e. returns the condition that is true iff the result of the
55 /// comparison is true. Assumes unsigned comparison
56 pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
57 return switch (op) {
58 .gte => .cs,
59 .gt => .hi,
60 .neq => .ne,
61 .lt => .cc,
62 .lte => .ls,
63 .eq => .eq,
64 };
65 }
66
67 /// Returns the condition which is true iff the given condition is
68 /// false (if such a condition exists)
69 pub fn negate(cond: Condition) Condition {
70 return switch (cond) {
71 .eq => .ne,
72 .ne => .eq,
73 .cs => .cc,
74 .cc => .cs,
75 .mi => .pl,
76 .pl => .mi,
77 .vs => .vc,
78 .vc => .vs,
79 .hi => .ls,
80 .ls => .hi,
81 .ge => .lt,
82 .lt => .ge,
83 .gt => .le,
84 .le => .gt,
85 .al => unreachable,
86 };
87 }
88};
89
90test "condition from CompareOperator" {
91 try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq));
92 try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq));
93
94 try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt));
95 try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt));
96
97 try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte));
98 try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte));
99}
100
101test "negate condition" {
102 try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate());
103 try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate());
104}
105
106/// Represents a register in the ARM instruction set architecture
107pub const Register = enum(u5) {
108 r0,
109 r1,
110 r2,
111 r3,
112 r4,
113 r5,
114 r6,
115 r7,
116 r8,
117 r9,
118 r10,
119 r11,
120 r12,
121 r13,
122 r14,
123 r15,
124
125 /// Argument / result / scratch register 1
126 a1,
127 /// Argument / result / scratch register 2
128 a2,
129 /// Argument / scratch register 3
130 a3,
131 /// Argument / scratch register 4
132 a4,
133 /// Variable-register 1
134 v1,
135 /// Variable-register 2
136 v2,
137 /// Variable-register 3
138 v3,
139 /// Variable-register 4
140 v4,
141 /// Variable-register 5
142 v5,
143 /// Platform register
144 v6,
145 /// Variable-register 7
146 v7,
147 /// Frame pointer or Variable-register 8
148 fp,
149 /// Intra-Procedure-call scratch register
150 ip,
151 /// Stack pointer
152 sp,
153 /// Link register
154 lr,
155 /// Program counter
156 pc,
157
158 /// Returns the unique 4-bit ID of this register which is used in
159 /// the machine code
160 pub fn id(reg: Register) u4 {
161 return @truncate(@intFromEnum(reg));
162 }
163
164 pub fn dwarfNum(reg: Register) u4 {
165 return reg.id();
166 }
167};
168
169test "Register.id" {
170 try testing.expectEqual(@as(u4, 15), Register.r15.id());
171 try testing.expectEqual(@as(u4, 15), Register.pc.id());
172}
173
174/// Program status registers containing flags, mode bits and other
175/// vital information
176pub const Psr = enum {
177 cpsr,
178 spsr,
179};
180
181/// Represents an instruction in the ARM instruction set architecture
182pub const Instruction = union(enum) {
183 data_processing: packed struct {
184 // Note to self: The order of the fields top-to-bottom is
185 // right-to-left in the actual 32-bit int representation
186 op2: u12,
187 rd: u4,
188 rn: u4,
189 s: u1,
190 opcode: u4,
191 i: u1,
192 fixed: u2 = 0b00,
193 cond: u4,
194 },
195 multiply: packed struct {
196 rn: u4,
197 fixed_1: u4 = 0b1001,
198 rm: u4,
199 ra: u4,
200 rd: u4,
201 set_cond: u1,
202 accumulate: u1,
203 fixed_2: u6 = 0b000000,
204 cond: u4,
205 },
206 multiply_long: packed struct {
207 rn: u4,
208 fixed_1: u4 = 0b1001,
209 rm: u4,
210 rdlo: u4,
211 rdhi: u4,
212 set_cond: u1,
213 accumulate: u1,
214 unsigned: u1,
215 fixed_2: u5 = 0b00001,
216 cond: u4,
217 },
218 signed_multiply_halfwords: packed struct {
219 rn: u4,
220 fixed_1: u1 = 0b0,
221 n: u1,
222 m: u1,
223 fixed_2: u1 = 0b1,
224 rm: u4,
225 fixed_3: u4 = 0b0000,
226 rd: u4,
227 fixed_4: u8 = 0b00010110,
228 cond: u4,
229 },
230 integer_saturating_arithmetic: packed struct {
231 rm: u4,
232 fixed_1: u8 = 0b0000_0101,
233 rd: u4,
234 rn: u4,
235 fixed_2: u1 = 0b0,
236 opc: u2,
237 fixed_3: u5 = 0b00010,
238 cond: u4,
239 },
240 bit_field_extract: packed struct {
241 rn: u4,
242 fixed_1: u3 = 0b101,
243 lsb: u5,
244 rd: u4,
245 widthm1: u5,
246 fixed_2: u1 = 0b1,
247 unsigned: u1,
248 fixed_3: u5 = 0b01111,
249 cond: u4,
250 },
251 single_data_transfer: packed struct {
252 offset: u12,
253 rd: u4,
254 rn: u4,
255 load_store: u1,
256 write_back: u1,
257 byte_word: u1,
258 up_down: u1,
259 pre_post: u1,
260 imm: u1,
261 fixed: u2 = 0b01,
262 cond: u4,
263 },
264 extra_load_store: packed struct {
265 imm4l: u4,
266 fixed_1: u1 = 0b1,
267 op2: u2,
268 fixed_2: u1 = 0b1,
269 imm4h: u4,
270 rt: u4,
271 rn: u4,
272 o1: u1,
273 write_back: u1,
274 imm: u1,
275 up_down: u1,
276 pre_index: u1,
277 fixed_3: u3 = 0b000,
278 cond: u4,
279 },
280 block_data_transfer: packed struct {
281 register_list: u16,
282 rn: u4,
283 load_store: u1,
284 write_back: u1,
285 psr_or_user: u1,
286 up_down: u1,
287 pre_post: u1,
288 fixed: u3 = 0b100,
289 cond: u4,
290 },
291 branch: packed struct {
292 offset: u24,
293 link: u1,
294 fixed: u3 = 0b101,
295 cond: u4,
296 },
297 branch_exchange: packed struct {
298 rn: u4,
299 fixed_1: u1 = 0b1,
300 link: u1,
301 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
302 cond: u4,
303 },
304 supervisor_call: packed struct {
305 comment: u24,
306 fixed: u4 = 0b1111,
307 cond: u4,
308 },
309 undefined_instruction: packed struct {
310 imm32: u32 = 0xe7ffdefe,
311 },
312 breakpoint: packed struct {
313 imm4: u4,
314 fixed_1: u4 = 0b0111,
315 imm12: u12,
316 fixed_2_and_cond: u12 = 0b1110_0001_0010,
317 },
318
319 /// Represents the possible operations which can be performed by a
320 /// Data Processing instruction
321 const Opcode = enum(u4) {
322 // Rd := Op1 AND Op2
323 @"and",
324 // Rd := Op1 EOR Op2
325 eor,
326 // Rd := Op1 - Op2
327 sub,
328 // Rd := Op2 - Op1
329 rsb,
330 // Rd := Op1 + Op2
331 add,
332 // Rd := Op1 + Op2 + C
333 adc,
334 // Rd := Op1 - Op2 + C - 1
335 sbc,
336 // Rd := Op2 - Op1 + C - 1
337 rsc,
338 // set condition codes on Op1 AND Op2
339 tst,
340 // set condition codes on Op1 EOR Op2
341 teq,
342 // set condition codes on Op1 - Op2
343 cmp,
344 // set condition codes on Op1 + Op2
345 cmn,
346 // Rd := Op1 OR Op2
347 orr,
348 // Rd := Op2
349 mov,
350 // Rd := Op1 AND NOT Op2
351 bic,
352 // Rd := NOT Op2
353 mvn,
354 };
355
356 /// Represents the second operand to a data processing instruction
357 /// which can either be content from a register or an immediate
358 /// value
359 pub const Operand = union(enum) {
360 register: packed struct {
361 rm: u4,
362 shift: u8,
363 },
364 immediate: packed struct {
365 imm: u8,
366 rotate: u4,
367 },
368
369 /// Represents multiple ways a register can be shifted. A
370 /// register can be shifted by a specific immediate value or
371 /// by the contents of another register
372 pub const Shift = union(enum) {
373 immediate: packed struct {
374 fixed: u1 = 0b0,
375 typ: u2,
376 amount: u5,
377 },
378 register: packed struct {
379 fixed_1: u1 = 0b1,
380 typ: u2,
381 fixed_2: u1 = 0b0,
382 rs: u4,
383 },
384
385 pub const Type = enum(u2) {
386 logical_left,
387 logical_right,
388 arithmetic_right,
389 rotate_right,
390 };
391
392 pub const none = Shift{
393 .immediate = .{
394 .amount = 0,
395 .typ = 0,
396 },
397 };
398
399 pub fn toU8(self: Shift) u8 {
400 return switch (self) {
401 .register => |v| @as(u8, @bitCast(v)),
402 .immediate => |v| @as(u8, @bitCast(v)),
403 };
404 }
405
406 pub fn reg(rs: Register, typ: Type) Shift {
407 return Shift{
408 .register = .{
409 .rs = rs.id(),
410 .typ = @intFromEnum(typ),
411 },
412 };
413 }
414
415 pub fn imm(amount: u5, typ: Type) Shift {
416 return Shift{
417 .immediate = .{
418 .amount = amount,
419 .typ = @intFromEnum(typ),
420 },
421 };
422 }
423 };
424
425 pub fn toU12(self: Operand) u12 {
426 return switch (self) {
427 .register => |v| @as(u12, @bitCast(v)),
428 .immediate => |v| @as(u12, @bitCast(v)),
429 };
430 }
431
432 pub fn reg(rm: Register, shift: Shift) Operand {
433 return Operand{
434 .register = .{
435 .rm = rm.id(),
436 .shift = shift.toU8(),
437 },
438 };
439 }
440
441 pub fn imm(immediate: u8, rotate: u4) Operand {
442 return Operand{
443 .immediate = .{
444 .imm = immediate,
445 .rotate = rotate,
446 },
447 };
448 }
449
450 /// Tries to convert an unsigned 32 bit integer into an
451 /// immediate operand using rotation. Returns null when there
452 /// is no conversion
453 pub fn fromU32(x: u32) ?Operand {
454 const masks = comptime blk: {
455 const base_mask: u32 = std.math.maxInt(u8);
456 var result = [_]u32{0} ** 16;
457 for (&result, 0..) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i);
458 break :blk result;
459 };
460
461 return for (masks, 0..) |mask, i| {
462 if (x & mask == x) {
463 break Operand{
464 .immediate = .{
465 .imm = @as(u8, @intCast(std.math.rotl(u32, x, 2 * i))),
466 .rotate = @as(u4, @intCast(i)),
467 },
468 };
469 }
470 } else null;
471 }
472 };
473
474 pub const AddressingMode = enum {
475 /// [<Rn>, <offset>]
476 ///
477 /// Address = Rn + offset
478 offset,
479 /// [<Rn>, <offset>]!
480 ///
481 /// Address = Rn + offset
482 /// Rn = Rn + offset
483 pre_index,
484 /// [<Rn>], <offset>
485 ///
486 /// Address = Rn
487 /// Rn = Rn + offset
488 post_index,
489 };
490
491 /// Represents the offset operand of a load or store
492 /// instruction. Data can be loaded from memory with either an
493 /// immediate offset or an offset that is stored in some register.
494 pub const Offset = union(enum) {
495 immediate: u12,
496 register: packed struct {
497 rm: u4,
498 fixed: u1 = 0b0,
499 stype: u2,
500 imm5: u5,
501 },
502
503 pub const Shift = union(enum) {
504 /// No shift
505 none,
506 /// Logical shift left
507 lsl: u5,
508 /// Logical shift right
509 lsr: u5,
510 /// Arithmetic shift right
511 asr: u5,
512 /// Rotate right
513 ror: u5,
514 /// Rotate right one bit, with extend
515 rrx,
516 };
517
518 pub const none = Offset{
519 .immediate = 0,
520 };
521
522 pub fn toU12(self: Offset) u12 {
523 return switch (self) {
524 .register => |v| @as(u12, @bitCast(v)),
525 .immediate => |v| v,
526 };
527 }
528
529 pub fn reg(rm: Register, shift: Shift) Offset {
530 return Offset{
531 .register = .{
532 .rm = rm.id(),
533 .stype = switch (shift) {
534 .none => 0b00,
535 .lsl => 0b00,
536 .lsr => 0b01,
537 .asr => 0b10,
538 .ror => 0b11,
539 .rrx => 0b11,
540 },
541 .imm5 = switch (shift) {
542 .none => 0,
543 .lsl => |n| n,
544 .lsr => |n| n,
545 .asr => |n| n,
546 .ror => |n| n,
547 .rrx => 0,
548 },
549 },
550 };
551 }
552
553 pub fn imm(immediate: u12) Offset {
554 return Offset{
555 .immediate = immediate,
556 };
557 }
558 };
559
560 /// Represents the offset operand of an extra load or store
561 /// instruction.
562 pub const ExtraLoadStoreOffset = union(enum) {
563 immediate: u8,
564 register: u4,
565
566 pub const none = ExtraLoadStoreOffset{
567 .immediate = 0,
568 };
569
570 pub fn reg(register: Register) ExtraLoadStoreOffset {
571 return ExtraLoadStoreOffset{
572 .register = register.id(),
573 };
574 }
575
576 pub fn imm(immediate: u8) ExtraLoadStoreOffset {
577 return ExtraLoadStoreOffset{
578 .immediate = immediate,
579 };
580 }
581 };
582
583 /// Represents the register list operand to a block data transfer
584 /// instruction
585 pub const RegisterList = packed struct {
586 r0: bool = false,
587 r1: bool = false,
588 r2: bool = false,
589 r3: bool = false,
590 r4: bool = false,
591 r5: bool = false,
592 r6: bool = false,
593 r7: bool = false,
594 r8: bool = false,
595 r9: bool = false,
596 r10: bool = false,
597 r11: bool = false,
598 r12: bool = false,
599 r13: bool = false,
600 r14: bool = false,
601 r15: bool = false,
602 };
603
604 pub fn toU32(self: Instruction) u32 {
605 return switch (self) {
606 .data_processing => |v| @as(u32, @bitCast(v)),
607 .multiply => |v| @as(u32, @bitCast(v)),
608 .multiply_long => |v| @as(u32, @bitCast(v)),
609 .signed_multiply_halfwords => |v| @as(u32, @bitCast(v)),
610 .integer_saturating_arithmetic => |v| @as(u32, @bitCast(v)),
611 .bit_field_extract => |v| @as(u32, @bitCast(v)),
612 .single_data_transfer => |v| @as(u32, @bitCast(v)),
613 .extra_load_store => |v| @as(u32, @bitCast(v)),
614 .block_data_transfer => |v| @as(u32, @bitCast(v)),
615 .branch => |v| @as(u32, @bitCast(v)),
616 .branch_exchange => |v| @as(u32, @bitCast(v)),
617 .supervisor_call => |v| @as(u32, @bitCast(v)),
618 .undefined_instruction => |v| v.imm32,
619 .breakpoint => |v| @as(u32, @intCast(v.imm4)) | (@as(u32, @intCast(v.fixed_1)) << 4) | (@as(u32, @intCast(v.imm12)) << 8) | (@as(u32, @intCast(v.fixed_2_and_cond)) << 20),
620 };
621 }
622
623 // Helper functions for the "real" functions below
624
625 fn dataProcessing(
626 cond: Condition,
627 opcode: Opcode,
628 s: u1,
629 rd: Register,
630 rn: Register,
631 op2: Operand,
632 ) Instruction {
633 return Instruction{
634 .data_processing = .{
635 .cond = @intFromEnum(cond),
636 .i = @intFromBool(op2 == .immediate),
637 .opcode = @intFromEnum(opcode),
638 .s = s,
639 .rn = rn.id(),
640 .rd = rd.id(),
641 .op2 = op2.toU12(),
642 },
643 };
644 }
645
646 fn specialMov(
647 cond: Condition,
648 rd: Register,
649 imm: u16,
650 top: bool,
651 ) Instruction {
652 return Instruction{
653 .data_processing = .{
654 .cond = @intFromEnum(cond),
655 .i = 1,
656 .opcode = if (top) 0b1010 else 0b1000,
657 .s = 0,
658 .rn = @as(u4, @truncate(imm >> 12)),
659 .rd = rd.id(),
660 .op2 = @as(u12, @truncate(imm)),
661 },
662 };
663 }
664
665 fn initMultiply(
666 cond: Condition,
667 set_cond: u1,
668 rd: Register,
669 rn: Register,
670 rm: Register,
671 ra: ?Register,
672 ) Instruction {
673 return Instruction{
674 .multiply = .{
675 .cond = @intFromEnum(cond),
676 .accumulate = @intFromBool(ra != null),
677 .set_cond = set_cond,
678 .rd = rd.id(),
679 .rn = rn.id(),
680 .ra = if (ra) |reg| reg.id() else 0b0000,
681 .rm = rm.id(),
682 },
683 };
684 }
685
686 fn multiplyLong(
687 cond: Condition,
688 signed: u1,
689 accumulate: u1,
690 set_cond: u1,
691 rdhi: Register,
692 rdlo: Register,
693 rm: Register,
694 rn: Register,
695 ) Instruction {
696 return Instruction{
697 .multiply_long = .{
698 .cond = @intFromEnum(cond),
699 .unsigned = signed,
700 .accumulate = accumulate,
701 .set_cond = set_cond,
702 .rdlo = rdlo.id(),
703 .rdhi = rdhi.id(),
704 .rn = rn.id(),
705 .rm = rm.id(),
706 },
707 };
708 }
709
710 fn signedMultiplyHalfwords(
711 n: u1,
712 m: u1,
713 cond: Condition,
714 rd: Register,
715 rn: Register,
716 rm: Register,
717 ) Instruction {
718 return Instruction{
719 .signed_multiply_halfwords = .{
720 .rn = rn.id(),
721 .n = n,
722 .m = m,
723 .rm = rm.id(),
724 .rd = rd.id(),
725 .cond = @intFromEnum(cond),
726 },
727 };
728 }
729
730 fn integerSaturationArithmetic(
731 cond: Condition,
732 rd: Register,
733 rm: Register,
734 rn: Register,
735 opc: u2,
736 ) Instruction {
737 return Instruction{
738 .integer_saturating_arithmetic = .{
739 .rm = rm.id(),
740 .rd = rd.id(),
741 .rn = rn.id(),
742 .opc = opc,
743 .cond = @intFromEnum(cond),
744 },
745 };
746 }
747
748 fn bitFieldExtract(
749 unsigned: u1,
750 cond: Condition,
751 rd: Register,
752 rn: Register,
753 lsb: u5,
754 width: u6,
755 ) Instruction {
756 assert(width > 0 and width <= 32);
757 return Instruction{
758 .bit_field_extract = .{
759 .rn = rn.id(),
760 .lsb = lsb,
761 .rd = rd.id(),
762 .widthm1 = @as(u5, @intCast(width - 1)),
763 .unsigned = unsigned,
764 .cond = @intFromEnum(cond),
765 },
766 };
767 }
768
769 fn singleDataTransfer(
770 cond: Condition,
771 rd: Register,
772 rn: Register,
773 offset: Offset,
774 mode: AddressingMode,
775 positive: bool,
776 byte_word: u1,
777 load_store: u1,
778 ) Instruction {
779 return Instruction{
780 .single_data_transfer = .{
781 .cond = @intFromEnum(cond),
782 .rn = rn.id(),
783 .rd = rd.id(),
784 .offset = offset.toU12(),
785 .load_store = load_store,
786 .write_back = switch (mode) {
787 .offset => 0b0,
788 .pre_index, .post_index => 0b1,
789 },
790 .byte_word = byte_word,
791 .up_down = @intFromBool(positive),
792 .pre_post = switch (mode) {
793 .offset, .pre_index => 0b1,
794 .post_index => 0b0,
795 },
796 .imm = @intFromBool(offset != .immediate),
797 },
798 };
799 }
800
801 fn extraLoadStore(
802 cond: Condition,
803 mode: AddressingMode,
804 positive: bool,
805 o1: u1,
806 op2: u2,
807 rn: Register,
808 rt: Register,
809 offset: ExtraLoadStoreOffset,
810 ) Instruction {
811 const imm4l: u4 = switch (offset) {
812 .immediate => |imm| @as(u4, @truncate(imm)),
813 .register => |reg| reg,
814 };
815 const imm4h: u4 = switch (offset) {
816 .immediate => |imm| @as(u4, @truncate(imm >> 4)),
817 .register => 0b0000,
818 };
819
820 return Instruction{
821 .extra_load_store = .{
822 .imm4l = imm4l,
823 .op2 = op2,
824 .imm4h = imm4h,
825 .rt = rt.id(),
826 .rn = rn.id(),
827 .o1 = o1,
828 .write_back = switch (mode) {
829 .offset => 0b0,
830 .pre_index, .post_index => 0b1,
831 },
832 .imm = @intFromBool(offset == .immediate),
833 .up_down = @intFromBool(positive),
834 .pre_index = switch (mode) {
835 .offset, .pre_index => 0b1,
836 .post_index => 0b0,
837 },
838 .cond = @intFromEnum(cond),
839 },
840 };
841 }
842
843 fn blockDataTransfer(
844 cond: Condition,
845 rn: Register,
846 reg_list: RegisterList,
847 pre_post: u1,
848 up_down: u1,
849 psr_or_user: u1,
850 write_back: bool,
851 load_store: u1,
852 ) Instruction {
853 return Instruction{
854 .block_data_transfer = .{
855 .register_list = @as(u16, @bitCast(reg_list)),
856 .rn = rn.id(),
857 .load_store = load_store,
858 .write_back = @intFromBool(write_back),
859 .psr_or_user = psr_or_user,
860 .up_down = up_down,
861 .pre_post = pre_post,
862 .cond = @intFromEnum(cond),
863 },
864 };
865 }
866
867 fn initBranch(cond: Condition, offset: i26, link: u1) Instruction {
868 return Instruction{
869 .branch = .{
870 .cond = @intFromEnum(cond),
871 .link = link,
872 .offset = @as(u24, @bitCast(@as(i24, @intCast(offset >> 2)))),
873 },
874 };
875 }
876
877 fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
878 return Instruction{
879 .branch_exchange = .{
880 .cond = @intFromEnum(cond),
881 .link = link,
882 .rn = rn.id(),
883 },
884 };
885 }
886
887 fn supervisorCall(cond: Condition, comment: u24) Instruction {
888 return Instruction{
889 .supervisor_call = .{
890 .cond = @intFromEnum(cond),
891 .comment = comment,
892 },
893 };
894 }
895
896 // This instruction has no official mnemonic equivalent so it is public as-is.
897 pub fn undefinedInstruction() Instruction {
898 return Instruction{
899 .undefined_instruction = .{},
900 };
901 }
902
903 fn initBreakpoint(imm: u16) Instruction {
904 return Instruction{
905 .breakpoint = .{
906 .imm12 = @as(u12, @truncate(imm >> 4)),
907 .imm4 = @as(u4, @truncate(imm)),
908 },
909 };
910 }
911
912 // Public functions replicating assembler syntax as closely as
913 // possible
914
915 // Data processing
916
917 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
918 return dataProcessing(cond, .@"and", 0, rd, rn, op2);
919 }
920
921 pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
922 return dataProcessing(cond, .@"and", 1, rd, rn, op2);
923 }
924
925 pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
926 return dataProcessing(cond, .eor, 0, rd, rn, op2);
927 }
928
929 pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
930 return dataProcessing(cond, .eor, 1, rd, rn, op2);
931 }
932
933 pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
934 return dataProcessing(cond, .sub, 0, rd, rn, op2);
935 }
936
937 pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
938 return dataProcessing(cond, .sub, 1, rd, rn, op2);
939 }
940
941 pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
942 return dataProcessing(cond, .rsb, 0, rd, rn, op2);
943 }
944
945 pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
946 return dataProcessing(cond, .rsb, 1, rd, rn, op2);
947 }
948
949 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
950 return dataProcessing(cond, .add, 0, rd, rn, op2);
951 }
952
953 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
954 return dataProcessing(cond, .add, 1, rd, rn, op2);
955 }
956
957 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
958 return dataProcessing(cond, .adc, 0, rd, rn, op2);
959 }
960
961 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
962 return dataProcessing(cond, .adc, 1, rd, rn, op2);
963 }
964
965 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
966 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
967 }
968
969 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
970 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
971 }
972
973 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
974 return dataProcessing(cond, .rsc, 0, rd, rn, op2);
975 }
976
977 pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
978 return dataProcessing(cond, .rsc, 1, rd, rn, op2);
979 }
980
981 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
982 return dataProcessing(cond, .tst, 1, .r0, rn, op2);
983 }
984
985 pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
986 return dataProcessing(cond, .teq, 1, .r0, rn, op2);
987 }
988
989 pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
990 return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
991 }
992
993 pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
994 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
995 }
996
997 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
998 return dataProcessing(cond, .orr, 0, rd, rn, op2);
999 }
1000
1001 pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
1002 return dataProcessing(cond, .orr, 1, rd, rn, op2);
1003 }
1004
1005 pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
1006 return dataProcessing(cond, .mov, 0, rd, .r0, op2);
1007 }
1008
1009 pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
1010 return dataProcessing(cond, .mov, 1, rd, .r0, op2);
1011 }
1012
1013 pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
1014 return dataProcessing(cond, .bic, 0, rd, rn, op2);
1015 }
1016
1017 pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
1018 return dataProcessing(cond, .bic, 1, rd, rn, op2);
1019 }
1020
1021 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
1022 return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
1023 }
1024
1025 pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
1026 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
1027 }
1028
1029 // Integer Saturating Arithmetic
1030
1031 pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
1032 return integerSaturationArithmetic(cond, rd, rm, rn, 0b00);
1033 }
1034
1035 pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
1036 return integerSaturationArithmetic(cond, rd, rm, rn, 0b01);
1037 }
1038
1039 pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
1040 return integerSaturationArithmetic(cond, rd, rm, rn, 0b10);
1041 }
1042
1043 pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
1044 return integerSaturationArithmetic(cond, rd, rm, rn, 0b11);
1045 }
1046
1047 // movw and movt
1048
1049 pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction {
1050 return specialMov(cond, rd, imm, false);
1051 }
1052
1053 pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction {
1054 return specialMov(cond, rd, imm, true);
1055 }
1056
1057 // PSR transfer
1058
1059 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
1060 return Instruction{
1061 .data_processing = .{
1062 .cond = @intFromEnum(cond),
1063 .i = 0,
1064 .opcode = if (psr == .spsr) 0b1010 else 0b1000,
1065 .s = 0,
1066 .rn = 0b1111,
1067 .rd = rd.id(),
1068 .op2 = 0b0000_0000_0000,
1069 },
1070 };
1071 }
1072
1073 pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction {
1074 return Instruction{
1075 .data_processing = .{
1076 .cond = @intFromEnum(cond),
1077 .i = 0,
1078 .opcode = if (psr == .spsr) 0b1011 else 0b1001,
1079 .s = 0,
1080 .rn = 0b1111,
1081 .rd = 0b1111,
1082 .op2 = op.toU12(),
1083 },
1084 };
1085 }
1086
1087 // Multiply
1088
1089 pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1090 return initMultiply(cond, 0, rd, rn, rm, null);
1091 }
1092
1093 pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1094 return initMultiply(cond, 1, rd, rn, rm, null);
1095 }
1096
1097 pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
1098 return initMultiply(cond, 0, rd, rn, rm, ra);
1099 }
1100
1101 pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
1102 return initMultiply(cond, 1, rd, rn, rm, ra);
1103 }
1104
1105 // Multiply long
1106
1107 pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1108 return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn);
1109 }
1110
1111 pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1112 return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn);
1113 }
1114
1115 pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1116 return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn);
1117 }
1118
1119 pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1120 return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn);
1121 }
1122
1123 pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1124 return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn);
1125 }
1126
1127 pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1128 return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn);
1129 }
1130
1131 pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1132 return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn);
1133 }
1134
1135 pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1136 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
1137 }
1138
1139 // Signed Multiply (halfwords)
1140
1141 pub fn smulbb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1142 return signedMultiplyHalfwords(0, 0, cond, rd, rn, rm);
1143 }
1144
1145 pub fn smulbt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1146 return signedMultiplyHalfwords(0, 1, cond, rd, rn, rm);
1147 }
1148
1149 pub fn smultb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1150 return signedMultiplyHalfwords(1, 0, cond, rd, rn, rm);
1151 }
1152
1153 pub fn smultt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1154 return signedMultiplyHalfwords(1, 1, cond, rd, rn, rm);
1155 }
1156
1157 // Bit field extract
1158
1159 pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
1160 return bitFieldExtract(0b1, cond, rd, rn, lsb, width);
1161 }
1162
1163 pub fn sbfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
1164 return bitFieldExtract(0b0, cond, rd, rn, lsb, width);
1165 }
1166
1167 // Single data transfer
1168
1169 pub const OffsetArgs = struct {
1170 mode: AddressingMode = .offset,
1171 positive: bool = true,
1172 offset: Offset,
1173 };
1174
1175 pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1176 return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 1);
1177 }
1178
1179 pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1180 return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 1);
1181 }
1182
1183 pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1184 return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 0);
1185 }
1186
1187 pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1188 return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 0);
1189 }
1190
1191 // Extra load/store
1192
1193 pub const ExtraLoadStoreOffsetArgs = struct {
1194 mode: AddressingMode = .offset,
1195 positive: bool = true,
1196 offset: ExtraLoadStoreOffset,
1197 };
1198
1199 pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1200 return extraLoadStore(cond, args.mode, args.positive, 0b0, 0b01, rn, rt, args.offset);
1201 }
1202
1203 pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1204 return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b01, rn, rt, args.offset);
1205 }
1206
1207 pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1208 return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b11, rn, rt, args.offset);
1209 }
1210
1211 pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1212 return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b10, rn, rt, args.offset);
1213 }
1214
1215 // Block data transfer
1216
1217 pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1218 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
1219 }
1220
1221 pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1222 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
1223 }
1224
1225 pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1226 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
1227 }
1228
1229 pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1230 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
1231 }
1232
1233 pub const ldmfa = ldmda;
1234 pub const ldmea = ldmdb;
1235 pub const ldmed = ldmib;
1236 pub const ldmfd = ldmia;
1237 pub const ldm = ldmia;
1238
1239 pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1240 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
1241 }
1242
1243 pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1244 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
1245 }
1246
1247 pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1248 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
1249 }
1250
1251 pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1252 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
1253 }
1254
1255 pub const stmed = stmda;
1256 pub const stmfd = stmdb;
1257 pub const stmfa = stmib;
1258 pub const stmea = stmia;
1259 pub const stm = stmia;
1260
1261 // Branch
1262
1263 pub fn b(cond: Condition, offset: i26) Instruction {
1264 return initBranch(cond, offset, 0);
1265 }
1266
1267 pub fn bl(cond: Condition, offset: i26) Instruction {
1268 return initBranch(cond, offset, 1);
1269 }
1270
1271 // Branch and exchange
1272
1273 pub fn bx(cond: Condition, rn: Register) Instruction {
1274 return branchExchange(cond, rn, 0);
1275 }
1276
1277 pub fn blx(cond: Condition, rn: Register) Instruction {
1278 return branchExchange(cond, rn, 1);
1279 }
1280
1281 // Supervisor Call
1282
1283 pub const swi = svc;
1284
1285 pub fn svc(cond: Condition, comment: u24) Instruction {
1286 return supervisorCall(cond, comment);
1287 }
1288
1289 // Breakpoint
1290
1291 pub fn bkpt(imm: u16) Instruction {
1292 return initBreakpoint(imm);
1293 }
1294
1295 // Aliases
1296
1297 pub fn nop() Instruction {
1298 return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none));
1299 }
1300
1301 pub fn pop(cond: Condition, args: anytype) Instruction {
1302 if (@typeInfo(@TypeOf(args)) != .@"struct") {
1303 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1304 }
1305
1306 if (args.len < 1) {
1307 @compileError("Expected at least one register");
1308 } else if (args.len == 1) {
1309 const reg = args[0];
1310 return ldr(cond, reg, .sp, .{
1311 .mode = .post_index,
1312 .positive = true,
1313 .offset = Offset.imm(4),
1314 });
1315 } else {
1316 var register_list: u16 = 0;
1317 inline for (args) |arg| {
1318 const reg = @as(Register, arg);
1319 register_list |= @as(u16, 1) << reg.id();
1320 }
1321 return ldm(cond, .sp, true, @as(RegisterList, @bitCast(register_list)));
1322 }
1323 }
1324
1325 pub fn push(cond: Condition, args: anytype) Instruction {
1326 if (@typeInfo(@TypeOf(args)) != .@"struct") {
1327 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1328 }
1329
1330 if (args.len < 1) {
1331 @compileError("Expected at least one register");
1332 } else if (args.len == 1) {
1333 const reg = args[0];
1334 return str(cond, reg, .sp, .{
1335 .mode = .pre_index,
1336 .positive = false,
1337 .offset = Offset.imm(4),
1338 });
1339 } else {
1340 var register_list: u16 = 0;
1341 inline for (args) |arg| {
1342 const reg = @as(Register, arg);
1343 register_list |= @as(u16, 1) << reg.id();
1344 }
1345 return stmdb(cond, .sp, true, @as(RegisterList, @bitCast(register_list)));
1346 }
1347 }
1348
1349 pub const ShiftAmount = union(enum) {
1350 immediate: u5,
1351 register: Register,
1352
1353 pub fn imm(immediate: u5) ShiftAmount {
1354 return .{
1355 .immediate = immediate,
1356 };
1357 }
1358
1359 pub fn reg(register: Register) ShiftAmount {
1360 return .{
1361 .register = register,
1362 };
1363 }
1364 };
1365
1366 pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1367 return switch (shift) {
1368 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
1369 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
1370 };
1371 }
1372
1373 pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1374 return switch (shift) {
1375 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
1376 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
1377 };
1378 }
1379
1380 pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1381 return switch (shift) {
1382 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
1383 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
1384 };
1385 }
1386
1387 pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1388 return switch (shift) {
1389 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
1390 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
1391 };
1392 }
1393
1394 pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1395 return switch (shift) {
1396 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
1397 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
1398 };
1399 }
1400
1401 pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1402 return switch (shift) {
1403 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
1404 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
1405 };
1406 }
1407
1408 pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1409 return switch (shift) {
1410 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
1411 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
1412 };
1413 }
1414
1415 pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1416 return switch (shift) {
1417 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
1418 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
1419 };
1420 }
1421};
1422
1423test "serialize instructions" {
1424 const Testcase = struct {
1425 inst: Instruction,
1426 expected: u32,
1427 };
1428
1429 const testcases = [_]Testcase{
1430 .{ // add r0, r0, r0
1431 .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
1432 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
1433 },
1434 .{ // mov r4, r2
1435 .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
1436 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
1437 },
1438 .{ // mov r0, #42
1439 .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
1440 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
1441 },
1442 .{ // mrs r5, cpsr
1443 .inst = Instruction.mrs(.al, .r5, .cpsr),
1444 .expected = 0b1110_00010_0_001111_0101_000000000000,
1445 },
1446 .{ // mul r0, r1, r2
1447 .inst = Instruction.mul(.al, .r0, .r1, .r2),
1448 .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001,
1449 },
1450 .{ // umlal r0, r1, r5, r6
1451 .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6),
1452 .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101,
1453 },
1454 .{ // ldr r0, [r2, #42]
1455 .inst = Instruction.ldr(.al, .r0, .r2, .{
1456 .offset = Instruction.Offset.imm(42),
1457 }),
1458 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
1459 },
1460 .{ // str r0, [r3]
1461 .inst = Instruction.str(.al, .r0, .r3, .{
1462 .offset = Instruction.Offset.none,
1463 }),
1464 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
1465 },
1466 .{ // strh r1, [r5]
1467 .inst = Instruction.strh(.al, .r1, .r5, .{
1468 .offset = Instruction.ExtraLoadStoreOffset.none,
1469 }),
1470 .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000,
1471 },
1472 .{ // b #12
1473 .inst = Instruction.b(.al, 12),
1474 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011,
1475 },
1476 .{ // bl #-4
1477 .inst = Instruction.bl(.al, -4),
1478 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111,
1479 },
1480 .{ // bx lr
1481 .inst = Instruction.bx(.al, .lr),
1482 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
1483 },
1484 .{ // svc #0
1485 .inst = Instruction.svc(.al, 0),
1486 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
1487 },
1488 .{ // bkpt #42
1489 .inst = Instruction.bkpt(42),
1490 .expected = 0b1110_0001_0010_000000000010_0111_1010,
1491 },
1492 .{ // stmdb r9, {r0}
1493 .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
1494 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
1495 },
1496 .{ // ldmea r4!, {r2, r5}
1497 .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
1498 .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
1499 },
1500 .{ // qadd r0, r7, r8
1501 .inst = Instruction.qadd(.al, .r0, .r7, .r8),
1502 .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
1503 },
1504 .{ // smulbt r0, r0, r0
1505 .inst = Instruction.smulbt(.al, .r0, .r0, .r0),
1506 .expected = 0b1110_00010110_0000_0000_0000_1_1_0_0_0000,
1507 },
1508 };
1509
1510 for (testcases) |case| {
1511 const actual = case.inst.toU32();
1512 try testing.expectEqual(case.expected, actual);
1513 }
1514}
1515
1516test "aliases" {
1517 const Testcase = struct {
1518 expected: Instruction,
1519 actual: Instruction,
1520 };
1521
1522 const testcases = [_]Testcase{
1523 .{ // pop { r6 }
1524 .actual = Instruction.pop(.al, .{.r6}),
1525 .expected = Instruction.ldr(.al, .r6, .sp, .{
1526 .mode = .post_index,
1527 .positive = true,
1528 .offset = Instruction.Offset.imm(4),
1529 }),
1530 },
1531 .{ // pop { r1, r5 }
1532 .actual = Instruction.pop(.al, .{ .r1, .r5 }),
1533 .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
1534 },
1535 .{ // push { r3 }
1536 .actual = Instruction.push(.al, .{.r3}),
1537 .expected = Instruction.str(.al, .r3, .sp, .{
1538 .mode = .pre_index,
1539 .positive = false,
1540 .offset = Instruction.Offset.imm(4),
1541 }),
1542 },
1543 .{ // push { r0, r2 }
1544 .actual = Instruction.push(.al, .{ .r0, .r2 }),
1545 .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
1546 },
1547 .{ // lsl r4, r5, #5
1548 .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)),
1549 .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg(
1550 .r5,
1551 Instruction.Operand.Shift.imm(5, .logical_left),
1552 )),
1553 },
1554 .{ // asrs r1, r1, r3
1555 .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)),
1556 .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg(
1557 .r1,
1558 Instruction.Operand.Shift.reg(.r3, .arithmetic_right),
1559 )),
1560 },
1561 };
1562
1563 for (testcases) |case| {
1564 try testing.expectEqual(case.expected.toU32(), case.actual.toU32());
1565 }
1566}
src/arch/mips/abi.zig deleted-84
......@@ -1,84 +0,0 @@
1const std = @import("std");
2const Type = @import("../../Type.zig");
3const Zcu = @import("../../Zcu.zig");
4const assert = std.debug.assert;
5
6pub const Class = union(enum) {
7 memory,
8 byval,
9 i32_array: u8,
10};
11
12pub const Context = enum { ret, arg };
13
14pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
15 const target = zcu.getTarget();
16 std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
17
18 const max_direct_size = target.ptrBitWidth() * 2;
19 switch (ty.zigTypeTag(zcu)) {
20 .@"struct" => {
21 const bit_size = ty.bitSize(zcu);
22 if (ty.containerLayout(zcu) == .@"packed") {
23 if (bit_size > max_direct_size) return .memory;
24 return .byval;
25 }
26 if (bit_size > max_direct_size) return .memory;
27 // TODO: for bit_size <= 32 using byval is more correct, but that needs inreg argument attribute
28 const count = @as(u8, @intCast(std.mem.alignForward(u64, bit_size, 32) / 32));
29 return .{ .i32_array = count };
30 },
31 .@"union" => {
32 const bit_size = ty.bitSize(zcu);
33 if (ty.containerLayout(zcu) == .@"packed") {
34 if (bit_size > max_direct_size) return .memory;
35 return .byval;
36 }
37 if (bit_size > max_direct_size) return .memory;
38
39 return .byval;
40 },
41 .bool => return .byval,
42 .float => return .byval,
43 .int, .@"enum", .error_set => {
44 return .byval;
45 },
46 .vector => {
47 const elem_type = ty.elemType2(zcu);
48 switch (elem_type.zigTypeTag(zcu)) {
49 .bool, .int => {
50 const bit_size = ty.bitSize(zcu);
51 if (ctx == .ret and bit_size > 128) return .memory;
52 if (bit_size > 512) return .memory;
53 // TODO: byval vector arguments with non power of 2 size need inreg attribute
54 return .byval;
55 },
56 .float => return .memory,
57 else => unreachable,
58 }
59 },
60 .optional => {
61 std.debug.assert(ty.isPtrLikeOptional(zcu));
62 return .byval;
63 },
64 .pointer => {
65 std.debug.assert(!ty.isSlice(zcu));
66 return .byval;
67 },
68 .error_union,
69 .frame,
70 .@"anyframe",
71 .noreturn,
72 .void,
73 .type,
74 .comptime_float,
75 .comptime_int,
76 .undefined,
77 .null,
78 .@"fn",
79 .@"opaque",
80 .enum_literal,
81 .array,
82 => unreachable,
83 }
84}
src/arch/powerpc/CodeGen.zig deleted-51
......@@ -1,51 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("std");
3
4const Air = @import("../../Air.zig");
5const codegen = @import("../../codegen.zig");
6const InternPool = @import("../../InternPool.zig");
7const link = @import("../../link.zig");
8const Zcu = @import("../../Zcu.zig");
9
10const assert = std.debug.assert;
11const log = std.log.scoped(.codegen);
12
13pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
14 return null;
15}
16
17pub fn generate(
18 bin_file: *link.File,
19 pt: Zcu.PerThread,
20 src_loc: Zcu.LazySrcLoc,
21 func_index: InternPool.Index,
22 air: *const Air,
23 liveness: *const Air.Liveness,
24) codegen.CodeGenError!noreturn {
25 _ = bin_file;
26 _ = pt;
27 _ = src_loc;
28 _ = func_index;
29 _ = air;
30 _ = liveness;
31
32 unreachable;
33}
34
35pub fn generateLazy(
36 bin_file: *link.File,
37 pt: Zcu.PerThread,
38 src_loc: Zcu.LazySrcLoc,
39 lazy_sym: link.File.LazySymbol,
40 code: *std.ArrayListUnmanaged(u8),
41 debug_output: link.File.DebugInfoOutput,
42) codegen.CodeGenError!void {
43 _ = bin_file;
44 _ = pt;
45 _ = src_loc;
46 _ = lazy_sym;
47 _ = code;
48 _ = debug_output;
49
50 unreachable;
51}
src/arch/riscv64/CodeGen.zig+30-33
......@@ -436,7 +436,7 @@ const InstTracking = struct {
436436 fn trackSpill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void {
437437 try function.freeValue(inst_tracking.short);
438438 inst_tracking.reuseFrame();
439 tracking_log.debug("%{f} => {f} (spilled)", .{ inst, inst_tracking.* });
439 tracking_log.debug("%{d} => {f} (spilled)", .{ inst, inst_tracking.* });
440440 }
441441
442442 fn verifyMaterialize(inst_tracking: InstTracking, target: InstTracking) void {
......@@ -500,14 +500,14 @@ const InstTracking = struct {
500500 else => target.long,
501501 } else target.long;
502502 inst_tracking.short = target.short;
503 tracking_log.debug("%{f} => {f} (materialize)", .{ inst, inst_tracking.* });
503 tracking_log.debug("%{d} => {f} (materialize)", .{ inst, inst_tracking.* });
504504 }
505505
506506 fn resurrect(inst_tracking: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void {
507507 switch (inst_tracking.short) {
508508 .dead => |die_generation| if (die_generation >= scope_generation) {
509509 inst_tracking.reuseFrame();
510 tracking_log.debug("%{f} => {f} (resurrect)", .{ inst, inst_tracking.* });
510 tracking_log.debug("%{d} => {f} (resurrect)", .{ inst, inst_tracking.* });
511511 },
512512 else => {},
513513 }
......@@ -517,7 +517,7 @@ const InstTracking = struct {
517517 if (inst_tracking.short == .dead) return;
518518 try function.freeValue(inst_tracking.short);
519519 inst_tracking.short = .{ .dead = function.scope_generation };
520 tracking_log.debug("%{f} => {f} (death)", .{ inst, inst_tracking.* });
520 tracking_log.debug("%{d} => {f} (death)", .{ inst, inst_tracking.* });
521521 }
522522
523523 fn reuse(
......@@ -528,15 +528,15 @@ const InstTracking = struct {
528528 ) void {
529529 inst_tracking.short = .{ .dead = function.scope_generation };
530530 if (new_inst) |inst|
531 tracking_log.debug("%{f} => {f} (reuse %{f})", .{ inst, inst_tracking.*, old_inst })
531 tracking_log.debug("%{d} => {f} (reuse %{d})", .{ inst, inst_tracking.*, old_inst })
532532 else
533 tracking_log.debug("tmp => {f} (reuse %{f})", .{ inst_tracking.*, old_inst });
533 tracking_log.debug("tmp => {f} (reuse %{d})", .{ inst_tracking.*, old_inst });
534534 }
535535
536536 fn liveOut(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) void {
537537 for (inst_tracking.getRegs()) |reg| {
538538 if (function.register_manager.isRegFree(reg)) {
539 tracking_log.debug("%{f} => {f} (live-out)", .{ inst, inst_tracking.* });
539 tracking_log.debug("%{d} => {f} (live-out)", .{ inst, inst_tracking.* });
540540 continue;
541541 }
542542
......@@ -563,14 +563,13 @@ const InstTracking = struct {
563563 // Perform side-effects of freeValue manually.
564564 function.register_manager.freeReg(reg);
565565
566 tracking_log.debug("%{f} => {f} (live-out %{f})", .{ inst, inst_tracking.*, tracked_inst });
566 tracking_log.debug("%{d} => {f} (live-out %{d})", .{ inst, inst_tracking.*, tracked_inst });
567567 }
568568 }
569569
570 pub fn format(inst_tracking: InstTracking, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
571 comptime assert(fmt.len == 0);
572 if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try bw.print("|{}| ", .{inst_tracking.long});
573 try bw.print("{}", .{inst_tracking.short});
570 pub fn format(inst_tracking: InstTracking, writer: *std.io.Writer) std.io.Writer.Error!void {
571 if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try writer.print("|{}| ", .{inst_tracking.long});
572 try writer.print("{}", .{inst_tracking.short});
574573 }
575574};
576575
......@@ -934,7 +933,7 @@ const FormatWipMirData = struct {
934933 func: *Func,
935934 inst: Mir.Inst.Index,
936935};
937fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void {
936fn formatWipMir(data: FormatWipMirData, writer: *std.io.Writer) std.io.Writer.Error!void {
938937 const pt = data.func.pt;
939938 const comp = pt.zcu.comp;
940939 var lower: Lower = .{
......@@ -957,11 +956,11 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
957956 lower.err_msg.?.deinit(data.func.gpa);
958957 lower.err_msg = null;
959958 }
960 try bw.writeAll(lower.err_msg.?.msg);
959 try writer.writeAll(lower.err_msg.?.msg);
961960 return;
962961 },
963962 error.OutOfMemory, error.InvalidInstruction => |e| {
964 try bw.writeAll(switch (e) {
963 try writer.writeAll(switch (e) {
965964 error.OutOfMemory => "Out of memory",
966965 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
967966 });
......@@ -969,12 +968,12 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
969968 },
970969 else => |e| return e,
971970 }).insts) |lowered_inst| {
972 if (!first) try bw.writeAll("\ndebug(wip_mir): ");
973 try bw.print(" | {}", .{lowered_inst});
971 if (!first) try writer.writeAll("\ndebug(wip_mir): ");
972 try writer.print(" | {}", .{lowered_inst});
974973 first = false;
975974 }
976975}
977fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) {
976fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(FormatWipMirData, formatWipMir) {
978977 return .{ .data = .{ .func = func, .inst = inst } };
979978}
980979
......@@ -982,10 +981,10 @@ const FormatNavData = struct {
982981 ip: *const InternPool,
983982 nav_index: InternPool.Nav.Index,
984983};
985fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void {
986 try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});
984fn formatNav(data: FormatNavData, writer: *std.io.Writer) std.io.Writer.Error!void {
985 try writer.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});
987986}
988fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) {
987fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(FormatNavData, formatNav) {
989988 return .{ .data = .{
990989 .ip = ip,
991990 .nav_index = nav_index,
......@@ -996,27 +995,25 @@ const FormatAirData = struct {
996995 func: *Func,
997996 inst: Air.Inst.Index,
998997};
999fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
1000 comptime assert(fmt.len == 0);
1001 // not acceptable implementation:
1002 // data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness);
998fn formatAir(data: FormatAirData, writer: *std.io.Writer) std.io.Writer.Error!void {
999 // Not acceptable implementation because it ignores `writer`:
1000 //data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness);
10031001 _ = data;
1004 _ = w;
1005 @panic("TODO: unimplemented");
1002 _ = writer;
1003 @panic("unimplemented");
10061004}
1007fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) {
1005fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) {
10081006 return .{ .data = .{ .func = func, .inst = inst } };
10091007}
10101008
10111009const FormatTrackingData = struct {
10121010 func: *Func,
10131011};
1014fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
1015 comptime assert(fmt.len == 0);
1012fn formatTracking(data: FormatTrackingData, writer: *std.io.Writer) std.io.Writer.Error!void {
10161013 var it = data.func.inst_tracking.iterator();
1017 while (it.next()) |entry| try bw.print("\n%{d} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* });
1014 while (it.next()) |entry| try writer.print("\n%{d} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* });
10181015}
1019fn fmtTracking(func: *Func) std.fmt.Formatter(formatTracking) {
1016fn fmtTracking(func: *Func) std.fmt.Formatter(FormatTrackingData, formatTracking) {
10201017 return .{ .data = .{ .func = func } };
10211018}
10221019
......@@ -1826,7 +1823,7 @@ fn computeFrameLayout(func: *Func) !FrameLayout {
18261823 total_alloc_size + 64 + args_frame_size + spill_frame_size + call_frame_size,
18271824 @intCast(frame_align[@intFromEnum(FrameIndex.base_ptr)].toByteUnits().?),
18281825 );
1829 log.debug("frame size: {}", .{acc_frame_size});
1826 log.debug("frame size: {d}", .{acc_frame_size});
18301827
18311828 // store the ra at total_size - 8, so it's the very first thing in the stack
18321829 // relative to the fp
src/arch/riscv64/Mir.zig+2-3
......@@ -92,9 +92,8 @@ pub const Inst = struct {
9292 },
9393 };
9494
95 pub fn format(inst: Inst, bw: *std.io.Writer, comptime fmt: []const u8) !void {
96 assert(fmt.len == 0);
97 try bw.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) });
95 pub fn format(inst: Inst, writer: *std.io.Writer) std.io.Writer.Error!void {
96 try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) });
9897 }
9998};
10099
src/arch/riscv64/bits.zig-9
......@@ -256,15 +256,6 @@ pub const FrameIndex = enum(u32) {
256256 pub fn isNamed(fi: FrameIndex) bool {
257257 return @intFromEnum(fi) < named_count;
258258 }
259
260 pub fn format(fi: FrameIndex, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
261 comptime assert(fmt.len == 0);
262 try bw.writeAll("FrameIndex");
263 if (fi.isNamed())
264 try bw.print(".{s}", .{@tagName(fi)})
265 else
266 try bw.print("({d})", .{@intFromEnum(fi)});
267 }
268259};
269260
270261/// A linker symbol not yet allocated in VM.
src/arch/sparc64/CodeGen.zig+1-1
......@@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
723723
724724 if (std.debug.runtime_safety) {
725725 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
726 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
726 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{t}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[@intFromEnum(inst)] });
727727 }
728728 }
729729 }
src/arch/wasm/CodeGen.zig+6-13
......@@ -18,7 +18,7 @@ const Compilation = @import("../../Compilation.zig");
1818const link = @import("../../link.zig");
1919const Air = @import("../../Air.zig");
2020const Mir = @import("Mir.zig");
21const abi = @import("abi.zig");
21const abi = @import("../../codegen/wasm/abi.zig");
2222const Alignment = InternPool.Alignment;
2323const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
2424const errUnionErrorOffset = codegen.errUnionErrorOffset;
......@@ -1960,7 +1960,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19601960 .wasm_memory_size => cg.airWasmMemorySize(inst),
19611961 .wasm_memory_grow => cg.airWasmMemoryGrow(inst),
19621962
1963 .memcpy => cg.airMemcpy(inst),
1963 .memcpy, .memmove => cg.airMemcpy(inst),
19641964
19651965 .ret_addr => cg.airRetAddr(inst),
19661966 .tag_name => cg.airTagName(inst),
......@@ -1984,7 +1984,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19841984 .c_va_copy,
19851985 .c_va_end,
19861986 .c_va_start,
1987 .memmove,
19881987 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
19891988
19901989 .atomic_load => cg.airAtomicLoad(inst),
......@@ -2047,7 +2046,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
20472046 try cg.genInst(inst);
20482047
20492048 if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) {
2050 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{
2049 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{t}')", .{
20512050 inst,
20522051 cg.air.instructions.items(.tag)[@intFromEnum(inst)],
20532052 });
......@@ -2405,10 +2404,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
24052404 try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });
24062405 },
24072406 else => if (abi_size > 8) {
2408 return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{
2409 ty.fmt(pt),
2410 abi_size,
2411 });
2407 return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{ ty.fmt(pt), abi_size });
24122408 },
24132409 }
24142410 try cg.emitWValue(lhs);
......@@ -2597,10 +2593,7 @@ fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WV
25972593 if (ty.zigTypeTag(zcu) == .int) {
25982594 return cg.binOpBigInt(lhs, rhs, ty, op);
25992595 } else {
2600 return cg.fail(
2601 "TODO: Implement binary operation for type: {f}",
2602 .{ty.fmt(pt)},
2603 );
2596 return cg.fail("TODO: Implement binary operation for type: {f}", .{ty.fmt(pt)});
26042597 }
26052598 }
26062599
......@@ -3333,7 +3326,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
33333326 },
33343327 else => unreachable,
33353328 },
3336 else => return cg.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag(zcu)}),
3329 else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}),
33373330 }
33383331}
33393332
src/arch/wasm/abi.zig deleted-87
......@@ -1,87 +0,0 @@
1//! Classifies Zig types to follow the C-ABI for Wasm.
2//! The convention for Wasm's C-ABI can be found at the tool-conventions repo:
3//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
4//! When not targeting the C-ABI, Zig is allowed to do derail from this convention.
5//! Note: Above mentioned document is not an official specification, therefore called a convention.
6
7const std = @import("std");
8const Target = std.Target;
9const assert = std.debug.assert;
10
11const Type = @import("../../Type.zig");
12const Zcu = @import("../../Zcu.zig");
13
14/// Defines how to pass a type as part of a function signature,
15/// both for parameters as well as return values.
16pub const Class = union(enum) {
17 direct: Type,
18 indirect,
19};
20
21/// Classifies a given Zig type to determine how they must be passed
22/// or returned as value within a wasm function.
23pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
24 const ip = &zcu.intern_pool;
25 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
26 switch (ty.zigTypeTag(zcu)) {
27 .int, .@"enum", .error_set => return .{ .direct = ty },
28 .float => return .{ .direct = ty },
29 .bool => return .{ .direct = ty },
30 .vector => return .{ .direct = ty },
31 .array => return .indirect,
32 .optional => {
33 assert(ty.isPtrLikeOptional(zcu));
34 return .{ .direct = ty };
35 },
36 .pointer => {
37 assert(!ty.isSlice(zcu));
38 return .{ .direct = ty };
39 },
40 .@"struct" => {
41 const struct_type = zcu.typeToStruct(ty).?;
42 if (struct_type.layout == .@"packed") {
43 return .{ .direct = ty };
44 }
45 if (struct_type.field_types.len > 1) {
46 // The struct type is non-scalar.
47 return .indirect;
48 }
49 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]);
50 const explicit_align = struct_type.fieldAlign(ip, 0);
51 if (explicit_align != .none) {
52 if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu)))
53 return .indirect;
54 }
55 return classifyType(field_ty, zcu);
56 },
57 .@"union" => {
58 const union_obj = zcu.typeToUnion(ty).?;
59 if (union_obj.flagsUnordered(ip).layout == .@"packed") {
60 return .{ .direct = ty };
61 }
62 const layout = ty.unionGetLayout(zcu);
63 assert(layout.tag_size == 0);
64 if (union_obj.field_types.len > 1) return .indirect;
65 const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
66 return classifyType(first_field_ty, zcu);
67 },
68 .error_union,
69 .frame,
70 .@"anyframe",
71 .noreturn,
72 .void,
73 .type,
74 .comptime_float,
75 .comptime_int,
76 .undefined,
77 .null,
78 .@"fn",
79 .@"opaque",
80 .enum_literal,
81 => unreachable,
82 }
83}
84
85pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool {
86 return scalar_ty.bitSize(zcu) > 64;
87}
src/arch/x86/bits.zig deleted-100
......@@ -1,100 +0,0 @@
1const std = @import("std");
2
3// zig fmt: off
4pub const Register = enum(u8) {
5 // 0 through 7, 32-bit registers. id is int value
6 eax, ecx, edx, ebx, esp, ebp, esi, edi,
7
8 // 8-15, 16-bit registers. id is int value - 8.
9 ax, cx, dx, bx, sp, bp, si, di,
10
11 // 16-23, 8-bit registers. id is int value - 16.
12 al, cl, dl, bl, ah, ch, dh, bh,
13
14 /// Returns the bit-width of the register.
15 pub fn size(self: Register) u7 {
16 return switch (@intFromEnum(self)) {
17 0...7 => 32,
18 8...15 => 16,
19 16...23 => 8,
20 else => unreachable,
21 };
22 }
23
24 /// Returns the register's id. This is used in practically every opcode the
25 /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
26 /// instruction, and is used in the R/M byte.
27 pub fn id(self: Register) u3 {
28 return @truncate(@intFromEnum(self));
29 }
30
31 /// Convert from any register to its 32 bit alias.
32 pub fn to32(self: Register) Register {
33 return @enumFromInt(@as(u8, self.id()));
34 }
35
36 /// Convert from any register to its 16 bit alias.
37 pub fn to16(self: Register) Register {
38 return @enumFromInt(@as(u8, self.id()) + 8);
39 }
40
41 /// Convert from any register to its 8 bit alias.
42 pub fn to8(self: Register) Register {
43 return @enumFromInt(@as(u8, self.id()) + 16);
44 }
45
46 pub fn dwarfNum(reg: Register) u8 {
47 return @intFromEnum(reg.to32());
48 }
49};
50
51// zig fmt: on
52
53/// TODO this set is actually a set of caller-saved registers.
54pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
55
56// TODO add these to Register enum and corresponding dwarfNum
57// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.
58// RA = (8, "RA"),
59//
60// ST0 = (11, "st0"),
61// ST1 = (12, "st1"),
62// ST2 = (13, "st2"),
63// ST3 = (14, "st3"),
64// ST4 = (15, "st4"),
65// ST5 = (16, "st5"),
66// ST6 = (17, "st6"),
67// ST7 = (18, "st7"),
68//
69// XMM0 = (21, "xmm0"),
70// XMM1 = (22, "xmm1"),
71// XMM2 = (23, "xmm2"),
72// XMM3 = (24, "xmm3"),
73// XMM4 = (25, "xmm4"),
74// XMM5 = (26, "xmm5"),
75// XMM6 = (27, "xmm6"),
76// XMM7 = (28, "xmm7"),
77//
78// MM0 = (29, "mm0"),
79// MM1 = (30, "mm1"),
80// MM2 = (31, "mm2"),
81// MM3 = (32, "mm3"),
82// MM4 = (33, "mm4"),
83// MM5 = (34, "mm5"),
84// MM6 = (35, "mm6"),
85// MM7 = (36, "mm7"),
86//
87// MXCSR = (39, "mxcsr"),
88//
89// ES = (40, "es"),
90// CS = (41, "cs"),
91// SS = (42, "ss"),
92// DS = (43, "ds"),
93// FS = (44, "fs"),
94// GS = (45, "gs"),
95//
96// TR = (48, "tr"),
97// LDTR = (49, "ldtr"),
98//
99// FS_BASE = (93, "fs.base"),
100// GS_BASE = (94, "gs.base"),
src/arch/x86_64/CodeGen.zig+71-78
......@@ -525,47 +525,47 @@ pub const MCValue = union(enum) {
525525 };
526526 }
527527
528 pub fn format(mcv: MCValue, bw: *Writer, comptime _: []const u8) Writer.Error!void {
528 pub fn format(mcv: MCValue, w: *Writer) Writer.Error!void {
529529 switch (mcv) {
530 .none, .unreach, .dead, .undef => try bw.print("({s})", .{@tagName(mcv)}),
531 .immediate => |pl| try bw.print("0x{x}", .{pl}),
532 .memory => |pl| try bw.print("[ds:0x{x}]", .{pl}),
533 inline .eflags, .register => |pl| try bw.print("{s}", .{@tagName(pl)}),
534 .register_pair => |pl| try bw.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),
535 .register_triple => |pl| try bw.print("{s}:{s}:{s}", .{
530 .none, .unreach, .dead, .undef => try w.print("({s})", .{@tagName(mcv)}),
531 .immediate => |pl| try w.print("0x{x}", .{pl}),
532 .memory => |pl| try w.print("[ds:0x{x}]", .{pl}),
533 inline .eflags, .register => |pl| try w.print("{s}", .{@tagName(pl)}),
534 .register_pair => |pl| try w.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),
535 .register_triple => |pl| try w.print("{s}:{s}:{s}", .{
536536 @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]),
537537 }),
538 .register_quadruple => |pl| try bw.print("{s}:{s}:{s}:{s}", .{
538 .register_quadruple => |pl| try w.print("{s}:{s}:{s}:{s}", .{
539539 @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]),
540540 }),
541 .register_offset => |pl| try bw.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),
542 .register_overflow => |pl| try bw.print("{s}:{s}", .{
541 .register_offset => |pl| try w.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),
542 .register_overflow => |pl| try w.print("{s}:{s}", .{
543543 @tagName(pl.eflags),
544544 @tagName(pl.reg),
545545 }),
546 .register_mask => |pl| try bw.print("mask({s},{f}):{c}{s}", .{
546 .register_mask => |pl| try w.print("mask({s},{f}):{c}{s}", .{
547547 @tagName(pl.info.kind),
548548 pl.info.scalar,
549549 @as(u8, if (pl.info.inverted) '!' else ' '),
550550 @tagName(pl.reg),
551551 }),
552 .indirect => |pl| try bw.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
553 .indirect_load_frame => |pl| try bw.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }),
554 .load_frame => |pl| try bw.print("[{} + 0x{x}]", .{ pl.index, pl.off }),
555 .lea_frame => |pl| try bw.print("{} + 0x{x}", .{ pl.index, pl.off }),
556 .load_nav => |pl| try bw.print("[nav:{d}]", .{@intFromEnum(pl)}),
557 .lea_nav => |pl| try bw.print("nav:{d}", .{@intFromEnum(pl)}),
558 .load_uav => |pl| try bw.print("[uav:{d}]", .{@intFromEnum(pl.val)}),
559 .lea_uav => |pl| try bw.print("uav:{d}", .{@intFromEnum(pl.val)}),
560 .load_lazy_sym => |pl| try bw.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
561 .lea_lazy_sym => |pl| try bw.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
562 .load_extern_func => |pl| try bw.print("[extern:{d}]", .{@intFromEnum(pl)}),
563 .lea_extern_func => |pl| try bw.print("extern:{d}", .{@intFromEnum(pl)}),
564 .elementwise_args => |pl| try bw.print("elementwise:{d}:[{} + 0x{x}]", .{
552 .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
553 .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }),
554 .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }),
555 .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }),
556 .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}),
557 .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}),
558 .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}),
559 .lea_uav => |pl| try w.print("uav:{d}", .{@intFromEnum(pl.val)}),
560 .load_lazy_sym => |pl| try w.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
561 .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
562 .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}),
563 .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}),
564 .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{
565565 pl.regs, pl.frame_index, pl.frame_off,
566566 }),
567 .reserved_frame => |pl| try bw.print("(dead:{})", .{pl}),
568 .air_ref => |pl| try bw.print("(air:0x{x})", .{@intFromEnum(pl)}),
567 .reserved_frame => |pl| try w.print("(dead:{})", .{pl}),
568 .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}),
569569 }
570570 }
571571};
......@@ -812,7 +812,7 @@ const InstTracking = struct {
812812 }
813813 }
814814
815 pub fn format(tracking: InstTracking, bw: *Writer, comptime _: []const u8) Writer.Error!void {
815 pub fn format(tracking: InstTracking, bw: *Writer) Writer.Error!void {
816816 if (!std.meta.eql(tracking.long, tracking.short)) try bw.print("|{f}| ", .{tracking.long});
817817 try bw.print("{f}", .{tracking.short});
818818 }
......@@ -1088,10 +1088,10 @@ const FormatNavData = struct {
10881088 ip: *const InternPool,
10891089 nav_index: InternPool.Nav.Index,
10901090};
1091fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1092 try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});
1091fn formatNav(data: FormatNavData, w: *Writer) Writer.Error!void {
1092 try w.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});
10931093}
1094fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) {
1094fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(FormatNavData, formatNav) {
10951095 return .{ .data = .{
10961096 .ip = ip,
10971097 .nav_index = nav_index,
......@@ -1102,15 +1102,14 @@ const FormatAirData = struct {
11021102 self: *CodeGen,
11031103 inst: Air.Inst.Index,
11041104};
1105fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) Writer.Error!void {
1106 comptime assert(fmt.len == 0);
1107 // not acceptable implementation:
1105fn formatAir(data: FormatAirData, w: *std.io.Writer) Writer.Error!void {
1106 // not acceptable implementation because it ignores `w`:
11081107 //data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness);
11091108 _ = data;
11101109 _ = w;
11111110 @panic("TODO: unimplemented");
11121111}
1113fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) {
1112fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) {
11141113 return .{ .data = .{ .self = self, .inst = inst } };
11151114}
11161115
......@@ -1118,7 +1117,7 @@ const FormatWipMirData = struct {
11181117 self: *CodeGen,
11191118 inst: Mir.Inst.Index,
11201119};
1121fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void {
1120fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void {
11221121 var lower: Lower = .{
11231122 .target = data.self.target,
11241123 .allocator = data.self.gpa,
......@@ -1133,27 +1132,22 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
11331132 lower.err_msg.?.deinit(data.self.gpa);
11341133 lower.err_msg = null;
11351134 }
1136 try bw.writeAll(lower.err_msg.?.msg);
1135 try w.writeAll(lower.err_msg.?.msg);
11371136 return;
11381137 },
1139 error.OutOfMemory, error.InvalidInstruction, error.CannotEncode => |e| {
1140 try bw.writeAll(switch (e) {
1141 error.OutOfMemory => "Out of memory",
1142 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
1143 error.CannotEncode => "CodeGen failed to encode the instruction.",
1144 });
1138 else => |e| {
1139 try w.writeAll(@errorName(e));
11451140 return;
11461141 },
1147 else => |e| return e,
11481142 }).insts) |lowered_inst| {
1149 if (!first) try bw.writeAll("\ndebug(wip_mir): ");
1150 try bw.print(" | {f}", .{lowered_inst});
1143 if (!first) try w.writeAll("\ndebug(wip_mir): ");
1144 try w.print(" | {f}", .{lowered_inst});
11511145 first = false;
11521146 }
11531147 if (first) {
11541148 const ip = &data.self.pt.zcu.intern_pool;
11551149 const mir_inst = lower.mir.instructions.get(data.inst);
1156 try bw.print(" | .{s}", .{@tagName(mir_inst.ops)});
1150 try w.print(" | .{s}", .{@tagName(mir_inst.ops)});
11571151 switch (mir_inst.ops) {
11581152 else => unreachable,
11591153 .pseudo_dbg_prologue_end_none,
......@@ -1165,20 +1159,20 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
11651159 .pseudo_dbg_var_none,
11661160 .pseudo_dead_none,
11671161 => {},
1168 .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try bw.print(
1162 .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try w.print(
11691163 " {[line]d}, {[column]d}",
11701164 mir_inst.data.line_column,
11711165 ),
1172 .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try bw.print(" {f}", .{
1166 .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try w.print(" {f}", .{
11731167 ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip),
11741168 }),
1175 .pseudo_dbg_arg_i_s, .pseudo_dbg_var_i_s => try bw.print(" {d}", .{
1169 .pseudo_dbg_arg_i_s, .pseudo_dbg_var_i_s => try w.print(" {d}", .{
11761170 @as(i32, @bitCast(mir_inst.data.i.i)),
11771171 }),
1178 .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_u => try bw.print(" {d}", .{
1172 .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_u => try w.print(" {d}", .{
11791173 mir_inst.data.i.i,
11801174 }),
1181 .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try bw.print(" {d}", .{
1175 .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try w.print(" {d}", .{
11821176 mir_inst.data.i64,
11831177 }),
11841178 .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => {
......@@ -1186,40 +1180,39 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
11861180 .base = .{ .reg = mir_inst.data.ro.reg },
11871181 .disp = mir_inst.data.ro.off,
11881182 }) };
1189 try bw.print(" {f}", .{mem_op.fmt(.m)});
1183 try w.print(" {f}", .{mem_op.fmt(.m)});
11901184 },
11911185 .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {
11921186 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{
11931187 .base = .{ .frame = mir_inst.data.fa.index },
11941188 .disp = mir_inst.data.fa.off,
11951189 }) };
1196 try bw.print(" {f}", .{mem_op.fmt(.m)});
1190 try w.print(" {f}", .{mem_op.fmt(.m)});
11971191 },
11981192 .pseudo_dbg_arg_m, .pseudo_dbg_var_m => {
11991193 const mem_op: encoder.Instruction.Operand = .{
12001194 .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.x.payload).data.decode(),
12011195 };
1202 try bw.print(" {f}", .{mem_op.fmt(.m)});
1196 try w.print(" {f}", .{mem_op.fmt(.m)});
12031197 },
1204 .pseudo_dbg_arg_val, .pseudo_dbg_var_val => try bw.print(" {}", .{
1198 .pseudo_dbg_arg_val, .pseudo_dbg_var_val => try w.print(" {f}", .{
12051199 Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt),
12061200 }),
12071201 }
12081202 }
12091203}
1210fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) {
1204fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(FormatWipMirData, formatWipMir) {
12111205 return .{ .data = .{ .self = self, .inst = inst } };
12121206}
12131207
12141208const FormatTrackingData = struct {
12151209 self: *CodeGen,
12161210};
1217fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
1218 comptime assert(fmt.len == 0);
1211fn formatTracking(data: FormatTrackingData, w: *Writer) Writer.Error!void {
12191212 var it = data.self.inst_tracking.iterator();
1220 while (it.next()) |entry| try bw.print("\n{f} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* });
1213 while (it.next()) |entry| try w.print("\n{f} = {f}", .{ entry.key_ptr.*, entry.value_ptr.* });
12211214}
1222fn fmtTracking(self: *CodeGen) std.fmt.Formatter(formatTracking) {
1215fn fmtTracking(self: *CodeGen) std.fmt.Formatter(FormatTrackingData, formatTracking) {
12231216 return .{ .data = .{ .self = self } };
12241217}
12251218
......@@ -2033,7 +2026,7 @@ fn gen(
20332026 .{},
20342027 );
20352028 self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } };
2036 tracking_log.debug("spill {f} to {f}", .{ self.ret_mcv.long, frame_index });
2029 tracking_log.debug("spill {f} to {}", .{ self.ret_mcv.long, frame_index });
20372030 },
20382031 else => unreachable,
20392032 }
......@@ -12894,7 +12887,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
1289412887 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ },
1289512888 } },
1289612889 } }) catch |err| switch (err) {
12897 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{
12890 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{
1289812891 @tagName(air_tag),
1289912892 cg.typeOf(bin_op.lhs).fmt(pt),
1290012893 ops[0].tracking(cg),
......@@ -21771,7 +21764,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
2177121764 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ },
2177221765 } },
2177321766 } }) catch |err| switch (err) {
21774 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{
21767 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{
2177521768 @tagName(air_tag),
2177621769 cg.typeOf(bin_op.lhs).fmt(pt),
2177721770 ops[0].tracking(cg),
......@@ -32489,7 +32482,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
3248932482 .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp3q, ._, ._ },
3249032483 } },
3249132484 } }) catch |err| switch (err) {
32492 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{
32485 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{
3249332486 @tagName(air_tag),
3249432487 cg.typeOf(bin_op.lhs).fmt(pt),
3249532488 ops[0].tracking(cg),
......@@ -59317,7 +59310,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
5931759310 .{ ._, ._, .@"or", .tmp4q, .tmp5q, ._, ._ },
5931859311 } },
5931959312 } }) catch |err| switch (err) {
59320 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{
59313 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{
5932159314 @tagName(air_tag),
5932259315 ty_pl.ty.toType().fmt(pt),
5932359316 ops[0].tracking(cg),
......@@ -60816,7 +60809,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
6081660809 .{ ._, ._, .@"or", .dst0d, .tmp0d, ._, ._ },
6081760810 } },
6081860811 } }) catch |err| switch (err) {
60819 error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{
60812 error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{
6082060813 @tagName(air_tag),
6082160814 cg.typeOf(bin_op.rhs).fmt(pt),
6082260815 ops[1].tracking(cg),
......@@ -64073,7 +64066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
6407364066 .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp1q, ._, ._ },
6407464067 } },
6407564068 } }) catch |err| switch (err) {
64076 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{
64069 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f}", .{
6407764070 @tagName(air_tag),
6407864071 lhs_ty.fmt(pt),
6407964072 ops[0].tracking(cg),
......@@ -79435,7 +79428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
7943579428 .@"struct", .@"union" => {
7943679429 assert(ty.containerLayout(zcu) == .@"packed");
7943779430 for (&ops) |*op| op.wrapInt(cg) catch |err| switch (err) {
79438 error.SelectFailed => return cg.fail("failed to select {s} wrap {} {}", .{
79431 error.SelectFailed => return cg.fail("failed to select {s} wrap {f} {f}", .{
7943979432 @tagName(air_tag),
7944079433 ty.fmt(pt),
7944179434 op.tracking(cg),
......@@ -86528,7 +86521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
8652886521 } },
8652986522 }),
8653086523 }) catch |err| switch (err) {
86531 error.SelectFailed => return cg.fail("failed to select {s} {s} {} {} {}", .{
86524 error.SelectFailed => return cg.fail("failed to select {s} {s} {f} {f} {f}", .{
8653286525 @tagName(air_tag),
8653386526 @tagName(vector_cmp.compareOperator()),
8653486527 cg.typeOf(vector_cmp.lhs).fmt(pt),
......@@ -157193,7 +157186,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
157193157186 } },
157194157187 } },
157195157188 }) catch |err| switch (err) {
157196 error.SelectFailed => return cg.fail("failed to select {s}.{s} {} {}", .{
157189 error.SelectFailed => return cg.fail("failed to select {s}.{s} {f} {f}", .{
157197157190 @tagName(air_tag),
157198157191 @tagName(reduce.operation),
157199157192 cg.typeOf(reduce.operand).fmt(pt),
......@@ -157204,7 +157197,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
157204157197 switch (reduce.operation) {
157205157198 .And, .Or, .Xor, .Min, .Max => {},
157206157199 .Add, .Mul => if (cg.intInfo(res_ty)) |_| res[0].wrapInt(cg) catch |err| switch (err) {
157207 error.SelectFailed => return cg.fail("failed to select {s}.{s} wrap {} {}", .{
157200 error.SelectFailed => return cg.fail("failed to select {s}.{s} wrap {f} {f}", .{
157208157201 @tagName(air_tag),
157209157202 @tagName(reduce.operation),
157210157203 res_ty.fmt(pt),
......@@ -164487,7 +164480,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
164487164480 } },
164488164481 } },
164489164482 }) catch |err| switch (err) {
164490 error.SelectFailed => return cg.fail("failed to select {s}.{s} {} {}", .{
164483 error.SelectFailed => return cg.fail("failed to select {s}.{s} {f} {f}", .{
164491164484 @tagName(air_tag),
164492164485 @tagName(reduce.operation),
164493164486 cg.typeOf(reduce.operand).fmt(pt),
......@@ -166284,7 +166277,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
166284166277 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ },
166285166278 } },
166286166279 } }) catch |err| switch (err) {
166287 error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{
166280 error.SelectFailed => return cg.fail("failed to select {s} {f} {f}", .{
166288166281 @tagName(air_tag),
166289166282 ty_op.ty.toType().fmt(pt),
166290166283 ops[0].tracking(cg),
......@@ -166300,7 +166293,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
166300166293 const bin_op = air_datas[@intFromEnum(inst)].bin_op;
166301166294 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }) ++ .{undefined};
166302166295 ops[2] = ops[0].getByteLen(cg) catch |err| switch (err) {
166303 error.SelectFailed => return cg.fail("failed to select {s} {} {} {} {}", .{
166296 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f} {f}", .{
166304166297 @tagName(air_tag),
166305166298 cg.typeOf(bin_op.lhs).fmt(pt),
166306166299 cg.typeOf(bin_op.rhs).fmt(pt),
......@@ -166340,7 +166333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
166340166333 } },
166341166334 }},
166342166335 }) catch |err| switch (err) {
166343 error.SelectFailed => return cg.fail("failed to select {s} {} {} {} {} {}", .{
166336 error.SelectFailed => return cg.fail("failed to select {s} {f} {f} {f} {f} {f}", .{
166344166337 @tagName(air_tag),
166345166338 cg.typeOf(bin_op.lhs).fmt(pt),
166346166339 cg.typeOf(bin_op.rhs).fmt(pt),
......@@ -181509,7 +181502,7 @@ fn genSetReg(
181509181502 assert(!ty.optionalReprIsPayload(zcu));
181510181503 break :first_ty opt_child;
181511181504 },
181512 else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, ty.fmt(pt) }),
181505 else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, ty.fmt(pt) }),
181513181506 });
181514181507 const first_size: u31 = @intCast(first_ty.abiSize(zcu));
181515181508 const frame_size = std.math.ceilPowerOfTwoAssert(u32, abi_size);
......@@ -186937,7 +186930,7 @@ const Temp = struct {
186937186930 assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable);
186938186931 break :part_ty .u64;
186939186932 } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) {
186940 else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
186933 else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
186941186934 .ptr_type => |ptr_info| {
186942186935 assert(ptr_info.flags.size == .slice);
186943186936 assert(src_regs.len == 2);
......@@ -186948,7 +186941,7 @@ const Temp = struct {
186948186941 break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_abi_size, 8));
186949186942 },
186950186943 .opt_type => |opt_child| switch (ip.indexToKey(opt_child)) {
186951 else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
186944 else => std.debug.panic("{s}: {f}\n", .{ @src().fn_name, src_ty.fmt(cg.pt) }),
186952186945 .ptr_type => |ptr_info| {
186953186946 assert(ptr_info.flags.size == .slice);
186954186947 assert(src_regs.len == 2);
src/arch/x86_64/Emit.zig+8-1
......@@ -707,7 +707,14 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
707707 const comp = emit.bin_file.comp;
708708 const gpa = comp.gpa;
709709 const start_offset: u32 = @intCast(emit.code.items.len);
710 try lowered_inst.encode(emit.code.writer(gpa), .{});
710 {
711 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, emit.code);
712 defer emit.code.* = aw.toArrayList();
713 lowered_inst.encode(&aw.writer, .{}) catch |err| switch (err) {
714 error.WriteFailed => return error.OutOfMemory,
715 else => |e| return e,
716 };
717 }
711718 const end_offset: u32 = @intCast(emit.code.items.len);
712719 for (reloc_info) |reloc| switch (reloc.target.type) {
713720 .inst => {
src/arch/x86_64/Encoding.zig+31-25
......@@ -159,14 +159,12 @@ pub fn modRmExt(encoding: Encoding) u3 {
159159 };
160160}
161161
162pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
163 comptime assert(fmt.len == 0);
164
162pub fn format(encoding: Encoding, writer: *std.io.Writer) std.io.Writer.Error!void {
165163 var opc = encoding.opcode();
166164 if (encoding.data.mode.isVex()) {
167 try bw.writeAll("VEX.");
165 try writer.writeAll("VEX.");
168166
169 try bw.writeAll(switch (encoding.data.mode) {
167 try writer.writeAll(switch (encoding.data.mode) {
170168 .vex_128_w0, .vex_128_w1, .vex_128_wig => "128",
171169 .vex_256_w0, .vex_256_w1, .vex_256_wig => "256",
172170 .vex_lig_w0, .vex_lig_w1, .vex_lig_wig => "LIG",
......@@ -177,25 +175,25 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
177175 switch (opc[0]) {
178176 else => {},
179177 0x66, 0xf3, 0xf2 => {
180 try bw.print(".{X:0>2}", .{opc[0]});
178 try writer.print(".{X:0>2}", .{opc[0]});
181179 opc = opc[1..];
182180 },
183181 }
184182
185 try bw.print(".{X}", .{opc[0 .. opc.len - 1]});
183 try writer.print(".{X}", .{opc[0 .. opc.len - 1]});
186184 opc = opc[opc.len - 1 ..];
187185
188 try bw.writeAll(".W");
189 try bw.writeAll(switch (encoding.data.mode) {
186 try writer.writeAll(".W");
187 try writer.writeAll(switch (encoding.data.mode) {
190188 .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0",
191189 .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1",
192190 .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG",
193191 else => unreachable,
194192 });
195193
196 try bw.writeByte(' ');
197 } else if (encoding.data.mode.isLong()) try bw.writeAll("REX.W + ");
198 for (opc) |byte| try bw.print("{x:0>2} ", .{byte});
194 try writer.writeByte(' ');
195 } else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + ");
196 for (opc) |byte| try writer.print("{x:0>2} ", .{byte});
199197
200198 switch (encoding.data.op_en) {
201199 .z, .fd, .td, .i, .zi, .ii, .d => {},
......@@ -212,10 +210,10 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
212210 .r64 => "rd",
213211 else => unreachable,
214212 };
215 try bw.print("+{s} ", .{tag});
213 try writer.print("+{s} ", .{tag});
216214 },
217 .ia, .m, .mi, .m1, .mc, .vm, .vmi => try bw.print("/{d} ", .{encoding.modRmExt()}),
218 .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try bw.writeAll("/r "),
215 .ia, .m, .mi, .m1, .mc, .vm, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
216 .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "),
219217 }
220218
221219 switch (encoding.data.op_en) {
......@@ -244,24 +242,24 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
244242 .rel32 => "cd",
245243 else => unreachable,
246244 };
247 try bw.print("{s} ", .{tag});
245 try writer.print("{s} ", .{tag});
248246 },
249 .rvmr => try bw.writeAll("/is4 "),
247 .rvmr => try writer.writeAll("/is4 "),
250248 .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {},
251249 }
252250
253 try bw.print("{s} ", .{@tagName(encoding.mnemonic)});
251 try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
254252
255253 for (encoding.data.ops) |op| switch (op) {
256254 .none => break,
257 else => try bw.print("{s} ", .{@tagName(op)}),
255 else => try writer.print("{s} ", .{@tagName(op)}),
258256 };
259257
260258 const op_en = switch (encoding.data.op_en) {
261259 .zi => .i,
262260 else => |op_en| op_en,
263261 };
264 try bw.print("{s}", .{@tagName(op_en)});
262 try writer.print("{s}", .{@tagName(op_en)});
265263}
266264
267265pub const Mnemonic = enum {
......@@ -1016,13 +1014,21 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
10161014 };
10171015 @memcpy(inst.ops[0..ops.len], ops);
10181016
1019 var buf: [15]u8 = undefined;
1020 var bw: Writer = .fixed(&buf);
1021 inst.encode(&bw, .{
1017 // By using a buffer with maximum length of encoded instruction, we can use
1018 // the `end` field of the Writer for the count.
1019 var buf: [16]u8 = undefined;
1020 var trash: std.io.Writer.Discarding = .init(&buf);
1021 inst.encode(&trash.writer, .{
10221022 .allow_frame_locs = true,
10231023 .allow_symbols = true,
1024 }) catch unreachable;
1025 return @intCast(bw.end);
1024 }) catch {
1025 // Since the function signature for encode() does not mention under what
1026 // conditions it can fail, I have changed `unreachable` to `@panic` here.
1027 // This is a TODO item since it indicates this function
1028 // (`estimateInstructionLength`) has the wrong function signature.
1029 @panic("unexpected failure to encode");
1030 };
1031 return trash.writer.end;
10261032}
10271033
10281034const mnemonic_to_encodings_map = init: {
src/arch/x86_64/bits.zig+4-19
......@@ -729,15 +729,6 @@ pub const FrameIndex = enum(u32) {
729729 pub fn isNamed(fi: FrameIndex) bool {
730730 return @intFromEnum(fi) < named_count;
731731 }
732
733 pub fn format(fi: FrameIndex, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
734 comptime assert(fmt.len == 0);
735 try bw.writeAll("FrameIndex");
736 if (fi.isNamed())
737 try bw.print(".{s}", .{@tagName(fi)})
738 else
739 try bw.print("({d})", .{@intFromEnum(fi)});
740 }
741732};
742733
743734pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
......@@ -838,14 +829,13 @@ pub const Memory = struct {
838829 };
839830 }
840831
841 pub fn format(s: Size, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
842 comptime assert(fmt.len == 0);
832 pub fn format(s: Size, writer: *std.io.Writer) std.io.Writer.Error!void {
843833 if (s == .none) return;
844 try bw.writeAll(@tagName(s));
834 try writer.writeAll(@tagName(s));
845835 switch (s) {
846836 .none => unreachable,
847837 .ptr, .gpr => {},
848 else => try bw.writeAll(" ptr"),
838 else => try writer.writeAll(" ptr"),
849839 }
850840 }
851841 };
......@@ -901,12 +891,7 @@ pub const Immediate = union(enum) {
901891 return .{ .signed = x };
902892 }
903893
904 pub fn format(
905 imm: Immediate,
906 comptime _: []const u8,
907 _: std.fmt.FormatOptions,
908 writer: anytype,
909 ) @TypeOf(writer).Error!void {
894 pub fn format(imm: Immediate, writer: *std.io.Writer) std.io.Writer.Error!void {
910895 switch (imm) {
911896 inline else => |int| try writer.print("{d}", .{int}),
912897 .nav => |nav_off| try writer.print("Nav({d}) + {d}", .{ @intFromEnum(nav_off.nav), nav_off.off }),
src/arch/x86_64/encoder.zig+1-2
......@@ -353,8 +353,7 @@ pub const Instruction = struct {
353353 return inst;
354354 }
355355
356 pub fn format(inst: Instruction, w: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {
357 comptime assert(unused_format_string.len == 0);
356 pub fn format(inst: Instruction, w: *Writer) Writer.Error!void {
358357 switch (inst.prefix) {
359358 .none, .directive => {},
360359 else => try w.print("{s} ", .{@tagName(inst.prefix)}),
src/codegen.zig+9-28
......@@ -35,7 +35,7 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature {
3535 .stage2_arm => .arm_backend,
3636 .stage2_c => .c_backend,
3737 .stage2_llvm => .llvm_backend,
38 .stage2_powerpc => .powerpc_backend,
38 .stage2_powerpc => unreachable,
3939 .stage2_riscv64 => .riscv64_backend,
4040 .stage2_sparc64 => .sparc64_backend,
4141 .stage2_spirv => .spirv_backend,
......@@ -49,11 +49,11 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature {
4949fn importBackend(comptime backend: std.builtin.CompilerBackend) type {
5050 return switch (backend) {
5151 .other, .stage1 => unreachable,
52 .stage2_aarch64 => @import("arch/aarch64/CodeGen.zig"),
53 .stage2_arm => @import("arch/arm/CodeGen.zig"),
52 .stage2_aarch64 => unreachable,
53 .stage2_arm => unreachable,
5454 .stage2_c => @import("codegen/c.zig"),
5555 .stage2_llvm => @import("codegen/llvm.zig"),
56 .stage2_powerpc => @import("arch/powerpc/CodeGen.zig"),
56 .stage2_powerpc => unreachable,
5757 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),
5858 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),
5959 .stage2_spirv => @import("codegen/spirv.zig"),
......@@ -71,14 +71,11 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co
7171 inline .stage2_llvm,
7272 .stage2_c,
7373 .stage2_wasm,
74 .stage2_arm,
7574 .stage2_x86_64,
76 .stage2_aarch64,
7775 .stage2_x86,
7876 .stage2_riscv64,
7977 .stage2_sparc64,
8078 .stage2_spirv,
81 .stage2_powerpc,
8279 => |backend| {
8380 dev.check(devFeatureForBackend(backend));
8481 return importBackend(backend).legalizeFeatures(target);
......@@ -90,9 +87,6 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co
9087/// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a
9188/// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`.
9289pub const AnyMir = union {
93 aarch64: @import("arch/aarch64/Mir.zig"),
94 arm: @import("arch/arm/Mir.zig"),
95 powerpc: noreturn, //@import("arch/powerpc/Mir.zig"),
9690 riscv64: @import("arch/riscv64/Mir.zig"),
9791 sparc64: @import("arch/sparc64/Mir.zig"),
9892 x86_64: @import("arch/x86_64/Mir.zig"),
......@@ -103,7 +97,6 @@ pub const AnyMir = union {
10397 return switch (backend) {
10498 .stage2_aarch64 => "aarch64",
10599 .stage2_arm => "arm",
106 .stage2_powerpc => "powerpc",
107100 .stage2_riscv64 => "riscv64",
108101 .stage2_sparc64 => "sparc64",
109102 .stage2_x86_64 => "x86_64",
......@@ -118,10 +111,7 @@ pub const AnyMir = union {
118111 const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm);
119112 switch (backend) {
120113 else => unreachable,
121 inline .stage2_aarch64,
122 .stage2_arm,
123 .stage2_powerpc,
124 .stage2_riscv64,
114 inline .stage2_riscv64,
125115 .stage2_sparc64,
126116 .stage2_x86_64,
127117 .stage2_wasm,
......@@ -149,10 +139,7 @@ pub fn generateFunction(
149139 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;
150140 switch (target_util.zigBackend(target, false)) {
151141 else => unreachable,
152 inline .stage2_aarch64,
153 .stage2_arm,
154 .stage2_powerpc,
155 .stage2_riscv64,
142 inline .stage2_riscv64,
156143 .stage2_sparc64,
157144 .stage2_x86_64,
158145 .stage2_wasm,
......@@ -187,10 +174,7 @@ pub fn emitFunction(
187174 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;
188175 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) {
189176 else => unreachable,
190 inline .stage2_aarch64,
191 .stage2_arm,
192 .stage2_powerpc,
193 .stage2_riscv64,
177 inline .stage2_riscv64,
194178 .stage2_sparc64,
195179 .stage2_x86_64,
196180 => |backend| {
......@@ -216,10 +200,7 @@ pub fn generateLazyFunction(
216200 zcu.getTarget();
217201 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) {
218202 else => unreachable,
219 inline .stage2_powerpc,
220 .stage2_riscv64,
221 .stage2_x86_64,
222 => |backend| {
203 inline .stage2_riscv64, .stage2_x86_64 => |backend| {
223204 dev.check(devFeatureForBackend(backend));
224205 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
225206 },
......@@ -910,7 +891,7 @@ pub fn genNavRef(
910891 const zcu = pt.zcu;
911892 const ip = &zcu.intern_pool;
912893 const nav = ip.getNav(nav_index);
913 log.debug("genNavRef({})", .{nav.fqn.fmt(ip)});
894 log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)});
914895
915896 const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e|
916897 .{ e.lib_name, e.linkage, e.is_threadlocal and zcu.comp.config.any_non_single_threaded }
src/codegen/aarch64/abi.zig created+150
......@@ -0,0 +1,150 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const bits = @import("../../arch/aarch64/bits.zig");
4const Register = bits.Register;
5const Type = @import("../../Type.zig");
6const Zcu = @import("../../Zcu.zig");
7
8pub const Class = union(enum) {
9 memory,
10 byval,
11 integer,
12 double_integer,
13 float_array: u8,
14};
15
16/// For `float_array` the second element will be the amount of floats.
17pub fn classifyType(ty: Type, zcu: *Zcu) Class {
18 std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
19
20 var maybe_float_bits: ?u16 = null;
21 switch (ty.zigTypeTag(zcu)) {
22 .@"struct" => {
23 if (ty.containerLayout(zcu) == .@"packed") return .byval;
24 const float_count = countFloats(ty, zcu, &maybe_float_bits);
25 if (float_count <= sret_float_count) return .{ .float_array = float_count };
26
27 const bit_size = ty.bitSize(zcu);
28 if (bit_size > 128) return .memory;
29 if (bit_size > 64) return .double_integer;
30 return .integer;
31 },
32 .@"union" => {
33 if (ty.containerLayout(zcu) == .@"packed") return .byval;
34 const float_count = countFloats(ty, zcu, &maybe_float_bits);
35 if (float_count <= sret_float_count) return .{ .float_array = float_count };
36
37 const bit_size = ty.bitSize(zcu);
38 if (bit_size > 128) return .memory;
39 if (bit_size > 64) return .double_integer;
40 return .integer;
41 },
42 .int, .@"enum", .error_set, .float, .bool => return .byval,
43 .vector => {
44 const bit_size = ty.bitSize(zcu);
45 // TODO is this controlled by a cpu feature?
46 if (bit_size > 128) return .memory;
47 return .byval;
48 },
49 .optional => {
50 std.debug.assert(ty.isPtrLikeOptional(zcu));
51 return .byval;
52 },
53 .pointer => {
54 std.debug.assert(!ty.isSlice(zcu));
55 return .byval;
56 },
57 .error_union,
58 .frame,
59 .@"anyframe",
60 .noreturn,
61 .void,
62 .type,
63 .comptime_float,
64 .comptime_int,
65 .undefined,
66 .null,
67 .@"fn",
68 .@"opaque",
69 .enum_literal,
70 .array,
71 => unreachable,
72 }
73}
74
75const sret_float_count = 4;
76fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u8 {
77 const ip = &zcu.intern_pool;
78 const target = zcu.getTarget();
79 const invalid = std.math.maxInt(u8);
80 switch (ty.zigTypeTag(zcu)) {
81 .@"union" => {
82 const union_obj = zcu.typeToUnion(ty).?;
83 var max_count: u8 = 0;
84 for (union_obj.field_types.get(ip)) |field_ty| {
85 const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits);
86 if (field_count == invalid) return invalid;
87 if (field_count > max_count) max_count = field_count;
88 if (max_count > sret_float_count) return invalid;
89 }
90 return max_count;
91 },
92 .@"struct" => {
93 const fields_len = ty.structFieldCount(zcu);
94 var count: u8 = 0;
95 var i: u32 = 0;
96 while (i < fields_len) : (i += 1) {
97 const field_ty = ty.fieldType(i, zcu);
98 const field_count = countFloats(field_ty, zcu, maybe_float_bits);
99 if (field_count == invalid) return invalid;
100 count += field_count;
101 if (count > sret_float_count) return invalid;
102 }
103 return count;
104 },
105 .float => {
106 const float_bits = maybe_float_bits.* orelse {
107 maybe_float_bits.* = ty.floatBits(target);
108 return 1;
109 };
110 if (ty.floatBits(target) == float_bits) return 1;
111 return invalid;
112 },
113 .void => return 0,
114 else => return invalid,
115 }
116}
117
118pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type {
119 const ip = &zcu.intern_pool;
120 switch (ty.zigTypeTag(zcu)) {
121 .@"union" => {
122 const union_obj = zcu.typeToUnion(ty).?;
123 for (union_obj.field_types.get(ip)) |field_ty| {
124 if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some;
125 }
126 return null;
127 },
128 .@"struct" => {
129 const fields_len = ty.structFieldCount(zcu);
130 var i: u32 = 0;
131 while (i < fields_len) : (i += 1) {
132 const field_ty = ty.fieldType(i, zcu);
133 if (getFloatArrayType(field_ty, zcu)) |some| return some;
134 }
135 return null;
136 },
137 .float => return ty,
138 else => return null,
139 }
140}
141
142pub const callee_preserved_regs = [_]Register{
143 .x19, .x20, .x21, .x22, .x23,
144 .x24, .x25, .x26, .x27, .x28,
145};
146
147pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
148pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
149
150const allocatable_registers = callee_preserved_regs;
src/codegen/arm/abi.zig created+163
......@@ -0,0 +1,163 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
4const Type = @import("../../Type.zig");
5const Zcu = @import("../../Zcu.zig");
6
7pub const Class = union(enum) {
8 memory,
9 byval,
10 i32_array: u8,
11 i64_array: u8,
12
13 fn arrSize(total_size: u64, arr_size: u64) Class {
14 const count = @as(u8, @intCast(std.mem.alignForward(u64, total_size, arr_size) / arr_size));
15 if (arr_size == 32) {
16 return .{ .i32_array = count };
17 } else {
18 return .{ .i64_array = count };
19 }
20 }
21};
22
23pub const Context = enum { ret, arg };
24
25pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
26 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
27
28 var maybe_float_bits: ?u16 = null;
29 const max_byval_size = 512;
30 const ip = &zcu.intern_pool;
31 switch (ty.zigTypeTag(zcu)) {
32 .@"struct" => {
33 const bit_size = ty.bitSize(zcu);
34 if (ty.containerLayout(zcu) == .@"packed") {
35 if (bit_size > 64) return .memory;
36 return .byval;
37 }
38 if (bit_size > max_byval_size) return .memory;
39 const float_count = countFloats(ty, zcu, &maybe_float_bits);
40 if (float_count <= byval_float_count) return .byval;
41
42 const fields = ty.structFieldCount(zcu);
43 var i: u32 = 0;
44 while (i < fields) : (i += 1) {
45 const field_ty = ty.fieldType(i, zcu);
46 const field_alignment = ty.fieldAlignment(i, zcu);
47 const field_size = field_ty.bitSize(zcu);
48 if (field_size > 32 or field_alignment.compare(.gt, .@"32")) {
49 return Class.arrSize(bit_size, 64);
50 }
51 }
52 return Class.arrSize(bit_size, 32);
53 },
54 .@"union" => {
55 const bit_size = ty.bitSize(zcu);
56 const union_obj = zcu.typeToUnion(ty).?;
57 if (union_obj.flagsUnordered(ip).layout == .@"packed") {
58 if (bit_size > 64) return .memory;
59 return .byval;
60 }
61 if (bit_size > max_byval_size) return .memory;
62 const float_count = countFloats(ty, zcu, &maybe_float_bits);
63 if (float_count <= byval_float_count) return .byval;
64
65 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
66 if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or
67 ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32"))
68 {
69 return Class.arrSize(bit_size, 64);
70 }
71 }
72 return Class.arrSize(bit_size, 32);
73 },
74 .bool, .float => return .byval,
75 .int => {
76 // TODO this is incorrect for _BitInt(128) but implementing
77 // this correctly makes implementing compiler-rt impossible.
78 // const bit_size = ty.bitSize(zcu);
79 // if (bit_size > 64) return .memory;
80 return .byval;
81 },
82 .@"enum", .error_set => {
83 const bit_size = ty.bitSize(zcu);
84 if (bit_size > 64) return .memory;
85 return .byval;
86 },
87 .vector => {
88 const bit_size = ty.bitSize(zcu);
89 // TODO is this controlled by a cpu feature?
90 if (ctx == .ret and bit_size > 128) return .memory;
91 if (bit_size > 512) return .memory;
92 return .byval;
93 },
94 .optional => {
95 assert(ty.isPtrLikeOptional(zcu));
96 return .byval;
97 },
98 .pointer => {
99 assert(!ty.isSlice(zcu));
100 return .byval;
101 },
102 .error_union,
103 .frame,
104 .@"anyframe",
105 .noreturn,
106 .void,
107 .type,
108 .comptime_float,
109 .comptime_int,
110 .undefined,
111 .null,
112 .@"fn",
113 .@"opaque",
114 .enum_literal,
115 .array,
116 => unreachable,
117 }
118}
119
120const byval_float_count = 4;
121fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 {
122 const ip = &zcu.intern_pool;
123 const target = zcu.getTarget();
124 const invalid = std.math.maxInt(u32);
125 switch (ty.zigTypeTag(zcu)) {
126 .@"union" => {
127 const union_obj = zcu.typeToUnion(ty).?;
128 var max_count: u32 = 0;
129 for (union_obj.field_types.get(ip)) |field_ty| {
130 const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits);
131 if (field_count == invalid) return invalid;
132 if (field_count > max_count) max_count = field_count;
133 if (max_count > byval_float_count) return invalid;
134 }
135 return max_count;
136 },
137 .@"struct" => {
138 const fields_len = ty.structFieldCount(zcu);
139 var count: u32 = 0;
140 var i: u32 = 0;
141 while (i < fields_len) : (i += 1) {
142 const field_ty = ty.fieldType(i, zcu);
143 const field_count = countFloats(field_ty, zcu, maybe_float_bits);
144 if (field_count == invalid) return invalid;
145 count += field_count;
146 if (count > byval_float_count) return invalid;
147 }
148 return count;
149 },
150 .float => {
151 const float_bits = maybe_float_bits.* orelse {
152 const float_bits = ty.floatBits(target);
153 if (float_bits != 32 and float_bits != 64) return invalid;
154 maybe_float_bits.* = float_bits;
155 return 1;
156 };
157 if (ty.floatBits(target) == float_bits) return 1;
158 return invalid;
159 },
160 .void => return 0,
161 else => return invalid,
162 }
163}
src/codegen/c.zig+341-305
......@@ -345,12 +345,15 @@ fn isReservedIdent(ident: []const u8) bool {
345345 } else return reserved_idents.has(ident);
346346}
347347
348fn formatIdent(
349 ident: []const u8,
350 w: *Writer,
351 comptime fmt_str: []const u8,
352) Writer.Error!void {
353 const solo = fmt_str.len != 0 and fmt_str[0] == ' '; // space means solo; not part of a bigger ident.
348fn formatIdentSolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
349 return formatIdentOptions(ident, w, true);
350}
351
352fn formatIdentUnsolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
353 return formatIdentOptions(ident, w, false);
354}
355
356fn formatIdentOptions(ident: []const u8, w: *std.io.Writer, solo: bool) std.io.Writer.Error!void {
354357 if (solo and isReservedIdent(ident)) {
355358 try w.writeAll("zig_e_");
356359 }
......@@ -367,29 +370,36 @@ fn formatIdent(
367370 }
368371 }
369372}
370pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
373
374pub fn fmtIdentSolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentSolo) {
375 return .{ .data = ident };
376}
377
378pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentUnsolo) {
371379 return .{ .data = ident };
372380}
373381
374382const CTypePoolStringFormatData = struct {
375383 ctype_pool_string: CType.Pool.String,
376384 ctype_pool: *const CType.Pool,
385 solo: bool,
377386};
378fn formatCTypePoolString(
379 data: CTypePoolStringFormatData,
380 w: *Writer,
381 comptime fmt_str: []const u8,
382) Writer.Error!void {
387fn formatCTypePoolString(data: CTypePoolStringFormatData, w: *std.io.Writer) std.io.Writer.Error!void {
383388 if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice|
384 try formatIdent(slice, w, fmt_str)
389 try formatIdentOptions(slice, w, data.solo)
385390 else
386391 try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)});
387392}
388393pub fn fmtCTypePoolString(
389394 ctype_pool_string: CType.Pool.String,
390395 ctype_pool: *const CType.Pool,
391) std.fmt.Formatter(formatCTypePoolString) {
392 return .{ .data = .{ .ctype_pool_string = ctype_pool_string, .ctype_pool = ctype_pool } };
396 solo: bool,
397) std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString) {
398 return .{ .data = .{
399 .ctype_pool_string = ctype_pool_string,
400 .ctype_pool = ctype_pool,
401 .solo = solo,
402 } };
393403}
394404
395405// Returns true if `formatIdent` would make any edits to ident.
......@@ -443,7 +453,7 @@ pub const Function = struct {
443453 const ty = f.typeOf(ref);
444454
445455 const result: CValue = if (lowersToArray(ty, pt)) result: {
446 const ch = &f.object.code_header.buffered_writer;
456 const ch = &f.object.code_header.writer;
447457 const decl_c_value = try f.allocLocalValue(.{
448458 .ctype = try f.ctypeFromType(ty, .complete),
449459 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)),
......@@ -599,8 +609,12 @@ pub const Function = struct {
599609 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);
600610 }
601611
602 fn fmtIntLiteral(f: *Function, val: Value) !std.fmt.Formatter(formatIntLiteral) {
603 return f.object.dg.fmtIntLiteral(val, .Other);
612 fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
613 return f.object.dg.fmtIntLiteralDec(val, .Other);
614 }
615
616 fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
617 return f.object.dg.fmtIntLiteralHex(val, .Other);
604618 }
605619
606620 fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 {
......@@ -619,14 +633,14 @@ pub const Function = struct {
619633 .tag_name,
620634 => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
621635 @tagName(key),
622 fmtIdent(ip.loadEnumType(enum_ty).name.toSlice(ip)),
636 fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)),
623637 @intFromEnum(enum_ty),
624638 }),
625639 .never_tail,
626640 .never_inline,
627641 => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
628642 @tagName(key),
629 fmtIdent(ip.getNav(owner_nav).name.toSlice(ip)),
643 fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)),
630644 @intFromEnum(owner_nav),
631645 }),
632646 },
......@@ -662,7 +676,7 @@ pub const Function = struct {
662676 },
663677 else => {},
664678 }
665 const w = &f.object.code.buffered_writer;
679 const w = &f.object.code.writer;
666680 const a = try Assignment.start(f, w, ctype);
667681 try f.writeCValue(w, dst, .Other);
668682 try a.assign(f, w);
......@@ -704,7 +718,7 @@ pub const Object = struct {
704718 const indent_char = ' ';
705719
706720 fn newline(o: *Object) !void {
707 const w = &o.code.buffered_writer;
721 const w = &o.code.writer;
708722 try w.writeByte('\n');
709723 try w.splatByteAll(indent_char, o.indent_counter);
710724 }
......@@ -716,9 +730,9 @@ pub const Object = struct {
716730 const written = o.code.getWritten();
717731 switch (written[written.len - 1]) {
718732 indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width),
719 '\n' => try o.code.buffered_writer.splatByteAll(indent_char, o.indent_counter),
733 '\n' => try o.code.writer.splatByteAll(indent_char, o.indent_counter),
720734 else => {
721 std.debug.print("\"{f}\"\n", .{std.zig.fmtEscapes(written[written.len -| 100..])});
735 std.debug.print("\"{f}\"\n", .{std.zig.fmtString(written[written.len -| 100..])});
722736 unreachable;
723737 },
724738 }
......@@ -884,7 +898,7 @@ pub const DeclGen = struct {
884898 const addr_val = try pt.intValue(.usize, int.addr);
885899 try w.writeByte('(');
886900 try dg.renderCType(w, ptr_ctype);
887 try w.print("){fx}", .{try dg.fmtIntLiteral(addr_val, .Other)});
901 try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .Other)});
888902 },
889903
890904 .nav_ptr => |nav| try dg.renderNav(w, nav, location),
......@@ -924,7 +938,7 @@ pub const DeclGen = struct {
924938 const offset_val = try pt.intValue(.usize, byte_offset);
925939 try w.writeAll("((char *)");
926940 try dg.renderPointer(w, field.parent.*, location);
927 try w.print(" + {f})", .{try dg.fmtIntLiteral(offset_val, .Other)});
941 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)});
928942 },
929943 }
930944 },
......@@ -946,7 +960,7 @@ pub const DeclGen = struct {
946960 // The pointer already has an appropriate type - just do the arithmetic.
947961 try w.writeByte('(');
948962 try dg.renderPointer(w, elem.parent.*, location);
949 try w.print(" + {f})", .{try dg.fmtIntLiteral(index_val, .Other)});
963 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
950964 } else {
951965 // We probably have an array pointer `T (*)[n]`. Cast to an element pointer,
952966 // and *then* apply the index.
......@@ -954,7 +968,7 @@ pub const DeclGen = struct {
954968 try dg.renderCType(w, result_ctype);
955969 try w.writeByte(')');
956970 try dg.renderPointer(w, elem.parent.*, location);
957 try w.print(" + {f})", .{try dg.fmtIntLiteral(index_val, .Other)});
971 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
958972 }
959973 },
960974
......@@ -969,14 +983,14 @@ pub const DeclGen = struct {
969983 const offset_val = try pt.intValue(.usize, oac.byte_offset);
970984 try w.writeAll("((char *)");
971985 try dg.renderPointer(w, oac.parent.*, location);
972 try w.print(" + {f})", .{try dg.fmtIntLiteral(offset_val, .Other)});
986 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)});
973987 }
974988 },
975989 }
976990 }
977991
978992 fn renderErrorName(dg: *DeclGen, w: *Writer, err_name: InternPool.NullTerminatedString) !void {
979 try w.print("zig_error_{f}", .{fmtIdent(err_name.toSlice(&dg.pt.zcu.intern_pool))});
993 try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name.toSlice(&dg.pt.zcu.intern_pool))});
980994 }
981995
982996 fn renderValue(
......@@ -1040,11 +1054,11 @@ pub const DeclGen = struct {
10401054 .empty_enum_value,
10411055 => unreachable, // non-runtime values
10421056 .int => |int| switch (int.storage) {
1043 .u64, .i64, .big_int => try w.print("{f}", .{try dg.fmtIntLiteral(val, location)}),
1057 .u64, .i64, .big_int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}),
10441058 .lazy_align, .lazy_size => {
10451059 try w.writeAll("((");
10461060 try dg.renderCType(w, ctype);
1047 try w.print("){fx})", .{try dg.fmtIntLiteral(
1061 try w.print("){f})", .{try dg.fmtIntLiteralHex(
10481062 try pt.intValue(.usize, val.toUnsignedInt(zcu)),
10491063 .Other,
10501064 )});
......@@ -1173,7 +1187,7 @@ pub const DeclGen = struct {
11731187 try w.writeAll(", ");
11741188 empty = false;
11751189 }
1176 try w.print("{fx}", .{try dg.fmtIntLiteral(
1190 try w.print("{f}", .{try dg.fmtIntLiteralHex(
11771191 try pt.intValue_big(repr_ty, repr_val_big.toConst()),
11781192 location,
11791193 )});
......@@ -1281,7 +1295,7 @@ pub const DeclGen = struct {
12811295 }
12821296 const ai = ty.arrayInfo(zcu);
12831297 if (ai.elem_type.eql(.u8, zcu)) {
1284 var literal: StringLiteral = .init(w, ty.arrayLenIncludingSentinel(zcu));
1298 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));
12851299 try literal.start();
12861300 var index: usize = 0;
12871301 while (index < ai.len) : (index += 1) {
......@@ -1562,7 +1576,7 @@ pub const DeclGen = struct {
15621576 .payload => {
15631577 try w.writeByte('{');
15641578 if (field_ty.hasRuntimeBits(zcu)) {
1565 try w.print(" .{f } = ", .{fmtIdent(field_name.toSlice(ip))});
1579 try w.print(" .{f} = ", .{fmtIdentSolo(field_name.toSlice(ip))});
15661580 try dg.renderValue(
15671581 w,
15681582 Value.fromInterned(un.val),
......@@ -1645,15 +1659,15 @@ pub const DeclGen = struct {
16451659 .enum_type,
16461660 .error_set_type,
16471661 .inferred_error_set_type,
1648 => return w.print("{fx}", .{
1649 try dg.fmtIntLiteral(try pt.undefValue(ty), location),
1662 => return w.print("{f}", .{
1663 try dg.fmtIntLiteralHex(try pt.undefValue(ty), location),
16501664 }),
16511665 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
16521666 .one, .many, .c => {
16531667 try w.writeAll("((");
16541668 try dg.renderCType(w, ctype);
1655 return w.print("){fx})", .{
1656 try dg.fmtIntLiteral(.undef_usize, .Other),
1669 return w.print("){f})", .{
1670 try dg.fmtIntLiteralHex(.undef_usize, .Other),
16571671 });
16581672 },
16591673 .slice => {
......@@ -1666,8 +1680,8 @@ pub const DeclGen = struct {
16661680 try w.writeAll("{(");
16671681 const ptr_ty = ty.slicePtrFieldType(zcu);
16681682 try dg.renderType(w, ptr_ty);
1669 return w.print("){fx}, {0fx}}}", .{
1670 try dg.fmtIntLiteral(.undef_usize, .Other),
1683 return w.print("){f}, {0f}}}", .{
1684 try dg.fmtIntLiteralHex(.undef_usize, .Other),
16711685 });
16721686 },
16731687 },
......@@ -1730,8 +1744,8 @@ pub const DeclGen = struct {
17301744 }
17311745 return w.writeByte('}');
17321746 },
1733 .@"packed" => return w.print("{fx}", .{
1734 try dg.fmtIntLiteral(try pt.undefValue(ty), .Other),
1747 .@"packed" => return w.print("{f}", .{
1748 try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other),
17351749 }),
17361750 }
17371751 },
......@@ -1800,8 +1814,8 @@ pub const DeclGen = struct {
18001814 }
18011815 if (has_tag) try w.writeByte('}');
18021816 },
1803 .@"packed" => return w.print("{fx}", .{
1804 try dg.fmtIntLiteral(try pt.undefValue(ty), .Other),
1817 .@"packed" => return w.print("{f}", .{
1818 try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other),
18051819 }),
18061820 }
18071821 },
......@@ -1840,7 +1854,7 @@ pub const DeclGen = struct {
18401854 const ai = ty.arrayInfo(zcu);
18411855 if (ai.elem_type.eql(.u8, zcu)) {
18421856 const c_len = ty.arrayLenIncludingSentinel(zcu);
1843 var literal: StringLiteral = .init(w, c_len);
1857 var literal: StringLiteral = .init(w, @intCast(c_len));
18441858 try literal.start();
18451859 var index: u64 = 0;
18461860 while (index < c_len) : (index += 1)
......@@ -1899,7 +1913,7 @@ pub const DeclGen = struct {
18991913 kind: CType.Kind,
19001914 name: union(enum) {
19011915 nav: InternPool.Nav.Index,
1902 fmt_ctype_pool_string: std.fmt.Formatter(formatCTypePoolString),
1916 fmt_ctype_pool_string: std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString),
19031917 @"export": struct {
19041918 main_name: InternPool.NullTerminatedString,
19051919 extern_name: InternPool.NullTerminatedString,
......@@ -1943,8 +1957,8 @@ pub const DeclGen = struct {
19431957 try w.print("{f}", .{trailing});
19441958 switch (name) {
19451959 .nav => |nav| try dg.renderNavName(w, nav),
1946 .fmt_ctype_pool_string => |fmt| try w.print("{f }", .{fmt}),
1947 .@"export" => |@"export"| try w.print("{f }", .{fmtIdent(@"export".extern_name.toSlice(ip))}),
1960 .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}),
1961 .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}),
19481962 }
19491963
19501964 try renderTypeSuffix(
......@@ -1971,17 +1985,17 @@ pub const DeclGen = struct {
19711985 const is_mangled = isMangledIdent(extern_name, true);
19721986 const is_export = @"export".extern_name != @"export".main_name;
19731987 if (is_mangled and is_export) {
1974 try w.print(" zig_mangled_export({f }, {fs}, {fs})", .{
1975 fmtIdent(extern_name),
1988 try w.print(" zig_mangled_export({f}, {f}, {f})", .{
1989 fmtIdentSolo(extern_name),
19761990 fmtStringLiteral(extern_name, null),
19771991 fmtStringLiteral(@"export".main_name.toSlice(ip), null),
19781992 });
19791993 } else if (is_mangled) {
1980 try w.print(" zig_mangled({f }, {fs})", .{
1981 fmtIdent(extern_name), fmtStringLiteral(extern_name, null),
1994 try w.print(" zig_mangled({f}, {f})", .{
1995 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
19821996 });
19831997 } else if (is_export) {
1984 try w.print(" zig_export({fs}, {fs})", .{
1998 try w.print(" zig_export({f}, {f})", .{
19851999 fmtStringLiteral(@"export".main_name.toSlice(ip), null),
19862000 fmtStringLiteral(extern_name, null),
19872001 });
......@@ -2129,7 +2143,7 @@ pub const DeclGen = struct {
21292143 } else if (dest_bits > 64 and src_bits <= 64) {
21302144 try w.writeAll("zig_make_");
21312145 try dg.renderTypeForBuiltinFnName(w, dest_ty);
2132 try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral?
2146 try w.writeAll("(0, ");
21332147 if (src_is_ptr) {
21342148 try w.writeByte('(');
21352149 try dg.renderType(w, src_eff_ty);
......@@ -2209,7 +2223,7 @@ pub const DeclGen = struct {
22092223 .new_local, .local => |i| try w.print("t{d}", .{i}),
22102224 .constant => |uav| try renderUavName(w, uav),
22112225 .nav => |nav| try dg.renderNavName(w, nav),
2212 .identifier => |ident| try w.print("{f }", .{fmtIdent(ident)}),
2226 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
22132227 else => unreachable,
22142228 }
22152229 }
......@@ -2226,13 +2240,13 @@ pub const DeclGen = struct {
22262240 try dg.renderNavName(w, nav);
22272241 },
22282242 .undef => |ty| try dg.renderUndefValue(w, ty, .Other),
2229 .identifier => |ident| try w.print("{f }", .{fmtIdent(ident)}),
2230 .payload_identifier => |ident| try w.print("{f }.{f }", .{
2231 fmtIdent("payload"),
2232 fmtIdent(ident),
2243 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
2244 .payload_identifier => |ident| try w.print("{f}.{f}", .{
2245 fmtIdentSolo("payload"),
2246 fmtIdentSolo(ident),
22332247 }),
2234 .ctype_pool_string => |string| try w.print("{f }", .{
2235 fmtCTypePoolString(string, &dg.ctype_pool),
2248 .ctype_pool_string => |string| try w.print("{f}", .{
2249 fmtCTypePoolString(string, &dg.ctype_pool, true),
22362250 }),
22372251 }
22382252 }
......@@ -2256,10 +2270,10 @@ pub const DeclGen = struct {
22562270 },
22572271 .nav_ref => |nav| try dg.renderNavName(w, nav),
22582272 .undef => unreachable,
2259 .identifier => |ident| try w.print("(*{f })", .{fmtIdent(ident)}),
2260 .payload_identifier => |ident| try w.print("(*{f }.{f })", .{
2261 fmtIdent("payload"),
2262 fmtIdent(ident),
2273 .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}),
2274 .payload_identifier => |ident| try w.print("(*{f}.{f})", .{
2275 fmtIdentSolo("payload"),
2276 fmtIdentSolo(ident),
22632277 }),
22642278 }
22652279 }
......@@ -2318,7 +2332,7 @@ pub const DeclGen = struct {
23182332 const zcu = dg.pt.zcu;
23192333 const ip = &zcu.intern_pool;
23202334 const nav = ip.getNav(nav_index);
2321 const fwd = &dg.fwd_decl.buffered_writer;
2335 const fwd = &dg.fwd_decl.writer;
23222336 try fwd.writeAll(switch (flags.linkage) {
23232337 .internal => "static ",
23242338 .strong, .weak, .link_once => "zig_extern ",
......@@ -2349,15 +2363,15 @@ pub const DeclGen = struct {
23492363 const ip = &zcu.intern_pool;
23502364 const nav = ip.getNav(nav_index);
23512365 if (nav.getExtern(ip)) |@"extern"| {
2352 try w.print("{f }", .{
2353 fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
2366 try w.print("{f}", .{
2367 fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
23542368 });
23552369 } else {
23562370 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
23572371 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
23582372 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
23592373 try w.print("{f}__{d}", .{
2360 fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
2374 fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]),
23612375 @intFromEnum(nav_index),
23622376 });
23632377 }
......@@ -2406,7 +2420,7 @@ pub const DeclGen = struct {
24062420 };
24072421
24082422 if (is_big) try w.print(", {}", .{int_info.signedness == .signed});
2409 try w.print(", {f}", .{try dg.fmtIntLiteral(
2423 try w.print(", {f}", .{try dg.fmtIntLiteralDec(
24102424 try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),
24112425 .FunctionArgument,
24122426 )});
......@@ -2416,18 +2430,38 @@ pub const DeclGen = struct {
24162430 dg: *DeclGen,
24172431 val: Value,
24182432 loc: ValueRenderLocation,
2419 ) !std.fmt.Formatter(formatIntLiteral) {
2433 base: u8,
2434 case: std.fmt.Case,
2435 ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
24202436 const zcu = dg.pt.zcu;
24212437 const kind = loc.toCTypeKind();
24222438 const ty = val.typeOf(zcu);
2423 return std.fmt.Formatter(formatIntLiteral){ .data = .{
2439 return .{ .data = .{
24242440 .dg = dg,
24252441 .int_info = ty.intInfo(zcu),
24262442 .kind = kind,
24272443 .ctype = try dg.ctypeFromType(ty, kind),
24282444 .val = val,
2445 .base = base,
2446 .case = case,
24292447 } };
24302448 }
2449
2450 fn fmtIntLiteralDec(
2451 dg: *DeclGen,
2452 val: Value,
2453 loc: ValueRenderLocation,
2454 ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
2455 return fmtIntLiteral(dg, val, loc, 10, .lower);
2456 }
2457
2458 fn fmtIntLiteralHex(
2459 dg: *DeclGen,
2460 val: Value,
2461 loc: ValueRenderLocation,
2462 ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
2463 return fmtIntLiteral(dg, val, loc, 16, .lower);
2464 }
24312465};
24322466
24332467const CTypeFix = enum { prefix, suffix };
......@@ -2437,13 +2471,7 @@ const RenderCTypeTrailing = enum {
24372471 no_space,
24382472 maybe_space,
24392473
2440 pub fn format(
2441 self: @This(),
2442 w: *Writer,
2443 comptime fmt: []const u8,
2444 ) Writer.Error!void {
2445 if (fmt.len != 0) @compileError("invalid format string '" ++
2446 fmt ++ "' for type '" ++ @typeName(@This()) ++ "'");
2474 pub fn format(self: @This(), w: *Writer) Writer.Error!void {
24472475 switch (self) {
24482476 .no_space => {},
24492477 .maybe_space => try w.writeByte(' '),
......@@ -2465,7 +2493,7 @@ fn renderFwdDeclTypeName(
24652493 switch (fwd_decl.name) {
24662494 .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}),
24672495 .index => |index| try w.print("{f}__{d}", .{
2468 fmtIdent(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)),
2496 fmtIdentUnsolo(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)),
24692497 @intFromEnum(index),
24702498 }),
24712499 }
......@@ -2679,7 +2707,7 @@ fn renderFields(
26792707 .suffix,
26802708 .{},
26812709 );
2682 try w.print("{f}{f }", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool) });
2710 try w.print("{f}{f}", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool, true) });
26832711 try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{});
26842712 try w.writeAll(";\n");
26852713 }
......@@ -2771,7 +2799,7 @@ pub fn genTypeDecl(
27712799
27722800pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {
27732801 for (zcu.global_assembly.values()) |asm_source| {
2774 try w.print("__asm({fs});\n", .{fmtStringLiteral(asm_source, null)});
2802 try w.print("__asm({f});\n", .{fmtStringLiteral(asm_source, null)});
27752803 }
27762804}
27772805
......@@ -2779,7 +2807,7 @@ pub fn genErrDecls(o: *Object) Error!void {
27792807 const pt = o.dg.pt;
27802808 const zcu = pt.zcu;
27812809 const ip = &zcu.intern_pool;
2782 const w = &o.code.buffered_writer;
2810 const w = &o.code.writer;
27832811
27842812 var max_name_len: usize = 0;
27852813 // do not generate an invalid empty enum when the global error set is empty
......@@ -2858,8 +2886,8 @@ pub fn genErrDecls(o: *Object) Error!void {
28582886 const name = name_nts.toSlice(ip);
28592887 if (val > 1) try w.writeAll(", ");
28602888 try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{
2861 fmtIdent(name),
2862 try o.dg.fmtIntLiteral(try pt.intValue(.usize, name.len), .StaticInitializer),
2889 fmtIdentUnsolo(name),
2890 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer),
28632891 });
28642892 }
28652893 try w.writeAll("};");
......@@ -2871,7 +2899,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
28712899 const zcu = pt.zcu;
28722900 const ip = &zcu.intern_pool;
28732901 const ctype_pool = &o.dg.ctype_pool;
2874 const w = &o.code.buffered_writer;
2902 const w = &o.code.writer;
28752903 const key = lazy_fn.key_ptr.*;
28762904 const val = lazy_fn.value_ptr;
28772905 switch (key) {
......@@ -2906,7 +2934,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
29062934 } });
29072935
29082936 try w.print("case {f}: {{", .{
2909 try o.dg.fmtIntLiteral(try tag_val.intFromEnum(enum_ty, pt), .Other),
2937 try o.dg.fmtIntLiteralDec(try tag_val.intFromEnum(enum_ty, pt), .Other),
29102938 });
29112939 o.indent();
29122940 try o.newline();
......@@ -2919,8 +2947,8 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
29192947 try w.writeAll("return (");
29202948 try o.dg.renderType(w, name_slice_ty);
29212949 try w.print("){{{f}, {f}}};", .{
2922 fmtIdent("name"),
2923 try o.dg.fmtIntLiteral(try pt.intValue(.usize, tag_name_len), .Other),
2950 fmtIdentUnsolo("name"),
2951 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other),
29242952 });
29252953 try o.newline();
29262954 try o.outdent();
......@@ -2939,9 +2967,9 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
29392967 const fn_val = zcu.navValue(fn_nav_index);
29402968 const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete);
29412969 const fn_info = fn_ctype.info(ctype_pool).function;
2942 const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool);
2970 const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool, true);
29432971
2944 const fwd = &o.dg.fwd_decl.buffered_writer;
2972 const fwd = &o.dg.fwd_decl.writer;
29452973 try fwd.print("static zig_{s} ", .{@tagName(key)});
29462974 try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
29472975 .fmt_ctype_pool_string = fn_name,
......@@ -3001,20 +3029,20 @@ pub fn generate(
30013029 .pass = .{ .nav = func.owner_nav },
30023030 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
30033031 .expected_block = null,
3004 .fwd_decl = undefined,
3032 .fwd_decl = .init(gpa),
30053033 .ctype_pool = .empty,
30063034 .scratch = .empty,
30073035 .uavs = .empty,
30083036 },
3009 .code_header = undefined,
3010 .code = undefined,
3037 .code_header = .init(gpa),
3038 .code = .init(gpa),
30113039 .indent_counter = 0,
30123040 },
30133041 .lazy_fns = .empty,
30143042 };
30153043 defer {
3016 function.object.code_header.init(gpa);
3017 function.object.code.init(gpa);
3044 function.object.code_header.deinit();
3045 function.object.code.deinit();
30183046 function.object.dg.fwd_decl.deinit();
30193047 function.object.dg.ctype_pool.deinit(gpa);
30203048 function.object.dg.scratch.deinit(gpa);
......@@ -3022,18 +3050,17 @@ pub fn generate(
30223050 function.deinit();
30233051 }
30243052 try function.object.dg.ctype_pool.init(gpa);
3025 function.object.dg.fwd_decl.init(gpa);
3026 function.object.code_header.init(gpa);
3027 function.object.code.init(gpa);
30283053
30293054 genFunc(&function) catch |err| switch (err) {
30303055 error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?),
3031 error.OutOfMemory => |e| return e,
3056 error.OutOfMemory => return error.OutOfMemory,
3057 error.WriteFailed => return error.OutOfMemory,
30323058 };
30333059
30343060 var mir: Mir = .{
30353061 .uavs = .empty,
30363062 .code = &.{},
3063 .code_header = &.{},
30373064 .fwd_decl = &.{},
30383065 .ctype_pool = .empty,
30393066 .lazy_fns = .empty,
......@@ -3060,7 +3087,7 @@ pub fn genFunc(f: *Function) Error!void {
30603087 const nav_val = zcu.navValue(nav_index);
30613088 const nav = ip.getNav(nav_index);
30623089
3063 const fwd = &o.dg.fwd_decl.buffered_writer;
3090 const fwd = &o.dg.fwd_decl.writer;
30643091 try fwd.writeAll("static ");
30653092 try o.dg.renderFunctionSignature(
30663093 fwd,
......@@ -3071,9 +3098,9 @@ pub fn genFunc(f: *Function) Error!void {
30713098 );
30723099 try fwd.writeAll(";\n");
30733100
3074 const ch = &o.code_header.buffered_writer;
3101 const ch = &o.code_header.writer;
30753102 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
3076 try ch.print("zig_linksection_fn({fs}) ", .{fmtStringLiteral(s, null)});
3103 try ch.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)});
30773104 try o.dg.renderFunctionSignature(
30783105 ch,
30793106 nav_val,
......@@ -3089,7 +3116,7 @@ pub fn genFunc(f: *Function) Error!void {
30893116 o.indent();
30903117 try genBodyResolveState(f, undefined, &.{}, main_body, true);
30913118 try o.outdent();
3092 try o.code.buffered_writer.writeByte('}');
3119 try o.code.writer.writeByte('}');
30933120 try o.newline();
30943121 if (o.dg.expected_block) |_|
30953122 return f.fail("runtime code not allowed in naked function", .{});
......@@ -3150,7 +3177,7 @@ pub fn genDecl(o: *Object) Error!void {
31503177 .visibility = @"extern".visibility,
31513178 });
31523179
3153 const fwd = &o.dg.fwd_decl.buffered_writer;
3180 const fwd = &o.dg.fwd_decl.writer;
31543181 try fwd.writeAll("zig_extern ");
31553182 try o.dg.renderFunctionSignature(
31563183 fwd,
......@@ -3171,10 +3198,10 @@ pub fn genDecl(o: *Object) Error!void {
31713198 .linkage = .internal,
31723199 .visibility = .default,
31733200 });
3174 const w = &o.code.buffered_writer;
3201 const w = &o.code.writer;
31753202 if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
31763203 if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
3177 try w.print("zig_linksection({fs}) ", .{fmtStringLiteral(s, null)});
3204 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
31783205 try o.dg.renderTypeAndName(
31793206 w,
31803207 nav_ty,
......@@ -3208,14 +3235,14 @@ pub fn genDeclValue(
32083235 const zcu = o.dg.pt.zcu;
32093236 const ty = val.typeOf(zcu);
32103237
3211 const fwd = &o.dg.fwd_decl.buffered_writer;
3238 const fwd = &o.dg.fwd_decl.writer;
32123239 try fwd.writeAll("static ");
32133240 try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete);
32143241 try fwd.writeAll(";\n");
32153242
3216 const w = &o.code.buffered_writer;
3243 const w = &o.code.writer;
32173244 if (@"linksection".toSlice(&zcu.intern_pool)) |s|
3218 try w.print("zig_linksection({fs}) ", .{fmtStringLiteral(s, null)});
3245 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
32193246 try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete);
32203247 try w.writeAll(" = ");
32213248 try o.dg.renderValue(w, val, .StaticInitializer);
......@@ -3226,7 +3253,7 @@ pub fn genDeclValue(
32263253pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {
32273254 const zcu = dg.pt.zcu;
32283255 const ip = &zcu.intern_pool;
3229 const fwd = &dg.fwd_decl.buffered_writer;
3256 const fwd = &dg.fwd_decl.writer;
32303257
32313258 const main_name = export_indices[0].ptr(zcu).opts.name;
32323259 try fwd.writeAll("#define ");
......@@ -3235,7 +3262,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
32353262 .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)),
32363263 }
32373264 try fwd.writeByte(' ');
3238 try fwd.print("{f }", .{fmtIdent(main_name.toSlice(ip))});
3265 try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});
32393266 try fwd.writeByte('\n');
32403267
32413268 const exported_val = exported.getValue(zcu);
......@@ -3265,7 +3292,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
32653292 const @"export" = export_index.ptr(zcu);
32663293 try fwd.writeAll("zig_extern ");
32673294 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");
3268 if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({fs}) ", .{
3295 if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({f}) ", .{
32693296 fmtStringLiteral(s, null),
32703297 });
32713298 const extern_name = @"export".opts.name.toSlice(ip);
......@@ -3280,17 +3307,17 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
32803307 .complete,
32813308 );
32823309 if (is_mangled and is_export) {
3283 try fwd.print(" zig_mangled_export({f }, {fs}, {fs})", .{
3284 fmtIdent(extern_name),
3310 try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{
3311 fmtIdentSolo(extern_name),
32853312 fmtStringLiteral(extern_name, null),
32863313 fmtStringLiteral(main_name.toSlice(ip), null),
32873314 });
32883315 } else if (is_mangled) {
3289 try fwd.print(" zig_mangled({f }, {fs})", .{
3290 fmtIdent(extern_name), fmtStringLiteral(extern_name, null),
3316 try fwd.print(" zig_mangled({f}, {f})", .{
3317 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
32913318 });
32923319 } else if (is_export) {
3293 try fwd.print(" zig_export({fs}, {fs})", .{
3320 try fwd.print(" zig_export({f}, {f})", .{
32943321 fmtStringLiteral(main_name.toSlice(ip), null),
32953322 fmtStringLiteral(extern_name, null),
32963323 });
......@@ -3304,7 +3331,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
33043331/// have been added to `free_locals_map`. For a version of this function that restores this state,
33053332/// see `genBodyResolveState`.
33063333fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
3307 const w = &f.object.code.buffered_writer;
3334 const w = &f.object.code.writer;
33083335 if (body.len == 0) {
33093336 try w.writeAll("{}");
33103337 } else {
......@@ -3326,7 +3353,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
33263353fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void {
33273354 if (body.len == 0) {
33283355 // Don't go to the expense of cloning everything!
3329 if (!inner) try f.object.code.buffered_writer.writeAll("{}");
3356 if (!inner) try f.object.code.writer.writeAll("{}");
33303357 return;
33313358 }
33323359
......@@ -3643,7 +3670,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
36433670 .ret => return airRet(f, inst, false),
36443671 .ret_safe => return airRet(f, inst, false), // TODO
36453672 .ret_load => return airRet(f, inst, true),
3646 .trap => return airTrap(f, &f.object.code.buffered_writer),
3673 .trap => return airTrap(f, &f.object.code.writer),
36473674 .unreach => return airUnreach(&f.object),
36483675
36493676 // Instructions which may be `noreturn`.
......@@ -3687,7 +3714,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
36873714 const operand = try f.resolveInst(ty_op.operand);
36883715 try reap(f, inst, &.{ty_op.operand});
36893716
3690 const w = &f.object.code.buffered_writer;
3717 const w = &f.object.code.writer;
36913718 const local = try f.allocLocal(inst, inst_ty);
36923719 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
36933720 try f.writeCValue(w, local, .Other);
......@@ -3713,7 +3740,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
37133740 const index = try f.resolveInst(bin_op.rhs);
37143741 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37153742
3716 const w = &f.object.code.buffered_writer;
3743 const w = &f.object.code.writer;
37173744 const local = try f.allocLocal(inst, inst_ty);
37183745 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
37193746 try f.writeCValue(w, local, .Other);
......@@ -3740,7 +3767,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
37403767 const index = try f.resolveInst(bin_op.rhs);
37413768 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37423769
3743 const w = &f.object.code.buffered_writer;
3770 const w = &f.object.code.writer;
37443771 const local = try f.allocLocal(inst, inst_ty);
37453772 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
37463773 try f.writeCValue(w, local, .Other);
......@@ -3775,7 +3802,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
37753802 const index = try f.resolveInst(bin_op.rhs);
37763803 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37773804
3778 const w = &f.object.code.buffered_writer;
3805 const w = &f.object.code.writer;
37793806 const local = try f.allocLocal(inst, inst_ty);
37803807 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
37813808 try f.writeCValue(w, local, .Other);
......@@ -3803,7 +3830,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
38033830 const index = try f.resolveInst(bin_op.rhs);
38043831 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
38053832
3806 const w = &f.object.code.buffered_writer;
3833 const w = &f.object.code.writer;
38073834 const local = try f.allocLocal(inst, inst_ty);
38083835 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
38093836 try f.writeCValue(w, local, .Other);
......@@ -3832,7 +3859,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
38323859 const index = try f.resolveInst(bin_op.rhs);
38333860 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
38343861
3835 const w = &f.object.code.buffered_writer;
3862 const w = &f.object.code.writer;
38363863 const local = try f.allocLocal(inst, inst_ty);
38373864 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
38383865 try f.writeCValue(w, local, .Other);
......@@ -3895,7 +3922,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
38953922 .{ .arg_array = i };
38963923
38973924 if (f.liveness.isUnused(inst)) {
3898 const w = &f.object.code.buffered_writer;
3925 const w = &f.object.code.writer;
38993926 try w.writeByte('(');
39003927 try f.renderType(w, .void);
39013928 try w.writeByte(')');
......@@ -3934,7 +3961,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
39343961 const is_array = lowersToArray(src_ty, pt);
39353962 const need_memcpy = !is_aligned or is_array;
39363963
3937 const w = &f.object.code.buffered_writer;
3964 const w = &f.object.code.writer;
39383965 const local = try f.allocLocal(inst, src_ty);
39393966 const v = try Vectorize.start(f, inst, w, ptr_ty);
39403967
......@@ -3979,7 +4006,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
39794006 try w.writeByte('(');
39804007 try f.writeCValueDeref(w, operand);
39814008 try v.elem(f, w);
3982 try w.print(", {f})", .{try f.fmtIntLiteral(bit_offset_val)});
4009 try w.print(", {f})", .{try f.fmtIntLiteralDec(bit_offset_val)});
39834010 if (cant_cast) try w.writeByte(')');
39844011 try f.object.dg.renderBuiltinInfo(w, field_ty, .bits);
39854012 try w.writeByte(')');
......@@ -4001,7 +4028,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
40014028 const pt = f.object.dg.pt;
40024029 const zcu = pt.zcu;
40034030 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4004 const w = &f.object.code.buffered_writer;
4031 const w = &f.object.code.writer;
40054032 const op_inst = un_op.toIndex();
40064033 const op_ty = f.typeOf(un_op);
40074034 const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty;
......@@ -4040,7 +4067,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
40404067 try f.writeCValueDeref(w, ret_val)
40414068 else
40424069 try f.writeCValue(w, ret_val, .Other);
4043 try w.write(";\n");
4070 try w.writeAll(";\n");
40444071 if (is_array) {
40454072 try freeLocal(f, inst, ret_val.new_local, null);
40464073 }
......@@ -4066,7 +4093,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
40664093
40674094 if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand);
40684095
4069 const w = &f.object.code.buffered_writer;
4096 const w = &f.object.code.writer;
40704097 const local = try f.allocLocal(inst, inst_ty);
40714098 const v = try Vectorize.start(f, inst, w, operand_ty);
40724099 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
......@@ -4102,7 +4129,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
41024129 const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);
41034130 if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand);
41044131
4105 const w = &f.object.code.buffered_writer;
4132 const w = &f.object.code.writer;
41064133 const local = try f.allocLocal(inst, inst_ty);
41074134 const v = try Vectorize.start(f, inst, w, operand_ty);
41084135 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete));
......@@ -4129,8 +4156,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
41294156 try w.writeByte('(');
41304157 try f.writeCValue(w, operand, .FunctionArgument);
41314158 try v.elem(f, w);
4132 try w.print(", {fx})", .{
4133 try f.fmtIntLiteral(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),
4159 try w.print(", {f})", .{
4160 try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),
41344161 });
41354162 },
41364163 .signed => {
......@@ -4154,9 +4181,9 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
41544181 try f.writeCValue(w, operand, .FunctionArgument);
41554182 try v.elem(f, w);
41564183 if (c_bits == 128) try w.writeByte(')');
4157 try w.print(", {f})", .{try f.fmtIntLiteral(shift_val)});
4184 try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});
41584185 if (c_bits == 128) try w.writeByte(')');
4159 try w.print(", {f})", .{try f.fmtIntLiteral(shift_val)});
4186 try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});
41604187 },
41614188 }
41624189 if (need_lo) try w.writeByte(')');
......@@ -4180,7 +4207,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
41804207
41814208 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndefDeep(zcu) else false;
41824209
4183 const w = &f.object.code.buffered_writer;
4210 const w = &f.object.code.writer;
41844211 if (val_is_undef) {
41854212 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
41864213 if (safety and ptr_info.packed_offset.host_size == 0) {
......@@ -4273,7 +4300,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
42734300 try w.writeByte('(');
42744301 try f.writeCValueDeref(w, ptr_val);
42754302 try v.elem(f, w);
4276 try w.print(", {fx}), zig_shl_", .{try f.fmtIntLiteral(mask_val)});
4303 try w.print(", {f}), zig_shl_", .{try f.fmtIntLiteralHex(mask_val)});
42774304 try f.object.dg.renderTypeForBuiltinFnName(w, host_ty);
42784305 try w.writeByte('(');
42794306 const cant_cast = host_ty.isInt(zcu) and host_ty.bitSize(zcu) > 64;
......@@ -4296,7 +4323,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
42964323 try f.writeCValue(w, src_val, .Other);
42974324 try v.elem(f, w);
42984325 if (cant_cast) try w.writeByte(')');
4299 try w.print(", {f}))", .{try f.fmtIntLiteral(bit_offset_val)});
4326 try w.print(", {f}))", .{try f.fmtIntLiteralDec(bit_offset_val)});
43004327 try a.end(f, w);
43014328 try v.end(f, inst, w);
43024329 } else {
......@@ -4335,7 +4362,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
43354362 const operand_ty = f.typeOf(bin_op.lhs);
43364363 const scalar_ty = operand_ty.scalarType(zcu);
43374364
4338 const w = &f.object.code.buffered_writer;
4365 const w = &f.object.code.writer;
43394366 const local = try f.allocLocal(inst, inst_ty);
43404367 const v = try Vectorize.start(f, inst, w, operand_ty);
43414368 try f.writeCValueMember(w, local, .{ .field = 1 });
......@@ -4374,7 +4401,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
43744401
43754402 const inst_ty = f.typeOfIndex(inst);
43764403
4377 const w = &f.object.code.buffered_writer;
4404 const w = &f.object.code.writer;
43784405 const local = try f.allocLocal(inst, inst_ty);
43794406 const v = try Vectorize.start(f, inst, w, operand_ty);
43804407 try f.writeCValue(w, local, .Other);
......@@ -4411,7 +4438,7 @@ fn airBinOp(
44114438
44124439 const inst_ty = f.typeOfIndex(inst);
44134440
4414 const w = &f.object.code.buffered_writer;
4441 const w = &f.object.code.writer;
44154442 const local = try f.allocLocal(inst, inst_ty);
44164443 const v = try Vectorize.start(f, inst, w, operand_ty);
44174444 try f.writeCValue(w, local, .Other);
......@@ -4462,7 +4489,7 @@ fn airCmpOp(
44624489
44634490 const rhs_ty = f.typeOf(data.rhs);
44644491 const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu);
4465 const w = &f.object.code.buffered_writer;
4492 const w = &f.object.code.writer;
44664493 const local = try f.allocLocal(inst, inst_ty);
44674494 const v = try Vectorize.start(f, inst, w, lhs_ty);
44684495 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
......@@ -4515,7 +4542,7 @@ fn airEquality(
45154542 const rhs = try f.resolveInst(bin_op.rhs);
45164543 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
45174544
4518 const w = &f.object.code.buffered_writer;
4545 const w = &f.object.code.writer;
45194546 const local = try f.allocLocal(inst, .bool);
45204547 const a = try Assignment.start(f, w, .bool);
45214548 try f.writeCValue(w, local, .Other);
......@@ -4573,12 +4600,12 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
45734600 const operand = try f.resolveInst(un_op);
45744601 try reap(f, inst, &.{un_op});
45754602
4576 const w = &f.object.code.buffered_writer;
4603 const w = &f.object.code.writer;
45774604 const local = try f.allocLocal(inst, .bool);
45784605 try f.writeCValue(w, local, .Other);
45794606 try w.writeAll(" = ");
45804607 try f.writeCValue(w, operand, .Other);
4581 try w.print(" < sizeof({f }) / sizeof(*{0f });", .{fmtIdent("zig_errorName")});
4608 try w.print(" < sizeof({f}) / sizeof(*{0f});", .{fmtIdentSolo("zig_errorName")});
45824609 try f.object.newline();
45834610 return local;
45844611}
......@@ -4600,7 +4627,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
46004627 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
46014628
46024629 const local = try f.allocLocal(inst, inst_ty);
4603 const w = &f.object.code.buffered_writer;
4630 const w = &f.object.code.writer;
46044631 const v = try Vectorize.start(f, inst, w, inst_ty);
46054632 const a = try Assignment.start(f, w, inst_scalar_ctype);
46064633 try f.writeCValue(w, local, .Other);
......@@ -4642,7 +4669,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
46424669 const rhs = try f.resolveInst(bin_op.rhs);
46434670 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
46444671
4645 const w = &f.object.code.buffered_writer;
4672 const w = &f.object.code.writer;
46464673 const local = try f.allocLocal(inst, inst_ty);
46474674 const v = try Vectorize.start(f, inst, w, inst_ty);
46484675 try f.writeCValue(w, local, .Other);
......@@ -4682,7 +4709,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
46824709 const inst_ty = f.typeOfIndex(inst);
46834710 const ptr_ty = inst_ty.slicePtrFieldType(zcu);
46844711
4685 const w = &f.object.code.buffered_writer;
4712 const w = &f.object.code.writer;
46864713 const local = try f.allocLocal(inst, inst_ty);
46874714 {
46884715 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));
......@@ -4713,7 +4740,7 @@ fn airCall(
47134740 if (f.object.dg.is_naked_fn) return .none;
47144741
47154742 const gpa = f.object.dg.gpa;
4716 const w = &f.object.code.buffered_writer;
4743 const w = &f.object.code.writer;
47174744
47184745 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
47194746 const extra = f.air.extraData(Air.Call, pl_op.payload);
......@@ -4864,7 +4891,7 @@ fn airCall(
48644891
48654892fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
48664893 const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
4867 const w = &f.object.code.buffered_writer;
4894 const w = &f.object.code.writer;
48684895 // TODO re-evaluate whether to emit these or not. If we naively emit
48694896 // these directives, the output file will report bogus line numbers because
48704897 // every newline after the #line directive adds one to the line.
......@@ -4880,7 +4907,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
48804907}
48814908
48824909fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {
4883 try f.object.code.buffered_writer.writeAll("(void)0;");
4910 try f.object.code.writer.writeAll("(void)0;");
48844911 try f.object.newline();
48854912 return .none;
48864913}
......@@ -4892,7 +4919,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
48924919 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
48934920 const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
48944921 const owner_nav = ip.getNav(zcu.funcInfo(extra.data.func).owner_nav);
4895 const w = &f.object.code.buffered_writer;
4922 const w = &f.object.code.writer;
48964923 try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)});
48974924 try f.object.newline();
48984925 return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]));
......@@ -4908,7 +4935,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
49084935 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
49094936
49104937 try reap(f, inst, &.{pl_op.operand});
4911 const w = &f.object.code.buffered_writer;
4938 const w = &f.object.code.writer;
49124939 try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) });
49134940 try f.object.newline();
49144941 return .none;
......@@ -4927,7 +4954,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
49274954
49284955 const block_id = f.next_block_index;
49294956 f.next_block_index += 1;
4930 const w = &f.object.code.buffered_writer;
4957 const w = &f.object.code.writer;
49314958
49324959 const inst_ty = f.typeOfIndex(inst);
49334960 const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst))
......@@ -4996,7 +5023,7 @@ fn lowerTry(
49965023 const err_union = try f.resolveInst(operand);
49975024 const inst_ty = f.typeOfIndex(inst);
49985025 const liveness_condbr = f.liveness.getCondBr(inst);
4999 const w = &f.object.code.buffered_writer;
5026 const w = &f.object.code.writer;
50005027 const payload_ty = err_union_ty.errorUnionPayload(zcu);
50015028 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu);
50025029
......@@ -5058,7 +5085,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
50585085 const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
50595086 const block = f.blocks.get(branch.block_inst).?;
50605087 const result = block.result;
5061 const w = &f.object.code.buffered_writer;
5088 const w = &f.object.code.writer;
50625089
50635090 if (f.object.dg.is_naked_fn) {
50645091 if (result != .none) return f.fail("runtime code not allowed in naked function", .{});
......@@ -5084,14 +5111,14 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
50845111
50855112fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
50865113 const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
5087 try f.object.code.buffered_writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
5114 try f.object.code.writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
50885115}
50895116
50905117fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
50915118 const pt = f.object.dg.pt;
50925119 const zcu = pt.zcu;
50935120 const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
5094 const w = &f.object.code.buffered_writer;
5121 const w = &f.object.code.writer;
50955122
50965123 if (try f.air.value(br.operand, pt)) |cond_val| {
50975124 // Comptime-known dispatch. Iterate the cases to find the correct
......@@ -5145,7 +5172,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
51455172 const zcu = pt.zcu;
51465173 const target = &f.object.dg.mod.resolved_target.result;
51475174 const ctype_pool = &f.object.dg.ctype_pool;
5148 const w = &f.object.code.buffered_writer;
5175 const w = &f.object.code.writer;
51495176
51505177 if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {
51515178 const src_info = dest_ty.intInfo(zcu);
......@@ -5169,13 +5196,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
51695196 const operand_lval = if (operand == .constant) blk: {
51705197 const operand_local = try f.allocLocal(null, operand_ty);
51715198 try f.writeCValue(w, operand_local, .Other);
5172 if (operand_ty.isAbiInt(zcu)) {
5173 try w.writeAll(" = ");
5174 } else {
5175 try w.writeAll(" = (");
5176 try f.renderType(w, operand_ty);
5177 try w.writeByte(')');
5178 }
5199 try w.writeAll(" = ");
51795200 try f.writeCValue(w, operand, .Other);
51805201 try w.writeByte(';');
51815202 try f.object.newline();
......@@ -5264,14 +5285,14 @@ fn airTrap(f: *Function, w: *Writer) !void {
52645285}
52655286
52665287fn airBreakpoint(f: *Function) !CValue {
5267 const w = &f.object.code.buffered_writer;
5288 const w = &f.object.code.writer;
52685289 try w.writeAll("zig_breakpoint();");
52695290 try f.object.newline();
52705291 return .none;
52715292}
52725293
52735294fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
5274 const w = &f.object.code.buffered_writer;
5295 const w = &f.object.code.writer;
52755296 const local = try f.allocLocal(inst, .usize);
52765297 try f.writeCValue(w, local, .Other);
52775298 try w.writeAll(" = (");
......@@ -5282,7 +5303,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
52825303}
52835304
52845305fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
5285 const w = &f.object.code.buffered_writer;
5306 const w = &f.object.code.writer;
52865307 const local = try f.allocLocal(inst, .usize);
52875308 try f.writeCValue(w, local, .Other);
52885309 try w.writeAll(" = (");
......@@ -5295,14 +5316,14 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
52955316fn airUnreach(o: *Object) !void {
52965317 // Not even allowed to call unreachable in a naked function.
52975318 if (o.dg.is_naked_fn) return;
5298 try o.code.buffered_writer.writeAll("zig_unreachable();\n");
5319 try o.code.writer.writeAll("zig_unreachable();\n");
52995320}
53005321
53015322fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
53025323 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
53035324 const loop = f.air.extraData(Air.Block, ty_pl.payload);
53045325 const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[loop.end..][0..loop.data.body_len]);
5305 const w = &f.object.code.buffered_writer;
5326 const w = &f.object.code.writer;
53065327
53075328 // `repeat` instructions matching this loop will branch to
53085329 // this label. Since we need a label for arbitrary `repeat`
......@@ -5321,7 +5342,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
53215342 const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]);
53225343 const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
53235344 const liveness_condbr = f.liveness.getCondBr(inst);
5324 const w = &f.object.code.buffered_writer;
5345 const w = &f.object.code.writer;
53255346
53265347 try w.writeAll("if (");
53275348 try f.writeCValue(w, cond, .Other);
......@@ -5354,7 +5375,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
53545375 const init_condition = try f.resolveInst(switch_br.operand);
53555376 try reap(f, inst, &.{switch_br.operand});
53565377 const condition_ty = f.typeOf(switch_br.operand);
5357 const w = &f.object.code.buffered_writer;
5378 const w = &f.object.code.writer;
53585379
53595380 // For dispatches, we will create a local alloc to contain the condition value.
53605381 // This may not result in optimal codegen for switch loops, but it minimizes the
......@@ -5408,7 +5429,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
54085429 write_val: {
54095430 if (condition_ty.isPtrAtRuntime(zcu)) {
54105431 if (item_value.?.getUnsignedInt(zcu)) |item_int| {
5411 try w.print("{f}", .{try f.fmtIntLiteral(try pt.intValue(lowered_condition_ty, item_int))});
5432 try w.print("{f}", .{try f.fmtIntLiteralDec(try pt.intValue(lowered_condition_ty, item_int))});
54125433 break :write_val;
54135434 }
54145435 }
......@@ -5534,7 +5555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
55345555 extra_i += inputs.len;
55355556
55365557 const result = result: {
5537 const w = &f.object.code.buffered_writer;
5558 const w = &f.object.code.writer;
55385559 const inst_ty = f.typeOfIndex(inst);
55395560 const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: {
55405561 const inst_local = try f.allocLocalValue(.{
......@@ -5683,7 +5704,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
56835704
56845705 try w.writeAll("__asm");
56855706 if (is_volatile) try w.writeAll(" volatile");
5686 try w.print("({fs}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)});
5707 try w.print("({f}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)});
56875708 }
56885709
56895710 extra_i = constraints_extra_begin;
......@@ -5701,7 +5722,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
57015722 try w.writeByte(' ');
57025723 if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name});
57035724 const is_reg = constraint[1] == '{';
5704 try w.print("{fs}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});
5725 try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});
57055726 if (is_reg) {
57065727 try f.writeCValue(w, .{ .local = locals_index }, .Other);
57075728 locals_index += 1;
......@@ -5727,7 +5748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
57275748
57285749 const is_reg = constraint[0] == '{';
57295750 const input_val = try f.resolveInst(input);
5730 try w.print("{fs}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)});
5751 try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)});
57315752 try f.writeCValue(w, if (asmInputNeedsLocal(f, constraint, input_val)) local: {
57325753 const input_local_idx = locals_index;
57335754 locals_index += 1;
......@@ -5745,7 +5766,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
57455766 if (clobber.len == 0) continue;
57465767
57475768 if (clobber_i > 0) try w.writeByte(',');
5748 try w.print(" {fs}", .{fmtStringLiteral(clobber, null)});
5769 try w.print(" {f}", .{fmtStringLiteral(clobber, null)});
57495770 }
57505771 try w.writeAll(");");
57515772 try f.object.newline();
......@@ -5800,7 +5821,7 @@ fn airIsNull(
58005821 const ctype_pool = &f.object.dg.ctype_pool;
58015822 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
58025823
5803 const w = &f.object.code.buffered_writer;
5824 const w = &f.object.code.writer;
58045825 const operand = try f.resolveInst(un_op);
58055826 try reap(f, inst, &.{un_op});
58065827
......@@ -5868,7 +5889,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue
58685889 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
58695890 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
58705891 .is_null, .payload => {
5871 const w = &f.object.code.buffered_writer;
5892 const w = &f.object.code.writer;
58725893 const local = try f.allocLocal(inst, inst_ty);
58735894 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
58745895 try f.writeCValue(w, local, .Other);
......@@ -5890,7 +5911,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
58905911 const pt = f.object.dg.pt;
58915912 const zcu = pt.zcu;
58925913 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5893 const w = &f.object.code.buffered_writer;
5914 const w = &f.object.code.writer;
58945915 const operand = try f.resolveInst(ty_op.operand);
58955916 try reap(f, inst, &.{ty_op.operand});
58965917 const operand_ty = f.typeOf(ty_op.operand);
......@@ -6040,7 +6061,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
60406061 const field_ptr_val = try f.resolveInst(extra.field_ptr);
60416062 try reap(f, inst, &.{extra.field_ptr});
60426063
6043 const w = &f.object.code.buffered_writer;
6064 const w = &f.object.code.writer;
60446065 const local = try f.allocLocal(inst, container_ptr_ty);
60456066 try f.writeCValue(w, local, .Other);
60466067 try w.writeAll(" = (");
......@@ -6070,7 +6091,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
60706091 try w.writeByte(')');
60716092 try f.writeCValue(w, field_ptr_val, .Other);
60726093 try w.print(" - {f})", .{
6073 try f.fmtIntLiteral(try pt.intValue(.usize, byte_offset)),
6094 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
60746095 });
60756096 },
60766097 }
......@@ -6095,7 +6116,7 @@ fn fieldPtr(
60956116 // Ensure complete type definition is visible before accessing fields.
60966117 _ = try f.ctypeFromType(container_ty, .complete);
60976118
6098 const w = &f.object.code.buffered_writer;
6119 const w = &f.object.code.writer;
60996120 const local = try f.allocLocal(inst, field_ptr_ty);
61006121 try f.writeCValue(w, local, .Other);
61016122 try w.writeAll(" = (");
......@@ -6116,7 +6137,7 @@ fn fieldPtr(
61166137 try w.writeByte(')');
61176138 try f.writeCValue(w, container_ptr_val, .Other);
61186139 try w.print(" + {f})", .{
6119 try f.fmtIntLiteral(try pt.intValue(.usize, byte_offset)),
6140 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
61206141 });
61216142 },
61226143 }
......@@ -6142,7 +6163,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
61426163 const struct_byval = try f.resolveInst(extra.struct_operand);
61436164 try reap(f, inst, &.{extra.struct_operand});
61446165 const struct_ty = f.typeOf(extra.struct_operand);
6145 const w = &f.object.code.buffered_writer;
6166 const w = &f.object.code.writer;
61466167
61476168 // Ensure complete type definition is visible before accessing fields.
61486169 _ = try f.ctypeFromType(struct_ty, .complete);
......@@ -6189,7 +6210,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
61896210 }
61906211 try f.writeCValue(w, struct_byval, .Other);
61916212 if (bit_offset > 0) try w.print(", {f})", .{
6192 try f.fmtIntLiteral(try pt.intValue(bit_offset_ty, bit_offset)),
6213 try f.fmtIntLiteralDec(try pt.intValue(bit_offset_ty, bit_offset)),
61936214 });
61946215 if (cant_cast) try w.writeByte(')');
61956216 try f.object.dg.renderBuiltinInfo(w, field_int_ty, .bits);
......@@ -6291,7 +6312,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
62916312 return local;
62926313 }
62936314
6294 const w = &f.object.code.buffered_writer;
6315 const w = &f.object.code.writer;
62956316 try f.writeCValue(w, local, .Other);
62966317 try w.writeAll(" = ");
62976318
......@@ -6299,7 +6320,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
62996320 try f.writeCValue(w, operand, .Other)
63006321 else if (error_ty.errorSetIsEmpty(zcu))
63016322 try w.print("{f}", .{
6302 try f.fmtIntLiteral(try pt.intValue(try pt.errorIntType(), 0)),
6323 try f.fmtIntLiteralDec(try pt.intValue(try pt.errorIntType(), 0)),
63036324 })
63046325 else if (operand_is_ptr)
63056326 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
......@@ -6321,7 +6342,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
63216342 const operand_ty = f.typeOf(ty_op.operand);
63226343 const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
63236344
6324 const w = &f.object.code.buffered_writer;
6345 const w = &f.object.code.writer;
63256346 if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
63266347 if (!is_ptr) return .none;
63276348
......@@ -6363,7 +6384,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
63636384 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
63646385 .is_null, .payload => {
63656386 const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete);
6366 const w = &f.object.code.buffered_writer;
6387 const w = &f.object.code.writer;
63676388 const local = try f.allocLocal(inst, inst_ty);
63686389 {
63696390 const a = try Assignment.start(f, w, .bool);
......@@ -6399,7 +6420,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
63996420 const err = try f.resolveInst(ty_op.operand);
64006421 try reap(f, inst, &.{ty_op.operand});
64016422
6402 const w = &f.object.code.buffered_writer;
6423 const w = &f.object.code.writer;
64036424 const local = try f.allocLocal(inst, inst_ty);
64046425
64056426 if (repr_is_err and err == .local and err.local == local.new_local) {
......@@ -6430,7 +6451,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
64306451fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
64316452 const pt = f.object.dg.pt;
64326453 const zcu = pt.zcu;
6433 const w = &f.object.code.buffered_writer;
6454 const w = &f.object.code.writer;
64346455 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
64356456 const inst_ty = f.typeOfIndex(inst);
64366457 const operand = try f.resolveInst(ty_op.operand);
......@@ -6447,7 +6468,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
64476468 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));
64486469 try f.writeCValueDeref(w, operand);
64496470 try a.assign(f, w);
6450 try w.print("{f}", .{try f.fmtIntLiteral(no_err)});
6471 try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)});
64516472 try a.end(f, w);
64526473 return .none;
64536474 }
......@@ -6455,7 +6476,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
64556476 const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete));
64566477 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" });
64576478 try a.assign(f, w);
6458 try w.print("{f}", .{try f.fmtIntLiteral(no_err)});
6479 try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)});
64596480 try a.end(f, w);
64606481 }
64616482
......@@ -6499,7 +6520,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
64996520 const err_ty = inst_ty.errorUnionSet(zcu);
65006521 try reap(f, inst, &.{ty_op.operand});
65016522
6502 const w = &f.object.code.buffered_writer;
6523 const w = &f.object.code.writer;
65036524 const local = try f.allocLocal(inst, inst_ty);
65046525 if (!repr_is_err) {
65056526 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));
......@@ -6526,7 +6547,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
65266547 const zcu = pt.zcu;
65276548 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
65286549
6529 const w = &f.object.code.buffered_writer;
6550 const w = &f.object.code.writer;
65306551 const operand = try f.resolveInst(un_op);
65316552 try reap(f, inst, &.{un_op});
65326553 const operand_ty = f.typeOf(un_op);
......@@ -6567,7 +6588,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
65676588 try reap(f, inst, &.{ty_op.operand});
65686589 const inst_ty = f.typeOfIndex(inst);
65696590 const ptr_ty = inst_ty.slicePtrFieldType(zcu);
6570 const w = &f.object.code.buffered_writer;
6591 const w = &f.object.code.writer;
65716592 const local = try f.allocLocal(inst, inst_ty);
65726593 const operand_ty = f.typeOf(ty_op.operand);
65736594 const array_ty = operand_ty.childType(zcu);
......@@ -6593,7 +6614,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
65936614 if (operand_child_ctype.info(ctype_pool) == .array) {
65946615 try w.writeByte('&');
65956616 try f.writeCValueDeref(w, operand);
6596 try w.print("[{f}]", .{try f.fmtIntLiteral(.zero_usize)});
6617 try w.print("[{f}]", .{try f.fmtIntLiteralDec(.zero_usize)});
65976618 } else try f.writeCValue(w, operand, .Other);
65986619 }
65996620 try a.end(f, w);
......@@ -6603,7 +6624,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
66036624 try f.writeCValueMember(w, local, .{ .identifier = "len" });
66046625 try a.assign(f, w);
66056626 try w.print("{f}", .{
6606 try f.fmtIntLiteral(try pt.intValue(.usize, array_ty.arrayLen(zcu))),
6627 try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))),
66076628 });
66086629 try a.end(f, w);
66096630 }
......@@ -6632,7 +6653,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
66326653 else
66336654 unreachable;
66346655
6635 const w = &f.object.code.buffered_writer;
6656 const w = &f.object.code.writer;
66366657 const local = try f.allocLocal(inst, inst_ty);
66376658 const v = try Vectorize.start(f, inst, w, operand_ty);
66386659 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
......@@ -6682,7 +6703,7 @@ fn airUnBuiltinCall(
66826703 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
66836704 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
66846705
6685 const w = &f.object.code.buffered_writer;
6706 const w = &f.object.code.writer;
66866707 const local = try f.allocLocal(inst, inst_ty);
66876708 const v = try Vectorize.start(f, inst, w, operand_ty);
66886709 if (!ref_ret) {
......@@ -6733,7 +6754,7 @@ fn airBinBuiltinCall(
67336754 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
67346755 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
67356756
6736 const w = &f.object.code.buffered_writer;
6757 const w = &f.object.code.writer;
67376758 const local = try f.allocLocal(inst, inst_ty);
67386759 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
67396760 const v = try Vectorize.start(f, inst, w, operand_ty);
......@@ -6784,7 +6805,7 @@ fn airCmpBuiltinCall(
67846805 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
67856806 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
67866807
6787 const w = &f.object.code.buffered_writer;
6808 const w = &f.object.code.writer;
67886809 const local = try f.allocLocal(inst, inst_ty);
67896810 const v = try Vectorize.start(f, inst, w, operand_ty);
67906811 if (!ref_ret) {
......@@ -6812,7 +6833,7 @@ fn airCmpBuiltinCall(
68126833 try w.writeByte(')');
68136834 if (!ref_ret) try w.print("{s}{f}", .{
68146835 compareOperatorC(operator),
6815 try f.fmtIntLiteral(try pt.intValue(.i32, 0)),
6836 try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)),
68166837 });
68176838 try w.writeByte(';');
68186839 try f.object.newline();
......@@ -6834,7 +6855,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
68346855 const ty = ptr_ty.childType(zcu);
68356856 const ctype = try f.ctypeFromType(ty, .complete);
68366857
6837 const w = &f.object.code.buffered_writer;
6858 const w = &f.object.code.writer;
68386859 const new_value_mat = try Materialize.start(f, inst, ty, new_value);
68396860 try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value });
68406861
......@@ -6941,7 +6962,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
69416962 const ptr = try f.resolveInst(pl_op.operand);
69426963 const operand = try f.resolveInst(extra.operand);
69436964
6944 const w = &f.object.code.buffered_writer;
6965 const w = &f.object.code.writer;
69456966 const operand_mat = try Materialize.start(f, inst, ty, operand);
69466967 try reap(f, inst, &.{ pl_op.operand, extra.operand });
69476968
......@@ -7002,7 +7023,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
70027023 ty;
70037024
70047025 const inst_ty = f.typeOfIndex(inst);
7005 const w = &f.object.code.buffered_writer;
7026 const w = &f.object.code.writer;
70067027 const local = try f.allocLocal(inst, inst_ty);
70077028
70087029 try w.writeAll("zig_atomic_load(");
......@@ -7034,7 +7055,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
70347055 const ptr = try f.resolveInst(bin_op.lhs);
70357056 const element = try f.resolveInst(bin_op.rhs);
70367057
7037 const w = &f.object.code.buffered_writer;
7058 const w = &f.object.code.writer;
70387059 const element_mat = try Materialize.start(f, inst, ty, element);
70397060 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
70407061
......@@ -7082,7 +7103,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
70827103 const elem_ty = f.typeOf(bin_op.rhs);
70837104 const elem_abi_size = elem_ty.abiSize(zcu);
70847105 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(zcu) else false;
7085 const w = &f.object.code.buffered_writer;
7106 const w = &f.object.code.writer;
70867107
70877108 if (val_is_undef) {
70887109 if (!safety) {
......@@ -7206,7 +7227,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV
72067227 const src_ptr = try f.resolveInst(bin_op.rhs);
72077228 const dest_ty = f.typeOf(bin_op.lhs);
72087229 const src_ty = f.typeOf(bin_op.rhs);
7209 const w = &f.object.code.buffered_writer;
7230 const w = &f.object.code.writer;
72107231
72117232 if (dest_ty.ptrSize(zcu) != .one) {
72127233 try w.writeAll("if (");
......@@ -7231,10 +7252,10 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV
72317252fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void {
72327253 const pt = f.object.dg.pt;
72337254 const zcu = pt.zcu;
7234 const w = &f.object.code.buffered_writer;
7255 const w = &f.object.code.writer;
72357256 switch (dest_ty.ptrSize(zcu)) {
72367257 .one => try w.print("{f}", .{
7237 try f.fmtIntLiteral(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))),
7258 try f.fmtIntLiteralDec(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))),
72387259 }),
72397260 .many, .c => unreachable,
72407261 .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }),
......@@ -7254,7 +7275,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
72547275 if (layout.tag_size == 0) return .none;
72557276 const tag_ty = union_ty.unionTagTypeSafety(zcu).?;
72567277
7257 const w = &f.object.code.buffered_writer;
7278 const w = &f.object.code.writer;
72587279 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
72597280 try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" });
72607281 try a.assign(f, w);
......@@ -7276,7 +7297,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
72767297 if (layout.tag_size == 0) return .none;
72777298
72787299 const inst_ty = f.typeOfIndex(inst);
7279 const w = &f.object.code.buffered_writer;
7300 const w = &f.object.code.writer;
72807301 const local = try f.allocLocal(inst, inst_ty);
72817302 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
72827303 try f.writeCValue(w, local, .Other);
......@@ -7294,7 +7315,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
72947315 const operand = try f.resolveInst(un_op);
72957316 try reap(f, inst, &.{un_op});
72967317
7297 const w = &f.object.code.buffered_writer;
7318 const w = &f.object.code.writer;
72987319 const local = try f.allocLocal(inst, inst_ty);
72997320 try f.writeCValue(w, local, .Other);
73007321 try w.print(" = {s}(", .{
......@@ -7310,7 +7331,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
73107331fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
73117332 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
73127333
7313 const w = &f.object.code.buffered_writer;
7334 const w = &f.object.code.writer;
73147335 const inst_ty = f.typeOfIndex(inst);
73157336 const operand = try f.resolveInst(un_op);
73167337 try reap(f, inst, &.{un_op});
......@@ -7335,7 +7356,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
73357356 const inst_ty = f.typeOfIndex(inst);
73367357 const inst_scalar_ty = inst_ty.scalarType(zcu);
73377358
7338 const w = &f.object.code.buffered_writer;
7359 const w = &f.object.code.writer;
73397360 const local = try f.allocLocal(inst, inst_ty);
73407361 const v = try Vectorize.start(f, inst, w, inst_ty);
73417362 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete));
......@@ -7360,7 +7381,7 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
73607381
73617382 const inst_ty = f.typeOfIndex(inst);
73627383
7363 const w = &f.object.code.buffered_writer;
7384 const w = &f.object.code.writer;
73647385 const local = try f.allocLocal(inst, inst_ty);
73657386 const v = try Vectorize.start(f, inst, w, inst_ty);
73667387 try f.writeCValue(w, local, .Other);
......@@ -7390,7 +7411,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
73907411 const operand = try f.resolveInst(unwrapped.operand);
73917412 const inst_ty = unwrapped.result_ty;
73927413
7393 const w = &f.object.code.buffered_writer;
7414 const w = &f.object.code.writer;
73947415 const local = try f.allocLocal(inst, inst_ty);
73957416 try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand
73967417 for (mask, 0..) |mask_elem, out_idx| {
......@@ -7424,7 +7445,7 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
74247445 const inst_ty = unwrapped.result_ty;
74257446 const elem_ty = inst_ty.childType(zcu);
74267447
7427 const w = &f.object.code.buffered_writer;
7448 const w = &f.object.code.writer;
74287449 const local = try f.allocLocal(inst, inst_ty);
74297450 try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands
74307451 for (mask, 0..) |mask_elem, out_idx| {
......@@ -7463,7 +7484,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
74637484 const operand = try f.resolveInst(reduce.operand);
74647485 try reap(f, inst, &.{reduce.operand});
74657486 const operand_ty = f.typeOf(reduce.operand);
7466 const w = &f.object.code.buffered_writer;
7487 const w = &f.object.code.writer;
74677488
74687489 const use_operator = scalar_ty.bitSize(zcu) <= 64;
74697490 const op: union(enum) {
......@@ -7613,7 +7634,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
76137634 }
76147635 }
76157636
7616 const w = &f.object.code.buffered_writer;
7637 const w = &f.object.code.writer;
76177638 const local = try f.allocLocal(inst, inst_ty);
76187639 switch (ip.indexToKey(inst_ty.toIntern())) {
76197640 inline .array_type, .vector_type => |info, tag| {
......@@ -7727,7 +7748,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
77277748 }
77287749
77297750 try w.print(", {f}", .{
7730 try f.fmtIntLiteral(try pt.intValue(bit_offset_ty, bit_offset)),
7751 try f.fmtIntLiteralDec(try pt.intValue(bit_offset_ty, bit_offset)),
77317752 });
77327753 try f.object.dg.renderBuiltinInfo(w, inst_ty, .bits);
77337754 try w.writeByte(')');
......@@ -7772,7 +7793,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
77727793 const payload = try f.resolveInst(extra.init);
77737794 try reap(f, inst, &.{extra.init});
77747795
7775 const w = &f.object.code.buffered_writer;
7796 const w = &f.object.code.writer;
77767797 const local = try f.allocLocal(inst, union_ty);
77777798 if (loaded_union.flagsUnordered(ip).layout == .@"packed") return f.moveCValue(inst, union_ty, payload);
77787799
......@@ -7785,7 +7806,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
77857806 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
77867807 try f.writeCValueMember(w, local, .{ .identifier = "tag" });
77877808 try a.assign(f, w);
7788 try w.print("{f}", .{try f.fmtIntLiteral(try tag_val.intFromEnum(tag_ty, pt))});
7809 try w.print("{f}", .{try f.fmtIntLiteralDec(try tag_val.intFromEnum(tag_ty, pt))});
77897810 try a.end(f, w);
77907811 }
77917812 break :field .{ .payload_identifier = field_name.toSlice(ip) };
......@@ -7808,7 +7829,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
78087829 const ptr = try f.resolveInst(prefetch.ptr);
78097830 try reap(f, inst, &.{prefetch.ptr});
78107831
7811 const w = &f.object.code.buffered_writer;
7832 const w = &f.object.code.writer;
78127833 switch (prefetch.cache) {
78137834 .data => {
78147835 try w.writeAll("zig_prefetch(");
......@@ -7830,7 +7851,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
78307851fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
78317852 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
78327853
7833 const w = &f.object.code.buffered_writer;
7854 const w = &f.object.code.writer;
78347855 const inst_ty = f.typeOfIndex(inst);
78357856 const local = try f.allocLocal(inst, inst_ty);
78367857 try f.writeCValue(w, local, .Other);
......@@ -7845,7 +7866,7 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
78457866fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
78467867 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
78477868
7848 const w = &f.object.code.buffered_writer;
7869 const w = &f.object.code.writer;
78497870 const inst_ty = f.typeOfIndex(inst);
78507871 const operand = try f.resolveInst(pl_op.operand);
78517872 try reap(f, inst, &.{pl_op.operand});
......@@ -7874,7 +7895,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
78747895 const inst_ty = f.typeOfIndex(inst);
78757896 const inst_scalar_ty = inst_ty.scalarType(zcu);
78767897
7877 const w = &f.object.code.buffered_writer;
7898 const w = &f.object.code.writer;
78787899 const local = try f.allocLocal(inst, inst_ty);
78797900 const v = try Vectorize.start(f, inst, w, inst_ty);
78807901 try f.writeCValue(w, local, .Other);
......@@ -7899,7 +7920,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
78997920
79007921fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {
79017922 const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
7902 const w = &f.object.code.buffered_writer;
7923 const w = &f.object.code.writer;
79037924 const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));
79047925 try f.writeCValue(w, local, .Other);
79057926 try w.writeAll(" = ");
......@@ -7917,7 +7938,7 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
79177938 const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function;
79187939 assert(function_info.varargs);
79197940
7920 const w = &f.object.code.buffered_writer;
7941 const w = &f.object.code.writer;
79217942 const local = try f.allocLocal(inst, inst_ty);
79227943 try w.writeAll("va_start(*(va_list *)&");
79237944 try f.writeCValue(w, local, .Other);
......@@ -7937,7 +7958,7 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
79377958 const va_list = try f.resolveInst(ty_op.operand);
79387959 try reap(f, inst, &.{ty_op.operand});
79397960
7940 const w = &f.object.code.buffered_writer;
7961 const w = &f.object.code.writer;
79417962 const local = try f.allocLocal(inst, inst_ty);
79427963 try f.writeCValue(w, local, .Other);
79437964 try w.writeAll(" = va_arg(*(va_list *)");
......@@ -7955,7 +7976,7 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
79557976 const va_list = try f.resolveInst(un_op);
79567977 try reap(f, inst, &.{un_op});
79577978
7958 const w = &f.object.code.buffered_writer;
7979 const w = &f.object.code.writer;
79597980 try w.writeAll("va_end(*(va_list *)");
79607981 try f.writeCValue(w, va_list, .Other);
79617982 try w.writeAll(");");
......@@ -7970,7 +7991,7 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
79707991 const va_list = try f.resolveInst(ty_op.operand);
79717992 try reap(f, inst, &.{ty_op.operand});
79727993
7973 const w = &f.object.code.buffered_writer;
7994 const w = &f.object.code.writer;
79747995 const local = try f.allocLocal(inst, inst_ty);
79757996 try w.writeAll("va_copy(*(va_list *)&");
79767997 try f.writeCValue(w, local, .Other);
......@@ -8136,8 +8157,8 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
81368157const StringLiteral = struct {
81378158 len: usize,
81388159 cur_len: usize,
8139 start_count: usize,
81408160 w: *Writer,
8161 first: bool,
81418162
81428163 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
81438164 // regardless of the length of the string literal initializing it. Array initializer syntax is
......@@ -8154,8 +8175,8 @@ const StringLiteral = struct {
81548175 return .{
81558176 .cur_len = 0,
81568177 .len = len,
8157 .start_count = w.count,
81588178 .w = w,
8179 .first = true,
81598180 };
81608181 }
81618182
......@@ -8175,50 +8196,83 @@ const StringLiteral = struct {
81758196 }
81768197 }
81778198
8178 fn writeStringLiteralChar(sl: *StringLiteral, c: u8) Writer.Error!void {
8199 fn writeStringLiteralChar(sl: *StringLiteral, c: u8) Writer.Error!usize {
8200 const w = sl.w;
81798201 switch (c) {
8180 7 => try sl.w.writeAll("\\a"),
8181 8 => try sl.w.writeAll("\\b"),
8182 '\t' => try sl.w.writeAll("\\t"),
8183 '\n' => try sl.w.writeAll("\\n"),
8184 11 => try sl.w.writeAll("\\v"),
8185 12 => try sl.w.writeAll("\\f"),
8186 '\r' => try sl.w.writeAll("\\r"),
8187 '"', '\'', '?', '\\' => try sl.w.print("\\{c}", .{c}),
8188 else => switch (c) {
8189 ' '...'~' => try sl.w.writeByte(c),
8190 else => try sl.w.print("\\{o:0>3}", .{c}),
8202 7 => {
8203 try w.writeAll("\\a");
8204 return 2;
8205 },
8206 8 => {
8207 try w.writeAll("\\b");
8208 return 2;
8209 },
8210 '\t' => {
8211 try w.writeAll("\\t");
8212 return 2;
8213 },
8214 '\n' => {
8215 try w.writeAll("\\n");
8216 return 2;
8217 },
8218 11 => {
8219 try w.writeAll("\\v");
8220 return 2;
8221 },
8222 12 => {
8223 try w.writeAll("\\f");
8224 return 2;
8225 },
8226 '\r' => {
8227 try w.writeAll("\\r");
8228 return 2;
8229 },
8230 '"', '\'', '?', '\\' => {
8231 try w.print("\\{c}", .{c});
8232 return 2;
8233 },
8234 ' '...'!', '#'...'&', '('...'>', '@'...'[', ']'...'~' => {
8235 try w.writeByte(c);
8236 return 1;
8237 },
8238 else => {
8239 var buf: [4]u8 = undefined;
8240 const printed = std.fmt.bufPrint(&buf, "\\{o:0>3}", .{c}) catch unreachable;
8241 try w.writeAll(printed);
8242 return printed.len;
81918243 },
81928244 }
81938245 }
81948246
81958247 pub fn writeChar(sl: *StringLiteral, c: u8) Writer.Error!void {
81968248 if (sl.len <= max_string_initializer_len) {
8197 if (sl.cur_len == 0 and sl.w.count - sl.start_count > 1)
8198 try sl.w.writeAll("\"\"");
8249 if (sl.cur_len == 0 and !sl.first) try sl.w.writeAll("\"\"");
81998250
8200 const count = sl.w.count;
8201 try sl.writeStringLiteralChar(c);
8202 const char_len = sl.w.count - count;
8251 const char_len = try sl.writeStringLiteralChar(c);
82038252 assert(char_len <= max_char_len);
82048253 sl.cur_len += char_len;
82058254
8206 if (sl.cur_len >= max_literal_len) sl.cur_len = 0;
8255 if (sl.cur_len >= max_literal_len) {
8256 sl.cur_len = 0;
8257 sl.first = false;
8258 }
82078259 } else {
8208 if (sl.w.count - sl.start_count > 1) try sl.w.writeByte(',');
8209 try sl.w.print("'\\x{x}'", .{c});
8260 if (!sl.first) try sl.w.writeByte(',');
8261 var buf: [6]u8 = undefined;
8262 const printed = std.fmt.bufPrint(&buf, "'\\x{x}'", .{c}) catch unreachable;
8263 try sl.w.writeAll(printed);
8264 sl.cur_len += printed.len;
8265 sl.first = false;
82108266 }
82118267 }
82128268};
82138269
8214const FormatStringContext = struct { str: []const u8, sentinel: ?u8 };
8215fn formatStringLiteral(
8216 data: FormatStringContext,
8217 w: *Writer,
8218 comptime fmt: []const u8,
8219) Writer.Error!void {
8220 if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt);
8270const FormatStringContext = struct {
8271 str: []const u8,
8272 sentinel: ?u8,
8273};
82218274
8275fn formatStringLiteral(data: FormatStringContext, w: *std.io.Writer) std.io.Writer.Error!void {
82228276 var literal: StringLiteral = .init(w, data.str.len + @intFromBool(data.sentinel != null));
82238277 try literal.start();
82248278 for (data.str) |c| try literal.writeChar(c);
......@@ -8226,7 +8280,7 @@ fn formatStringLiteral(
82268280 try literal.end();
82278281}
82288282
8229fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) {
8283fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(FormatStringContext, formatStringLiteral) {
82308284 return .{ .data = .{ .str = str, .sentinel = sentinel } };
82318285}
82328286
......@@ -8242,12 +8296,10 @@ const FormatIntLiteralContext = struct {
82428296 kind: CType.Kind,
82438297 ctype: CType,
82448298 val: Value,
8299 base: u8,
8300 case: std.fmt.Case,
82458301};
8246fn formatIntLiteral(
8247 data: FormatIntLiteralContext,
8248 w: *Writer,
8249 comptime fmt: []const u8,
8250) Writer.Error!void {
8302fn formatIntLiteral(data: FormatIntLiteralContext, w: *std.io.Writer) std.io.Writer.Error!void {
82518303 const pt = data.dg.pt;
82528304 const zcu = pt.zcu;
82538305 const target = &data.dg.mod.resolved_target.result;
......@@ -8337,32 +8389,14 @@ fn formatIntLiteral(
83378389 if (!int.positive) try w.writeByte('-');
83388390 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);
83398391
8340 const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) {
8341 0 => .{ .base = 10 },
8342 1 => switch (fmt[0]) {
8343 'b' => style: {
8344 try w.writeAll("0b");
8345 break :style .{ .base = 2 };
8346 },
8347 'o' => style: {
8348 try w.writeByte('0');
8349 break :style .{ .base = 8 };
8350 },
8351 'd' => .{ .base = 10 },
8352 'x', 'X' => |base| style: {
8353 try w.writeAll("0x");
8354 break :style .{ .base = 16, .case = switch (base) {
8355 'x' => .lower,
8356 'X' => .upper,
8357 else => unreachable,
8358 } };
8359 },
8360 else => @compileError("Invalid fmt: " ++ fmt),
8361 },
8362 else => @compileError("Invalid fmt: " ++ fmt),
8363 };
8364
8365 const string = int.abs().toStringAlloc(allocator, style.base, style.case) catch
8392 switch (data.base) {
8393 2 => try w.writeAll("0b"),
8394 8 => try w.writeByte('0'),
8395 10 => {},
8396 16 => try w.writeAll("0x"),
8397 else => unreachable,
8398 }
8399 const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch
83668400 return error.WriteFailed;
83678401 defer allocator.free(string);
83688402 try w.writeAll(string);
......@@ -8418,7 +8452,9 @@ fn formatIntLiteral(
84188452 .ctype = c_limb_ctype,
84198453 .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch
84208454 return error.WriteFailed,
8421 }, w, fmt);
8455 .base = data.base,
8456 .case = data.case,
8457 }, w);
84228458 }
84238459 }
84248460 try data.ctype.renderLiteralSuffix(w, ctype_pool);
......@@ -8499,11 +8535,11 @@ const Vectorize = struct {
84998535
85008536 try w.writeAll("for (");
85018537 try f.writeCValue(w, local, .Other);
8502 try w.print(" = {fd}; ", .{try f.fmtIntLiteral(.zero_usize)});
8538 try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)});
85038539 try f.writeCValue(w, local, .Other);
8504 try w.print(" < {fd}; ", .{try f.fmtIntLiteral(try pt.intValue(.usize, ty.vectorLen(zcu)))});
8540 try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))});
85058541 try f.writeCValue(w, local, .Other);
8506 try w.print(" += {fd}) {{\n", .{try f.fmtIntLiteral(.one_usize)});
8542 try w.print(" += {f}) {{\n", .{try f.fmtIntLiteralDec(.one_usize)});
85078543 f.object.indent();
85088544 try f.object.newline();
85098545
src/codegen/llvm.zig+40-47
......@@ -21,11 +21,11 @@ const Air = @import("../Air.zig");
2121const Value = @import("../Value.zig");
2222const Type = @import("../Type.zig");
2323const x86_64_abi = @import("../arch/x86_64/abi.zig");
24const wasm_c_abi = @import("../arch/wasm/abi.zig");
25const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
26const arm_c_abi = @import("../arch/arm/abi.zig");
24const wasm_c_abi = @import("wasm/abi.zig");
25const aarch64_c_abi = @import("aarch64/abi.zig");
26const arm_c_abi = @import("arm/abi.zig");
2727const riscv_c_abi = @import("../arch/riscv64/abi.zig");
28const mips_c_abi = @import("../arch/mips/abi.zig");
28const mips_c_abi = @import("mips/abi.zig");
2929const dev = @import("../dev.zig");
3030
3131const target_util = @import("../target.zig");
......@@ -945,7 +945,9 @@ pub const Object = struct {
945945 if (std.mem.eql(u8, path, "-")) {
946946 o.builder.dump();
947947 } else {
948 _ = o.builder.printToFile(path);
948 o.builder.printToFilePath(std.fs.cwd(), path) catch |err| {
949 log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) });
950 };
949951 }
950952 }
951953
......@@ -1053,6 +1055,7 @@ pub const Object = struct {
10531055 comp.data_sections,
10541056 float_abi,
10551057 if (target_util.llvmMachineAbi(&comp.root_mod.resolved_target.result)) |s| s.ptr else null,
1058 target_util.useEmulatedTls(&comp.root_mod.resolved_target.result),
10561059 );
10571060 errdefer target_machine.dispose();
10581061
......@@ -2765,7 +2768,7 @@ pub const Object = struct {
27652768 llvm_arg_i += 1;
27662769 }
27672770
2768 if (fn_info.cc == .@"async") {
2771 if (fn_info.cc == .async) {
27692772 @panic("TODO: LLVM backend lower async function");
27702773 }
27712774
......@@ -2917,7 +2920,7 @@ pub const Object = struct {
29172920 try attributes.addFnAttr(.nounwind, &o.builder);
29182921 if (owner_mod.unwind_tables != .none) {
29192922 try attributes.addFnAttr(
2920 .{ .uwtable = if (owner_mod.unwind_tables == .@"async") .@"async" else .sync },
2923 .{ .uwtable = if (owner_mod.unwind_tables == .async) .async else .sync },
29212924 &o.builder,
29222925 );
29232926 }
......@@ -5280,7 +5283,7 @@ pub const FuncGen = struct {
52805283 switch (modifier) {
52815284 .auto, .always_tail => {},
52825285 .never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder),
5283 .async_kw, .no_async, .always_inline, .compile_time => unreachable,
5286 .no_suspend, .always_inline, .compile_time => unreachable,
52845287 }
52855288
52865289 const ret_ptr = if (!sret) null else blk: {
......@@ -5288,7 +5291,7 @@ pub const FuncGen = struct {
52885291 try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);
52895292
52905293 const alignment = return_type.abiAlignment(zcu).toLlvm();
5291 const ret_ptr = try self.buildAllocaWorkaround(return_type, alignment);
5294 const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment);
52925295 try llvm_args.append(ret_ptr);
52935296 break :blk ret_ptr;
52945297 };
......@@ -5336,7 +5339,7 @@ pub const FuncGen = struct {
53365339
53375340 const alignment = param_ty.abiAlignment(zcu).toLlvm();
53385341 const param_llvm_ty = try o.lowerType(param_ty);
5339 const arg_ptr = try self.buildAllocaWorkaround(param_ty, alignment);
5342 const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment);
53405343 if (isByRef(param_ty, zcu)) {
53415344 const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, "");
53425345 _ = try self.wip.store(.normal, loaded, arg_ptr, alignment);
......@@ -5359,7 +5362,7 @@ pub const FuncGen = struct {
53595362 // LLVM does not allow bitcasting structs so we must allocate
53605363 // a local, store as one type, and then load as another type.
53615364 const alignment = param_ty.abiAlignment(zcu).toLlvm();
5362 const int_ptr = try self.buildAllocaWorkaround(param_ty, alignment);
5365 const int_ptr = try self.buildAlloca(int_llvm_ty, alignment);
53635366 _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment);
53645367 const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, "");
53655368 try llvm_args.append(loaded);
......@@ -5495,7 +5498,7 @@ pub const FuncGen = struct {
54955498 .auto, .never_inline => .normal,
54965499 .never_tail => .notail,
54975500 .always_tail => .musttail,
5498 .async_kw, .no_async, .always_inline, .compile_time => unreachable,
5501 .no_suspend, .always_inline, .compile_time => unreachable,
54995502 },
55005503 toLlvmCallConvTag(fn_info.cc, target).?,
55015504 try attributes.finish(&o.builder),
......@@ -5734,7 +5737,7 @@ pub const FuncGen = struct {
57345737 const llvm_va_list_ty = try o.lowerType(va_list_ty);
57355738
57365739 const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm();
5737 const dest_list = try self.buildAllocaWorkaround(va_list_ty, result_alignment);
5740 const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment);
57385741
57395742 _ = try self.wip.callIntrinsic(.normal, .none, .va_copy, &.{dest_list.typeOfWip(&self.wip)}, &.{ dest_list, src_list }, "");
57405743 return if (isByRef(va_list_ty, zcu))
......@@ -5759,7 +5762,7 @@ pub const FuncGen = struct {
57595762 const llvm_va_list_ty = try o.lowerType(va_list_ty);
57605763
57615764 const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm();
5762 const dest_list = try self.buildAllocaWorkaround(va_list_ty, result_alignment);
5765 const dest_list = try self.buildAlloca(llvm_va_list_ty, result_alignment);
57635766
57645767 _ = try self.wip.callIntrinsic(.normal, .none, .va_start, &.{dest_list.typeOfWip(&self.wip)}, &.{dest_list}, "");
57655768 return if (isByRef(va_list_ty, zcu))
......@@ -8037,7 +8040,7 @@ pub const FuncGen = struct {
80378040 self.ret_ptr
80388041 else brk: {
80398042 const alignment = optional_ty.abiAlignment(zcu).toLlvm();
8040 const optional_ptr = try self.buildAllocaWorkaround(optional_ty, alignment);
8043 const optional_ptr = try self.buildAlloca(llvm_optional_ty, alignment);
80418044 break :brk optional_ptr;
80428045 };
80438046
......@@ -8074,7 +8077,7 @@ pub const FuncGen = struct {
80748077 self.ret_ptr
80758078 else brk: {
80768079 const alignment = err_un_ty.abiAlignment(pt.zcu).toLlvm();
8077 const result_ptr = try self.buildAllocaWorkaround(err_un_ty, alignment);
8080 const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment);
80788081 break :brk result_ptr;
80798082 };
80808083
......@@ -8113,7 +8116,7 @@ pub const FuncGen = struct {
81138116 self.ret_ptr
81148117 else brk: {
81158118 const alignment = err_un_ty.abiAlignment(zcu).toLlvm();
8116 const result_ptr = try self.buildAllocaWorkaround(err_un_ty, alignment);
8119 const result_ptr = try self.buildAlloca(err_un_llvm_ty, alignment);
81178120 break :brk result_ptr;
81188121 };
81198122
......@@ -8647,7 +8650,7 @@ pub const FuncGen = struct {
86478650
86488651 if (isByRef(inst_ty, zcu)) {
86498652 const result_alignment = inst_ty.abiAlignment(zcu).toLlvm();
8650 const alloca_inst = try self.buildAllocaWorkaround(inst_ty, result_alignment);
8653 const alloca_inst = try self.buildAlloca(llvm_inst_ty, result_alignment);
86518654 {
86528655 const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, result_index, "");
86538656 _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment);
......@@ -9007,7 +9010,7 @@ pub const FuncGen = struct {
90079010
90089011 if (isByRef(dest_ty, zcu)) {
90099012 const result_alignment = dest_ty.abiAlignment(zcu).toLlvm();
9010 const alloca_inst = try self.buildAllocaWorkaround(dest_ty, result_alignment);
9013 const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment);
90119014 {
90129015 const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, "");
90139016 _ = try self.wip.store(.normal, result, field_ptr, result_alignment);
......@@ -9432,7 +9435,7 @@ pub const FuncGen = struct {
94329435 return self.ng.todo("implement bitcast vector to non-ref array", .{});
94339436 }
94349437 const alignment = inst_ty.abiAlignment(zcu).toLlvm();
9435 const array_ptr = try self.buildAllocaWorkaround(inst_ty, alignment);
9438 const array_ptr = try self.buildAlloca(llvm_dest_ty, alignment);
94369439 const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8;
94379440 if (bitcast_ok) {
94389441 _ = try self.wip.store(.normal, operand, array_ptr, alignment);
......@@ -9493,7 +9496,7 @@ pub const FuncGen = struct {
94939496
94949497 if (result_is_ref) {
94959498 const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm();
9496 const result_ptr = try self.buildAllocaWorkaround(inst_ty, alignment);
9499 const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment);
94979500 _ = try self.wip.store(.normal, operand, result_ptr, alignment);
94989501 return result_ptr;
94999502 }
......@@ -9506,7 +9509,7 @@ pub const FuncGen = struct {
95069509 // but LLVM won't let us bitcast struct values or vectors with padding bits.
95079510 // Therefore, we store operand to alloca, then load for result.
95089511 const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm();
9509 const result_ptr = try self.buildAllocaWorkaround(inst_ty, alignment);
9512 const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment);
95109513 _ = try self.wip.store(.normal, operand, result_ptr, alignment);
95119514 return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, "");
95129515 }
......@@ -9615,9 +9618,9 @@ pub const FuncGen = struct {
96159618 if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu))
96169619 return (try o.lowerPtrToVoid(ptr_ty)).toValue();
96179620
9618 //const pointee_llvm_ty = try o.lowerType(pointee_type);
9621 const pointee_llvm_ty = try o.lowerType(pointee_type);
96199622 const alignment = ptr_ty.ptrAlignment(zcu).toLlvm();
9620 return self.buildAllocaWorkaround(pointee_type, alignment);
9623 return self.buildAlloca(pointee_llvm_ty, alignment);
96219624 }
96229625
96239626 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
......@@ -9629,9 +9632,9 @@ pub const FuncGen = struct {
96299632 if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu))
96309633 return (try o.lowerPtrToVoid(ptr_ty)).toValue();
96319634 if (self.ret_ptr != .none) return self.ret_ptr;
9632 //const ret_llvm_ty = try o.lowerType(ret_ty);
9635 const ret_llvm_ty = try o.lowerType(ret_ty);
96339636 const alignment = ptr_ty.ptrAlignment(zcu).toLlvm();
9634 return self.buildAllocaWorkaround(ret_ty, alignment);
9637 return self.buildAlloca(ret_llvm_ty, alignment);
96359638 }
96369639
96379640 /// Use this instead of builder.buildAlloca, because this function makes sure to
......@@ -9645,16 +9648,6 @@ pub const FuncGen = struct {
96459648 return buildAllocaInner(&self.wip, llvm_ty, alignment, target);
96469649 }
96479650
9648 // Workaround for https://github.com/ziglang/zig/issues/16392
9649 fn buildAllocaWorkaround(
9650 self: *FuncGen,
9651 ty: Type,
9652 alignment: Builder.Alignment,
9653 ) Allocator.Error!Builder.Value {
9654 const o = self.ng.object;
9655 return self.buildAlloca(try o.builder.arrayType(ty.abiSize(o.pt.zcu), .i8), alignment);
9656 }
9657
96589651 fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value {
96599652 const o = self.ng.object;
96609653 const pt = o.pt;
......@@ -10693,7 +10686,7 @@ pub const FuncGen = struct {
1069310686 const llvm_result_ty = accum_init.typeOfWip(&self.wip);
1069410687
1069510688 // Allocate and initialize our mutable variables
10696 const i_ptr = try self.buildAllocaWorkaround(Type.usize, .default);
10689 const i_ptr = try self.buildAlloca(usize_ty, .default);
1069710690 _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default);
1069810691 const accum_ptr = try self.buildAlloca(llvm_result_ty, .default);
1069910692 _ = try self.wip.store(.normal, accum_init, accum_ptr, .default);
......@@ -10906,7 +10899,7 @@ pub const FuncGen = struct {
1090610899 // TODO in debug builds init to undef so that the padding will be 0xaa
1090710900 // even if we fully populate the fields.
1090810901 const alignment = result_ty.abiAlignment(zcu).toLlvm();
10909 const alloca_inst = try self.buildAllocaWorkaround(result_ty, alignment);
10902 const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment);
1091010903
1091110904 for (elements, 0..) |elem, i| {
1091210905 if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue;
......@@ -10943,7 +10936,7 @@ pub const FuncGen = struct {
1094310936 const llvm_usize = try o.lowerType(Type.usize);
1094410937 const usize_zero = try o.builder.intValue(llvm_usize, 0);
1094510938 const alignment = result_ty.abiAlignment(zcu).toLlvm();
10946 const alloca_inst = try self.buildAllocaWorkaround(result_ty, alignment);
10939 const alloca_inst = try self.buildAlloca(llvm_result_ty, alignment);
1094710940
1094810941 const array_info = result_ty.arrayInfo(zcu);
1094910942 const elem_ptr_ty = try pt.ptrType(.{
......@@ -11018,7 +11011,7 @@ pub const FuncGen = struct {
1101811011 // We must construct the correct unnamed struct type here, in order to then set
1101911012 // the fields appropriately.
1102011013 const alignment = layout.abi_align.toLlvm();
11021 const result_ptr = try self.buildAllocaWorkaround(union_ty, alignment);
11014 const result_ptr = try self.buildAlloca(union_llvm_ty, alignment);
1102211015 const llvm_payload = try self.resolveInst(extra.init);
1102311016 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]);
1102411017 const field_llvm_ty = try o.lowerType(field_ty);
......@@ -11315,7 +11308,7 @@ pub const FuncGen = struct {
1131511308
1131611309 if (isByRef(optional_ty, zcu)) {
1131711310 const payload_alignment = optional_ty.abiAlignment(pt.zcu).toLlvm();
11318 const alloca_inst = try self.buildAllocaWorkaround(optional_ty, payload_alignment);
11311 const alloca_inst = try self.buildAlloca(optional_llvm_ty, payload_alignment);
1131911312
1132011313 {
1132111314 const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, "");
......@@ -11458,10 +11451,10 @@ pub const FuncGen = struct {
1145811451 ) !Builder.Value {
1145911452 const o = fg.ng.object;
1146011453 const pt = o.pt;
11461 //const pointee_llvm_ty = try o.lowerType(pointee_type);
11454 const pointee_llvm_ty = try o.lowerType(pointee_type);
1146211455 const result_align = InternPool.Alignment.fromLlvm(ptr_alignment)
1146311456 .max(pointee_type.abiAlignment(pt.zcu)).toLlvm();
11464 const result_ptr = try fg.buildAllocaWorkaround(pointee_type, result_align);
11457 const result_ptr = try fg.buildAlloca(pointee_llvm_ty, result_align);
1146511458 const size_bytes = pointee_type.abiSize(pt.zcu);
1146611459 _ = try fg.wip.callMemCpy(
1146711460 result_ptr,
......@@ -11522,7 +11515,7 @@ pub const FuncGen = struct {
1152211515
1152311516 if (isByRef(elem_ty, zcu)) {
1152411517 const result_align = elem_ty.abiAlignment(zcu).toLlvm();
11525 const result_ptr = try self.buildAllocaWorkaround(elem_ty, result_align);
11518 const result_ptr = try self.buildAlloca(elem_llvm_ty, result_align);
1152611519
1152711520 const same_size_int = try o.builder.intType(@intCast(elem_bits));
1152811521 const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, "");
......@@ -11878,7 +11871,7 @@ fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const s
1187811871 }
1187911872 return switch (cc_tag) {
1188011873 .@"inline" => unreachable,
11881 .auto, .@"async" => .fastcc,
11874 .auto, .async => .fastcc,
1188211875 .naked => .ccc,
1188311876 .x86_64_sysv => .x86_64_sysvcc,
1188411877 .x86_64_win => .win64cc,
......@@ -12386,7 +12379,7 @@ const ParamTypeIterator = struct {
1238612379 return .byval;
1238712380 }
1238812381 },
12389 .@"async" => {
12382 .async => {
1239012383 @panic("TODO implement async function lowering in the LLVM backend");
1239112384 },
1239212385 .x86_64_sysv => return it.nextSystemV(ty),
......@@ -12641,7 +12634,7 @@ fn ccAbiPromoteInt(
1264112634) ?std.builtin.Signedness {
1264212635 const target = zcu.getTarget();
1264312636 switch (cc) {
12644 .auto, .@"inline", .@"async" => return null,
12637 .auto, .@"inline", .async => return null,
1264512638 else => {},
1264612639 }
1264712640 const int_info = switch (ty.zigTypeTag(zcu)) {
src/codegen/llvm/bindings.zig+1
......@@ -79,6 +79,7 @@ pub const TargetMachine = opaque {
7979 data_sections: bool,
8080 float_abi: FloatABI,
8181 abi_name: ?[*:0]const u8,
82 emulated_tls: bool,
8283 ) *TargetMachine;
8384
8485 pub const dispose = LLVMDisposeTargetMachine;
src/codegen/mips/abi.zig created+84
......@@ -0,0 +1,84 @@
1const std = @import("std");
2const Type = @import("../../Type.zig");
3const Zcu = @import("../../Zcu.zig");
4const assert = std.debug.assert;
5
6pub const Class = union(enum) {
7 memory,
8 byval,
9 i32_array: u8,
10};
11
12pub const Context = enum { ret, arg };
13
14pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
15 const target = zcu.getTarget();
16 std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
17
18 const max_direct_size = target.ptrBitWidth() * 2;
19 switch (ty.zigTypeTag(zcu)) {
20 .@"struct" => {
21 const bit_size = ty.bitSize(zcu);
22 if (ty.containerLayout(zcu) == .@"packed") {
23 if (bit_size > max_direct_size) return .memory;
24 return .byval;
25 }
26 if (bit_size > max_direct_size) return .memory;
27 // TODO: for bit_size <= 32 using byval is more correct, but that needs inreg argument attribute
28 const count = @as(u8, @intCast(std.mem.alignForward(u64, bit_size, 32) / 32));
29 return .{ .i32_array = count };
30 },
31 .@"union" => {
32 const bit_size = ty.bitSize(zcu);
33 if (ty.containerLayout(zcu) == .@"packed") {
34 if (bit_size > max_direct_size) return .memory;
35 return .byval;
36 }
37 if (bit_size > max_direct_size) return .memory;
38
39 return .byval;
40 },
41 .bool => return .byval,
42 .float => return .byval,
43 .int, .@"enum", .error_set => {
44 return .byval;
45 },
46 .vector => {
47 const elem_type = ty.elemType2(zcu);
48 switch (elem_type.zigTypeTag(zcu)) {
49 .bool, .int => {
50 const bit_size = ty.bitSize(zcu);
51 if (ctx == .ret and bit_size > 128) return .memory;
52 if (bit_size > 512) return .memory;
53 // TODO: byval vector arguments with non power of 2 size need inreg attribute
54 return .byval;
55 },
56 .float => return .memory,
57 else => unreachable,
58 }
59 },
60 .optional => {
61 std.debug.assert(ty.isPtrLikeOptional(zcu));
62 return .byval;
63 },
64 .pointer => {
65 std.debug.assert(!ty.isSlice(zcu));
66 return .byval;
67 },
68 .error_union,
69 .frame,
70 .@"anyframe",
71 .noreturn,
72 .void,
73 .type,
74 .comptime_float,
75 .comptime_int,
76 .undefined,
77 .null,
78 .@"fn",
79 .@"opaque",
80 .enum_literal,
81 .array,
82 => unreachable,
83 }
84}
src/codegen/spirv.zig+5-3
......@@ -1259,11 +1259,13 @@ const NavGen = struct {
12591259 }
12601260
12611261 // Turn a Zig type's name into a cache reference.
1262 fn resolveTypeName(self: *NavGen, ty: Type) Allocator.Error![]const u8 {
1262 fn resolveTypeName(self: *NavGen, ty: Type) ![]const u8 {
12631263 var aw: std.io.Writer.Allocating = .init(self.gpa);
12641264 defer aw.deinit();
1265 ty.print(&aw.interface, self.pt) catch return error.OutOfMemory;
1266 return aw.toOwnedSlice();
1265 ty.print(&aw.writer, self.pt) catch |err| switch (err) {
1266 error.WriteFailed => return error.OutOfMemory,
1267 };
1268 return try aw.toOwnedSlice();
12671269 }
12681270
12691271 /// Create an integer type suitable for storing at least 'bits' bits.
src/codegen/spirv/spec.zig+4-4
......@@ -1,6 +1,7 @@
11//! This file is auto-generated by tools/gen_spirv_spec.zig.
22
33const std = @import("std");
4const assert = std.debug.assert;
45
56pub const Version = packed struct(Word) {
67 padding: u8 = 0,
......@@ -18,11 +19,10 @@ pub const IdResult = enum(Word) {
1819 none,
1920 _,
2021
21 pub fn format(self: IdResult, bw: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
22 comptime std.debug.assert(fmt.len == 0);
22 pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void {
2323 switch (self) {
24 .none => try bw.writeAll("(none)"),
25 else => try bw.print("%{}", .{@intFromEnum(self)}),
24 .none => try writer.writeAll("(none)"),
25 else => try writer.print("%{d}", .{@intFromEnum(self)}),
2626 }
2727 }
2828};
src/codegen/wasm/abi.zig created+87
......@@ -0,0 +1,87 @@
1//! Classifies Zig types to follow the C-ABI for Wasm.
2//! The convention for Wasm's C-ABI can be found at the tool-conventions repo:
3//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
4//! When not targeting the C-ABI, Zig is allowed to do derail from this convention.
5//! Note: Above mentioned document is not an official specification, therefore called a convention.
6
7const std = @import("std");
8const Target = std.Target;
9const assert = std.debug.assert;
10
11const Type = @import("../../Type.zig");
12const Zcu = @import("../../Zcu.zig");
13
14/// Defines how to pass a type as part of a function signature,
15/// both for parameters as well as return values.
16pub const Class = union(enum) {
17 direct: Type,
18 indirect,
19};
20
21/// Classifies a given Zig type to determine how they must be passed
22/// or returned as value within a wasm function.
23pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
24 const ip = &zcu.intern_pool;
25 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
26 switch (ty.zigTypeTag(zcu)) {
27 .int, .@"enum", .error_set => return .{ .direct = ty },
28 .float => return .{ .direct = ty },
29 .bool => return .{ .direct = ty },
30 .vector => return .{ .direct = ty },
31 .array => return .indirect,
32 .optional => {
33 assert(ty.isPtrLikeOptional(zcu));
34 return .{ .direct = ty };
35 },
36 .pointer => {
37 assert(!ty.isSlice(zcu));
38 return .{ .direct = ty };
39 },
40 .@"struct" => {
41 const struct_type = zcu.typeToStruct(ty).?;
42 if (struct_type.layout == .@"packed") {
43 return .{ .direct = ty };
44 }
45 if (struct_type.field_types.len > 1) {
46 // The struct type is non-scalar.
47 return .indirect;
48 }
49 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]);
50 const explicit_align = struct_type.fieldAlign(ip, 0);
51 if (explicit_align != .none) {
52 if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu)))
53 return .indirect;
54 }
55 return classifyType(field_ty, zcu);
56 },
57 .@"union" => {
58 const union_obj = zcu.typeToUnion(ty).?;
59 if (union_obj.flagsUnordered(ip).layout == .@"packed") {
60 return .{ .direct = ty };
61 }
62 const layout = ty.unionGetLayout(zcu);
63 assert(layout.tag_size == 0);
64 if (union_obj.field_types.len > 1) return .indirect;
65 const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
66 return classifyType(first_field_ty, zcu);
67 },
68 .error_union,
69 .frame,
70 .@"anyframe",
71 .noreturn,
72 .void,
73 .type,
74 .comptime_float,
75 .comptime_int,
76 .undefined,
77 .null,
78 .@"fn",
79 .@"opaque",
80 .enum_literal,
81 => unreachable,
82 }
83}
84
85pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool {
86 return scalar_ty.bitSize(zcu) > 64;
87}
src/deprecated.zig+10-2
......@@ -15,6 +15,14 @@ pub fn LinearFifo(comptime T: type) type {
1515 count: usize,
1616
1717 const Self = @This();
18<<<<<<<< HEAD:src/deprecated.zig
19|||||||| edf785db0f:lib/std/fifo.zig
20 pub const Reader = std.io.Reader(*Self, error{}, readFn);
21 pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
22========
23 pub const Reader = std.io.GenericReader(*Self, error{}, readFn);
24 pub const Writer = std.io.GenericWriter(*Self, error{OutOfMemory}, appendWrite);
25>>>>>>>> origin/master:lib/std/fifo.zig
1826
1927 pub fn init(allocator: Allocator) Self {
2028 return .{
......@@ -160,7 +168,7 @@ pub fn LinearFifo(comptime T: type) type {
160168 }
161169
162170 /// Same as `read` except it returns an error union
163 /// The purpose of this function existing is to match `std.io.Reader` API.
171 /// The purpose of this function existing is to match `std.io.GenericReader` API.
164172 fn readFn(self: *Self, dest: []u8) error{}!usize {
165173 return self.read(dest);
166174 }
......@@ -241,7 +249,7 @@ pub fn LinearFifo(comptime T: type) type {
241249 }
242250
243251 /// Same as `write` except it returns the number of bytes written, which is always the same
244 /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API.
252 /// as `bytes.len`. The purpose of this function existing is to match `std.io.GenericWriter` API.
245253 fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {
246254 try self.write(bytes);
247255 return bytes.len;
src/dev.zig+1
......@@ -154,6 +154,7 @@ pub const Env = enum {
154154 else => Env.ast_gen.supports(feature),
155155 },
156156 .cbe => switch (feature) {
157 .legalize,
157158 .c_backend,
158159 .c_linker,
159160 => true,
src/libs/freebsd.zig+2-2
......@@ -497,13 +497,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
497497 .lt => continue,
498498 .gt => {
499499 // TODO Expose via compile error mechanism instead of log.
500 log.warn("invalid target FreeBSD libc version: {}", .{target_version});
500 log.warn("invalid target FreeBSD libc version: {f}", .{target_version});
501501 return error.InvalidTargetLibCVersion;
502502 },
503503 }
504504 } else blk: {
505505 const latest_index = metadata.all_versions.len - 1;
506 log.warn("zig cannot build new FreeBSD libc version {}; providing instead {}", .{
506 log.warn("zig cannot build new FreeBSD libc version {f}; providing instead {f}", .{
507507 target_version, metadata.all_versions[latest_index],
508508 });
509509 break :blk latest_index;
src/libs/libcxx.zig+1-1
......@@ -325,7 +325,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
325325 // See the `-fno-exceptions` logic for WASI.
326326 // The old 32-bit x86 variant of SEH doesn't use tables.
327327 const unwind_tables: std.builtin.UnwindTables =
328 if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) .none else .@"async";
328 if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) .none else .async;
329329
330330 const config = Compilation.Config.resolve(.{
331331 .output_mode = output_mode,
src/libs/libtsan.zig+2-2
......@@ -48,7 +48,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
4848 const optimize_mode = comp.compilerRtOptMode();
4949 const strip = comp.compilerRtStrip();
5050 const unwind_tables: std.builtin.UnwindTables =
51 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async";
51 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .async;
5252 const link_libcpp = target.os.tag.isDarwin();
5353
5454 const config = Compilation.Config.resolve(.{
......@@ -268,7 +268,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
268268 const skip_linker_dependencies = !target.os.tag.isDarwin();
269269 const linker_allow_shlib_undefined = target.os.tag.isDarwin();
270270 const install_name = if (target.os.tag.isDarwin())
271 try std.fmt.allocPrintZ(arena, "@rpath/{s}", .{basename})
271 try std.fmt.allocPrintSentinel(arena, "@rpath/{s}", .{basename}, 0)
272272 else
273273 null;
274274 // Workaround for https://github.com/llvm/llvm-project/issues/97627
src/libs/libunwind.zig+1-1
......@@ -29,7 +29,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
2929 const output_mode = .Lib;
3030 const target = &comp.root_mod.resolved_target.result;
3131 const unwind_tables: std.builtin.UnwindTables =
32 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async";
32 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .async;
3333 const config = Compilation.Config.resolve(.{
3434 .output_mode = output_mode,
3535 .resolved_target = comp.root_mod.resolved_target,
src/libs/mingw.zig+4-7
......@@ -29,7 +29,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
2929 const target = comp.getTarget();
3030
3131 // The old 32-bit x86 variant of SEH doesn't use tables.
32 const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch != .x86) .@"async" else .none;
32 const unwind_tables: std.builtin.UnwindTables = if (target.cpu.arch != .x86) .async else .none;
3333
3434 switch (crt_file) {
3535 .crt2_o => {
......@@ -325,7 +325,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
325325
326326 for (aro_comp.diagnostics.list.items) |diagnostic| {
327327 if (diagnostic.kind == .@"fatal error" or diagnostic.kind == .@"error") {
328 aro.Diagnostics.render(&aro_comp, std.io.tty.detectConfig(.stderr()));
328 aro.Diagnostics.render(&aro_comp, std.io.tty.detectConfig(std.fs.File.stderr()));
329329 return error.AroPreprocessorFailed;
330330 }
331331 }
......@@ -334,7 +334,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
334334 // new scope to ensure definition file is written before passing the path to WriteImportLibrary
335335 const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true });
336336 defer def_final_file.close();
337 try pp.prettyPrintTokens(def_final_file.writer(), .result_only);
337 try pp.prettyPrintTokens(def_final_file.deprecatedWriter(), .result_only);
338338 }
339339
340340 const lib_final_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename });
......@@ -930,7 +930,6 @@ const mingw32_x86_src = [_][]const u8{
930930 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S",
931931 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c",
932932 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c",
933 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S",
934933 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c",
935934 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c",
936935 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S",
......@@ -974,7 +973,6 @@ const mingw32_x86_32_src = [_][]const u8{
974973 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c",
975974 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c",
976975 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S",
977 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S",
978976 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c",
979977};
980978
......@@ -1013,6 +1011,7 @@ const mingw32_winpthreads_src = [_][]const u8{
10131011 "winpthreads" ++ path.sep_str ++ "thread.c",
10141012};
10151013
1014// Note: kernel32 and ntdll are always linked even without targeting MinGW-w64.
10161015pub const always_link_libs = [_][]const u8{
10171016 "api-ms-win-crt-conio-l1-1-0",
10181017 "api-ms-win-crt-convert-l1-1-0",
......@@ -1030,8 +1029,6 @@ pub const always_link_libs = [_][]const u8{
10301029 "api-ms-win-crt-time-l1-1-0",
10311030 "api-ms-win-crt-utility-l1-1-0",
10321031 "advapi32",
1033 "kernel32",
1034 "ntdll",
10351032 "shell32",
10361033 "user32",
10371034};
src/libs/musl.zig-12
......@@ -821,8 +821,6 @@ const src_files = [_][]const u8{
821821 "musl/src/malloc/replaced.c",
822822 "musl/src/math/aarch64/ceil.c",
823823 "musl/src/math/aarch64/ceilf.c",
824 "musl/src/math/aarch64/floor.c",
825 "musl/src/math/aarch64/floorf.c",
826824 "musl/src/math/aarch64/fma.c",
827825 "musl/src/math/aarch64/fmaf.c",
828826 "musl/src/math/aarch64/fmax.c",
......@@ -912,9 +910,6 @@ const src_files = [_][]const u8{
912910 "musl/src/math/fdiml.c",
913911 "musl/src/math/finite.c",
914912 "musl/src/math/finitef.c",
915 "musl/src/math/floor.c",
916 "musl/src/math/floorf.c",
917 "musl/src/math/floorl.c",
918913 "musl/src/math/fma.c",
919914 "musl/src/math/fmaf.c",
920915 "musl/src/math/fmal.c",
......@@ -955,8 +950,6 @@ const src_files = [_][]const u8{
955950 "musl/src/math/i386/exp_ld.s",
956951 "musl/src/math/i386/expl.s",
957952 "musl/src/math/i386/expm1l.s",
958 "musl/src/math/i386/floorf.s",
959 "musl/src/math/i386/floorl.s",
960953 "musl/src/math/i386/floor.s",
961954 "musl/src/math/i386/fmod.c",
962955 "musl/src/math/i386/fmodf.c",
......@@ -1089,8 +1082,6 @@ const src_files = [_][]const u8{
10891082 "musl/src/math/pow_data.c",
10901083 "musl/src/math/powerpc64/ceil.c",
10911084 "musl/src/math/powerpc64/ceilf.c",
1092 "musl/src/math/powerpc64/floor.c",
1093 "musl/src/math/powerpc64/floorf.c",
10941085 "musl/src/math/powerpc64/fma.c",
10951086 "musl/src/math/powerpc64/fmaf.c",
10961087 "musl/src/math/powerpc64/fmax.c",
......@@ -1153,9 +1144,6 @@ const src_files = [_][]const u8{
11531144 "musl/src/math/s390x/ceil.c",
11541145 "musl/src/math/s390x/ceilf.c",
11551146 "musl/src/math/s390x/ceill.c",
1156 "musl/src/math/s390x/floor.c",
1157 "musl/src/math/s390x/floorf.c",
1158 "musl/src/math/s390x/floorl.c",
11591147 "musl/src/math/s390x/fma.c",
11601148 "musl/src/math/s390x/fmaf.c",
11611149 "musl/src/math/s390x/nearbyint.c",
src/libs/netbsd.zig+2-2
......@@ -442,13 +442,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
442442 .lt => continue,
443443 .gt => {
444444 // TODO Expose via compile error mechanism instead of log.
445 log.warn("invalid target NetBSD libc version: {}", .{target_version});
445 log.warn("invalid target NetBSD libc version: {f}", .{target_version});
446446 return error.InvalidTargetLibCVersion;
447447 },
448448 }
449449 } else blk: {
450450 const latest_index = metadata.all_versions.len - 1;
451 log.warn("zig cannot build new NetBSD libc version {}; providing instead {}", .{
451 log.warn("zig cannot build new NetBSD libc version {f}; providing instead {f}", .{
452452 target_version, metadata.all_versions[latest_index],
453453 });
454454 break :blk latest_index;
src/libs/wasi_libc.zig+75-110
......@@ -10,43 +10,8 @@ pub const CrtFile = enum {
1010 crt1_reactor_o,
1111 crt1_command_o,
1212 libc_a,
13 libdl_a,
14 libwasi_emulated_process_clocks_a,
15 libwasi_emulated_getpid_a,
16 libwasi_emulated_mman_a,
17 libwasi_emulated_signal_a,
1813};
1914
20pub fn getEmulatedLibCrtFile(lib_name: []const u8) ?CrtFile {
21 if (mem.eql(u8, lib_name, "dl")) {
22 return .libdl_a;
23 }
24 if (mem.eql(u8, lib_name, "wasi-emulated-process-clocks")) {
25 return .libwasi_emulated_process_clocks_a;
26 }
27 if (mem.eql(u8, lib_name, "wasi-emulated-getpid")) {
28 return .libwasi_emulated_getpid_a;
29 }
30 if (mem.eql(u8, lib_name, "wasi-emulated-mman")) {
31 return .libwasi_emulated_mman_a;
32 }
33 if (mem.eql(u8, lib_name, "wasi-emulated-signal")) {
34 return .libwasi_emulated_signal_a;
35 }
36 return null;
37}
38
39pub fn emulatedLibCRFileLibName(crt_file: CrtFile) []const u8 {
40 return switch (crt_file) {
41 .libdl_a => "libdl.a",
42 .libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a",
43 .libwasi_emulated_getpid_a => "libwasi-emulated-getpid.a",
44 .libwasi_emulated_mman_a => "libwasi-emulated-mman.a",
45 .libwasi_emulated_signal_a => "libwasi-emulated-signal.a",
46 else => unreachable,
47 };
48}
49
5015pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile {
5116 return switch (wasi_exec_model) {
5217 .reactor => CrtFile.crt1_reactor_o,
......@@ -157,87 +122,57 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
157122 }
158123 }
159124
160 try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items, .{});
161 },
162
163 .libdl_a => {
164 var args = std.ArrayList([]const u8).init(arena);
165 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
166 try addLibcBottomHalfIncludes(comp, arena, &args);
125 {
126 // Compile libdl.
127 var args = std.ArrayList([]const u8).init(arena);
128 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
129 try addLibcBottomHalfIncludes(comp, arena, &args);
167130
168 var emu_dl_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
169 for (emulated_dl_src_files) |file_path| {
170 try emu_dl_sources.append(.{
171 .src_path = try comp.dirs.zig_lib.join(arena, &.{
172 "libc", try sanitize(arena, file_path),
173 }),
174 .extra_flags = args.items,
175 .owner = undefined,
176 });
131 for (emulated_dl_src_files) |file_path| {
132 try libc_sources.append(.{
133 .src_path = try comp.dirs.zig_lib.join(arena, &.{
134 "libc", try sanitize(arena, file_path),
135 }),
136 .extra_flags = args.items,
137 .owner = undefined,
138 });
139 }
177140 }
178 try comp.build_crt_file("dl", .Lib, .@"wasi libdl.a", prog_node, emu_dl_sources.items, .{});
179 },
180
181 .libwasi_emulated_process_clocks_a => {
182 var args = std.ArrayList([]const u8).init(arena);
183 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
184 try addLibcBottomHalfIncludes(comp, arena, &args);
185141
186 var emu_clocks_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
187 for (emulated_process_clocks_src_files) |file_path| {
188 try emu_clocks_sources.append(.{
189 .src_path = try comp.dirs.zig_lib.join(arena, &.{
190 "libc", try sanitize(arena, file_path),
142 {
143 // Compile libwasi-emulated-process-clocks.
144 var args = std.ArrayList([]const u8).init(arena);
145 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
146 try args.appendSlice(&.{
147 "-I",
148 try comp.dirs.zig_lib.join(arena, &.{
149 "libc",
150 "wasi",
151 "libc-bottom-half",
152 "cloudlibc",
153 "src",
191154 }),
192 .extra_flags = args.items,
193 .owner = undefined,
194155 });
195 }
196 try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items, .{});
197 },
198 .libwasi_emulated_getpid_a => {
199 var args = std.ArrayList([]const u8).init(arena);
200 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
201 try addLibcBottomHalfIncludes(comp, arena, &args);
202156
203 var emu_getpid_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
204 for (emulated_getpid_src_files) |file_path| {
205 try emu_getpid_sources.append(.{
206 .src_path = try comp.dirs.zig_lib.join(arena, &.{
207 "libc", try sanitize(arena, file_path),
208 }),
209 .extra_flags = args.items,
210 .owner = undefined,
211 });
212 }
213 try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items, .{});
214 },
215 .libwasi_emulated_mman_a => {
216 var args = std.ArrayList([]const u8).init(arena);
217 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
218 try addLibcBottomHalfIncludes(comp, arena, &args);
219
220 var emu_mman_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
221 for (emulated_mman_src_files) |file_path| {
222 try emu_mman_sources.append(.{
223 .src_path = try comp.dirs.zig_lib.join(arena, &.{
224 "libc", try sanitize(arena, file_path),
225 }),
226 .extra_flags = args.items,
227 .owner = undefined,
228 });
157 for (emulated_process_clocks_src_files) |file_path| {
158 try libc_sources.append(.{
159 .src_path = try comp.dirs.zig_lib.join(arena, &.{
160 "libc", try sanitize(arena, file_path),
161 }),
162 .extra_flags = args.items,
163 .owner = undefined,
164 });
165 }
229166 }
230 try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items, .{});
231 },
232 .libwasi_emulated_signal_a => {
233 var emu_signal_sources = std.ArrayList(Compilation.CSourceFile).init(arena);
234167
235168 {
169 // Compile libwasi-emulated-getpid.
236170 var args = std.ArrayList([]const u8).init(arena);
237171 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
172 try addLibcBottomHalfIncludes(comp, arena, &args);
238173
239 for (emulated_signal_bottom_half_src_files) |file_path| {
240 try emu_signal_sources.append(.{
174 for (emulated_getpid_src_files) |file_path| {
175 try libc_sources.append(.{
241176 .src_path = try comp.dirs.zig_lib.join(arena, &.{
242177 "libc", try sanitize(arena, file_path),
243178 }),
......@@ -248,13 +183,13 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
248183 }
249184
250185 {
186 // Compile libwasi-emulated-mman.
251187 var args = std.ArrayList([]const u8).init(arena);
252188 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
253 try addLibcTopHalfIncludes(comp, arena, &args);
254 try args.append("-D_WASI_EMULATED_SIGNAL");
189 try addLibcBottomHalfIncludes(comp, arena, &args);
255190
256 for (emulated_signal_top_half_src_files) |file_path| {
257 try emu_signal_sources.append(.{
191 for (emulated_mman_src_files) |file_path| {
192 try libc_sources.append(.{
258193 .src_path = try comp.dirs.zig_lib.join(arena, &.{
259194 "libc", try sanitize(arena, file_path),
260195 }),
......@@ -264,7 +199,38 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
264199 }
265200 }
266201
267 try comp.build_crt_file("wasi-emulated-signal", .Lib, .@"libwasi-emulated-signal.a", prog_node, emu_signal_sources.items, .{});
202 {
203 // Compile libwasi-emulated-signal.
204 var bottom_args = std.ArrayList([]const u8).init(arena);
205 try addCCArgs(comp, arena, &bottom_args, .{ .want_O3 = true });
206
207 for (emulated_signal_bottom_half_src_files) |file_path| {
208 try libc_sources.append(.{
209 .src_path = try comp.dirs.zig_lib.join(arena, &.{
210 "libc", try sanitize(arena, file_path),
211 }),
212 .extra_flags = bottom_args.items,
213 .owner = undefined,
214 });
215 }
216
217 var top_args = std.ArrayList([]const u8).init(arena);
218 try addCCArgs(comp, arena, &top_args, .{ .want_O3 = true });
219 try addLibcTopHalfIncludes(comp, arena, &top_args);
220 try top_args.append("-D_WASI_EMULATED_SIGNAL");
221
222 for (emulated_signal_top_half_src_files) |file_path| {
223 try libc_sources.append(.{
224 .src_path = try comp.dirs.zig_lib.join(arena, &.{
225 "libc", try sanitize(arena, file_path),
226 }),
227 .extra_flags = top_args.items,
228 .owner = undefined,
229 });
230 }
231 }
232
233 try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items, .{});
268234 },
269235 }
270236}
......@@ -754,7 +720,6 @@ const libc_top_half_src_files = [_][]const u8{
754720 "musl/src/math/fdiml.c",
755721 "musl/src/math/finite.c",
756722 "musl/src/math/finitef.c",
757 "musl/src/math/floorl.c",
758723 "musl/src/math/fma.c",
759724 "musl/src/math/fmaf.c",
760725 "musl/src/math/fmaxl.c",
src/link.zig+8-7
......@@ -838,8 +838,10 @@ pub const File = struct {
838838 const cached_pp_file_path = the_key.status.success.object_path;
839839 cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| {
840840 const diags = &base.comp.link_diags;
841 return diags.fail("failed to copy '{f'}' to '{f'}': {s}", .{
842 @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err),
841 return diags.fail("failed to copy '{f}' to '{f}': {s}", .{
842 std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar),
843 std.fmt.alt(@as(Path, emit), .formatEscapeChar),
844 @errorName(err),
843845 });
844846 };
845847 return;
......@@ -2095,8 +2097,8 @@ fn resolvePathInputLib(
20952097 }) {
20962098 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
20972099 error.FileNotFound => return .no_match,
2098 else => |e| fatal("unable to search for {s} library '{f'}': {s}", .{
2099 @tagName(link_mode), test_path, @errorName(e),
2100 else => |e| fatal("unable to search for {s} library '{f}': {s}", .{
2101 @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e),
21002102 }),
21012103 };
21022104 errdefer file.close();
......@@ -2105,9 +2107,8 @@ fn resolvePathInputLib(
21052107 var br = fr.interface().unbuffered();
21062108 ok: {
21072109 br.readSlice(ld_script_bytes.items) catch |err| switch (err) {
2108 error.ReadFailed => fatal("failed to read '{f'}': {s}", .{
2109 test_path,
2110 @errorName(fr.err.?),
2110 error.ReadFailed => fatal("failed to read '{f}': {s}", .{
2111 test_path, @errorName(fr.err.?),
21112112 }),
21122113 error.EndOfStream => break :ok,
21132114 };
src/link/C.zig+40-32
......@@ -63,6 +63,14 @@ const String = extern struct {
6363 .start = 0,
6464 .len = 0,
6565 };
66
67 fn concat(lhs: String, rhs: String) String {
68 assert(lhs.start + lhs.len == rhs.start);
69 return .{
70 .start = lhs.start,
71 .len = lhs.len + rhs.len,
72 };
73 }
6674};
6775
6876/// Per-declaration data.
......@@ -205,8 +213,10 @@ pub fn updateFunc(
205213 .ctype_pool = mir.c.ctype_pool.move(),
206214 .lazy_fns = mir.c.lazy_fns.move(),
207215 };
208 gop.value_ptr.fwd_decl = try self.addString(&.{&function.object.dg.fwd_decl});
209 gop.value_ptr.code = try self.addString(&.{ &function.object.code_header, &function.object.code });
216 gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl);
217 const code_header = try self.addString(mir.c.code_header);
218 const code = try self.addString(mir.c.code);
219 gop.value_ptr.code = code_header.concat(code);
210220 try self.addUavsFromCodegen(&mir.c.uavs);
211221}
212222
......@@ -232,8 +242,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {
232242 .code = undefined,
233243 .indent_counter = 0,
234244 };
235 object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf);
236 object.code.initOwnedSlice(gpa, self.code_buf);
245 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
246 object.code = .initOwnedSlice(gpa, self.code_buf);
237247 defer {
238248 object.dg.uavs.deinit(gpa);
239249 object.dg.ctype_pool.deinit(object.dg.gpa);
......@@ -259,8 +269,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {
259269
260270 object.dg.ctype_pool.freeUnusedCapacity(gpa);
261271 self.uavs.values()[i] = .{
262 .fwd_decl = try self.addString(&.{&object.dg.fwd_decl}),
263 .code = try self.addString(&.{&object.code}),
272 .fwd_decl = try self.addString(object.dg.fwd_decl.getWritten()),
273 .code = try self.addString(object.code.getWritten()),
264274 .ctype_pool = object.dg.ctype_pool.move(),
265275 };
266276}
......@@ -307,8 +317,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l
307317 .code = undefined,
308318 .indent_counter = 0,
309319 };
310 object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf);
311 object.code.initOwnedSlice(gpa, self.code_buf);
320 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
321 object.code = .initOwnedSlice(gpa, self.code_buf);
312322 defer {
313323 object.dg.uavs.deinit(gpa);
314324 ctype_pool.* = object.dg.ctype_pool.move();
......@@ -326,8 +336,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l
326336 },
327337 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
328338 };
329 gop.value_ptr.fwd_decl = try self.addString(&.{&object.dg.fwd_decl});
330 gop.value_ptr.code = try self.addString(&.{&object.code});
339 gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.getWritten());
340 gop.value_ptr.code = try self.addString(object.code.getWritten());
331341 try self.addUavsFromCodegen(&object.dg.uavs);
332342}
333343
......@@ -339,12 +349,12 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn
339349 _ = ti_id;
340350}
341351
342fn abiDefines(bw: *std.io.BufferedWriter, target: std.Target) !void {
352fn abiDefines(w: *std.io.Writer, target: *const std.Target) !void {
343353 switch (target.abi) {
344 .msvc, .itanium => try bw.writeAll("#define ZIG_TARGET_ABI_MSVC\n"),
354 .msvc, .itanium => try w.writeAll("#define ZIG_TARGET_ABI_MSVC\n"),
345355 else => {},
346356 }
347 try bw.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{
357 try w.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{
348358 target.cMaxIntAlignment(),
349359 });
350360}
......@@ -391,10 +401,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
391401 };
392402 defer f.deinit(gpa);
393403
394 var abi_defines_aw: std.io.AllocatingWriter = undefined;
395 abi_defines_aw.init(gpa);
404 var abi_defines_aw: std.io.Writer.Allocating = .init(gpa);
396405 defer abi_defines_aw.deinit();
397 abiDefines(&abi_defines_aw.buffered_writer, zcu.getTarget()) catch |err| switch (err) {
406 abiDefines(&abi_defines_aw.writer, zcu.getTarget()) catch |err| switch (err) {
398407 error.WriteFailed => return error.OutOfMemory,
399408 };
400409
......@@ -407,10 +416,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
407416 const ctypes_index = f.all_buffers.items.len;
408417 f.all_buffers.items.len += 1;
409418
410 var asm_aw: std.io.AllocatingWriter = undefined;
411 asm_aw.init(gpa);
419 var asm_aw: std.io.Writer.Allocating = .init(gpa);
412420 defer asm_aw.deinit();
413 codegen.genGlobalAsm(zcu, &asm_aw.buffered_writer) catch |err| switch (err) {
421 codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) {
414422 error.WriteFailed => return error.OutOfMemory,
415423 };
416424 f.appendBufAssumeCapacity(asm_aw.getWritten());
......@@ -501,11 +509,11 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
501509
502510 const file = self.base.file.?;
503511 file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)});
504 var fw = file.writer();
505 var bw = fw.interface().unbuffered();
506 bw.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
507 error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{
508 self.base.emit, @errorName(fw.err.?),
512 var fw = file.writer(&.{});
513 var w = &fw.interface;
514 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
515 error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{
516 std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?),
509517 }),
510518 };
511519}
......@@ -575,8 +583,8 @@ fn flushCTypes(
575583 try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len);
576584 defer global_from_decl_map.clearRetainingCapacity();
577585
578 var ctypes_aw: std.io.AllocatingWriter = undefined;
579 const ctypes_bw = ctypes_aw.fromArrayList(gpa, &f.ctypes);
586 var ctypes_aw: std.io.Writer.Allocating = .fromArrayList(gpa, &f.ctypes);
587 const ctypes_bw = &ctypes_aw.writer;
580588 defer f.ctypes = ctypes_aw.toArrayList();
581589
582590 for (0..decl_ctype_pool.items.len) |decl_ctype_pool_index| {
......@@ -640,8 +648,8 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, f: *Flush) FlushDeclError!void {
640648 .code = undefined,
641649 .indent_counter = 0,
642650 };
643 _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl);
644 _ = object.code.fromArrayList(gpa, &f.lazy_code);
651 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
652 object.code = .fromArrayList(gpa, &f.lazy_code);
645653 defer {
646654 object.dg.uavs.deinit(gpa);
647655 f.lazy_ctype_pool = object.dg.ctype_pool.move();
......@@ -688,8 +696,8 @@ fn flushLazyFn(
688696 .code = undefined,
689697 .indent_counter = 0,
690698 };
691 _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl);
692 _ = object.code.fromArrayList(gpa, &f.lazy_code);
699 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
700 object.code = .fromArrayList(gpa, &f.lazy_code);
693701 defer {
694702 // If this assert trips just handle the anon_decl_deps the same as
695703 // `updateFunc()` does.
......@@ -830,7 +838,7 @@ pub fn updateExports(
830838 .scratch = .initBuffer(self.scratch_buf),
831839 .uavs = .empty,
832840 };
833 dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf);
841 dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
834842 defer {
835843 assert(dg.uavs.count() == 0);
836844 ctype_pool.* = dg.ctype_pool.move();
......@@ -842,7 +850,7 @@ pub fn updateExports(
842850 codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) {
843851 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
844852 };
845 exported_block.* = .{ .fwd_decl = try self.addString(&.{&dg.fwd_decl}) };
853 exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.getWritten()) };
846854}
847855
848856pub fn deleteExport(
src/link/Coff.zig+15-23
......@@ -2623,7 +2623,7 @@ fn logSymtab(coff: *Coff) void {
26232623 .DEBUG => unreachable, // TODO
26242624 else => @intFromEnum(sym.section_number),
26252625 };
2626 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
2626 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
26272627 sym_id,
26282628 coff.getSymbolName(.{ .sym_index = @as(u32, @intCast(sym_id)), .file = null }),
26292629 sym.value,
......@@ -3096,33 +3096,25 @@ const ImportTable = struct {
30963096 return base_vaddr + index * @sizeOf(u64);
30973097 }
30983098
3099 const FormatContext = struct {
3099 const Format = struct {
31003100 itab: ImportTable,
31013101 ctx: Context,
3102 };
31033102
3104 fn format(itab: ImportTable, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {
3105 _ = itab;
3106 _ = bw;
3107 _ = unused_format_string;
3108 @compileError("do not format ImportTable directly; use itab.fmtDebug()");
3109 }
3110
3111 fn format2(fmt_ctx: FormatContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {
3112 comptime assert(unused_format_string.len == 0);
3113 const lib_name = fmt_ctx.ctx.coff.temp_strtab.getAssumeExists(fmt_ctx.ctx.name_off);
3114 const base_vaddr = getBaseAddress(fmt_ctx.ctx);
3115 try bw.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr });
3116 for (fmt_ctx.itab.entries.items, 0..) |entry, i| {
3117 try bw.print("\n {d}@{?x} => {s}", .{
3118 i,
3119 fmt_ctx.itab.getImportAddress(entry, fmt_ctx.ctx),
3120 fmt_ctx.ctx.coff.getSymbolName(entry),
3121 });
3103 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
3104 const lib_name = f.ctx.coff.temp_strtab.getAssumeExists(f.ctx.name_off);
3105 const base_vaddr = getBaseAddress(f.ctx);
3106 try writer.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr });
3107 for (f.itab.entries.items, 0..) |entry, i| {
3108 try writer.print("\n {d}@{?x} => {s}", .{
3109 i,
3110 f.itab.getImportAddress(entry, f.ctx),
3111 f.ctx.coff.getSymbolName(entry),
3112 });
3113 }
31223114 }
3123 }
3115 };
31243116
3125 fn fmtDebug(itab: ImportTable, ctx: Context) fmt.Formatter(format2) {
3117 fn fmtDebug(itab: ImportTable, ctx: Context) fmt.Formatter(Format, Format.default) {
31263118 return .{ .data = .{ .itab = itab, .ctx = ctx } };
31273119 }
31283120
src/link/Dwarf.zig+4-4
......@@ -2557,7 +2557,7 @@ fn initWipNavInner(
25572557 const addr: Loc = .{ .addr_reloc = sym_index };
25582558 const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr;
25592559 switch (decl.kind) {
2560 .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable,
2560 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
25612561 .@"const" => {
25622562 const const_ty_reloc_index = try wip_nav.refForward();
25632563 try wip_nav.infoExprLoc(loc);
......@@ -2834,7 +2834,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
28342834
28352835 const is_test = switch (decl.kind) {
28362836 .unnamed_test, .@"test", .decltest => true,
2837 .@"comptime", .@"usingnamespace", .@"const", .@"var" => false,
2837 .@"comptime", .@"const", .@"var" => false,
28382838 };
28392839 if (is_test) {
28402840 // This isn't actually a comptime Nav! It's a test, so it'll definitely never be referenced at comptime.
......@@ -3657,7 +3657,7 @@ fn updateLazyType(
36573657 // For better or worse, we try to match what Clang emits.
36583658 break :cc switch (func_type.cc) {
36593659 .@"inline" => .nocall,
3660 .@"async", .auto, .naked => .normal,
3660 .async, .auto, .naked => .normal,
36613661 .x86_64_sysv => .LLVM_X86_64SysV,
36623662 .x86_64_win => .LLVM_Win64,
36633663 .x86_64_regcall_v3_sysv => .LLVM_X86RegCall,
......@@ -4301,7 +4301,7 @@ fn updateContainerTypeInner(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: Intern
43014301 };
43024302 defer wip_nav.deinit();
43034303 const diw = wip_nav.debug_info.writer(dwarf.gpa);
4304 const name = try std.fmt.allocPrint(dwarf.gpa, "{}", .{ty.fmt(pt)});
4304 const name = try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)});
43054305 defer dwarf.gpa.free(name);
43064306
43074307 switch (ip.indexToKey(type_index)) {
src/link/Elf.zig+55-69
......@@ -3870,22 +3870,21 @@ pub fn failFile(
38703870 return error.LinkFailure;
38713871}
38723872
3873const FormatShdrCtx = struct {
3873const FormatShdr = struct {
38743874 elf_file: *Elf,
38753875 shdr: elf.Elf64_Shdr,
38763876};
38773877
3878fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) {
3878fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(FormatShdr, formatShdr) {
38793879 return .{ .data = .{
38803880 .shdr = shdr,
38813881 .elf_file = self,
38823882 } };
38833883}
38843884
3885fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
3886 _ = unused_fmt_string;
3885fn formatShdr(ctx: FormatShdr, writer: *std.io.Writer) std.io.Writer.Error!void {
38873886 const shdr = ctx.shdr;
3888 try bw.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({f})", .{
3887 try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({f})", .{
38893888 ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,
38903889 shdr.sh_addr, shdr.sh_addralign,
38913890 shdr.sh_size, shdr.sh_entsize,
......@@ -3893,74 +3892,68 @@ fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []con
38933892 });
38943893}
38953894
3896pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) {
3895pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(u64, formatShdrFlags) {
38973896 return .{ .data = sh_flags };
38983897}
38993898
3900fn formatShdrFlags(sh_flags: u64, bw: *Writer, comptime unused_fmt_string: []const u8) !void {
3901 _ = unused_fmt_string;
3899fn formatShdrFlags(sh_flags: u64, writer: *std.io.Writer) std.io.Writer.Error!void {
39023900 if (elf.SHF_WRITE & sh_flags != 0) {
3903 try bw.writeByte('W');
3901 try writer.writeByte('W');
39043902 }
39053903 if (elf.SHF_ALLOC & sh_flags != 0) {
3906 try bw.writeByte('A');
3904 try writer.writeByte('A');
39073905 }
39083906 if (elf.SHF_EXECINSTR & sh_flags != 0) {
3909 try bw.writeByte('X');
3907 try writer.writeByte('X');
39103908 }
39113909 if (elf.SHF_MERGE & sh_flags != 0) {
3912 try bw.writeByte('M');
3910 try writer.writeByte('M');
39133911 }
39143912 if (elf.SHF_STRINGS & sh_flags != 0) {
3915 try bw.writeByte('S');
3913 try writer.writeByte('S');
39163914 }
39173915 if (elf.SHF_INFO_LINK & sh_flags != 0) {
3918 try bw.writeByte('I');
3916 try writer.writeByte('I');
39193917 }
39203918 if (elf.SHF_LINK_ORDER & sh_flags != 0) {
3921 try bw.writeByte('L');
3919 try writer.writeByte('L');
39223920 }
39233921 if (elf.SHF_EXCLUDE & sh_flags != 0) {
3924 try bw.writeByte('E');
3922 try writer.writeByte('E');
39253923 }
39263924 if (elf.SHF_COMPRESSED & sh_flags != 0) {
3927 try bw.writeByte('C');
3925 try writer.writeByte('C');
39283926 }
39293927 if (elf.SHF_GROUP & sh_flags != 0) {
3930 try bw.writeByte('G');
3928 try writer.writeByte('G');
39313929 }
39323930 if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) {
3933 try bw.writeByte('O');
3931 try writer.writeByte('O');
39343932 }
39353933 if (elf.SHF_TLS & sh_flags != 0) {
3936 try bw.writeByte('T');
3934 try writer.writeByte('T');
39373935 }
39383936 if (elf.SHF_X86_64_LARGE & sh_flags != 0) {
3939 try bw.writeByte('l');
3937 try writer.writeByte('l');
39403938 }
39413939 if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) {
3942 try bw.writeByte('p');
3940 try writer.writeByte('p');
39433941 }
39443942}
39453943
3946const FormatPhdrCtx = struct {
3944const FormatPhdr = struct {
39473945 elf_file: *Elf,
39483946 phdr: elf.Elf64_Phdr,
39493947};
39503948
3951fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) {
3949fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(FormatPhdr, formatPhdr) {
39523950 return .{ .data = .{
39533951 .phdr = phdr,
39543952 .elf_file = self,
39553953 } };
39563954}
39573955
3958fn formatPhdr(
3959 ctx: FormatPhdrCtx,
3960 bw: *Writer,
3961 comptime unused_fmt_string: []const u8,
3962) !void {
3963 _ = unused_fmt_string;
3956fn formatPhdr(ctx: FormatPhdr, writer: *std.io.Writer) std.io.Writer.Error!void {
39643957 const phdr = ctx.phdr;
39653958 const write = phdr.p_flags & elf.PF_W != 0;
39663959 const read = phdr.p_flags & elf.PF_R != 0;
......@@ -3981,40 +3974,34 @@ fn formatPhdr(
39813974 elf.PT_NOTE => "NOTE",
39823975 else => "UNKNOWN",
39833976 };
3984 try bw.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{
3977 try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{
39853978 p_type, flags, phdr.p_offset, phdr.p_vaddr,
39863979 phdr.p_align, phdr.p_filesz, phdr.p_memsz,
39873980 });
39883981}
39893982
3990pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
3983pub fn dumpState(self: *Elf) std.fmt.Formatter(*Elf, fmtDumpState) {
39913984 return .{ .data = self };
39923985}
39933986
3994fn fmtDumpState(
3995 self: *Elf,
3996 bw: *Writer,
3997 comptime unused_fmt_string: []const u8,
3998) !void {
3999 _ = unused_fmt_string;
4000
3987fn fmtDumpState(self: *Elf, writer: *std.io.Writer) std.io.Writer.Error!void {
40013988 const shared_objects = self.shared_objects.values();
40023989
40033990 if (self.zigObjectPtr()) |zig_object| {
4004 try bw.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename });
4005 try bw.print("{f}{f}", .{
3991 try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename });
3992 try writer.print("{f}{f}", .{
40063993 zig_object.fmtAtoms(self),
40073994 zig_object.fmtSymtab(self),
40083995 });
4009 try bw.writeByte('\n');
3996 try writer.writeByte('\n');
40103997 }
40113998
40123999 for (self.objects.items) |index| {
40134000 const object = self.file(index).?.object;
4014 try bw.print("object({d}) : {f}", .{ index, object.fmtPath() });
4015 if (!object.alive) try bw.writeAll(" : [*]");
4016 try bw.writeByte('\n');
4017 try bw.print("{f}{f}{f}{f}{f}\n", .{
4001 try writer.print("object({d}) : {f}", .{ index, object.fmtPath() });
4002 if (!object.alive) try writer.writeAll(" : [*]");
4003 try writer.writeByte('\n');
4004 try writer.print("{f}{f}{f}{f}{f}\n", .{
40184005 object.fmtAtoms(self),
40194006 object.fmtCies(self),
40204007 object.fmtFdes(self),
......@@ -4025,59 +4012,59 @@ fn fmtDumpState(
40254012
40264013 for (shared_objects) |index| {
40274014 const shared_object = self.file(index).?.shared_object;
4028 try bw.print("shared_object({d}) : {f} : needed({})", .{
4015 try writer.print("shared_object({d}) : {f} : needed({})", .{
40294016 index, shared_object.path, shared_object.needed,
40304017 });
4031 if (!shared_object.alive) try bw.writeAll(" : [*]");
4032 try bw.writeByte('\n');
4033 try bw.print("{f}\n", .{shared_object.fmtSymtab(self)});
4018 if (!shared_object.alive) try writer.writeAll(" : [*]");
4019 try writer.writeByte('\n');
4020 try writer.print("{f}\n", .{shared_object.fmtSymtab(self)});
40344021 }
40354022
40364023 if (self.linker_defined_index) |index| {
40374024 const linker_defined = self.file(index).?.linker_defined;
4038 try bw.print("linker_defined({d}) : (linker defined)\n", .{index});
4039 try bw.print("{f}\n", .{linker_defined.fmtSymtab(self)});
4025 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
4026 try writer.print("{f}\n", .{linker_defined.fmtSymtab(self)});
40404027 }
40414028
40424029 const slice = self.sections.slice();
40434030 {
4044 try bw.writeAll("atom lists\n");
4031 try writer.writeAll("atom lists\n");
40454032 for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| {
4046 try bw.print("shdr({d}) : {s} : {f}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) });
4033 try writer.print("shdr({d}) : {s} : {f}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) });
40474034 }
40484035 }
40494036
40504037 if (self.requiresThunks()) {
4051 try bw.writeAll("thunks\n");
4038 try writer.writeAll("thunks\n");
40524039 for (self.thunks.items, 0..) |th, index| {
4053 try bw.print("thunk({d}) : {f}\n", .{ index, th.fmt(self) });
4040 try writer.print("thunk({d}) : {f}\n", .{ index, th.fmt(self) });
40544041 }
40554042 }
40564043
4057 try bw.print("{f}\n", .{self.got.fmt(self)});
4058 try bw.print("{f}\n", .{self.plt.fmt(self)});
4044 try writer.print("{f}\n", .{self.got.fmt(self)});
4045 try writer.print("{f}\n", .{self.plt.fmt(self)});
40594046
4060 try bw.writeAll("Output groups\n");
4047 try writer.writeAll("Output groups\n");
40614048 for (self.group_sections.items) |cg| {
4062 try bw.print(" shdr({d}) : GROUP({f})\n", .{ cg.shndx, cg.cg_ref });
4049 try writer.print(" shdr({d}) : GROUP({f})\n", .{ cg.shndx, cg.cg_ref });
40634050 }
40644051
4065 try bw.writeAll("\nOutput merge sections\n");
4052 try writer.writeAll("\nOutput merge sections\n");
40664053 for (self.merge_sections.items) |msec| {
4067 try bw.print(" shdr({d}) : {f}\n", .{ msec.output_section_index, msec.fmt(self) });
4054 try writer.print(" shdr({d}) : {f}\n", .{ msec.output_section_index, msec.fmt(self) });
40684055 }
40694056
4070 try bw.writeAll("\nOutput shdrs\n");
4057 try writer.writeAll("\nOutput shdrs\n");
40714058 for (slice.items(.shdr), slice.items(.phndx), 0..) |shdr, phndx, shndx| {
4072 try bw.print(" shdr({d}) : phdr({?d}) : {f}\n", .{
4059 try writer.print(" shdr({d}) : phdr({d}) : {f}\n", .{
40734060 shndx,
40744061 phndx,
40754062 self.fmtShdr(shdr),
40764063 });
40774064 }
4078 try bw.writeAll("\nOutput phdrs\n");
4065 try writer.writeAll("\nOutput phdrs\n");
40794066 for (self.phdrs.items, 0..) |phdr, phndx| {
4080 try bw.print(" phdr({d}) : {f}\n", .{ phndx, self.fmtPhdr(phdr) });
4067 try writer.print(" phdr({d}) : {f}\n", .{ phndx, self.fmtPhdr(phdr) });
40814068 }
40824069}
40834070
......@@ -4215,9 +4202,8 @@ pub const Ref = struct {
42154202 return ref.index == other.index and ref.file == other.file;
42164203 }
42174204
4218 pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
4219 _ = unused_fmt_string;
4220 try bw.print("ref({},{})", .{ ref.index, ref.file });
4205 pub fn format(ref: Ref, writer: *std.io.Writer) std.io.Writer.Error!void {
4206 try writer.print("ref({d},{d})", .{ ref.index, ref.file });
42214207 }
42224208};
42234209
src/link/Elf/Archive.zig+16-25
......@@ -45,7 +45,7 @@ pub fn parse(
4545
4646 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
4747 return diags.failParse(path, "invalid archive header delimiter: {f}", .{
48 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
48 std.ascii.hexEscape(&hdr.ar_fmag, .lower),
4949 });
5050 }
5151
......@@ -84,7 +84,7 @@ pub fn parse(
8484 };
8585
8686 log.debug("extracting object '{f}' from archive '{f}'", .{
87 object.path, path,
87 @as(Path, object.path), @as(Path, path),
8888 });
8989
9090 try objects.append(gpa, object);
......@@ -184,36 +184,28 @@ pub const ArSymtab = struct {
184184 }
185185 }
186186
187 pub fn format(ar: ArSymtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
188 _ = ar;
189 _ = bw;
190 _ = unused_fmt_string;
191 @compileError("do not format ar symtab directly; use fmt instead");
192 }
193
194 const FormatContext = struct {
187 const Format = struct {
195188 ar: ArSymtab,
196189 elf_file: *Elf,
190
191 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
192 const ar = f.ar;
193 const elf_file = f.elf_file;
194 for (ar.symtab.items, 0..) |entry, i| {
195 const name = ar.strtab.getAssumeExists(entry.off);
196 const file = elf_file.file(entry.file_index).?;
197 try writer.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file_index, file.fmtPath() });
198 }
199 }
197200 };
198201
199 pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(format2) {
202 pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
200203 return .{ .data = .{
201204 .ar = ar,
202205 .elf_file = elf_file,
203206 } };
204207 }
205208
206 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
207 _ = unused_fmt_string;
208 const ar = ctx.ar;
209 const elf_file = ctx.elf_file;
210 for (ar.symtab.items, 0..) |entry, i| {
211 const name = ar.strtab.getAssumeExists(entry.off);
212 const file = elf_file.file(entry.file_index).?;
213 try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file_index, file.fmtPath() });
214 }
215 }
216
217209 const Entry = struct {
218210 /// Offset into the string table.
219211 off: u32,
......@@ -251,9 +243,8 @@ pub const ArStrtab = struct {
251243 try writer.writeAll(ar.buffer.items);
252244 }
253245
254 pub fn format(ar: ArStrtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
255 comptime assert(unused_fmt_string.len == 0);
256 try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)});
246 pub fn format(ar: ArStrtab, writer: *std.io.Writer) std.io.Writer.Error!void {
247 try writer.print("{f}", .{std.ascii.hexEscape(ar.buffer.items, .lower)});
257248 }
258249};
259250
src/link/Elf/Atom.zig+35-44
......@@ -906,53 +906,45 @@ pub fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void {
906906 atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras);
907907}
908908
909pub fn format(atom: Atom, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
910 _ = atom;
911 _ = bw;
912 _ = unused_fmt_string;
913 @compileError("do not format Atom directly");
914}
915
916pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) {
909pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
917910 return .{ .data = .{
918911 .atom = atom,
919912 .elf_file = elf_file,
920913 } };
921914}
922915
923const FormatContext = struct {
916const Format = struct {
924917 atom: Atom,
925918 elf_file: *Elf,
926};
927919
928fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
929 _ = unused_fmt_string;
930 const atom = ctx.atom;
931 const elf_file = ctx.elf_file;
932 try bw.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{
933 atom.atom_index, atom.name(elf_file), atom.address(elf_file),
934 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,
935 atom.prev_atom_ref, atom.next_atom_ref,
936 });
937 if (atom.file(elf_file)) |atom_file| switch (atom_file) {
938 .object => |object| {
939 if (atom.fdes(object).len > 0) {
940 try bw.writeAll(" : fdes{ ");
941 const extras = atom.extra(elf_file);
942 for (atom.fdes(object), extras.fde_start..) |fde, i| {
943 try bw.print("{d}", .{i});
944 if (!fde.alive) try bw.writeAll("([*])");
945 if (i - extras.fde_start < extras.fde_count - 1) try bw.writeAll(", ");
920 fn default(f: Format, w: *std.io.Writer) std.io.Writer.Error!void {
921 const atom = f.atom;
922 const elf_file = f.elf_file;
923 try w.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{
924 atom.atom_index, atom.name(elf_file), atom.address(elf_file),
925 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,
926 atom.prev_atom_ref, atom.next_atom_ref,
927 });
928 if (atom.file(elf_file)) |atom_file| switch (atom_file) {
929 .object => |object| {
930 if (atom.fdes(object).len > 0) {
931 try w.writeAll(" : fdes{ ");
932 const extras = atom.extra(elf_file);
933 for (atom.fdes(object), extras.fde_start..) |fde, i| {
934 try w.print("{d}", .{i});
935 if (!fde.alive) try w.writeAll("([*])");
936 if (i - extras.fde_start < extras.fde_count - 1) try w.writeAll(", ");
937 }
938 try w.writeAll(" }");
946939 }
947 try bw.writeAll(" }");
948 }
949 },
950 else => {},
951 };
952 if (!atom.alive) {
953 try bw.writeAll(" : [*]");
940 },
941 else => {},
942 };
943 if (!atom.alive) {
944 try w.writeAll(" : [*]");
945 }
954946 }
955}
947};
956948
957949pub const Index = u32;
958950
......@@ -1385,9 +1377,8 @@ const x86_64 = struct {
13851377 // TODO: hack to force imm32s in the assembler
13861378 .{ .imm = .s(-129) },
13871379 }, t) catch return false;
1388 var buf: [std.atomic.cache_line]u8 = undefined;
1389 var bw = Writer.null.buffered(&buf);
1390 inst.encode(&bw, .{}) catch return false;
1380 var trash: std.io.Writer.Discarding = .init(&.{});
1381 inst.encode(&trash.writer, .{}) catch return false;
13911382 return true;
13921383 },
13931384 else => return false,
......@@ -1433,7 +1424,7 @@ const x86_64 = struct {
14331424 rels: []const elf.Elf64_Rela,
14341425 value: i32,
14351426 elf_file: *Elf,
1436 bw: *Writer,
1427 writer: *Writer,
14371428 ) !void {
14381429 dev.check(.x86_64_backend);
14391430 assert(rels.len == 2);
......@@ -1450,8 +1441,8 @@ const x86_64 = struct {
14501441 0x48, 0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %rax
14511442 };
14521443 std.mem.writeInt(i32, insts[12..][0..4], value, .little);
1453 bw.end -= 4;
1454 try bw.writeAll(&insts);
1444 try writer.seekBy(-4);
1445 try writer.writeAll(&insts);
14551446 relocs_log.debug(" relaxing {f} and {f}", .{
14561447 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
14571448 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
......@@ -1481,8 +1472,8 @@ const x86_64 = struct {
14811472 }
14821473
14831474 fn encode(insts: []const Instruction, code: []u8) !void {
1484 var bw: Writer = .fixed(code);
1485 for (insts) |inst| try inst.encode(&bw, .{});
1475 var stream: std.io.Writer = .fixed(code);
1476 for (insts) |inst| try inst.encode(&stream, .{});
14861477 }
14871478
14881479 const bits = @import("../../arch/x86_64/bits.zig");
src/link/Elf/AtomList.zig+22-25
......@@ -167,32 +167,29 @@ pub fn lastAtom(list: AtomList, elf_file: *Elf) *Atom {
167167 return elf_file.atom(list.atoms.keys()[list.atoms.keys().len - 1]).?;
168168}
169169
170pub fn format(list: AtomList, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
171 _ = list;
172 _ = bw;
173 _ = unused_fmt_string;
174 @compileError("do not format AtomList directly");
175}
176
177const FormatCtx = struct { AtomList, *Elf };
178
179pub fn fmt(list: AtomList, elf_file: *Elf) std.fmt.Formatter(format2) {
180 return .{ .data = .{ list, elf_file } };
181}
182
183fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
184 comptime assert(unused_fmt_string.len == 0);
185 const list, const elf_file = ctx;
186 try bw.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{
187 list.address(elf_file), list.output_section_index,
188 list.alignment.toByteUnits() orelse 0, list.size,
189 });
190 try bw.writeAll(" : atoms{ ");
191 for (list.atoms.keys(), 0..) |ref, i| {
192 try bw.print("{f}", .{ref});
193 if (i < list.atoms.keys().len - 1) try bw.writeAll(", ");
170const Format = struct {
171 atom_list: AtomList,
172 elf_file: *Elf,
173
174 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
175 const list = f.atom_list;
176 try writer.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{
177 list.address(f.elf_file),
178 list.output_section_index,
179 list.alignment.toByteUnits() orelse 0,
180 list.size,
181 });
182 try writer.writeAll(" : atoms{ ");
183 for (list.atoms.keys(), 0..) |ref, i| {
184 try writer.print("{f}", .{ref});
185 if (i < list.atoms.keys().len - 1) try writer.writeAll(", ");
186 }
187 try writer.writeAll(" }");
194188 }
195 try bw.writeAll(" }");
189};
190
191pub fn fmt(atom_list: AtomList, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
192 return .{ .data = .{ .atom_list = atom_list, .elf_file = elf_file } };
196193}
197194
198195const std = @import("std");
src/link/Elf/LinkerDefined.zig+16-17
......@@ -147,9 +147,9 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
147147 for (slice.items(.shdr)) |shdr| {
148148 // TODO use getOrPut for incremental so that we don't create duplicates
149149 if (elf_file.getStartStopBasename(shdr)) |name| {
150 const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name});
150 const start_name = try std.fmt.allocPrintSentinel(gpa, "__start_{s}", .{name}, 0);
151151 defer gpa.free(start_name);
152 const stop_name = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name});
152 const stop_name = try std.fmt.allocPrintSentinel(gpa, "__stop_{s}", .{name}, 0);
153153 defer gpa.free(stop_name);
154154
155155 for (&[_][]const u8{ start_name, stop_name }) |nn| {
......@@ -437,32 +437,31 @@ pub fn setSymbolExtra(self: *LinkerDefined, index: u32, extra: Symbol.Extra) voi
437437 }
438438}
439439
440pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
440pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) {
441441 return .{ .data = .{
442442 .self = self,
443443 .elf_file = elf_file,
444444 } };
445445}
446446
447const FormatContext = struct {
447const Format = struct {
448448 self: *LinkerDefined,
449449 elf_file: *Elf,
450};
451450
452fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
453 comptime assert(unused_fmt_string.len == 0);
454 const self = ctx.self;
455 const elf_file = ctx.elf_file;
456 try bw.writeAll(" globals\n");
457 for (self.symbols.items, 0..) |sym, i| {
458 const ref = self.resolveSymbol(@intCast(i), elf_file);
459 if (elf_file.symbol(ref)) |ref_sym| {
460 try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)});
461 } else {
462 try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
451 fn symtab(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
452 const self = ctx.self;
453 const elf_file = ctx.elf_file;
454 try writer.writeAll(" globals\n");
455 for (self.symbols.items, 0..) |sym, i| {
456 const ref = self.resolveSymbol(@intCast(i), elf_file);
457 if (elf_file.symbol(ref)) |ref_sym| {
458 try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)});
459 } else {
460 try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
461 }
463462 }
464463 }
465}
464};
466465
467466const std = @import("std");
468467const Allocator = mem.Allocator;
src/link/Elf/Merge.zig+31-47
......@@ -157,42 +157,34 @@ pub const Section = struct {
157157 }
158158 };
159159
160 pub fn format(msec: Section, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
161 _ = msec;
162 _ = bw;
163 _ = unused_fmt_string;
164 @compileError("do not format directly");
165 }
166
167 pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(format2) {
160 pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
168161 return .{ .data = .{
169162 .msec = msec,
170163 .elf_file = elf_file,
171164 } };
172165 }
173166
174 const FormatContext = struct {
167 const Format = struct {
175168 msec: Section,
176169 elf_file: *Elf,
177 };
178170
179 pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
180 _ = unused_fmt_string;
181 const msec = ctx.msec;
182 const elf_file = ctx.elf_file;
183 try bw.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{
184 msec.name(elf_file),
185 msec.address(elf_file),
186 msec.size,
187 msec.alignment.toByteUnits() orelse 0,
188 msec.entsize,
189 msec.type,
190 msec.flags,
191 });
192 for (msec.subsections.items) |msub| {
193 try bw.print(" {f}\n", .{msub.fmt(elf_file)});
171 pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
172 const msec = f.msec;
173 const elf_file = f.elf_file;
174 try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{
175 msec.name(elf_file),
176 msec.address(elf_file),
177 msec.size,
178 msec.alignment.toByteUnits() orelse 0,
179 msec.entsize,
180 msec.type,
181 msec.flags,
182 });
183 for (msec.subsections.items) |msub| {
184 try writer.print(" {f}\n", .{msub.fmt(elf_file)});
185 }
194186 }
195 }
187 };
196188
197189 pub const Index = u32;
198190};
......@@ -219,36 +211,28 @@ pub const Subsection = struct {
219211 return msec.bytes.items[msub.string_index..][0..msub.size];
220212 }
221213
222 pub fn format(msub: Subsection, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
223 _ = msub;
224 _ = bw;
225 _ = unused_fmt_string;
226 @compileError("do not format directly");
227 }
228
229 pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(format2) {
214 pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
230215 return .{ .data = .{
231216 .msub = msub,
232217 .elf_file = elf_file,
233218 } };
234219 }
235220
236 const FormatContext = struct {
221 const Format = struct {
237222 msub: Subsection,
238223 elf_file: *Elf,
239 };
240224
241 pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
242 _ = unused_fmt_string;
243 const msub = ctx.msub;
244 const elf_file = ctx.elf_file;
245 try bw.print("@{x} : align({x}) : size({x})", .{
246 msub.address(elf_file),
247 msub.alignment,
248 msub.size,
249 });
250 if (!msub.alive) try bw.writeAll(" : [*]");
251 }
225 pub fn default(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
226 const msub = ctx.msub;
227 const elf_file = ctx.elf_file;
228 try writer.print("@{x} : align({x}) : size({x})", .{
229 msub.address(elf_file),
230 msub.alignment,
231 msub.size,
232 });
233 if (!msub.alive) try writer.writeAll(" : [*]");
234 }
235 };
252236
253237 pub const Index = u32;
254238};
src/link/Elf/Object.zig+70-86
......@@ -488,10 +488,7 @@ fn parseEhFrame(
488488 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
489489 } else {
490490 // TODO convert into an error
491 log.debug("{f}: no matching CIE found for FDE at offset {x}", .{
492 self.fmtPath(),
493 fde.offset,
494 });
491 log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ self.fmtPath(), fde.offset });
495492 continue;
496493 };
497494 fde.cie_index = cie_index;
......@@ -582,7 +579,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
582579 if (sym.flags.import) {
583580 if (sym.type(elf_file) != elf.STT_FUNC)
584581 // TODO convert into an error
585 log.debug("{fs}: {s}: CIE referencing external data reference", .{
582 log.debug("{f}: {s}: CIE referencing external data reference", .{
586583 self.fmtPath(), sym.name(elf_file),
587584 });
588585 sym.flags.needs_plt = true;
......@@ -1428,129 +1425,116 @@ pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group {
14281425 return &self.groups.items[index];
14291426}
14301427
1431pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1432 _ = self;
1433 _ = bw;
1434 _ = unused_fmt_string;
1435 @compileError("do not format objects directly");
1436}
1437
1438pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
1428pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) {
14391429 return .{ .data = .{
14401430 .object = self,
14411431 .elf_file = elf_file,
14421432 } };
14431433}
14441434
1445const FormatContext = struct {
1435const Format = struct {
14461436 object: *Object,
14471437 elf_file: *Elf,
1448};
14491438
1450fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1451 _ = unused_fmt_string;
1452 const object = ctx.object;
1453 const elf_file = ctx.elf_file;
1454 try bw.writeAll(" locals\n");
1455 for (object.locals()) |sym| {
1456 try bw.print(" {f}\n", .{sym.fmt(elf_file)});
1439 fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1440 const object = f.object;
1441 const elf_file = f.elf_file;
1442 try writer.writeAll(" locals\n");
1443 for (object.locals()) |sym| {
1444 try writer.print(" {f}\n", .{sym.fmt(elf_file)});
1445 }
1446 try writer.writeAll(" globals\n");
1447 for (object.globals(), 0..) |sym, i| {
1448 const first_global = object.first_global.?;
1449 const ref = object.resolveSymbol(@intCast(i + first_global), elf_file);
1450 if (elf_file.symbol(ref)) |ref_sym| {
1451 try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)});
1452 } else {
1453 try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
1454 }
1455 }
14571456 }
1458 try bw.writeAll(" globals\n");
1459 for (object.globals(), 0..) |sym, i| {
1460 const first_global = object.first_global.?;
1461 const ref = object.resolveSymbol(@intCast(i + first_global), elf_file);
1462 if (elf_file.symbol(ref)) |ref_sym| {
1463 try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)});
1464 } else {
1465 try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
1457
1458 fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1459 const object = f.object;
1460 try writer.writeAll(" atoms\n");
1461 for (object.atoms_indexes.items) |atom_index| {
1462 const atom_ptr = object.atom(atom_index) orelse continue;
1463 try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)});
14661464 }
14671465 }
1468}
14691466
1470pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
1467 fn cies(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1468 const object = f.object;
1469 try writer.writeAll(" cies\n");
1470 for (object.cies.items, 0..) |cie, i| {
1471 try writer.print(" cie({d}) : {f}\n", .{ i, cie.fmt(f.elf_file) });
1472 }
1473 }
1474
1475 fn fdes(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1476 const object = f.object;
1477 try writer.writeAll(" fdes\n");
1478 for (object.fdes.items, 0..) |fde, i| {
1479 try writer.print(" fde({d}) : {f}\n", .{ i, fde.fmt(f.elf_file) });
1480 }
1481 }
1482
1483 fn groups(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1484 const object = f.object;
1485 const elf_file = f.elf_file;
1486 try writer.writeAll(" groups\n");
1487 for (object.groups.items, 0..) |g, g_index| {
1488 try writer.print(" {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index });
1489 if (!g.alive) try writer.writeAll(" : [*]");
1490 try writer.writeByte('\n');
1491 const g_members = g.members(elf_file);
1492 for (g_members) |shndx| {
1493 const atom_index = object.atoms_indexes.items[shndx];
1494 const atom_ptr = object.atom(atom_index) orelse continue;
1495 try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) });
1496 }
1497 }
1498 }
1499};
1500
1501pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) {
14711502 return .{ .data = .{
14721503 .object = self,
14731504 .elf_file = elf_file,
14741505 } };
14751506}
14761507
1477fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1478 _ = unused_fmt_string;
1479 const object = ctx.object;
1480 try bw.writeAll(" atoms\n");
1481 for (object.atoms_indexes.items) |atom_index| {
1482 const atom_ptr = object.atom(atom_index) orelse continue;
1483 try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)});
1484 }
1485}
1486
1487pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) {
1508pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.cies) {
14881509 return .{ .data = .{
14891510 .object = self,
14901511 .elf_file = elf_file,
14911512 } };
14921513}
14931514
1494fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1495 _ = unused_fmt_string;
1496 const object = ctx.object;
1497 try bw.writeAll(" cies\n");
1498 for (object.cies.items, 0..) |cie, i| {
1499 try bw.print(" cie({d}) : {f}\n", .{ i, cie.fmt(ctx.elf_file) });
1500 }
1501}
1502
1503pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) {
1515pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.fdes) {
15041516 return .{ .data = .{
15051517 .object = self,
15061518 .elf_file = elf_file,
15071519 } };
15081520}
15091521
1510fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1511 _ = unused_fmt_string;
1512 const object = ctx.object;
1513 try bw.writeAll(" fdes\n");
1514 for (object.fdes.items, 0..) |fde, i| {
1515 try bw.print(" fde({d}) : {f}\n", .{ i, fde.fmt(ctx.elf_file) });
1516 }
1517}
1518
1519pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatGroups) {
1522pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.groups) {
15201523 return .{ .data = .{
15211524 .object = self,
15221525 .elf_file = elf_file,
15231526 } };
15241527}
15251528
1526fn formatGroups(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1527 comptime assert(unused_fmt_string.len == 0);
1528 const object = ctx.object;
1529 const elf_file = ctx.elf_file;
1530 try bw.writeAll(" groups\n");
1531 for (object.groups.items, 0..) |g, g_index| {
1532 try bw.print(" {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index });
1533 if (!g.alive) try bw.writeAll(" : [*]");
1534 try bw.writeByte('\n');
1535 const g_members = g.members(elf_file);
1536 for (g_members) |shndx| {
1537 const atom_index = object.atoms_indexes.items[shndx];
1538 const atom_ptr = object.atom(atom_index) orelse continue;
1539 try bw.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) });
1540 }
1541 }
1542}
1543
1544pub fn fmtPath(self: Object) std.fmt.Formatter(formatPath) {
1529pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) {
15451530 return .{ .data = self };
15461531}
15471532
1548fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1549 comptime assert(unused_fmt_string.len == 0);
1533fn formatPath(object: Object, writer: *std.io.Writer) std.io.Writer.Error!void {
15501534 if (object.archive) |ar| {
1551 try bw.print("{f}({f})", .{ ar.path, object.path });
1535 try writer.print("{f}({f})", .{ ar.path, object.path });
15521536 } else {
1553 try bw.print("{f}", .{object.path});
1537 try writer.print("{f}", .{object.path});
15541538 }
15551539}
15561540
src/link/Elf/SharedObject.zig+14-22
......@@ -509,39 +509,31 @@ pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void
509509 }
510510}
511511
512pub fn format(self: SharedObject, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
513 _ = self;
514 _ = bw;
515 _ = unused_fmt_string;
516 @compileError("unreachable");
517}
518
519pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
512pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) {
520513 return .{ .data = .{
521514 .shared = self,
522515 .elf_file = elf_file,
523516 } };
524517}
525518
526const FormatContext = struct {
519const Format = struct {
527520 shared: SharedObject,
528521 elf_file: *Elf,
529};
530522
531fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
532 comptime assert(unused_fmt_string.len == 0);
533 const shared = ctx.shared;
534 const elf_file = ctx.elf_file;
535 try bw.writeAll(" globals\n");
536 for (shared.symbols.items, 0..) |sym, i| {
537 const ref = shared.resolveSymbol(@intCast(i), elf_file);
538 if (elf_file.symbol(ref)) |ref_sym| {
539 try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)});
540 } else {
541 try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
523 fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
524 const shared = f.shared;
525 const elf_file = f.elf_file;
526 try writer.writeAll(" globals\n");
527 for (shared.symbols.items, 0..) |sym, i| {
528 const ref = shared.resolveSymbol(@intCast(i), elf_file);
529 if (elf_file.symbol(ref)) |ref_sym| {
530 try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)});
531 } else {
532 try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
533 }
542534 }
543535 }
544}
536};
545537
546538const SharedObject = @This();
547539
src/link/Elf/Symbol.zig+50-59
......@@ -316,81 +316,72 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
316316 out.st_size = esym.st_size;
317317}
318318
319pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
320 _ = symbol;
321 _ = bw;
322 _ = unused_fmt_string;
323 @compileError("do not format Symbol directly");
324}
325
326const FormatContext = struct {
319const Format = struct {
327320 symbol: Symbol,
328321 elf_file: *Elf,
322
323 fn name(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
324 const elf_file = f.elf_file;
325 const symbol = f.symbol;
326 try writer.writeAll(symbol.name(elf_file));
327 switch (symbol.version_index.VERSION) {
328 @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {},
329 else => {
330 const file_ptr = symbol.file(elf_file).?;
331 assert(file_ptr == .shared_object);
332 const shared_object = file_ptr.shared_object;
333 try writer.print("@{s}", .{shared_object.versionString(symbol.version_index)});
334 },
335 }
336 }
337
338 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
339 const symbol = f.symbol;
340 const elf_file = f.elf_file;
341 try writer.print("%{d} : {f} : @{x}", .{
342 symbol.esym_index,
343 symbol.fmtName(elf_file),
344 symbol.address(.{ .plt = false, .trampoline = false }, elf_file),
345 });
346 if (symbol.file(elf_file)) |file_ptr| {
347 if (symbol.isAbs(elf_file)) {
348 if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) {
349 try writer.writeAll(" : undef");
350 } else {
351 try writer.writeAll(" : absolute");
352 }
353 } else if (symbol.outputShndx(elf_file)) |shndx| {
354 try writer.print(" : shdr({d})", .{shndx});
355 }
356 if (symbol.atom(elf_file)) |atom_ptr| {
357 try writer.print(" : atom({d})", .{atom_ptr.atom_index});
358 }
359 var buf: [2]u8 = .{'_'} ** 2;
360 if (symbol.flags.@"export") buf[0] = 'E';
361 if (symbol.flags.import) buf[1] = 'I';
362 try writer.print(" : {s}", .{&buf});
363 if (symbol.flags.weak) try writer.writeAll(" : weak");
364 switch (file_ptr) {
365 inline else => |x| try writer.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }),
366 }
367 } else try writer.writeAll(" : unresolved");
368 }
329369};
330370
331pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(formatName) {
371pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.name) {
332372 return .{ .data = .{
333373 .symbol = symbol,
334374 .elf_file = elf_file,
335375 } };
336376}
337377
338fn formatName(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
339 _ = unused_fmt_string;
340 const elf_file = ctx.elf_file;
341 const symbol = ctx.symbol;
342 try bw.writeAll(symbol.name(elf_file));
343 switch (symbol.version_index.VERSION) {
344 @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {},
345 else => {
346 const file_ptr = symbol.file(elf_file).?;
347 assert(file_ptr == .shared_object);
348 const shared_object = file_ptr.shared_object;
349 try bw.print("@{s}", .{shared_object.versionString(symbol.version_index)});
350 },
351 }
352}
353
354pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(format2) {
378pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
355379 return .{ .data = .{
356380 .symbol = symbol,
357381 .elf_file = elf_file,
358382 } };
359383}
360384
361fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
362 comptime assert(unused_fmt_string.len == 0);
363 const symbol = ctx.symbol;
364 const elf_file = ctx.elf_file;
365 try bw.print("%{d} : {f} : @{x}", .{
366 symbol.esym_index,
367 symbol.fmtName(elf_file),
368 symbol.address(.{ .plt = false, .trampoline = false }, elf_file),
369 });
370 if (symbol.file(elf_file)) |file_ptr| {
371 if (symbol.isAbs(elf_file)) {
372 if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) {
373 try bw.writeAll(" : undef");
374 } else {
375 try bw.writeAll(" : absolute");
376 }
377 } else if (symbol.outputShndx(elf_file)) |shndx| {
378 try bw.print(" : shdr({d})", .{shndx});
379 }
380 if (symbol.atom(elf_file)) |atom_ptr| {
381 try bw.print(" : atom({d})", .{atom_ptr.atom_index});
382 }
383 var buf: [2]u8 = .{'_'} ** 2;
384 if (symbol.flags.@"export") buf[0] = 'E';
385 if (symbol.flags.import) buf[1] = 'I';
386 try bw.print(" : {s}", .{&buf});
387 if (symbol.flags.weak) try bw.writeAll(" : weak");
388 switch (file_ptr) {
389 inline else => |x| try bw.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }),
390 }
391 } else try bw.writeAll(" : unresolved");
392}
393
394385pub const Flags = packed struct {
395386 /// Whether the symbol is imported at runtime.
396387 import: bool = false,
src/link/Elf/Thunk.zig+11-19
......@@ -65,35 +65,27 @@ fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
6565 };
6666}
6767
68pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
69 _ = thunk;
70 _ = bw;
71 _ = unused_fmt_string;
72 @compileError("do not format Thunk directly");
73}
74
75pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) {
68pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
7669 return .{ .data = .{
7770 .thunk = thunk,
7871 .elf_file = elf_file,
7972 } };
8073}
8174
82const FormatContext = struct {
75const Format = struct {
8376 thunk: Thunk,
8477 elf_file: *Elf,
85};
8678
87fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
88 comptime assert(unused_fmt_string.len == 0);
89 const thunk = ctx.thunk;
90 const elf_file = ctx.elf_file;
91 try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
92 for (thunk.symbols.keys()) |ref| {
93 const sym = elf_file.symbol(ref).?;
94 try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
79 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
80 const thunk = f.thunk;
81 const elf_file = f.elf_file;
82 try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
83 for (thunk.symbols.keys()) |ref| {
84 const sym = elf_file.symbol(ref).?;
85 try writer.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
86 }
9587 }
96}
88};
9789
9890pub const Index = u32;
9991
src/link/Elf/ZigObject.zig+34-36
......@@ -799,9 +799,9 @@ pub fn initRelaSections(self: *ZigObject, elf_file: *Elf) !void {
799799 const out_shndx = atom_ptr.output_section_index;
800800 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];
801801 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;
802 const rela_sect_name = try std.fmt.allocPrintZ(gpa, ".rela{s}", .{
802 const rela_sect_name = try std.fmt.allocPrintSentinel(gpa, ".rela{s}", .{
803803 elf_file.getShString(out_shdr.sh_name),
804 });
804 }, 0);
805805 defer gpa.free(rela_sect_name);
806806 _ = elf_file.sectionByName(rela_sect_name) orelse
807807 try elf_file.addRelaShdr(try elf_file.insertShString(rela_sect_name), out_shndx);
......@@ -820,9 +820,9 @@ pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void {
820820 const out_shndx = atom_ptr.output_section_index;
821821 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];
822822 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;
823 const rela_sect_name = try std.fmt.allocPrintZ(gpa, ".rela{s}", .{
823 const rela_sect_name = try std.fmt.allocPrintSentinel(gpa, ".rela{s}", .{
824824 elf_file.getShString(out_shdr.sh_name),
825 });
825 }, 0);
826826 defer gpa.free(rela_sect_name);
827827 const out_rela_shndx = elf_file.sectionByName(rela_sect_name).?;
828828 const out_rela_shdr = &elf_file.sections.items(.shdr)[out_rela_shndx];
......@@ -1932,7 +1932,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e
19321932 .requires_padding = requires_padding,
19331933 });
19341934 atom_ptr.value = @intCast(alloc_res.value);
1935 log.debug("allocated {s} at {x}\n placement {?}", .{
1935 log.debug("allocated {s} at {x}\n placement {f}", .{
19361936 atom_ptr.name(elf_file),
19371937 atom_ptr.offset(elf_file),
19381938 alloc_res.placement,
......@@ -1977,7 +1977,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e
19771977 atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 };
19781978 }
19791979
1980 log.debug(" prev {?}, next {?}", .{ atom_ptr.prev_atom_ref, atom_ptr.next_atom_ref });
1980 log.debug(" prev {f}, next {f}", .{ atom_ptr.prev_atom_ref, atom_ptr.next_atom_ref });
19811981}
19821982
19831983pub fn resetShdrIndexes(self: *ZigObject, backlinks: []const u32) void {
......@@ -2186,48 +2186,46 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
21862186 }
21872187}
21882188
2189pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
2190 return .{ .data = .{
2191 .self = self,
2192 .elf_file = elf_file,
2193 } };
2194}
2195
2196const FormatContext = struct {
2189const Format = struct {
21972190 self: *ZigObject,
21982191 elf_file: *Elf,
2199};
22002192
2201fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2202 _ = unused_fmt_string;
2203 const self = ctx.self;
2204 const elf_file = ctx.elf_file;
2205 try bw.writeAll(" locals\n");
2206 for (self.local_symbols.items) |index| {
2207 const local = self.symbols.items[index];
2208 try bw.print(" {f}\n", .{local.fmt(elf_file)});
2193 fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
2194 const self = f.self;
2195 const elf_file = f.elf_file;
2196 try writer.writeAll(" locals\n");
2197 for (self.local_symbols.items) |index| {
2198 const local = self.symbols.items[index];
2199 try writer.print(" {f}\n", .{local.fmt(elf_file)});
2200 }
2201 try writer.writeAll(" globals\n");
2202 for (f.self.global_symbols.items) |index| {
2203 const global = self.symbols.items[index];
2204 try writer.print(" {f}\n", .{global.fmt(elf_file)});
2205 }
22092206 }
2210 try bw.writeAll(" globals\n");
2211 for (ctx.self.global_symbols.items) |index| {
2212 const global = self.symbols.items[index];
2213 try bw.print(" {f}\n", .{global.fmt(elf_file)});
2207
2208 fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
2209 try writer.writeAll(" atoms\n");
2210 for (f.self.atoms_indexes.items) |atom_index| {
2211 const atom_ptr = f.self.atom(atom_index) orelse continue;
2212 try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)});
2213 }
22142214 }
2215}
2215};
22162216
2217pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
2217pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) {
22182218 return .{ .data = .{
22192219 .self = self,
22202220 .elf_file = elf_file,
22212221 } };
22222222}
22232223
2224fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2225 comptime assert(unused_fmt_string.len == 0);
2226 try bw.writeAll(" atoms\n");
2227 for (ctx.self.atoms_indexes.items) |atom_index| {
2228 const atom_ptr = ctx.self.atom(atom_index) orelse continue;
2229 try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)});
2230 }
2224pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) {
2225 return .{ .data = .{
2226 .self = self,
2227 .elf_file = elf_file,
2228 } };
22312229}
22322230
22332231const ElfSym = struct {
src/link/Elf/eh_frame.zig+30-62
......@@ -47,48 +47,32 @@ pub const Fde = struct {
4747 return object.relocs.items[fde.rel_index..][0..fde.rel_num];
4848 }
4949
50 pub fn format(
51 fde: Fde,
52 bw: *Writer,
53 comptime unused_fmt_string: []const u8,
54 ) !void {
55 _ = fde;
56 _ = unused_fmt_string;
57 _ = bw;
58 @compileError("do not format FDEs directly");
59 }
60
61 pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) {
50 pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
6251 return .{ .data = .{
6352 .fde = fde,
6453 .elf_file = elf_file,
6554 } };
6655 }
6756
68 const FdeFormatContext = struct {
57 const Format = struct {
6958 fde: Fde,
7059 elf_file: *Elf,
71 };
7260
73 fn format2(
74 ctx: FdeFormatContext,
75 bw: *Writer,
76 comptime unused_fmt_string: []const u8,
77 ) !void {
78 _ = unused_fmt_string;
79 const fde = ctx.fde;
80 const elf_file = ctx.elf_file;
81 const base_addr = fde.address(elf_file);
82 const object = elf_file.file(fde.file_index).?.object;
83 const atom_name = fde.atom(object).name(elf_file);
84 try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{
85 base_addr + fde.out_offset,
86 fde.calcSize(),
87 fde.cie_index,
88 atom_name,
89 });
90 if (!fde.alive) try bw.writeAll(" : [*]");
91 }
61 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
62 const fde = f.fde;
63 const elf_file = f.elf_file;
64 const base_addr = fde.address(elf_file);
65 const object = elf_file.file(fde.file_index).?.object;
66 const atom_name = fde.atom(object).name(elf_file);
67 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
68 base_addr + fde.out_offset,
69 fde.calcSize(),
70 fde.cie_index,
71 atom_name,
72 });
73 if (!fde.alive) try writer.writeAll(" : [*]");
74 }
75 };
9276};
9377
9478pub const Cie = struct {
......@@ -146,44 +130,28 @@ pub const Cie = struct {
146130 return true;
147131 }
148132
149 pub fn format(
150 cie: Cie,
151 bw: *Writer,
152 comptime unused_fmt_string: []const u8,
153 ) !void {
154 _ = cie;
155 _ = unused_fmt_string;
156 _ = bw;
157 @compileError("do not format CIEs directly");
158 }
159
160 pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) {
133 pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
161134 return .{ .data = .{
162135 .cie = cie,
163136 .elf_file = elf_file,
164137 } };
165138 }
166139
167 const CieFormatContext = struct {
140 const Format = struct {
168141 cie: Cie,
169142 elf_file: *Elf,
170 };
171143
172 fn format2(
173 ctx: CieFormatContext,
174 bw: *Writer,
175 comptime unused_fmt_string: []const u8,
176 ) !void {
177 _ = unused_fmt_string;
178 const cie = ctx.cie;
179 const elf_file = ctx.elf_file;
180 const base_addr = cie.address(elf_file);
181 try bw.print("@{x} : size({x})", .{
182 base_addr + cie.out_offset,
183 cie.calcSize(),
184 });
185 if (!cie.alive) try bw.writeAll(" : [*]");
186 }
144 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
145 const cie = f.cie;
146 const elf_file = f.elf_file;
147 const base_addr = cie.address(elf_file);
148 try writer.print("@{x} : size({x})", .{
149 base_addr + cie.out_offset,
150 cie.calcSize(),
151 });
152 if (!cie.alive) try writer.writeAll(" : [*]");
153 }
154 };
187155};
188156
189157pub const Iterator = struct {
src/link/Elf/file.zig+6-7
......@@ -10,17 +10,16 @@ pub const File = union(enum) {
1010 };
1111 }
1212
13 pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {
13 pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) {
1414 return .{ .data = file };
1515 }
1616
17 fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
18 comptime assert(unused_fmt_string.len == 0);
17 fn formatPath(file: File, writer: *std.io.Writer) std.io.Writer.Error!void {
1918 switch (file) {
20 .zig_object => |zo| try bw.writeAll(zo.basename),
21 .linker_defined => try bw.writeAll("(linker defined)"),
22 .object => |x| try bw.print("{f}", .{x.fmtPath()}),
23 .shared_object => |x| try bw.print("{f}", .{x.path}),
19 .zig_object => |zo| try writer.writeAll(zo.basename),
20 .linker_defined => try writer.writeAll("(linker defined)"),
21 .object => |x| try writer.print("{f}", .{x.fmtPath()}),
22 .shared_object => |x| try writer.print("{f}", .{@as(Path, x.path)}),
2423 }
2524 }
2625
src/link/Elf/gc.zig+2-3
......@@ -185,9 +185,8 @@ const Level = struct {
185185 self.value += 1;
186186 }
187187
188 pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
189 comptime assert(unused_fmt_string.len == 0);
190 try bw.splatByteAll(' ', self.value);
188 pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void {
189 try w.splatByteAll(' ', self.value);
191190 }
192191};
193192
src/link/Elf/relocation.zig+5-6
......@@ -141,20 +141,19 @@ const FormatRelocTypeCtx = struct {
141141 cpu_arch: std.Target.Cpu.Arch,
142142};
143143
144pub fn fmtRelocType(r_type: u32, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatRelocType) {
144pub fn fmtRelocType(r_type: u32, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(FormatRelocTypeCtx, formatRelocType) {
145145 return .{ .data = .{
146146 .r_type = r_type,
147147 .cpu_arch = cpu_arch,
148148 } };
149149}
150150
151fn formatRelocType(ctx: FormatRelocTypeCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
152 comptime assert(unused_fmt_string.len == 0);
151fn formatRelocType(ctx: FormatRelocTypeCtx, writer: *std.io.Writer) std.io.Writer.Error!void {
153152 const r_type = ctx.r_type;
154153 switch (ctx.cpu_arch) {
155 .x86_64 => try bw.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}),
156 .aarch64 => try bw.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}),
157 .riscv64 => try bw.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}),
154 .x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}),
155 .aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}),
156 .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}),
158157 else => unreachable,
159158 }
160159}
src/link/Elf/synthetic_sections.zig+36-38
......@@ -606,31 +606,30 @@ pub const GotSection = struct {
606606 }
607607 }
608608
609 const FormatCtx = struct {
609 const Format = struct {
610610 got: GotSection,
611611 elf_file: *Elf,
612
613 pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
614 const got = f.got;
615 const elf_file = f.elf_file;
616 try writer.writeAll("GOT\n");
617 for (got.entries.items) |entry| {
618 const symbol = elf_file.symbol(entry.ref).?;
619 try writer.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
620 entry.cell_index,
621 entry.address(elf_file),
622 entry.ref,
623 symbol.address(.{}, elf_file),
624 symbol.name(elf_file),
625 });
626 }
627 }
612628 };
613629
614 pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) {
630 pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
615631 return .{ .data = .{ .got = got, .elf_file = elf_file } };
616632 }
617
618 pub fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
619 _ = unused_fmt_string;
620 const got = ctx.got;
621 const elf_file = ctx.elf_file;
622 try bw.writeAll("GOT\n");
623 for (got.entries.items) |entry| {
624 const symbol = elf_file.symbol(entry.ref).?;
625 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
626 entry.cell_index,
627 entry.address(elf_file),
628 entry.ref,
629 symbol.address(.{}, elf_file),
630 symbol.name(elf_file),
631 });
632 }
633 }
634633};
635634
636635pub const PltSection = struct {
......@@ -743,32 +742,31 @@ pub const PltSection = struct {
743742 }
744743 }
745744
746 const FormatCtx = struct {
745 const Format = struct {
747746 plt: PltSection,
748747 elf_file: *Elf,
748
749 pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
750 const plt = f.plt;
751 const elf_file = f.elf_file;
752 try writer.writeAll("PLT\n");
753 for (plt.symbols.items, 0..) |ref, i| {
754 const symbol = elf_file.symbol(ref).?;
755 try writer.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
756 i,
757 symbol.pltAddress(elf_file),
758 ref,
759 symbol.address(.{}, elf_file),
760 symbol.name(elf_file),
761 });
762 }
763 }
749764 };
750765
751 pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Formatter(format2) {
766 pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
752767 return .{ .data = .{ .plt = plt, .elf_file = elf_file } };
753768 }
754769
755 pub fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
756 _ = unused_fmt_string;
757 const plt = ctx.plt;
758 const elf_file = ctx.elf_file;
759 try bw.writeAll("PLT\n");
760 for (plt.symbols.items, 0..) |ref, i| {
761 const symbol = elf_file.symbol(ref).?;
762 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
763 i,
764 symbol.pltAddress(elf_file),
765 ref,
766 symbol.address(.{}, elf_file),
767 symbol.name(elf_file),
768 });
769 }
770 }
771
772770 const x86_64 = struct {
773771 fn write(plt: PltSection, elf_file: *Elf, bw: *Writer) Writer.Error!void {
774772 const shdrs = elf_file.sections.items(.shdr);
src/link/LdScript.zig+1-1
......@@ -42,7 +42,7 @@ pub fn parse(
4242 switch (tok.id) {
4343 .invalid => {
4444 return diags.failParse(path, "invalid token in LD script: '{f}' ({d}:{d})", .{
45 std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column,
45 std.ascii.hexEscape(tok.get(data), .lower), line, column,
4646 });
4747 },
4848 .new_line => {
src/link/Lld.zig+12-23
......@@ -294,7 +294,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
294294 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
295295 } else null;
296296
297 log.debug("zcu_obj_path={?}", .{zcu_obj_path});
297 log.debug("zcu_obj_path={?f}", .{zcu_obj_path});
298298
299299 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
300300 comp.compiler_rt_obj.?.full_object_path
......@@ -437,7 +437,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
437437 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename}));
438438 }
439439 if (comp.version) |version| {
440 try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor }));
440 try argv.append(try allocPrint(arena, "-VERSION:{d}.{d}", .{ version.major, version.minor }));
441441 }
442442
443443 if (target_util.llvmMachineAbi(target)) |mabi| {
......@@ -507,7 +507,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
507507
508508 if (comp.emit_implib) |raw_emit_path| {
509509 const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path);
510 try argv.append(try allocPrint(arena, "-IMPLIB:{}", .{path}));
510 try argv.append(try allocPrint(arena, "-IMPLIB:{f}", .{path}));
511511 }
512512
513513 if (comp.config.link_libc) {
......@@ -533,7 +533,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
533533 },
534534 .object, .archive => |obj| {
535535 if (obj.must_link) {
536 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Cache.Path, obj.path)}));
536 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{f}", .{@as(Cache.Path, obj.path)}));
537537 } else {
538538 argv.appendAssumeCapacity(try obj.path.toString(arena));
539539 }
......@@ -933,9 +933,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
933933 .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
934934 @tagName(base.build_id),
935935 })),
936 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
937 std.fmt.fmtSliceHexLower(hs.toSlice()),
938 })),
936 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})),
939937 }
940938
941939 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{elf.image_base}));
......@@ -1218,7 +1216,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
12181216 if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
12191217 }
12201218
1221 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
1219 const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{
12221220 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
12231221 });
12241222 try argv.append(lib_path);
......@@ -1231,14 +1229,14 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
12311229 }));
12321230 } else if (target.isFreeBSDLibC()) {
12331231 for (freebsd.libs) |lib| {
1234 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
1232 const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{
12351233 comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
12361234 });
12371235 try argv.append(lib_path);
12381236 }
12391237 } else if (target.isNetBSDLibC()) {
12401238 for (netbsd.libs) |lib| {
1241 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
1239 const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{
12421240 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
12431241 });
12441242 try argv.append(lib_path);
......@@ -1511,9 +1509,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
15111509 .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
15121510 @tagName(base.build_id),
15131511 })),
1514 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
1515 std.fmt.fmtSliceHexLower(hs.toSlice()),
1516 })),
1512 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})),
15171513 .md5 => {},
15181514 }
15191515
......@@ -1539,13 +1535,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
15391535
15401536 if (comp.config.link_libc and is_exe_or_dyn_lib) {
15411537 if (target.os.tag == .wasi) {
1542 for (comp.wasi_emulated_libs) |crt_file| {
1543 try argv.append(try comp.crtFileAsString(
1544 arena,
1545 wasi_libc.emulatedLibCRFileLibName(crt_file),
1546 ));
1547 }
1548
15491538 try argv.append(try comp.crtFileAsString(
15501539 arena,
15511540 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
......@@ -1660,7 +1649,7 @@ fn spawnLld(
16601649 child.stderr_behavior = .Pipe;
16611650
16621651 child.spawn() catch |err| break :term err;
1663 stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
1652 stderr = try child.stderr.?.deprecatedReader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
16641653 break :term child.wait();
16651654 }) catch |first_err| term: {
16661655 const err = switch (first_err) {
......@@ -1674,7 +1663,7 @@ fn spawnLld(
16741663 log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });
16751664 {
16761665 defer rsp_file.close();
1677 var rsp_buf = std.io.bufferedWriter(rsp_file.writer());
1666 var rsp_buf = std.io.bufferedWriter(rsp_file.deprecatedWriter());
16781667 const rsp_writer = rsp_buf.writer();
16791668 for (argv[2..]) |arg| {
16801669 try rsp_writer.writeByte('"');
......@@ -1708,7 +1697,7 @@ fn spawnLld(
17081697 rsp_child.stderr_behavior = .Pipe;
17091698
17101699 rsp_child.spawn() catch |err| break :err err;
1711 stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
1700 stderr = try rsp_child.stderr.?.deprecatedReader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
17121701 break :term rsp_child.wait() catch |err| break :err err;
17131702 }
17141703 },
src/link/MachO.zig+43-49
......@@ -3898,29 +3898,28 @@ pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void {
38983898 self.hot_state.mach_task = null;
38993899}
39003900
3901pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) {
3901pub fn dumpState(self: *MachO) std.fmt.Formatter(*MachO, fmtDumpState) {
39023902 return .{ .data = self };
39033903}
39043904
3905fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
3906 _ = unused_fmt_string;
3905fn fmtDumpState(self: *MachO, w: *Writer) Writer.Error!void {
39073906 if (self.getZigObject()) |zo| {
3908 try bw.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename });
3909 try bw.print("{f}{f}\n", .{
3907 try w.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename });
3908 try w.print("{f}{f}\n", .{
39103909 zo.fmtAtoms(self),
39113910 zo.fmtSymtab(self),
39123911 });
39133912 }
39143913 for (self.objects.items) |index| {
39153914 const object = self.getFile(index).?.object;
3916 try bw.print("object({d}) : {f} : has_debug({})", .{
3915 try w.print("object({d}) : {f} : has_debug({})", .{
39173916 index,
39183917 object.fmtPath(),
39193918 object.hasDebugInfo(),
39203919 });
3921 if (!object.alive) try bw.writeAll(" : ([*])");
3922 try bw.writeByte('\n');
3923 try bw.print("{f}{f}{f}{f}{f}\n", .{
3920 if (!object.alive) try w.writeAll(" : ([*])");
3921 try w.writeByte('\n');
3922 try w.print("{f}{f}{f}{f}{f}\n", .{
39243923 object.fmtAtoms(self),
39253924 object.fmtCies(self),
39263925 object.fmtFdes(self),
......@@ -3930,42 +3929,41 @@ fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u
39303929 }
39313930 for (self.dylibs.items) |index| {
39323931 const dylib = self.getFile(index).?.dylib;
3933 try bw.print("dylib({d}) : {f} : needed({}) : weak({})", .{
3932 try w.print("dylib({d}) : {f} : needed({}) : weak({})", .{
39343933 index,
39353934 @as(Path, dylib.path),
39363935 dylib.needed,
39373936 dylib.weak,
39383937 });
3939 if (!dylib.isAlive(self)) try bw.writeAll(" : ([*])");
3940 try bw.writeByte('\n');
3941 try bw.print("{f}\n", .{dylib.fmtSymtab(self)});
3938 if (!dylib.isAlive(self)) try w.writeAll(" : ([*])");
3939 try w.writeByte('\n');
3940 try w.print("{f}\n", .{dylib.fmtSymtab(self)});
39423941 }
39433942 if (self.getInternalObject()) |internal| {
3944 try bw.print("internal({d}) : internal\n", .{internal.index});
3945 try bw.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });
3943 try w.print("internal({d}) : internal\n", .{internal.index});
3944 try w.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });
39463945 }
3947 try bw.writeAll("thunks\n");
3946 try w.writeAll("thunks\n");
39483947 for (self.thunks.items, 0..) |thunk, index| {
3949 try bw.print("thunk({d}) : {f}\n", .{ index, thunk.fmt(self) });
3948 try w.print("thunk({d}) : {f}\n", .{ index, thunk.fmt(self) });
39503949 }
3951 try bw.print("stubs\n{f}\n", .{self.stubs.fmt(self)});
3952 try bw.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)});
3953 try bw.print("got\n{f}\n", .{self.got.fmt(self)});
3954 try bw.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)});
3955 try bw.writeByte('\n');
3956 try bw.print("sections\n{f}\n", .{self.fmtSections()});
3957 try bw.print("segments\n{f}\n", .{self.fmtSegments()});
3950 try w.print("stubs\n{f}\n", .{self.stubs.fmt(self)});
3951 try w.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)});
3952 try w.print("got\n{f}\n", .{self.got.fmt(self)});
3953 try w.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)});
3954 try w.writeByte('\n');
3955 try w.print("sections\n{f}\n", .{self.fmtSections()});
3956 try w.print("segments\n{f}\n", .{self.fmtSegments()});
39583957}
39593958
3960fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) {
3959fn fmtSections(self: *MachO) std.fmt.Formatter(*MachO, formatSections) {
39613960 return .{ .data = self };
39623961}
39633962
3964fn formatSections(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
3965 _ = unused_fmt_string;
3963fn formatSections(self: *MachO, w: *Writer) Writer.Error!void {
39663964 const slice = self.sections.slice();
39673965 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
3968 try bw.print(
3966 try w.print(
39693967 "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n",
39703968 .{
39713969 i, seg_id, header.segName(), header.sectName(), header.addr, header.offset,
......@@ -3975,26 +3973,24 @@ fn formatSections(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const
39753973 }
39763974}
39773975
3978fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) {
3976fn fmtSegments(self: *MachO) std.fmt.Formatter(*MachO, formatSegments) {
39793977 return .{ .data = self };
39803978}
39813979
3982fn formatSegments(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
3983 _ = unused_fmt_string;
3980fn formatSegments(self: *MachO, w: *Writer) Writer.Error!void {
39843981 for (self.segments.items, 0..) |seg, i| {
3985 try bw.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{
3982 try w.print("seg({d}) : {s} : @{x}-{x} ({x}-{x})\n", .{
39863983 i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize,
39873984 seg.fileoff, seg.fileoff + seg.filesize,
39883985 });
39893986 }
39903987}
39913988
3992pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) {
3989pub fn fmtSectType(tt: u8) std.fmt.Formatter(u8, formatSectType) {
39933990 return .{ .data = tt };
39943991}
39953992
3996fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
3997 _ = unused_fmt_string;
3993fn formatSectType(tt: u8, w: *Writer) Writer.Error!void {
39983994 const name = switch (tt) {
39993995 macho.S_REGULAR => "REGULAR",
40003996 macho.S_ZEROFILL => "ZEROFILL",
......@@ -4018,9 +4014,9 @@ fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) W
40184014 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS",
40194015 macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS",
40204016 macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS",
4021 else => |x| return bw.print("UNKNOWN({x})", .{x}),
4017 else => |x| return w.print("UNKNOWN({x})", .{x}),
40224018 };
4023 try bw.print("{s}", .{name});
4019 try w.print("{s}", .{name});
40244020}
40254021
40264022const is_hot_update_compatible = switch (builtin.target.os.tag) {
......@@ -4253,28 +4249,27 @@ pub const Platform = struct {
42534249 return false;
42544250 }
42554251
4256 pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) {
4252 pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.target) {
42574253 return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
42584254 }
42594255
4260 const FmtCtx = struct {
4256 const Format = struct {
42614257 platform: Platform,
42624258 cpu_arch: std.Target.Cpu.Arch,
4263 };
42644259
4265 pub fn formatTarget(ctx: FmtCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
4266 _ = unused_fmt_string;
4267 try bw.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });
4268 if (ctx.platform.abi != .none) {
4269 try bw.print("-{s}", .{@tagName(ctx.platform.abi)});
4260 pub fn target(f: Format, w: *Writer) Writer.Error!void {
4261 try w.print("{s}-{s}", .{ @tagName(f.cpu_arch), @tagName(f.platform.os_tag) });
4262 if (f.platform.abi != .none) {
4263 try w.print("-{s}", .{@tagName(f.platform.abi)});
4264 }
42704265 }
4271 }
4266 };
42724267
42734268 /// Caller owns the memory.
42744269 pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
42754270 var buffer = std.ArrayList(u8).init(gpa);
42764271 defer buffer.deinit();
4277 try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)});
4272 try buffer.writer().print("{f}", .{plat.fmtTarget(cpu_arch)});
42784273 return buffer.toOwnedSlice();
42794274 }
42804275
......@@ -4475,8 +4470,7 @@ pub const Ref = struct {
44754470 };
44764471 }
44774472
4478 pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
4479 comptime assert(unused_fmt_string.len == 0);
4473 pub fn format(ref: Ref, bw: *Writer) Writer.Error!void {
44804474 try bw.print("%{d} in file({d})", .{ ref.index, ref.file });
44814475 }
44824476};
src/link/MachO/Archive.zig+13-14
......@@ -30,7 +30,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
3030
3131 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
3232 return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{
33 std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
33 std.ascii.hexEscape(ARFMAG, .lower), std.ascii.hexEscape(&hdr.ar_fmag, .lower),
3434 });
3535 }
3636
......@@ -203,26 +203,25 @@ pub const ArSymtab = struct {
203203 try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len);
204204 }
205205
206 const FormatContext = struct {
206 const PrintFormat = struct {
207207 ar: ArSymtab,
208208 macho_file: *MachO,
209
210 fn default(f: PrintFormat, bw: *Writer) Writer.Error!void {
211 const ar = f.ar;
212 const macho_file = f.macho_file;
213 for (ar.entries.items, 0..) |entry, i| {
214 const name = ar.strtab.getAssumeExists(entry.off);
215 const file = macho_file.getFile(entry.file).?;
216 try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file, file.fmtPath() });
217 }
218 }
209219 };
210220
211 pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(format2) {
221 pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(PrintFormat, PrintFormat.default) {
212222 return .{ .data = .{ .ar = ar, .macho_file = macho_file } };
213223 }
214224
215 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
216 _ = unused_fmt_string;
217 const ar = ctx.ar;
218 const macho_file = ctx.macho_file;
219 for (ar.entries.items, 0..) |entry, i| {
220 const name = ar.strtab.getAssumeExists(entry.off);
221 const file = macho_file.getFile(entry.file).?;
222 try bw.print(" {d}: {s} in file({d})({f})\n", .{ i, name, entry.file, file.fmtPath() });
223 }
224 }
225
226225 const Entry = struct {
227226 /// Symbol name offset
228227 off: u32,
src/link/MachO/Atom.zig+25-39
......@@ -937,8 +937,8 @@ const x86_64 = struct {
937937 }
938938
939939 fn encode(insts: []const Instruction, code: []u8) !void {
940 var bw: Writer = .fixed(code);
941 for (insts) |inst| try inst.encode(&bw, .{});
940 var stream: Writer = .fixed(code);
941 for (insts) |inst| try inst.encode(&stream, .{});
942942 }
943943
944944 const bits = @import("../../arch/x86_64/bits.zig");
......@@ -1113,54 +1113,40 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
11131113 assert(i == buffer.len);
11141114}
11151115
1116pub fn format(
1117 atom: Atom,
1118 comptime unused_fmt_string: []const u8,
1119 options: std.fmt.FormatOptions,
1120 writer: anytype,
1121) !void {
1122 _ = atom;
1123 _ = unused_fmt_string;
1124 _ = options;
1125 _ = writer;
1126 @compileError("do not format Atom directly");
1127}
1128
1129pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(format2) {
1116pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
11301117 return .{ .data = .{
11311118 .atom = atom,
11321119 .macho_file = macho_file,
11331120 } };
11341121}
11351122
1136const FormatContext = struct {
1123const Format = struct {
11371124 atom: Atom,
11381125 macho_file: *MachO,
1139};
11401126
1141fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1142 comptime assert(unused_fmt_string.len == 0);
1143 const atom = ctx.atom;
1144 const macho_file = ctx.macho_file;
1145 const file = atom.getFile(macho_file);
1146 try bw.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1147 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1148 atom.out_n_sect, atom.alignment, atom.size,
1149 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
1150 });
1151 if (!atom.isAlive()) try bw.writeAll(" : [*]");
1152 if (atom.getUnwindRecords(macho_file).len > 0) {
1153 try bw.writeAll(" : unwind{ ");
1154 const extra = atom.getExtra(macho_file);
1155 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
1156 const rec = file.object.getUnwindRecord(index);
1157 try bw.print("{d}", .{index});
1158 if (!rec.alive) try bw.writeAll("([*])");
1159 if (i < extra.unwind_index + extra.unwind_count - 1) try bw.writeAll(", ");
1127 fn print(f: Format, w: *Writer) Writer.Error!void {
1128 const atom = f.atom;
1129 const macho_file = f.macho_file;
1130 const file = atom.getFile(macho_file);
1131 try w.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1132 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1133 atom.out_n_sect, atom.alignment, atom.size,
1134 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
1135 });
1136 if (!atom.isAlive()) try w.writeAll(" : [*]");
1137 if (atom.getUnwindRecords(macho_file).len > 0) {
1138 try w.writeAll(" : unwind{ ");
1139 const extra = atom.getExtra(macho_file);
1140 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
1141 const rec = file.object.getUnwindRecord(index);
1142 try w.print("{d}", .{index});
1143 if (!rec.alive) try w.writeAll("([*])");
1144 if (i < extra.unwind_index + extra.unwind_count - 1) try w.writeAll(", ");
1145 }
1146 try w.writeAll(" }");
11601147 }
1161 try bw.writeAll(" }");
11621148 }
1163}
1149};
11641150
11651151pub const Index = u32;
11661152
src/link/MachO/Dylib.zig+15-29
......@@ -650,46 +650,32 @@ pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void {
650650 }
651651}
652652
653pub fn format(
654 self: *Dylib,
655 comptime unused_fmt_string: []const u8,
656 options: std.fmt.FormatOptions,
657 writer: anytype,
658) !void {
659 _ = self;
660 _ = unused_fmt_string;
661 _ = options;
662 _ = writer;
663 @compileError("do not format dylib directly");
664}
665
666pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(formatSymtab) {
653pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
667654 return .{ .data = .{
668655 .dylib = self,
669656 .macho_file = macho_file,
670657 } };
671658}
672659
673const FormatContext = struct {
660const Format = struct {
674661 dylib: *Dylib,
675662 macho_file: *MachO,
676};
677663
678fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
679 _ = unused_fmt_string;
680 const dylib = ctx.dylib;
681 const macho_file = ctx.macho_file;
682 try bw.writeAll(" globals\n");
683 for (dylib.symbols.items, 0..) |sym, i| {
684 const ref = dylib.getSymbolRef(@intCast(i), macho_file);
685 if (ref.getFile(macho_file) == null) {
686 // TODO any better way of handling this?
687 try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
688 } else {
689 try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
664 fn symtab(f: Format, w: *Writer) Writer.Error!void {
665 const dylib = f.dylib;
666 const macho_file = f.macho_file;
667 try w.writeAll(" globals\n");
668 for (dylib.symbols.items, 0..) |sym, i| {
669 const ref = dylib.getSymbolRef(@intCast(i), macho_file);
670 if (ref.getFile(macho_file) == null) {
671 // TODO any better way of handling this?
672 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
673 } else {
674 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
675 }
690676 }
691677 }
692}
678};
693679
694680pub const TargetMatcher = struct {
695681 allocator: Allocator,
src/link/MachO/InternalObject.zig+26-28
......@@ -836,50 +836,48 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool {
836836 return false;
837837}
838838
839const FormatContext = struct {
839const Format = struct {
840840 self: *InternalObject,
841841 macho_file: *MachO,
842
843 fn atoms(f: Format, w: *Writer) Writer.Error!void {
844 try w.writeAll(" atoms\n");
845 for (f.self.getAtoms()) |atom_index| {
846 const atom = f.self.getAtom(atom_index) orelse continue;
847 try w.print(" {f}\n", .{atom.fmt(f.macho_file)});
848 }
849 }
850
851 fn symtab(f: Format, w: *Writer) Writer.Error!void {
852 const macho_file = f.macho_file;
853 const self = f.self;
854 try w.writeAll(" symbols\n");
855 for (self.symbols.items, 0..) |sym, i| {
856 const ref = self.getSymbolRef(@intCast(i), macho_file);
857 if (ref.getFile(macho_file) == null) {
858 // TODO any better way of handling this?
859 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
860 } else {
861 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
862 }
863 }
864 }
842865};
843866
844pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) {
867pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) {
845868 return .{ .data = .{
846869 .self = self,
847870 .macho_file = macho_file,
848871 } };
849872}
850873
851fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
852 _ = unused_fmt_string;
853 try bw.writeAll(" atoms\n");
854 for (ctx.self.getAtoms()) |atom_index| {
855 const atom = ctx.self.getAtom(atom_index) orelse continue;
856 try bw.print(" {f}\n", .{atom.fmt(ctx.macho_file)});
857 }
858}
859
860pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) {
874pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
861875 return .{ .data = .{
862876 .self = self,
863877 .macho_file = macho_file,
864878 } };
865879}
866880
867fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
868 comptime assert(unused_fmt_string.len == 0);
869 const macho_file = ctx.macho_file;
870 const self = ctx.self;
871 try bw.writeAll(" symbols\n");
872 for (self.symbols.items, 0..) |sym, i| {
873 const ref = self.getSymbolRef(@intCast(i), macho_file);
874 if (ref.getFile(macho_file) == null) {
875 // TODO any better way of handling this?
876 try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
877 } else {
878 try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
879 }
880 }
881}
882
883881const Section = struct {
884882 header: macho.section_64,
885883 relocs: std.ArrayListUnmanaged(Relocation) = .empty,
src/link/MachO/Object.zig+95-112
......@@ -308,7 +308,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
308308 } else nlists.len;
309309
310310 if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
311 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}$begin", .{ sect.segName(), sect.sectName() });
311 const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$begin", .{
312 sect.segName(), sect.sectName(),
313 }, 0);
312314 defer allocator.free(name);
313315 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
314316 const atom_index = try self.addAtom(allocator, .{
......@@ -364,7 +366,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
364366 // which cannot be contained in any non-zero atom (since then this atom
365367 // would exceed section boundaries). In order to facilitate this behaviour,
366368 // we create a dummy zero-sized atom at section end (addr + size).
367 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}$end", .{ sect.segName(), sect.sectName() });
369 const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}$end", .{
370 sect.segName(), sect.sectName(),
371 }, 0);
368372 defer allocator.free(name);
369373 const atom_index = try self.addAtom(allocator, .{
370374 .name = try self.addString(allocator, name),
......@@ -394,7 +398,7 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
394398 if (isFixedSizeLiteral(sect)) continue;
395399 if (isPtrLiteral(sect)) continue;
396400
397 const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
401 const name = try std.fmt.allocPrintSentinel(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }, 0);
398402 defer allocator.free(name);
399403
400404 const atom_index = try self.addAtom(allocator, .{
......@@ -462,7 +466,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m
462466 }
463467 end += 1;
464468
465 const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count});
469 const name = try std.fmt.allocPrintSentinel(allocator, "l._str{d}", .{count}, 0);
466470 defer allocator.free(name);
467471 const name_str = try self.addString(allocator, name);
468472
......@@ -529,7 +533,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO
529533 pos += rec_size;
530534 count += 1;
531535 }) {
532 const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count});
536 const name = try std.fmt.allocPrintSentinel(allocator, "l._literal{d}", .{count}, 0);
533537 defer allocator.free(name);
534538 const name_str = try self.addString(allocator, name);
535539
......@@ -587,7 +591,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)
587591 for (0..num_ptrs) |i| {
588592 const pos: u32 = @as(u32, @intCast(i)) * rec_size;
589593
590 const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i});
594 const name = try std.fmt.allocPrintSentinel(allocator, "l._ptr{d}", .{i}, 0);
591595 defer allocator.free(name);
592596 const name_str = try self.addString(allocator, name);
593597
......@@ -1558,7 +1562,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
15581562 const nlist = &self.symtab.items(.nlist)[nlist_idx];
15591563 const nlist_atom = &self.symtab.items(.atom)[nlist_idx];
15601564
1561 const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});
1565 const name = try std.fmt.allocPrintSentinel(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)}, 0);
15621566 defer gpa.free(name);
15631567
15641568 const alignment = (nlist.n_desc >> 8) & 0x0f;
......@@ -2512,130 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_
25122516 return data;
25132517}
25142518
2515pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2516 _ = self;
2517 _ = bw;
2518 _ = unused_fmt_string;
2519 @compileError("do not format objects directly");
2520}
2521
2522const FormatContext = struct {
2519const Format = struct {
25232520 object: *Object,
25242521 macho_file: *MachO,
2522
2523 fn atoms(f: Format, w: *Writer) Writer.Error!void {
2524 const object = f.object;
2525 const macho_file = f.macho_file;
2526 try w.writeAll(" atoms\n");
2527 for (object.getAtoms()) |atom_index| {
2528 const atom = object.getAtom(atom_index) orelse continue;
2529 try w.print(" {f}\n", .{atom.fmt(macho_file)});
2530 }
2531 }
2532 fn cies(f: Format, w: *Writer) Writer.Error!void {
2533 const object = f.object;
2534 try w.writeAll(" cies\n");
2535 for (object.cies.items, 0..) |cie, i| {
2536 try w.print(" cie({d}) : {f}\n", .{ i, cie.fmt(f.macho_file) });
2537 }
2538 }
2539 fn fdes(f: Format, w: *Writer) Writer.Error!void {
2540 const object = f.object;
2541 try w.writeAll(" fdes\n");
2542 for (object.fdes.items, 0..) |fde, i| {
2543 try w.print(" fde({d}) : {f}\n", .{ i, fde.fmt(f.macho_file) });
2544 }
2545 }
2546 fn unwindRecords(f: Format, w: *Writer) Writer.Error!void {
2547 const object = f.object;
2548 const macho_file = f.macho_file;
2549 try w.writeAll(" unwind records\n");
2550 for (object.unwind_records_indexes.items) |rec| {
2551 try w.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) });
2552 }
2553 }
2554
2555 fn symtab(f: Format, w: *Writer) Writer.Error!void {
2556 const object = f.object;
2557 const macho_file = f.macho_file;
2558 try w.writeAll(" symbols\n");
2559 for (object.symbols.items, 0..) |sym, i| {
2560 const ref = object.getSymbolRef(@intCast(i), macho_file);
2561 if (ref.getFile(macho_file) == null) {
2562 // TODO any better way of handling this?
2563 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
2564 } else {
2565 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
2566 }
2567 }
2568 for (object.stab_files.items) |sf| {
2569 try w.print(" stabs({s},{s},{s})\n", .{
2570 sf.getCompDir(object.*),
2571 sf.getTuName(object.*),
2572 sf.getOsoPath(object.*),
2573 });
2574 for (sf.stabs.items) |stab| {
2575 try w.print(" {f}", .{stab.fmt(object.*)});
2576 }
2577 }
2578 }
25252579};
25262580
2527pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatAtoms) {
2581pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) {
25282582 return .{ .data = .{
25292583 .object = self,
25302584 .macho_file = macho_file,
25312585 } };
25322586}
25332587
2534fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2535 _ = unused_fmt_string;
2536 const object = ctx.object;
2537 const macho_file = ctx.macho_file;
2538 try bw.writeAll(" atoms\n");
2539 for (object.getAtoms()) |atom_index| {
2540 const atom = object.getAtom(atom_index) orelse continue;
2541 try bw.print(" {f}\n", .{atom.fmt(macho_file)});
2542 }
2543}
2544
2545pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatCies) {
2588pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.cies) {
25462589 return .{ .data = .{
25472590 .object = self,
25482591 .macho_file = macho_file,
25492592 } };
25502593}
25512594
2552fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2553 _ = unused_fmt_string;
2554 const object = ctx.object;
2555 try bw.writeAll(" cies\n");
2556 for (object.cies.items, 0..) |cie, i| {
2557 try bw.print(" cie({d}) : {f}\n", .{ i, cie.fmt(ctx.macho_file) });
2558 }
2559}
2560
2561pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatFdes) {
2595pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.fdes) {
25622596 return .{ .data = .{
25632597 .object = self,
25642598 .macho_file = macho_file,
25652599 } };
25662600}
25672601
2568fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2569 _ = unused_fmt_string;
2570 const object = ctx.object;
2571 try bw.writeAll(" fdes\n");
2572 for (object.fdes.items, 0..) |fde, i| {
2573 try bw.print(" fde({d}) : {f}\n", .{ i, fde.fmt(ctx.macho_file) });
2574 }
2575}
2576
2577pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatUnwindRecords) {
2602pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.unwindRecords) {
25782603 return .{ .data = .{
25792604 .object = self,
25802605 .macho_file = macho_file,
25812606 } };
25822607}
25832608
2584fn formatUnwindRecords(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2585 _ = unused_fmt_string;
2586 const object = ctx.object;
2587 const macho_file = ctx.macho_file;
2588 try bw.writeAll(" unwind records\n");
2589 for (object.unwind_records_indexes.items) |rec| {
2590 try bw.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) });
2591 }
2592}
2593
2594pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatSymtab) {
2609pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
25952610 return .{ .data = .{
25962611 .object = self,
25972612 .macho_file = macho_file,
25982613 } };
25992614}
26002615
2601fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2602 _ = unused_fmt_string;
2603 const object = ctx.object;
2604 const macho_file = ctx.macho_file;
2605 try bw.writeAll(" symbols\n");
2606 for (object.symbols.items, 0..) |sym, i| {
2607 const ref = object.getSymbolRef(@intCast(i), macho_file);
2608 if (ref.getFile(macho_file) == null) {
2609 // TODO any better way of handling this?
2610 try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
2611 } else {
2612 try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
2613 }
2614 }
2615 for (object.stab_files.items) |sf| {
2616 try bw.print(" stabs({s},{s},{s})\n", .{
2617 sf.getCompDir(object.*),
2618 sf.getTuName(object.*),
2619 sf.getOsoPath(object.*),
2620 });
2621 for (sf.stabs.items) |stab| {
2622 try bw.print(" {f}", .{stab.fmt(object.*)});
2623 }
2624 }
2625}
2626
2627pub fn fmtPath(self: Object) std.fmt.Formatter(formatPath) {
2616pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) {
26282617 return .{ .data = self };
26292618}
26302619
2631fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2632 _ = unused_fmt_string;
2620fn formatPath(object: Object, w: *Writer) Writer.Error!void {
26332621 if (object.in_archive) |ar| {
2634 try bw.print("{f}({s})", .{
2622 try w.print("{f}({s})", .{
26352623 ar.path, object.path.basename(),
26362624 });
26372625 } else {
2638 try bw.print("{f}", .{object.path});
2626 try w.print("{f}", .{object.path});
26392627 }
26402628}
26412629
......@@ -2689,30 +2677,25 @@ const StabFile = struct {
26892677 return object.symbols.items[index];
26902678 }
26912679
2692 pub fn format(stab: Stab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2693 _ = stab;
2694 _ = bw;
2695 _ = unused_fmt_string;
2696 @compileError("do not format stabs directly");
2697 }
2698
2699 const StabFormatContext = struct { Stab, Object };
2680 const Format = struct {
2681 stab: Stab,
2682 object: Object,
27002683
2701 pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) {
2702 return .{ .data = .{ stab, object } };
2703 }
2704
2705 fn format2(ctx: StabFormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
2706 _ = unused_fmt_string;
2707 const stab, const object = ctx;
2708 const sym = stab.getSymbol(object).?;
2709 if (stab.is_func) {
2710 try bw.print("func({d})", .{stab.index.?});
2711 } else if (sym.visibility == .global) {
2712 try bw.print("gsym({d})", .{stab.index.?});
2713 } else {
2714 try bw.print("stsym({d})", .{stab.index.?});
2684 fn default(f: Stab.Format, w: *Writer) Writer.Error!void {
2685 const stab = f.stab;
2686 const sym = stab.getSymbol(f.object).?;
2687 if (stab.is_func) {
2688 try w.print("func({d})", .{stab.index.?});
2689 } else if (sym.visibility == .global) {
2690 try w.print("gsym({d})", .{stab.index.?});
2691 } else {
2692 try w.print("stsym({d})", .{stab.index.?});
2693 }
27152694 }
2695 };
2696
2697 pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(Stab.Format, Stab.Format.default) {
2698 return .{ .data = .{ .stab = stab, .object = object } };
27162699 }
27172700 };
27182701};
src/link/MachO/Relocation.zig+43-42
......@@ -70,50 +70,51 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {
7070 return lhs.offset < rhs.offset;
7171}
7272
73const FormatCtx = struct { Relocation, std.Target.Cpu.Arch };
74
75pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatPretty) {
76 return .{ .data = .{ rel, cpu_arch } };
73pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) {
74 return .{ .data = .{ .relocation = rel, .arch = cpu_arch } };
7775}
7876
79fn formatPretty(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
80 _ = unused_fmt_string;
81 const rel, const cpu_arch = ctx;
82 try bw.writeAll(switch (rel.type) {
83 .signed => "X86_64_RELOC_SIGNED",
84 .signed1 => "X86_64_RELOC_SIGNED_1",
85 .signed2 => "X86_64_RELOC_SIGNED_2",
86 .signed4 => "X86_64_RELOC_SIGNED_4",
87 .got_load => "X86_64_RELOC_GOT_LOAD",
88 .tlv => "X86_64_RELOC_TLV",
89 .page => "ARM64_RELOC_PAGE21",
90 .pageoff => "ARM64_RELOC_PAGEOFF12",
91 .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21",
92 .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
93 .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21",
94 .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
95 .branch => switch (cpu_arch) {
96 .x86_64 => "X86_64_RELOC_BRANCH",
97 .aarch64 => "ARM64_RELOC_BRANCH26",
98 else => unreachable,
99 },
100 .got => switch (cpu_arch) {
101 .x86_64 => "X86_64_RELOC_GOT",
102 .aarch64 => "ARM64_RELOC_POINTER_TO_GOT",
103 else => unreachable,
104 },
105 .subtractor => switch (cpu_arch) {
106 .x86_64 => "X86_64_RELOC_SUBTRACTOR",
107 .aarch64 => "ARM64_RELOC_SUBTRACTOR",
108 else => unreachable,
109 },
110 .unsigned => switch (cpu_arch) {
111 .x86_64 => "X86_64_RELOC_UNSIGNED",
112 .aarch64 => "ARM64_RELOC_UNSIGNED",
113 else => unreachable,
114 },
115 });
116}
77const Format = struct {
78 relocation: Relocation,
79 arch: std.Target.Cpu.Arch,
80
81 fn pretty(f: Format, w: *Writer) Writer.Error!void {
82 try w.writeAll(switch (f.relocation.type) {
83 .signed => "X86_64_RELOC_SIGNED",
84 .signed1 => "X86_64_RELOC_SIGNED_1",
85 .signed2 => "X86_64_RELOC_SIGNED_2",
86 .signed4 => "X86_64_RELOC_SIGNED_4",
87 .got_load => "X86_64_RELOC_GOT_LOAD",
88 .tlv => "X86_64_RELOC_TLV",
89 .page => "ARM64_RELOC_PAGE21",
90 .pageoff => "ARM64_RELOC_PAGEOFF12",
91 .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21",
92 .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
93 .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21",
94 .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
95 .branch => switch (f.arch) {
96 .x86_64 => "X86_64_RELOC_BRANCH",
97 .aarch64 => "ARM64_RELOC_BRANCH26",
98 else => unreachable,
99 },
100 .got => switch (f.arch) {
101 .x86_64 => "X86_64_RELOC_GOT",
102 .aarch64 => "ARM64_RELOC_POINTER_TO_GOT",
103 else => unreachable,
104 },
105 .subtractor => switch (f.arch) {
106 .x86_64 => "X86_64_RELOC_SUBTRACTOR",
107 .aarch64 => "ARM64_RELOC_SUBTRACTOR",
108 else => unreachable,
109 },
110 .unsigned => switch (f.arch) {
111 .x86_64 => "X86_64_RELOC_UNSIGNED",
112 .aarch64 => "ARM64_RELOC_UNSIGNED",
113 else => unreachable,
114 },
115 });
116 }
117};
117118
118119pub const Type = enum {
119120 // x86_64
src/link/MachO/Symbol.zig+39-47
......@@ -286,59 +286,51 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
286286 }
287287}
288288
289pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
290 _ = symbol;
291 _ = bw;
292 _ = unused_fmt_string;
293 @compileError("do not format symbols directly");
294}
295
296const FormatContext = struct {
297 symbol: Symbol,
298 macho_file: *MachO,
299};
300
301pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(format2) {
289pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
302290 return .{ .data = .{
303291 .symbol = symbol,
304292 .macho_file = macho_file,
305293 } };
306294}
307295
308fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
309 comptime assert(unused_fmt_string.len == 0);
310 const symbol = ctx.symbol;
311 try bw.print("%{d} : {s} : @{x}", .{
312 symbol.nlist_idx,
313 symbol.getName(ctx.macho_file),
314 symbol.getAddress(.{}, ctx.macho_file),
315 });
316 if (symbol.getFile(ctx.macho_file)) |file| {
317 if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {
318 try bw.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});
319 }
320 if (symbol.getAtom(ctx.macho_file)) |atom| {
321 try bw.print(" : atom({d})", .{atom.atom_index});
322 }
323 var buf: [3]u8 = .{'_'} ** 3;
324 if (symbol.flags.@"export") buf[0] = 'E';
325 if (symbol.flags.import) buf[1] = 'I';
326 switch (symbol.visibility) {
327 .local => buf[2] = 'L',
328 .hidden => buf[2] = 'H',
329 .global => buf[2] = 'G',
330 }
331 try bw.print(" : {s}", .{&buf});
332 if (symbol.flags.weak) try bw.writeAll(" : weak");
333 if (symbol.isSymbolStab(ctx.macho_file)) try bw.writeAll(" : stab");
334 switch (file) {
335 .zig_object => |x| try bw.print(" : zig_object({d})", .{x.index}),
336 .internal => |x| try bw.print(" : internal({d})", .{x.index}),
337 .object => |x| try bw.print(" : object({d})", .{x.index}),
338 .dylib => |x| try bw.print(" : dylib({d})", .{x.index}),
339 }
340 } else try bw.writeAll(" : unresolved");
341}
296const Format = struct {
297 symbol: Symbol,
298 macho_file: *MachO,
299
300 fn default(f: Format, w: *Writer) Writer.Error!void {
301 const symbol = f.symbol;
302 try w.print("%{d} : {s} : @{x}", .{
303 symbol.nlist_idx,
304 symbol.getName(f.macho_file),
305 symbol.getAddress(.{}, f.macho_file),
306 });
307 if (symbol.getFile(f.macho_file)) |file| {
308 if (symbol.getOutputSectionIndex(f.macho_file) != 0) {
309 try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)});
310 }
311 if (symbol.getAtom(f.macho_file)) |atom| {
312 try w.print(" : atom({d})", .{atom.atom_index});
313 }
314 var buf: [3]u8 = .{'_'} ** 3;
315 if (symbol.flags.@"export") buf[0] = 'E';
316 if (symbol.flags.import) buf[1] = 'I';
317 switch (symbol.visibility) {
318 .local => buf[2] = 'L',
319 .hidden => buf[2] = 'H',
320 .global => buf[2] = 'G',
321 }
322 try w.print(" : {s}", .{&buf});
323 if (symbol.flags.weak) try w.writeAll(" : weak");
324 if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab");
325 switch (file) {
326 .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}),
327 .internal => |x| try w.print(" : internal({d})", .{x.index}),
328 .object => |x| try w.print(" : object({d})", .{x.index}),
329 .dylib => |x| try w.print(" : dylib({d})", .{x.index}),
330 }
331 } else try w.writeAll(" : unresolved");
332 }
333};
342334
343335pub const Flags = packed struct {
344336 /// Whether the symbol is imported at runtime.
src/link/MachO/Thunk.zig+11-19
......@@ -61,35 +61,27 @@ pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
6161 }
6262}
6363
64pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
65 _ = thunk;
66 _ = bw;
67 _ = unused_fmt_string;
68 @compileError("do not format Thunk directly");
69}
70
71pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) {
64pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
7265 return .{ .data = .{
7366 .thunk = thunk,
7467 .macho_file = macho_file,
7568 } };
7669}
7770
78const FormatContext = struct {
71const Format = struct {
7972 thunk: Thunk,
8073 macho_file: *MachO,
81};
8274
83fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
84 _ = unused_fmt_string;
85 const thunk = ctx.thunk;
86 const macho_file = ctx.macho_file;
87 try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
88 for (thunk.symbols.keys()) |ref| {
89 const sym = ref.getSymbol(macho_file).?;
90 try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
75 fn default(f: Format, w: *Writer) Writer.Error!void {
76 const thunk = f.thunk;
77 const macho_file = f.macho_file;
78 try w.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
79 for (thunk.symbols.keys()) |ref| {
80 const sym = ref.getSymbol(macho_file).?;
81 try w.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
82 }
9183 }
92}
84};
9385
9486const trampoline_size = 3 * @sizeOf(u32);
9587
src/link/MachO/UnwindInfo.zig+29-46
......@@ -449,9 +449,8 @@ pub const Encoding = extern struct {
449449 return enc.enc == other.enc;
450450 }
451451
452 pub fn format(enc: Encoding, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
453 _ = unused_fmt_string;
454 try bw.print("0x{x:0>8}", .{enc.enc});
452 pub fn format(enc: Encoding, w: *Writer) Writer.Error!void {
453 try w.print("0x{x:0>8}", .{enc.enc});
455454 }
456455};
457456
......@@ -505,36 +504,28 @@ pub const Record = struct {
505504 return lsda.getAddress(macho_file) + rec.lsda_offset;
506505 }
507506
508 pub fn format(rec: Record, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
509 _ = rec;
510 _ = bw;
511 _ = unused_fmt_string;
512 @compileError("do not format UnwindInfo.Records directly");
513 }
514
515 pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(format2) {
507 pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
516508 return .{ .data = .{
517509 .rec = rec,
518510 .macho_file = macho_file,
519511 } };
520512 }
521513
522 const FormatContext = struct {
514 const Format = struct {
523515 rec: Record,
524516 macho_file: *MachO,
525 };
526517
527 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
528 _ = unused_fmt_string;
529 const rec = ctx.rec;
530 const macho_file = ctx.macho_file;
531 try bw.print("{x} : len({x})", .{
532 rec.enc.enc, rec.length,
533 });
534 if (rec.enc.isDwarf(macho_file)) try bw.print(" : fde({d})", .{rec.fde});
535 try bw.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)});
536 if (!rec.alive) try bw.writeAll(" : [*]");
537 }
518 fn default(f: Format, w: *Writer) Writer.Error!void {
519 const rec = f.rec;
520 const macho_file = f.macho_file;
521 try w.print("{x} : len({x})", .{
522 rec.enc.enc, rec.length,
523 });
524 if (rec.enc.isDwarf(macho_file)) try w.print(" : fde({d})", .{rec.fde});
525 try w.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)});
526 if (!rec.alive) try w.writeAll(" : [*]");
527 }
528 };
538529
539530 pub const Index = u32;
540531
......@@ -589,33 +580,25 @@ const Page = struct {
589580 return null;
590581 }
591582
592 fn format(page: *const Page, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {
593 _ = page;
594 _ = bw;
595 _ = unused_format_string;
596 @compileError("do not format Page directly; use page.fmt()");
597 }
598
599 const FormatPageContext = struct {
583 const Format = struct {
600584 page: Page,
601585 info: UnwindInfo,
602 };
603586
604 fn format2(ctx: FormatPageContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {
605 _ = unused_format_string;
606 try bw.writeAll("Page:\n");
607 try bw.print(" kind: {s}\n", .{@tagName(ctx.page.kind)});
608 try bw.print(" entries: {d} - {d}\n", .{
609 ctx.page.start,
610 ctx.page.start + ctx.page.count,
611 });
612 try bw.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count});
613 for (ctx.page.page_encodings[0..ctx.page.page_encodings_count], 0..) |enc, i| {
614 try bw.print(" {d}: {f}\n", .{ ctx.info.common_encodings_count + i, enc });
587 fn default(f: Format, w: *Writer) Writer.Error!void {
588 try w.writeAll("Page:\n");
589 try w.print(" kind: {s}\n", .{@tagName(f.page.kind)});
590 try w.print(" entries: {d} - {d}\n", .{
591 f.page.start,
592 f.page.start + f.page.count,
593 });
594 try w.print(" encodings (count = {d})\n", .{f.page.page_encodings_count});
595 for (f.page.page_encodings[0..f.page.page_encodings_count], 0..) |enc, i| {
596 try w.print(" {d}: {f}\n", .{ f.info.common_encodings_count + i, enc });
597 }
615598 }
616 }
599 };
617600
618 fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(format2) {
601 fn fmt(page: Page, info: UnwindInfo) std.fmt.Formatter(Format, Format.default) {
619602 return .{ .data = .{
620603 .page = page,
621604 .info = info,
src/link/MachO/ZigObject.zig+27-29
......@@ -957,7 +957,7 @@ fn updateNavCode(
957957 sym.out_n_sect = sect_index;
958958 atom.out_n_sect = sect_index;
959959
960 const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{nav.fqn.toSlice(ip)});
960 const sym_name = try std.fmt.allocPrintSentinel(gpa, "_{s}", .{nav.fqn.toSlice(ip)}, 0);
961961 defer gpa.free(sym_name);
962962 sym.name = try self.addString(gpa, sym_name);
963963 atom.setAlive(true);
......@@ -1676,52 +1676,50 @@ pub fn asFile(self: *ZigObject) File {
16761676 return .{ .zig_object = self };
16771677}
16781678
1679pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) {
1679pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
16801680 return .{ .data = .{
16811681 .self = self,
16821682 .macho_file = macho_file,
16831683 } };
16841684}
16851685
1686const FormatContext = struct {
1686const Format = struct {
16871687 self: *ZigObject,
16881688 macho_file: *MachO,
1689};
16901689
1691fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1692 _ = unused_fmt_string;
1693 try bw.writeAll(" symbols\n");
1694 const self = ctx.self;
1695 const macho_file = ctx.macho_file;
1696 for (self.symbols.items, 0..) |sym, i| {
1697 const ref = self.getSymbolRef(@intCast(i), macho_file);
1698 if (ref.getFile(macho_file) == null) {
1699 // TODO any better way of handling this?
1700 try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1701 } else {
1702 try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1690 fn symtab(f: Format, w: *Writer) Writer.Error!void {
1691 try w.writeAll(" symbols\n");
1692 const self = f.self;
1693 const macho_file = f.macho_file;
1694 for (self.symbols.items, 0..) |sym, i| {
1695 const ref = self.getSymbolRef(@intCast(i), macho_file);
1696 if (ref.getFile(macho_file) == null) {
1697 // TODO any better way of handling this?
1698 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1699 } else {
1700 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1701 }
17031702 }
17041703 }
1705}
17061704
1707pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) {
1705 fn atoms(f: Format, w: *Writer) Writer.Error!void {
1706 const self = f.self;
1707 const macho_file = f.macho_file;
1708 try w.writeAll(" atoms\n");
1709 for (self.getAtoms()) |atom_index| {
1710 const atom = self.getAtom(atom_index) orelse continue;
1711 try w.print(" {f}\n", .{atom.fmt(macho_file)});
1712 }
1713 }
1714};
1715
1716pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) {
17081717 return .{ .data = .{
17091718 .self = self,
17101719 .macho_file = macho_file,
17111720 } };
17121721}
17131722
1714fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
1715 _ = unused_fmt_string;
1716 const self = ctx.self;
1717 const macho_file = ctx.macho_file;
1718 try bw.writeAll(" atoms\n");
1719 for (self.getAtoms()) |atom_index| {
1720 const atom = self.getAtom(atom_index) orelse continue;
1721 try bw.print(" {f}\n", .{atom.fmt(macho_file)});
1722 }
1723}
1724
17251723const AvMetadata = struct {
17261724 symbol_index: Symbol.Index,
17271725 /// A list of all exports aliases of this Av.
src/link/MachO/dead_strip.zig+2-3
......@@ -196,9 +196,8 @@ const Level = struct {
196196 self.value += 1;
197197 }
198198
199 pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
200 _ = unused_fmt_string;
201 try bw.splatByteAll(' ', self.value);
199 pub fn format(self: *const @This(), w: *Writer) Writer.Error!void {
200 try w.splatByteAll(' ', self.value);
202201 }
203202};
204203
src/link/MachO/dyld_info/bind.zig+2-2
......@@ -193,7 +193,7 @@ pub const Bind = struct {
193193 }
194194 }
195195
196 log.debug("{x}, {d}, {x}, {?x}, {s}", .{ offset, count, skip, addend, @tagName(state) });
196 log.debug("{x}, {d}, {x}, {x}, {s}", .{ offset, count, skip, addend, @tagName(state) });
197197 log.debug(" => {x}", .{current.offset});
198198 switch (state) {
199199 .start => {
......@@ -423,7 +423,7 @@ pub const WeakBind = struct {
423423 }
424424 }
425425
426 log.debug("{x}, {d}, {x}, {?x}, {s}", .{ offset, count, skip, addend, @tagName(state) });
426 log.debug("{x}, {d}, {x}, {x}, {s}", .{ offset, count, skip, addend, @tagName(state) });
427427 log.debug(" => {x}", .{current.offset});
428428 switch (state) {
429429 .start => {
src/link/MachO/eh_frame.zig+25-53
......@@ -78,40 +78,26 @@ pub const Cie = struct {
7878 return true;
7979 }
8080
81 pub fn format(
82 cie: Cie,
83 comptime unused_fmt_string: []const u8,
84 options: std.fmt.FormatOptions,
85 writer: anytype,
86 ) !void {
87 _ = cie;
88 _ = unused_fmt_string;
89 _ = options;
90 _ = writer;
91 @compileError("do not format CIEs directly");
92 }
93
94 pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(format2) {
81 pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
9582 return .{ .data = .{
9683 .cie = cie,
9784 .macho_file = macho_file,
9885 } };
9986 }
10087
101 const FormatContext = struct {
88 const Format = struct {
10289 cie: Cie,
10390 macho_file: *MachO,
104 };
10591
106 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
107 _ = unused_fmt_string;
108 const cie = ctx.cie;
109 try bw.print("@{x} : size({x})", .{
110 cie.offset,
111 cie.getSize(),
112 });
113 if (!cie.alive) try bw.writeAll(" : [*]");
114 }
92 fn default(f: Format, w: *Writer) Writer.Error!void {
93 const cie = f.cie;
94 try w.print("@{x} : size({x})", .{
95 cie.offset,
96 cie.getSize(),
97 });
98 if (!cie.alive) try w.writeAll(" : [*]");
99 }
100 };
115101
116102 pub const Index = u32;
117103
......@@ -223,43 +209,29 @@ pub const Fde = struct {
223209 return fde.getObject(macho_file).getAtom(fde.lsda);
224210 }
225211
226 pub fn format(
227 fde: Fde,
228 comptime unused_fmt_string: []const u8,
229 options: std.fmt.FormatOptions,
230 writer: anytype,
231 ) !void {
232 _ = fde;
233 _ = unused_fmt_string;
234 _ = options;
235 _ = writer;
236 @compileError("do not format FDEs directly");
237 }
238
239 pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(format2) {
212 pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
240213 return .{ .data = .{
241214 .fde = fde,
242215 .macho_file = macho_file,
243216 } };
244217 }
245218
246 const FormatContext = struct {
219 const Format = struct {
247220 fde: Fde,
248221 macho_file: *MachO,
249 };
250222
251 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
252 _ = unused_fmt_string;
253 const fde = ctx.fde;
254 const macho_file = ctx.macho_file;
255 try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{
256 fde.offset,
257 fde.getSize(),
258 fde.cie,
259 fde.getAtom(macho_file).getName(macho_file),
260 });
261 if (!fde.alive) try bw.writeAll(" : [*]");
262 }
223 fn default(f: Format, writer: *Writer) Writer.Error!void {
224 const fde = f.fde;
225 const macho_file = f.macho_file;
226 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
227 fde.offset,
228 fde.getSize(),
229 fde.cie,
230 fde.getAtom(macho_file).getName(macho_file),
231 });
232 if (!fde.alive) try writer.writeAll(" : [*]");
233 }
234 };
263235
264236 pub const Index = u32;
265237};
src/link/MachO/file.zig+6-7
......@@ -10,17 +10,16 @@ pub const File = union(enum) {
1010 };
1111 }
1212
13 pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {
13 pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) {
1414 return .{ .data = file };
1515 }
1616
17 fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {
18 _ = unused_fmt_string;
17 fn formatPath(file: File, w: *Writer) Writer.Error!void {
1918 switch (file) {
20 .zig_object => |zo| try bw.writeAll(zo.basename),
21 .internal => try bw.writeAll("internal"),
22 .object => |x| try bw.print("{f}", .{x.fmtPath()}),
23 .dylib => |dl| try bw.print("{f}", .{@as(Path, dl.path)}),
19 .zig_object => |zo| try w.writeAll(zo.basename),
20 .internal => try w.writeAll("internal"),
21 .object => |x| try w.print("{f}", .{x.fmtPath()}),
22 .dylib => |dl| try w.print("{f}", .{@as(Path, dl.path)}),
2423 }
2524 }
2625
src/link/MachO/synthetic.zig+66-86
......@@ -37,32 +37,27 @@ pub const GotSection = struct {
3737 }
3838 }
3939
40 const FormatCtx = struct {
40 const Format = struct {
4141 got: GotSection,
4242 macho_file: *MachO,
43
44 pub fn print(f: Format, w: *Writer) Writer.Error!void {
45 for (f.got.symbols.items, 0..) |ref, i| {
46 const symbol = ref.getSymbol(f.macho_file).?;
47 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
48 i,
49 symbol.getGotAddress(f.macho_file),
50 ref,
51 symbol.getAddress(.{}, f.macho_file),
52 symbol.getName(f.macho_file),
53 });
54 }
55 }
4356 };
4457
45 pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(format2) {
58 pub fn fmt(got: GotSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
4659 return .{ .data = .{ .got = got, .macho_file = macho_file } };
4760 }
48
49 pub fn format2(
50 ctx: FormatCtx,
51 bw: *Writer,
52 comptime unused_fmt_string: []const u8,
53 ) !void {
54 _ = unused_fmt_string;
55 for (ctx.got.symbols.items, 0..) |ref, i| {
56 const symbol = ref.getSymbol(ctx.macho_file).?;
57 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
58 i,
59 symbol.getGotAddress(ctx.macho_file),
60 ref,
61 symbol.getAddress(.{}, ctx.macho_file),
62 symbol.getName(ctx.macho_file),
63 });
64 }
65 }
6661};
6762
6863pub const StubsSection = struct {
......@@ -126,32 +121,27 @@ pub const StubsSection = struct {
126121 }
127122 }
128123
129 const FormatCtx = struct {
130 stubs: StubsSection,
131 macho_file: *MachO,
132 };
133
134 pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(format2) {
124 pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
135125 return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } };
136126 }
137127
138 pub fn format2(
139 ctx: FormatCtx,
140 bw: *Writer,
141 comptime unused_fmt_string: []const u8,
142 ) !void {
143 _ = unused_fmt_string;
144 for (ctx.stubs.symbols.items, 0..) |ref, i| {
145 const symbol = ref.getSymbol(ctx.macho_file).?;
146 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
147 i,
148 symbol.getStubsAddress(ctx.macho_file),
149 ref,
150 symbol.getAddress(.{}, ctx.macho_file),
151 symbol.getName(ctx.macho_file),
152 });
128 const Format = struct {
129 stubs: StubsSection,
130 macho_file: *MachO,
131
132 pub fn print(f: Format, w: *Writer) Writer.Error!void {
133 for (f.stubs.symbols.items, 0..) |ref, i| {
134 const symbol = ref.getSymbol(f.macho_file).?;
135 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
136 i,
137 symbol.getStubsAddress(f.macho_file),
138 ref,
139 symbol.getAddress(.{}, f.macho_file),
140 symbol.getName(f.macho_file),
141 });
142 }
153143 }
154 }
144 };
155145};
156146
157147pub const StubsHelperSection = struct {
......@@ -353,32 +343,27 @@ pub const TlvPtrSection = struct {
353343 }
354344 }
355345
356 const FormatCtx = struct {
357 tlv: TlvPtrSection,
358 macho_file: *MachO,
359 };
360
361 pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(format2) {
346 pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
362347 return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } };
363348 }
364349
365 pub fn format2(
366 ctx: FormatCtx,
367 bw: *Writer,
368 comptime unused_fmt_string: []const u8,
369 ) !void {
370 _ = unused_fmt_string;
371 for (ctx.tlv.symbols.items, 0..) |ref, i| {
372 const symbol = ref.getSymbol(ctx.macho_file).?;
373 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
374 i,
375 symbol.getTlvPtrAddress(ctx.macho_file),
376 ref,
377 symbol.getAddress(.{}, ctx.macho_file),
378 symbol.getName(ctx.macho_file),
379 });
350 const Format = struct {
351 tlv: TlvPtrSection,
352 macho_file: *MachO,
353
354 pub fn print(f: Format, w: *Writer) Writer.Error!void {
355 for (f.tlv.symbols.items, 0..) |ref, i| {
356 const symbol = ref.getSymbol(f.macho_file).?;
357 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
358 i,
359 symbol.getTlvPtrAddress(f.macho_file),
360 ref,
361 symbol.getAddress(.{}, f.macho_file),
362 symbol.getName(f.macho_file),
363 });
364 }
380365 }
381 }
366 };
382367};
383368
384369pub const ObjcStubsSection = struct {
......@@ -476,32 +461,27 @@ pub const ObjcStubsSection = struct {
476461 }
477462 }
478463
479 const FormatCtx = struct {
480 objc: ObjcStubsSection,
481 macho_file: *MachO,
482 };
483
484 pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(format2) {
464 pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
485465 return .{ .data = .{ .objc = objc, .macho_file = macho_file } };
486466 }
487467
488 pub fn format2(
489 ctx: FormatCtx,
490 bw: *Writer,
491 comptime unused_fmt_string: []const u8,
492 ) !void {
493 _ = unused_fmt_string;
494 for (ctx.objc.symbols.items, 0..) |ref, i| {
495 const symbol = ref.getSymbol(ctx.macho_file).?;
496 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
497 i,
498 symbol.getObjcStubsAddress(ctx.macho_file),
499 ref,
500 symbol.getAddress(.{}, ctx.macho_file),
501 symbol.getName(ctx.macho_file),
502 });
468 const Format = struct {
469 objc: ObjcStubsSection,
470 macho_file: *MachO,
471
472 pub fn print(f: Format, w: *Writer) Writer.Error!void {
473 for (f.objc.symbols.items, 0..) |ref, i| {
474 const symbol = ref.getSymbol(f.macho_file).?;
475 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
476 i,
477 symbol.getObjcStubsAddress(f.macho_file),
478 ref,
479 symbol.getAddress(.{}, f.macho_file),
480 symbol.getName(f.macho_file),
481 });
482 }
503483 }
504 }
484 };
505485
506486 pub const Index = u32;
507487};
src/link/SpirV.zig+4-4
......@@ -206,7 +206,7 @@ pub fn flush(
206206 var error_info: std.io.Writer.Allocating = .init(self.object.gpa);
207207 defer error_info.deinit();
208208
209 try error_info.writer.writeAll("zig_errors:");
209 error_info.writer.writeAll("zig_errors:") catch return error.OutOfMemory;
210210 const ip = &self.base.comp.zcu.?.intern_pool;
211211 for (ip.global_error_set.getNamesFromMainThread()) |name| {
212212 // Errors can contain pretty much any character - to encode them in a string we must escape
......@@ -214,8 +214,8 @@ pub fn flush(
214214 // name if it contains no strange characters is nice for debugging. URI encoding fits the bill.
215215 // We're using : as separator, which is a reserved character.
216216
217 try error_info.writer.writeByte(':');
218 try std.Uri.Component.percentEncode(
217 error_info.writer.writeByte(':') catch return error.OutOfMemory;
218 std.Uri.Component.percentEncode(
219219 &error_info.writer,
220220 name.toSlice(ip),
221221 struct {
......@@ -226,7 +226,7 @@ pub fn flush(
226226 };
227227 }
228228 }.isValidChar,
229 );
229 ) catch return error.OutOfMemory;
230230 }
231231 try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{
232232 .extension = error_info.getWritten(),
src/link/Wasm.zig+12-14
......@@ -32,7 +32,7 @@ const Writer = std.io.Writer;
3232
3333const Mir = @import("../arch/wasm/Mir.zig");
3434const CodeGen = @import("../arch/wasm/CodeGen.zig");
35const abi = @import("../arch/wasm/abi.zig");
35const abi = @import("../codegen/wasm/abi.zig");
3636const Compilation = @import("../Compilation.zig");
3737const Dwarf = @import("Dwarf.zig");
3838const InternPool = @import("../InternPool.zig");
......@@ -2125,26 +2125,25 @@ pub const FunctionType = extern struct {
21252125 wasm: *const Wasm,
21262126 ft: FunctionType,
21272127
2128 pub fn format(self: Formatter, bw: *Writer, comptime format_string: []const u8) Writer.Error!void {
2129 comptime assert(format_string.len == 0);
2128 pub fn format(self: Formatter, writer: *std.io.Writer) std.io.Writer.Error!void {
21302129 const params = self.ft.params.slice(self.wasm);
21312130 const returns = self.ft.returns.slice(self.wasm);
21322131
2133 try bw.writeByte('(');
2132 try writer.writeByte('(');
21342133 for (params, 0..) |param, i| {
2135 try bw.print("{s}", .{@tagName(param)});
2134 try writer.print("{s}", .{@tagName(param)});
21362135 if (i + 1 != params.len) {
2137 try bw.writeAll(", ");
2136 try writer.writeAll(", ");
21382137 }
21392138 }
2140 try bw.writeAll(") -> ");
2139 try writer.writeAll(") -> ");
21412140 if (returns.len == 0) {
2142 try bw.writeAll("nil");
2141 try writer.writeAll("nil");
21432142 } else {
21442143 for (returns, 0..) |return_ty, i| {
2145 try bw.print("{s}", .{@tagName(return_ty)});
2144 try writer.print("{s}", .{@tagName(return_ty)});
21462145 if (i + 1 != returns.len) {
2147 try bw.writeAll(", ");
2146 try writer.writeAll(", ");
21482147 }
21492148 }
21502149 }
......@@ -2905,9 +2904,8 @@ pub const Feature = packed struct(u8) {
29052904 @"=",
29062905 };
29072906
2908 pub fn format(feature: Feature, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {
2909 _ = fmt;
2910 try bw.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
2907 pub fn format(feature: Feature, writer: *std.io.Writer) std.io.Writer.Error!void {
2908 try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
29112909 }
29122910
29132911 pub fn lessThan(_: void, a: Feature, b: Feature) bool {
......@@ -3299,7 +3297,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
32993297 .variable => |variable| .{ variable.init, variable.owner_nav },
33003298 else => .{ nav.status.fully_resolved.val, nav_index },
33013299 };
3302 //log.debug("updateNav {} {d}", .{ nav.fqn.fmt(ip), chased_nav_index });
3300 //log.debug("updateNav {f} {d}", .{ nav.fqn.fmt(ip), chased_nav_index });
33033301 assert(!wasm.imports.contains(chased_nav_index));
33043302
33053303 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
src/link/Wasm/Flush.zig+1-1
......@@ -534,7 +534,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
534534 wasm.memories.limits.max = @intCast(max_memory / page_size);
535535 wasm.memories.limits.flags.has_max = true;
536536 if (shared_memory) wasm.memories.limits.flags.is_shared = true;
537 log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});
537 log.debug("maximum memory pages: {d}", .{wasm.memories.limits.max});
538538 }
539539 f.memory_layout_finished = true;
540540
src/link/table_section.zig+3-4
......@@ -39,11 +39,10 @@ pub fn TableSection(comptime Entry: type) type {
3939 return self.entries.items.len;
4040 }
4141
42 pub fn format(self: Self, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {
43 comptime assert(unused_format_string.len == 0);
44 try bw.writeAll("TableSection:\n");
42 pub fn format(self: Self, writer: *std.io.Writer) std.io.Writer.Error!void {
43 try writer.writeAll("TableSection:\n");
4544 for (self.entries.items, 0..) |entry, i| {
46 try bw.print(" {d} => {}\n", .{ i, entry });
45 try writer.print(" {d} => {}\n", .{ i, entry });
4746 }
4847 }
4948
src/link/tapi/parse.zig+10-43
......@@ -57,14 +57,9 @@ pub const Node = struct {
5757 }
5858 }
5959
60 pub fn format(
61 self: *const Node,
62 comptime fmt: []const u8,
63 options: std.fmt.FormatOptions,
64 writer: anytype,
65 ) !void {
60 pub fn format(self: *const Node, writer: *std.io.Writer) std.io.Writer.Error!void {
6661 switch (self.tag) {
67 inline else => |tag| return @as(*tag.Type(), @fieldParentPtr("base", self)).format(fmt, options, writer),
62 inline else => |tag| return @as(*tag.Type(), @fieldParentPtr("base", self)).format(writer),
6863 }
6964 }
7065
......@@ -86,24 +81,17 @@ pub const Node = struct {
8681 }
8782 }
8883
89 pub fn format(
90 self: *const Doc,
91 comptime fmt: []const u8,
92 options: std.fmt.FormatOptions,
93 writer: anytype,
94 ) !void {
95 _ = options;
96 _ = fmt;
84 pub fn format(self: *const Doc, writer: *std.io.Writer) std.io.Writer.Error!void {
9785 if (self.directive) |id| {
98 try std.fmt.format(writer, "{{ ", .{});
86 try writer.print("{{ ", .{});
9987 const directive = self.base.tree.getRaw(id, id);
100 try std.fmt.format(writer, ".directive = {s}, ", .{directive});
88 try writer.print(".directive = {s}, ", .{directive});
10189 }
10290 if (self.value) |node| {
103 try std.fmt.format(writer, "{}", .{node});
91 try writer.print("{}", .{node});
10492 }
10593 if (self.directive != null) {
106 try std.fmt.format(writer, " }}", .{});
94 try writer.print(" }}", .{});
10795 }
10896 }
10997 };
......@@ -133,14 +121,7 @@ pub const Node = struct {
133121 self.values.deinit(allocator);
134122 }
135123
136 pub fn format(
137 self: *const Map,
138 comptime fmt: []const u8,
139 options: std.fmt.FormatOptions,
140 writer: anytype,
141 ) !void {
142 _ = options;
143 _ = fmt;
124 pub fn format(self: *const Map, writer: *std.io.Writer) std.io.Writer.Error!void {
144125 try std.fmt.format(writer, "{{ ", .{});
145126 for (self.values.items) |entry| {
146127 const key = self.base.tree.getRaw(entry.key, entry.key);
......@@ -172,14 +153,7 @@ pub const Node = struct {
172153 self.values.deinit(allocator);
173154 }
174155
175 pub fn format(
176 self: *const List,
177 comptime fmt: []const u8,
178 options: std.fmt.FormatOptions,
179 writer: anytype,
180 ) !void {
181 _ = options;
182 _ = fmt;
156 pub fn format(self: *const List, writer: *std.io.Writer) std.io.Writer.Error!void {
183157 try std.fmt.format(writer, "[ ", .{});
184158 for (self.values.items) |node| {
185159 try std.fmt.format(writer, "{}, ", .{node});
......@@ -203,14 +177,7 @@ pub const Node = struct {
203177 self.string_value.deinit(allocator);
204178 }
205179
206 pub fn format(
207 self: *const Value,
208 comptime fmt: []const u8,
209 options: std.fmt.FormatOptions,
210 writer: anytype,
211 ) !void {
212 _ = options;
213 _ = fmt;
180 pub fn format(self: *const Value, writer: *std.io.Writer) std.io.Writer.Error!void {
214181 const raw = self.base.tree.getRaw(self.base.start, self.base.end);
215182 return std.fmt.format(writer, "{s}", .{raw});
216183 }
src/main.zig+62-53
......@@ -309,6 +309,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
309309 return jitCmd(gpa, arena, cmd_args, .{
310310 .cmd_name = "resinator",
311311 .root_src_path = "resinator/main.zig",
312 .windows_libs = &.{"advapi32"},
312313 .depend_on_aro = true,
313314 .prepend_zig_lib_dir_path = true,
314315 .server = use_server,
......@@ -972,8 +973,6 @@ fn buildOutputType(
972973 .windows_libs = .empty,
973974 .link_inputs = .empty,
974975
975 .wasi_emulated_libs = .{},
976
977976 .c_source_files = .{},
978977 .rc_source_files = .{},
979978
......@@ -1418,7 +1417,7 @@ fn buildOutputType(
14181417 } else if (mem.eql(u8, arg, "-funwind-tables")) {
14191418 mod_opts.unwind_tables = .sync;
14201419 } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) {
1421 mod_opts.unwind_tables = .@"async";
1420 mod_opts.unwind_tables = .async;
14221421 } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
14231422 mod_opts.unwind_tables = .none;
14241423 } else if (mem.eql(u8, arg, "-fstack-check")) {
......@@ -2039,15 +2038,15 @@ fn buildOutputType(
20392038 .none => {
20402039 mod_opts.unwind_tables = .sync;
20412040 },
2042 .sync, .@"async" => {},
2041 .sync, .async => {},
20432042 } else {
20442043 mod_opts.unwind_tables = .sync;
20452044 },
20462045 .no_unwind_tables => mod_opts.unwind_tables = .none,
2047 .asynchronous_unwind_tables => mod_opts.unwind_tables = .@"async",
2046 .asynchronous_unwind_tables => mod_opts.unwind_tables = .async,
20482047 .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
20492048 .none, .sync => {},
2050 .@"async" => {
2049 .async => {
20512050 mod_opts.unwind_tables = .sync;
20522051 },
20532052 } else {
......@@ -2955,7 +2954,7 @@ fn buildOutputType(
29552954 create_module.opts.any_fuzz = true;
29562955 if (mod_opts.unwind_tables) |uwt| switch (uwt) {
29572956 .none => {},
2958 .sync, .@"async" => create_module.opts.any_unwind_tables = true,
2957 .sync, .async => create_module.opts.any_unwind_tables = true,
29592958 };
29602959 if (mod_opts.strip == false)
29612960 create_module.opts.any_non_stripped = true;
......@@ -3331,16 +3330,16 @@ fn buildOutputType(
33313330 // We are providing our own cache key, because this file has nothing
33323331 // to do with the cache manifest.
33333332 var file_writer = f.writer(&.{});
3334 var hasher_writer = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"));
33353333 var buffer: [1000]u8 = undefined;
3336 var bw = hasher_writer.writer(&buffer);
3337 bw.writeFileAll(.stdin(), .{}) catch |err| switch (err) {
3338 error.WriteFailed => fatal("failed to write {s}: {s}", .{ dump_path, file_writer.err.? }),
3339 else => fatal("failed to pipe stdin to {s}: {s}", .{ dump_path, err }),
3334 var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer);
3335 var stdin_reader = fs.File.stdin().readerStreaming(&.{});
3336 _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) {
3337 error.WriteFailed => fatal("failed to write {s}: {t}", .{ dump_path, file_writer.err.? }),
3338 else => fatal("failed to pipe stdin to {s}: {t}", .{ dump_path, err }),
33403339 };
3341 try bw.flush();
3340 try hasher.writer.flush();
33423341
3343 const bin_digest: Cache.BinDigest = hasher_writer.final();
3342 const bin_digest: Cache.BinDigest = hasher.hasher.finalResult();
33443343
33453344 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{
33463345 &bin_digest, ext.canonicalName(target),
......@@ -3412,7 +3411,6 @@ fn buildOutputType(
34123411 .framework_dirs = create_module.framework_dirs.items,
34133412 .frameworks = resolved_frameworks.items,
34143413 .windows_lib_names = create_module.windows_libs.keys(),
3415 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
34163414 .want_compiler_rt = want_compiler_rt,
34173415 .want_ubsan_rt = want_ubsan_rt,
34183416 .hash_style = hash_style,
......@@ -3562,8 +3560,8 @@ fn buildOutputType(
35623560 .stdio => {
35633561 try serve(
35643562 comp,
3565 fs.File.stdin(),
3566 fs.File.stdout(),
3563 .stdin(),
3564 .stdout(),
35673565 test_exec_args.items,
35683566 self_exe_path,
35693567 arg_mode,
......@@ -3638,7 +3636,6 @@ fn buildOutputType(
36383636 } else if (target.os.tag == .windows) {
36393637 try test_exec_args.appendSlice(arena, &.{
36403638 "--subsystem", "console",
3641 "-lkernel32", "-lntdll",
36423639 });
36433640 }
36443641
......@@ -3694,8 +3691,6 @@ const CreateModule = struct {
36943691 /// output. Allocated with gpa.
36953692 link_inputs: std.ArrayListUnmanaged(link.Input),
36963693
3697 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),
3698
36993694 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
37003695 rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile),
37013696
......@@ -3826,14 +3821,6 @@ fn createModule(
38263821 .name_query => |nq| {
38273822 const lib_name = nq.name;
38283823
3829 if (target.os.tag == .wasi) {
3830 if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| {
3831 try create_module.wasi_emulated_libs.append(arena, crt_file);
3832 create_module.opts.link_libc = true;
3833 continue;
3834 }
3835 }
3836
38373824 if (std.zig.target.isLibCLibName(target, lib_name)) {
38383825 create_module.opts.link_libc = true;
38393826 continue;
......@@ -3852,7 +3839,8 @@ fn createModule(
38523839 .only_compiler_rt => continue,
38533840 }
38543841
3855 if (target.isMinGW()) {
3842 // We currently prefer import libraries provided by MinGW-w64 even for MSVC.
3843 if (target.os.tag == .windows) {
38563844 const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| {
38573845 fatal("failed to check zig installation for DLL import libs: {s}", .{
38583846 @errorName(err),
......@@ -5228,6 +5216,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
52285216
52295217 try root_mod.deps.put(arena, "@build", build_mod);
52305218
5219 var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty;
5220
5221 if (resolved_target.result.os.tag == .windows) {
5222 try windows_libs.put(arena, "advapi32", {});
5223 }
5224
52315225 const comp = Compilation.create(gpa, arena, .{
52325226 .dirs = dirs,
52335227 .root_name = "build",
......@@ -5249,6 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
52495243 .cache_mode = .whole,
52505244 .reference_trace = reference_trace,
52515245 .debug_compile_errors = debug_compile_errors,
5246 .windows_lib_names = windows_libs.keys(),
52525247 }) catch |err| {
52535248 fatal("unable to create compilation: {s}", .{@errorName(err)});
52545249 };
......@@ -5299,7 +5294,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
52995294 const s = fs.path.sep_str;
53005295 const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
53015296 const stdout = dirs.local_cache.handle.readFileAlloc(arena, tmp_sub_path, 50 * 1024 * 1024) catch |err| {
5302 fatal("unable to read results of configure phase from '{}{s}': {s}", .{
5297 fatal("unable to read results of configure phase from '{f}{s}': {s}", .{
53035298 dirs.local_cache, tmp_sub_path, @errorName(err),
53045299 });
53055300 };
......@@ -5352,6 +5347,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
53525347const JitCmdOptions = struct {
53535348 cmd_name: []const u8,
53545349 root_src_path: []const u8,
5350 windows_libs: []const []const u8 = &.{},
53555351 prepend_zig_lib_dir_path: bool = false,
53565352 prepend_global_cache_path: bool = false,
53575353 prepend_zig_exe_path: bool = false,
......@@ -5468,6 +5464,13 @@ fn jitCmd(
54685464 try root_mod.deps.put(arena, "aro", aro_mod);
54695465 }
54705466
5467 var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty;
5468
5469 if (resolved_target.result.os.tag == .windows) {
5470 try windows_libs.ensureUnusedCapacity(arena, options.windows_libs.len);
5471 for (options.windows_libs) |lib| windows_libs.putAssumeCapacity(lib, {});
5472 }
5473
54715474 const comp = Compilation.create(gpa, arena, .{
54725475 .dirs = dirs,
54735476 .root_name = options.cmd_name,
......@@ -5478,6 +5481,7 @@ fn jitCmd(
54785481 .self_exe_path = self_exe_path,
54795482 .thread_pool = &thread_pool,
54805483 .cache_mode = .whole,
5484 .windows_lib_names = windows_libs.keys(),
54815485 }) catch |err| {
54825486 fatal("unable to create compilation: {s}", .{@errorName(err)});
54835487 };
......@@ -6049,7 +6053,7 @@ fn cmdAstCheck(
60496053 break :file fs.cwd().openFile(p, .{}) catch |err| {
60506054 fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
60516055 };
6052 } else io.getStdIn();
6056 } else fs.File.stdin();
60536057 defer if (zig_source_path != null) f.close();
60546058 break :s std.zig.readSourceFileToEndAlloc(arena, f, null) catch |err| {
60556059 fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
......@@ -6068,7 +6072,8 @@ fn cmdAstCheck(
60686072
60696073 const tree = try Ast.parse(arena, source, mode);
60706074
6071 var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer);
6075 var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer);
6076 const stdout_bw = &stdout_writer.interface;
60726077 switch (mode) {
60736078 .zig => {
60746079 const zir = try AstGen.generate(arena, tree);
......@@ -6133,7 +6138,7 @@ fn cmdAstCheck(
61336138 // zig fmt: on
61346139 }
61356140
6136 try @import("print_zir.zig").renderAsText(arena, tree, zir, &stdout_bw);
6141 try @import("print_zir.zig").renderAsText(arena, tree, zir, stdout_bw);
61376142 try stdout_bw.flush();
61386143
61396144 if (zir.hasCompileErrors()) {
......@@ -6161,7 +6166,7 @@ fn cmdAstCheck(
61616166 fatal("-t option only available in builds of zig with debug extensions", .{});
61626167 }
61636168
6164 try @import("print_zoir.zig").renderToWriter(zoir, arena, &stdout_bw);
6169 try @import("print_zoir.zig").renderToWriter(zoir, arena, stdout_bw);
61656170 try stdout_bw.flush();
61666171 return cleanExit();
61676172 },
......@@ -6282,7 +6287,8 @@ fn detectNativeCpuWithLLVM(
62826287}
62836288
62846289fn printCpu(cpu: std.Target.Cpu) !void {
6285 var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer);
6290 var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer);
6291 const stdout_bw = &stdout_writer.interface;
62866292
62876293 if (cpu.model.llvm_name) |llvm_name| {
62886294 try stdout_bw.print("{s}\n", .{llvm_name});
......@@ -6326,11 +6332,12 @@ fn cmdDumpLlvmInts(
63266332 if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad");
63276333 break :t target;
63286334 };
6329 const tm = llvm.TargetMachine.create(target, triple, null, null, .None, .Default, .Default, false, false, .Default, null);
6335 const tm = llvm.TargetMachine.create(target, triple, null, null, .None, .Default, .Default, false, false, .Default, null, false);
63306336 const dl = tm.createTargetDataLayout();
63316337 const context = llvm.Context.create();
63326338
6333 var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer);
6339 var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer);
6340 const stdout_bw = &stdout_writer.interface;
63346341 for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| {
63356342 const int_type = context.intType(bits);
63366343 const alignment = dl.abiAlignmentOfType(int_type);
......@@ -6358,9 +6365,8 @@ fn cmdDumpZir(
63586365 defer f.close();
63596366
63606367 const zir = try Zcu.loadZirCache(arena, f);
6361
6362 var stdout_fw = fs.File.stdout().writer();
6363 var stdout_bw = stdout_fw.interface().buffered(&stdio_buffer);
6368 var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer);
6369 const stdout_bw = &stdout_writer.interface;
63646370 {
63656371 const instruction_bytes = zir.instructions.len *
63666372 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
......@@ -6385,7 +6391,7 @@ fn cmdDumpZir(
63856391 // zig fmt: on
63866392 }
63876393
6388 try @import("print_zir.zig").renderAsText(arena, null, zir, &stdout_bw);
6394 try @import("print_zir.zig").renderAsText(arena, null, zir, stdout_bw);
63896395 try stdout_bw.flush();
63906396}
63916397
......@@ -6444,7 +6450,8 @@ fn cmdChangelist(
64446450 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;
64456451 try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map);
64466452
6447 var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer);
6453 var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer);
6454 const stdout_bw = &stdout_writer.interface;
64486455 {
64496456 try stdout_bw.print("Instruction mappings:\n", .{});
64506457 var it = inst_map.iterator();
......@@ -6903,9 +6910,9 @@ fn cmdFetch(
69036910
69046911 const name = switch (save) {
69056912 .no => {
6906 var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer);
6907 try stdout_bw.print("{s}\n", .{package_hash_slice});
6908 try stdout_bw.flush();
6913 var stdout = fs.File.stdout().writerStreaming(&stdio_buffer);
6914 try stdout.interface.print("{s}\n", .{package_hash_slice});
6915 try stdout.interface.flush();
69096916 return cleanExit();
69106917 },
69116918 .yes, .exact => |name| name: {
......@@ -6954,7 +6961,9 @@ fn cmdFetch(
69546961 std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex });
69556962
69566963 // include the original refspec in a query parameter, could be used to check for updates
6957 uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f%}", .{fragment}) };
6964 uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{
6965 std.fmt.alt(fragment, .formatEscaped),
6966 }) };
69586967 } else {
69596968 std.log.info("resolved to commit {s}", .{latest_commit_hex});
69606969 }
......@@ -6974,12 +6983,12 @@ fn cmdFetch(
69746983 \\ .hash = "{f}",
69756984 \\ }}
69766985 , .{
6977 std.zig.fmtEscapes(saved_path_or_url),
6978 std.zig.fmtEscapes(package_hash_slice),
6986 std.zig.fmtString(saved_path_or_url),
6987 std.zig.fmtString(package_hash_slice),
69796988 });
69806989
6981 const new_node_text = try std.fmt.allocPrint(arena, ".{fp_} = {s},\n", .{
6982 std.zig.fmtId(name), new_node_init,
6990 const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{
6991 std.zig.fmtIdPU(name), new_node_init,
69836992 });
69846993
69856994 const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{
......@@ -7006,12 +7015,12 @@ fn cmdFetch(
70067015 const location_replace = try std.fmt.allocPrint(
70077016 arena,
70087017 "\"{f}\"",
7009 .{std.zig.fmtEscapes(saved_path_or_url)},
7018 .{std.zig.fmtString(saved_path_or_url)},
70107019 );
70117020 const hash_replace = try std.fmt.allocPrint(
70127021 arena,
70137022 "\"{f}\"",
7014 .{std.zig.fmtEscapes(package_hash_slice)},
7023 .{std.zig.fmtString(package_hash_slice)},
70157024 );
70167025
70177026 warn("overwriting existing dependency named '{s}'", .{name});
......@@ -7411,7 +7420,7 @@ fn handleModArg(
74117420 create_module.opts.any_fuzz = true;
74127421 if (mod_opts.unwind_tables) |uwt| switch (uwt) {
74137422 .none => {},
7414 .sync, .@"async" => create_module.opts.any_unwind_tables = true,
7423 .sync, .async => create_module.opts.any_unwind_tables = true,
74157424 };
74167425 if (mod_opts.strip == false)
74177426 create_module.opts.any_non_stripped = true;
src/print_value.zig+114-117
......@@ -9,7 +9,6 @@ const Sema = @import("Sema.zig");
99const InternPool = @import("InternPool.zig");
1010const Allocator = std.mem.Allocator;
1111const Target = std.Target;
12const Writer = std.io.Writer;
1312
1413const max_aggregate_items = 100;
1514const max_string_len = 256;
......@@ -21,10 +20,9 @@ pub const FormatContext = struct {
2120 depth: u8,
2221};
2322
24pub fn formatSema(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
23pub fn formatSema(ctx: FormatContext, writer: *std.io.Writer) std.io.Writer.Error!void {
2524 const sema = ctx.opt_sema.?;
26 comptime std.debug.assert(fmt.len == 0);
27 return print(ctx.val, bw, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
25 return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
2826 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
2927 error.ComptimeBreak, error.ComptimeReturn => unreachable,
3028 error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully
......@@ -32,10 +30,9 @@ pub fn formatSema(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std
3230 };
3331}
3432
35pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.Writer.Error!void {
33pub fn format(ctx: FormatContext, writer: *std.io.Writer) std.io.Writer.Error!void {
3634 std.debug.assert(ctx.opt_sema == null);
37 comptime std.debug.assert(fmt.len == 0);
38 return print(ctx.val, bw, ctx.depth, ctx.pt, null) catch |err| switch (err) {
35 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
3936 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
4037 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,
4138 else => |e| return e,
......@@ -44,7 +41,7 @@ pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.
4441
4542pub fn print(
4643 val: Value,
47 bw: *Writer,
44 writer: *std.io.Writer,
4845 level: u8,
4946 pt: Zcu.PerThread,
5047 opt_sema: ?*Sema,
......@@ -68,62 +65,62 @@ pub fn print(
6865 .func_type,
6966 .error_set_type,
7067 .inferred_error_set_type,
71 => try Type.print(val.toType(), bw, pt),
72 .undef => try bw.writeAll("undefined"),
68 => try Type.print(val.toType(), writer, pt),
69 .undef => try writer.writeAll("undefined"),
7370 .simple_value => |simple_value| switch (simple_value) {
74 .void => try bw.writeAll("{}"),
75 .empty_tuple => try bw.writeAll(".{}"),
76 else => try bw.writeAll(@tagName(simple_value)),
71 .void => try writer.writeAll("{}"),
72 .empty_tuple => try writer.writeAll(".{}"),
73 else => try writer.writeAll(@tagName(simple_value)),
7774 },
78 .variable => try bw.writeAll("(variable)"),
79 .@"extern" => |e| try bw.print("(extern '{f}')", .{e.name.fmt(ip)}),
80 .func => |func| try bw.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}),
75 .variable => try writer.writeAll("(variable)"),
76 .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}),
77 .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}),
8178 .int => |int| switch (int.storage) {
82 inline .u64, .i64 => |x| try bw.print("{d}", .{x}),
83 .big_int => |x| try bw.print("{f}", .{x}),
79 inline .u64, .i64 => |x| try writer.print("{d}", .{x}),
80 .big_int => |x| try writer.print("{d}", .{x}),
8481 .lazy_align => |ty| if (opt_sema != null) {
8582 const a = try Type.fromInterned(ty).abiAlignmentSema(pt);
86 try bw.print("{}", .{a.toByteUnits() orelse 0});
87 } else try bw.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
83 try writer.print("{d}", .{a.toByteUnits() orelse 0});
84 } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
8885 .lazy_size => |ty| if (opt_sema != null) {
8986 const s = try Type.fromInterned(ty).abiSizeSema(pt);
90 try bw.print("{}", .{s});
91 } else try bw.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
87 try writer.print("{d}", .{s});
88 } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
9289 },
93 .err => |err| try bw.print("error.{f}", .{
90 .err => |err| try writer.print("error.{f}", .{
9491 err.name.fmt(ip),
9592 }),
9693 .error_union => |error_union| switch (error_union.val) {
97 .err_name => |err_name| try bw.print("error.{f}", .{
94 .err_name => |err_name| try writer.print("error.{f}", .{
9895 err_name.fmt(ip),
9996 }),
100 .payload => |payload| try print(Value.fromInterned(payload), bw, level, pt, opt_sema),
97 .payload => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema),
10198 },
102 .enum_literal => |enum_literal| try bw.print(".{f}", .{
99 .enum_literal => |enum_literal| try writer.print(".{f}", .{
103100 enum_literal.fmt(ip),
104101 }),
105102 .enum_tag => |enum_tag| {
106103 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());
107104 if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| {
108 return bw.print(".{fi}", .{enum_type.names.get(ip)[tag_index].fmt(ip)});
105 return writer.print(".{f}", .{enum_type.names.get(ip)[tag_index].fmt(ip)});
109106 }
110107 if (level == 0) {
111 return bw.writeAll("@enumFromInt(...)");
108 return writer.writeAll("@enumFromInt(...)");
112109 }
113 try bw.writeAll("@enumFromInt(");
114 try print(Value.fromInterned(enum_tag.int), bw, level - 1, pt, opt_sema);
115 try bw.writeAll(")");
110 try writer.writeAll("@enumFromInt(");
111 try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
112 try writer.writeAll(")");
116113 },
117 .empty_enum_value => try bw.writeAll("(empty enum value)"),
114 .empty_enum_value => try writer.writeAll("(empty enum value)"),
118115 .float => |float| switch (float.storage) {
119 inline else => |x| try bw.print("{d}", .{@as(f64, @floatCast(x))}),
116 inline else => |x| try writer.print("{d}", .{@as(f64, @floatCast(x))}),
120117 },
121118 .slice => |slice| {
122119 if (ip.isUndef(slice.ptr)) {
123120 if (slice.len == .zero_usize) {
124 return bw.writeAll("&.{}");
121 return writer.writeAll("&.{}");
125122 }
126 try print(.fromInterned(slice.ptr), bw, level - 1, pt, opt_sema);
123 try print(.fromInterned(slice.ptr), writer, level - 1, pt, opt_sema);
127124 } else {
128125 const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) {
129126 .field, .arr_elem, .eu_payload, .opt_payload => unreachable,
......@@ -134,15 +131,15 @@ pub fn print(
134131 // TODO: eventually we want to load the slice as an array with `sema`, but that's
135132 // currently not possible without e.g. triggering compile errors.
136133 }
137 try printPtr(Value.fromInterned(slice.ptr), null, bw, level, pt, opt_sema);
134 try printPtr(Value.fromInterned(slice.ptr), null, writer, level, pt, opt_sema);
138135 }
139 try bw.writeAll("[0..");
136 try writer.writeAll("[0..");
140137 if (level == 0) {
141 try bw.writeAll("(...)");
138 try writer.writeAll("(...)");
142139 } else {
143 try print(Value.fromInterned(slice.len), bw, level - 1, pt, opt_sema);
140 try print(Value.fromInterned(slice.len), writer, level - 1, pt, opt_sema);
144141 }
145 try bw.writeAll("]");
142 try writer.writeAll("]");
146143 },
147144 .ptr => {
148145 const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) {
......@@ -154,29 +151,29 @@ pub fn print(
154151 // TODO: eventually we want to load the pointer with `sema`, but that's
155152 // currently not possible without e.g. triggering compile errors.
156153 }
157 try printPtr(val, .rvalue, bw, level, pt, opt_sema);
154 try printPtr(val, .rvalue, writer, level, pt, opt_sema);
158155 },
159156 .opt => |opt| switch (opt.val) {
160 .none => try bw.writeAll("null"),
161 else => |payload| try print(Value.fromInterned(payload), bw, level, pt, opt_sema),
157 .none => try writer.writeAll("null"),
158 else => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema),
162159 },
163 .aggregate => |aggregate| try printAggregate(val, aggregate, false, bw, level, pt, opt_sema),
160 .aggregate => |aggregate| try printAggregate(val, aggregate, false, writer, level, pt, opt_sema),
164161 .un => |un| {
165162 if (level == 0) {
166 try bw.writeAll(".{ ... }");
163 try writer.writeAll(".{ ... }");
167164 return;
168165 }
169166 if (un.tag == .none) {
170167 const backing_ty = try val.typeOf(zcu).unionBackingType(pt);
171 try bw.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
172 try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema);
173 try bw.writeAll("))");
168 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
169 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
170 try writer.writeAll("))");
174171 } else {
175 try bw.writeAll(".{ ");
176 try print(Value.fromInterned(un.tag), bw, level - 1, pt, opt_sema);
177 try bw.writeAll(" = ");
178 try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema);
179 try bw.writeAll(" }");
172 try writer.writeAll(".{ ");
173 try print(Value.fromInterned(un.tag), writer, level - 1, pt, opt_sema);
174 try writer.writeAll(" = ");
175 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
176 try writer.writeAll(" }");
180177 }
181178 },
182179 .memoized_call => unreachable,
......@@ -187,33 +184,33 @@ fn printAggregate(
187184 val: Value,
188185 aggregate: InternPool.Key.Aggregate,
189186 is_ref: bool,
190 bw: *Writer,
187 writer: *std.io.Writer,
191188 level: u8,
192189 pt: Zcu.PerThread,
193190 opt_sema: ?*Sema,
194191) (std.io.Writer.Error || Zcu.CompileError)!void {
195192 if (level == 0) {
196 if (is_ref) try bw.writeByte('&');
197 return bw.writeAll(".{ ... }");
193 if (is_ref) try writer.writeByte('&');
194 return writer.writeAll(".{ ... }");
198195 }
199196 const zcu = pt.zcu;
200197 const ip = &zcu.intern_pool;
201198 const ty = Type.fromInterned(aggregate.ty);
202199 switch (ty.zigTypeTag(zcu)) {
203200 .@"struct" => if (!ty.isTuple(zcu)) {
204 if (is_ref) try bw.writeByte('&');
201 if (is_ref) try writer.writeByte('&');
205202 if (ty.structFieldCount(zcu) == 0) {
206 return bw.writeAll(".{}");
203 return writer.writeAll(".{}");
207204 }
208 try bw.writeAll(".{ ");
205 try writer.writeAll(".{ ");
209206 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items);
210207 for (0..max_len) |i| {
211 if (i != 0) try bw.writeAll(", ");
208 if (i != 0) try writer.writeAll(", ");
212209 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?;
213 try bw.print(".{fi} = ", .{field_name.fmt(ip)});
214 try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema);
210 try writer.print(".{f} = ", .{field_name.fmt(ip)});
211 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
215212 }
216 try bw.writeAll(" }");
213 try writer.writeAll(" }");
217214 return;
218215 },
219216 .array => {
......@@ -222,16 +219,16 @@ fn printAggregate(
222219 const len = ty.arrayLenIncludingSentinel(zcu);
223220 if (len == 0) break :string;
224221 const slice = bytes.toSlice(if (bytes.at(len - 1, ip) == 0) len - 1 else len, ip);
225 try bw.print("\"{f}\"", .{std.zig.fmtEscapes(slice)});
226 if (!is_ref) try bw.writeAll(".*");
222 try writer.print("\"{f}\"", .{std.zig.fmtString(slice)});
223 if (!is_ref) try writer.writeAll(".*");
227224 return;
228225 },
229226 .elems, .repeated_elem => {},
230227 }
231228 switch (ty.arrayLen(zcu)) {
232229 0 => {
233 if (is_ref) try bw.writeByte('&');
234 return bw.writeAll(".{}");
230 if (is_ref) try writer.writeByte('&');
231 return writer.writeAll(".{}");
235232 },
236233 1 => one_byte_str: {
237234 // The repr isn't `bytes`, but we might still be able to print this as a string
......@@ -239,47 +236,47 @@ fn printAggregate(
239236 const elem_val = Value.fromInterned(aggregate.storage.values()[0]);
240237 if (elem_val.isUndef(zcu)) break :one_byte_str;
241238 const byte = elem_val.toUnsignedInt(zcu);
242 try bw.print("\"{f}\"", .{std.zig.fmtEscapes(&.{@intCast(byte)})});
243 if (!is_ref) try bw.writeAll(".*");
239 try writer.print("\"{f}\"", .{std.zig.fmtString(&.{@intCast(byte)})});
240 if (!is_ref) try writer.writeAll(".*");
244241 return;
245242 },
246243 else => {},
247244 }
248245 },
249246 .vector => if (ty.arrayLen(zcu) == 0) {
250 if (is_ref) try bw.writeByte('&');
251 return bw.writeAll(".{}");
247 if (is_ref) try writer.writeByte('&');
248 return writer.writeAll(".{}");
252249 },
253250 else => unreachable,
254251 }
255252
256253 const len = ty.arrayLen(zcu);
257254
258 if (is_ref) try bw.writeByte('&');
259 try bw.writeAll(".{ ");
255 if (is_ref) try writer.writeByte('&');
256 try writer.writeAll(".{ ");
260257
261258 const max_len = @min(len, max_aggregate_items);
262259 for (0..max_len) |i| {
263 if (i != 0) try bw.writeAll(", ");
264 try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema);
260 if (i != 0) try writer.writeAll(", ");
261 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
265262 }
266263 if (len > max_aggregate_items) {
267 try bw.writeAll(", ...");
264 try writer.writeAll(", ...");
268265 }
269 return bw.writeAll(" }");
266 return writer.writeAll(" }");
270267}
271268
272269fn printPtr(
273270 ptr_val: Value,
274271 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.
275272 want_kind: ?PrintPtrKind,
276 bw: *Writer,
273 writer: *std.io.Writer,
277274 level: u8,
278275 pt: Zcu.PerThread,
279276 opt_sema: ?*Sema,
280277) (std.io.Writer.Error || Zcu.CompileError)!void {
281278 const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
282 .undef => return bw.writeAll("undefined"),
279 .undef => return writer.writeAll("undefined"),
283280 .ptr => |ptr| ptr,
284281 else => unreachable,
285282 };
......@@ -291,7 +288,7 @@ fn printPtr(
291288 Value.fromInterned(ptr.base_addr.uav.val),
292289 agg,
293290 true,
294 bw,
291 writer,
295292 level,
296293 pt,
297294 opt_sema,
......@@ -307,7 +304,7 @@ fn printPtr(
307304 else
308305 try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null);
309306
310 _ = try printPtrDerivation(derivation, bw, pt, want_kind, .{ .print_val = .{
307 _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{
311308 .level = level,
312309 .opt_sema = opt_sema,
313310 } }, 20);
......@@ -319,7 +316,7 @@ const PrintPtrKind = enum { lvalue, rvalue };
319316/// Returns the root derivation, which may be ignored.
320317pub fn printPtrDerivation(
321318 derivation: Value.PointerDeriveStep,
322 bw: *Writer,
319 writer: *std.io.Writer,
323320 pt: Zcu.PerThread,
324321 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.
325322 /// If this is `.rvalue`, the result may look like `&foo`, so it's not necessarily valid to treat it as
......@@ -337,7 +334,7 @@ pub fn printPtrDerivation(
337334 /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary,
338335 /// so at this depth we just write "..." to prevent stack overflow.
339336 ptr_depth: u8,
340) (std.io.Writer.Error || Zcu.CompileError)!Value.PointerDeriveStep {
337) !Value.PointerDeriveStep {
341338 const zcu = pt.zcu;
342339 const ip = &zcu.intern_pool;
343340
......@@ -351,7 +348,7 @@ pub fn printPtrDerivation(
351348 => |step| continue :root step.parent.*,
352349 else => |step| break :root step,
353350 };
354 try bw.writeAll("...");
351 try writer.writeAll("...");
355352 return root_step;
356353 }
357354
......@@ -374,39 +371,39 @@ pub fn printPtrDerivation(
374371 const need_kind = want_kind orelse result_kind;
375372
376373 if (need_kind == .rvalue and result_kind == .lvalue) {
377 try bw.writeByte('&');
374 try writer.writeByte('&');
378375 }
379376
380377 // null if `derivation` is the root.
381378 const root_or_null: ?Value.PointerDeriveStep = switch (derivation) {
382379 .eu_payload_ptr => |info| root: {
383 try bw.writeByte('(');
384 const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1);
385 try bw.writeAll(" catch unreachable)");
380 try writer.writeByte('(');
381 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1);
382 try writer.writeAll(" catch unreachable)");
386383 break :root root;
387384 },
388385 .opt_payload_ptr => |info| root: {
389 const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1);
390 try bw.writeAll(".?");
386 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1);
387 try writer.writeAll(".?");
391388 break :root root;
392389 },
393390 .field_ptr => |field| root: {
394 const root = try printPtrDerivation(field.parent.*, bw, pt, null, root_strat, ptr_depth - 1);
391 const root = try printPtrDerivation(field.parent.*, writer, pt, null, root_strat, ptr_depth - 1);
395392 const agg_ty = (try field.parent.ptrType(pt)).childType(zcu);
396393 switch (agg_ty.zigTypeTag(zcu)) {
397394 .@"struct" => if (agg_ty.structFieldName(field.field_idx, zcu).unwrap()) |field_name| {
398 try bw.print(".{fi}", .{field_name.fmt(ip)});
395 try writer.print(".{f}", .{field_name.fmt(ip)});
399396 } else {
400 try bw.print("[{d}]", .{field.field_idx});
397 try writer.print("[{d}]", .{field.field_idx});
401398 },
402399 .@"union" => {
403400 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu);
404401 const field_name = tag_ty.enumFieldName(field.field_idx, zcu);
405 try bw.print(".{fi}", .{field_name.fmt(ip)});
402 try writer.print(".{f}", .{field_name.fmt(ip)});
406403 },
407404 .pointer => switch (field.field_idx) {
408 Value.slice_ptr_index => try bw.writeAll(".ptr"),
409 Value.slice_len_index => try bw.writeAll(".len"),
405 Value.slice_ptr_index => try writer.writeAll(".ptr"),
406 Value.slice_len_index => try writer.writeAll(".len"),
410407 else => unreachable,
411408 },
412409 else => unreachable,
......@@ -414,20 +411,20 @@ pub fn printPtrDerivation(
414411 break :root root;
415412 },
416413 .elem_ptr => |elem| root: {
417 const root = try printPtrDerivation(elem.parent.*, bw, pt, null, root_strat, ptr_depth - 1);
418 try bw.print("[{d}]", .{elem.elem_idx});
414 const root = try printPtrDerivation(elem.parent.*, writer, pt, null, root_strat, ptr_depth - 1);
415 try writer.print("[{d}]", .{elem.elem_idx});
419416 break :root root;
420417 },
421418
422419 .offset_and_cast => |oac| if (oac.byte_offset == 0) root: {
423 try bw.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)});
424 const root = try printPtrDerivation(oac.parent.*, bw, pt, .rvalue, root_strat, ptr_depth - 1);
425 try bw.writeAll("))");
420 try writer.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)});
421 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1);
422 try writer.writeAll("))");
426423 break :root root;
427424 } else root: {
428 try bw.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)});
429 const root = try printPtrDerivation(oac.parent.*, bw, pt, .rvalue, root_strat, ptr_depth - 1);
430 try bw.print(") + {d}))", .{oac.byte_offset});
425 try writer.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)});
426 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1);
427 try writer.print(") + {d}))", .{oac.byte_offset});
431428 break :root root;
432429 },
433430
......@@ -435,33 +432,33 @@ pub fn printPtrDerivation(
435432 };
436433
437434 if (root_or_null == null) switch (root_strat) {
438 .str => |x| try bw.writeAll(x),
435 .str => |x| try writer.writeAll(x),
439436 .print_val => |x| switch (derivation) {
440 .int => |int| try bw.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }),
441 .nav_ptr => |nav| try bw.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}),
437 .int => |int| try writer.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }),
438 .nav_ptr => |nav| try writer.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}),
442439 .uav_ptr => |uav| {
443440 const ty = Value.fromInterned(uav.val).typeOf(zcu);
444 try bw.print("@as({f}, ", .{ty.fmt(pt)});
445 try print(Value.fromInterned(uav.val), bw, x.level - 1, pt, x.opt_sema);
446 try bw.writeByte(')');
441 try writer.print("@as({f}, ", .{ty.fmt(pt)});
442 try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema);
443 try writer.writeByte(')');
447444 },
448445 .comptime_alloc_ptr => |info| {
449 try bw.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)});
450 try print(info.val, bw, x.level - 1, pt, x.opt_sema);
451 try bw.writeByte(')');
446 try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)});
447 try print(info.val, writer, x.level - 1, pt, x.opt_sema);
448 try writer.writeByte(')');
452449 },
453450 .comptime_field_ptr => |val| {
454451 const ty = val.typeOf(zcu);
455 try bw.print("@as({f}, ", .{ty.fmt(pt)});
456 try print(val, bw, x.level - 1, pt, x.opt_sema);
457 try bw.writeByte(')');
452 try writer.print("@as({f}, ", .{ty.fmt(pt)});
453 try print(val, writer, x.level - 1, pt, x.opt_sema);
454 try writer.writeByte(')');
458455 },
459456 else => unreachable,
460457 },
461458 };
462459
463460 if (need_kind == .lvalue and result_kind == .rvalue) {
464 try bw.writeAll(".*");
461 try writer.writeAll(".*");
465462 }
466463
467464 return root_or_null orelse derivation;
src/print_zir.zig+9-27
......@@ -253,14 +253,12 @@ const Writer = struct {
253253 .tag_name,
254254 .type_name,
255255 .frame_type,
256 .frame_size,
257256 .clz,
258257 .ctz,
259258 .pop_count,
260259 .byte_swap,
261260 .bit_reverse,
262261 .@"resume",
263 .@"await",
264262 .make_ptr_const,
265263 .validate_deref,
266264 .validate_const,
......@@ -557,7 +555,6 @@ const Writer = struct {
557555
558556 .tuple_decl => try self.writeTupleDecl(stream, extended),
559557
560 .await_nosuspend,
561558 .c_undef,
562559 .c_include,
563560 .set_float_mode,
......@@ -603,7 +600,6 @@ const Writer = struct {
603600 try self.writeSrcNode(stream, inst_data.node);
604601 },
605602
606 .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended),
607603 .cmpxchg => try self.writeCmpxchg(stream, extended),
608604 .ptr_cast_full => try self.writePtrCastFull(stream, extended),
609605 .ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended),
......@@ -924,19 +920,6 @@ const Writer = struct {
924920 try self.writeSrcNode(stream, extra.src_node);
925921 }
926922
927 fn writeBuiltinAsyncCall(self: *Writer, stream: *std.io.Writer, extended: Zir.Inst.Extended.InstData) !void {
928 const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data;
929 try self.writeInstRef(stream, extra.frame_buffer);
930 try stream.writeAll(", ");
931 try self.writeInstRef(stream, extra.result_ptr);
932 try stream.writeAll(", ");
933 try self.writeInstRef(stream, extra.fn_ptr);
934 try stream.writeAll(", ");
935 try self.writeInstRef(stream, extra.args);
936 try stream.writeAll(") ");
937 try self.writeSrcNode(stream, extra.node);
938 }
939
940923 fn writeParam(self: *Writer, stream: *std.io.Writer, inst: Zir.Inst.Index) !void {
941924 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
942925 const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index);
......@@ -1229,8 +1212,8 @@ const Writer = struct {
12291212
12301213 const name = self.code.nullTerminatedString(output.data.name);
12311214 const constraint = self.code.nullTerminatedString(output.data.constraint);
1232 try stream.print("output({fp}, \"{f}\", ", .{
1233 std.zig.fmtId(name), std.zig.fmtString(constraint),
1215 try stream.print("output({f}, \"{f}\", ", .{
1216 std.zig.fmtIdP(name), std.zig.fmtString(constraint),
12341217 });
12351218 try self.writeFlag(stream, "->", is_type);
12361219 try self.writeInstRef(stream, output.data.operand);
......@@ -1248,8 +1231,8 @@ const Writer = struct {
12481231
12491232 const name = self.code.nullTerminatedString(input.data.name);
12501233 const constraint = self.code.nullTerminatedString(input.data.constraint);
1251 try stream.print("input({fp}, \"{f}\", ", .{
1252 std.zig.fmtId(name), std.zig.fmtString(constraint),
1234 try stream.print("input({f}, \"{f}\", ", .{
1235 std.zig.fmtIdP(name), std.zig.fmtString(constraint),
12531236 });
12541237 try self.writeInstRef(stream, input.data.operand);
12551238 try stream.writeAll(")");
......@@ -1264,7 +1247,7 @@ const Writer = struct {
12641247 const str_index = self.code.extra[extra_i];
12651248 extra_i += 1;
12661249 const clobber = self.code.nullTerminatedString(@enumFromInt(str_index));
1267 try stream.print("{fp}", .{std.zig.fmtId(clobber)});
1250 try stream.print("{f}", .{std.zig.fmtIdP(clobber)});
12681251 if (i + 1 < clobbers_len) {
12691252 try stream.writeAll(", ");
12701253 }
......@@ -1528,7 +1511,7 @@ const Writer = struct {
15281511 try self.writeFlag(stream, "comptime ", field.is_comptime);
15291512 if (field.name != .empty) {
15301513 const field_name = self.code.nullTerminatedString(field.name);
1531 try stream.print("{fp}: ", .{std.zig.fmtId(field_name)});
1514 try stream.print("{f}: ", .{std.zig.fmtIdP(field_name)});
15321515 } else {
15331516 try stream.print("@\"{d}\": ", .{i});
15341517 }
......@@ -1691,7 +1674,7 @@ const Writer = struct {
16911674 extra_index += 1;
16921675
16931676 try stream.splatByteAll(' ', self.indent);
1694 try stream.print("{fp}", .{std.zig.fmtId(field_name)});
1677 try stream.print("{f}", .{std.zig.fmtIdP(field_name)});
16951678
16961679 if (has_type) {
16971680 const field_type = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
......@@ -1825,7 +1808,7 @@ const Writer = struct {
18251808 extra_index += 1;
18261809
18271810 try stream.splatByteAll(' ', self.indent);
1828 try stream.print("{fp}", .{std.zig.fmtId(field_name)});
1811 try stream.print("{f}", .{std.zig.fmtIdP(field_name)});
18291812
18301813 if (has_tag_value) {
18311814 const tag_value_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
......@@ -1930,7 +1913,7 @@ const Writer = struct {
19301913 const name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
19311914 const name = self.code.nullTerminatedString(name_index);
19321915 try stream.splatByteAll(' ', self.indent);
1933 try stream.print("{fp},\n", .{std.zig.fmtId(name)});
1916 try stream.print("{f},\n", .{std.zig.fmtIdP(name)});
19341917 }
19351918
19361919 self.indent -= 2;
......@@ -2597,7 +2580,6 @@ const Writer = struct {
25972580 }
25982581 switch (decl.kind) {
25992582 .@"comptime" => try stream.writeAll("comptime"),
2600 .@"usingnamespace" => try stream.writeAll("usingnamespace"),
26012583 .unnamed_test => try stream.writeAll("test"),
26022584 .@"test", .decltest, .@"const", .@"var" => {
26032585 try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) });
src/print_zoir.zig+3-3
......@@ -1,4 +1,6 @@
1pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: *Writer) error{ WriteFailed, OutOfMemory }!void {
1pub const Error = error{ WriteFailed, OutOfMemory };
2
3pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: *Writer) Error!void {
24 assert(!zoir.hasCompileErrors());
35
46 const bytes_per_node = comptime n: {
......@@ -46,8 +48,6 @@ const PrintZon = struct {
4648 zoir: Zoir,
4749 indent: u32,
4850
49 const Error = Writer.Error;
50
5151 fn renderRoot(pz: *PrintZon) Error!void {
5252 try pz.renderNode(.root);
5353 try pz.w.writeByte('\n');
src/target.zig+29-10
......@@ -85,6 +85,19 @@ pub fn defaultSingleThreaded(target: *const std.Target) bool {
8585 return false;
8686}
8787
88pub fn useEmulatedTls(target: *const std.Target) bool {
89 if (target.abi.isAndroid()) {
90 if (target.os.version_range.linux.android < 29) return true;
91 return false;
92 }
93 if (target.abi.isOpenHarmony()) return true;
94 return switch (target.os.tag) {
95 .openbsd => true,
96 .windows => target.abi == .cygnus,
97 else => false,
98 };
99}
100
88101pub fn hasValgrindSupport(target: *const std.Target, backend: std.builtin.CompilerBackend) bool {
89102 // We can't currently output the necessary Valgrind client request assembly when using the C
90103 // backend and compiling with an MSVC-like compiler.
......@@ -222,10 +235,16 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
222235/// than or equal to the number of behavior tests as the respective LLVM backend.
223236pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool {
224237 if (target.cpu.arch.isSpirV()) return true;
225 if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) return switch (target.ofmt) {
226 .elf, .macho => true,
227 else => false,
228 };
238 if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) {
239 if (target.os.tag == .netbsd) {
240 // Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341
241 return false;
242 }
243 return switch (target.ofmt) {
244 .elf, .macho => true,
245 else => false,
246 };
247 }
229248 return false;
230249}
231250
......@@ -464,12 +483,12 @@ pub fn clangSupportsNoImplicitFloatArg(target: *const std.Target) bool {
464483pub fn defaultUnwindTables(target: *const std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables {
465484 if (target.os.tag == .windows) {
466485 // The old 32-bit x86 variant of SEH doesn't use tables.
467 return if (target.cpu.arch != .x86) .@"async" else .none;
486 return if (target.cpu.arch != .x86) .async else .none;
468487 }
469 if (target.os.tag.isDarwin()) return .@"async";
470 if (libunwind) return .@"async";
471 if (libtsan) return .@"async";
472 if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .@"async";
488 if (target.os.tag.isDarwin()) return .async;
489 if (libunwind) return .async;
490 if (libtsan) return .async;
491 if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .async;
473492 return .none;
474493}
475494
......@@ -796,7 +815,7 @@ pub fn compilerRtIntAbbrev(bits: u16) []const u8 {
796815
797816pub fn fnCallConvAllowsZigTypes(cc: std.builtin.CallingConvention) bool {
798817 return switch (cc) {
799 .auto, .@"async", .@"inline" => true,
818 .auto, .async, .@"inline" => true,
800819 // For now we want to authorize PTX kernel to use zig objects, even if
801820 // we end up exposing the ABI. The goal is to experiment with more
802821 // integrated CPU/GPU code.
src/tracy.zig+34-16
......@@ -120,20 +120,21 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
120120 .vtable = &.{
121121 .alloc = allocFn,
122122 .resize = resizeFn,
123 .remap = remapFn,
123124 .free = freeFn,
124125 },
125126 };
126127 }
127128
128 fn allocFn(ptr: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
129 fn allocFn(ptr: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
129130 const self: *Self = @ptrCast(@alignCast(ptr));
130 const result = self.parent_allocator.rawAlloc(len, ptr_align, ret_addr);
131 if (result) |data| {
131 const result = self.parent_allocator.rawAlloc(len, alignment, ret_addr);
132 if (result) |memory| {
132133 if (len != 0) {
133134 if (name) |n| {
134 allocNamed(data, len, n);
135 allocNamed(memory, len, n);
135136 } else {
136 alloc(data, len);
137 alloc(memory, len);
137138 }
138139 }
139140 } else {
......@@ -142,15 +143,15 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
142143 return result;
143144 }
144145
145 fn resizeFn(ptr: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
146 fn resizeFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
146147 const self: *Self = @ptrCast(@alignCast(ptr));
147 if (self.parent_allocator.rawResize(buf, buf_align, new_len, ret_addr)) {
148 if (self.parent_allocator.rawResize(memory, alignment, new_len, ret_addr)) {
148149 if (name) |n| {
149 freeNamed(buf.ptr, n);
150 allocNamed(buf.ptr, new_len, n);
150 freeNamed(memory.ptr, n);
151 allocNamed(memory.ptr, new_len, n);
151152 } else {
152 free(buf.ptr);
153 alloc(buf.ptr, new_len);
153 free(memory.ptr);
154 alloc(memory.ptr, new_len);
154155 }
155156
156157 return true;
......@@ -161,16 +162,33 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
161162 return false;
162163 }
163164
164 fn freeFn(ptr: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
165 fn remapFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
165166 const self: *Self = @ptrCast(@alignCast(ptr));
166 self.parent_allocator.rawFree(buf, buf_align, ret_addr);
167 if (self.parent_allocator.rawRemap(memory, alignment, new_len, ret_addr)) |new_memory| {
168 if (name) |n| {
169 freeNamed(memory.ptr, n);
170 allocNamed(new_memory, new_len, n);
171 } else {
172 free(memory.ptr);
173 alloc(new_memory, new_len);
174 }
175 return new_memory;
176 } else {
177 messageColor("reallocation failed", 0xFF0000);
178 return null;
179 }
180 }
181
182 fn freeFn(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
183 const self: *Self = @ptrCast(@alignCast(ptr));
184 self.parent_allocator.rawFree(memory, alignment, ret_addr);
167185 // this condition is to handle free being called on an empty slice that was never even allocated
168186 // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}`
169 if (buf.len != 0) {
187 if (memory.len != 0) {
170188 if (name) |n| {
171 freeNamed(buf.ptr, n);
189 freeNamed(memory.ptr, n);
172190 } else {
173 free(buf.ptr);
191 free(memory.ptr);
174192 }
175193 }
176194 }
src/translate_c.zig+9-9
......@@ -357,7 +357,7 @@ fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.Fi
357357 var len: usize = undefined;
358358 const bytes_ptr = asm_string.getString_bytes_begin_size(&len);
359359
360 const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
360 const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtString(bytes_ptr[0..len])});
361361 const str_node = try Tag.string_literal.create(c.arena, str);
362362
363363 const asm_node = try Tag.asm_simple.create(c.arena, str_node);
......@@ -2276,7 +2276,7 @@ fn transNarrowStringLiteral(
22762276 var len: usize = undefined;
22772277 const bytes_ptr = stmt.getString_bytes_begin_size(&len);
22782278
2279 const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
2279 const str = try std.fmt.allocPrint(c.arena, "\"{f}\"", .{std.zig.fmtString(bytes_ptr[0..len])});
22802280 const node = try Tag.string_literal.create(c.arena, str);
22812281 return maybeSuppressResult(c, result_used, node);
22822282}
......@@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined
33383338
33393339fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
33403340 return Tag.char_literal.create(c.arena, if (narrow)
3341 try std.fmt.allocPrint(c.arena, "'{f'}'", .{std.zig.fmtEscapes(&.{@as(u8, @intCast(val))})})
3341 try std.fmt.allocPrint(c.arena, "'{f}'", .{std.zig.fmtChar(&.{@as(u8, @intCast(val))})})
33423342 else
33433343 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
33443344}
......@@ -5832,7 +5832,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
58325832 num += c - 'A' + 10;
58335833 },
58345834 else => {
5835 i += std.fmt.printInt(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
5835 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
58365836 num = 0;
58375837 if (c == '\\')
58385838 state = .escape
......@@ -5858,7 +5858,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
58585858 };
58595859 num += c - '0';
58605860 } else {
5861 i += std.fmt.printInt(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
5861 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
58625862 num = 0;
58635863 count = 0;
58645864 if (c == '\\')
......@@ -5872,19 +5872,19 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
58725872 }
58735873 }
58745874 if (state == .hex or state == .octal)
5875 i += std.fmt.printInt(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
5875 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
58765876 return bytes[0..i];
58775877}
58785878
5879/// non-ASCII characters (c > 127) are also treated as non-printable by fmtSliceEscapeLower.
5879/// non-ASCII characters (c > 127) are also treated as non-printable by ascii.hexEscape.
58805880/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
58815881/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
58825882fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 {
58835883 const zigified = try zigifyEscapeSequences(ctx, m);
58845884 if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
58855885
5886 const formatter = std.fmt.fmtSliceEscapeLower(zigified);
5887 const encoded_size = std.fmt.count("{f}", .{formatter});
5886 const formatter = std.ascii.hexEscape(zigified, .lower);
5887 const encoded_size: usize = @intCast(std.fmt.count("{f}", .{formatter}));
58885888 const output = try ctx.arena.alloc(u8, encoded_size);
58895889 return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {
58905890 error.NoSpaceLeft => unreachable,
src/zig_llvm.cpp+5-1
......@@ -83,7 +83,7 @@ static const bool assertions_on = false;
8383LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,
8484 const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,
8585 LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi,
86 const char *abi_name)
86 const char *abi_name, bool emulated_tls)
8787{
8888 std::optional<Reloc::Model> RM;
8989 switch (Reloc){
......@@ -149,6 +149,10 @@ LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Tri
149149 opt.MCOptions.ABIName = abi_name;
150150 }
151151
152 if (emulated_tls) {
153 opt.EmulatedTLS = true;
154 }
155
152156 TargetMachine *TM = reinterpret_cast<Target*>(T)->createTargetMachine(Triple, CPU, Features, opt, RM, CM,
153157 OL, JIT);
154158 return reinterpret_cast<LLVMTargetMachineRef>(TM);
src/zig_llvm.h+1-1
......@@ -105,7 +105,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi
105105ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,
106106 const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,
107107 LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi,
108 const char *abi_name);
108 const char *abi_name, bool emulated_tls);
109109
110110ZIG_EXTERN_C void ZigLLVMSetOptBisectLimit(LLVMContextRef context_ref, int limit);
111111
stage1/wasi.c+18-6
......@@ -520,12 +520,15 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov
520520 default: panic("unimplemented: fd_read special file");
521521 }
522522
523 if (fds[fd].stream == NULL) {
524 store32_align2(res_size_ptr, 0);
525 return wasi_errno_success;
526 }
527
523528 size_t size = 0;
524529 for (uint32_t i = 0; i < iovs_len; i += 1) {
525530 uint32_t len = load32_align2(&iovs_ptr[i].len);
526 size_t read_size = 0;
527 if (fds[fd].stream != NULL)
528 read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
531 size_t read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
529532 size += read_size;
530533 if (read_size < len) break;
531534 }
......@@ -633,8 +636,10 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i
633636 }
634637
635638 fpos_t pos;
636 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
637 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
639 if (fds[fd].stream != NULL) {
640 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
641 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
642 }
638643
639644 size_t size = 0;
640645 for (uint32_t i = 0; i < iovs_len; i += 1) {
......@@ -648,7 +653,9 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i
648653 if (written_size < len) break;
649654 }
650655
651 if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
656 if (fds[fd].stream != NULL) {
657 if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
658 }
652659
653660 if (size > 0) {
654661 time_t now = time(NULL);
......@@ -964,6 +971,11 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io
964971 default: panic("unimplemented: fd_pread special file");
965972 }
966973
974 if (fds[fd].stream == NULL) {
975 store32_align2(res_size_ptr, 0);
976 return wasi_errno_success;
977 }
978
967979 fpos_t pos;
968980 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
969981 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior.zig-3
......@@ -5,9 +5,7 @@ test {
55 _ = @import("behavior/align.zig");
66 _ = @import("behavior/alignof.zig");
77 _ = @import("behavior/array.zig");
8 _ = @import("behavior/async_fn.zig");
98 _ = @import("behavior/atomics.zig");
10 _ = @import("behavior/await_struct.zig");
119 _ = @import("behavior/basic.zig");
1210 _ = @import("behavior/bit_shifting.zig");
1311 _ = @import("behavior/bitcast.zig");
......@@ -103,7 +101,6 @@ test {
103101 _ = @import("behavior/underscore.zig");
104102 _ = @import("behavior/union.zig");
105103 _ = @import("behavior/union_with_members.zig");
106 _ = @import("behavior/usingnamespace.zig");
107104 _ = @import("behavior/var_args.zig");
108105 // https://github.com/llvm/llvm-project/issues/118879
109106 // https://github.com/llvm/llvm-project/issues/134659
test/behavior/align.zig-24
......@@ -425,30 +425,6 @@ test "struct field explicit alignment" {
425425 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);
426426}
427427
428test "align(@alignOf(T)) T does not force resolution of T" {
429 if (true) return error.SkipZigTest; // TODO
430
431 const S = struct {
432 const A = struct {
433 a: *align(@alignOf(A)) A,
434 };
435 fn doTheTest() void {
436 suspend {
437 resume @frame();
438 }
439 _ = bar(@Frame(doTheTest));
440 }
441 fn bar(comptime T: type) *align(@alignOf(T)) T {
442 ok = true;
443 return undefined;
444 }
445
446 var ok = false;
447 };
448 _ = async S.doTheTest();
449 try expect(S.ok);
450}
451
452428test "align(N) on functions" {
453429 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
454430 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/async_fn.zig deleted-1911
......@@ -1,1911 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6const expectEqualStrings = std.testing.expectEqualStrings;
7const expectError = std.testing.expectError;
8
9var global_x: i32 = 1;
10
11test "simple coroutine suspend and resume" {
12 if (true) return error.SkipZigTest; // TODO
13 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
14
15 var frame = async simpleAsyncFn();
16 try expect(global_x == 2);
17 resume frame;
18 try expect(global_x == 3);
19 const af: anyframe->void = &frame;
20 _ = af;
21 resume frame;
22 try expect(global_x == 4);
23}
24fn simpleAsyncFn() void {
25 global_x += 1;
26 suspend {}
27 global_x += 1;
28 suspend {}
29 global_x += 1;
30}
31
32var global_y: i32 = 1;
33
34test "pass parameter to coroutine" {
35 if (true) return error.SkipZigTest; // TODO
36 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
37
38 var p = async simpleAsyncFnWithArg(2);
39 try expect(global_y == 3);
40 resume p;
41 try expect(global_y == 5);
42}
43fn simpleAsyncFnWithArg(delta: i32) void {
44 global_y += delta;
45 suspend {}
46 global_y += delta;
47}
48
49test "suspend at end of function" {
50 if (true) return error.SkipZigTest; // TODO
51 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
52
53 const S = struct {
54 var x: i32 = 1;
55
56 fn doTheTest() !void {
57 try expect(x == 1);
58 const p = async suspendAtEnd();
59 _ = p;
60 try expect(x == 2);
61 }
62
63 fn suspendAtEnd() void {
64 x += 1;
65 suspend {}
66 }
67 };
68 try S.doTheTest();
69}
70
71test "local variable in async function" {
72 if (true) return error.SkipZigTest; // TODO
73 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
74
75 const S = struct {
76 var x: i32 = 0;
77
78 fn doTheTest() !void {
79 try expect(x == 0);
80 var p = async add(1, 2);
81 try expect(x == 0);
82 resume p;
83 try expect(x == 0);
84 resume p;
85 try expect(x == 0);
86 resume p;
87 try expect(x == 3);
88 }
89
90 fn add(a: i32, b: i32) void {
91 var accum: i32 = 0;
92 suspend {}
93 accum += a;
94 suspend {}
95 accum += b;
96 suspend {}
97 x = accum;
98 }
99 };
100 try S.doTheTest();
101}
102
103test "calling an inferred async function" {
104 if (true) return error.SkipZigTest; // TODO
105 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
106
107 const S = struct {
108 var x: i32 = 1;
109 var other_frame: *@Frame(other) = undefined;
110
111 fn doTheTest() !void {
112 _ = async first();
113 try expect(x == 1);
114 resume other_frame.*;
115 try expect(x == 2);
116 }
117
118 fn first() void {
119 other();
120 }
121 fn other() void {
122 other_frame = @frame();
123 suspend {}
124 x += 1;
125 }
126 };
127 try S.doTheTest();
128}
129
130test "@frameSize" {
131 if (true) return error.SkipZigTest; // TODO
132 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
133
134 if (builtin.target.cpu.arch == .thumb or builtin.target.cpu.arch == .thumbeb)
135 return error.SkipZigTest;
136
137 const S = struct {
138 fn doTheTest() !void {
139 {
140 var ptr = @as(fn (i32) callconv(.@"async") void, @ptrCast(other));
141 _ = &ptr;
142 const size = @frameSize(ptr);
143 try expect(size == @sizeOf(@Frame(other)));
144 }
145 {
146 var ptr = @as(fn () callconv(.@"async") void, @ptrCast(first));
147 _ = &ptr;
148 const size = @frameSize(ptr);
149 try expect(size == @sizeOf(@Frame(first)));
150 }
151 }
152
153 fn first() void {
154 other(1);
155 }
156 fn other(param: i32) void {
157 _ = param;
158 var local: i32 = undefined;
159 _ = &local;
160 suspend {}
161 }
162 };
163 try S.doTheTest();
164}
165
166test "coroutine suspend, resume" {
167 if (true) return error.SkipZigTest; // TODO
168 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
169
170 const S = struct {
171 var frame: anyframe = undefined;
172
173 fn doTheTest() !void {
174 _ = async amain();
175 seq('d');
176 resume frame;
177 seq('h');
178
179 try expect(std.mem.eql(u8, &points, "abcdefgh"));
180 }
181
182 fn amain() void {
183 seq('a');
184 var f = async testAsyncSeq();
185 seq('c');
186 await f;
187 seq('g');
188 }
189
190 fn testAsyncSeq() void {
191 defer seq('f');
192
193 seq('b');
194 suspend {
195 frame = @frame();
196 }
197 seq('e');
198 }
199 var points = [_]u8{'x'} ** "abcdefgh".len;
200 var index: usize = 0;
201
202 fn seq(c: u8) void {
203 points[index] = c;
204 index += 1;
205 }
206 };
207 try S.doTheTest();
208}
209
210test "coroutine suspend with block" {
211 if (true) return error.SkipZigTest; // TODO
212 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
213
214 const p = async testSuspendBlock();
215 _ = p;
216 try expect(!global_result);
217 resume a_promise;
218 try expect(global_result);
219}
220
221var a_promise: anyframe = undefined;
222var global_result = false;
223fn testSuspendBlock() callconv(.@"async") void {
224 suspend {
225 comptime assert(@TypeOf(@frame()) == *@Frame(testSuspendBlock)) catch unreachable;
226 a_promise = @frame();
227 }
228
229 // Test to make sure that @frame() works as advertised (issue #1296)
230 // var our_handle: anyframe = @frame();
231 expect(a_promise == @as(anyframe, @frame())) catch @panic("test failed");
232
233 global_result = true;
234}
235
236var await_a_promise: anyframe = undefined;
237var await_final_result: i32 = 0;
238
239test "coroutine await" {
240 if (true) return error.SkipZigTest; // TODO
241 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
242
243 await_seq('a');
244 var p = async await_amain();
245 _ = &p;
246 await_seq('f');
247 resume await_a_promise;
248 await_seq('i');
249 try expect(await_final_result == 1234);
250 try expect(std.mem.eql(u8, &await_points, "abcdefghi"));
251}
252fn await_amain() callconv(.@"async") void {
253 await_seq('b');
254 var p = async await_another();
255 await_seq('e');
256 await_final_result = await p;
257 await_seq('h');
258}
259fn await_another() callconv(.@"async") i32 {
260 await_seq('c');
261 suspend {
262 await_seq('d');
263 await_a_promise = @frame();
264 }
265 await_seq('g');
266 return 1234;
267}
268
269var await_points = [_]u8{0} ** "abcdefghi".len;
270var await_seq_index: usize = 0;
271
272fn await_seq(c: u8) void {
273 await_points[await_seq_index] = c;
274 await_seq_index += 1;
275}
276
277var early_final_result: i32 = 0;
278
279test "coroutine await early return" {
280 if (true) return error.SkipZigTest; // TODO
281 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
282
283 early_seq('a');
284 var p = async early_amain();
285 _ = &p;
286 early_seq('f');
287 try expect(early_final_result == 1234);
288 try expect(std.mem.eql(u8, &early_points, "abcdef"));
289}
290fn early_amain() callconv(.@"async") void {
291 early_seq('b');
292 var p = async early_another();
293 early_seq('d');
294 early_final_result = await p;
295 early_seq('e');
296}
297fn early_another() callconv(.@"async") i32 {
298 early_seq('c');
299 return 1234;
300}
301
302var early_points = [_]u8{0} ** "abcdef".len;
303var early_seq_index: usize = 0;
304
305fn early_seq(c: u8) void {
306 early_points[early_seq_index] = c;
307 early_seq_index += 1;
308}
309
310test "async function with dot syntax" {
311 if (true) return error.SkipZigTest; // TODO
312 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
313
314 const S = struct {
315 var y: i32 = 1;
316 fn foo() callconv(.@"async") void {
317 y += 1;
318 suspend {}
319 }
320 };
321 const p = async S.foo();
322 _ = p;
323 try expect(S.y == 2);
324}
325
326test "async fn pointer in a struct field" {
327 if (true) return error.SkipZigTest; // TODO
328 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
329
330 var data: i32 = 1;
331 const Foo = struct {
332 bar: fn (*i32) callconv(.@"async") void,
333 };
334 var foo = Foo{ .bar = simpleAsyncFn2 };
335 _ = &foo;
336 var bytes: [64]u8 align(16) = undefined;
337 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
338 comptime assert(@TypeOf(f) == anyframe->void);
339 try expect(data == 2);
340 resume f;
341 try expect(data == 4);
342 _ = async doTheAwait(f);
343 try expect(data == 4);
344}
345
346fn doTheAwait(f: anyframe->void) void {
347 await f;
348}
349fn simpleAsyncFn2(y: *i32) callconv(.@"async") void {
350 defer y.* += 2;
351 y.* += 1;
352 suspend {}
353}
354
355test "@asyncCall with return type" {
356 if (true) return error.SkipZigTest; // TODO
357 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
358
359 const Foo = struct {
360 bar: fn () callconv(.@"async") i32,
361
362 var global_frame: anyframe = undefined;
363 fn middle() callconv(.@"async") i32 {
364 return afunc();
365 }
366
367 fn afunc() i32 {
368 global_frame = @frame();
369 suspend {}
370 return 1234;
371 }
372 };
373 var foo = Foo{ .bar = Foo.middle };
374 _ = &foo;
375 var bytes: [150]u8 align(16) = undefined;
376 var aresult: i32 = 0;
377 _ = @asyncCall(&bytes, &aresult, foo.bar, .{});
378 try expect(aresult == 0);
379 resume Foo.global_frame;
380 try expect(aresult == 1234);
381}
382
383test "async fn with inferred error set" {
384 if (true) return error.SkipZigTest; // TODO
385 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
386
387 const S = struct {
388 var global_frame: anyframe = undefined;
389
390 fn doTheTest() !void {
391 var frame: [1]@Frame(middle) = undefined;
392 var fn_ptr = middle;
393 _ = &fn_ptr;
394 var result: @typeInfo(@typeInfo(@TypeOf(fn_ptr)).@"fn".return_type.?).error_union.error_set!void = undefined;
395 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
396 resume global_frame;
397 try std.testing.expectError(error.Fail, result);
398 }
399 fn middle() callconv(.@"async") !void {
400 var f = async middle2();
401 return await f;
402 }
403
404 fn middle2() !void {
405 return failing();
406 }
407
408 fn failing() !void {
409 global_frame = @frame();
410 suspend {}
411 return error.Fail;
412 }
413 };
414 try S.doTheTest();
415}
416
417test "error return trace across suspend points - early return" {
418 if (true) return error.SkipZigTest; // TODO
419 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
420
421 const p = nonFailing();
422 resume p;
423 const p2 = async printTrace(p);
424 _ = p2;
425}
426
427test "error return trace across suspend points - async return" {
428 if (true) return error.SkipZigTest; // TODO
429 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
430
431 const p = nonFailing();
432 const p2 = async printTrace(p);
433 _ = p2;
434 resume p;
435}
436
437fn nonFailing() (anyframe->anyerror!void) {
438 const Static = struct {
439 var frame: @Frame(suspendThenFail) = undefined;
440 };
441 Static.frame = async suspendThenFail();
442 return &Static.frame;
443}
444fn suspendThenFail() callconv(.@"async") anyerror!void {
445 suspend {}
446 return error.Fail;
447}
448fn printTrace(p: anyframe->(anyerror!void)) callconv(.@"async") void {
449 (await p) catch |e| {
450 std.testing.expect(e == error.Fail) catch @panic("test failure");
451 if (@errorReturnTrace()) |trace| {
452 expect(trace.index == 1) catch @panic("test failure");
453 } else switch (builtin.mode) {
454 .Debug, .ReleaseSafe => @panic("expected return trace"),
455 .ReleaseFast, .ReleaseSmall => {},
456 }
457 };
458}
459
460test "break from suspend" {
461 if (true) return error.SkipZigTest; // TODO
462 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
463
464 var my_result: i32 = 1;
465 const p = async testBreakFromSuspend(&my_result);
466 _ = p;
467 try std.testing.expect(my_result == 2);
468}
469fn testBreakFromSuspend(my_result: *i32) callconv(.@"async") void {
470 suspend {
471 resume @frame();
472 }
473 my_result.* += 1;
474 suspend {}
475 my_result.* += 1;
476}
477
478test "heap allocated async function frame" {
479 if (true) return error.SkipZigTest; // TODO
480 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
481
482 const S = struct {
483 var x: i32 = 42;
484
485 fn doTheTest() !void {
486 const frame = try std.testing.allocator.create(@Frame(someFunc));
487 defer std.testing.allocator.destroy(frame);
488
489 try expect(x == 42);
490 frame.* = async someFunc();
491 try expect(x == 43);
492 resume frame;
493 try expect(x == 44);
494 }
495
496 fn someFunc() void {
497 x += 1;
498 suspend {}
499 x += 1;
500 }
501 };
502 try S.doTheTest();
503}
504
505test "async function call return value" {
506 if (true) return error.SkipZigTest; // TODO
507 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
508
509 const S = struct {
510 var frame: anyframe = undefined;
511 var pt = Point{ .x = 10, .y = 11 };
512
513 fn doTheTest() !void {
514 try expectEqual(pt.x, 10);
515 try expectEqual(pt.y, 11);
516 _ = async first();
517 try expectEqual(pt.x, 10);
518 try expectEqual(pt.y, 11);
519 resume frame;
520 try expectEqual(pt.x, 1);
521 try expectEqual(pt.y, 2);
522 }
523
524 fn first() void {
525 pt = second(1, 2);
526 }
527
528 fn second(x: i32, y: i32) Point {
529 return other(x, y);
530 }
531
532 fn other(x: i32, y: i32) Point {
533 frame = @frame();
534 suspend {}
535 return Point{
536 .x = x,
537 .y = y,
538 };
539 }
540
541 const Point = struct {
542 x: i32,
543 y: i32,
544 };
545 };
546 try S.doTheTest();
547}
548
549test "suspension points inside branching control flow" {
550 if (true) return error.SkipZigTest; // TODO
551 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
552
553 const S = struct {
554 var result: i32 = 10;
555
556 fn doTheTest() !void {
557 try expect(10 == result);
558 var frame = async func(true);
559 try expect(10 == result);
560 resume frame;
561 try expect(11 == result);
562 resume frame;
563 try expect(12 == result);
564 resume frame;
565 try expect(13 == result);
566 }
567
568 fn func(b: bool) void {
569 while (b) {
570 suspend {}
571 result += 1;
572 }
573 }
574 };
575 try S.doTheTest();
576}
577
578test "call async function which has struct return type" {
579 if (true) return error.SkipZigTest; // TODO
580 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
581
582 const S = struct {
583 var frame: anyframe = undefined;
584
585 fn doTheTest() void {
586 _ = async atest();
587 resume frame;
588 }
589
590 fn atest() void {
591 const result = func();
592 expect(result.x == 5) catch @panic("test failed");
593 expect(result.y == 6) catch @panic("test failed");
594 }
595
596 const Point = struct {
597 x: usize,
598 y: usize,
599 };
600
601 fn func() Point {
602 suspend {
603 frame = @frame();
604 }
605 return Point{
606 .x = 5,
607 .y = 6,
608 };
609 }
610 };
611 S.doTheTest();
612}
613
614test "pass string literal to async function" {
615 if (true) return error.SkipZigTest; // TODO
616 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
617
618 const S = struct {
619 var frame: anyframe = undefined;
620 var ok: bool = false;
621
622 fn doTheTest() !void {
623 _ = async hello("hello");
624 resume frame;
625 try expect(ok);
626 }
627
628 fn hello(msg: []const u8) void {
629 frame = @frame();
630 suspend {}
631 expectEqualStrings("hello", msg) catch @panic("test failed");
632 ok = true;
633 }
634 };
635 try S.doTheTest();
636}
637
638test "await inside an errdefer" {
639 if (true) return error.SkipZigTest; // TODO
640 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
641
642 const S = struct {
643 var frame: anyframe = undefined;
644
645 fn doTheTest() !void {
646 _ = async amainWrap();
647 resume frame;
648 }
649
650 fn amainWrap() !void {
651 var foo = async func();
652 errdefer await foo;
653 return error.Bad;
654 }
655
656 fn func() void {
657 frame = @frame();
658 suspend {}
659 }
660 };
661 try S.doTheTest();
662}
663
664test "try in an async function with error union and non-zero-bit payload" {
665 if (true) return error.SkipZigTest; // TODO
666 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
667
668 const S = struct {
669 var frame: anyframe = undefined;
670 var ok = false;
671
672 fn doTheTest() !void {
673 _ = async amain();
674 resume frame;
675 try expect(ok);
676 }
677
678 fn amain() void {
679 std.testing.expectError(error.Bad, theProblem()) catch @panic("test failed");
680 ok = true;
681 }
682
683 fn theProblem() ![]u8 {
684 frame = @frame();
685 suspend {}
686 const result = try other();
687 return result;
688 }
689
690 fn other() ![]u8 {
691 return error.Bad;
692 }
693 };
694 try S.doTheTest();
695}
696
697test "returning a const error from async function" {
698 if (true) return error.SkipZigTest; // TODO
699 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
700
701 const S = struct {
702 var frame: anyframe = undefined;
703 var ok = false;
704
705 fn doTheTest() !void {
706 _ = async amain();
707 resume frame;
708 try expect(ok);
709 }
710
711 fn amain() !void {
712 var download_frame = async fetchUrl(10, "a string");
713 const download_text = try await download_frame;
714 _ = download_text;
715
716 @panic("should not get here");
717 }
718
719 fn fetchUrl(unused: i32, url: []const u8) ![]u8 {
720 _ = unused;
721 _ = url;
722 frame = @frame();
723 suspend {}
724 ok = true;
725 return error.OutOfMemory;
726 }
727 };
728 try S.doTheTest();
729}
730
731test "async/await typical usage" {
732 if (true) return error.SkipZigTest; // TODO
733 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
734
735 inline for ([_]bool{ false, true }) |b1| {
736 inline for ([_]bool{ false, true }) |b2| {
737 inline for ([_]bool{ false, true }) |b3| {
738 inline for ([_]bool{ false, true }) |b4| {
739 testAsyncAwaitTypicalUsage(b1, b2, b3, b4).doTheTest();
740 }
741 }
742 }
743 }
744}
745
746fn testAsyncAwaitTypicalUsage(
747 comptime simulate_fail_download: bool,
748 comptime simulate_fail_file: bool,
749 comptime suspend_download: bool,
750 comptime suspend_file: bool,
751) type {
752 return struct {
753 fn doTheTest() void {
754 _ = async amainWrap();
755 if (suspend_file) {
756 resume global_file_frame;
757 }
758 if (suspend_download) {
759 resume global_download_frame;
760 }
761 }
762 fn amainWrap() void {
763 if (amain()) |_| {
764 expect(!simulate_fail_download) catch @panic("test failure");
765 expect(!simulate_fail_file) catch @panic("test failure");
766 } else |e| switch (e) {
767 error.NoResponse => expect(simulate_fail_download) catch @panic("test failure"),
768 error.FileNotFound => expect(simulate_fail_file) catch @panic("test failure"),
769 else => @panic("test failure"),
770 }
771 }
772
773 fn amain() !void {
774 const allocator = std.testing.allocator;
775 var download_frame = async fetchUrl(allocator, "https://example.com/");
776 var download_awaited = false;
777 errdefer if (!download_awaited) {
778 if (await download_frame) |x| allocator.free(x) else |_| {}
779 };
780
781 var file_frame = async readFile(allocator, "something.txt");
782 var file_awaited = false;
783 errdefer if (!file_awaited) {
784 if (await file_frame) |x| allocator.free(x) else |_| {}
785 };
786
787 download_awaited = true;
788 const download_text = try await download_frame;
789 defer allocator.free(download_text);
790
791 file_awaited = true;
792 const file_text = try await file_frame;
793 defer allocator.free(file_text);
794
795 try expect(std.mem.eql(u8, "expected download text", download_text));
796 try expect(std.mem.eql(u8, "expected file text", file_text));
797 }
798
799 var global_download_frame: anyframe = undefined;
800 fn fetchUrl(allocator: std.mem.Allocator, url: []const u8) anyerror![]u8 {
801 _ = url;
802 const result = try allocator.dupe(u8, "expected download text");
803 errdefer allocator.free(result);
804 if (suspend_download) {
805 suspend {
806 global_download_frame = @frame();
807 }
808 }
809 if (simulate_fail_download) return error.NoResponse;
810 return result;
811 }
812
813 var global_file_frame: anyframe = undefined;
814 fn readFile(allocator: std.mem.Allocator, filename: []const u8) anyerror![]u8 {
815 _ = filename;
816 const result = try allocator.dupe(u8, "expected file text");
817 errdefer allocator.free(result);
818 if (suspend_file) {
819 suspend {
820 global_file_frame = @frame();
821 }
822 }
823 if (simulate_fail_file) return error.FileNotFound;
824 return result;
825 }
826 };
827}
828
829test "alignment of local variables in async functions" {
830 if (true) return error.SkipZigTest; // TODO
831 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
832
833 const S = struct {
834 fn doTheTest() !void {
835 var y: u8 = 123;
836 _ = &y;
837 var x: u8 align(128) = 1;
838 try expect(@intFromPtr(&x) % 128 == 0);
839 }
840 };
841 try S.doTheTest();
842}
843
844test "no reason to resolve frame still works" {
845 if (true) return error.SkipZigTest; // TODO
846 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
847
848 _ = async simpleNothing();
849}
850fn simpleNothing() void {
851 var x: i32 = 1234;
852 _ = &x;
853}
854
855test "async call a generic function" {
856 if (true) return error.SkipZigTest; // TODO
857 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
858
859 const S = struct {
860 fn doTheTest() !void {
861 var f = async func(i32, 2);
862 const result = await f;
863 try expect(result == 3);
864 }
865
866 fn func(comptime T: type, inc: T) T {
867 var x: T = 1;
868 suspend {
869 resume @frame();
870 }
871 x += inc;
872 return x;
873 }
874 };
875 _ = async S.doTheTest();
876}
877
878test "return from suspend block" {
879 if (true) return error.SkipZigTest; // TODO
880 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
881
882 const S = struct {
883 fn doTheTest() !void {
884 expect(func() == 1234) catch @panic("test failure");
885 }
886 fn func() i32 {
887 suspend {
888 return 1234;
889 }
890 }
891 };
892 _ = async S.doTheTest();
893}
894
895test "struct parameter to async function is copied to the frame" {
896 if (true) return error.SkipZigTest; // TODO
897 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
898
899 const S = struct {
900 const Point = struct {
901 x: i32,
902 y: i32,
903 };
904
905 var frame: anyframe = undefined;
906
907 fn doTheTest() void {
908 _ = async atest();
909 resume frame;
910 }
911
912 fn atest() void {
913 var f: @Frame(foo) = undefined;
914 bar(&f);
915 clobberStack(10);
916 }
917
918 fn clobberStack(x: i32) void {
919 if (x == 0) return;
920 clobberStack(x - 1);
921 var y: i32 = x;
922 _ = &y;
923 }
924
925 fn bar(f: *@Frame(foo)) void {
926 var pt = Point{ .x = 1, .y = 2 };
927 _ = &pt;
928 f.* = async foo(pt);
929 const result = await f;
930 expect(result == 1) catch @panic("test failure");
931 }
932
933 fn foo(point: Point) i32 {
934 suspend {
935 frame = @frame();
936 }
937 return point.x;
938 }
939 };
940 S.doTheTest();
941}
942
943test "cast fn to async fn when it is inferred to be async" {
944 if (true) return error.SkipZigTest; // TODO
945 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
946
947 const S = struct {
948 var frame: anyframe = undefined;
949 var ok = false;
950
951 fn doTheTest() void {
952 var ptr: fn () callconv(.@"async") i32 = undefined;
953 ptr = func;
954 var buf: [100]u8 align(16) = undefined;
955 var result: i32 = undefined;
956 const f = @asyncCall(&buf, &result, ptr, .{});
957 _ = await f;
958 expect(result == 1234) catch @panic("test failure");
959 ok = true;
960 }
961
962 fn func() i32 {
963 suspend {
964 frame = @frame();
965 }
966 return 1234;
967 }
968 };
969 _ = async S.doTheTest();
970 resume S.frame;
971 try expect(S.ok);
972}
973
974test "cast fn to async fn when it is inferred to be async, awaited directly" {
975 if (true) return error.SkipZigTest; // TODO
976 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
977
978 const S = struct {
979 var frame: anyframe = undefined;
980 var ok = false;
981
982 fn doTheTest() void {
983 var ptr: fn () callconv(.@"async") i32 = undefined;
984 ptr = func;
985 var buf: [100]u8 align(16) = undefined;
986 var result: i32 = undefined;
987 _ = await @asyncCall(&buf, &result, ptr, .{});
988 expect(result == 1234) catch @panic("test failure");
989 ok = true;
990 }
991
992 fn func() i32 {
993 suspend {
994 frame = @frame();
995 }
996 return 1234;
997 }
998 };
999 _ = async S.doTheTest();
1000 resume S.frame;
1001 try expect(S.ok);
1002}
1003
1004test "await does not force async if callee is blocking" {
1005 if (true) return error.SkipZigTest; // TODO
1006 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1007
1008 const S = struct {
1009 fn simple() i32 {
1010 return 1234;
1011 }
1012 };
1013 var x = async S.simple();
1014 try expect(await x == 1234);
1015}
1016
1017test "recursive async function" {
1018 if (true) return error.SkipZigTest; // TODO
1019 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1020
1021 try expect(recursiveAsyncFunctionTest(false).doTheTest() == 55);
1022 try expect(recursiveAsyncFunctionTest(true).doTheTest() == 55);
1023}
1024
1025fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type {
1026 return struct {
1027 fn fib(allocator: std.mem.Allocator, x: u32) error{OutOfMemory}!u32 {
1028 if (x <= 1) return x;
1029
1030 if (suspending_implementation) {
1031 suspend {
1032 resume @frame();
1033 }
1034 }
1035
1036 const f1 = try allocator.create(@Frame(fib));
1037 defer allocator.destroy(f1);
1038
1039 const f2 = try allocator.create(@Frame(fib));
1040 defer allocator.destroy(f2);
1041
1042 f1.* = async fib(allocator, x - 1);
1043 var f1_awaited = false;
1044 errdefer if (!f1_awaited) {
1045 _ = await f1;
1046 };
1047
1048 f2.* = async fib(allocator, x - 2);
1049 var f2_awaited = false;
1050 errdefer if (!f2_awaited) {
1051 _ = await f2;
1052 };
1053
1054 var sum: u32 = 0;
1055
1056 f1_awaited = true;
1057 sum += try await f1;
1058
1059 f2_awaited = true;
1060 sum += try await f2;
1061
1062 return sum;
1063 }
1064
1065 fn doTheTest() u32 {
1066 if (suspending_implementation) {
1067 var result: u32 = undefined;
1068 _ = async amain(&result);
1069 return result;
1070 } else {
1071 return fib(std.testing.allocator, 10) catch unreachable;
1072 }
1073 }
1074
1075 fn amain(result: *u32) void {
1076 var x = async fib(std.testing.allocator, 10);
1077 result.* = (await x) catch unreachable;
1078 }
1079 };
1080}
1081
1082test "@asyncCall with comptime-known function, but not awaited directly" {
1083 if (true) return error.SkipZigTest; // TODO
1084 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1085
1086 const S = struct {
1087 var global_frame: anyframe = undefined;
1088
1089 fn doTheTest() !void {
1090 var frame: [1]@Frame(middle) = undefined;
1091 var result: @typeInfo(@typeInfo(@TypeOf(middle)).@"fn".return_type.?).error_union.error_set!void = undefined;
1092 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
1093 resume global_frame;
1094 try std.testing.expectError(error.Fail, result);
1095 }
1096 fn middle() callconv(.@"async") !void {
1097 var f = async middle2();
1098 return await f;
1099 }
1100
1101 fn middle2() !void {
1102 return failing();
1103 }
1104
1105 fn failing() !void {
1106 global_frame = @frame();
1107 suspend {}
1108 return error.Fail;
1109 }
1110 };
1111 try S.doTheTest();
1112}
1113
1114test "@asyncCall with actual frame instead of byte buffer" {
1115 if (true) return error.SkipZigTest; // TODO
1116 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1117
1118 const S = struct {
1119 fn func() i32 {
1120 suspend {}
1121 return 1234;
1122 }
1123 };
1124 var frame: @Frame(S.func) = undefined;
1125 var result: i32 = undefined;
1126 const ptr = @asyncCall(&frame, &result, S.func, .{});
1127 resume ptr;
1128 try expect(result == 1234);
1129}
1130
1131test "@asyncCall using the result location inside the frame" {
1132 if (true) return error.SkipZigTest; // TODO
1133 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1134
1135 const S = struct {
1136 fn simple2(y: *i32) callconv(.@"async") i32 {
1137 defer y.* += 2;
1138 y.* += 1;
1139 suspend {}
1140 return 1234;
1141 }
1142 fn getAnswer(f: anyframe->i32, out: *i32) void {
1143 out.* = await f;
1144 }
1145 };
1146 var data: i32 = 1;
1147 const Foo = struct {
1148 bar: fn (*i32) callconv(.@"async") i32,
1149 };
1150 var foo = Foo{ .bar = S.simple2 };
1151 _ = &foo;
1152 var bytes: [64]u8 align(16) = undefined;
1153 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
1154 comptime assert(@TypeOf(f) == anyframe->i32);
1155 try expect(data == 2);
1156 resume f;
1157 try expect(data == 4);
1158 _ = async S.getAnswer(f, &data);
1159 try expect(data == 1234);
1160}
1161
1162test "@TypeOf an async function call of generic fn with error union type" {
1163 if (true) return error.SkipZigTest; // TODO
1164 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1165
1166 const S = struct {
1167 fn func(comptime x: anytype) anyerror!i32 {
1168 const T = @TypeOf(async func(x));
1169 comptime assert(T == @typeInfo(@TypeOf(@frame())).pointer.child);
1170 return undefined;
1171 }
1172 };
1173 _ = async S.func(i32);
1174}
1175
1176test "using @TypeOf on a generic function call" {
1177 if (true) return error.SkipZigTest; // TODO
1178 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1179
1180 const S = struct {
1181 var global_frame: anyframe = undefined;
1182 var global_ok = false;
1183
1184 var buf: [100]u8 align(16) = undefined;
1185
1186 fn amain(x: anytype) void {
1187 if (x == 0) {
1188 global_ok = true;
1189 return;
1190 }
1191 suspend {
1192 global_frame = @frame();
1193 }
1194 const F = @TypeOf(async amain(x - 1));
1195 const frame = @as(*F, @ptrFromInt(@intFromPtr(&buf)));
1196 return await @asyncCall(frame, {}, amain, .{x - 1});
1197 }
1198 };
1199 _ = async S.amain(@as(u32, 1));
1200 resume S.global_frame;
1201 try expect(S.global_ok);
1202}
1203
1204test "recursive call of await @asyncCall with struct return type" {
1205 if (true) return error.SkipZigTest; // TODO
1206 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1207
1208 const S = struct {
1209 var global_frame: anyframe = undefined;
1210 var global_ok = false;
1211
1212 var buf: [100]u8 align(16) = undefined;
1213
1214 fn amain(x: anytype) Foo {
1215 if (x == 0) {
1216 global_ok = true;
1217 return Foo{ .x = 1, .y = 2, .z = 3 };
1218 }
1219 suspend {
1220 global_frame = @frame();
1221 }
1222 const F = @TypeOf(async amain(x - 1));
1223 const frame = @as(*F, @ptrFromInt(@intFromPtr(&buf)));
1224 return await @asyncCall(frame, {}, amain, .{x - 1});
1225 }
1226
1227 const Foo = struct {
1228 x: u64,
1229 y: u64,
1230 z: u64,
1231 };
1232 };
1233 var res: S.Foo = undefined;
1234 var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined;
1235 _ = @asyncCall(&frame, &res, S.amain, .{@as(u32, 1)});
1236 resume S.global_frame;
1237 try expect(S.global_ok);
1238 try expect(res.x == 1);
1239 try expect(res.y == 2);
1240 try expect(res.z == 3);
1241}
1242
1243test "nosuspend function call" {
1244 if (true) return error.SkipZigTest; // TODO
1245 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1246
1247 const S = struct {
1248 fn doTheTest() !void {
1249 const result = nosuspend add(50, 100);
1250 try expect(result == 150);
1251 }
1252 fn add(a: i32, b: i32) i32 {
1253 if (a > 100) {
1254 suspend {}
1255 }
1256 return a + b;
1257 }
1258 };
1259 try S.doTheTest();
1260}
1261
1262test "await used in expression and awaiting fn with no suspend but async calling convention" {
1263 if (true) return error.SkipZigTest; // TODO
1264 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1265
1266 const S = struct {
1267 fn atest() void {
1268 var f1 = async add(1, 2);
1269 var f2 = async add(3, 4);
1270
1271 const sum = (await f1) + (await f2);
1272 expect(sum == 10) catch @panic("test failure");
1273 }
1274 fn add(a: i32, b: i32) callconv(.@"async") i32 {
1275 return a + b;
1276 }
1277 };
1278 _ = async S.atest();
1279}
1280
1281test "await used in expression after a fn call" {
1282 if (true) return error.SkipZigTest; // TODO
1283 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1284
1285 const S = struct {
1286 fn atest() void {
1287 var f1 = async add(3, 4);
1288 var sum: i32 = 0;
1289 sum = foo() + await f1;
1290 expect(sum == 8) catch @panic("test failure");
1291 }
1292 fn add(a: i32, b: i32) callconv(.@"async") i32 {
1293 return a + b;
1294 }
1295 fn foo() i32 {
1296 return 1;
1297 }
1298 };
1299 _ = async S.atest();
1300}
1301
1302test "async fn call used in expression after a fn call" {
1303 if (true) return error.SkipZigTest; // TODO
1304 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1305
1306 const S = struct {
1307 fn atest() void {
1308 var sum: i32 = 0;
1309 sum = foo() + add(3, 4);
1310 expect(sum == 8) catch @panic("test failure");
1311 }
1312 fn add(a: i32, b: i32) callconv(.@"async") i32 {
1313 return a + b;
1314 }
1315 fn foo() i32 {
1316 return 1;
1317 }
1318 };
1319 _ = async S.atest();
1320}
1321
1322test "suspend in for loop" {
1323 if (true) return error.SkipZigTest; // TODO
1324 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1325
1326 const S = struct {
1327 var global_frame: ?anyframe = null;
1328
1329 fn doTheTest() void {
1330 _ = async atest();
1331 while (global_frame) |f| resume f;
1332 }
1333
1334 fn atest() void {
1335 expect(func(&[_]u8{ 1, 2, 3 }) == 6) catch @panic("test failure");
1336 }
1337 fn func(stuff: []const u8) u32 {
1338 global_frame = @frame();
1339 var sum: u32 = 0;
1340 for (stuff) |x| {
1341 suspend {}
1342 sum += x;
1343 }
1344 global_frame = null;
1345 return sum;
1346 }
1347 };
1348 S.doTheTest();
1349}
1350
1351test "suspend in while loop" {
1352 if (true) return error.SkipZigTest; // TODO
1353 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1354
1355 const S = struct {
1356 var global_frame: ?anyframe = null;
1357
1358 fn doTheTest() void {
1359 _ = async atest();
1360 while (global_frame) |f| resume f;
1361 }
1362
1363 fn atest() void {
1364 expect(optional(6) == 6) catch @panic("test failure");
1365 expect(errunion(6) == 6) catch @panic("test failure");
1366 }
1367 fn optional(stuff: ?u32) u32 {
1368 global_frame = @frame();
1369 defer global_frame = null;
1370 while (stuff) |val| {
1371 suspend {}
1372 return val;
1373 }
1374 return 0;
1375 }
1376 fn errunion(stuff: anyerror!u32) u32 {
1377 global_frame = @frame();
1378 defer global_frame = null;
1379 while (stuff) |val| {
1380 suspend {}
1381 return val;
1382 } else |err| {
1383 err catch {};
1384 return 0;
1385 }
1386 }
1387 };
1388 S.doTheTest();
1389}
1390
1391test "correctly spill when returning the error union result of another async fn" {
1392 if (true) return error.SkipZigTest; // TODO
1393 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1394
1395 const S = struct {
1396 var global_frame: anyframe = undefined;
1397
1398 fn doTheTest() !void {
1399 expect((atest() catch unreachable) == 1234) catch @panic("test failure");
1400 }
1401
1402 fn atest() !i32 {
1403 return fallible1();
1404 }
1405
1406 fn fallible1() anyerror!i32 {
1407 suspend {
1408 global_frame = @frame();
1409 }
1410 return 1234;
1411 }
1412 };
1413 _ = async S.doTheTest();
1414 resume S.global_frame;
1415}
1416
1417test "spill target expr in a for loop" {
1418 if (true) return error.SkipZigTest; // TODO
1419 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1420
1421 const S = struct {
1422 var global_frame: anyframe = undefined;
1423
1424 fn doTheTest() !void {
1425 var foo = Foo{
1426 .slice = &[_]i32{ 1, 2 },
1427 };
1428 expect(atest(&foo) == 3) catch @panic("test failure");
1429 }
1430
1431 const Foo = struct {
1432 slice: []const i32,
1433 };
1434
1435 fn atest(foo: *Foo) i32 {
1436 var sum: i32 = 0;
1437 for (foo.slice) |x| {
1438 suspend {
1439 global_frame = @frame();
1440 }
1441 sum += x;
1442 }
1443 return sum;
1444 }
1445 };
1446 _ = async S.doTheTest();
1447 resume S.global_frame;
1448 resume S.global_frame;
1449}
1450
1451test "spill target expr in a for loop, with a var decl in the loop body" {
1452 if (true) return error.SkipZigTest; // TODO
1453 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1454
1455 const S = struct {
1456 var global_frame: anyframe = undefined;
1457
1458 fn doTheTest() !void {
1459 var foo = Foo{
1460 .slice = &[_]i32{ 1, 2 },
1461 };
1462 expect(atest(&foo) == 3) catch @panic("test failure");
1463 }
1464
1465 const Foo = struct {
1466 slice: []const i32,
1467 };
1468
1469 fn atest(foo: *Foo) i32 {
1470 var sum: i32 = 0;
1471 for (foo.slice) |x| {
1472 // Previously this var decl would prevent spills. This test makes sure
1473 // the for loop spills still happen even though there is a VarDecl in scope
1474 // before the suspend.
1475 var anything = true;
1476 _ = &anything;
1477 suspend {
1478 global_frame = @frame();
1479 }
1480 sum += x;
1481 }
1482 return sum;
1483 }
1484 };
1485 _ = async S.doTheTest();
1486 resume S.global_frame;
1487 resume S.global_frame;
1488}
1489
1490test "async call with @call" {
1491 if (true) return error.SkipZigTest; // TODO
1492 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1493
1494 const S = struct {
1495 var global_frame: anyframe = undefined;
1496 fn doTheTest() void {
1497 _ = @call(.{ .modifier = .async_kw }, atest, .{});
1498 resume global_frame;
1499 }
1500 fn atest() void {
1501 var frame = @call(.{ .modifier = .async_kw }, afoo, .{});
1502 const res = await frame;
1503 expect(res == 42) catch @panic("test failure");
1504 }
1505 fn afoo() i32 {
1506 suspend {
1507 global_frame = @frame();
1508 }
1509 return 42;
1510 }
1511 };
1512 S.doTheTest();
1513}
1514
1515test "async function passed 0-bit arg after non-0-bit arg" {
1516 if (true) return error.SkipZigTest; // TODO
1517 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1518
1519 const S = struct {
1520 var global_frame: anyframe = undefined;
1521 var global_int: i32 = 0;
1522
1523 fn foo() void {
1524 bar(1, .{}) catch unreachable;
1525 }
1526
1527 fn bar(x: i32, args: anytype) anyerror!void {
1528 _ = args;
1529 global_frame = @frame();
1530 suspend {}
1531 global_int = x;
1532 }
1533 };
1534 _ = async S.foo();
1535 resume S.global_frame;
1536 try expect(S.global_int == 1);
1537}
1538
1539test "async function passed align(16) arg after align(8) arg" {
1540 if (true) return error.SkipZigTest; // TODO
1541 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1542
1543 const S = struct {
1544 var global_frame: anyframe = undefined;
1545 var global_int: u128 = 0;
1546
1547 fn foo() void {
1548 var a: u128 = 99;
1549 _ = &a;
1550 bar(10, .{a}) catch unreachable;
1551 }
1552
1553 fn bar(x: u64, args: anytype) anyerror!void {
1554 try expect(x == 10);
1555 global_frame = @frame();
1556 suspend {}
1557 global_int = args[0];
1558 }
1559 };
1560 _ = async S.foo();
1561 resume S.global_frame;
1562 try expect(S.global_int == 99);
1563}
1564
1565test "async function call resolves target fn frame, comptime func" {
1566 if (true) return error.SkipZigTest; // TODO
1567 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1568
1569 const S = struct {
1570 var global_frame: anyframe = undefined;
1571 var global_int: i32 = 9;
1572
1573 fn foo() anyerror!void {
1574 const stack_size = 1000;
1575 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
1576 return await @asyncCall(&stack_frame, {}, bar, .{});
1577 }
1578
1579 fn bar() anyerror!void {
1580 global_frame = @frame();
1581 suspend {}
1582 global_int += 1;
1583 }
1584 };
1585 _ = async S.foo();
1586 resume S.global_frame;
1587 try expect(S.global_int == 10);
1588}
1589
1590test "async function call resolves target fn frame, runtime func" {
1591 if (true) return error.SkipZigTest; // TODO
1592 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1593
1594 const S = struct {
1595 var global_frame: anyframe = undefined;
1596 var global_int: i32 = 9;
1597
1598 fn foo() anyerror!void {
1599 const stack_size = 1000;
1600 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
1601 var func: fn () callconv(.@"async") anyerror!void = bar;
1602 _ = &func;
1603 return await @asyncCall(&stack_frame, {}, func, .{});
1604 }
1605
1606 fn bar() anyerror!void {
1607 global_frame = @frame();
1608 suspend {}
1609 global_int += 1;
1610 }
1611 };
1612 _ = async S.foo();
1613 resume S.global_frame;
1614 try expect(S.global_int == 10);
1615}
1616
1617test "properly spill optional payload capture value" {
1618 if (true) return error.SkipZigTest; // TODO
1619 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1620
1621 const S = struct {
1622 var global_frame: anyframe = undefined;
1623 var global_int: usize = 2;
1624
1625 fn foo() void {
1626 var opt: ?usize = 1234;
1627 _ = &opt;
1628 if (opt) |x| {
1629 bar();
1630 global_int += x;
1631 }
1632 }
1633
1634 fn bar() void {
1635 global_frame = @frame();
1636 suspend {}
1637 global_int += 1;
1638 }
1639 };
1640 _ = async S.foo();
1641 resume S.global_frame;
1642 try expect(S.global_int == 1237);
1643}
1644
1645test "handle defer interfering with return value spill" {
1646 if (true) return error.SkipZigTest; // TODO
1647 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1648
1649 const S = struct {
1650 var global_frame1: anyframe = undefined;
1651 var global_frame2: anyframe = undefined;
1652 var finished = false;
1653 var baz_happened = false;
1654
1655 fn doTheTest() !void {
1656 _ = async testFoo();
1657 resume global_frame1;
1658 resume global_frame2;
1659 try expect(baz_happened);
1660 try expect(finished);
1661 }
1662
1663 fn testFoo() void {
1664 expectError(error.Bad, foo()) catch @panic("test failure");
1665 finished = true;
1666 }
1667
1668 fn foo() anyerror!void {
1669 defer baz();
1670 return bar() catch |err| return err;
1671 }
1672
1673 fn bar() anyerror!void {
1674 global_frame1 = @frame();
1675 suspend {}
1676 return error.Bad;
1677 }
1678
1679 fn baz() void {
1680 global_frame2 = @frame();
1681 suspend {}
1682 baz_happened = true;
1683 }
1684 };
1685 try S.doTheTest();
1686}
1687
1688test "take address of temporary async frame" {
1689 if (true) return error.SkipZigTest; // TODO
1690 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1691
1692 const S = struct {
1693 var global_frame: anyframe = undefined;
1694 var finished = false;
1695
1696 fn doTheTest() !void {
1697 _ = async asyncDoTheTest();
1698 resume global_frame;
1699 try expect(finished);
1700 }
1701
1702 fn asyncDoTheTest() void {
1703 expect(finishIt(&async foo(10)) == 1245) catch @panic("test failure");
1704 finished = true;
1705 }
1706
1707 fn foo(arg: i32) i32 {
1708 global_frame = @frame();
1709 suspend {}
1710 return arg + 1234;
1711 }
1712
1713 fn finishIt(frame: anyframe->i32) i32 {
1714 return (await frame) + 1;
1715 }
1716 };
1717 try S.doTheTest();
1718}
1719
1720test "nosuspend await" {
1721 if (true) return error.SkipZigTest; // TODO
1722 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1723
1724 const S = struct {
1725 var finished = false;
1726
1727 fn doTheTest() !void {
1728 var frame = async foo(false);
1729 try expect(nosuspend await frame == 42);
1730 finished = true;
1731 }
1732
1733 fn foo(want_suspend: bool) i32 {
1734 if (want_suspend) {
1735 suspend {}
1736 }
1737 return 42;
1738 }
1739 };
1740 try S.doTheTest();
1741 try expect(S.finished);
1742}
1743
1744test "nosuspend on function calls" {
1745 if (true) return error.SkipZigTest; // TODO
1746 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1747
1748 const S0 = struct {
1749 b: i32 = 42,
1750 };
1751 const S1 = struct {
1752 fn c() S0 {
1753 return S0{};
1754 }
1755 fn d() !S0 {
1756 return S0{};
1757 }
1758 };
1759 try expectEqual(@as(i32, 42), nosuspend S1.c().b);
1760 try expectEqual(@as(i32, 42), (try nosuspend S1.d()).b);
1761}
1762
1763test "nosuspend on async function calls" {
1764 if (true) return error.SkipZigTest; // TODO
1765 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1766
1767 const S0 = struct {
1768 b: i32 = 42,
1769 };
1770 const S1 = struct {
1771 fn c() S0 {
1772 return S0{};
1773 }
1774 fn d() !S0 {
1775 return S0{};
1776 }
1777 };
1778 var frame_c = nosuspend async S1.c();
1779 try expectEqual(@as(i32, 42), (await frame_c).b);
1780 var frame_d = nosuspend async S1.d();
1781 try expectEqual(@as(i32, 42), (try await frame_d).b);
1782}
1783
1784// test "resume nosuspend async function calls" {
1785// if (true) return error.SkipZigTest; // if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1786// const S0 = struct {
1787// b: i32 = 42,
1788// };
1789// const S1 = struct {
1790// fn c() S0 {
1791// suspend {}
1792// return S0{};
1793// }
1794// fn d() !S0 {
1795// suspend {}
1796// return S0{};
1797// }
1798// };
1799// var frame_c = nosuspend async S1.c();
1800// resume frame_c;
1801// try expectEqual(@as(i32, 42), (await frame_c).b);
1802// var frame_d = nosuspend async S1.d();
1803// resume frame_d;
1804// try expectEqual(@as(i32, 42), (try await frame_d).b);
1805// }
1806
1807test "nosuspend resume async function calls" {
1808 if (true) return error.SkipZigTest; // TODO
1809 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1810
1811 const S0 = struct {
1812 b: i32 = 42,
1813 };
1814 const S1 = struct {
1815 fn c() S0 {
1816 suspend {}
1817 return S0{};
1818 }
1819 fn d() !S0 {
1820 suspend {}
1821 return S0{};
1822 }
1823 };
1824 var frame_c = async S1.c();
1825 nosuspend resume frame_c;
1826 try expectEqual(@as(i32, 42), (await frame_c).b);
1827 var frame_d = async S1.d();
1828 nosuspend resume frame_d;
1829 try expectEqual(@as(i32, 42), (try await frame_d).b);
1830}
1831
1832test "avoid forcing frame alignment resolution implicit cast to *anyopaque" {
1833 if (true) return error.SkipZigTest; // TODO
1834 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1835
1836 const S = struct {
1837 var x: ?*anyopaque = null;
1838
1839 fn foo() bool {
1840 suspend {
1841 x = @frame();
1842 }
1843 return true;
1844 }
1845 };
1846 var frame = async S.foo();
1847 resume @as(anyframe->bool, @ptrCast(@alignCast(S.x)));
1848 try expect(nosuspend await frame);
1849}
1850
1851test "@asyncCall with pass-by-value arguments" {
1852 if (true) return error.SkipZigTest; // TODO
1853 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1854
1855 const F0: u64 = 0xbeefbeefbeefbeef;
1856 const F1: u64 = 0xf00df00df00df00d;
1857 const F2: u64 = 0xcafecafecafecafe;
1858
1859 const S = struct {
1860 pub const ST = struct { f0: usize, f1: usize };
1861 pub const AT = [5]u8;
1862
1863 pub fn f(_fill0: u64, s: ST, _fill1: u64, a: AT, _fill2: u64) callconv(.@"async") void {
1864 _ = s;
1865 _ = a;
1866 // Check that the array and struct arguments passed by value don't
1867 // end up overflowing the adjacent fields in the frame structure.
1868 expectEqual(F0, _fill0) catch @panic("test failure");
1869 expectEqual(F1, _fill1) catch @panic("test failure");
1870 expectEqual(F2, _fill2) catch @panic("test failure");
1871 }
1872 };
1873
1874 var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
1875 // The function pointer must not be comptime-known.
1876 var t = S.f;
1877 _ = &t;
1878 var frame_ptr = @asyncCall(&buffer, {}, t, .{
1879 F0,
1880 .{ .f0 = 1, .f1 = 2 },
1881 F1,
1882 [_]u8{ 1, 2, 3, 4, 5 },
1883 F2,
1884 });
1885 _ = &frame_ptr;
1886}
1887
1888test "@asyncCall with arguments having non-standard alignment" {
1889 if (true) return error.SkipZigTest; // TODO
1890 if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
1891
1892 const F0: u64 = 0xbeefbeef;
1893 const F1: u64 = 0xf00df00df00df00d;
1894
1895 const S = struct {
1896 pub fn f(_fill0: u32, s: struct { x: u64 align(16) }, _fill1: u64) callconv(.@"async") void {
1897 _ = s;
1898 // The compiler inserts extra alignment for s, check that the
1899 // generated code picks the right slot for fill1.
1900 expectEqual(F0, _fill0) catch @panic("test failure");
1901 expectEqual(F1, _fill1) catch @panic("test failure");
1902 }
1903 };
1904
1905 var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
1906 // The function pointer must not be comptime-known.
1907 var t = S.f;
1908 _ = &t;
1909 var frame_ptr = @asyncCall(&buffer, {}, t, .{ F0, undefined, F1 });
1910 _ = &frame_ptr;
1911}
test/behavior/await_struct.zig deleted-47
......@@ -1,47 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5const Foo = struct {
6 x: i32,
7};
8
9var await_a_promise: anyframe = undefined;
10var await_final_result = Foo{ .x = 0 };
11
12test "coroutine await struct" {
13 if (true) return error.SkipZigTest; // TODO
14
15 await_seq('a');
16 var p = async await_amain();
17 _ = &p;
18 await_seq('f');
19 resume await_a_promise;
20 await_seq('i');
21 try expect(await_final_result.x == 1234);
22 try expect(std.mem.eql(u8, &await_points, "abcdefghi"));
23}
24fn await_amain() callconv(.@"async") void {
25 await_seq('b');
26 var p = async await_another();
27 await_seq('e');
28 await_final_result = await p;
29 await_seq('h');
30}
31fn await_another() callconv(.@"async") Foo {
32 await_seq('c');
33 suspend {
34 await_seq('d');
35 await_a_promise = @frame();
36 }
37 await_seq('g');
38 return Foo{ .x = 1234 };
39}
40
41var await_points = [_]u8{0} ** "abcdefghi".len;
42var await_seq_index: usize = 0;
43
44fn await_seq(c: u8) void {
45 await_points[await_seq_index] = c;
46 await_seq_index += 1;
47}
test/behavior/basic.zig-21
......@@ -1107,27 +1107,6 @@ test "inline call of function with a switch inside the return statement" {
11071107 try expect(S.foo(1) == 1);
11081108}
11091109
1110test "ambiguous reference error ignores current declaration" {
1111 const S = struct {
1112 const foo = 666;
1113
1114 const a = @This();
1115 const b = struct {
1116 const foo = a.foo;
1117 const bar = struct {
1118 bar: u32 = b.foo,
1119 };
1120
1121 comptime {
1122 _ = b.foo;
1123 }
1124 };
1125
1126 usingnamespace b;
1127 };
1128 try expect(S.b.foo == 666);
1129}
1130
11311110test "pointer to zero sized global is mutable" {
11321111 const S = struct {
11331112 const Thing = struct {};
test/behavior/call.zig+3-26
......@@ -37,7 +37,7 @@ test "basic invocations" {
3737 comptime {
3838 // comptime calls with supported modifiers
3939 try expect(@call(.auto, foo, .{2}) == 1234);
40 try expect(@call(.no_async, foo, .{3}) == 1234);
40 try expect(@call(.no_suspend, foo, .{3}) == 1234);
4141 try expect(@call(.always_tail, foo, .{4}) == 1234);
4242 try expect(@call(.always_inline, foo, .{5}) == 1234);
4343 }
......@@ -45,7 +45,7 @@ test "basic invocations" {
4545 const result = @call(.compile_time, foo, .{6}) == 1234;
4646 comptime assert(result);
4747 // runtime calls of comptime-known function
48 try expect(@call(.no_async, foo, .{7}) == 1234);
48 try expect(@call(.no_suspend, foo, .{7}) == 1234);
4949 try expect(@call(.never_tail, foo, .{8}) == 1234);
5050 try expect(@call(.never_inline, foo, .{9}) == 1234);
5151 // CBE does not support attributes on runtime functions
......@@ -53,7 +53,7 @@ test "basic invocations" {
5353 // runtime calls of non comptime-known function
5454 var alias_foo = &foo;
5555 _ = &alias_foo;
56 try expect(@call(.no_async, alias_foo, .{10}) == 1234);
56 try expect(@call(.no_suspend, alias_foo, .{10}) == 1234);
5757 try expect(@call(.never_tail, alias_foo, .{11}) == 1234);
5858 try expect(@call(.never_inline, alias_foo, .{12}) == 1234);
5959 }
......@@ -507,29 +507,6 @@ test "call inline fn through pointer" {
507507 try f(123);
508508}
509509
510test "call coerced function" {
511 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
512
513 const T = struct {
514 x: f64,
515 const T = @This();
516 usingnamespace Implement(1);
517 const F = fn (comptime f64) type;
518 const Implement: F = opaque {
519 fn implementer(comptime val: anytype) type {
520 return opaque {
521 fn incr(self: T) T {
522 return .{ .x = self.x + val };
523 }
524 };
525 }
526 }.implementer;
527 };
528
529 const a = T{ .x = 3 };
530 try std.testing.expect(a.incr().x == 4);
531}
532
533510test "call function in comptime field" {
534511 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
535512
test/behavior/error.zig-38
......@@ -1032,44 +1032,6 @@ test "function called at runtime is properly analyzed for inferred error set" {
10321032 };
10331033}
10341034
1035test "generic type constructed from inferred error set of unresolved function" {
1036 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1037 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1038 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1039
1040 const S = struct {
1041 fn write(_: void, bytes: []const u8) !usize {
1042 _ = bytes;
1043 return 0;
1044 }
1045 fn Writer(
1046 comptime Context: type,
1047 comptime WriteError: type,
1048 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
1049 ) type {
1050 return struct {
1051 context: Context,
1052 comptime {
1053 _ = writeFn;
1054 }
1055 };
1056 }
1057 const T = Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).@"fn".return_type.?).error_union.error_set, write);
1058 fn writer() T {
1059 return .{ .context = {} };
1060 }
1061 fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) {
1062 return .{ .streams = streams };
1063 }
1064 fn MultiWriter(comptime Writers: type) type {
1065 return struct {
1066 streams: Writers,
1067 };
1068 }
1069 };
1070 _ = S.multiWriter(.{S.writer()});
1071}
1072
10731035test "errorCast to adhoc inferred error set" {
10741036 const S = struct {
10751037 inline fn baz() !i32 {
test/behavior/import.zig-10
......@@ -18,16 +18,6 @@ test "importing the same thing gives the same import" {
1818 try expect(@import("std") == @import("std"));
1919}
2020
21test "import in non-toplevel scope" {
22 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
24
25 const S = struct {
26 usingnamespace @import("import/a_namespace.zig");
27 };
28 try expect(@as(i32, 1234) == S.foo());
29}
30
3121test "import empty file" {
3222 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
3323 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
test/behavior/struct.zig-11
......@@ -236,17 +236,6 @@ test "call method with mutable reference to struct with no fields" {
236236 try expect(s.do());
237237}
238238
239test "usingnamespace within struct scope" {
240 const S = struct {
241 usingnamespace struct {
242 pub fn inner() i32 {
243 return 42;
244 }
245 };
246 };
247 try expect(@as(i32, 42) == S.inner());
248}
249
250239test "struct field init with catch" {
251240 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
252241 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/type_info.zig-83
......@@ -592,24 +592,6 @@ test "StructField.is_comptime" {
592592 try expect(info.fields[1].is_comptime);
593593}
594594
595test "typeInfo resolves usingnamespace declarations" {
596 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
597
598 const A = struct {
599 pub const f1 = 42;
600 };
601
602 const B = struct {
603 pub const f0 = 42;
604 pub usingnamespace A;
605 };
606
607 const decls = @typeInfo(B).@"struct".decls;
608 try expect(decls.len == 2);
609 try expectEqualStrings(decls[0].name, "f0");
610 try expectEqualStrings(decls[1].name, "f1");
611}
612
613595test "value from struct @typeInfo default_value_ptr can be loaded at comptime" {
614596 comptime {
615597 const a = @typeInfo(@TypeOf(.{ .foo = @as(u8, 1) })).@"struct".fields[0].default_value_ptr;
......@@ -617,77 +599,12 @@ test "value from struct @typeInfo default_value_ptr can be loaded at comptime" {
617599 }
618600}
619601
620test "@typeInfo decls and usingnamespace" {
621 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
622 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
623 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
624
625 const A = struct {
626 pub const x = 5;
627 pub const y = 34;
628
629 comptime {}
630 };
631 const B = struct {
632 pub usingnamespace A;
633 pub const z = 56;
634
635 test {}
636 };
637 const decls = @typeInfo(B).@"struct".decls;
638 try expect(decls.len == 3);
639 try expectEqualStrings(decls[0].name, "z");
640 try expectEqualStrings(decls[1].name, "x");
641 try expectEqualStrings(decls[2].name, "y");
642}
643
644test "@typeInfo decls ignore dependency loops" {
645 const S = struct {
646 pub fn Def(comptime T: type) type {
647 std.debug.assert(@typeInfo(T).@"struct".decls.len == 1);
648 return struct {
649 const foo = u32;
650 };
651 }
652 usingnamespace Def(@This());
653 };
654 _ = S.foo;
655}
656
657602test "type info of tuple of string literal default value" {
658603 const struct_field = @typeInfo(@TypeOf(.{"hi"})).@"struct".fields[0];
659604 const value = struct_field.defaultValue().?;
660605 comptime std.debug.assert(value[0] == 'h');
661606}
662607
663test "@typeInfo only contains pub decls" {
664 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
665
666 const other = struct {
667 const std = @import("std");
668
669 usingnamespace struct {
670 pub const inside_non_pub_usingnamespace = 0;
671 };
672
673 pub const Enum = enum {
674 a,
675 b,
676 c,
677 };
678
679 pub const Struct = struct {
680 foo: i32,
681 };
682 };
683 const ti = @typeInfo(other);
684 const decls = ti.@"struct".decls;
685
686 try std.testing.expectEqual(2, decls.len);
687 try std.testing.expectEqualStrings("Enum", decls[0].name);
688 try std.testing.expectEqualStrings("Struct", decls[1].name);
689}
690
691608test "@typeInfo function with generic return type and inferred error set" {
692609 const S = struct {
693610 fn testFn(comptime T: type) !T {}
test/behavior/usingnamespace.zig deleted-125
......@@ -1,125 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5const A = struct {
6 pub const B = bool;
7};
8
9const C = struct {
10 usingnamespace A;
11};
12
13test "basic usingnamespace" {
14 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
16
17 try std.testing.expect(C.B == bool);
18}
19
20fn Foo(comptime T: type) type {
21 return struct {
22 usingnamespace T;
23 };
24}
25
26test "usingnamespace inside a generic struct" {
27 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
28 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
29
30 const std2 = Foo(std);
31 const testing2 = Foo(std.testing);
32 try std2.testing.expect(true);
33 try testing2.expect(true);
34}
35
36usingnamespace struct {
37 pub const foo = 42;
38};
39
40test "usingnamespace does not redeclare an imported variable" {
41 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
42 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
43
44 try comptime std.testing.expect(@This().foo == 42);
45}
46
47usingnamespace @import("usingnamespace/foo.zig");
48test "usingnamespace omits mixing in private functions" {
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
52 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
53
54 try expect(@This().privateFunction());
55 try expect(!@This().printText());
56}
57fn privateFunction() bool {
58 return true;
59}
60
61test {
62 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
64
65 _ = @import("usingnamespace/import_segregation.zig");
66}
67
68usingnamespace @import("usingnamespace/a.zig");
69test "two files usingnamespace import each other" {
70 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
71 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
72 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
73 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
74
75 try expect(@This().ok());
76}
77
78test {
79 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
80 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
81 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
82 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
83
84 const AA = struct {
85 x: i32,
86 fn b(x: i32) @This() {
87 return .{ .x = x };
88 }
89 fn c() type {
90 return if (true) struct {
91 const expected: i32 = 42;
92 } else struct {};
93 }
94 usingnamespace c();
95 };
96 const a = AA.b(42);
97 try expect(a.x == AA.c().expected);
98}
99
100const Bar = struct {
101 usingnamespace Mixin;
102};
103
104const Mixin = struct {
105 pub fn two(self: Bar) void {
106 _ = self;
107 }
108};
109
110test "container member access usingnamespace decls" {
111 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
112 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
113
114 var foo = Bar{};
115 foo.two();
116}
117
118usingnamespace opaque {};
119
120usingnamespace @Type(.{ .@"struct" = .{
121 .layout = .auto,
122 .fields = &.{},
123 .decls = &.{},
124 .is_tuple = false,
125} });
test/behavior/usingnamespace/a.zig deleted-7
......@@ -1,7 +0,0 @@
1usingnamespace @import("b.zig");
2
3pub const a_text = "OK\n";
4
5pub fn ok() bool {
6 return @import("std").mem.eql(u8, @This().b_text, "OK\n");
7}
test/behavior/usingnamespace/b.zig deleted-3
......@@ -1,3 +0,0 @@
1usingnamespace @import("a.zig");
2
3pub const b_text = @This().a_text;
test/behavior/usingnamespace/bar.zig deleted-8
......@@ -1,8 +0,0 @@
1usingnamespace @import("other.zig");
2
3pub var saw_bar_function = false;
4pub fn bar_function() void {
5 if (@This().foo_function()) {
6 saw_bar_function = true;
7 }
8}
test/behavior/usingnamespace/foo.zig deleted-14
......@@ -1,14 +0,0 @@
1// purposefully conflicting function with main source file
2// but it's private so it should be OK
3fn privateFunction() bool {
4 return false;
5}
6
7pub fn printText() bool {
8 return privateFunction();
9}
10
11pub var saw_foo_function = false;
12pub fn foo_function() void {
13 saw_foo_function = true;
14}
test/behavior/usingnamespace/import_segregation.zig deleted-20
......@@ -1,20 +0,0 @@
1const expect = @import("std").testing.expect;
2const builtin = @import("builtin");
3
4usingnamespace @import("foo.zig");
5usingnamespace @import("bar.zig");
6
7test "no clobbering happened" {
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
10
11 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) {
12 // https://github.com/ziglang/zig/issues/16846
13 return error.SkipZigTest;
14 }
15
16 @This().foo_function();
17 @This().bar_function();
18 try expect(@This().saw_foo_function);
19 try expect(@This().saw_bar_function);
20}
test/behavior/usingnamespace/other.zig deleted-4
......@@ -1,4 +0,0 @@
1pub fn foo_function() bool {
2 // this one conflicts with the one from foo
3 return true;
4}
test/cases/compile_errors/async/async_function_depends_on_its_own_frame.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn entry() void {
2 _ = async amain();
3}
4fn amain() callconv(.@"async") void {
5 var x: [@sizeOf(@Frame(amain))]u8 = undefined;
6 _ = &x;
7}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet
test/cases/compile_errors/async/async_function_indirectly_depends_on_its_own_frame.zig deleted-17
......@@ -1,17 +0,0 @@
1export fn entry() void {
2 _ = async amain();
3}
4fn amain() callconv(.@"async") void {
5 other();
6}
7fn other() void {
8 var x: [@sizeOf(@Frame(amain))]u8 = undefined;
9 _ = &x;
10}
11
12// error
13// backend=stage1
14// target=native
15//
16// tmp.zig:4:1: error: unable to determine async function frame of 'amain'
17// tmp.zig:5:10: note: analysis of function 'other' depends on the frame
test/cases/compile_errors/async/const_frame_cast_to_anyframe.zig deleted-19
......@@ -1,19 +0,0 @@
1export fn a() void {
2 const f = async func();
3 resume f;
4}
5export fn b() void {
6 const f = async func();
7 var x: anyframe = &f;
8 _ = &x;
9}
10fn func() void {
11 suspend {}
12}
13
14// error
15// backend=stage1
16// target=native
17//
18// tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'
19// tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)'
test/cases/compile_errors/async/function_with_ccc_indirectly_calling_async_function.zig deleted-18
......@@ -1,18 +0,0 @@
1export fn entry() void {
2 foo();
3}
4fn foo() void {
5 bar();
6}
7fn bar() void {
8 suspend {}
9}
10
11// error
12// backend=stage1
13// target=native
14//
15// tmp.zig:1:1: error: function with calling convention 'C' cannot be async
16// tmp.zig:2:8: note: async function call here
17// tmp.zig:5:8: note: async function call here
18// tmp.zig:8:5: note: suspends here
test/cases/compile_errors/async/indirect_recursion_of_async_functions_detected.zig deleted-36
......@@ -1,36 +0,0 @@
1var frame: ?anyframe = null;
2
3export fn a() void {
4 _ = async rangeSum(10);
5 while (frame) |f| resume f;
6}
7
8fn rangeSum(x: i32) i32 {
9 suspend {
10 frame = @frame();
11 }
12 frame = null;
13
14 if (x == 0) return 0;
15 const child = rangeSumIndirect(x - 1);
16 return child + 1;
17}
18
19fn rangeSumIndirect(x: i32) i32 {
20 suspend {
21 frame = @frame();
22 }
23 frame = null;
24
25 if (x == 0) return 0;
26 const child = rangeSum(x - 1);
27 return child + 1;
28}
29
30// error
31// backend=stage1
32// target=native
33//
34// tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself
35// tmp.zig:15:35: note: when analyzing type '@Frame(rangeSum)' here
36// tmp.zig:28:25: note: when analyzing type '@Frame(rangeSumIndirect)' here
test/cases/compile_errors/async/invalid_suspend_in_exported_function.zig deleted-15
......@@ -1,15 +0,0 @@
1export fn entry() void {
2 var frame = async func();
3 var result = await frame;
4 _ = &result;
5}
6fn func() void {
7 suspend {}
8}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:1:1: error: function with calling convention 'C' cannot be async
15// tmp.zig:3:18: note: await here is a suspend point
test/cases/compile_errors/async/returning_error_from_void_async_function.zig deleted-12
......@@ -1,12 +0,0 @@
1export fn entry() void {
2 _ = async amain();
3}
4fn amain() callconv(.@"async") void {
5 return error.ShouldBeCompileError;
6}
7
8// error
9// backend=stage1
10// target=native
11//
12// tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}'
test/cases/compile_errors/async/runtime-known_async_function_called.zig deleted-15
......@@ -1,15 +0,0 @@
1export fn entry() void {
2 _ = async amain();
3}
4fn amain() void {
5 var ptr = afunc;
6 _ = ptr();
7 _ = &ptr;
8}
9fn afunc() callconv(.@"async") void {}
10
11// error
12// backend=stage1
13// target=native
14//
15// tmp.zig:6:12: error: function is not comptime-known; @asyncCall required
test/cases/compile_errors/async/runtime-known_function_called_with_async_keyword.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn entry() void {
2 var ptr = afunc;
3 _ = async ptr();
4 _ = &ptr;
5}
6
7fn afunc() callconv(.@"async") void {}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:3:15: error: function is not comptime-known; @asyncCall required
test/cases/compile_errors/async/wrong_frame_type_used_for_async_call.zig deleted-16
......@@ -1,16 +0,0 @@
1export fn entry() void {
2 var frame: @Frame(foo) = undefined;
3 frame = async bar();
4}
5fn foo() void {
6 suspend {}
7}
8fn bar() void {
9 suspend {}
10}
11
12// error
13// backend=stage1
14// target=native
15//
16// tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'
test/cases/compile_errors/async/wrong_type_for_result_ptr_to_asyncCall.zig deleted-16
......@@ -1,16 +0,0 @@
1export fn entry() void {
2 _ = async amain();
3}
4fn amain() i32 {
5 var frame: @Frame(foo) = undefined;
6 return await @asyncCall(&frame, false, foo, .{});
7}
8fn foo() i32 {
9 return 1234;
10}
11
12// error
13// backend=stage1
14// target=native
15//
16// tmp.zig:6:37: error: expected type '*i32', found 'bool'
test/cases/compile_errors/bad_usingnamespace_transitive_failure.zig deleted-31
......@@ -1,31 +0,0 @@
1//! The full test name would be:
2//! struct field type resolution marks transitive error from bad usingnamespace in @typeInfo call from non-initial field type
3//!
4//! This test is rather esoteric. It's ensuring that errors triggered by `@typeInfo` analyzing
5//! a bad `usingnamespace` correctly trigger transitive errors when analyzed by struct field type
6//! resolution, meaning we don't incorrectly analyze code past the uses of `S`.
7
8const S = struct {
9 ok: u32,
10 bad: @typeInfo(T),
11};
12
13const T = struct {
14 pub usingnamespace @compileError("usingnamespace analyzed");
15};
16
17comptime {
18 const a: S = .{ .ok = 123, .bad = undefined };
19 _ = a;
20 @compileError("should not be reached");
21}
22
23comptime {
24 const b: S = .{ .ok = 123, .bad = undefined };
25 _ = b;
26 @compileError("should not be reached");
27}
28
29// error
30//
31// :14:24: error: usingnamespace analyzed
test/cases/compile_errors/combination_of_nosuspend_and_async.zig deleted-15
......@@ -1,15 +0,0 @@
1export fn entry() void {
2 nosuspend {
3 const bar = async foo();
4 suspend {}
5 resume bar;
6 }
7}
8fn foo() void {}
9
10// error
11// backend=stage2
12// target=native
13//
14// :4:9: error: suspend inside nosuspend block
15// :2:5: note: nosuspend block here
test/cases/compile_errors/suspend_inside_suspend_block.zig deleted-15
......@@ -1,15 +0,0 @@
1export fn entry() void {
2 _ = async foo();
3}
4fn foo() void {
5 suspend {
6 suspend {}
7 }
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :6:9: error: cannot suspend inside suspend block
15// :5:5: note: other suspend block here
test/cases/compile_errors/usingnamespace_with_wrong_type.zig deleted-7
......@@ -1,7 +0,0 @@
1usingnamespace void;
2
3// error
4// backend=stage2
5// target=native
6//
7// :1:16: error: type void has no namespace
test/cases/safety/@asyncCall with too small a frame.zig deleted-26
......@@ -1,26 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
5 _ = message;
6 _ = stack_trace;
7 std.process.exit(0);
8}
9pub fn main() !void {
10 if (builtin.zig_backend == .stage1 and builtin.os.tag == .wasi) {
11 // TODO file a bug for this failure
12 std.process.exit(0); // skip the test
13 }
14 var bytes: [1]u8 align(16) = undefined;
15 var ptr = other;
16 _ = &ptr;
17 var frame = @asyncCall(&bytes, {}, ptr, .{});
18 _ = &frame;
19 return error.TestFailed;
20}
21fn other() callconv(.@"async") void {
22 suspend {}
23}
24// run
25// backend=stage1
26// target=native
test/cases/safety/awaiting twice.zig deleted-29
......@@ -1,29 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8var frame: anyframe = undefined;
9
10pub fn main() !void {
11 _ = async amain();
12 resume frame;
13 return error.TestFailed;
14}
15
16fn amain() void {
17 var f = async func();
18 await f;
19 await f;
20}
21
22fn func() void {
23 suspend {
24 frame = @frame();
25 }
26}
27// run
28// backend=stage1
29// target=native
test/cases/safety/error return trace across suspend points.zig deleted-38
......@@ -1,38 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8
9var failing_frame: @Frame(failing) = undefined;
10
11pub fn main() !void {
12 const p = nonFailing();
13 resume p;
14 const p2 = async printTrace(p);
15 _ = p2;
16 return error.TestFailed;
17}
18
19fn nonFailing() anyframe->anyerror!void {
20 failing_frame = async failing();
21 return &failing_frame;
22}
23
24fn failing() anyerror!void {
25 suspend {}
26 return second();
27}
28
29fn second() callconv(.@"async") anyerror!void {
30 return error.Fail;
31}
32
33fn printTrace(p: anyframe->anyerror!void) void {
34 (await p) catch unreachable;
35}
36// run
37// backend=stage1
38// target=native
test/cases/safety/invalid resume of async function.zig deleted-19
......@@ -1,19 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8pub fn main() !void {
9 var p = async suspendOnce();
10 resume p; //ok
11 resume p; //bad
12 return error.TestFailed;
13}
14fn suspendOnce() void {
15 suspend {}
16}
17// run
18// backend=stage1
19// target=native
test/cases/safety/resuming a function which is awaiting a call.zig deleted-21
......@@ -1,21 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8pub fn main() !void {
9 var frame = async first();
10 resume frame;
11 return error.TestFailed;
12}
13fn first() void {
14 other();
15}
16fn other() void {
17 suspend {}
18}
19// run
20// backend=stage1
21// target=native
test/cases/safety/resuming a function which is awaiting a frame.zig deleted-22
......@@ -1,22 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8pub fn main() !void {
9 var frame = async first();
10 resume frame;
11 return error.TestFailed;
12}
13fn first() void {
14 var frame = async other();
15 await frame;
16}
17fn other() void {
18 suspend {}
19}
20// run
21// backend=stage1
22// target=native
test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig deleted-32
......@@ -1,32 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8fn foo() void {
9 suspend {
10 global_frame = @frame();
11 }
12 var f = async bar(@frame());
13 _ = &f;
14 std.process.exit(1);
15}
16
17fn bar(frame: anyframe) void {
18 suspend {
19 resume frame;
20 }
21 std.process.exit(1);
22}
23
24var global_frame: anyframe = undefined;
25pub fn main() !void {
26 _ = async foo();
27 resume global_frame;
28 std.process.exit(1);
29}
30// run
31// backend=stage1
32// target=native
test/cases/safety/resuming a non-suspended function which never been suspended.zig deleted-27
......@@ -1,27 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8fn foo() void {
9 var f = async bar(@frame());
10 _ = &f;
11 std.process.exit(1);
12}
13
14fn bar(frame: anyframe) void {
15 suspend {
16 resume frame;
17 }
18 std.process.exit(1);
19}
20
21pub fn main() !void {
22 _ = async foo();
23 return error.TestFailed;
24}
25// run
26// backend=stage1
27// target=native
test/cases/safety/slice sentinel mismatch - floats.zig +1-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22
33pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
44 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 1.2e0, found 4e0")) {
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 1.2, found 4")) {
66 std.process.exit(0);
77 }
88 std.process.exit(1);
test/compare_output.zig+3-286
......@@ -17,15 +17,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
1717 \\}
1818 , "Hello, world!" ++ if (@import("builtin").os.tag == .windows) "\r\n" else "\n");
1919
20 cases.add("hello world without libc",
21 \\const io = @import("std").io;
22 \\
23 \\pub fn main() void {
24 \\ const stdout = io.getStdOut().writer();
25 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable;
26 \\}
27 , "Hello, world!\n 12 12 a\n");
28
2920 cases.addC("number literals",
3021 \\const std = @import("std");
3122 \\const builtin = @import("builtin");
......@@ -158,24 +149,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
158149 \\
159150 );
160151
161 cases.add("order-independent declarations",
162 \\const io = @import("std").io;
163 \\const z = io.stdin_fileno;
164 \\const x : @TypeOf(y) = 1234;
165 \\const y : u16 = 5678;
166 \\pub fn main() void {
167 \\ var x_local : i32 = print_ok(x);
168 \\ _ = &x_local;
169 \\}
170 \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) {
171 \\ _ = val;
172 \\ const stdout = io.getStdOut().writer();
173 \\ stdout.print("OK\n", .{}) catch unreachable;
174 \\ return 0;
175 \\}
176 \\const foo : i32 = 0;
177 , "OK\n");
178
179152 cases.addC("expose function pointer to C land",
180153 \\const c = @cImport(@cInclude("stdlib.h"));
181154 \\
......@@ -236,267 +209,11 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
236209 \\}
237210 , "3.25\n3\n3.00\n-0.40\n");
238211
239 cases.add("same named methods in incomplete struct",
240 \\const io = @import("std").io;
241 \\
242 \\const Foo = struct {
243 \\ field1: Bar,
244 \\
245 \\ fn method(a: *const Foo) bool {
246 \\ _ = a;
247 \\ return true;
248 \\ }
249 \\};
250 \\
251 \\const Bar = struct {
252 \\ field2: i32,
253 \\
254 \\ fn method(b: *const Bar) bool {
255 \\ _ = b;
256 \\ return true;
257 \\ }
258 \\};
259 \\
260 \\pub fn main() void {
261 \\ const bar = Bar {.field2 = 13,};
262 \\ const foo = Foo {.field1 = bar,};
263 \\ const stdout = io.getStdOut().writer();
264 \\ if (!foo.method()) {
265 \\ stdout.print("BAD\n", .{}) catch unreachable;
266 \\ }
267 \\ if (!bar.method()) {
268 \\ stdout.print("BAD\n", .{}) catch unreachable;
269 \\ }
270 \\ stdout.print("OK\n", .{}) catch unreachable;
271 \\}
272 , "OK\n");
273
274 cases.add("defer with only fallthrough",
275 \\const io = @import("std").io;
276 \\pub fn main() void {
277 \\ const stdout = io.getStdOut().writer();
278 \\ stdout.print("before\n", .{}) catch unreachable;
279 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
280 \\ defer stdout.print("defer2\n", .{}) catch unreachable;
281 \\ defer stdout.print("defer3\n", .{}) catch unreachable;
282 \\ stdout.print("after\n", .{}) catch unreachable;
283 \\}
284 , "before\nafter\ndefer3\ndefer2\ndefer1\n");
285
286 cases.add("defer with return",
287 \\const io = @import("std").io;
288 \\const os = @import("std").os;
289 \\pub fn main() void {
290 \\ const stdout = io.getStdOut().writer();
291 \\ stdout.print("before\n", .{}) catch unreachable;
292 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
293 \\ defer stdout.print("defer2\n", .{}) catch unreachable;
294 \\ var gpa: @import("std").heap.GeneralPurposeAllocator(.{}) = .init;
295 \\ defer _ = gpa.deinit();
296 \\ var arena = @import("std").heap.ArenaAllocator.init(gpa.allocator());
297 \\ defer arena.deinit();
298 \\ var args_it = @import("std").process.argsWithAllocator(arena.allocator()) catch unreachable;
299 \\ if (args_it.skip() and !args_it.skip()) return;
300 \\ defer stdout.print("defer3\n", .{}) catch unreachable;
301 \\ stdout.print("after\n", .{}) catch unreachable;
302 \\}
303 , "before\ndefer2\ndefer1\n");
304
305 cases.add("errdefer and it fails",
306 \\const io = @import("std").io;
307 \\pub fn main() void {
308 \\ do_test() catch return;
309 \\}
310 \\fn do_test() !void {
311 \\ const stdout = io.getStdOut().writer();
312 \\ stdout.print("before\n", .{}) catch unreachable;
313 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
314 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
315 \\ try its_gonna_fail();
316 \\ defer stdout.print("defer3\n", .{}) catch unreachable;
317 \\ stdout.print("after\n", .{}) catch unreachable;
318 \\}
319 \\fn its_gonna_fail() !void {
320 \\ return error.IToldYouItWouldFail;
321 \\}
322 , "before\ndeferErr\ndefer1\n");
323
324 cases.add("errdefer and it passes",
325 \\const io = @import("std").io;
326 \\pub fn main() void {
327 \\ do_test() catch return;
328 \\}
329 \\fn do_test() !void {
330 \\ const stdout = io.getStdOut().writer();
331 \\ stdout.print("before\n", .{}) catch unreachable;
332 \\ defer stdout.print("defer1\n", .{}) catch unreachable;
333 \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable;
334 \\ try its_gonna_pass();
335 \\ defer stdout.print("defer3\n", .{}) catch unreachable;
336 \\ stdout.print("after\n", .{}) catch unreachable;
337 \\}
338 \\fn its_gonna_pass() anyerror!void { }
339 , "before\nafter\ndefer3\ndefer1\n");
340
341 cases.addCase(x: {
342 var tc = cases.create("@embedFile",
343 \\const foo_txt = @embedFile("foo.txt");
344 \\const io = @import("std").io;
345 \\
346 \\pub fn main() void {
347 \\ const stdout = io.getStdOut().writer();
348 \\ stdout.print(foo_txt, .{}) catch unreachable;
349 \\}
350 , "1234\nabcd\n");
351
352 tc.addSourceFile("foo.txt", "1234\nabcd\n");
353
354 break :x tc;
355 });
356
357 cases.addCase(x: {
358 var tc = cases.create("parsing args",
359 \\const std = @import("std");
360 \\const io = std.io;
361 \\const os = std.os;
362 \\
363 \\pub fn main() !void {
364 \\ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
365 \\ defer _ = gpa.deinit();
366 \\ var arena = std.heap.ArenaAllocator.init(gpa.allocator());
367 \\ defer arena.deinit();
368 \\ var args_it = try std.process.argsWithAllocator(arena.allocator());
369 \\ const stdout = io.getStdOut().writer();
370 \\ var index: usize = 0;
371 \\ _ = args_it.skip();
372 \\ while (args_it.next()) |arg| : (index += 1) {
373 \\ try stdout.print("{}: {s}\n", .{index, arg});
374 \\ }
375 \\}
376 ,
377 \\0: first arg
378 \\1: 'a' 'b' \
379 \\2: bare
380 \\3: ba""re
381 \\4: "
382 \\5: last arg
383 \\
384 );
385
386 tc.setCommandLineArgs(&[_][]const u8{
387 "first arg",
388 "'a' 'b' \\",
389 "bare",
390 "ba\"\"re",
391 "\"",
392 "last arg",
393 });
394
395 break :x tc;
396 });
397
398 cases.addCase(x: {
399 var tc = cases.create("parsing args new API",
400 \\const std = @import("std");
401 \\const io = std.io;
402 \\const os = std.os;
403 \\
404 \\pub fn main() !void {
405 \\ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
406 \\ defer _ = gpa.deinit();
407 \\ var arena = std.heap.ArenaAllocator.init(gpa.allocator());
408 \\ defer arena.deinit();
409 \\ var args_it = try std.process.argsWithAllocator(arena.allocator());
410 \\ const stdout = io.getStdOut().writer();
411 \\ var index: usize = 0;
412 \\ _ = args_it.skip();
413 \\ while (args_it.next()) |arg| : (index += 1) {
414 \\ try stdout.print("{}: {s}\n", .{index, arg});
415 \\ }
416 \\}
417 ,
418 \\0: first arg
419 \\1: 'a' 'b' \
420 \\2: bare
421 \\3: ba""re
422 \\4: "
423 \\5: last arg
424 \\
425 );
426
427 tc.setCommandLineArgs(&[_][]const u8{
428 "first arg",
429 "'a' 'b' \\",
430 "bare",
431 "ba\"\"re",
432 "\"",
433 "last arg",
434 });
435
436 break :x tc;
437 });
438
439 // It is required to override the log function in order to print to stdout instead of stderr
440 cases.add("std.log per scope log level override",
441 \\const std = @import("std");
442 \\
443 \\pub const std_options: std.Options = .{
444 \\ .log_level = .debug,
445 \\
446 \\ .log_scope_levels = &.{
447 \\ .{ .scope = .a, .level = .warn },
448 \\ .{ .scope = .c, .level = .err },
449 \\ },
450 \\ .logFn = log,
451 \\};
452 \\
453 \\const loga = std.log.scoped(.a);
454 \\const logb = std.log.scoped(.b);
455 \\const logc = std.log.scoped(.c);
456 \\
457 \\pub fn main() !void {
458 \\ loga.debug("", .{});
459 \\ logb.debug("", .{});
460 \\ logc.debug("", .{});
461 \\
462 \\ loga.info("", .{});
463 \\ logb.info("", .{});
464 \\ logc.info("", .{});
465 \\
466 \\ loga.warn("", .{});
467 \\ logb.warn("", .{});
468 \\ logc.warn("", .{});
469 \\
470 \\ loga.err("", .{});
471 \\ logb.err("", .{});
472 \\ logc.err("", .{});
473 \\}
474 \\pub fn log(
475 \\ comptime level: std.log.Level,
476 \\ comptime scope: @TypeOf(.EnumLiteral),
477 \\ comptime format: []const u8,
478 \\ args: anytype,
479 \\) void {
480 \\ const level_txt = comptime level.asText();
481 \\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "):";
482 \\ const stdout = std.io.getStdOut().writer();
483 \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
484 \\}
485 ,
486 \\debug(b):
487 \\info(b):
488 \\warning(a):
489 \\warning(b):
490 \\error(a):
491 \\error(b):
492 \\error(c):
493 \\
494 );
495
496 cases.add("valid carriage return example", "const io = @import(\"std\").io;\r\n" ++ // Testing CRLF line endings are valid
212 cases.add("valid carriage return example", "const std = @import(\"std\");\r\n" ++ // Testing CRLF line endings are valid
497213 "\r\n" ++
498214 "pub \r fn main() void {\r\n" ++ // Testing isolated carriage return as whitespace is valid
499 " const stdout = io.getStdOut().writer();\r\n" ++
215 " var file_writer = std.fs.File.stdout().writerStreaming(&.{});\r\n" ++
216 " const stdout = &file_writer.interface;\r\n" ++
500217 " stdout.print(\\\\A Multiline\r\n" ++ // testing CRLF at end of multiline string line is valid and normalises to \n in the output
501218 " \\\\String\r\n" ++
502219 " , .{}) catch unreachable;\r\n" ++
test/incremental/add_decl+7-7
......@@ -6,7 +6,7 @@
66#file=main.zig
77const std = @import("std");
88pub fn main() !void {
9 try std.io.getStdOut().writeAll(foo);
9 try std.fs.File.stdout().writeAll(foo);
1010}
1111const foo = "good morning\n";
1212#expect_stdout="good morning\n"
......@@ -15,7 +15,7 @@ const foo = "good morning\n";
1515#file=main.zig
1616const std = @import("std");
1717pub fn main() !void {
18 try std.io.getStdOut().writeAll(foo);
18 try std.fs.File.stdout().writeAll(foo);
1919}
2020const foo = "good morning\n";
2121const bar = "good evening\n";
......@@ -25,7 +25,7 @@ const bar = "good evening\n";
2525#file=main.zig
2626const std = @import("std");
2727pub fn main() !void {
28 try std.io.getStdOut().writeAll(bar);
28 try std.fs.File.stdout().writeAll(bar);
2929}
3030const foo = "good morning\n";
3131const bar = "good evening\n";
......@@ -35,17 +35,17 @@ const bar = "good evening\n";
3535#file=main.zig
3636const std = @import("std");
3737pub fn main() !void {
38 try std.io.getStdOut().writeAll(qux);
38 try std.fs.File.stdout().writeAll(qux);
3939}
4040const foo = "good morning\n";
4141const bar = "good evening\n";
42#expect_error=main.zig:3:37: error: use of undeclared identifier 'qux'
42#expect_error=main.zig:3:39: error: use of undeclared identifier 'qux'
4343
4444#update=add missing declaration
4545#file=main.zig
4646const std = @import("std");
4747pub fn main() !void {
48 try std.io.getStdOut().writeAll(qux);
48 try std.fs.File.stdout().writeAll(qux);
4949}
5050const foo = "good morning\n";
5151const bar = "good evening\n";
......@@ -56,7 +56,7 @@ const qux = "good night\n";
5656#file=main.zig
5757const std = @import("std");
5858pub fn main() !void {
59 try std.io.getStdOut().writeAll(qux);
59 try std.fs.File.stdout().writeAll(qux);
6060}
6161const qux = "good night\n";
6262#expect_stdout="good night\n"
test/incremental/add_decl_namespaced+7-7
......@@ -6,7 +6,7 @@
66#file=main.zig
77const std = @import("std");
88pub fn main() !void {
9 try std.io.getStdOut().writeAll(@This().foo);
9 try std.fs.File.stdout().writeAll(@This().foo);
1010}
1111const foo = "good morning\n";
1212#expect_stdout="good morning\n"
......@@ -15,7 +15,7 @@ const foo = "good morning\n";
1515#file=main.zig
1616const std = @import("std");
1717pub fn main() !void {
18 try std.io.getStdOut().writeAll(@This().foo);
18 try std.fs.File.stdout().writeAll(@This().foo);
1919}
2020const foo = "good morning\n";
2121const bar = "good evening\n";
......@@ -25,7 +25,7 @@ const bar = "good evening\n";
2525#file=main.zig
2626const std = @import("std");
2727pub fn main() !void {
28 try std.io.getStdOut().writeAll(@This().bar);
28 try std.fs.File.stdout().writeAll(@This().bar);
2929}
3030const foo = "good morning\n";
3131const bar = "good evening\n";
......@@ -35,18 +35,18 @@ const bar = "good evening\n";
3535#file=main.zig
3636const std = @import("std");
3737pub fn main() !void {
38 try std.io.getStdOut().writeAll(@This().qux);
38 try std.fs.File.stdout().writeAll(@This().qux);
3939}
4040const foo = "good morning\n";
4141const bar = "good evening\n";
42#expect_error=main.zig:3:44: error: root source file struct 'main' has no member named 'qux'
42#expect_error=main.zig:3:46: error: root source file struct 'main' has no member named 'qux'
4343#expect_error=main.zig:1:1: note: struct declared here
4444
4545#update=add missing declaration
4646#file=main.zig
4747const std = @import("std");
4848pub fn main() !void {
49 try std.io.getStdOut().writeAll(@This().qux);
49 try std.fs.File.stdout().writeAll(@This().qux);
5050}
5151const foo = "good morning\n";
5252const bar = "good evening\n";
......@@ -57,7 +57,7 @@ const qux = "good night\n";
5757#file=main.zig
5858const std = @import("std");
5959pub fn main() !void {
60 try std.io.getStdOut().writeAll(@This().qux);
60 try std.fs.File.stdout().writeAll(@This().qux);
6161}
6262const qux = "good night\n";
6363#expect_stdout="good night\n"
test/incremental/bad_import+2-2
......@@ -7,7 +7,7 @@
77#file=main.zig
88pub fn main() !void {
99 _ = @import("foo.zig");
10 try std.io.getStdOut().writeAll("success\n");
10 try std.fs.File.stdout().writeAll("success\n");
1111}
1212const std = @import("std");
1313#file=foo.zig
......@@ -29,7 +29,7 @@ comptime {
2929#file=main.zig
3030pub fn main() !void {
3131 //_ = @import("foo.zig");
32 try std.io.getStdOut().writeAll("success\n");
32 try std.fs.File.stdout().writeAll("success\n");
3333}
3434const std = @import("std");
3535#expect_stdout="success\n"
test/incremental/change_embed_file+3-3
......@@ -7,7 +7,7 @@
77const std = @import("std");
88const string = @embedFile("string.txt");
99pub fn main() !void {
10 try std.io.getStdOut().writeAll(string);
10 try std.fs.File.stdout().writeAll(string);
1111}
1212#file=string.txt
1313Hello, World!
......@@ -27,7 +27,7 @@ Hello again, World!
2727const std = @import("std");
2828const string = @embedFile("string.txt");
2929pub fn main() !void {
30 try std.io.getStdOut().writeAll("a hardcoded string\n");
30 try std.fs.File.stdout().writeAll("a hardcoded string\n");
3131}
3232#expect_stdout="a hardcoded string\n"
3333
......@@ -36,7 +36,7 @@ pub fn main() !void {
3636const std = @import("std");
3737const string = @embedFile("string.txt");
3838pub fn main() !void {
39 try std.io.getStdOut().writeAll(string);
39 try std.fs.File.stdout().writeAll(string);
4040}
4141#expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound
4242
test/incremental/change_enum_tag_type+6-3
......@@ -14,7 +14,8 @@ const Foo = enum(Tag) {
1414pub fn main() !void {
1515 var val: Foo = undefined;
1616 val = .a;
17 try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
17 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
18 try stdout_writer.interface.print("{s}\n", .{@tagName(val)});
1819}
1920const std = @import("std");
2021#expect_stdout="a\n"
......@@ -31,7 +32,8 @@ const Foo = enum(Tag) {
3132pub fn main() !void {
3233 var val: Foo = undefined;
3334 val = .a;
34 try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
35 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
36 try stdout_writer.interface.print("{s}\n", .{@tagName(val)});
3537}
3638comptime {
3739 // These can't be true at the same time; analysis should stop as soon as it sees `Foo`
......@@ -53,7 +55,8 @@ const Foo = enum(Tag) {
5355pub fn main() !void {
5456 var val: Foo = undefined;
5557 val = .a;
56 try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
58 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
59 try stdout_writer.interface.print("{s}\n", .{@tagName(val)});
5760}
5861const std = @import("std");
5962#expect_stdout="a\n"
test/incremental/change_exports+12-6
......@@ -16,7 +16,8 @@ pub fn main() !void {
1616 extern const bar: u32;
1717 };
1818 S.foo();
19 try std.io.getStdOut().writer().print("{}\n", .{S.bar});
19 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
20 try stdout_writer.interface.print("{}\n", .{S.bar});
2021}
2122const std = @import("std");
2223#expect_stdout="123\n"
......@@ -37,7 +38,8 @@ pub fn main() !void {
3738 extern const other: u32;
3839 };
3940 S.foo();
40 try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other });
41 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
42 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
4143}
4244const std = @import("std");
4345#expect_error=main.zig:6:5: error: exported symbol collision: foo
......@@ -59,7 +61,8 @@ pub fn main() !void {
5961 extern const other: u32;
6062 };
6163 S.foo();
62 try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other });
64 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
65 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
6366}
6467const std = @import("std");
6568#expect_stdout="123 456\n"
......@@ -83,7 +86,8 @@ pub fn main() !void {
8386 extern const other: u32;
8487 };
8588 S.foo();
86 try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other });
89 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
90 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
8791}
8892const std = @import("std");
8993#expect_stdout="123 456\n"
......@@ -128,7 +132,8 @@ pub fn main() !void {
128132 extern const other: u32;
129133 };
130134 S.foo();
131 try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other });
135 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
136 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
132137}
133138const std = @import("std");
134139#expect_stdout="123 456\n"
......@@ -152,7 +157,8 @@ pub fn main() !void {
152157 extern const other: u32;
153158 };
154159 S.foo();
155 try std.io.getStdOut().writer().print("{} {}\n", .{ S.bar, S.other });
160 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
161 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
156162}
157163const std = @import("std");
158164#expect_error=main.zig:5:5: error: exported symbol collision: bar
test/incremental/change_fn_type+6-3
......@@ -7,7 +7,8 @@ pub fn main() !void {
77 try foo(123);
88}
99fn foo(x: u8) !void {
10 return std.io.getStdOut().writer().print("{d}\n", .{x});
10 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
11 return stdout_writer.interface.print("{d}\n", .{x});
1112}
1213const std = @import("std");
1314#expect_stdout="123\n"
......@@ -18,7 +19,8 @@ pub fn main() !void {
1819 try foo(123);
1920}
2021fn foo(x: i64) !void {
21 return std.io.getStdOut().writer().print("{d}\n", .{x});
22 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
23 return stdout_writer.interface.print("{d}\n", .{x});
2224}
2325const std = @import("std");
2426#expect_stdout="123\n"
......@@ -29,7 +31,8 @@ pub fn main() !void {
2931 try foo(-42);
3032}
3133fn foo(x: i64) !void {
32 return std.io.getStdOut().writer().print("{d}\n", .{x});
34 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
35 return stdout_writer.interface.print("{d}\n", .{x});
3336}
3437const std = @import("std");
3538#expect_stdout="-42\n"
test/incremental/change_generic_line_number+2-2
......@@ -6,7 +6,7 @@ const std = @import("std");
66fn Printer(message: []const u8) type {
77 return struct {
88 fn print() !void {
9 try std.io.getStdOut().writeAll(message);
9 try std.fs.File.stdout().writeAll(message);
1010 }
1111 };
1212}
......@@ -22,7 +22,7 @@ const std = @import("std");
2222fn Printer(message: []const u8) type {
2323 return struct {
2424 fn print() !void {
25 try std.io.getStdOut().writeAll(message);
25 try std.fs.File.stdout().writeAll(message);
2626 }
2727 };
2828}
test/incremental/change_line_number+2-2
......@@ -4,7 +4,7 @@
44#file=main.zig
55const std = @import("std");
66pub fn main() !void {
7 try std.io.getStdOut().writeAll("foo\n");
7 try std.fs.File.stdout().writeAll("foo\n");
88}
99#expect_stdout="foo\n"
1010#update=change line number
......@@ -12,6 +12,6 @@ pub fn main() !void {
1212const std = @import("std");
1313
1414pub fn main() !void {
15 try std.io.getStdOut().writeAll("foo\n");
15 try std.fs.File.stdout().writeAll("foo\n");
1616}
1717#expect_stdout="foo\n"
test/incremental/change_panic_handler+6-3
......@@ -11,7 +11,8 @@ pub fn main() !u8 {
1111}
1212pub const panic = std.debug.FullPanic(myPanic);
1313fn myPanic(msg: []const u8, _: ?usize) noreturn {
14 std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {};
14 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
15 stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {};
1516 std.process.exit(0);
1617}
1718const std = @import("std");
......@@ -27,7 +28,8 @@ pub fn main() !u8 {
2728}
2829pub const panic = std.debug.FullPanic(myPanic);
2930fn myPanic(msg: []const u8, _: ?usize) noreturn {
30 std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {};
31 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
32 stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {};
3133 std.process.exit(0);
3234}
3335const std = @import("std");
......@@ -43,7 +45,8 @@ pub fn main() !u8 {
4345}
4446pub const panic = std.debug.FullPanic(myPanicNew);
4547fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
46 std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {};
48 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
49 stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {};
4750 std.process.exit(0);
4851}
4952const std = @import("std");
test/incremental/change_panic_handler_explicit+6-3
......@@ -41,7 +41,8 @@ pub const panic = struct {
4141 pub const noreturnReturned = no_panic.noreturnReturned;
4242};
4343fn myPanic(msg: []const u8, _: ?usize) noreturn {
44 std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {};
44 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
45 stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {};
4546 std.process.exit(0);
4647}
4748const std = @import("std");
......@@ -87,7 +88,8 @@ pub const panic = struct {
8788 pub const noreturnReturned = no_panic.noreturnReturned;
8889};
8990fn myPanic(msg: []const u8, _: ?usize) noreturn {
90 std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {};
91 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
92 stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {};
9193 std.process.exit(0);
9294}
9395const std = @import("std");
......@@ -133,7 +135,8 @@ pub const panic = struct {
133135 pub const noreturnReturned = no_panic.noreturnReturned;
134136};
135137fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
136 std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {};
138 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
139 stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {};
137140 std.process.exit(0);
138141}
139142const std = @import("std");
test/incremental/change_shift_op+4-2
......@@ -8,7 +8,8 @@ pub fn main() !void {
88 try foo(0x1300);
99}
1010fn foo(x: u16) !void {
11 try std.io.getStdOut().writer().print("0x{x}\n", .{x << 4});
11 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
12 try stdout_writer.interface.print("0x{x}\n", .{x << 4});
1213}
1314const std = @import("std");
1415#expect_stdout="0x3000\n"
......@@ -18,7 +19,8 @@ pub fn main() !void {
1819 try foo(0x1300);
1920}
2021fn foo(x: u16) !void {
21 try std.io.getStdOut().writer().print("0x{x}\n", .{x >> 4});
22 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
23 try stdout_writer.interface.print("0x{x}\n", .{x >> 4});
2224}
2325const std = @import("std");
2426#expect_stdout="0x130\n"
test/incremental/change_struct_same_fields+6-3
......@@ -10,7 +10,8 @@ pub fn main() !void {
1010 try foo(&val);
1111}
1212fn foo(val: *const S) !void {
13 try std.io.getStdOut().writer().print(
13 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
14 try stdout_writer.interface.print(
1415 "{d} {d}\n",
1516 .{ val.x, val.y },
1617 );
......@@ -26,7 +27,8 @@ pub fn main() !void {
2627 try foo(&val);
2728}
2829fn foo(val: *const S) !void {
29 try std.io.getStdOut().writer().print(
30 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
31 try stdout_writer.interface.print(
3032 "{d} {d}\n",
3133 .{ val.x, val.y },
3234 );
......@@ -42,7 +44,8 @@ pub fn main() !void {
4244 try foo(&val);
4345}
4446fn foo(val: *const S) !void {
45 try std.io.getStdOut().writer().print(
47 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
48 try stdout_writer.interface.print(
4649 "{d} {d}\n",
4750 .{ val.x, val.y },
4851 );
test/incremental/change_zon_file+3-3
......@@ -7,7 +7,7 @@
77const std = @import("std");
88const message: []const u8 = @import("message.zon");
99pub fn main() !void {
10 try std.io.getStdOut().writeAll(message);
10 try std.fs.File.stdout().writeAll(message);
1111}
1212#file=message.zon
1313"Hello, World!\n"
......@@ -28,7 +28,7 @@ pub fn main() !void {
2828const std = @import("std");
2929const message: []const u8 = @import("message.zon");
3030pub fn main() !void {
31 try std.io.getStdOut().writeAll("a hardcoded string\n");
31 try std.fs.File.stdout().writeAll("a hardcoded string\n");
3232}
3333#expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound
3434#expect_error=main.zig:2:37: note: file imported here
......@@ -43,6 +43,6 @@ pub fn main() !void {
4343const std = @import("std");
4444const message: []const u8 = @import("message.zon");
4545pub fn main() !void {
46 try std.io.getStdOut().writeAll(message);
46 try std.fs.File.stdout().writeAll(message);
4747}
4848#expect_stdout="We're back, World!\n"
test/incremental/change_zon_file_no_result_type+1-1
......@@ -6,7 +6,7 @@
66#file=main.zig
77const std = @import("std");
88pub fn main() !void {
9 try std.io.getStdOut().writeAll(@import("foo.zon").message);
9 try std.fs.File.stdout().writeAll(@import("foo.zon").message);
1010}
1111#file=foo.zon
1212.{
test/incremental/compile_log+3-3
......@@ -7,7 +7,7 @@
77#file=main.zig
88const std = @import("std");
99pub fn main() !void {
10 try std.io.getStdOut().writeAll("Hello, World!\n");
10 try std.fs.File.stdout().writeAll("Hello, World!\n");
1111}
1212#expect_stdout="Hello, World!\n"
1313
......@@ -15,7 +15,7 @@ pub fn main() !void {
1515#file=main.zig
1616const std = @import("std");
1717pub fn main() !void {
18 try std.io.getStdOut().writeAll("Hello, World!\n");
18 try std.fs.File.stdout().writeAll("Hello, World!\n");
1919 @compileLog("this is a log");
2020}
2121#expect_error=main.zig:4:5: error: found compile log statement
......@@ -25,6 +25,6 @@ pub fn main() !void {
2525#file=main.zig
2626const std = @import("std");
2727pub fn main() !void {
28 try std.io.getStdOut().writeAll("Hello, World!\n");
28 try std.fs.File.stdout().writeAll("Hello, World!\n");
2929}
3030#expect_stdout="Hello, World!\n"
test/incremental/fix_astgen_failure+5-5
......@@ -9,28 +9,28 @@ pub fn main() !void {
99}
1010#file=foo.zig
1111pub fn hello() !void {
12 try std.io.getStdOut().writeAll("Hello, World!\n");
12 try std.fs.File.stdout().writeAll("Hello, World!\n");
1313}
1414#expect_error=foo.zig:2:9: error: use of undeclared identifier 'std'
1515#update=fix the error
1616#file=foo.zig
1717const std = @import("std");
1818pub fn hello() !void {
19 try std.io.getStdOut().writeAll("Hello, World!\n");
19 try std.fs.File.stdout().writeAll("Hello, World!\n");
2020}
2121#expect_stdout="Hello, World!\n"
2222#update=add new error
2323#file=foo.zig
2424const std = @import("std");
2525pub fn hello() !void {
26 try std.io.getStdOut().writeAll(hello_str);
26 try std.fs.File.stdout().writeAll(hello_str);
2727}
28#expect_error=foo.zig:3:37: error: use of undeclared identifier 'hello_str'
28#expect_error=foo.zig:3:39: error: use of undeclared identifier 'hello_str'
2929#update=fix the new error
3030#file=foo.zig
3131const std = @import("std");
3232const hello_str = "Hello, World! Again!\n";
3333pub fn hello() !void {
34 try std.io.getStdOut().writeAll(hello_str);
34 try std.fs.File.stdout().writeAll(hello_str);
3535}
3636#expect_stdout="Hello, World! Again!\n"
test/incremental/function_becomes_inline+3-3
......@@ -7,7 +7,7 @@ pub fn main() !void {
77 try foo();
88}
99fn foo() !void {
10 try std.io.getStdOut().writer().writeAll("Hello, World!\n");
10 try std.fs.File.stdout().writeAll("Hello, World!\n");
1111}
1212const std = @import("std");
1313#expect_stdout="Hello, World!\n"
......@@ -18,7 +18,7 @@ pub fn main() !void {
1818 try foo();
1919}
2020inline fn foo() !void {
21 try std.io.getStdOut().writer().writeAll("Hello, World!\n");
21 try std.fs.File.stdout().writeAll("Hello, World!\n");
2222}
2323const std = @import("std");
2424#expect_stdout="Hello, World!\n"
......@@ -29,7 +29,7 @@ pub fn main() !void {
2929 try foo();
3030}
3131inline fn foo() !void {
32 try std.io.getStdOut().writer().writeAll("Hello, `inline` World!\n");
32 try std.fs.File.stdout().writeAll("Hello, `inline` World!\n");
3333}
3434const std = @import("std");
3535#expect_stdout="Hello, `inline` World!\n"
test/incremental/hello+2-2
......@@ -6,13 +6,13 @@
66#file=main.zig
77const std = @import("std");
88pub fn main() !void {
9 try std.io.getStdOut().writeAll("good morning\n");
9 try std.fs.File.stdout().writeAll("good morning\n");
1010}
1111#expect_stdout="good morning\n"
1212#update=change the string
1313#file=main.zig
1414const std = @import("std");
1515pub fn main() !void {
16 try std.io.getStdOut().writeAll("おはようございます\n");
16 try std.fs.File.stdout().writeAll("おはようございます\n");
1717}
1818#expect_stdout="おはようございます\n"
test/incremental/make_decl_pub+2-2
......@@ -11,7 +11,7 @@ pub fn main() !void {
1111#file=foo.zig
1212const std = @import("std");
1313fn hello() !void {
14 try std.io.getStdOut().writeAll("Hello, World!\n");
14 try std.fs.File.stdout().writeAll("Hello, World!\n");
1515}
1616#expect_error=main.zig:3:12: error: 'hello' is not marked 'pub'
1717#expect_error=foo.zig:2:1: note: declared here
......@@ -20,6 +20,6 @@ fn hello() !void {
2020#file=foo.zig
2121const std = @import("std");
2222pub fn hello() !void {
23 try std.io.getStdOut().writeAll("Hello, World!\n");
23 try std.fs.File.stdout().writeAll("Hello, World!\n");
2424}
2525#expect_stdout="Hello, World!\n"
test/incremental/modify_inline_fn+2-2
......@@ -7,7 +7,7 @@
77const std = @import("std");
88pub fn main() !void {
99 const str = getStr();
10 try std.io.getStdOut().writeAll(str);
10 try std.fs.File.stdout().writeAll(str);
1111}
1212inline fn getStr() []const u8 {
1313 return "foo\n";
......@@ -18,7 +18,7 @@ inline fn getStr() []const u8 {
1818const std = @import("std");
1919pub fn main() !void {
2020 const str = getStr();
21 try std.io.getStdOut().writeAll(str);
21 try std.fs.File.stdout().writeAll(str);
2222}
2323inline fn getStr() []const u8 {
2424 return "bar\n";
test/incremental/move_src+6-4
......@@ -6,7 +6,8 @@
66#file=main.zig
77const std = @import("std");
88pub fn main() !void {
9 try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() });
9 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
10 try stdout_writer.interface.print("{d} {d}\n", .{ foo(), bar() });
1011}
1112fn foo() u32 {
1213 return @src().line;
......@@ -14,13 +15,14 @@ fn foo() u32 {
1415fn bar() u32 {
1516 return 123;
1617}
17#expect_stdout="6 123\n"
18#expect_stdout="7 123\n"
1819
1920#update=add newline
2021#file=main.zig
2122const std = @import("std");
2223pub fn main() !void {
23 try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() });
24 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
25 try stdout_writer.interface.print("{d} {d}\n", .{ foo(), bar() });
2426}
2527
2628fn foo() u32 {
......@@ -29,4 +31,4 @@ fn foo() u32 {
2931fn bar() u32 {
3032 return 123;
3133}
32#expect_stdout="7 123\n"
34#expect_stdout="8 123\n"
test/incremental/no_change_preserves_tag_names+2-2
......@@ -7,7 +7,7 @@
77const std = @import("std");
88var some_enum: enum { first, second } = .first;
99pub fn main() !void {
10 try std.io.getStdOut().writeAll(@tagName(some_enum));
10 try std.fs.File.stdout().writeAll(@tagName(some_enum));
1111}
1212#expect_stdout="first"
1313#update=no change
......@@ -15,6 +15,6 @@ pub fn main() !void {
1515const std = @import("std");
1616var some_enum: enum { first, second } = .first;
1717pub fn main() !void {
18 try std.io.getStdOut().writeAll(@tagName(some_enum));
18 try std.fs.File.stdout().writeAll(@tagName(some_enum));
1919}
2020#expect_stdout="first"
test/incremental/recursive_function_becomes_non_recursive+2-2
......@@ -8,7 +8,7 @@ pub fn main() !void {
88 try foo(false);
99}
1010fn foo(recurse: bool) !void {
11 const stdout = std.io.getStdOut().writer();
11 const stdout = std.fs.File.stdout();
1212 if (recurse) return foo(true);
1313 try stdout.writeAll("non-recursive path\n");
1414}
......@@ -21,7 +21,7 @@ pub fn main() !void {
2121 try foo(true);
2222}
2323fn foo(recurse: bool) !void {
24 const stdout = std.io.getStdOut().writer();
24 const stdout = std.fs.File.stdout();
2525 if (recurse) return stdout.writeAll("x==1\n");
2626 try stdout.writeAll("non-recursive path\n");
2727}
test/incremental/remove_enum_field+5-3
......@@ -9,7 +9,8 @@ const MyEnum = enum(u8) {
99 bar = 2,
1010};
1111pub fn main() !void {
12 try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)});
12 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
13 try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)});
1314}
1415const std = @import("std");
1516#expect_stdout="1\n"
......@@ -20,8 +21,9 @@ const MyEnum = enum(u8) {
2021 bar = 2,
2122};
2223pub fn main() !void {
23 try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)});
24 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
25 try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)});
2426}
2527const std = @import("std");
26#expect_error=main.zig:6:73: error: enum 'main.MyEnum' has no member named 'foo'
28#expect_error=main.zig:7:69: error: enum 'main.MyEnum' has no member named 'foo'
2729#expect_error=main.zig:1:16: note: enum declared here
test/incremental/unreferenced_error+4-4
......@@ -6,7 +6,7 @@
66#file=main.zig
77const std = @import("std");
88pub fn main() !void {
9 try std.io.getStdOut().writeAll(a);
9 try std.fs.File.stdout().writeAll(a);
1010}
1111const a = "Hello, World!\n";
1212#expect_stdout="Hello, World!\n"
......@@ -15,7 +15,7 @@ const a = "Hello, World!\n";
1515#file=main.zig
1616const std = @import("std");
1717pub fn main() !void {
18 try std.io.getStdOut().writeAll(a);
18 try std.fs.File.stdout().writeAll(a);
1919}
2020const a = @compileError("bad a");
2121#expect_error=main.zig:5:11: error: bad a
......@@ -24,7 +24,7 @@ const a = @compileError("bad a");
2424#file=main.zig
2525const std = @import("std");
2626pub fn main() !void {
27 try std.io.getStdOut().writeAll(b);
27 try std.fs.File.stdout().writeAll(b);
2828}
2929const a = @compileError("bad a");
3030const b = "Hi there!\n";
......@@ -34,7 +34,7 @@ const b = "Hi there!\n";
3434#file=main.zig
3535const std = @import("std");
3636pub fn main() !void {
37 try std.io.getStdOut().writeAll(a);
37 try std.fs.File.stdout().writeAll(a);
3838}
3939const a = "Back to a\n";
4040const b = @compileError("bad b");
test/link/bss/main.zig+4-1
......@@ -4,8 +4,11 @@ const std = @import("std");
44var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000;
55
66pub fn main() anyerror!void {
7 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
8
79 buffer[0x10] = 1;
8 try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{
10
11 try stdout_writer.interface.print("{d}, {d}, {d}\n", .{
912 // workaround the dreaded decl_val
1013 (&buffer)[0],
1114 (&buffer)[0x10],
test/link/elf.zig+4-4
......@@ -1315,8 +1315,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
13151315 \\extern var live_var2: i32;
13161316 \\extern fn live_fn2() void;
13171317 \\pub fn main() void {
1318 \\ const stdout = std.io.getStdOut();
1319 \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
1318 \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
1319 \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
13201320 \\ live_fn2();
13211321 \\}
13221322 ,
......@@ -1357,8 +1357,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
13571357 \\extern var live_var2: i32;
13581358 \\extern fn live_fn2() void;
13591359 \\pub fn main() void {
1360 \\ const stdout = std.io.getStdOut();
1361 \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;
1360 \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
1361 \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
13621362 \\ live_fn2();
13631363 \\}
13641364 ,
test/link/macho.zig+4-3
......@@ -710,7 +710,7 @@ fn testHelloZig(b: *Build, opts: Options) *Step {
710710 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
711711 \\const std = @import("std");
712712 \\pub fn main() void {
713 \\ std.io.getStdOut().writer().print("Hello world!\n", .{}) catch unreachable;
713 \\ std.fs.File.stdout().writeAll("Hello world!\n") catch @panic("fail");
714714 \\}
715715 });
716716
......@@ -2365,10 +2365,11 @@ fn testTlsZig(b: *Build, opts: Options) *Step {
23652365 \\threadlocal var x: i32 = 0;
23662366 \\threadlocal var y: i32 = -1;
23672367 \\pub fn main() void {
2368 \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
2368 \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
2369 \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable;
23692370 \\ x -= 1;
23702371 \\ y += 1;
2371 \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
2372 \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable;
23722373 \\}
23732374 });
23742375
test/link/wasm/extern/main.zig+2-2
......@@ -3,6 +3,6 @@ const std = @import("std");
33extern const foo: u32;
44
55pub fn main() void {
6 const std_out = std.io.getStdOut();
7 std_out.writer().print("Result: {d}", .{foo}) catch {};
6 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
7 stdout_writer.interface.print("Result: {d}", .{foo}) catch {};
88}
test/src/Cases.zig+33-6
......@@ -594,30 +594,57 @@ pub fn lowerToTranslateCSteps(
594594 };
595595}
596596
597pub const CaseTestOptions = struct {
598 test_filters: []const []const u8,
599 test_target_filters: []const []const u8,
600 skip_non_native: bool,
601 skip_freebsd: bool,
602 skip_netbsd: bool,
603 skip_windows: bool,
604 skip_macos: bool,
605 skip_linux: bool,
606 skip_llvm: bool,
607 skip_libc: bool,
608};
609
597610pub fn lowerToBuildSteps(
598611 self: *Cases,
599612 b: *std.Build,
600613 parent_step: *std.Build.Step,
601 test_filters: []const []const u8,
602 test_target_filters: []const []const u8,
614 options: CaseTestOptions,
603615) void {
604616 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|
605617 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
606618 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");
607619
608620 for (self.cases.items) |case| {
609 for (test_filters) |test_filter| {
621 for (options.test_filters) |test_filter| {
610622 if (std.mem.indexOf(u8, case.name, test_filter)) |_| break;
611 } else if (test_filters.len > 0) continue;
623 } else if (options.test_filters.len > 0) continue;
624
625 if (options.skip_non_native and !case.target.query.isNative())
626 continue;
627
628 if (options.skip_freebsd and case.target.query.os_tag == .freebsd) continue;
629 if (options.skip_netbsd and case.target.query.os_tag == .netbsd) continue;
630 if (options.skip_windows and case.target.query.os_tag == .windows) continue;
631 if (options.skip_macos and case.target.query.os_tag == .macos) continue;
632 if (options.skip_linux and case.target.query.os_tag == .linux) continue;
633
634 const would_use_llvm = @import("../tests.zig").wouldUseLlvm(case.backend == .llvm, case.target.query, case.optimize_mode);
635 if (options.skip_llvm and would_use_llvm) continue;
612636
613637 const triple_txt = case.target.query.zigTriple(b.allocator) catch @panic("OOM");
614638
615 if (test_target_filters.len > 0) {
616 for (test_target_filters) |filter| {
639 if (options.test_target_filters.len > 0) {
640 for (options.test_target_filters) |filter| {
617641 if (std.mem.indexOf(u8, triple_txt, filter) != null) break;
618642 } else continue;
619643 }
620644
645 if (options.skip_libc and case.link_libc)
646 continue;
647
621648 const writefiles = b.addWriteFiles();
622649 var file_sources = std.StringHashMap(std.Build.LazyPath).init(b.allocator);
623650 defer file_sources.deinit();
test/src/check-stack-trace.zig+1-1
......@@ -84,5 +84,5 @@ pub fn main() !void {
8484 break :got_result try buf.toOwnedSlice();
8585 };
8686
87 try std.io.getStdOut().writeAll(got);
87 try std.fs.File.stdout().writeAll(got);
8888}
test/standalone/build.zig.zon-3
......@@ -48,9 +48,6 @@
4848 .pkg_import = .{
4949 .path = "pkg_import",
5050 },
51 .use_alias = .{
52 .path = "use_alias",
53 },
5451 .install_raw_hex = .{
5552 .path = "install_raw_hex",
5653 },
test/standalone/child_process/child.zig+4-3
......@@ -27,12 +27,12 @@ fn run(allocator: std.mem.Allocator) !void {
2727 }
2828
2929 // test stdout pipe; parent verifies
30 try std.io.getStdOut().writer().writeAll("hello from stdout");
30 try std.fs.File.stdout().writeAll("hello from stdout");
3131
3232 // test stdin pipe from parent
3333 const hello_stdin = "hello from stdin";
3434 var buf: [hello_stdin.len]u8 = undefined;
35 const stdin = std.io.getStdIn().reader();
35 const stdin: std.fs.File = .stdin();
3636 const n = try stdin.readAll(&buf);
3737 if (!std.mem.eql(u8, buf[0..n], hello_stdin)) {
3838 testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin });
......@@ -40,7 +40,8 @@ fn run(allocator: std.mem.Allocator) !void {
4040}
4141
4242fn testError(comptime fmt: []const u8, args: anytype) void {
43 const stderr = std.io.getStdErr().writer();
43 var stderr_writer = std.fs.File.stderr().writer(&.{});
44 const stderr = &stderr_writer.interface;
4445 stderr.print("CHILD TEST ERROR: ", .{}) catch {};
4546 stderr.print(fmt, args) catch {};
4647 if (fmt[fmt.len - 1] != '\n') {
test/standalone/child_process/main.zig+4-3
......@@ -19,13 +19,13 @@ pub fn main() !void {
1919 child.stderr_behavior = .Inherit;
2020 try child.spawn();
2121 const child_stdin = child.stdin.?;
22 try child_stdin.writer().writeAll("hello from stdin"); // verified in child
22 try child_stdin.writeAll("hello from stdin"); // verified in child
2323 child_stdin.close();
2424 child.stdin = null;
2525
2626 const hello_stdout = "hello from stdout";
2727 var buf: [hello_stdout.len]u8 = undefined;
28 const n = try child.stdout.?.reader().readAll(&buf);
28 const n = try child.stdout.?.deprecatedReader().readAll(&buf);
2929 if (!std.mem.eql(u8, buf[0..n], hello_stdout)) {
3030 testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout });
3131 }
......@@ -45,7 +45,8 @@ pub fn main() !void {
4545var parent_test_error = false;
4646
4747fn testError(comptime fmt: []const u8, args: anytype) void {
48 const stderr = std.io.getStdErr().writer();
48 var stderr_writer = std.fs.File.stderr().writer(&.{});
49 const stderr = &stderr_writer.interface;
4950 stderr.print("PARENT TEST ERROR: ", .{}) catch {};
5051 stderr.print(fmt, args) catch {};
5152 if (fmt[fmt.len - 1] != '\n') {
test/standalone/run_output_paths/create_file.zig+1-1
......@@ -10,7 +10,7 @@ pub fn main() !void {
1010 dir_name, .{});
1111 const file_name = args.next().?;
1212 const file = try dir.createFile(file_name, .{});
13 try file.writer().print(
13 try file.deprecatedWriter().print(
1414 \\{s}
1515 \\{s}
1616 \\Hello, world!
test/standalone/sigpipe/breakpipe.zig+1-1
......@@ -10,7 +10,7 @@ pub fn main() !void {
1010 std.posix.close(pipe[0]);
1111 _ = std.posix.write(pipe[1], "a") catch |err| switch (err) {
1212 error.BrokenPipe => {
13 try std.io.getStdOut().writer().writeAll("BrokenPipe\n");
13 try std.fs.File.stdout().writeAll("BrokenPipe\n");
1414 std.posix.exit(123);
1515 },
1616 else => |e| return e,
test/standalone/simple/brace_expansion.zig deleted-292
......@@ -1,292 +0,0 @@
1const std = @import("std");
2const io = std.io;
3const mem = std.mem;
4const debug = std.debug;
5const assert = debug.assert;
6const testing = std.testing;
7const ArrayList = std.ArrayList;
8const maxInt = std.math.maxInt;
9
10const Token = union(enum) {
11 Word: []const u8,
12 OpenBrace,
13 CloseBrace,
14 Comma,
15 Eof,
16};
17
18var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
19var global_allocator = gpa.allocator();
20
21fn tokenize(input: []const u8) !ArrayList(Token) {
22 const State = enum {
23 Start,
24 Word,
25 };
26
27 var token_list = ArrayList(Token).init(global_allocator);
28 errdefer token_list.deinit();
29 var tok_begin: usize = undefined;
30 var state = State.Start;
31
32 for (input, 0..) |b, i| {
33 switch (state) {
34 .Start => switch (b) {
35 'a'...'z', 'A'...'Z' => {
36 state = State.Word;
37 tok_begin = i;
38 },
39 '{' => try token_list.append(Token.OpenBrace),
40 '}' => try token_list.append(Token.CloseBrace),
41 ',' => try token_list.append(Token.Comma),
42 else => return error.InvalidInput,
43 },
44 .Word => switch (b) {
45 'a'...'z', 'A'...'Z' => {},
46 '{', '}', ',' => {
47 try token_list.append(Token{ .Word = input[tok_begin..i] });
48 switch (b) {
49 '{' => try token_list.append(Token.OpenBrace),
50 '}' => try token_list.append(Token.CloseBrace),
51 ',' => try token_list.append(Token.Comma),
52 else => unreachable,
53 }
54 state = State.Start;
55 },
56 else => return error.InvalidInput,
57 },
58 }
59 }
60 switch (state) {
61 State.Start => {},
62 State.Word => try token_list.append(Token{ .Word = input[tok_begin..] }),
63 }
64 try token_list.append(Token.Eof);
65 return token_list;
66}
67
68const Node = union(enum) {
69 Scalar: []const u8,
70 List: ArrayList(Node),
71 Combine: []Node,
72
73 fn deinit(self: Node) void {
74 switch (self) {
75 .Scalar => {},
76 .Combine => |pair| {
77 pair[0].deinit();
78 pair[1].deinit();
79 global_allocator.free(pair);
80 },
81 .List => |list| {
82 for (list.items) |item| {
83 item.deinit();
84 }
85 list.deinit();
86 },
87 }
88 }
89};
90
91const ParseError = error{
92 InvalidInput,
93 OutOfMemory,
94};
95
96fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node {
97 const first_token = tokens.items[token_index.*];
98 token_index.* += 1;
99
100 const result_node = switch (first_token) {
101 .Word => |word| Node{ .Scalar = word },
102 .OpenBrace => blk: {
103 var list = ArrayList(Node).init(global_allocator);
104 errdefer {
105 for (list.items) |node| node.deinit();
106 list.deinit();
107 }
108 while (true) {
109 try list.append(try parse(tokens, token_index));
110
111 const token = tokens.items[token_index.*];
112 token_index.* += 1;
113
114 switch (token) {
115 .CloseBrace => break,
116 .Comma => continue,
117 else => return error.InvalidInput,
118 }
119 }
120 break :blk Node{ .List = list };
121 },
122 else => return error.InvalidInput,
123 };
124
125 switch (tokens.items[token_index.*]) {
126 .Word, .OpenBrace => {
127 const pair = try global_allocator.alloc(Node, 2);
128 errdefer global_allocator.free(pair);
129 pair[0] = result_node;
130 pair[1] = try parse(tokens, token_index);
131 return Node{ .Combine = pair };
132 },
133 else => return result_node,
134 }
135}
136
137fn expandString(input: []const u8, output: *ArrayList(u8)) !void {
138 const tokens = try tokenize(input);
139 defer tokens.deinit();
140 if (tokens.items.len == 1) {
141 return output.resize(0);
142 }
143
144 var token_index: usize = 0;
145 const root = try parse(&tokens, &token_index);
146 defer root.deinit();
147 const last_token = tokens.items[token_index];
148 switch (last_token) {
149 Token.Eof => {},
150 else => return error.InvalidInput,
151 }
152
153 var result_list = ArrayList(ArrayList(u8)).init(global_allocator);
154 defer {
155 for (result_list.items) |*buf| buf.deinit();
156 result_list.deinit();
157 }
158
159 try expandNode(root, &result_list);
160
161 try output.resize(0);
162 for (result_list.items, 0..) |buf, i| {
163 if (i != 0) {
164 try output.append(' ');
165 }
166 try output.appendSlice(buf.items);
167 }
168}
169
170const ExpandNodeError = error{OutOfMemory};
171
172fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void {
173 assert(output.items.len == 0);
174 switch (node) {
175 .Scalar => |scalar| {
176 var list = ArrayList(u8).init(global_allocator);
177 errdefer list.deinit();
178 try list.appendSlice(scalar);
179 try output.append(list);
180 },
181 .Combine => |pair| {
182 const a_node = pair[0];
183 const b_node = pair[1];
184
185 var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator);
186 defer {
187 for (child_list_a.items) |*buf| buf.deinit();
188 child_list_a.deinit();
189 }
190 try expandNode(a_node, &child_list_a);
191
192 var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator);
193 defer {
194 for (child_list_b.items) |*buf| buf.deinit();
195 child_list_b.deinit();
196 }
197 try expandNode(b_node, &child_list_b);
198
199 for (child_list_a.items) |buf_a| {
200 for (child_list_b.items) |buf_b| {
201 var combined_buf = ArrayList(u8).init(global_allocator);
202 errdefer combined_buf.deinit();
203
204 try combined_buf.appendSlice(buf_a.items);
205 try combined_buf.appendSlice(buf_b.items);
206 try output.append(combined_buf);
207 }
208 }
209 },
210 .List => |list| {
211 for (list.items) |child_node| {
212 var child_list = ArrayList(ArrayList(u8)).init(global_allocator);
213 errdefer for (child_list.items) |*buf| buf.deinit();
214 defer child_list.deinit();
215
216 try expandNode(child_node, &child_list);
217
218 for (child_list.items) |buf| {
219 try output.append(buf);
220 }
221 }
222 },
223 }
224}
225
226pub fn main() !void {
227 defer _ = gpa.deinit();
228 const stdin_file = io.getStdIn();
229 const stdout_file = io.getStdOut();
230
231 const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize));
232 defer global_allocator.free(stdin);
233
234 var result_buf = ArrayList(u8).init(global_allocator);
235 defer result_buf.deinit();
236
237 try expandString(stdin, &result_buf);
238 try stdout_file.writeAll(result_buf.items);
239}
240
241test "invalid inputs" {
242 global_allocator = std.testing.allocator;
243
244 try expectError("}ABC", error.InvalidInput);
245 try expectError("{ABC", error.InvalidInput);
246 try expectError("}{", error.InvalidInput);
247 try expectError("{}", error.InvalidInput);
248 try expectError("A,B,C", error.InvalidInput);
249 try expectError("{A{B,C}", error.InvalidInput);
250 try expectError("{A,}", error.InvalidInput);
251
252 try expectError("\n", error.InvalidInput);
253}
254
255fn expectError(test_input: []const u8, expected_err: anyerror) !void {
256 var output_buf = ArrayList(u8).init(global_allocator);
257 defer output_buf.deinit();
258
259 try testing.expectError(expected_err, expandString(test_input, &output_buf));
260}
261
262test "valid inputs" {
263 global_allocator = std.testing.allocator;
264
265 try expectExpansion("{x,y,z}", "x y z");
266 try expectExpansion("{A,B}{x,y}", "Ax Ay Bx By");
267 try expectExpansion("{A,B{x,y}}", "A Bx By");
268
269 try expectExpansion("{ABC}", "ABC");
270 try expectExpansion("{A,B,C}", "A B C");
271 try expectExpansion("ABC", "ABC");
272
273 try expectExpansion("", "");
274 try expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh");
275 try expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh");
276 try expectExpansion("{A,B}a", "Aa Ba");
277 try expectExpansion("{C,{x,y}}", "C x y");
278 try expectExpansion("z{C,{x,y}}", "zC zx zy");
279 try expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg");
280 try expectExpansion("a{x,y}b", "axb ayb");
281 try expectExpansion("z{{a,b}}", "za zb");
282 try expectExpansion("a{b}", "ab");
283}
284
285fn expectExpansion(test_input: []const u8, expected_result: []const u8) !void {
286 var result = ArrayList(u8).init(global_allocator);
287 defer result.deinit();
288
289 expandString(test_input, &result) catch unreachable;
290
291 try testing.expectEqualSlices(u8, expected_result, result.items);
292}
test/standalone/simple/build.zig+8-4
......@@ -50,6 +50,10 @@ pub fn build(b: *std.Build) void {
5050 });
5151 if (case.link_libc) exe.root_module.link_libc = true;
5252
53 if (resolved_target.result.os.tag == .windows) {
54 exe.root_module.linkSystemLibrary("advapi32", .{});
55 }
56
5357 _ = exe.getEmittedBin();
5458
5559 step.dependOn(&exe.step);
......@@ -66,6 +70,10 @@ pub fn build(b: *std.Build) void {
6670 });
6771 if (case.link_libc) exe.root_module.link_libc = true;
6872
73 if (resolved_target.result.os.tag == .windows) {
74 exe.root_module.linkSystemLibrary("advapi32", .{});
75 }
76
6977 const run = b.addRunArtifact(exe);
7078 step.dependOn(&run.step);
7179 }
......@@ -101,10 +109,6 @@ const cases = [_]Case{
101109 //.{
102110 // .src_path = "issue_9693/main.zig",
103111 //},
104 .{
105 .src_path = "brace_expansion.zig",
106 .is_test = true,
107 },
108112 .{
109113 .src_path = "issue_7030.zig",
110114 .target = .{
test/standalone/simple/cat/main.zig+10-10
......@@ -1,42 +1,42 @@
11const std = @import("std");
22const io = std.io;
3const process = std.process;
43const fs = std.fs;
54const mem = std.mem;
65const warn = std.log.warn;
6const fatal = std.process.fatal;
77
88pub fn main() !void {
99 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1010 defer arena_instance.deinit();
1111 const arena = arena_instance.allocator();
1212
13 const args = try process.argsAlloc(arena);
13 const args = try std.process.argsAlloc(arena);
1414
1515 const exe = args[0];
1616 var catted_anything = false;
17 const stdout_file = io.getStdOut();
17 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
18 const stdout = &stdout_writer.interface;
19 var stdin_reader = std.fs.File.stdin().reader(&.{});
1820
1921 const cwd = fs.cwd();
2022
2123 for (args[1..]) |arg| {
2224 if (mem.eql(u8, arg, "-")) {
2325 catted_anything = true;
24 try stdout_file.writeFileAll(io.getStdIn(), .{});
26 _ = try stdout.sendFileAll(&stdin_reader, .unlimited);
2527 } else if (mem.startsWith(u8, arg, "-")) {
2628 return usage(exe);
2729 } else {
28 const file = cwd.openFile(arg, .{}) catch |err| {
29 warn("Unable to open file: {s}\n", .{@errorName(err)});
30 return err;
31 };
30 const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err});
3231 defer file.close();
3332
3433 catted_anything = true;
35 try stdout_file.writeFileAll(file, .{});
34 var file_reader = file.reader(&.{});
35 _ = try stdout.sendFileAll(&file_reader, .unlimited);
3636 }
3737 }
3838 if (!catted_anything) {
39 try stdout_file.writeFileAll(io.getStdIn(), .{});
39 _ = try stdout.sendFileAll(&stdin_reader, .unlimited);
4040 }
4141}
4242
test/standalone/simple/guess_number/main.zig+11-13
......@@ -1,37 +1,35 @@
11const builtin = @import("builtin");
22const std = @import("std");
3const io = std.io;
4const fmt = std.fmt;
53
64pub fn main() !void {
7 const stdout = io.getStdOut().writer();
8 const stdin = io.getStdIn();
5 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
6 const out = &stdout_writer.interface;
7 const stdin: std.fs.File = .stdin();
98
10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
9 try out.writeAll("Welcome to the Guess Number Game in Zig.\n");
1110
1211 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;
1312
1413 while (true) {
15 try stdout.print("\nGuess a number between 1 and 100: ", .{});
14 try out.writeAll("\nGuess a number between 1 and 100: ");
1615 var line_buf: [20]u8 = undefined;
17
1816 const amt = try stdin.read(&line_buf);
1917 if (amt == line_buf.len) {
20 try stdout.print("Input too long.\n", .{});
18 try out.writeAll("Input too long.\n");
2119 continue;
2220 }
2321 const line = std.mem.trimEnd(u8, line_buf[0..amt], "\r\n");
2422
25 const guess = fmt.parseUnsigned(u8, line, 10) catch {
26 try stdout.print("Invalid number.\n", .{});
23 const guess = std.fmt.parseUnsigned(u8, line, 10) catch {
24 try out.writeAll("Invalid number.\n");
2725 continue;
2826 };
2927 if (guess > answer) {
30 try stdout.print("Guess lower.\n", .{});
28 try out.writeAll("Guess lower.\n");
3129 } else if (guess < answer) {
32 try stdout.print("Guess higher.\n", .{});
30 try out.writeAll("Guess higher.\n");
3331 } else {
34 try stdout.print("You win!\n", .{});
32 try out.writeAll("You win!\n");
3533 return;
3634 }
3735 }
test/standalone/simple/std_enums_big_enums.zig+1
......@@ -6,6 +6,7 @@ pub fn main() void {
66 const Big = @Type(.{ .@"enum" = .{
77 .tag_type = u16,
88 .fields = make_fields: {
9 @setEvalBranchQuota(500000);
910 var fields: [1001]std.builtin.Type.EnumField = undefined;
1011 for (&fields, 0..) |*field, i| {
1112 field.* = .{ .name = std.fmt.comptimePrint("field_{d}", .{i}), .value = i };
test/standalone/stack_iterator/build.zig+3-3
......@@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void {
2929 .root_source_file = b.path("unwind.zig"),
3030 .target = target,
3131 .optimize = optimize,
32 .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null,
32 .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
3333 .omit_frame_pointer = false,
3434 }),
3535 });
......@@ -54,7 +54,7 @@ pub fn build(b: *std.Build) void {
5454 .root_source_file = b.path("unwind.zig"),
5555 .target = target,
5656 .optimize = optimize,
57 .unwind_tables = .@"async",
57 .unwind_tables = .async,
5858 .omit_frame_pointer = true,
5959 }),
6060 // self-hosted lacks omit_frame_pointer support
......@@ -101,7 +101,7 @@ pub fn build(b: *std.Build) void {
101101 .root_source_file = b.path("shared_lib_unwind.zig"),
102102 .target = target,
103103 .optimize = optimize,
104 .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null,
104 .unwind_tables = if (target.result.os.tag.isDarwin()) .async else null,
105105 .omit_frame_pointer = true,
106106 }),
107107 // zig objcopy doesn't support incremental binaries
test/standalone/use_alias/build.zig deleted-17
......@@ -1,17 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const optimize: std.builtin.OptimizeMode = .Debug;
8
9 const main = b.addTest(.{ .root_module = b.createModule(.{
10 .root_source_file = b.path("main.zig"),
11 .target = b.graph.host,
12 .optimize = optimize,
13 }) });
14 main.root_module.addIncludePath(b.path("."));
15
16 test_step.dependOn(&b.addRunArtifact(main).step);
17}
test/standalone/use_alias/c.zig deleted-1
......@@ -1 +0,0 @@
1pub usingnamespace @cImport(@cInclude("foo.h"));
test/standalone/use_alias/foo.h deleted-4
......@@ -1,4 +0,0 @@
1struct Foo {
2 int a;
3 int b;
4};
test/standalone/use_alias/main.zig deleted-11
......@@ -1,11 +0,0 @@
1const c = @import("c.zig");
2const expect = @import("std").testing.expect;
3
4test "symbol exists" {
5 var foo = c.Foo{
6 .a = 1,
7 .b = 1,
8 };
9 _ = &foo;
10 try expect(foo.a + foo.b == 2);
11}
test/standalone/windows_argv/build.zig+2
......@@ -47,6 +47,8 @@ pub fn build(b: *std.Build) !void {
4747 }),
4848 });
4949
50 fuzz.root_module.linkSystemLibrary("advapi32", .{});
51
5052 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;
5153 const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom");
5254
test/standalone/windows_argv/fuzz.zig+1-1
......@@ -58,7 +58,7 @@ pub fn main() !void {
5858 std.debug.print(">>> found discrepancy <<<\n", .{});
5959 const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w);
6060 defer allocator.free(cmd_line_wtf8);
61 std.debug.print("\"{}\"\n\n", .{std.zig.fmtEscapes(cmd_line_wtf8)});
61 std.debug.print("\"{f}\"\n\n", .{std.zig.fmtString(cmd_line_wtf8)});
6262
6363 errors += 1;
6464 }
test/standalone/windows_argv/lib.zig+6-6
......@@ -27,8 +27,8 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
2727 wtf8_buf.clearRetainingCapacity();
2828 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(expected_arg));
2929 if (!std.mem.eql(u8, wtf8_buf.items, arg_wtf8)) {
30 std.debug.print("{}: expected: \"{}\"\n", .{ i, std.zig.fmtEscapes(wtf8_buf.items) });
31 std.debug.print("{}: actual: \"{}\"\n", .{ i, std.zig.fmtEscapes(arg_wtf8) });
30 std.debug.print("{}: expected: \"{f}\"\n", .{ i, std.zig.fmtString(wtf8_buf.items) });
31 std.debug.print("{}: actual: \"{f}\"\n", .{ i, std.zig.fmtString(arg_wtf8) });
3232 eql = false;
3333 }
3434 }
......@@ -36,22 +36,22 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
3636 for (expected_args[min_len..], min_len..) |arg, i| {
3737 wtf8_buf.clearRetainingCapacity();
3838 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(arg));
39 std.debug.print("{}: expected: \"{}\"\n", .{ i, std.zig.fmtEscapes(wtf8_buf.items) });
39 std.debug.print("{}: expected: \"{f}\"\n", .{ i, std.zig.fmtString(wtf8_buf.items) });
4040 }
4141 for (args[min_len..], min_len..) |arg, i| {
42 std.debug.print("{}: actual: \"{}\"\n", .{ i, std.zig.fmtEscapes(arg) });
42 std.debug.print("{}: actual: \"{f}\"\n", .{ i, std.zig.fmtString(arg) });
4343 }
4444 const peb = std.os.windows.peb();
4545 const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer);
4646 wtf8_buf.clearRetainingCapacity();
4747 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(lpCmdLine));
48 std.debug.print("command line: \"{}\"\n", .{std.zig.fmtEscapes(wtf8_buf.items)});
48 std.debug.print("command line: \"{f}\"\n", .{std.zig.fmtString(wtf8_buf.items)});
4949 std.debug.print("expected argv:\n", .{});
5050 std.debug.print("&.{{\n", .{});
5151 for (expected_args) |arg| {
5252 wtf8_buf.clearRetainingCapacity();
5353 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(arg));
54 std.debug.print(" \"{}\",\n", .{std.zig.fmtEscapes(wtf8_buf.items)});
54 std.debug.print(" \"{f}\",\n", .{std.zig.fmtString(wtf8_buf.items)});
5555 }
5656 std.debug.print("}}\n", .{});
5757 return error.ArgvMismatch;
test/standalone/windows_bat_args/build.zig+4
......@@ -28,6 +28,8 @@ pub fn build(b: *std.Build) !void {
2828 }),
2929 });
3030
31 test_exe.root_module.linkSystemLibrary("advapi32", .{});
32
3133 const run = b.addRunArtifact(test_exe);
3234 run.addArtifactArg(echo_args);
3335 run.expectExitCode(0);
......@@ -44,6 +46,8 @@ pub fn build(b: *std.Build) !void {
4446 }),
4547 });
4648
49 fuzz.root_module.linkSystemLibrary("advapi32", .{});
50
4751 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;
4852 const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom");
4953
test/standalone/windows_bat_args/echo-args.zig+2-1
......@@ -5,7 +5,8 @@ pub fn main() !void {
55 defer arena_state.deinit();
66 const arena = arena_state.allocator();
77
8 const stdout = std.io.getStdOut().writer();
8 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
9 const stdout = &stdout_writer.interface;
910 var args = try std.process.argsAlloc(arena);
1011 for (args[1..], 1..) |arg, i| {
1112 try stdout.writeAll(arg);
test/standalone/windows_spawn/build.zig+2
......@@ -28,6 +28,8 @@ pub fn build(b: *std.Build) void {
2828 }),
2929 });
3030
31 main.root_module.linkSystemLibrary("advapi32", .{});
32
3133 const run = b.addRunArtifact(main);
3234 run.addArtifactArg(hello);
3335 run.expectExitCode(0);
test/standalone/windows_spawn/hello.zig+2-1
......@@ -1,6 +1,7 @@
11const std = @import("std");
22
33pub fn main() !void {
4 const stdout = std.io.getStdOut().writer();
4 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
5 const stdout = &stdout_writer.interface;
56 try stdout.writeAll("hello from exe\n");
67}
test/tests.zig+83-39
......@@ -918,14 +918,16 @@ const test_targets = blk: {
918918 .link_libc = true,
919919 },
920920
921 .{
922 .target = std.Target.Query.parse(.{
923 .arch_os_abi = "riscv64-linux-none",
924 .cpu_features = "baseline+v+zbb",
925 }) catch unreachable,
926 .use_llvm = false,
927 .use_lld = false,
928 },
921 // TODO implement codegen airFieldParentPtr
922 // TODO implement airMemmove for riscv64
923 //.{
924 // .target = std.Target.Query.parse(.{
925 // .arch_os_abi = "riscv64-linux-none",
926 // .cpu_features = "baseline+v+zbb",
927 // }) catch unreachable,
928 // .use_llvm = false,
929 // .use_lld = false,
930 //},
929931 .{
930932 .target = .{
931933 .cpu_arch = .riscv64,
......@@ -1480,16 +1482,8 @@ const test_targets = blk: {
14801482 .target = .{
14811483 .cpu_arch = .aarch64,
14821484 .os_tag = .windows,
1483 .abi = .none,
1484 },
1485 },
1486 .{
1487 .target = .{
1488 .cpu_arch = .aarch64,
1489 .os_tag = .windows,
1490 .abi = .gnu,
1485 .abi = .msvc,
14911486 },
1492 .link_libc = true,
14931487 },
14941488 .{
14951489 .target = .{
......@@ -1499,37 +1493,36 @@ const test_targets = blk: {
14991493 },
15001494 .link_libc = true,
15011495 },
1502
15031496 .{
15041497 .target = .{
1505 .cpu_arch = .x86,
1498 .cpu_arch = .aarch64,
15061499 .os_tag = .windows,
1507 .abi = .none,
1500 .abi = .gnu,
15081501 },
15091502 },
15101503 .{
15111504 .target = .{
1512 .cpu_arch = .x86,
1505 .cpu_arch = .aarch64,
15131506 .os_tag = .windows,
15141507 .abi = .gnu,
15151508 },
15161509 .link_libc = true,
15171510 },
1511
15181512 .{
15191513 .target = .{
1520 .cpu_arch = .x86,
1514 .cpu_arch = .thumb,
15211515 .os_tag = .windows,
15221516 .abi = .msvc,
15231517 },
1524 .link_libc = true,
15251518 },
1526
15271519 .{
15281520 .target = .{
15291521 .cpu_arch = .thumb,
15301522 .os_tag = .windows,
1531 .abi = .none,
1523 .abi = .msvc,
15321524 },
1525 .link_libc = true,
15331526 },
15341527 // https://github.com/ziglang/zig/issues/24016
15351528 // .{
......@@ -1538,22 +1531,52 @@ const test_targets = blk: {
15381531 // .os_tag = .windows,
15391532 // .abi = .gnu,
15401533 // },
1534 // },
1535 // .{
1536 // .target = .{
1537 // .cpu_arch = .thumb,
1538 // .os_tag = .windows,
1539 // .abi = .gnu,
1540 // },
15411541 // .link_libc = true,
15421542 // },
1543
15431544 .{
15441545 .target = .{
1545 .cpu_arch = .thumb,
1546 .cpu_arch = .x86,
1547 .os_tag = .windows,
1548 .abi = .msvc,
1549 },
1550 },
1551 .{
1552 .target = .{
1553 .cpu_arch = .x86,
15461554 .os_tag = .windows,
15471555 .abi = .msvc,
15481556 },
15491557 .link_libc = true,
15501558 },
1559 .{
1560 .target = .{
1561 .cpu_arch = .x86,
1562 .os_tag = .windows,
1563 .abi = .gnu,
1564 },
1565 },
1566 .{
1567 .target = .{
1568 .cpu_arch = .x86,
1569 .os_tag = .windows,
1570 .abi = .gnu,
1571 },
1572 .link_libc = true,
1573 },
15511574
15521575 .{
15531576 .target = .{
15541577 .cpu_arch = .x86_64,
15551578 .os_tag = .windows,
1556 .abi = .none,
1579 .abi = .msvc,
15571580 },
15581581 .use_llvm = false,
15591582 .use_lld = false,
......@@ -1562,17 +1585,16 @@ const test_targets = blk: {
15621585 .target = .{
15631586 .cpu_arch = .x86_64,
15641587 .os_tag = .windows,
1565 .abi = .gnu,
1588 .abi = .msvc,
15661589 },
1567 .use_llvm = false,
1568 .use_lld = false,
15691590 },
15701591 .{
15711592 .target = .{
15721593 .cpu_arch = .x86_64,
15731594 .os_tag = .windows,
1574 .abi = .none,
1595 .abi = .msvc,
15751596 },
1597 .link_libc = true,
15761598 },
15771599 .{
15781600 .target = .{
......@@ -1580,13 +1602,21 @@ const test_targets = blk: {
15801602 .os_tag = .windows,
15811603 .abi = .gnu,
15821604 },
1583 .link_libc = true,
1605 .use_llvm = false,
1606 .use_lld = false,
15841607 },
15851608 .{
15861609 .target = .{
15871610 .cpu_arch = .x86_64,
15881611 .os_tag = .windows,
1589 .abi = .msvc,
1612 .abi = .gnu,
1613 },
1614 },
1615 .{
1616 .target = .{
1617 .cpu_arch = .x86_64,
1618 .os_tag = .windows,
1619 .abi = .gnu,
15901620 },
15911621 .link_libc = true,
15921622 },
......@@ -2280,6 +2310,7 @@ const ModuleTestOptions = struct {
22802310 desc: []const u8,
22812311 optimize_modes: []const OptimizeMode,
22822312 include_paths: []const []const u8,
2313 windows_libs: []const []const u8,
22832314 skip_single_threaded: bool,
22842315 skip_non_native: bool,
22852316 skip_freebsd: bool,
......@@ -2409,6 +2440,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
24092440
24102441 for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path));
24112442
2443 if (target.os.tag == .windows) {
2444 for (options.windows_libs) |lib| these_tests.linkSystemLibrary(lib);
2445 }
2446
24122447 const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{
24132448 options.name,
24142449 triple_txt,
......@@ -2517,7 +2552,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
25172552 return step;
25182553}
25192554
2520fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool {
2555pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool {
25212556 if (use_llvm) |x| return x;
25222557 if (query.ofmt == .c) return false;
25232558 switch (optimize_mode) {
......@@ -2629,9 +2664,8 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step {
26292664pub fn addCases(
26302665 b: *std.Build,
26312666 parent_step: *Step,
2632 test_filters: []const []const u8,
2633 test_target_filters: []const []const u8,
26342667 target: std.Build.ResolvedTarget,
2668 case_test_options: @import("src/Cases.zig").CaseTestOptions,
26352669 translate_c_options: @import("src/Cases.zig").TranslateCOptions,
26362670 build_options: @import("cases.zig").BuildOptions,
26372671) !void {
......@@ -2646,13 +2680,19 @@ pub fn addCases(
26462680 cases.addFromDir(dir, b);
26472681 try @import("cases.zig").addCases(&cases, build_options, b);
26482682
2649 cases.lowerToTranslateCSteps(b, parent_step, test_filters, test_target_filters, target, translate_c_options);
2683 cases.lowerToTranslateCSteps(
2684 b,
2685 parent_step,
2686 case_test_options.test_filters,
2687 case_test_options.test_target_filters,
2688 target,
2689 translate_c_options,
2690 );
26502691
26512692 cases.lowerToBuildSteps(
26522693 b,
26532694 parent_step,
2654 test_filters,
2655 test_target_filters,
2695 case_test_options,
26562696 );
26572697}
26582698
......@@ -2699,6 +2739,10 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void {
26992739 }),
27002740 });
27012741
2742 if (b.graph.host.result.os.tag == .windows) {
2743 incr_check.root_module.linkSystemLibrary("advapi32", .{});
2744 }
2745
27022746 var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true });
27032747 defer dir.close();
27042748
tools/docgen.zig+4-8
......@@ -43,8 +43,7 @@ pub fn main() !void {
4343 while (args_it.next()) |arg| {
4444 if (mem.startsWith(u8, arg, "-")) {
4545 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
46 const stdout = io.getStdOut().writer();
47 try stdout.writeAll(usage);
46 try fs.File.stdout().writeAll(usage);
4847 process.exit(0);
4948 } else if (mem.eql(u8, arg, "--code-dir")) {
5049 if (args_it.next()) |param| {
......@@ -76,9 +75,9 @@ pub fn main() !void {
7675 var code_dir = try fs.cwd().openDir(code_dir_path, .{});
7776 defer code_dir.close();
7877
79 const input_file_bytes = try in_file.reader().readAllAlloc(arena, max_doc_file_size);
78 const input_file_bytes = try in_file.deprecatedReader().readAllAlloc(arena, max_doc_file_size);
8079
81 var buffered_writer = io.bufferedWriter(out_file.writer());
80 var buffered_writer = io.bufferedWriter(out_file.deprecatedWriter());
8281
8382 var tokenizer = Tokenizer.init(input_path, input_file_bytes);
8483 var toc = try genToc(arena, &tokenizer);
......@@ -426,7 +425,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
426425 try toc.writeByte('\n');
427426 try toc.writeByteNTimes(' ', header_stack_size * 4);
428427 if (last_columns) |n| {
429 try toc.print("<ul style=\"columns: {}\">\n", .{n});
428 try toc.print("<ul style=\"columns: {d}\">\n", .{n});
430429 } else {
431430 try toc.writeAll("<ul>\n");
432431 }
......@@ -710,8 +709,6 @@ fn tokenizeAndPrintRaw(
710709 .keyword_align,
711710 .keyword_and,
712711 .keyword_asm,
713 .keyword_async,
714 .keyword_await,
715712 .keyword_break,
716713 .keyword_catch,
717714 .keyword_comptime,
......@@ -748,7 +745,6 @@ fn tokenizeAndPrintRaw(
748745 .keyword_try,
749746 .keyword_union,
750747 .keyword_unreachable,
751 .keyword_usingnamespace,
752748 .keyword_var,
753749 .keyword_volatile,
754750 .keyword_allowzero,
tools/doctest.zig+2-5
......@@ -44,7 +44,7 @@ pub fn main() !void {
4444 while (args_it.next()) |arg| {
4545 if (mem.startsWith(u8, arg, "-")) {
4646 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
47 try std.io.getStdOut().writeAll(usage);
47 try std.fs.File.stdout().writeAll(usage);
4848 process.exit(0);
4949 } else if (mem.eql(u8, arg, "-i")) {
5050 opt_input = args_it.next() orelse fatal("expected parameter after -i", .{});
......@@ -85,7 +85,7 @@ pub fn main() !void {
8585 var out_file = try fs.cwd().createFile(output_path, .{});
8686 defer out_file.close();
8787
88 var bw = std.io.bufferedWriter(out_file.writer());
88 var bw = std.io.bufferedWriter(out_file.deprecatedWriter());
8989 const out = bw.writer();
9090
9191 try printSourceBlock(arena, out, source, fs.path.basename(input_path));
......@@ -653,8 +653,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {
653653 .keyword_align,
654654 .keyword_and,
655655 .keyword_asm,
656 .keyword_async,
657 .keyword_await,
658656 .keyword_break,
659657 .keyword_catch,
660658 .keyword_comptime,
......@@ -691,7 +689,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {
691689 .keyword_try,
692690 .keyword_union,
693691 .keyword_unreachable,
694 .keyword_usingnamespace,
695692 .keyword_var,
696693 .keyword_volatile,
697694 .keyword_allowzero,
tools/dump-cov.zig+4-3
......@@ -48,8 +48,9 @@ pub fn main() !void {
4848 fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });
4949 };
5050
51 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
52 const stdout = bw.writer();
51 var stdout_buffer: [4000]u8 = undefined;
52 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
53 const stdout = &stdout_writer.interface;
5354
5455 const header: *SeenPcsHeader = @ptrCast(cov_bytes);
5556 try stdout.print("{any}\n", .{header.*});
......@@ -83,5 +84,5 @@ pub fn main() !void {
8384 });
8485 }
8586
86 try bw.flush();
87 try stdout.flush();
8788}
tools/fetch_them_macos_headers.zig+2-13
......@@ -5,6 +5,8 @@ const mem = std.mem;
55const process = std.process;
66const assert = std.debug.assert;
77const tmpDir = std.testing.tmpDir;
8const fatal = std.process.fatal;
9const info = std.log.info;
810
911const Allocator = mem.Allocator;
1012const OsTag = std.Target.Os.Tag;
......@@ -245,19 +247,6 @@ const ArgsIterator = struct {
245247 }
246248};
247249
248fn info(comptime format: []const u8, args: anytype) void {
249 const msg = std.fmt.allocPrint(gpa, "info: " ++ format ++ "\n", args) catch return;
250 std.io.getStdOut().writeAll(msg) catch {};
251}
252
253fn fatal(comptime format: []const u8, args: anytype) noreturn {
254 ret: {
255 const msg = std.fmt.allocPrint(gpa, "fatal: " ++ format ++ "\n", args) catch break :ret;
256 std.io.getStdErr().writeAll(msg) catch {};
257 }
258 std.process.exit(1);
259}
260
261250const Version = struct {
262251 major: u16,
263252 minor: u8,
tools/gen_macos_headers_c.zig+9-17
......@@ -1,5 +1,7 @@
11const std = @import("std");
22const assert = std.debug.assert;
3const info = std.log.info;
4const fatal = std.process.fatal;
35
46const Allocator = std.mem.Allocator;
57
......@@ -13,19 +15,6 @@ const usage =
1315 \\-h, --help Print this help and exit
1416;
1517
16fn info(comptime format: []const u8, args: anytype) void {
17 const msg = std.fmt.allocPrint(gpa, "info: " ++ format ++ "\n", args) catch return;
18 std.io.getStdOut().writeAll(msg) catch {};
19}
20
21fn fatal(comptime format: []const u8, args: anytype) noreturn {
22 ret: {
23 const msg = std.fmt.allocPrint(gpa, "fatal: " ++ format ++ "\n", args) catch break :ret;
24 std.io.getStdErr().writeAll(msg) catch {};
25 }
26 std.process.exit(1);
27}
28
2918pub fn main() anyerror!void {
3019 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
3120 defer arena_allocator.deinit();
......@@ -58,16 +47,19 @@ pub fn main() anyerror!void {
5847
5948 std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan);
6049
61 const stdout = std.io.getStdOut().writer();
62 try stdout.writeAll("#define _XOPEN_SOURCE\n");
50 var buffer: [2000]u8 = undefined;
51 var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer);
52 const w = &stdout_writer.interface;
53 try w.writeAll("#define _XOPEN_SOURCE\n");
6354 for (paths.items) |path| {
64 try stdout.print("#include <{s}>\n", .{path});
55 try w.print("#include <{s}>\n", .{path});
6556 }
66 try stdout.writeAll(
57 try w.writeAll(
6758 \\int main(int argc, char **argv) {
6859 \\ return 0;
6960 \\}
7061 );
62 try w.flush();
7163}
7264
7365fn findHeaders(
tools/gen_outline_atomics.zig+4-3
......@@ -17,8 +17,9 @@ pub fn main() !void {
1717
1818 //const args = try std.process.argsAlloc(arena);
1919
20 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
21 const w = bw.writer();
20 var stdout_buffer: [2000]u8 = undefined;
21 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
22 const w = &stdout_writer.interface;
2223
2324 try w.writeAll(
2425 \\//! This file is generated by tools/gen_outline_atomics.zig.
......@@ -57,7 +58,7 @@ pub fn main() !void {
5758
5859 try w.writeAll(footer.items);
5960 try w.writeAll("}\n");
60 try bw.flush();
61 try w.flush();
6162}
6263
6364fn writeFunction(
tools/gen_spirv_spec.zig+9-12
......@@ -91,9 +91,10 @@ pub fn main() !void {
9191
9292 try readExtRegistry(&exts, a, std.fs.cwd(), args[2]);
9393
94 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
95 try render(bw.writer(), a, core_spec, exts.items);
96 try bw.flush();
94 var buffer: [4000]u8 = undefined;
95 var w = std.fs.File.stdout().writerStreaming(&buffer);
96 try render(&w, a, core_spec, exts.items);
97 try w.flush();
9798}
9899
99100fn readExtRegistry(exts: *std.ArrayList(Extension), a: Allocator, dir: std.fs.Dir, sub_path: []const u8) !void {
......@@ -166,7 +167,7 @@ fn tagPriorityScore(tag: []const u8) usize {
166167 }
167168}
168169
169fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void {
170fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void {
170171 try writer.writeAll(
171172 \\//! This file is auto-generated by tools/gen_spirv_spec.zig.
172173 \\
......@@ -188,15 +189,10 @@ fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []c
188189 \\ none,
189190 \\ _,
190191 \\
191 \\ pub fn format(
192 \\ self: IdResult,
193 \\ comptime _: []const u8,
194 \\ _: std.fmt.FormatOptions,
195 \\ writer: anytype,
196 \\ ) @TypeOf(writer).Error!void {
192 \\ pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void {
197193 \\ switch (self) {
198194 \\ .none => try writer.writeAll("(none)"),
199 \\ else => try writer.print("%{}", .{@intFromEnum(self)}),
195 \\ else => try writer.print("%{d}", .{@intFromEnum(self)}),
200196 \\ }
201197 \\ }
202198 \\};
......@@ -899,7 +895,8 @@ fn parseHexInt(text: []const u8) !u31 {
899895}
900896
901897fn usageAndExit(arg0: []const u8, code: u8) noreturn {
902 std.io.getStdErr().writer().print(
898 const stderr = std.debug.lockStderrWriter(&.{});
899 stderr.print(
903900 \\Usage: {s} <SPIRV-Headers repository path> <path/to/zig/src/codegen/spirv/extinst.zig.grammar.json>
904901 \\
905902 \\Generates Zig bindings for SPIR-V specifications found in the SPIRV-Headers
tools/gen_stubs.zig+5-1
......@@ -333,7 +333,9 @@ pub fn main() !void {
333333 }
334334 }
335335
336 const stdout = std.io.getStdOut().writer();
336 var stdout_buffer: [2000]u8 = undefined;
337 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
338 const stdout = &stdout_writer.interface;
337339 try stdout.writeAll(
338340 \\#ifdef PTR64
339341 \\#define WEAK64 .weak
......@@ -533,6 +535,8 @@ pub fn main() !void {
533535 .all => {},
534536 .single, .multi, .family, .time32 => try stdout.writeAll("#endif\n"),
535537 }
538
539 try stdout.flush();
536540}
537541
538542fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: builtin.Endian) !void {
tools/generate_JSONTestSuite.zig+5-1
......@@ -6,7 +6,9 @@ pub fn main() !void {
66 var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
77 var allocator = gpa.allocator();
88
9 var output = std.io.getStdOut().writer();
9 var stdout_buffer: [2000]u8 = undefined;
10 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
11 const output = &stdout_writer.interface;
1012 try output.writeAll(
1113 \\// This file was generated by _generate_JSONTestSuite.zig
1214 \\// These test cases are sourced from: https://github.com/nst/JSONTestSuite
......@@ -44,6 +46,8 @@ pub fn main() !void {
4446 try writeString(output, contents);
4547 try output.writeAll(");\n}\n");
4648 }
49
50 try output.flush();
4751}
4852
4953const i_structure_500_nested_arrays = "[" ** 500 ++ "]" ** 500;
tools/generate_c_size_and_align_checks.zig+7-4
......@@ -42,20 +42,23 @@ pub fn main() !void {
4242 const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] });
4343 const target = try std.zig.system.resolveTargetQuery(query);
4444
45 const stdout = std.io.getStdOut().writer();
45 var buffer: [2000]u8 = undefined;
46 var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer);
47 const w = &stdout_writer.interface;
4648 inline for (@typeInfo(std.Target.CType).@"enum".fields) |field| {
4749 const c_type: std.Target.CType = @enumFromInt(field.value);
48 try stdout.print("_Static_assert(sizeof({0s}) == {1d}, \"sizeof({0s}) == {1d}\");\n", .{
50 try w.print("_Static_assert(sizeof({0s}) == {1d}, \"sizeof({0s}) == {1d}\");\n", .{
4951 cName(c_type),
5052 target.cTypeByteSize(c_type),
5153 });
52 try stdout.print("_Static_assert(_Alignof({0s}) == {1d}, \"_Alignof({0s}) == {1d}\");\n", .{
54 try w.print("_Static_assert(_Alignof({0s}) == {1d}, \"_Alignof({0s}) == {1d}\");\n", .{
5355 cName(c_type),
5456 target.cTypeAlignment(c_type),
5557 });
56 try stdout.print("_Static_assert(__alignof({0s}) == {1d}, \"__alignof({0s}) == {1d}\");\n\n", .{
58 try w.print("_Static_assert(__alignof({0s}) == {1d}, \"__alignof({0s}) == {1d}\");\n\n", .{
5759 cName(c_type),
5860 target.cTypePreferredAlignment(c_type),
5961 });
6062 }
63 try w.flush();
6164}
tools/generate_linux_syscalls.zig+11-9
......@@ -666,13 +666,16 @@ pub fn main() !void {
666666 const allocator = arena.allocator();
667667
668668 const args = try std.process.argsAlloc(allocator);
669 if (args.len < 3 or mem.eql(u8, args[1], "--help"))
670 usageAndExit(std.io.getStdErr(), args[0], 1);
669 if (args.len < 3 or mem.eql(u8, args[1], "--help")) {
670 usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2);
671 std.process.exit(1);
672 }
671673 const zig_exe = args[1];
672674 const linux_path = args[2];
673675
674 var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer());
675 const writer = buf_out.writer();
676 var stdout_buffer: [2000]u8 = undefined;
677 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
678 const writer = &stdout_writer.interface;
676679
677680 var linux_dir = try std.fs.cwd().openDir(linux_path, .{});
678681 defer linux_dir.close();
......@@ -714,17 +717,16 @@ pub fn main() !void {
714717 }
715718 }
716719
717 try buf_out.flush();
720 try writer.flush();
718721}
719722
720fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
721 file.writer().print(
723fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {
724 try w.print(
722725 \\Usage: {s} /path/to/zig /path/to/linux
723726 \\Alternative Usage: zig run /path/to/git/zig/tools/generate_linux_syscalls.zig -- /path/to/zig /path/to/linux
724727 \\
725728 \\Generates the list of Linux syscalls for each supported cpu arch, using the Linux development tree.
726729 \\Prints to stdout Zig code which you can use to replace the file lib/std/os/linux/syscalls.zig.
727730 \\
728 , .{arg0}) catch std.process.exit(1);
729 std.process.exit(code);
731 , .{arg0});
730732}
tools/lldb_pretty_printers.py-3
......@@ -50,8 +50,6 @@ zig_keywords = {
5050 'anyframe',
5151 'anytype',
5252 'asm',
53 'async',
54 'await',
5553 'break',
5654 'callconv',
5755 'catch',
......@@ -88,7 +86,6 @@ zig_keywords = {
8886 'try',
8987 'union',
9088 'unreachable',
91 'usingnamespace',
9289 'var',
9390 'volatile',
9491 'while',
tools/update_clang_options.zig+22-20
......@@ -634,25 +634,25 @@ pub fn main() anyerror!void {
634634 const allocator = arena.allocator();
635635 const args = try std.process.argsAlloc(allocator);
636636
637 if (args.len <= 1) {
638 usageAndExit(std.io.getStdErr(), args[0], 1);
639 }
637 var stdout_buffer: [4000]u8 = undefined;
638 var stdout_writer = fs.stdout().writerStreaming(&stdout_buffer);
639 const stdout = &stdout_writer.interface;
640
641 if (args.len <= 1) printUsageAndExit(args[0]);
642
640643 if (std.mem.eql(u8, args[1], "--help")) {
641 usageAndExit(std.io.getStdOut(), args[0], 0);
642 }
643 if (args.len < 3) {
644 usageAndExit(std.io.getStdErr(), args[0], 1);
644 printUsage(stdout, args[0]) catch std.process.exit(2);
645 stdout.flush() catch std.process.exit(2);
646 std.process.exit(0);
645647 }
646648
649 if (args.len < 3) printUsageAndExit(args[0]);
650
647651 const llvm_tblgen_exe = args[1];
648 if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) {
649 usageAndExit(std.io.getStdErr(), args[0], 1);
650 }
652 if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) printUsageAndExit(args[0]);
651653
652654 const llvm_src_root = args[2];
653 if (std.mem.startsWith(u8, llvm_src_root, "-")) {
654 usageAndExit(std.io.getStdErr(), args[0], 1);
655 }
655 if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]);
656656
657657 var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator);
658658
......@@ -719,8 +719,6 @@ pub fn main() anyerror!void {
719719 // "W" and "Wl,". So we sort this list in order of descending priority.
720720 std.mem.sort(*json.ObjectMap, all_objects.items, {}, objectLessThan);
721721
722 var buffered_stdout = std.io.bufferedWriter(std.io.getStdOut().writer());
723 const stdout = buffered_stdout.writer();
724722 try stdout.writeAll(
725723 \\// This file is generated by tools/update_clang_options.zig.
726724 \\// zig fmt: off
......@@ -815,7 +813,7 @@ pub fn main() anyerror!void {
815813 \\
816814 );
817815
818 try buffered_stdout.flush();
816 try stdout.flush();
819817}
820818
821819// TODO we should be able to import clang_options.zig but currently this is problematic because it will
......@@ -966,13 +964,17 @@ fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool {
966964 return std.mem.lessThan(u8, a_key, b_key);
967965}
968966
969fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
970 file.writer().print(
967fn printUsageAndExit(arg0: []const u8) noreturn {
968 printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2);
969 std.process.exit(1);
970}
971
972fn printUsage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {
973 try w.print(
971974 \\Usage: {s} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
972975 \\Alternative Usage: zig run /path/to/git/zig/tools/update_clang_options.zig -- /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
973976 \\
974977 \\Prints to stdout Zig code which you can use to replace the file src/clang_options_data.zig.
975978 \\
976 , .{arg0}) catch std.process.exit(1);
977 std.process.exit(code);
979 , .{arg0});
978980}
tools/update_cpu_features.zig+2-2
......@@ -2082,8 +2082,8 @@ fn processOneTarget(job: Job) void {
20822082}
20832083
20842084fn usageAndExit(arg0: []const u8, code: u8) noreturn {
2085 const stderr = std.io.getStdErr();
2086 stderr.writer().print(
2085 const stderr = std.debug.lockStderrWriter(&.{});
2086 stderr.print(
20872087 \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig [zig_name filter]
20882088 \\
20892089 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .
tools/update_crc_catalog.zig+10-10
......@@ -11,14 +11,10 @@ pub fn main() anyerror!void {
1111 const arena = arena_state.allocator();
1212
1313 const args = try std.process.argsAlloc(arena);
14 if (args.len <= 1) {
15 usageAndExit(std.io.getStdErr(), args[0], 1);
16 }
14 if (args.len <= 1) printUsageAndExit(args[0]);
1715
1816 const zig_src_root = args[1];
19 if (mem.startsWith(u8, zig_src_root, "-")) {
20 usageAndExit(std.io.getStdErr(), args[0], 1);
21 }
17 if (mem.startsWith(u8, zig_src_root, "-")) printUsageAndExit(args[0]);
2218
2319 var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{});
2420 defer zig_src_dir.close();
......@@ -193,10 +189,14 @@ pub fn main() anyerror!void {
193189 }
194190}
195191
196fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
197 file.writer().print(
192fn printUsageAndExit(arg0: []const u8) noreturn {
193 printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2);
194 std.process.exit(1);
195}
196
197fn printUsage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {
198 return w.print(
198199 \\Usage: {s} /path/git/zig
199200 \\
200 , .{arg0}) catch std.process.exit(1);
201 std.process.exit(code);
201 , .{arg0});
202202}