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...@@ -537,45 +537,6 @@ set(ZIG_STAGE2_SOURCES
537 src/Value.zig537 src/Value.zig
538 src/Zcu.zig538 src/Zcu.zig
539 src/Zcu/PerThread.zig539 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
579 src/clang.zig540 src/clang.zig
580 src/clang_options.zig541 src/clang_options.zig
581 src/clang_options_data.zig542 src/clang_options_data.zig
build.zig+34-3
...@@ -415,7 +415,18 @@ pub fn build(b: *std.Build) !void {...@@ -415,7 +415,18 @@ pub fn build(b: *std.Build) !void {
415 test_step.dependOn(check_fmt);415 test_step.dependOn(check_fmt);
416416
417 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");417 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 }, .{
419 .skip_translate_c = skip_translate_c,430 .skip_translate_c = skip_translate_c,
420 .skip_run_translated_c = skip_run_translated_c,431 .skip_run_translated_c = skip_run_translated_c,
421 }, .{432 }, .{
...@@ -439,6 +450,7 @@ pub fn build(b: *std.Build) !void {...@@ -439,6 +450,7 @@ pub fn build(b: *std.Build) !void {
439 .desc = "Run the behavior tests",450 .desc = "Run the behavior tests",
440 .optimize_modes = optimization_modes,451 .optimize_modes = optimization_modes,
441 .include_paths = &.{},452 .include_paths = &.{},
453 .windows_libs = &.{},
442 .skip_single_threaded = skip_single_threaded,454 .skip_single_threaded = skip_single_threaded,
443 .skip_non_native = skip_non_native,455 .skip_non_native = skip_non_native,
444 .skip_freebsd = skip_freebsd,456 .skip_freebsd = skip_freebsd,
...@@ -448,8 +460,8 @@ pub fn build(b: *std.Build) !void {...@@ -448,8 +460,8 @@ pub fn build(b: *std.Build) !void {
448 .skip_linux = skip_linux,460 .skip_linux = skip_linux,
449 .skip_llvm = skip_llvm,461 .skip_llvm = skip_llvm,
450 .skip_libc = skip_libc,462 .skip_libc = skip_libc,
451 // 2923515904 was observed on an x86_64-linux-gnu host.463 // 3888779264 was observed on an x86_64-linux-gnu host.
452 .max_rss = 3100000000,464 .max_rss = 4000000000,
453 }));465 }));
454466
455 test_modules_step.dependOn(tests.addModuleTests(b, .{467 test_modules_step.dependOn(tests.addModuleTests(b, .{
...@@ -461,6 +473,7 @@ pub fn build(b: *std.Build) !void {...@@ -461,6 +473,7 @@ pub fn build(b: *std.Build) !void {
461 .desc = "Run the @cImport tests",473 .desc = "Run the @cImport tests",
462 .optimize_modes = optimization_modes,474 .optimize_modes = optimization_modes,
463 .include_paths = &.{"test/c_import"},475 .include_paths = &.{"test/c_import"},
476 .windows_libs = &.{},
464 .skip_single_threaded = true,477 .skip_single_threaded = true,
465 .skip_non_native = skip_non_native,478 .skip_non_native = skip_non_native,
466 .skip_freebsd = skip_freebsd,479 .skip_freebsd = skip_freebsd,
...@@ -481,6 +494,7 @@ pub fn build(b: *std.Build) !void {...@@ -481,6 +494,7 @@ pub fn build(b: *std.Build) !void {
481 .desc = "Run the compiler_rt tests",494 .desc = "Run the compiler_rt tests",
482 .optimize_modes = optimization_modes,495 .optimize_modes = optimization_modes,
483 .include_paths = &.{},496 .include_paths = &.{},
497 .windows_libs = &.{},
484 .skip_single_threaded = true,498 .skip_single_threaded = true,
485 .skip_non_native = skip_non_native,499 .skip_non_native = skip_non_native,
486 .skip_freebsd = skip_freebsd,500 .skip_freebsd = skip_freebsd,
...@@ -502,6 +516,7 @@ pub fn build(b: *std.Build) !void {...@@ -502,6 +516,7 @@ pub fn build(b: *std.Build) !void {
502 .desc = "Run the zigc tests",516 .desc = "Run the zigc tests",
503 .optimize_modes = optimization_modes,517 .optimize_modes = optimization_modes,
504 .include_paths = &.{},518 .include_paths = &.{},
519 .windows_libs = &.{},
505 .skip_single_threaded = true,520 .skip_single_threaded = true,
506 .skip_non_native = skip_non_native,521 .skip_non_native = skip_non_native,
507 .skip_freebsd = skip_freebsd,522 .skip_freebsd = skip_freebsd,
...@@ -523,6 +538,12 @@ pub fn build(b: *std.Build) !void {...@@ -523,6 +538,12 @@ pub fn build(b: *std.Build) !void {
523 .desc = "Run the standard library tests",538 .desc = "Run the standard library tests",
524 .optimize_modes = optimization_modes,539 .optimize_modes = optimization_modes,
525 .include_paths = &.{},540 .include_paths = &.{},
541 .windows_libs = &.{
542 "advapi32",
543 "crypt32",
544 "iphlpapi",
545 "ws2_32",
546 },
526 .skip_single_threaded = skip_single_threaded,547 .skip_single_threaded = skip_single_threaded,
527 .skip_non_native = skip_non_native,548 .skip_non_native = skip_non_native,
528 .skip_freebsd = skip_freebsd,549 .skip_freebsd = skip_freebsd,
...@@ -720,6 +741,12 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu...@@ -720,6 +741,12 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu
720 compiler_mod.addImport("aro", aro_mod);741 compiler_mod.addImport("aro", aro_mod);
721 compiler_mod.addImport("aro_translate_c", aro_translate_c_mod);742 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
723 return compiler_mod;750 return compiler_mod;
724}751}
725752
...@@ -1417,6 +1444,10 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {...@@ -1417,6 +1444,10 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
1417 }),1444 }),
1418 });1445 });
14191446
1447 if (b.graph.host.result.os.tag == .windows) {
1448 doctest_exe.root_module.linkSystemLibrary("advapi32", .{});
1449 }
1450
1420 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {1451 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
1421 std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{1452 std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
1422 b.build_root, @errorName(err),1453 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"...@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460"
12PREFIX="$HOME/deps/$CACHE_BASENAME"12PREFIX="$HOME/deps/$CACHE_BASENAME"
13ZIG="$PREFIX/bin/zig"13ZIG="$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
17# Make the `zig version` number consistent.17# Make the `zig version` number consistent.
18# This will affect the cmake command below.18# 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"...@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460"
12PREFIX="$HOME/deps/$CACHE_BASENAME"12PREFIX="$HOME/deps/$CACHE_BASENAME"
13ZIG="$PREFIX/bin/zig"13ZIG="$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
17# Make the `zig version` number consistent.17# Make the `zig version` number consistent.
18# This will affect the cmake command below.18# 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"...@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460"
12PREFIX="$HOME/deps/$CACHE_BASENAME"12PREFIX="$HOME/deps/$CACHE_BASENAME"
13ZIG="$PREFIX/bin/zig"13ZIG="$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
17# Make the `zig version` number consistent.17# Make the `zig version` number consistent.
18# This will affect the cmake command below.18# This will affect the cmake command below.
doc/langref.html.in+10-88
...@@ -374,7 +374,8 @@...@@ -374,7 +374,8 @@
374 <p>374 <p>
375 Most of the time, it is more appropriate to write to stderr rather than stdout, and375 Most of the time, it is more appropriate to write to stderr rather than stdout, and
376 whether or not the message is successfully written to the stream is irrelevant.376 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:
378 </p>379 </p>
379 {#code|hello_again.zig#}380 {#code|hello_again.zig#}
380381
...@@ -3842,37 +3843,6 @@ void do_a_thing(struct Foo *foo) {...@@ -3842,37 +3843,6 @@ void do_a_thing(struct Foo *foo) {
3842 {#header_close#}3843 {#header_close#}
3843 {#header_close#}3844 {#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
3876 {#header_open|comptime#}3846 {#header_open|comptime#}
3877 <p>3847 <p>
3878 Zig places importance on the concept of whether an expression is known at compile-time.3848 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 {...@@ -4279,16 +4249,9 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
4279 {#header_close#}4249 {#header_close#}
42804250
4281 {#header_open|Async Functions#}4251 {#header_open|Async Functions#}
4282 <p>Async functions regressed with the release of 0.11.0. Their future in4252 <p>Async functions regressed with the release of 0.11.0. The current plan is to
4283 the Zig language is unclear due to multiple unsolved problems:</p>4253 reintroduce them as a lower level primitive that powers I/O implementations.</p>
4284 <ul>4254 <p>Tracking issue: <a href="https://github.com/ziglang/zig/issues/23446">Proposal: stackless coroutines as low-level primitives</a></p>
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>
4292 {#header_close#}4255 {#header_close#}
42934256
4294 {#header_open|Builtin Functions|2col#}4257 {#header_open|Builtin Functions|2col#}
...@@ -6552,7 +6515,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -6552,7 +6515,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
6552 </p>6515 </p>
6553 <ul>6516 <ul>
6554 <li>If a call to {#syntax#}@import{#endsyntax#} is analyzed, the file being imported is analyzed.</li>6517 <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>
6556 <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>6519 <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>
6557 <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>6520 <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>
6558 </ul>6521 </ul>
...@@ -7372,29 +7335,6 @@ fn readU32Be() u32 {}...@@ -7372,29 +7335,6 @@ fn readU32Be() u32 {}
7372 </ul>7335 </ul>
7373 </td>7336 </td>
7374 </tr>7337 </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>
7398 <tr>7338 <tr>
7399 <th scope="row">7339 <th scope="row">
7400 <pre>{#syntax#}break{#endsyntax#}</pre>7340 <pre>{#syntax#}break{#endsyntax#}</pre>
...@@ -7812,18 +7752,6 @@ fn readU32Be() u32 {}...@@ -7812,18 +7752,6 @@ fn readU32Be() u32 {}
7812 </ul>7752 </ul>
7813 </td>7753 </td>
7814 </tr>7754 </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>
7827 <tr>7755 <tr>
7828 <th scope="row">7756 <th scope="row">
7829 <pre>{#syntax#}var{#endsyntax#}</pre>7757 <pre>{#syntax#}var{#endsyntax#}</pre>
...@@ -7893,7 +7821,6 @@ ComptimeDecl <- KEYWORD_comptime Block...@@ -7893,7 +7821,6 @@ ComptimeDecl <- KEYWORD_comptime Block
7893Decl7821Decl
7894 <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)7822 <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
7895 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl7823 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl
7896 / KEYWORD_usingnamespace Expr SEMICOLON
78977824
7898FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr7825FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
78997826
...@@ -8006,8 +7933,7 @@ TypeExpr <- PrefixTypeOp* ErrorUnionExpr...@@ -8006,8 +7933,7 @@ TypeExpr <- PrefixTypeOp* ErrorUnionExpr
8006ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?7933ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
80077934
8008SuffixExpr7935SuffixExpr
8009 <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments7936 <- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
8010 / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
80117937
8012PrimaryTypeExpr7938PrimaryTypeExpr
8013 <- BUILTINIDENTIFIER FnCallArguments7939 <- BUILTINIDENTIFIER FnCallArguments
...@@ -8183,7 +8109,6 @@ PrefixOp...@@ -8183,7 +8109,6 @@ PrefixOp
8183 / MINUSPERCENT8109 / MINUSPERCENT
8184 / AMPERSAND8110 / AMPERSAND
8185 / KEYWORD_try8111 / KEYWORD_try
8186 / KEYWORD_await
81878112
8188PrefixTypeOp8113PrefixTypeOp
8189 <- QUESTIONMARK8114 <- QUESTIONMARK
...@@ -8404,8 +8329,6 @@ KEYWORD_and <- 'and' end_of_word...@@ -8404,8 +8329,6 @@ KEYWORD_and <- 'and' end_of_word
8404KEYWORD_anyframe <- 'anyframe' end_of_word8329KEYWORD_anyframe <- 'anyframe' end_of_word
8405KEYWORD_anytype <- 'anytype' end_of_word8330KEYWORD_anytype <- 'anytype' end_of_word
8406KEYWORD_asm <- 'asm' end_of_word8331KEYWORD_asm <- 'asm' end_of_word
8407KEYWORD_async <- 'async' end_of_word
8408KEYWORD_await <- 'await' end_of_word
8409KEYWORD_break <- 'break' end_of_word8332KEYWORD_break <- 'break' end_of_word
8410KEYWORD_callconv <- 'callconv' end_of_word8333KEYWORD_callconv <- 'callconv' end_of_word
8411KEYWORD_catch <- 'catch' end_of_word8334KEYWORD_catch <- 'catch' end_of_word
...@@ -8442,14 +8365,13 @@ KEYWORD_threadlocal <- 'threadlocal' end_of_word...@@ -8442,14 +8365,13 @@ KEYWORD_threadlocal <- 'threadlocal' end_of_word
8442KEYWORD_try <- 'try' end_of_word8365KEYWORD_try <- 'try' end_of_word
8443KEYWORD_union <- 'union' end_of_word8366KEYWORD_union <- 'union' end_of_word
8444KEYWORD_unreachable <- 'unreachable' end_of_word8367KEYWORD_unreachable <- 'unreachable' end_of_word
8445KEYWORD_usingnamespace <- 'usingnamespace' end_of_word
8446KEYWORD_var <- 'var' end_of_word8368KEYWORD_var <- 'var' end_of_word
8447KEYWORD_volatile <- 'volatile' end_of_word8369KEYWORD_volatile <- 'volatile' end_of_word
8448KEYWORD_while <- 'while' end_of_word8370KEYWORD_while <- 'while' end_of_word
84498371
8450keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and8372keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
8451 / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async8373 / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm
8452 / KEYWORD_await / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch8374 / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
8453 / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer8375 / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer
8454 / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export8376 / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
8455 / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if8377 / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if
...@@ -8458,7 +8380,7 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and...@@ -8458,7 +8380,7 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
8458 / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection8380 / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
8459 / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test8381 / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test
8460 / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable8382 / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable
8461 / KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while8383 / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
8462 {#end_syntax_block#}8384 {#end_syntax_block#}
8463 {#header_close#}8385 {#header_close#}
8464 {#header_open|Zen#}8386 {#header_open|Zen#}
doc/langref/bad_default_value.zig+1-1
...@@ -17,7 +17,7 @@ pub fn main() !void {...@@ -17,7 +17,7 @@ pub fn main() !void {
17 .maximum = 0.20,17 .maximum = 0.20,
18 };18 };
19 const category = threshold.categorize(0.90);19 const category = threshold.categorize(0.90);
20 try std.io.getStdOut().writeAll(@tagName(category));20 try std.fs.File.stdout().writeAll(@tagName(category));
21}21}
2222
23const std = @import("std");23const std = @import("std");
doc/langref/hello.zig+1-2
...@@ -1,8 +1,7 @@...@@ -1,8 +1,7 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn main() !void {3pub fn main() !void {
4 const stdout = std.io.getStdOut().writer();4 try std.fs.File.stdout().writeAll("Hello, World!\n");
5 try stdout.print("Hello, {s}!\n", .{"world"});
6}5}
76
8// exe=succeed7// exe=succeed
doc/langref/hello_again.zig+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn main() void {3pub fn main() void {
4 std.debug.print("Hello, world!\n", .{});4 std.debug.print("Hello, {s}!\n", .{"World"});
5}5}
66
7// exe=succeed7// 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...@@ -1432,7 +1432,7 @@ fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u
1432 defer buf.deinit();1432 defer buf.deinit();
14331433
1434 const max = limit orelse std.math.maxInt(u32);1434 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) {
1436 error.StreamTooLong => if (limit == null) return e,1436 error.StreamTooLong => if (limit == null) return e,
1437 else => return e,1437 else => return e,
1438 };1438 };
lib/compiler/aro/aro/Diagnostics.zig+17-27
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
2const Allocator = mem.Allocator;3const Allocator = mem.Allocator;
3const mem = std.mem;4const mem = std.mem;
4const Source = @import("Source.zig");5const Source = @import("Source.zig");
...@@ -323,12 +324,13 @@ pub fn addExtra(...@@ -323,12 +324,13 @@ pub fn addExtra(
323324
324pub fn render(comp: *Compilation, config: std.io.tty.Config) void {325pub fn render(comp: *Compilation, config: std.io.tty.Config) void {
325 if (comp.diagnostics.list.items.len == 0) return;326 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);
327 defer m.deinit();329 defer m.deinit();
328 renderMessages(comp, &m);330 renderMessages(comp, &m);
329}331}
330pub fn defaultMsgWriter(config: std.io.tty.Config) MsgWriter {332pub fn defaultMsgWriter(config: std.io.tty.Config, buffer: []u8) MsgWriter {
331 return MsgWriter.init(config);333 return MsgWriter.init(config, buffer);
332}334}
333335
334pub fn renderMessages(comp: *Compilation, m: anytype) void {336pub fn renderMessages(comp: *Compilation, m: anytype) void {
...@@ -449,12 +451,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {...@@ -449,12 +451,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
449 },451 },
450 .normalized => {452 .normalized => {
451 const f = struct {453 const f = struct {
452 pub fn f(454 pub fn f(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
453 bytes: []const u8,
454 comptime _: []const u8,
455 _: std.fmt.FormatOptions,
456 writer: anytype,
457 ) !void {
458 var it: std.unicode.Utf8Iterator = .{455 var it: std.unicode.Utf8Iterator = .{
459 .bytes = bytes,456 .bytes = bytes,
460 .i = 0,457 .i = 0,
...@@ -464,22 +461,16 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {...@@ -464,22 +461,16 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
464 try writer.writeByte(@intCast(codepoint));461 try writer.writeByte(@intCast(codepoint));
465 } else if (codepoint < 0xFFFF) {462 } else if (codepoint < 0xFFFF) {
466 try writer.writeAll("\\u");463 try writer.writeAll("\\u");
467 try std.fmt.formatInt(codepoint, 16, .upper, .{464 try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 4 });
468 .fill = '0',
469 .width = 4,
470 }, writer);
471 } else {465 } else {
472 try writer.writeAll("\\U");466 try writer.writeAll("\\U");
473 try std.fmt.formatInt(codepoint, 16, .upper, .{467 try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 8 });
474 .fill = '0',
475 .width = 8,
476 }, writer);
477 }468 }
478 }469 }
479 }470 }
480 }.f;471 }.f;
481 printRt(m, prop.msg, .{"{s}"}, .{472 printRt(m, prop.msg, .{"{f}"}, .{
482 std.fmt.Formatter(f){ .data = msg.extra.normalized },473 std.fmt.Formatter([]const u8, f){ .data = msg.extra.normalized },
483 });474 });
484 },475 },
485 .none, .offset => m.write(prop.msg),476 .none, .offset => m.write(prop.msg),
...@@ -535,32 +526,31 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind {...@@ -535,32 +526,31 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind {
535}526}
536527
537const MsgWriter = struct {528const MsgWriter = struct {
538 w: *std.fs.File.Writer,529 writer: *std.io.Writer,
539 config: std.io.tty.Config,530 config: std.io.tty.Config,
540531
541 fn init(config: std.io.tty.Config, buffer: []u8) MsgWriter {532 fn init(config: std.io.tty.Config, buffer: []u8) MsgWriter {
542 std.debug.lockStdErr();
543 return .{533 return .{
544 .w = std.fs.stderr().writer(buffer),534 .writer = std.debug.lockStderrWriter(buffer),
545 .config = config,535 .config = config,
546 };536 };
547 }537 }
548538
549 pub fn deinit(m: *MsgWriter) void {539 pub fn deinit(m: *MsgWriter) void {
550 m.w.flush() catch {};540 std.debug.unlockStderrWriter();
551 std.debug.unlockStdErr();541 m.* = undefined;
552 }542 }
553543
554 pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {544 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 {};
556 }546 }
557547
558 fn write(m: *MsgWriter, msg: []const u8) void {548 fn write(m: *MsgWriter, msg: []const u8) void {
559 m.w.writer().writeAll(msg) catch {};549 m.writer.writeAll(msg) catch {};
560 }550 }
561551
562 fn setColor(m: *MsgWriter, color: std.io.tty.Color) void {552 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 {};
564 }554 }
565555
566 fn location(m: *MsgWriter, path: []const u8, line: u32, col: u32) void {556 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 {...@@ -519,7 +519,7 @@ fn option(arg: []const u8, name: []const u8) ?[]const u8 {
519519
520fn addSource(d: *Driver, path: []const u8) !Source {520fn addSource(d: *Driver, path: []const u8) !Source {
521 if (mem.eql(u8, "-", path)) {521 if (mem.eql(u8, "-", path)) {
522 const stdin = std.io.getStdIn().reader();522 const stdin = std.fs.File.stdin().deprecatedReader();
523 const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32));523 const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32));
524 defer d.comp.gpa.free(input);524 defer d.comp.gpa.free(input);
525 return d.comp.addSourceFromBuffer("<stdin>", input);525 return d.comp.addSourceFromBuffer("<stdin>", input);
...@@ -541,7 +541,7 @@ pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{ FatalEr...@@ -541,7 +541,7 @@ pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{ FatalEr
541}541}
542542
543pub fn renderErrors(d: *Driver) void {543pub 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()));
545}545}
546546
547pub fn detectConfig(d: *Driver, file: std.fs.File) std.io.tty.Config {547pub 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_...@@ -591,7 +591,7 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8, comptime fast_
591 var macro_buf = std.ArrayList(u8).init(d.comp.gpa);591 var macro_buf = std.ArrayList(u8).init(d.comp.gpa);
592 defer macro_buf.deinit();592 defer macro_buf.deinit();
593593
594 const std_out = std.io.getStdOut().writer();594 const std_out = std.fs.File.stdout().deprecatedWriter();
595 if (try parseArgs(d, std_out, macro_buf.writer(), args)) return;595 if (try parseArgs(d, std_out, macro_buf.writer(), args)) return;
596596
597 const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile);597 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(...@@ -686,10 +686,10 @@ fn processSource(
686 std.fs.cwd().createFile(some, .{}) catch |er|686 std.fs.cwd().createFile(some, .{}) catch |er|
687 return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) })687 return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) })
688 else688 else
689 std.io.getStdOut();689 std.fs.File.stdout();
690 defer if (d.output_name != null) file.close();690 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
694 pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er|694 pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er|
695 return d.fatal("unable to write result: {s}", .{errorDescription(er)});695 return d.fatal("unable to write result: {s}", .{errorDescription(er)});
...@@ -704,8 +704,8 @@ fn processSource(...@@ -704,8 +704,8 @@ fn processSource(
704 defer tree.deinit();704 defer tree.deinit();
705705
706 if (d.verbose_ast) {706 if (d.verbose_ast) {
707 const stdout = std.io.getStdOut();707 const stdout = std.fs.File.stdout();
708 var buf_writer = std.io.bufferedWriter(stdout.writer());708 var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter());
709 tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {};709 tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {};
710 buf_writer.flush() catch {};710 buf_writer.flush() catch {};
711 }711 }
...@@ -734,8 +734,8 @@ fn processSource(...@@ -734,8 +734,8 @@ fn processSource(
734 defer ir.deinit(d.comp.gpa);734 defer ir.deinit(d.comp.gpa);
735735
736 if (d.verbose_ir) {736 if (d.verbose_ir) {
737 const stdout = std.io.getStdOut();737 const stdout = std.fs.File.stdout();
738 var buf_writer = std.io.bufferedWriter(stdout.writer());738 var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter());
739 ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {};739 ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {};
740 buf_writer.flush() catch {};740 buf_writer.flush() catch {};
741 }741 }
...@@ -806,10 +806,10 @@ fn processSource(...@@ -806,10 +806,10 @@ fn processSource(
806}806}
807807
808fn dumpLinkerArgs(items: []const []const u8) !void {808fn dumpLinkerArgs(items: []const []const u8) !void {
809 const stdout = std.io.getStdOut().writer();809 const stdout = std.fs.File.stdout().deprecatedWriter();
810 for (items, 0..) |item, i| {810 for (items, 0..) |item, i| {
811 if (i > 0) try stdout.writeByte(' ');811 if (i > 0) try stdout.writeByte(' ');
812 try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)});812 try stdout.print("\"{f}\"", .{std.zig.fmtString(item)});
813 }813 }
814 try stdout.writeByte('\n');814 try stdout.writeByte('\n');
815}815}
lib/compiler/aro/aro/Parser.zig+5-5
...@@ -500,8 +500,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_...@@ -500,8 +500,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_
500500
501 const w = p.strings.writer();501 const w = p.strings.writer();
502 const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes;502 const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes;
503 try w.print("call to '{s}' declared with attribute error: {}", .{503 try w.print("call to '{s}' declared with attribute error: {f}", .{
504 p.tokSlice(@"error".__name_tok), std.zig.fmtEscapes(msg_str),504 p.tokSlice(@"error".__name_tok), std.zig.fmtString(msg_str),
505 });505 });
506 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);506 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
507 try p.errStr(.error_attribute, usage_tok, str);507 try p.errStr(.error_attribute, usage_tok, str);
...@@ -512,8 +512,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_...@@ -512,8 +512,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_
512512
513 const w = p.strings.writer();513 const w = p.strings.writer();
514 const msg_str = p.comp.interner.get(warning.msg.ref()).bytes;514 const msg_str = p.comp.interner.get(warning.msg.ref()).bytes;
515 try w.print("call to '{s}' declared with attribute warning: {}", .{515 try w.print("call to '{s}' declared with attribute warning: {f}", .{
516 p.tokSlice(warning.__name_tok), std.zig.fmtEscapes(msg_str),516 p.tokSlice(warning.__name_tok), std.zig.fmtString(msg_str),
517 });517 });
518 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);518 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
519 try p.errStr(.warning_attribute, usage_tok, str);519 try p.errStr(.warning_attribute, usage_tok, str);
...@@ -542,7 +542,7 @@ fn errDeprecated(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, msg: ?Valu...@@ -542,7 +542,7 @@ fn errDeprecated(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, msg: ?Valu
542 try w.writeAll(reason);542 try w.writeAll(reason);
543 if (msg) |m| {543 if (msg) |m| {
544 const str = p.comp.interner.get(m.ref()).bytes;544 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)});
546 }546 }
547 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);547 const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
548 return p.errStr(tag, tok_i, str);548 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:...@@ -811,7 +811,7 @@ fn verboseLog(pp: *Preprocessor, raw: RawToken, comptime fmt: []const u8, args:
811 const source = pp.comp.getSource(raw.source);811 const source = pp.comp.getSource(raw.source);
812 const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start });812 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();
815 var buf_writer = std.io.bufferedWriter(stderr);815 var buf_writer = std.io.bufferedWriter(stderr);
816 const writer = buf_writer.writer();816 const writer = buf_writer.writer();
817 defer buf_writer.flush() catch {};817 defer buf_writer.flush() catch {};
...@@ -3262,7 +3262,8 @@ fn printLinemarker(...@@ -3262,7 +3262,8 @@ fn printLinemarker(
3262 // containing the same bytes as the input regardless of encoding.3262 // containing the same bytes as the input regardless of encoding.
3263 else => {3263 else => {
3264 try w.writeAll("\\x");3264 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});
3266 },3267 },
3267 };3268 };
3268 try w.writeByte('"');3269 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...@@ -961,7 +961,7 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w
961 switch (key) {961 switch (key) {
962 .null => return w.writeAll("nullptr_t"),962 .null => return w.writeAll("nullptr_t"),
963 .int => |repr| switch (repr) {963 .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}),
965 },965 },
966 .float => |repr| switch (repr) {966 .float => |repr| switch (repr) {
967 .f16 => |x| return w.print("{d}", .{@round(@as(f64, @floatCast(x)) * 1000) / 1000}),967 .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...@@ -982,7 +982,7 @@ pub fn printString(bytes: []const u8, ty: Type, comp: *const Compilation, w: any
982 const without_null = bytes[0 .. bytes.len - @intFromEnum(size)];982 const without_null = bytes[0 .. bytes.len - @intFromEnum(size)];
983 try w.writeByte('"');983 try w.writeByte('"');
984 switch (size) {984 switch (size) {
985 .@"1" => try w.print("{}", .{std.zig.fmtEscapes(without_null)}),985 .@"1" => try w.print("{f}", .{std.zig.fmtString(without_null)}),
986 .@"2" => {986 .@"2" => {
987 var items: [2]u16 = undefined;987 var items: [2]u16 = undefined;
988 var i: usize = 0;988 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,...@@ -171,7 +171,7 @@ pub fn addRelocation(elf: *Elf, name: []const u8, section_kind: Object.Section,
171/// strtab171/// strtab
172/// section headers172/// section headers
173pub fn finish(elf: *Elf, file: std.fs.File) !void {173pub 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());
175 const w = buf_writer.writer();175 const w = buf_writer.writer();
176176
177 var num_sections: std.elf.Elf64_Half = additional_sections;177 var num_sections: std.elf.Elf64_Half = additional_sections;
lib/compiler/aro_translate_c.zig+3-2
...@@ -1781,7 +1781,8 @@ test "Macro matching" {...@@ -1781,7 +1781,8 @@ test "Macro matching" {
1781fn renderErrorsAndExit(comp: *aro.Compilation) noreturn {1781fn renderErrorsAndExit(comp: *aro.Compilation) noreturn {
1782 defer std.process.exit(1);1782 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);
1785 defer writer.deinit(); // writer deinit must run *before* exit so that stderr is flushed1786 defer writer.deinit(); // writer deinit must run *before* exit so that stderr is flushed
17861787
1787 var saw_error = false;1788 var saw_error = false;
...@@ -1824,6 +1825,6 @@ pub fn main() !void {...@@ -1824,6 +1825,6 @@ pub fn main() !void {
1824 defer tree.deinit(gpa);1825 defer tree.deinit(gpa);
18251826
1826 const formatted = try tree.render(arena);1827 const formatted = try tree.render(arena);
1827 try std.io.getStdOut().writeAll(formatted);1828 try std.fs.File.stdout().writeAll(formatted);
1828 return std.process.cleanExit();1829 return std.process.cleanExit();
1829}1830}
lib/compiler/aro_translate_c/ast.zig+6-6
...@@ -849,7 +849,7 @@ const Context = struct {...@@ -849,7 +849,7 @@ const Context = struct {
849 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {849 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
850 if (std.zig.primitives.isPrimitive(bytes))850 if (std.zig.primitives.isPrimitive(bytes))
851 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});851 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 })});
853 }853 }
854854
855 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {855 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
...@@ -1201,7 +1201,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1201,7 +1201,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
12011201
1202 const compile_error_tok = try c.addToken(.builtin, "@compileError");1202 const compile_error_tok = try c.addToken(.builtin, "@compileError");
1203 _ = try c.addToken(.l_paren, "(");1203 _ = 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)});
1205 const err_msg = try c.addNode(.{1205 const err_msg = try c.addNode(.{
1206 .tag = .string_literal,1206 .tag = .string_literal,
1207 .main_token = err_msg_tok,1207 .main_token = err_msg_tok,
...@@ -2116,7 +2116,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {...@@ -2116,7 +2116,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
2116 defer c.gpa.free(members);2116 defer c.gpa.free(members);
21172117
2118 for (payload.fields, 0..) |field, i| {2118 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 })});
2120 _ = try c.addToken(.colon, ":");2120 _ = try c.addToken(.colon, ":");
2121 const type_expr = try renderNode(c, field.type);2121 const type_expr = try renderNode(c, field.type);
21222122
...@@ -2205,7 +2205,7 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI...@@ -2205,7 +2205,7 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI
2205 .main_token = try c.addToken(.period, "."),2205 .main_token = try c.addToken(.period, "."),
2206 .data = .{ .node_and_token = .{2206 .data = .{ .node_and_token = .{
2207 lhs,2207 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 })}),
2209 } },2209 } },
2210 });2210 });
2211}2211}
...@@ -2681,7 +2681,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {...@@ -2681,7 +2681,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
2681 _ = try c.addToken(.l_paren, "(");2681 _ = try c.addToken(.l_paren, "(");
2682 const res = try c.addNode(.{2682 const res = try c.addNode(.{
2683 .tag = .string_literal,2683 .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)}),
2685 .data = undefined,2685 .data = undefined,
2686 });2686 });
2687 _ = try c.addToken(.r_paren, ")");2687 _ = try c.addToken(.r_paren, ")");
...@@ -2765,7 +2765,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {...@@ -2765,7 +2765,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
2765 _ = try c.addToken(.l_paren, "(");2765 _ = try c.addToken(.l_paren, "(");
2766 const res = try c.addNode(.{2766 const res = try c.addNode(.{
2767 .tag = .string_literal,2767 .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)}),
2769 .data = undefined,2769 .data = undefined,
2770 });2770 });
2771 _ = try c.addToken(.r_paren, ")");2771 _ = try c.addToken(.r_paren, ")");
lib/compiler/build_runner.zig+3-1
...@@ -255,7 +255,7 @@ pub fn main() !void {...@@ -255,7 +255,7 @@ pub fn main() !void {
255 builder.verbose_llvm_ir = "-";255 builder.verbose_llvm_ir = "-";
256 } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {256 } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {
257 builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..];257 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=")) {
259 builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];259 builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];
260 } else if (mem.eql(u8, arg, "--verbose-cimport")) {260 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
261 builder.verbose_cimport = true;261 builder.verbose_cimport = true;
...@@ -719,6 +719,8 @@ fn runStepNames(...@@ -719,6 +719,8 @@ fn runStepNames(
719 if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {};719 if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {};
720 if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {};720 if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {};
721721
722 w.writeAll("\n") catch {};
723
722 // Print a fancy tree with build results.724 // Print a fancy tree with build results.
723 var step_stack_copy = try step_stack.clone(gpa);725 var step_stack_copy = try step_stack.clone(gpa);
724 defer step_stack_copy.deinit(gpa);726 defer step_stack_copy.deinit(gpa);
lib/compiler/libc.zig+3-3
...@@ -40,7 +40,7 @@ pub fn main() !void {...@@ -40,7 +40,7 @@ pub fn main() !void {
40 const arg = args[i];40 const arg = args[i];
41 if (mem.startsWith(u8, arg, "-")) {41 if (mem.startsWith(u8, arg, "-")) {
42 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {42 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();
44 try stdout.writeAll(usage_libc);44 try stdout.writeAll(usage_libc);
45 return std.process.cleanExit();45 return std.process.cleanExit();
46 } else if (mem.eql(u8, arg, "-target")) {46 } else if (mem.eql(u8, arg, "-target")) {
...@@ -97,7 +97,7 @@ pub fn main() !void {...@@ -97,7 +97,7 @@ pub fn main() !void {
97 fatal("no include dirs detected for target {s}", .{zig_target});97 fatal("no include dirs detected for target {s}", .{zig_target});
98 }98 }
9999
100 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());100 var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter());
101 var writer = bw.writer();101 var writer = bw.writer();
102 for (libc_dirs.libc_include_dir_list) |include_dir| {102 for (libc_dirs.libc_include_dir_list) |include_dir| {
103 try writer.writeAll(include_dir);103 try writer.writeAll(include_dir);
...@@ -125,7 +125,7 @@ pub fn main() !void {...@@ -125,7 +125,7 @@ pub fn main() !void {
125 };125 };
126 defer libc.deinit(gpa);126 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());
129 try libc.render(bw.writer());129 try libc.render(bw.writer());
130 try bw.flush();130 try bw.flush();
131 }131 }
lib/compiler/objcopy.zig+6-6
...@@ -54,7 +54,7 @@ fn cmdObjCopy(...@@ -54,7 +54,7 @@ fn cmdObjCopy(
54 fatal("unexpected positional argument: '{s}'", .{arg});54 fatal("unexpected positional argument: '{s}'", .{arg});
55 }55 }
56 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {56 } 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);
58 } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) {58 } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) {
59 i += 1;59 i += 1;
60 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});60 if (i >= args.len) fatal("expected another argument after '{s}'", .{arg});
...@@ -227,8 +227,8 @@ fn cmdObjCopy(...@@ -227,8 +227,8 @@ fn cmdObjCopy(
227 if (listen) {227 if (listen) {
228 var server = try Server.init(.{228 var server = try Server.init(.{
229 .gpa = gpa,229 .gpa = gpa,
230 .in = std.io.getStdIn(),230 .in = .stdin(),
231 .out = std.io.getStdOut(),231 .out = .stdout(),
232 .zig_version = builtin.zig_version_string,232 .zig_version = builtin.zig_version_string,
233 });233 });
234 defer server.deinit();234 defer server.deinit();
...@@ -635,11 +635,11 @@ const HexWriter = struct {...@@ -635,11 +635,11 @@ const HexWriter = struct {
635 const payload_bytes = self.getPayloadBytes();635 const payload_bytes = self.getPayloadBytes();
636 assert(payload_bytes.len <= MAX_PAYLOAD_LEN);636 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, .{
639 @as(u8, @intCast(payload_bytes.len)),639 @as(u8, @intCast(payload_bytes.len)),
640 self.address,640 self.address,
641 @intFromEnum(self.payload),641 @intFromEnum(self.payload),
642 std.fmt.fmtSliceHexUpper(payload_bytes),642 payload_bytes,
643 self.checksum(),643 self.checksum(),
644 });644 });
645 try file.writeAll(line);645 try file.writeAll(line);
...@@ -1495,7 +1495,7 @@ const ElfFileHelper = struct {...@@ -1495,7 +1495,7 @@ const ElfFileHelper = struct {
1495 if (size < prefix.len) return null;1495 if (size < prefix.len) return null;
14961496
1497 try in_file.seekTo(offset);1497 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
1500 // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.1500 // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.
1501 const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size));1501 const compressed_data = try allocator.alignedAlloc(u8, .@"8", @intCast(size));
lib/compiler/reduce.zig+1-1
...@@ -68,7 +68,7 @@ pub fn main() !void {...@@ -68,7 +68,7 @@ pub fn main() !void {
68 const arg = args[i];68 const arg = args[i];
69 if (mem.startsWith(u8, arg, "-")) {69 if (mem.startsWith(u8, arg, "-")) {
70 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {70 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();
72 try stdout.writeAll(usage);72 try stdout.writeAll(usage);
73 return std.process.cleanExit();73 return std.process.cleanExit();
74 } else if (mem.eql(u8, arg, "--")) {74 } 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 {...@@ -160,12 +160,6 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
160 try walkExpression(w, decl);160 try walkExpression(w, decl);
161 },161 },
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
169 .global_var_decl,163 .global_var_decl,
170 .local_var_decl,164 .local_var_decl,
171 .simple_var_decl,165 .simple_var_decl,
...@@ -335,7 +329,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -335,7 +329,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
335 .address_of,329 .address_of,
336 .@"try",330 .@"try",
337 .@"resume",331 .@"resume",
338 .@"await",
339 .deref,332 .deref,
340 => {333 => {
341 return walkExpression(w, ast.nodeData(node).node);334 return walkExpression(w, ast.nodeData(node).node);
...@@ -379,12 +372,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -379,12 +372,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
379372
380 .call_one,373 .call_one,
381 .call_one_comma,374 .call_one_comma,
382 .async_call_one,
383 .async_call_one_comma,
384 .call,375 .call,
385 .call_comma,376 .call_comma,
386 .async_call,
387 .async_call_comma,
388 => {377 => {
389 var buf: [1]Ast.Node.Index = undefined;378 var buf: [1]Ast.Node.Index = undefined;
390 return walkCall(w, ast.fullCall(&buf, node).?);379 return walkCall(w, ast.fullCall(&buf, node).?);
...@@ -525,7 +514,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -525,7 +514,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
525 .local_var_decl => unreachable,514 .local_var_decl => unreachable,
526 .simple_var_decl => unreachable,515 .simple_var_decl => unreachable,
527 .aligned_var_decl => unreachable,516 .aligned_var_decl => unreachable,
528 .@"usingnamespace" => unreachable,
529 .test_decl => unreachable,517 .test_decl => unreachable,
530 .asm_output => unreachable,518 .asm_output => unreachable,
531 .asm_input => unreachable,519 .asm_input => unreachable,
lib/compiler/resinator/cli.zig+13-14
...@@ -125,13 +125,12 @@ pub const Diagnostics = struct {...@@ -125,13 +125,12 @@ pub const Diagnostics = struct {
125 }125 }
126126
127 pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {127 pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {
128 std.debug.lockStdErr();128 const stderr = std.debug.lockStderrWriter(&.{});
129 defer std.debug.unlockStdErr();129 defer std.debug.unlockStderrWriter();
130 const stderr = std.io.getStdErr().writer();
131 self.renderToWriter(args, stderr, config) catch return;130 self.renderToWriter(args, stderr, config) catch return;
132 }131 }
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 {
135 for (self.errors.items) |err_details| {134 for (self.errors.items) |err_details| {
136 try renderErrorMessage(writer, config, err_details, args);135 try renderErrorMessage(writer, config, err_details, args);
137 }136 }
...@@ -1403,7 +1402,7 @@ test parsePercent {...@@ -1403,7 +1402,7 @@ test parsePercent {
1403 try std.testing.expectError(error.InvalidFormat, parsePercent("~1"));1402 try std.testing.expectError(error.InvalidFormat, parsePercent("~1"));
1404}1403}
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 {
1407 try config.setColor(writer, .dim);1406 try config.setColor(writer, .dim);
1408 try writer.writeAll("<cli>");1407 try writer.writeAll("<cli>");
1409 try config.setColor(writer, .reset);1408 try config.setColor(writer, .reset);
...@@ -1481,27 +1480,27 @@ pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_detail...@@ -1481,27 +1480,27 @@ pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_detail
1481 try writer.writeByte('\n');1480 try writer.writeByte('\n');
14821481
1483 try config.setColor(writer, .green);1482 try config.setColor(writer, .green);
1484 try writer.writeByteNTimes(' ', prefix.len);1483 try writer.splatByteAll(' ', prefix.len);
1485 // Special case for when the option is *only* a prefix (e.g. invalid option: -)1484 // Special case for when the option is *only* a prefix (e.g. invalid option: -)
1486 if (err_details.arg_span.prefix_len == arg_with_name.len) {1485 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);
1488 } else {1487 } else {
1489 try writer.writeByteNTimes('~', err_details.arg_span.prefix_len);1488 try writer.splatByteAll('~', err_details.arg_span.prefix_len);
1490 try writer.writeByteNTimes(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len);1489 try writer.splatByteAll(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len);
1491 if (!err_details.arg_span.point_at_next_arg and err_details.arg_span.value_offset == 0) {1490 if (!err_details.arg_span.point_at_next_arg and err_details.arg_span.value_offset == 0) {
1492 try writer.writeByte('^');1491 try writer.writeByte('^');
1493 try writer.writeByteNTimes('~', name_slice.len - 1);1492 try writer.splatByteAll('~', name_slice.len - 1);
1494 } else if (err_details.arg_span.value_offset > 0) {1493 } 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);
1496 try writer.writeByte('^');1495 try writer.writeByte('^');
1497 if (err_details.arg_span.value_offset < arg_with_name.len) {1496 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);
1499 }1498 }
1500 } else if (err_details.arg_span.point_at_next_arg) {1499 } 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);
1502 try writer.writeByte('^');1501 try writer.writeByte('^');
1503 if (next_arg_len > 0) {1502 if (next_arg_len > 0) {
1504 try writer.writeByteNTimes('~', next_arg_len - 1);1503 try writer.splatByteAll('~', next_arg_len - 1);
1505 }1504 }
1506 }1505 }
1507 }1506 }
lib/compiler/resinator/compile.zig+11-11
...@@ -570,7 +570,7 @@ pub const Compiler = struct {...@@ -570,7 +570,7 @@ pub const Compiler = struct {
570 switch (predefined_type) {570 switch (predefined_type) {
571 .GROUP_ICON, .GROUP_CURSOR => {571 .GROUP_ICON, .GROUP_CURSOR => {
572 // Check for animated icon first572 // Check for animated icon first
573 if (ani.isAnimatedIcon(file.reader())) {573 if (ani.isAnimatedIcon(file.deprecatedReader())) {
574 // Animated icons are just put into the resource unmodified,574 // Animated icons are just put into the resource unmodified,
575 // and the resource type changes to ANIICON/ANICURSOR575 // and the resource type changes to ANIICON/ANICURSOR
576576
...@@ -586,14 +586,14 @@ pub const Compiler = struct {...@@ -586,14 +586,14 @@ pub const Compiler = struct {
586586
587 try header.write(writer, self.errContext(node.id));587 try header.write(writer, self.errContext(node.id));
588 try file.seekTo(0);588 try file.seekTo(0);
589 try writeResourceData(writer, file.reader(), header.data_size);589 try writeResourceData(writer, file.deprecatedReader(), header.data_size);
590 return;590 return;
591 }591 }
592592
593 // isAnimatedIcon moved the file cursor so reset to the start593 // isAnimatedIcon moved the file cursor so reset to the start
594 try file.seekTo(0);594 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) {
597 error.OutOfMemory => |e| return e,597 error.OutOfMemory => |e| return e,
598 else => |e| {598 else => |e| {
599 return self.iconReadError(599 return self.iconReadError(
...@@ -672,7 +672,7 @@ pub const Compiler = struct {...@@ -672,7 +672,7 @@ pub const Compiler = struct {
672 }672 }
673673
674 try file.seekTo(entry.data_offset_from_start_of_file);674 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 {
676 return self.iconReadError(676 return self.iconReadError(
677 error.UnexpectedEOF,677 error.UnexpectedEOF,
678 filename_utf8,678 filename_utf8,
...@@ -803,7 +803,7 @@ pub const Compiler = struct {...@@ -803,7 +803,7 @@ pub const Compiler = struct {
803 }803 }
804804
805 try file.seekTo(entry.data_offset_from_start_of_file);805 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);
807 try writeDataPadding(writer, full_data_size);807 try writeDataPadding(writer, full_data_size);
808808
809 if (self.state.icon_id == std.math.maxInt(u16)) {809 if (self.state.icon_id == std.math.maxInt(u16)) {
...@@ -859,7 +859,7 @@ pub const Compiler = struct {...@@ -859,7 +859,7 @@ pub const Compiler = struct {
859 header.applyMemoryFlags(node.common_resource_attributes, self.source);859 header.applyMemoryFlags(node.common_resource_attributes, self.source);
860 const file_size = try file.getEndPos();860 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| {
863 const filename_string_index = try self.diagnostics.putString(filename_utf8);863 const filename_string_index = try self.diagnostics.putString(filename_utf8);
864 return self.addErrorDetailsAndFail(.{864 return self.addErrorDetailsAndFail(.{
865 .err = .bmp_read_error,865 .err = .bmp_read_error,
...@@ -922,7 +922,7 @@ pub const Compiler = struct {...@@ -922,7 +922,7 @@ pub const Compiler = struct {
922 header.data_size = bmp_bytes_to_write;922 header.data_size = bmp_bytes_to_write;
923 try header.write(writer, self.errContext(node.id));923 try header.write(writer, self.errContext(node.id));
924 try file.seekTo(bmp.file_header_len);924 try file.seekTo(bmp.file_header_len);
925 const file_reader = file.reader();925 const file_reader = file.deprecatedReader();
926 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size);926 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size);
927 if (bitmap_info.getBitmasksByteLen() > 0) {927 if (bitmap_info.getBitmasksByteLen() > 0) {
928 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen());928 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen());
...@@ -968,7 +968,7 @@ pub const Compiler = struct {...@@ -968,7 +968,7 @@ pub const Compiler = struct {
968 header.data_size = @intCast(file_size);968 header.data_size = @intCast(file_size);
969 try header.write(writer, self.errContext(node.id));969 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());
972 try writeResourceData(writer, header_slurping_reader.reader(), header.data_size);972 try writeResourceData(writer, header_slurping_reader.reader(), header.data_size);
973973
974 try self.state.font_dir.add(self.arena, FontDir.Font{974 try self.state.font_dir.add(self.arena, FontDir.Font{
...@@ -1002,7 +1002,7 @@ pub const Compiler = struct {...@@ -1002,7 +1002,7 @@ pub const Compiler = struct {
1002 // We now know that the data size will fit in a u321002 // We now know that the data size will fit in a u32
1003 header.data_size = @intCast(data_size);1003 header.data_size = @intCast(data_size);
1004 try header.write(writer, self.errContext(node.id));1004 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);
1006 }1006 }
10071007
1008 fn iconReadError(1008 fn iconReadError(
...@@ -2947,7 +2947,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype)...@@ -2947,7 +2947,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype)
2947 slurped_header: [size]u8 = [_]u8{0x00} ** size,2947 slurped_header: [size]u8 = [_]u8{0x00} ** size,
29482948
2949 pub const Error = ReaderType.Error;2949 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
2952 pub fn read(self: *@This(), buf: []u8) Error!usize {2952 pub fn read(self: *@This(), buf: []u8) Error!usize {
2953 const amt = try self.child_reader.read(buf);2953 const amt = try self.child_reader.read(buf);
...@@ -2981,7 +2981,7 @@ pub fn LimitedWriter(comptime WriterType: type) type {...@@ -2981,7 +2981,7 @@ pub fn LimitedWriter(comptime WriterType: type) type {
2981 bytes_left: u64,2981 bytes_left: u64,
29822982
2983 pub const Error = error{NoSpaceLeft} || WriterType.Error;2983 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
2986 const Self = @This();2986 const Self = @This();
29872987
lib/compiler/resinator/errors.zig+27-31
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
2const Token = @import("lex.zig").Token;3const Token = @import("lex.zig").Token;
3const SourceMappings = @import("source_mapping.zig").SourceMappings;4const SourceMappings = @import("source_mapping.zig").SourceMappings;
4const utils = @import("utils.zig");5const utils = @import("utils.zig");
...@@ -61,16 +62,15 @@ pub const Diagnostics = struct {...@@ -61,16 +62,15 @@ pub const Diagnostics = struct {
61 }62 }
6263
63 pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void {64 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 const stderr = std.debug.lockStderrWriter(&.{});
65 defer std.debug.unlockStdErr();66 defer std.debug.unlockStderrWriter();
66 const stderr = std.io.getStdErr().writer();
67 for (self.errors.items) |err_details| {67 for (self.errors.items) |err_details| {
68 renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;68 renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;
69 }69 }
70 }70 }
7171
72 pub fn renderToStdErrDetectTTY(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void {72 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());
74 return self.renderToStdErr(cwd, source, tty_config, source_mappings);74 return self.renderToStdErr(cwd, source, tty_config, source_mappings);
75 }75 }
7676
...@@ -409,15 +409,7 @@ pub const ErrorDetails = struct {...@@ -409,15 +409,7 @@ pub const ErrorDetails = struct {
409 failed_to_open_cwd,409 failed_to_open_cwd,
410 };410 };
411411
412 fn formatToken(412 fn formatToken(ctx: TokenFormatContext, writer: *std.io.Writer) std.io.Writer.Error!void {
413 ctx: TokenFormatContext,
414 comptime fmt: []const u8,
415 options: std.fmt.FormatOptions,
416 writer: anytype,
417 ) !void {
418 _ = fmt;
419 _ = options;
420
421 switch (ctx.token.id) {413 switch (ctx.token.id) {
422 .eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()),414 .eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()),
423 else => {},415 else => {},
...@@ -441,7 +433,7 @@ pub const ErrorDetails = struct {...@@ -441,7 +433,7 @@ pub const ErrorDetails = struct {
441 code_page: SupportedCodePage,433 code_page: SupportedCodePage,
442 };434 };
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) {
445 return .{ .data = .{437 return .{ .data = .{
446 .token = self.token,438 .token = self.token,
447 .code_page = self.code_page,439 .code_page = self.code_page,
...@@ -452,7 +444,7 @@ pub const ErrorDetails = struct {...@@ -452,7 +444,7 @@ pub const ErrorDetails = struct {
452 pub fn render(self: ErrorDetails, writer: anytype, source: []const u8, strings: []const []const u8) !void {444 pub fn render(self: ErrorDetails, writer: anytype, source: []const u8, strings: []const []const u8) !void {
453 switch (self.err) {445 switch (self.err) {
454 .unfinished_string_literal => {446 .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)});
456 },448 },
457 .string_literal_too_long => {449 .string_literal_too_long => {
458 return writer.print("string literal too long (max is currently {} characters)", .{self.extra.number});450 return writer.print("string literal too long (max is currently {} characters)", .{self.extra.number});
...@@ -466,10 +458,14 @@ pub const ErrorDetails = struct {...@@ -466,10 +458,14 @@ pub const ErrorDetails = struct {
466 .hint => return,458 .hint => return,
467 },459 },
468 .illegal_byte => {460 .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 });
470 },464 },
471 .illegal_byte_outside_string_literals => {465 .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 });
473 },469 },
474 .illegal_codepoint_outside_string_literals => {470 .illegal_codepoint_outside_string_literals => {
475 // This is somewhat hacky, but we know that:471 // This is somewhat hacky, but we know that:
...@@ -527,26 +523,26 @@ pub const ErrorDetails = struct {...@@ -527,26 +523,26 @@ pub const ErrorDetails = struct {
527 return writer.print("unsupported code page '{s} (id={})' in #pragma code_page", .{ @tagName(code_page), number });523 return writer.print("unsupported code page '{s} (id={})' in #pragma code_page", .{ @tagName(code_page), number });
528 },524 },
529 .unfinished_raw_data_block => {525 .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)});
531 },527 },
532 .unfinished_string_table_block => {528 .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)});
534 },530 },
535 .expected_token => {531 .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) });
537 },533 },
538 .expected_something_else => {534 .expected_something_else => {
539 try writer.writeAll("expected ");535 try writer.writeAll("expected ");
540 try self.extra.expected_types.writeCommaSeparated(writer);536 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)});
542 },538 },
543 .resource_type_cant_use_raw_data => switch (self.type) {539 .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() }),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() }),
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)}),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)}),
546 .hint => return,542 .hint => return,
547 },543 },
548 .id_must_be_ordinal => {544 .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) });
550 },546 },
551 .name_or_id_not_allowed => {547 .name_or_id_not_allowed => {
552 try writer.print("name or id is not allowed for resource type '{s}'", .{self.extra.resource.nameForErrorDisplay()});548 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 {...@@ -562,7 +558,7 @@ pub const ErrorDetails = struct {
562 try writer.writeAll("ASCII character not equivalent to virtual key code");558 try writer.writeAll("ASCII character not equivalent to virtual key code");
563 },559 },
564 .empty_menu_not_allowed => {560 .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)});
566 },562 },
567 .rc_would_miscompile_version_value_padding => switch (self.type) {563 .rc_would_miscompile_version_value_padding => switch (self.type) {
568 .err, .warning => return writer.print("the padding before this quoted string value would be miscompiled by the Win32 RC compiler", .{}),564 .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 {...@@ -627,7 +623,7 @@ pub const ErrorDetails = struct {
627 .string_already_defined => switch (self.type) {623 .string_already_defined => switch (self.type) {
628 .err, .warning => {624 .err, .warning => {
629 const language = self.extra.string_and_language.language;625 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 });
631 },627 },
632 .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 }),628 .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 }),
633 .hint => return,629 .hint => return,
...@@ -642,7 +638,7 @@ pub const ErrorDetails = struct {...@@ -642,7 +638,7 @@ pub const ErrorDetails = struct {
642 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) });638 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) });
643 },639 },
644 .invalid_accelerator_key => {640 .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) });
646 },642 },
647 .accelerator_type_required => {643 .accelerator_type_required => {
648 try writer.writeAll("accelerator type [ASCII or VIRTKEY] required when key is an integer");644 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...@@ -898,7 +894,7 @@ fn cellCount(code_page: SupportedCodePage, source: []const u8, start_index: usiz
898894
899const truncated_str = "<...truncated...>";895const 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 {
902 if (err_details.type == .hint) return;898 if (err_details.type == .hint) return;
903899
904 const source_line_start = err_details.token.getLineStartForErrorDisplay(source);900 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...@@ -981,10 +977,10 @@ pub fn renderErrorMessage(writer: anytype, tty_config: std.io.tty.Config, cwd: s
981977
982 try tty_config.setColor(writer, .green);978 try tty_config.setColor(writer, .green);
983 const num_spaces = truncated_visual_info.point_offset - truncated_visual_info.before_len;979 const num_spaces = truncated_visual_info.point_offset - truncated_visual_info.before_len;
984 try writer.writeByteNTimes(' ', num_spaces);980 try writer.splatByteAll(' ', num_spaces);
985 try writer.writeByteNTimes('~', truncated_visual_info.before_len);981 try writer.splatByteAll('~', truncated_visual_info.before_len);
986 try writer.writeByte('^');982 try writer.writeByte('^');
987 try writer.writeByteNTimes('~', truncated_visual_info.after_len);983 try writer.splatByteAll('~', truncated_visual_info.after_len);
988 try writer.writeByte('\n');984 try writer.writeByte('\n');
989 try tty_config.setColor(writer, .reset);985 try tty_config.setColor(writer, .reset);
990986
lib/compiler/resinator/lex.zig+3-1
...@@ -237,7 +237,9 @@ pub const Lexer = struct {...@@ -237,7 +237,9 @@ pub const Lexer = struct {
237 }237 }
238238
239 pub fn dump(self: *Self, token: *const Token) void {239 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 });
241 }243 }
242244
243 pub const LexMethod = enum {245 pub const LexMethod = enum {
lib/compiler/resinator/main.zig+17-13
...@@ -22,14 +22,14 @@ pub fn main() !void {...@@ -22,14 +22,14 @@ pub fn main() !void {
22 defer arena_state.deinit();22 defer arena_state.deinit();
23 const arena = arena_state.allocator();23 const arena = arena_state.allocator();
2424
25 const stderr = std.io.getStdErr();25 const stderr = std.fs.File.stderr();
26 const stderr_config = std.io.tty.detectConfig(stderr);26 const stderr_config = std.io.tty.detectConfig(stderr);
2727
28 const args = try std.process.argsAlloc(allocator);28 const args = try std.process.argsAlloc(allocator);
29 defer std.process.argsFree(allocator, args);29 defer std.process.argsFree(allocator, args);
3030
31 if (args.len < 2) {31 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", .{});
33 std.process.exit(1);33 std.process.exit(1);
34 }34 }
35 const zig_lib_dir = args[1];35 const zig_lib_dir = args[1];
...@@ -44,7 +44,7 @@ pub fn main() !void {...@@ -44,7 +44,7 @@ pub fn main() !void {
44 var error_handler: ErrorHandler = switch (zig_integration) {44 var error_handler: ErrorHandler = switch (zig_integration) {
45 true => .{45 true => .{
46 .server = .{46 .server = .{
47 .out = std.io.getStdOut(),47 .out = std.fs.File.stdout(),
48 .in = undefined, // won't be receiving messages48 .in = undefined, // won't be receiving messages
49 .receive_fifo = undefined, // won't be receiving messages49 .receive_fifo = undefined, // won't be receiving messages
50 },50 },
...@@ -81,15 +81,15 @@ pub fn main() !void {...@@ -81,15 +81,15 @@ pub fn main() !void {
81 defer options.deinit();81 defer options.deinit();
8282
83 if (options.print_help_and_exit) {83 if (options.print_help_and_exit) {
84 const stdout = std.io.getStdOut();84 const stdout = std.fs.File.stdout();
85 try cli.writeUsage(stdout.writer(), "zig rc");85 try cli.writeUsage(stdout.deprecatedWriter(), "zig rc");
86 return;86 return;
87 }87 }
8888
89 // Don't allow verbose when integrating with Zig via stdout89 // Don't allow verbose when integrating with Zig via stdout
90 options.verbose = false;90 options.verbose = false;
9191
92 const stdout_writer = std.io.getStdOut().writer();92 const stdout_writer = std.fs.File.stdout().deprecatedWriter();
93 if (options.verbose) {93 if (options.verbose) {
94 try options.dumpVerbose(stdout_writer);94 try options.dumpVerbose(stdout_writer);
95 try stdout_writer.writeByte('\n');95 try stdout_writer.writeByte('\n');
...@@ -290,7 +290,7 @@ pub fn main() !void {...@@ -290,7 +290,7 @@ pub fn main() !void {
290 };290 };
291 defer depfile.close();291 defer depfile.close();
292292
293 const depfile_writer = depfile.writer();293 const depfile_writer = depfile.deprecatedWriter();
294 var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer);294 var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer);
295 switch (options.depfile_fmt) {295 switch (options.depfile_fmt) {
296 .json => {296 .json => {
...@@ -343,7 +343,7 @@ pub fn main() !void {...@@ -343,7 +343,7 @@ pub fn main() !void {
343 switch (err) {343 switch (err) {
344 error.DuplicateResource => {344 error.DuplicateResource => {
345 const duplicate_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];345 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}]", .{
347 duplicate_resource.name_value,347 duplicate_resource.name_value,
348 fmtResourceType(duplicate_resource.type_value),348 fmtResourceType(duplicate_resource.type_value),
349 duplicate_resource.language,349 duplicate_resource.language,
...@@ -352,7 +352,7 @@ pub fn main() !void {...@@ -352,7 +352,7 @@ pub fn main() !void {
352 error.ResourceDataTooLong => {352 error.ResourceDataTooLong => {
353 const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];353 const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];
354 try error_handler.emitMessage(allocator, .err, "resource has a data length that is too large to be written into a coff section", .{});354 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}]", .{
356 overflow_resource.name_value,356 overflow_resource.name_value,
357 fmtResourceType(overflow_resource.type_value),357 fmtResourceType(overflow_resource.type_value),
358 overflow_resource.language,358 overflow_resource.language,
...@@ -361,7 +361,7 @@ pub fn main() !void {...@@ -361,7 +361,7 @@ pub fn main() !void {
361 error.TotalResourceDataTooLong => {361 error.TotalResourceDataTooLong => {
362 const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];362 const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];
363 try error_handler.emitMessage(allocator, .err, "total resource data exceeds the maximum of the coff 'size of raw data' field", .{});363 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}]", .{
365 overflow_resource.name_value,365 overflow_resource.name_value,
366 fmtResourceType(overflow_resource.type_value),366 fmtResourceType(overflow_resource.type_value),
367 overflow_resource.language,367 overflow_resource.language,
...@@ -471,7 +471,7 @@ const IoStream = struct {...@@ -471,7 +471,7 @@ const IoStream = struct {
471 allocator: std.mem.Allocator,471 allocator: std.mem.Allocator,
472 };472 };
473 pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError;473 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
476 pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize {476 pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize {
477 switch (ctx.self.*) {477 switch (ctx.self.*) {
...@@ -645,7 +645,9 @@ const ErrorHandler = union(enum) {...@@ -645,7 +645,9 @@ const ErrorHandler = union(enum) {
645 },645 },
646 .tty => {646 .tty => {
647 // extra newline to separate this line from the aro errors647 // 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});
649 aro.Diagnostics.render(comp, self.tty);651 aro.Diagnostics.render(comp, self.tty);
650 },652 },
651 }653 }
...@@ -690,7 +692,9 @@ const ErrorHandler = union(enum) {...@@ -690,7 +692,9 @@ const ErrorHandler = union(enum) {
690 try server.serveErrorBundle(error_bundle);692 try server.serveErrorBundle(error_bundle);
691 },693 },
692 .tty => {694 .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);
694 },698 },
695 }699 }
696 }700 }
lib/compiler/resinator/res.zig+11-31
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
2const rc = @import("rc.zig");3const rc = @import("rc.zig");
3const ResourceType = rc.ResourceType;4const ResourceType = rc.ResourceType;
4const CommonResourceAttributes = rc.CommonResourceAttributes;5const CommonResourceAttributes = rc.CommonResourceAttributes;
...@@ -163,14 +164,7 @@ pub const Language = packed struct(u16) {...@@ -163,14 +164,7 @@ pub const Language = packed struct(u16) {
163 return @bitCast(self);164 return @bitCast(self);
164 }165 }
165166
166 pub fn format(167 pub fn format(language: Language, w: *std.io.Writer) std.io.Writer.Error!void {
167 language: Language,
168 comptime fmt: []const u8,
169 options: std.fmt.FormatOptions,
170 out_stream: anytype,
171 ) !void {
172 _ = fmt;
173 _ = options;
174 const language_id = language.asInt();168 const language_id = language.asInt();
175 const language_name = language_name: {169 const language_name = language_name: {
176 if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {170 if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| {
...@@ -181,7 +175,7 @@ pub const Language = packed struct(u16) {...@@ -181,7 +175,7 @@ pub const Language = packed struct(u16) {
181 }175 }
182 break :language_name "<UNKNOWN>";176 break :language_name "<UNKNOWN>";
183 };177 };
184 try out_stream.print("{s} (0x{X})", .{ language_name, language_id });178 try w.print("{s} (0x{X})", .{ language_name, language_id });
185 }179 }
186};180};
187181
...@@ -445,47 +439,33 @@ pub const NameOrOrdinal = union(enum) {...@@ -445,47 +439,33 @@ pub const NameOrOrdinal = union(enum) {
445 }439 }
446 }440 }
447441
448 pub fn format(442 pub fn format(self: NameOrOrdinal, w: *std.io.Writer) !void {
449 self: NameOrOrdinal,
450 comptime fmt: []const u8,
451 options: std.fmt.FormatOptions,
452 out_stream: anytype,
453 ) !void {
454 _ = fmt;
455 _ = options;
456 switch (self) {443 switch (self) {
457 .name => |name| {444 .name => |name| {
458 try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)});445 try w.print("{f}", .{std.unicode.fmtUtf16Le(name)});
459 },446 },
460 .ordinal => |ordinal| {447 .ordinal => |ordinal| {
461 try out_stream.print("{d}", .{ordinal});448 try w.print("{d}", .{ordinal});
462 },449 },
463 }450 }
464 }451 }
465452
466 fn formatResourceType(453 fn formatResourceType(self: NameOrOrdinal, w: *std.io.Writer) std.io.Writer.Error!void {
467 self: NameOrOrdinal,
468 comptime fmt: []const u8,
469 options: std.fmt.FormatOptions,
470 out_stream: anytype,
471 ) !void {
472 _ = fmt;
473 _ = options;
474 switch (self) {454 switch (self) {
475 .name => |name| {455 .name => |name| {
476 try out_stream.print("{s}", .{std.unicode.fmtUtf16Le(name)});456 try w.print("{f}", .{std.unicode.fmtUtf16Le(name)});
477 },457 },
478 .ordinal => |ordinal| {458 .ordinal => |ordinal| {
479 if (std.enums.tagName(RT, @enumFromInt(ordinal))) |predefined_type_name| {459 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});
481 } else {461 } else {
482 try out_stream.print("{d}", .{ordinal});462 try w.print("{d}", .{ordinal});
483 }463 }
484 },464 },
485 }465 }
486 }466 }
487467
488 pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(formatResourceType) {468 pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(NameOrOrdinal, formatResourceType) {
489 return .{ .data = type_value };469 return .{ .data = type_value };
490 }470 }
491};471};
lib/compiler/resinator/utils.zig+1-1
...@@ -86,7 +86,7 @@ pub const ErrorMessageType = enum { err, warning, note };...@@ -86,7 +86,7 @@ pub const ErrorMessageType = enum { err, warning, note };
8686
87/// Used for generic colored errors/warnings/notes, more context-specific error messages87/// Used for generic colored errors/warnings/notes, more context-specific error messages
88/// are handled elsewhere.88/// 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 {
90 switch (msg_type) {90 switch (msg_type) {
91 .err => {91 .err => {
92 try config.setColor(writer, .bold);92 try config.setColor(writer, .bold);
lib/compiler/test_runner.zig+2-2
...@@ -303,7 +303,7 @@ pub fn mainSimple() anyerror!void {...@@ -303,7 +303,7 @@ pub fn mainSimple() anyerror!void {
303 var failed: u64 = 0;303 var failed: u64 = 0;
304304
305 // we don't want to bring in File and Writer if the backend doesn't support it305 // 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
308 for (builtin.test_functions) |test_fn| {308 for (builtin.test_functions) |test_fn| {
309 if (test_fn.func()) |_| {309 if (test_fn.func()) |_| {
...@@ -330,7 +330,7 @@ pub fn mainSimple() anyerror!void {...@@ -330,7 +330,7 @@ pub fn mainSimple() anyerror!void {
330 passed += 1;330 passed += 1;
331 }331 }
332 if (enable_print and print_summary) {332 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 {};
334 }334 }
335 if (failed != 0) std.process.exit(1);335 if (failed != 0) std.process.exit(1);
336}336}
lib/compiler_rt/clear_cache.zig+20
...@@ -86,6 +86,26 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void {...@@ -86,6 +86,26 @@ fn clear_cache(start: usize, end: usize) callconv(.c) void {
86 const result = std.os.linux.syscall3(.cacheflush, start, end - start, flags);86 const result = std.os.linux.syscall3(.cacheflush, start, end - start, flags);
87 std.debug.assert(result == 0);87 std.debug.assert(result == 0);
88 exportIt();88 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();
89 } else if (mips and os == .openbsd) {109 } else if (mips and os == .openbsd) {
90 // TODO110 // TODO
91 //cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE);111 //cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE);
lib/compiler_rt/emutls.zig+1-1
...@@ -18,7 +18,7 @@ const gcc_word = usize;...@@ -18,7 +18,7 @@ const gcc_word = usize;
18pub const panic = common.panic;18pub const panic = common.panic;
1919
20comptime {20comptime {
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)) {
22 @export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility });22 @export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility });
23 }23 }
24}24}
lib/docs/wasm/Walk.zig+1-12
...@@ -238,12 +238,8 @@ pub const File = struct {...@@ -238,12 +238,8 @@ pub const File = struct {
238238
239 .call_one,239 .call_one,
240 .call_one_comma,240 .call_one_comma,
241 .async_call_one,
242 .async_call_one_comma,
243 .call,241 .call,
244 .call_comma,242 .call_comma,
245 .async_call,
246 .async_call_comma,
247 => {243 => {
248 var buf: [1]Ast.Node.Index = undefined;244 var buf: [1]Ast.Node.Index = undefined;
249 return categorize_call(file_index, node, ast.fullCall(&buf, node).?);245 return categorize_call(file_index, node, ast.fullCall(&buf, node).?);
...@@ -450,7 +446,7 @@ fn parse(file_name: []const u8, source: []u8) Oom!Ast {...@@ -450,7 +446,7 @@ fn parse(file_name: []const u8, source: []u8) Oom!Ast {
450 error.WriteFailed => return error.OutOfMemory,446 error.WriteFailed => return error.OutOfMemory,
451 };447 };
452 }448 }
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 });
454 }450 }
455 return Ast.parse(gpa, "", .zig);451 return Ast.parse(gpa, "", .zig);
456 }452 }
...@@ -577,7 +573,6 @@ fn struct_decl(...@@ -577,7 +573,6 @@ fn struct_decl(
577 },573 },
578574
579 .@"comptime",575 .@"comptime",
580 .@"usingnamespace",
581 => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node),576 => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node),
582577
583 .test_decl => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).opt_token_and_node[1]),578 .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)...@@ -649,7 +644,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
649 const ast = w.file.get_ast();644 const ast = w.file.get_ast();
650 switch (ast.nodeTag(node)) {645 switch (ast.nodeTag(node)) {
651 .root => unreachable, // Top-level declaration.646 .root => unreachable, // Top-level declaration.
652 .@"usingnamespace" => unreachable, // Top-level declaration.
653 .test_decl => unreachable, // Top-level declaration.647 .test_decl => unreachable, // Top-level declaration.
654 .container_field_init => unreachable, // Top-level declaration.648 .container_field_init => unreachable, // Top-level declaration.
655 .container_field_align => unreachable, // Top-level declaration.649 .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)...@@ -749,7 +743,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
749 .@"comptime",743 .@"comptime",
750 .@"nosuspend",744 .@"nosuspend",
751 .@"suspend",745 .@"suspend",
752 .@"await",
753 .@"resume",746 .@"resume",
754 .@"try",747 .@"try",
755 => try expr(w, scope, parent_decl, ast.nodeData(node).node),748 => 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)...@@ -812,12 +805,8 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
812805
813 .call_one,806 .call_one,
814 .call_one_comma,807 .call_one_comma,
815 .async_call_one,
816 .async_call_one_comma,
817 .call,808 .call,
818 .call_comma,809 .call_comma,
819 .async_call,
820 .async_call_comma,
821 => {810 => {
822 var buf: [1]Ast.Node.Index = undefined;811 var buf: [1]Ast.Node.Index = undefined;
823 const full = ast.fullCall(&buf, node).?;812 const full = ast.fullCall(&buf, node).?;
lib/docs/wasm/html_render.zig-3
...@@ -101,8 +101,6 @@ pub fn fileSourceHtml(...@@ -101,8 +101,6 @@ pub fn fileSourceHtml(
101 .keyword_align,101 .keyword_align,
102 .keyword_and,102 .keyword_and,
103 .keyword_asm,103 .keyword_asm,
104 .keyword_async,
105 .keyword_await,
106 .keyword_break,104 .keyword_break,
107 .keyword_catch,105 .keyword_catch,
108 .keyword_comptime,106 .keyword_comptime,
...@@ -139,7 +137,6 @@ pub fn fileSourceHtml(...@@ -139,7 +137,6 @@ pub fn fileSourceHtml(
139 .keyword_try,137 .keyword_try,
140 .keyword_union,138 .keyword_union,
141 .keyword_unreachable,139 .keyword_unreachable,
142 .keyword_usingnamespace,
143 .keyword_var,140 .keyword_var,
144 .keyword_volatile,141 .keyword_volatile,
145 .keyword_allowzero,142 .keyword_allowzero,
lib/docs/wasm/markdown.zig+2-2
...@@ -143,7 +143,7 @@ fn mainImpl() !void {...@@ -143,7 +143,7 @@ fn mainImpl() !void {
143 var parser = try Parser.init(gpa);143 var parser = try Parser.init(gpa);
144 defer parser.deinit();144 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());
147 var line_buf = std.ArrayList(u8).init(gpa);147 var line_buf = std.ArrayList(u8).init(gpa);
148 defer line_buf.deinit();148 defer line_buf.deinit();
149 while (stdin_buf.reader().streamUntilDelimiter(line_buf.writer(), '\n', null)) {149 while (stdin_buf.reader().streamUntilDelimiter(line_buf.writer(), '\n', null)) {
...@@ -158,7 +158,7 @@ fn mainImpl() !void {...@@ -158,7 +158,7 @@ fn mainImpl() !void {
158 var doc = try parser.endInput();158 var doc = try parser.endInput();
159 defer doc.deinit(gpa);159 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());
162 try doc.render(stdout_buf.writer());162 try doc.render(stdout_buf.writer());
163 try stdout_buf.flush();163 try stdout_buf.flush();
164}164}
lib/fuzzer.zig+12-9
...@@ -9,7 +9,8 @@ pub const std_options = std.Options{...@@ -9,7 +9,8 @@ pub const std_options = std.Options{
9 .logFn = logOverride,9 .logFn = logOverride,
10};10};
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
14fn logOverride(15fn logOverride(
15 comptime level: std.log.Level,16 comptime level: std.log.Level,
...@@ -17,15 +18,17 @@ fn logOverride(...@@ -17,15 +18,17 @@ fn logOverride(
17 comptime format: []const u8,18 comptime format: []const u8,
18 args: anytype,19 args: anytype,
19) void {20) void {
20 const f = if (log_file) |f| f else f: {21 const fw = if (log_file_writer) |*f| f else f: {
21 const f = fuzzer.cache_dir.createFile("tmp/libfuzzer.log", .{}) catch22 const f = fuzzer.cache_dir.createFile("tmp/libfuzzer.log", .{}) catch
22 @panic("failed to open fuzzer log file");23 @panic("failed to open fuzzer log file");
23 log_file = f;24 log_file_writer = f.writer(&log_file_buffer);
24 break :f f;25 break :f &log_file_writer.?;
25 };26 };
26 const prefix1 = comptime level.asText();27 const prefix1 = comptime level.asText();
27 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";28 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");
29}32}
3033
31/// Helps determine run uniqueness in the face of recursion.34/// Helps determine run uniqueness in the face of recursion.
...@@ -226,18 +229,18 @@ const Fuzzer = struct {...@@ -226,18 +229,18 @@ const Fuzzer = struct {
226 .read = true,229 .read = true,
227 }) catch |e| switch (e) {230 }) catch |e| switch (e) {
228 error.PathAlreadyExists => continue,231 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) }),
230 };233 };
231 errdefer input_file.close();234 errdefer input_file.close();
232 // Initialize the mmap for the current input.235 // Initialize the mmap for the current input.
233 f.input = MemoryMappedList.create(input_file, 0, std.heap.page_size_max) catch |e| {236 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}", .{
235 f.corpus_directory, i, @errorName(e),238 f.corpus_directory, i, @errorName(e),
236 });239 });
237 };240 };
238 break;241 break;
239 },242 },
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) }),
241 };244 };
242 errdefer gpa.free(input);245 errdefer gpa.free(input);
243 f.corpus.append(gpa, .{246 f.corpus.append(gpa, .{
...@@ -263,7 +266,7 @@ const Fuzzer = struct {...@@ -263,7 +266,7 @@ const Fuzzer = struct {
263 const sub_path = try std.fmt.allocPrint(gpa, "f/{s}", .{f.unit_test_name});266 const sub_path = try std.fmt.allocPrint(gpa, "f/{s}", .{f.unit_test_name});
264 f.corpus_directory = .{267 f.corpus_directory = .{
265 .handle = f.cache_dir.makeOpenPath(sub_path, .{}) catch |err|268 .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 }),
267 .path = sub_path,270 .path = sub_path,
268 };271 };
269 initNextInput(f);272 initNextInput(f);
lib/init/src/root.zig+1-1
...@@ -5,7 +5,7 @@ pub fn bufferedPrint() !void {...@@ -5,7 +5,7 @@ pub fn bufferedPrint() !void {
5 // Stdout is for the actual output of your application, for example if you5 // Stdout is for the actual output of your application, for example if you
6 // are implementing gzip, then only the compressed bytes should be sent to6 // are implementing gzip, then only the compressed bytes should be sent to
7 // stdout, not any debugging messages.7 // stdout, not any debugging messages.
8 const stdout_file = std.io.getStdOut().writer();8 const stdout_file = std.fs.File.stdout().deprecatedWriter();
9 // Buffering can improve performance significantly in print-heavy programs.9 // Buffering can improve performance significantly in print-heavy programs.
10 var bw = std.io.bufferedWriter(stdout_file);10 var bw = std.io.bufferedWriter(stdout_file);
11 const stdout = bw.writer();11 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,20 +1,5 @@
1.global floorf1/* zig patch: removed `floorl` and `floorf` in favor of using zig compiler_rt's implementations */
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
122
13.global floor
14.type floor,@function
15floor:
16 fldl 4(%esp)
171: mov $0x7,%al
181: fstcw 4(%esp)31: fstcw 4(%esp)
19 mov 5(%esp),%ah4 mov 5(%esp),%ah
20 mov %al,5(%esp)5 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 {...@@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct {
24662466
2467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {2467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {
2468 return gen.path orelse {2468 return gen.path orelse {
2469 std.debug.lockStdErr();2469 const w = debug.lockStderrWriter(&.{});
2470 const stderr = std.io.getStdErr();2470 dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2471 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};2471 debug.unlockStderrWriter();
2472 std.debug.unlockStdErr();
2473 @panic("misconfigured build script");2472 @panic("misconfigured build script");
2474 };2473 };
2475 }2474 }
...@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {...@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {
2676 var file_path: Cache.Path = .{2675 var file_path: Cache.Path = .{
2677 .root_dir = Cache.Directory.cwd(),2676 .root_dir = Cache.Directory.cwd(),
2678 .sub_path = gen.file.path orelse {2677 .sub_path = gen.file.path orelse {
2679 std.debug.lockStdErr();2678 const w = debug.lockStderrWriter(&.{});
2680 const stderr: fs.File = .stderr();2679 dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2681 dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};2680 debug.unlockStderrWriter();
2682 std.debug.unlockStdErr();
2683 @panic("misconfigured build script");2681 @panic("misconfigured build script");
2684 },2682 },
2685 };2683 };
...@@ -2769,17 +2767,16 @@ fn dumpBadDirnameHelp(...@@ -2769,17 +2767,16 @@ fn dumpBadDirnameHelp(
2769 const w = debug.lockStderrWriter(&.{});2767 const w = debug.lockStderrWriter(&.{});
2770 defer debug.unlockStderrWriter();2768 defer debug.unlockStderrWriter();
27712769
2772 const stderr: fs.File = .stderr();
2773 try w.print(msg, args);2770 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
2777 if (fail_step) |s| {2774 if (fail_step) |s| {
2778 tty_config.setColor(w, .red) catch {};2775 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");
2780 tty_config.setColor(w, .reset) catch {};2777 tty_config.setColor(w, .reset) catch {};
27812778
2782 s.dump(stderr);2779 s.dump(w, tty_config);
2783 }2780 }
27842781
2785 if (asking_step) |as| {2782 if (asking_step) |as| {
...@@ -2787,24 +2784,23 @@ fn dumpBadDirnameHelp(...@@ -2787,24 +2784,23 @@ fn dumpBadDirnameHelp(
2787 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});2784 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2788 tty_config.setColor(w, .reset) catch {};2785 tty_config.setColor(w, .reset) catch {};
27892786
2790 as.dump(stderr);2787 as.dump(w, tty_config);
2791 }2788 }
27922789
2793 tty_config.setColor(w, .red) catch {};2790 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");
2795 tty_config.setColor(w, .reset) catch {};2792 tty_config.setColor(w, .reset) catch {};
2796}2793}
27972794
2798/// In this function the stderr mutex has already been locked.2795/// In this function the stderr mutex has already been locked.
2799pub fn dumpBadGetPathHelp(2796pub fn dumpBadGetPathHelp(
2800 s: *Step,2797 s: *Step,
2801 stderr: fs.File,2798 w: *std.io.Writer,
2799 tty_config: std.io.tty.Config,
2802 src_builder: *Build,2800 src_builder: *Build,
2803 asking_step: ?*Step,2801 asking_step: ?*Step,
2804) anyerror!void {2802) anyerror!void {
2805 var fw = stderr.writer(&.{});2803 try w.print(
2806 const bw = &fw.interface;
2807 try bw.print(
2808 \\getPath() was called on a GeneratedFile that wasn't built yet.2804 \\getPath() was called on a GeneratedFile that wasn't built yet.
2809 \\ source package path: {s}2805 \\ source package path: {s}
2810 \\ Is there a missing Step dependency on step '{s}'?2806 \\ Is there a missing Step dependency on step '{s}'?
...@@ -2814,22 +2810,21 @@ pub fn dumpBadGetPathHelp(...@@ -2814,22 +2810,21 @@ pub fn dumpBadGetPathHelp(
2814 s.name,2810 s.name,
2815 });2811 });
28162812
2817 const tty_config = std.io.tty.detectConfig(stderr);2813 tty_config.setColor(w, .red) catch {};
2818 tty_config.setColor(&bw, .red) catch {};2814 try w.writeAll(" The step was created by this stack trace:\n");
2819 try stderr.writeAll(" The step was created by this stack trace:\n");2815 tty_config.setColor(w, .reset) catch {};
2820 tty_config.setColor(&bw, .reset) catch {};
28212816
2822 s.dump(stderr);2817 s.dump(w, tty_config);
2823 if (asking_step) |as| {2818 if (asking_step) |as| {
2824 tty_config.setColor(&bw, .red) catch {};2819 tty_config.setColor(w, .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});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});
2826 tty_config.setColor(&bw, .reset) catch {};2821 tty_config.setColor(w, .reset) catch {};
28272822
2828 as.dump(stderr);2823 as.dump(w, tty_config);
2829 }2824 }
2830 tty_config.setColor(&bw, .red) catch {};2825 tty_config.setColor(w, .red) catch {};
2831 try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");2826 try w.writeAll(" Hope that helps. Proceeding to panic.\n");
2832 tty_config.setColor(&bw, .reset) catch {};2827 tty_config.setColor(w, .reset) catch {};
2833}2828}
28342829
2835pub const InstallDir = union(enum) {2830pub const InstallDir = union(enum) {
...@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {...@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {
2866 return result_path;2861 return result_path;
2867}2862}
28682863
2869/// Deprecated; use `std.fmt.hex` instead.
2870pub fn hex64(x: u64) [16]u8 {
2871 return std.fmt.hex(x);
2872}
2873
2874/// A pair of target query and fully resolved target.2864/// A pair of target query and fully resolved target.
2875/// This type is generally required by build system API that need to be given a2865/// This type is generally required by build system API that need to be given a
2876/// target. The query is kept because the Zig toolchain needs to know which parts2866/// 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 @@...@@ -2,6 +2,18 @@
2//! This is not a general-purpose cache. It is designed to be fast and simple,2//! This is not a general-purpose cache. It is designed to be fast and simple,
3//! not to withstand attacks using specially-crafted input.3//! 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
5gpa: Allocator,17gpa: Allocator,
6manifest_dir: fs.Dir,18manifest_dir: fs.Dir,
7hash: HashHelper = .{},19hash: HashHelper = .{},
...@@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig");...@@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig");
21pub const Directory = @import("Cache/Directory.zig");33pub const Directory = @import("Cache/Directory.zig");
22pub const DepTokenizer = @import("Cache/DepTokenizer.zig");34pub 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
36pub fn addPrefix(cache: *Cache, directory: Directory) void {36pub fn addPrefix(cache: *Cache, directory: Directory) void {
37 cache.prefixes_buffer[cache.prefixes_len] = directory;37 cache.prefixes_buffer[cache.prefixes_len] = directory;
38 cache.prefixes_len += 1;38 cache.prefixes_len += 1;
...@@ -1118,25 +1118,12 @@ pub const Manifest = struct {...@@ -1118,25 +1118,12 @@ pub const Manifest = struct {
1118 if (self.manifest_dirty) {1118 if (self.manifest_dirty) {
1119 self.manifest_dirty = false;1119 self.manifest_dirty = false;
11201120
1121 const gpa = self.cache.gpa;1121 var buffer: [4000]u8 = undefined;
1122 var contents: std.ArrayListUnmanaged(u8) = .empty;1122 var fw = manifest_file.writer(&buffer);
1123 defer contents.deinit(gpa);1123 writeDirtyManifestToStream(self, &fw) catch |err| switch (err) {
11241124 error.WriteFailed => return fw.err.?,
1125 try contents.appendSlice(gpa, manifest_header ++ "\n");1125 else => |e| return e,
1126 for (self.files.keys()) |file| {1126 };
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);
1140 }1127 }
11411128
1142 if (self.want_shared_lock) {1129 if (self.want_shared_lock) {
...@@ -1144,6 +1131,21 @@ pub const Manifest = struct {...@@ -1144,6 +1131,21 @@ pub const Manifest = struct {
1144 }1131 }
1145 }1132 }
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
1147 fn downgradeToSharedLock(self: *Manifest) !void {1149 fn downgradeToSharedLock(self: *Manifest) !void {
1148 if (!self.have_exclusive_lock) return;1150 if (!self.have_exclusive_lock) return;
11491151
lib/std/Build/Cache/Directory.zig+4-4
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const Directory = @This();1const Directory = @This();
2const std = @import("../../std.zig");2const std = @import("../../std.zig");
3const assert = std.debug.assert;
3const fs = std.fs;4const fs = std.fs;
4const fmt = std.fmt;5const fmt = std.fmt;
5const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
...@@ -55,11 +56,10 @@ pub fn closeAndFree(self: *Directory, gpa: Allocator) void {...@@ -55,11 +56,10 @@ pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
55 self.* = undefined;56 self.* = undefined;
56}57}
5758
58pub fn format(self: Directory, w: *std.io.Writer, comptime fmt_string: []const u8) !void {59pub fn format(self: Directory, writer: *std.io.Writer) std.io.Writer.Error!void {
59 if (fmt_string.len != 0) fmt.invalidFmtError(fmt_string, self);
60 if (self.path) |p| {60 if (self.path) |p| {
61 try w.writeAll(p);61 try writer.writeAll(p);
62 try w.writeAll(fs.path.sep_str);62 try writer.writeAll(fs.path.sep_str);
63 }63 }
64}64}
6565
lib/std/Build/Cache/Path.zig+42-32
...@@ -1,3 +1,10 @@...@@ -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
1root_dir: Cache.Directory,8root_dir: Cache.Directory,
2/// The path, relative to the root dir, that this `Path` represents.9/// The path, relative to the root dir, that this `Path` represents.
3/// Empty string means the root_dir is the path.10/// Empty string means the root_dir is the path.
...@@ -137,46 +144,55 @@ pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 {...@@ -137,46 +144,55 @@ pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 {
137}144}
138145
139pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 {146pub 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);
141}148}
142149
143pub fn format(self: Path, w: *std.io.Writer, comptime fmt_string: []const u8) !void {150pub fn fmtEscapeString(path: Path) std.fmt.Formatter(Path, formatEscapeString) {
144 if (fmt_string.len == 1) {151 return .{ .data = path };
145 // Quote-escape the string.152}
146 const stringEscape = std.zig.stringEscape;153
147 const f = switch (fmt_string[0]) {154pub fn formatEscapeString(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void {
148 'q' => "",155 if (path.root_dir.path) |p| {
149 '\'' => "\'",156 try std.zig.stringEscape(p, writer);
150 else => @compileError("unsupported format string: " ++ fmt_string),157 if (path.sub_path.len > 0) try std.zig.stringEscape(fs.path.sep_str, writer);
151 };158 }
152 if (self.root_dir.path) |p| {159 if (path.sub_path.len > 0) {
153 try stringEscape(p, w, f);160 try std.zig.stringEscape(path.sub_path, writer);
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;
160 }161 }
161 if (fmt_string.len > 0)162}
162 std.fmt.invalidFmtError(fmt_string, self);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 {
163 if (std.fs.path.isAbsolute(self.sub_path)) {179 if (std.fs.path.isAbsolute(self.sub_path)) {
164 try w.writeAll(self.sub_path);180 try writer.writeAll(self.sub_path);
165 return;181 return;
166 }182 }
167 if (self.root_dir.path) |p| {183 if (self.root_dir.path) |p| {
168 try w.writeAll(p);184 try writer.writeAll(p);
169 if (self.sub_path.len > 0) {185 if (self.sub_path.len > 0) {
170 try w.writeAll(fs.path.sep_str);186 try writer.writeAll(fs.path.sep_str);
171 try w.writeAll(self.sub_path);187 try writer.writeAll(self.sub_path);
172 }188 }
173 return;189 return;
174 }190 }
175 if (self.sub_path.len > 0) {191 if (self.sub_path.len > 0) {
176 try w.writeAll(self.sub_path);192 try writer.writeAll(self.sub_path);
177 return;193 return;
178 }194 }
179 try w.writeByte('.');195 try writer.writeByte('.');
180}196}
181197
182pub fn eql(self: Path, other: Path) bool {198pub fn eql(self: Path, other: Path) bool {
...@@ -218,9 +234,3 @@ pub const TableAdapter = struct {...@@ -218,9 +234,3 @@ pub const TableAdapter = struct {
218 return a.eql(b);234 return a.eql(b);
219 }235 }
220};236};
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...@@ -124,9 +124,10 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par
124 const show_stderr = compile.step.result_stderr.len > 0;124 const show_stderr = compile.step.result_stderr.len > 0;
125125
126 if (show_error_msgs or show_compile_errors or show_stderr) {126 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);
128 defer std.debug.unlockStderrWriter();129 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 {};
130 }131 }
131132
132 const rebuilt_bin_path = result catch |err| switch (err) {133 const rebuilt_bin_path = result catch |err| switch (err) {
...@@ -151,9 +152,10 @@ fn fuzzWorkerRun(...@@ -151,9 +152,10 @@ fn fuzzWorkerRun(
151152
152 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {153 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
153 error.MakeFailed => {154 error.MakeFailed => {
154 const bw = std.debug.lockStderrWriter(&.{});155 var buf: [256]u8 = undefined;
156 const w = std.debug.lockStderrWriter(&buf);
155 defer std.debug.unlockStderrWriter();157 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 {};
157 return;159 return;
158 },160 },
159 else => {161 else => {
lib/std/Build/Fuzz/WebServer.zig+1-1
...@@ -176,7 +176,7 @@ fn serveFile(...@@ -176,7 +176,7 @@ fn serveFile(
176 // We load the file with every request so that the user can make changes to the file176 // We load the file with every request so that the user can make changes to the file
177 // and refresh the HTML page without restarting this server.177 // and refresh the HTML page without restarting this server.
178 const file_contents = ws.zig_lib_directory.handle.readFileAlloc(name, gpa, .limited(10 * 1024 * 1024)) catch |err| {178 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 });
180 return error.AlreadyReported;180 return error.AlreadyReported;
181 };181 };
182 defer gpa.free(file_contents);182 defer gpa.free(file_contents);
lib/std/Build/Module.zig+2-2
...@@ -186,7 +186,7 @@ pub const IncludeDir = union(enum) {...@@ -186,7 +186,7 @@ pub const IncludeDir = union(enum) {
186 .embed_path => |lazy_path| {186 .embed_path => |lazy_path| {
187 // Special case: this is a single arg.187 // Special case: this is a single arg.
188 const resolved = lazy_path.getPath3(b, asking_step);188 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});
190 return zig_args.append(arg);190 return zig_args.append(arg);
191 },191 },
192 };192 };
...@@ -572,7 +572,7 @@ pub fn appendZigProcessFlags(...@@ -572,7 +572,7 @@ pub fn appendZigProcessFlags(
572 try zig_args.append(switch (unwind_tables) {572 try zig_args.append(switch (unwind_tables) {
573 .none => "-fno-unwind-tables",573 .none => "-fno-unwind-tables",
574 .sync => "-funwind-tables",574 .sync => "-funwind-tables",
575 .@"async" => "-fasync-unwind-tables",575 .async => "-fasync-unwind-tables",
576 });576 });
577 }577 }
578578
lib/std/Build/Step.zig+10-13
...@@ -286,28 +286,25 @@ pub fn cast(step: *Step, comptime T: type) ?*T {...@@ -286,28 +286,25 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
286}286}
287287
288/// For debugging purposes, prints identifying information about this Step.288/// For debugging purposes, prints identifying information about this Step.
289pub fn dump(step: *Step, file: std.fs.File) void {289pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void {
290 var fw = file.writer(&.{});
291 const bw = &fw.interface;
292 const tty_config = std.io.tty.detectConfig(file);
293 const debug_info = std.debug.getSelfDebugInfo() catch |err| {290 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", .{
295 @errorName(err),292 @errorName(err),
296 }) catch {};293 }) catch {};
297 return;294 return;
298 };295 };
299 if (step.getStackTrace()) |stack_trace| {296 if (step.getStackTrace()) |stack_trace| {
300 bw.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};297 w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
301 std.debug.writeStackTrace(stack_trace, &bw, debug_info, tty_config) catch |err| {298 std.debug.writeStackTrace(stack_trace, w, debug_info, tty_config) catch |err| {
302 bw.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};299 w.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {};
303 return;300 return;
304 };301 };
305 } else {302 } else {
306 const field = "debug_stack_frames_count";303 const field = "debug_stack_frames_count";
307 comptime assert(@hasField(Build, field));304 comptime assert(@hasField(Build, field));
308 tty_config.setColor(&bw, .yellow) catch {};305 tty_config.setColor(w, .yellow) catch {};
309 bw.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {};306 w.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 {};307 tty_config.setColor(w, .reset) catch {};
311 }308 }
312}309}
313310
...@@ -483,9 +480,9 @@ pub fn evalZigProcess(...@@ -483,9 +480,9 @@ pub fn evalZigProcess(
483pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus {480pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus {
484 const b = s.owner;481 const b = s.owner;
485 const src_path = src_lazy_path.getPath3(b, s);482 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 });
487 return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| {484 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}", .{
489 src_path, dest_path, @errorName(err),486 src_path, dest_path, @errorName(err),
490 });487 });
491 };488 };
lib/std/Build/Step/CheckObject.zig+79-97
...@@ -230,16 +230,11 @@ const ComputeCompareExpected = struct {...@@ -230,16 +230,11 @@ const ComputeCompareExpected = struct {
230 literal: u64,230 literal: u64,
231 },231 },
232232
233 pub fn format(233 pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void {
234 value: ComputeCompareExpected,234 try w.print("{t} ", .{value.op});
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)});
240 switch (value.value) {235 switch (value.value) {
241 .variable => |name| try bw.writeAll(name),236 .variable => |name| try w.writeAll(name),
242 .literal => |x| try bw.print("{x}", .{x}),237 .literal => |x| try w.print("{x}", .{x}),
243 }238 }
244 }239 }
245};240};
...@@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {...@@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
571 null,566 null,
572 .of(u64),567 .of(u64),
573 null,568 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
576 var vars: std.StringHashMap(u64) = .init(gpa);573 var vars: std.StringHashMap(u64) = .init(gpa);
577 for (check_object.checks.items) |chk| {574 for (check_object.checks.items) |chk| {
...@@ -606,7 +603,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {...@@ -606,7 +603,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
606 // we either format message string with escaped codes, or not to aid debugging603 // we either format message string with escaped codes, or not to aid debugging
607 // the failed test.604 // the failed test.
608 const fmtMessageString = struct {605 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) {
610 return .{ .data = .{607 return .{ .data = .{
611 .kind = kind,608 .kind = kind,
612 .msg = msg,609 .msg = msg,
...@@ -618,15 +615,10 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {...@@ -618,15 +615,10 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
618 msg: []const u8,615 msg: []const u8,
619 };616 };
620617
621 fn formatMessageString(618 fn formatMessageString(ctx: Ctx, w: *Writer) !void {
622 ctx: Ctx,
623 bw: *Writer,
624 comptime unused_fmt_string: []const u8,
625 ) !void {
626 _ = unused_fmt_string;
627 switch (ctx.kind) {619 switch (ctx.kind) {
628 .dump_section => try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}),620 .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}),
629 else => try bw.writeAll(ctx.msg),621 else => try w.writeAll(ctx.msg),
630 }622 }
631 }623 }
632 }.fmtMessageString;624 }.fmtMessageString;
...@@ -882,9 +874,9 @@ const MachODumper = struct {...@@ -882,9 +874,9 @@ const MachODumper = struct {
882 try bw.writeByte('\n');874 try bw.writeByte('\n');
883 }875 }
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 {
886 // print header first878 // print header first
887 try bw.print(879 try writer.print(
888 \\LC {d}880 \\LC {d}
889 \\cmd {s}881 \\cmd {s}
890 \\cmdsize {d}882 \\cmdsize {d}
...@@ -893,8 +885,8 @@ const MachODumper = struct {...@@ -893,8 +885,8 @@ const MachODumper = struct {
893 switch (lc.cmd()) {885 switch (lc.cmd()) {
894 .SEGMENT_64 => {886 .SEGMENT_64 => {
895 const seg = lc.cast(macho.segment_command_64).?;887 const seg = lc.cast(macho.segment_command_64).?;
896 try bw.writeByte('\n');888 try writer.writeByte('\n');
897 try bw.print(889 try writer.print(
898 \\segname {s}890 \\segname {s}
899 \\vmaddr {x}891 \\vmaddr {x}
900 \\vmsize {x}892 \\vmsize {x}
...@@ -909,8 +901,8 @@ const MachODumper = struct {...@@ -909,8 +901,8 @@ const MachODumper = struct {
909 });901 });
910902
911 for (lc.getSections()) |sect| {903 for (lc.getSections()) |sect| {
912 try bw.writeByte('\n');904 try writer.writeByte('\n');
913 try bw.print(905 try writer.print(
914 \\sectname {s}906 \\sectname {s}
915 \\addr {x}907 \\addr {x}
916 \\size {x}908 \\size {x}
...@@ -932,8 +924,8 @@ const MachODumper = struct {...@@ -932,8 +924,8 @@ const MachODumper = struct {
932 .REEXPORT_DYLIB,924 .REEXPORT_DYLIB,
933 => {925 => {
934 const dylib = lc.cast(macho.dylib_command).?;926 const dylib = lc.cast(macho.dylib_command).?;
935 try bw.writeByte('\n');927 try writer.writeByte('\n');
936 try bw.print(928 try writer.print(
937 \\name {s}929 \\name {s}
938 \\timestamp {d}930 \\timestamp {d}
939 \\current version {x}931 \\current version {x}
...@@ -948,16 +940,16 @@ const MachODumper = struct {...@@ -948,16 +940,16 @@ const MachODumper = struct {
948940
949 .MAIN => {941 .MAIN => {
950 const main = lc.cast(macho.entry_point_command).?;942 const main = lc.cast(macho.entry_point_command).?;
951 try bw.writeByte('\n');943 try writer.writeByte('\n');
952 try bw.print(944 try writer.print(
953 \\entryoff {x}945 \\entryoff {x}
954 \\stacksize {x}946 \\stacksize {x}
955 , .{ main.entryoff, main.stacksize });947 , .{ main.entryoff, main.stacksize });
956 },948 },
957949
958 .RPATH => {950 .RPATH => {
959 try bw.writeByte('\n');951 try writer.writeByte('\n');
960 try bw.print(952 try writer.print(
961 \\path {s}953 \\path {s}
962 , .{954 , .{
963 lc.getRpathPathName(),955 lc.getRpathPathName(),
...@@ -966,8 +958,8 @@ const MachODumper = struct {...@@ -966,8 +958,8 @@ const MachODumper = struct {
966958
967 .UUID => {959 .UUID => {
968 const uuid = lc.cast(macho.uuid_command).?;960 const uuid = lc.cast(macho.uuid_command).?;
969 try bw.writeByte('\n');961 try writer.writeByte('\n');
970 try bw.print("uuid {x}", .{&uuid.uuid});962 try writer.print("uuid {x}", .{&uuid.uuid});
971 },963 },
972964
973 .DATA_IN_CODE,965 .DATA_IN_CODE,
...@@ -975,8 +967,8 @@ const MachODumper = struct {...@@ -975,8 +967,8 @@ const MachODumper = struct {
975 .CODE_SIGNATURE,967 .CODE_SIGNATURE,
976 => {968 => {
977 const llc = lc.cast(macho.linkedit_data_command).?;969 const llc = lc.cast(macho.linkedit_data_command).?;
978 try bw.writeByte('\n');970 try writer.writeByte('\n');
979 try bw.print(971 try writer.print(
980 \\dataoff {x}972 \\dataoff {x}
981 \\datasize {x}973 \\datasize {x}
982 , .{ llc.dataoff, llc.datasize });974 , .{ llc.dataoff, llc.datasize });
...@@ -984,8 +976,8 @@ const MachODumper = struct {...@@ -984,8 +976,8 @@ const MachODumper = struct {
984976
985 .DYLD_INFO_ONLY => {977 .DYLD_INFO_ONLY => {
986 const dlc = lc.cast(macho.dyld_info_command).?;978 const dlc = lc.cast(macho.dyld_info_command).?;
987 try bw.writeByte('\n');979 try writer.writeByte('\n');
988 try bw.print(980 try writer.print(
989 \\rebaseoff {x}981 \\rebaseoff {x}
990 \\rebasesize {x}982 \\rebasesize {x}
991 \\bindoff {x}983 \\bindoff {x}
...@@ -1012,8 +1004,8 @@ const MachODumper = struct {...@@ -1012,8 +1004,8 @@ const MachODumper = struct {
10121004
1013 .SYMTAB => {1005 .SYMTAB => {
1014 const slc = lc.cast(macho.symtab_command).?;1006 const slc = lc.cast(macho.symtab_command).?;
1015 try bw.writeByte('\n');1007 try writer.writeByte('\n');
1016 try bw.print(1008 try writer.print(
1017 \\symoff {x}1009 \\symoff {x}
1018 \\nsyms {x}1010 \\nsyms {x}
1019 \\stroff {x}1011 \\stroff {x}
...@@ -1028,8 +1020,8 @@ const MachODumper = struct {...@@ -1028,8 +1020,8 @@ const MachODumper = struct {
10281020
1029 .DYSYMTAB => {1021 .DYSYMTAB => {
1030 const dlc = lc.cast(macho.dysymtab_command).?;1022 const dlc = lc.cast(macho.dysymtab_command).?;
1031 try bw.writeByte('\n');1023 try writer.writeByte('\n');
1032 try bw.print(1024 try writer.print(
1033 \\ilocalsym {x}1025 \\ilocalsym {x}
1034 \\nlocalsym {x}1026 \\nlocalsym {x}
1035 \\iextdefsym {x}1027 \\iextdefsym {x}
...@@ -1052,8 +1044,8 @@ const MachODumper = struct {...@@ -1052,8 +1044,8 @@ const MachODumper = struct {
10521044
1053 .BUILD_VERSION => {1045 .BUILD_VERSION => {
1054 const blc = lc.cast(macho.build_version_command).?;1046 const blc = lc.cast(macho.build_version_command).?;
1055 try bw.writeByte('\n');1047 try writer.writeByte('\n');
1056 try bw.print(1048 try writer.print(
1057 \\platform {s}1049 \\platform {s}
1058 \\minos {d}.{d}.{d}1050 \\minos {d}.{d}.{d}
1059 \\sdk {d}.{d}.{d}1051 \\sdk {d}.{d}.{d}
...@@ -1069,12 +1061,12 @@ const MachODumper = struct {...@@ -1069,12 +1061,12 @@ const MachODumper = struct {
1069 blc.ntools,1061 blc.ntools,
1070 });1062 });
1071 for (lc.getBuildVersionTools()) |tool| {1063 for (lc.getBuildVersionTools()) |tool| {
1072 try bw.writeByte('\n');1064 try writer.writeByte('\n');
1073 switch (tool.tool) {1065 switch (tool.tool) {
1074 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try bw.print("tool {s}\n", .{@tagName(tool.tool)}),1066 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}),
1075 else => |x| try bw.print("tool {d}\n", .{@intFromEnum(x)}),1067 else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}),
1076 }1068 }
1077 try bw.print(1069 try writer.print(
1078 \\version {d}.{d}.{d}1070 \\version {d}.{d}.{d}
1079 , .{1071 , .{
1080 tool.version >> 16,1072 tool.version >> 16,
...@@ -1090,8 +1082,8 @@ const MachODumper = struct {...@@ -1090,8 +1082,8 @@ const MachODumper = struct {
1090 .VERSION_MIN_TVOS,1082 .VERSION_MIN_TVOS,
1091 => {1083 => {
1092 const vlc = lc.cast(macho.version_min_command).?;1084 const vlc = lc.cast(macho.version_min_command).?;
1093 try bw.writeByte('\n');1085 try writer.writeByte('\n');
1094 try bw.print(1086 try writer.print(
1095 \\version {d}.{d}.{d}1087 \\version {d}.{d}.{d}
1096 \\sdk {d}.{d}.{d}1088 \\sdk {d}.{d}.{d}
1097 , .{1089 , .{
...@@ -1943,58 +1935,58 @@ const ElfDumper = struct {...@@ -1943,58 +1935,58 @@ const ElfDumper = struct {
1943 try bw.print("entry {x}\n", .{ctx.hdr.e_entry});1935 try bw.print("entry {x}\n", .{ctx.hdr.e_entry});
1944 }1936 }
19451937
1946 fn dumpPhdrs(ctx: ObjectContext, bw: *Writer) !void {1938 fn dumpPhdrs(ctx: ObjectContext, writer: *Writer) !void {
1947 if (ctx.phdrs.len == 0) return;1939 if (ctx.phdrs.len == 0) return;
19481940
1949 try bw.writeAll("program headers\n");1941 try writer.writeAll("program headers\n");
19501942
1951 for (ctx.phdrs, 0..) |phdr, phndx| {1943 for (ctx.phdrs, 0..) |phdr, phndx| {
1952 try bw.print("phdr {d}\n", .{phndx});1944 try writer.print("phdr {d}\n", .{phndx});
1953 try bw.print("type {f}\n", .{fmtPhType(phdr.p_type)});1945 try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)});
1954 try bw.print("vaddr {x}\n", .{phdr.p_vaddr});1946 try writer.print("vaddr {x}\n", .{phdr.p_vaddr});
1955 try bw.print("paddr {x}\n", .{phdr.p_paddr});1947 try writer.print("paddr {x}\n", .{phdr.p_paddr});
1956 try bw.print("offset {x}\n", .{phdr.p_offset});1948 try writer.print("offset {x}\n", .{phdr.p_offset});
1957 try bw.print("memsz {x}\n", .{phdr.p_memsz});1949 try writer.print("memsz {x}\n", .{phdr.p_memsz});
1958 try bw.print("filesz {x}\n", .{phdr.p_filesz});1950 try writer.print("filesz {x}\n", .{phdr.p_filesz});
1959 try bw.print("align {x}\n", .{phdr.p_align});1951 try writer.print("align {x}\n", .{phdr.p_align});
19601952
1961 {1953 {
1962 const flags = phdr.p_flags;1954 const flags = phdr.p_flags;
1963 try bw.writeAll("flags");1955 try writer.writeAll("flags");
1964 if (flags > 0) try bw.writeByte(' ');1956 if (flags > 0) try writer.writeByte(' ');
1965 if (flags & elf.PF_R != 0) {1957 if (flags & elf.PF_R != 0) {
1966 try bw.writeByte('R');1958 try writer.writeByte('R');
1967 }1959 }
1968 if (flags & elf.PF_W != 0) {1960 if (flags & elf.PF_W != 0) {
1969 try bw.writeByte('W');1961 try writer.writeByte('W');
1970 }1962 }
1971 if (flags & elf.PF_X != 0) {1963 if (flags & elf.PF_X != 0) {
1972 try bw.writeByte('E');1964 try writer.writeByte('E');
1973 }1965 }
1974 if (flags & elf.PF_MASKOS != 0) {1966 if (flags & elf.PF_MASKOS != 0) {
1975 try bw.writeAll("OS");1967 try writer.writeAll("OS");
1976 }1968 }
1977 if (flags & elf.PF_MASKPROC != 0) {1969 if (flags & elf.PF_MASKPROC != 0) {
1978 try bw.writeAll("PROC");1970 try writer.writeAll("PROC");
1979 }1971 }
1980 try bw.writeByte('\n');1972 try writer.writeByte('\n');
1981 }1973 }
1982 }1974 }
1983 }1975 }
19841976
1985 fn dumpShdrs(ctx: ObjectContext, bw: *Writer) !void {1977 fn dumpShdrs(ctx: ObjectContext, writer: *Writer) !void {
1986 if (ctx.shdrs.len == 0) return;1978 if (ctx.shdrs.len == 0) return;
19871979
1988 try bw.writeAll("section headers\n");1980 try writer.writeAll("section headers\n");
19891981
1990 for (ctx.shdrs, 0..) |shdr, shndx| {1982 for (ctx.shdrs, 0..) |shdr, shndx| {
1991 try bw.print("shdr {d}\n", .{shndx});1983 try writer.print("shdr {d}\n", .{shndx});
1992 try bw.print("name {s}\n", .{ctx.getSectionName(shndx)});1984 try writer.print("name {s}\n", .{ctx.getSectionName(shndx)});
1993 try bw.print("type {f}\n", .{fmtShType(shdr.sh_type)});1985 try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)});
1994 try bw.print("addr {x}\n", .{shdr.sh_addr});1986 try writer.print("addr {x}\n", .{shdr.sh_addr});
1995 try bw.print("offset {x}\n", .{shdr.sh_offset});1987 try writer.print("offset {x}\n", .{shdr.sh_offset});
1996 try bw.print("size {x}\n", .{shdr.sh_size});1988 try writer.print("size {x}\n", .{shdr.sh_size});
1997 try bw.print("addralign {x}\n", .{shdr.sh_addralign});1989 try writer.print("addralign {x}\n", .{shdr.sh_addralign});
1998 // TODO dump formatted sh_flags1990 // TODO dump formatted sh_flags
1999 }1991 }
2000 }1992 }
...@@ -2263,16 +2255,11 @@ const ElfDumper = struct {...@@ -2263,16 +2255,11 @@ const ElfDumper = struct {
2263 return str[0..std.mem.indexOfScalar(u8, str, 0).?];2255 return str[0..std.mem.indexOfScalar(u8, str, 0).?];
2264 }2256 }
22652257
2266 fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) {2258 fn fmtShType(sh_type: u32) std.fmt.Formatter(u32, formatShType) {
2267 return .{ .data = sh_type };2259 return .{ .data = sh_type };
2268 }2260 }
22692261
2270 fn formatShType(2262 fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void {
2271 sh_type: u32,
2272 bw: *Writer,
2273 comptime unused_fmt_string: []const u8,
2274 ) !void {
2275 _ = unused_fmt_string;
2276 const name = switch (sh_type) {2263 const name = switch (sh_type) {
2277 elf.SHT_NULL => "NULL",2264 elf.SHT_NULL => "NULL",
2278 elf.SHT_PROGBITS => "PROGBITS",2265 elf.SHT_PROGBITS => "PROGBITS",
...@@ -2298,26 +2285,21 @@ const ElfDumper = struct {...@@ -2298,26 +2285,21 @@ const ElfDumper = struct {
2298 elf.SHT_GNU_VERNEED => "VERNEED",2285 elf.SHT_GNU_VERNEED => "VERNEED",
2299 elf.SHT_GNU_VERSYM => "VERSYM",2286 elf.SHT_GNU_VERSYM => "VERSYM",
2300 else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) {2287 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});
2302 } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) {2289 } 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});
2304 } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) {2291 } 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});
2306 } else "UNKNOWN",2293 } else "UNKNOWN",
2307 };2294 };
2308 try bw.writeAll(name);2295 try writer.writeAll(name);
2309 }2296 }
23102297
2311 fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) {2298 fn fmtPhType(ph_type: u32) std.fmt.Formatter(u32, formatPhType) {
2312 return .{ .data = ph_type };2299 return .{ .data = ph_type };
2313 }2300 }
23142301
2315 fn formatPhType(2302 fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void {
2316 ph_type: u32,
2317 bw: *Writer,
2318 comptime unused_fmt_string: []const u8,
2319 ) !void {
2320 _ = unused_fmt_string;
2321 const p_type = switch (ph_type) {2303 const p_type = switch (ph_type) {
2322 elf.PT_NULL => "NULL",2304 elf.PT_NULL => "NULL",
2323 elf.PT_LOAD => "LOAD",2305 elf.PT_LOAD => "LOAD",
...@@ -2332,12 +2314,12 @@ const ElfDumper = struct {...@@ -2332,12 +2314,12 @@ const ElfDumper = struct {
2332 elf.PT_GNU_STACK => "GNU_STACK",2314 elf.PT_GNU_STACK => "GNU_STACK",
2333 elf.PT_GNU_RELRO => "GNU_RELRO",2315 elf.PT_GNU_RELRO => "GNU_RELRO",
2334 else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) {2316 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});
2336 } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) {2318 } 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});
2338 } else "UNKNOWN",2320 } else "UNKNOWN",
2339 };2321 };
2340 try bw.writeAll(p_type);2322 try writer.writeAll(p_type);
2341 }2323 }
2342};2324};
23432325
lib/std/Build/Step/Compile.zig+6-10
...@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking...@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
1017 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);1017 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
10181018
1019 const generated_file = maybe_path orelse {1019 const generated_file = maybe_path orelse {
1020 std.debug.lockStdErr();1020 const w = std.debug.lockStderrWriter(&.{});
1021 const stderr: fs.File = .stderr();1021 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
10221022 std.debug.unlockStderrWriter();
1023 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1024
1025 @panic("missing emit option for " ++ tag_name);1023 @panic("missing emit option for " ++ tag_name);
1026 };1024 };
10271025
1028 const path = generated_file.path orelse {1026 const path = generated_file.path orelse {
1029 std.debug.lockStdErr();1027 const w = std.debug.lockStderrWriter(&.{});
1030 const stderr: fs.File = .stderr();1028 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
10311029 std.debug.unlockStderrWriter();
1032 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1033
1034 @panic(tag_name ++ " is null. Is there a missing step dependency?");1030 @panic(tag_name ++ " is null. Is there a missing step dependency?");
1035 };1031 };
10361032
lib/std/Build/Step/ConfigHeader.zig+8-8
...@@ -198,7 +198,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -198,7 +198,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
198198
199 var aw: std.io.Writer.Allocating = .init(gpa);199 var aw: std.io.Writer.Allocating = .init(gpa);
200 defer aw.deinit();200 defer aw.deinit();
201 const bw = &aw.interface;201 const bw = &aw.writer;
202202
203 const header_text = "This file was generated by ConfigHeader using the Zig Build System.";203 const header_text = "This file was generated by ConfigHeader using the Zig Build System.";
204 const c_generated_line = "/* " ++ header_text ++ " */\n";204 const c_generated_line = "/* " ++ header_text ++ " */\n";
...@@ -335,7 +335,7 @@ fn render_autoconf_at(...@@ -335,7 +335,7 @@ fn render_autoconf_at(
335) !void {335) !void {
336 const build = step.owner;336 const build = step.owner;
337 const allocator = build.allocator;337 const allocator = build.allocator;
338 const bw = &aw.interface;338 const bw = &aw.writer;
339339
340 const used = allocator.alloc(bool, values.count()) catch @panic("OOM");340 const used = allocator.alloc(bool, values.count()) catch @panic("OOM");
341 for (used) |*u| u.* = false;341 for (used) |*u| u.* = false;
...@@ -553,7 +553,7 @@ fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void {...@@ -553,7 +553,7 @@ fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void {
553 .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }),553 .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }),
554 .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }),554 .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }),
555 // TODO: use C-specific escaping instead of zig string literals555 // 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) }),
557 }557 }
558}558}
559559
...@@ -565,7 +565,7 @@ fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void {...@@ -565,7 +565,7 @@ fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void {
565 .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }),565 .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }),
566 .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }),566 .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }),
567 // TODO: use nasm-specific escaping instead of zig string literals567 // 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) }),
569 }569 }
570}570}
571571
...@@ -753,17 +753,17 @@ fn testReplaceVariablesAutoconfAt(...@@ -753,17 +753,17 @@ fn testReplaceVariablesAutoconfAt(
753 expected: []const u8,753 expected: []const u8,
754 values: std.StringArrayHashMap(Value),754 values: std.StringArrayHashMap(Value),
755) !void {755) !void {
756 var output: std.io.Writer.Allocating = .init(allocator);756 var aw: std.io.Writer.Allocating = .init(allocator);
757 defer output.deinit();757 defer aw.deinit();
758758
759 const used = try allocator.alloc(bool, values.count());759 const used = try allocator.alloc(bool, values.count());
760 for (used) |*u| u.* = false;760 for (used) |*u| u.* = false;
761 defer allocator.free(used);761 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
765 for (used) |u| if (!u) return error.UnusedValue;765 for (used) |u| if (!u) return error.UnusedValue;
766 try std.testing.expectEqualStrings(expected, output.getWritten());766 try std.testing.expectEqualStrings(expected, aw.getWritten());
767}767}
768768
769fn testReplaceVariablesCMake(769fn testReplaceVariablesCMake(
lib/std/Build/Step/Options.zig+33-22
...@@ -62,7 +62,7 @@ fn printType(...@@ -62,7 +62,7 @@ fn printType(
6262
63 for (value) |slice| {63 for (value) |slice| {
64 try out.appendNTimes(gpa, ' ', indent);64 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)});
66 }66 }
6767
68 if (name != null) {68 if (name != null) {
...@@ -76,28 +76,28 @@ fn printType(...@@ -76,28 +76,28 @@ fn printType(
76 []const u8 => {76 []const u8 => {
77 if (name) |some| {77 if (name) |some| {
78 try out.print(gpa, "pub const {f}: []const u8 = \"{f}\";", .{78 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),
80 });80 });
81 } else {81 } else {
82 try out.print(gpa, "\"{f}\",", .{std.zig.fmtEscapes(value)});82 try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)});
83 }83 }
84 return out.appendSlice(gpa, "\n");84 return out.appendSlice(gpa, "\n");
85 },85 },
86 [:0]const u8 => {86 [:0]const u8 => {
87 if (name) |some| {87 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) });
89 } else {89 } else {
90 try out.print(gpa, "\"{f}\",", .{std.zig.fmtEscapes(value)});90 try out.print(gpa, "\"{f}\",", .{std.zig.fmtString(value)});
91 }91 }
92 return out.appendSlice(gpa, "\n");92 return out.appendSlice(gpa, "\n");
93 },93 },
94 ?[]const u8 => {94 ?[]const u8 => {
95 if (name) |some| {95 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)});
97 }97 }
9898
99 if (value) |payload| {99 if (value) |payload| {
100 try out.print(gpa, "\"{f}\"", .{std.zig.fmtEscapes(payload)});100 try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)});
101 } else {101 } else {
102 try out.appendSlice(gpa, "null");102 try out.appendSlice(gpa, "null");
103 }103 }
...@@ -111,11 +111,11 @@ fn printType(...@@ -111,11 +111,11 @@ fn printType(
111 },111 },
112 ?[:0]const u8 => {112 ?[:0]const u8 => {
113 if (name) |some| {113 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)});
115 }115 }
116116
117 if (value) |payload| {117 if (value) |payload| {
118 try out.print(gpa, "\"{f}\"", .{std.zig.fmtEscapes(payload)});118 try out.print(gpa, "\"{f}\"", .{std.zig.fmtString(payload)});
119 } else {119 } else {
120 try out.appendSlice(gpa, "null");120 try out.appendSlice(gpa, "null");
121 }121 }
...@@ -142,11 +142,11 @@ fn printType(...@@ -142,11 +142,11 @@ fn printType(
142142
143 if (value.pre) |some| {143 if (value.pre) |some| {
144 try out.appendNTimes(gpa, ' ', indent);144 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)});
146 }146 }
147 if (value.build) |some| {147 if (value.build) |some| {
148 try out.appendNTimes(gpa, ' ', indent);148 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)});
150 }150 }
151151
152 if (name != null) {152 if (name != null) {
...@@ -162,7 +162,7 @@ fn printType(...@@ -162,7 +162,7 @@ fn printType(
162 switch (@typeInfo(T)) {162 switch (@typeInfo(T)) {
163 .array => {163 .array => {
164 if (name) |some| {164 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) });
166 }166 }
167167
168 try out.print(gpa, "{s} {{\n", .{@typeName(T)});168 try out.print(gpa, "{s} {{\n", .{@typeName(T)});
...@@ -186,7 +186,7 @@ fn printType(...@@ -186,7 +186,7 @@ fn printType(
186 }186 }
187187
188 if (name) |some| {188 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) });
190 }190 }
191191
192 try out.print(gpa, "&[_]{s} {{\n", .{@typeName(p.child)});192 try out.print(gpa, "&[_]{s} {{\n", .{@typeName(p.child)});
...@@ -206,7 +206,7 @@ fn printType(...@@ -206,7 +206,7 @@ fn printType(
206 },206 },
207 .optional => {207 .optional => {
208 if (name) |some| {208 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) });
210 }210 }
211211
212 if (value) |inner| {212 if (value) |inner| {
...@@ -243,10 +243,10 @@ fn printType(...@@ -243,10 +243,10 @@ fn printType(
243 try printEnum(options, out, T, info, indent);243 try printEnum(options, out, T, info, indent);
244244
245 if (name) |some| {245 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", .{
247 std.zig.fmtId(some),247 std.zig.fmtId(some),
248 std.zig.fmtId(@typeName(T)),248 std.zig.fmtId(@typeName(T)),
249 std.zig.fmtId(@tagName(value)),249 std.zig.fmtIdFlags(@tagName(value), .{ .allow_underscore = true, .allow_primitive = true }),
250 });250 });
251 }251 }
252 return;252 return;
...@@ -295,7 +295,9 @@ fn printEnum(...@@ -295,7 +295,9 @@ fn printEnum(
295295
296 inline for (val.fields) |field| {296 inline for (val.fields) |field| {
297 try out.appendNTimes(gpa, ' ', indent);297 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 });
299 }301 }
300302
301 if (!val.is_exhaustive) {303 if (!val.is_exhaustive) {
...@@ -313,7 +315,7 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T:...@@ -313,7 +315,7 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T:
313 if (gop.found_existing) return;315 if (gop.found_existing) return;
314316
315 try out.appendNTimes(gpa, ' ', indent);317 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
318 switch (val.layout) {320 switch (val.layout) {
319 .@"extern" => try out.appendSlice(gpa, "extern struct"),321 .@"extern" => try out.appendSlice(gpa, "extern struct"),
...@@ -330,9 +332,15 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T:...@@ -330,9 +332,15 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T:
330332
331 // If the type name doesn't contains a '.' the type is from zig builtins.333 // If the type name doesn't contains a '.' the type is from zig builtins.
332 if (std.mem.containsAtLeast(u8, type_name, 1, ".")) {334 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 });
334 } else {339 } 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 });
336 }344 }
337345
338 if (field.defaultValue()) |default_value| {346 if (field.defaultValue()) |default_value| {
...@@ -377,7 +385,9 @@ fn printStructValue(...@@ -377,7 +385,9 @@ fn printStructValue(
377 } else {385 } else {
378 inline for (struct_val.fields) |field| {386 inline for (struct_val.fields) |field| {
379 try out.appendNTimes(gpa, ' ', indent);387 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
382 const field_name = @field(val, field.name);392 const field_name = @field(val, field.name);
383 switch (@typeInfo(@TypeOf(field_name))) {393 switch (@typeInfo(@TypeOf(field_name))) {
...@@ -405,7 +415,8 @@ pub fn addOptionPath(...@@ -405,7 +415,8 @@ pub fn addOptionPath(
405 name: []const u8,415 name: []const u8,
406 path: LazyPath,416 path: LazyPath,
407) void {417) void {
408 options.args.append(.{418 const arena = options.step.owner.allocator;
419 options.args.append(arena, .{
409 .name = options.step.owner.dupe(name),420 .name = options.step.owner.dupe(name),
410 .path = path.dupe(options.step.owner),421 .path = path.dupe(options.step.owner),
411 }) catch @panic("OOM");422 }) catch @panic("OOM");
lib/std/Build/Step/Run.zig+9-8
...@@ -1015,16 +1015,17 @@ fn populateGeneratedPaths(...@@ -1015,16 +1015,17 @@ fn populateGeneratedPaths(
1015 }1015 }
1016}1016}
10171017
1018fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer, comptime fmt: []const u8) !void {1018fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer) std.io.Writer.Error!void {
1019 comptime assert(fmt.len == 0);
1020 if (term) |t| switch (t) {1019 if (term) |t| switch (t) {
1021 .Exited => |code| try w.print("exited with code {}", .{code}),1020 .Exited => |code| try w.print("exited with code {d}", .{code}),
1022 .Signal => |sig| try w.print("terminated with signal {}", .{sig}),1021 .Signal => |sig| try w.print("terminated with signal {d}", .{sig}),
1023 .Stopped => |sig| try w.print("stopped with signal {}", .{sig}),1022 .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
1024 .Unknown => |code| try w.print("terminated for unknown reason with code {}", .{code}),1023 .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
1025 } else try w.writeAll("exited with any code");1024 } else {
1025 try w.writeAll("exited with any code");
1026 }
1026}1027}
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) {
1028 return .{ .data = term };1029 return .{ .data = term };
1029}1030}
10301031
lib/std/Build/Watch.zig+1-1
...@@ -659,7 +659,7 @@ const Os = switch (builtin.os.tag) {...@@ -659,7 +659,7 @@ const Os = switch (builtin.os.tag) {
659 path.root_dir.handle.fd659 path.root_dir.handle.fd
660 else660 else
661 posix.openat(path.root_dir.handle.fd, path.sub_path, dir_open_flags, 0) catch |err| {661 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) });
663 };663 };
664 // Empirically the dir has to stay open or else no events are triggered.664 // Empirically the dir has to stay open or else no events are triggered.
665 errdefer if (!skip_open_dir) posix.close(dir_fd);665 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 {...@@ -150,15 +150,10 @@ fn parseNum(text: []const u8) error{ InvalidVersion, Overflow }!usize {
150 };150 };
151}151}
152152
153pub fn format(153pub fn format(self: Version, w: *std.io.Writer) std.io.Writer.Error!void {
154 self: Version,154 try w.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch });
155 bw: *std.io.Writer,155 if (self.pre) |pre| try w.print("-{s}", .{pre});
156 comptime fmt: []const u8,156 if (self.build) |build| try w.print("+{s}", .{build});
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});
162}157}
163158
164const expect = std.testing.expect;159const expect = std.testing.expect;
...@@ -200,7 +195,7 @@ test format {...@@ -200,7 +195,7 @@ test format {
200 "1.0.0+0.build.1-rc.10000aaa-kk-0.1",195 "1.0.0+0.build.1-rc.10000aaa-kk-0.1",
201 "5.4.0-1018-raspi",196 "5.4.0-1018-raspi",
202 "5.7.123",197 "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
205 // Invalid version strings should be rejected.200 // Invalid version strings should be rejected.
206 for ([_][]const u8{201 for ([_][]const u8{
...@@ -267,12 +262,12 @@ test format {...@@ -267,12 +262,12 @@ test format {
267 // Valid version string that may overflow.262 // Valid version string that may overflow.
268 const big_valid = "99999999999999999999999.999999999999999999.99999999999999999";263 const big_valid = "99999999999999999999999.999999999999999999.99999999999999999";
269 if (parse(big_valid)) |ver| {264 if (parse(big_valid)) |ver| {
270 try std.testing.expectFmt(big_valid, "{}", .{ver});265 try std.testing.expectFmt(big_valid, "{f}", .{ver});
271 } else |err| try expect(err == error.Overflow);266 } else |err| try expect(err == error.Overflow);
272267
273 // Invalid version string that may overflow.268 // Invalid version string that may overflow.
274 const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12";269 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 |_| {}
276}271}
277272
278test "precedence" {273test "precedence" {
lib/std/Target.zig+8-19
...@@ -301,24 +301,13 @@ pub const Os = struct {...@@ -301,24 +301,13 @@ pub const Os = struct {
301301
302 /// This function is defined to serialize a Zig source code representation of this302 /// This function is defined to serialize a Zig source code representation of this
303 /// type, that, when parsed, will deserialize into the same data.303 /// 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 {304 pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void {
305 const maybe_name = std.enums.tagName(WindowsVersion, ver);305 if (std.enums.tagName(WindowsVersion, wv)) |name| {
306 if (comptime std.mem.eql(u8, fmt_str, "s")) {306 var vecs: [2][]const u8 = .{ ".", name };
307 if (maybe_name) |name|307 return w.writeVecAll(&vecs);
308 try bw.print(".{s}", .{name})308 } else {
309 else309 return w.print("@enumFromInt(0x{X:0>8})", .{wv});
310 try bw.print(".{d}", .{@intFromEnum(ver)});310 }
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);
322 }311 }
323 };312 };
324313
...@@ -1686,7 +1675,7 @@ pub const Cpu = struct {...@@ -1686,7 +1675,7 @@ pub const Cpu = struct {
1686 pub fn fromCallingConvention(cc: std.builtin.CallingConvention.Tag) []const Arch {1675 pub fn fromCallingConvention(cc: std.builtin.CallingConvention.Tag) []const Arch {
1687 return switch (cc) {1676 return switch (cc) {
1688 .auto,1677 .auto,
1689 .@"async",1678 .async,
1690 .naked,1679 .naked,
1691 .@"inline",1680 .@"inline",
1692 => unreachable,1681 => unreachable,
lib/std/Thread.zig+21-1
...@@ -165,10 +165,18 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -165,10 +165,18 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
165 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});165 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
166 const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only });166 const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only });
167 defer file.close();167 defer file.close();
168<<<<<<< HEAD
168 var fw = file.writer(&.{});169 var fw = file.writer(&.{});
169 fw.interface.writeAll(name) catch |err| switch (err) {170 fw.interface.writeAll(name) catch |err| switch (err) {
170 error.WriteFailed => return fw.err.?,171 error.WriteFailed => return fw.err.?,
171 };172 };
173||||||| edf785db0f
174
175 try file.writer().writeAll(name);
176=======
177
178 try file.deprecatedWriter().writeAll(name);
179>>>>>>> origin/master
172 return;180 return;
173 },181 },
174 .windows => {182 .windows => {
...@@ -280,11 +288,23 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -280,11 +288,23 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
280 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});288 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
281 const file = try std.fs.cwd().openFile(path, .{});289 const file = try std.fs.cwd().openFile(path, .{});
282 defer file.close();290 defer file.close();
291<<<<<<< HEAD
283 var fr = file.reader(&.{});292 var fr = file.reader(&.{});
284 const n = fr.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) {293 const n = fr.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) {
285 error.ReadFailed => return fr.err.?,294 error.ReadFailed => return fr.err.?,
286 };295 };
287 return if (n == 0) null else buffer[0 .. n - 1];296 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
288 },308 },
289 .windows => {309 .windows => {
290 const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);310 const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);
...@@ -1164,7 +1184,7 @@ const LinuxThreadImpl = struct {...@@ -1164,7 +1184,7 @@ const LinuxThreadImpl = struct {
11641184
1165 fn getCurrentId() Id {1185 fn getCurrentId() Id {
1166 return tls_thread_id orelse {1186 return tls_thread_id orelse {
1167 const tid = @as(u32, @bitCast(linux.gettid()));1187 const tid: u32 = @bitCast(linux.gettid());
1168 tls_thread_id = tid;1188 tls_thread_id = tid;
1169 return tid;1189 return tid;
1170 };1190 };
lib/std/Uri.zig+148-106
...@@ -3,12 +3,10 @@...@@ -3,12 +3,10 @@
33
4const std = @import("std.zig");4const std = @import("std.zig");
5const testing = std.testing;5const testing = std.testing;
6const Uri = @This();
6const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
7const assert = std.debug.assert;
8const Writer = std.io.Writer;8const Writer = std.io.Writer;
99
10const Uri = @This();
11
12scheme: []const u8,10scheme: []const u8,
13user: ?Component = null,11user: ?Component = null,
14password: ?Component = null,12password: ?Component = null,
...@@ -65,7 +63,7 @@ pub const Component = union(enum) {...@@ -65,7 +63,7 @@ pub const Component = union(enum) {
65 return switch (component) {63 return switch (component) {
66 .raw => |raw| raw,64 .raw => |raw| raw,
67 .percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_|65 .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)})
69 else67 else
70 percent_encoded,68 percent_encoded,
71 };69 };
...@@ -85,16 +83,9 @@ pub const Component = union(enum) {...@@ -85,16 +83,9 @@ pub const Component = union(enum) {
85 };83 };
86 }84 }
8785
88 pub fn format(component: Component, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {86 pub fn formatRaw(component: Component, w: *Writer) Writer.Error!void {
89 if (fmt.len == 0) {87 switch (component) {
90 try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{88 .raw => |raw| try w.writeAll(raw),
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),
98 .percent_encoded => |percent_encoded| {89 .percent_encoded => |percent_encoded| {
99 var start: usize = 0;90 var start: usize = 0;
100 var index: usize = 0;91 var index: usize = 0;
...@@ -103,51 +94,75 @@ pub const Component = union(enum) {...@@ -103,51 +94,75 @@ pub const Component = union(enum) {
103 if (percent_encoded.len - index < 2) continue;94 if (percent_encoded.len - index < 2) continue;
104 const percent_encoded_char =95 const percent_encoded_char =
105 std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue;96 std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue;
106 try bw.print("{s}{c}", .{97 try w.print("{s}{c}", .{
107 percent_encoded[start..percent],98 percent_encoded[start..percent],
108 percent_encoded_char,99 percent_encoded_char,
109 });100 });
110 start = percent + 3;101 start = percent + 3;
111 index = percent + 3;102 index = percent + 3;
112 }103 }
113 try bw.writeAll(percent_encoded[start..]);104 try w.writeAll(percent_encoded[start..]);
114 },105 },
115 } else if (comptime std.mem.eql(u8, fmt, "%")) switch (component) {106 }
116 .raw => |raw| try percentEncode(bw, raw, isUnreserved),107 }
117 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),108
118 } else if (comptime std.mem.eql(u8, fmt, "user")) switch (component) {109 pub fn formatEscaped(component: Component, w: *Writer) Writer.Error!void {
119 .raw => |raw| try percentEncode(bw, raw, isUserChar),110 switch (component) {
120 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),111 .raw => |raw| try percentEncode(w, raw, isUnreserved),
121 } else if (comptime std.mem.eql(u8, fmt, "password")) switch (component) {112 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
122 .raw => |raw| try percentEncode(bw, raw, isPasswordChar),113 }
123 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),114 }
124 } else if (comptime std.mem.eql(u8, fmt, "host")) switch (component) {115
125 .raw => |raw| try percentEncode(bw, raw, isHostChar),116 pub fn formatUser(component: Component, w: *Writer) Writer.Error!void {
126 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),117 switch (component) {
127 } else if (comptime std.mem.eql(u8, fmt, "path")) switch (component) {118 .raw => |raw| try percentEncode(w, raw, isUserChar),
128 .raw => |raw| try percentEncode(bw, raw, isPathChar),119 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
129 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),120 }
130 } else if (comptime std.mem.eql(u8, fmt, "query")) switch (component) {121 }
131 .raw => |raw| try percentEncode(bw, raw, isQueryChar),122
132 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),123 pub fn formatPassword(component: Component, w: *Writer) Writer.Error!void {
133 } else if (comptime std.mem.eql(u8, fmt, "fragment")) switch (component) {124 switch (component) {
134 .raw => |raw| try percentEncode(bw, raw, isFragmentChar),125 .raw => |raw| try percentEncode(w, raw, isPasswordChar),
135 .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded),126 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
136 } else @compileError("invalid format string '" ++ fmt ++ "'");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 }
137 }142 }
138143
139 pub fn percentEncode(144 pub fn formatQuery(component: Component, w: *Writer) Writer.Error!void {
140 bw: *Writer,145 switch (component) {
141 raw: []const u8,146 .raw => |raw| try percentEncode(w, raw, isQueryChar),
142 comptime isValidChar: fn (u8) bool,147 .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded),
143 ) Writer.Error!void {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 {
144 var start: usize = 0;159 var start: usize = 0;
145 for (raw, 0..) |char, index| {160 for (raw, 0..) |char, index| {
146 if (isValidChar(char)) continue;161 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 });
148 start = index + 1;163 start = index + 1;
149 }164 }
150 try bw.writeAll(raw[start..]);165 try w.writeAll(raw[start..]);
151 }166 }
152};167};
153168
...@@ -264,76 +279,91 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri {...@@ -264,76 +279,91 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri {
264 return uri;279 return uri;
265}280}
266281
267pub const WriteToStreamOptions = struct {282pub fn format(uri: *const Uri, writer: *Writer) Writer.Error!void {
268 /// When true, include the scheme part of the URI.283 return writeToStream(uri, writer, .all);
269 scheme: bool = false,284}
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};
283285
284pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *Writer) Writer.Error!void {286pub fn writeToStream(uri: *const Uri, writer: *Writer, flags: Format.Flags) Writer.Error!void {
285 if (options.scheme) {287 if (flags.scheme) {
286 try bw.print("{s}:", .{uri.scheme});288 try writer.print("{s}:", .{uri.scheme});
287 if (options.authority and uri.host != null) {289 if (flags.authority and uri.host != null) {
288 try bw.writeAll("//");290 try writer.writeAll("//");
289 }291 }
290 }292 }
291 if (options.authority) {293 if (flags.authority) {
292 if (options.authentication and uri.host != null) {294 if (flags.authentication and uri.host != null) {
293 if (uri.user) |user| {295 if (uri.user) |user| {
294 try bw.print("{fuser}", .{user});296 try user.formatUser(writer);
295 if (uri.password) |password| {297 if (uri.password) |password| {
296 try bw.print(":{fpassword}", .{password});298 try writer.writeByte(':');
299 try password.formatPassword(writer);
297 }300 }
298 try bw.writeByte('@');301 try writer.writeByte('@');
299 }302 }
300 }303 }
301 if (uri.host) |host| {304 if (uri.host) |host| {
302 try bw.print("{fhost}", .{host});305 try host.formatHost(writer);
303 if (options.port) {306 if (flags.port) {
304 if (uri.port) |port| try bw.print(":{d}", .{port});307 if (uri.port) |port| try writer.print(":{d}", .{port});
305 }308 }
306 }309 }
307 }310 }
308 if (options.path) {311 if (flags.path) {
309 try bw.print("{fpath}", .{312 const uri_path: Component = if (uri.path.isEmpty()) .{ .percent_encoded = "/" } else uri.path;
310 if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path,313 try uri_path.formatPath(writer);
311 });314 if (flags.query) {
312 if (options.query) {315 if (uri.query) |query| {
313 if (uri.query) |query| try bw.print("?{fquery}", .{query});316 try writer.writeByte('?');
317 try query.formatQuery(writer);
318 }
314 }319 }
315 if (options.fragment) {320 if (flags.fragment) {
316 if (uri.fragment) |fragment| try bw.print("#{ffragment}", .{fragment});321 if (uri.fragment) |fragment| {
322 try writer.writeByte('#');
323 try fragment.formatFragment(writer);
324 }
317 }325 }
318 }326 }
319}327}
320328
321pub fn format(uri: Uri, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {329pub const Format = struct {
322 const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0;330 uri: *const Uri,
323 const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0;331 flags: Flags = .{},
324 const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0;332
325 const path = comptime std.mem.indexOfScalar(u8, fmt, '/') != null or fmt.len == 0;333 pub const Flags = struct {
326 const query = comptime std.mem.indexOfScalar(u8, fmt, '?') != null or fmt.len == 0;334 /// When true, include the scheme part of the URI.
327 const fragment = comptime std.mem.indexOfScalar(u8, fmt, '#') != null or fmt.len == 0;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, .{360 pub fn default(f: Format, writer: *Writer) Writer.Error!void {
330 .scheme = scheme,361 return writeToStream(f.uri, writer, f.flags);
331 .authentication = authentication,362 }
332 .authority = authority,363};
333 .path = path,364
334 .query = query,365pub fn fmt(uri: *const Uri, flags: Format.Flags) std.fmt.Formatter(Format, Format.default) {
335 .fragment = fragment,366 return .{ .data = .{ .uri = uri, .flags = flags } };
336 }, bw);
337}367}
338368
339/// The return value will contain strings pointing into the original `text`.369/// The return value will contain strings pointing into the original `text`.
...@@ -464,9 +494,8 @@ test remove_dot_segments {...@@ -464,9 +494,8 @@ test remove_dot_segments {
464fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component {494fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component {
465 var aux: Writer = .fixed(aux_buf.*);495 var aux: Writer = .fixed(aux_buf.*);
466 if (!base.isEmpty()) {496 if (!base.isEmpty()) {
467 aux.print("{fpath}", .{base}) catch return error.NoSpaceLeft;497 base.formatPath(&aux) catch return error.NoSpaceLeft;
468 aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse498 aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse return remove_dot_segments(new);
469 return remove_dot_segments(new);
470 }499 }
471 aux.print("/{s}", .{new}) catch return error.NoSpaceLeft;500 aux.print("/{s}", .{new}) catch return error.NoSpaceLeft;
472 const merged_path = remove_dot_segments(aux.buffered());501 const merged_path = remove_dot_segments(aux.buffered());
...@@ -745,8 +774,11 @@ test "Special test" {...@@ -745,8 +774,11 @@ test "Special test" {
745test "URI percent encoding" {774test "URI percent encoding" {
746 try std.testing.expectFmt(775 try std.testing.expectFmt(
747 "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad",776 "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad",
748 "{%}",777 "{f}",
749 .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }},778 .{std.fmt.alt(
779 @as(Component, .{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }),
780 .formatEscaped,
781 )},
750 );782 );
751}783}
752784
...@@ -755,7 +787,10 @@ test "URI percent decoding" {...@@ -755,7 +787,10 @@ test "URI percent decoding" {
755 const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad";787 const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad";
756 var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*;788 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
760 var output: [expected.len]u8 = undefined;795 var output: [expected.len]u8 = undefined;
761 try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);796 try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);
...@@ -767,7 +802,10 @@ test "URI percent decoding" {...@@ -767,7 +802,10 @@ test "URI percent decoding" {
767 const expected = "/abc%";802 const expected = "/abc%";
768 var input = expected.*;803 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
772 var output: [expected.len]u8 = undefined;810 var output: [expected.len]u8 = undefined;
773 try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);811 try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected);
...@@ -781,7 +819,9 @@ test "URI query encoding" {...@@ -781,7 +819,9 @@ test "URI query encoding" {
781 const parsed = try Uri.parse(address);819 const parsed = try Uri.parse(address);
782820
783 // format the URI to percent encode it821 // 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 });
785}825}
786826
787test "format" {827test "format" {
...@@ -795,7 +835,9 @@ test "format" {...@@ -795,7 +835,9 @@ test "format" {
795 .query = null,835 .query = null,
796 .fragment = null,836 .fragment = null,
797 };837 };
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 });
799}841}
800842
801test "URI malformed input" {843test "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...@@ -339,9 +339,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
339 }339 }
340340
341 pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {341 pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
342 const gpa = self.allocator;
342 var unmanaged = self.moveToUnmanaged();343 var unmanaged = self.moveToUnmanaged();
343 try unmanaged.print(self.allocator, fmt, args);344 defer self.* = unmanaged.toManaged(gpa);
344 self.* = unmanaged.toManaged(self.allocator);345 try unmanaged.print(gpa, fmt, args);
345 }346 }
346347
347 /// Append a value to the list `n` times.348 /// Append a value to the list `n` times.
...@@ -907,7 +908,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -907,7 +908,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
907 try self.ensureUnusedCapacity(gpa, fmt.len);908 try self.ensureUnusedCapacity(gpa, fmt.len);
908 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self);909 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self);
909 defer self.* = aw.toArrayList();910 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) {
911 error.WriteFailed => return error.OutOfMemory,912 error.WriteFailed => return error.OutOfMemory,
912 };913 };
913 }914 }
lib/std/ascii.zig+45
...@@ -10,6 +10,10 @@...@@ -10,6 +10,10 @@
1010
11const std = @import("std");11const std = @import("std");
1212
13pub const lowercase = "abcdefghijklmnopqrstuvwxyz";
14pub const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
15pub const letters = lowercase ++ uppercase;
16
13/// The C0 control codes of the ASCII encoding.17/// The C0 control codes of the ASCII encoding.
14///18///
15/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes and `isControl`19/// 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 {...@@ -435,3 +439,44 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {
435pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {439pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
436 return orderIgnoreCase(lhs, rhs) == .lt;440 return orderIgnoreCase(lhs, rhs) == .lt;
437}441}
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 {...@@ -108,7 +108,7 @@ pub const Base64Encoder = struct {
108 }108 }
109 }109 }
110110
111 // dest must be compatible with std.io.Writer's writeAll interface111 // dest must be compatible with std.io.GenericWriter's writeAll interface
112 pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void {112 pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void {
113 var chunker = window(u8, source, 3, 3);113 var chunker = window(u8, source, 3, 3);
114 while (chunker.next()) |chunk| {114 while (chunker.next()) |chunk| {
...@@ -118,8 +118,8 @@ pub const Base64Encoder = struct {...@@ -118,8 +118,8 @@ pub const Base64Encoder = struct {
118 }118 }
119 }119 }
120120
121 // destWriter must be compatible with std.io.Writer's writeAll interface121 // destWriter must be compatible with std.io.GenericWriter's writeAll interface
122 // sourceReader must be compatible with std.io.Reader's read interface122 // sourceReader must be compatible with `std.io.GenericReader` read interface
123 pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void {123 pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void {
124 while (true) {124 while (true) {
125 var tempSource: [3]u8 = undefined;125 var tempSource: [3]u8 = undefined;
lib/std/bounded_array.zig+2-2
...@@ -277,7 +277,7 @@ pub fn BoundedArrayAligned(...@@ -277,7 +277,7 @@ pub fn BoundedArrayAligned(
277 @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++277 @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++
278 "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)")278 "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)")
279 else279 else
280 std.io.Writer(*Self, error{Overflow}, appendWrite);280 std.io.GenericWriter(*Self, error{Overflow}, appendWrite);
281281
282 /// Initializes a writer which will write into the array.282 /// Initializes a writer which will write into the array.
283 pub fn writer(self: *Self) Writer {283 pub fn writer(self: *Self) Writer {
...@@ -285,7 +285,7 @@ pub fn BoundedArrayAligned(...@@ -285,7 +285,7 @@ pub fn BoundedArrayAligned(
285 }285 }
286286
287 /// Same as `appendSlice` except it returns the number of bytes written, which is always the same287 /// 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.
289 fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize {289 fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize {
290 try self.appendSlice(m);290 try self.appendSlice(m);
291 return m.len;291 return m.len;
lib/std/builtin.zig+9-22
...@@ -34,23 +34,21 @@ pub const StackTrace = struct {...@@ -34,23 +34,21 @@ pub const StackTrace = struct {
34 index: usize,34 index: usize,
35 instruction_addresses: []usize,35 instruction_addresses: []usize,
3636
37 pub fn format(st: StackTrace, bw: *std.io.Writer, comptime fmt: []const u8) !void {37 pub fn format(self: StackTrace, writer: *std.io.Writer) std.io.Writer.Error!void {
38 comptime if (fmt.len != 0) unreachable;
39
40 // TODO: re-evaluate whether to use format() methods at all.38 // TODO: re-evaluate whether to use format() methods at all.
41 // Until then, avoid an error when using DebugAllocator with WebAssembly39 // Until then, avoid an error when using DebugAllocator with WebAssembly
42 // where it tries to call detectTTYConfig here.40 // where it tries to call detectTTYConfig here.
43 if (builtin.os.tag == .freestanding) return 0;41 if (builtin.os.tag == .freestanding) return 0;
4442
45 const debug_info = std.debug.getSelfDebugInfo() catch |err| {43 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", .{
47 @errorName(err),45 @errorName(err),
48 });46 });
49 };47 };
50 const tty_config = std.io.tty.detectConfig(.stderr());48 const tty_config = std.io.tty.detectConfig(std.fs.File.stderr());
51 try bw.writeAll("\n");49 try writer.writeAll("\n");
52 std.debug.writeStackTrace(st, bw, debug_info, tty_config) catch |err| {50 std.debug.writeStackTrace(self, writer, debug_info, tty_config) catch |err| {
53 try bw.print("Unable to print stack trace: {s}\n", .{@errorName(err)});51 try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)});
54 };52 };
55 }53 }
56};54};
...@@ -195,8 +193,6 @@ pub const CallingConvention = union(enum(u8)) {...@@ -195,8 +193,6 @@ pub const CallingConvention = union(enum(u8)) {
195 pub const C: CallingConvention = .c;193 pub const C: CallingConvention = .c;
196 /// Deprecated; use `.naked`.194 /// Deprecated; use `.naked`.
197 pub const Naked: CallingConvention = .naked;195 pub const Naked: CallingConvention = .naked;
198 /// Deprecated; use `.@"async"`.
199 pub const Async: CallingConvention = .@"async";
200 /// Deprecated; use `.@"inline"`.196 /// Deprecated; use `.@"inline"`.
201 pub const Inline: CallingConvention = .@"inline";197 pub const Inline: CallingConvention = .@"inline";
202 /// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`.198 /// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`.
...@@ -244,7 +240,7 @@ pub const CallingConvention = union(enum(u8)) {...@@ -244,7 +240,7 @@ pub const CallingConvention = union(enum(u8)) {
244 /// The calling convention of a function that can be called with `async` syntax. An `async` call240 /// The calling convention of a function that can be called with `async` syntax. An `async` call
245 /// of a runtime-known function must target a function with this calling convention.241 /// of a runtime-known function must target a function with this calling convention.
246 /// Comptime-known functions with other calling conventions may be coerced to this one.242 /// Comptime-known functions with other calling conventions may be coerced to this one.
247 @"async",243 async,
248244
249 /// Functions with this calling convention have no prologue or epilogue, making the function245 /// Functions with this calling convention have no prologue or epilogue, making the function
250 /// uncallable in regular Zig code. This can be useful when integrating with assembly.246 /// uncallable in regular Zig code. This can be useful when integrating with assembly.
...@@ -847,7 +843,7 @@ pub const LinkMode = enum {...@@ -847,7 +843,7 @@ pub const LinkMode = enum {
847pub const UnwindTables = enum {843pub const UnwindTables = enum {
848 none,844 none,
849 sync,845 sync,
850 @"async",846 async,
851};847};
852848
853/// This data structure is used by the Zig language code generation and849/// This data structure is used by the Zig language code generation and
...@@ -862,32 +858,23 @@ pub const WasiExecModel = enum {...@@ -862,32 +858,23 @@ pub const WasiExecModel = enum {
862pub const CallModifier = enum {858pub const CallModifier = enum {
863 /// Equivalent to function call syntax.859 /// Equivalent to function call syntax.
864 auto,860 auto,
865
866 /// Equivalent to async keyword used with function call syntax.
867 async_kw,
868
869 /// Prevents tail call optimization. This guarantees that the return861 /// Prevents tail call optimization. This guarantees that the return
870 /// address will point to the callsite, as opposed to the callsite's862 /// address will point to the callsite, as opposed to the callsite's
871 /// callsite. If the call is otherwise required to be tail-called863 /// callsite. If the call is otherwise required to be tail-called
872 /// or inlined, a compile error is emitted instead.864 /// or inlined, a compile error is emitted instead.
873 never_tail,865 never_tail,
874
875 /// Guarantees that the call will not be inlined. If the call is866 /// Guarantees that the call will not be inlined. If the call is
876 /// otherwise required to be inlined, a compile error is emitted instead.867 /// otherwise required to be inlined, a compile error is emitted instead.
877 never_inline,868 never_inline,
878
879 /// Asserts that the function call will not suspend. This allows a869 /// Asserts that the function call will not suspend. This allows a
880 /// non-async function to call an async function.870 /// non-async function to call an async function.
881 no_async,871 no_suspend,
882
883 /// Guarantees that the call will be generated with tail call optimization.872 /// Guarantees that the call will be generated with tail call optimization.
884 /// If this is not possible, a compile error is emitted instead.873 /// If this is not possible, a compile error is emitted instead.
885 always_tail,874 always_tail,
886
887 /// Guarantees that the call will be inlined at the callsite.875 /// Guarantees that the call will be inlined at the callsite.
888 /// If this is not possible, a compile error is emitted instead.876 /// If this is not possible, a compile error is emitted instead.
889 always_inline,877 always_inline,
890
891 /// Evaluates the call at compile-time. If the call cannot be completed at878 /// Evaluates the call at compile-time. If the call cannot be completed at
892 /// compile-time, a compile error is emitted instead.879 /// compile-time, a compile error is emitted instead.
893 compile_time,880 compile_time,
lib/std/c.zig+5-1
...@@ -10412,7 +10412,10 @@ pub const sigfillset = switch (native_os) {...@@ -10412,7 +10412,10 @@ pub const sigfillset = switch (native_os) {
10412};10412};
1041310413
10414pub const sigaddset = private.sigaddset;10414pub const sigaddset = private.sigaddset;
10415pub const sigemptyset = private.sigemptyset;10415pub const sigemptyset = switch (native_os) {
10416 .netbsd => private.__sigemptyset14,
10417 else => private.sigemptyset,
10418};
10416pub const sigdelset = private.sigdelset;10419pub const sigdelset = private.sigdelset;
10417pub const sigismember = private.sigismember;10420pub const sigismember = private.sigismember;
1041810421
...@@ -11268,6 +11271,7 @@ const private = struct {...@@ -11268,6 +11271,7 @@ const private = struct {
11268 extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;11271 extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
11269 extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int;11272 extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int;
11270 extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;11273 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;
11271 extern "c" fn __sigfillset14(set: ?*sigset_t) c_int;11275 extern "c" fn __sigfillset14(set: ?*sigset_t) c_int;
11272 extern "c" fn __sigprocmask14(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;11276 extern "c" fn __sigprocmask14(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
11273 extern "c" fn __socket30(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;11277 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 {...@@ -34,7 +34,7 @@ pub fn Decompress(comptime ReaderType: type) type {
34 const Self = @This();34 const Self = @This();
3535
36 pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error;36 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
39 allocator: Allocator,39 allocator: Allocator,
40 block_decoder: block.Decoder(ReaderType),40 block_decoder: block.Decoder(ReaderType),
lib/std/compress/xz/block.zig+1-1
...@@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -27,7 +27,7 @@ pub fn Decoder(comptime ReaderType: type) type {
27 ReaderType.Error ||27 ReaderType.Error ||
28 DecodeError ||28 DecodeError ||
29 Allocator.Error;29 Allocator.Error;
30 pub const Reader = std.io.Reader(*Self, Error, read);30 pub const Reader = std.io.GenericReader(*Self, Error, read);
3131
32 allocator: Allocator,32 allocator: Allocator,
33 inner_reader: ReaderType,33 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 {...@@ -45,7 +45,7 @@ pub fn prependSlice(self: *ArrayListReverse, data: []const u8) Error!void {
45 self.data.ptr = begin;45 self.data.ptr = begin;
46}46}
4747
48pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize);48pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize);
49/// Warning: This writer writes backwards. `fn print` will NOT work as expected.49/// Warning: This writer writes backwards. `fn print` will NOT work as expected.
50pub fn writer(self: *ArrayListReverse) Writer {50pub fn writer(self: *ArrayListReverse) Writer {
51 return .{ .context = self };51 return .{ .context = self };
lib/std/crypto/sha2.zig+16
...@@ -383,12 +383,28 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {...@@ -383,12 +383,28 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {
383 for (&d.s, v) |*dv, vv| dv.* +%= vv;383 for (&d.s, v) |*dv, vv| dv.* +%= vv;
384 }384 }
385385
386<<<<<<< HEAD
386 pub fn writer(this: *@This(), buffer: []u8) Writer {387 pub fn writer(this: *@This(), buffer: []u8) Writer {
387 return .{388 return .{
388 .context = this,389 .context = this,
389 .vtable = &.{ .drain = drain },390 .vtable = &.{ .drain = drain },
390 .buffer = buffer,391 .buffer = buffer,
391 };392 };
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
392 }408 }
393409
394 fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {410 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 {...@@ -222,7 +222,8 @@ pub fn unlockStderrWriter() void {
222/// Print to stderr, unbuffered, and silently returning on failure. Intended222/// Print to stderr, unbuffered, and silently returning on failure. Intended
223/// for use in "printf debugging". Use `std.log` functions for proper logging.223/// for use in "printf debugging". Use `std.log` functions for proper logging.
224pub fn print(comptime fmt: []const u8, args: anytype) void {224pub fn print(comptime fmt: []const u8, args: anytype) void {
225 const bw = lockStderrWriter(&.{});225 var buffer: [32]u8 = undefined;
226 const bw = lockStderrWriter(&buffer);
226 defer unlockStderrWriter();227 defer unlockStderrWriter();
227 nosuspend bw.print(fmt, args) catch return;228 nosuspend bw.print(fmt, args) catch return;
228}229}
...@@ -307,7 +308,7 @@ test dumpHexFallible {...@@ -307,7 +308,7 @@ test dumpHexFallible {
307 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);308 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
308 defer aw.deinit();309 defer aw.deinit();
309310
310 try dumpHexFallible(&aw.interface, .no_color, bytes);311 try dumpHexFallible(&aw.writer, .no_color, bytes);
311 const expected = try std.fmt.allocPrint(std.testing.allocator,312 const expected = try std.fmt.allocPrint(std.testing.allocator,
312 \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........313 \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........
313 \\{x:0>[2]} 01 12 13 ...314 \\{x:0>[2]} 01 12 13 ...
...@@ -1228,9 +1229,9 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi...@@ -1228,9 +1229,9 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi
1228}1229}
12291230
1230test printLineFromFileAnyOs {1231test printLineFromFileAnyOs {
1231 var output = std.ArrayList(u8).init(std.testing.allocator);1232 var aw: Writer.Allocating = .init(std.testing.allocator);
1232 defer output.deinit();1233 defer aw.deinit();
1233 const output_stream = output.writer();1234 const output_stream = &aw.writer;
12341235
1235 const allocator = std.testing.allocator;1236 const allocator = std.testing.allocator;
1236 const join = std.fs.path.join;1237 const join = std.fs.path.join;
...@@ -1252,8 +1253,8 @@ test printLineFromFileAnyOs {...@@ -1252,8 +1253,8 @@ test printLineFromFileAnyOs {
1252 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));1253 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
12531254
1254 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });1255 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 try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.getWritten());
1256 output.clearRetainingCapacity();1257 aw.clearRetainingCapacity();
1257 }1258 }
1258 {1259 {
1259 const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });1260 const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
...@@ -1268,12 +1269,12 @@ test printLineFromFileAnyOs {...@@ -1268,12 +1269,12 @@ test printLineFromFileAnyOs {
1268 });1269 });
12691270
1270 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });1271 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1271 try expectEqualStrings("1\n", output.items);1272 try expectEqualStrings("1\n", aw.getWritten());
1272 output.clearRetainingCapacity();1273 aw.clearRetainingCapacity();
12731274
1274 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });1275 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
1275 try expectEqualStrings("3\n", output.items);1276 try expectEqualStrings("3\n", aw.getWritten());
1276 output.clearRetainingCapacity();1277 aw.clearRetainingCapacity();
1277 }1278 }
1278 {1279 {
1279 const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});1280 const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
...@@ -1282,14 +1283,17 @@ test printLineFromFileAnyOs {...@@ -1282,14 +1283,17 @@ test printLineFromFileAnyOs {
1282 defer allocator.free(path);1283 defer allocator.free(path);
12831284
1284 const overlap = 10;1285 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;
1286 try writer.splatByteAll('a', std.heap.page_size_min - overlap);1289 try writer.splatByteAll('a', std.heap.page_size_min - overlap);
1287 try writer.writeByte('\n');1290 try writer.writeByte('\n');
1288 try writer.splatByteAll('a', overlap);1291 try writer.splatByteAll('a', overlap);
1292 try writer.flush();
12891293
1290 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });1294 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
1291 try expectEqualStrings(("a" ** overlap) ++ "\n", output.items);1295 try expectEqualStrings(("a" ** overlap) ++ "\n", aw.getWritten());
1292 output.clearRetainingCapacity();1296 aw.clearRetainingCapacity();
1293 }1297 }
1294 {1298 {
1295 const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});1299 const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
...@@ -1297,12 +1301,13 @@ test printLineFromFileAnyOs {...@@ -1297,12 +1301,13 @@ test printLineFromFileAnyOs {
1297 const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });1301 const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
1298 defer allocator.free(path);1302 defer allocator.free(path);
12991303
1300 var writer = file.writer();1304 var file_writer = file.writer(&.{});
1305 const writer = &file_writer.interface;
1301 try writer.splatByteAll('a', std.heap.page_size_max);1306 try writer.splatByteAll('a', std.heap.page_size_max);
13021307
1303 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });1308 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1304 try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items);1309 try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.getWritten());
1305 output.clearRetainingCapacity();1310 aw.clearRetainingCapacity();
1306 }1311 }
1307 {1312 {
1308 const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});1313 const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
...@@ -1310,24 +1315,25 @@ test printLineFromFileAnyOs {...@@ -1310,24 +1315,25 @@ test printLineFromFileAnyOs {
1310 const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });1315 const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
1311 defer allocator.free(path);1316 defer allocator.free(path);
13121317
1313 var writer = file.writer();1318 var file_writer = file.writer(&.{});
1319 const writer = &file_writer.interface;
1314 try writer.splatByteAll('a', 3 * std.heap.page_size_max);1320 try writer.splatByteAll('a', 3 * std.heap.page_size_max);
13151321
1316 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));1322 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
13171323
1318 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });1324 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1319 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", output.items);1325 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.getWritten());
1320 output.clearRetainingCapacity();1326 aw.clearRetainingCapacity();
13211327
1322 try writer.writeAll("a\na");1328 try writer.writeAll("a\na");
13231329
1324 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });1330 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);1331 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.getWritten());
1326 output.clearRetainingCapacity();1332 aw.clearRetainingCapacity();
13271333
1328 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });1334 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
1329 try expectEqualStrings("a\n", output.items);1335 try expectEqualStrings("a\n", aw.getWritten());
1330 output.clearRetainingCapacity();1336 aw.clearRetainingCapacity();
1331 }1337 }
1332 {1338 {
1333 const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});1339 const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
...@@ -1335,18 +1341,19 @@ test printLineFromFileAnyOs {...@@ -1335,18 +1341,19 @@ test printLineFromFileAnyOs {
1335 const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });1341 const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
1336 defer allocator.free(path);1342 defer allocator.free(path);
13371343
1338 var writer = file.writer();1344 var file_writer = file.writer(&.{});
1345 const writer = &file_writer.interface;
1339 const real_file_start = 3 * std.heap.page_size_min;1346 const real_file_start = 3 * std.heap.page_size_min;
1340 try writer.splatByteAll('\n', real_file_start);1347 try writer.splatByteAll('\n', real_file_start);
1341 try writer.writeAll("abc\ndef");1348 try writer.writeAll("abc\ndef");
13421349
1343 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });1350 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
1344 try expectEqualStrings("abc\n", output.items);1351 try expectEqualStrings("abc\n", aw.getWritten());
1345 output.clearRetainingCapacity();1352 aw.clearRetainingCapacity();
13461353
1347 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });1354 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
1348 try expectEqualStrings("def\n", output.items);1355 try expectEqualStrings("def\n", aw.getWritten());
1349 output.clearRetainingCapacity();1356 aw.clearRetainingCapacity();
1350 }1357 }
1351}1358}
13521359
...@@ -1597,10 +1604,10 @@ test "manage resources correctly" {...@@ -1597,10 +1604,10 @@ test "manage resources correctly" {
1597 // self-hosted debug info is still too buggy1604 // self-hosted debug info is still too buggy
1598 if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest;1605 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(&.{});
1601 var di = try SelfInfo.open(testing.allocator);1608 var di = try SelfInfo.open(testing.allocator);
1602 defer di.deinit();1609 defer di.deinit();
1603 try printSourceAtAddress(&di, writer, showMyTrace(), io.tty.detectConfig(.stderr()));1610 try printSourceAtAddress(&di, &discarding.writer, showMyTrace(), io.tty.detectConfig(.stderr()));
1604}1611}
16051612
1606noinline fn showMyTrace() usize {1613noinline fn showMyTrace() usize {
lib/std/debug/Pdb.zig+3-3
...@@ -395,7 +395,7 @@ const Msf = struct {...@@ -395,7 +395,7 @@ const Msf = struct {
395 streams: []MsfStream,395 streams: []MsfStream,
396396
397 fn init(allocator: Allocator, file: File) !Msf {397 fn init(allocator: Allocator, file: File) !Msf {
398 const in = file.reader();398 const in = file.deprecatedReader();
399399
400 const superblock = try in.takeStruct(pdb.SuperBlock);400 const superblock = try in.takeStruct(pdb.SuperBlock);
401401
...@@ -514,7 +514,7 @@ const MsfStream = struct {...@@ -514,7 +514,7 @@ const MsfStream = struct {
514 var offset = self.pos % self.block_size;514 var offset = self.pos % self.block_size;
515515
516 try self.in_file.seekTo(block * self.block_size + offset);516 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
519 var size: usize = 0;519 var size: usize = 0;
520 var rem_buffer = buffer;520 var rem_buffer = buffer;
...@@ -562,7 +562,7 @@ const MsfStream = struct {...@@ -562,7 +562,7 @@ const MsfStream = struct {
562 return block * self.block_size + offset;562 return block * self.block_size + offset;
563 }563 }
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) {
566 return .{ .context = self };566 return .{ .context = self };
567 }567 }
568};568};
lib/std/elf.zig+164
...@@ -508,6 +508,7 @@ pub const Header = struct {...@@ -508,6 +508,7 @@ pub const Header = struct {
508 };508 };
509 }509 }
510510
511<<<<<<< HEAD
511 pub const ReadError = std.io.Reader.Error || ParseError;512 pub const ReadError = std.io.Reader.Error || ParseError;
512513
513 pub fn read(r: *std.io.Reader) ReadError!Header {514 pub fn read(r: *std.io.Reader) ReadError!Header {
...@@ -515,6 +516,19 @@ pub const Header = struct {...@@ -515,6 +516,19 @@ pub const Header = struct {
515 const result = try parse(@ptrCast(buf));516 const result = try parse(@ptrCast(buf));
516 r.toss(if (result.is_64) @sizeOf(Elf64_Ehdr) else @sizeOf(Elf32_Ehdr));517 r.toss(if (result.is_64) @sizeOf(Elf64_Ehdr) else @sizeOf(Elf32_Ehdr));
517 return result;518 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
518 }532 }
519533
520 pub const ParseError = error{534 pub const ParseError = error{
...@@ -590,14 +604,92 @@ pub const ProgramHeaderIterator = struct {...@@ -590,14 +604,92 @@ pub const ProgramHeaderIterator = struct {
590 if (it.index >= it.elf_header.phnum) return null;604 if (it.index >= it.elf_header.phnum) return null;
591 defer it.index += 1;605 defer it.index += 1;
592606
607<<<<<<< HEAD
593 if (it.elf_header.is_64) {608 if (it.elf_header.is_64) {
594 var phdr: Elf64_Phdr = undefined;609 var phdr: Elf64_Phdr = undefined;
595 const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index;610 const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index;
596 try it.file_reader.seekTo(offset);611 try it.file_reader.seekTo(offset);
597 try it.file_reader.interface.readSlice(@ptrCast(&phdr));612 try it.file_reader.interface.readSlice(@ptrCast(&phdr));
598 if (it.elf_header.endian != native_endian)613 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
599 mem.byteSwapAllFields(Elf64_Phdr, &phdr);637 mem.byteSwapAllFields(Elf64_Phdr, &phdr);
638<<<<<<< HEAD
600 return phdr;639 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
601 }693 }
602694
603 var phdr: Elf32_Phdr = undefined;695 var phdr: Elf32_Phdr = undefined;
...@@ -624,9 +716,23 @@ pub const SectionHeaderIterator = struct {...@@ -624,9 +716,23 @@ pub const SectionHeaderIterator = struct {
624 file_reader: *std.fs.File.Reader,716 file_reader: *std.fs.File.Reader,
625 index: usize = 0,717 index: usize = 0,
626718
719<<<<<<< HEAD
627 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {720 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {
628 if (it.index >= it.elf_header.shnum) return null;721 if (it.index >= it.elf_header.shnum) return null;
629 defer it.index += 1;722 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
631 if (it.elf_header.is_64) {737 if (it.elf_header.is_64) {
632 var shdr: Elf64_Shdr = undefined;738 var shdr: Elf64_Shdr = undefined;
...@@ -635,7 +741,65 @@ pub const SectionHeaderIterator = struct {...@@ -635,7 +741,65 @@ pub const SectionHeaderIterator = struct {
635 try it.file_reader.interface.readSlice(@ptrCast(&shdr));741 try it.file_reader.interface.readSlice(@ptrCast(&shdr));
636 if (it.elf_header.endian != native_endian)742 if (it.elf_header.endian != native_endian)
637 mem.byteSwapAllFields(Elf64_Shdr, &shdr);743 mem.byteSwapAllFields(Elf64_Shdr, &shdr);
744<<<<<<< HEAD
638 return shdr;745 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
639 }803 }
640804
641 var shdr: Elf32_Shdr = undefined;805 var shdr: Elf32_Shdr = undefined;
lib/std/fmt.zig+245-567
...@@ -7,7 +7,6 @@ const io = std.io;...@@ -7,7 +7,6 @@ const io = std.io;
7const math = std.math;7const math = std.math;
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const mem = std.mem;9const mem = std.mem;
10const unicode = std.unicode;
11const meta = std.meta;10const meta = std.meta;
12const lossyCast = math.lossyCast;11const lossyCast = math.lossyCast;
13const expectFmt = std.testing.expectFmt;12const expectFmt = std.testing.expectFmt;
...@@ -25,10 +24,12 @@ pub const Alignment = enum {...@@ -25,10 +24,12 @@ pub const Alignment = enum {
25 right,24 right,
26};25};
2726
27pub const Case = enum { lower, upper };
28
28const default_alignment = .right;29const default_alignment = .right;
29const default_fill_char = ' ';30const default_fill_char = ' ';
3031
31/// Deprecated; to be removed after 0.14.0 is tagged.32/// Deprecated in favor of `Options`.
32pub const FormatOptions = Options;33pub const FormatOptions = Options;
3334
34pub const Options = struct {35pub const Options = struct {
...@@ -36,229 +37,78 @@ pub const Options = struct {...@@ -36,229 +37,78 @@ pub const Options = struct {
36 width: ?usize = null,37 width: ?usize = null,
37 alignment: Alignment = default_alignment,38 alignment: Alignment = default_alignment,
38 fill: u8 = default_fill_char,39 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;41 pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number {
123 comptime var unescape_brace = false;42 return .{
12443 .mode = mode,
125 // Handle {{ and }}, those are un-escaped as single braces44 .case = case,
126 if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) {45 .precision = o.precision,
127 unescape_brace = true;46 .width = o.width,
128 // Make the first brace part of the literal...47 .alignment = o.alignment,
129 end_index += 1;48 .fill = o.fill,
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 },
185 };49 };
50 }
51};
18652
187 const precision = switch (placeholder.precision) {53pub const Number = struct {
188 .none => null,54 mode: Mode = .decimal,
189 .number => |v| v,55 /// Affects hex digits as well as floating point "inf"/"INF".
190 .named => |arg_name| blk: {56 case: Case = .lower,
191 const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse57 precision: ?usize = null,
192 @compileError("no argument with name '" ++ arg_name ++ "'");58 width: ?usize = null,
193 _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments");59 alignment: Alignment = default_alignment,
194 break :blk @field(args, arg_name);60 fill: u8 = default_fill_char,
195 },
196 };
19761
198 const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse62 pub const Mode = enum {
199 @compileError("too few arguments");63 decimal,
20064 binary,
201 try bw.printValue(65 octal,
202 placeholder.specifier_arg,66 hex,
203 .{67 scientific,
204 .fill = placeholder.fill,68
205 .alignment = placeholder.alignment,69 pub fn base(mode: Mode) ?u8 {
206 .width = width,70 return switch (mode) {
207 .precision = precision,71 .decimal => 10,
208 },72 .binary => 2,
209 @field(args, fields_info[arg_to_print].name),73 .octal => 8,
210 std.options.fmt_max_depth,74 .hex => 16,
211 );75 .scientific => null,
212 }76 };
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 ++ "'"),
220 }77 }
221 }78 };
222}79};
22380
224fn cacheString(str: anytype) []const u8 {81/// Deprecated in favor of `Writer.print`.
225 return &str;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 };
226}87}
22788
228pub const Placeholder = struct {89pub const Placeholder = struct {
229 specifier_arg: []const u8,90 specifier_arg: []const u8,
230 fill: u21,91 fill: u8,
231 alignment: Alignment,92 alignment: Alignment,
232 arg: Specifier,93 arg: Specifier,
233 width: Specifier,94 width: Specifier,
234 precision: Specifier,95 precision: Specifier,
23596
236 pub fn parse(comptime str: anytype) Placeholder {97 pub fn parse(comptime bytes: []const u8) Placeholder {
237 const view = std.unicode.Utf8View.initComptime(&str);98 var parser: Parser = .{ .bytes = bytes, .i = 0 };
238 comptime var parser = Parser{99 const arg = parser.specifier() catch |err| @compileError(@errorName(err));
239 .iter = view.iterator(),100 const specifier_arg = parser.until(':');
240 };101 if (parser.char()) |b| {
241102 if (b != ':') @compileError("expected : or }, found '" ++ &[1]u8{b} ++ "'");
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 }
254 }103 }
255104
256 // Parse the fill character, if present.105 // Parse the fill byte, if present.
257 // When the width field is also specified, the fill character must106 //
107 // When the width field is also specified, the fill byte must
258 // be followed by an alignment specifier, unless it's '0' (zero)108 // be followed by an alignment specifier, unless it's '0' (zero)
259 // (in which case it's handled as part of the width specifier)109 // (in which case it's handled as part of the width specifier).
260 var fill: ?u21 = comptime if (parser.peek(1)) |ch|110 var fill: ?u8 = if (parser.peek(1)) |b|
261 switch (ch) {111 switch (b) {
262 '<', '^', '>' => parser.char(),112 '<', '^', '>' => parser.char(),
263 else => null,113 else => null,
264 }114 }
...@@ -266,8 +116,8 @@ pub const Placeholder = struct {...@@ -266,8 +116,8 @@ pub const Placeholder = struct {
266 null;116 null;
267117
268 // Parse the alignment parameter118 // Parse the alignment parameter
269 const alignment: ?Alignment = comptime if (parser.peek(0)) |ch| init: {119 const alignment: ?Alignment = if (parser.peek(0)) |b| init: {
270 switch (ch) {120 switch (b) {
271 '<', '^', '>' => {121 '<', '^', '>' => {
272 // consume the character122 // consume the character
273 break :init switch (parser.char().?) {123 break :init switch (parser.char().?) {
...@@ -283,30 +133,26 @@ pub const Placeholder = struct {...@@ -283,30 +133,26 @@ pub const Placeholder = struct {
283 // When none of the fill character and the alignment specifier have133 // When none of the fill character and the alignment specifier have
284 // been provided, check whether the width starts with a zero.134 // been provided, check whether the width starts with a zero.
285 if (fill == null and alignment == null) {135 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;
287 }137 }
288138
289 // Parse the width parameter139 // Parse the width parameter
290 const width = comptime parser.specifier() catch |err|140 const width = parser.specifier() catch |err| @compileError(@errorName(err));
291 @compileError(@errorName(err));
292141
293 // Skip the dot, if present142 // Skip the dot, if present
294 if (comptime parser.char()) |ch| {143 if (parser.char()) |b| {
295 if (ch != '.') {144 if (b != '.') @compileError("expected . or }, found '" ++ &[1]u8{b} ++ "'");
296 @compileError("expected . or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'");
297 }
298 }145 }
299146
300 // Parse the precision parameter147 // Parse the precision parameter
301 const precision = comptime parser.specifier() catch |err|148 const precision = parser.specifier() catch |err| @compileError(@errorName(err));
302 @compileError(@errorName(err));
303149
304 if (comptime parser.char()) |ch| {150 if (parser.char()) |b| @compileError("extraneous trailing character '" ++ &[1]u8{b} ++ "'");
305 @compileError("extraneous trailing character '" ++ unicode.utf8EncodeComptime(ch) ++ "'");151
306 }152 const specifier_array = specifier_arg[0..specifier_arg.len].*;
307153
308 return .{154 return .{
309 .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*),155 .specifier_arg = &specifier_array,
310 .fill = fill orelse default_fill_char,156 .fill = fill orelse default_fill_char,
311 .alignment = alignment orelse default_alignment,157 .alignment = alignment orelse default_alignment,
312 .arg = arg,158 .arg = arg,
...@@ -327,93 +173,64 @@ pub const Specifier = union(enum) {...@@ -327,93 +173,64 @@ pub const Specifier = union(enum) {
327/// Allows to implement formatters compatible with std.fmt without replicating173/// Allows to implement formatters compatible with std.fmt without replicating
328/// the standard library behavior.174/// the standard library behavior.
329pub const Parser = struct {175pub 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
334 pub fn number(self: *@This()) ?usize {179 pub fn number(self: *@This()) ?usize {
335 var r: ?usize = null;180 var r: ?usize = null;
336181 while (self.peek(0)) |byte| {
337 while (self.peek(0)) |code_point| {182 switch (byte) {
338 switch (code_point) {
339 '0'...'9' => {183 '0'...'9' => {
340 if (r == null) r = 0;184 if (r == null) r = 0;
341 r.? *= 10;185 r.? *= 10;
342 r.? += code_point - '0';186 r.? += byte - '0';
343 },187 },
344 else => break,188 else => break,
345 }189 }
346 _ = self.iter.nextCodepoint();190 self.i += 1;
347 }191 }
348
349 return r;192 return r;
350 }193 }
351194
352 // Returns a substring of the input starting from the current position195 pub fn until(self: *@This(), delimiter: u8) []const u8 {
353 // and ending where `ch` is found or until the end if not found196 const start = self.i;
354 pub fn until(self: *@This(), ch: u21) []const u8 {197 self.i = std.mem.indexOfScalarPos(u8, self.bytes, self.i, delimiter) orelse self.bytes.len;
355 const start = self.iter.i;198 return self.bytes[start..self.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];
362 }199 }
363200
364 // Returns the character pointed to by the iterator if available, or201 pub fn char(self: *@This()) ?u8 {
365 // null otherwise202 const i = self.i;
366 pub fn char(self: *@This()) ?u21 {203 if (self.bytes.len - i == 0) return null;
367 if (self.iter.nextCodepoint()) |code_point| {204 self.i = i + 1;
368 return code_point;205 return self.bytes[i];
369 }
370 return null;
371 }206 }
372207
373 // Returns true if the iterator points to an existing character and208 pub fn maybe(self: *@This(), byte: u8) bool {
374 // false otherwise209 if (self.peek(0) == byte) {
375 pub fn maybe(self: *@This(), val: u21) bool {210 self.i += 1;
376 if (self.peek(0) == val) {
377 _ = self.iter.nextCodepoint();
378 return true;211 return true;
379 }212 }
380 return false;213 return false;
381 }214 }
382215
383 // Returns a decimal number or null if the current character is not a
384 // digit
385 pub fn specifier(self: *@This()) !Specifier {216 pub fn specifier(self: *@This()) !Specifier {
386 if (self.maybe('[')) {217 if (self.maybe('[')) {
387 const arg_name = self.until(']');218 const arg_name = self.until(']');
388219 if (!self.maybe(']')) return error.@"Expected closing ]";
389 if (!self.maybe(']'))220 return .{ .named = arg_name };
390 return @field(anyerror, "Expected closing ]");
391
392 return Specifier{ .named = arg_name };
393 }221 }
394 if (self.number()) |i|222 if (self.number()) |i| return .{ .number = i };
395 return Specifier{ .number = i };223 return .{ .none = {} };
396
397 return Specifier{ .none = {} };
398 }224 }
399225
400 // Returns the n-th next character or null if that's past the end226 pub fn peek(self: *@This(), i: usize) ?u8 {
401 pub fn peek(self: *@This(), n: usize) ?u21 {227 const peek_index = self.i + i;
402 const original_i = self.iter.i;228 if (peek_index >= self.bytes.len) return null;
403 defer self.iter.i = original_i;229 return self.bytes[peek_index];
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;
412 }230 }
413};231};
414232
415pub const ArgSetType = u32;233pub const ArgSetType = u32;
416const max_format_args = @typeInfo(ArgSetType).int.bits;
417234
418pub const ArgState = struct {235pub const ArgState = struct {
419 next_arg: usize = 0,236 next_arg: usize = 0,
...@@ -441,63 +258,12 @@ pub const ArgState = struct {...@@ -441,63 +258,12 @@ pub const ArgState = struct {
441 }258 }
442};259};
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
495/// Asserts the rendered integer value fits in `buffer`.261/// Asserts the rendered integer value fits in `buffer`.
496/// Returns the end index within `buffer`.262/// Returns the end index within `buffer`.
497pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize {263pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize {
498 var bw: Writer = .fixed(buffer);264 var w: Writer = .fixed(buffer);
499 bw.printIntOptions(value, base, case, options) catch unreachable;265 w.printInt(value, base, case, options) catch unreachable;
500 return bw.end;266 return w.end;
501}267}
502268
503/// Converts values in the range [0, 100) to a base 10 string.269/// Converts values in the range [0, 100) to a base 10 string.
...@@ -509,35 +275,49 @@ pub fn digits2(value: u8) [2]u8 {...@@ -509,35 +275,49 @@ pub fn digits2(value: u8) [2]u8 {
509 }275 }
510}276}
511277
512pub const ParseIntError = error{278/// Deprecated in favor of `Alt`.
513 /// The result cannot fit in the type specified279pub const Formatter = Alt;
514 Overflow,
515
516 /// The input was empty or contained an invalid character
517 InvalidCharacter,
518};
519280
520/// Creates a Formatter type from a format function. Wrapping data in Formatter(func) causes281/// Creates a type suitable for instantiating and passing to a "{f}" placeholder.
521/// the data to be formatted using the given function `func`. `func` must be of the following282pub fn Alt(
522/// form:283 comptime Data: type,
523///284 comptime formatFn: fn (data: Data, writer: *Writer) Writer.Error!void,
524/// fn formatExample(285) type {
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.?;
533 return struct {286 return struct {
534 data: Data,287 data: Data,
535 pub fn format(self: @This(), writer: *Writer, comptime fmt: []const u8) Writer.Error!void {288 pub inline fn format(self: @This(), writer: *Writer) Writer.Error!void {
536 try formatFn(self.data, writer, fmt);289 try formatFn(self.data, writer);
537 }290 }
538 };291 };
539}292}
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
541/// Parses the string `buf` as signed or unsigned representation in the321/// Parses the string `buf` as signed or unsigned representation in the
542/// specified base of an integral value of type `T`.322/// specified base of an integral value of type `T`.
543///323///
...@@ -845,17 +625,17 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr...@@ -845,17 +625,17 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
845/// Count the characters needed for format.625/// Count the characters needed for format.
846pub fn count(comptime fmt: []const u8, args: anytype) usize {626pub fn count(comptime fmt: []const u8, args: anytype) usize {
847 var trash_buffer: [64]u8 = undefined;627 var trash_buffer: [64]u8 = undefined;
848 var w: Writer = .discarding(&trash_buffer);628 var dw: Writer.Discarding = .init(&trash_buffer);
849 w.print(fmt, args) catch |err| switch (err) {629 dw.writer.print(fmt, args) catch |err| switch (err) {
850 error.WriteFailed => unreachable,630 error.WriteFailed => unreachable,
851 };631 };
852 return w.count;632 return @intCast(dw.count + dw.writer.end);
853}633}
854634
855pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 {635pub 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);
857 defer aw.deinit();637 defer aw.deinit();
858 aw.interface.print(fmt, args) catch |err| switch (err) {638 aw.writer.print(fmt, args) catch |err| switch (err) {
859 error.WriteFailed => return error.OutOfMemory,639 error.WriteFailed => return error.OutOfMemory,
860 };640 };
861 return aw.toOwnedSlice();641 return aw.toOwnedSlice();
...@@ -867,9 +647,9 @@ pub fn allocPrintSentinel(...@@ -867,9 +647,9 @@ pub fn allocPrintSentinel(
867 args: anytype,647 args: anytype,
868 comptime sentinel: u8,648 comptime sentinel: u8,
869) Allocator.Error![:sentinel]u8 {649) 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);
871 defer aw.deinit();651 defer aw.deinit();
872 aw.interface.print(fmt, args) catch |err| switch (err) {652 aw.writer.print(fmt, args) catch |err| switch (err) {
873 error.WriteFailed => return error.OutOfMemory,653 error.WriteFailed => return error.OutOfMemory,
874 };654 };
875 return aw.toOwnedSliceSentinel(sentinel);655 return aw.toOwnedSliceSentinel(sentinel);
...@@ -1003,10 +783,6 @@ test "int.padded" {...@@ -1003,10 +783,6 @@ test "int.padded" {
1003 try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});783 try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
1004 try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});784 try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
1005 try expectFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});785 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}'", .{'ü'});
1010}786}
1011787
1012test "buffer" {788test "buffer" {
...@@ -1036,36 +812,24 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime...@@ -1036,36 +812,24 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime
1036}812}
1037813
1038test "array" {814test "array" {
1039 {815 const value: [3]u8 = "abc".*;
1040 const value: [3]u8 = "abc".*;816 try expectArrayFmt("array: abc\n", "array: {s}\n", value);
1041 try expectArrayFmt("array: abc\n", "array: {s}\n", value);817 try expectArrayFmt("array: 616263\n", "array: {x}\n", value);
1042 try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {d}\n", value);818 try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\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);
1045819
1046 var buf: [100]u8 = undefined;820 var buf: [100]u8 = undefined;
1047 try expectFmt(821 try expectFmt(
1048 try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}),822 try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}),
1049 "array: {*}\n",823 "array: {*}\n",
1050 .{&value},824 .{&value},
1051 );825 );
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 }
1061}826}
1062827
1063test "slice" {828test "slice" {
1064 {829 {
1065 const value: []const u8 = "abc";830 const value: []const u8 = "abc";
1066 try expectFmt("slice: abc\n", "slice: {s}\n", .{value});831 try expectFmt("slice: abc\n", "slice: {s}\n", .{value});
1067 try expectFmt("slice: { 97, 98, 99 }\n", "slice: {d}\n", .{value});832 try expectFmt("slice: 616263\n", "slice: {x}\n", .{value});
1068 try expectFmt("slice: { 61, 62, 63 }\n", "slice: {x}\n", .{value});
1069 try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value});833 try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value});
1070 }834 }
1071 {835 {
...@@ -1079,45 +843,33 @@ test "slice" {...@@ -1079,45 +843,33 @@ test "slice" {
1079 try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice});843 try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice});
1080 }844 }
1081845
1082 try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
1083 try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});846 try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
1084847
1085 {848 {
1086 var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 };849 var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 };
1087 var runtime_zero: usize = 0;850 const input: []const u32 = &int_slice;
1088 _ = &runtime_zero;851 try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input});
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..]});
1093 }852 }
1094 {853 {
1095 const S1 = struct {854 const S1 = struct {
1096 x: u8,855 x: u8,
1097 };856 };
1098 const struct_slice: []const S1 = &[_]S1{ S1{ .x = 8 }, S1{ .x = 42 } };857 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});
1100 }859 }
1101 {860 {
1102 const S2 = struct {861 const S2 = struct {
1103 x: u8,862 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 {
1106 try writer.print("S2({})", .{s.x});865 try writer.print("S2({})", .{s.x});
1107 }866 }
1108 };867 };
1109 const struct_slice: []const S2 = &[_]S2{ S2{ .x = 8 }, S2{ .x = 42 } };868 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});
1111 }870 }
1112}871}
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
1121test "pointer" {873test "pointer" {
1122 {874 {
1123 const value = @as(*align(1) i32, @ptrFromInt(0xdeadbeef));875 const value = @as(*align(1) i32, @ptrFromInt(0xdeadbeef));
...@@ -1141,11 +893,6 @@ test "cstr" {...@@ -1141,11 +893,6 @@ test "cstr" {
1141 "cstr: {s}\n",893 "cstr: {s}\n",
1142 .{@as([*c]const u8, @ptrCast("Test C"))},894 .{@as([*c]const u8, @ptrCast("Test C"))},
1143 );895 );
1144 try expectFmt(
1145 "cstr: Test C\n",
1146 "cstr: {s:10}\n",
1147 .{@as([*c]const u8, @ptrCast("Test C"))},
1148 );
1149}896}
1150897
1151test "struct" {898test "struct" {
...@@ -1154,8 +901,8 @@ test "struct" {...@@ -1154,8 +901,8 @@ test "struct" {
1154 field: u8,901 field: u8,
1155 };902 };
1156 const value = Struct{ .field = 42 };903 const value = Struct{ .field = 42 };
1157 try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{value});904 try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{value});
1158 try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{&value});905 try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{&value});
1159 }906 }
1160 {907 {
1161 const Struct = struct {908 const Struct = struct {
...@@ -1163,7 +910,7 @@ test "struct" {...@@ -1163,7 +910,7 @@ test "struct" {
1163 b: u1,910 b: u1,
1164 };911 };
1165 const value = Struct{ .a = 0, .b = 1 };912 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});
1167 }914 }
1168915
1169 const S = struct {916 const S = struct {
...@@ -1176,11 +923,11 @@ test "struct" {...@@ -1176,11 +923,11 @@ test "struct" {
1176 .b = error.Unused,923 .b = error.Unused,
1177 };924 };
1178925
1179 try expectFmt("fmt.test.struct.S{ .a = 456, .b = error.Unused }", "{}", .{inst});926 try expectFmt(".{ .a = 456, .b = error.Unused }", "{}", .{inst});
1180 // Tuples927 // Tuples
1181 try expectFmt("{ }", "{}", .{.{}});928 try expectFmt(".{ }", "{}", .{.{}});
1182 try expectFmt("{ -1 }", "{}", .{.{-1}});929 try expectFmt(".{ -1 }", "{}", .{.{-1}});
1183 try expectFmt("{ -1, 42, 2.5e4 }", "{}", .{.{ -1, 42, 0.25e5 }});930 try expectFmt(".{ -1, 42, 25000 }", "{}", .{.{ -1, 42, 0.25e5 }});
1184}931}
1185932
1186test "enum" {933test "enum" {
...@@ -1189,15 +936,15 @@ test "enum" {...@@ -1189,15 +936,15 @@ test "enum" {
1189 Two,936 Two,
1190 };937 };
1191 const value = Enum.Two;938 const value = Enum.Two;
1192 try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{value});939 try expectFmt("enum: .Two\n", "enum: {}\n", .{value});
1193 try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{&value});940 try expectFmt("enum: .Two\n", "enum: {}\n", .{&value});
1194 try expectFmt("enum: fmt.test.enum.Enum.One\n", "enum: {}\n", .{Enum.One});941 try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One});
1195 try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{Enum.Two});942 try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two});
1196943
1197 // test very large enum to verify ct branch quota is large enough944 // test very large enum to verify ct branch quota is large enough
1198 // TODO: https://github.com/ziglang/zig/issues/15609945 // TODO: https://github.com/ziglang/zig/issues/15609
1199 if (!((builtin.cpu.arch == .wasm32) and builtin.mode == .Debug)) {946 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});
1201 }948 }
1202949
1203 const E = enum {950 const E = enum {
...@@ -1208,7 +955,7 @@ test "enum" {...@@ -1208,7 +955,7 @@ test "enum" {
1208955
1209 const inst = E.Two;956 const inst = E.Two;
1210957
1211 try expectFmt("fmt.test.enum.E.Two", "{}", .{inst});958 try expectFmt(".Two", "{}", .{inst});
1212}959}
1213960
1214test "non-exhaustive enum" {961test "non-exhaustive enum" {
...@@ -1217,13 +964,17 @@ test "non-exhaustive enum" {...@@ -1217,13 +964,17 @@ test "non-exhaustive enum" {
1217 Two = 0xbeef,964 Two = 0xbeef,
1218 _,965 _,
1219 };966 };
1220 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {}\n", .{Enum.One});967 try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One});
1221 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {}\n", .{Enum.Two});968 try expectFmt("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))});969 try expectFmt("enum: @enumFromInt(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});970 try expectFmt("enum: f\n", "enum: {x}\n", .{Enum.One});
1224 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {x}\n", .{Enum.Two});971 try expectFmt("enum: beef\n", "enum: {x}\n", .{Enum.Two});
1225 try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {X}\n", .{Enum.Two});972 try expectFmt("enum: BEEF\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))});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))});
1227}978}
1228979
1229test "float.scientific" {980test "float.scientific" {
...@@ -1349,41 +1100,6 @@ test "float.libc.sanity" {...@@ -1349,41 +1100,6 @@ test "float.libc.sanity" {
1349 try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1518338049))))});1100 try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1518338049))))});
1350}1101}
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
1387test "union" {1103test "union" {
1388 const TU = union(enum) {1104 const TU = union(enum) {
1389 float: f32,1105 float: f32,
...@@ -1400,18 +1116,13 @@ test "union" {...@@ -1400,18 +1116,13 @@ test "union" {
1400 int: u32,1116 int: u32,
1401 };1117 };
14021118
1403 const tu_inst = TU{ .int = 123 };1119 const tu_inst: TU = .{ .int = 123 };
1404 const uu_inst = UU{ .int = 456 };1120 const uu_inst: UU = .{ .int = 456 };
1405 const eu_inst = EU{ .float = 321.123 };1121 const eu_inst: EU = .{ .float = 321.123 };
1406
1407 try expectFmt("fmt.test.union.TU{ .int = 123 }", "{}", .{tu_inst});
14081122
1409 var buf: [100]u8 = undefined;1123 try expectFmt(".{ .int = 123 }", "{}", .{tu_inst});
1410 const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst});1124 try expectFmt(".{ ... }", "{}", .{uu_inst});
1411 try std.testing.expectEqualStrings("fmt.test.union.UU@", uu_result[0..18]);1125 try expectFmt(".{ .float = 321.123, .int = 1134596030 }", "{}", .{eu_inst});
1412
1413 const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst});
1414 try std.testing.expectEqualStrings("fmt.test.union.EU@", eu_result[0..18]);
1415}1126}
14161127
1417test "struct.self-referential" {1128test "struct.self-referential" {
...@@ -1425,7 +1136,7 @@ test "struct.self-referential" {...@@ -1425,7 +1136,7 @@ test "struct.self-referential" {
1425 };1136 };
1426 inst.a = &inst;1137 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});
1429}1140}
14301141
1431test "struct.zero-size" {1142test "struct.zero-size" {
...@@ -1440,7 +1151,7 @@ test "struct.zero-size" {...@@ -1440,7 +1151,7 @@ test "struct.zero-size" {
1440 const a = A{};1151 const a = A{};
1441 const b = B{ .a = a, .c = 0 };1152 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});
1444}1155}
14451156
1446/// Encodes a sequence of bytes as hexadecimal digits.1157/// Encodes a sequence of bytes as hexadecimal digits.
...@@ -1551,33 +1262,17 @@ test "enum-literal" {...@@ -1551,33 +1262,17 @@ test "enum-literal" {
15511262
1552test "padding" {1263test "padding" {
1553 try expectFmt("Simple", "{s}", .{"Simple"});1264 try expectFmt("Simple", "{s}", .{"Simple"});
1554 try expectFmt(" true", "{:10}", .{true});1265 try expectFmt(" 1234", "{:10}", .{1234});
1555 try expectFmt(" true", "{:>10}", .{true});1266 try expectFmt(" 1234", "{:>10}", .{1234});
1556 try expectFmt("======true", "{:=>10}", .{true});1267 try expectFmt("======1234", "{:=>10}", .{1234});
1557 try expectFmt("true======", "{:=<10}", .{true});1268 try expectFmt("1234======", "{:=<10}", .{1234});
1558 try expectFmt(" true ", "{:^10}", .{true});1269 try expectFmt(" 1234 ", "{:^10}", .{1234});
1559 try expectFmt("===true===", "{:=^10}", .{true});1270 try expectFmt("===1234===", "{:=^10}", .{1234});
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"});
1567 try expectFmt("====a", "{c:=>5}", .{'a'});1271 try expectFmt("====a", "{c:=>5}", .{'a'});
1568 try expectFmt("==a==", "{c:=^5}", .{'a'});1272 try expectFmt("==a==", "{c:=^5}", .{'a'});
1569 try expectFmt("a====", "{c:=<5}", .{'a'});1273 try expectFmt("a====", "{c:=<5}", .{'a'});
1570}1274}
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
1581test "decimal float padding" {1276test "decimal float padding" {
1582 const number: f32 = 3.1415;1277 const number: f32 = 3.1415;
1583 try expectFmt("left-pad: **3.142\n", "left-pad: {d:*>7.3}\n", .{number});1278 try expectFmt("left-pad: **3.142\n", "left-pad: {d:*>7.3}\n", .{number});
...@@ -1620,17 +1315,17 @@ test "named arguments" {...@@ -1620,17 +1315,17 @@ test "named arguments" {
16201315
1621test "runtime width specifier" {1316test "runtime width specifier" {
1622 const width: usize = 9;1317 const width: usize = 9;
1623 try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width });1318 try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width });
1624 try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width });1319 try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width });
1625 try expectFmt(" hello", "{s:[1]}", .{ "hello", width });1320 try expectFmt(" 12345", "{d:[1]}", .{ 12345, width });
1626 try expectFmt("42 hello", "{d} {s:[2]}", .{ 42, "hello", width });1321 try expectFmt("42 12345", "{d} {d:[2]}", .{ 42, 12345, width });
1627}1322}
16281323
1629test "runtime precision specifier" {1324test "runtime precision specifier" {
1630 const number: f32 = 3.1415;1325 const number: f32 = 3.1415;
1631 const precision: usize = 2;1326 const precision: usize = 2;
1632 try expectFmt("3.14e0", "{:1.[1]}", .{ number, precision });1327 try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision });
1633 try expectFmt("3.14e0", "{:1.[precision]}", .{ .number = number, .precision = precision });1328 try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision });
1634}1329}
16351330
1636test "recursive format function" {1331test "recursive format function" {
...@@ -1639,16 +1334,16 @@ test "recursive format function" {...@@ -1639,16 +1334,16 @@ test "recursive format function" {
1639 Leaf: i32,1334 Leaf: i32,
1640 Branch: struct { left: *const R, right: *const R },1335 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 {
1643 return switch (self) {1338 return switch (self) {
1644 .Leaf => |n| std.fmt.format(writer, "Leaf({})", .{n}),1339 .Leaf => |n| writer.print("Leaf({})", .{n}),
1645 .Branch => |b| std.fmt.format(writer, "Branch({}, {})", .{ b.left, b.right }),1340 .Branch => |b| writer.print("Branch({f}, {f})", .{ b.left, b.right }),
1646 };1341 };
1647 }1342 }
1648 };1343 };
16491344
1650 var r = R{ .Leaf = 1 };1345 var r: R = .{ .Leaf = 1 };
1651 try expectFmt("Leaf(1)\n", "{}\n", .{&r});1346 try expectFmt("Leaf(1)\n", "{f}\n", .{&r});
1652}1347}
16531348
1654pub const hex_charset = "0123456789abcdef";1349pub const hex_charset = "0123456789abcdef";
...@@ -1682,54 +1377,39 @@ test hex {...@@ -1682,54 +1377,39 @@ test hex {
16821377
1683test "parser until" {1378test "parser until" {
1684 { // return substring till ':'1379 { // return substring till ':'
1685 var parser: Parser = .{1380 var parser: Parser = .{ .bytes = "abc:1234", .i = 0 };
1686 .iter = .{ .bytes = "abc:1234", .i = 0 },
1687 };
1688 try testing.expectEqualStrings("abc", parser.until(':'));1381 try testing.expectEqualStrings("abc", parser.until(':'));
1689 }1382 }
16901383
1691 { // return the entire string - `ch` not found1384 { // return the entire string - `ch` not found
1692 var parser: Parser = .{1385 var parser: Parser = .{ .bytes = "abc1234", .i = 0 };
1693 .iter = .{ .bytes = "abc1234", .i = 0 },
1694 };
1695 try testing.expectEqualStrings("abc1234", parser.until(':'));1386 try testing.expectEqualStrings("abc1234", parser.until(':'));
1696 }1387 }
16971388
1698 { // substring is empty - `ch` is the only character1389 { // substring is empty - `ch` is the only character
1699 var parser: Parser = .{1390 var parser: Parser = .{ .bytes = ":", .i = 0 };
1700 .iter = .{ .bytes = ":", .i = 0 },
1701 };
1702 try testing.expectEqualStrings("", parser.until(':'));1391 try testing.expectEqualStrings("", parser.until(':'));
1703 }1392 }
17041393
1705 { // empty string and `ch` not found1394 { // empty string and `ch` not found
1706 var parser: Parser = .{1395 var parser: Parser = .{ .bytes = "", .i = 0 };
1707 .iter = .{ .bytes = "", .i = 0 },
1708 };
1709 try testing.expectEqualStrings("", parser.until(':'));1396 try testing.expectEqualStrings("", parser.until(':'));
1710 }1397 }
17111398
1712 { // substring starts at index 2 and goes upto `ch`1399 { // substring starts at index 2 and goes upto `ch`
1713 var parser: Parser = .{1400 var parser: Parser = .{ .bytes = "abc:1234", .i = 2 };
1714 .iter = .{ .bytes = "abc:1234", .i = 2 },
1715 };
1716 try testing.expectEqualStrings("c", parser.until(':'));1401 try testing.expectEqualStrings("c", parser.until(':'));
1717 }1402 }
17181403
1719 { // substring starts at index 4 and goes upto the end - `ch` not found1404 { // substring starts at index 4 and goes upto the end - `ch` not found
1720 var parser: Parser = .{1405 var parser: Parser = .{ .bytes = "abc1234", .i = 4 };
1721 .iter = .{ .bytes = "abc1234", .i = 4 },
1722 };
1723 try testing.expectEqualStrings("234", parser.until(':'));1406 try testing.expectEqualStrings("234", parser.until(':'));
1724 }1407 }
1725}1408}
17261409
1727test "parser peek" {1410test "parser peek" {
1728 { // start iteration from the first index1411 { // start iteration from the first index
1729 var parser: Parser = .{1412 var parser: Parser = .{ .bytes = "hello world", .i = 0 };
1730 .iter = .{ .bytes = "hello world", .i = 0 },
1731 };
1732
1733 try testing.expectEqual('h', parser.peek(0));1413 try testing.expectEqual('h', parser.peek(0));
1734 try testing.expectEqual('e', parser.peek(1));1414 try testing.expectEqual('e', parser.peek(1));
1735 try testing.expectEqual(' ', parser.peek(5));1415 try testing.expectEqual(' ', parser.peek(5));
...@@ -1738,9 +1418,7 @@ test "parser peek" {...@@ -1738,9 +1418,7 @@ test "parser peek" {
1738 }1418 }
17391419
1740 { // start iteration from the second last index1420 { // start iteration from the second last index
1741 var parser: Parser = .{1421 var parser: Parser = .{ .bytes = "hello world!", .i = 10 };
1742 .iter = .{ .bytes = "hello world!", .i = 10 },
1743 };
17441422
1745 try testing.expectEqual('d', parser.peek(0));1423 try testing.expectEqual('d', parser.peek(0));
1746 try testing.expectEqual('!', parser.peek(1));1424 try testing.expectEqual('!', parser.peek(1));
...@@ -1748,18 +1426,14 @@ test "parser peek" {...@@ -1748,18 +1426,14 @@ test "parser peek" {
1748 }1426 }
17491427
1750 { // start iteration beyond the length of the string1428 { // start iteration beyond the length of the string
1751 var parser: Parser = .{1429 var parser: Parser = .{ .bytes = "hello", .i = 5 };
1752 .iter = .{ .bytes = "hello", .i = 5 },
1753 };
17541430
1755 try testing.expectEqual(null, parser.peek(0));1431 try testing.expectEqual(null, parser.peek(0));
1756 try testing.expectEqual(null, parser.peek(1));1432 try testing.expectEqual(null, parser.peek(1));
1757 }1433 }
17581434
1759 { // empty string1435 { // empty string
1760 var parser: Parser = .{1436 var parser: Parser = .{ .bytes = "", .i = 0 };
1761 .iter = .{ .bytes = "", .i = 0 },
1762 };
17631437
1764 try testing.expectEqual(null, parser.peek(0));1438 try testing.expectEqual(null, parser.peek(0));
1765 try testing.expectEqual(null, parser.peek(2));1439 try testing.expectEqual(null, parser.peek(2));
...@@ -1768,78 +1442,78 @@ test "parser peek" {...@@ -1768,78 +1442,78 @@ test "parser peek" {
17681442
1769test "parser char" {1443test "parser char" {
1770 // character exists - iterator at 01444 // character exists - iterator at 0
1771 var parser: Parser = .{ .iter = .{ .bytes = "~~hello", .i = 0 } };1445 var parser: Parser = .{ .bytes = "~~hello", .i = 0 };
1772 try testing.expectEqual('~', parser.char());1446 try testing.expectEqual('~', parser.char());
17731447
1774 // character exists - iterator in the middle1448 // character exists - iterator in the middle
1775 parser = .{ .iter = .{ .bytes = "~~hello", .i = 3 } };1449 parser = .{ .bytes = "~~hello", .i = 3 };
1776 try testing.expectEqual('e', parser.char());1450 try testing.expectEqual('e', parser.char());
17771451
1778 // character exists - iterator at the end1452 // character exists - iterator at the end
1779 parser = .{ .iter = .{ .bytes = "~~hello", .i = 6 } };1453 parser = .{ .bytes = "~~hello", .i = 6 };
1780 try testing.expectEqual('o', parser.char());1454 try testing.expectEqual('o', parser.char());
17811455
1782 // character doesn't exist - iterator beyond the length of the string1456 // character doesn't exist - iterator beyond the length of the string
1783 parser = .{ .iter = .{ .bytes = "~~hello", .i = 7 } };1457 parser = .{ .bytes = "~~hello", .i = 7 };
1784 try testing.expectEqual(null, parser.char());1458 try testing.expectEqual(null, parser.char());
1785}1459}
17861460
1787test "parser maybe" {1461test "parser maybe" {
1788 // character exists - iterator at 01462 // character exists - iterator at 0
1789 var parser: Parser = .{ .iter = .{ .bytes = "hello world", .i = 0 } };1463 var parser: Parser = .{ .bytes = "hello world", .i = 0 };
1790 try testing.expect(parser.maybe('h'));1464 try testing.expect(parser.maybe('h'));
17911465
1792 // character exists - iterator at space1466 // character exists - iterator at space
1793 parser = .{ .iter = .{ .bytes = "hello world", .i = 5 } };1467 parser = .{ .bytes = "hello world", .i = 5 };
1794 try testing.expect(parser.maybe(' '));1468 try testing.expect(parser.maybe(' '));
17951469
1796 // character exists - iterator at the end1470 // character exists - iterator at the end
1797 parser = .{ .iter = .{ .bytes = "hello world", .i = 10 } };1471 parser = .{ .bytes = "hello world", .i = 10 };
1798 try testing.expect(parser.maybe('d'));1472 try testing.expect(parser.maybe('d'));
17991473
1800 // character doesn't exist - iterator beyond the length of the string1474 // 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 };
1802 try testing.expect(!parser.maybe('e'));1476 try testing.expect(!parser.maybe('e'));
1803}1477}
18041478
1805test "parser number" {1479test "parser number" {
1806 // input is a single digit natural number - iterator at 01480 // 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 };
1808 try testing.expect(7 == parser.number());1482 try testing.expect(7 == parser.number());
18091483
1810 // input is a two digit natural number - iterator at 11484 // input is a two digit natural number - iterator at 1
1811 parser = .{ .iter = .{ .bytes = "29", .i = 1 } };1485 parser = .{ .bytes = "29", .i = 1 };
1812 try testing.expect(9 == parser.number());1486 try testing.expect(9 == parser.number());
18131487
1814 // input is a two digit natural number - iterator beyond the length of the string1488 // 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 };
1816 try testing.expectEqual(null, parser.number());1490 try testing.expectEqual(null, parser.number());
18171491
1818 // input is an integer1492 // input is an integer
1819 parser = .{ .iter = .{ .bytes = "0", .i = 0 } };1493 parser = .{ .bytes = "0", .i = 0 };
1820 try testing.expect(0 == parser.number());1494 try testing.expect(0 == parser.number());
18211495
1822 // input is a negative integer1496 // input is a negative integer
1823 parser = .{ .iter = .{ .bytes = "-2", .i = 0 } };1497 parser = .{ .bytes = "-2", .i = 0 };
1824 try testing.expectEqual(null, parser.number());1498 try testing.expectEqual(null, parser.number());
18251499
1826 // input is a string1500 // input is a string
1827 parser = .{ .iter = .{ .bytes = "no_number", .i = 2 } };1501 parser = .{ .bytes = "no_number", .i = 2 };
1828 try testing.expectEqual(null, parser.number());1502 try testing.expectEqual(null, parser.number());
18291503
1830 // input is a single character string1504 // input is a single character string
1831 parser = .{ .iter = .{ .bytes = "n", .i = 0 } };1505 parser = .{ .bytes = "n", .i = 0 };
1832 try testing.expectEqual(null, parser.number());1506 try testing.expectEqual(null, parser.number());
18331507
1834 // input is an empty string1508 // input is an empty string
1835 parser = .{ .iter = .{ .bytes = "", .i = 0 } };1509 parser = .{ .bytes = "", .i = 0 };
1836 try testing.expectEqual(null, parser.number());1510 try testing.expectEqual(null, parser.number());
1837}1511}
18381512
1839test "parser specifier" {1513test "parser specifier" {
1840 { // input string is a digit; iterator at 01514 { // input string is a digit; iterator at 0
1841 const expected: Specifier = Specifier{ .number = 1 };1515 const expected: Specifier = Specifier{ .number = 1 };
1842 var parser: Parser = .{ .iter = .{ .bytes = "1", .i = 0 } };1516 var parser: Parser = .{ .bytes = "1", .i = 0 };
18431517
1844 const result = try parser.specifier();1518 const result = try parser.specifier();
1845 try testing.expect(expected.number == result.number);1519 try testing.expect(expected.number == result.number);
...@@ -1847,7 +1521,7 @@ test "parser specifier" {...@@ -1847,7 +1521,7 @@ test "parser specifier" {
18471521
1848 { // input string is a two digit number; iterator at 01522 { // input string is a two digit number; iterator at 0
1849 const digit: Specifier = Specifier{ .number = 42 };1523 const digit: Specifier = Specifier{ .number = 42 };
1850 var parser: Parser = .{ .iter = .{ .bytes = "42", .i = 0 } };1524 var parser: Parser = .{ .bytes = "42", .i = 0 };
18511525
1852 const result = try parser.specifier();1526 const result = try parser.specifier();
1853 try testing.expect(digit.number == result.number);1527 try testing.expect(digit.number == result.number);
...@@ -1855,7 +1529,7 @@ test "parser specifier" {...@@ -1855,7 +1529,7 @@ test "parser specifier" {
18551529
1856 { // input string is a two digit number digit; iterator at 11530 { // input string is a two digit number digit; iterator at 1
1857 const digit: Specifier = Specifier{ .number = 8 };1531 const digit: Specifier = Specifier{ .number = 8 };
1858 var parser: Parser = .{ .iter = .{ .bytes = "28", .i = 1 } };1532 var parser: Parser = .{ .bytes = "28", .i = 1 };
18591533
1860 const result = try parser.specifier();1534 const result = try parser.specifier();
1861 try testing.expect(digit.number == result.number);1535 try testing.expect(digit.number == result.number);
...@@ -1863,7 +1537,7 @@ test "parser specifier" {...@@ -1863,7 +1537,7 @@ test "parser specifier" {
18631537
1864 { // input string is a two digit number with square brackets; iterator at 01538 { // input string is a two digit number with square brackets; iterator at 0
1865 const digit: Specifier = Specifier{ .named = "15" };1539 const digit: Specifier = Specifier{ .named = "15" };
1866 var parser: Parser = .{ .iter = .{ .bytes = "[15]", .i = 0 } };1540 var parser: Parser = .{ .bytes = "[15]", .i = 0 };
18671541
1868 const result = try parser.specifier();1542 const result = try parser.specifier();
1869 try testing.expectEqualStrings(digit.named, result.named);1543 try testing.expectEqualStrings(digit.named, result.named);
...@@ -1871,21 +1545,21 @@ test "parser specifier" {...@@ -1871,21 +1545,21 @@ test "parser specifier" {
18711545
1872 { // input string is not a number and contains square brackets; iterator at 01546 { // input string is not a number and contains square brackets; iterator at 0
1873 const digit: Specifier = Specifier{ .named = "hello" };1547 const digit: Specifier = Specifier{ .named = "hello" };
1874 var parser: Parser = .{ .iter = .{ .bytes = "[hello]", .i = 0 } };1548 var parser: Parser = .{ .bytes = "[hello]", .i = 0 };
18751549
1876 const result = try parser.specifier();1550 const result = try parser.specifier();
1877 try testing.expectEqualStrings(digit.named, result.named);1551 try testing.expectEqualStrings(digit.named, result.named);
1878 }1552 }
18791553
1880 { // input string is not a number and doesn't contain closing square bracket; iterator at 01554 { // 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
1883 const result = parser.specifier();1557 const result = parser.specifier();
1884 try testing.expectError(@field(anyerror, "Expected closing ]"), result);1558 try testing.expectError(@field(anyerror, "Expected closing ]"), result);
1885 }1559 }
18861560
1887 { // input string is not a number and doesn't contain closing square bracket; iterator at 21561 { // 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
1890 const result = parser.specifier();1564 const result = parser.specifier();
1891 try testing.expectError(@field(anyerror, "Expected closing ]"), result);1565 try testing.expectError(@field(anyerror, "Expected closing ]"), result);
...@@ -1893,7 +1567,7 @@ test "parser specifier" {...@@ -1893,7 +1567,7 @@ test "parser specifier" {
18931567
1894 { // input string is not a number and contains unbalanced square brackets; iterator at 01568 { // input string is not a number and contains unbalanced square brackets; iterator at 0
1895 const digit: Specifier = Specifier{ .named = "[[hello" };1569 const digit: Specifier = Specifier{ .named = "[[hello" };
1896 var parser: Parser = .{ .iter = .{ .bytes = "[[[hello]", .i = 0 } };1570 var parser: Parser = .{ .bytes = "[[[hello]", .i = 0 };
18971571
1898 const result = try parser.specifier();1572 const result = try parser.specifier();
1899 try testing.expectEqualStrings(digit.named, result.named);1573 try testing.expectEqualStrings(digit.named, result.named);
...@@ -1901,7 +1575,7 @@ test "parser specifier" {...@@ -1901,7 +1575,7 @@ test "parser specifier" {
19011575
1902 { // input string is not a number and contains unbalanced square brackets; iterator at 11576 { // input string is not a number and contains unbalanced square brackets; iterator at 1
1903 const digit: Specifier = Specifier{ .named = "[[hello" };1577 const digit: Specifier = Specifier{ .named = "[[hello" };
1904 var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]", .i = 1 } };1578 var parser: Parser = .{ .bytes = "[[[[hello]]]]]", .i = 1 };
19051579
1906 const result = try parser.specifier();1580 const result = try parser.specifier();
1907 try testing.expectEqualStrings(digit.named, result.named);1581 try testing.expectEqualStrings(digit.named, result.named);
...@@ -1909,9 +1583,13 @@ test "parser specifier" {...@@ -1909,9 +1583,13 @@ test "parser specifier" {
19091583
1910 { // input string is neither a digit nor a named argument1584 { // input string is neither a digit nor a named argument
1911 const char: Specifier = Specifier{ .none = {} };1585 const char: Specifier = Specifier{ .none = {} };
1912 var parser: Parser = .{ .iter = .{ .bytes = "hello", .i = 0 } };1586 var parser: Parser = .{ .bytes = "hello", .i = 0 };
19131587
1914 const result = try parser.specifier();1588 const result = try parser.specifier();
1915 try testing.expectEqual(char.none, result.none);1589 try testing.expectEqual(char.none, result.none);
1916 }1590 }
1917}1591}
1592
1593test {
1594 _ = float;
1595}
lib/std/fs/File.zig+193-108
...@@ -1,3 +1,20 @@...@@ -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
1/// The OS-specific file descriptor or file handle.18/// The OS-specific file descriptor or file handle.
2handle: Handle,19handle: Handle,
320
...@@ -844,7 +861,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {...@@ -844,7 +861,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {
844 return posix.write(self.handle, bytes);861 return posix.write(self.handle, bytes);
845}862}
846863
847/// One-shot alternative to `std.io.Writer.writeAll` via `writer`.864/// Deprecated in favor of `Writer`.
848pub fn writeAll(self: File, bytes: []const u8) WriteError!void {865pub fn writeAll(self: File, bytes: []const u8) WriteError!void {
849 var index: usize = 0;866 var index: usize = 0;
850 while (index < bytes.len) {867 while (index < bytes.len) {
...@@ -900,6 +917,8 @@ pub const Reader = struct {...@@ -900,6 +917,8 @@ pub const Reader = struct {
900 file: File,917 file: File,
901 err: ?ReadError = null,918 err: ?ReadError = null,
902 mode: Reader.Mode = .positional,919 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.
903 pos: u64 = 0,922 pos: u64 = 0,
904 size: ?u64 = null,923 size: ?u64 = null,
905 size_err: ?GetEndPosError = null,924 size_err: ?GetEndPosError = null,
...@@ -1008,7 +1027,7 @@ pub const Reader = struct {...@@ -1008,7 +1027,7 @@ pub const Reader = struct {
1008 };1027 };
1009 var remaining = std.math.cast(u64, offset) orelse return seek_err;1028 var remaining = std.math.cast(u64, offset) orelse return seek_err;
1010 while (remaining > 0) {1029 while (remaining > 0) {
1011 const n = discard(&r.interface, .limited(remaining)) catch |err| {1030 const n = discard(&r.interface, .limited64(remaining)) catch |err| {
1012 r.seek_err = err;1031 r.seek_err = err;
1013 return err;1032 return err;
1014 };1033 };
...@@ -1043,7 +1062,7 @@ pub const Reader = struct {...@@ -1043,7 +1062,7 @@ pub const Reader = struct {
1043 const max_buffers_len = 16;1062 const max_buffers_len = 16;
10441063
1045 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {1064 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));
1047 switch (r.mode) {1066 switch (r.mode) {
1048 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {1067 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
1049 error.Unimplemented => {1068 error.Unimplemented => {
...@@ -1067,10 +1086,14 @@ pub const Reader = struct {...@@ -1067,10 +1086,14 @@ pub const Reader = struct {
1067 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {1086 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1068 error.Unseekable => {1087 error.Unseekable => {
1069 r.mode = r.mode.toStreaming();1088 r.mode = r.mode.toStreaming();
1070 if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch {1089 const pos = r.pos;
1071 r.mode = .failure;1090 if (pos != 0) {
1072 return error.ReadFailed;1091 r.pos = 0;
1073 };1092 r.seekBy(@intCast(pos)) catch {
1093 r.mode = .failure;
1094 return error.ReadFailed;
1095 };
1096 }
1074 return 0;1097 return 0;
1075 },1098 },
1076 else => |e| {1099 else => |e| {
...@@ -1113,7 +1136,7 @@ pub const Reader = struct {...@@ -1113,7 +1136,7 @@ pub const Reader = struct {
1113 }1136 }
11141137
1115 fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize {1138 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));
1117 const file = r.file;1140 const file = r.file;
1118 const pos = r.pos;1141 const pos = r.pos;
1119 switch (r.mode) {1142 switch (r.mode) {
...@@ -1195,10 +1218,14 @@ pub const Reader = struct {...@@ -1195,10 +1218,14 @@ pub const Reader = struct {
1195 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {1218 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
1196 error.Unseekable => {1219 error.Unseekable => {
1197 r.mode = r.mode.toStreaming();1220 r.mode = r.mode.toStreaming();
1198 if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch {1221 const pos = r.pos;
1199 r.mode = .failure;1222 if (pos != 0) {
1200 return error.ReadFailed;1223 r.pos = 0;
1201 };1224 r.seekBy(@intCast(pos)) catch {
1225 r.mode = .failure;
1226 return error.ReadFailed;
1227 };
1228 }
1202 return 0;1229 return 0;
1203 },1230 },
1204 else => |e| {1231 else => |e| {
...@@ -1246,6 +1273,8 @@ pub const Writer = struct {...@@ -1246,6 +1273,8 @@ pub const Writer = struct {
1246 file: File,1273 file: File,
1247 err: ?WriteError = null,1274 err: ?WriteError = null,
1248 mode: Writer.Mode = .positional,1275 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.
1249 pos: u64 = 0,1278 pos: u64 = 0,
1250 sendfile_err: ?SendfileError = null,1279 sendfile_err: ?SendfileError = null,
1251 copy_file_range_err: ?CopyFileRangeError = null,1280 copy_file_range_err: ?CopyFileRangeError = null,
...@@ -1308,110 +1337,162 @@ pub const Writer = struct {...@@ -1308,110 +1337,162 @@ pub const Writer = struct {
1308 };1337 };
1309 }1338 }
13101339
1311 pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {1340 pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1312 const w: *Writer = @fieldParentPtr("interface", io_writer);1341 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
1313 const handle = w.file.handle;1342 const handle = w.file.handle;
1314 const buffered = io_writer.buffered();1343 const buffered = io_w.buffered();
1315 var splat_buffer: [256]u8 = undefined;1344 if (is_windows) switch (w.mode) {
1316 if (is_windows) {1345 .positional, .positional_reading => {
1317 var i: usize = 0;1346 if (buffered.len != 0) {
1318 while (i < buffered.len) {1347 const n = windows.WriteFile(handle, buffered, w.pos) catch |err| {
1319 const n = windows.WriteFile(handle, buffered[i..], null) 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| {
1320 w.err = err;1366 w.err = err;
1321 w.pos += i;
1322 _ = io_writer.consume(i);
1323 return error.WriteFailed;1367 return error.WriteFailed;
1324 };1368 };
1325 i += n;1369 w.pos += n;
1326 if (data.len > 0 and buffered.len - i < n) {1370 return io_w.consume(n);
1327 w.pos += i;1371 },
1328 return io_writer.consume(i);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);
1329 }1380 }
1330 }1381 for (data[0 .. data.len - 1]) |buf| {
1331 if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) {1382 if (buf.len == 0) continue;
1332 w.pos += i;1383 const n = windows.WriteFile(handle, buf, null) catch |err| {
1333 return io_writer.consume(i);1384 w.err = err;
1334 }1385 return error.WriteFailed;
1335 const n = windows.WriteFile(handle, data[0], null) catch |err| {1386 };
1336 w.err = err;1387 w.pos += n;
1337 return 0;1388 return io_w.consume(n);
1338 };1389 }
1339 w.pos += n;1390 const pattern = data[data.len - 1];
1340 return n;1391 if (pattern.len == 0 or splat == 0) return 0;
1341 }1392 const n = windows.WriteFile(handle, pattern, null) catch |err| {
1342 if (data.len == 0) {1393 std.debug.print("windows write file failed3: {t}\n", .{err});
1343 var i: usize = 0;
1344 while (i < buffered.len) {
1345 i += std.posix.write(handle, buffered) catch |err| {
1346 w.err = err;1394 w.err = err;
1347 w.pos += i;
1348 _ = io_writer.consume(i);
1349 return error.WriteFailed;1395 return error.WriteFailed;
1350 };1396 };
1351 }1397 w.pos += n;
1352 w.pos += i;1398 return io_w.consume(n);
1353 return io_writer.consumeAll();1399 },
1354 }1400 .failure => return error.WriteFailed,
1401 };
1355 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;1402 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1356 var len: usize = 0;1403 var len: usize = 0;
1357 if (buffered.len > 0) {1404 if (buffered.len > 0) {
1358 iovecs[len] = .{ .base = buffered.ptr, .len = buffered.len };1405 iovecs[len] = .{ .base = buffered.ptr, .len = buffered.len };
1359 len += 1;1406 len += 1;
1360 }1407 }
1361 for (data) |d| {1408 for (data[0 .. data.len - 1]) |d| {
1362 if (d.len == 0) continue;1409 if (d.len == 0) continue;
1363 if (iovecs.len - len == 0) break;
1364 iovecs[len] = .{ .base = d.ptr, .len = d.len };1410 iovecs[len] = .{ .base = d.ptr, .len = d.len };
1365 len += 1;1411 len += 1;
1412 if (iovecs.len - len == 0) break;
1366 }1413 }
1367 switch (splat) {1414 const pattern = data[data.len - 1];
1368 0 => if (data[data.len - 1].len != 0) {1415 if (iovecs.len - len != 0) switch (splat) {
1369 len -= 1;1416 0 => {},
1417 1 => if (pattern.len != 0) {
1418 iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len };
1419 len += 1;
1370 },1420 },
1371 1 => {},1421 else => switch (pattern.len) {
1372 else => switch (data[data.len - 1].len) {
1373 0 => {},1422 0 => {},
1374 1 => {1423 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;
1375 const memset_len = @min(splat_buffer.len, splat);1430 const memset_len = @min(splat_buffer.len, splat);
1376 const buf = splat_buffer[0..memset_len];1431 const buf = splat_buffer[0..memset_len];
1377 @memset(buf, data[data.len - 1][0]);1432 @memset(buf, pattern[0]);
1378 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };1433 iovecs[len] = .{ .base = buf.ptr, .len = buf.len };
1434 len += 1;
1379 var remaining_splat = splat - buf.len;1435 var remaining_splat = splat - buf.len;
1380 while (remaining_splat > splat_buffer.len and len < iovecs.len) {1436 while (remaining_splat > splat_buffer.len and iovecs.len - len != 0) {
1381 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };1437 assert(buf.len == splat_buffer.len);
1382 remaining_splat -= splat_buffer.len;1438 iovecs[len] = .{ .base = splat_buffer.ptr, .len = splat_buffer.len };
1383 len += 1;1439 len += 1;
1440 remaining_splat -= splat_buffer.len;
1384 }1441 }
1385 if (remaining_splat > 0 and len < iovecs.len) {1442 if (remaining_splat > 0 and iovecs.len - len != 0) {
1386 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };1443 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
1387 len += 1;1444 len += 1;
1388 }1445 }
1389 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1390 w.err = err;
1391 return error.WriteFailed;
1392 };
1393 },1446 },
1394 else => for (0..splat - 1) |_| {1447 else => for (0..splat) |_| {
1395 if (iovecs.len - len == 0) break;1448 iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len };
1396 iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].len };
1397 len += 1;1449 len += 1;
1450 if (iovecs.len - len == 0) break;
1398 },1451 },
1399 },1452 },
1400 }
1401 const n = std.posix.writev(handle, iovecs[0..len]) catch |err| {
1402 w.err = err;
1403 return error.WriteFailed;
1404 };1453 };
1405 w.pos += n;1454 if (len == 0) return 0;
1406 return io_writer.consume(n);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 }
1407 }1488 }
14081489
1409 pub fn sendFile(1490 pub fn sendFile(
1410 io_writer: *std.io.Writer,1491 io_w: *std.io.Writer,
1411 file_reader: *Reader,1492 file_reader: *Reader,
1412 limit: std.io.Limit,1493 limit: std.io.Limit,
1413 ) std.io.Writer.FileError!usize {1494 ) std.io.Writer.FileError!usize {
1414 const w: *Writer = @fieldParentPtr("interface", io_writer);1495 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
1415 const out_fd = w.file.handle;1496 const out_fd = w.file.handle;
1416 const in_fd = file_reader.file.handle;1497 const in_fd = file_reader.file.handle;
1417 // TODO try using copy_file_range on FreeBSD1498 // TODO try using copy_file_range on FreeBSD
...@@ -1422,7 +1503,7 @@ pub const Writer = struct {...@@ -1422,7 +1503,7 @@ pub const Writer = struct {
1422 if (w.sendfile_err != null) break :sf;1503 if (w.sendfile_err != null) break :sf;
1423 // Linux sendfile does not support headers.1504 // Linux sendfile does not support headers.
1424 const buffered = limit.slice(file_reader.interface.buffer);1505 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);
1426 const max_count = 0x7ffff000; // Avoid EINVAL.1507 const max_count = 0x7ffff000; // Avoid EINVAL.
1427 var off: std.os.linux.off_t = undefined;1508 var off: std.os.linux.off_t = undefined;
1428 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {1509 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
...@@ -1446,10 +1527,14 @@ pub const Writer = struct {...@@ -1446,10 +1527,14 @@ pub const Writer = struct {
1446 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) {1527 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) {
1447 error.Unseekable => {1528 error.Unseekable => {
1448 file_reader.mode = file_reader.mode.toStreaming();1529 file_reader.mode = file_reader.mode.toStreaming();
1449 if (file_reader.pos != 0) file_reader.seekBy(@intCast(file_reader.pos)) catch {1530 const pos = file_reader.pos;
1450 file_reader.mode = .failure;1531 if (pos != 0) {
1451 return error.ReadFailed;1532 file_reader.pos = 0;
1452 };1533 file_reader.seekBy(@intCast(pos)) catch {
1534 file_reader.mode = .failure;
1535 return error.ReadFailed;
1536 };
1537 }
1453 return 0;1538 return 0;
1454 },1539 },
1455 else => |e| {1540 else => |e| {
...@@ -1465,21 +1550,21 @@ pub const Writer = struct {...@@ -1465,21 +1550,21 @@ pub const Writer = struct {
1465 w.pos += n;1550 w.pos += n;
1466 return n;1551 return n;
1467 }1552 }
1468 const copy_file_range_fn = switch (native_os) {1553 const copy_file_range = switch (native_os) {
1469 .freebsd => std.os.freebsd.copy_file_range,1554 .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,1555 .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {},
1471 else => null,1556 else => {},
1472 };1557 };
1473 if (copy_file_range_fn) |copy_file_range| cfr: {1558 if (@TypeOf(copy_file_range) != void) cfr: {
1474 if (w.copy_file_range_err != null) break :cfr;1559 if (w.copy_file_range_err != null) break :cfr;
1475 const buffered = limit.slice(file_reader.interface.buffer);1560 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);
1477 var off_in: i64 = undefined;1562 var off_in: i64 = undefined;
1478 var off_out: i64 = undefined;1563 var off_out: i64 = undefined;
1479 const off_in_ptr: ?*i64 = switch (file_reader.mode) {1564 const off_in_ptr: ?*i64 = switch (file_reader.mode) {
1480 .positional_reading, .streaming_reading => return error.Unimplemented,1565 .positional_reading, .streaming_reading => return error.Unimplemented,
1481 .positional => p: {1566 .positional => p: {
1482 off_in = file_reader.pos;1567 off_in = @intCast(file_reader.pos);
1483 break :p &off_in;1568 break :p &off_in;
1484 },1569 },
1485 .streaming => null,1570 .streaming => null,
...@@ -1488,7 +1573,7 @@ pub const Writer = struct {...@@ -1488,7 +1573,7 @@ pub const Writer = struct {
1488 const off_out_ptr: ?*i64 = switch (w.mode) {1573 const off_out_ptr: ?*i64 = switch (w.mode) {
1489 .positional_reading, .streaming_reading => return error.Unimplemented,1574 .positional_reading, .streaming_reading => return error.Unimplemented,
1490 .positional => p: {1575 .positional => p: {
1491 off_out = w.pos;1576 off_out = @intCast(w.pos);
1492 break :p &off_out;1577 break :p &off_out;
1493 },1578 },
1494 .streaming => null,1579 .streaming => null,
...@@ -1542,19 +1627,35 @@ pub const Writer = struct {...@@ -1542,19 +1627,35 @@ pub const Writer = struct {
1542 }1627 }
15431628
1544 pub fn seekTo(w: *Writer, offset: u64) SeekError!void {1629 pub fn seekTo(w: *Writer, offset: u64) SeekError!void {
1545 if (w.seek_err) |err| return err;
1546 switch (w.mode) {1630 switch (w.mode) {
1547 .positional, .positional_reading => {1631 .positional, .positional_reading => {
1548 w.pos = offset;1632 w.pos = offset;
1549 },1633 },
1550 .streaming, .streaming_reading => {1634 .streaming, .streaming_reading => {
1635 if (w.seek_err) |err| return err;
1551 posix.lseek_SET(w.file.handle, offset) catch |err| {1636 posix.lseek_SET(w.file.handle, offset) catch |err| {
1552 w.seek_err = err;1637 w.seek_err = err;
1553 return err;1638 return err;
1554 };1639 };
1640 w.pos = offset;
1555 },1641 },
1642 .failure => return w.seek_err.?,
1556 }1643 }
1557 }1644 }
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 }
1558};1659};
15591660
1560/// Defaults to positional reading; falls back to streaming.1661/// Defaults to positional reading; falls back to streaming.
...@@ -1568,9 +1669,10 @@ pub fn reader(file: File, buffer: []u8) Reader {...@@ -1568,9 +1669,10 @@ pub fn reader(file: File, buffer: []u8) Reader {
1568/// Positional is more threadsafe, since the global seek position is not1669/// Positional is more threadsafe, since the global seek position is not
1569/// affected, but when such syscalls are not available, preemptively choosing1670/// affected, but when such syscalls are not available, preemptively choosing
1570/// `Reader.Mode.streaming` will skip a failed syscall.1671/// `Reader.Mode.streaming` will skip a failed syscall.
1571pub fn readerStreaming(file: File) Reader {1672pub fn readerStreaming(file: File, buffer: []u8) Reader {
1572 return .{1673 return .{
1573 .file = file,1674 .file = file,
1675 .interface = Reader.initInterface(buffer),
1574 .mode = .streaming,1676 .mode = .streaming,
1575 .seek_err = error.Unseekable,1677 .seek_err = error.Unseekable,
1576 };1678 };
...@@ -1753,20 +1855,3 @@ pub fn downgradeLock(file: File) LockError!void {...@@ -1753,20 +1855,3 @@ pub fn downgradeLock(file: File) LockError!void {
1753 };1855 };
1754 }1856 }
1755}1857}
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 {...@@ -146,30 +146,28 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {
146 return out[0 .. out.len - 1 :0];146 return out[0 .. out.len - 1 :0];
147}147}
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) {
150 return .{ .data = paths };150 return .{ .data = paths };
151}151}
152152
153fn formatJoin(paths: []const []const u8, bw: *std.io.Writer, comptime fmt: []const u8) !void {153fn formatJoin(paths: []const []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
154 comptime assert(fmt.len == 0);
155
156 const first_path_idx = for (paths, 0..) |p, idx| {154 const first_path_idx = for (paths, 0..) |p, idx| {
157 if (p.len != 0) break idx;155 if (p.len != 0) break idx;
158 } else return;156 } else return;
159157
160 try bw.writeAll(paths[first_path_idx]); // first component158 try w.writeAll(paths[first_path_idx]); // first component
161 var prev_path = paths[first_path_idx];159 var prev_path = paths[first_path_idx];
162 for (paths[first_path_idx + 1 ..]) |this_path| {160 for (paths[first_path_idx + 1 ..]) |this_path| {
163 if (this_path.len == 0) continue; // skip empty components161 if (this_path.len == 0) continue; // skip empty components
164 const prev_sep = isSep(prev_path[prev_path.len - 1]);162 const prev_sep = isSep(prev_path[prev_path.len - 1]);
165 const this_sep = isSep(this_path[0]);163 const this_sep = isSep(this_path[0]);
166 if (!prev_sep and !this_sep) {164 if (!prev_sep and !this_sep) {
167 try bw.writeByte(sep);165 try w.writeByte(sep);
168 }166 }
169 if (prev_sep and this_sep) {167 if (prev_sep and this_sep) {
170 try bw.writeAll(this_path[1..]); // skip redundant separator168 try w.writeAll(this_path[1..]); // skip redundant separator
171 } else {169 } else {
172 try bw.writeAll(this_path);170 try w.writeAll(this_path);
173 }171 }
174 prev_path = this_path;172 prev_path = this_path;
175 }173 }
lib/std/fs/test.zig+2-2
...@@ -1798,11 +1798,11 @@ test "walker" {...@@ -1798,11 +1798,11 @@ test "walker" {
1798 var num_walked: usize = 0;1798 var num_walked: usize = 0;
1799 while (try walker.next()) |entry| {1799 while (try walker.next()) |entry| {
1800 testing.expect(expected_basenames.has(entry.basename)) catch |err| {1800 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)});
1802 return err;1802 return err;
1803 };1803 };
1804 testing.expect(expected_paths.has(entry.path)) catch |err| {1804 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)});
1806 return err;1806 return err;
1807 };1807 };
1808 // make sure that the entry.dir is the containing dir1808 // make sure that the entry.dir is the containing dir
lib/std/heap.zig+1-1
...@@ -287,7 +287,7 @@ fn rawCAlloc(...@@ -287,7 +287,7 @@ fn rawCAlloc(
287) ?[*]u8 {287) ?[*]u8 {
288 _ = context;288 _ = context;
289 _ = return_address;289 _ = 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)));
291 // Note that this pointer cannot be aligncasted to max_align_t because if291 // Note that this pointer cannot be aligncasted to max_align_t because if
292 // len is < max_align_t then the alignment can be smaller. For example, if292 // len is < max_align_t then the alignment can be smaller. For example, if
293 // max_align_t is 16, but the user requests 8 bytes, there is no built-in293 // 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 {...@@ -42,7 +42,7 @@ pub const ArenaAllocator = struct {
42 data: usize,42 data: usize,
43 node: std.SinglyLinkedList.Node = .{},43 node: std.SinglyLinkedList.Node = .{},
44 };44 };
45 const BufNode_alignment: Alignment = .fromByteUnits(@alignOf(BufNode));45 const BufNode_alignment: Alignment = .of(BufNode);
4646
47 pub fn init(child_allocator: Allocator) ArenaAllocator {47 pub fn init(child_allocator: Allocator) ArenaAllocator {
48 return (State{}).promote(child_allocator);48 return (State{}).promote(child_allocator);
lib/std/heap/debug_allocator.zig+1-1
...@@ -1054,7 +1054,7 @@ const TraceKind = enum {...@@ -1054,7 +1054,7 @@ const TraceKind = enum {
1054 free,1054 free,
1055};1055};
10561056
1057const test_config = Config{};1057const test_config: Config = .{};
10581058
1059test "small allocations - free in same order" {1059test "small allocations - free in same order" {
1060 var gpa = DebugAllocator(test_config){};1060 var gpa = DebugAllocator(test_config){};
lib/std/http.zig+2-2
...@@ -42,8 +42,8 @@ pub const Method = enum(u64) {...@@ -42,8 +42,8 @@ pub const Method = enum(u64) {
42 return x;42 return x;
43 }43 }
4444
45 pub fn write(self: Method, w: anytype) !void {45 pub fn format(self: Method, w: *std.io.Writer) std.io.Writer.Error!void {
46 const bytes = std.mem.asBytes(&@intFromEnum(self));46 const bytes: []const u8 = @ptrCast(&@intFromEnum(self));
47 const str = std.mem.sliceTo(bytes, 0);47 const str = std.mem.sliceTo(bytes, 0);
48 try w.writeAll(str);48 try w.writeAll(str);
49 }49 }
lib/std/http/Client.zig+13-4
...@@ -920,7 +920,7 @@ pub const Request = struct {...@@ -920,7 +920,7 @@ pub const Request = struct {
920 .authority = connection.proxied,920 .authority = connection.proxied,
921 .path = true,921 .path = true,
922 .query = true,922 .query = true,
923 }, w);923 });
924 }924 }
925 try w.writeByte(' ');925 try w.writeByte(' ');
926 try w.writeAll(@tagName(r.version));926 try w.writeAll(@tagName(r.version));
...@@ -1280,9 +1280,18 @@ pub const basic_authorization = struct {...@@ -1280,9 +1280,18 @@ pub const basic_authorization = struct {
1280 }1280 }
12811281
1282 pub fn valueLengthFromUri(uri: Uri) usize {1282 pub fn valueLengthFromUri(uri: Uri) usize {
1283 // TODO don't abuse formatted printing to count percent encoded characters1283 const user: Uri.Component = uri.user orelse .empty;
1284 const user_len = std.fmt.count("{fuser}", .{uri.user orelse Uri.Component.empty});1284 const password: Uri.Component = uri.password orelse .empty;
1285 const password_len = std.fmt.count("{fpassword}", .{uri.password orelse Uri.Component.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
1286 return valueLength(@intCast(user_len), @intCast(password_len));1295 return valueLength(@intCast(user_len), @intCast(password_len));
1287 }1296 }
12881297
lib/std/http/test.zig+2-4
...@@ -405,10 +405,8 @@ test "general client/server API coverage" {...@@ -405,10 +405,8 @@ test "general client/server API coverage" {
405 fn handleRequest(request: *http.Server.Request, listen_port: u16) !void {405 fn handleRequest(request: *http.Server.Request, listen_port: u16) !void {
406 const log = std.log.scoped(.server);406 const log = std.log.scoped(.server);
407407
408 log.info("{} {s} {s}", .{408 log.info("{f} {s} {s}", .{
409 request.head.method,409 request.head.method, @tagName(request.head.version), request.head.target,
410 @tagName(request.head.version),
411 request.head.target,
412 });410 });
413411
414 const gpa = std.testing.allocator;412 const gpa = std.testing.allocator;
lib/std/io.zig+10
...@@ -19,6 +19,12 @@ pub const Limit = enum(usize) {...@@ -19,6 +19,12 @@ pub const Limit = enum(usize) {
19 return @enumFromInt(n);19 return @enumFromInt(n);
20 }20 }
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
22 pub fn countVec(data: []const []const u8) Limit {28 pub fn countVec(data: []const []const u8) Limit {
23 var total: usize = 0;29 var total: usize = 0;
24 for (data) |d| total += d.len;30 for (data) |d| total += d.len;
...@@ -33,6 +39,10 @@ pub const Limit = enum(usize) {...@@ -33,6 +39,10 @@ pub const Limit = enum(usize) {
33 return @min(n, @intFromEnum(l));39 return @min(n, @intFromEnum(l));
34 }40 }
3541
42 pub fn minInt64(l: Limit, n: u64) usize {
43 return @min(n, @intFromEnum(l));
44 }
45
36 pub fn slice(l: Limit, s: []u8) []u8 {46 pub fn slice(l: Limit, s: []u8) []u8 {
37 return s[0..l.minInt(s.len)];47 return s[0..l.minInt(s.len)];
38 }48 }
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 {...@@ -26,7 +26,8 @@ pub const VTable = struct {
26 /// Returns the number of bytes written, which will be at minimum `0` and26 /// Returns the number of bytes written, which will be at minimum `0` and
27 /// at most `limit`. The number returned, including zero, does not indicate27 /// at most `limit`. The number returned, including zero, does not indicate
28 /// end of stream. `limit` is guaranteed to be at least as large as the28 /// 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.
30 ///31 ///
31 /// The reader's internal logical seek position moves forward in accordance32 /// The reader's internal logical seek position moves forward in accordance
32 /// with the number of bytes returned from this function.33 /// with the number of bytes returned from this function.
...@@ -35,7 +36,15 @@ pub const VTable = struct {...@@ -35,7 +36,15 @@ pub const VTable = struct {
35 /// sizes combined with short reads (returning a value less than `limit`)36 /// sizes combined with short reads (returning a value less than `limit`)
36 /// in order to minimize complexity.37 /// in order to minimize complexity.
37 ///38 ///
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.
39 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,48 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
4049
41 /// Consumes bytes from the internally tracked stream position without50 /// Consumes bytes from the internally tracked stream position without
...@@ -55,6 +64,8 @@ pub const VTable = struct {...@@ -55,6 +64,8 @@ pub const VTable = struct {
55 /// The default implementation is is based on calling `stream`, borrowing64 /// The default implementation is is based on calling `stream`, borrowing
56 /// `buffer` to construct a temporary `Writer` and ignoring the written65 /// `buffer` to construct a temporary `Writer` and ignoring the written
57 /// data.66 /// data.
67 ///
68 /// This function is only called when `buffer` is empty.
58 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,69 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
59};70};
6071
...@@ -102,7 +113,7 @@ const ending_state: Reader = .fixed(&.{});...@@ -102,7 +113,7 @@ const ending_state: Reader = .fixed(&.{});
102pub const ending: *Reader = @constCast(&ending_state);113pub const ending: *Reader = @constCast(&ending_state);
103114
104pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {115pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {
105 return Limited.init(r, limit, buffer);116 return .init(r, limit, buffer);
106}117}
107118
108/// Constructs a `Reader` such that it will read from `buffer` and then end.119/// 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 {...@@ -128,10 +139,8 @@ pub fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
128 r.seek += n;139 r.seek += n;
129 return n;140 return n;
130 }141 }
131 const before = w.count;
132 const n = try r.vtable.stream(r, w, limit);142 const n = try r.vtable.stream(r, w, limit);
133 assert(n <= @intFromEnum(limit));143 assert(n <= @intFromEnum(limit));
134 assert(w.count == before + n);
135 return n;144 return n;
136}145}
137146
...@@ -154,19 +163,13 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {...@@ -154,19 +163,13 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {
154pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {163pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
155 assert(r.seek == 0);164 assert(r.seek == 0);
156 assert(r.end == 0);165 assert(r.end == 0);
157 var w: Writer = .discarding(r.buffer);166 var dw: Writer.Discarding = .init(r.buffer);
158 const n = r.stream(&w, limit) catch |err| switch (err) {167 const n = r.stream(&dw.writer, limit) catch |err| switch (err) {
159 error.WriteFailed => unreachable,168 error.WriteFailed => unreachable,
160 error.ReadFailed => return error.ReadFailed,169 error.ReadFailed => return error.ReadFailed,
161 error.EndOfStream => return error.EndOfStream,170 error.EndOfStream => return error.EndOfStream,
162 };171 };
163 if (n > @intFromEnum(limit)) {172 assert(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 }
170 return n;173 return n;
171}174}
172175
...@@ -193,7 +196,7 @@ pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize {...@@ -193,7 +196,7 @@ pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize {
193/// Consumes the stream until the end, ignoring all the data, returning the196/// Consumes the stream until the end, ignoring all the data, returning the
194/// number of bytes discarded.197/// number of bytes discarded.
195pub fn discardRemaining(r: *Reader) ShortError!usize {198pub fn discardRemaining(r: *Reader) ShortError!usize {
196 var offset: usize = r.end;199 var offset: usize = r.end - r.seek;
197 r.seek = 0;200 r.seek = 0;
198 r.end = 0;201 r.end = 0;
199 while (true) {202 while (true) {
...@@ -262,10 +265,9 @@ pub fn appendRemaining(...@@ -262,10 +265,9 @@ pub fn appendRemaining(
262 error.EndOfStream => break,265 error.EndOfStream => break,
263 error.ReadFailed => return error.ReadFailed,266 error.ReadFailed => return error.ReadFailed,
264 };267 };
265 if (n >= dest.len) {268 if (n > dest.len) {
266 r.end = n - dest.len;269 r.end = n - dest.len;
267 list.items.len += dest.len;270 list.items.len += dest.len;
268 if (n == dest.len) return;
269 return error.StreamTooLong;271 return error.StreamTooLong;
270 }272 }
271 list.items.len += n;273 list.items.len += n;
...@@ -320,22 +322,29 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -320,22 +322,29 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
320 },322 },
321 .writer = .{323 .writer = .{
322 .buffer = if (first.len >= r.buffer.len) first else r.buffer,324 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
323 .vtable = &Writer.VectorWrapper.vtable,325 .vtable = Writer.VectorWrapper.vtable,
324 },326 },
325 };327 };
326 var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {328 var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) {
327 error.WriteFailed => {329 error.WriteFailed => {
330 assert(!wrapper.used);
328 if (wrapper.writer.buffer.ptr == first.ptr) {331 if (wrapper.writer.buffer.ptr == first.ptr) {
329 remaining -= wrapper.writer.end;332 remaining -= wrapper.writer.end;
330 } else {333 } else {
334 assert(wrapper.writer.end <= r.buffer.len);
331 r.end = wrapper.writer.end;335 r.end = wrapper.writer.end;
332 }336 }
333 break;337 break;
334 },338 },
335 else => |e| return e,339 else => |e| return e,
336 };340 };
337 if (wrapper.writer.buffer.ptr != first.ptr) {341 if (!wrapper.used) {
338 r.end = n;342 if (wrapper.writer.buffer.ptr == first.ptr) {
343 remaining -= n;
344 } else {
345 assert(n <= r.buffer.len);
346 r.end = n;
347 }
339 break;348 break;
340 }349 }
341 if (n < first.len) {350 if (n < first.len) {
...@@ -352,6 +361,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -352,6 +361,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
352 remaining -= mid.len;361 remaining -= mid.len;
353 n -= mid.len;362 n -= mid.len;
354 }363 }
364 assert(n <= r.buffer.len);
355 r.end = n;365 r.end = n;
356 break;366 break;
357 }367 }
...@@ -441,7 +451,7 @@ pub fn toss(r: *Reader, n: usize) void {...@@ -441,7 +451,7 @@ pub fn toss(r: *Reader, n: usize) void {
441}451}
442452
443/// Equivalent to `toss(r.bufferedLen())`.453/// Equivalent to `toss(r.bufferedLen())`.
444pub fn tossAll(r: *Reader) void {454pub fn tossBuffered(r: *Reader) void {
445 r.seek = 0;455 r.seek = 0;
446 r.end = 0;456 r.end = 0;
447}457}
...@@ -553,7 +563,7 @@ pub fn discardShort(r: *Reader, n: usize) ShortError!usize {...@@ -553,7 +563,7 @@ pub fn discardShort(r: *Reader, n: usize) ShortError!usize {
553/// See also:563/// See also:
554/// * `peek`564/// * `peek`
555/// * `readSliceShort`565/// * `readSliceShort`
556pub fn readSlice(r: *Reader, buffer: []u8) Error!void {566pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
557 const n = try readSliceShort(r, buffer);567 const n = try readSliceShort(r, buffer);
558 if (n != buffer.len) return error.EndOfStream;568 if (n != buffer.len) return error.EndOfStream;
559}569}
...@@ -567,7 +577,7 @@ pub fn readSlice(r: *Reader, buffer: []u8) Error!void {...@@ -567,7 +577,7 @@ pub fn readSlice(r: *Reader, buffer: []u8) Error!void {
567/// only if the stream reached the end.577/// only if the stream reached the end.
568///578///
569/// See also:579/// See also:
570/// * `readSlice`580/// * `readSliceAll`
571pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {581pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
572 const in_buffer = r.buffer[r.seek..r.end];582 const in_buffer = r.buffer[r.seek..r.end];
573 const copy_len = @min(buffer.len, in_buffer.len);583 const copy_len = @min(buffer.len, in_buffer.len);
...@@ -588,17 +598,16 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {...@@ -588,17 +598,16 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
588 },598 },
589 .writer = .{599 .writer = .{
590 .buffer = if (remaining.len >= r.buffer.len) remaining else r.buffer,600 .buffer = if (remaining.len >= r.buffer.len) remaining else r.buffer,
591 .vtable = &Writer.VectorWrapper.vtable,601 .vtable = Writer.VectorWrapper.vtable,
592 },602 },
593 };603 };
594 const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) {604 const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) {
595 error.WriteFailed => {605 error.WriteFailed => {
596 if (wrapper.writer.buffer.ptr != remaining.ptr) {606 if (!wrapper.used) {
597 assert(r.seek == 0);607 assert(r.seek == 0);
598 r.seek = remaining.len;608 r.seek = remaining.len;
599 r.end = wrapper.writer.end;609 r.end = wrapper.writer.end;
600 @memcpy(remaining, r.buffer[0..remaining.len]);610 @memcpy(remaining, r.buffer[0..remaining.len]);
601 return buffer.len;
602 }611 }
603 return buffer.len;612 return buffer.len;
604 },613 },
...@@ -626,7 +635,7 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {...@@ -626,7 +635,7 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
626/// comptime-known and matches host endianness.635/// comptime-known and matches host endianness.
627///636///
628/// See also:637/// See also:
629/// * `readSlice`638/// * `readSliceAll`
630/// * `readSliceEndianAlloc`639/// * `readSliceEndianAlloc`
631pub inline fn readSliceEndian(640pub inline fn readSliceEndian(
632 r: *Reader,641 r: *Reader,
...@@ -634,7 +643,7 @@ pub inline fn readSliceEndian(...@@ -634,7 +643,7 @@ pub inline fn readSliceEndian(
634 buffer: []Elem,643 buffer: []Elem,
635 endian: std.builtin.Endian,644 endian: std.builtin.Endian,
636) Error!void {645) Error!void {
637 try readSlice(r, @ptrCast(buffer));646 try readSliceAll(r, @ptrCast(buffer));
638 if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem);647 if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem);
639}648}
640649
...@@ -651,15 +660,16 @@ pub inline fn readSliceEndianAlloc(...@@ -651,15 +660,16 @@ pub inline fn readSliceEndianAlloc(
651) ReadAllocError![]Elem {660) ReadAllocError![]Elem {
652 const dest = try allocator.alloc(Elem, len);661 const dest = try allocator.alloc(Elem, len);
653 errdefer allocator.free(dest);662 errdefer allocator.free(dest);
654 try readSlice(r, @ptrCast(dest));663 try readSliceAll(r, @ptrCast(dest));
655 if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem);664 if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem);
656 return dest;665 return dest;
657}666}
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 {
660 const dest = try allocator.alloc(u8, len);670 const dest = try allocator.alloc(u8, len);
661 errdefer allocator.free(dest);671 errdefer allocator.free(dest);
662 try readSlice(r, dest);672 try readSliceAll(r, dest);
663 return dest;673 return dest;
664}674}
665675
...@@ -692,6 +702,17 @@ pub fn takeSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel...@@ -692,6 +702,17 @@ pub fn takeSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel
692 return result;702 return result;
693}703}
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`
695pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {716pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 {
696 const result = try r.peekDelimiterInclusive(sentinel);717 const result = try r.peekDelimiterInclusive(sentinel);
697 return result[0 .. result.len - 1 :sentinel];718 return result[0 .. result.len - 1 :sentinel];
...@@ -732,26 +753,21 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -732,26 +753,21 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
732 @branchHint(.likely);753 @branchHint(.likely);
733 return buffer[seek .. end + 1];754 return buffer[seek .. end + 1];
734 }755 }
735 if (seek > 0) {756 if (r.vtable.stream == &endingStream) {
736 const remainder = buffer[seek..];757 // Protect the `@constCast` of `fixed`.
737 @memmove(buffer[0..remainder.len], remainder);758 return error.EndOfStream;
738 r.end = remainder.len;
739 r.seek = 0;
740 }759 }
741 var writer: Writer = .{760 r.rebase();
742 .buffer = r.buffer,761 while (r.buffer.len - r.end != 0) {
743 .vtable = &.{ .drain = Writer.fixedDrain },762 const end_cap = r.buffer[r.end..];
744 };763 var writer: Writer = .fixed(end_cap);
745 while (r.end < r.buffer.len) {764 const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) {
746 writer.end = r.end;
747 const n = r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
748 error.WriteFailed => unreachable,765 error.WriteFailed => unreachable,
749 else => |e| return e,766 else => |e| return e,
750 };767 };
751 const prev_end = r.end;768 r.end += n;
752 r.end = prev_end + n;769 if (std.mem.indexOfScalarPos(u8, end_cap[0..n], 0, delimiter)) |end| {
753 if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], prev_end, delimiter)) |end| {770 return r.buffer[0 .. r.end - n + end + 1];
754 return r.buffer[0 .. end + 1];
755 }771 }
756 }772 }
757 return error.StreamTooLong;773 return error.StreamTooLong;
...@@ -777,9 +793,10 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -777,9 +793,10 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
777pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {793pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
778 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {794 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
779 error.EndOfStream => {795 error.EndOfStream => {
780 if (r.end == 0) return error.EndOfStream;796 const remaining = r.buffer[r.seek..r.end];
781 r.toss(r.end);797 if (remaining.len == 0) return error.EndOfStream;
782 return r.buffer[0..r.end];798 r.toss(remaining.len);
799 return remaining;
783 },800 },
784 else => |e| return e,801 else => |e| return e,
785 };802 };
...@@ -807,8 +824,10 @@ pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -807,8 +824,10 @@ pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
807pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {824pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
808 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {825 const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) {
809 error.EndOfStream => {826 error.EndOfStream => {
810 if (r.end == 0) return error.EndOfStream;827 const remaining = r.buffer[r.seek..r.end];
811 return r.buffer[0..r.end];828 if (remaining.len == 0) return error.EndOfStream;
829 r.toss(remaining.len);
830 return remaining;
812 },831 },
813 else => |e| return e,832 else => |e| return e,
814 };833 };
...@@ -818,37 +837,50 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -818,37 +837,50 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
818/// Appends to `w` contents by reading from the stream until `delimiter` is837/// Appends to `w` contents by reading from the stream until `delimiter` is
819/// found. Does not write the delimiter itself.838/// found. Does not write the delimiter itself.
820///839///
821/// Returns number of bytes streamed.840/// Returns number of bytes streamed, which may be zero, or error.EndOfStream
822pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize {841/// if the delimiter was not found.
823 const amount, const to = try r.readAny(w, delimiter, .unlimited);842///
824 return switch (to) {843/// Asserts buffer capacity of at least one. This function performs better with
825 .delimiter => amount,844/// larger buffers.
826 .limit => unreachable,845///
827 .end => error.EndOfStream,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,
828 };853 };
854 if (r.seek == r.end) return error.EndOfStream;
855 return n;
829}856}
830857
831/// Appends to `w` contents by reading from the stream until `delimiter` is found.858/// Appends to `w` contents by reading from the stream until `delimiter` is found.
832/// Does not write the delimiter itself.859/// Does not write the delimiter itself.
833///860///
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.
835///866///
836/// Returns number of bytes streamed. The end is not signaled to the writer.867/// See also:
837pub fn readDelimiterEnding(868/// * `streamDelimiter`
869/// * `streamDelimiterLimit`
870pub fn streamDelimiterEnding(
838 r: *Reader,871 r: *Reader,
839 w: *Writer,872 w: *Writer,
840 delimiter: u8,873 delimiter: u8,
841) StreamRemainingError!usize {874) StreamRemainingError!usize {
842 const amount, const to = try r.readAny(w, delimiter, .unlimited);875 return streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) {
843 return switch (to) {876 error.StreamTooLong => unreachable, // unlimited is passed
844 .delimiter, .end => amount,877 else => |e| return e,
845 .limit => unreachable,
846 };878 };
847}879}
848880
849pub const StreamDelimiterLimitedError = StreamRemainingError || error{881pub const StreamDelimiterLimitError = error{
850 /// Stream ended before the delimiter was found.882 ReadFailed,
851 EndOfStream,883 WriteFailed,
852 /// The delimiter was not found within the limit.884 /// The delimiter was not found within the limit.
853 StreamTooLong,885 StreamTooLong,
854};886};
...@@ -856,65 +888,103 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{...@@ -856,65 +888,103 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{
856/// Appends to `w` contents by reading from the stream until `delimiter` is found.888/// Appends to `w` contents by reading from the stream until `delimiter` is found.
857/// Does not write the delimiter itself.889/// Does not write the delimiter itself.
858///890///
859/// Returns number of bytes streamed.891/// Returns number of bytes streamed, which may be zero. End of stream can be
860pub fn readDelimiterLimit(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(
861 r: *Reader,897 r: *Reader,
862 w: *Writer,898 w: *Writer,
863 delimiter: u8,899 delimiter: u8,
864 limit: Limit,900 limit: Limit,
865) StreamDelimiterLimitedError!usize {901) StreamDelimiterLimitError!usize {
866 const amount, const to = try r.readAny(w, delimiter, limit);902 var remaining = @intFromEnum(limit);
867 return switch (to) {903 while (remaining != 0) {
868 .delimiter => amount,904 const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) {
869 .limit => error.StreamTooLong,905 error.ReadFailed => return error.ReadFailed,
870 .end => error.EndOfStream,906 error.EndOfStream => return @intFromEnum(limit) - remaining,
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 },
886 });907 });
887 if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| {908 if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| {
888 try w.writeAll(available[0..delimiter_index]);909 try w.writeAll(available[0..delimiter_index]);
889 r.toss(delimiter_index + 1);910 r.toss(delimiter_index);
890 return .{ amount + delimiter_index, .delimiter };911 remaining -= delimiter_index;
891 };912 return @intFromEnum(limit) - remaining;
913 }
892 try w.writeAll(available);914 try w.writeAll(available);
893 r.toss(available.len);915 r.toss(available.len);
894 amount += available.len;916 remaining -= available.len;
895 remaining = remaining.subtract(available.len).?;
896 }917 }
897 return .{ amount, .limit };918 return error.StreamTooLong;
898}919}
899920
900/// Reads from the stream until specified byte is found, discarding all data,921/// Reads from the stream until specified byte is found, discarding all data,
901/// including the delimiter.922/// including the delimiter.
902///923///
903/// If end of stream is found, this function succeeds.924/// Returns number of bytes discarded, or `error.EndOfStream` if the delimiter
904pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void {925/// is not found.
905 _ = r;926///
906 _ = delimiter;927/// See also:
907 @panic("TODO");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;
908}939}
909940
910/// Reads from the stream until specified byte is found, discarding all data,941/// Reads from the stream until specified byte is found, discarding all data,
911/// excluding the delimiter.942/// excluding the delimiter.
912///943///
913/// Succeeds if stream ends before delimiter found.944/// Returns the number of bytes discarded.
914pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!void {945///
915 _ = r;946/// Succeeds if stream ends before delimiter found. End of stream can be
916 _ = delimiter;947/// detected by checking if the delimiter is buffered.
917 @panic("TODO");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;
918}988}
919989
920/// Fills the buffer such that it contains at least `n` bytes, without990/// 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 {...@@ -930,6 +1000,19 @@ pub fn fill(r: *Reader, n: usize) Error!void {
930 @branchHint(.likely);1000 @branchHint(.likely);
931 return;1001 return;
932 }1002 }
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 }
933 rebaseCapacity(r, n);1016 rebaseCapacity(r, n);
934 var writer: Writer = .{1017 var writer: Writer = .{
935 .buffer = r.buffer,1018 .buffer = r.buffer,
...@@ -970,11 +1053,12 @@ pub fn fillMore(r: *Reader) Error!void {...@@ -970,11 +1053,12 @@ pub fn fillMore(r: *Reader) Error!void {
970pub fn peekByte(r: *Reader) Error!u8 {1053pub fn peekByte(r: *Reader) Error!u8 {
971 const buffer = r.buffer[0..r.end];1054 const buffer = r.buffer[0..r.end];
972 const seek = r.seek;1055 const seek = r.seek;
973 if (seek >= buffer.len) {1056 if (seek < buffer.len) {
974 @branchHint(.unlikely);1057 @branchHint(.likely);
975 try fill(r, 1);1058 return buffer[seek];
976 }1059 }
977 return buffer[seek];1060 try fill(r, 1);
1061 return r.buffer[r.seek];
978}1062}
9791063
980/// Reads 1 byte from the stream or returns `error.EndOfStream`.1064/// 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:...@@ -1009,6 +1093,7 @@ pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n:
1009///1093///
1010/// See also:1094/// See also:
1011/// * `peekStruct`1095/// * `peekStruct`
1096/// * `takeStructEndian`
1012pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {1097pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
1013 // Only extern and packed structs have defined in-memory layout.1098 // Only extern and packed structs have defined in-memory layout.
1014 comptime assert(@typeInfo(T).@"struct".layout != .auto);1099 comptime assert(@typeInfo(T).@"struct".layout != .auto);
...@@ -1021,6 +1106,7 @@ pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {...@@ -1021,6 +1106,7 @@ pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T {
1021///1106///
1022/// See also:1107/// See also:
1023/// * `takeStruct`1108/// * `takeStruct`
1109/// * `peekStructEndian`
1024pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {1110pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
1025 // Only extern and packed structs have defined in-memory layout.1111 // Only extern and packed structs have defined in-memory layout.
1026 comptime assert(@typeInfo(T).@"struct".layout != .auto);1112 comptime assert(@typeInfo(T).@"struct".layout != .auto);
...@@ -1031,6 +1117,10 @@ pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {...@@ -1031,6 +1117,10 @@ pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T {
1031///1117///
1032/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`1118/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
1033/// when `endian` is comptime-known and matches the host endianness.1119/// when `endian` is comptime-known and matches the host endianness.
1120///
1121/// See also:
1122/// * `takeStruct`
1123/// * `peekStructEndian`
1034pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {1124pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1035 var res = (try r.takeStruct(T)).*;1125 var res = (try r.takeStruct(T)).*;
1036 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);1126 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...@@ -1041,6 +1131,10 @@ pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin
1041///1131///
1042/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`1132/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
1043/// when `endian` is comptime-known and matches the host endianness.1133/// when `endian` is comptime-known and matches the host endianness.
1134///
1135/// See also:
1136/// * `takeStructEndian`
1137/// * `peekStruct`
1044pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {1138pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
1045 var res = (try r.peekStruct(T)).*;1139 var res = (try r.peekStruct(T)).*;
1046 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);1140 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
...@@ -1218,146 +1312,295 @@ test fixed {...@@ -1218,146 +1312,295 @@ test fixed {
1218}1312}
12191313
1220test peek {1314test 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));
1222}1318}
12231319
1224test peekGreedy {1320test peekGreedy {
1225 return error.Unimplemented;1321 var r: Reader = .fixed("abc");
1322 try testing.expectEqualStrings("abc", try r.peekGreedy(1));
1226}1323}
12271324
1228test toss {1325test toss {
1229 return error.Unimplemented;1326 var r: Reader = .fixed("abc");
1327 r.toss(1);
1328 try testing.expectEqualStrings("bc", r.buffered());
1230}1329}
12311330
1232test take {1331test 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));
1234}1335}
12351336
1236test takeArray {1337test 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));
1238}1341}
12391342
1240test peekArray {1343test 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));
1242}1347}
12431348
1244test discardAll {1349test discardAll {
1245 var r: Reader = .fixed("foobar");1350 var r: Reader = .fixed("foobar");
1246 try r.discard(3);1351 try r.discardAll(3);
1247 try testing.expectEqualStrings("bar", try r.take(3));1352 try testing.expectEqualStrings("bar", try r.take(3));
1248 try r.discard(0);1353 try r.discardAll(0);
1249 try testing.expectError(error.EndOfStream, r.discard(1));1354 try testing.expectError(error.EndOfStream, r.discardAll(1));
1250}1355}
12511356
1252test discardRemaining {1357test 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());
1254}1362}
12551363
1256test stream {1364test 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());
1258}1373}
12591374
1260test takeSentinel {1375test 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));
1262}1380}
12631381
1264test peekSentinel {1382test 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'));
1266}1386}
12671387
1268test takeDelimiterInclusive {1388test 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'));
1270}1392}
12711393
1272test peekDelimiterInclusive {1394test 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'));
1274}1400}
12751401
1276test takeDelimiterExclusive {1402test 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'));
1278}1407}
12791408
1280test peekDelimiterExclusive {1409test peekDelimiterExclusive {
1281 return error.Unimplemented;1410 var r: Reader = .fixed("ab\nc");
1282}1411 try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n'));
12831412 try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n'));
1284test readDelimiter {1413 r.toss(3);
1285 return error.Unimplemented;1414 try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n'));
1286}1415}
12871416
1288test readDelimiterEnding {1417test streamDelimiter {
1289 return error.Unimplemented;1418 var out_buffer: [10]u8 = undefined;
1290}1419 var r: Reader = .fixed("foo\nbars");
12911420 var w: Writer = .fixed(&out_buffer);
1292test readDelimiterLimit {1421 try testing.expectEqual(3, try r.streamDelimiter(&w, '\n'));
1293 return error.Unimplemented;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());
1294}1450}
12951451
1296test discardDelimiterExclusive {1452test 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'));
1298}1458}
12991459
1300test discardDelimiterInclusive {1460test 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));
1302}1473}
13031474
1304test fill {1475test fill {
1305 return error.Unimplemented;1476 var r: Reader = .fixed("abc");
1477 try r.fill(1);
1478 try r.fill(3);
1306}1479}
13071480
1308test takeByte {1481test 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());
1310}1486}
13111487
1312test takeByteSigned {1488test 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());
1314}1493}
13151494
1316test takeInt {1495test 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));
1318}1499}
13191500
1320test takeVarInt {1501test 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));
1322}1505}
13231506
1324test takeStruct {1507test 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));
1326}1515}
13271516
1328test peekStruct {1517test 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 }
1330}1530}
13311531
1332test takeStructEndian {1532test 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));
1334}1537}
13351538
1336test peekStructEndian {1539test 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));
1338}1544}
13391545
1340test takeEnum {1546test 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));
1342}1552}
13431553
1344test takeLeb128 {1554test 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));
1346}1559}
13471560
1348test readSliceShort {1561test 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));
1350}1569}
13511570
1352test readVec {1571test 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]);
1354}1595}
13551596
1356test "expected error.EndOfStream" {1597test "expected error.EndOfStream" {
1357 // Unit test inspired by https://github.com/ziglang/zig/issues/177331598 // Unit test inspired by https://github.com/ziglang/zig/issues/17733
1358 var r: std.io.Reader = .fixed("");1599 var buffer: [3]u8 = undefined;
1359 try std.testing.expectError(error.EndOfStream, r.readEnum(enum(u8) { a, b }, .little));1600 var r: std.io.Reader = .fixed(&buffer);
1360 try std.testing.expectError(error.EndOfStream, r.isBytes("foo"));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));
1361}1604}
13621605
1363fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {1606fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
...@@ -1389,25 +1632,51 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {...@@ -1389,25 +1632,51 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
1389test "readAlloc when the backing reader provides one byte at a time" {1632test "readAlloc when the backing reader provides one byte at a time" {
1390 const OneByteReader = struct {1633 const OneByteReader = struct {
1391 str: []const u8,1634 str: []const u8,
1392 curr: usize,1635 i: usize,
13931636 reader: Reader,
1394 fn read(self: *@This(), dest: []u8) usize {1637
1395 if (self.str.len <= self.curr or dest.len == 0)1638 fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1396 return 0;1639 assert(@intFromEnum(limit) >= 1);
13971640 const self: *@This() = @fieldParentPtr("reader", r);
1398 dest[0] = self.str[self.curr];1641 if (self.str.len - self.i == 0) return error.EndOfStream;
1399 self.curr += 1;1642 try w.writeByte(self.str[self.i]);
1643 self.i += 1;
1400 return 1;1644 return 1;
1401 }1645 }
1402 };1646 };
1403
1404 const str = "This is a test";1647 const str = "This is a test";
1405 var one_byte_stream: OneByteReader = .init(str);1648 var one_byte_stream: OneByteReader = .{
1406 const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1);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);
1407 defer std.testing.allocator.free(res);1659 defer std.testing.allocator.free(res);
1408 try std.testing.expectEqualStrings(str, res);1660 try std.testing.expectEqualStrings(str, res);
1409}1661}
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
1411/// Provides a `Reader` implementation by passing data from an underlying1680/// Provides a `Reader` implementation by passing data from an underlying
1412/// reader through `Hasher.update`.1681/// reader through `Hasher.update`.
1413///1682///
lib/std/io/Writer.zig+819-534
...@@ -14,12 +14,6 @@ vtable: *const VTable,...@@ -14,12 +14,6 @@ vtable: *const VTable,
14buffer: []u8,14buffer: []u8,
15/// In `buffer` before this are buffered bytes, after this is `undefined`.15/// In `buffer` before this are buffered bytes, after this is `undefined`.
16end: usize = 0,16end: 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
24pub const VTable = struct {18pub const VTable = struct {
25 /// Sends bytes to the logical sink. A write will only be sent here if it19 /// Sends bytes to the logical sink. A write will only be sent here if it
...@@ -37,6 +31,10 @@ pub const VTable = struct {...@@ -37,6 +31,10 @@ pub const VTable = struct {
37 /// The last element of `data` is repeated as necessary so that it is31 /// The last element of `data` is repeated as necessary so that it is
38 /// written `splat` number of times, which may be zero.32 /// written `splat` number of times, which may be zero.
39 ///33 ///
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 ///
40 /// Number of bytes consumed from `data` is returned, excluding bytes from38 /// Number of bytes consumed from `data` is returned, excluding bytes from
41 /// `buffer`.39 /// `buffer`.
42 ///40 ///
...@@ -113,8 +111,7 @@ pub const FileError = error{...@@ -113,8 +111,7 @@ pub const FileError = error{
113 Unimplemented,111 Unimplemented,
114};112};
115113
116/// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless114/// Writes to `buffer` and returns `error.WriteFailed` when it is full.
117/// modified externally, `count` will always equal `end`.
118pub fn fixed(buffer: []u8) Writer {115pub fn fixed(buffer: []u8) Writer {
119 return .{116 return .{
120 .vtable = &.{ .drain = fixedDrain },117 .vtable = &.{ .drain = fixedDrain },
...@@ -122,8 +119,8 @@ pub fn fixed(buffer: []u8) Writer {...@@ -122,8 +119,8 @@ pub fn fixed(buffer: []u8) Writer {
122 };119 };
123}120}
124121
125pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) {122pub fn hashed(w: *Writer, hasher: anytype, buffer: []u8) Hashed(@TypeOf(hasher)) {
126 return .{ .out = w, .hasher = hasher };123 return .initHasher(w, hasher, buffer);
127}124}
128125
129pub const failing: Writer = .{126pub const failing: Writer = .{
...@@ -133,16 +130,6 @@ pub const failing: Writer = .{...@@ -133,16 +130,6 @@ pub const failing: Writer = .{
133 },130 },
134};131};
135132
136pub fn discarding(buffer: []u8) Writer {
137 return .{
138 .vtable = &.{
139 .drain = discardingDrain,
140 .sendFile = discardingSendFile,
141 },
142 .buffer = buffer,
143 };
144}
145
146/// Returns the contents not yet drained.133/// Returns the contents not yet drained.
147pub fn buffered(w: *const Writer) []u8 {134pub fn buffered(w: *const Writer) []u8 {
148 return w.buffer[0..w.end];135 return w.buffer[0..w.end];
...@@ -174,53 +161,26 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz...@@ -174,53 +161,26 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz
174 assert(data.len > 0);161 assert(data.len > 0);
175 const buffer = w.buffer;162 const buffer = w.buffer;
176 const count = countSplat(data, splat);163 const count = countSplat(data, splat);
177 if (w.end + count > buffer.len) {164 if (w.end + count > buffer.len) return w.vtable.drain(w, data, splat);
178 const n = try w.vtable.drain(w, data, splat);165 for (data[0 .. data.len - 1]) |bytes| {
179 w.count += n;
180 return n;
181 }
182 w.count += count;
183 for (data) |bytes| {
184 @memcpy(buffer[w.end..][0..bytes.len], bytes);166 @memcpy(buffer[w.end..][0..bytes.len], bytes);
185 w.end += bytes.len;167 w.end += bytes.len;
186 }168 }
187 const pattern = data[data.len - 1];169 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;
194 switch (pattern.len) {170 switch (pattern.len) {
195 0 => {},171 0 => {},
196 1 => {172 1 => {
197 @memset(buffer[w.end..][0..remaining_splat], pattern[0]);173 @memset(buffer[w.end..][0..splat], pattern[0]);
198 w.end += remaining_splat;174 w.end += splat;
199 },175 },
200 else => {176 else => for (0..splat) |_| {
201 const new_end = w.end + pattern.len * remaining_splat;177 @memcpy(buffer[w.end..][0..pattern.len], pattern);
202 while (w.end < new_end) : (w.end += pattern.len) {178 w.end += pattern.len;
203 @memcpy(buffer[w.end..][0..pattern.len], pattern);
204 }
205 },179 },
206 }180 }
207 return count;181 return count;
208}182}
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
224/// Returns how many bytes were consumed from `header` and `data`.184/// Returns how many bytes were consumed from `header` and `data`.
225pub fn writeSplatHeader(185pub fn writeSplatHeader(
226 w: *Writer,186 w: *Writer,
...@@ -232,38 +192,40 @@ pub fn writeSplatHeader(...@@ -232,38 +192,40 @@ pub fn writeSplatHeader(
232 if (new_end <= w.buffer.len) {192 if (new_end <= w.buffer.len) {
233 @memcpy(w.buffer[w.end..][0..header.len], header);193 @memcpy(w.buffer[w.end..][0..header.len], header);
234 w.end = new_end;194 w.end = new_end;
235 w.count += header.len;
236 return header.len + try writeSplat(w, data, splat);195 return header.len + try writeSplat(w, data, splat);
237 }196 }
238 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.197 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
239 var i: usize = 1;198 var i: usize = 1;
240 vecs[0] = header;199 vecs[0] = header;
241 for (data) |buf| {200 for (data[0 .. data.len - 1]) |buf| {
242 if (buf.len == 0) continue;201 if (buf.len == 0) continue;
243 vecs[i] = buf;202 vecs[i] = buf;
244 i += 1;203 i += 1;
245 if (vecs.len - i == 0) break;204 if (vecs.len - i == 0) break;
246 }205 }
247 const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1;206 const pattern = data[data.len - 1];
248 const n = try w.vtable.drain(w, vecs[0..i], new_splat);207 const new_splat = s: {
249 w.count += n;208 if (pattern.len == 0 or vecs.len - i == 0) break :s 1;
250 return n;209 vecs[i] = pattern;
210 i += 1;
211 break :s splat;
212 };
213 return w.vtable.drain(w, vecs[0..i], new_splat);
251}214}
252215
253/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.216test "writeSplatHeader splatting avoids buffer aliasing temptation" {
254pub fn writeSplatHeaderLimit(217 const initial_buf = try testing.allocator.alloc(u8, 8);
255 w: *Writer,218 var aw: std.io.Writer.Allocating = .initOwnedSlice(testing.allocator, initial_buf);
256 header: []const u8,219 defer aw.deinit();
257 data: []const []const u8,220 // This test assumes 8 vector buffer in this function.
258 splat: usize,221 const n = try aw.writer.writeSplatHeader("header which is longer than buf ", &.{
259 limit: Limit,222 "1", "2", "3", "4", "5", "6", "foo", "bar", "foo",
260) Error!usize {223 }, 3);
261 _ = w;224 try testing.expectEqual(41, n);
262 _ = header;225 try testing.expectEqualStrings(
263 _ = data;226 "header which is longer than buf 123456foo",
264 _ = splat;227 aw.writer.buffered(),
265 _ = limit;228 );
266 @panic("TODO");
267}229}
268230
269/// Drains all remaining buffered data.231/// Drains all remaining buffered data.
...@@ -386,12 +348,18 @@ pub const WritableVectorIterator = struct {...@@ -386,12 +348,18 @@ pub const WritableVectorIterator = struct {
386pub const VectorWrapper = struct {348pub const VectorWrapper = struct {
387 writer: Writer,349 writer: Writer,
388 it: WritableVectorIterator,350 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 };
390};357};
391358
392pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {359pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {
393 if (w.vtable == &VectorWrapper.vtable) {360 if (w.vtable == VectorWrapper.vtable) {
394 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);361 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
362 wrapper.used = true;
395 return wrapper.it;363 return wrapper.it;
396 }364 }
397 return .{ .first = try writableSliceGreedy(w, 1) };365 return .{ .first = try writableSliceGreedy(w, 1) };
...@@ -419,7 +387,6 @@ pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {...@@ -419,7 +387,6 @@ pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
419387
420pub fn undo(w: *Writer, n: usize) void {388pub fn undo(w: *Writer, n: usize) void {
421 w.end -= n;389 w.end -= n;
422 w.count -= n;
423}390}
424391
425/// After calling `writableSliceGreedy`, this function tracks how many bytes392/// After calling `writableSliceGreedy`, this function tracks how many bytes
...@@ -430,13 +397,11 @@ pub fn advance(w: *Writer, n: usize) void {...@@ -430,13 +397,11 @@ pub fn advance(w: *Writer, n: usize) void {
430 const new_end = w.end + n;397 const new_end = w.end + n;
431 assert(new_end <= w.buffer.len);398 assert(new_end <= w.buffer.len);
432 w.end = new_end;399 w.end = new_end;
433 w.count += n;
434}400}
435401
436/// After calling `writableVector`, this function tracks how many bytes were402/// After calling `writableVector`, this function tracks how many bytes were
437/// written to it.403/// written to it.
438pub fn advanceVector(w: *Writer, n: usize) usize {404pub fn advanceVector(w: *Writer, n: usize) usize {
439 w.count += n;
440 return consume(w, n);405 return consume(w, n);
441}406}
442407
...@@ -494,12 +459,9 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {...@@ -494,12 +459,9 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {
494 @branchHint(.likely);459 @branchHint(.likely);
495 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);460 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
496 w.end += bytes.len;461 w.end += bytes.len;
497 w.count += bytes.len;
498 return bytes.len;462 return bytes.len;
499 }463 }
500 const n = try w.vtable.drain(w, &.{bytes}, 1);464 return w.vtable.drain(w, &.{bytes}, 1);
501 w.count += n;
502 return n;
503}465}
504466
505/// Asserts `buffer` capacity exceeds `preserve_length`.467/// Asserts `buffer` capacity exceeds `preserve_length`.
...@@ -509,7 +471,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro...@@ -509,7 +471,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro
509 @branchHint(.likely);471 @branchHint(.likely);
510 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);472 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
511 w.end += bytes.len;473 w.end += bytes.len;
512 w.count += bytes.len;
513 return bytes.len;474 return bytes.len;
514 }475 }
515 const temp_end = w.end -| preserve_length;476 const temp_end = w.end -| preserve_length;
...@@ -517,7 +478,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro...@@ -517,7 +478,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro
517 w.end = temp_end;478 w.end = temp_end;
518 defer w.end += preserved.len;479 defer w.end += preserved.len;
519 const n = try w.vtable.drain(w, &.{bytes}, 1);480 const n = try w.vtable.drain(w, &.{bytes}, 1);
520 w.count += n;
521 assert(w.end <= temp_end + preserved.len);481 assert(w.end <= temp_end + preserved.len);
522 @memmove(w.buffer[w.end..][0..preserved.len], preserved);482 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
523 return n;483 return n;
...@@ -542,23 +502,207 @@ pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) E...@@ -542,23 +502,207 @@ pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) E
542 while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]);502 while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]);
543}503}
544504
545pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {505/// Renders fmt string with args, calling `writer` with slices of bytes.
546 try std.fmt.format(w, format, args);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 }
547}695}
548696
549/// Calls `drain` as many times as necessary such that `byte` is transferred.697/// Calls `drain` as many times as necessary such that `byte` is transferred.
550pub fn writeByte(w: *Writer, byte: u8) Error!void {698pub fn writeByte(w: *Writer, byte: u8) Error!void {
551 while (w.buffer.len - w.end == 0) {699 while (w.buffer.len - w.end == 0) {
552 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);700 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);
553 if (n > 0) {701 if (n > 0) return;
554 w.count += 1;
555 return;
556 }
557 } else {702 } else {
558 @branchHint(.likely);703 @branchHint(.likely);
559 w.buffer[w.end] = byte;704 w.buffer[w.end] = byte;
560 w.end += 1;705 w.end += 1;
561 w.count += 1;
562 }706 }
563}707}
564708
...@@ -571,7 +715,6 @@ pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!voi...@@ -571,7 +715,6 @@ pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!voi
571 @branchHint(.likely);715 @branchHint(.likely);
572 w.buffer[w.end] = byte;716 w.buffer[w.end] = byte;
573 w.end += 1;717 w.end += 1;
574 w.count += 1;
575 }718 }
576}719}
577720
...@@ -625,12 +768,23 @@ pub fn writeStruct(w: *Writer, value: anytype) Error!void {...@@ -625,12 +768,23 @@ pub fn writeStruct(w: *Writer, value: anytype) Error!void {
625/// comptime-known and matches host endianness.768/// comptime-known and matches host endianness.
626/// TODO: make sure this value is not a reference type769/// TODO: make sure this value is not a reference type
627pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {770pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void {
628 if (native_endian == endian) {771 switch (@typeInfo(@TypeOf(value))) {
629 return w.writeStruct(value);772 .@"struct" => |info| switch (info.layout) {
630 } else {773 .auto => @compileError("ill-defined memory layout"),
631 var copy = value;774 .@"extern" => {
632 std.mem.byteSwapAllFields(@TypeOf(value), &copy);775 if (native_endian == endian) {
633 return w.writeStruct(copy);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"),
634 }788 }
635}789}
636790
...@@ -647,14 +801,6 @@ pub inline fn writeSliceEndian(...@@ -647,14 +801,6 @@ pub inline fn writeSliceEndian(
647 }801 }
648}802}
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
658/// Unlike `writeSplat` and `writeVec`, this function will call into `VTable`804/// Unlike `writeSplat` and `writeVec`, this function will call into `VTable`
659/// even if there is enough buffer capacity for the file contents.805/// even if there is enough buffer capacity for the file contents.
660///806///
...@@ -680,12 +826,10 @@ pub fn sendFileHeader(...@@ -680,12 +826,10 @@ pub fn sendFileHeader(
680 if (new_end <= w.buffer.len) {826 if (new_end <= w.buffer.len) {
681 @memcpy(w.buffer[w.end..][0..header.len], header);827 @memcpy(w.buffer[w.end..][0..header.len], header);
682 w.end = new_end;828 w.end = new_end;
683 w.count += header.len;
684 return header.len + try w.vtable.sendFile(w, file_reader, limit);829 return header.len + try w.vtable.sendFile(w, file_reader, limit);
685 }830 }
686 const buffered_contents = limit.slice(file_reader.interface.buffered());831 const buffered_contents = limit.slice(file_reader.interface.buffered());
687 const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1);832 const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1);
688 w.count += n;
689 file_reader.interface.toss(n - header.len);833 file_reader.interface.toss(n - header.len);
690 return n;834 return n;
691}835}
...@@ -698,6 +842,8 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File...@@ -698,6 +842,8 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File
698 return n;842 return n;
699}843}
700844
845/// Number of bytes logically written is returned. This excludes bytes from
846/// `buffer` because they have already been logically written.
701pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {847pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize {
702 var remaining = @intFromEnum(limit);848 var remaining = @intFromEnum(limit);
703 while (remaining > 0) {849 while (remaining > 0) {
...@@ -772,16 +918,13 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {...@@ -772,16 +918,13 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
772 switch (@typeInfo(T)) {918 switch (@typeInfo(T)) {
773 .pointer => |info| {919 .pointer => |info| {
774 try w.writeAll(@typeName(info.child) ++ "@");920 try w.writeAll(@typeName(info.child) ++ "@");
775 if (info.size == .slice)921 const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value);
776 try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{})922 return w.printInt(int, 16, .lower, .{});
777 else
778 try w.printIntOptions(@intFromPtr(value), 16, .lower, .{});
779 return;
780 },923 },
781 .optional => |info| {924 .optional => |info| {
782 if (@typeInfo(info.child) == .pointer) {925 if (@typeInfo(info.child) == .pointer) {
783 try w.writeAll(@typeName(info.child) ++ "@");926 try w.writeAll(@typeName(info.child) ++ "@");
784 try w.printIntOptions(@intFromPtr(value), 16, .lower, .{});927 try w.printInt(@intFromPtr(value), 16, .lower, .{});
785 return;928 return;
786 }929 }
787 },930 },
...@@ -791,6 +934,7 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {...@@ -791,6 +934,7 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
791 @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier");934 @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier");
792}935}
793936
937/// Asserts `buffer` capacity of at least 2 if `value` is a union.
794pub fn printValue(938pub fn printValue(
795 w: *Writer,939 w: *Writer,
796 comptime fmt: []const u8,940 comptime fmt: []const u8,
...@@ -800,26 +944,181 @@ pub fn printValue(...@@ -800,26 +944,181 @@ pub fn printValue(
800) Error!void {944) Error!void {
801 const T = @TypeOf(value);945 const T = @TypeOf(value);
802946
803 if (comptime std.mem.eql(u8, fmt, "*")) {947 switch (fmt.len) {
804 return w.printAddress(value);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 => {},
805 }1101 }
8061102
807 const is_any = comptime std.mem.eql(u8, fmt, ANY);1103 const is_any = comptime std.mem.eql(u8, fmt, ANY);
808 if (!is_any and std.meta.hasMethod(T, "format")) {1104 if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) {
809 if (fmt.len > 0 and fmt[0] == 'f') {1105 // after 0.15.0 is tagged, delete this compile error and its condition
810 return value.format(w, fmt[1..]);1106 @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
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 }
815 }1107 }
8161108
817 switch (@typeInfo(T)) {1109 switch (@typeInfo(T)) {
818 .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value),1110 .float, .comptime_float => {
819 .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value),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 },
820 .bool => {1118 .bool => {
821 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);1119 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);
823 },1122 },
824 .void => {1123 .void => {
825 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);1124 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
...@@ -852,50 +1151,30 @@ pub fn printValue(...@@ -852,50 +1151,30 @@ pub fn printValue(
852 }1151 }
853 },1152 },
854 .error_set => {1153 .error_set => {
855 if (fmt.len == 1 and fmt[0] == 's') return w.writeAll(@errorName(value));
856 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);1154 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
857 try printErrorSet(w, value);1155 optionsForbidden(options);
1156 return printErrorSet(w, value);
858 },1157 },
859 .@"enum" => {1158 .@"enum" => |info| {
860 if (fmt.len == 1 and fmt[0] == 's') {1159 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
861 try w.writeAll(@tagName(value));1160 optionsForbidden(options);
862 return;1161 if (info.is_exhaustive) {
863 }1162 return printEnumExhaustive(w, value);
864 if (!is_any) {1163 } else {
865 if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth);1164 return printEnumNonexhaustive(w, value);
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 }
882 }1165 }
883 try w.writeByte('(');
884 try w.printValue(ANY, options, @intFromEnum(value), max_depth);
885 try w.writeByte(')');
886 },1166 },
887 .@"union" => |info| {1167 .@"union" => |info| {
888 if (!is_any) {1168 if (!is_any) {
889 if (fmt.len != 0) invalidFmtError(fmt, value);1169 if (fmt.len != 0) invalidFmtError(fmt, value);
890 return printValue(w, ANY, options, value, max_depth);1170 return printValue(w, ANY, options, value, max_depth);
891 }1171 }
892 try w.writeAll(@typeName(T));
893 if (max_depth == 0) {1172 if (max_depth == 0) {
894 try w.writeAll("{ ... }");1173 try w.writeAll(".{ ... }");
895 return;1174 return;
896 }1175 }
897 if (info.tag_type) |UnionTagType| {1176 if (info.tag_type) |UnionTagType| {
898 try w.writeAll("{ .");1177 try w.writeAll(".{ .");
899 try w.writeAll(@tagName(@as(UnionTagType, value)));1178 try w.writeAll(@tagName(@as(UnionTagType, value)));
900 try w.writeAll(" = ");1179 try w.writeAll(" = ");
901 inline for (info.fields) |u_field| {1180 inline for (info.fields) |u_field| {
...@@ -904,9 +1183,22 @@ pub fn printValue(...@@ -904,9 +1183,22 @@ pub fn printValue(
904 }1183 }
905 }1184 }
906 try w.writeAll(" }");1185 try w.writeAll(" }");
907 } else {1186 } else switch (info.layout) {
908 try w.writeByte('@');1187 .auto => {
909 try w.printIntOptions(@intFromPtr(&value), 16, .lower, options);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 },
910 }1202 }
911 },1203 },
912 .@"struct" => |info| {1204 .@"struct" => |info| {
...@@ -917,10 +1209,10 @@ pub fn printValue(...@@ -917,10 +1209,10 @@ pub fn printValue(
917 if (info.is_tuple) {1209 if (info.is_tuple) {
918 // Skip the type and field names when formatting tuples.1210 // Skip the type and field names when formatting tuples.
919 if (max_depth == 0) {1211 if (max_depth == 0) {
920 try w.writeAll("{ ... }");1212 try w.writeAll(".{ ... }");
921 return;1213 return;
922 }1214 }
923 try w.writeAll("{");1215 try w.writeAll(".{");
924 inline for (info.fields, 0..) |f, i| {1216 inline for (info.fields, 0..) |f, i| {
925 if (i == 0) {1217 if (i == 0) {
926 try w.writeAll(" ");1218 try w.writeAll(" ");
...@@ -932,12 +1224,11 @@ pub fn printValue(...@@ -932,12 +1224,11 @@ pub fn printValue(
932 try w.writeAll(" }");1224 try w.writeAll(" }");
933 return;1225 return;
934 }1226 }
935 try w.writeAll(@typeName(T));
936 if (max_depth == 0) {1227 if (max_depth == 0) {
937 try w.writeAll("{ ... }");1228 try w.writeAll(".{ ... }");
938 return;1229 return;
939 }1230 }
940 try w.writeAll("{");1231 try w.writeAll(".{");
941 inline for (info.fields, 0..) |f, i| {1232 inline for (info.fields, 0..) |f, i| {
942 if (i == 0) {1233 if (i == 0) {
943 try w.writeAll(" .");1234 try w.writeAll(" .");
...@@ -952,44 +1243,24 @@ pub fn printValue(...@@ -952,44 +1243,24 @@ pub fn printValue(
952 },1243 },
953 .pointer => |ptr_info| switch (ptr_info.size) {1244 .pointer => |ptr_info| switch (ptr_info.size) {
954 .one => switch (@typeInfo(ptr_info.child)) {1245 .one => switch (@typeInfo(ptr_info.child)) {
955 .array, .@"enum", .@"union", .@"struct" => {1246 .array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth),
956 return w.printValue(fmt, options, value.*, max_depth);1247 .@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth),
957 },
958 else => {1248 else => {
959 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };1249 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
960 try w.writeVecAll(&buffers);1250 try w.writeVecAll(&buffers);
961 try w.printIntOptions(@intFromPtr(value), 16, .lower, options);1251 try w.printInt(@intFromPtr(value), 16, .lower, options);
962 return;1252 return;
963 },1253 },
964 },1254 },
965 .many, .c => {1255 .many, .c => {
966 if (ptr_info.sentinel() != null)1256 if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
967 return w.printValue(fmt, options, std.mem.span(value), max_depth);1257 optionsForbidden(options);
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);
974 try w.printAddress(value);1258 try w.printAddress(value);
975 },1259 },
976 .slice => {1260 .slice => {
977 if (!is_any and fmt.len == 0)1261 if (!is_any)
978 @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");1262 @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");
979 if (max_depth == 0)1263 if (max_depth == 0) return w.writeAll("{ ... }");
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 };
993 try w.writeAll("{ ");1264 try w.writeAll("{ ");
994 for (value, 0..) |elem, i| {1265 for (value, 0..) |elem, i| {
995 try w.printValue(fmt, options, elem, max_depth - 1);1266 try w.printValue(fmt, options, elem, max_depth - 1);
...@@ -1000,21 +1271,9 @@ pub fn printValue(...@@ -1000,21 +1271,9 @@ pub fn printValue(
1000 try w.writeAll(" }");1271 try w.writeAll(" }");
1001 },1272 },
1002 },1273 },
1003 .array => |info| {1274 .array => {
1004 if (fmt.len == 0)1275 if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})");
1005 @compileError("cannot format array without a specifier (i.e. {s} or {any})");1276 if (max_depth == 0) return w.writeAll("{ ... }");
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 }
1018 try w.writeAll("{ ");1277 try w.writeAll("{ ");
1019 for (value, 0..) |elem, i| {1278 for (value, 0..) |elem, i| {
1020 try w.printValue(fmt, options, elem, max_depth - 1);1279 try w.printValue(fmt, options, elem, max_depth - 1);
...@@ -1024,19 +1283,9 @@ pub fn printValue(...@@ -1024,19 +1283,9 @@ pub fn printValue(
1024 }1283 }
1025 try w.writeAll(" }");1284 try w.writeAll(" }");
1026 },1285 },
1027 .vector => |info| {1286 .vector => {
1028 if (max_depth == 0) {1287 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1029 return w.writeAll("{ ... }");1288 return printVector(w, fmt, options, value, max_depth);
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(" }");
1040 },1289 },
1041 .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),1290 .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),
1042 .type => {1291 .type => {
...@@ -1045,8 +1294,9 @@ pub fn printValue(...@@ -1045,8 +1294,9 @@ pub fn printValue(
1045 },1294 },
1046 .enum_literal => {1295 .enum_literal => {
1047 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);1296 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1048 const buffer = [_]u8{'.'} ++ @tagName(value);1297 optionsForbidden(options);
1049 return w.alignBufferOptions(buffer, options);1298 var vecs: [2][]const u8 = .{ ".", @tagName(value) };
1299 return w.writeVecAll(&vecs);
1050 },1300 },
1051 .null => {1301 .null => {
1052 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);1302 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
...@@ -1056,75 +1306,78 @@ pub fn printValue(...@@ -1056,75 +1306,78 @@ pub fn printValue(
1056 }1306 }
1057}1307}
10581308
1309fn optionsForbidden(options: std.fmt.Options) void {
1310 assert(options.precision == null);
1311 assert(options.width == null);
1312}
1313
1059fn printErrorSet(w: *Writer, error_set: anyerror) Error!void {1314fn printErrorSet(w: *Writer, error_set: anyerror) Error!void {
1060 var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) };1315 var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) };
1061 try w.writeVecAll(&vecs);1316 try w.writeVecAll(&vecs);
1062}1317}
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(
1065 w: *Writer,1336 w: *Writer,
1066 comptime fmt: []const u8,1337 comptime fmt: []const u8,
1067 options: std.fmt.Options,1338 options: std.fmt.Options,
1068 value: anytype,1339 value: anytype,
1340 max_depth: usize,
1069) Error!void {1341) Error!void {
1070 const int_value = if (@TypeOf(value) == comptime_int) blk: {1342 const len = @typeInfo(@TypeOf(value)).vector.len;
1071 const Int = std.math.IntFittingRange(value, value);1343 if (max_depth == 0) return w.writeAll("{ ... }");
1072 break :blk @as(Int, value);1344 try w.writeAll("{ ");
1073 } else value;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) {1352// A wrapper around `printIntAny` to avoid the generic explosion of this
1076 0 => return w.printIntOptions(int_value, 10, .lower, options),1353// function by funneling smaller integer types through `isize` and `usize`.
1077 1 => switch (fmt[0]) {1354pub inline fn printInt(
1078 'd' => return w.printIntOptions(int_value, 10, .lower, options),1355 w: *Writer,
1079 'c' => {1356 value: anytype,
1080 if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) {1357 base: u8,
1081 return w.printAsciiChar(@as(u8, int_value), options);1358 case: std.fmt.Case,
1082 } else {1359 options: std.fmt.Options,
1083 @compileError("cannot print integer that is larger than 8 bits as an ASCII character");1360) Error!void {
1084 }1361 switch (@TypeOf(value)) {
1085 },1362 isize, usize => {},
1086 'u' => {1363 comptime_int => {
1087 if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) {1364 if (comptime std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options);
1088 return w.printUnicodeCodepoint(@as(u21, int_value), options);1365 if (comptime std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options);
1089 } else {1366 const Int = std.math.IntFittingRange(value, value);
1090 @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence");1367 return printIntAny(w, @as(Int, value), base, case, options);
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),
1100 },1368 },
1101 2 => {1369 else => switch (@typeInfo(@TypeOf(value)).int.signedness) {
1102 if (fmt[0] == 'B' and fmt[1] == 'i') {1370 .signed => if (std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options),
1103 return w.printByteSize(int_value, .binary, options);1371 .unsigned => if (std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options),
1104 } else {
1105 invalidFmtError(fmt, value);
1106 }
1107 },1372 },
1108 else => invalidFmtError(fmt, value),
1109 }1373 }
1110 comptime unreachable;1374 return printIntAny(w, value, base, case, options);
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);
1115}1375}
11161376
1117pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void {1377/// In general, prefer `printInt` to avoid generic explosion. However this
1118 return w.alignBufferOptions(bytes, options);1378/// function may be used when optimal codegen for a particular integer type is
1119}1379/// desired.
11201380pub fn printIntAny(
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(
1128 w: *Writer,1381 w: *Writer,
1129 value: anytype,1382 value: anytype,
1130 base: u8,1383 base: u8,
...@@ -1132,20 +1385,14 @@ pub fn printIntOptions(...@@ -1132,20 +1385,14 @@ pub fn printIntOptions(
1132 options: std.fmt.Options,1385 options: std.fmt.Options,
1133) Error!void {1386) Error!void {
1134 assert(base >= 2);1387 assert(base >= 2);
11351388 const value_info = @typeInfo(@TypeOf(value)).int;
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;
11421389
1143 // The type must have the same size as `base` or be wider in order for the1390 // The type must have the same size as `base` or be wider in order for the
1144 // division to work1391 // division to work
1145 const min_int_bits = comptime @max(value_info.bits, 8);1392 const min_int_bits = comptime @max(value_info.bits, 8);
1146 const MinInt = std.meta.Int(.unsigned, min_int_bits);1393 const MinInt = std.meta.Int(.unsigned, min_int_bits);
11471394
1148 const abs_value = @abs(int_value);1395 const abs_value = @abs(value);
1149 // The worst case in terms of space needed is base 2, plus 1 for the sign1396 // The worst case in terms of space needed is base 2, plus 1 for the sign
1150 var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined;1397 var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined;
11511398
...@@ -1192,41 +1439,69 @@ pub fn printIntOptions(...@@ -1192,41 +1439,69 @@ pub fn printIntOptions(
1192 return w.alignBufferOptions(buf[index..], options);1439 return w.alignBufferOptions(buf[index..], options);
1193}1440}
11941441
1195pub fn printFloat(1442pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void {
1196 w: *Writer,1443 return w.alignBufferOptions(@as(*const [1]u8, &c), options);
1197 comptime fmt: []const u8,1444}
1198 options: std.fmt.Options,
1199 value: anytype,
1200) Error!void {
1201 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
12021445
1203 if (fmt.len > 1) invalidFmtError(fmt, value);1446pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void {
1204 switch (if (fmt.len == 0) 'e' else fmt[0]) {1447 return w.alignBufferOptions(bytes, options);
1205 'e' => {1448}
1206 const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) {1449
1207 error.BufferTooSmall => "(float)",1450pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void {
1208 };1451 var buf: [4]u8 = undefined;
1209 return w.alignBufferOptions(s, options);1452 const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) {
1210 },1453 error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: {
1211 'd' => {1454 buf[0..3].* = std.unicode.replacement_character_utf8;
1212 const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) {1455 break :l 3;
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);
1221 },1456 },
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 => {},
1223 }1488 }
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);
1224}1493}
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 {
1227 if (std.math.signbit(value)) try w.writeByte('-');1496 if (std.math.signbit(value)) try w.writeByte('-');
1228 if (std.math.isNan(value)) return w.writeAll("nan");1497 if (std.math.isNan(value)) return w.writeAll(switch (case) {
1229 if (std.math.isInf(value)) return w.writeAll("inf");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
1231 const T = @TypeOf(value);1506 const T = @TypeOf(value);
1232 const TU = std.meta.Int(.unsigned, @bitSizeOf(T));1507 const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
...@@ -1302,7 +1577,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize)...@@ -1302,7 +1577,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize)
13021577
1303 // +1 for the decimal part.1578 // +1 for the decimal part.
1304 var buf: [1 + mantissa_digits]u8 = undefined;1579 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
1307 try w.writeAll("0x");1582 try w.writeAll("0x");
1308 try w.writeByte(buf[0]);1583 try w.writeByte(buf[0]);
...@@ -1319,7 +1594,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize)...@@ -1319,7 +1594,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize)
1319 try w.splatByteAll('0', precision - trimmed.len);1594 try w.splatByteAll('0', precision - trimmed.len);
1320 };1595 };
1321 try w.writeAll("p");1596 try w.writeAll("p");
1322 try w.printIntOptions(exponent - exponent_bias, 10, .lower, .{});1597 try w.printInt(exponent - exponent_bias, 10, case, .{});
1323}1598}
13241599
1325pub const ByteSizeUnits = enum {1600pub const ByteSizeUnits = enum {
...@@ -1415,7 +1690,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {...@@ -1415,7 +1690,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
1415 }) |unit| {1690 }) |unit| {
1416 if (ns_remaining >= unit.ns) {1691 if (ns_remaining >= unit.ns) {
1417 const units = ns_remaining / unit.ns;1692 const units = ns_remaining / unit.ns;
1418 try w.printIntOptions(units, 10, .lower, .{});1693 try w.printInt(units, 10, .lower, .{});
1419 try w.writeByte(unit.sep);1694 try w.writeByte(unit.sep);
1420 ns_remaining -= units * unit.ns;1695 ns_remaining -= units * unit.ns;
1421 if (ns_remaining == 0) return;1696 if (ns_remaining == 0) return;
...@@ -1429,13 +1704,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {...@@ -1429,13 +1704,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
1429 }) |unit| {1704 }) |unit| {
1430 const kunits = ns_remaining * 1000 / unit.ns;1705 const kunits = ns_remaining * 1000 / unit.ns;
1431 if (kunits >= 1000) {1706 if (kunits >= 1000) {
1432 try w.printIntOptions(kunits / 1000, 10, .lower, .{});1707 try w.printInt(kunits / 1000, 10, .lower, .{});
1433 const frac = kunits % 1000;1708 const frac = kunits % 1000;
1434 if (frac > 0) {1709 if (frac > 0) {
1435 // Write up to 3 decimal places1710 // Write up to 3 decimal places
1436 var decimal_buf = [_]u8{ '.', 0, 0, 0 };1711 var decimal_buf = [_]u8{ '.', 0, 0, 0 };
1437 var inner: Writer = .fixed(decimal_buf[1..]);1712 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;
1439 var end: usize = 4;1714 var end: usize = 4;
1440 while (end > 1) : (end -= 1) {1715 while (end > 1) : (end -= 1) {
1441 if (decimal_buf[end - 1] != '0') break;1716 if (decimal_buf[end - 1] != '0') break;
...@@ -1446,7 +1721,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {...@@ -1446,7 +1721,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
1446 }1721 }
1447 }1722 }
14481723
1449 try w.printIntOptions(ns_remaining, 10, .lower, .{});1724 try w.printInt(ns_remaining, 10, .lower, .{});
1450 try w.writeAll("ns");1725 try w.writeAll("ns");
1451}1726}
14521727
...@@ -1456,12 +1731,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {...@@ -1456,12 +1731,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
1456pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void {1731pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void {
1457 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 241732 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24
1458 var buf: [24]u8 = undefined;1733 var buf: [24]u8 = undefined;
1459 var sub_bw: Writer = .fixed(&buf);1734 var sub_writer: Writer = .fixed(&buf);
1460 switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) {1735 if (@TypeOf(nanoseconds) == comptime_int) {
1461 .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable,1736 if (nanoseconds >= 0) {
1462 .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable,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,
1463 }1744 }
1464 return w.alignBufferOptions(sub_bw.buffered(), options);1745 return w.alignBufferOptions(sub_writer.buffered(), options);
1465}1746}
14661747
1467pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void {1748pub 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 {...@@ -1547,24 +1828,14 @@ fn writeMultipleOf7Leb128(w: *Writer, value: anytype) Error!void {
1547 }1828 }
1548}1829}
15491830
1550test "formatValue max_depth" {1831test "printValue max_depth" {
1551 const Vec2 = struct {1832 const Vec2 = struct {
1552 const SelfType = @This();1833 const SelfType = @This();
1553 x: f32,1834 x: f32,
1554 y: f32,1835 y: f32,
15551836
1556 pub fn format(1837 pub fn format(self: SelfType, w: *Writer) Error!void {
1557 self: SelfType,1838 return w.print("({d:.3},{d:.3})", .{ self.x, self.y });
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 }
1568 }1839 }
1569 };1840 };
1570 const E = enum {1841 const E = enum {
...@@ -1598,133 +1869,133 @@ test "formatValue max_depth" {...@@ -1598,133 +1869,133 @@ test "formatValue max_depth" {
1598 var buf: [1000]u8 = undefined;1869 var buf: [1000]u8 = undefined;
1599 var w: Writer = .fixed(&buf);1870 var w: Writer = .fixed(&buf);
1600 try w.printValue("", .{}, inst, 0);1871 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);
1604 try w.printValue("", .{}, inst, 1);1875 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);
1608 try w.printValue("", .{}, inst, 2);1879 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);
1612 try w.printValue("", .{}, inst, 3);1883 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
1615 const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 };1886 const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 };
1616 w.reset();1887 w = .fixed(&buf);
1617 try w.printValue("", .{}, vec, 0);1888 try w.printValue("", .{}, vec, 0);
1618 try testing.expectEqualStrings("{ ... }", w.buffered());1889 try testing.expectEqualStrings("{ ... }", w.buffered());
16191890
1620 w.reset();1891 w = .fixed(&buf);
1621 try w.printValue("", .{}, vec, 1);1892 try w.printValue("", .{}, vec, 1);
1622 try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());1893 try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());
1623}1894}
16241895
1625test printDuration {1896test printDuration {
1626 testDurationCase("0ns", 0);1897 try testDurationCase("0ns", 0);
1627 testDurationCase("1ns", 1);1898 try testDurationCase("1ns", 1);
1628 testDurationCase("999ns", std.time.ns_per_us - 1);1899 try testDurationCase("999ns", std.time.ns_per_us - 1);
1629 testDurationCase("1us", std.time.ns_per_us);1900 try testDurationCase("1us", std.time.ns_per_us);
1630 testDurationCase("1.45us", 1450);1901 try testDurationCase("1.45us", 1450);
1631 testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);1902 try testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);
1632 testDurationCase("14.5us", 14500);1903 try testDurationCase("14.5us", 14500);
1633 testDurationCase("145us", 145000);1904 try testDurationCase("145us", 145000);
1634 testDurationCase("999.999us", std.time.ns_per_ms - 1);1905 try testDurationCase("999.999us", std.time.ns_per_ms - 1);
1635 testDurationCase("1ms", std.time.ns_per_ms + 1);1906 try testDurationCase("1ms", std.time.ns_per_ms + 1);
1636 testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);1907 try testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);
1637 testDurationCase("1.11ms", 1110000);1908 try testDurationCase("1.11ms", 1110000);
1638 testDurationCase("1.111ms", 1111000);1909 try testDurationCase("1.111ms", 1111000);
1639 testDurationCase("1.111ms", 1111100);1910 try testDurationCase("1.111ms", 1111100);
1640 testDurationCase("999.999ms", std.time.ns_per_s - 1);1911 try testDurationCase("999.999ms", std.time.ns_per_s - 1);
1641 testDurationCase("1s", std.time.ns_per_s);1912 try testDurationCase("1s", std.time.ns_per_s);
1642 testDurationCase("59.999s", std.time.ns_per_min - 1);1913 try testDurationCase("59.999s", std.time.ns_per_min - 1);
1643 testDurationCase("1m", std.time.ns_per_min);1914 try testDurationCase("1m", std.time.ns_per_min);
1644 testDurationCase("1h", std.time.ns_per_hour);1915 try testDurationCase("1h", std.time.ns_per_hour);
1645 testDurationCase("1d", std.time.ns_per_day);1916 try testDurationCase("1d", std.time.ns_per_day);
1646 testDurationCase("1w", std.time.ns_per_week);1917 try testDurationCase("1w", std.time.ns_per_week);
1647 testDurationCase("1y", 365 * std.time.ns_per_day);1918 try testDurationCase("1y", 365 * std.time.ns_per_day);
1648 testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w11919 try 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);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);
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);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);
1651 testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);1922 try 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);1923 try 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);1924 try 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);1925 try testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1655 testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));1926 try testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));
16561927
1657 testing.expectFmt("=======0ns", "{D:=>10}", .{0});1928 try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
1658 testing.expectFmt("1ns=======", "{D:=<10}", .{1});1929 try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
1659 testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});1930 try testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});
1660}1931}
16611932
1662test printDurationSigned {1933test printDurationSigned {
1663 testDurationCaseSigned("0ns", 0);1934 try testDurationCaseSigned("0ns", 0);
1664 testDurationCaseSigned("1ns", 1);1935 try testDurationCaseSigned("1ns", 1);
1665 testDurationCaseSigned("-1ns", -(1));1936 try testDurationCaseSigned("-1ns", -(1));
1666 testDurationCaseSigned("999ns", std.time.ns_per_us - 1);1937 try testDurationCaseSigned("999ns", std.time.ns_per_us - 1);
1667 testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));1938 try testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));
1668 testDurationCaseSigned("1us", std.time.ns_per_us);1939 try testDurationCaseSigned("1us", std.time.ns_per_us);
1669 testDurationCaseSigned("-1us", -(std.time.ns_per_us));1940 try testDurationCaseSigned("-1us", -(std.time.ns_per_us));
1670 testDurationCaseSigned("1.45us", 1450);1941 try testDurationCaseSigned("1.45us", 1450);
1671 testDurationCaseSigned("-1.45us", -(1450));1942 try testDurationCaseSigned("-1.45us", -(1450));
1672 testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);1943 try testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);
1673 testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));1944 try testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));
1674 testDurationCaseSigned("14.5us", 14500);1945 try testDurationCaseSigned("14.5us", 14500);
1675 testDurationCaseSigned("-14.5us", -(14500));1946 try testDurationCaseSigned("-14.5us", -(14500));
1676 testDurationCaseSigned("145us", 145000);1947 try testDurationCaseSigned("145us", 145000);
1677 testDurationCaseSigned("-145us", -(145000));1948 try testDurationCaseSigned("-145us", -(145000));
1678 testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);1949 try testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);
1679 testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));1950 try testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));
1680 testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);1951 try testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);
1681 testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));1952 try testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));
1682 testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);1953 try testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);
1683 testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));1954 try testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));
1684 testDurationCaseSigned("1.11ms", 1110000);1955 try testDurationCaseSigned("1.11ms", 1110000);
1685 testDurationCaseSigned("-1.11ms", -(1110000));1956 try testDurationCaseSigned("-1.11ms", -(1110000));
1686 testDurationCaseSigned("1.111ms", 1111000);1957 try testDurationCaseSigned("1.111ms", 1111000);
1687 testDurationCaseSigned("-1.111ms", -(1111000));1958 try testDurationCaseSigned("-1.111ms", -(1111000));
1688 testDurationCaseSigned("1.111ms", 1111100);1959 try testDurationCaseSigned("1.111ms", 1111100);
1689 testDurationCaseSigned("-1.111ms", -(1111100));1960 try testDurationCaseSigned("-1.111ms", -(1111100));
1690 testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);1961 try testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);
1691 testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));1962 try testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));
1692 testDurationCaseSigned("1s", std.time.ns_per_s);1963 try testDurationCaseSigned("1s", std.time.ns_per_s);
1693 testDurationCaseSigned("-1s", -(std.time.ns_per_s));1964 try testDurationCaseSigned("-1s", -(std.time.ns_per_s));
1694 testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);1965 try testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);
1695 testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));1966 try testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));
1696 testDurationCaseSigned("1m", std.time.ns_per_min);1967 try testDurationCaseSigned("1m", std.time.ns_per_min);
1697 testDurationCaseSigned("-1m", -(std.time.ns_per_min));1968 try testDurationCaseSigned("-1m", -(std.time.ns_per_min));
1698 testDurationCaseSigned("1h", std.time.ns_per_hour);1969 try testDurationCaseSigned("1h", std.time.ns_per_hour);
1699 testDurationCaseSigned("-1h", -(std.time.ns_per_hour));1970 try testDurationCaseSigned("-1h", -(std.time.ns_per_hour));
1700 testDurationCaseSigned("1d", std.time.ns_per_day);1971 try testDurationCaseSigned("1d", std.time.ns_per_day);
1701 testDurationCaseSigned("-1d", -(std.time.ns_per_day));1972 try testDurationCaseSigned("-1d", -(std.time.ns_per_day));
1702 testDurationCaseSigned("1w", std.time.ns_per_week);1973 try testDurationCaseSigned("1w", std.time.ns_per_week);
1703 testDurationCaseSigned("-1w", -(std.time.ns_per_week));1974 try testDurationCaseSigned("-1w", -(std.time.ns_per_week));
1704 testDurationCaseSigned("1y", 365 * std.time.ns_per_day);1975 try testDurationCaseSigned("1y", 365 * std.time.ns_per_day);
1705 testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));1976 try testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));
1706 testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d1977 try testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
1707 testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d1978 try 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);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);
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));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));
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);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);
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));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));
1712 testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);1983 try 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));1984 try 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);1985 try 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));1986 try 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);1987 try 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));1988 try 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);1989 try 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));1990 try testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
1720 testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));1991 try testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));
1721 testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);1992 try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
1722 testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));1993 try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));
17231994
1724 testing.expectFmt("=======0ns", "{s:=>10}", .{0});1995 try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
1725 testing.expectFmt("1ns=======", "{s:=<10}", .{1});1996 try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
1726 testing.expectFmt("-1ns======", "{s:=<10}", .{-(1)});1997 try testing.expectFmt("-1ns======", "{D:=<10}", .{-(1)});
1727 testing.expectFmt(" -999ns ", "{s:^10}", .{-(std.time.ns_per_us - 1)});1998 try testing.expectFmt(" -999ns ", "{D:^10}", .{-(std.time.ns_per_us - 1)});
1728}1999}
17292000
1730fn testDurationCase(expected: []const u8, input: u64) !void {2001fn testDurationCase(expected: []const u8, input: u64) !void {
...@@ -1741,7 +2012,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void {...@@ -1741,7 +2012,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void {
1741 try testing.expectEqualStrings(expected, w.buffered());2012 try testing.expectEqualStrings(expected, w.buffered());
1742}2013}
17432014
1744test printIntOptions {2015test printInt {
1745 try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{});2016 try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{});
17462017
1747 try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{});2018 try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{});
...@@ -1757,27 +2028,22 @@ test printIntOptions {...@@ -1757,27 +2028,22 @@ test printIntOptions {
17572028
1758 try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 });2029 try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 });
1759 try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 });2030 try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 });
1760}
17612031
1762test "printInt with comptime_int" {2032 try testPrintIntCase("123456789123456789", @as(comptime_int, 123456789123456789), 10, .lower, .{});
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());
1767}2033}
17682034
1769test "printFloat with comptime_float" {2035test "printFloat with comptime_float" {
1770 var buf: [20]u8 = undefined;2036 var buf: [20]u8 = undefined;
1771 var w: Writer = .fixed(&buf);2037 var w: Writer = .fixed(&buf);
1772 try w.printFloat("", .{}, @as(comptime_float, 1.0));2038 try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
1773 try std.testing.expectEqualStrings(w.buffered(), "1e0");2039 try testing.expectEqualStrings(w.buffered(), "1e0");
1774 try std.testing.expectFmt("1e0", "{}", .{1.0});2040 try testing.expectFmt("1", "{}", .{1.0});
1775}2041}
17762042
1777fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {2043fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {
1778 var buffer: [100]u8 = undefined;2044 var buffer: [100]u8 = undefined;
1779 var w: Writer = .fixed(&buffer);2045 var w: Writer = .fixed(&buffer);
1780 w.printIntOptions(value, base, case, options);2046 try w.printInt(value, base, case, options);
1781 try testing.expectEqualStrings(expected, w.buffered());2047 try testing.expectEqualStrings(expected, w.buffered());
1782}2048}
17832049
...@@ -1798,12 +2064,12 @@ test printByteSize {...@@ -1798,12 +2064,12 @@ test printByteSize {
17982064
1799test "bytes.hex" {2065test "bytes.hex" {
1800 const some_bytes = "\xCA\xFE\xBA\xBE";2066 const some_bytes = "\xCA\xFE\xBA\xBE";
1801 try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});2067 try testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
1802 try std.testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});2068 try 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]});2069 try 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..]});2070 try testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
1805 const bytes_with_zeros = "\x00\x0E\xBA\xBE";2071 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});
1807}2073}
18082074
1809test fixed {2075test fixed {
...@@ -1832,17 +2098,22 @@ test "fixed output" {...@@ -1832,17 +2098,22 @@ test "fixed output" {
1832 try w.writeAll("world");2098 try w.writeAll("world");
1833 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));2099 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("!"));
1836 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));2102 try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld"));
18372103
1838 w.reset();2104 w = .fixed(&buffer);
2105
1839 try testing.expect(w.buffered().len == 0);2106 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!"));
1842 try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl"));2109 try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl"));
2110}
18432111
1844 try w.seekTo((try w.getEndPos()) + 1);2112test "writeSplat 0 len splat larger than capacity" {
1845 try testing.expectError(error.WriteStreamEnd, w.writeAll("H"));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);
1846}2117}
18472118
1848pub fn failingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {2119pub 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...@@ -1859,29 +2130,52 @@ pub fn failingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) File
1859 return error.WriteFailed;2130 return error.WriteFailed;
1860}2131}
18612132
1862pub fn discardingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {2133pub const Discarding = struct {
1863 const slice = data[0 .. data.len - 1];2134 count: u64,
1864 const pattern = data[slice.len..];2135 writer: Writer,
1865 var written: usize = pattern.len * splat;
1866 for (slice) |bytes| written += bytes.len;
1867 w.end = 0;
1868 return written;
1869}
18702136
1871pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {2137 pub fn init(buffer: []u8) Discarding {
1872 if (File.Handle == void) return error.Unimplemented;2138 return .{
1873 w.end = 0;2139 .count = 0,
1874 if (file_reader.getSize()) |size| {2140 .writer = .{
1875 const n = limit.minInt(size - file_reader.pos);2141 .vtable = &.{
1876 file_reader.seekBy(@intCast(n)) catch return error.Unimplemented;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;
1877 w.end = 0;2157 w.end = 0;
1878 return n;2158 return written;
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;
1883 }2159 }
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
1886/// Removes the first `n` bytes from `buffer` by shifting buffer contents,2180/// Removes the first `n` bytes from `buffer` by shifting buffer contents,
1887/// returning how many bytes are left after consuming the entire buffer, or2181/// returning how many bytes are left after consuming the entire buffer, or
...@@ -1966,28 +2260,27 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1966,28 +2260,27 @@ pub fn Hashed(comptime Hasher: type) type {
1966 return struct {2260 return struct {
1967 out: *Writer,2261 out: *Writer,
1968 hasher: Hasher,2262 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() {
1972 return .{2270 return .{
1973 .out = out,2271 .out = out,
1974 .hasher = .{},2272 .hasher = hasher,
1975 .interface = .{2273 .writer = .{
1976 .vtable = &.{@This().drain},2274 .buffer = buffer,
2275 .vtable = &.{ .drain = @This().drain },
1977 },2276 },
1978 };2277 };
1979 }2278 }
19802279
1981 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {2280 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1982 const this: *@This() = @alignCast(@fieldParentPtr("interface", w));2281 const this: *@This() = @alignCast(@fieldParentPtr("writer", w));
1983 if (data.len == 0) {2282 const aux = w.buffered();
1984 const buf = w.buffered();2283 const aux_n = try this.out.writeSplatHeader(aux, data, splat);
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);
1991 if (aux_n < w.end) {2284 if (aux_n < w.end) {
1992 this.hasher.update(w.buffer[0..aux_n]);2285 this.hasher.update(w.buffer[0..aux_n]);
1993 const remaining = w.buffer[aux_n..w.end];2286 const remaining = w.buffer[aux_n..w.end];
...@@ -1995,29 +2288,20 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1995,29 +2288,20 @@ pub fn Hashed(comptime Hasher: type) type {
1995 w.end = remaining.len;2288 w.end = remaining.len;
1996 return 0;2289 return 0;
1997 }2290 }
1998 this.hasher.update(w.buffered());2291 this.hasher.update(aux);
1999 const n = aux_n - w.end;2292 const n = aux_n - w.end;
2000 w.end = 0;2293 w.end = 0;
2001 var remaining: usize = n;2294 var remaining: usize = n;
2002 const short_data = data[0 .. data.len - @intFromBool(splat == 0)];2295 for (data[0 .. data.len - 1]) |slice| {
2003 for (short_data) |slice| {2296 if (remaining <= slice.len) {
2004 if (remaining < slice.len) {
2005 this.hasher.update(slice[0..remaining]);2297 this.hasher.update(slice[0..remaining]);
2006 return n;2298 return n;
2007 } else {
2008 remaining -= slice.len;
2009 this.hasher.update(slice);
2010 }2299 }
2300 remaining -= slice.len;
2301 this.hasher.update(slice);
2011 }2302 }
2012 const remaining_splat = switch (splat) {
2013 0, 1 => {
2014 assert(remaining == 0);
2015 return n;
2016 },
2017 else => splat - 1,
2018 };
2019 const pattern = data[data.len - 1];2303 const pattern = data[data.len - 1];
2020 assert(remaining == remaining_splat * pattern.len);2304 assert(remaining == splat * pattern.len);
2021 switch (pattern.len) {2305 switch (pattern.len) {
2022 0 => {2306 0 => {
2023 assert(remaining == 0);2307 assert(remaining == 0);
...@@ -2053,12 +2337,12 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -2053,12 +2337,12 @@ pub fn Hashed(comptime Hasher: type) type {
2053/// When using this API, it is not necessary to call `flush`.2337/// When using this API, it is not necessary to call `flush`.
2054pub const Allocating = struct {2338pub const Allocating = struct {
2055 allocator: Allocator,2339 allocator: Allocator,
2056 interface: Writer,2340 writer: Writer,
20572341
2058 pub fn init(allocator: Allocator) Allocating {2342 pub fn init(allocator: Allocator) Allocating {
2059 return .{2343 return .{
2060 .allocator = allocator,2344 .allocator = allocator,
2061 .interface = .{2345 .writer = .{
2062 .buffer = &.{},2346 .buffer = &.{},
2063 .vtable = &vtable,2347 .vtable = &vtable,
2064 },2348 },
...@@ -2068,7 +2352,7 @@ pub const Allocating = struct {...@@ -2068,7 +2352,7 @@ pub const Allocating = struct {
2068 pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating {2352 pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating {
2069 return .{2353 return .{
2070 .allocator = allocator,2354 .allocator = allocator,
2071 .interface = .{2355 .writer = .{
2072 .buffer = try allocator.alloc(u8, capacity),2356 .buffer = try allocator.alloc(u8, capacity),
2073 .vtable = &vtable,2357 .vtable = &vtable,
2074 },2358 },
...@@ -2078,7 +2362,7 @@ pub const Allocating = struct {...@@ -2078,7 +2362,7 @@ pub const Allocating = struct {
2078 pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating {2362 pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating {
2079 return .{2363 return .{
2080 .allocator = allocator,2364 .allocator = allocator,
2081 .interface = .{2365 .writer = .{
2082 .buffer = slice,2366 .buffer = slice,
2083 .vtable = &vtable,2367 .vtable = &vtable,
2084 },2368 },
...@@ -2090,7 +2374,7 @@ pub const Allocating = struct {...@@ -2090,7 +2374,7 @@ pub const Allocating = struct {
2090 defer array_list.* = .empty;2374 defer array_list.* = .empty;
2091 return .{2375 return .{
2092 .allocator = allocator,2376 .allocator = allocator,
2093 .interface = .{2377 .writer = .{
2094 .vtable = &vtable,2378 .vtable = &vtable,
2095 .buffer = array_list.allocatedSlice(),2379 .buffer = array_list.allocatedSlice(),
2096 .end = array_list.items.len,2380 .end = array_list.items.len,
...@@ -2105,14 +2389,14 @@ pub const Allocating = struct {...@@ -2105,14 +2389,14 @@ pub const Allocating = struct {
2105 };2389 };
21062390
2107 pub fn deinit(a: *Allocating) void {2391 pub fn deinit(a: *Allocating) void {
2108 a.allocator.free(a.interface.buffer);2392 a.allocator.free(a.writer.buffer);
2109 a.* = undefined;2393 a.* = undefined;
2110 }2394 }
21112395
2112 /// Returns an array list that takes ownership of the allocated memory.2396 /// Returns an array list that takes ownership of the allocated memory.
2113 /// Resets the `Allocating` to an empty state.2397 /// Resets the `Allocating` to an empty state.
2114 pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) {2398 pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) {
2115 const w = &a.interface;2399 const w = &a.writer;
2116 const result: std.ArrayListUnmanaged(u8) = .{2400 const result: std.ArrayListUnmanaged(u8) = .{
2117 .items = w.buffer[0..w.end],2401 .items = w.buffer[0..w.end],
2118 .capacity = w.buffer.len,2402 .capacity = w.buffer.len,
...@@ -2134,13 +2418,11 @@ pub const Allocating = struct {...@@ -2134,13 +2418,11 @@ pub const Allocating = struct {
2134 }2418 }
21352419
2136 pub fn getWritten(a: *Allocating) []u8 {2420 pub fn getWritten(a: *Allocating) []u8 {
2137 return a.interface.buffered();2421 return a.writer.buffered();
2138 }2422 }
21392423
2140 pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void {2424 pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void {
2141 const shrink_by = a.interface.end - new_len;2425 a.writer.end = new_len;
2142 a.interface.end = new_len;
2143 a.interface.count -= shrink_by;
2144 }2426 }
21452427
2146 pub fn clearRetainingCapacity(a: *Allocating) void {2428 pub fn clearRetainingCapacity(a: *Allocating) void {
...@@ -2148,15 +2430,18 @@ pub const Allocating = struct {...@@ -2148,15 +2430,18 @@ pub const Allocating = struct {
2148 }2430 }
21492431
2150 fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {2432 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);
2152 const gpa = a.allocator;2434 const gpa = a.allocator;
2153 const pattern = data[data.len - 1];2435 const pattern = data[data.len - 1];
2154 const splat_len = pattern.len * splat;2436 const splat_len = pattern.len * splat;
2155 var list = a.toArrayList();2437 var list = a.toArrayList();
2156 defer setArrayList(a, list);2438 defer setArrayList(a, list);
2157 const start_len = list.items.len;2439 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);
2158 for (data) |bytes| {2443 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;
2160 list.appendSliceAssumeCapacity(bytes);2445 list.appendSliceAssumeCapacity(bytes);
2161 }2446 }
2162 if (splat == 0) {2447 if (splat == 0) {
...@@ -2171,13 +2456,13 @@ pub const Allocating = struct {...@@ -2171,13 +2456,13 @@ pub const Allocating = struct {
21712456
2172 fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize {2457 fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize {
2173 if (File.Handle == void) return error.Unimplemented;2458 if (File.Handle == void) return error.Unimplemented;
2174 const a: *Allocating = @fieldParentPtr("interface", w);2459 const a: *Allocating = @fieldParentPtr("writer", w);
2175 const gpa = a.allocator;2460 const gpa = a.allocator;
2176 var list = a.toArrayList();2461 var list = a.toArrayList();
2177 defer setArrayList(a, list);2462 defer setArrayList(a, list);
2178 const pos = file_reader.pos;2463 const pos = file_reader.pos;
2179 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;2464 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;
2181 const dest = limit.slice(list.unusedCapacitySlice());2466 const dest = limit.slice(list.unusedCapacitySlice());
2182 const n = file_reader.read(dest) catch |err| switch (err) {2467 const n = file_reader.read(dest) catch |err| switch (err) {
2183 error.ReadFailed => return error.ReadFailed,2468 error.ReadFailed => return error.ReadFailed,
...@@ -2188,14 +2473,14 @@ pub const Allocating = struct {...@@ -2188,14 +2473,14 @@ pub const Allocating = struct {
2188 }2473 }
21892474
2190 fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void {2475 fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void {
2191 a.interface.buffer = list.allocatedSlice();2476 a.writer.buffer = list.allocatedSlice();
2192 a.interface.end = list.items.len;2477 a.writer.end = list.items.len;
2193 }2478 }
21942479
2195 test Allocating {2480 test Allocating {
2196 var a: Allocating = .init(std.testing.allocator);2481 var a: Allocating = .init(testing.allocator);
2197 defer a.deinit();2482 defer a.deinit();
2198 const w = &a.interface;2483 const w = &a.writer;
21992484
2200 const x: i32 = 42;2485 const x: i32 = 42;
2201 const y: i32 = 1234;2486 const y: i32 = 1234;
lib/std/io/change_detection_stream.zig+1-1
...@@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type {...@@ -8,7 +8,7 @@ pub fn ChangeDetectionStream(comptime WriterType: type) type {
8 return struct {8 return struct {
9 const Self = @This();9 const Self = @This();
10 pub const Error = WriterType.Error;10 pub const Error = WriterType.Error;
11 pub const Writer = io.Writer(*Self, Error, write);11 pub const Writer = io.GenericWriter(*Self, Error, write);
1212
13 anything_changed: bool,13 anything_changed: bool,
14 underlying_writer: WriterType,14 underlying_writer: WriterType,
lib/std/io/find_byte_writer.zig+1-1
...@@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type {...@@ -8,7 +8,7 @@ pub fn FindByteWriter(comptime UnderlyingWriter: type) type {
8 return struct {8 return struct {
9 const Self = @This();9 const Self = @This();
10 pub const Error = UnderlyingWriter.Error;10 pub const Error = UnderlyingWriter.Error;
11 pub const Writer = io.Writer(*Self, Error, write);11 pub const Writer = io.GenericWriter(*Self, Error, write);
1212
13 underlying_writer: UnderlyingWriter,13 underlying_writer: UnderlyingWriter,
14 byte_found: bool,14 byte_found: bool,
lib/std/io/test.zig+4-4
...@@ -24,7 +24,7 @@ test "write a file, read it, then delete it" {...@@ -24,7 +24,7 @@ test "write a file, read it, then delete it" {
24 var file = try tmp.dir.createFile(tmp_file_name, .{});24 var file = try tmp.dir.createFile(tmp_file_name, .{});
25 defer file.close();25 defer file.close();
2626
27 var buf_stream = io.bufferedWriter(file.writer());27 var buf_stream = io.bufferedWriter(file.deprecatedWriter());
28 const st = buf_stream.writer();28 const st = buf_stream.writer();
29 try st.print("begin", .{});29 try st.print("begin", .{});
30 try st.writeAll(data[0..]);30 try st.writeAll(data[0..]);
...@@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {...@@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {
45 const expected_file_size: u64 = "begin".len + data.len + "end".len;45 const expected_file_size: u64 = "begin".len + data.len + "end".len;
46 try expectEqual(expected_file_size, file_size);46 try expectEqual(expected_file_size, file_size);
4747
48 var buf_stream = io.bufferedReader(file.reader());48 var buf_stream = io.bufferedReader(file.deprecatedReader());
49 const st = buf_stream.reader();49 const st = buf_stream.reader();
50 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);50 const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024);
51 defer std.testing.allocator.free(contents);51 defer std.testing.allocator.free(contents);
...@@ -66,7 +66,7 @@ test "BitStreams with File Stream" {...@@ -66,7 +66,7 @@ test "BitStreams with File Stream" {
66 var file = try tmp.dir.createFile(tmp_file_name, .{});66 var file = try tmp.dir.createFile(tmp_file_name, .{});
67 defer file.close();67 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
71 try bit_stream.writeBits(@as(u2, 1), 1);71 try bit_stream.writeBits(@as(u2, 1), 1);
72 try bit_stream.writeBits(@as(u5, 2), 2);72 try bit_stream.writeBits(@as(u5, 2), 2);
...@@ -80,7 +80,7 @@ test "BitStreams with File Stream" {...@@ -80,7 +80,7 @@ test "BitStreams with File Stream" {
80 var file = try tmp.dir.openFile(tmp_file_name, .{});80 var file = try tmp.dir.openFile(tmp_file_name, .{});
81 defer file.close();81 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
85 var out_bits: u16 = undefined;85 var out_bits: u16 = undefined;
8686
lib/std/io/tty.zig+35-29
...@@ -5,36 +5,9 @@ const process = std.process;...@@ -5,36 +5,9 @@ const process = std.process;
5const windows = std.os.windows;5const windows = std.os.windows;
6const native_os = builtin.os.tag;6const native_os = builtin.os.tag;
77
8/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).8/// Deprecated in favor of `Config.detect`.
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.
12pub fn detectConfig(file: File) Config {9pub fn detectConfig(file: File) Config {
13 const force_color: ?bool = if (builtin.os.tag == .wasi)10 return .detect(file);
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;
38}11}
3912
40pub const Color = enum {13pub const Color = enum {
...@@ -66,6 +39,38 @@ pub const Config = union(enum) {...@@ -66,6 +39,38 @@ pub const Config = union(enum) {
66 escape_codes,39 escape_codes,
67 windows_api: if (native_os == .windows) WindowsContext else void,40 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
69 pub const WindowsContext = struct {74 pub const WindowsContext = struct {
70 handle: File.Handle,75 handle: File.Handle,
71 reset_attributes: u16,76 reset_attributes: u16,
...@@ -123,6 +128,7 @@ pub const Config = union(enum) {...@@ -123,6 +128,7 @@ pub const Config = union(enum) {
123 .dim => windows.FOREGROUND_INTENSITY,128 .dim => windows.FOREGROUND_INTENSITY,
124 .reset => ctx.reset_attributes,129 .reset => ctx.reset_attributes,
125 };130 };
131 try w.flush();
126 try windows.SetConsoleTextAttribute(ctx.handle, attributes);132 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
127 } else {133 } else {
128 unreachable;134 unreachable;
lib/std/json.zig+2-2
...@@ -1,12 +1,12 @@...@@ -1,12 +1,12 @@
1//! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc82591//! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259
2//!2//!
3//! The low-level `Scanner` API produces `Token`s from an input slice or successive slices of inputs,3//! 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`.
5//!5//!
6//! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type.6//! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type.
7//! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection.7//! Parse into a dynamically-typed `Value` to load any JSON value for runtime inspection.
8//!8//!
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`.
10//! The high-level `stringify` serializes a Zig or `Value` type into JSON.10//! The high-level `stringify` serializes a Zig or `Value` type into JSON.
1111
12const builtin = @import("builtin");12const builtin = @import("builtin");
lib/std/json/dynamic.zig+2-2
...@@ -51,10 +51,10 @@ pub const Value = union(enum) {...@@ -51,10 +51,10 @@ pub const Value = union(enum) {
51 }51 }
5252
53 pub fn dump(v: Value) void {53 pub fn dump(v: Value) void {
54 const bw = std.debug.lockStderrWriter(&.{});54 const w = std.debug.lockStderrWriter(&.{});
55 defer std.debug.unlockStderrWriter();55 defer std.debug.unlockStderrWriter();
5656
57 json.Stringify.value(v, .{}, bw) catch return;57 json.Stringify.value(v, .{}, w) catch return;
58 }58 }
5959
60 pub fn jsonStringify(value: @This(), jws: anytype) !void {60 pub fn jsonStringify(value: @This(), jws: anytype) !void {
lib/std/json/dynamic_test.zig+1-1
...@@ -251,7 +251,7 @@ test "Value.jsonStringify" {...@@ -251,7 +251,7 @@ test "Value.jsonStringify" {
251 \\ true,251 \\ true,
252 \\ 42,252 \\ 42,
253 \\ 43,253 \\ 43,
254 \\ 4.2e1,254 \\ 42,
255 \\ "weeee",255 \\ "weeee",
256 \\ [256 \\ [
257 \\ 1,257 \\ 1,
lib/std/json/scanner.zig+1-1
...@@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always };...@@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always };
219/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.219/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.
220pub const default_max_value_len = 4 * 1024 * 1024;220pub 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`.
223/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.223/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.
224pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {224pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {
225 return struct {225 return struct {
lib/std/log.zig+6-8
...@@ -45,9 +45,8 @@...@@ -45,9 +45,8 @@
45//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix;45//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix;
46//!46//!
47//! // Print the message to stderr, silently ignoring any errors47//! // Print the message to stderr, silently ignoring any errors
48//! std.debug.lockStdErr();48//! const stderr = std.debug.lockStderrWriter(&.{});
49//! defer std.debug.unlockStdErr();49//! defer std.debug.unlockStderrWriter();
50//! const stderr = std.fs.File.stderr().writer();
51//! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;50//! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
52//! }51//! }
53//!52//!
...@@ -101,8 +100,7 @@ pub const Level = enum {...@@ -101,8 +100,7 @@ pub const Level = enum {
101/// The default log level is based on build mode.100/// The default log level is based on build mode.
102pub const default_level: Level = switch (builtin.mode) {101pub const default_level: Level = switch (builtin.mode) {
103 .Debug => .debug,102 .Debug => .debug,
104 .ReleaseSafe => .info,103 .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info,
105 .ReleaseFast, .ReleaseSmall => .err,
106};104};
107105
108const level = std.options.log_level;106const level = std.options.log_level;
...@@ -148,10 +146,10 @@ pub fn defaultLog(...@@ -148,10 +146,10 @@ pub fn defaultLog(
148) void {146) void {
149 const level_txt = comptime message_level.asText();147 const level_txt = comptime message_level.asText();
150 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";148 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
151 var buffer: [1024]u8 = undefined;149 var buffer: [32]u8 = undefined;
152 const bw = std.debug.lockStderrWriter(&buffer);150 const stderr = std.debug.lockStderrWriter(&buffer);
153 defer std.debug.unlockStderrWriter();151 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;
155}153}
156154
157/// Returns a scoped logging namespace that logs all messages using the scope155/// 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 {...@@ -2028,6 +2028,14 @@ pub const Mutable = struct {
2028 pub fn normalize(r: *Mutable, length: usize) void {2028 pub fn normalize(r: *Mutable, length: usize) void {
2029 r.len = llnormalize(r.limbs[0..length]);2029 r.len = llnormalize(r.limbs[0..length]);
2030 }2030 }
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 }
2031};2039};
20322040
2033/// A arbitrary-precision big integer, with a fixed set of immutable limbs.2041/// A arbitrary-precision big integer, with a fixed set of immutable limbs.
...@@ -2317,46 +2325,25 @@ pub const Const = struct {...@@ -2317,46 +2325,25 @@ pub const Const = struct {
2317 return .{ normalized_res.reconstruct(if (self.positive) .positive else .negative), exactness };2325 return .{ normalized_res.reconstruct(if (self.positive) .positive else .negative), exactness };
2318 }2326 }
23192327
2320 /// To allow `std.fmt.format` to work with this type.
2321 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,2328 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
2322 /// this function will fail to print the string, printing "(BigInt)" instead of a number.2329 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
2323 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.2330 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
2324 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.2331 /// 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 {2332 pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void {
2326 comptime var base = 10;2333 const available_len = 64;
2327 comptime var case: std.fmt.Case = .lower;2334 if (self.limbs.len > available_len)
23282335 return w.writeAll("(BigInt)");
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 }
23442336
2345 const max_str_len = self.sizeInBaseUpperBound(base);2337 var limbs: [calcToStringLimbsBufferLen(available_len, 10)]Limb = undefined;
2346 const limbs_len = calcToStringLimbsBufferLen(self.limbs.len, base);2338
2347 if (bw.writableSliceGreedy(max_str_len + @alignOf(Limb) - 1 + @sizeOf(Limb) * limbs_len)) |buf| {2339 const biggest: Const = .{
2348 const limbs: [*]Limb = @alignCast(@ptrCast(std.mem.alignPointer(buf[max_str_len..].ptr, @alignOf(Limb))));2340 .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len),
2349 bw.advance(self.toString(buf[0..max_str_len], base, case, limbs[0..limbs_len]));2341 .positive = false,
2350 return;2342 };
2351 } else |_| if (bw.writableSliceGreedy(max_str_len)) |buf| {2343 var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined;
2352 const available_len = 64;2344 const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form");
2353 var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined;2345 const len = self.toString(&buf, base, number.case, &limbs);
2354 if (limbs.len >= limbs_len) {2346 return w.writeAll(buf[0..len]);
2355 bw.advance(self.toString(buf, base, case, &limbs));
2356 return;
2357 }
2358 } else |_| {}
2359 try bw.writeAll("(BigInt)");
2360 }2347 }
23612348
2362 /// Converts self to a string in the requested base.2349 /// Converts self to a string in the requested base.
...@@ -2926,17 +2913,16 @@ pub const Managed = struct {...@@ -2926,17 +2913,16 @@ pub const Managed = struct {
2926 }2913 }
29272914
2928 /// To allow `std.fmt.format` to work with `Managed`.2915 /// 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
2929 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,2920 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
2930 /// this function will fail to print the string, printing "(BigInt)" instead of a number.2921 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
2931 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.2922 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
2932 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.2923 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
2933 pub fn format(2924 pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
2934 self: Managed,2925 return self.toConst().formatNumber(w, n);
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);
2940 }2926 }
29412927
2942 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==2928 /// 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" {...@@ -3813,13 +3813,10 @@ test "(BigInt) positive" {
3813 try a.pow(&a, 64 * @sizeOf(Limb) * 8);3813 try a.pow(&a, 64 * @sizeOf(Limb) * 8);
3814 try b.sub(&a, &c);3814 try b.sub(&a, &c);
38153815
3816 const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a});3816 try testing.expectFmt("(BigInt)", "{d}", .{a});
3817 defer testing.allocator.free(a_fmt);
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});
3820 defer testing.allocator.free(b_fmt);3819 defer testing.allocator.free(b_fmt);
3821
3822 try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
3823 try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));3820 try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));
3824}3821}
38253822
...@@ -3838,10 +3835,10 @@ test "(BigInt) negative" {...@@ -3838,10 +3835,10 @@ test "(BigInt) negative" {
3838 a.negate();3835 a.negate();
3839 try b.add(&a, &c);3836 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});
3842 defer testing.allocator.free(a_fmt);3839 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});
3845 defer testing.allocator.free(b_fmt);3842 defer testing.allocator.free(b_fmt);
38463843
3847 try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));3844 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)...@@ -1714,7 +1714,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian)
1714 }1714 }
1715 },1715 },
1716 }1716 }
1717 return @as(ReturnType, @truncate(result));1717 return @truncate(result);
1718}1718}
17191719
1720test readVarInt {1720test readVarInt {
lib/std/mem/Allocator.zig+1-1
...@@ -253,7 +253,7 @@ pub inline fn allocAdvancedWithRetAddr(...@@ -253,7 +253,7 @@ pub inline fn allocAdvancedWithRetAddr(
253 n: usize,253 n: usize,
254 return_address: usize,254 return_address: usize,
255) Error![]align(if (alignment) |a| a.toByteUnits() else @alignOf(T)) T {255) 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));
257 const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address));257 const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address));
258 return ptr[0..n];258 return ptr[0..n];
259}259}
lib/std/multi_array_list.zig+1
...@@ -991,6 +991,7 @@ test "0 sized struct" {...@@ -991,6 +991,7 @@ test "0 sized struct" {
991test "struct with many fields" {991test "struct with many fields" {
992 const ManyFields = struct {992 const ManyFields = struct {
993 fn Type(count: comptime_int) type {993 fn Type(count: comptime_int) type {
994 @setEvalBranchQuota(50000);
994 var fields: [count]std.builtin.Type.StructField = undefined;995 var fields: [count]std.builtin.Type.StructField = undefined;
995 for (0..count) |i| {996 for (0..count) |i| {
996 fields[i] = .{997 fields[i] = .{
lib/std/net.zig+16-45
...@@ -164,22 +164,13 @@ pub const Address = extern union {...@@ -164,22 +164,13 @@ pub const Address = extern union {
164 }164 }
165 }165 }
166166
167 pub fn format(167 pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void {
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);
174 switch (self.any.family) {168 switch (self.any.family) {
175 posix.AF.INET => try self.in.format(fmt, options, out_stream),169 posix.AF.INET => try self.in.format(w),
176 posix.AF.INET6 => try self.in6.format(fmt, options, out_stream),170 posix.AF.INET6 => try self.in6.format(w),
177 posix.AF.UNIX => {171 posix.AF.UNIX => {
178 if (!has_unix_sockets) {172 if (!has_unix_sockets) unreachable;
179 unreachable;173 try w.writeAll(std.mem.sliceTo(&self.un.path, 0));
180 }
181
182 try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)});
183 },174 },
184 else => unreachable,175 else => unreachable,
185 }176 }
...@@ -352,22 +343,9 @@ pub const Ip4Address = extern struct {...@@ -352,22 +343,9 @@ pub const Ip4Address = extern struct {
352 self.sa.port = mem.nativeToBig(u16, port);343 self.sa.port = mem.nativeToBig(u16, port);
353 }344 }
354345
355 pub fn format(346 pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void {
356 self: Ip4Address,347 const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);
357 comptime fmt: []const u8,348 try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });
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 });
371 }349 }
372350
373 pub fn getOsSockLen(self: Ip4Address) posix.socklen_t {351 pub fn getOsSockLen(self: Ip4Address) posix.socklen_t {
...@@ -656,17 +634,10 @@ pub const Ip6Address = extern struct {...@@ -656,17 +634,10 @@ pub const Ip6Address = extern struct {
656 self.sa.port = mem.nativeToBig(u16, port);634 self.sa.port = mem.nativeToBig(u16, port);
657 }635 }
658636
659 pub fn format(637 pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void {
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;
667 const port = mem.bigToNative(u16, self.sa.port);638 const port = mem.bigToNative(u16, self.sa.port);
668 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {639 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}", .{
670 self.sa.addr[12],641 self.sa.addr[12],
671 self.sa.addr[13],642 self.sa.addr[13],
672 self.sa.addr[14],643 self.sa.addr[14],
...@@ -714,14 +685,14 @@ pub const Ip6Address = extern struct {...@@ -714,14 +685,14 @@ pub const Ip6Address = extern struct {
714 longest_len = 0;685 longest_len = 0;
715 }686 }
716687
717 try out_stream.writeAll("[");688 try w.writeAll("[");
718 var i: usize = 0;689 var i: usize = 0;
719 var abbrv = false;690 var abbrv = false;
720 while (i < native_endian_parts.len) : (i += 1) {691 while (i < native_endian_parts.len) : (i += 1) {
721 if (i == longest_start) {692 if (i == longest_start) {
722 // Emit "::" for the longest zero run693 // Emit "::" for the longest zero run
723 if (!abbrv) {694 if (!abbrv) {
724 try out_stream.writeAll(if (i == 0) "::" else ":");695 try w.writeAll(if (i == 0) "::" else ":");
725 abbrv = true;696 abbrv = true;
726 }697 }
727 i += longest_len - 1; // Skip the compressed range698 i += longest_len - 1; // Skip the compressed range
...@@ -730,12 +701,12 @@ pub const Ip6Address = extern struct {...@@ -730,12 +701,12 @@ pub const Ip6Address = extern struct {
730 if (abbrv) {701 if (abbrv) {
731 abbrv = false;702 abbrv = false;
732 }703 }
733 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});704 try w.print("{x}", .{native_endian_parts[i]});
734 if (i != native_endian_parts.len - 1) {705 if (i != native_endian_parts.len - 1) {
735 try out_stream.writeAll(":");706 try w.writeAll(":");
736 }707 }
737 }708 }
738 try std.fmt.format(out_stream, "]:{}", .{port});709 try w.print("]:{}", .{port});
739 }710 }
740711
741 pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {712 pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {
...@@ -898,7 +869,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis...@@ -898,7 +869,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis
898 const name_c = try gpa.dupeZ(u8, name);869 const name_c = try gpa.dupeZ(u8, name);
899 defer gpa.free(name_c);870 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);
902 defer gpa.free(port_c);873 defer gpa.free(port_c);
903874
904 const ws2_32 = windows.ws2_32;875 const ws2_32 = windows.ws2_32;
lib/std/net/test.zig+16-53
...@@ -5,20 +5,13 @@ const mem = std.mem;...@@ -5,20 +5,13 @@ const mem = std.mem;
5const testing = std.testing;5const testing = std.testing;
66
7test "parse and render IP addresses at comptime" {7test "parse and render IP addresses at comptime" {
8 if (builtin.os.tag == .wasi) return error.SkipZigTest;
9 comptime {8 comptime {
10 var ipAddrBuffer: [16]u8 = undefined;
11 // Parses IPv6 at comptime
12 const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable;9 const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable;
13 var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable;10 try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr});
14 try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3]));
1511
16 // Parses IPv4 at comptime
17 const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable;12 const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable;
18 var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable;13 try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr});
19 try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2]));
2014
21 // Returns error for invalid IP addresses at comptime
22 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0));15 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0));
23 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0));16 try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0));
24 try testing.expectError(error.InvalidIPAddressFormat, net.Address.resolveIp("::123.123.123.123", 0));17 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" {...@@ -27,47 +20,23 @@ test "parse and render IP addresses at comptime" {
27}20}
2821
29test "format IPv6 address with no zero runs" {22test "format IPv6 address with no zero runs" {
30 if (builtin.os.tag == .wasi) return error.SkipZigTest;
31
32 const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0);23 const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0);
3324 try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr});
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);
38}25}
3926
40test "parse IPv6 addresses and check compressed form" {27test "parse IPv6 addresses and check compressed form" {
41 if (builtin.os.tag == .wasi) return error.SkipZigTest;28 try std.testing.expectFmt("[2001:db8::1:0:0:2]:0", "{f}", .{
4229 try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0),
43 const alloc = testing.allocator;30 });
4431 try std.testing.expectFmt("[2001:db8::1:2]:0", "{f}", .{
45 // 1) Parse an IPv6 address that should compress to [2001:db8::1:0:0:2]:032 try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0),
46 const addr1 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0);33 });
4734 try std.testing.expectFmt("[2001:db8:1:0:1::2]:0", "{f}", .{
48 // 2) Parse an IPv6 address that should compress to [2001:db8::1:2]:035 try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0),
49 const addr2 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0);36 });
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);
66}37}
6738
68test "parse IPv6 address, check raw bytes" {39test "parse IPv6 address, check raw bytes" {
69 if (builtin.os.tag == .wasi) return error.SkipZigTest;
70
71 const expected_raw: [16]u8 = .{40 const expected_raw: [16]u8 = .{
72 0x20, 0x01, 0x0d, 0xb8, // 2001:db841 0x20, 0x01, 0x0d, 0xb8, // 2001:db8
73 0x00, 0x00, 0x00, 0x00, // :0000:000042 0x00, 0x00, 0x00, 0x00, // :0000:0000
...@@ -82,8 +51,6 @@ test "parse IPv6 address, check raw bytes" {...@@ -82,8 +51,6 @@ test "parse IPv6 address, check raw bytes" {
82}51}
8352
84test "parse and render IPv6 addresses" {53test "parse and render IPv6 addresses" {
85 if (builtin.os.tag == .wasi) return error.SkipZigTest;
86
87 var buffer: [100]u8 = undefined;54 var buffer: [100]u8 = undefined;
88 const ips = [_][]const u8{55 const ips = [_][]const u8{
89 "FF01:0:0:0:0:0:0:FB",56 "FF01:0:0:0:0:0:0:FB",
...@@ -111,12 +78,12 @@ test "parse and render IPv6 addresses" {...@@ -111,12 +78,12 @@ test "parse and render IPv6 addresses" {
111 };78 };
112 for (ips, 0..) |ip, i| {79 for (ips, 0..) |ip, i| {
113 const addr = net.Address.parseIp6(ip, 0) catch unreachable;80 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;
115 try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));82 try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
11683
117 if (builtin.os.tag == .linux) {84 if (builtin.os.tag == .linux) {
118 const addr_via_resolve = net.Address.resolveIp6(ip, 0) catch unreachable;85 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;
120 try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3]));87 try std.testing.expect(std.mem.eql(u8, printed[i], newResolvedIp[1 .. newResolvedIp.len - 3]));
121 }88 }
122 }89 }
...@@ -148,8 +115,6 @@ test "invalid but parseable IPv6 scope ids" {...@@ -148,8 +115,6 @@ test "invalid but parseable IPv6 scope ids" {
148}115}
149116
150test "parse and render IPv4 addresses" {117test "parse and render IPv4 addresses" {
151 if (builtin.os.tag == .wasi) return error.SkipZigTest;
152
153 var buffer: [18]u8 = undefined;118 var buffer: [18]u8 = undefined;
154 for ([_][]const u8{119 for ([_][]const u8{
155 "0.0.0.0",120 "0.0.0.0",
...@@ -159,7 +124,7 @@ test "parse and render IPv4 addresses" {...@@ -159,7 +124,7 @@ test "parse and render IPv4 addresses" {
159 "127.0.0.1",124 "127.0.0.1",
160 }) |ip| {125 }) |ip| {
161 const addr = net.Address.parseIp4(ip, 0) catch unreachable;126 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;
163 try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));128 try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
164 }129 }
165130
...@@ -175,10 +140,8 @@ test "parse and render UNIX addresses" {...@@ -175,10 +140,8 @@ test "parse and render UNIX addresses" {
175 if (builtin.os.tag == .wasi) return error.SkipZigTest;140 if (builtin.os.tag == .wasi) return error.SkipZigTest;
176 if (!net.has_unix_sockets) return error.SkipZigTest;141 if (!net.has_unix_sockets) return error.SkipZigTest;
177142
178 var buffer: [14]u8 = undefined;
179 const addr = net.Address.initUnix("/tmp/testpath") catch unreachable;143 const addr = net.Address.initUnix("/tmp/testpath") catch unreachable;
180 const fmt_addr = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;144 try std.testing.expectFmt("/tmp/testpath", "{f}", .{addr});
181 try std.testing.expectEqualSlices(u8, "/tmp/testpath", fmt_addr);
182145
183 const too_long = [_]u8{'a'} ** 200;146 const too_long = [_]u8{'a'} ** 200;
184 try testing.expectError(error.NameTooLong, net.Address.initUnix(too_long[0..]));147 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;...@@ -4,7 +4,7 @@ const off_t = std.c.off_t;
4const unexpectedErrno = std.posix.unexpectedErrno;4const unexpectedErrno = std.posix.unexpectedErrno;
5const errno = std.posix.errno;5const errno = std.posix.errno;
66
7pub const CopyFileRangeError = error{7pub const CopyFileRangeError = std.posix.UnexpectedError || error{
8 /// If infd is not open for reading or outfd is not open for writing, or8 /// If infd is not open for reading or outfd is not open for writing, or
9 /// opened for writing with O_APPEND, or if infd and outfd refer to the9 /// opened for writing with O_APPEND, or if infd and outfd refer to the
10 /// same file.10 /// same file.
lib/std/os/uefi.zig+14-23
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const assert = std.debug.assert;
23
3/// A protocol is an interface identified by a GUID.4/// A protocol is an interface identified by a GUID.
4pub const protocol = @import("uefi/protocol.zig");5pub const protocol = @import("uefi/protocol.zig");
...@@ -59,29 +60,19 @@ pub const Guid = extern struct {...@@ -59,29 +60,19 @@ pub const Guid = extern struct {
59 node: [6]u8,60 node: [6]u8,
6061
61 /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format62 /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
62 pub fn format(63 pub fn format(self: @This(), writer: *std.io.Writer) std.io.Writer.Error!void {
63 self: @This(),64 const time_low = @byteSwap(self.time_low);
64 comptime f: []const u8,65 const time_mid = @byteSwap(self.time_mid);
65 options: std.fmt.FormatOptions,66 const time_high_and_version = @byteSwap(self.time_high_and_version);
66 writer: anytype,67
67 ) !void {68 return writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
68 _ = options;69 std.mem.asBytes(&time_low),
69 if (f.len == 0) {70 std.mem.asBytes(&time_mid),
70 const time_low = @byteSwap(self.time_low);71 std.mem.asBytes(&time_high_and_version),
71 const time_mid = @byteSwap(self.time_mid);72 std.mem.asBytes(&self.clock_seq_high_and_reserved),
72 const time_high_and_version = @byteSwap(self.time_high_and_version);73 std.mem.asBytes(&self.clock_seq_low),
7374 std.mem.asBytes(&self.node),
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 });
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 }
85 }76 }
8677
87 pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool {78 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 {...@@ -2812,9 +2812,8 @@ pub fn unexpectedError(err: Win32Error) UnexpectedError {
2812 buf_wstr.len,2812 buf_wstr.len,
2813 null,2813 null,
2814 );2814 );
2815 std.debug.print("error.Unexpected: GetLastError({}): {}\n", .{2815 std.debug.print("error.Unexpected: GetLastError({d}): {f}\n", .{
2816 @intFromEnum(err),2816 err, std.unicode.fmtUtf16Le(buf_wstr[0..len]),
2817 std.unicode.fmtUtf16Le(buf_wstr[0..len]),
2818 });2817 });
2819 std.debug.dumpCurrentStackTrace(@returnAddress());2818 std.debug.dumpCurrentStackTrace(@returnAddress());
2820 }2819 }
lib/std/os/windows/test.zig+2-2
...@@ -30,7 +30,7 @@ fn testToPrefixedFileNoOracle(comptime path: []const u8, comptime expected_path:...@@ -30,7 +30,7 @@ fn testToPrefixedFileNoOracle(comptime path: []const u8, comptime expected_path:
30 const expected_path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(expected_path);30 const expected_path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(expected_path);
31 const actual_path = try windows.wToPrefixedFileW(null, path_utf16);31 const actual_path = try windows.wToPrefixedFileW(null, path_utf16);
32 std.testing.expectEqualSlices(u16, expected_path_utf16, actual_path.span()) catch |e| {32 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) });
34 return e;34 return e;
35 };35 };
36}36}
...@@ -48,7 +48,7 @@ fn testToPrefixedFileOnlyOracle(comptime path: []const u8) !void {...@@ -48,7 +48,7 @@ fn testToPrefixedFileOnlyOracle(comptime path: []const u8) !void {
48 const zig_result = try windows.wToPrefixedFileW(null, path_utf16);48 const zig_result = try windows.wToPrefixedFileW(null, path_utf16);
49 const win32_api_result = try RtlDosPathNameToNtPathName_U(path_utf16);49 const win32_api_result = try RtlDosPathNameToNtPathName_U(path_utf16);
50 std.testing.expectEqualSlices(u16, win32_api_result.span(), zig_result.span()) catch |e| {50 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()) });
52 return e;52 return e;
53 };53 };
54}54}
lib/std/posix.zig+3-2
...@@ -651,7 +651,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {...@@ -651,7 +651,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
651 }651 }
652652
653 const file: fs.File = .{ .handle = fd };653 const file: fs.File = .{ .handle = fd };
654 const stream = file.reader();654 const stream = file.deprecatedReader();
655 stream.readNoEof(buf) catch return error.Unexpected;655 stream.readNoEof(buf) catch return error.Unexpected;
656}656}
657657
...@@ -3936,6 +3936,7 @@ pub fn accept(...@@ -3936,6 +3936,7 @@ pub fn accept(
3936 .WSANOTINITIALISED => unreachable, // not initialized WSA3936 .WSANOTINITIALISED => unreachable, // not initialized WSA
3937 .WSAECONNRESET => return error.ConnectionResetByPeer,3937 .WSAECONNRESET => return error.ConnectionResetByPeer,
3938 .WSAEFAULT => unreachable,3938 .WSAEFAULT => unreachable,
3939 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
3939 .WSAEINVAL => return error.SocketNotListening,3940 .WSAEINVAL => return error.SocketNotListening,
3940 .WSAEMFILE => return error.ProcessFdQuotaExceeded,3941 .WSAEMFILE => return error.ProcessFdQuotaExceeded,
3941 .WSAENETDOWN => return error.NetworkSubsystemFailed,3942 .WSAENETDOWN => return error.NetworkSubsystemFailed,
...@@ -4335,7 +4336,7 @@ pub const GetSockOptError = error{...@@ -4335,7 +4336,7 @@ pub const GetSockOptError = error{
4335} || UnexpectedError;4336} || UnexpectedError;
43364337
4337pub fn getsockopt(fd: socket_t, level: i32, optname: u32, opt: []u8) GetSockOptError!void {4338pub 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);
4339 switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) {4340 switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) {
4340 .SUCCESS => {4341 .SUCCESS => {
4341 std.debug.assert(len == opt.len);4342 std.debug.assert(len == opt.len);
lib/std/posix/test.zig+1-1
...@@ -667,7 +667,7 @@ test "mmap" {...@@ -667,7 +667,7 @@ test "mmap" {
667 const file = try tmp.dir.createFile(test_out_file, .{});667 const file = try tmp.dir.createFile(test_out_file, .{});
668 defer file.close();668 defer file.close();
669669
670 const stream = file.writer();670 const stream = file.deprecatedWriter();
671671
672 var i: u32 = 0;672 var i: u32 = 0;
673 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {673 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 {...@@ -1553,7 +1553,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
1553 const file = try std.fs.openFileAbsolute("/etc/passwd", .{});1553 const file = try std.fs.openFileAbsolute("/etc/passwd", .{});
1554 defer file.close();1554 defer file.close();
15551555
1556 const reader = file.reader();1556 const reader = file.deprecatedReader();
15571557
1558 const State = enum {1558 const State = enum {
1559 Start,1559 Start,
lib/std/start.zig+2-2
...@@ -486,7 +486,7 @@ fn _start() callconv(.naked) noreturn {...@@ -486,7 +486,7 @@ fn _start() callconv(.naked) noreturn {
486486
487fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {487fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
488 // Switch from the x87 fpu state set by windows to the state expected by the gnu abi.488 // 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
491 if (!builtin.single_threaded and !builtin.link_libc) {491 if (!builtin.single_threaded and !builtin.link_libc) {
492 _ = @import("os/windows/tls.zig");492 _ = @import("os/windows/tls.zig");
...@@ -499,7 +499,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {...@@ -499,7 +499,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
499499
500fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {500fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {
501 // Switch from the x87 fpu state set by windows to the state expected by the gnu abi.501 // 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
504 if (!builtin.single_threaded and !builtin.link_libc) {504 if (!builtin.single_threaded and !builtin.link_libc) {
505 _ = @import("os/windows/tls.zig");505 _ = @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 {...@@ -103,7 +103,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
103 .error_set,103 .error_set,
104 => {104 => {
105 if (actual != expected) {105 if (actual != expected) {
106 print("expected {}, found {}\n", .{ expected, actual });106 print("expected {any}, found {any}\n", .{ expected, actual });
107 return error.TestExpectedEqual;107 return error.TestExpectedEqual;
108 }108 }
109 },109 },
...@@ -265,9 +265,13 @@ test "expectEqual null" {...@@ -265,9 +265,13 @@ test "expectEqual null" {
265265
266/// This function is intended to be used only in tests. When the formatted result of the template266/// This function is intended to be used only in tests. When the formatted result of the template
267/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how267/// 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 printing268/// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing
269/// diagnostics.269/// diagnostics.
270pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {270pub 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 }
271 const actual = try std.fmt.allocPrint(allocator, template, args);275 const actual = try std.fmt.allocPrint(allocator, template, args);
272 defer allocator.free(actual);276 defer allocator.free(actual);
273 return expectEqualStrings(expected, actual);277 return expectEqualStrings(expected, actual);
...@@ -354,9 +358,6 @@ test expectApproxEqRel {...@@ -354,9 +358,6 @@ test expectApproxEqRel {
354/// The colorized output is optional and controlled by the return of `std.io.tty.detectConfig()`.358/// The colorized output is optional and controlled by the return of `std.io.tty.detectConfig()`.
355/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.359/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
356pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {360pub 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 }
360 const diff_index: usize = diff_index: {361 const diff_index: usize = diff_index: {
361 const shortest = @min(expected.len, actual.len);362 const shortest = @min(expected.len, actual.len);
362 var index: usize = 0;363 var index: usize = 0;
...@@ -365,12 +366,21 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -365,12 +366,21 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
365 }366 }
366 break :diff_index if (expected.len == actual.len) return else shortest;367 break :diff_index if (expected.len == actual.len) return else shortest;
367 };368 };
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) {376fn failEqualSlices(
370 return error.TestExpectedEqual;377 comptime T: type,
371 }378 expected: []const T,
372379 actual: []const T,
373 print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });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
375 // TODO: Should this be configurable by the caller?385 // TODO: Should this be configurable by the caller?
376 const max_lines: usize = 16;386 const max_lines: usize = 16;
...@@ -388,8 +398,6 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -388,8 +398,6 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
388 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];398 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
389 const actual_truncated = window_start + actual_window.len < actual.len;399 const actual_truncated = window_start + actual_window.len < actual.len;
390400
391 const bw = std.debug.lockStderrWriter(&.{});
392 defer std.debug.unlockStderrWriter();
393 const ttyconf = std.io.tty.detectConfig(.stderr());401 const ttyconf = std.io.tty.detectConfig(.stderr());
394 var differ = if (T == u8) BytesDiffer{402 var differ = if (T == u8) BytesDiffer{
395 .expected = expected_window,403 .expected = expected_window,
...@@ -406,47 +414,47 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -406,47 +414,47 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
406 // that is usually useful.414 // that is usually useful.
407 const index_fmt = if (T == u8) "0x{X}" else "{}";415 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 });
410 if (window_start > 0) {418 if (window_start > 0) {
411 if (T == u8) {419 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});
413 } else {421 } else {
414 print("... truncated ...\n", .{});422 try w.print("... truncated ...\n", .{});
415 }423 }
416 }424 }
417 differ.write(bw) catch {};425 differ.write(w) catch {};
418 if (expected_truncated) {426 if (expected_truncated) {
419 const end_offset = window_start + expected_window.len;427 const end_offset = window_start + expected_window.len;
420 const num_missing_items = expected.len - (window_start + expected_window.len);428 const num_missing_items = expected.len - (window_start + expected_window.len);
421 if (T == u8) {429 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 });
423 } else {431 } 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});
425 }433 }
426 }434 }
427435
428 // now reverse expected/actual and print again436 // now reverse expected/actual and print again
429 differ.expected = actual_window;437 differ.expected = actual_window;
430 differ.actual = expected_window;438 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 });
432 if (window_start > 0) {440 if (window_start > 0) {
433 if (T == u8) {441 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});
435 } else {443 } else {
436 print("... truncated ...\n", .{});444 try w.print("... truncated ...\n", .{});
437 }445 }
438 }446 }
439 differ.write(bw) catch {};447 differ.write(w) catch {};
440 if (actual_truncated) {448 if (actual_truncated) {
441 const end_offset = window_start + actual_window.len;449 const end_offset = window_start + actual_window.len;
442 const num_missing_items = actual.len - (window_start + actual_window.len);450 const num_missing_items = actual.len - (window_start + actual_window.len);
443 if (T == u8) {451 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 });
445 } else {453 } 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});
447 }455 }
448 }456 }
449 print("\n================================================\n\n", .{});457 try w.print("\n================================================\n\n", .{});
450458
451 return error.TestExpectedEqual;459 return error.TestExpectedEqual;
452}460}
...@@ -460,17 +468,17 @@ fn SliceDiffer(comptime T: type) type {...@@ -460,17 +468,17 @@ fn SliceDiffer(comptime T: type) type {
460468
461 const Self = @This();469 const Self = @This();
462470
463 pub fn write(self: Self, bw: *Writer) !void {471 pub fn write(self: Self, writer: *std.io.Writer) !void {
464 for (self.expected, 0..) |value, i| {472 for (self.expected, 0..) |value, i| {
465 const full_index = self.start_index + i;473 const full_index = self.start_index + i;
466 const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true;474 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);
468 if (@typeInfo(T) == .pointer) {476 if (@typeInfo(T) == .pointer) {
469 try bw.print("[{}]{*}: {any}\n", .{ full_index, value, value });477 try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value });
470 } else {478 } else {
471 try bw.print("[{}]: {any}\n", .{ full_index, value });479 try writer.print("[{}]: {any}\n", .{ full_index, value });
472 }480 }
473 if (diff) try self.ttyconf.setColor(bw, .reset);481 if (diff) try self.ttyconf.setColor(writer, .reset);
474 }482 }
475 }483 }
476 };484 };
...@@ -481,7 +489,7 @@ const BytesDiffer = struct {...@@ -481,7 +489,7 @@ const BytesDiffer = struct {
481 actual: []const u8,489 actual: []const u8,
482 ttyconf: std.io.tty.Config,490 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 {
485 var expected_iterator = std.mem.window(u8, self.expected, 16, 16);493 var expected_iterator = std.mem.window(u8, self.expected, 16, 16);
486 var row: usize = 0;494 var row: usize = 0;
487 while (expected_iterator.next()) |chunk| {495 while (expected_iterator.next()) |chunk| {
...@@ -491,23 +499,23 @@ const BytesDiffer = struct {...@@ -491,23 +499,23 @@ const BytesDiffer = struct {
491 const absolute_byte_index = col + row * 16;499 const absolute_byte_index = col + row * 16;
492 const diff = if (absolute_byte_index < self.actual.len) self.actual[absolute_byte_index] != byte else true;500 const diff = if (absolute_byte_index < self.actual.len) self.actual[absolute_byte_index] != byte else true;
493 if (diff) diffs.set(col);501 if (diff) diffs.set(col);
494 try self.writeDiff(bw, "{X:0>2} ", .{byte}, diff);502 try self.writeDiff(writer, "{X:0>2} ", .{byte}, diff);
495 if (col == 7) try bw.writeByte(' ');503 if (col == 7) try writer.writeByte(' ');
496 }504 }
497 try bw.writeByte(' ');505 try writer.writeByte(' ');
498 if (chunk.len < 16) {506 if (chunk.len < 16) {
499 var missing_columns = (16 - chunk.len) * 3;507 var missing_columns = (16 - chunk.len) * 3;
500 if (chunk.len < 8) missing_columns += 1;508 if (chunk.len < 8) missing_columns += 1;
501 try bw.splatByteAll(' ', missing_columns);509 try writer.splatByteAll(' ', missing_columns);
502 }510 }
503 for (chunk, 0..) |byte, col| {511 for (chunk, 0..) |byte, col| {
504 const diff = diffs.isSet(col);512 const diff = diffs.isSet(col);
505 if (std.ascii.isPrint(byte)) {513 if (std.ascii.isPrint(byte)) {
506 try self.writeDiff(bw, "{c}", .{byte}, diff);514 try self.writeDiff(writer, "{c}", .{byte}, diff);
507 } else {515 } else {
508 // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed516 // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed
509 if (self.ttyconf == .windows_api) {517 if (self.ttyconf == .windows_api) {
510 try self.writeDiff(bw, ".", .{}, diff);518 try self.writeDiff(writer, ".", .{}, diff);
511 continue;519 continue;
512 }520 }
513521
...@@ -515,22 +523,22 @@ const BytesDiffer = struct {...@@ -515,22 +523,22 @@ const BytesDiffer = struct {
515 // We don't want to do this for all control codes because most control codes apart from523 // We don't want to do this for all control codes because most control codes apart from
516 // the ones that Zig has escape sequences for are likely not very useful to print as symbols.524 // the ones that Zig has escape sequences for are likely not very useful to print as symbols.
517 switch (byte) {525 switch (byte) {
518 '\n' => try self.writeDiff(bw, "␊", .{}, diff),526 '\n' => try self.writeDiff(writer, "␊", .{}, diff),
519 '\r' => try self.writeDiff(bw, "␍", .{}, diff),527 '\r' => try self.writeDiff(writer, "␍", .{}, diff),
520 '\t' => try self.writeDiff(bw, "␉", .{}, diff),528 '\t' => try self.writeDiff(writer, "␉", .{}, diff),
521 else => try self.writeDiff(bw, ".", .{}, diff),529 else => try self.writeDiff(writer, ".", .{}, diff),
522 }530 }
523 }531 }
524 }532 }
525 try bw.writeByte('\n');533 try writer.writeByte('\n');
526 row += 1;534 row += 1;
527 }535 }
528 }536 }
529537
530 fn writeDiff(self: BytesDiffer, bw: *Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void {538 fn writeDiff(self: BytesDiffer, writer: *std.io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void {
531 if (diff) try self.ttyconf.setColor(bw, .red);539 if (diff) try self.ttyconf.setColor(writer, .red);
532 try bw.print(fmt, args);540 try writer.print(fmt, args);
533 if (diff) try self.ttyconf.setColor(bw, .reset);541 if (diff) try self.ttyconf.setColor(writer, .reset);
534 }542 }
535};543};
536544
...@@ -641,6 +649,11 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir {...@@ -641,6 +649,11 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir {
641649
642pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {650pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
643 if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {651 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 }
644 print("\n====== expected this output: =========\n", .{});657 print("\n====== expected this output: =========\n", .{});
645 printWithVisibleNewlines(expected);658 printWithVisibleNewlines(expected);
646 print("\n======== instead found this: =========\n", .{});659 print("\n======== instead found this: =========\n", .{});
...@@ -1112,7 +1125,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime...@@ -1112,7 +1125,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
1112 const arg_i_str = comptime str: {1125 const arg_i_str = comptime str: {
1113 var str_buf: [100]u8 = undefined;1126 var str_buf: [100]u8 = undefined;
1114 const args_i = i + 1;1127 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, .{});
1116 break :str str_buf[0..str_len];1129 break :str str_buf[0..str_len];
1117 };1130 };
1118 @field(args, arg_i_str) = @field(extra_args, field.name);1131 @field(args, arg_i_str) = @field(extra_args, field.name);
...@@ -1142,7 +1155,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime...@@ -1142,7 +1155,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
1142 error.OutOfMemory => {1155 error.OutOfMemory => {
1143 if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {1156 if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {
1144 print(1157 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}",
1146 .{1159 .{
1147 fail_index,1160 fail_index,
1148 needed_alloc_count,1161 needed_alloc_count,
...@@ -1196,3 +1209,43 @@ pub inline fn fuzz(...@@ -1196,3 +1209,43 @@ pub inline fn fuzz(
1196) anyerror!void {1209) anyerror!void {
1197 return @import("root").fuzz(context, testOne, options);1210 return @import("root").fuzz(context, testOne, options);
1198}1211}
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();...@@ -9,6 +9,7 @@ const native_endian = builtin.cpu.arch.endian();
9///9///
10/// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character10/// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character
11pub const replacement_character: u21 = 0xFFFD;11pub const replacement_character: u21 = 0xFFFD;
12pub const replacement_character_utf8: [3]u8 = utf8EncodeComptime(replacement_character);
1213
13/// Returns how many bytes the UTF-8 representation would require14/// Returns how many bytes the UTF-8 representation would require
14/// for the given codepoint.15/// for the given codepoint.
...@@ -802,14 +803,7 @@ fn testDecode(bytes: []const u8) !u21 {...@@ -802,14 +803,7 @@ fn testDecode(bytes: []const u8) !u21 {
802/// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD)803/// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD)
803/// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of804/// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of
804/// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder805/// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder
805fn formatUtf8(806fn formatUtf8(utf8: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
806 utf8: []const u8,
807 comptime fmt: []const u8,
808 options: std.fmt.FormatOptions,
809 writer: anytype,
810) !void {
811 _ = fmt;
812 _ = options;
813 var buf: [300]u8 = undefined; // just an arbitrary size807 var buf: [300]u8 = undefined; // just an arbitrary size
814 var u8len: usize = 0;808 var u8len: usize = 0;
815809
...@@ -898,27 +892,27 @@ fn formatUtf8(...@@ -898,27 +892,27 @@ fn formatUtf8(
898/// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD)892/// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD)
899/// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of893/// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of
900/// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder894/// 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) {
902 return .{ .data = utf8 };896 return .{ .data = utf8 };
903}897}
904898
905test fmtUtf8 {899test fmtUtf8 {
906 const expectFmt = testing.expectFmt;900 const expectFmt = testing.expectFmt;
907 try expectFmt("", "{}", .{fmtUtf8("")});901 try expectFmt("", "{f}", .{fmtUtf8("")});
908 try expectFmt("foo", "{}", .{fmtUtf8("foo")});902 try expectFmt("foo", "{f}", .{fmtUtf8("foo")});
909 try expectFmt("𐐷", "{}", .{fmtUtf8("𐐷")});903 try expectFmt("𐐷", "{f}", .{fmtUtf8("𐐷")});
910904
911 // Table 3-8. U+FFFD for Non-Shortest Form Sequences905 // 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
914 // Table 3-9. U+FFFD for Ill-Formed Sequences for Surrogates908 // 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
917 // Table 3-10. U+FFFD for Other Ill-Formed Sequences911 // 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
920 // Table 3-11. U+FFFD for Truncated Sequences914 // 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")});
922}916}
923917
924fn utf16LeToUtf8ArrayListImpl(918fn utf16LeToUtf8ArrayListImpl(
...@@ -1477,14 +1471,7 @@ test calcWtf16LeLen {...@@ -1477,14 +1471,7 @@ test calcWtf16LeLen {
14771471
1478/// Print the given `utf16le` string, encoded as UTF-8 bytes.1472/// Print the given `utf16le` string, encoded as UTF-8 bytes.
1479/// Unpaired surrogates are replaced by the replacement character (U+FFFD).1473/// Unpaired surrogates are replaced by the replacement character (U+FFFD).
1480fn formatUtf16Le(1474fn formatUtf16Le(utf16le: []const u16, writer: *std.io.Writer) std.io.Writer.Error!void {
1481 utf16le: []const u16,
1482 comptime fmt: []const u8,
1483 options: std.fmt.FormatOptions,
1484 writer: anytype,
1485) !void {
1486 _ = fmt;
1487 _ = options;
1488 var buf: [300]u8 = undefined; // just an arbitrary size1475 var buf: [300]u8 = undefined; // just an arbitrary size
1489 var it = Utf16LeIterator.init(utf16le);1476 var it = Utf16LeIterator.init(utf16le);
1490 var u8len: usize = 0;1477 var u8len: usize = 0;
...@@ -1505,23 +1492,23 @@ pub const fmtUtf16le = @compileError("deprecated; renamed to fmtUtf16Le");...@@ -1505,23 +1492,23 @@ pub const fmtUtf16le = @compileError("deprecated; renamed to fmtUtf16Le");
1505/// Return a Formatter for a (potentially ill-formed) UTF-16 LE string,1492/// Return a Formatter for a (potentially ill-formed) UTF-16 LE string,
1506/// which will be converted to UTF-8 during formatting.1493/// which will be converted to UTF-8 during formatting.
1507/// Unpaired surrogates are replaced by the replacement character (U+FFFD).1494/// 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) {
1509 return .{ .data = utf16le };1496 return .{ .data = utf16le };
1510}1497}
15111498
1512test fmtUtf16Le {1499test fmtUtf16Le {
1513 const expectFmt = testing.expectFmt;1500 const expectFmt = testing.expectFmt;
1514 try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});1501 try expectFmt("", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))});
1515 try expectFmt("", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))});1502 try expectFmt("", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))});
1516 try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});1503 try expectFmt("foo", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))});
1517 try expectFmt("foo", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))});1504 try expectFmt("foo", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))});
1518 try expectFmt("𐐷", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))});1505 try expectFmt("𐐷", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))});
1519 try expectFmt("퟿", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})});1506 try expectFmt("퟿", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})});
1520 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})});1507 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})});
1521 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})});1508 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})});
1522 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})});1509 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})});
1523 try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})});1510 try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})});
1524 try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})});1511 try expectFmt("", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})});
1525}1512}
15261513
1527fn testUtf8ToUtf16LeStringLiteral(utf8ToUtf16LeStringLiteral_: anytype) !void {1514fn testUtf8ToUtf16LeStringLiteral(utf8ToUtf16LeStringLiteral_: anytype) !void {
lib/std/zig.zig+108-110
...@@ -54,7 +54,7 @@ pub const Color = enum {...@@ -54,7 +54,7 @@ pub const Color = enum {
5454
55 pub fn get_tty_conf(color: Color) std.io.tty.Config {55 pub fn get_tty_conf(color: Color) std.io.tty.Config {
56 return switch (color) {56 return switch (color) {
57 .auto => std.io.tty.detectConfig(.stderr()),57 .auto => std.io.tty.detectConfig(std.fs.File.stderr()),
58 .on => .escape_codes,58 .on => .escape_codes,
59 .off => .no_color,59 .off => .no_color,
60 };60 };
...@@ -364,138 +364,136 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![...@@ -364,138 +364,136 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![
364364
365/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.365/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
366///366///
367/// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives367/// See also `fmtIdFlags`.
368/// and the reserved `_` identifier.368pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) {
369/// - Add `p` to the specifier to render identifiers that shadow primitives unescaped.369 return .{ .data = .{ .bytes = bytes, .flags = .{} } };
370/// - Add `_` to the specifier to render the reserved `_` identifier unescaped.370}
371/// - `p` and `_` can be combined, e.g. `{p_}`.371
372/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
372///373///
373pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {374/// See also `fmtId`.
374 return .{ .data = bytes };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 } } };
375}385}
376386
377test fmtId {387test fmtId {
378 const expectFmt = std.testing.expectFmt;388 const expectFmt = std.testing.expectFmt;
379 try expectFmt("@\"while\"", "{}", .{fmtId("while")});389 try expectFmt("@\"while\"", "{f}", .{fmtId("while")});
380 try expectFmt("@\"while\"", "{p}", .{fmtId("while")});390 try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true })});
381 try expectFmt("@\"while\"", "{_}", .{fmtId("while")});391 try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_underscore = true })});
382 try expectFmt("@\"while\"", "{p_}", .{fmtId("while")});392 try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true, .allow_underscore = true })});
383 try expectFmt("@\"while\"", "{_p}", .{fmtId("while")});393
384394 try expectFmt("hello", "{f}", .{fmtId("hello")});
385 try expectFmt("hello", "{}", .{fmtId("hello")});395 try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true })});
386 try expectFmt("hello", "{p}", .{fmtId("hello")});396 try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_underscore = true })});
387 try expectFmt("hello", "{_}", .{fmtId("hello")});397 try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true, .allow_underscore = true })});
388 try expectFmt("hello", "{p_}", .{fmtId("hello")});398
389 try expectFmt("hello", "{_p}", .{fmtId("hello")});399 try expectFmt("@\"type\"", "{f}", .{fmtId("type")});
390400 try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true })});
391 try expectFmt("@\"type\"", "{}", .{fmtId("type")});401 try expectFmt("@\"type\"", "{f}", .{fmtIdFlags("type", .{ .allow_underscore = true })});
392 try expectFmt("type", "{p}", .{fmtId("type")});402 try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true, .allow_underscore = true })});
393 try expectFmt("@\"type\"", "{_}", .{fmtId("type")});403
394 try expectFmt("type", "{p_}", .{fmtId("type")});404 try expectFmt("@\"_\"", "{f}", .{fmtId("_")});
395 try expectFmt("type", "{_p}", .{fmtId("type")});405 try expectFmt("@\"_\"", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true })});
396406 try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_underscore = true })});
397 try expectFmt("@\"_\"", "{}", .{fmtId("_")});407 try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true, .allow_underscore = true })});
398 try expectFmt("@\"_\"", "{p}", .{fmtId("_")});408
399 try expectFmt("_", "{_}", .{fmtId("_")});409 try expectFmt("@\"i123\"", "{f}", .{fmtId("i123")});
400 try expectFmt("_", "{p_}", .{fmtId("_")});410 try expectFmt("i123", "{f}", .{fmtIdFlags("i123", .{ .allow_primitive = true })});
401 try expectFmt("_", "{_p}", .{fmtId("_")});411 try expectFmt("@\"4four\"", "{f}", .{fmtId("4four")});
402412 try expectFmt("_underscore", "{f}", .{fmtId("_underscore")});
403 try expectFmt("@\"i123\"", "{}", .{fmtId("i123")});413 try expectFmt("@\"11\\\"23\"", "{f}", .{fmtId("11\"23")});
404 try expectFmt("i123", "{p}", .{fmtId("i123")});414 try expectFmt("@\"11\\x0f23\"", "{f}", .{fmtId("11\x0F23")});
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")});
409415
410 // These are technically not currently legal in Zig.416 // These are technically not currently legal in Zig.
411 try expectFmt("@\"\"", "{}", .{fmtId("")});417 try expectFmt("@\"\"", "{f}", .{fmtId("")});
412 try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")});418 try expectFmt("@\"\\x00\"", "{f}", .{fmtId("\x00")});
413}419}
414420
415/// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.421pub const FormatId = struct {
416fn formatId(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void {422 bytes: []const u8,
417 const allow_primitive, const allow_underscore = comptime parse_fmt: {423 flags: Flags,
418 var allow_primitive = false;424 pub const Flags = struct {
419 var allow_underscore = false;425 allow_primitive: bool = false,
420 for (fmt) |char| {426 allow_underscore: bool = false,
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 };
435 };427 };
436428
437 if (isValidId(bytes) and429 /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
438 (allow_primitive or !std.zig.isPrimitive(bytes)) and430 fn render(ctx: FormatId, writer: *std.io.Writer) std.io.Writer.Error!void {
439 (allow_underscore or !isUnderscore(bytes)))431 const bytes = ctx.bytes;
440 {432 if (isValidId(bytes) and
441 return bw.writeAll(bytes);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('"');
442 }441 }
443 try bw.writeAll("@\"");442};
444 try stringEscape(bytes, bw, "");443
445 try bw.writeByte('"');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 };
446}447}
447448
448/// Return a Formatter for Zig Escapes of a double quoted string.449/// Return a formatter for escaping a single quoted Zig string.
449/// The format specifier must be one of:450pub fn fmtChar(bytes: []const u8) std.fmt.Formatter([]const u8, charEscape) {
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) {
453 return .{ .data = bytes };451 return .{ .data = bytes };
454}452}
455453
456test fmtEscapes {454test fmtString {
457 const expectFmt = std.testing.expectFmt;455 try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")});
458 try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});456 try std.testing.expectFmt(
459 try expectFmt(
460 \\" \\ hi \x07 \x11 " derp \'"
461 , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
462 try expectFmt(
463 \\" \\ hi \x07 \x11 \" derp '"457 \\" \\ hi \x07 \x11 \" derp '"
464 , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});458 , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")});
465}459}
466460
467/// Print the string as escaped contents of a double quoted or single-quoted string.461test fmtChar {
468/// Format `{}` treats contents as a double-quoted string.462 try std.testing.expectFmt(
469/// Format `{'}` treats contents as a single-quoted string.463 \\" \\ hi \x07 \x11 " derp \'"
470pub fn stringEscape(bytes: []const u8, bw: *Writer, comptime f: []const u8) !void {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 {
471 for (bytes) |byte| switch (byte) {469 for (bytes) |byte| switch (byte) {
472 '\n' => try bw.writeAll("\\n"),470 '\n' => try w.writeAll("\\n"),
473 '\r' => try bw.writeAll("\\r"),471 '\r' => try w.writeAll("\\r"),
474 '\t' => try bw.writeAll("\\t"),472 '\t' => try w.writeAll("\\t"),
475 '\\' => try bw.writeAll("\\\\"),473 '\\' => try w.writeAll("\\\\"),
476 '"' => {474 '"' => try w.writeAll("\\\""),
477 if (f.len == 1 and f[0] == '\'') {475 '\'' => try w.writeByte('\''),
478 try bw.writeByte('"');476 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte),
479 } else if (f.len == 0) {477 else => {
480 try bw.writeAll("\\\"");478 try w.writeAll("\\x");
481 } else {479 try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
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 }
493 },480 },
494 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte),481 };
495 // Use hex escapes for rest any unprintable characters.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),
496 else => {494 else => {
497 try bw.writeAll("\\x");495 try w.writeAll("\\x");
498 try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' });496 try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
499 },497 },
500 };498 };
501}499}
lib/std/zig/Ast.zig+75-146
...@@ -320,261 +320,261 @@ pub fn rootDecls(tree: Ast) []const Node.Index {...@@ -320,261 +320,261 @@ pub fn rootDecls(tree: Ast) []const Node.Index {
320 }320 }
321}321}
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 {
324 switch (parse_error.tag) {324 switch (parse_error.tag) {
325 .asterisk_after_ptr_deref => {325 .asterisk_after_ptr_deref => {
326 // Note that the token will point at the `.*` but ideally the source326 // Note that the token will point at the `.*` but ideally the source
327 // location would point to the `*` after the `.*`.327 // 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?");
329 },329 },
330 .chained_comparison_operators => {330 .chained_comparison_operators => {
331 return bw.writeAll("comparison operators cannot be chained");331 return w.writeAll("comparison operators cannot be chained");
332 },332 },
333 .decl_between_fields => {333 .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");
335 },335 },
336 .expected_block => {336 .expected_block => {
337 return bw.print("expected block, found '{s}'", .{337 return w.print("expected block, found '{s}'", .{
338 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),338 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
339 });339 });
340 },340 },
341 .expected_block_or_assignment => {341 .expected_block_or_assignment => {
342 return bw.print("expected block or assignment, found '{s}'", .{342 return w.print("expected block or assignment, found '{s}'", .{
343 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),343 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
344 });344 });
345 },345 },
346 .expected_block_or_expr => {346 .expected_block_or_expr => {
347 return bw.print("expected block or expression, found '{s}'", .{347 return w.print("expected block or expression, found '{s}'", .{
348 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),348 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
349 });349 });
350 },350 },
351 .expected_block_or_field => {351 .expected_block_or_field => {
352 return bw.print("expected block or field, found '{s}'", .{352 return w.print("expected block or field, found '{s}'", .{
353 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),353 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
354 });354 });
355 },355 },
356 .expected_container_members => {356 .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}'", .{
358 tree.tokenTag(parse_error.token).symbol(),358 tree.tokenTag(parse_error.token).symbol(),
359 });359 });
360 },360 },
361 .expected_expr => {361 .expected_expr => {
362 return bw.print("expected expression, found '{s}'", .{362 return w.print("expected expression, found '{s}'", .{
363 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),363 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
364 });364 });
365 },365 },
366 .expected_expr_or_assignment => {366 .expected_expr_or_assignment => {
367 return bw.print("expected expression or assignment, found '{s}'", .{367 return w.print("expected expression or assignment, found '{s}'", .{
368 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),368 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
369 });369 });
370 },370 },
371 .expected_expr_or_var_decl => {371 .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}'", .{
373 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),373 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
374 });374 });
375 },375 },
376 .expected_fn => {376 .expected_fn => {
377 return bw.print("expected function, found '{s}'", .{377 return w.print("expected function, found '{s}'", .{
378 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),378 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
379 });379 });
380 },380 },
381 .expected_inlinable => {381 .expected_inlinable => {
382 return bw.print("expected 'while' or 'for', found '{s}'", .{382 return w.print("expected 'while' or 'for', found '{s}'", .{
383 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),383 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
384 });384 });
385 },385 },
386 .expected_labelable => {386 .expected_labelable => {
387 return bw.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{387 return w.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{
388 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),388 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
389 });389 });
390 },390 },
391 .expected_param_list => {391 .expected_param_list => {
392 return bw.print("expected parameter list, found '{s}'", .{392 return w.print("expected parameter list, found '{s}'", .{
393 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),393 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
394 });394 });
395 },395 },
396 .expected_prefix_expr => {396 .expected_prefix_expr => {
397 return bw.print("expected prefix expression, found '{s}'", .{397 return w.print("expected prefix expression, found '{s}'", .{
398 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),398 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
399 });399 });
400 },400 },
401 .expected_primary_type_expr => {401 .expected_primary_type_expr => {
402 return bw.print("expected primary type expression, found '{s}'", .{402 return w.print("expected primary type expression, found '{s}'", .{
403 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),403 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
404 });404 });
405 },405 },
406 .expected_pub_item => {406 .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");
408 },408 },
409 .expected_return_type => {409 .expected_return_type => {
410 return bw.print("expected return type expression, found '{s}'", .{410 return w.print("expected return type expression, found '{s}'", .{
411 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),411 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
412 });412 });
413 },413 },
414 .expected_semi_or_else => {414 .expected_semi_or_else => {
415 return bw.writeAll("expected ';' or 'else' after statement");415 return w.writeAll("expected ';' or 'else' after statement");
416 },416 },
417 .expected_semi_or_lbrace => {417 .expected_semi_or_lbrace => {
418 return bw.writeAll("expected ';' or block after function prototype");418 return w.writeAll("expected ';' or block after function prototype");
419 },419 },
420 .expected_statement => {420 .expected_statement => {
421 return bw.print("expected statement, found '{s}'", .{421 return w.print("expected statement, found '{s}'", .{
422 tree.tokenTag(parse_error.token).symbol(),422 tree.tokenTag(parse_error.token).symbol(),
423 });423 });
424 },424 },
425 .expected_suffix_op => {425 .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}'", .{
427 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),427 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
428 });428 });
429 },429 },
430 .expected_type_expr => {430 .expected_type_expr => {
431 return bw.print("expected type expression, found '{s}'", .{431 return w.print("expected type expression, found '{s}'", .{
432 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),432 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
433 });433 });
434 },434 },
435 .expected_var_decl => {435 .expected_var_decl => {
436 return bw.print("expected variable declaration, found '{s}'", .{436 return w.print("expected variable declaration, found '{s}'", .{
437 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),437 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
438 });438 });
439 },439 },
440 .expected_var_decl_or_fn => {440 .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}'", .{
442 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),442 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
443 });443 });
444 },444 },
445 .expected_loop_payload => {445 .expected_loop_payload => {
446 return bw.print("expected loop payload, found '{s}'", .{446 return w.print("expected loop payload, found '{s}'", .{
447 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),447 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
448 });448 });
449 },449 },
450 .expected_container => {450 .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}'", .{
452 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),452 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
453 });453 });
454 },454 },
455 .extern_fn_body => {455 .extern_fn_body => {
456 return bw.writeAll("extern functions have no body");456 return w.writeAll("extern functions have no body");
457 },457 },
458 .extra_addrspace_qualifier => {458 .extra_addrspace_qualifier => {
459 return bw.writeAll("extra addrspace qualifier");459 return w.writeAll("extra addrspace qualifier");
460 },460 },
461 .extra_align_qualifier => {461 .extra_align_qualifier => {
462 return bw.writeAll("extra align qualifier");462 return w.writeAll("extra align qualifier");
463 },463 },
464 .extra_allowzero_qualifier => {464 .extra_allowzero_qualifier => {
465 return bw.writeAll("extra allowzero qualifier");465 return w.writeAll("extra allowzero qualifier");
466 },466 },
467 .extra_const_qualifier => {467 .extra_const_qualifier => {
468 return bw.writeAll("extra const qualifier");468 return w.writeAll("extra const qualifier");
469 },469 },
470 .extra_volatile_qualifier => {470 .extra_volatile_qualifier => {
471 return bw.writeAll("extra volatile qualifier");471 return w.writeAll("extra volatile qualifier");
472 },472 },
473 .ptr_mod_on_array_child_type => {473 .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", .{
475 tree.tokenTag(parse_error.token).symbol(),475 tree.tokenTag(parse_error.token).symbol(),
476 });476 });
477 },477 },
478 .invalid_bit_range => {478 .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");
480 },480 },
481 .same_line_doc_comment => {481 .same_line_doc_comment => {
482 return bw.writeAll("same line documentation comment");482 return w.writeAll("same line documentation comment");
483 },483 },
484 .unattached_doc_comment => {484 .unattached_doc_comment => {
485 return bw.writeAll("unattached documentation comment");485 return w.writeAll("unattached documentation comment");
486 },486 },
487 .test_doc_comment => {487 .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");
489 },489 },
490 .comptime_doc_comment => {490 .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");
492 },492 },
493 .varargs_nonfinal => {493 .varargs_nonfinal => {
494 return bw.writeAll("function prototype has parameter after varargs");494 return w.writeAll("function prototype has parameter after varargs");
495 },495 },
496 .expected_continue_expr => {496 .expected_continue_expr => {
497 return bw.writeAll("expected ':' before while continue expression");497 return w.writeAll("expected ':' before while continue expression");
498 },498 },
499499
500 .expected_semi_after_decl => {500 .expected_semi_after_decl => {
501 return bw.writeAll("expected ';' after declaration");501 return w.writeAll("expected ';' after declaration");
502 },502 },
503 .expected_semi_after_stmt => {503 .expected_semi_after_stmt => {
504 return bw.writeAll("expected ';' after statement");504 return w.writeAll("expected ';' after statement");
505 },505 },
506 .expected_comma_after_field => {506 .expected_comma_after_field => {
507 return bw.writeAll("expected ',' after field");507 return w.writeAll("expected ',' after field");
508 },508 },
509 .expected_comma_after_arg => {509 .expected_comma_after_arg => {
510 return bw.writeAll("expected ',' after argument");510 return w.writeAll("expected ',' after argument");
511 },511 },
512 .expected_comma_after_param => {512 .expected_comma_after_param => {
513 return bw.writeAll("expected ',' after parameter");513 return w.writeAll("expected ',' after parameter");
514 },514 },
515 .expected_comma_after_initializer => {515 .expected_comma_after_initializer => {
516 return bw.writeAll("expected ',' after initializer");516 return w.writeAll("expected ',' after initializer");
517 },517 },
518 .expected_comma_after_switch_prong => {518 .expected_comma_after_switch_prong => {
519 return bw.writeAll("expected ',' after switch prong");519 return w.writeAll("expected ',' after switch prong");
520 },520 },
521 .expected_comma_after_for_operand => {521 .expected_comma_after_for_operand => {
522 return bw.writeAll("expected ',' after for operand");522 return w.writeAll("expected ',' after for operand");
523 },523 },
524 .expected_comma_after_capture => {524 .expected_comma_after_capture => {
525 return bw.writeAll("expected ',' after for capture");525 return w.writeAll("expected ',' after for capture");
526 },526 },
527 .expected_initializer => {527 .expected_initializer => {
528 return bw.writeAll("expected field initializer");528 return w.writeAll("expected field initializer");
529 },529 },
530 .mismatched_binary_op_whitespace => {530 .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().?});
532 },532 },
533 .invalid_ampersand_ampersand => {533 .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");
535 },535 },
536 .c_style_container => {536 .c_style_container => {
537 return bw.print("'{s} {s}' is invalid", .{537 return w.print("'{s} {s}' is invalid", .{
538 parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token),538 parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token),
539 });539 });
540 },540 },
541 .zig_style_container => {541 .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}'", .{
543 tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(),543 tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(),
544 });544 });
545 },545 },
546 .previous_field => {546 .previous_field => {
547 return bw.writeAll("field before declarations here");547 return w.writeAll("field before declarations here");
548 },548 },
549 .next_field => {549 .next_field => {
550 return bw.writeAll("field after declarations here");550 return w.writeAll("field after declarations here");
551 },551 },
552 .expected_var_const => {552 .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");
554 },554 },
555 .wrong_equal_var_decl => {555 .wrong_equal_var_decl => {
556 return bw.writeAll("variable initialized with '==' instead of '='");556 return w.writeAll("variable initialized with '==' instead of '='");
557 },557 },
558 .var_const_decl => {558 .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");
560 },560 },
561 .extra_for_capture => {561 .extra_for_capture => {
562 return bw.writeAll("extra capture in for loop");562 return w.writeAll("extra capture in for loop");
563 },563 },
564 .for_input_not_captured => {564 .for_input_not_captured => {
565 return bw.writeAll("for input is not captured");565 return w.writeAll("for input is not captured");
566 },566 },
567567
568 .invalid_byte => {568 .invalid_byte => {
569 const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..];569 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}'", .{
571 switch (tok_slice[0]) {571 switch (tok_slice[0]) {
572 '\'' => "character literal",572 '\'' => "character literal",
573 '"', '\\' => "string literal",573 '"', '\\' => "string literal",
574 '/' => "comment",574 '/' => "comment",
575 else => unreachable,575 else => unreachable,
576 },576 },
577 std.zig.fmtEscapes(tok_slice[parse_error.extra.offset..][0..1]),577 std.zig.fmtChar(tok_slice[parse_error.extra.offset..][0..1]),
578 });578 });
579 },579 },
580580
...@@ -582,10 +582,10 @@ pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void...@@ -582,10 +582,10 @@ pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void
582 const found_tag = tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev));582 const found_tag = tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev));
583 const expected_symbol = parse_error.extra.expected_tag.symbol();583 const expected_symbol = parse_error.extra.expected_tag.symbol();
584 switch (found_tag) {584 switch (found_tag) {
585 .invalid => return bw.print("expected '{s}', found invalid bytes", .{585 .invalid => return w.print("expected '{s}', found invalid bytes", .{
586 expected_symbol,586 expected_symbol,
587 }),587 }),
588 else => return bw.print("expected '{s}', found '{s}'", .{588 else => return w.print("expected '{s}', found '{s}'", .{
589 expected_symbol, found_tag.symbol(),589 expected_symbol, found_tag.symbol(),
590 }),590 }),
591 }591 }
...@@ -608,7 +608,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -608,7 +608,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
608 .negation_wrap,608 .negation_wrap,
609 .address_of,609 .address_of,
610 .@"try",610 .@"try",
611 .@"await",
612 .optional_type,611 .optional_type,
613 .@"switch",612 .@"switch",
614 .switch_comma,613 .switch_comma,
...@@ -758,27 +757,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -758,27 +757,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
758 return i - end_offset;757 return i - end_offset;
759 },758 },
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
782 .container_field_init,760 .container_field_init,
783 .container_field_align,761 .container_field_align,
784 .container_field,762 .container_field,
...@@ -898,14 +876,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -898,14 +876,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
898 while (true) switch (tree.nodeTag(n)) {876 while (true) switch (tree.nodeTag(n)) {
899 .root => return @intCast(tree.tokens.len - 1),877 .root => return @intCast(tree.tokens.len - 1),
900878
901 .@"usingnamespace",
902 .bool_not,879 .bool_not,
903 .negation,880 .negation,
904 .bit_not,881 .bit_not,
905 .negation_wrap,882 .negation_wrap,
906 .address_of,883 .address_of,
907 .@"try",884 .@"try",
908 .@"await",
909 .optional_type,885 .optional_type,
910 .@"suspend",886 .@"suspend",
911 .@"resume",887 .@"resume",
...@@ -1024,7 +1000,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -1024,7 +1000,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
1024 };1000 };
1025 },1001 },
10261002
1027 .call, .async_call => {1003 .call => {
1028 _, const extra_index = tree.nodeData(n).node_and_extra;1004 _, const extra_index = tree.nodeData(n).node_and_extra;
1029 const params = tree.extraData(extra_index, Node.SubRange);1005 const params = tree.extraData(extra_index, Node.SubRange);
1030 assert(params.start != params.end);1006 assert(params.start != params.end);
...@@ -1043,7 +1019,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -1043,7 +1019,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
1043 }1019 }
1044 },1020 },
1045 .call_comma,1021 .call_comma,
1046 .async_call_comma,
1047 .tagged_union_enum_tag_trailing,1022 .tagged_union_enum_tag_trailing,
1048 => {1023 => {
1049 _, const extra_index = tree.nodeData(n).node_and_extra;1024 _, const extra_index = tree.nodeData(n).node_and_extra;
...@@ -1124,7 +1099,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -1124,7 +1099,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
1124 n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member1099 n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member
1125 },1100 },
1126 .call_one,1101 .call_one,
1127 .async_call_one,
1128 => {1102 => {
1129 _, const first_param = tree.nodeData(n).node_and_opt_node;1103 _, const first_param = tree.nodeData(n).node_and_opt_node;
1130 end_offset += 1; // for the rparen1104 end_offset += 1; // for the rparen
...@@ -1273,7 +1247,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -1273,7 +1247,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
1273 n = first_element;1247 n = first_element;
1274 },1248 },
1275 .call_one_comma,1249 .call_one_comma,
1276 .async_call_one_comma,
1277 .struct_init_one_comma,1250 .struct_init_one_comma,
1278 => {1251 => {
1279 _, const first_field = tree.nodeData(n).node_and_opt_node;1252 _, const first_field = tree.nodeData(n).node_and_opt_node;
...@@ -1990,21 +1963,21 @@ pub fn forFull(tree: Ast, node: Node.Index) full.For {...@@ -1990,21 +1963,21 @@ pub fn forFull(tree: Ast, node: Node.Index) full.For {
1990pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call {1963pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call {
1991 const fn_expr, const first_param = tree.nodeData(node).node_and_opt_node;1964 const fn_expr, const first_param = tree.nodeData(node).node_and_opt_node;
1992 const params = loadOptionalNodesIntoBuffer(1, buffer, .{first_param});1965 const params = loadOptionalNodesIntoBuffer(1, buffer, .{first_param});
1993 return tree.fullCallComponents(.{1966 return .{ .ast = .{
1994 .lparen = tree.nodeMainToken(node),1967 .lparen = tree.nodeMainToken(node),
1995 .fn_expr = fn_expr,1968 .fn_expr = fn_expr,
1996 .params = params,1969 .params = params,
1997 });1970 } };
1998}1971}
19991972
2000pub fn callFull(tree: Ast, node: Node.Index) full.Call {1973pub fn callFull(tree: Ast, node: Node.Index) full.Call {
2001 const fn_expr, const extra_index = tree.nodeData(node).node_and_extra;1974 const fn_expr, const extra_index = tree.nodeData(node).node_and_extra;
2002 const params = tree.extraDataSlice(tree.extraData(extra_index, Node.SubRange), Node.Index);1975 const params = tree.extraDataSlice(tree.extraData(extra_index, Node.SubRange), Node.Index);
2003 return tree.fullCallComponents(.{1976 return .{ .ast = .{
2004 .lparen = tree.nodeMainToken(node),1977 .lparen = tree.nodeMainToken(node),
2005 .fn_expr = fn_expr,1978 .fn_expr = fn_expr,
2006 .params = params,1979 .params = params,
2007 });1980 } };
2008}1981}
20091982
2010fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl {1983fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl {
...@@ -2338,18 +2311,6 @@ fn fullForComponents(tree: Ast, info: full.For.Components) full.For {...@@ -2338,18 +2311,6 @@ fn fullForComponents(tree: Ast, info: full.For.Components) full.For {
2338 return result;2311 return result;
2339}2312}
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
2353pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl {2314pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl {
2354 return switch (tree.nodeTag(node)) {2315 return switch (tree.nodeTag(node)) {
2355 .global_var_decl => tree.globalVarDecl(node),2316 .global_var_decl => tree.globalVarDecl(node),
...@@ -2490,8 +2451,8 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm {...@@ -2490,8 +2451,8 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm {
24902451
2491pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call {2452pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call {
2492 return switch (tree.nodeTag(node)) {2453 return switch (tree.nodeTag(node)) {
2493 .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),2454 .call, .call_comma => tree.callFull(node),
2494 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node),2455 .call_one, .call_one_comma => tree.callOne(buffer, node),
2495 else => null,2456 else => null,
2496 };2457 };
2497}2458}
...@@ -2884,7 +2845,6 @@ pub const full = struct {...@@ -2884,7 +2845,6 @@ pub const full = struct {
28842845
2885 pub const Call = struct {2846 pub const Call = struct {
2886 ast: Components,2847 ast: Components,
2887 async_token: ?TokenIndex,
28882848
2889 pub const Components = struct {2849 pub const Components = struct {
2890 lparen: TokenIndex,2850 lparen: TokenIndex,
...@@ -3067,12 +3027,6 @@ pub const Node = struct {...@@ -3067,12 +3027,6 @@ pub const Node = struct {
3067 ///3027 ///
3068 /// The `main_token` field is the first token for the source file.3028 /// The `main_token` field is the first token for the source file.
3069 root,3029 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",
3076 /// `test {}`,3030 /// `test {}`,
3077 /// `test "name" {}`,3031 /// `test "name" {}`,
3078 /// `test identifier {}`.3032 /// `test identifier {}`.
...@@ -3303,8 +3257,6 @@ pub const Node = struct {...@@ -3303,8 +3257,6 @@ pub const Node = struct {
3303 address_of,3257 address_of,
3304 /// `try expr`. The `main_token` field is the `try` token.3258 /// `try expr`. The `main_token` field is the `try` token.
3305 @"try",3259 @"try",
3306 /// `await expr`. The `main_token` field is the `await` token.
3307 @"await",
3308 /// `?expr`. The `main_token` field is the `?` token.3260 /// `?expr`. The `main_token` field is the `?` token.
3309 optional_type,3261 optional_type,
3310 /// `[lhs]rhs`. The `main_token` field is the `[` token.3262 /// `[lhs]rhs`. The `main_token` field is the `[` token.
...@@ -3500,17 +3452,6 @@ pub const Node = struct {...@@ -3500,17 +3452,6 @@ pub const Node = struct {
3500 /// Same as `call_one` except there is known to be a trailing comma3452 /// Same as `call_one` except there is known to be a trailing comma
3501 /// before the final rparen.3453 /// before the final rparen.
3502 call_one_comma,3454 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,
3514 /// `a(b, c, d)`.3455 /// `a(b, c, d)`.
3515 ///3456 ///
3516 /// The `data` field is a `.node_and_extra`:3457 /// The `data` field is a `.node_and_extra`:
...@@ -3523,18 +3464,6 @@ pub const Node = struct {...@@ -3523,18 +3464,6 @@ pub const Node = struct {
3523 /// Same as `call` except there is known to be a trailing comma before3464 /// Same as `call` except there is known to be a trailing comma before
3524 /// the final rparen.3465 /// the final rparen.
3525 call_comma,3466 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,
3538 /// `switch(a) {}`.3467 /// `switch(a) {}`.
3539 ///3468 ///
3540 /// The `data` field is a `.node_and_extra`:3469 /// The `data` field is a `.node_and_extra`:
lib/std/zig/Ast/Render.zig+3-23
...@@ -265,17 +265,6 @@ fn renderMember(...@@ -265,17 +265,6 @@ fn renderMember(
265 return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon265 return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon
266 },266 },
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
279 .global_var_decl,268 .global_var_decl,
280 .local_var_decl,269 .local_var_decl,
281 .simple_var_decl,270 .simple_var_decl,
...@@ -594,7 +583,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -594,7 +583,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
594583
595 .@"try",584 .@"try",
596 .@"resume",585 .@"resume",
597 .@"await",
598 => {586 => {
599 try renderToken(r, tree.nodeMainToken(node), .space);587 try renderToken(r, tree.nodeMainToken(node), .space);
600 return renderExpression(r, tree.nodeData(node).node, space);588 return renderExpression(r, tree.nodeData(node).node, space);
...@@ -638,12 +626,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -638,12 +626,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
638626
639 .call_one,627 .call_one,
640 .call_one_comma,628 .call_one_comma,
641 .async_call_one,
642 .async_call_one_comma,
643 .call,629 .call,
644 .call_comma,630 .call_comma,
645 .async_call,
646 .async_call_comma,
647 => {631 => {
648 var buf: [1]Ast.Node.Index = undefined;632 var buf: [1]Ast.Node.Index = undefined;
649 return renderCall(r, tree.fullCall(&buf, node).?, space);633 return renderCall(r, tree.fullCall(&buf, node).?, space);
...@@ -885,7 +869,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -885,7 +869,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
885 .local_var_decl => unreachable,869 .local_var_decl => unreachable,
886 .simple_var_decl => unreachable,870 .simple_var_decl => unreachable,
887 .aligned_var_decl => unreachable,871 .aligned_var_decl => unreachable,
888 .@"usingnamespace" => unreachable,
889 .test_decl => unreachable,872 .test_decl => unreachable,
890 .asm_output => unreachable,873 .asm_output => unreachable,
891 .asm_input => unreachable,874 .asm_input => unreachable,
...@@ -1584,7 +1567,7 @@ fn renderBuiltinCall(...@@ -1584,7 +1567,7 @@ fn renderBuiltinCall(
1584 defer r.gpa.free(new_string);1567 defer r.gpa.free(new_string);
15851568
1586 try renderToken(r, builtin_token + 1, .none); // (1569 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)});
1588 return renderToken(r, str_lit_token + 1, space); // )1571 return renderToken(r, str_lit_token + 1, space); // )
1589 }1572 }
1590 }1573 }
...@@ -2556,9 +2539,6 @@ fn renderCall(...@@ -2556,9 +2539,6 @@ fn renderCall(
2556 call: Ast.full.Call,2539 call: Ast.full.Call,
2557 space: Space,2540 space: Space,
2558) Error!void {2541) Error!void {
2559 if (call.async_token) |async_token| {
2560 try renderToken(r, async_token, .space);
2561 }
2562 try renderExpression(r, call.ast.fn_expr, .none);2542 try renderExpression(r, call.ast.fn_expr, .none);
2563 try renderParamList(r, call.ast.lparen, call.ast.params, space);2543 try renderParamList(r, call.ast.lparen, call.ast.params, space);
2564}2544}
...@@ -2897,7 +2877,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void...@@ -2897,7 +2877,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
2897 .success => |codepoint| {2877 .success => |codepoint| {
2898 if (codepoint <= 0x7f) {2878 if (codepoint <= 0x7f) {
2899 const buf = [1]u8{@as(u8, @intCast(codepoint))};2879 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)});
2901 } else {2881 } else {
2902 try ais.writeAll(escape_sequence);2882 try ais.writeAll(escape_sequence);
2903 }2883 }
...@@ -2909,7 +2889,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void...@@ -2909,7 +2889,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void
2909 },2889 },
2910 0x00...('\\' - 1), ('\\' + 1)...0x7f => {2890 0x00...('\\' - 1), ('\\' + 1)...0x7f => {
2911 const buf = [1]u8{byte};2891 const buf = [1]u8{byte};
2912 try ais.print("{f}", .{std.zig.fmtEscapes(&buf)});2892 try ais.print("{f}", .{std.zig.fmtString(&buf)});
2913 pos += 1;2893 pos += 1;
2914 },2894 },
2915 0x80...0xff => {2895 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...@@ -442,7 +442,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
442 const tree = astgen.tree;442 const tree = astgen.tree;
443 switch (tree.nodeTag(node)) {443 switch (tree.nodeTag(node)) {
444 .root => unreachable,444 .root => unreachable,
445 .@"usingnamespace" => unreachable,
446 .test_decl => unreachable,445 .test_decl => unreachable,
447 .global_var_decl => unreachable,446 .global_var_decl => unreachable,
448 .local_var_decl => unreachable,447 .local_var_decl => unreachable,
...@@ -510,12 +509,8 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -510,12 +509,8 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
510 .number_literal,509 .number_literal,
511 .call,510 .call,
512 .call_comma,511 .call_comma,
513 .async_call,
514 .async_call_comma,
515 .call_one,512 .call_one,
516 .call_one_comma,513 .call_one_comma,
517 .async_call_one,
518 .async_call_one_comma,
519 .unreachable_literal,514 .unreachable_literal,
520 .@"return",515 .@"return",
521 .@"if",516 .@"if",
...@@ -547,7 +542,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -547,7 +542,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
547 .merge_error_sets,542 .merge_error_sets,
548 .switch_range,543 .switch_range,
549 .for_range,544 .for_range,
550 .@"await",
551 .bit_not,545 .bit_not,
552 .negation,546 .negation,
553 .negation_wrap,547 .negation_wrap,
...@@ -642,7 +636,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -642,7 +636,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
642636
643 switch (tree.nodeTag(node)) {637 switch (tree.nodeTag(node)) {
644 .root => unreachable, // Top-level declaration.638 .root => unreachable, // Top-level declaration.
645 .@"usingnamespace" => unreachable, // Top-level declaration.
646 .test_decl => unreachable, // Top-level declaration.639 .test_decl => unreachable, // Top-level declaration.
647 .container_field_init => unreachable, // Top-level declaration.640 .container_field_init => unreachable, // Top-level declaration.
648 .container_field_align => unreachable, // Top-level declaration.641 .container_field_align => unreachable, // Top-level declaration.
...@@ -836,12 +829,8 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -836,12 +829,8 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
836829
837 .call_one,830 .call_one,
838 .call_one_comma,831 .call_one_comma,
839 .async_call_one,
840 .async_call_one_comma,
841 .call,832 .call,
842 .call_comma,833 .call_comma,
843 .async_call,
844 .async_call_comma,
845 => {834 => {
846 var buf: [1]Ast.Node.Index = undefined;835 var buf: [1]Ast.Node.Index = undefined;
847 return callExpr(gz, scope, ri, .none, node, tree.fullCall(&buf, node).?);836 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...@@ -1114,7 +1103,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
11141103
1115 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),1104 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
1116 .@"suspend" => return suspendExpr(gz, scope, node),1105 .@"suspend" => return suspendExpr(gz, scope, node),
1117 .@"await" => return awaitExpr(gz, scope, ri, node),
1118 .@"resume" => return resumeExpr(gz, scope, ri, node),1106 .@"resume" => return resumeExpr(gz, scope, ri, node),
11191107
1120 .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node),1108 .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node),
...@@ -1259,33 +1247,6 @@ fn suspendExpr(...@@ -1259,33 +1247,6 @@ fn suspendExpr(
1259 return suspend_inst.toRef();1247 return suspend_inst.toRef();
1260}1248}
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
1289fn resumeExpr(1250fn resumeExpr(
1290 gz: *GenZir,1251 gz: *GenZir,
1291 scope: *Scope,1252 scope: *Scope,
...@@ -2853,7 +2814,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2853,7 +2814,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2853 .tag_name,2814 .tag_name,
2854 .type_name,2815 .type_name,
2855 .frame_type,2816 .frame_type,
2856 .frame_size,
2857 .int_from_float,2817 .int_from_float,
2858 .float_from_int,2818 .float_from_int,
2859 .ptr_from_int,2819 .ptr_from_int,
...@@ -2887,7 +2847,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2887,7 +2847,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2887 .min,2847 .min,
2888 .c_import,2848 .c_import,
2889 .@"resume",2849 .@"resume",
2890 .@"await",
2891 .ret_err_value_code,2850 .ret_err_value_code,
2892 .ret_ptr,2851 .ret_ptr,
2893 .ret_type,2852 .ret_type,
...@@ -4739,69 +4698,6 @@ fn comptimeDecl(...@@ -4739,69 +4698,6 @@ fn comptimeDecl(
4739 });4698 });
4740}4699}
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
4805fn testDecl(4701fn testDecl(
4806 astgen: *AstGen,4702 astgen: *AstGen,
4807 gz: *GenZir,4703 gz: *GenZir,
...@@ -5971,23 +5867,6 @@ fn containerMember(...@@ -5971,23 +5867,6 @@ fn containerMember(
5971 },5867 },
5972 };5868 };
5973 },5869 },
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 },
5991 .test_decl => {5870 .test_decl => {
5992 const prev_decl_index = wip_members.decl_index;5871 const prev_decl_index = wip_members.decl_index;
5993 // We need to have *some* decl here so that the decl count matches what's expected.5872 // We need to have *some* decl here so that the decl count matches what's expected.
...@@ -9501,7 +9380,6 @@ fn builtinCall(...@@ -9501,7 +9380,6 @@ fn builtinCall(
9501 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),9380 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
9502 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),9381 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
9503 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),9382 .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
9506 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),9384 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
9507 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),9385 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
...@@ -9767,16 +9645,6 @@ fn builtinCall(...@@ -9767,16 +9645,6 @@ fn builtinCall(
9767 });9645 });
9768 return rvalue(gz, ri, result, node);9646 return rvalue(gz, ri, result, node);
9769 },9647 },
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 },
9780 .Vector => {9648 .Vector => {
9781 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{9649 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
9782 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type),9650 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type),
...@@ -10175,11 +10043,8 @@ fn callExpr(...@@ -10175,11 +10043,8 @@ fn callExpr(
1017510043
10176 const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr);10044 const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr);
10177 const modifier: std.builtin.CallModifier = blk: {10045 const modifier: std.builtin.CallModifier = blk: {
10178 if (call.async_token != null) {
10179 break :blk .async_kw;
10180 }
10181 if (gz.nosuspend_node != .none) {10046 if (gz.nosuspend_node != .none) {
10182 break :blk .no_async;10047 break :blk .no_suspend;
10183 }10048 }
10184 break :blk .auto;10049 break :blk .auto;
10185 };10050 };
...@@ -10451,7 +10316,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev...@@ -10451,7 +10316,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
10451 while (true) {10316 while (true) {
10452 switch (tree.nodeTag(node)) {10317 switch (tree.nodeTag(node)) {
10453 .root,10318 .root,
10454 .@"usingnamespace",
10455 .test_decl,10319 .test_decl,
10456 .switch_case,10320 .switch_case,
10457 .switch_case_inline,10321 .switch_case_inline,
...@@ -10483,12 +10347,8 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev...@@ -10483,12 +10347,8 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
10483 .switch_comma,10347 .switch_comma,
10484 .call_one,10348 .call_one,
10485 .call_one_comma,10349 .call_one_comma,
10486 .async_call_one,
10487 .async_call_one_comma,
10488 .call,10350 .call,
10489 .call_comma,10351 .call_comma,
10490 .async_call,
10491 .async_call_comma,
10492 => return .maybe,10352 => return .maybe,
1049310353
10494 .@"return",10354 .@"return",
...@@ -10613,7 +10473,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev...@@ -10613,7 +10473,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
1061310473
10614 // Forward the question to the LHS sub-expression.10474 // Forward the question to the LHS sub-expression.
10615 .@"try",10475 .@"try",
10616 .@"await",
10617 .@"comptime",10476 .@"comptime",
10618 .@"nosuspend",10477 .@"nosuspend",
10619 => node = tree.nodeData(node).node,10478 => node = tree.nodeData(node).node,
...@@ -10664,7 +10523,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In...@@ -10664,7 +10523,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
10664 while (true) {10523 while (true) {
10665 switch (tree.nodeTag(node)) {10524 switch (tree.nodeTag(node)) {
10666 .root,10525 .root,
10667 .@"usingnamespace",
10668 .test_decl,10526 .test_decl,
10669 .switch_case,10527 .switch_case,
10670 .switch_case_inline,10528 .switch_case_inline,
...@@ -10803,12 +10661,8 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In...@@ -10803,12 +10661,8 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
10803 .switch_comma,10661 .switch_comma,
10804 .call_one,10662 .call_one,
10805 .call_one_comma,10663 .call_one_comma,
10806 .async_call_one,
10807 .async_call_one_comma,
10808 .call,10664 .call,
10809 .call_comma,10665 .call_comma,
10810 .async_call,
10811 .async_call_comma,
10812 .block_two,10666 .block_two,
10813 .block_two_semicolon,10667 .block_two_semicolon,
10814 .block,10668 .block,
...@@ -10826,7 +10680,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In...@@ -10826,7 +10680,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
1082610680
10827 // Forward the question to the LHS sub-expression.10681 // Forward the question to the LHS sub-expression.
10828 .@"try",10682 .@"try",
10829 .@"await",
10830 .@"comptime",10683 .@"comptime",
10831 .@"nosuspend",10684 .@"nosuspend",
10832 => node = tree.nodeData(node).node,10685 => node = tree.nodeData(node).node,
...@@ -10908,7 +10761,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -10908,7 +10761,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
10908 while (true) {10761 while (true) {
10909 switch (tree.nodeTag(node)) {10762 switch (tree.nodeTag(node)) {
10910 .root,10763 .root,
10911 .@"usingnamespace",
10912 .test_decl,10764 .test_decl,
10913 .switch_case,10765 .switch_case,
10914 .switch_case_inline,10766 .switch_case_inline,
...@@ -11047,12 +10899,8 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -11047,12 +10899,8 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
11047 .switch_comma,10899 .switch_comma,
11048 .call_one,10900 .call_one,
11049 .call_one_comma,10901 .call_one_comma,
11050 .async_call_one,
11051 .async_call_one_comma,
11052 .call,10902 .call,
11053 .call_comma,10903 .call_comma,
11054 .async_call,
11055 .async_call_comma,
11056 .block_two,10904 .block_two,
11057 .block_two_semicolon,10905 .block_two_semicolon,
11058 .block,10906 .block,
...@@ -11079,7 +10927,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -11079,7 +10927,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
1107910927
11080 // Forward the question to the LHS sub-expression.10928 // Forward the question to the LHS sub-expression.
11081 .@"try",10929 .@"try",
11082 .@"await",
11083 .@"comptime",10930 .@"comptime",
11084 .@"nosuspend",10931 .@"nosuspend",
11085 => node = tree.nodeData(node).node,10932 => node = tree.nodeData(node).node,
...@@ -11462,13 +11309,7 @@ fn failWithStrLitError(...@@ -11462,13 +11309,7 @@ fn failWithStrLitError(
11462 offset: u32,11309 offset: u32,
11463) InnerError {11310) InnerError {
11464 const raw_string = bytes[offset..];11311 const raw_string = bytes[offset..];
11465 return failOff(11312 return failOff(astgen, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)});
11466 astgen,
11467 token,
11468 @intCast(offset + err.offset()),
11469 "{f}",
11470 .{err.fmt(raw_string)},
11471 );
11472}11313}
1147311314
11474fn failNode(11315fn failNode(
...@@ -13591,7 +13432,7 @@ fn scanContainer(...@@ -13591,7 +13432,7 @@ fn scanContainer(
13591 break :blk .{ .decl, ident };13432 break :blk .{ .decl, ident };
13592 },13433 },
1359313434
13594 .@"comptime", .@"usingnamespace" => {13435 .@"comptime" => {
13595 decl_count += 1;13436 decl_count += 1;
13596 continue;13437 continue;
13597 },13438 },
...@@ -13970,7 +13811,6 @@ const DeclarationName = union(enum) {...@@ -13970,7 +13811,6 @@ const DeclarationName = union(enum) {
13970 decltest: Ast.TokenIndex,13811 decltest: Ast.TokenIndex,
13971 unnamed_test,13812 unnamed_test,
13972 @"comptime",13813 @"comptime",
13973 @"usingnamespace",
13974};13814};
1397513815
13976fn addFailedDeclaration(13816fn addFailedDeclaration(
...@@ -14060,7 +13900,6 @@ fn setDeclaration(...@@ -14060,7 +13900,6 @@ fn setDeclaration(
14060 .@"test" => .@"test",13900 .@"test" => .@"test",
14061 .decltest => .decltest,13901 .decltest => .decltest,
14062 .@"comptime" => .@"comptime",13902 .@"comptime" => .@"comptime",
14063 .@"usingnamespace" => if (args.is_pub) .pub_usingnamespace else .@"usingnamespace",
14064 .@"const" => switch (args.linkage) {13903 .@"const" => switch (args.linkage) {
14065 .normal => if (args.is_pub) id: {13904 .normal => if (args.is_pub) id: {
14066 if (has_special_body) break :id .pub_const;13905 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...@@ -165,10 +165,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
165 }165 }
166 return false;166 return false;
167 },167 },
168 .@"usingnamespace" => {
169 _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.type_only);
170 return false;
171 },
172 .test_decl => {168 .test_decl => {
173 _ = try astrl.expr(tree.nodeData(node).opt_token_and_node[1], block, ResultInfo.none);169 _ = try astrl.expr(tree.nodeData(node).opt_token_and_node[1], block, ResultInfo.none);
174 return false;170 return false;
...@@ -334,12 +330,8 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI...@@ -334,12 +330,8 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
334330
335 .call_one,331 .call_one,
336 .call_one_comma,332 .call_one_comma,
337 .async_call_one,
338 .async_call_one_comma,
339 .call,333 .call,
340 .call_comma,334 .call_comma,
341 .async_call,
342 .async_call_comma,
343 => {335 => {
344 var buf: [1]Ast.Node.Index = undefined;336 var buf: [1]Ast.Node.Index = undefined;
345 const full = tree.fullCall(&buf, node).?;337 const full = tree.fullCall(&buf, node).?;
...@@ -353,11 +345,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI...@@ -353,11 +345,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
353 .call,345 .call,
354 .call_comma,346 .call_comma,
355 => false, // TODO: once function calls are passed result locations this will change347 => 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
361 else => unreachable,348 else => unreachable,
362 };349 };
363 },350 },
...@@ -503,7 +490,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI...@@ -503,7 +490,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
503 return false;490 return false;
504 },491 },
505 .@"try",492 .@"try",
506 .@"await",
507 .@"nosuspend",493 .@"nosuspend",
508 => return astrl.expr(tree.nodeData(node).node, block, ri),494 => return astrl.expr(tree.nodeData(node).node, block, ri),
509 .grouped_expression,495 .grouped_expression,
...@@ -948,7 +934,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -948,7 +934,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
948 .tag_name,934 .tag_name,
949 .type_name,935 .type_name,
950 .Frame,936 .Frame,
951 .frame_size,
952 .int_from_float,937 .int_from_float,
953 .float_from_int,938 .float_from_int,
954 .ptr_from_int,939 .ptr_from_int,
...@@ -1079,13 +1064,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -1079,13 +1064,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
1079 _ = try astrl.expr(args[3], block, ResultInfo.none);1064 _ = try astrl.expr(args[3], block, ResultInfo.none);
1080 return false;1065 return false;
1081 },1066 },
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 },
1089 .Vector => {1067 .Vector => {
1090 _ = try astrl.expr(args[0], block, ResultInfo.type_only);1068 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1091 _ = try astrl.expr(args[1], block, ResultInfo.type_only);1069 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig-16
...@@ -4,7 +4,6 @@ pub const Tag = enum {...@@ -4,7 +4,6 @@ pub const Tag = enum {
4 align_cast,4 align_cast,
5 align_of,5 align_of,
6 as,6 as,
7 async_call,
8 atomic_load,7 atomic_load,
9 atomic_rmw,8 atomic_rmw,
10 atomic_store,9 atomic_store,
...@@ -55,7 +54,6 @@ pub const Tag = enum {...@@ -55,7 +54,6 @@ pub const Tag = enum {
55 frame,54 frame,
56 Frame,55 Frame,
57 frame_address,56 frame_address,
58 frame_size,
59 has_decl,57 has_decl,
60 has_field,58 has_field,
61 import,59 import,
...@@ -184,13 +182,6 @@ pub const list = list: {...@@ -184,13 +182,6 @@ pub const list = list: {
184 .param_count = 2,182 .param_count = 2,
185 },183 },
186 },184 },
187 .{
188 "@asyncCall",
189 .{
190 .tag = .async_call,
191 .param_count = 4,
192 },
193 },
194 .{185 .{
195 "@atomicLoad",186 "@atomicLoad",
196 .{187 .{
...@@ -550,13 +541,6 @@ pub const list = list: {...@@ -550,13 +541,6 @@ pub const list = list: {
550 .illegal_outside_function = true,541 .illegal_outside_function = true,
551 },542 },
552 },543 },
553 .{
554 "@frameSize",
555 .{
556 .tag = .frame_size,
557 .param_count = 1,
558 },
559 },
560 .{544 .{
561 "@hasDecl",545 "@hasDecl",
562 .{546 .{
lib/std/zig/ErrorBundle.zig+64-56
...@@ -164,22 +164,22 @@ pub const RenderOptions = struct {...@@ -164,22 +164,22 @@ pub const RenderOptions = struct {
164164
165pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {165pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
166 var buffer: [256]u8 = undefined;166 var buffer: [256]u8 = undefined;
167 const bw = std.debug.lockStderrWriter(&buffer);167 const w = std.debug.lockStderrWriter(&buffer);
168 defer std.debug.unlockStderrWriter();168 defer std.debug.unlockStderrWriter();
169 renderToWriter(eb, options, bw) catch return;169 renderToWriter(eb, options, w) catch return;
170}170}
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 {
173 if (eb.extra.len == 0) return;173 if (eb.extra.len == 0) return;
174 for (eb.getMessages()) |err_msg| {174 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);
176 }176 }
177177
178 if (options.include_log_text) {178 if (options.include_log_text) {
179 const log_text = eb.getCompileLogOutput();179 const log_text = eb.getCompileLogOutput();
180 if (log_text.len != 0) {180 if (log_text.len != 0) {
181 try bw.writeAll("\nCompile Log Output:\n");181 try w.writeAll("\nCompile Log Output:\n");
182 try bw.writeAll(log_text);182 try w.writeAll(log_text);
183 }183 }
184 }184 }
185}185}
...@@ -188,73 +188,81 @@ fn renderErrorMessageToWriter(...@@ -188,73 +188,81 @@ fn renderErrorMessageToWriter(
188 eb: ErrorBundle,188 eb: ErrorBundle,
189 options: RenderOptions,189 options: RenderOptions,
190 err_msg_index: MessageIndex,190 err_msg_index: MessageIndex,
191 bw: *Writer,191 w: *Writer,
192 kind: []const u8,192 kind: []const u8,
193 color: std.io.tty.Color,193 color: std.io.tty.Color,
194 indent: usize,194 indent: usize,
195) (Writer.Error || std.posix.UnexpectedError)!void {195) (Writer.Error || std.posix.UnexpectedError)!void {
196 const ttyconf = options.ttyconf;196 const ttyconf = options.ttyconf;
197 const err_msg = eb.getErrorMessage(err_msg_index);197 const err_msg = eb.getErrorMessage(err_msg_index);
198 const prefix_start = bw.count;
199 if (err_msg.src_loc != .none) {198 if (err_msg.src_loc != .none) {
200 const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));199 const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));
201 try bw.splatByteAll(' ', indent);200 var prefix: std.io.Writer.Discarding = .init(&.{});
202 try ttyconf.setColor(bw, .bold);201 try w.splatByteAll(' ', indent);
203 try bw.print("{s}:{d}:{d}: ", .{202 prefix.count += indent;
203 try ttyconf.setColor(w, .bold);
204 try w.print("{s}:{d}:{d}: ", .{
204 eb.nullTerminatedString(src.data.src_path),205 eb.nullTerminatedString(src.data.src_path),
205 src.data.line + 1,206 src.data.line + 1,
206 src.data.column + 1,207 src.data.column + 1,
207 });208 });
208 try ttyconf.setColor(bw, color);209 try prefix.writer.print("{s}:{d}:{d}: ", .{
209 try bw.writeAll(kind);210 eb.nullTerminatedString(src.data.src_path),
210 try bw.writeAll(": ");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;
211 // This is the length of the part before the error message:219 // This is the length of the part before the error message:
212 // e.g. "file.zig:4:5: error: "220 // e.g. "file.zig:4:5: error: "
213 const prefix_len = bw.count - prefix_start;221 const prefix_len: usize = @intCast(prefix.count);
214 try ttyconf.setColor(bw, .reset);222 try ttyconf.setColor(w, .reset);
215 try ttyconf.setColor(bw, .bold);223 try ttyconf.setColor(w, .bold);
216 if (err_msg.count == 1) {224 if (err_msg.count == 1) {
217 try writeMsg(eb, err_msg, bw, prefix_len);225 try writeMsg(eb, err_msg, w, prefix_len);
218 try bw.writeByte('\n');226 try w.writeByte('\n');
219 } else {227 } else {
220 try writeMsg(eb, err_msg, bw, prefix_len);228 try writeMsg(eb, err_msg, w, prefix_len);
221 try ttyconf.setColor(bw, .dim);229 try ttyconf.setColor(w, .dim);
222 try bw.print(" ({d} times)\n", .{err_msg.count});230 try w.print(" ({d} times)\n", .{err_msg.count});
223 }231 }
224 try ttyconf.setColor(bw, .reset);232 try ttyconf.setColor(w, .reset);
225 if (src.data.source_line != 0 and options.include_source_line) {233 if (src.data.source_line != 0 and options.include_source_line) {
226 const line = eb.nullTerminatedString(src.data.source_line);234 const line = eb.nullTerminatedString(src.data.source_line);
227 for (line) |b| switch (b) {235 for (line) |b| switch (b) {
228 '\t' => try bw.writeByte(' '),236 '\t' => try w.writeByte(' '),
229 else => try bw.writeByte(b),237 else => try w.writeByte(b),
230 };238 };
231 try bw.writeByte('\n');239 try w.writeByte('\n');
232 // TODO basic unicode code point monospace width240 // TODO basic unicode code point monospace width
233 const before_caret = src.data.span_main - src.data.span_start;241 const before_caret = src.data.span_main - src.data.span_start;
234 // -1 since span.main includes the caret242 // -1 since span.main includes the caret
235 const after_caret = src.data.span_end -| src.data.span_main -| 1;243 const after_caret = src.data.span_end -| src.data.span_main -| 1;
236 try bw.splatByteAll(' ', src.data.column - before_caret);244 try w.splatByteAll(' ', src.data.column - before_caret);
237 try ttyconf.setColor(bw, .green);245 try ttyconf.setColor(w, .green);
238 try bw.splatByteAll('~', before_caret);246 try w.splatByteAll('~', before_caret);
239 try bw.writeByte('^');247 try w.writeByte('^');
240 try bw.splatByteAll('~', after_caret);248 try w.splatByteAll('~', after_caret);
241 try bw.writeByte('\n');249 try w.writeByte('\n');
242 try ttyconf.setColor(bw, .reset);250 try ttyconf.setColor(w, .reset);
243 }251 }
244 for (eb.getNotes(err_msg_index)) |note| {252 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);
246 }254 }
247 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {255 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
248 try ttyconf.setColor(bw, .reset);256 try ttyconf.setColor(w, .reset);
249 try ttyconf.setColor(bw, .dim);257 try ttyconf.setColor(w, .dim);
250 try bw.print("referenced by:\n", .{});258 try w.print("referenced by:\n", .{});
251 var ref_index = src.end;259 var ref_index = src.end;
252 for (0..src.data.reference_trace_len) |_| {260 for (0..src.data.reference_trace_len) |_| {
253 const ref_trace = eb.extraData(ReferenceTrace, ref_index);261 const ref_trace = eb.extraData(ReferenceTrace, ref_index);
254 ref_index = ref_trace.end;262 ref_index = ref_trace.end;
255 if (ref_trace.data.src_loc != .none) {263 if (ref_trace.data.src_loc != .none) {
256 const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);264 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", .{
258 eb.nullTerminatedString(ref_trace.data.decl_name),266 eb.nullTerminatedString(ref_trace.data.decl_name),
259 eb.nullTerminatedString(ref_src.src_path),267 eb.nullTerminatedString(ref_src.src_path),
260 ref_src.line + 1,268 ref_src.line + 1,
...@@ -262,36 +270,36 @@ fn renderErrorMessageToWriter(...@@ -262,36 +270,36 @@ fn renderErrorMessageToWriter(
262 });270 });
263 } else if (ref_trace.data.decl_name != 0) {271 } else if (ref_trace.data.decl_name != 0) {
264 const count = ref_trace.data.decl_name;272 const count = ref_trace.data.decl_name;
265 try bw.print(273 try w.print(
266 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",274 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
267 .{ count, count + src.data.reference_trace_len - 1 },275 .{ count, count + src.data.reference_trace_len - 1 },
268 );276 );
269 } else {277 } else {
270 try bw.print(278 try w.print(
271 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",279 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
272 .{},280 .{},
273 );281 );
274 }282 }
275 }283 }
276 try ttyconf.setColor(bw, .reset);284 try ttyconf.setColor(w, .reset);
277 }285 }
278 } else {286 } else {
279 try ttyconf.setColor(bw, color);287 try ttyconf.setColor(w, color);
280 try bw.splatByteAll(' ', indent);288 try w.splatByteAll(' ', indent);
281 try bw.writeAll(kind);289 try w.writeAll(kind);
282 try bw.writeAll(": ");290 try w.writeAll(": ");
283 try ttyconf.setColor(bw, .reset);291 try ttyconf.setColor(w, .reset);
284 const msg = eb.nullTerminatedString(err_msg.msg);292 const msg = eb.nullTerminatedString(err_msg.msg);
285 if (err_msg.count == 1) {293 if (err_msg.count == 1) {
286 try bw.print("{s}\n", .{msg});294 try w.print("{s}\n", .{msg});
287 } else {295 } else {
288 try bw.print("{s}", .{msg});296 try w.print("{s}", .{msg});
289 try ttyconf.setColor(bw, .dim);297 try ttyconf.setColor(w, .dim);
290 try bw.print(" ({d} times)\n", .{err_msg.count});298 try w.print(" ({d} times)\n", .{err_msg.count});
291 }299 }
292 try ttyconf.setColor(bw, .reset);300 try ttyconf.setColor(w, .reset);
293 for (eb.getNotes(err_msg_index)) |note| {301 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);
295 }303 }
296 }304 }
297}305}
...@@ -300,13 +308,13 @@ fn renderErrorMessageToWriter(...@@ -300,13 +308,13 @@ fn renderErrorMessageToWriter(
300/// to allow for long, good-looking error messages.308/// to allow for long, good-looking error messages.
301///309///
302/// This is used to split the message in `@compileError("hello\nworld")` for example.310/// 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 {
304 var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');312 var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');
305 while (lines.next()) |line| {313 while (lines.next()) |line| {
306 try bw.writeAll(line);314 try w.writeAll(line);
307 if (lines.index == null) break;315 if (lines.index == null) break;
308 try bw.writeByte('\n');316 try w.writeByte('\n');
309 try bw.splatByteAll(' ', indent);317 try w.splatByteAll(' ', indent);
310 }318 }
311}319}
312320
lib/std/zig/Parse.zig+2-90
...@@ -359,16 +359,6 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members {...@@ -359,16 +359,6 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members {
359 }359 }
360 trailing = p.tokenTag(p.tok_i - 1) == .semicolon;360 trailing = p.tokenTag(p.tok_i - 1) == .semicolon;
361 },361 },
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 },
372 .keyword_const,362 .keyword_const,
373 .keyword_var,363 .keyword_var,
374 .keyword_threadlocal,364 .keyword_threadlocal,
...@@ -496,7 +486,6 @@ fn findNextContainerMember(p: *Parse) void {...@@ -496,7 +486,6 @@ fn findNextContainerMember(p: *Parse) void {
496 .keyword_extern,486 .keyword_extern,
497 .keyword_inline,487 .keyword_inline,
498 .keyword_noinline,488 .keyword_noinline,
499 .keyword_usingnamespace,
500 .keyword_threadlocal,489 .keyword_threadlocal,
501 .keyword_const,490 .keyword_const,
502 .keyword_var,491 .keyword_var,
...@@ -601,7 +590,6 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {...@@ -601,7 +590,6 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
601/// Decl590/// Decl
602/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)591/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
603/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl592/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
604/// / KEYWORD_usingnamespace Expr SEMICOLON
605fn expectTopLevelDecl(p: *Parse) !?Node.Index {593fn expectTopLevelDecl(p: *Parse) !?Node.Index {
606 const extern_export_inline_token = p.nextToken();594 const extern_export_inline_token = p.nextToken();
607 var is_extern: bool = false;595 var is_extern: bool = false;
...@@ -664,10 +652,7 @@ fn expectTopLevelDecl(p: *Parse) !?Node.Index {...@@ -664,10 +652,7 @@ fn expectTopLevelDecl(p: *Parse) !?Node.Index {
664 if (expect_var_or_fn) {652 if (expect_var_or_fn) {
665 return p.fail(.expected_var_decl_or_fn);653 return p.fail(.expected_var_decl_or_fn);
666 }654 }
667 if (p.tokenTag(p.tok_i) != .keyword_usingnamespace) {655 return p.fail(.expected_pub_item);
668 return p.fail(.expected_pub_item);
669 }
670 return try p.expectUsingNamespace();
671}656}
672657
673fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {658fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
...@@ -680,27 +665,6 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {...@@ -680,27 +665,6 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
680 };665 };
681}666}
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
704/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr668/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
705fn parseFnProto(p: *Parse) !?Node.Index {669fn parseFnProto(p: *Parse) !?Node.Index {
706 const fn_token = p.eatToken(.keyword_fn) orelse return null;670 const fn_token = p.eatToken(.keyword_fn) orelse return null;
...@@ -1688,7 +1652,6 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index {...@@ -1688,7 +1652,6 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index {
1688/// / MINUSPERCENT1652/// / MINUSPERCENT
1689/// / AMPERSAND1653/// / AMPERSAND
1690/// / KEYWORD_try1654/// / KEYWORD_try
1691/// / KEYWORD_await
1692fn parsePrefixExpr(p: *Parse) Error!?Node.Index {1655fn parsePrefixExpr(p: *Parse) Error!?Node.Index {
1693 const tag: Node.Tag = switch (p.tokenTag(p.tok_i)) {1656 const tag: Node.Tag = switch (p.tokenTag(p.tok_i)) {
1694 .bang => .bool_not,1657 .bang => .bool_not,
...@@ -1697,7 +1660,6 @@ fn parsePrefixExpr(p: *Parse) Error!?Node.Index {...@@ -1697,7 +1660,6 @@ fn parsePrefixExpr(p: *Parse) Error!?Node.Index {
1697 .minus_percent => .negation_wrap,1660 .minus_percent => .negation_wrap,
1698 .ampersand => .address_of,1661 .ampersand => .address_of,
1699 .keyword_try => .@"try",1662 .keyword_try => .@"try",
1700 .keyword_await => .@"await",
1701 else => return p.parsePrimaryExpr(),1663 else => return p.parsePrimaryExpr(),
1702 };1664 };
1703 return try p.addNode(.{1665 return try p.addNode(.{
...@@ -2385,62 +2347,12 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index {...@@ -2385,62 +2347,12 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index {
2385}2347}
23862348
2387/// SuffixExpr2349/// SuffixExpr
2388/// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments2350/// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
2389/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
2390///2351///
2391/// FnCallArguments <- LPAREN ExprList RPAREN2352/// FnCallArguments <- LPAREN ExprList RPAREN
2392///2353///
2393/// ExprList <- (Expr COMMA)* Expr?2354/// ExprList <- (Expr COMMA)* Expr?
2394fn parseSuffixExpr(p: *Parse) !?Node.Index {2355fn 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
2444 var res = try p.parsePrimaryTypeExpr() orelse return null;2356 var res = try p.parsePrimaryTypeExpr() orelse return null;
2445 while (true) {2357 while (true) {
2446 const opt_suffix_op = try p.parseSuffixOp(res);2358 const opt_suffix_op = try p.parseSuffixOp(res);
lib/std/zig/Zir.zig+4-43
...@@ -899,8 +899,6 @@ pub const Inst = struct {...@@ -899,8 +899,6 @@ pub const Inst = struct {
899 type_name,899 type_name,
900 /// Implement builtin `@Frame`. Uses `un_node`.900 /// Implement builtin `@Frame`. Uses `un_node`.
901 frame_type,901 frame_type,
902 /// Implement builtin `@frameSize`. Uses `un_node`.
903 frame_size,
904902
905 /// Implements the `@intFromFloat` builtin.903 /// Implements the `@intFromFloat` builtin.
906 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.904 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
...@@ -1044,7 +1042,6 @@ pub const Inst = struct {...@@ -1044,7 +1042,6 @@ pub const Inst = struct {
10441042
1045 /// Implements `resume` syntax. Uses `un_node` field.1043 /// Implements `resume` syntax. Uses `un_node` field.
1046 @"resume",1044 @"resume",
1047 @"await",
10481045
1049 /// A defer statement.1046 /// A defer statement.
1050 /// Uses the `defer` union field.1047 /// Uses the `defer` union field.
...@@ -1241,7 +1238,6 @@ pub const Inst = struct {...@@ -1241,7 +1238,6 @@ pub const Inst = struct {
1241 .tag_name,1238 .tag_name,
1242 .type_name,1239 .type_name,
1243 .frame_type,1240 .frame_type,
1244 .frame_size,
1245 .int_from_float,1241 .int_from_float,
1246 .float_from_int,1242 .float_from_int,
1247 .ptr_from_int,1243 .ptr_from_int,
...@@ -1279,7 +1275,6 @@ pub const Inst = struct {...@@ -1279,7 +1275,6 @@ pub const Inst = struct {
1279 .min,1275 .min,
1280 .c_import,1276 .c_import,
1281 .@"resume",1277 .@"resume",
1282 .@"await",
1283 .ret_err_value_code,1278 .ret_err_value_code,
1284 .extended,1279 .extended,
1285 .ret_ptr,1280 .ret_ptr,
...@@ -1526,7 +1521,6 @@ pub const Inst = struct {...@@ -1526,7 +1521,6 @@ pub const Inst = struct {
1526 .tag_name,1521 .tag_name,
1527 .type_name,1522 .type_name,
1528 .frame_type,1523 .frame_type,
1529 .frame_size,
1530 .int_from_float,1524 .int_from_float,
1531 .float_from_int,1525 .float_from_int,
1532 .ptr_from_int,1526 .ptr_from_int,
...@@ -1560,7 +1554,6 @@ pub const Inst = struct {...@@ -1560,7 +1554,6 @@ pub const Inst = struct {
1560 .min,1554 .min,
1561 .c_import,1555 .c_import,
1562 .@"resume",1556 .@"resume",
1563 .@"await",
1564 .ret_err_value_code,1557 .ret_err_value_code,
1565 .@"break",1558 .@"break",
1566 .break_inline,1559 .break_inline,
...@@ -1791,7 +1784,6 @@ pub const Inst = struct {...@@ -1791,7 +1784,6 @@ pub const Inst = struct {
1791 .tag_name = .un_node,1784 .tag_name = .un_node,
1792 .type_name = .un_node,1785 .type_name = .un_node,
1793 .frame_type = .un_node,1786 .frame_type = .un_node,
1794 .frame_size = .un_node,
17951787
1796 .int_from_float = .pl_node,1788 .int_from_float = .pl_node,
1797 .float_from_int = .pl_node,1789 .float_from_int = .pl_node,
...@@ -1852,7 +1844,6 @@ pub const Inst = struct {...@@ -1852,7 +1844,6 @@ pub const Inst = struct {
1852 .make_ptr_const = .un_node,1844 .make_ptr_const = .un_node,
18531845
1854 .@"resume" = .un_node,1846 .@"resume" = .un_node,
1855 .@"await" = .un_node,
18561847
1857 .@"defer" = .@"defer",1848 .@"defer" = .@"defer",
1858 .defer_err_code = .defer_err_code,1849 .defer_err_code = .defer_err_code,
...@@ -2016,8 +2007,6 @@ pub const Inst = struct {...@@ -2016,8 +2007,6 @@ pub const Inst = struct {
2016 /// Implements the `@errorCast` builtin.2007 /// Implements the `@errorCast` builtin.
2017 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.2008 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
2018 error_cast,2009 error_cast,
2019 /// `operand` is payload index to `UnNode`.
2020 await_nosuspend,
2021 /// Implements `@breakpoint`.2010 /// Implements `@breakpoint`.
2022 /// `operand` is `src_node: Ast.Node.Offset`.2011 /// `operand` is `src_node: Ast.Node.Offset`.
2023 breakpoint,2012 breakpoint,
...@@ -2038,9 +2027,6 @@ pub const Inst = struct {...@@ -2038,9 +2027,6 @@ pub const Inst = struct {
2038 /// `operand` is payload index to `Reify`.2027 /// `operand` is payload index to `Reify`.
2039 /// `small` contains `NameStrategy`.2028 /// `small` contains `NameStrategy`.
2040 reify,2029 reify,
2041 /// Implements the `@asyncCall` builtin.
2042 /// `operand` is payload index to `AsyncCall`.
2043 builtin_async_call,
2044 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.2030 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
2045 /// `small` 0=>weak 1=>strong2031 /// `small` 0=>weak 1=>strong
2046 /// `operand` is payload index to `Cmpxchg`.2032 /// `operand` is payload index to `Cmpxchg`.
...@@ -2689,7 +2675,6 @@ pub const Inst = struct {...@@ -2689,7 +2675,6 @@ pub const Inst = struct {
2689 @"test",2675 @"test",
2690 decltest,2676 decltest,
2691 @"comptime",2677 @"comptime",
2692 @"usingnamespace",
2693 @"const",2678 @"const",
2694 @"var",2679 @"var",
2695 };2680 };
...@@ -2706,7 +2691,7 @@ pub const Inst = struct {...@@ -2706,7 +2691,7 @@ pub const Inst = struct {
2706 src_column: u32,2691 src_column: u32,
27072692
2708 kind: Kind,2693 kind: Kind,
2709 /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`.2694 /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`
2710 name: NullTerminatedString,2695 name: NullTerminatedString,
2711 /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`.2696 /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`.
2712 is_pub: bool,2697 is_pub: bool,
...@@ -2737,9 +2722,6 @@ pub const Inst = struct {...@@ -2737,9 +2722,6 @@ pub const Inst = struct {
2737 decltest,2722 decltest,
2738 @"comptime",2723 @"comptime",
27392724
2740 @"usingnamespace",
2741 pub_usingnamespace,
2742
2743 const_simple,2725 const_simple,
2744 const_typed,2726 const_typed,
2745 @"const",2727 @"const",
...@@ -2776,8 +2758,6 @@ pub const Inst = struct {...@@ -2776,8 +2758,6 @@ pub const Inst = struct {
2776 return switch (id) {2758 return switch (id) {
2777 .unnamed_test,2759 .unnamed_test,
2778 .@"comptime",2760 .@"comptime",
2779 .@"usingnamespace",
2780 .pub_usingnamespace,
2781 => false,2761 => false,
2782 else => true,2762 else => true,
2783 };2763 };
...@@ -2802,8 +2782,6 @@ pub const Inst = struct {...@@ -2802,8 +2782,6 @@ pub const Inst = struct {
2802 .@"test",2782 .@"test",
2803 .decltest,2783 .decltest,
2804 .@"comptime",2784 .@"comptime",
2805 .@"usingnamespace",
2806 .pub_usingnamespace,
2807 => false, // these constructs are untyped2785 => false, // these constructs are untyped
2808 .const_simple,2786 .const_simple,
2809 .pub_const_simple,2787 .pub_const_simple,
...@@ -2835,8 +2813,6 @@ pub const Inst = struct {...@@ -2835,8 +2813,6 @@ pub const Inst = struct {
2835 .@"test",2813 .@"test",
2836 .decltest,2814 .decltest,
2837 .@"comptime",2815 .@"comptime",
2838 .@"usingnamespace",
2839 .pub_usingnamespace,
2840 => false, // these constructs are untyped2816 => false, // these constructs are untyped
2841 .const_simple,2817 .const_simple,
2842 .const_typed,2818 .const_typed,
...@@ -2879,7 +2855,6 @@ pub const Inst = struct {...@@ -2879,7 +2855,6 @@ pub const Inst = struct {
2879 .@"test" => .@"test",2855 .@"test" => .@"test",
2880 .decltest => .decltest,2856 .decltest => .decltest,
2881 .@"comptime" => .@"comptime",2857 .@"comptime" => .@"comptime",
2882 .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace",
2883 .const_simple,2858 .const_simple,
2884 .const_typed,2859 .const_typed,
2885 .@"const",2860 .@"const",
...@@ -2913,7 +2888,6 @@ pub const Inst = struct {...@@ -2913,7 +2888,6 @@ pub const Inst = struct {
29132888
2914 pub fn isPub(id: Id) bool {2889 pub fn isPub(id: Id) bool {
2915 return switch (id) {2890 return switch (id) {
2916 .pub_usingnamespace,
2917 .pub_const_simple,2891 .pub_const_simple,
2918 .pub_const_typed,2892 .pub_const_typed,
2919 .pub_const,2893 .pub_const,
...@@ -2949,8 +2923,7 @@ pub const Inst = struct {...@@ -2949,8 +2923,7 @@ pub const Inst = struct {
29492923
2950 pub const Name = enum(u32) {2924 pub const Name = enum(u32) {
2951 @"comptime" = std.math.maxInt(u32),2925 @"comptime" = std.math.maxInt(u32),
2952 @"usingnamespace" = std.math.maxInt(u32) - 1,2926 unnamed_test = std.math.maxInt(u32) - 1,
2953 unnamed_test = std.math.maxInt(u32) - 2,
2954 /// Other values are `NullTerminatedString` values, i.e. index into2927 /// Other values are `NullTerminatedString` values, i.e. index into
2955 /// `string_bytes`. If the byte referenced is 0, the decl is a named2928 /// `string_bytes`. If the byte referenced is 0, the decl is a named
2956 /// test, and the actual name begins at the following byte.2929 /// test, and the actual name begins at the following byte.
...@@ -2958,13 +2931,13 @@ pub const Inst = struct {...@@ -2958,13 +2931,13 @@ pub const Inst = struct {
29582931
2959 pub fn isNamedTest(name: Name, zir: Zir) bool {2932 pub fn isNamedTest(name: Name, zir: Zir) bool {
2960 return switch (name) {2933 return switch (name) {
2961 .@"comptime", .@"usingnamespace", .unnamed_test => false,2934 .@"comptime", .unnamed_test => false,
2962 _ => zir.string_bytes[@intFromEnum(name)] == 0,2935 _ => zir.string_bytes[@intFromEnum(name)] == 0,
2963 };2936 };
2964 }2937 }
2965 pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {2938 pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {
2966 switch (name) {2939 switch (name) {
2967 .@"comptime", .@"usingnamespace", .unnamed_test => return null,2940 .@"comptime", .unnamed_test => return null,
2968 _ => {},2941 _ => {},
2969 }2942 }
2970 const idx: u32 = @intFromEnum(name);2943 const idx: u32 = @intFromEnum(name);
...@@ -3771,14 +3744,6 @@ pub const Inst = struct {...@@ -3771,14 +3744,6 @@ pub const Inst = struct {
3771 b: Ref,3744 b: Ref,
3772 };3745 };
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
3782 /// Trailing: inst: Index // for every body_len3747 /// Trailing: inst: Index // for every body_len
3783 pub const Param = struct {3748 pub const Param = struct {
3784 /// Null-terminated string index.3749 /// Null-terminated string index.
...@@ -4297,7 +4262,6 @@ fn findTrackableInner(...@@ -4297,7 +4262,6 @@ fn findTrackableInner(
4297 .tag_name,4262 .tag_name,
4298 .type_name,4263 .type_name,
4299 .frame_type,4264 .frame_type,
4300 .frame_size,
4301 .int_from_float,4265 .int_from_float,
4302 .float_from_int,4266 .float_from_int,
4303 .ptr_from_int,4267 .ptr_from_int,
...@@ -4337,7 +4301,6 @@ fn findTrackableInner(...@@ -4337,7 +4301,6 @@ fn findTrackableInner(
4337 .resolve_inferred_alloc,4301 .resolve_inferred_alloc,
4338 .make_ptr_const,4302 .make_ptr_const,
4339 .@"resume",4303 .@"resume",
4340 .@"await",
4341 .save_err_ret_index,4304 .save_err_ret_index,
4342 .restore_err_ret_index_unconditional,4305 .restore_err_ret_index_unconditional,
4343 .restore_err_ret_index_fn_entry,4306 .restore_err_ret_index_fn_entry,
...@@ -4380,14 +4343,12 @@ fn findTrackableInner(...@@ -4380,14 +4343,12 @@ fn findTrackableInner(
4380 .prefetch,4343 .prefetch,
4381 .set_float_mode,4344 .set_float_mode,
4382 .error_cast,4345 .error_cast,
4383 .await_nosuspend,
4384 .breakpoint,4346 .breakpoint,
4385 .disable_instrumentation,4347 .disable_instrumentation,
4386 .disable_intrinsics,4348 .disable_intrinsics,
4387 .select,4349 .select,
4388 .int_from_error,4350 .int_from_error,
4389 .error_from_int,4351 .error_from_int,
4390 .builtin_async_call,
4391 .cmpxchg,4352 .cmpxchg,
4392 .c_va_arg,4353 .c_va_arg,
4393 .c_va_copy,4354 .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...@@ -100,7 +100,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
100100
101 switch (tree.nodeTag(node)) {101 switch (tree.nodeTag(node)) {
102 .root => unreachable,102 .root => unreachable,
103 .@"usingnamespace" => unreachable,
104 .test_decl => unreachable,103 .test_decl => unreachable,
105 .container_field_init => unreachable,104 .container_field_init => unreachable,
106 .container_field_align => unreachable,105 .container_field_align => unreachable,
...@@ -204,12 +203,8 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator...@@ -204,12 +203,8 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
204203
205 .call_one,204 .call_one,
206 .call_one_comma,205 .call_one_comma,
207 .async_call_one,
208 .async_call_one_comma,
209 .call,206 .call,
210 .call_comma,207 .call_comma,
211 .async_call,
212 .async_call_comma,
213 .@"return",208 .@"return",
214 .if_simple,209 .if_simple,
215 .@"if",210 .@"if",
...@@ -226,7 +221,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator...@@ -226,7 +221,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
226 .switch_comma,221 .switch_comma,
227 .@"nosuspend",222 .@"nosuspend",
228 .@"suspend",223 .@"suspend",
229 .@"await",
230 .@"resume",224 .@"resume",
231 .@"try",225 .@"try",
232 .unreachable_literal,226 .unreachable_literal,
...@@ -776,13 +770,7 @@ fn lowerStrLitError(...@@ -776,13 +770,7 @@ fn lowerStrLitError(
776 raw_string: []const u8,770 raw_string: []const u8,
777 offset: u32,771 offset: u32,
778) Allocator.Error!void {772) Allocator.Error!void {
779 return ZonGen.addErrorTokOff(773 return ZonGen.addErrorTokOff(zg, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)});
780 zg,
781 token,
782 @intCast(offset + err.offset()),
783 "{f}",
784 .{err.fmt(raw_string)},
785 );
786}774}
787775
788fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void {776fn 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 @@...@@ -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
1gpa: Allocator,12gpa: Allocator,
2strip: bool,13strip: bool,
314
...@@ -90,26 +101,38 @@ pub const String = enum(u32) {...@@ -90,26 +101,38 @@ pub const String = enum(u32) {
90 const FormatData = struct {101 const FormatData = struct {
91 string: String,102 string: String,
92 builder: *const Builder,103 builder: *const Builder,
104 quote_behavior: ?QuoteBehavior,
93 };105 };
94 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {106 fn format(data: FormatData, w: *Writer) Writer.Error!void {
95 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
96 @compileError("invalid format string: '" ++ fmt_str ++ "'");
97 assert(data.string != .none);107 assert(data.string != .none);
98 const string_slice = data.string.slice(data.builder) orelse108 const string_slice = data.string.slice(data.builder) orelse
99 return bw.print("{d}", .{@intFromEnum(data.string)});109 return w.print("{d}", .{@intFromEnum(data.string)});
100 if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_|110 const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice);
101 return bw.writeAll(string_slice);111 return printEscapedString(string_slice, quote_behavior, w);
102 try printEscapedString(112 }
103 string_slice,113
104 if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_|114 pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) {
105 .always_quote115 return .{ .data = .{
106 else116 .string = self,
107 .quote_unless_valid_identifier,117 .builder = builder,
108 bw,118 .quote_behavior = .quote_unless_valid_identifier,
109 );119 } };
110 }120 }
111 pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) {121
112 return .{ .data = .{ .string = self, .builder = builder } };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 } };
113 }136 }
114137
115 fn fromIndex(index: ?usize) String {138 fn fromIndex(index: ?usize) String {
...@@ -223,7 +246,7 @@ pub const Type = enum(u32) {...@@ -223,7 +246,7 @@ pub const Type = enum(u32) {
223 _,246 _,
224247
225 pub const ptr_amdgpu_constant =248 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
228 pub const Tag = enum(u4) {251 pub const Tag = enum(u4) {
229 simple,252 simple,
...@@ -648,13 +671,16 @@ pub const Type = enum(u32) {...@@ -648,13 +671,16 @@ pub const Type = enum(u32) {
648 const FormatData = struct {671 const FormatData = struct {
649 type: Type,672 type: Type,
650 builder: *const Builder,673 builder: *const Builder,
674 mode: Mode,
675
676 const Mode = enum { default, m, lt, gt, percent };
651 };677 };
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 {
653 assert(data.type != .none);679 assert(data.type != .none);
654 if (comptime std.mem.eql(u8, fmt_str, "m")) {680 if (data.mode == .m) {
655 const item = data.builder.type_items.items[@intFromEnum(data.type)];681 const item = data.builder.type_items.items[@intFromEnum(data.type)];
656 switch (item.tag) {682 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))) {
658 .void => "isVoid",684 .void => "isVoid",
659 .half => "f16",685 .half => "f16",
660 .bfloat => "bf16",686 .bfloat => "bf16",
...@@ -671,36 +697,36 @@ pub const Type = enum(u32) {...@@ -671,36 +697,36 @@ pub const Type = enum(u32) {
671 .function, .vararg_function => |kind| {697 .function, .vararg_function => |kind| {
672 var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);698 var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);
673 const params = extra.trail.next(extra.data.params_len, Type, data.builder);699 const params = extra.trail.next(extra.data.params_len, Type, data.builder);
674 try bw.print("f_{fm}", .{extra.data.ret.fmt(data.builder)});700 try w.print("f_{f}", .{extra.data.ret.fmt(data.builder, .m)});
675 for (params) |param| try bw.print("{fm}", .{param.fmt(data.builder)});701 for (params) |param| try w.print("{f}", .{param.fmt(data.builder, .m)});
676 switch (kind) {702 switch (kind) {
677 .function => {},703 .function => {},
678 .vararg_function => try bw.writeAll("vararg"),704 .vararg_function => try w.writeAll("vararg"),
679 else => unreachable,705 else => unreachable,
680 }706 }
681 try bw.writeByte('f');707 try w.writeByte('f');
682 },708 },
683 .integer => try bw.print("i{d}", .{item.data}),709 .integer => try w.print("i{d}", .{item.data}),
684 .pointer => try bw.print("p{d}", .{item.data}),710 .pointer => try w.print("p{d}", .{item.data}),
685 .target => {711 .target => {
686 var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);712 var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
687 const types = extra.trail.next(extra.data.types_len, Type, data.builder);713 const types = extra.trail.next(extra.data.types_len, Type, data.builder);
688 const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);714 const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
689 try bw.print("t{s}", .{extra.data.name.slice(data.builder).?});715 try w.print("t{s}", .{extra.data.name.slice(data.builder).?});
690 for (types) |ty| try bw.print("_{fm}", .{ty.fmt(data.builder)});716 for (types) |ty| try w.print("_{f}", .{ty.fmt(data.builder, .m)});
691 for (ints) |int| try bw.print("_{d}", .{int});717 for (ints) |int| try w.print("_{d}", .{int});
692 try bw.writeByte('t');718 try w.writeByte('t');
693 },719 },
694 .vector, .scalable_vector => |kind| {720 .vector, .scalable_vector => |kind| {
695 const extra = data.builder.typeExtraData(Type.Vector, item.data);721 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}", .{
697 switch (kind) {723 switch (kind) {
698 .vector => "",724 .vector => "",
699 .scalable_vector => "nx",725 .scalable_vector => "nx",
700 else => unreachable,726 else => unreachable,
701 },727 },
702 extra.len,728 extra.len,
703 extra.child.fmt(data.builder),729 extra.child.fmt(data.builder, .m),
704 });730 });
705 },731 },
706 inline .small_array, .array => |kind| {732 inline .small_array, .array => |kind| {
...@@ -709,72 +735,72 @@ pub const Type = enum(u32) {...@@ -709,72 +735,72 @@ pub const Type = enum(u32) {
709 .array => Type.Array,735 .array => Type.Array,
710 else => unreachable,736 else => unreachable,
711 }, item.data);737 }, 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) });
713 },739 },
714 .structure, .packed_structure => {740 .structure, .packed_structure => {
715 var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);741 var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);
716 const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);742 const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);
717 try bw.writeAll("sl_");743 try w.writeAll("sl_");
718 for (fields) |field| try bw.print("{fm}", .{field.fmt(data.builder)});744 for (fields) |field| try w.print("{f}", .{field.fmt(data.builder, .m)});
719 try bw.writeByte('s');745 try w.writeByte('s');
720 },746 },
721 .named_structure => {747 .named_structure => {
722 const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);748 const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);
723 try bw.writeAll("s_");749 try w.writeAll("s_");
724 if (extra.id.slice(data.builder)) |id| try bw.writeAll(id);750 if (extra.id.slice(data.builder)) |id| try w.writeAll(id);
725 },751 },
726 }752 }
727 return;753 return;
728 }754 }
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);
730 const item = data.builder.type_items.items[@intFromEnum(data.type)];756 const item = data.builder.type_items.items[@intFromEnum(data.type)];
731 switch (item.tag) {757 switch (item.tag) {
732 .simple => unreachable,758 .simple => unreachable,
733 .function, .vararg_function => |kind| {759 .function, .vararg_function => |kind| {
734 var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);760 var extra = data.builder.typeExtraDataTrail(Type.Function, item.data);
735 const params = extra.trail.next(extra.data.params_len, Type, data.builder);761 const params = extra.trail.next(extra.data.params_len, Type, data.builder);
736 if (!comptime std.mem.eql(u8, fmt_str, ">"))762 if (data.mode != .gt)
737 try bw.print("{f%} ", .{extra.data.ret.fmt(data.builder)});763 try w.print("{f} ", .{extra.data.ret.fmt(data.builder, .percent)});
738 if (!comptime std.mem.eql(u8, fmt_str, "<")) {764 if (data.mode != .lt) {
739 try bw.writeByte('(');765 try w.writeByte('(');
740 for (params, 0..) |param, index| {766 for (params, 0..) |param, index| {
741 if (index > 0) try bw.writeAll(", ");767 if (index > 0) try w.writeAll(", ");
742 try bw.print("{f%}", .{param.fmt(data.builder)});768 try w.print("{f}", .{param.fmt(data.builder, .percent)});
743 }769 }
744 switch (kind) {770 switch (kind) {
745 .function => {},771 .function => {},
746 .vararg_function => {772 .vararg_function => {
747 if (params.len > 0) try bw.writeAll(", ");773 if (params.len > 0) try w.writeAll(", ");
748 try bw.writeAll("...");774 try w.writeAll("...");
749 },775 },
750 else => unreachable,776 else => unreachable,
751 }777 }
752 try bw.writeByte(')');778 try w.writeByte(')');
753 }779 }
754 },780 },
755 .integer => try bw.print("i{d}", .{item.data}),781 .integer => try w.print("i{d}", .{item.data}),
756 .pointer => try bw.print("ptr{f }", .{@as(AddrSpace, @enumFromInt(item.data))}),782 .pointer => try w.print("ptr{f}", .{@as(AddrSpace, @enumFromInt(item.data)).fmt(" ")}),
757 .target => {783 .target => {
758 var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);784 var extra = data.builder.typeExtraDataTrail(Type.Target, item.data);
759 const types = extra.trail.next(extra.data.types_len, Type, data.builder);785 const types = extra.trail.next(extra.data.types_len, Type, data.builder);
760 const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);786 const ints = extra.trail.next(extra.data.ints_len, u32, data.builder);
761 try bw.print(787 try w.print(
762 \\target({f"}788 \\target({f}
763 , .{extra.data.name.fmt(data.builder)});789 , .{extra.data.name.fmtQ(data.builder)});
764 for (types) |ty| try bw.print(", {f%}", .{ty.fmt(data.builder)});790 for (types) |ty| try w.print(", {f}", .{ty.fmt(data.builder, .percent)});
765 for (ints) |int| try bw.print(", {d}", .{int});791 for (ints) |int| try w.print(", {d}", .{int});
766 try bw.writeByte(')');792 try w.writeByte(')');
767 },793 },
768 .vector, .scalable_vector => |kind| {794 .vector, .scalable_vector => |kind| {
769 const extra = data.builder.typeExtraData(Type.Vector, item.data);795 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}>", .{
771 switch (kind) {797 switch (kind) {
772 .vector => "",798 .vector => "",
773 .scalable_vector => "vscale x ",799 .scalable_vector => "vscale x ",
774 else => unreachable,800 else => unreachable,
775 },801 },
776 extra.len,802 extra.len,
777 extra.child.fmt(data.builder),803 extra.child.fmt(data.builder, .percent),
778 });804 });
779 },805 },
780 inline .small_array, .array => |kind| {806 inline .small_array, .array => |kind| {
...@@ -783,44 +809,45 @@ pub const Type = enum(u32) {...@@ -783,44 +809,45 @@ pub const Type = enum(u32) {
783 .array => Type.Array,809 .array => Type.Array,
784 else => unreachable,810 else => unreachable,
785 }, item.data);811 }, 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) });
787 },813 },
788 .structure, .packed_structure => |kind| {814 .structure, .packed_structure => |kind| {
789 var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);815 var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data);
790 const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);816 const fields = extra.trail.next(extra.data.fields_len, Type, data.builder);
791 switch (kind) {817 switch (kind) {
792 .structure => {},818 .structure => {},
793 .packed_structure => try bw.writeByte('<'),819 .packed_structure => try w.writeByte('<'),
794 else => unreachable,820 else => unreachable,
795 }821 }
796 try bw.writeAll("{ ");822 try w.writeAll("{ ");
797 for (fields, 0..) |field, index| {823 for (fields, 0..) |field, index| {
798 if (index > 0) try bw.writeAll(", ");824 if (index > 0) try w.writeAll(", ");
799 try bw.print("{f%}", .{field.fmt(data.builder)});825 try w.print("{f}", .{field.fmt(data.builder, .percent)});
800 }826 }
801 try bw.writeAll(" }");827 try w.writeAll(" }");
802 switch (kind) {828 switch (kind) {
803 .structure => {},829 .structure => {},
804 .packed_structure => try bw.writeByte('>'),830 .packed_structure => try w.writeByte('>'),
805 else => unreachable,831 else => unreachable,
806 }832 }
807 },833 },
808 .named_structure => {834 .named_structure => {
809 const extra = data.builder.typeExtraData(Type.NamedStructure, item.data);835 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}", .{
811 extra.id.fmt(data.builder),837 extra.id.fmt(data.builder),
812 }) else switch (extra.body) {838 }) else switch (extra.body) {
813 .none => try bw.writeAll("opaque"),839 .none => try w.writeAll("opaque"),
814 else => try format(.{840 else => try format(.{
815 .type = extra.body,841 .type = extra.body,
816 .builder = data.builder,842 .builder = data.builder,
817 }, bw, fmt_str),843 .mode = data.mode,
844 }, w),
818 }845 }
819 },846 },
820 }847 }
821 }848 }
822 pub fn fmt(self: Type, builder: *const Builder) std.fmt.Formatter(format) {849 pub fn fmt(self: Type, builder: *const Builder, mode: FormatData.Mode) std.fmt.Formatter(FormatData, format) {
823 return .{ .data = .{ .type = self, .builder = builder } };850 return .{ .data = .{ .type = self, .builder = builder, .mode = mode } };
824 }851 }
825852
826 const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void);853 const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void);
...@@ -1128,10 +1155,13 @@ pub const Attribute = union(Kind) {...@@ -1128,10 +1155,13 @@ pub const Attribute = union(Kind) {
1128 const FormatData = struct {1155 const FormatData = struct {
1129 attribute_index: Index,1156 attribute_index: Index,
1130 builder: *const Builder,1157 builder: *const Builder,
1158 flags: Flags = .{},
1159 const Flags = struct {
1160 pound: bool = false,
1161 quote: bool = false,
1162 };
1131 };1163 };
1132 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {1164 fn format(data: FormatData, w: *Writer) Writer.Error!void {
1133 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_|
1134 @compileError("invalid format string: '" ++ fmt_str ++ "'");
1135 const attribute = data.attribute_index.toAttribute(data.builder);1165 const attribute = data.attribute_index.toAttribute(data.builder);
1136 switch (attribute) {1166 switch (attribute) {
1137 .zeroext,1167 .zeroext,
...@@ -1204,97 +1234,99 @@ pub const Attribute = union(Kind) {...@@ -1204,97 +1234,99 @@ pub const Attribute = union(Kind) {
1204 .no_sanitize_address,1234 .no_sanitize_address,
1205 .no_sanitize_hwaddress,1235 .no_sanitize_hwaddress,
1206 .sanitize_address_dyninit,1236 .sanitize_address_dyninit,
1207 => try bw.print(" {s}", .{@tagName(attribute)}),1237 => try w.print(" {s}", .{@tagName(attribute)}),
1208 .byval,1238 .byval,
1209 .byref,1239 .byref,
1210 .preallocated,1240 .preallocated,
1211 .inalloca,1241 .inalloca,
1212 .sret,1242 .sret,
1213 .elementtype,1243 .elementtype,
1214 => |ty| try bw.print(" {s}({f%})", .{ @tagName(attribute), ty.fmt(data.builder) }),1244 => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }),
1215 .@"align" => |alignment| try bw.print("{f }", .{alignment}),1245 .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}),
1216 .dereferenceable,1246 .dereferenceable,
1217 .dereferenceable_or_null,1247 .dereferenceable_or_null,
1218 => |size| try bw.print(" {s}({d})", .{ @tagName(attribute), size }),1248 => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }),
1219 .nofpclass => |fpclass| {1249 .nofpclass => |fpclass| {
1220 const Int = @typeInfo(FpClass).@"struct".backing_integer.?;1250 const Int = @typeInfo(FpClass).@"struct".backing_integer.?;
1221 try bw.print(" {s}(", .{@tagName(attribute)});1251 try w.print(" {s}(", .{@tagName(attribute)});
1222 var any = false;1252 var any = false;
1223 var remaining: Int = @bitCast(fpclass);1253 var remaining: Int = @bitCast(fpclass);
1224 inline for (@typeInfo(FpClass).@"struct".decls) |decl| {1254 inline for (@typeInfo(FpClass).@"struct".decls) |decl| {
1225 const pattern: Int = @bitCast(@field(FpClass, decl.name));1255 const pattern: Int = @bitCast(@field(FpClass, decl.name));
1226 if (remaining & pattern == pattern) {1256 if (remaining & pattern == pattern) {
1227 if (!any) {1257 if (!any) {
1228 try bw.writeByte(' ');1258 try w.writeByte(' ');
1229 any = true;1259 any = true;
1230 }1260 }
1231 try bw.writeAll(decl.name);1261 try w.writeAll(decl.name);
1232 remaining &= ~pattern;1262 remaining &= ~pattern;
1233 }1263 }
1234 }1264 }
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 }
1236 },1275 },
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 ),
1244 .allockind => |allockind| {1276 .allockind => |allockind| {
1245 try bw.print(" {s}(\"", .{@tagName(attribute)});1277 try w.print(" {t}(\"", .{attribute});
1246 var any = false;1278 var any = false;
1247 inline for (@typeInfo(AllocKind).@"struct".fields) |field| {1279 inline for (@typeInfo(AllocKind).@"struct".fields) |field| {
1248 if (comptime std.mem.eql(u8, field.name, "_")) continue;1280 if (comptime std.mem.eql(u8, field.name, "_")) continue;
1249 if (@field(allockind, field.name)) {1281 if (@field(allockind, field.name)) {
1250 if (!any) {1282 if (!any) {
1251 try bw.writeByte(',');1283 try w.writeByte(',');
1252 any = true;1284 any = true;
1253 }1285 }
1254 try bw.writeAll(field.name);1286 try w.writeAll(field.name);
1255 }1287 }
1256 }1288 }
1257 try bw.writeAll("\")");1289 try w.writeAll("\")");
1258 },1290 },
1259 .allocsize => |allocsize| {1291 .allocsize => |allocsize| {
1260 try bw.print(" {s}({d}", .{ @tagName(attribute), allocsize.elem_size });1292 try w.print(" {t}({d}", .{ attribute, allocsize.elem_size });
1261 if (allocsize.num_elems != AllocSize.none)1293 if (allocsize.num_elems != AllocSize.none)
1262 try bw.print(",{d}", .{allocsize.num_elems});1294 try w.print(",{d}", .{allocsize.num_elems});
1263 try bw.writeByte(')');1295 try w.writeByte(')');
1264 },1296 },
1265 .memory => |memory| {1297 .memory => |memory| {
1266 try bw.print(" {s}(", .{@tagName(attribute)});1298 try w.print(" {t}(", .{attribute});
1267 var any = memory.other != .none or1299 var any = memory.other != .none or
1268 (memory.argmem == .none and memory.inaccessiblemem == .none);1300 (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));
1270 inline for (.{ "argmem", "inaccessiblemem" }) |kind| {1302 inline for (.{ "argmem", "inaccessiblemem" }) |kind| {
1271 if (@field(memory, kind) != memory.other) {1303 if (@field(memory, kind) != memory.other) {
1272 if (any) try bw.writeAll(", ");1304 if (any) try w.writeAll(", ");
1273 try bw.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) });1305 try w.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) });
1274 any = true;1306 any = true;
1275 }1307 }
1276 }1308 }
1277 try bw.writeByte(')');1309 try w.writeByte(')');
1278 },1310 },
1279 .uwtable => |uwtable| if (uwtable != .none) {1311 .uwtable => |uwtable| if (uwtable != .none) {
1280 try bw.print(" {s}", .{@tagName(attribute)});1312 try w.print(" {s}", .{@tagName(attribute)});
1281 if (uwtable != UwTable.default) try bw.print("({s})", .{@tagName(uwtable)});1313 if (uwtable != UwTable.default) try w.print("({s})", .{@tagName(uwtable)});
1282 },1314 },
1283 .vscale_range => |vscale_range| try bw.print(" {s}({d},{d})", .{1315 .vscale_range => |vscale_range| try w.print(" {s}({d},{d})", .{
1284 @tagName(attribute),1316 @tagName(attribute),
1285 vscale_range.min.toByteUnits().?,1317 vscale_range.min.toByteUnits().?,
1286 vscale_range.max.toByteUnits() orelse 0,1318 vscale_range.max.toByteUnits() orelse 0,
1287 }),1319 }),
1288 .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) {1320 .string => |string_attr| if (data.flags.quote) {
1289 try bw.print(" {f\"}", .{string_attr.kind.fmt(data.builder)});1321 try w.print(" {f}", .{string_attr.kind.fmtQ(data.builder)});
1290 if (string_attr.value != .empty)1322 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)});
1292 },1324 },
1293 .none => unreachable,1325 .none => unreachable,
1294 }1326 }
1295 }1327 }
1296 pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) {1328 pub fn fmt(self: Index, builder: *const Builder, mode: FormatData.mode) std.fmt.Formatter(FormatData, format) {
1297 return .{ .data = .{ .attribute_index = self, .builder = builder } };1329 return .{ .data = .{ .attribute_index = self, .builder = builder, .mode = mode } };
1298 }1330 }
12991331
1300 fn toStorage(self: Index, builder: *const Builder) Storage {1332 fn toStorage(self: Index, builder: *const Builder) Storage {
...@@ -1506,9 +1538,9 @@ pub const Attribute = union(Kind) {...@@ -1506,9 +1538,9 @@ pub const Attribute = union(Kind) {
1506 pub const UwTable = enum(u32) {1538 pub const UwTable = enum(u32) {
1507 none,1539 none,
1508 sync,1540 sync,
1509 @"async",1541 async,
15101542
1511 pub const default = UwTable.@"async";1543 pub const default = UwTable.async;
1512 };1544 };
15131545
1514 pub const VScaleRange = packed struct(u32) {1546 pub const VScaleRange = packed struct(u32) {
...@@ -1567,15 +1599,18 @@ pub const Attributes = enum(u32) {...@@ -1567,15 +1599,18 @@ pub const Attributes = enum(u32) {
1567 const FormatData = struct {1599 const FormatData = struct {
1568 attributes: Attributes,1600 attributes: Attributes,
1569 builder: *const Builder,1601 builder: *const Builder,
1602 flags: Flags = .{},
1603 const Flags = Attribute.Index.FormatData.Flags;
1570 };1604 };
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 {
1572 for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{1606 for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{
1573 .attribute_index = attribute_index,1607 .attribute_index = attribute_index,
1574 .builder = data.builder,1608 .builder = data.builder,
1575 }, bw, fmt_str);1609 .flags = data.flags,
1610 }, w);
1576 }1611 }
1577 pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) {1612 pub fn fmt(self: Attributes, builder: *const Builder, flags: FormatData.Flags) std.fmt.Formatter(FormatData, format) {
1578 return .{ .data = .{ .attributes = self, .builder = builder } };1613 return .{ .data = .{ .attributes = self, .builder = builder, .flags = flags } };
1579 }1614 }
1580};1615};
15811616
...@@ -1761,14 +1796,14 @@ pub const Linkage = enum(u4) {...@@ -1761,14 +1796,14 @@ pub const Linkage = enum(u4) {
1761 extern_weak = 7,1796 extern_weak = 7,
1762 external = 0,1797 external = 0,
17631798
1764 pub fn format(self: Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void {1799 pub fn format(self: Linkage, w: *Writer) Writer.Error!void {
1765 if (self != .external) try bw.print(" {s}", .{@tagName(self)});1800 if (self != .external) try w.print(" {s}", .{@tagName(self)});
1766 }1801 }
17671802
1768 fn formatOptional(data: ?Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void {1803 fn formatOptional(data: ?Linkage, w: *Writer) Writer.Error!void {
1769 if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)});1804 if (data) |linkage| try w.print(" {s}", .{@tagName(linkage)});
1770 }1805 }
1771 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) {1806 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(?Linkage, formatOptional) {
1772 return .{ .data = self };1807 return .{ .data = self };
1773 }1808 }
1774};1809};
...@@ -1778,8 +1813,8 @@ pub const Preemption = enum {...@@ -1778,8 +1813,8 @@ pub const Preemption = enum {
1778 dso_local,1813 dso_local,
1779 implicit_dso_local,1814 implicit_dso_local,
17801815
1781 pub fn format(self: Preemption, bw: *Writer, comptime _: []const u8) Writer.Error!void {1816 pub fn format(self: Preemption, w: *Writer) Writer.Error!void {
1782 if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)});1817 if (self == .dso_local) try w.print(" {s}", .{@tagName(self)});
1783 }1818 }
1784};1819};
17851820
...@@ -1796,8 +1831,7 @@ pub const Visibility = enum(u2) {...@@ -1796,8 +1831,7 @@ pub const Visibility = enum(u2) {
1796 };1831 };
1797 }1832 }
17981833
1799 pub fn format(self: Visibility, comptime format_string: []const u8, writer: *Writer) Writer.Error!void {1834 pub fn format(self: Visibility, writer: *Writer) Writer.Error!void {
1800 comptime assert(format_string.len == 0);
1801 if (self != .default) try writer.print(" {s}", .{@tagName(self)});1835 if (self != .default) try writer.print(" {s}", .{@tagName(self)});
1802 }1836 }
1803};1837};
...@@ -1807,8 +1841,8 @@ pub const DllStorageClass = enum(u2) {...@@ -1807,8 +1841,8 @@ pub const DllStorageClass = enum(u2) {
1807 dllimport = 1,1841 dllimport = 1,
1808 dllexport = 2,1842 dllexport = 2,
18091843
1810 pub fn format(self: DllStorageClass, bw: *Writer, comptime _: []const u8) Writer.Error!void {1844 pub fn format(self: DllStorageClass, w: *Writer) Writer.Error!void {
1811 if (self != .default) try bw.print(" {s}", .{@tagName(self)});1845 if (self != .default) try w.print(" {s}", .{@tagName(self)});
1812 }1846 }
1813};1847};
18141848
...@@ -1819,10 +1853,31 @@ pub const ThreadLocal = enum(u3) {...@@ -1819,10 +1853,31 @@ pub const ThreadLocal = enum(u3) {
1819 initialexec = 3,1853 initialexec = 3,
1820 localexec = 4,1854 localexec = 4,
18211855
1822 pub fn format(self: ThreadLocal, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {1856 pub fn format(tl: ThreadLocal, w: *Writer) Writer.Error!void {
1823 if (self == .default) return;1857 return Prefixed.format(.{ .thread_local = tl, .prefix = "" }, w);
1824 try bw.print("{s}thread_local", .{prefix});1858 }
1825 if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)});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 };
1826 }1881 }
1827};1882};
18281883
...@@ -1833,8 +1888,8 @@ pub const UnnamedAddr = enum(u2) {...@@ -1833,8 +1888,8 @@ pub const UnnamedAddr = enum(u2) {
1833 unnamed_addr = 1,1888 unnamed_addr = 1,
1834 local_unnamed_addr = 2,1889 local_unnamed_addr = 2,
18351890
1836 pub fn format(self: UnnamedAddr, bw: *Writer, comptime _: []const u8) Writer.Error!void {1891 pub fn format(self: UnnamedAddr, w: *Writer) Writer.Error!void {
1837 if (self != .default) try bw.print(" {s}", .{@tagName(self)});1892 if (self != .default) try w.print(" {s}", .{@tagName(self)});
1838 }1893 }
1839};1894};
18401895
...@@ -1927,8 +1982,24 @@ pub const AddrSpace = enum(u24) {...@@ -1927,8 +1982,24 @@ pub const AddrSpace = enum(u24) {
1927 pub const funcref: AddrSpace = @enumFromInt(20);1982 pub const funcref: AddrSpace = @enumFromInt(20);
1928 };1983 };
19291984
1930 pub fn format(self: AddrSpace, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {1985 pub fn format(addr_space: AddrSpace, w: *Writer) Writer.Error!void {
1931 if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) });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 };
1932 }2003 }
1933};2004};
19342005
...@@ -1936,8 +2007,8 @@ pub const ExternallyInitialized = enum {...@@ -1936,8 +2007,8 @@ pub const ExternallyInitialized = enum {
1936 default,2007 default,
1937 externally_initialized,2008 externally_initialized,
19382009
1939 pub fn format(self: ExternallyInitialized, bw: *Writer, comptime _: []const u8) Writer.Error!void {2010 pub fn format(self: ExternallyInitialized, w: *Writer) Writer.Error!void {
1940 if (self != .default) try bw.print(" {s}", .{@tagName(self)});2011 if (self != .default) try w.print(" {s}", .{@tagName(self)});
1941 }2012 }
1942};2013};
19432014
...@@ -1960,8 +2031,18 @@ pub const Alignment = enum(u6) {...@@ -1960,8 +2031,18 @@ pub const Alignment = enum(u6) {
1960 return if (self == .default) 0 else (@intFromEnum(self) + 1);2031 return if (self == .default) 0 else (@intFromEnum(self) + 1);
1961 }2032 }
19622033
1963 pub fn format(self: Alignment, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {2034 pub const Prefixed = struct {
1964 try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return });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 };
1965 }2046 }
1966};2047};
19672048
...@@ -2034,7 +2115,7 @@ pub const CallConv = enum(u10) {...@@ -2034,7 +2115,7 @@ pub const CallConv = enum(u10) {
20342115
2035 pub const default = CallConv.ccc;2116 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 {
2038 switch (self) {2119 switch (self) {
2039 default => {},2120 default => {},
2040 .fastcc,2121 .fastcc,
...@@ -2088,8 +2169,8 @@ pub const CallConv = enum(u10) {...@@ -2088,8 +2169,8 @@ pub const CallConv = enum(u10) {
2088 .aarch64_sme_preservemost_from_x2,2169 .aarch64_sme_preservemost_from_x2,
2089 .m68k_rtdcc,2170 .m68k_rtdcc,
2090 .riscv_vectorcallcc,2171 .riscv_vectorcallcc,
2091 => try bw.print(" {s}", .{@tagName(self)}),2172 => try w.print(" {s}", .{@tagName(self)}),
2092 _ => try bw.print(" cc{d}", .{@intFromEnum(self)}),2173 _ => try w.print(" cc{d}", .{@intFromEnum(self)}),
2093 }2174 }
2094 }2175 }
2095};2176};
...@@ -2114,26 +2195,25 @@ pub const StrtabString = enum(u32) {...@@ -2114,26 +2195,25 @@ pub const StrtabString = enum(u32) {
2114 const FormatData = struct {2195 const FormatData = struct {
2115 string: StrtabString,2196 string: StrtabString,
2116 builder: *const Builder,2197 builder: *const Builder,
2198 quote_behavior: ?QuoteBehavior,
2117 };2199 };
2118 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {2200 fn format(data: FormatData, w: *Writer) Writer.Error!void {
2119 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
2120 @compileError("invalid format string: '" ++ fmt_str ++ "'");
2121 assert(data.string != .none);2201 assert(data.string != .none);
2122 const string_slice = data.string.slice(data.builder) orelse2202 const string_slice = data.string.slice(data.builder) orelse
2123 return bw.print("{d}", .{@intFromEnum(data.string)});2203 return w.print("{d}", .{@intFromEnum(data.string)});
2124 if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_|2204 const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice);
2125 return bw.writeAll(string_slice);2205 return printEscapedString(string_slice, quote_behavior, w);
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 );
2134 }2206 }
2135 pub fn fmt(self: StrtabString, builder: *const Builder) std.fmt.Formatter(format) {2207 pub fn fmt(
2136 return .{ .data = .{ .string = self, .builder = builder } };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 } };
2137 }2217 }
21382218
2139 fn fromIndex(index: ?usize) StrtabString {2219 fn fromIndex(index: ?usize) StrtabString {
...@@ -2302,12 +2382,12 @@ pub const Global = struct {...@@ -2302,12 +2382,12 @@ pub const Global = struct {
2302 global: Index,2382 global: Index,
2303 builder: *const Builder,2383 builder: *const Builder,
2304 };2384 };
2305 fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void {2385 fn format(data: FormatData, w: *Writer) Writer.Error!void {
2306 try bw.print("@{f}", .{2386 try w.print("@{f}", .{
2307 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder),2387 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder, null),
2308 });2388 });
2309 }2389 }
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) {
2311 return .{ .data = .{ .global = self, .builder = builder } };2391 return .{ .data = .{ .global = self, .builder = builder } };
2312 }2392 }
23132393
...@@ -4747,24 +4827,23 @@ pub const Function = struct {...@@ -4747,24 +4827,23 @@ pub const Function = struct {
4747 instruction: Instruction.Index,4827 instruction: Instruction.Index,
4748 function: Function.Index,4828 function: Function.Index,
4749 builder: *Builder,4829 builder: *Builder,
4830 flags: FormatFlags,
4750 };4831 };
4751 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {4832 fn format(data: FormatData, w: *Writer) Writer.Error!void {
4752 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|4833 if (data.flags.comma) {
4753 @compileError("invalid format string: '" ++ fmt_str ++ "'");
4754 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
4755 if (data.instruction == .none) return;4834 if (data.instruction == .none) return;
4756 try bw.writeByte(',');4835 try w.writeByte(',');
4757 }4836 }
4758 if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) {4837 if (data.flags.space) {
4759 if (data.instruction == .none) return;4838 if (data.instruction == .none) return;
4760 try bw.writeByte(' ');4839 try w.writeByte(' ');
4761 }4840 }
4762 if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null) try bw.print(4841 if (data.flags.percent) try w.print(
4763 "{f%} ",4842 "{f} ",
4764 .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder)},4843 .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder, .percent)},
4765 );4844 );
4766 assert(data.instruction != .none);4845 assert(data.instruction != .none);
4767 try bw.print("%{f}", .{4846 try w.print("%{f}", .{
4768 data.instruction.name(data.function.ptrConst(data.builder)).fmt(data.builder),4847 data.instruction.name(data.function.ptrConst(data.builder)).fmt(data.builder),
4769 });4848 });
4770 }4849 }
...@@ -4772,8 +4851,14 @@ pub const Function = struct {...@@ -4772,8 +4851,14 @@ pub const Function = struct {
4772 self: Instruction.Index,4851 self: Instruction.Index,
4773 function: Function.Index,4852 function: Function.Index,
4774 builder: *Builder,4853 builder: *Builder,
4775 ) std.fmt.Formatter(format) {4854 flags: FormatFlags,
4776 return .{ .data = .{ .instruction = self, .function = function, .builder = builder } };4855 ) std.fmt.Formatter(FormatData, format) {
4856 return .{ .data = .{
4857 .instruction = self,
4858 .function = function,
4859 .builder = builder,
4860 .flags = flags,
4861 } };
4777 }4862 }
4778 };4863 };
47794864
...@@ -6270,10 +6355,10 @@ pub const WipFunction = struct {...@@ -6270,10 +6355,10 @@ pub const WipFunction = struct {
62706355
6271 while (true) {6356 while (true) {
6272 gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1);6357 gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1);
6273 const unique_name = try wip_name.builder.fmt("{fr}{s}{fr}", .{6358 const unique_name = try wip_name.builder.fmt("{f}{s}{f}", .{
6274 name.fmt(wip_name.builder),6359 name.fmtRaw(wip_name.builder),
6275 sep,6360 sep,
6276 gop.value_ptr.fmt(wip_name.builder),6361 gop.value_ptr.fmtRaw(wip_name.builder),
6277 });6362 });
6278 const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name);6363 const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name);
6279 if (!unique_gop.found_existing) {6364 if (!unique_gop.found_existing) {
...@@ -6940,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) {...@@ -6940,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) {
6940 normal,7025 normal,
6941 @"volatile",7026 @"volatile",
69427027
6943 pub fn format(self: MemoryAccessKind, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {7028 pub fn format(memory_access_kind: MemoryAccessKind, w: *Writer) Writer.Error!void {
6944 if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) });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 };
6945 }7049 }
6946};7050};
69477051
...@@ -6949,10 +7053,27 @@ pub const SyncScope = enum(u1) {...@@ -6949,10 +7053,27 @@ pub const SyncScope = enum(u1) {
6949 singlethread,7053 singlethread,
6950 system,7054 system,
69517055
6952 pub fn format(self: SyncScope, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {7056 pub fn format(sync_scope: SyncScope, w: *Writer) Writer.Error!void {
6953 if (self != .system) try bw.print(7057 return Prefixed.format(.{ .sync_scope = sync_scope, .prefix = "" }, w);
6954 \\{s}syncscope("{s}")7058 }
6955 , .{ prefix, @tagName(self) });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 };
6956 }7077 }
6957};7078};
69587079
...@@ -6965,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) {...@@ -6965,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) {
6965 acq_rel = 5,7086 acq_rel = 5,
6966 seq_cst = 6,7087 seq_cst = 6,
69677088
6968 pub fn format(self: AtomicOrdering, bw: *Writer, comptime prefix: []const u8) Writer.Error!void {7089 pub fn format(atomic_ordering: AtomicOrdering, w: *Writer) Writer.Error!void {
6969 if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) });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 };
6970 }7110 }
6971};7111};
69727112
...@@ -7380,22 +7520,21 @@ pub const Constant = enum(u32) {...@@ -7380,22 +7520,21 @@ pub const Constant = enum(u32) {
7380 const FormatData = struct {7520 const FormatData = struct {
7381 constant: Constant,7521 constant: Constant,
7382 builder: *Builder,7522 builder: *Builder,
7523 flags: FormatFlags,
7383 };7524 };
7384 fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void {7525 fn format(data: FormatData, w: *Writer) Writer.Error!void {
7385 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|7526 if (data.flags.comma) {
7386 @compileError("invalid format string: '" ++ fmt_str ++ "'");
7387 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
7388 if (data.constant == .no_init) return;7527 if (data.constant == .no_init) return;
7389 try bw.writeByte(',');7528 try w.writeByte(',');
7390 }7529 }
7391 if (comptime std.mem.indexOfScalar(u8, fmt_str, ' ') != null) {7530 if (data.flags.space) {
7392 if (data.constant == .no_init) return;7531 if (data.constant == .no_init) return;
7393 try bw.writeByte(' ');7532 try w.writeByte(' ');
7394 }7533 }
7395 if (comptime std.mem.indexOfScalar(u8, fmt_str, '%') != null)7534 if (data.flags.percent)
7396 try bw.print("{f%} ", .{data.constant.typeOf(data.builder).fmt(data.builder)});7535 try w.print("{f} ", .{data.constant.typeOf(data.builder).fmt(data.builder, .percent)});
7397 assert(data.constant != .no_init);7536 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);
7399 switch (data.constant.unwrap()) {7538 switch (data.constant.unwrap()) {
7400 .constant => |constant| {7539 .constant => |constant| {
7401 const item = data.builder.constant_items.get(constant);7540 const item = data.builder.constant_items.get(constant);
...@@ -7432,13 +7571,13 @@ pub const Constant = enum(u32) {...@@ -7432,13 +7571,13 @@ pub const Constant = enum(u32) {
7432 var stack align(@alignOf(ExpectedContents)) =7571 var stack align(@alignOf(ExpectedContents)) =
7433 std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);7572 std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);
7434 const allocator = stack.get();7573 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;
7436 defer allocator.free(str);7575 defer allocator.free(str);
7437 try bw.writeAll(str);7576 try w.writeAll(str);
7438 },7577 },
7439 .half,7578 .half,
7440 .bfloat,7579 .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) {
7442 .half => 'H',7581 .half => 'H',
7443 .bfloat => 'R',7582 .bfloat => 'R',
7444 else => unreachable,7583 else => unreachable,
...@@ -7469,7 +7608,7 @@ pub const Constant = enum(u32) {...@@ -7469,7 +7608,7 @@ pub const Constant = enum(u32) {
7469 ) + 1,7608 ) + 1,
7470 else => 0,7609 else => 0,
7471 };7610 };
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){
7473 .mantissa = std.math.shl(7612 .mantissa = std.math.shl(
7474 Mantissa64,7613 Mantissa64,
7475 repr.mantissa,7614 repr.mantissa,
...@@ -7491,13 +7630,13 @@ pub const Constant = enum(u32) {...@@ -7491,13 +7630,13 @@ pub const Constant = enum(u32) {
7491 },7630 },
7492 .double => {7631 .double => {
7493 const extra = data.builder.constantExtraData(Double, item.data);7632 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 });
7495 },7634 },
7496 .fp128,7635 .fp128,
7497 .ppc_fp128,7636 .ppc_fp128,
7498 => |tag| {7637 => |tag| {
7499 const extra = data.builder.constantExtraData(Fp128, item.data);7638 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}", .{
7501 @as(u8, switch (tag) {7640 @as(u8, switch (tag) {
7502 .fp128 => 'L',7641 .fp128 => 'L',
7503 .ppc_fp128 => 'M',7642 .ppc_fp128 => 'M',
...@@ -7511,7 +7650,7 @@ pub const Constant = enum(u32) {...@@ -7511,7 +7650,7 @@ pub const Constant = enum(u32) {
7511 },7650 },
7512 .x86_fp80 => {7651 .x86_fp80 => {
7513 const extra = data.builder.constantExtraData(Fp80, item.data);7652 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}", .{
7515 extra.hi, extra.lo_hi, extra.lo_lo,7654 extra.hi, extra.lo_hi, extra.lo_lo,
7516 });7655 });
7517 },7656 },
...@@ -7520,7 +7659,7 @@ pub const Constant = enum(u32) {...@@ -7520,7 +7659,7 @@ pub const Constant = enum(u32) {
7520 .zeroinitializer,7659 .zeroinitializer,
7521 .undef,7660 .undef,
7522 .poison,7661 .poison,
7523 => |tag| try bw.writeAll(@tagName(tag)),7662 => |tag| try w.writeAll(@tagName(tag)),
7524 .structure,7663 .structure,
7525 .packed_structure,7664 .packed_structure,
7526 .array,7665 .array,
...@@ -7529,7 +7668,7 @@ pub const Constant = enum(u32) {...@@ -7529,7 +7668,7 @@ pub const Constant = enum(u32) {
7529 var extra = data.builder.constantExtraDataTrail(Aggregate, item.data);7668 var extra = data.builder.constantExtraDataTrail(Aggregate, item.data);
7530 const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder));7669 const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder));
7531 const vals = extra.trail.next(len, Constant, data.builder);7670 const vals = extra.trail.next(len, Constant, data.builder);
7532 try bw.writeAll(switch (tag) {7671 try w.writeAll(switch (tag) {
7533 .structure => "{ ",7672 .structure => "{ ",
7534 .packed_structure => "<{ ",7673 .packed_structure => "<{ ",
7535 .array => "[",7674 .array => "[",
...@@ -7537,10 +7676,10 @@ pub const Constant = enum(u32) {...@@ -7537,10 +7676,10 @@ pub const Constant = enum(u32) {
7537 else => unreachable,7676 else => unreachable,
7538 });7677 });
7539 for (vals, 0..) |val, index| {7678 for (vals, 0..) |val, index| {
7540 if (index > 0) try bw.writeAll(", ");7679 if (index > 0) try w.writeAll(", ");
7541 try bw.print("{f%}", .{val.fmt(data.builder)});7680 try w.print("{f}", .{val.fmt(data.builder, .{ .percent = true })});
7542 }7681 }
7543 try bw.writeAll(switch (tag) {7682 try w.writeAll(switch (tag) {
7544 .structure => " }",7683 .structure => " }",
7545 .packed_structure => " }>",7684 .packed_structure => " }>",
7546 .array => "]",7685 .array => "]",
...@@ -7551,30 +7690,30 @@ pub const Constant = enum(u32) {...@@ -7551,30 +7690,30 @@ pub const Constant = enum(u32) {
7551 .splat => {7690 .splat => {
7552 const extra = data.builder.constantExtraData(Splat, item.data);7691 const extra = data.builder.constantExtraData(Splat, item.data);
7553 const len = extra.type.vectorLen(data.builder);7692 const len = extra.type.vectorLen(data.builder);
7554 try bw.writeByte('<');7693 try w.writeByte('<');
7555 for (0..len) |index| {7694 for (0..len) |index| {
7556 if (index > 0) try bw.writeAll(", ");7695 if (index > 0) try w.writeAll(", ");
7557 try bw.print("{f%}", .{extra.value.fmt(data.builder)});7696 try w.print("{f}", .{extra.value.fmt(data.builder, .{ .percent = true })});
7558 }7697 }
7559 try bw.writeByte('>');7698 try w.writeByte('>');
7560 },7699 },
7561 .string => try bw.print("c{f\"}", .{7700 .string => try w.print("c{f}", .{
7562 @as(String, @enumFromInt(item.data)).fmt(data.builder),7701 @as(String, @enumFromInt(item.data)).fmtQ(data.builder),
7563 }),7702 }),
7564 .blockaddress => |tag| {7703 .blockaddress => |tag| {
7565 const extra = data.builder.constantExtraData(BlockAddress, item.data);7704 const extra = data.builder.constantExtraData(BlockAddress, item.data);
7566 const function = extra.function.ptrConst(data.builder);7705 const function = extra.function.ptrConst(data.builder);
7567 try bw.print("{s}({f}, {f})", .{7706 try w.print("{s}({f}, {f})", .{
7568 @tagName(tag),7707 @tagName(tag),
7569 function.global.fmt(data.builder),7708 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, .{}),
7571 });7710 });
7572 },7711 },
7573 .dso_local_equivalent,7712 .dso_local_equivalent,
7574 .no_cfi,7713 .no_cfi,
7575 => |tag| {7714 => |tag| {
7576 const function: Function.Index = @enumFromInt(item.data);7715 const function: Function.Index = @enumFromInt(item.data);
7577 try bw.print("{s} {f}", .{7716 try w.print("{s} {f}", .{
7578 @tagName(tag),7717 @tagName(tag),
7579 function.ptrConst(data.builder).global.fmt(data.builder),7718 function.ptrConst(data.builder).global.fmt(data.builder),
7580 });7719 });
...@@ -7586,10 +7725,10 @@ pub const Constant = enum(u32) {...@@ -7586,10 +7725,10 @@ pub const Constant = enum(u32) {
7586 .addrspacecast,7725 .addrspacecast,
7587 => |tag| {7726 => |tag| {
7588 const extra = data.builder.constantExtraData(Cast, item.data);7727 const extra = data.builder.constantExtraData(Cast, item.data);
7589 try bw.print("{s} ({f%} to {f%})", .{7728 try w.print("{s} ({f} to {f})", .{
7590 @tagName(tag),7729 @tagName(tag),
7591 extra.val.fmt(data.builder),7730 extra.val.fmt(data.builder, .{ .percent = true }),
7592 extra.type.fmt(data.builder),7731 extra.type.fmt(data.builder, .percent),
7593 });7732 });
7594 },7733 },
7595 .getelementptr,7734 .getelementptr,
...@@ -7598,13 +7737,13 @@ pub const Constant = enum(u32) {...@@ -7598,13 +7737,13 @@ pub const Constant = enum(u32) {
7598 var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data);7737 var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data);
7599 const indices =7738 const indices =
7600 extra.trail.next(extra.data.info.indices_len, Constant, data.builder);7739 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}", .{
7602 @tagName(tag),7741 @tagName(tag),
7603 extra.data.type.fmt(data.builder),7742 extra.data.type.fmt(data.builder, .percent),
7604 extra.data.base.fmt(data.builder),7743 extra.data.base.fmt(data.builder, .{ .percent = true }),
7605 });7744 });
7606 for (indices) |index| try bw.print(", {f%}", .{index.fmt(data.builder)});7745 for (indices) |index| try w.print(", {f}", .{index.fmt(data.builder, .{ .percent = true })});
7607 try bw.writeByte(')');7746 try w.writeByte(')');
7608 },7747 },
7609 .add,7748 .add,
7610 .@"add nsw",7749 .@"add nsw",
...@@ -7616,10 +7755,10 @@ pub const Constant = enum(u32) {...@@ -7616,10 +7755,10 @@ pub const Constant = enum(u32) {
7616 .xor,7755 .xor,
7617 => |tag| {7756 => |tag| {
7618 const extra = data.builder.constantExtraData(Binary, item.data);7757 const extra = data.builder.constantExtraData(Binary, item.data);
7619 try bw.print("{s} ({f%}, {f%})", .{7758 try w.print("{s} ({f}, {f})", .{
7620 @tagName(tag),7759 @tagName(tag),
7621 extra.lhs.fmt(data.builder),7760 extra.lhs.fmt(data.builder, .{ .percent = true }),
7622 extra.rhs.fmt(data.builder),7761 extra.rhs.fmt(data.builder, .{ .percent = true }),
7623 });7762 });
7624 },7763 },
7625 .@"asm",7764 .@"asm",
...@@ -7640,19 +7779,23 @@ pub const Constant = enum(u32) {...@@ -7640,19 +7779,23 @@ pub const Constant = enum(u32) {
7640 .@"asm sideeffect alignstack inteldialect unwind",7779 .@"asm sideeffect alignstack inteldialect unwind",
7641 => |tag| {7780 => |tag| {
7642 const extra = data.builder.constantExtraData(Assembly, item.data);7781 const extra = data.builder.constantExtraData(Assembly, item.data);
7643 try bw.print("{s} {f\"}, {f\"}", .{7782 try w.print("{s} {f}, {f}", .{
7644 @tagName(tag),7783 @tagName(tag),
7645 extra.assembly.fmt(data.builder),7784 extra.assembly.fmtQ(data.builder),
7646 extra.constraints.fmt(data.builder),7785 extra.constraints.fmtQ(data.builder),
7647 });7786 });
7648 },7787 },
7649 }7788 }
7650 },7789 },
7651 .global => |global| try bw.print("{f}", .{global.fmt(data.builder)}),7790 .global => |global| try w.print("{f}", .{global.fmt(data.builder)}),
7652 }7791 }
7653 }7792 }
7654 pub fn fmt(self: Constant, builder: *Builder) std.fmt.Formatter(format) {7793 pub fn fmt(self: Constant, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) {
7655 return .{ .data = .{ .constant = self, .builder = builder } };7794 return .{ .data = .{
7795 .constant = self,
7796 .builder = builder,
7797 .flags = flags,
7798 } };
7656 }7799 }
7657};7800};
76587801
...@@ -7707,23 +7850,26 @@ pub const Value = enum(u32) {...@@ -7707,23 +7850,26 @@ pub const Value = enum(u32) {
7707 value: Value,7850 value: Value,
7708 function: Function.Index,7851 function: Function.Index,
7709 builder: *Builder,7852 builder: *Builder,
7853 flags: FormatFlags,
7710 };7854 };
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 {
7712 switch (data.value.unwrap()) {7856 switch (data.value.unwrap()) {
7713 .instruction => |instruction| try Function.Instruction.Index.format(.{7857 .instruction => |instruction| try Function.Instruction.Index.format(.{
7714 .instruction = instruction,7858 .instruction = instruction,
7715 .function = data.function,7859 .function = data.function,
7716 .builder = data.builder,7860 .builder = data.builder,
7717 }, bw, fmt_str),7861 .flags = data.flags,
7862 }, w),
7718 .constant => |constant| try Constant.format(.{7863 .constant => |constant| try Constant.format(.{
7719 .constant = constant,7864 .constant = constant,
7720 .builder = data.builder,7865 .builder = data.builder,
7721 }, bw, fmt_str),7866 .flags = data.flags,
7867 }, w),
7722 .metadata => unreachable,7868 .metadata => unreachable,
7723 }7869 }
7724 }7870 }
7725 pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) {7871 pub fn fmt(self: Value, function: Function.Index, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) {
7726 return .{ .data = .{ .value = self, .function = function, .builder = builder } };7872 return .{ .data = .{ .value = self, .function = function, .builder = builder, .flags = flags } };
7727 }7873 }
7728};7874};
77297875
...@@ -7753,10 +7899,10 @@ pub const MetadataString = enum(u32) {...@@ -7753,10 +7899,10 @@ pub const MetadataString = enum(u32) {
7753 metadata_string: MetadataString,7899 metadata_string: MetadataString,
7754 builder: *const Builder,7900 builder: *const Builder,
7755 };7901 };
7756 fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void {7902 fn format(data: FormatData, w: *Writer) Writer.Error!void {
7757 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw);7903 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, w);
7758 }7904 }
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) {
7760 return .{ .data = .{ .metadata_string = self, .builder = builder } };7906 return .{ .data = .{ .metadata_string = self, .builder = builder } };
7761 }7907 }
7762};7908};
...@@ -7918,24 +8064,24 @@ pub const Metadata = enum(u32) {...@@ -7918,24 +8064,24 @@ pub const Metadata = enum(u32) {
7918 AllCallsDescribed: bool = false,8064 AllCallsDescribed: bool = false,
7919 Unused: u2 = 0,8065 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 {
7922 var need_pipe = false;8068 var need_pipe = false;
7923 inline for (@typeInfo(DIFlags).@"struct".fields) |field| {8069 inline for (@typeInfo(DIFlags).@"struct".fields) |field| {
7924 switch (@typeInfo(field.type)) {8070 switch (@typeInfo(field.type)) {
7925 .bool => if (@field(self, field.name)) {8071 .bool => if (@field(self, field.name)) {
7926 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;8072 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
7927 try bw.print("DIFlag{s}", .{field.name});8073 try w.print("DIFlag{s}", .{field.name});
7928 },8074 },
7929 .@"enum" => if (@field(self, field.name) != .Zero) {8075 .@"enum" => if (@field(self, field.name) != .Zero) {
7930 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;8076 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
7931 try bw.print("DIFlag{s}", .{@tagName(@field(self, field.name))});8077 try w.print("DIFlag{s}", .{@tagName(@field(self, field.name))});
7932 },8078 },
7933 .int => assert(@field(self, field.name) == 0),8079 .int => assert(@field(self, field.name) == 0),
7934 else => @compileError("bad field type: " ++ field.name ++ ": " ++8080 else => @compileError("bad field type: " ++ field.name ++ ": " ++
7935 @typeName(field.type)),8081 @typeName(field.type)),
7936 }8082 }
7937 }8083 }
7938 if (!need_pipe) try bw.writeByte('0');8084 if (!need_pipe) try w.writeByte('0');
7939 }8085 }
7940 };8086 };
79418087
...@@ -7975,24 +8121,24 @@ pub const Metadata = enum(u32) {...@@ -7975,24 +8121,24 @@ pub const Metadata = enum(u32) {
7975 ObjCDirect: bool = false,8121 ObjCDirect: bool = false,
7976 Unused: u20 = 0,8122 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 {
7979 var need_pipe = false;8125 var need_pipe = false;
7980 inline for (@typeInfo(DISPFlags).@"struct".fields) |field| {8126 inline for (@typeInfo(DISPFlags).@"struct".fields) |field| {
7981 switch (@typeInfo(field.type)) {8127 switch (@typeInfo(field.type)) {
7982 .bool => if (@field(self, field.name)) {8128 .bool => if (@field(self, field.name)) {
7983 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;8129 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
7984 try bw.print("DISPFlag{s}", .{field.name});8130 try w.print("DISPFlag{s}", .{field.name});
7985 },8131 },
7986 .@"enum" => if (@field(self, field.name) != .Zero) {8132 .@"enum" => if (@field(self, field.name) != .Zero) {
7987 if (need_pipe) try bw.writeAll(" | ") else need_pipe = true;8133 if (need_pipe) try w.writeAll(" | ") else need_pipe = true;
7988 try bw.print("DISPFlag{s}", .{@tagName(@field(self, field.name))});8134 try w.print("DISPFlag{s}", .{@tagName(@field(self, field.name))});
7989 },8135 },
7990 .int => assert(@field(self, field.name) == 0),8136 .int => assert(@field(self, field.name) == 0),
7991 else => @compileError("bad field type: " ++ field.name ++ ": " ++8137 else => @compileError("bad field type: " ++ field.name ++ ": " ++
7992 @typeName(field.type)),8138 @typeName(field.type)),
7993 }8139 }
7994 }8140 }
7995 if (!need_pipe) try bw.writeByte('0');8141 if (!need_pipe) try w.writeByte('0');
7996 }8142 }
7997 };8143 };
79988144
...@@ -8167,6 +8313,7 @@ pub const Metadata = enum(u32) {...@@ -8167,6 +8313,7 @@ pub const Metadata = enum(u32) {
8167 formatter: *Formatter,8313 formatter: *Formatter,
8168 prefix: []const u8 = "",8314 prefix: []const u8 = "",
8169 node: Node,8315 node: Node,
8316 specialized: ?FormatFlags,
81708317
8171 const Node = union(enum) {8318 const Node = union(enum) {
8172 none,8319 none,
...@@ -8192,15 +8339,14 @@ pub const Metadata = enum(u32) {...@@ -8192,15 +8339,14 @@ pub const Metadata = enum(u32) {
8192 };8339 };
8193 };8340 };
8194 };8341 };
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 {
8196 if (data.node == .none) return;8343 if (data.node == .none) return;
81978344
8198 const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S';8345 const is_specialized = data.specialized != null;
8199 const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str;
82008346
8201 if (data.formatter.need_comma) try bw.writeAll(", ");8347 if (data.formatter.need_comma) try w.writeAll(", ");
8202 defer data.formatter.need_comma = true;8348 defer data.formatter.need_comma = true;
8203 try bw.writeAll(data.prefix);8349 try w.writeAll(data.prefix);
82048350
8205 const builder = data.formatter.builder;8351 const builder = data.formatter.builder;
8206 switch (data.node) {8352 switch (data.node) {
...@@ -8215,50 +8361,57 @@ pub const Metadata = enum(u32) {...@@ -8215,50 +8361,57 @@ pub const Metadata = enum(u32) {
8215 .expression => {8361 .expression => {
8216 var extra = builder.metadataExtraDataTrail(Expression, item.data);8362 var extra = builder.metadataExtraDataTrail(Expression, item.data);
8217 const elements = extra.trail.next(extra.data.elements_len, u32, builder);8363 const elements = extra.trail.next(extra.data.elements_len, u32, builder);
8218 try bw.writeAll("!DIExpression(");8364 try w.writeAll("!DIExpression(");
8219 for (elements) |element| try format(.{8365 for (elements) |element| try format(.{
8220 .formatter = data.formatter,8366 .formatter = data.formatter,
8221 .node = .{ .u64 = element },8367 .node = .{ .u64 = element },
8222 }, bw, "%");8368 .specialized = .{ .percent = true },
8223 try bw.writeByte(')');8369 }, w);
8370 try w.writeByte(')');
8224 },8371 },
8225 .constant => try Constant.format(.{8372 .constant => try Constant.format(.{
8226 .constant = @enumFromInt(item.data),8373 .constant = @enumFromInt(item.data),
8227 .builder = builder,8374 .builder = builder,
8228 }, bw, recurse_fmt_str),8375 .flags = data.specialized orelse .{},
8376 }, w),
8229 else => unreachable,8377 else => unreachable,
8230 }8378 }
8231 },8379 },
8232 .index => |node| try bw.print("!{d}", .{node}),8380 .index => |node| try w.print("!{d}", .{node}),
8233 inline .local_value, .local_metadata => |node, tag| try Value.format(.{8381 inline .local_value, .local_metadata => |node, tag| try Value.format(.{
8234 .value = node.value,8382 .value = node.value,
8235 .function = node.function,8383 .function = node.function,
8236 .builder = builder,8384 .builder = builder,
8237 }, bw, switch (tag) {8385 .flags = switch (tag) {
8238 .local_value => recurse_fmt_str,8386 .local_value => data.specialized orelse .{},
8239 .local_metadata => "%",8387 .local_metadata => .{ .percent = true },
8240 else => unreachable,8388 else => unreachable,
8241 }),8389 },
8390 }, w),
8242 inline .local_inline, .local_index => |node, tag| {8391 inline .local_inline, .local_index => |node, tag| {
8243 if (comptime std.mem.eql(u8, recurse_fmt_str, "%"))8392 if (data.specialized) |flags| {
8244 try bw.print("{f%} ", .{Type.metadata.fmt(builder)});8393 if (flags.onlyPercent()) {
8394 try w.print("{f} ", .{Type.metadata.fmt(builder, .percent)});
8395 }
8396 }
8245 try format(.{8397 try format(.{
8246 .formatter = data.formatter,8398 .formatter = data.formatter,
8247 .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node),8399 .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node),
8248 }, bw, "%");8400 .specialized = .{ .percent = true },
8401 }, w);
8249 },8402 },
8250 .string => |node| try bw.print((if (is_specialized) "" else "!") ++ "{f}", .{8403 .string => |node| try w.print("{s}{f}", .{
8251 node.fmt(builder),8404 @as([]const u8, if (is_specialized) "" else "!"), node.fmt(builder),
8252 }),8405 }),
8253 inline .bool, .u32, .u64 => |node| try bw.print("{}", .{node}),8406 inline .bool, .u32, .u64 => |node| try w.print("{}", .{node}),
8254 inline .di_flags, .sp_flags => |node| try bw.print("{f}", .{node}),8407 inline .di_flags, .sp_flags => |node| try w.print("{f}", .{node}),
8255 .raw => |node| try bw.writeAll(node),8408 .raw => |node| try w.writeAll(node),
8256 }8409 }
8257 }8410 }
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)) {
8259 Metadata => Allocator.Error,8412 Metadata => Allocator.Error,
8260 else => error{},8413 else => error{},
8261 }!std.fmt.Formatter(format) {8414 }!std.fmt.Formatter(FormatData, format) {
8262 const Node = @TypeOf(node);8415 const Node = @TypeOf(node);
8263 const MaybeNode = switch (@typeInfo(Node)) {8416 const MaybeNode = switch (@typeInfo(Node)) {
8264 .optional => Node,8417 .optional => Node,
...@@ -8295,6 +8448,7 @@ pub const Metadata = enum(u32) {...@@ -8295,6 +8448,7 @@ pub const Metadata = enum(u32) {
8295 .optional, .null => .none,8448 .optional, .null => .none,
8296 else => unreachable,8449 else => unreachable,
8297 },8450 },
8451 .specialized = special,
8298 } };8452 } };
8299 }8453 }
8300 inline fn fmtLocal(8454 inline fn fmtLocal(
...@@ -8302,7 +8456,7 @@ pub const Metadata = enum(u32) {...@@ -8302,7 +8456,7 @@ pub const Metadata = enum(u32) {
8302 prefix: []const u8,8456 prefix: []const u8,
8303 value: Value,8457 value: Value,
8304 function: Function.Index,8458 function: Function.Index,
8305 ) Allocator.Error!std.fmt.Formatter(format) {8459 ) Allocator.Error!std.fmt.Formatter(FormatData, format) {
8306 return .{ .data = .{8460 return .{ .data = .{
8307 .formatter = formatter,8461 .formatter = formatter,
8308 .prefix = prefix,8462 .prefix = prefix,
...@@ -8327,6 +8481,7 @@ pub const Metadata = enum(u32) {...@@ -8327,6 +8481,7 @@ pub const Metadata = enum(u32) {
8327 };8481 };
8328 },8482 },
8329 },8483 },
8484 .specialized = null,
8330 } };8485 } };
8331 }8486 }
8332 fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node {8487 fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node {
...@@ -8366,7 +8521,7 @@ pub const Metadata = enum(u32) {...@@ -8366,7 +8521,7 @@ pub const Metadata = enum(u32) {
8366 DIGlobalVariableExpression,8521 DIGlobalVariableExpression,
8367 },8522 },
8368 nodes: anytype,8523 nodes: anytype,
8369 bw: *Writer,8524 w: *Writer,
8370 ) !void {8525 ) !void {
8371 comptime var fmt_str: []const u8 = "";8526 comptime var fmt_str: []const u8 = "";
8372 const names = comptime std.meta.fieldNames(@TypeOf(nodes));8527 const names = comptime std.meta.fieldNames(@TypeOf(nodes));
...@@ -8383,10 +8538,10 @@ pub const Metadata = enum(u32) {...@@ -8383,10 +8538,10 @@ pub const Metadata = enum(u32) {
8383 }8538 }
8384 fmt_str = fmt_str ++ "(";8539 fmt_str = fmt_str ++ "(";
8385 inline for (fields[2..], names) |*field, name| {8540 inline for (fields[2..], names) |*field, name| {
8386 fmt_str = fmt_str ++ "{[" ++ name ++ "]fS}";8541 fmt_str = fmt_str ++ "{[" ++ name ++ "]f}";
8387 field.* = .{8542 field.* = .{
8388 .name = name,8543 .name = name,
8389 .type = std.fmt.Formatter(format),8544 .type = std.fmt.Formatter(FormatData, format),
8390 .default_value_ptr = null,8545 .default_value_ptr = null,
8391 .is_comptime = false,8546 .is_comptime = false,
8392 .alignment = 0,8547 .alignment = 0,
...@@ -8405,8 +8560,9 @@ pub const Metadata = enum(u32) {...@@ -8405,8 +8560,9 @@ pub const Metadata = enum(u32) {
8405 inline for (names) |name| @field(fmt_args, name) = try formatter.fmt(8560 inline for (names) |name| @field(fmt_args, name) = try formatter.fmt(
8406 name ++ ": ",8561 name ++ ": ",
8407 @field(nodes, name),8562 @field(nodes, name),
8563 null,
8408 );8564 );
8409 try bw.print(fmt_str, fmt_args);8565 try w.print(fmt_str, fmt_args);
8410 }8566 }
8411 };8567 };
8412};8568};
...@@ -8496,7 +8652,7 @@ pub fn init(options: Options) Allocator.Error!Builder {...@@ -8496,7 +8652,7 @@ pub fn init(options: Options) Allocator.Error!Builder {
8496 inline for (.{ 0, 4 }) |addr_space_index| {8652 inline for (.{ 0, 4 }) |addr_space_index| {
8497 const addr_space: AddrSpace = @enumFromInt(addr_space_index);8653 const addr_space: AddrSpace = @enumFromInt(addr_space_index);
8498 assert(self.ptrTypeAssumeCapacity(addr_space) ==8654 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(" ")})));
8500 }8656 }
8501 }8657 }
85028658
...@@ -8619,7 +8775,7 @@ pub fn deinit(self: *Builder) void {...@@ -8619,7 +8775,7 @@ pub fn deinit(self: *Builder) void {
8619 self.* = undefined;8775 self.* = undefined;
8620}8776}
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 {
8623 self.module_asm = aw.toArrayList();8779 self.module_asm = aw.toArrayList();
8624 if (self.module_asm.getLastOrNull()) |last| if (last != '\n')8780 if (self.module_asm.getLastOrNull()) |last| if (last != '\n')
8625 try self.module_asm.append(self.gpa, '\n');8781 try self.module_asm.append(self.gpa, '\n');
...@@ -8929,11 +9085,11 @@ pub fn getIntrinsic(...@@ -8929,11 +9085,11 @@ pub fn getIntrinsic(
89299085
8930 const name = name: {9086 const name = name: {
8931 {9087 {
8932 var aw: std.io.Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes);9088 var aw: Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes);
8933 const bw = &aw.interface;9089 const w = &aw.writer;
8934 defer self.strtab_string_bytes = aw.toArrayList();9090 defer self.strtab_string_bytes = aw.toArrayList();
8935 bw.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory;9091 w.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory;
8936 for (overload) |ty| bw.print(".{fm}", .{ty.fmt(self)}) catch return error.OutOfMemory;9092 for (overload) |ty| w.print(".{f}", .{ty.fmt(self, .m)}) catch return error.OutOfMemory;
8937 }9093 }
8938 break :name try self.trailingStrtabString();9094 break :name try self.trailingStrtabString();
8939 };9095 };
...@@ -9348,110 +9504,105 @@ pub fn asmValue(...@@ -9348,110 +9504,105 @@ pub fn asmValue(
9348 return (try self.asmConst(ty, info, assembly, constraints)).toValue();9504 return (try self.asmConst(ty, info, assembly, constraints)).toValue();
9349}9505}
93509506
9351pub fn dump(self: *Builder) void {9507pub fn dump(b: *Builder) void {
9508 var buffer: [4000]u8 = undefined;
9352 const stderr: std.fs.File = .stderr();9509 const stderr: std.fs.File = .stderr();
9353 self.printBuffered(stderr.writer()) catch {};9510 b.printToFile(stderr, &buffer) catch {};
9354}9511}
93559512
9356pub fn printToFile(self: *Builder, path: []const u8) bool {9513pub fn printToFilePath(b: *Builder, dir: std.fs.Dir, path: []const u8) !void {
9357 var file = std.fs.cwd().createFile(path, .{}) catch |err| {9514 var buffer: [4000]u8 = undefined;
9358 log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) });9515 const file = try dir.createFile(path, .{});
9359 return false;
9360 };
9361 defer file.close();9516 defer file.close();
9362 self.printBuffered(file.writer()) catch |err| {9517 try b.printToFile(file, &buffer);
9363 log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) });
9364 return false;
9365 };
9366 return true;
9367}9518}
93689519
9369pub fn printBuffered(self: *Builder, writer: Writer) Writer.Error!void {9520pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void {
9370 var buffer: [4096]u8 = undefined;9521 var fw = file.writer(buffer);
9371 var bw = writer.buffered(&buffer);9522 try print(b, &fw.interface);
9372 try self.print(&bw);9523 try fw.interface.flush();
9373 try bw.flush();
9374}9524}
93759525
9376pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {9526pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void {
9377 var need_newline = false;9527 var need_newline = false;
9378 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };9528 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
9379 defer metadata_formatter.map.deinit(self.gpa);9529 defer metadata_formatter.map.deinit(self.gpa);
93809530
9381 if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) {9531 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;9532 if (need_newline) try w.writeByte('\n') else need_newline = true;
9383 if (self.source_filename != .none) try bw.print(9533 if (self.source_filename != .none) try w.print(
9384 \\; ModuleID = '{s}'9534 \\; ModuleID = '{s}'
9385 \\source_filename = {f"}9535 \\source_filename = {f}
9386 \\9536 \\
9387 , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) });9537 , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) });
9388 if (self.data_layout != .none) try bw.print(9538 if (self.data_layout != .none) try w.print(
9389 \\target datalayout = {f"}9539 \\target datalayout = {f}
9390 \\9540 \\
9391 , .{self.data_layout.fmt(self)});9541 , .{self.data_layout.fmtQ(self)});
9392 if (self.target_triple != .none) try bw.print(9542 if (self.target_triple != .none) try w.print(
9393 \\target triple = {f"}9543 \\target triple = {f}
9394 \\9544 \\
9395 , .{self.target_triple.fmt(self)});9545 , .{self.target_triple.fmtQ(self)});
9396 }9546 }
93979547
9398 if (self.module_asm.items.len > 0) {9548 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;
9400 var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n');9550 var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n');
9401 while (line_it.next()) |line| {9551 while (line_it.next()) |line| {
9402 try bw.writeAll("module asm ");9552 try w.writeAll("module asm ");
9403 try printEscapedString(line, .always_quote, bw);9553 try printEscapedString(line, .always_quote, w);
9404 try bw.writeByte('\n');9554 try w.writeByte('\n');
9405 }9555 }
9406 }9556 }
94079557
9408 if (self.types.count() > 0) {9558 if (self.types.count() > 0) {
9409 if (need_newline) try bw.writeByte('\n') else need_newline = true;9559 if (need_newline) try w.writeByte('\n') else need_newline = true;
9410 for (self.types.keys(), self.types.values()) |id, ty| try bw.print(9560 for (self.types.keys(), self.types.values()) |id, ty| try w.print(
9411 \\%{f} = type {f}9561 \\%{f} = type {f}
9412 \\9562 \\
9413 , .{ id.fmt(self), ty.fmt(self) });9563 , .{ id.fmt(self), ty.fmt(self, .default) });
9414 }9564 }
94159565
9416 if (self.variables.items.len > 0) {9566 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;
9418 for (self.variables.items) |variable| {9568 for (self.variables.items) |variable| {
9419 if (variable.global.getReplacement(self) != .none) continue;9569 if (variable.global.getReplacement(self) != .none) continue;
9420 const global = variable.global.ptrConst(self);9570 const global = variable.global.ptrConst(self);
9421 metadata_formatter.need_comma = true;9571 metadata_formatter.need_comma = true;
9422 defer metadata_formatter.need_comma = undefined;9572 defer metadata_formatter.need_comma = undefined;
9423 try bw.print(9573 try w.print(
9424 \\{f} ={f}{f}{f}{f}{f }{f}{f }{f} {s} {f%}{f }{f, }{f}9574 \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f}
9425 \\9575 \\
9426 , .{9576 , .{
9427 variable.global.fmt(self),9577 variable.global.fmt(self),
9428 Linkage.fmtOptional(if (global.linkage == .external and9578 Linkage.fmtOptional(
9429 variable.init != .no_init) null else global.linkage),9579 if (global.linkage == .external and variable.init != .no_init) null else global.linkage,
9580 ),
9430 global.preemption,9581 global.preemption,
9431 global.visibility,9582 global.visibility,
9432 global.dll_storage_class,9583 global.dll_storage_class,
9433 variable.thread_local,9584 variable.thread_local.fmt(" "),
9434 global.unnamed_addr,9585 global.unnamed_addr,
9435 global.addr_space,9586 global.addr_space.fmt(" "),
9436 global.externally_initialized,9587 global.externally_initialized,
9437 @tagName(variable.mutability),9588 @tagName(variable.mutability),
9438 global.type.fmt(self),9589 global.type.fmt(self, .percent),
9439 variable.init.fmt(self),9590 variable.init.fmt(self, .{ .space = true }),
9440 variable.alignment,9591 variable.alignment.fmt(", "),
9441 try metadata_formatter.fmt("!dbg ", global.dbg),9592 try metadata_formatter.fmt("!dbg ", global.dbg, null),
9442 });9593 });
9443 }9594 }
9444 }9595 }
94459596
9446 if (self.aliases.items.len > 0) {9597 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;
9448 for (self.aliases.items) |alias| {9599 for (self.aliases.items) |alias| {
9449 if (alias.global.getReplacement(self) != .none) continue;9600 if (alias.global.getReplacement(self) != .none) continue;
9450 const global = alias.global.ptrConst(self);9601 const global = alias.global.ptrConst(self);
9451 metadata_formatter.need_comma = true;9602 metadata_formatter.need_comma = true;
9452 defer metadata_formatter.need_comma = undefined;9603 defer metadata_formatter.need_comma = undefined;
9453 try bw.print(9604 try w.print(
9454 \\{f} ={f}{f}{f}{f}{f }{f} alias {f%}, {f%}{f}9605 \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f}
9455 \\9606 \\
9456 , .{9607 , .{
9457 alias.global.fmt(self),9608 alias.global.fmt(self),
...@@ -9459,11 +9610,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9459,11 +9610,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9459 global.preemption,9610 global.preemption,
9460 global.visibility,9611 global.visibility,
9461 global.dll_storage_class,9612 global.dll_storage_class,
9462 alias.thread_local,9613 alias.thread_local.fmt(" "),
9463 global.unnamed_addr,9614 global.unnamed_addr,
9464 global.type.fmt(self),9615 global.type.fmt(self, .percent),
9465 alias.aliasee.fmt(self),9616 alias.aliasee.fmt(self, .{ .percent = true }),
9466 try metadata_formatter.fmt("!dbg ", global.dbg),9617 try metadata_formatter.fmt("!dbg ", global.dbg, null),
9467 });9618 });
9468 }9619 }
9469 }9620 }
...@@ -9473,17 +9624,17 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9473,17 +9624,17 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
94739624
9474 for (0.., self.functions.items) |function_i, function| {9625 for (0.., self.functions.items) |function_i, function| {
9475 if (function.global.getReplacement(self) != .none) continue;9626 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;
9477 const function_index: Function.Index = @enumFromInt(function_i);9628 const function_index: Function.Index = @enumFromInt(function_i);
9478 const global = function.global.ptrConst(self);9629 const global = function.global.ptrConst(self);
9479 const params_len = global.type.functionParameters(self).len;9630 const params_len = global.type.functionParameters(self).len;
9480 const function_attributes = function.attributes.func(self);9631 const function_attributes = function.attributes.func(self);
9481 if (function_attributes != .none) try bw.print(9632 if (function_attributes != .none) try w.print(
9482 \\; Function Attrs:{f}9633 \\; Function Attrs:{f}
9483 \\9634 \\
9484 , .{function_attributes.fmt(self)});9635 , .{function_attributes.fmt(self, .{})});
9485 try bw.print(9636 try w.print(
9486 \\{s}{f}{f}{f}{f}{f}{f"} {f%} {f}(9637 \\{s}{f}{f}{f}{f}{f}{f} {f} {f}(
9487 , .{9638 , .{
9488 if (function.instructions.len > 0) "define" else "declare",9639 if (function.instructions.len > 0) "define" else "declare",
9489 global.linkage,9640 global.linkage,
...@@ -9491,45 +9642,45 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9491,45 +9642,45 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9491 global.visibility,9642 global.visibility,
9492 global.dll_storage_class,9643 global.dll_storage_class,
9493 function.call_conv,9644 function.call_conv,
9494 function.attributes.ret(self).fmt(self),9645 function.attributes.ret(self).fmt(self, .{}),
9495 global.type.functionReturn(self).fmt(self),9646 global.type.functionReturn(self).fmt(self, .percent),
9496 function.global.fmt(self),9647 function.global.fmt(self),
9497 });9648 });
9498 for (0..params_len) |arg| {9649 for (0..params_len) |arg| {
9499 if (arg > 0) try bw.writeAll(", ");9650 if (arg > 0) try w.writeAll(", ");
9500 try bw.print(9651 try w.print(
9501 \\{f%}{f"}9652 \\{f}{f}
9502 , .{9653 , .{
9503 global.type.functionParameters(self)[arg].fmt(self),9654 global.type.functionParameters(self)[arg].fmt(self, .percent),
9504 function.attributes.param(arg, self).fmt(self),9655 function.attributes.param(arg, self).fmt(self, .{}),
9505 });9656 });
9506 if (function.instructions.len > 0)9657 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, .{})})
9508 else9659 else
9509 try bw.print(" %{d}", .{arg});9660 try w.print(" %{d}", .{arg});
9510 }9661 }
9511 switch (global.type.functionKind(self)) {9662 switch (global.type.functionKind(self)) {
9512 .normal => {},9663 .normal => {},
9513 .vararg => {9664 .vararg => {
9514 if (params_len > 0) try bw.writeAll(", ");9665 if (params_len > 0) try w.writeAll(", ");
9515 try bw.writeAll("...");9666 try w.writeAll("...");
9516 },9667 },
9517 }9668 }
9518 try bw.print("){f}{f }", .{ global.unnamed_addr, global.addr_space });9669 try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") });
9519 if (function_attributes != .none) try bw.print(" #{d}", .{9670 if (function_attributes != .none) try w.print(" #{d}", .{
9520 (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index,9671 (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index,
9521 });9672 });
9522 {9673 {
9523 metadata_formatter.need_comma = false;9674 metadata_formatter.need_comma = false;
9524 defer metadata_formatter.need_comma = undefined;9675 defer metadata_formatter.need_comma = undefined;
9525 try bw.print("{f }{f}", .{9676 try w.print("{f}{f}", .{
9526 function.alignment,9677 function.alignment.fmt(" "),
9527 try metadata_formatter.fmt(" !dbg ", global.dbg),9678 try metadata_formatter.fmt(" !dbg ", global.dbg, null),
9528 });9679 });
9529 }9680 }
9530 if (function.instructions.len > 0) {9681 if (function.instructions.len > 0) {
9531 var block_incoming_len: u32 = undefined;9682 var block_incoming_len: u32 = undefined;
9532 try bw.writeAll(" {\n");9683 try w.writeAll(" {\n");
9533 var maybe_dbg_index: ?u32 = null;9684 var maybe_dbg_index: ?u32 = null;
9534 for (params_len..function.instructions.len) |instruction_i| {9685 for (params_len..function.instructions.len) |instruction_i| {
9535 const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i);9686 const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i);
...@@ -9627,11 +9778,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9627,11 +9778,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9627 .xor,9778 .xor,
9628 => |tag| {9779 => |tag| {
9629 const extra = function.extraData(Function.Instruction.Binary, instruction.data);9780 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}", .{
9631 instruction_index.name(&function).fmt(self),9782 instruction_index.name(&function).fmt(self),
9632 @tagName(tag),9783 @tagName(tag),
9633 extra.lhs.fmt(function_index, self),9784 extra.lhs.fmt(function_index, self, .{ .percent = true }),
9634 extra.rhs.fmt(function_index, self),9785 extra.rhs.fmt(function_index, self, .{ .percent = true }),
9635 });9786 });
9636 },9787 },
9637 .addrspacecast,9788 .addrspacecast,
...@@ -9649,73 +9800,76 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9649,73 +9800,76 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9649 .zext,9800 .zext,
9650 => |tag| {9801 => |tag| {
9651 const extra = function.extraData(Function.Instruction.Cast, instruction.data);9802 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}", .{
9653 instruction_index.name(&function).fmt(self),9804 instruction_index.name(&function).fmt(self),
9654 @tagName(tag),9805 @tagName(tag),
9655 extra.val.fmt(function_index, self),9806 extra.val.fmt(function_index, self, .{ .percent = true }),
9656 extra.type.fmt(self),9807 extra.type.fmt(self, .percent),
9657 });9808 });
9658 },9809 },
9659 .alloca,9810 .alloca,
9660 .@"alloca inalloca",9811 .@"alloca inalloca",
9661 => |tag| {9812 => |tag| {
9662 const extra = function.extraData(Function.Instruction.Alloca, instruction.data);9813 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}", .{
9664 instruction_index.name(&function).fmt(self),9815 instruction_index.name(&function).fmt(self),
9665 @tagName(tag),9816 @tagName(tag),
9666 extra.type.fmt(self),9817 extra.type.fmt(self, .percent),
9667 Value.fmt(switch (extra.len) {9818 Value.fmt(switch (extra.len) {
9668 .@"1" => .none,9819 .@"1" => .none,
9669 else => extra.len,9820 else => extra.len,
9670 }, function_index, self),9821 }, function_index, self, .{
9671 extra.info.alignment,9822 .comma = true,
9672 extra.info.addr_space,9823 .percent = true,
9824 }),
9825 extra.info.alignment.fmt(", "),
9826 extra.info.addr_space.fmt(", "),
9673 });9827 });
9674 },9828 },
9675 .arg => unreachable,9829 .arg => unreachable,
9676 .atomicrmw => |tag| {9830 .atomicrmw => |tag| {
9677 const extra =9831 const extra =
9678 function.extraData(Function.Instruction.AtomicRmw, instruction.data);9832 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}", .{
9680 instruction_index.name(&function).fmt(self),9834 instruction_index.name(&function).fmt(self),
9681 @tagName(tag),9835 tag,
9682 extra.info.access_kind,9836 extra.info.access_kind.fmt(" "),
9683 @tagName(extra.info.atomic_rmw_operation),9837 extra.info.atomic_rmw_operation,
9684 extra.ptr.fmt(function_index, self),9838 extra.ptr.fmt(function_index, self, .{ .percent = true }),
9685 extra.val.fmt(function_index, self),9839 extra.val.fmt(function_index, self, .{ .percent = true }),
9686 extra.info.sync_scope,9840 extra.info.sync_scope.fmt(" "),
9687 extra.info.success_ordering,9841 extra.info.success_ordering.fmt(" "),
9688 extra.info.alignment,9842 extra.info.alignment.fmt(", "),
9689 });9843 });
9690 },9844 },
9691 .block => {9845 .block => {
9692 block_incoming_len = instruction.data;9846 block_incoming_len = instruction.data;
9693 const name = instruction_index.name(&function);9847 const name = instruction_index.name(&function);
9694 if (@intFromEnum(instruction_index) > params_len)9848 if (@intFromEnum(instruction_index) > params_len)
9695 try bw.writeByte('\n');9849 try w.writeByte('\n');
9696 try bw.print("{f}:\n", .{name.fmt(self)});9850 try w.print("{f}:\n", .{name.fmt(self)});
9697 continue;9851 continue;
9698 },9852 },
9699 .br => |tag| {9853 .br => |tag| {
9700 const target: Function.Block.Index = @enumFromInt(instruction.data);9854 const target: Function.Block.Index = @enumFromInt(instruction.data);
9701 try bw.print(" {s} {f%}", .{9855 try w.print(" {s} {f}", .{
9702 @tagName(tag), target.toInst(&function).fmt(function_index, self),9856 @tagName(tag), target.toInst(&function).fmt(function_index, self, .{ .percent = true }),
9703 });9857 });
9704 },9858 },
9705 .br_cond => {9859 .br_cond => {
9706 const extra = function.extraData(Function.Instruction.BrCond, instruction.data);9860 const extra = function.extraData(Function.Instruction.BrCond, instruction.data);
9707 try bw.print(" br {f%}, {f%}, {f%}", .{9861 try w.print(" br {f}, {f}, {f}", .{
9708 extra.cond.fmt(function_index, self),9862 extra.cond.fmt(function_index, self, .{ .percent = true }),
9709 extra.then.toInst(&function).fmt(function_index, self),9863 extra.then.toInst(&function).fmt(function_index, self, .{ .percent = true }),
9710 extra.@"else".toInst(&function).fmt(function_index, self),9864 extra.@"else".toInst(&function).fmt(function_index, self, .{ .percent = true }),
9711 });9865 });
9712 metadata_formatter.need_comma = true;9866 metadata_formatter.need_comma = true;
9713 defer metadata_formatter.need_comma = undefined;9867 defer metadata_formatter.need_comma = undefined;
9714 switch (extra.weights) {9868 switch (extra.weights) {
9715 .none => {},9869 .none => {},
9716 .unpredictable => try bw.writeAll("!unpredictable !{}"),9870 .unpredictable => try w.writeAll("!unpredictable !{}"),
9717 _ => try bw.print("{f}", .{9871 _ => try w.print("{f}", .{
9718 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))),9872 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights))), null),
9719 }),9873 }),
9720 }9874 }
9721 },9875 },
...@@ -9731,42 +9885,42 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9731,42 +9885,42 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9731 var extra =9885 var extra =
9732 function.extraDataTrail(Function.Instruction.Call, instruction.data);9886 function.extraDataTrail(Function.Instruction.Call, instruction.data);
9733 const args = extra.trail.next(extra.data.args_len, Value, &function);9887 const args = extra.trail.next(extra.data.args_len, Value, &function);
9734 try bw.writeAll(" ");9888 try w.writeAll(" ");
9735 const ret_ty = extra.data.ty.functionReturn(self);9889 const ret_ty = extra.data.ty.functionReturn(self);
9736 switch (ret_ty) {9890 switch (ret_ty) {
9737 .void => {},9891 .void => {},
9738 else => try bw.print("%{f} = ", .{9892 else => try w.print("%{f} = ", .{
9739 instruction_index.name(&function).fmt(self),9893 instruction_index.name(&function).fmt(self),
9740 }),9894 }),
9741 .none => unreachable,9895 .none => unreachable,
9742 }9896 }
9743 try bw.print("{s}{f}{f}{f} {f%} {f}(", .{9897 try w.print("{t}{f}{f}{f} {f} {f}(", .{
9744 @tagName(tag),9898 tag,
9745 extra.data.info.call_conv,9899 extra.data.info.call_conv,
9746 extra.data.attributes.ret(self).fmt(self),9900 extra.data.attributes.ret(self).fmt(self, .{}),
9747 extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self),9901 extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self),
9748 switch (extra.data.ty.functionKind(self)) {9902 switch (extra.data.ty.functionKind(self)) {
9749 .normal => ret_ty,9903 .normal => ret_ty,
9750 .vararg => extra.data.ty,9904 .vararg => extra.data.ty,
9751 }.fmt(self),9905 }.fmt(self, .percent),
9752 extra.data.callee.fmt(function_index, self),9906 extra.data.callee.fmt(function_index, self, .{}),
9753 });9907 });
9754 for (0.., args) |arg_index, arg| {9908 for (0.., args) |arg_index, arg| {
9755 if (arg_index > 0) try bw.writeAll(", ");9909 if (arg_index > 0) try w.writeAll(", ");
9756 metadata_formatter.need_comma = false;9910 metadata_formatter.need_comma = false;
9757 defer metadata_formatter.need_comma = undefined;9911 defer metadata_formatter.need_comma = undefined;
9758 try bw.print("{f%}{f}{f}", .{9912 try w.print("{f}{f}{f}", .{
9759 arg.typeOf(function_index, self).fmt(self),9913 arg.typeOf(function_index, self).fmt(self, .percent),
9760 extra.data.attributes.param(arg_index, self).fmt(self),9914 extra.data.attributes.param(arg_index, self).fmt(self, .{}),
9761 try metadata_formatter.fmtLocal(" ", arg, function_index),9915 try metadata_formatter.fmtLocal(" ", arg, function_index),
9762 });9916 });
9763 }9917 }
9764 try bw.writeByte(')');9918 try w.writeByte(')');
9765 if (extra.data.info.has_op_bundle_cold) {9919 if (extra.data.info.has_op_bundle_cold) {
9766 try bw.writeAll(" [ \"cold\"() ]");9920 try w.writeAll(" [ \"cold\"() ]");
9767 }9921 }
9768 const call_function_attributes = extra.data.attributes.func(self);9922 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}", .{
9770 (try attribute_groups.getOrPutValue(9924 (try attribute_groups.getOrPutValue(
9771 self.gpa,9925 self.gpa,
9772 call_function_attributes,9926 call_function_attributes,
...@@ -9779,27 +9933,27 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9779,27 +9933,27 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9779 => |tag| {9933 => |tag| {
9780 const extra =9934 const extra =
9781 function.extraData(Function.Instruction.CmpXchg, instruction.data);9935 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}", .{
9783 instruction_index.name(&function).fmt(self),9937 instruction_index.name(&function).fmt(self),
9784 @tagName(tag),9938 tag,
9785 extra.info.access_kind,9939 extra.info.access_kind.fmt(" "),
9786 extra.ptr.fmt(function_index, self),9940 extra.ptr.fmt(function_index, self, .{ .percent = true }),
9787 extra.cmp.fmt(function_index, self),9941 extra.cmp.fmt(function_index, self, .{ .percent = true }),
9788 extra.new.fmt(function_index, self),9942 extra.new.fmt(function_index, self, .{ .percent = true }),
9789 extra.info.sync_scope,9943 extra.info.sync_scope.fmt(" "),
9790 extra.info.success_ordering,9944 extra.info.success_ordering.fmt(" "),
9791 extra.info.failure_ordering,9945 extra.info.failure_ordering.fmt(" "),
9792 extra.info.alignment,9946 extra.info.alignment.fmt(", "),
9793 });9947 });
9794 },9948 },
9795 .extractelement => |tag| {9949 .extractelement => |tag| {
9796 const extra =9950 const extra =
9797 function.extraData(Function.Instruction.ExtractElement, instruction.data);9951 function.extraData(Function.Instruction.ExtractElement, instruction.data);
9798 try bw.print(" %{f} = {s} {f%}, {f%}", .{9952 try w.print(" %{f} = {s} {f}, {f}", .{
9799 instruction_index.name(&function).fmt(self),9953 instruction_index.name(&function).fmt(self),
9800 @tagName(tag),9954 @tagName(tag),
9801 extra.val.fmt(function_index, self),9955 extra.val.fmt(function_index, self, .{ .percent = true }),
9802 extra.index.fmt(function_index, self),9956 extra.index.fmt(function_index, self, .{ .percent = true }),
9803 });9957 });
9804 },9958 },
9805 .extractvalue => |tag| {9959 .extractvalue => |tag| {
...@@ -9808,29 +9962,29 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9808,29 +9962,29 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9808 instruction.data,9962 instruction.data,
9809 );9963 );
9810 const indices = extra.trail.next(extra.data.indices_len, u32, &function);9964 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}", .{
9812 instruction_index.name(&function).fmt(self),9966 instruction_index.name(&function).fmt(self),
9813 @tagName(tag),9967 @tagName(tag),
9814 extra.data.val.fmt(function_index, self),9968 extra.data.val.fmt(function_index, self, .{ .percent = true }),
9815 });9969 });
9816 for (indices) |index| try bw.print(", {d}", .{index});9970 for (indices) |index| try w.print(", {d}", .{index});
9817 },9971 },
9818 .fence => |tag| {9972 .fence => |tag| {
9819 const info: MemoryAccessInfo = @bitCast(instruction.data);9973 const info: MemoryAccessInfo = @bitCast(instruction.data);
9820 try bw.print(" {s}{f }{f }", .{9974 try w.print(" {t}{f}{f}", .{
9821 @tagName(tag),9975 tag,
9822 info.sync_scope,9976 info.sync_scope.fmt(" "),
9823 info.success_ordering,9977 info.success_ordering.fmt(" "),
9824 });9978 });
9825 },9979 },
9826 .fneg,9980 .fneg,
9827 .@"fneg fast",9981 .@"fneg fast",
9828 => |tag| {9982 => |tag| {
9829 const val: Value = @enumFromInt(instruction.data);9983 const val: Value = @enumFromInt(instruction.data);
9830 try bw.print(" %{f} = {s} {f%}", .{9984 try w.print(" %{f} = {s} {f}", .{
9831 instruction_index.name(&function).fmt(self),9985 instruction_index.name(&function).fmt(self),
9832 @tagName(tag),9986 @tagName(tag),
9833 val.fmt(function_index, self),9987 val.fmt(function_index, self, .{ .percent = true }),
9834 });9988 });
9835 },9989 },
9836 .getelementptr,9990 .getelementptr,
...@@ -9841,14 +9995,14 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9841,14 +9995,14 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9841 instruction.data,9995 instruction.data,
9842 );9996 );
9843 const indices = extra.trail.next(extra.data.indices_len, Value, &function);9997 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}", .{
9845 instruction_index.name(&function).fmt(self),9999 instruction_index.name(&function).fmt(self),
9846 @tagName(tag),10000 @tagName(tag),
9847 extra.data.type.fmt(self),10001 extra.data.type.fmt(self, .percent),
9848 extra.data.base.fmt(function_index, self),10002 extra.data.base.fmt(function_index, self, .{ .percent = true }),
9849 });10003 });
9850 for (indices) |index| try bw.print(", {f%}", .{10004 for (indices) |index| try w.print(", {f}", .{
9851 index.fmt(function_index, self),10005 index.fmt(function_index, self, .{ .percent = true }),
9852 });10006 });
9853 },10007 },
9854 .indirectbr => |tag| {10008 .indirectbr => |tag| {
...@@ -9856,54 +10010,54 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9856,54 +10010,54 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9856 function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data);10010 function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data);
9857 const targets =10011 const targets =
9858 extra.trail.next(extra.data.targets_len, Function.Block.Index, &function);10012 extra.trail.next(extra.data.targets_len, Function.Block.Index, &function);
9859 try bw.print(" {s} {f%}, [", .{10013 try w.print(" {s} {f}, [", .{
9860 @tagName(tag),10014 @tagName(tag),
9861 extra.data.addr.fmt(function_index, self),10015 extra.data.addr.fmt(function_index, self, .{ .percent = true }),
9862 });10016 });
9863 for (0.., targets) |target_index, target| {10017 for (0.., targets) |target_index, target| {
9864 if (target_index > 0) try bw.writeAll(", ");10018 if (target_index > 0) try w.writeAll(", ");
9865 try bw.print("{f%}", .{10019 try w.print("{f}", .{
9866 target.toInst(&function).fmt(function_index, self),10020 target.toInst(&function).fmt(function_index, self, .{ .percent = true }),
9867 });10021 });
9868 }10022 }
9869 try bw.writeByte(']');10023 try w.writeByte(']');
9870 },10024 },
9871 .insertelement => |tag| {10025 .insertelement => |tag| {
9872 const extra =10026 const extra =
9873 function.extraData(Function.Instruction.InsertElement, instruction.data);10027 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}", .{
9875 instruction_index.name(&function).fmt(self),10029 instruction_index.name(&function).fmt(self),
9876 @tagName(tag),10030 @tagName(tag),
9877 extra.val.fmt(function_index, self),10031 extra.val.fmt(function_index, self, .{ .percent = true }),
9878 extra.elem.fmt(function_index, self),10032 extra.elem.fmt(function_index, self, .{ .percent = true }),
9879 extra.index.fmt(function_index, self),10033 extra.index.fmt(function_index, self, .{ .percent = true }),
9880 });10034 });
9881 },10035 },
9882 .insertvalue => |tag| {10036 .insertvalue => |tag| {
9883 var extra =10037 var extra =
9884 function.extraDataTrail(Function.Instruction.InsertValue, instruction.data);10038 function.extraDataTrail(Function.Instruction.InsertValue, instruction.data);
9885 const indices = extra.trail.next(extra.data.indices_len, u32, &function);10039 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}", .{
9887 instruction_index.name(&function).fmt(self),10041 instruction_index.name(&function).fmt(self),
9888 @tagName(tag),10042 @tagName(tag),
9889 extra.data.val.fmt(function_index, self),10043 extra.data.val.fmt(function_index, self, .{ .percent = true }),
9890 extra.data.elem.fmt(function_index, self),10044 extra.data.elem.fmt(function_index, self, .{ .percent = true }),
9891 });10045 });
9892 for (indices) |index| try bw.print(", {d}", .{index});10046 for (indices) |index| try w.print(", {d}", .{index});
9893 },10047 },
9894 .load,10048 .load,
9895 .@"load atomic",10049 .@"load atomic",
9896 => |tag| {10050 => |tag| {
9897 const extra = function.extraData(Function.Instruction.Load, instruction.data);10051 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}", .{
9899 instruction_index.name(&function).fmt(self),10053 instruction_index.name(&function).fmt(self),
9900 @tagName(tag),10054 tag,
9901 extra.info.access_kind,10055 extra.info.access_kind.fmt(" "),
9902 extra.type.fmt(self),10056 extra.type.fmt(self, .percent),
9903 extra.ptr.fmt(function_index, self),10057 extra.ptr.fmt(function_index, self, .{ .percent = true }),
9904 extra.info.sync_scope,10058 extra.info.sync_scope.fmt(" "),
9905 extra.info.success_ordering,10059 extra.info.success_ordering.fmt(" "),
9906 extra.info.alignment,10060 extra.info.alignment.fmt(", "),
9907 });10061 });
9908 },10062 },
9909 .phi,10063 .phi,
...@@ -9913,64 +10067,64 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9913,64 +10067,64 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9913 const vals = extra.trail.next(block_incoming_len, Value, &function);10067 const vals = extra.trail.next(block_incoming_len, Value, &function);
9914 const blocks =10068 const blocks =
9915 extra.trail.next(block_incoming_len, Function.Block.Index, &function);10069 extra.trail.next(block_incoming_len, Function.Block.Index, &function);
9916 try bw.print(" %{f} = {s} {f%} ", .{10070 try w.print(" %{f} = {s} {f} ", .{
9917 instruction_index.name(&function).fmt(self),10071 instruction_index.name(&function).fmt(self),
9918 @tagName(tag),10072 @tagName(tag),
9919 vals[0].typeOf(function_index, self).fmt(self),10073 vals[0].typeOf(function_index, self).fmt(self, .percent),
9920 });10074 });
9921 for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| {10075 for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| {
9922 if (incoming_index > 0) try bw.writeAll(", ");10076 if (incoming_index > 0) try w.writeAll(", ");
9923 try bw.print("[ {f}, {f} ]", .{10077 try w.print("[ {f}, {f} ]", .{
9924 incoming_val.fmt(function_index, self),10078 incoming_val.fmt(function_index, self, .{}),
9925 incoming_block.toInst(&function).fmt(function_index, self),10079 incoming_block.toInst(&function).fmt(function_index, self, .{}),
9926 });10080 });
9927 }10081 }
9928 },10082 },
9929 .ret => |tag| {10083 .ret => |tag| {
9930 const val: Value = @enumFromInt(instruction.data);10084 const val: Value = @enumFromInt(instruction.data);
9931 try bw.print(" {s} {f%}", .{10085 try w.print(" {s} {f}", .{
9932 @tagName(tag),10086 @tagName(tag),
9933 val.fmt(function_index, self),10087 val.fmt(function_index, self, .{ .percent = true }),
9934 });10088 });
9935 },10089 },
9936 .@"ret void",10090 .@"ret void",
9937 .@"unreachable",10091 .@"unreachable",
9938 => |tag| try bw.print(" {s}", .{@tagName(tag)}),10092 => |tag| try w.print(" {s}", .{@tagName(tag)}),
9939 .select,10093 .select,
9940 .@"select fast",10094 .@"select fast",
9941 => |tag| {10095 => |tag| {
9942 const extra = function.extraData(Function.Instruction.Select, instruction.data);10096 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}", .{
9944 instruction_index.name(&function).fmt(self),10098 instruction_index.name(&function).fmt(self),
9945 @tagName(tag),10099 @tagName(tag),
9946 extra.cond.fmt(function_index, self),10100 extra.cond.fmt(function_index, self, .{ .percent = true }),
9947 extra.lhs.fmt(function_index, self),10101 extra.lhs.fmt(function_index, self, .{ .percent = true }),
9948 extra.rhs.fmt(function_index, self),10102 extra.rhs.fmt(function_index, self, .{ .percent = true }),
9949 });10103 });
9950 },10104 },
9951 .shufflevector => |tag| {10105 .shufflevector => |tag| {
9952 const extra =10106 const extra =
9953 function.extraData(Function.Instruction.ShuffleVector, instruction.data);10107 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}", .{
9955 instruction_index.name(&function).fmt(self),10109 instruction_index.name(&function).fmt(self),
9956 @tagName(tag),10110 @tagName(tag),
9957 extra.lhs.fmt(function_index, self),10111 extra.lhs.fmt(function_index, self, .{ .percent = true }),
9958 extra.rhs.fmt(function_index, self),10112 extra.rhs.fmt(function_index, self, .{ .percent = true }),
9959 extra.mask.fmt(function_index, self),10113 extra.mask.fmt(function_index, self, .{ .percent = true }),
9960 });10114 });
9961 },10115 },
9962 .store,10116 .store,
9963 .@"store atomic",10117 .@"store atomic",
9964 => |tag| {10118 => |tag| {
9965 const extra = function.extraData(Function.Instruction.Store, instruction.data);10119 const extra = function.extraData(Function.Instruction.Store, instruction.data);
9966 try bw.print(" {s}{f } {f%}, {f%}{f }{f }{f, }", .{10120 try w.print(" {t}{f} {f}, {f}{f}{f}{f}", .{
9967 @tagName(tag),10121 tag,
9968 extra.info.access_kind,10122 extra.info.access_kind.fmt(" "),
9969 extra.val.fmt(function_index, self),10123 extra.val.fmt(function_index, self, .{ .percent = true }),
9970 extra.ptr.fmt(function_index, self),10124 extra.ptr.fmt(function_index, self, .{ .percent = true }),
9971 extra.info.sync_scope,10125 extra.info.sync_scope.fmt(" "),
9972 extra.info.success_ordering,10126 extra.info.success_ordering.fmt(" "),
9973 extra.info.alignment,10127 extra.info.alignment.fmt(", "),
9974 });10128 });
9975 },10129 },
9976 .@"switch" => |tag| {10130 .@"switch" => |tag| {
...@@ -9979,80 +10133,80 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -9979,80 +10133,80 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
9979 const vals = extra.trail.next(extra.data.cases_len, Constant, &function);10133 const vals = extra.trail.next(extra.data.cases_len, Constant, &function);
9980 const blocks =10134 const blocks =
9981 extra.trail.next(extra.data.cases_len, Function.Block.Index, &function);10135 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", .{
9983 @tagName(tag),10137 @tagName(tag),
9984 extra.data.val.fmt(function_index, self),10138 extra.data.val.fmt(function_index, self, .{ .percent = true }),
9985 extra.data.default.toInst(&function).fmt(function_index, self),10139 extra.data.default.toInst(&function).fmt(function_index, self, .{ .percent = true }),
9986 });10140 });
9987 for (vals, blocks) |case_val, case_block| try bw.print(10141 for (vals, blocks) |case_val, case_block| try w.print(
9988 " {f%}, {f%}\n",10142 " {f}, {f}\n",
9989 .{10143 .{
9990 case_val.fmt(self),10144 case_val.fmt(self, .{ .percent = true }),
9991 case_block.toInst(&function).fmt(function_index, self),10145 case_block.toInst(&function).fmt(function_index, self, .{ .percent = true }),
9992 },10146 },
9993 );10147 );
9994 try bw.writeAll(" ]");10148 try w.writeAll(" ]");
9995 metadata_formatter.need_comma = true;10149 metadata_formatter.need_comma = true;
9996 defer metadata_formatter.need_comma = undefined;10150 defer metadata_formatter.need_comma = undefined;
9997 switch (extra.data.weights) {10151 switch (extra.data.weights) {
9998 .none => {},10152 .none => {},
9999 .unpredictable => try bw.writeAll("!unpredictable !{}"),10153 .unpredictable => try w.writeAll("!unpredictable !{}"),
10000 _ => try bw.print("{f}", .{10154 _ => try w.print("{f}", .{
10001 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))),10155 try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights))), null),
10002 }),10156 }),
10003 }10157 }
10004 },10158 },
10005 .va_arg => |tag| {10159 .va_arg => |tag| {
10006 const extra = function.extraData(Function.Instruction.VaArg, instruction.data);10160 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}", .{
10008 instruction_index.name(&function).fmt(self),10162 instruction_index.name(&function).fmt(self),
10009 @tagName(tag),10163 @tagName(tag),
10010 extra.list.fmt(function_index, self),10164 extra.list.fmt(function_index, self, .{ .percent = true }),
10011 extra.type.fmt(self),10165 extra.type.fmt(self, .percent),
10012 });10166 });
10013 },10167 },
10014 }10168 }
1001510169
10016 if (maybe_dbg_index) |dbg_index| {10170 if (maybe_dbg_index) |dbg_index| {
10017 try bw.print(", !dbg !{d}", .{dbg_index});10171 try w.print(", !dbg !{d}", .{dbg_index});
10018 }10172 }
10019 try bw.writeByte('\n');10173 try w.writeByte('\n');
10020 }10174 }
10021 try bw.writeByte('}');10175 try w.writeByte('}');
10022 }10176 }
10023 try bw.writeByte('\n');10177 try w.writeByte('\n');
10024 }10178 }
1002510179
10026 if (attribute_groups.count() > 0) {10180 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;
10028 for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group|10182 for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group|
10029 try bw.print(10183 try w.print(
10030 \\attributes #{d} = {{{f#"} }}10184 \\attributes #{d} = {{{f} }}
10031 \\10185 \\
10032 , .{ attribute_group_index, attribute_group.fmt(self) });10186 , .{ attribute_group_index, attribute_group.fmt(self, .{ .pound = true, .quote = true }) });
10033 }10187 }
1003410188
10035 if (self.metadata_named.count() > 0) {10189 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;
10037 for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| {10191 for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| {
10038 const elements: []const Metadata =10192 const elements: []const Metadata =
10039 @ptrCast(self.metadata_extra.items[data.index..][0..data.len]);10193 @ptrCast(self.metadata_extra.items[data.index..][0..data.len]);
10040 try bw.writeByte('!');10194 try w.writeByte('!');
10041 try printEscapedString(name.slice(self), .quote_unless_valid_identifier, bw);10195 try printEscapedString(name.slice(self), .quote_unless_valid_identifier, w);
10042 try bw.writeAll(" = !{");10196 try w.writeAll(" = !{");
10043 metadata_formatter.need_comma = false;10197 metadata_formatter.need_comma = false;
10044 defer metadata_formatter.need_comma = undefined;10198 defer metadata_formatter.need_comma = undefined;
10045 for (elements) |element| try bw.print("{f}", .{try metadata_formatter.fmt("", element)});10199 for (elements) |element| try w.print("{f}", .{try metadata_formatter.fmt("", element, null)});
10046 try bw.writeAll("}\n");10200 try w.writeAll("}\n");
10047 }10201 }
10048 }10202 }
1004910203
10050 if (metadata_formatter.map.count() > 0) {10204 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;
10052 var metadata_index: usize = 0;10206 var metadata_index: usize = 0;
10053 while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) {10207 while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) {
10054 @setEvalBranchQuota(10_000);10208 @setEvalBranchQuota(10_000);
10055 try bw.print("!{d} = ", .{metadata_index});10209 try w.print("!{d} = ", .{metadata_index});
10056 metadata_formatter.need_comma = false;10210 metadata_formatter.need_comma = false;
10057 defer metadata_formatter.need_comma = undefined;10211 defer metadata_formatter.need_comma = undefined;
1005810212
...@@ -10065,7 +10219,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10065,7 +10219,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10065 .scope = location.scope,10219 .scope = location.scope,
10066 .inlinedAt = location.inlined_at,10220 .inlinedAt = location.inlined_at,
10067 .isImplicitCode = false,10221 .isImplicitCode = false,
10068 }, bw);10222 }, w);
10069 continue;10223 continue;
10070 },10224 },
10071 .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)),10225 .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)),
...@@ -10081,7 +10235,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10081,7 +10235,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10081 .checksumkind = null,10235 .checksumkind = null,
10082 .checksum = null,10236 .checksum = null,
10083 .source = null,10237 .source = null,
10084 }, bw);10238 }, w);
10085 },10239 },
10086 .compile_unit,10240 .compile_unit,
10087 .@"compile_unit optimized",10241 .@"compile_unit optimized",
...@@ -10112,7 +10266,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10112,7 +10266,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10112 .rangesBaseAddress = null,10266 .rangesBaseAddress = null,
10113 .sysroot = null,10267 .sysroot = null,
10114 .sdk = null,10268 .sdk = null,
10115 }, bw);10269 }, w);
10116 },10270 },
10117 .subprogram,10271 .subprogram,
10118 .@"subprogram local",10272 .@"subprogram local",
...@@ -10146,7 +10300,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10146,7 +10300,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10146 .thrownTypes = null,10300 .thrownTypes = null,
10147 .annotations = null,10301 .annotations = null,
10148 .targetFuncName = null,10302 .targetFuncName = null,
10149 }, bw);10303 }, w);
10150 },10304 },
10151 .lexical_block => {10305 .lexical_block => {
10152 const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data);10306 const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data);
...@@ -10155,7 +10309,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10155,7 +10309,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10155 .file = extra.file,10309 .file = extra.file,
10156 .line = extra.line,10310 .line = extra.line,
10157 .column = extra.column,10311 .column = extra.column,
10158 }, bw);10312 }, w);
10159 },10313 },
10160 .location => {10314 .location => {
10161 const extra = self.metadataExtraData(Metadata.Location, metadata_item.data);10315 const extra = self.metadataExtraData(Metadata.Location, metadata_item.data);
...@@ -10165,7 +10319,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10165,7 +10319,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10165 .scope = extra.scope,10319 .scope = extra.scope,
10166 .inlinedAt = extra.inlined_at,10320 .inlinedAt = extra.inlined_at,
10167 .isImplicitCode = false,10321 .isImplicitCode = false,
10168 }, bw);10322 }, w);
10169 },10323 },
10170 .basic_bool_type,10324 .basic_bool_type,
10171 .basic_unsigned_type,10325 .basic_unsigned_type,
...@@ -10194,7 +10348,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10194,7 +10348,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10194 else => unreachable,10348 else => unreachable,
10195 }),10349 }),
10196 .flags = null,10350 .flags = null,
10197 }, bw);10351 }, w);
10198 },10352 },
10199 .composite_struct_type,10353 .composite_struct_type,
10200 .composite_union_type,10354 .composite_union_type,
...@@ -10239,7 +10393,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10239,7 +10393,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10239 .allocated = null,10393 .allocated = null,
10240 .rank = null,10394 .rank = null,
10241 .annotations = null,10395 .annotations = null,
10242 }, bw);10396 }, w);
10243 },10397 },
10244 .derived_pointer_type,10398 .derived_pointer_type,
10245 .derived_member_type,10399 .derived_member_type,
...@@ -10272,7 +10426,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10272,7 +10426,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10272 .extraData = null,10426 .extraData = null,
10273 .dwarfAddressSpace = null,10427 .dwarfAddressSpace = null,
10274 .annotations = null,10428 .annotations = null,
10275 }, bw);10429 }, w);
10276 },10430 },
10277 .subroutine_type => {10431 .subroutine_type => {
10278 const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data);10432 const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data);
...@@ -10280,7 +10434,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10280,7 +10434,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10280 .flags = null,10434 .flags = null,
10281 .cc = null,10435 .cc = null,
10282 .types = extra.types_tuple,10436 .types = extra.types_tuple,
10283 }, bw);10437 }, w);
10284 },10438 },
10285 .enumerator_unsigned,10439 .enumerator_unsigned,
10286 .enumerator_signed_positive,10440 .enumerator_signed_positive,
...@@ -10330,7 +10484,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10330,7 +10484,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10330 => false,10484 => false,
10331 else => unreachable,10485 else => unreachable,
10332 },10486 },
10333 }, bw);10487 }, w);
10334 },10488 },
10335 .subrange => {10489 .subrange => {
10336 const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data);10490 const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data);
...@@ -10339,34 +10493,34 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10339,34 +10493,34 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10339 .lowerBound = extra.lower_bound,10493 .lowerBound = extra.lower_bound,
10340 .upperBound = null,10494 .upperBound = null,
10341 .stride = null,10495 .stride = null,
10342 }, bw);10496 }, w);
10343 },10497 },
10344 .tuple => {10498 .tuple => {
10345 var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data);10499 var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data);
10346 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);10500 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
10347 try bw.writeAll("!{");10501 try w.writeAll("!{");
10348 for (elements) |element| try bw.print("{[element]f%}", .{10502 for (elements) |element| try w.print("{[element]f}", .{
10349 .element = try metadata_formatter.fmt("", element),10503 .element = try metadata_formatter.fmt("", element, .{ .percent = true }),
10350 });10504 });
10351 try bw.writeAll("}\n");10505 try w.writeAll("}\n");
10352 },10506 },
10353 .str_tuple => {10507 .str_tuple => {
10354 var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data);10508 var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data);
10355 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);10509 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
10356 try bw.print("!{{{[str]f%}", .{10510 try w.print("!{{{[str]f}", .{
10357 .str = try metadata_formatter.fmt("", extra.data.str),10511 .str = try metadata_formatter.fmt("", extra.data.str, .{ .percent = true }),
10358 });10512 });
10359 for (elements) |element| try bw.print("{[element]f%}", .{10513 for (elements) |element| try w.print("{[element]f}", .{
10360 .element = try metadata_formatter.fmt("", element),10514 .element = try metadata_formatter.fmt("", element, .{ .percent = true }),
10361 });10515 });
10362 try bw.writeAll("}\n");10516 try w.writeAll("}\n");
10363 },10517 },
10364 .module_flag => {10518 .module_flag => {
10365 const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data);10519 const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data);
10366 try bw.print("!{{{[behavior]f%}{[name]f%}{[constant]f%}}}\n", .{10520 try w.print("!{{{[behavior]f}{[name]f}{[constant]f}}}\n", .{
10367 .behavior = try metadata_formatter.fmt("", extra.behavior),10521 .behavior = try metadata_formatter.fmt("", extra.behavior, .{ .percent = true }),
10368 .name = try metadata_formatter.fmt("", extra.name),10522 .name = try metadata_formatter.fmt("", extra.name, .{ .percent = true }),
10369 .constant = try metadata_formatter.fmt("", extra.constant),10523 .constant = try metadata_formatter.fmt("", extra.constant, .{ .percent = true }),
10370 });10524 });
10371 },10525 },
10372 .local_var => {10526 .local_var => {
...@@ -10381,7 +10535,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10381,7 +10535,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10381 .flags = null,10535 .flags = null,
10382 .@"align" = null,10536 .@"align" = null,
10383 .annotations = null,10537 .annotations = null,
10384 }, bw);10538 }, w);
10385 },10539 },
10386 .parameter => {10540 .parameter => {
10387 const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data);10541 const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data);
...@@ -10395,7 +10549,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10395,7 +10549,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10395 .flags = null,10549 .flags = null,
10396 .@"align" = null,10550 .@"align" = null,
10397 .annotations = null,10551 .annotations = null,
10398 }, bw);10552 }, w);
10399 },10553 },
10400 .global_var,10554 .global_var,
10401 .@"global_var local",10555 .@"global_var local",
...@@ -10418,7 +10572,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10418,7 +10572,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10418 .templateParams = null,10572 .templateParams = null,
10419 .@"align" = null,10573 .@"align" = null,
10420 .annotations = null,10574 .annotations = null,
10421 }, bw);10575 }, w);
10422 },10576 },
10423 .global_var_expression => {10577 .global_var_expression => {
10424 const extra =10578 const extra =
...@@ -10426,7 +10580,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {...@@ -10426,7 +10580,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void {
10426 try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{10580 try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{
10427 .@"var" = extra.variable,10581 .@"var" = extra.variable,
10428 .expr = extra.expression,10582 .expr = extra.expression,
10429 }, bw);10583 }, w);
10430 },10584 },
10431 }10585 }
10432 }10586 }
...@@ -10445,18 +10599,18 @@ fn isValidIdentifier(id: []const u8) bool {...@@ -10445,18 +10599,18 @@ fn isValidIdentifier(id: []const u8) bool {
10445}10599}
1044610600
10447const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier };10601const 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 {
10449 const need_quotes = switch (quotes) {10603 const need_quotes = switch (quotes) {
10450 .always_quote => true,10604 .always_quote => true,
10451 .quote_unless_valid_identifier => !isValidIdentifier(slice),10605 .quote_unless_valid_identifier => !isValidIdentifier(slice),
10452 };10606 };
10453 if (need_quotes) try bw.writeByte('"');10607 if (need_quotes) try w.writeByte('"');
10454 for (slice) |byte| switch (byte) {10608 for (slice) |byte| switch (byte) {
10455 '\\' => try bw.writeAll("\\\\"),10609 '\\' => try w.writeAll("\\\\"),
10456 ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try bw.writeByte(byte),10610 ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try w.writeByte(byte),
10457 else => try bw.print("\\{X:0>2}", .{byte}),10611 else => try w.print("\\{X:0>2}", .{byte}),
10458 };10612 };
10459 if (need_quotes) try bw.writeByte('"');10613 if (need_quotes) try w.writeByte('"');
10460}10614}
1046110615
10462fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void {10616fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void {
...@@ -15084,13 +15238,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco...@@ -15084,13 +15238,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco
15084 return bitcode.toOwnedSlice();15238 return bitcode.toOwnedSlice();
15085}15239}
1508615240
15087const std = @import("../../std.zig");15241const FormatFlags = struct {
15088const Allocator = std.mem.Allocator;15242 comma: bool = false,
15089const assert = std.debug.assert;15243 space: bool = false,
15090const bitcode_writer = @import("bitcode_writer.zig");15244 percent: bool = false,
15091const Builder = @This();15245
15092const builtin = @import("builtin");15246 fn onlyPercent(f: FormatFlags) bool {
15093const DW = std.dwarf;15247 return !f.comma and !f.space and f.percent;
15094const ir = @import("ir.zig");15248 }
15095const log = std.log.scoped(.llvm);15249};
15096const Writer = std.io.Writer;
lib/std/zig/parser_test.zig+10-149
...@@ -1,3 +1,9 @@...@@ -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
1test "zig fmt: remove extra whitespace at start and end of file with comment between" {7test "zig fmt: remove extra whitespace at start and end of file with comment between" {
2 try testTransform(8 try testTransform(
3 \\9 \\
...@@ -341,15 +347,6 @@ test "zig fmt: nosuspend block" {...@@ -341,15 +347,6 @@ test "zig fmt: nosuspend block" {
341 );347 );
342}348}
343349
344test "zig fmt: nosuspend await" {
345 try testCanonical(
346 \\fn foo() void {
347 \\ x = nosuspend await y;
348 \\}
349 \\
350 );
351}
352
353test "zig fmt: container declaration, single line" {350test "zig fmt: container declaration, single line" {
354 try testCanonical(351 try testCanonical(
355 \\const X = struct { foo: i32 };352 \\const X = struct { foo: i32 };
...@@ -1093,18 +1090,6 @@ test "zig fmt: block in slice expression" {...@@ -1093,18 +1090,6 @@ test "zig fmt: block in slice expression" {
1093 );1090 );
1094}1091}
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
1108test "zig fmt: whitespace fixes" {1093test "zig fmt: whitespace fixes" {
1109 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",1094 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
1110 \\test "" {1095 \\test "" {
...@@ -1549,17 +1534,6 @@ test "zig fmt: spaces around slice operator" {...@@ -1549,17 +1534,6 @@ test "zig fmt: spaces around slice operator" {
1549 );1534 );
1550}1535}
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
1563test "zig fmt: 2nd arg multiline string" {1537test "zig fmt: 2nd arg multiline string" {
1564 try testCanonical(1538 try testCanonical(
1565 \\comptime {1539 \\comptime {
...@@ -2770,11 +2744,11 @@ test "zig fmt: preserve spacing" {...@@ -2770,11 +2744,11 @@ test "zig fmt: preserve spacing" {
2770 \\const std = @import("std");2744 \\const std = @import("std");
2771 \\2745 \\
2772 \\pub fn main() !void {2746 \\pub fn main() !void {
2773 \\ var stdout_file = std.io.getStdOut;2747 \\ var stdout_file = std.lol.abcd;
2774 \\ var stdout_file = std.io.getStdOut;2748 \\ var stdout_file = std.lol.abcd;
2775 \\2749 \\
2776 \\ var stdout_file = std.io.getStdOut;2750 \\ var stdout_file = std.lol.abcd;
2777 \\ var stdout_file = std.io.getStdOut;2751 \\ var stdout_file = std.lol.abcd;
2778 \\}2752 \\}
2779 \\2753 \\
2780 );2754 );
...@@ -3946,27 +3920,6 @@ test "zig fmt: inline asm" {...@@ -3946,27 +3920,6 @@ test "zig fmt: inline asm" {
3946 );3920 );
3947}3921}
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
3970test "zig fmt: nosuspend" {3923test "zig fmt: nosuspend" {
3971 try testCanonical(3924 try testCanonical(
3972 \\const a = nosuspend foo();3925 \\const a = nosuspend foo();
...@@ -3989,14 +3942,6 @@ test "zig fmt: Block after if" {...@@ -3989,14 +3942,6 @@ test "zig fmt: Block after if" {
3989 );3942 );
3990}3943}
39913944
3992test "zig fmt: usingnamespace" {
3993 try testCanonical(
3994 \\usingnamespace @import("std");
3995 \\pub usingnamespace @import("std");
3996 \\
3997 );
3998}
3999
4000test "zig fmt: string identifier" {3945test "zig fmt: string identifier" {
4001 try testCanonical(3946 try testCanonical(
4002 \\const @"a b" = @"c d".@"e f";3947 \\const @"a b" = @"c d".@"e f";
...@@ -5140,17 +5085,6 @@ test "zig fmt: line comment after multiline single expr if statement with multil...@@ -5140,17 +5085,6 @@ test "zig fmt: line comment after multiline single expr if statement with multil
5140 );5085 );
5141}5086}
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
5154test "zig fmt: respect extra newline between switch items" {5088test "zig fmt: respect extra newline between switch items" {
5155 try testCanonical(5089 try testCanonical(
5156 \\const a = switch (b) {5090 \\const a = switch (b) {
...@@ -5719,34 +5653,6 @@ test "zig fmt: canonicalize symbols (primitive types)" {...@@ -5719,34 +5653,6 @@ test "zig fmt: canonicalize symbols (primitive types)" {
5719 );5653 );
5720}5654}
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
5750test "zig fmt: no space before newline before multiline string" {5656test "zig fmt: no space before newline before multiline string" {
5751 try testCanonical(5657 try testCanonical(
5752 \\const S = struct {5658 \\const S = struct {
...@@ -6181,29 +6087,6 @@ test "recovery: missing return type" {...@@ -6181,29 +6087,6 @@ test "recovery: missing return type" {
6181 });6087 });
6182}6088}
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
6207test "recovery: invalid extern/inline" {6090test "recovery: invalid extern/inline" {
6208 try testError(6091 try testError(
6209 \\inline test "" { a & b; }6092 \\inline test "" { a & b; }
...@@ -6232,22 +6115,6 @@ test "recovery: missing semicolon" {...@@ -6232,22 +6115,6 @@ test "recovery: missing semicolon" {
6232 });6115 });
6233}6116}
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
6251// TODO after https://github.com/ziglang/zig/issues/35 is implemented,6118// TODO after https://github.com/ziglang/zig/issues/35 is implemented,
6252// we should be able to recover from this *at any indentation level*,6119// we should be able to recover from this *at any indentation level*,
6253// reporting a parse error and yet also parsing all the decls even6120// reporting a parse error and yet also parsing all the decls even
...@@ -6454,12 +6321,6 @@ test "ampersand" {...@@ -6454,12 +6321,6 @@ test "ampersand" {
6454 , &.{});6321 , &.{});
6455}6322}
64566323
6457const std = @import("std");
6458const mem = std.mem;
6459const print = std.debug.print;
6460const io = std.io;
6461const maxInt = std.math.maxInt;
6462
6463var fixed_buffer_mem: [100 * 1024]u8 = undefined;6324var fixed_buffer_mem: [100 * 1024]u8 = undefined;
64646325
6465fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {6326fn 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) {...@@ -45,50 +45,49 @@ pub const Error = union(enum) {
45 raw_string: []const u8,45 raw_string: []const u8,
46 };46 };
4747
48 fn formatMessage(self: FormatMessage, bw: *Writer, comptime f: []const u8) !void {48 fn formatMessage(self: FormatMessage, writer: *std.io.Writer) std.io.Writer.Error!void {
49 _ = f;
50 switch (self.err) {49 switch (self.err) {
51 .invalid_escape_character => |bad_index| try bw.print(50 .invalid_escape_character => |bad_index| try writer.print(
52 "invalid escape character: '{c}'",51 "invalid escape character: '{c}'",
53 .{self.raw_string[bad_index]},52 .{self.raw_string[bad_index]},
54 ),53 ),
55 .expected_hex_digit => |bad_index| try bw.print(54 .expected_hex_digit => |bad_index| try writer.print(
56 "expected hex digit, found '{c}'",55 "expected hex digit, found '{c}'",
57 .{self.raw_string[bad_index]},56 .{self.raw_string[bad_index]},
58 ),57 ),
59 .empty_unicode_escape_sequence => try bw.writeAll(58 .empty_unicode_escape_sequence => try writer.writeAll(
60 "empty unicode escape sequence",59 "empty unicode escape sequence",
61 ),60 ),
62 .expected_hex_digit_or_rbrace => |bad_index| try bw.print(61 .expected_hex_digit_or_rbrace => |bad_index| try writer.print(
63 "expected hex digit or '}}', found '{c}'",62 "expected hex digit or '}}', found '{c}'",
64 .{self.raw_string[bad_index]},63 .{self.raw_string[bad_index]},
65 ),64 ),
66 .invalid_unicode_codepoint => try bw.writeAll(65 .invalid_unicode_codepoint => try writer.writeAll(
67 "unicode escape does not correspond to a valid unicode scalar value",66 "unicode escape does not correspond to a valid unicode scalar value",
68 ),67 ),
69 .expected_lbrace => |bad_index| try bw.print(68 .expected_lbrace => |bad_index| try writer.print(
70 "expected '{{', found '{c}'",69 "expected '{{', found '{c}'",
71 .{self.raw_string[bad_index]},70 .{self.raw_string[bad_index]},
72 ),71 ),
73 .expected_rbrace => |bad_index| try bw.print(72 .expected_rbrace => |bad_index| try writer.print(
74 "expected '}}', found '{c}'",73 "expected '}}', found '{c}'",
75 .{self.raw_string[bad_index]},74 .{self.raw_string[bad_index]},
76 ),75 ),
77 .expected_single_quote => |bad_index| try bw.print(76 .expected_single_quote => |bad_index| try writer.print(
78 "expected single quote ('), found '{c}'",77 "expected single quote ('), found '{c}'",
79 .{self.raw_string[bad_index]},78 .{self.raw_string[bad_index]},
80 ),79 ),
81 .invalid_character => |bad_index| try bw.print(80 .invalid_character => |bad_index| try writer.print(
82 "invalid byte in string or character literal: '{c}'",81 "invalid byte in string or character literal: '{c}'",
83 .{self.raw_string[bad_index]},82 .{self.raw_string[bad_index]},
84 ),83 ),
85 .empty_char_literal => try bw.writeAll(84 .empty_char_literal => try writer.writeAll(
86 "empty character literal",85 "empty character literal",
87 ),86 ),
88 }87 }
89 }88 }
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) {
92 return .{ .data = .{91 return .{ .data = .{
93 .err = self,92 .err = self,
94 .raw_string = raw_string,93 .raw_string = raw_string,
...@@ -318,6 +317,7 @@ test parseCharLiteral {...@@ -318,6 +317,7 @@ test parseCharLiteral {
318}317}
319318
320/// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type.319/// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type.
320///
321/// Asserts `bytes` has '"' at beginning and end.321/// Asserts `bytes` has '"' at beginning and end.
322pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result {322pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result {
323 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');323 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 {...@@ -388,7 +388,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
388 const current_arch = builtin.cpu.arch;388 const current_arch = builtin.cpu.arch;
389 switch (current_arch) {389 switch (current_arch) {
390 .arm, .armeb, .thumb, .thumbeb => {390 .arm, .armeb, .thumb, .thumbeb => {
391 return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null;391 return ArmCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
392 },392 },
393 .aarch64, .aarch64_be => {393 .aarch64, .aarch64_be => {
394 const registers = [12]u64{394 const registers = [12]u64{
...@@ -410,13 +410,13 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {...@@ -410,13 +410,13 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
410 return core;410 return core;
411 },411 },
412 .sparc64 => {412 .sparc64 => {
413 return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;413 return SparcCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
414 },414 },
415 .powerpc, .powerpcle, .powerpc64, .powerpc64le => {415 .powerpc, .powerpcle, .powerpc64, .powerpc64le => {
416 return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null;416 return PowerpcCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
417 },417 },
418 .riscv64, .riscv32 => {418 .riscv64, .riscv32 => {
419 return RiscvCpuinfoParser.parse(current_arch, f.reader()) catch null;419 return RiscvCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null;
420 },420 },
421 else => {},421 else => {},
422 }422 }
lib/std/zig/tokenizer.zig-9
...@@ -17,8 +17,6 @@ pub const Token = struct {...@@ -17,8 +17,6 @@ pub const Token = struct {
17 .{ "anyframe", .keyword_anyframe },17 .{ "anyframe", .keyword_anyframe },
18 .{ "anytype", .keyword_anytype },18 .{ "anytype", .keyword_anytype },
19 .{ "asm", .keyword_asm },19 .{ "asm", .keyword_asm },
20 .{ "async", .keyword_async },
21 .{ "await", .keyword_await },
22 .{ "break", .keyword_break },20 .{ "break", .keyword_break },
23 .{ "callconv", .keyword_callconv },21 .{ "callconv", .keyword_callconv },
24 .{ "catch", .keyword_catch },22 .{ "catch", .keyword_catch },
...@@ -55,7 +53,6 @@ pub const Token = struct {...@@ -55,7 +53,6 @@ pub const Token = struct {
55 .{ "try", .keyword_try },53 .{ "try", .keyword_try },
56 .{ "union", .keyword_union },54 .{ "union", .keyword_union },
57 .{ "unreachable", .keyword_unreachable },55 .{ "unreachable", .keyword_unreachable },
58 .{ "usingnamespace", .keyword_usingnamespace },
59 .{ "var", .keyword_var },56 .{ "var", .keyword_var },
60 .{ "volatile", .keyword_volatile },57 .{ "volatile", .keyword_volatile },
61 .{ "while", .keyword_while },58 .{ "while", .keyword_while },
...@@ -146,8 +143,6 @@ pub const Token = struct {...@@ -146,8 +143,6 @@ pub const Token = struct {
146 keyword_anyframe,143 keyword_anyframe,
147 keyword_anytype,144 keyword_anytype,
148 keyword_asm,145 keyword_asm,
149 keyword_async,
150 keyword_await,
151 keyword_break,146 keyword_break,
152 keyword_callconv,147 keyword_callconv,
153 keyword_catch,148 keyword_catch,
...@@ -184,7 +179,6 @@ pub const Token = struct {...@@ -184,7 +179,6 @@ pub const Token = struct {
184 keyword_try,179 keyword_try,
185 keyword_union,180 keyword_union,
186 keyword_unreachable,181 keyword_unreachable,
187 keyword_usingnamespace,
188 keyword_var,182 keyword_var,
189 keyword_volatile,183 keyword_volatile,
190 keyword_while,184 keyword_while,
...@@ -273,8 +267,6 @@ pub const Token = struct {...@@ -273,8 +267,6 @@ pub const Token = struct {
273 .keyword_anyframe => "anyframe",267 .keyword_anyframe => "anyframe",
274 .keyword_anytype => "anytype",268 .keyword_anytype => "anytype",
275 .keyword_asm => "asm",269 .keyword_asm => "asm",
276 .keyword_async => "async",
277 .keyword_await => "await",
278 .keyword_break => "break",270 .keyword_break => "break",
279 .keyword_callconv => "callconv",271 .keyword_callconv => "callconv",
280 .keyword_catch => "catch",272 .keyword_catch => "catch",
...@@ -311,7 +303,6 @@ pub const Token = struct {...@@ -311,7 +303,6 @@ pub const Token = struct {
311 .keyword_try => "try",303 .keyword_try => "try",
312 .keyword_union => "union",304 .keyword_union => "union",
313 .keyword_unreachable => "unreachable",305 .keyword_unreachable => "unreachable",
314 .keyword_usingnamespace => "usingnamespace",
315 .keyword_var => "var",306 .keyword_var => "var",
316 .keyword_volatile => "volatile",307 .keyword_volatile => "volatile",
317 .keyword_while => "while",308 .keyword_while => "while",
lib/std/zon/parse.zig+113-130
...@@ -64,22 +64,14 @@ pub const Error = union(enum) {...@@ -64,22 +64,14 @@ pub const Error = union(enum) {
64 }64 }
65 };65 };
6666
67 fn formatMessage(67 fn formatMessage(self: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
68 self: []const u8,
69 comptime f: []const u8,
70 options: std.fmt.FormatOptions,
71 writer: anytype,
72 ) !void {
73 _ = f;
74 _ = options;
75
76 // Just writes the string for now, but we're keeping this behind a formatter so we have68 // Just writes the string for now, but we're keeping this behind a formatter so we have
77 // the option to extend it in the future to print more advanced messages (like `Error`69 // the option to extend it in the future to print more advanced messages (like `Error`
78 // does) without breaking the API.70 // does) without breaking the API.
79 try writer.writeAll(self);71 try w.writeAll(self);
80 }72 }
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) {
83 return .{ .data = switch (self) {75 return .{ .data = switch (self) {
84 .zoir => |note| note.msg.get(diag.zoir),76 .zoir => |note| note.msg.get(diag.zoir),
85 .type_check => |note| note.msg,77 .type_check => |note| note.msg,
...@@ -155,21 +147,14 @@ pub const Error = union(enum) {...@@ -155,21 +147,14 @@ pub const Error = union(enum) {
155 diag: *const Diagnostics,147 diag: *const Diagnostics,
156 };148 };
157149
158 fn formatMessage(150 fn formatMessage(self: FormatMessage, w: *std.io.Writer) std.io.Writer.Error!void {
159 self: FormatMessage,
160 comptime f: []const u8,
161 options: std.fmt.FormatOptions,
162 writer: anytype,
163 ) !void {
164 _ = f;
165 _ = options;
166 switch (self.err) {151 switch (self.err) {
167 .zoir => |err| try writer.writeAll(err.msg.get(self.diag.zoir)),152 .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)),
168 .type_check => |tc| try writer.writeAll(tc.message),153 .type_check => |tc| try w.writeAll(tc.message),
169 }154 }
170 }155 }
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) {
173 return .{ .data = .{158 return .{ .data = .{
174 .err = self,159 .err = self,
175 .diag = diag,160 .diag = diag,
...@@ -241,25 +226,18 @@ pub const Diagnostics = struct {...@@ -241,25 +226,18 @@ pub const Diagnostics = struct {
241 return .{ .diag = self };226 return .{ .diag = self };
242 }227 }
243228
244 pub fn format(229 pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void {
245 self: *const @This(),
246 comptime fmt: []const u8,
247 options: std.fmt.FormatOptions,
248 writer: anytype,
249 ) !void {
250 _ = fmt;
251 _ = options;
252 var errors = self.iterateErrors();230 var errors = self.iterateErrors();
253 while (errors.next()) |err| {231 while (errors.next()) |err| {
254 const loc = err.getLocation(self);232 const loc = err.getLocation(self);
255 const msg = err.fmtMessage(self);233 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
258 var notes = err.iterateNotes(self);236 var notes = err.iterateNotes(self);
259 while (notes.next()) |note| {237 while (notes.next()) |note| {
260 const note_loc = note.getLocation(self);238 const note_loc = note.getLocation(self);
261 const note_msg = note.fmtMessage(self);239 const note_msg = note.fmtMessage(self);
262 try writer.print("{}:{}: note: {s}\n", .{240 try w.print("{d}:{d}: note: {f}\n", .{
263 note_loc.line + 1,241 note_loc.line + 1,
264 note_loc.column + 1,242 note_loc.column + 1,
265 note_msg,243 note_msg,
...@@ -648,7 +626,7 @@ const Parser = struct {...@@ -648,7 +626,7 @@ const Parser = struct {
648 .failure => |err| {626 .failure => |err| {
649 const token = self.ast.nodeMainToken(ast_node);627 const token = self.ast.nodeMainToken(ast_node);
650 const raw_string = self.ast.tokenSlice(token);628 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)});
652 },630 },
653 }631 }
654632
...@@ -1089,7 +1067,10 @@ const Parser = struct {...@@ -1089,7 +1067,10 @@ const Parser = struct {
1089 try buf.appendSlice(gpa, msg);1067 try buf.appendSlice(gpa, msg);
1090 inline for (info.fields, 0..) |field_info, i| {1068 inline for (info.fields, 0..) |field_info, i| {
1091 if (i != 0) try buf.appendSlice(gpa, ", ");1069 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 })});
1093 }1074 }
1094 break :b .{1075 break :b .{
1095 .token = token,1076 .token = token,
...@@ -1300,7 +1281,7 @@ test "std.zon ast errors" {...@@ -1300,7 +1281,7 @@ test "std.zon ast errors" {
1300 error.ParseZon,1281 error.ParseZon,
1301 fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &diag, .{}),1282 fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &diag, .{}),
1302 );1283 );
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});
1304}1285}
13051286
1306test "std.zon comments" {1287test "std.zon comments" {
...@@ -1322,7 +1303,7 @@ test "std.zon comments" {...@@ -1322,7 +1303,7 @@ test "std.zon comments" {
1322 , &diag, .{}));1303 , &diag, .{}));
1323 try std.testing.expectFmt(1304 try std.testing.expectFmt(
1324 "1:1: error: expected expression, found 'a document comment'\n",1305 "1:1: error: expected expression, found 'a document comment'\n",
1325 "{}",1306 "{f}",
1326 .{diag},1307 .{diag},
1327 );1308 );
1328 }1309 }
...@@ -1343,7 +1324,7 @@ test "std.zon failure/oom formatting" {...@@ -1343,7 +1324,7 @@ test "std.zon failure/oom formatting" {
1343 &diag,1324 &diag,
1344 .{},1325 .{},
1345 ));1326 ));
1346 try std.testing.expectFmt("", "{}", .{diag});1327 try std.testing.expectFmt("", "{f}", .{diag});
1347}1328}
13481329
1349test "std.zon fromSlice syntax error" {1330test "std.zon fromSlice syntax error" {
...@@ -1423,7 +1404,7 @@ test "std.zon unions" {...@@ -1423,7 +1404,7 @@ test "std.zon unions" {
1423 \\1:4: note: supported: 'x', 'y'1404 \\1:4: note: supported: 'x', 'y'
1424 \\1405 \\
1425 ,1406 ,
1426 "{}",1407 "{f}",
1427 .{diag},1408 .{diag},
1428 );1409 );
1429 }1410 }
...@@ -1437,7 +1418,7 @@ test "std.zon unions" {...@@ -1437,7 +1418,7 @@ test "std.zon unions" {
1437 error.ParseZon,1418 error.ParseZon,
1438 fromSlice(Union, gpa, ".{.x=1}", &diag, .{}),1419 fromSlice(Union, gpa, ".{.x=1}", &diag, .{}),
1439 );1420 );
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});
1441 }1422 }
14421423
1443 // Extra field1424 // Extra field
...@@ -1449,7 +1430,7 @@ test "std.zon unions" {...@@ -1449,7 +1430,7 @@ test "std.zon unions" {
1449 error.ParseZon,1430 error.ParseZon,
1450 fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}),1431 fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}),
1451 );1432 );
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});
1453 }1434 }
14541435
1455 // No fields1436 // No fields
...@@ -1461,7 +1442,7 @@ test "std.zon unions" {...@@ -1461,7 +1442,7 @@ test "std.zon unions" {
1461 error.ParseZon,1442 error.ParseZon,
1462 fromSlice(Union, gpa, ".{}", &diag, .{}),1443 fromSlice(Union, gpa, ".{}", &diag, .{}),
1463 );1444 );
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});
1465 }1446 }
14661447
1467 // Enum literals cannot coerce into untagged unions1448 // Enum literals cannot coerce into untagged unions
...@@ -1470,7 +1451,7 @@ test "std.zon unions" {...@@ -1470,7 +1451,7 @@ test "std.zon unions" {
1470 var diag: Diagnostics = .{};1451 var diag: Diagnostics = .{};
1471 defer diag.deinit(gpa);1452 defer diag.deinit(gpa);
1472 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{}));1453 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});
1474 }1455 }
14751456
1476 // Unknown field for enum literal coercion1457 // Unknown field for enum literal coercion
...@@ -1484,7 +1465,7 @@ test "std.zon unions" {...@@ -1484,7 +1465,7 @@ test "std.zon unions" {
1484 \\1:2: note: supported: 'x'1465 \\1:2: note: supported: 'x'
1485 \\1466 \\
1486 ,1467 ,
1487 "{}",1468 "{f}",
1488 .{diag},1469 .{diag},
1489 );1470 );
1490 }1471 }
...@@ -1495,7 +1476,7 @@ test "std.zon unions" {...@@ -1495,7 +1476,7 @@ test "std.zon unions" {
1495 var diag: Diagnostics = .{};1476 var diag: Diagnostics = .{};
1496 defer diag.deinit(gpa);1477 defer diag.deinit(gpa);
1497 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{}));1478 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});
1499 }1480 }
1500}1481}
15011482
...@@ -1551,7 +1532,7 @@ test "std.zon structs" {...@@ -1551,7 +1532,7 @@ test "std.zon structs" {
1551 \\1:12: note: supported: 'x', 'y'1532 \\1:12: note: supported: 'x', 'y'
1552 \\1533 \\
1553 ,1534 ,
1554 "{}",1535 "{f}",
1555 .{diag},1536 .{diag},
1556 );1537 );
1557 }1538 }
...@@ -1569,7 +1550,7 @@ test "std.zon structs" {...@@ -1569,7 +1550,7 @@ test "std.zon structs" {
1569 \\1:4: error: duplicate struct field name1550 \\1:4: error: duplicate struct field name
1570 \\1:12: note: duplicate name here1551 \\1:12: note: duplicate name here
1571 \\1552 \\
1572 , "{}", .{diag});1553 , "{f}", .{diag});
1573 }1554 }
15741555
1575 // Ignore unknown fields1556 // Ignore unknown fields
...@@ -1594,7 +1575,7 @@ test "std.zon structs" {...@@ -1594,7 +1575,7 @@ test "std.zon structs" {
1594 \\1:4: error: unexpected field 'x'1575 \\1:4: error: unexpected field 'x'
1595 \\1:4: note: none expected1576 \\1:4: note: none expected
1596 \\1577 \\
1597 , "{}", .{diag});1578 , "{f}", .{diag});
1598 }1579 }
15991580
1600 // Missing field1581 // Missing field
...@@ -1606,7 +1587,7 @@ test "std.zon structs" {...@@ -1606,7 +1587,7 @@ test "std.zon structs" {
1606 error.ParseZon,1587 error.ParseZon,
1607 fromSlice(Vec2, gpa, ".{.x=1.5}", &diag, .{}),1588 fromSlice(Vec2, gpa, ".{.x=1.5}", &diag, .{}),
1608 );1589 );
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});
1610 }1591 }
16111592
1612 // Default field1593 // Default field
...@@ -1633,7 +1614,7 @@ test "std.zon structs" {...@@ -1633,7 +1614,7 @@ test "std.zon structs" {
1633 try std.testing.expectFmt(1614 try std.testing.expectFmt(
1634 \\1:18: error: cannot initialize comptime field1615 \\1:18: error: cannot initialize comptime field
1635 \\1616 \\
1636 , "{}", .{diag});1617 , "{f}", .{diag});
1637 }1618 }
16381619
1639 // Enum field (regression test, we were previously getting the field name in an1620 // Enum field (regression test, we were previously getting the field name in an
...@@ -1663,7 +1644,7 @@ test "std.zon structs" {...@@ -1663,7 +1644,7 @@ test "std.zon structs" {
1663 \\1:1: error: types are not available in ZON1644 \\1:1: error: types are not available in ZON
1664 \\1:1: note: replace the type with '.'1645 \\1:1: note: replace the type with '.'
1665 \\1646 \\
1666 , "{}", .{diag});1647 , "{f}", .{diag});
1667 }1648 }
16681649
1669 // Arrays1650 // Arrays
...@@ -1676,7 +1657,7 @@ test "std.zon structs" {...@@ -1676,7 +1657,7 @@ test "std.zon structs" {
1676 \\1:1: error: types are not available in ZON1657 \\1:1: error: types are not available in ZON
1677 \\1:1: note: replace the type with '.'1658 \\1:1: note: replace the type with '.'
1678 \\1659 \\
1679 , "{}", .{diag});1660 , "{f}", .{diag});
1680 }1661 }
16811662
1682 // Slices1663 // Slices
...@@ -1689,7 +1670,7 @@ test "std.zon structs" {...@@ -1689,7 +1670,7 @@ test "std.zon structs" {
1689 \\1:1: error: types are not available in ZON1670 \\1:1: error: types are not available in ZON
1690 \\1:1: note: replace the type with '.'1671 \\1:1: note: replace the type with '.'
1691 \\1672 \\
1692 , "{}", .{diag});1673 , "{f}", .{diag});
1693 }1674 }
16941675
1695 // Tuples1676 // Tuples
...@@ -1708,7 +1689,7 @@ test "std.zon structs" {...@@ -1708,7 +1689,7 @@ test "std.zon structs" {
1708 \\1:1: error: types are not available in ZON1689 \\1:1: error: types are not available in ZON
1709 \\1:1: note: replace the type with '.'1690 \\1:1: note: replace the type with '.'
1710 \\1691 \\
1711 , "{}", .{diag});1692 , "{f}", .{diag});
1712 }1693 }
17131694
1714 // Nested1695 // Nested
...@@ -1721,7 +1702,7 @@ test "std.zon structs" {...@@ -1721,7 +1702,7 @@ test "std.zon structs" {
1721 \\1:9: error: types are not available in ZON1702 \\1:9: error: types are not available in ZON
1722 \\1:9: note: replace the type with '.'1703 \\1:9: note: replace the type with '.'
1723 \\1704 \\
1724 , "{}", .{diag});1705 , "{f}", .{diag});
1725 }1706 }
1726 }1707 }
1727}1708}
...@@ -1766,7 +1747,7 @@ test "std.zon tuples" {...@@ -1766,7 +1747,7 @@ test "std.zon tuples" {
1766 error.ParseZon,1747 error.ParseZon,
1767 fromSlice(Tuple, gpa, ".{0.5, true, 123}", &diag, .{}),1748 fromSlice(Tuple, gpa, ".{0.5, true, 123}", &diag, .{}),
1768 );1749 );
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});
1770 }1751 }
17711752
1772 // Extra field1753 // Extra field
...@@ -1780,7 +1761,7 @@ test "std.zon tuples" {...@@ -1780,7 +1761,7 @@ test "std.zon tuples" {
1780 );1761 );
1781 try std.testing.expectFmt(1762 try std.testing.expectFmt(
1782 "1:2: error: missing tuple field with index 1\n",1763 "1:2: error: missing tuple field with index 1\n",
1783 "{}",1764 "{f}",
1784 .{diag},1765 .{diag},
1785 );1766 );
1786 }1767 }
...@@ -1794,7 +1775,7 @@ test "std.zon tuples" {...@@ -1794,7 +1775,7 @@ test "std.zon tuples" {
1794 error.ParseZon,1775 error.ParseZon,
1795 fromSlice(Tuple, gpa, ".{.foo = 10.0}", &diag, .{}),1776 fromSlice(Tuple, gpa, ".{.foo = 10.0}", &diag, .{}),
1796 );1777 );
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});
1798 }1779 }
17991780
1800 // Struct with missing field names1781 // Struct with missing field names
...@@ -1806,7 +1787,7 @@ test "std.zon tuples" {...@@ -1806,7 +1787,7 @@ test "std.zon tuples" {
1806 error.ParseZon,1787 error.ParseZon,
1807 fromSlice(Struct, gpa, ".{10.0}", &diag, .{}),1788 fromSlice(Struct, gpa, ".{10.0}", &diag, .{}),
1808 );1789 );
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});
1810 }1791 }
18111792
1812 // Comptime field1793 // Comptime field
...@@ -1826,7 +1807,7 @@ test "std.zon tuples" {...@@ -1826,7 +1807,7 @@ test "std.zon tuples" {
1826 try std.testing.expectFmt(1807 try std.testing.expectFmt(
1827 \\1:9: error: cannot initialize comptime field1808 \\1:9: error: cannot initialize comptime field
1828 \\1809 \\
1829 , "{}", .{diag});1810 , "{f}", .{diag});
1830 }1811 }
1831}1812}
18321813
...@@ -1938,7 +1919,7 @@ test "std.zon arrays and slices" {...@@ -1938,7 +1919,7 @@ test "std.zon arrays and slices" {
1938 );1919 );
1939 try std.testing.expectFmt(1920 try std.testing.expectFmt(
1940 "1:3: error: index 0 outside of array of length 0\n",1921 "1:3: error: index 0 outside of array of length 0\n",
1941 "{}",1922 "{f}",
1942 .{diag},1923 .{diag},
1943 );1924 );
1944 }1925 }
...@@ -1953,7 +1934,7 @@ test "std.zon arrays and slices" {...@@ -1953,7 +1934,7 @@ test "std.zon arrays and slices" {
1953 );1934 );
1954 try std.testing.expectFmt(1935 try std.testing.expectFmt(
1955 "1:8: error: index 1 outside of array of length 1\n",1936 "1:8: error: index 1 outside of array of length 1\n",
1956 "{}",1937 "{f}",
1957 .{diag},1938 .{diag},
1958 );1939 );
1959 }1940 }
...@@ -1968,7 +1949,7 @@ test "std.zon arrays and slices" {...@@ -1968,7 +1949,7 @@ test "std.zon arrays and slices" {
1968 );1949 );
1969 try std.testing.expectFmt(1950 try std.testing.expectFmt(
1970 "1:2: error: expected 2 array elements; found 1\n",1951 "1:2: error: expected 2 array elements; found 1\n",
1971 "{}",1952 "{f}",
1972 .{diag},1953 .{diag},
1973 );1954 );
1974 }1955 }
...@@ -1983,7 +1964,7 @@ test "std.zon arrays and slices" {...@@ -1983,7 +1964,7 @@ test "std.zon arrays and slices" {
1983 );1964 );
1984 try std.testing.expectFmt(1965 try std.testing.expectFmt(
1985 "1:2: error: expected 3 array elements; found 0\n",1966 "1:2: error: expected 3 array elements; found 0\n",
1986 "{}",1967 "{f}",
1987 .{diag},1968 .{diag},
1988 );1969 );
1989 }1970 }
...@@ -1998,7 +1979,7 @@ test "std.zon arrays and slices" {...@@ -1998,7 +1979,7 @@ test "std.zon arrays and slices" {
1998 error.ParseZon,1979 error.ParseZon,
1999 fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),1980 fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
2000 );1981 );
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});
2002 }1983 }
20031984
2004 // Slice1985 // Slice
...@@ -2009,7 +1990,7 @@ test "std.zon arrays and slices" {...@@ -2009,7 +1990,7 @@ test "std.zon arrays and slices" {
2009 error.ParseZon,1990 error.ParseZon,
2010 fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),1991 fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
2011 );1992 );
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});
2013 }1994 }
2014 }1995 }
20151996
...@@ -2023,7 +2004,7 @@ test "std.zon arrays and slices" {...@@ -2023,7 +2004,7 @@ test "std.zon arrays and slices" {
2023 error.ParseZon,2004 error.ParseZon,
2024 fromSlice([3]u8, gpa, "'a'", &diag, .{}),2005 fromSlice([3]u8, gpa, "'a'", &diag, .{}),
2025 );2006 );
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});
2027 }2008 }
20282009
2029 // Slice2010 // Slice
...@@ -2034,7 +2015,7 @@ test "std.zon arrays and slices" {...@@ -2034,7 +2015,7 @@ test "std.zon arrays and slices" {
2034 error.ParseZon,2015 error.ParseZon,
2035 fromSlice([]u8, gpa, "'a'", &diag, .{}),2016 fromSlice([]u8, gpa, "'a'", &diag, .{}),
2036 );2017 );
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});
2038 }2019 }
2039 }2020 }
20402021
...@@ -2048,7 +2029,7 @@ test "std.zon arrays and slices" {...@@ -2048,7 +2029,7 @@ test "std.zon arrays and slices" {
2048 );2029 );
2049 try std.testing.expectFmt(2030 try std.testing.expectFmt(
2050 "1:3: error: pointers are not available in ZON\n",2031 "1:3: error: pointers are not available in ZON\n",
2051 "{}",2032 "{f}",
2052 .{diag},2033 .{diag},
2053 );2034 );
2054 }2035 }
...@@ -2087,7 +2068,7 @@ test "std.zon string literal" {...@@ -2087,7 +2068,7 @@ test "std.zon string literal" {
2087 error.ParseZon,2068 error.ParseZon,
2088 fromSlice([]u8, gpa, "\"abcd\"", &diag, .{}),2069 fromSlice([]u8, gpa, "\"abcd\"", &diag, .{}),
2089 );2070 );
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});
2091 }2072 }
20922073
2093 {2074 {
...@@ -2097,7 +2078,7 @@ test "std.zon string literal" {...@@ -2097,7 +2078,7 @@ test "std.zon string literal" {
2097 error.ParseZon,2078 error.ParseZon,
2098 fromSlice([]u8, gpa, "\\\\abcd", &diag, .{}),2079 fromSlice([]u8, gpa, "\\\\abcd", &diag, .{}),
2099 );2080 );
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});
2101 }2082 }
2102 }2083 }
21032084
...@@ -2114,7 +2095,7 @@ test "std.zon string literal" {...@@ -2114,7 +2095,7 @@ test "std.zon string literal" {
2114 error.ParseZon,2095 error.ParseZon,
2115 fromSlice([4:0]u8, gpa, "\"abcd\"", &diag, .{}),2096 fromSlice([4:0]u8, gpa, "\"abcd\"", &diag, .{}),
2116 );2097 );
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});
2118 }2099 }
21192100
2120 {2101 {
...@@ -2124,7 +2105,7 @@ test "std.zon string literal" {...@@ -2124,7 +2105,7 @@ test "std.zon string literal" {
2124 error.ParseZon,2105 error.ParseZon,
2125 fromSlice([4:0]u8, gpa, "\\\\abcd", &diag, .{}),2106 fromSlice([4:0]u8, gpa, "\\\\abcd", &diag, .{}),
2126 );2107 );
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});
2128 }2109 }
2129 }2110 }
21302111
...@@ -2166,7 +2147,7 @@ test "std.zon string literal" {...@@ -2166,7 +2147,7 @@ test "std.zon string literal" {
2166 error.ParseZon,2147 error.ParseZon,
2167 fromSlice([:1]const u8, gpa, "\"foo\"", &diag, .{}),2148 fromSlice([:1]const u8, gpa, "\"foo\"", &diag, .{}),
2168 );2149 );
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});
2170 }2151 }
21712152
2172 {2153 {
...@@ -2176,7 +2157,7 @@ test "std.zon string literal" {...@@ -2176,7 +2157,7 @@ test "std.zon string literal" {
2176 error.ParseZon,2157 error.ParseZon,
2177 fromSlice([:1]const u8, gpa, "\\\\foo", &diag, .{}),2158 fromSlice([:1]const u8, gpa, "\\\\foo", &diag, .{}),
2178 );2159 );
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});
2180 }2161 }
2181 }2162 }
21822163
...@@ -2188,7 +2169,7 @@ test "std.zon string literal" {...@@ -2188,7 +2169,7 @@ test "std.zon string literal" {
2188 error.ParseZon,2169 error.ParseZon,
2189 fromSlice([]const u8, gpa, "true", &diag, .{}),2170 fromSlice([]const u8, gpa, "true", &diag, .{}),
2190 );2171 );
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});
2192 }2173 }
21932174
2194 // Expecting string literal, getting an incompatible tuple2175 // Expecting string literal, getting an incompatible tuple
...@@ -2199,7 +2180,7 @@ test "std.zon string literal" {...@@ -2199,7 +2180,7 @@ test "std.zon string literal" {
2199 error.ParseZon,2180 error.ParseZon,
2200 fromSlice([]const u8, gpa, ".{false}", &diag, .{}),2181 fromSlice([]const u8, gpa, ".{false}", &diag, .{}),
2201 );2182 );
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});
2203 }2184 }
22042185
2205 // Invalid string literal2186 // Invalid string literal
...@@ -2210,7 +2191,7 @@ test "std.zon string literal" {...@@ -2210,7 +2191,7 @@ test "std.zon string literal" {
2210 error.ParseZon,2191 error.ParseZon,
2211 fromSlice([]const i8, gpa, "\"\\a\"", &diag, .{}),2192 fromSlice([]const i8, gpa, "\"\\a\"", &diag, .{}),
2212 );2193 );
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});
2214 }2195 }
22152196
2216 // Slice wrong child type2197 // Slice wrong child type
...@@ -2222,7 +2203,7 @@ test "std.zon string literal" {...@@ -2222,7 +2203,7 @@ test "std.zon string literal" {
2222 error.ParseZon,2203 error.ParseZon,
2223 fromSlice([]const i8, gpa, "\"a\"", &diag, .{}),2204 fromSlice([]const i8, gpa, "\"a\"", &diag, .{}),
2224 );2205 );
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});
2226 }2207 }
22272208
2228 {2209 {
...@@ -2232,7 +2213,7 @@ test "std.zon string literal" {...@@ -2232,7 +2213,7 @@ test "std.zon string literal" {
2232 error.ParseZon,2213 error.ParseZon,
2233 fromSlice([]const i8, gpa, "\\\\a", &diag, .{}),2214 fromSlice([]const i8, gpa, "\\\\a", &diag, .{}),
2234 );2215 );
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});
2236 }2217 }
2237 }2218 }
22382219
...@@ -2245,7 +2226,7 @@ test "std.zon string literal" {...@@ -2245,7 +2226,7 @@ test "std.zon string literal" {
2245 error.ParseZon,2226 error.ParseZon,
2246 fromSlice([]align(2) const u8, gpa, "\"abc\"", &diag, .{}),2227 fromSlice([]align(2) const u8, gpa, "\"abc\"", &diag, .{}),
2247 );2228 );
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});
2249 }2230 }
22502231
2251 {2232 {
...@@ -2255,7 +2236,7 @@ test "std.zon string literal" {...@@ -2255,7 +2236,7 @@ test "std.zon string literal" {
2255 error.ParseZon,2236 error.ParseZon,
2256 fromSlice([]align(2) const u8, gpa, "\\\\abc", &diag, .{}),2237 fromSlice([]align(2) const u8, gpa, "\\\\abc", &diag, .{}),
2257 );2238 );
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});
2259 }2240 }
2260 }2241 }
22612242
...@@ -2329,7 +2310,7 @@ test "std.zon enum literals" {...@@ -2329,7 +2310,7 @@ test "std.zon enum literals" {
2329 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'2310 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
2330 \\2311 \\
2331 ,2312 ,
2332 "{}",2313 "{f}",
2333 .{diag},2314 .{diag},
2334 );2315 );
2335 }2316 }
...@@ -2347,7 +2328,7 @@ test "std.zon enum literals" {...@@ -2347,7 +2328,7 @@ test "std.zon enum literals" {
2347 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'2328 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
2348 \\2329 \\
2349 ,2330 ,
2350 "{}",2331 "{f}",
2351 .{diag},2332 .{diag},
2352 );2333 );
2353 }2334 }
...@@ -2360,7 +2341,7 @@ test "std.zon enum literals" {...@@ -2360,7 +2341,7 @@ test "std.zon enum literals" {
2360 error.ParseZon,2341 error.ParseZon,
2361 fromSlice(Enum, gpa, "true", &diag, .{}),2342 fromSlice(Enum, gpa, "true", &diag, .{}),
2362 );2343 );
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});
2364 }2345 }
23652346
2366 // Test embedded nulls in an identifier2347 // Test embedded nulls in an identifier
...@@ -2373,7 +2354,7 @@ test "std.zon enum literals" {...@@ -2373,7 +2354,7 @@ test "std.zon enum literals" {
2373 );2354 );
2374 try std.testing.expectFmt(2355 try std.testing.expectFmt(
2375 "1:2: error: identifier cannot contain null bytes\n",2356 "1:2: error: identifier cannot contain null bytes\n",
2376 "{}",2357 "{f}",
2377 .{diag},2358 .{diag},
2378 );2359 );
2379 }2360 }
...@@ -2399,13 +2380,13 @@ test "std.zon parse bool" {...@@ -2399,13 +2380,13 @@ test "std.zon parse bool" {
2399 \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'2380 \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
2400 \\1:2: note: precede identifier with '.' for an enum literal2381 \\1:2: note: precede identifier with '.' for an enum literal
2401 \\2382 \\
2402 , "{}", .{diag});2383 , "{f}", .{diag});
2403 }2384 }
2404 {2385 {
2405 var diag: Diagnostics = .{};2386 var diag: Diagnostics = .{};
2406 defer diag.deinit(gpa);2387 defer diag.deinit(gpa);
2407 try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &diag, .{}));2388 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});
2409 }2390 }
2410}2391}
24112392
...@@ -2478,7 +2459,7 @@ test "std.zon parse int" {...@@ -2478,7 +2459,7 @@ test "std.zon parse int" {
2478 ));2459 ));
2479 try std.testing.expectFmt(2460 try std.testing.expectFmt(
2480 "1:1: error: type 'i66' cannot represent value\n",2461 "1:1: error: type 'i66' cannot represent value\n",
2481 "{}",2462 "{f}",
2482 .{diag},2463 .{diag},
2483 );2464 );
2484 }2465 }
...@@ -2494,7 +2475,7 @@ test "std.zon parse int" {...@@ -2494,7 +2475,7 @@ test "std.zon parse int" {
2494 ));2475 ));
2495 try std.testing.expectFmt(2476 try std.testing.expectFmt(
2496 "1:1: error: type 'i66' cannot represent value\n",2477 "1:1: error: type 'i66' cannot represent value\n",
2497 "{}",2478 "{f}",
2498 .{diag},2479 .{diag},
2499 );2480 );
2500 }2481 }
...@@ -2583,7 +2564,7 @@ test "std.zon parse int" {...@@ -2583,7 +2564,7 @@ test "std.zon parse int" {
2583 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{}));2564 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{}));
2584 try std.testing.expectFmt(2565 try std.testing.expectFmt(
2585 "1:3: error: invalid digit 'a' for decimal base\n",2566 "1:3: error: invalid digit 'a' for decimal base\n",
2586 "{}",2567 "{f}",
2587 .{diag},2568 .{diag},
2588 );2569 );
2589 }2570 }
...@@ -2593,7 +2574,7 @@ test "std.zon parse int" {...@@ -2593,7 +2574,7 @@ test "std.zon parse int" {
2593 var diag: Diagnostics = .{};2574 var diag: Diagnostics = .{};
2594 defer diag.deinit(gpa);2575 defer diag.deinit(gpa);
2595 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &diag, .{}));2576 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});
2597 }2578 }
25982579
2599 // Failing because an int is out of range2580 // Failing because an int is out of range
...@@ -2603,7 +2584,7 @@ test "std.zon parse int" {...@@ -2603,7 +2584,7 @@ test "std.zon parse int" {
2603 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{}));2584 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{}));
2604 try std.testing.expectFmt(2585 try std.testing.expectFmt(
2605 "1:1: error: type 'u8' cannot represent value\n",2586 "1:1: error: type 'u8' cannot represent value\n",
2606 "{}",2587 "{f}",
2607 .{diag},2588 .{diag},
2608 );2589 );
2609 }2590 }
...@@ -2615,7 +2596,7 @@ test "std.zon parse int" {...@@ -2615,7 +2596,7 @@ test "std.zon parse int" {
2615 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{}));2596 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{}));
2616 try std.testing.expectFmt(2597 try std.testing.expectFmt(
2617 "1:1: error: type 'i8' cannot represent value\n",2598 "1:1: error: type 'i8' cannot represent value\n",
2618 "{}",2599 "{f}",
2619 .{diag},2600 .{diag},
2620 );2601 );
2621 }2602 }
...@@ -2627,7 +2608,7 @@ test "std.zon parse int" {...@@ -2627,7 +2608,7 @@ test "std.zon parse int" {
2627 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{}));2608 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{}));
2628 try std.testing.expectFmt(2609 try std.testing.expectFmt(
2629 "1:1: error: type 'u8' cannot represent value\n",2610 "1:1: error: type 'u8' cannot represent value\n",
2630 "{}",2611 "{f}",
2631 .{diag},2612 .{diag},
2632 );2613 );
2633 }2614 }
...@@ -2639,7 +2620,7 @@ test "std.zon parse int" {...@@ -2639,7 +2620,7 @@ test "std.zon parse int" {
2639 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{}));2620 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{}));
2640 try std.testing.expectFmt(2621 try std.testing.expectFmt(
2641 "1:1: error: type 'u8' cannot represent value\n",2622 "1:1: error: type 'u8' cannot represent value\n",
2642 "{}",2623 "{f}",
2643 .{diag},2624 .{diag},
2644 );2625 );
2645 }2626 }
...@@ -2651,7 +2632,7 @@ test "std.zon parse int" {...@@ -2651,7 +2632,7 @@ test "std.zon parse int" {
2651 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{}));2632 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{}));
2652 try std.testing.expectFmt(2633 try std.testing.expectFmt(
2653 "1:1: error: type 'u8' cannot represent value\n",2634 "1:1: error: type 'u8' cannot represent value\n",
2654 "{}",2635 "{f}",
2655 .{diag},2636 .{diag},
2656 );2637 );
2657 }2638 }
...@@ -2666,7 +2647,7 @@ test "std.zon parse int" {...@@ -2666,7 +2647,7 @@ test "std.zon parse int" {
2666 \\1:2: note: use '0' for an integer zero2647 \\1:2: note: use '0' for an integer zero
2667 \\1:2: note: use '-0.0' for a floating-point signed zero2648 \\1:2: note: use '-0.0' for a floating-point signed zero
2668 \\2649 \\
2669 , "{}", .{diag});2650 , "{f}", .{diag});
2670 }2651 }
26712652
2672 // Negative integer zero casted to float2653 // Negative integer zero casted to float
...@@ -2679,7 +2660,7 @@ test "std.zon parse int" {...@@ -2679,7 +2660,7 @@ test "std.zon parse int" {
2679 \\1:2: note: use '0' for an integer zero2660 \\1:2: note: use '0' for an integer zero
2680 \\1:2: note: use '-0.0' for a floating-point signed zero2661 \\1:2: note: use '-0.0' for a floating-point signed zero
2681 \\2662 \\
2682 , "{}", .{diag});2663 , "{f}", .{diag});
2683 }2664 }
26842665
2685 // Negative float 0 is allowed2666 // Negative float 0 is allowed
...@@ -2695,7 +2676,7 @@ test "std.zon parse int" {...@@ -2695,7 +2676,7 @@ test "std.zon parse int" {
2695 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{}));2676 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{}));
2696 try std.testing.expectFmt(2677 try std.testing.expectFmt(
2697 "1:1: error: expected number or 'inf' after '-'\n",2678 "1:1: error: expected number or 'inf' after '-'\n",
2698 "{}",2679 "{f}",
2699 .{diag},2680 .{diag},
2700 );2681 );
2701 }2682 }
...@@ -2709,7 +2690,7 @@ test "std.zon parse int" {...@@ -2709,7 +2690,7 @@ test "std.zon parse int" {
2709 );2690 );
2710 try std.testing.expectFmt(2691 try std.testing.expectFmt(
2711 "1:1: error: expected number or 'inf' after '-'\n",2692 "1:1: error: expected number or 'inf' after '-'\n",
2712 "{}",2693 "{f}",
2713 .{diag},2694 .{diag},
2714 );2695 );
2715 }2696 }
...@@ -2719,7 +2700,7 @@ test "std.zon parse int" {...@@ -2719,7 +2700,7 @@ test "std.zon parse int" {
2719 var diag: Diagnostics = .{};2700 var diag: Diagnostics = .{};
2720 defer diag.deinit(gpa);2701 defer diag.deinit(gpa);
2721 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &diag, .{}));2702 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});
2723 }2704 }
27242705
2725 // Notes on invalid int literal2706 // Notes on invalid int literal
...@@ -2731,7 +2712,7 @@ test "std.zon parse int" {...@@ -2731,7 +2712,7 @@ test "std.zon parse int" {
2731 \\1:1: error: number '0123' has leading zero2712 \\1:1: error: number '0123' has leading zero
2732 \\1:1: note: use '0o' prefix for octal literals2713 \\1:1: note: use '0o' prefix for octal literals
2733 \\2714 \\
2734 , "{}", .{diag});2715 , "{f}", .{diag});
2735 }2716 }
2736}2717}
27372718
...@@ -2744,7 +2725,7 @@ test "std.zon negative char" {...@@ -2744,7 +2725,7 @@ test "std.zon negative char" {
2744 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{}));2725 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{}));
2745 try std.testing.expectFmt(2726 try std.testing.expectFmt(
2746 "1:1: error: expected number or 'inf' after '-'\n",2727 "1:1: error: expected number or 'inf' after '-'\n",
2747 "{}",2728 "{f}",
2748 .{diag},2729 .{diag},
2749 );2730 );
2750 }2731 }
...@@ -2754,13 +2735,15 @@ test "std.zon negative char" {...@@ -2754,13 +2735,15 @@ test "std.zon negative char" {
2754 try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{}));2735 try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{}));
2755 try std.testing.expectFmt(2736 try std.testing.expectFmt(
2756 "1:1: error: expected number or 'inf' after '-'\n",2737 "1:1: error: expected number or 'inf' after '-'\n",
2757 "{}",2738 "{f}",
2758 .{diag},2739 .{diag},
2759 );2740 );
2760 }2741 }
2761}2742}
27622743
2763test "std.zon parse float" {2744test "std.zon parse float" {
2745 if (builtin.cpu.arch == .x86 and builtin.abi == .musl and builtin.link_mode == .dynamic) return error.SkipZigTest;
2746
2764 const gpa = std.testing.allocator;2747 const gpa = std.testing.allocator;
27652748
2766 // Test decimals2749 // Test decimals
...@@ -2841,7 +2824,7 @@ test "std.zon parse float" {...@@ -2841,7 +2824,7 @@ test "std.zon parse float" {
2841 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{}));2824 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{}));
2842 try std.testing.expectFmt(2825 try std.testing.expectFmt(
2843 "1:1: error: expected number or 'inf' after '-'\n",2826 "1:1: error: expected number or 'inf' after '-'\n",
2844 "{}",2827 "{f}",
2845 .{diag},2828 .{diag},
2846 );2829 );
2847 }2830 }
...@@ -2851,7 +2834,7 @@ test "std.zon parse float" {...@@ -2851,7 +2834,7 @@ test "std.zon parse float" {
2851 var diag: Diagnostics = .{};2834 var diag: Diagnostics = .{};
2852 defer diag.deinit(gpa);2835 defer diag.deinit(gpa);
2853 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{}));2836 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});
2855 }2838 }
28562839
2857 // nan as int not allowed2840 // nan as int not allowed
...@@ -2859,7 +2842,7 @@ test "std.zon parse float" {...@@ -2859,7 +2842,7 @@ test "std.zon parse float" {
2859 var diag: Diagnostics = .{};2842 var diag: Diagnostics = .{};
2860 defer diag.deinit(gpa);2843 defer diag.deinit(gpa);
2861 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{}));2844 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});
2863 }2846 }
28642847
2865 // inf as int not allowed2848 // inf as int not allowed
...@@ -2867,7 +2850,7 @@ test "std.zon parse float" {...@@ -2867,7 +2850,7 @@ test "std.zon parse float" {
2867 var diag: Diagnostics = .{};2850 var diag: Diagnostics = .{};
2868 defer diag.deinit(gpa);2851 defer diag.deinit(gpa);
2869 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &diag, .{}));2852 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});
2871 }2854 }
28722855
2873 // -inf as int not allowed2856 // -inf as int not allowed
...@@ -2875,7 +2858,7 @@ test "std.zon parse float" {...@@ -2875,7 +2858,7 @@ test "std.zon parse float" {
2875 var diag: Diagnostics = .{};2858 var diag: Diagnostics = .{};
2876 defer diag.deinit(gpa);2859 defer diag.deinit(gpa);
2877 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &diag, .{}));2860 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});
2879 }2862 }
28802863
2881 // Bad identifier as float2864 // Bad identifier as float
...@@ -2888,7 +2871,7 @@ test "std.zon parse float" {...@@ -2888,7 +2871,7 @@ test "std.zon parse float" {
2888 \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'2871 \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
2889 \\1:1: note: precede identifier with '.' for an enum literal2872 \\1:1: note: precede identifier with '.' for an enum literal
2890 \\2873 \\
2891 , "{}", .{diag});2874 , "{f}", .{diag});
2892 }2875 }
28932876
2894 {2877 {
...@@ -2897,7 +2880,7 @@ test "std.zon parse float" {...@@ -2897,7 +2880,7 @@ test "std.zon parse float" {
2897 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{}));2880 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{}));
2898 try std.testing.expectFmt(2881 try std.testing.expectFmt(
2899 "1:1: error: expected number or 'inf' after '-'\n",2882 "1:1: error: expected number or 'inf' after '-'\n",
2900 "{}",2883 "{f}",
2901 .{diag},2884 .{diag},
2902 );2885 );
2903 }2886 }
...@@ -2910,7 +2893,7 @@ test "std.zon parse float" {...@@ -2910,7 +2893,7 @@ test "std.zon parse float" {
2910 error.ParseZon,2893 error.ParseZon,
2911 fromSlice(f32, gpa, "\"foo\"", &diag, .{}),2894 fromSlice(f32, gpa, "\"foo\"", &diag, .{}),
2912 );2895 );
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});
2914 }2897 }
2915}2898}
29162899
...@@ -3154,7 +3137,7 @@ test "std.zon vector" {...@@ -3154,7 +3137,7 @@ test "std.zon vector" {
3154 );3137 );
3155 try std.testing.expectFmt(3138 try std.testing.expectFmt(
3156 "1:2: error: expected 2 vector elements; found 1\n",3139 "1:2: error: expected 2 vector elements; found 1\n",
3157 "{}",3140 "{f}",
3158 .{diag},3141 .{diag},
3159 );3142 );
3160 }3143 }
...@@ -3169,7 +3152,7 @@ test "std.zon vector" {...@@ -3169,7 +3152,7 @@ test "std.zon vector" {
3169 );3152 );
3170 try std.testing.expectFmt(3153 try std.testing.expectFmt(
3171 "1:2: error: expected 2 vector elements; found 3\n",3154 "1:2: error: expected 2 vector elements; found 3\n",
3172 "{}",3155 "{f}",
3173 .{diag},3156 .{diag},
3174 );3157 );
3175 }3158 }
...@@ -3184,7 +3167,7 @@ test "std.zon vector" {...@@ -3184,7 +3167,7 @@ test "std.zon vector" {
3184 );3167 );
3185 try std.testing.expectFmt(3168 try std.testing.expectFmt(
3186 "1:8: error: expected type 'f32'\n",3169 "1:8: error: expected type 'f32'\n",
3187 "{}",3170 "{f}",
3188 .{diag},3171 .{diag},
3189 );3172 );
3190 }3173 }
...@@ -3197,7 +3180,7 @@ test "std.zon vector" {...@@ -3197,7 +3180,7 @@ test "std.zon vector" {
3197 error.ParseZon,3180 error.ParseZon,
3198 fromSlice(@Vector(3, u8), gpa, "true", &diag, .{}),3181 fromSlice(@Vector(3, u8), gpa, "true", &diag, .{}),
3199 );3182 );
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});
3201 }3184 }
32023185
3203 // Elements should get freed on error3186 // Elements should get freed on error
...@@ -3208,7 +3191,7 @@ test "std.zon vector" {...@@ -3208,7 +3191,7 @@ test "std.zon vector" {
3208 error.ParseZon,3191 error.ParseZon,
3209 fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}),3192 fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}),
3210 );3193 );
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});
3212 }3195 }
3213}3196}
32143197
...@@ -3332,7 +3315,7 @@ test "std.zon add pointers" {...@@ -3332,7 +3315,7 @@ test "std.zon add pointers" {
3332 error.ParseZon,3315 error.ParseZon,
3333 fromSlice(*const ?*const u8, gpa, "true", &diag, .{}),3316 fromSlice(*const ?*const u8, gpa, "true", &diag, .{}),
3334 );3317 );
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});
3336 }3319 }
33373320
3338 {3321 {
...@@ -3342,7 +3325,7 @@ test "std.zon add pointers" {...@@ -3342,7 +3325,7 @@ test "std.zon add pointers" {
3342 error.ParseZon,3325 error.ParseZon,
3343 fromSlice(*const ?*const f32, gpa, "true", &diag, .{}),3326 fromSlice(*const ?*const f32, gpa, "true", &diag, .{}),
3344 );3327 );
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});
3346 }3329 }
33473330
3348 {3331 {
...@@ -3352,7 +3335,7 @@ test "std.zon add pointers" {...@@ -3352,7 +3335,7 @@ test "std.zon add pointers" {
3352 error.ParseZon,3335 error.ParseZon,
3353 fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}),3336 fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}),
3354 );3337 );
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});
3356 }3339 }
33573340
3358 {3341 {
...@@ -3362,7 +3345,7 @@ test "std.zon add pointers" {...@@ -3362,7 +3345,7 @@ test "std.zon add pointers" {
3362 error.ParseZon,3345 error.ParseZon,
3363 fromSlice(*const ?*const bool, gpa, "10", &diag, .{}),3346 fromSlice(*const ?*const bool, gpa, "10", &diag, .{}),
3364 );3347 );
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});
3366 }3349 }
33673350
3368 {3351 {
...@@ -3372,7 +3355,7 @@ test "std.zon add pointers" {...@@ -3372,7 +3355,7 @@ test "std.zon add pointers" {
3372 error.ParseZon,3355 error.ParseZon,
3373 fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}),3356 fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}),
3374 );3357 );
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});
3376 }3359 }
33773360
3378 {3361 {
...@@ -3382,7 +3365,7 @@ test "std.zon add pointers" {...@@ -3382,7 +3365,7 @@ test "std.zon add pointers" {
3382 error.ParseZon,3365 error.ParseZon,
3383 fromSlice(*const ?*const struct { i32 }, gpa, "true", &diag, .{}),3366 fromSlice(*const ?*const struct { i32 }, gpa, "true", &diag, .{}),
3384 );3367 );
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});
3386 }3369 }
33873370
3388 {3371 {
...@@ -3392,7 +3375,7 @@ test "std.zon add pointers" {...@@ -3392,7 +3375,7 @@ test "std.zon add pointers" {
3392 error.ParseZon,3375 error.ParseZon,
3393 fromSlice(*const ?*const union { x: void }, gpa, "true", &diag, .{}),3376 fromSlice(*const ?*const union { x: void }, gpa, "true", &diag, .{}),
3394 );3377 );
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});
3396 }3379 }
33973380
3398 {3381 {
...@@ -3402,7 +3385,7 @@ test "std.zon add pointers" {...@@ -3402,7 +3385,7 @@ test "std.zon add pointers" {
3402 error.ParseZon,3385 error.ParseZon,
3403 fromSlice(*const ?*const [3]u8, gpa, "true", &diag, .{}),3386 fromSlice(*const ?*const [3]u8, gpa, "true", &diag, .{}),
3404 );3387 );
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});
3406 }3389 }
34073390
3408 {3391 {
...@@ -3412,7 +3395,7 @@ test "std.zon add pointers" {...@@ -3412,7 +3395,7 @@ test "std.zon add pointers" {
3412 error.ParseZon,3395 error.ParseZon,
3413 fromSlice(?[3]u8, gpa, "true", &diag, .{}),3396 fromSlice(?[3]u8, gpa, "true", &diag, .{}),
3414 );3397 );
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});
3416 }3399 }
34173400
3418 {3401 {
...@@ -3422,7 +3405,7 @@ test "std.zon add pointers" {...@@ -3422,7 +3405,7 @@ test "std.zon add pointers" {
3422 error.ParseZon,3405 error.ParseZon,
3423 fromSlice(*const ?*const []u8, gpa, "true", &diag, .{}),3406 fromSlice(*const ?*const []u8, gpa, "true", &diag, .{}),
3424 );3407 );
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});
3426 }3409 }
34273410
3428 {3411 {
...@@ -3432,7 +3415,7 @@ test "std.zon add pointers" {...@@ -3432,7 +3415,7 @@ test "std.zon add pointers" {
3432 error.ParseZon,3415 error.ParseZon,
3433 fromSlice(?[]u8, gpa, "true", &diag, .{}),3416 fromSlice(?[]u8, gpa, "true", &diag, .{}),
3434 );3417 );
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});
3436 }3419 }
34373420
3438 {3421 {
...@@ -3442,7 +3425,7 @@ test "std.zon add pointers" {...@@ -3442,7 +3425,7 @@ test "std.zon add pointers" {
3442 error.ParseZon,3425 error.ParseZon,
3443 fromSlice(*const ?*const []const u8, gpa, "true", &diag, .{}),3426 fromSlice(*const ?*const []const u8, gpa, "true", &diag, .{}),
3444 );3427 );
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});
3446 }3429 }
34473430
3448 {3431 {
...@@ -3452,7 +3435,7 @@ test "std.zon add pointers" {...@@ -3452,7 +3435,7 @@ test "std.zon add pointers" {
3452 error.ParseZon,3435 error.ParseZon,
3453 fromSlice(*const ?*const enum { foo }, gpa, "true", &diag, .{}),3436 fromSlice(*const ?*const enum { foo }, gpa, "true", &diag, .{}),
3454 );3437 );
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});
3456 }3439 }
3457}3440}
34583441
lib/ubsan_rt.zig+26-44
...@@ -119,24 +119,22 @@ const Value = extern struct {...@@ -119,24 +119,22 @@ const Value = extern struct {
119 }119 }
120 }120 }
121121
122 pub fn format(value: Value, bw: *std.io.Writer, comptime fmt: []const u8) !void {122 pub fn format(value: Value, writer: *std.io.Writer) std.io.Writer.Error!void {
123 comptime assert(fmt.len == 0);
124
125 // Work around x86_64 backend limitation.123 // Work around x86_64 backend limitation.
126 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) {124 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) {
127 return bw.writeAll("(unknown)");125 return writer.writeAll("(unknown)");
128 }126 }
129127
130 switch (value.td.kind) {128 switch (value.td.kind) {
131 .integer => {129 .integer => {
132 if (value.td.isSigned()) {130 if (value.td.isSigned()) {
133 return bw.print("{d}", .{value.getSignedInteger()});131 try writer.print("{d}", .{value.getSignedInteger()});
134 } else {132 } else {
135 return bw.print("{d}", .{value.getUnsignedInteger()});133 try writer.print("{d}", .{value.getUnsignedInteger()});
136 }134 }
137 },135 },
138 .float => return bw.print("{d}", .{value.getFloat()}),136 .float => try writer.print("{d}", .{value.getFloat()}),
139 .unknown => return bw.writeAll("(unknown)"),137 .unknown => try writer.writeAll("(unknown)"),
140 }138 }
141 }139 }
142};140};
...@@ -166,17 +164,12 @@ fn overflowHandler(...@@ -166,17 +164,12 @@ fn overflowHandler(
166 ) callconv(.c) noreturn {164 ) callconv(.c) noreturn {
167 const lhs: Value = .{ .handle = lhs_handle, .td = data.td };165 const lhs: Value = .{ .handle = lhs_handle, .td = data.td };
168 const rhs: Value = .{ .handle = rhs_handle, .td = data.td };166 const rhs: Value = .{ .handle = rhs_handle, .td = data.td };
169167 const signed_str = if (data.td.isSigned()) "signed" else "unsigned";
170 const is_signed = data.td.isSigned();168 panic(
171 const fmt = "{s} integer overflow: " ++ "{f} " ++169 @returnAddress(),
172 operator ++ " {f} cannot be represented in type {s}";170 "{s} integer overflow: {f} " ++ operator ++ " {f} cannot be represented in type {s}",
173171 .{ signed_str, lhs, rhs, data.td.getName() },
174 panic(@returnAddress(), fmt, .{172 );
175 if (is_signed) "signed" else "unsigned",
176 lhs,
177 rhs,
178 data.td.getName(),
179 });
180 }173 }
181 };174 };
182175
...@@ -195,11 +188,9 @@ fn negationHandler(...@@ -195,11 +188,9 @@ fn negationHandler(
195 value_handle: ValueHandle,188 value_handle: ValueHandle,
196) callconv(.c) noreturn {189) callconv(.c) noreturn {
197 const value: Value = .{ .handle = value_handle, .td = data.td };190 const value: Value = .{ .handle = value_handle, .td = data.td };
198 panic(191 panic(@returnAddress(), "negation of {f} cannot be represented in type {s}", .{
199 @returnAddress(),192 value, data.td.getName(),
200 "negation of {f} cannot be represented in type {s}",193 });
201 .{ value, data.td.getName() },
202 );
203}194}
204195
205fn divRemHandlerAbort(196fn divRemHandlerAbort(
...@@ -219,11 +210,9 @@ fn divRemHandler(...@@ -219,11 +210,9 @@ fn divRemHandler(
219 const rhs: Value = .{ .handle = rhs_handle, .td = data.td };210 const rhs: Value = .{ .handle = rhs_handle, .td = data.td };
220211
221 if (rhs.isMinusOne()) {212 if (rhs.isMinusOne()) {
222 panic(213 panic(@returnAddress(), "division of {f} by -1 cannot be represented in type {s}", .{
223 @returnAddress(),214 lhs, data.td.getName(),
224 "division of {f} by -1 cannot be represented in type {s}",215 });
225 .{ lhs, data.td.getName() },
226 );
227 } else panic(@returnAddress(), "division by zero", .{});216 } else panic(@returnAddress(), "division by zero", .{});
228}217}
229218
...@@ -353,11 +342,10 @@ fn outOfBounds(...@@ -353,11 +342,10 @@ fn outOfBounds(
353 index_handle: ValueHandle,342 index_handle: ValueHandle,
354) callconv(.c) noreturn {343) callconv(.c) noreturn {
355 const index: Value = .{ .handle = index_handle, .td = data.index_type };344 const index: Value = .{ .handle = index_handle, .td = data.index_type };
356 panic(345 panic(@returnAddress(), "index {f} out of bounds for type {s}", .{
357 @returnAddress(),346 index,
358 "index {f} out of bounds for type {s}",347 data.array_type.getName(),
359 .{ index, data.array_type.getName() },348 });
360 );
361}349}
362350
363const PointerOverflowData = extern struct {351const PointerOverflowData = extern struct {
...@@ -547,11 +535,9 @@ fn loadInvalidValue(...@@ -547,11 +535,9 @@ fn loadInvalidValue(
547 value_handle: ValueHandle,535 value_handle: ValueHandle,
548) callconv(.c) noreturn {536) callconv(.c) noreturn {
549 const value: Value = .{ .handle = value_handle, .td = data.td };537 const value: Value = .{ .handle = value_handle, .td = data.td };
550 panic(538 panic(@returnAddress(), "load of value {f}, which is not valid for type {s}", .{
551 @returnAddress(),539 value, data.td.getName(),
552 "load of value {f}, which is not valid for type {s}",540 });
553 .{ value, data.td.getName() },
554 );
555}541}
556542
557const InvalidBuiltinData = extern struct {543const InvalidBuiltinData = extern struct {
...@@ -590,11 +576,7 @@ fn vlaBoundNotPositive(...@@ -590,11 +576,7 @@ fn vlaBoundNotPositive(
590 bound_handle: ValueHandle,576 bound_handle: ValueHandle,
591) callconv(.c) noreturn {577) callconv(.c) noreturn {
592 const bound: Value = .{ .handle = bound_handle, .td = data.td };578 const bound: Value = .{ .handle = bound_handle, .td = data.td };
593 panic(579 panic(@returnAddress(), "variable length array bound evaluates to non-positive value {f}", .{bound});
594 @returnAddress(),
595 "variable length array bound evaluates to non-positive value {f}",
596 .{bound},
597 );
598}580}
599581
600const FloatCastOverflowData = extern struct {582const FloatCastOverflowData = extern struct {
src/Air.zig+8-5
...@@ -747,7 +747,9 @@ pub const Inst = struct {...@@ -747,7 +747,9 @@ pub const Inst = struct {
747 /// Dest slice may have any alignment; source pointer may have any alignment.747 /// Dest slice may have any alignment; source pointer may have any alignment.
748 /// The two memory regions must not overlap.748 /// The two memory regions must not overlap.
749 /// Result type is always void.749 /// Result type is always void.
750 ///
750 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.751 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
752 ///
751 /// If the length is compile-time known (due to the destination or753 /// If the length is compile-time known (due to the destination or
752 /// source being a pointer-to-array), then it is guaranteed to be754 /// source being a pointer-to-array), then it is guaranteed to be
753 /// greater than zero.755 /// greater than zero.
...@@ -759,7 +761,9 @@ pub const Inst = struct {...@@ -759,7 +761,9 @@ pub const Inst = struct {
759 /// Dest slice may have any alignment; source pointer may have any alignment.761 /// Dest slice may have any alignment; source pointer may have any alignment.
760 /// The two memory regions may overlap.762 /// The two memory regions may overlap.
761 /// Result type is always void.763 /// Result type is always void.
764 ///
762 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.765 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
766 ///
763 /// If the length is compile-time known (due to the destination or767 /// If the length is compile-time known (due to the destination or
764 /// source being a pointer-to-array), then it is guaranteed to be768 /// source being a pointer-to-array), then it is guaranteed to be
765 /// greater than zero.769 /// greater than zero.
...@@ -958,14 +962,13 @@ pub const Inst = struct {...@@ -958,14 +962,13 @@ pub const Inst = struct {
958 return index.unwrap().target;962 return index.unwrap().target;
959 }963 }
960964
961 pub fn format(index: Index, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {965 pub fn format(index: Index, w: *std.io.Writer) std.io.Writer.Error!void {
962 comptime assert(fmt.len == 0);966 try w.writeByte('%');
963 try bw.writeByte('%');
964 switch (index.unwrap()) {967 switch (index.unwrap()) {
965 .ref => {},968 .ref => {},
966 .target => try bw.writeByte('t'),969 .target => try w.writeByte('t'),
967 }970 }
968 try bw.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))});971 try w.print("{d}", .{@as(u31, @truncate(@intFromEnum(index)))});
969 }972 }
970 };973 };
971974
src/Air/Liveness.zig+11-12
...@@ -1300,10 +1300,10 @@ fn analyzeOperands(...@@ -1300,10 +1300,10 @@ fn analyzeOperands(
13001300
1301 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.1301 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
1302 const immediate_death = if (data.live_set.remove(inst)) blk: {1302 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) });
1304 break :blk false;1304 break :blk false;
1305 } else blk: {1305 } else blk: {
1306 log.debug("[{}] %{}: immediate death", .{ pass, @intFromEnum(inst) });1306 log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) });
1307 break :blk true;1307 break :blk true;
1308 };1308 };
13091309
...@@ -1324,7 +1324,7 @@ fn analyzeOperands(...@@ -1324,7 +1324,7 @@ fn analyzeOperands(
1324 const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i));1324 const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i));
13251325
1326 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {1326 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 });
1328 tomb_bits |= mask;1328 tomb_bits |= mask;
1329 }1329 }
1330 }1330 }
...@@ -2037,15 +2037,15 @@ fn fmtInstSet(set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void)) FmtIns...@@ -2037,15 +2037,15 @@ fn fmtInstSet(set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void)) FmtIns
2037const FmtInstSet = struct {2037const FmtInstSet = struct {
2038 set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void),2038 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 {
2041 if (val.set.count() == 0) {2041 if (val.set.count() == 0) {
2042 try bw.writeAll("[no instructions]");2042 try w.writeAll("[no instructions]");
2043 return;2043 return;
2044 }2044 }
2045 var it = val.set.keyIterator();2045 var it = val.set.keyIterator();
2046 try bw.print("%{f}", .{it.next().?.*});2046 try w.print("%{f}", .{it.next().?.*});
2047 while (it.next()) |key| {2047 while (it.next()) |key| {
2048 try bw.print(" %{f}", .{key.*});2048 try w.print(" %{f}", .{key.*});
2049 }2049 }
2050 }2050 }
2051};2051};
...@@ -2057,15 +2057,14 @@ fn fmtInstList(list: []const Air.Inst.Index) FmtInstList {...@@ -2057,15 +2057,14 @@ fn fmtInstList(list: []const Air.Inst.Index) FmtInstList {
2057const FmtInstList = struct {2057const FmtInstList = struct {
2058 list: []const Air.Inst.Index,2058 list: []const Air.Inst.Index,
20592059
2060 pub fn format(val: FmtInstList, bw: *Writer, comptime fmt: []const u8) !void {2060 pub fn format(val: FmtInstList, w: *std.io.Writer) std.io.Writer.Error!void {
2061 comptime assert(fmt.len == 0);
2062 if (val.list.len == 0) {2061 if (val.list.len == 0) {
2063 try bw.writeAll("[no instructions]");2062 try w.writeAll("[no instructions]");
2064 return;2063 return;
2065 }2064 }
2066 try bw.print("%{f}", .{val.list[0]});2065 try w.print("%{f}", .{val.list[0]});
2067 for (val.list[1..]) |inst| {2066 for (val.list[1..]) |inst| {
2068 try bw.print(" %{f}", .{inst});2067 try w.print(" %{f}", .{inst});
2069 }2068 }
2070 }2069 }
2071};2070};
src/Air/Liveness/Verify.zig+5-3
...@@ -511,7 +511,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -511,7 +511,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
511511
512 // The same stuff should be alive after the loop as before it.512 // The same stuff should be alive after the loop as before it.
513 const gop = try self.loops.getOrPut(self.gpa, inst);513 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)});
515 defer {515 defer {
516 var live = self.loops.fetchRemove(inst).?;516 var live = self.loops.fetchRemove(inst).?;
517 live.value.deinit(self.gpa);517 live.value.deinit(self.gpa);
...@@ -560,7 +560,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -560,7 +560,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
560 // after the loop as before it.560 // after the loop as before it.
561 {561 {
562 const gop = try self.loops.getOrPut(self.gpa, inst);562 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)});
564 gop.value_ptr.* = self.live.move();564 gop.value_ptr.* = self.live.move();
565 }565 }
566 defer {566 defer {
...@@ -601,7 +601,9 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies...@@ -601,7 +601,9 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies
601 return;601 return;
602 };602 };
603 if (dies) {603 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 });
605 } else {607 } else {
606 if (!self.live.contains(operand)) return invalid("%{f}: dead operand %{f} reused", .{ inst, operand });608 if (!self.live.contains(operand)) return invalid("%{f}: dead operand %{f} reused", .{ inst, operand });
607 }609 }
src/Air/print.zig+6-6
...@@ -518,13 +518,13 @@ const Writer = struct {...@@ -518,13 +518,13 @@ const Writer = struct {
518 if (mask_idx > 0) try s.writeAll(", ");518 if (mask_idx > 0) try s.writeAll(", ");
519 switch (mask_elem.unwrap()) {519 switch (mask_elem.unwrap()) {
520 .elem => |idx| try s.print("elem {d}", .{idx}),520 .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)}),
522 }522 }
523 }523 }
524 try s.writeByte(']');524 try s.writeByte(']');
525 }525 }
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 {
528 const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst);528 const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst);
529 try w.writeType(s, unwrapped.result_ty);529 try w.writeType(s, unwrapped.result_ty);
530 try s.writeAll(", ");530 try s.writeAll(", ");
...@@ -590,7 +590,7 @@ const Writer = struct {...@@ -590,7 +590,7 @@ const Writer = struct {
590 const ip = &w.pt.zcu.intern_pool;590 const ip = &w.pt.zcu.intern_pool;
591 const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;591 const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
592 try w.writeType(s, .fromInterned(ty_nav.ty));592 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)});
594 }594 }
595595
596 fn writeAtomicLoad(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {596 fn writeAtomicLoad(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
...@@ -710,7 +710,7 @@ const Writer = struct {...@@ -710,7 +710,7 @@ const Writer = struct {
710 }710 }
711 }711 }
712 const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len];712 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)});
714 }714 }
715715
716 fn writeDbgStmt(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {716 fn writeDbgStmt(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
...@@ -722,7 +722,7 @@ const Writer = struct {...@@ -722,7 +722,7 @@ const Writer = struct {
722 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;722 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
723 try w.writeOperand(s, inst, 0, pl_op.operand);723 try w.writeOperand(s, inst, 0, pl_op.operand);
724 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);724 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))});
726 }726 }
727727
728 fn writeCall(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {728 fn writeCall(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void {
...@@ -1010,7 +1010,7 @@ const Writer = struct {...@@ -1010,7 +1010,7 @@ const Writer = struct {
10101010
1011 fn writeInstRef(1011 fn writeInstRef(
1012 w: *Writer,1012 w: *Writer,
1013 s: anytype,1013 s: *std.io.Writer,
1014 operand: Air.Inst.Ref,1014 operand: Air.Inst.Ref,
1015 dies: bool,1015 dies: bool,
1016 ) Error!void {1016 ) Error!void {
src/Builtin.zig+31-31
...@@ -57,49 +57,49 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -57,49 +57,49 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
57 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.57 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
58 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;58 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
59 \\pub const zig_version_string = "{s}";59 \\pub const zig_version_string = "{s}";
60 \\pub const zig_backend = std.builtin.CompilerBackend.{fp_};60 \\pub const zig_backend = std.builtin.CompilerBackend.{f};
61 \\61 \\
62 \\pub const output_mode: std.builtin.OutputMode = .{fp_};62 \\pub const output_mode: std.builtin.OutputMode = .{f};
63 \\pub const link_mode: std.builtin.LinkMode = .{fp_};63 \\pub const link_mode: std.builtin.LinkMode = .{f};
64 \\pub const unwind_tables: std.builtin.UnwindTables = .{fp_};64 \\pub const unwind_tables: std.builtin.UnwindTables = .{f};
65 \\pub const is_test = {};65 \\pub const is_test = {};
66 \\pub const single_threaded = {};66 \\pub const single_threaded = {};
67 \\pub const abi: std.Target.Abi = .{fp_};67 \\pub const abi: std.Target.Abi = .{f};
68 \\pub const cpu: std.Target.Cpu = .{{68 \\pub const cpu: std.Target.Cpu = .{{
69 \\ .arch = .{fp_},69 \\ .arch = .{f},
70 \\ .model = &std.Target.{fp_}.cpu.{fp_},70 \\ .model = &std.Target.{f}.cpu.{f},
71 \\ .features = std.Target.{fp_}.featureSet(&.{{71 \\ .features = std.Target.{f}.featureSet(&.{{
72 \\72 \\
73 , .{73 , .{
74 build_options.version,74 build_options.version,
75 std.zig.fmtId(@tagName(zig_backend)),75 std.zig.fmtIdPU(@tagName(zig_backend)),
76 std.zig.fmtId(@tagName(opts.output_mode)),76 std.zig.fmtIdPU(@tagName(opts.output_mode)),
77 std.zig.fmtId(@tagName(opts.link_mode)),77 std.zig.fmtIdPU(@tagName(opts.link_mode)),
78 std.zig.fmtId(@tagName(opts.unwind_tables)),78 std.zig.fmtIdPU(@tagName(opts.unwind_tables)),
79 opts.is_test,79 opts.is_test,
80 opts.single_threaded,80 opts.single_threaded,
81 std.zig.fmtId(@tagName(target.abi)),81 std.zig.fmtIdPU(@tagName(target.abi)),
82 std.zig.fmtId(@tagName(target.cpu.arch)),82 std.zig.fmtIdPU(@tagName(target.cpu.arch)),
83 std.zig.fmtId(arch_family_name),83 std.zig.fmtIdPU(arch_family_name),
84 std.zig.fmtId(target.cpu.model.name),84 std.zig.fmtIdPU(target.cpu.model.name),
85 std.zig.fmtId(arch_family_name),85 std.zig.fmtIdPU(arch_family_name),
86 });86 });
8787
88 for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {88 for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
89 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));89 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
90 const is_enabled = target.cpu.features.isEnabled(index);90 const is_enabled = target.cpu.features.isEnabled(index);
91 if (is_enabled) {91 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)});
93 }93 }
94 }94 }
95 try buffer.print(95 try buffer.print(
96 \\ }}),96 \\ }}),
97 \\}};97 \\}};
98 \\pub const os: std.Target.Os = .{{98 \\pub const os: std.Target.Os = .{{
99 \\ .tag = .{fp_},99 \\ .tag = .{f},
100 \\ .version_range = .{{100 \\ .version_range = .{{
101 ,101 ,
102 .{std.zig.fmtId(@tagName(target.os.tag))},102 .{std.zig.fmtIdPU(@tagName(target.os.tag))},
103 );103 );
104104
105 switch (target.os.versionRange()) {105 switch (target.os.versionRange()) {
...@@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
200 }),200 }),
201 .windows => |windows| try buffer.print(201 .windows => |windows| try buffer.print(
202 \\ .windows = .{{202 \\ .windows = .{{
203 \\ .min = {fc},203 \\ .min = {f},
204 \\ .max = {fc},204 \\ .max = {f},
205 \\ }}}},205 \\ }}}},
206 \\206 \\
207 , .{ windows.min, windows.max }),207 , .{ windows.min, windows.max }),
...@@ -238,8 +238,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -238,8 +238,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
238 const link_libc = opts.link_libc;238 const link_libc = opts.link_libc;
239239
240 try buffer.print(240 try buffer.print(
241 \\pub const object_format: std.Target.ObjectFormat = .{fp_};241 \\pub const object_format: std.Target.ObjectFormat = .{f};
242 \\pub const mode: std.builtin.OptimizeMode = .{fp_};242 \\pub const mode: std.builtin.OptimizeMode = .{f};
243 \\pub const link_libc = {};243 \\pub const link_libc = {};
244 \\pub const link_libcpp = {};244 \\pub const link_libcpp = {};
245 \\pub const have_error_return_tracing = {};245 \\pub const have_error_return_tracing = {};
...@@ -249,12 +249,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -249,12 +249,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
249 \\pub const position_independent_code = {};249 \\pub const position_independent_code = {};
250 \\pub const position_independent_executable = {};250 \\pub const position_independent_executable = {};
251 \\pub const strip_debug_info = {};251 \\pub const strip_debug_info = {};
252 \\pub const code_model: std.builtin.CodeModel = .{fp_};252 \\pub const code_model: std.builtin.CodeModel = .{f};
253 \\pub const omit_frame_pointer = {};253 \\pub const omit_frame_pointer = {};
254 \\254 \\
255 , .{255 , .{
256 std.zig.fmtId(@tagName(target.ofmt)),256 std.zig.fmtIdPU(@tagName(target.ofmt)),
257 std.zig.fmtId(@tagName(opts.optimize_mode)),257 std.zig.fmtIdPU(@tagName(opts.optimize_mode)),
258 link_libc,258 link_libc,
259 opts.link_libcpp,259 opts.link_libcpp,
260 opts.error_tracing,260 opts.error_tracing,
...@@ -264,15 +264,15 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {...@@ -264,15 +264,15 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
264 opts.pic,264 opts.pic,
265 opts.pie,265 opts.pie,
266 opts.strip,266 opts.strip,
267 std.zig.fmtId(@tagName(opts.code_model)),267 std.zig.fmtIdPU(@tagName(opts.code_model)),
268 opts.omit_frame_pointer,268 opts.omit_frame_pointer,
269 });269 });
270270
271 if (target.os.tag == .wasi) {271 if (target.os.tag == .wasi) {
272 try buffer.print(272 try buffer.print(
273 \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{fp_};273 \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{f};
274 \\274 \\
275 , .{std.zig.fmtId(@tagName(opts.wasi_exec_model))});275 , .{std.zig.fmtIdPU(@tagName(opts.wasi_exec_model))});
276 }276 }
277277
278 if (opts.is_test) {278 if (opts.is_test) {
...@@ -317,7 +317,7 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {...@@ -317,7 +317,7 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
317 if (root_dir.statFile(sub_path)) |stat| {317 if (root_dir.statFile(sub_path)) |stat| {
318 if (stat.size != file.source.?.len) {318 if (stat.size != file.source.?.len) {
319 std.log.warn(319 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}. " ++
321 "Overwriting with correct file contents now",321 "Overwriting with correct file contents now",
322 .{ file.path.fmt(comp), file.source.?.len, stat.size },322 .{ file.path.fmt(comp), file.source.?.len, stat.size },
323 );323 );
src/Compilation.zig+38-74
...@@ -237,7 +237,6 @@ fuzzer_lib: ?CrtFile = null,...@@ -237,7 +237,6 @@ fuzzer_lib: ?CrtFile = null,
237glibc_so_files: ?glibc.BuiltSharedObjects = null,237glibc_so_files: ?glibc.BuiltSharedObjects = null,
238freebsd_so_files: ?freebsd.BuiltSharedObjects = null,238freebsd_so_files: ?freebsd.BuiltSharedObjects = null,
239netbsd_so_files: ?netbsd.BuiltSharedObjects = null,239netbsd_so_files: ?netbsd.BuiltSharedObjects = null,
240wasi_emulated_libs: []const wasi_libc.CrtFile,
241240
242/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,241/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
243/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.242/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
...@@ -403,9 +402,7 @@ pub const Path = struct {...@@ -403,9 +402,7 @@ pub const Path = struct {
403 const Formatter = struct {402 const Formatter = struct {
404 p: Path,403 p: Path,
405 comp: *Compilation,404 comp: *Compilation,
406 pub fn format(f: Formatter, comptime unused_fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void {405 pub fn format(f: Formatter, w: *std.io.Writer) std.io.Writer.Error!void {
407 comptime assert(unused_fmt.len == 0);
408 _ = options;
409 const root_path: []const u8 = switch (f.p.root) {406 const root_path: []const u8 = switch (f.p.root) {
410 .zig_lib => f.comp.dirs.zig_lib.path orelse ".",407 .zig_lib => f.comp.dirs.zig_lib.path orelse ".",
411 .global_cache => f.comp.dirs.global_cache.path orelse ".",408 .global_cache => f.comp.dirs.global_cache.path orelse ".",
...@@ -734,10 +731,10 @@ pub const Directories = struct {...@@ -734,10 +731,10 @@ pub const Directories = struct {
734 };731 };
735732
736 if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) {733 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 });
738 }735 }
739 if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) {736 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 });
741 }738 }
742739
743 return .{740 return .{
...@@ -1570,12 +1567,6 @@ pub const CreateOptions = struct {...@@ -1570,12 +1567,6 @@ pub const CreateOptions = struct {
1570 framework_dirs: []const []const u8 = &[0][]const u8{},1567 framework_dirs: []const []const u8 = &[0][]const u8{},
1571 frameworks: []const Framework = &.{},1568 frameworks: []const Framework = &.{},
1572 windows_lib_names: []const []const u8 = &.{},1569 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 = &.{},
1579 /// This means that if the output mode is an executable it will be a1570 /// This means that if the output mode is an executable it will be a
1580 /// Position Independent Executable. If the output mode is not an1571 /// Position Independent Executable. If the output mode is not an
1581 /// executable this field is ignored.1572 /// executable this field is ignored.
...@@ -2055,7 +2046,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2055,7 +2046,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2055 .function_sections = options.function_sections,2046 .function_sections = options.function_sections,
2056 .data_sections = options.data_sections,2047 .data_sections = options.data_sections,
2057 .native_system_include_paths = options.native_system_include_paths,2048 .native_system_include_paths = options.native_system_include_paths,
2058 .wasi_emulated_libs = options.wasi_emulated_libs,
2059 .force_undefined_symbols = options.force_undefined_symbols,2049 .force_undefined_symbols = options.force_undefined_symbols,
2060 .link_eh_frame_hdr = link_eh_frame_hdr,2050 .link_eh_frame_hdr = link_eh_frame_hdr,
2061 .global_cc_argv = options.global_cc_argv,2051 .global_cc_argv = options.global_cc_argv,
...@@ -2070,12 +2060,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2070,12 +2060,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2070 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),2060 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
2071 };2061 };
20722062
2073 errdefer {2063 comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
2074 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);2064 errdefer comp.windows_libs.deinit(gpa);
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), {});
20792065
2080 // Prevent some footguns by making the "any" fields of config reflect2066 // Prevent some footguns by making the "any" fields of config reflect
2081 // the default Module settings.2067 // the default Module settings.
...@@ -2306,6 +2292,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2306,6 +2292,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23062292
2307 if (comp.emit_bin != null and target.ofmt != .c) {2293 if (comp.emit_bin != null and target.ofmt != .c) {
2308 if (!comp.skip_linker_dependencies) {2294 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
2309 // If we need to build libc for the target, add work items for it.2302 // If we need to build libc for the target, add work items for it.
2310 // We go through the work queue so that building can be done in parallel.2303 // We go through the work queue so that building can be done in parallel.
2311 // If linking against host libc installation, instead queue up jobs2304 // If linking against host libc installation, instead queue up jobs
...@@ -2381,11 +2374,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2381,11 +2374,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2381 } else if (target.isWasiLibC()) {2374 } else if (target.isWasiLibC()) {
2382 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;2375 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
2389 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;2377 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;
2390 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true;2378 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true;
2391 comp.link_task_queue.pending_prelink_tasks += 2;2379 comp.link_task_queue.pending_prelink_tasks += 2;
...@@ -2399,7 +2387,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2399,7 +2387,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23992387
2400 // When linking mingw-w64 there are some import libs we always need.2388 // When linking mingw-w64 there are some import libs we always need.
2401 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);2389 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, {});
2403 } else {2391 } else {
2404 return error.LibCUnavailable;2392 return error.LibCUnavailable;
2405 }2393 }
...@@ -2497,7 +2485,6 @@ pub fn destroy(comp: *Compilation) void {...@@ -2497,7 +2485,6 @@ pub fn destroy(comp: *Compilation) void {
2497 comp.c_object_work_queue.deinit();2485 comp.c_object_work_queue.deinit();
2498 comp.win32_resource_work_queue.deinit();2486 comp.win32_resource_work_queue.deinit();
24992487
2500 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
2501 comp.windows_libs.deinit(gpa);2488 comp.windows_libs.deinit(gpa);
25022489
2503 {2490 {
...@@ -2994,7 +2981,7 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat...@@ -2994,7 +2981,7 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat
2994 break @intCast(i);2981 break @intCast(i);
2995 }2982 }
2996 } else std.debug.panic(2983 } else std.debug.panic(
2997 "missing prefix directory '{s}' ('{}') for '{s}'",2984 "missing prefix directory '{s}' ('{f}') for '{s}'",
2998 .{ @tagName(path.root), want_prefix_dir, path.sub_path },2985 .{ @tagName(path.root), want_prefix_dir, path.sub_path },
2999 );2986 );
30002987
...@@ -3333,7 +3320,7 @@ fn emitFromCObject(...@@ -3333,7 +3320,7 @@ fn emitFromCObject(
3333 emit_path.root_dir.handle,3320 emit_path.root_dir.handle,
3334 emit_path.sub_path,3321 emit_path.sub_path,
3335 .{},3322 .{},
3336 ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{3323 ) catch |err| log.err("unable to copy '{f}' to '{f}': {s}", .{
3337 src_path,3324 src_path,
3338 emit_path,3325 emit_path,
3339 @errorName(err),3326 @errorName(err),
...@@ -3681,7 +3668,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3681,7 +3668,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3681 .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"),3668 .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"),
3682 },3669 },
3683 .src_loc = try bundle.addSourceLocation(.{3670 .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)}),
3685 .span_start = start,3672 .span_start = start,
3686 .span_main = start,3673 .span_main = start,
3687 .span_end = @intCast(end),3674 .span_end = @intCast(end),
...@@ -3728,7 +3715,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3728,7 +3715,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3728 assert(!is_retryable);3715 assert(!is_retryable);
3729 // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors.3716 // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors.
3730 _ = try file.getTree(zcu); // Tree must be loaded.3717 _ = 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)});
3732 defer gpa.free(path);3719 defer gpa.free(path);
3733 if (file.zir != null) {3720 if (file.zir != null) {
3734 try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path);3721 try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path);
...@@ -3784,8 +3771,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3784,8 +3771,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3784 }3771 }
37853772
3786 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{3773 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{
3787 error_msg.msg,3774 error_msg.msg, zcu.fmtAnalUnit(anal_unit),
3788 zcu.fmtAnalUnit(anal_unit),
3789 });3775 });
37903776
3791 try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error);3777 try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error);
...@@ -4047,7 +4033,7 @@ pub fn addModuleErrorMsg(...@@ -4047,7 +4033,7 @@ pub fn addModuleErrorMsg(
4047 const err_src_loc = module_err_msg.src_loc.upgrade(zcu);4033 const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
4048 const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {4034 const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {
4049 try eb.addRootErrorMessage(.{4035 try eb.addRootErrorMessage(.{
4050 .msg = try eb.printString("unable to load '{}': {s}", .{4036 .msg = try eb.printString("unable to load '{f}': {s}", .{
4051 err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err),4037 err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err),
4052 }),4038 }),
4053 });4039 });
...@@ -4110,7 +4096,7 @@ pub fn addModuleErrorMsg(...@@ -4110,7 +4096,7 @@ pub fn addModuleErrorMsg(
4110 }4096 }
41114097
4112 const src_loc = try eb.addSourceLocation(.{4098 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)}),
4114 .span_start = err_span.start,4100 .span_start = err_span.start,
4115 .span_main = err_span.main,4101 .span_main = err_span.main,
4116 .span_end = err_span.end,4102 .span_end = err_span.end,
...@@ -4142,7 +4128,7 @@ pub fn addModuleErrorMsg(...@@ -4142,7 +4128,7 @@ pub fn addModuleErrorMsg(
4142 const gop = try notes.getOrPutContext(gpa, .{4128 const gop = try notes.getOrPutContext(gpa, .{
4143 .msg = try eb.addString(module_note.msg),4129 .msg = try eb.addString(module_note.msg),
4144 .src_loc = try eb.addSourceLocation(.{4130 .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)}),
4146 .span_start = span.start,4132 .span_start = span.start,
4147 .span_main = span.main,4133 .span_main = span.main,
4148 .span_end = span.end,4134 .span_end = span.end,
...@@ -4187,7 +4173,7 @@ fn addReferenceTraceFrame(...@@ -4187,7 +4173,7 @@ fn addReferenceTraceFrame(
4187 try ref_traces.append(gpa, .{4173 try ref_traces.append(gpa, .{
4188 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),4174 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
4189 .src_loc = try eb.addSourceLocation(.{4175 .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)}),
4191 .span_start = span.start,4177 .span_start = span.start,
4192 .span_main = span.main,4178 .span_main = span.main,
4193 .span_end = span.end,4179 .span_end = span.end,
...@@ -4906,7 +4892,7 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,...@@ -4906,7 +4892,7 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,
4906 var walker = try mod_dir.walk(comp.gpa);4892 var walker = try mod_dir.walk(comp.gpa);
4907 defer walker.deinit();4893 defer walker.deinit();
49084894
4909 var archiver = std.tar.writer(tar_file.writer().any());4895 var archiver = std.tar.writer(tar_file.deprecatedWriter().any());
4910 archiver.prefix = name;4896 archiver.prefix = name;
49114897
4912 while (try walker.next()) |entry| {4898 while (try walker.next()) |entry| {
...@@ -4919,13 +4905,13 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,...@@ -4919,13 +4905,13 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,
4919 else => continue,4905 else => continue,
4920 }4906 }
4921 var file = mod_dir.openFile(entry.path, .{}) catch |err| {4907 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}", .{
4923 root.fmt(comp), entry.path, @errorName(err),4909 root.fmt(comp), entry.path, @errorName(err),
4924 });4910 });
4925 };4911 };
4926 defer file.close();4912 defer file.close();
4927 archiver.writeFile(entry.path, file) catch |err| {4913 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}", .{
4929 root.fmt(comp), entry.path, @errorName(err),4915 root.fmt(comp), entry.path, @errorName(err),
4930 });4916 });
4931 };4917 };
...@@ -5055,7 +5041,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye...@@ -5055,7 +5041,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
5055 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {5041 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
5056 return comp.lockAndSetMiscFailure(5042 return comp.lockAndSetMiscFailure(
5057 .docs_copy,5043 .docs_copy,
5058 "unable to create output directory '{}': {s}",5044 "unable to create output directory '{f}': {s}",
5059 .{ docs_path, @errorName(err) },5045 .{ docs_path, @errorName(err) },
5060 );5046 );
5061 };5047 };
...@@ -5067,10 +5053,8 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye...@@ -5067,10 +5053,8 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
5067 "main.wasm",5053 "main.wasm",
5068 .{},5054 .{},
5069 ) catch |err| {5055 ) catch |err| {
5070 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{5056 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {s}", .{
5071 crt_file.full_object_path,5057 crt_file.full_object_path, docs_path, @errorName(err),
5072 docs_path,
5073 @errorName(err),
5074 });5058 });
5075 };5059 };
5076}5060}
...@@ -6024,16 +6008,15 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -6024,16 +6008,15 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
60246008
6025 // In .rc files, a " within a quoted string is escaped as ""6009 // In .rc files, a " within a quoted string is escaped as ""
6026 const fmtRcEscape = struct {6010 const fmtRcEscape = struct {
6027 fn formatRcEscape(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void {6011 fn formatRcEscape(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
6028 comptime assert(fmt.len == 0);
6029 for (bytes) |byte| switch (byte) {6012 for (bytes) |byte| switch (byte) {
6030 '"' => try bw.writeAll("\"\""),6013 '"' => try writer.writeAll("\"\""),
6031 '\\' => try bw.writeAll("\\\\"),6014 '\\' => try writer.writeAll("\\\\"),
6032 else => try bw.writeByte(byte),6015 else => try writer.writeByte(byte),
6033 };6016 };
6034 }6017 }
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) {
6037 return .{ .data = bytes };6020 return .{ .data = bytes };
6038 }6021 }
6039 }.fmtRcEscape;6022 }.fmtRcEscape;
...@@ -6047,7 +6030,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -6047,7 +6030,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
6047 // 24 is RT_MANIFEST6030 // 24 is RT_MANIFEST
6048 const resource_type = 24;6031 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
6052 try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input });6037 try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input });
60536038
...@@ -6259,7 +6244,7 @@ fn spawnZigRc(...@@ -6259,7 +6244,7 @@ fn spawnZigRc(
6259 }6244 }
62606245
6261 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)6246 // 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();
6263 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);6248 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
62646249
6265 const term = child.wait() catch |err| {6250 const term = child.wait() catch |err| {
...@@ -6474,7 +6459,7 @@ pub fn addCCArgs(...@@ -6474,7 +6459,7 @@ pub fn addCCArgs(
6474 try argv.append("-fno-asynchronous-unwind-tables");6459 try argv.append("-fno-asynchronous-unwind-tables");
6475 try argv.append("-funwind-tables");6460 try argv.append("-funwind-tables");
6476 },6461 },
6477 .@"async" => try argv.append("-fasynchronous-unwind-tables"),6462 .async => try argv.append("-fasynchronous-unwind-tables"),
6478 }6463 }
64796464
6480 try argv.append("-nostdinc");6465 try argv.append("-nostdinc");
...@@ -7597,27 +7582,6 @@ fn getCrtPathsInner(...@@ -7597,27 +7582,6 @@ fn getCrtPathsInner(
7597 };7582 };
7598}7583}
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
7621/// This decides the optimization mode for all zig-provided libraries, including7585/// This decides the optimization mode for all zig-provided libraries, including
7622/// compiler-rt, libcxx, libc, libunwind, etc.7586/// compiler-rt, libcxx, libc, libunwind, etc.
7623pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode {7587pub 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...@@ -142,8 +142,8 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons
142 const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index");142 const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index");
143 const nav = ip.getNav(nav_index);143 const nav = ip.getNav(nav_index);
144 try w.print(144 try w.print(
145 \\name: '{}'145 \\name: '{f}'
146 \\fqn: '{}'146 \\fqn: '{f}'
147 \\status: {s}147 \\status: {s}
148 \\created on generation: {d}148 \\created on generation: {d}
149 \\149 \\
...@@ -234,7 +234,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons...@@ -234,7 +234,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons
234 for (unit_info.deps.items, 0..) |dependee, i| {234 for (unit_info.deps.items, 0..) |dependee, i| {
235 try w.print("[{d}] ", .{i});235 try w.print("[{d}] ", .{i});
236 switch (dependee) {236 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)}),
238 .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }),238 .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }),
239 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {239 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
240 .struct_type, .union_type, .enum_type => try w.print("type {d}", .{@intFromEnum(ip_index)}),240 .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...@@ -260,7 +260,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons
260 const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"));260 const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"));
261 const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type");261 const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type");
262 try w.print(262 try w.print(
263 \\name: '{}'263 \\name: '{f}'
264 \\created on generation: {d}264 \\created on generation: {d}
265 \\265 \\
266 , .{266 , .{
...@@ -365,7 +365,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void {...@@ -365,7 +365,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void {
365 .union_type,365 .union_type,
366 .enum_type,366 .enum_type,
367 .opaque_type,367 .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
370 else => unreachable,370 else => unreachable,
371 }371 }
src/InternPool.zig+1-13
...@@ -518,8 +518,6 @@ pub const Nav = struct {...@@ -518,8 +518,6 @@ pub const Nav = struct {
518 namespace: NamespaceIndex,518 namespace: NamespaceIndex,
519 zir_index: TrackedInst.Index,519 zir_index: TrackedInst.Index,
520 },520 },
521 /// TODO: this is a hack! If #20663 isn't accepted, let's figure out something a bit better.
522 is_usingnamespace: bool,
523 status: union(enum) {521 status: union(enum) {
524 /// This `Nav` is pending semantic analysis.522 /// This `Nav` is pending semantic analysis.
525 unresolved,523 unresolved,
...@@ -735,7 +733,7 @@ pub const Nav = struct {...@@ -735,7 +733,7 @@ pub const Nav = struct {
735 @"addrspace": std.builtin.AddressSpace,733 @"addrspace": std.builtin.AddressSpace,
736 /// Populated only if `bits.status == .type_resolved`.734 /// Populated only if `bits.status == .type_resolved`.
737 is_threadlocal: bool,735 is_threadlocal: bool,
738 is_usingnamespace: bool,736 _: u1 = 0,
739 };737 };
740738
741 fn unpack(repr: Repr) Nav {739 fn unpack(repr: Repr) Nav {
...@@ -749,7 +747,6 @@ pub const Nav = struct {...@@ -749,7 +747,6 @@ pub const Nav = struct {
749 assert(repr.analysis_zir_index == .none);747 assert(repr.analysis_zir_index == .none);
750 break :a null;748 break :a null;
751 },749 },
752 .is_usingnamespace = repr.bits.is_usingnamespace,
753 .status = switch (repr.bits.status) {750 .status = switch (repr.bits.status) {
754 .unresolved => .unresolved,751 .unresolved => .unresolved,
755 .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{752 .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{
...@@ -797,7 +794,6 @@ pub const Nav = struct {...@@ -797,7 +794,6 @@ pub const Nav = struct {
797 .is_const = false,794 .is_const = false,
798 .alignment = .none,795 .alignment = .none,
799 .@"addrspace" = .generic,796 .@"addrspace" = .generic,
800 .is_usingnamespace = nav.is_usingnamespace,
801 .is_threadlocal = false,797 .is_threadlocal = false,
802 },798 },
803 .type_resolved => |r| .{799 .type_resolved => |r| .{
...@@ -805,7 +801,6 @@ pub const Nav = struct {...@@ -805,7 +801,6 @@ pub const Nav = struct {
805 .is_const = r.is_const,801 .is_const = r.is_const,
806 .alignment = r.alignment,802 .alignment = r.alignment,
807 .@"addrspace" = r.@"addrspace",803 .@"addrspace" = r.@"addrspace",
808 .is_usingnamespace = nav.is_usingnamespace,
809 .is_threadlocal = r.is_threadlocal,804 .is_threadlocal = r.is_threadlocal,
810 },805 },
811 .fully_resolved => |r| .{806 .fully_resolved => |r| .{
...@@ -813,7 +808,6 @@ pub const Nav = struct {...@@ -813,7 +808,6 @@ pub const Nav = struct {
813 .is_const = r.is_const,808 .is_const = r.is_const,
814 .alignment = r.alignment,809 .alignment = r.alignment,
815 .@"addrspace" = r.@"addrspace",810 .@"addrspace" = r.@"addrspace",
816 .is_usingnamespace = nav.is_usingnamespace,
817 .is_threadlocal = false,811 .is_threadlocal = false,
818 },812 },
819 },813 },
...@@ -6865,8 +6859,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {...@@ -6865,8 +6859,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
6865 {6859 {
6866 namespace.pub_decls.deinit(gpa);6860 namespace.pub_decls.deinit(gpa);
6867 namespace.priv_decls.deinit(gpa);6861 namespace.priv_decls.deinit(gpa);
6868 namespace.pub_usingnamespace.deinit(gpa);
6869 namespace.priv_usingnamespace.deinit(gpa);
6870 namespace.comptime_decls.deinit(gpa);6862 namespace.comptime_decls.deinit(gpa);
6871 namespace.test_decls.deinit(gpa);6863 namespace.test_decls.deinit(gpa);
6872 }6864 }
...@@ -11502,7 +11494,6 @@ pub fn createNav(...@@ -11502,7 +11494,6 @@ pub fn createNav(
11502 .@"linksection" = opts.@"linksection",11494 .@"linksection" = opts.@"linksection",
11503 .@"addrspace" = opts.@"addrspace",11495 .@"addrspace" = opts.@"addrspace",
11504 } },11496 } },
11505 .is_usingnamespace = false,
11506 }));11497 }));
11507 return index_unwrapped.wrap(ip);11498 return index_unwrapped.wrap(ip);
11508}11499}
...@@ -11517,8 +11508,6 @@ pub fn createDeclNav(...@@ -11517,8 +11508,6 @@ pub fn createDeclNav(
11517 fqn: NullTerminatedString,11508 fqn: NullTerminatedString,
11518 zir_index: TrackedInst.Index,11509 zir_index: TrackedInst.Index,
11519 namespace: NamespaceIndex,11510 namespace: NamespaceIndex,
11520 /// TODO: this is hacky! See `Nav.is_usingnamespace`.
11521 is_usingnamespace: bool,
11522) Allocator.Error!Nav.Index {11511) Allocator.Error!Nav.Index {
11523 const navs = ip.getLocal(tid).getMutableNavs(gpa);11512 const navs = ip.getLocal(tid).getMutableNavs(gpa);
1152411513
...@@ -11537,7 +11526,6 @@ pub fn createDeclNav(...@@ -11537,7 +11526,6 @@ pub fn createDeclNav(
11537 .zir_index = zir_index,11526 .zir_index = zir_index,
11538 },11527 },
11539 .status = .unresolved,11528 .status = .unresolved,
11540 .is_usingnamespace = is_usingnamespace,
11541 }));11529 }));
1154211530
11543 return nav;11531 return nav;
src/Package/Fetch.zig+47-44
...@@ -27,6 +27,22 @@...@@ -27,6 +27,22 @@
27//! All of this must be done with only referring to the state inside this struct27//! All of this must be done with only referring to the state inside this struct
28//! because this work will be done in a dedicated thread.28//! 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
30arena: std.heap.ArenaAllocator,46arena: std.heap.ArenaAllocator,
31location: Location,47location: Location,
32location_tok: std.zig.Ast.TokenIndex,48location_tok: std.zig.Ast.TokenIndex,
...@@ -184,7 +200,7 @@ pub const JobQueue = struct {...@@ -184,7 +200,7 @@ pub const JobQueue = struct {
184200
185 const hash_slice = hash.toSlice();201 const hash_slice = hash.toSlice();
186202
187 try buf.print(203 try buf.writer().print(
188 \\ pub const {f} = struct {{204 \\ pub const {f} = struct {{
189 \\205 \\
190 , .{std.zig.fmtId(hash_slice)});206 , .{std.zig.fmtId(hash_slice)});
...@@ -211,15 +227,15 @@ pub const JobQueue = struct {...@@ -211,15 +227,15 @@ pub const JobQueue = struct {
211 }227 }
212228
213 try buf.print(229 try buf.print(
214 \\ pub const build_root = "{fq}";230 \\ pub const build_root = "{f}";
215 \\231 \\
216 , .{fetch.package_root});232 , .{std.fmt.alt(fetch.package_root, .formatEscapeString)});
217233
218 if (fetch.has_build_zig) {234 if (fetch.has_build_zig) {
219 try buf.print(235 try buf.print(
220 \\ pub const build_zig = @import("{f}");236 \\ pub const build_zig = @import("{f}");
221 \\237 \\
222 , .{std.zig.fmtEscapes(hash_slice)});238 , .{std.zig.fmtString(hash_slice)});
223 }239 }
224240
225 if (fetch.manifest) |*manifest| {241 if (fetch.manifest) |*manifest| {
...@@ -231,7 +247,7 @@ pub const JobQueue = struct {...@@ -231,7 +247,7 @@ pub const JobQueue = struct {
231 const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue;247 const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue;
232 try buf.print(248 try buf.print(
233 " .{{ \"{f}\", \"{f}\" }},\n",249 " .{{ \"{f}\", \"{f}\" }},\n",
234 .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) },250 .{ std.zig.fmtEscapes(name), std.zig.fmtString(h.toSlice()) },
235 );251 );
236 }252 }
237253
...@@ -263,7 +279,7 @@ pub const JobQueue = struct {...@@ -263,7 +279,7 @@ pub const JobQueue = struct {
263 const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue;279 const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue;
264 try buf.print(280 try buf.print(
265 " .{{ \"{f}\", \"{f}\" }},\n",281 " .{{ \"{f}\", \"{f}\" }},\n",
266 .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) },282 .{ std.zig.fmtString(name), std.zig.fmtString(h.toSlice()) },
267 );283 );
268 }284 }
269 try buf.appendSlice("};\n");285 try buf.appendSlice("};\n");
...@@ -420,14 +436,14 @@ pub fn run(f: *Fetch) RunError!void {...@@ -420,14 +436,14 @@ pub fn run(f: *Fetch) RunError!void {
420 }436 }
421 if (f.job_queue.read_only) return f.fail(437 if (f.job_queue.read_only) return f.fail(
422 f.name_tok,438 f.name_tok,
423 try eb.printString("package not found at '{}{s}'", .{439 try eb.printString("package not found at '{f}{s}'", .{
424 cache_root, pkg_sub_path,440 cache_root, pkg_sub_path,
425 }),441 }),
426 );442 );
427 },443 },
428 else => |e| {444 else => |e| {
429 try eb.addRootErrorMessage(.{445 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}", .{
431 cache_root, pkg_sub_path, @errorName(e),447 cache_root, pkg_sub_path, @errorName(e),
432 }),448 }),
433 });449 });
...@@ -961,7 +977,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re...@@ -961,7 +977,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
961 if (ascii.eqlIgnoreCase(uri.scheme, "file")) {977 if (ascii.eqlIgnoreCase(uri.scheme, "file")) {
962 const path = try uri.path.toRawMaybeAlloc(arena);978 const path = try uri.path.toRawMaybeAlloc(arena);
963 return .{ .file = f.parent_package_root.openFile(path, .{}) catch |err| {979 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}", .{
965 f.parent_package_root, path, @errorName(err),981 f.parent_package_root, path, @errorName(err),
966 }));982 }));
967 } };983 } };
...@@ -1063,13 +1079,16 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re...@@ -1063,13 +1079,16 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
1063 });1079 });
1064 const notes_start = try eb.reserveNotes(notes_len);1080 const notes_start = try eb.reserveNotes(notes_len);
1065 eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{1081 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 }),
1067 }));1086 }));
1068 return error.FetchFailed;1087 return error.FetchFailed;
1069 }1088 }
10701089
1071 var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined;1090 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;
1073 var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| {1092 var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| {
1074 return f.fail(f.location_tok, try eb.printString(1093 return f.fail(f.location_tok, try eb.printString(
1075 "unable to create fetch stream: {s}",1094 "unable to create fetch stream: {s}",
...@@ -1305,7 +1324,7 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult {...@@ -1305,7 +1324,7 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult {
1305 .{@errorName(err)},1324 .{@errorName(err)},
1306 ));1325 ));
1307 if (len == 0) break;1326 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(
1309 "write temporary zip file failed: {s}",1328 "write temporary zip file failed: {s}",
1310 .{@errorName(err)},1329 .{@errorName(err)},
1311 ));1330 ));
...@@ -1813,28 +1832,6 @@ pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifes...@@ -1813,28 +1832,6 @@ pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifes
1813 }1832 }
1814}1833}
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
1838// Detects executable header: ELF or Macho-O magic header or shebang line.1835// Detects executable header: ELF or Macho-O magic header or shebang line.
1839const FileHeader = struct {1836const FileHeader = struct {
1840 header: [4]u8 = undefined,1837 header: [4]u8 = undefined,
...@@ -2052,15 +2049,15 @@ const UnpackResult = struct {...@@ -2052,15 +2049,15 @@ const UnpackResult = struct {
2052 // output errors to string2049 // output errors to string
2053 var errors = try fetch.error_bundle.toOwnedBundle("");2050 var errors = try fetch.error_bundle.toOwnedBundle("");
2054 defer errors.deinit(gpa);2051 defer errors.deinit(gpa);
2055 var out = std.ArrayList(u8).init(gpa);2052 var aw: std.io.Writer.Allocating = .init(gpa);
2056 defer out.deinit();2053 defer aw.deinit();
2057 try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer());2054 try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer);
2058 try std.testing.expectEqualStrings(2055 try std.testing.expectEqualStrings(
2059 \\error: unable to unpack2056 \\error: unable to unpack
2060 \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError2057 \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError
2061 \\ note: file 'dir2/file4' has unsupported type 'x'2058 \\ note: file 'dir2/file4' has unsupported type 'x'
2062 \\2059 \\
2063 , out.items);2060 , aw.getWritten());
2064 }2061 }
2065};2062};
20662063
...@@ -2076,7 +2073,7 @@ test "zip" {...@@ -2076,7 +2073,7 @@ test "zip" {
2076 {2073 {
2077 var zip_file = try tmp.dir.createFile("test.zip", .{});2074 var zip_file = try tmp.dir.createFile("test.zip", .{});
2078 defer zip_file.close();2075 defer zip_file.close();
2079 var bw = std.io.bufferedWriter(zip_file.writer());2076 var bw = std.io.bufferedWriter(zip_file.deprecatedWriter());
2080 var store: [test_files.len]std.zip.testutil.FileStore = undefined;2077 var store: [test_files.len]std.zip.testutil.FileStore = undefined;
2081 try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{});2078 try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{});
2082 try bw.flush();2079 try bw.flush();
...@@ -2109,7 +2106,7 @@ test "zip with one root folder" {...@@ -2109,7 +2106,7 @@ test "zip with one root folder" {
2109 {2106 {
2110 var zip_file = try tmp.dir.createFile("test.zip", .{});2107 var zip_file = try tmp.dir.createFile("test.zip", .{});
2111 defer zip_file.close();2108 defer zip_file.close();
2112 var bw = std.io.bufferedWriter(zip_file.writer());2109 var bw = std.io.bufferedWriter(zip_file.deprecatedWriter());
2113 var store: [test_files.len]std.zip.testutil.FileStore = undefined;2110 var store: [test_files.len]std.zip.testutil.FileStore = undefined;
2114 try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{});2111 try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{});
2115 try bw.flush();2112 try bw.flush();
...@@ -2427,9 +2424,15 @@ const TestFetchBuilder = struct {...@@ -2427,9 +2424,15 @@ const TestFetchBuilder = struct {
2427 if (notes_len > 0) {2424 if (notes_len > 0) {
2428 try std.testing.expectEqual(notes_len, em.notes_len);2425 try std.testing.expectEqual(notes_len, em.notes_len);
2429 }2426 }
2430 var al = std.ArrayList(u8).init(std.testing.allocator);2427 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
2431 defer al.deinit();2428 defer aw.deinit();
2432 try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer());2429 try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer);
2433 try std.testing.expectEqualStrings(msg, al.items);2430 try std.testing.expectEqualStrings(msg, aw.getWritten());
2434 }2431 }
2435};2432};
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) {...@@ -135,9 +135,8 @@ pub const Oid = union(Format) {
135 } else error.InvalidOid;135 } else error.InvalidOid;
136 }136 }
137137
138 pub fn format(oid: Oid, w: *Writer, comptime fmt: []const u8) Writer.Error!void {138 pub fn format(oid: Oid, writer: *std.io.Writer) std.io.Writer.Error!void {
139 comptime assert(fmt.len == 0);139 try writer.print("{x}", .{oid.slice()});
140 try w.print("{x}", .{oid.slice()});
141 }140 }
142141
143 pub fn slice(oid: *const Oid) []const u8 {142 pub fn slice(oid: *const Oid) []const u8 {
...@@ -697,13 +696,21 @@ pub const Session = struct {...@@ -697,13 +696,21 @@ pub const Session = struct {
697 fn init(allocator: Allocator, uri: std.Uri) !Location {696 fn init(allocator: Allocator, uri: std.Uri) !Location {
698 const scheme = try allocator.dupe(u8, uri.scheme);697 const scheme = try allocator.dupe(u8, uri.scheme);
699 errdefer allocator.free(scheme);698 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;
701 errdefer if (user) |s| allocator.free(s);702 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;
703 errdefer if (password) |s| allocator.free(s);706 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;
705 errdefer if (host) |s| allocator.free(s);710 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 });
707 errdefer allocator.free(path);714 errdefer allocator.free(path);
708 // The query and fragment are not used as part of the base server URI.715 // The query and fragment are not used as part of the base server URI.
709 return .{716 return .{
...@@ -734,7 +741,9 @@ pub const Session = struct {...@@ -734,7 +741,9 @@ pub const Session = struct {
734 fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator {741 fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator {
735 var info_refs_uri = session.location.uri;742 var info_refs_uri = session.location.uri;
736 {743 {
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 });
738 defer session.allocator.free(session_uri_path);747 defer session.allocator.free(session_uri_path);
739 info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) };748 info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) };
740 }749 }
...@@ -758,7 +767,9 @@ pub const Session = struct {...@@ -758,7 +767,9 @@ pub const Session = struct {
758 if (request.response.status != .ok) return error.ProtocolError;767 if (request.response.status != .ok) return error.ProtocolError;
759 const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects;768 const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects;
760 if (any_redirects_occurred) {769 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 });
762 defer session.allocator.free(request_uri_path);773 defer session.allocator.free(request_uri_path);
763 if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect;774 if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect;
764 var new_uri = request.uri;775 var new_uri = request.uri;
...@@ -845,7 +856,9 @@ pub const Session = struct {...@@ -845,7 +856,9 @@ pub const Session = struct {
845 pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator {856 pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator {
846 var upload_pack_uri = session.location.uri;857 var upload_pack_uri = session.location.uri;
847 {858 {
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 });
849 defer session.allocator.free(session_uri_path);862 defer session.allocator.free(session_uri_path);
850 upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) };863 upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) };
851 }864 }
...@@ -962,7 +975,9 @@ pub const Session = struct {...@@ -962,7 +975,9 @@ pub const Session = struct {
962 ) !FetchStream {975 ) !FetchStream {
963 var upload_pack_uri = session.location.uri;976 var upload_pack_uri = session.location.uri;
964 {977 {
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 });
966 defer session.allocator.free(session_uri_path);981 defer session.allocator.free(session_uri_path);
967 upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) };982 upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) };
968 }983 }
...@@ -1058,7 +1073,7 @@ pub const Session = struct {...@@ -1058,7 +1073,7 @@ pub const Session = struct {
1058 ProtocolError,1073 ProtocolError,
1059 UnexpectedPacket,1074 UnexpectedPacket,
1060 };1075 };
1061 pub const Reader = std.io.Reader(*FetchStream, ReadError, read);1076 pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read);
10621077
1063 const StreamCode = enum(u8) {1078 const StreamCode = enum(u8) {
1064 pack_data = 1,1079 pack_data = 1,
src/Sema.zig+108-258
...@@ -5,6 +5,39 @@...@@ -5,6 +5,39 @@
5//! Does type checking, comptime control flow, and safety-check generation.5//! Does type checking, comptime control flow, and safety-check generation.
6//! This is the the heart of the Zig compiler.6//! 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
8pt: Zcu.PerThread,41pt: Zcu.PerThread,
9/// Alias to `zcu.gpa`.42/// Alias to `zcu.gpa`.
10gpa: Allocator,43gpa: Allocator,
...@@ -157,39 +190,6 @@ pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc {...@@ -157,39 +190,6 @@ pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc {
157 return &sema.comptime_allocs.items[@intFromEnum(idx)];190 return &sema.comptime_allocs.items[@intFromEnum(idx)];
158}191}
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
193pub const default_branch_quota = 1000;193pub const default_branch_quota = 1000;
194194
195pub const InferredErrorSet = struct {195pub const InferredErrorSet = struct {
...@@ -1144,7 +1144,7 @@ fn analyzeBodyInner(...@@ -1144,7 +1144,7 @@ fn analyzeBodyInner(
11441144
1145 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.1145 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.
1146 if (build_options.enable_logging) {1146 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: {
1148 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool);1148 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool);
1149 const file = zcu.fileByIndex(file_index);1149 const file = zcu.fileByIndex(file_index);
1150 break :path file.path.fmt(zcu.comp);1150 break :path file.path.fmt(zcu.comp);
...@@ -1280,7 +1280,6 @@ fn analyzeBodyInner(...@@ -1280,7 +1280,6 @@ fn analyzeBodyInner(
1280 .tag_name => try sema.zirTagName(block, inst),1280 .tag_name => try sema.zirTagName(block, inst),
1281 .type_name => try sema.zirTypeName(block, inst),1281 .type_name => try sema.zirTypeName(block, inst),
1282 .frame_type => try sema.zirFrameType(block, inst),1282 .frame_type => try sema.zirFrameType(block, inst),
1283 .frame_size => try sema.zirFrameSize(block, inst),
1284 .int_from_float => try sema.zirIntFromFloat(block, inst),1283 .int_from_float => try sema.zirIntFromFloat(block, inst),
1285 .float_from_int => try sema.zirFloatFromInt(block, inst),1284 .float_from_int => try sema.zirFloatFromInt(block, inst),
1286 .ptr_from_int => try sema.zirPtrFromInt(block, inst),1285 .ptr_from_int => try sema.zirPtrFromInt(block, inst),
...@@ -1302,7 +1301,6 @@ fn analyzeBodyInner(...@@ -1302,7 +1301,6 @@ fn analyzeBodyInner(
1302 .mul_add => try sema.zirMulAdd(block, inst),1301 .mul_add => try sema.zirMulAdd(block, inst),
1303 .builtin_call => try sema.zirBuiltinCall(block, inst),1302 .builtin_call => try sema.zirBuiltinCall(block, inst),
1304 .@"resume" => try sema.zirResume(block, inst),1303 .@"resume" => try sema.zirResume(block, inst),
1305 .@"await" => try sema.zirAwait(block, inst),
1306 .for_len => try sema.zirForLen(block, inst),1304 .for_len => try sema.zirForLen(block, inst),
1307 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),1305 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),
1308 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),1306 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),
...@@ -1410,12 +1408,10 @@ fn analyzeBodyInner(...@@ -1410,12 +1408,10 @@ fn analyzeBodyInner(
1410 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),1408 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
1411 .prefetch => try sema.zirPrefetch( block, extended),1409 .prefetch => try sema.zirPrefetch( block, extended),
1412 .error_cast => try sema.zirErrorCast( block, extended),1410 .error_cast => try sema.zirErrorCast( block, extended),
1413 .await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
1414 .select => try sema.zirSelect( block, extended),1411 .select => try sema.zirSelect( block, extended),
1415 .int_from_error => try sema.zirIntFromError( block, extended),1412 .int_from_error => try sema.zirIntFromError( block, extended),
1416 .error_from_int => try sema.zirErrorFromInt( block, extended),1413 .error_from_int => try sema.zirErrorFromInt( block, extended),
1417 .reify => try sema.zirReify( block, extended, inst),1414 .reify => try sema.zirReify( block, extended, inst),
1418 .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
1419 .cmpxchg => try sema.zirCmpxchg( block, extended),1415 .cmpxchg => try sema.zirCmpxchg( block, extended),
1420 .c_va_arg => try sema.zirCVaArg( block, extended),1416 .c_va_arg => try sema.zirCVaArg( block, extended),
1421 .c_va_copy => try sema.zirCVaCopy( block, extended),1417 .c_va_copy => try sema.zirCVaCopy( block, extended),
...@@ -2767,7 +2763,7 @@ fn zirTupleDecl(...@@ -2767,7 +2763,7 @@ fn zirTupleDecl(
2767 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);2763 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);
2768 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });2764 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });
2769 if (field_init_val.canMutateComptimeVarState(zcu)) {2765 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);
2771 return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);2767 return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);
2772 }2768 }
2773 break :init field_init_val.toIntern();2769 break :init field_init_val.toIntern();
...@@ -2864,7 +2860,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us...@@ -2864,7 +2860,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
2864 sema.code.nullTerminatedString(str),2860 sema.code.nullTerminatedString(str),
2865 .no_embedded_nulls,2861 .no_embedded_nulls,
2866 );2862 );
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);
2868 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav });2864 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav });
2869 },2865 },
2870 .decl_ref => |str| capture: {2866 .decl_ref => |str| capture: {
...@@ -2874,7 +2870,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us...@@ -2874,7 +2870,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us
2874 sema.code.nullTerminatedString(str),2870 sema.code.nullTerminatedString(str),
2875 .no_embedded_nulls,2871 .no_embedded_nulls,
2876 );2872 );
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);
2878 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav });2874 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav });
2879 },2875 },
2880 };2876 };
...@@ -3030,8 +3026,8 @@ pub fn createTypeName(...@@ -3030,8 +3026,8 @@ pub fn createTypeName(
30303026
3031 var aw: std.io.Writer.Allocating = .init(gpa);3027 var aw: std.io.Writer.Allocating = .init(gpa);
3032 defer aw.deinit();3028 defer aw.deinit();
3033 const bw = &aw.interface;3029 const w = &aw.writer;
3034 bw.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;3030 w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
30353031
3036 var arg_i: usize = 0;3032 var arg_i: usize = 0;
3037 for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {3033 for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
...@@ -3044,13 +3040,13 @@ pub fn createTypeName(...@@ -3044,13 +3040,13 @@ pub fn createTypeName(
3044 // result in a compile error.3040 // result in a compile error.
3045 const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat3041 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
3049 // Limiting the depth here helps avoid type names getting too long, which3045 // Limiting the depth here helps avoid type names getting too long, which
3050 // in turn helps to avoid unreasonably long symbol names for namespaced3046 // in turn helps to avoid unreasonably long symbol names for namespaced
3051 // symbols. Such names should ideally be human-readable, and additionally,3047 // symbols. Such names should ideally be human-readable, and additionally,
3052 // some tooling may not support very long symbol names.3048 // some tooling may not support very long symbol names.
3053 bw.print("{f}", .{Value.fmtValueSemaFull(.{3049 w.print("{f}", .{Value.fmtValueSemaFull(.{
3054 .val = arg_val,3050 .val = arg_val,
3055 .pt = pt,3051 .pt = pt,
3056 .opt_sema = sema,3052 .opt_sema = sema,
...@@ -3063,7 +3059,7 @@ pub fn createTypeName(...@@ -3063,7 +3059,7 @@ pub fn createTypeName(
3063 else => continue,3059 else => continue,
3064 };3060 };
30653061
3066 try bw.writeByte(')');3062 w.writeByte(')') catch return error.OutOfMemory;
3067 return .{3063 return .{
3068 .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls),3064 .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls),
3069 .nav = .none,3065 .nav = .none,
...@@ -5578,9 +5574,8 @@ fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -5578,9 +5574,8 @@ fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
55785574
5579 if (operand_ty.arrayLen(zcu) != extra.expect_len) {5575 if (operand_ty.arrayLen(zcu) != extra.expect_len) {
5580 return sema.failWithOwnedErrorMsg(block, msg: {5576 return sema.failWithOwnedErrorMsg(block, msg: {
5581 const msg = try sema.errMsg(src, "expected {} elements for destructure, found {}", .{5577 const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{
5582 extra.expect_len,5578 extra.expect_len, operand_ty.arrayLen(zcu),
5583 operand_ty.arrayLen(zcu),
5584 });5579 });
5585 errdefer msg.destroy(sema.gpa);5580 errdefer msg.destroy(sema.gpa);
5586 try sema.errNote(destructure_src, msg, "result destructured here", .{});5581 try sema.errNote(destructure_src, msg, "result destructured here", .{});
...@@ -5912,26 +5907,25 @@ fn zirCompileLog(...@@ -5912,26 +5907,25 @@ fn zirCompileLog(
59125907
5913 var aw: std.io.Writer.Allocating = .init(gpa);5908 var aw: std.io.Writer.Allocating = .init(gpa);
5914 defer aw.deinit();5909 defer aw.deinit();
5915 const bw = &aw.interface;5910 const writer = &aw.writer;
59165911
5917 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);5912 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
5918 const src_node = extra.data.src_node;5913 const src_node = extra.data.src_node;
5919 const args = sema.code.refSlice(extra.end, extended.small);5914 const args = sema.code.refSlice(extra.end, extended.small);
59205915
5921 for (args, 0..) |arg_ref, i| {5916 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
5924 const arg = try sema.resolveInst(arg_ref);5919 const arg = try sema.resolveInst(arg_ref);
5925 const arg_ty = sema.typeOf(arg);5920 const arg_ty = sema.typeOf(arg);
5926 if (try sema.resolveValueResolveLazy(arg)) |val| {5921 if (try sema.resolveValueResolveLazy(arg)) |val| {
5927 bw.print("@as({f}, {f})", .{5922 writer.print("@as({f}, {f})", .{
5928 arg_ty.fmt(pt), val.fmtValueSema(pt, sema),5923 arg_ty.fmt(pt), val.fmtValueSema(pt, sema),
5929 }) catch return error.OutOfMemory;5924 }) catch return error.OutOfMemory;
5930 } else {5925 } 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;
5932 }5927 }
5933 }5928 }
5934 bw.writeByte('\n') catch return error.OutOfMemory;
59355929
5936 const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls);5930 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...@@ -6928,7 +6922,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
6928 inst_data.get(sema.code),6922 inst_data.get(sema.code),
6929 .no_embedded_nulls,6923 .no_embedded_nulls,
6930 );6924 );
6931 const nav_index = try sema.lookupIdentifier(block, src, decl_name);6925 const nav_index = try sema.lookupIdentifier(block, decl_name);
6932 return sema.analyzeNavRef(block, src, nav_index);6926 return sema.analyzeNavRef(block, src, nav_index);
6933}6927}
69346928
...@@ -6943,16 +6937,16 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -6943,16 +6937,16 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
6943 inst_data.get(sema.code),6937 inst_data.get(sema.code),
6944 .no_embedded_nulls,6938 .no_embedded_nulls,
6945 );6939 );
6946 const nav = try sema.lookupIdentifier(block, src, decl_name);6940 const nav = try sema.lookupIdentifier(block, decl_name);
6947 return sema.analyzeNavVal(block, src, nav);6941 return sema.analyzeNavVal(block, src, nav);
6948}6942}
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 {
6951 const pt = sema.pt;6945 const pt = sema.pt;
6952 const zcu = pt.zcu;6946 const zcu = pt.zcu;
6953 var namespace = block.namespace;6947 var namespace = block.namespace;
6954 while (true) {6948 while (true) {
6955 if (try sema.lookupInNamespace(block, src, namespace, name, false)) |lookup| {6949 if (try sema.lookupInNamespace(block, namespace, name)) |lookup| {
6956 assert(lookup.accessible);6950 assert(lookup.accessible);
6957 return lookup.nav;6951 return lookup.nav;
6958 }6952 }
...@@ -6961,15 +6955,12 @@ fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPoo...@@ -6961,15 +6955,12 @@ fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPoo
6961 unreachable; // AstGen detects use of undeclared identifiers.6955 unreachable; // AstGen detects use of undeclared identifiers.
6962}6956}
69636957
6964/// This looks up a member of a specific namespace. It is affected by `usingnamespace` but6958/// This looks up a member of a specific namespace.
6965/// only for ones in the specified namespace.
6966fn lookupInNamespace(6959fn lookupInNamespace(
6967 sema: *Sema,6960 sema: *Sema,
6968 block: *Block,6961 block: *Block,
6969 src: LazySrcLoc,
6970 namespace_index: InternPool.NamespaceIndex,6962 namespace_index: InternPool.NamespaceIndex,
6971 ident_name: InternPool.NullTerminatedString,6963 ident_name: InternPool.NullTerminatedString,
6972 observe_usingnamespace: bool,
6973) CompileError!?struct {6964) CompileError!?struct {
6974 nav: InternPool.Nav.Index,6965 nav: InternPool.Nav.Index,
6975 /// If `false`, the declaration is in a different file and is not `pub`.6966 /// If `false`, the declaration is in a different file and is not `pub`.
...@@ -6978,7 +6969,6 @@ fn lookupInNamespace(...@@ -6978,7 +6969,6 @@ fn lookupInNamespace(
6978} {6969} {
6979 const pt = sema.pt;6970 const pt = sema.pt;
6980 const zcu = pt.zcu;6971 const zcu = pt.zcu;
6981 const ip = &zcu.intern_pool;
69826972
6983 try pt.ensureNamespaceUpToDate(namespace_index);6973 try pt.ensureNamespaceUpToDate(namespace_index);
69846974
...@@ -6995,75 +6985,7 @@ fn lookupInNamespace(...@@ -6995,75 +6985,7 @@ fn lookupInNamespace(
6995 } });6985 } });
6996 }6986 }
69976987
6998 if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) {6988 if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
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| {
7067 return .{6989 return .{
7068 .nav = nav_index,6990 .nav = nav_index,
7069 .accessible = true,6991 .accessible = true,
...@@ -7652,10 +7574,6 @@ fn analyzeCall(...@@ -7652,10 +7574,6 @@ fn analyzeCall(
7652 const ip = &zcu.intern_pool;7574 const ip = &zcu.intern_pool;
7653 const arena = sema.arena;7575 const arena = sema.arena;
76547576
7655 if (modifier == .async_kw) {
7656 return sema.failWithUseOfAsync(block, call_src);
7657 }
7658
7659 const maybe_func_inst = try sema.funcDeclSrcInst(callee);7577 const maybe_func_inst = try sema.funcDeclSrcInst(callee);
7660 const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{7578 const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{
7661 .base_node_inst = fn_decl_inst,7579 .base_node_inst = fn_decl_inst,
...@@ -8047,14 +7965,13 @@ fn analyzeCall(...@@ -8047,14 +7965,13 @@ fn analyzeCall(
8047 }7965 }
80487966
8049 const call_tag: Air.Inst.Tag = switch (modifier) {7967 const call_tag: Air.Inst.Tag = switch (modifier) {
8050 .auto, .no_async => .call,7968 .auto, .no_suspend => .call,
8051 .never_tail => .call_never_tail,7969 .never_tail => .call_never_tail,
8052 .never_inline => .call_never_inline,7970 .never_inline => .call_never_inline,
8053 .always_tail => .call_always_tail,7971 .always_tail => .call_always_tail,
80547972
8055 .always_inline,7973 .always_inline,
8056 .compile_time,7974 .compile_time,
8057 .async_kw,
8058 => unreachable,7975 => unreachable,
8059 };7976 };
80607977
...@@ -9417,14 +9334,6 @@ fn resolveGenericBody(...@@ -9417,14 +9334,6 @@ fn resolveGenericBody(
9417 return sema.resolveConstDefinedValue(block, src, result, reason);9334 return sema.resolveConstDefinedValue(block, src, result, reason);
9418}9335}
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`).
9428pub fn handleExternLibName(9337pub fn handleExternLibName(
9429 sema: *Sema,9338 sema: *Sema,
9430 block: *Block,9339 block: *Block,
...@@ -9474,11 +9383,6 @@ pub fn handleExternLibName(...@@ -9474,11 +9383,6 @@ pub fn handleExternLibName(
9474 .{ lib_name, lib_name },9383 .{ lib_name, lib_name },
9475 );9384 );
9476 }9385 }
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 };
9482 }9386 }
9483}9387}
94849388
...@@ -9543,18 +9447,17 @@ fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool {...@@ -9543,18 +9447,17 @@ fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool {
9543fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void {9447fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void {
9544 const CallingConventionsSupportingVarArgsList = struct {9448 const CallingConventionsSupportingVarArgsList = struct {
9545 arch: std.Target.Cpu.Arch,9449 arch: std.Target.Cpu.Arch,
9546 pub fn format(ctx: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void {9450 pub fn format(ctx: @This(), w: *std.io.Writer) std.io.Writer.Error!void {
9547 comptime assert(fmt.len == 0);
9548 var first = true;9451 var first = true;
9549 for (calling_conventions_supporting_var_args) |cc_inner| {9452 for (calling_conventions_supporting_var_args) |cc_inner| {
9550 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| {9453 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| {
9551 if (supported_arch == ctx.arch) break;9454 if (supported_arch == ctx.arch) break;
9552 } else continue; // callconv not supported by this arch9455 } else continue; // callconv not supported by this arch
9553 if (!first) {9456 if (!first) {
9554 try bw.writeAll(", ");9457 try w.writeAll(", ");
9555 }9458 }
9556 first = false;9459 first = false;
9557 try bw.print("'{s}'", .{@tagName(cc_inner)});9460 try w.print("'{s}'", .{@tagName(cc_inner)});
9558 }9461 }
9559 }9462 }
9560 };9463 };
...@@ -9989,12 +9892,11 @@ fn finishFunc(...@@ -9989,12 +9892,11 @@ fn finishFunc(
9989 .bad_arch => |allowed_archs| {9892 .bad_arch => |allowed_archs| {
9990 const ArchListFormatter = struct {9893 const ArchListFormatter = struct {
9991 archs: []const std.Target.Cpu.Arch,9894 archs: []const std.Target.Cpu.Arch,
9992 pub fn format(formatter: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void {9895 pub fn format(formatter: @This(), w: *std.io.Writer) std.io.Writer.Error!void {
9993 comptime assert(fmt.len == 0);
9994 for (formatter.archs, 0..) |arch, i| {9896 for (formatter.archs, 0..) |arch, i| {
9995 if (i != 0)9897 if (i != 0)
9996 try bw.writeAll(", ");9898 try w.writeAll(", ");
9997 try bw.print("'{s}'", .{@tagName(arch)});9899 try w.print("'{s}'", .{@tagName(arch)});
9998 }9900 }
9999 }9901 }
10000 };9902 };
...@@ -13965,7 +13867,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -13965,7 +13867,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13965 const zcu = pt.zcu;13867 const zcu = pt.zcu;
13966 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;13868 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
13967 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;13869 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
13968 const src = block.nodeOffset(inst_data.src_node);
13969 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);13870 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
13970 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);13871 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
13971 const container_type = try sema.resolveType(block, lhs_src, extra.lhs);13872 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...@@ -13974,7 +13875,7 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
13974 try sema.checkNamespaceType(block, lhs_src, container_type);13875 try sema.checkNamespaceType(block, lhs_src, container_type);
1397513876
13976 const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false;13877 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| {
13978 if (lookup.accessible) {13879 if (lookup.accessible) {
13979 return .bool_true;13880 return .bool_true;
13980 }13881 }
...@@ -14173,7 +14074,7 @@ fn zirShl(...@@ -14173,7 +14074,7 @@ fn zirShl(
14173 });14074 });
14174 }14075 }
14175 } else if (scalar_rhs_ty.isSignedInt(zcu)) {14076 } 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)});
14177 }14078 }
1417814079
14179 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {14080 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....@@ -14478,7 +14379,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
14478 const scalar_tag = scalar_ty.zigTypeTag(zcu);14379 const scalar_tag = scalar_ty.zigTypeTag(zcu);
1447914380
14480 if (scalar_tag != .int and scalar_tag != .bool)14381 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
14483 return analyzeBitNot(sema, block, operand, src);14384 return analyzeBitNot(sema, block, operand, src);
14484}14385}
...@@ -17094,7 +16995,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -17094,7 +16995,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
17094 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;16995 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
17095 const tree = file.getTree(zcu) catch |err| {16996 const tree = file.getTree(zcu) catch |err| {
17096 // In this case we emit a warning + a less precise source location.16997 // 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}", .{
17098 file.path.fmt(zcu.comp), @errorName(err),16999 file.path.fmt(zcu.comp), @errorName(err),
17099 });17000 });
17100 break :name null;17001 break :name null;
...@@ -17122,7 +17023,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -17122,7 +17023,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
17122 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;17023 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
17123 const tree = file.getTree(zcu) catch |err| {17024 const tree = file.getTree(zcu) catch |err| {
17124 // In this case we emit a warning + a less precise source location.17025 // 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}", .{
17126 file.path.fmt(zcu.comp), @errorName(err),17027 file.path.fmt(zcu.comp), @errorName(err),
17127 });17028 });
17128 break :name null;17029 break :name null;
...@@ -17755,7 +17656,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -17755,7 +17656,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
17755 } });17656 } });
17756 };17657 };
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
17760 const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum");17661 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...@@ -17868,7 +17769,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
17868 } });17769 } });
17869 };17770 };
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
17873 const enum_tag_ty_val = try pt.intern(.{ .opt = .{17774 const enum_tag_ty_val = try pt.intern(.{ .opt = .{
17874 .ty = (try pt.optionalType(.type_type)).toIntern(),17775 .ty = (try pt.optionalType(.type_type)).toIntern(),
...@@ -18063,7 +17964,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -18063,7 +17964,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
18063 } });17964 } });
18064 };17965 };
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
18068 const backing_integer_val = try pt.intern(.{ .opt = .{17969 const backing_integer_val = try pt.intern(.{ .opt = .{
18069 .ty = (try pt.optionalType(.type_type)).toIntern(),17970 .ty = (try pt.optionalType(.type_type)).toIntern(),
...@@ -18102,7 +18003,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -18102,7 +18003,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
18102 const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque");18003 const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque");
1810318004
18104 try ty.resolveFields(pt);18005 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
18107 const field_values = .{18008 const field_values = .{
18108 // decls: []const Declaration,18009 // decls: []const Declaration,
...@@ -18124,7 +18025,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -18124,7 +18025,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1812418025
18125fn typeInfoDecls(18026fn typeInfoDecls(
18126 sema: *Sema,18027 sema: *Sema,
18127 block: *Block,
18128 src: LazySrcLoc,18028 src: LazySrcLoc,
18129 opt_namespace: InternPool.OptionalNamespaceIndex,18029 opt_namespace: InternPool.OptionalNamespaceIndex,
18130) CompileError!InternPool.Index {18030) CompileError!InternPool.Index {
...@@ -18140,7 +18040,7 @@ fn typeInfoDecls(...@@ -18140,7 +18040,7 @@ fn typeInfoDecls(
18140 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);18040 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
18141 defer seen_namespaces.deinit();18041 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
18145 const array_decl_ty = try pt.arrayType(.{18045 const array_decl_ty = try pt.arrayType(.{
18146 .len = decl_vals.items.len,18046 .len = decl_vals.items.len,
...@@ -18174,8 +18074,6 @@ fn typeInfoDecls(...@@ -18174,8 +18074,6 @@ fn typeInfoDecls(
1817418074
18175fn typeInfoNamespaceDecls(18075fn typeInfoNamespaceDecls(
18176 sema: *Sema,18076 sema: *Sema,
18177 block: *Block,
18178 src: LazySrcLoc,
18179 opt_namespace_index: InternPool.OptionalNamespaceIndex,18077 opt_namespace_index: InternPool.OptionalNamespaceIndex,
18180 declaration_ty: Type,18078 declaration_ty: Type,
18181 decl_vals: *std.ArrayList(InternPool.Index),18079 decl_vals: *std.ArrayList(InternPool.Index),
...@@ -18231,15 +18129,6 @@ fn typeInfoNamespaceDecls(...@@ -18231,15 +18129,6 @@ fn typeInfoNamespaceDecls(
18231 .storage = .{ .elems = &fields },18129 .storage = .{ .elems = &fields },
18232 } }));18130 } }));
18233 }18131 }
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 }
18243}18132}
1824418133
18245fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {18134fn 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...@@ -18375,7 +18264,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
18375 const uncasted_ty = sema.typeOf(uncasted_operand);18264 const uncasted_ty = sema.typeOf(uncasted_operand);
18376 if (uncasted_ty.isVector(zcu)) {18265 if (uncasted_ty.isVector(zcu)) {
18377 if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) {18266 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}'", .{
18379 uncasted_ty.fmt(pt),18268 uncasted_ty.fmt(pt),
18380 });18269 });
18381 }18270 }
...@@ -19406,13 +19295,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19406,13 +19295,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1940619295
19407 if (host_size != 0) {19296 if (host_size != 0) {
19408 if (bit_offset >= host_size * 8) {19297 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", .{
19410 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,19299 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
19411 });19300 });
19412 }19301 }
19413 const elem_bit_size = try elem_ty.bitSizeSema(pt);19302 const elem_bit_size = try elem_ty.bitSizeSema(pt);
19414 if (elem_bit_size > host_size * 8 - bit_offset) {19303 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", .{
19416 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,19305 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
19417 });19306 });
19418 }19307 }
...@@ -20573,7 +20462,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -20573,7 +20462,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
20573 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;20462 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
20574 const operand_scalar_ty = operand_ty.scalarType(zcu);20463 const operand_scalar_ty = operand_ty.scalarType(zcu);
20575 if (operand_scalar_ty.toIntern() != .bool_type) {20464 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)});
20577 }20466 }
20578 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;20467 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
20579 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1;20468 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1;
...@@ -20856,7 +20745,7 @@ fn zirReify(...@@ -20856,7 +20745,7 @@ fn zirReify(
20856 64 => .f64,20745 64 => .f64,
20857 80 => .f80,20746 80 => .f80,
20858 128 => .f128,20747 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}),
20860 };20749 };
20861 return Air.internedToRef(ty.toIntern());20750 return Air.internedToRef(ty.toIntern());
20862 },20751 },
...@@ -21747,7 +21636,7 @@ fn reifyTuple(...@@ -21747,7 +21636,7 @@ fn reifyTuple(
21747 return sema.fail(21636 return sema.fail(
21748 block,21637 block,
21749 src,21638 src,
21750 "tuple field name '{}' does not match field index {}",21639 "tuple field name '{d}' does not match field index {d}",
21751 .{ field_name_index, field_idx },21640 .{ field_name_index, field_idx },
21752 );21641 );
21753 }21642 }
...@@ -22143,12 +22032,6 @@ fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -22143,12 +22032,6 @@ fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
22143 return sema.failWithUseOfAsync(block, src);22032 return sema.failWithUseOfAsync(block, src);
22144}22033}
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
22152fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {22035fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22153 const pt = sema.pt;22036 const pt = sema.pt;
22154 const zcu = pt.zcu;22037 const zcu = pt.zcu;
...@@ -22771,7 +22654,7 @@ fn ptrCastFull(...@@ -22771,7 +22654,7 @@ fn ptrCastFull(
2277122654
22772 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {22655 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
22773 return sema.failWithOwnedErrorMsg(block, msg: {22656 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}'", .{
22775 src_info.packed_offset.host_size,22658 src_info.packed_offset.host_size,
22776 dest_info.packed_offset.host_size,22659 dest_info.packed_offset.host_size,
22777 });22660 });
...@@ -22783,7 +22666,7 @@ fn ptrCastFull(...@@ -22783,7 +22666,7 @@ fn ptrCastFull(
2278322666
22784 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {22667 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
22785 return sema.failWithOwnedErrorMsg(block, msg: {22668 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}'", .{
22787 src_info.packed_offset.bit_offset,22670 src_info.packed_offset.bit_offset,
22788 dest_info.packed_offset.bit_offset,22671 dest_info.packed_offset.bit_offset,
22789 });22672 });
...@@ -23353,7 +23236,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -23353,7 +23236,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
23353 return sema.fail(23236 return sema.fail(
23354 block,23237 block,
23355 operand_src,23238 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",
23357 .{ scalar_ty.fmt(pt), bits },23240 .{ scalar_ty.fmt(pt), bits },
23358 );23241 );
23359 }23242 }
...@@ -23690,7 +23573,7 @@ fn checkNumericType(...@@ -23690,7 +23573,7 @@ fn checkNumericType(
23690 .comptime_float, .float, .comptime_int, .int => {},23573 .comptime_float, .float, .comptime_int, .int => {},
23691 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) {23574 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) {
23692 .comptime_float, .float, .comptime_int, .int => {},23575 .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}),
23694 },23577 },
23695 else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}),23578 else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}),
23696 }23579 }
...@@ -24367,7 +24250,7 @@ fn analyzeShuffle(...@@ -24367,7 +24250,7 @@ fn analyzeShuffle(
24367 if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: {24250 if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: {
24368 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});24251 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
24369 errdefer msg.destroy(sema.gpa);24252 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) });
24371 break :msg msg;24254 break :msg msg;
24372 });24255 });
24373 }24256 }
...@@ -24795,14 +24678,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -24795,14 +24678,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
24795 var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier);24678 var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier);
24796 switch (modifier) {24679 switch (modifier) {
24797 // These can be upgraded to comptime or nosuspend calls.24680 // These can be upgraded to comptime or nosuspend calls.
24798 .auto, .never_tail, .no_async => {24681 .auto, .never_tail, .no_suspend => {
24799 if (block.isComptime()) {24682 if (block.isComptime()) {
24800 if (modifier == .never_tail) {24683 if (modifier == .never_tail) {
24801 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});24684 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
24802 }24685 }
24803 modifier = .compile_time;24686 modifier = .compile_time;
24804 } else if (extra.flags.is_nosuspend) {24687 } else if (extra.flags.is_nosuspend) {
24805 modifier = .no_async;24688 modifier = .no_suspend;
24806 }24689 }
24807 },24690 },
24808 // These can be upgraded to comptime. nosuspend bit can be safely ignored.24691 // 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...@@ -24820,14 +24703,6 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
24820 modifier = .compile_time;24703 modifier = .compile_time;
24821 }24704 }
24822 },24705 },
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 },
24831 .never_inline => {24706 .never_inline => {
24832 if (block.isComptime()) {24707 if (block.isComptime()) {
24833 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});24708 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
...@@ -25160,7 +25035,7 @@ fn analyzeMinMax(...@@ -25160,7 +25035,7 @@ fn analyzeMinMax(
25160 try sema.checkNumericType(block, operand_src, operand_ty);25035 try sema.checkNumericType(block, operand_src, operand_ty);
25161 if (operand_ty.zigTypeTag(zcu) != .vector) {25036 if (operand_ty.zigTypeTag(zcu) != .vector) {
25162 return sema.failWithOwnedErrorMsg(block, msg: {25037 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)});
25164 errdefer msg.destroy(zcu.gpa);25039 errdefer msg.destroy(zcu.gpa);
25165 try sema.errNote(operand_srcs[0], msg, "vector operand here", .{});25040 try sema.errNote(operand_srcs[0], msg, "vector operand here", .{});
25166 break :msg msg;25041 break :msg msg;
...@@ -25168,7 +25043,7 @@ fn analyzeMinMax(...@@ -25168,7 +25043,7 @@ fn analyzeMinMax(
25168 }25043 }
25169 if (operand_ty.vectorLen(zcu) != vec_len) {25044 if (operand_ty.vectorLen(zcu) != vec_len) {
25170 return sema.failWithOwnedErrorMsg(block, msg: {25045 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) });
25172 errdefer msg.destroy(zcu.gpa);25047 errdefer msg.destroy(zcu.gpa);
25173 try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len});25048 try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len});
25174 break :msg msg;25049 break :msg msg;
...@@ -25181,7 +25056,7 @@ fn analyzeMinMax(...@@ -25181,7 +25056,7 @@ fn analyzeMinMax(
25181 const operand_ty = sema.typeOf(operand);25056 const operand_ty = sema.typeOf(operand);
25182 if (operand_ty.zigTypeTag(zcu) == .vector) {25057 if (operand_ty.zigTypeTag(zcu) == .vector) {
25183 return sema.failWithOwnedErrorMsg(block, msg: {25058 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)});
25185 errdefer msg.destroy(zcu.gpa);25060 errdefer msg.destroy(zcu.gpa);
25186 try sema.errNote(operand_src, msg, "vector operand here", .{});25061 try sema.errNote(operand_src, msg, "vector operand here", .{});
25187 break :msg msg;25062 break :msg msg;
...@@ -25816,40 +25691,12 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -25816,40 +25691,12 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
25816 });25691 });
25817}25692}
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
25825fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {25694fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
25826 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;25695 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
25827 const src = block.nodeOffset(inst_data.src_node);25696 const src = block.nodeOffset(inst_data.src_node);
25828 return sema.failWithUseOfAsync(block, src);25697 return sema.failWithUseOfAsync(block, src);
25829}25698}
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
25853fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {25700fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
25854 const tracy = trace(@src());25701 const tracy = trace(@src());
25855 defer tracy.end();25702 defer tracy.end();
...@@ -26757,7 +26604,7 @@ fn explainWhyTypeIsNotExtern(...@@ -26757,7 +26604,7 @@ fn explainWhyTypeIsNotExtern(
26757 }26604 }
26758 switch (ty.fnCallingConvention(zcu)) {26605 switch (ty.fnCallingConvention(zcu)) {
26759 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),26606 .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", .{}),
26761 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),26608 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),
26762 else => return,26609 else => return,
26763 }26610 }
...@@ -27779,7 +27626,7 @@ fn namespaceLookup(...@@ -27779,7 +27626,7 @@ fn namespaceLookup(
27779 const pt = sema.pt;27626 const pt = sema.pt;
27780 const zcu = pt.zcu;27627 const zcu = pt.zcu;
27781 const gpa = sema.gpa;27628 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| {
27783 if (!lookup.accessible) {27630 if (!lookup.accessible) {
27784 return sema.failWithOwnedErrorMsg(block, msg: {27631 return sema.failWithOwnedErrorMsg(block, msg: {
27785 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{27632 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{
...@@ -29312,7 +29159,7 @@ fn coerceExtra(...@@ -29312,7 +29159,7 @@ fn coerceExtra(
29312 // return sema.fail(29159 // return sema.fail(
29313 // block,29160 // block,
29314 // inst_src,29161 // inst_src,
29315 // "type '{f}' cannot represent integer value '{}'",29162 // "type '{f}' cannot represent integer value '{f}'",
29316 // .{ dest_ty.fmt(pt), val },29163 // .{ dest_ty.fmt(pt), val },
29317 // );29164 // );
29318 //}29165 //}
...@@ -29519,7 +29366,7 @@ fn coerceExtra(...@@ -29519,7 +29366,7 @@ fn coerceExtra(
29519 try sema.errNote(param_src, msg, "parameter type declared here", .{});29366 try sema.errNote(param_src, msg, "parameter type declared here", .{});
29520 }29367 }
2952129368
29522 // TODO maybe add "cannot store an error in type '{}'" note29369 // TODO maybe add "cannot store an error in type '{f}'" note
2952329370
29524 break :msg msg;29371 break :msg msg;
29525 };29372 };
...@@ -29867,12 +29714,12 @@ const InMemoryCoercionResult = union(enum) {...@@ -29867,12 +29714,12 @@ const InMemoryCoercionResult = union(enum) {
29867 },29714 },
29868 .ptr_bit_range => |bit_range| {29715 .ptr_bit_range => |bit_range| {
29869 if (bit_range.actual_host != bit_range.wanted_host) {29716 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}'", .{
29871 bit_range.actual_host, bit_range.wanted_host,29718 bit_range.actual_host, bit_range.wanted_host,
29872 });29719 });
29873 }29720 }
29874 if (bit_range.actual_offset != bit_range.wanted_offset) {29721 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}'", .{
29876 bit_range.actual_offset, bit_range.wanted_offset,29723 bit_range.actual_offset, bit_range.wanted_offset,
29877 });29724 });
29878 }29725 }
...@@ -34989,7 +34836,7 @@ fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_...@@ -34989,7 +34836,7 @@ fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_
34989 return sema.fail(34836 return sema.fail(
34990 block,34837 block,
34991 src,34838 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}",
34993 .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum },34840 .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum },
34994 );34841 );
34995 }34842 }
...@@ -35332,11 +35179,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load...@@ -35332,11 +35179,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load
35332 switch (union_type.flagsUnordered(ip).status) {35179 switch (union_type.flagsUnordered(ip).status) {
35333 .none => {},35180 .none => {},
35334 .field_types_wip => {35181 .field_types_wip => {
35335 const msg = try sema.errMsg(35182 const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)});
35336 ty.srcLoc(zcu),
35337 "union '{f}' depends on itself",
35338 .{ty.fmt(pt)},
35339 );
35340 return sema.failWithOwnedErrorMsg(null, msg);35183 return sema.failWithOwnedErrorMsg(null, msg);
35341 },35184 },
35342 .have_field_types,35185 .have_field_types,
...@@ -37330,7 +37173,14 @@ fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg...@@ -37330,7 +37173,14 @@ fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg
37330 }37173 }
37331}37174}
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) {
37334 done,37184 done,
37335 new_val: Value,37185 new_val: Value,
37336} {37186} {
...@@ -37341,9 +37191,9 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc,...@@ -37341,9 +37191,9 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc,
3734137191
37342 var first_path: std.ArrayListUnmanaged(u8) = .empty;37192 var first_path: std.ArrayListUnmanaged(u8) = .empty;
37343 if (intermediate_value_count == 0) {37193 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)});
37345 } else {37195 } else {
37346 try first_path.print(arena, "v{}", .{intermediate_value_count - 1});37196 try first_path.print(arena, "v{d}", .{intermediate_value_count - 1});
37347 }37197 }
3734837198
37349 const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path);37199 const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path);
...@@ -37373,7 +37223,7 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc,...@@ -37373,7 +37223,7 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc,
37373 const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count});37223 const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count});
37374 const deriv_start = @import("print_value.zig").printPtrDerivation(37224 const deriv_start = @import("print_value.zig").printPtrDerivation(
37375 derivation,37225 derivation,
37376 &second_path_aw.interface,37226 &second_path_aw.writer,
37377 pt,37227 pt,
37378 .lvalue,37228 .lvalue,
37379 .{ .str = inter_name },37229 .{ .str = inter_name },
...@@ -37437,7 +37287,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList...@@ -37437,7 +37287,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList
37437 const backing_enum = union_ty.unionTagTypeHypothetical(zcu);37287 const backing_enum = union_ty.unionTagTypeHypothetical(zcu);
37438 const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;37288 const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
37439 const field_name = backing_enum.enumFieldName(field_idx, zcu);37289 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)});
37441 return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path);37291 return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path);
37442 },37292 },
37443 .aggregate => |agg| {37293 .aggregate => |agg| {
...@@ -37462,7 +37312,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList...@@ -37462,7 +37312,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList
37462 try path.print(arena, "[{d}]", .{elem_idx});37312 try path.print(arena, "[{d}]", .{elem_idx});
37463 } else {37313 } else {
37464 const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?;37314 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)});
37466 },37316 },
37467 else => unreachable,37317 else => unreachable,
37468 }37318 }
src/Sema/LowerZon.zig+8-12
...@@ -360,11 +360,7 @@ fn fail(...@@ -360,11 +360,7 @@ fn fail(
360fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {360fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
361 const pt = self.sema.pt;361 const pt = self.sema.pt;
362 return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) {362 return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) {
363 error.WrongType => return self.fail(363 error.WrongType => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(pt)}),
364 node,
365 "expected type '{f}'",
366 .{res_ty.fmt(pt)},
367 ),
368 else => |e| return e,364 else => |e| return e,
369 };365 };
370}366}
...@@ -458,7 +454,7 @@ fn lowerInt(...@@ -458,7 +454,7 @@ fn lowerInt(
458 // If lhs is unsigned and rhs is less than 0, we're out of bounds454 // If lhs is unsigned and rhs is less than 0, we're out of bounds
459 if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(455 if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(
460 node,456 node,
461 "type '{f}' cannot represent integer value '{}'",457 "type '{f}' cannot represent integer value '{d}'",
462 .{ res_ty.fmt(self.sema.pt), rhs },458 .{ res_ty.fmt(self.sema.pt), rhs },
463 );459 );
464460
...@@ -478,7 +474,7 @@ fn lowerInt(...@@ -478,7 +474,7 @@ fn lowerInt(
478 if (rhs < min_int or rhs > max_int) {474 if (rhs < min_int or rhs > max_int) {
479 return self.fail(475 return self.fail(
480 node,476 node,
481 "type '{f}' cannot represent integer value '{}'",477 "type '{f}' cannot represent integer value '{d}'",
482 .{ res_ty.fmt(self.sema.pt), rhs },478 .{ res_ty.fmt(self.sema.pt), rhs },
483 );479 );
484 }480 }
...@@ -496,7 +492,7 @@ fn lowerInt(...@@ -496,7 +492,7 @@ fn lowerInt(
496 if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {492 if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {
497 return self.fail(493 return self.fail(
498 node,494 node,
499 "type '{f}' cannot represent integer value '{f}'",495 "type '{f}' cannot represent integer value '{d}'",
500 .{ res_ty.fmt(self.sema.pt), val },496 .{ res_ty.fmt(self.sema.pt), val },
501 );497 );
502 }498 }
...@@ -517,7 +513,7 @@ fn lowerInt(...@@ -517,7 +513,7 @@ fn lowerInt(
517 switch (big_int.setFloat(val, .trunc)) {513 switch (big_int.setFloat(val, .trunc)) {
518 .inexact => return self.fail(514 .inexact => return self.fail(
519 node,515 node,
520 "fractional component prevents float value '{}' from coercion to type '{f}'",516 "fractional component prevents float value '{d}' from coercion to type '{f}'",
521 .{ val, res_ty.fmt(self.sema.pt) },517 .{ val, res_ty.fmt(self.sema.pt) },
522 ),518 ),
523 .exact => {},519 .exact => {},
...@@ -528,8 +524,8 @@ fn lowerInt(...@@ -528,8 +524,8 @@ fn lowerInt(
528 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {524 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
529 return self.fail(525 return self.fail(
530 node,526 node,
531 "type '{}' cannot represent integer value '{f}'",527 "type '{f}' cannot represent integer value '{d}'",
532 .{ val, res_ty.fmt(self.sema.pt) },528 .{ res_ty.fmt(self.sema.pt), val },
533 );529 );
534 }530 }
535531
...@@ -550,7 +546,7 @@ fn lowerInt(...@@ -550,7 +546,7 @@ fn lowerInt(
550 if (val >= out_of_range) {546 if (val >= out_of_range) {
551 return self.fail(547 return self.fail(
552 node,548 node,
553 "type '{f}' cannot represent integer value '{}'",549 "type '{f}' cannot represent integer value '{d}'",
554 .{ res_ty.fmt(self.sema.pt), val },550 .{ res_ty.fmt(self.sema.pt), val },
555 );551 );
556 }552 }
src/Type.zig+81-82
...@@ -122,14 +122,13 @@ pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool {...@@ -122,14 +122,13 @@ pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool {
122 return a.toIntern() == b.toIntern();122 return a.toIntern() == b.toIntern();
123}123}
124124
125pub fn format(ty: Type, bw: *Writer, comptime f: []const u8) !usize {125pub fn format(ty: Type, writer: *std.io.Writer) !void {
126 _ = ty;126 _ = ty;
127 _ = f;127 _ = writer;
128 _ = bw;
129 @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");128 @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
130}129}
131130
132pub const Formatter = std.fmt.Formatter(format2);131pub const Formatter = std.fmt.Formatter(Format, Format.default);
133132
134pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {133pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {
135 return .{ .data = .{134 return .{ .data = .{
...@@ -138,30 +137,28 @@ pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {...@@ -138,30 +137,28 @@ pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {
138 } };137 } };
139}138}
140139
141const FormatContext = struct {140const Format = struct {
142 ty: Type,141 ty: Type,
143 pt: Zcu.PerThread,142 pt: Zcu.PerThread,
144};
145143
146fn format2(ctx: FormatContext, bw: *Writer, comptime f: []const u8) !void {144 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
147 comptime assert(f.len == 0);145 return print(f.ty, writer, f.pt);
148 try print(ctx.ty, bw, ctx.pt);146 }
149}147};
150148
151pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) {149pub fn fmtDebug(ty: Type) std.fmt.Formatter(Type, dump) {
152 return .{ .data = ty };150 return .{ .data = ty };
153}151}
154152
155/// This is a debug function. In order to print types in a meaningful way153/// This is a debug function. In order to print types in a meaningful way
156/// we also need access to the module.154/// we also need access to the module.
157pub fn dump(start_type: Type, bw: *Writer, comptime unused_format_string: []const u8) !void {155pub fn dump(start_type: Type, writer: *std.io.Writer) std.io.Writer.Error!void {
158 comptime assert(unused_format_string.len == 0);156 return writer.print("{any}", .{start_type.ip_index});
159 return bw.print("{any}", .{start_type.ip_index});
160}157}
161158
162/// Prints a name suitable for `@typeName`.159/// Prints a name suitable for `@typeName`.
163/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.160/// 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 {
165 const zcu = pt.zcu;162 const zcu = pt.zcu;
166 const ip = &zcu.intern_pool;163 const ip = &zcu.intern_pool;
167 switch (ip.indexToKey(ty.toIntern())) {164 switch (ip.indexToKey(ty.toIntern())) {
...@@ -171,22 +168,22 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {...@@ -171,22 +168,22 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
171 .signed => 'i',168 .signed => 'i',
172 .unsigned => 'u',169 .unsigned => 'u',
173 };170 };
174 try bw.print("{c}{d}", .{ sign_char, int_type.bits });171 try writer.print("{c}{d}", .{ sign_char, int_type.bits });
175 },172 },
176 .ptr_type => {173 .ptr_type => {
177 const info = ty.ptrInfo(zcu);174 const info = ty.ptrInfo(zcu);
178175
179 if (info.sentinel != .none) switch (info.flags.size) {176 if (info.sentinel != .none) switch (info.flags.size) {
180 .one, .c => unreachable,177 .one, .c => unreachable,
181 .many => try bw.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),178 .many => try writer.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
182 .slice => try bw.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),179 .slice => try writer.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
183 } else switch (info.flags.size) {180 } else switch (info.flags.size) {
184 .one => try bw.writeAll("*"),181 .one => try writer.writeAll("*"),
185 .many => try bw.writeAll("[*]"),182 .many => try writer.writeAll("[*]"),
186 .c => try bw.writeAll("[*c]"),183 .c => try writer.writeAll("[*c]"),
187 .slice => try bw.writeAll("[]"),184 .slice => try writer.writeAll("[]"),
188 }185 }
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 ");
190 if (info.flags.alignment != .none or187 if (info.flags.alignment != .none or
191 info.packed_offset.host_size != 0 or188 info.packed_offset.host_size != 0 or
192 info.flags.vector_index != .none)189 info.flags.vector_index != .none)
...@@ -195,72 +192,72 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {...@@ -195,72 +192,72 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
195 info.flags.alignment192 info.flags.alignment
196 else193 else
197 Type.fromInterned(info.child).abiAlignment(pt.zcu);194 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
200 if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) {197 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}", .{
202 info.packed_offset.bit_offset, info.packed_offset.host_size,199 info.packed_offset.bit_offset, info.packed_offset.host_size,
203 });200 });
204 }201 }
205 if (info.flags.vector_index == .runtime) {202 if (info.flags.vector_index == .runtime) {
206 try bw.writeAll(":?");203 try writer.writeAll(":?");
207 } else if (info.flags.vector_index != .none) {204 } 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)});
209 }206 }
210 try bw.writeAll(") ");207 try writer.writeAll(") ");
211 }208 }
212 if (info.flags.address_space != .generic) {209 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)});
214 }211 }
215 if (info.flags.is_const) try bw.writeAll("const ");212 if (info.flags.is_const) try writer.writeAll("const ");
216 if (info.flags.is_volatile) try bw.writeAll("volatile ");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);
219 },216 },
220 .array_type => |array_type| {217 .array_type => |array_type| {
221 if (array_type.sentinel == .none) {218 if (array_type.sentinel == .none) {
222 try bw.print("[{d}]", .{array_type.len});219 try writer.print("[{d}]", .{array_type.len});
223 try print(Type.fromInterned(array_type.child), bw, pt);220 try print(Type.fromInterned(array_type.child), writer, pt);
224 } else {221 } else {
225 try bw.print("[{d}:{f}]", .{222 try writer.print("[{d}:{f}]", .{
226 array_type.len,223 array_type.len,
227 Value.fromInterned(array_type.sentinel).fmtValue(pt),224 Value.fromInterned(array_type.sentinel).fmtValue(pt),
228 });225 });
229 try print(Type.fromInterned(array_type.child), bw, pt);226 try print(Type.fromInterned(array_type.child), writer, pt);
230 }227 }
231 },228 },
232 .vector_type => |vector_type| {229 .vector_type => |vector_type| {
233 try bw.print("@Vector({d}, ", .{vector_type.len});230 try writer.print("@Vector({d}, ", .{vector_type.len});
234 try print(Type.fromInterned(vector_type.child), bw, pt);231 try print(Type.fromInterned(vector_type.child), writer, pt);
235 try bw.writeAll(")");232 try writer.writeAll(")");
236 },233 },
237 .opt_type => |child| {234 .opt_type => |child| {
238 try bw.writeByte('?');235 try writer.writeByte('?');
239 try print(Type.fromInterned(child), bw, pt);236 try print(Type.fromInterned(child), writer, pt);
240 },237 },
241 .error_union_type => |error_union_type| {238 .error_union_type => |error_union_type| {
242 try print(Type.fromInterned(error_union_type.error_set_type), bw, pt);239 try print(Type.fromInterned(error_union_type.error_set_type), writer, pt);
243 try bw.writeByte('!');240 try writer.writeByte('!');
244 if (error_union_type.payload_type == .generic_poison_type) {241 if (error_union_type.payload_type == .generic_poison_type) {
245 try bw.writeAll("anytype");242 try writer.writeAll("anytype");
246 } else {243 } 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);
248 }245 }
249 },246 },
250 .inferred_error_set_type => |func_index| {247 .inferred_error_set_type => |func_index| {
251 const func_nav = ip.getNav(zcu.funcInfo(func_index).owner_nav);248 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", .{
253 func_nav.fqn.fmt(ip),250 func_nav.fqn.fmt(ip),
254 });251 });
255 },252 },
256 .error_set_type => |error_set_type| {253 .error_set_type => |error_set_type| {
257 const names = error_set_type.names;254 const names = error_set_type.names;
258 try bw.writeAll("error{");255 try writer.writeAll("error{");
259 for (names.get(ip), 0..) |name, i| {256 for (names.get(ip), 0..) |name, i| {
260 if (i != 0) try bw.writeByte(',');257 if (i != 0) try writer.writeByte(',');
261 try bw.print("{f}", .{name.fmt(ip)});258 try writer.print("{f}", .{name.fmt(ip)});
262 }259 }
263 try bw.writeAll("}");260 try writer.writeAll("}");
264 },261 },
265 .simple_type => |s| switch (s) {262 .simple_type => |s| switch (s) {
266 .f16,263 .f16,
...@@ -289,97 +286,99 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {...@@ -289,97 +286,99 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void {
289 .comptime_float,286 .comptime_float,
290 .noreturn,287 .noreturn,
291 .adhoc_inferred_error_set,288 .adhoc_inferred_error_set,
292 => return bw.writeAll(@tagName(s)),289 => return writer.writeAll(@tagName(s)),
293290
294 .null,291 .null,
295 .undefined,292 .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
300 .generic_poison => unreachable,297 .generic_poison => unreachable,
301 },298 },
302 .struct_type => {299 .struct_type => {
303 const name = ip.loadStructType(ty.toIntern()).name;300 const name = ip.loadStructType(ty.toIntern()).name;
304 return bw.print("{f}", .{name.fmt(ip)});301 try writer.print("{f}", .{name.fmt(ip)});
305 },302 },
306 .tuple_type => |tuple| {303 .tuple_type => |tuple| {
307 if (tuple.types.len == 0) {304 if (tuple.types.len == 0) {
308 return bw.writeAll("@TypeOf(.{})");305 return writer.writeAll("@TypeOf(.{})");
309 }306 }
310 try bw.writeAll("struct {");307 try writer.writeAll("struct {");
311 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {308 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {
312 try bw.writeAll(if (i == 0) " " else ", ");309 try writer.writeAll(if (i == 0) " " else ", ");
313 if (val != .none) try bw.writeAll("comptime ");310 if (val != .none) try writer.writeAll("comptime ");
314 try print(Type.fromInterned(field_ty), bw, pt);311 try print(Type.fromInterned(field_ty), writer, pt);
315 if (val != .none) try bw.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});312 if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
316 }313 }
317 try bw.writeAll(" }");314 try writer.writeAll(" }");
318 },315 },
319316
320 .union_type => {317 .union_type => {
321 const name = ip.loadUnionType(ty.toIntern()).name;318 const name = ip.loadUnionType(ty.toIntern()).name;
322 return bw.print("{f}", .{name.fmt(ip)});319 try writer.print("{f}", .{name.fmt(ip)});
323 },320 },
324 .opaque_type => {321 .opaque_type => {
325 const name = ip.loadOpaqueType(ty.toIntern()).name;322 const name = ip.loadOpaqueType(ty.toIntern()).name;
326 return bw.print("{f}", .{name.fmt(ip)});323 try writer.print("{f}", .{name.fmt(ip)});
327 },324 },
328 .enum_type => {325 .enum_type => {
329 const name = ip.loadEnumType(ty.toIntern()).name;326 const name = ip.loadEnumType(ty.toIntern()).name;
330 return bw.print("{f}", .{name.fmt(ip)});327 try writer.print("{f}", .{name.fmt(ip)});
331 },328 },
332 .func_type => |fn_info| {329 .func_type => |fn_info| {
333 if (fn_info.is_noinline) {330 if (fn_info.is_noinline) {
334 try bw.writeAll("noinline ");331 try writer.writeAll("noinline ");
335 }332 }
336 try bw.writeAll("fn (");333 try writer.writeAll("fn (");
337 const param_types = fn_info.param_types.get(&zcu.intern_pool);334 const param_types = fn_info.param_types.get(&zcu.intern_pool);
338 for (param_types, 0..) |param_ty, i| {335 for (param_types, 0..) |param_ty, i| {
339 if (i != 0) try bw.writeAll(", ");336 if (i != 0) try writer.writeAll(", ");
340 if (std.math.cast(u5, i)) |index| {337 if (std.math.cast(u5, i)) |index| {
341 if (fn_info.paramIsComptime(index)) {338 if (fn_info.paramIsComptime(index)) {
342 try bw.writeAll("comptime ");339 try writer.writeAll("comptime ");
343 }340 }
344 if (fn_info.paramIsNoalias(index)) {341 if (fn_info.paramIsNoalias(index)) {
345 try bw.writeAll("noalias ");342 try writer.writeAll("noalias ");
346 }343 }
347 }344 }
348 if (param_ty == .generic_poison_type) {345 if (param_ty == .generic_poison_type) {
349 try bw.writeAll("anytype");346 try writer.writeAll("anytype");
350 } else {347 } else {
351 try print(Type.fromInterned(param_ty), bw, pt);348 try print(Type.fromInterned(param_ty), writer, pt);
352 }349 }
353 }350 }
354 if (fn_info.is_var_args) {351 if (fn_info.is_var_args) {
355 if (param_types.len != 0) {352 if (param_types.len != 0) {
356 try bw.writeAll(", ");353 try writer.writeAll(", ");
357 }354 }
358 try bw.writeAll("...");355 try writer.writeAll("...");
359 }356 }
360 try bw.writeAll(") ");357 try writer.writeAll(") ");
361 if (fn_info.cc != .auto) print_cc: {358 if (fn_info.cc != .auto) print_cc: {
362 if (zcu.getTarget().cCallingConvention()) |ccc| {359 if (zcu.getTarget().cCallingConvention()) |ccc| {
363 if (fn_info.cc.eql(ccc)) {360 if (fn_info.cc.eql(ccc)) {
364 try bw.writeAll("callconv(.c) ");361 try writer.writeAll("callconv(.c) ");
365 break :print_cc;362 break :print_cc;
366 }363 }
367 }364 }
368 switch (fn_info.cc) {365 switch (fn_info.cc) {
369 .auto, .@"async", .naked, .@"inline" => try bw.print("callconv(.{f}) ", .{std.zig.fmtId(@tagName(fn_info.cc))}),366 .auto, .async, .naked, .@"inline" => try writer.print("callconv(.{f}) ", .{
370 else => try bw.print("callconv({any}) ", .{fn_info.cc}),367 std.zig.fmtId(@tagName(fn_info.cc)),
368 }),
369 else => try writer.print("callconv({any}) ", .{fn_info.cc}),
371 }370 }
372 }371 }
373 if (fn_info.return_type == .generic_poison_type) {372 if (fn_info.return_type == .generic_poison_type) {
374 try bw.writeAll("anytype");373 try writer.writeAll("anytype");
375 } else {374 } else {
376 try print(Type.fromInterned(fn_info.return_type), bw, pt);375 try print(Type.fromInterned(fn_info.return_type), writer, pt);
377 }376 }
378 },377 },
379 .anyframe_type => |child| {378 .anyframe_type => |child| {
380 if (child == .none) return bw.writeAll("anyframe");379 if (child == .none) return writer.writeAll("anyframe");
381 try bw.writeAll("anyframe->");380 try writer.writeAll("anyframe->");
382 try print(Type.fromInterned(child), bw, pt);381 try print(Type.fromInterned(child), writer, pt);
383 },382 },
384383
385 // values, not types384 // values, not types
src/Value.zig+7-15
...@@ -15,31 +15,23 @@ const Value = @This();...@@ -15,31 +15,23 @@ const Value = @This();
1515
16ip_index: InternPool.Index,16ip_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 {
19 _ = val;19 _ = val;
20 _ = fmt;
21 _ = options;
22 _ = writer;20 _ = writer;
23 @compileError("do not use format values directly; use either fmtDebug or fmtValue");21 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
24}22}
2523
26/// This is a debug function. In order to print values in a meaningful way24/// This is a debug function. In order to print values in a meaningful way
27/// we also need access to the type.25/// we also need access to the type.
28pub fn dump(26pub fn dump(start_val: Value, w: std.io.Writer) std.io.Writer.Error!void {
29 start_val: Value,27 try w.print("(interned: {})", .{start_val.toIntern()});
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()});
36}28}
3729
38pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) {30pub fn fmtDebug(val: Value) std.fmt.Formatter(Value, dump) {
39 return .{ .data = val };31 return .{ .data = val };
40}32}
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) {
43 return .{ .data = .{35 return .{ .data = .{
44 .val = val,36 .val = val,
45 .pt = pt,37 .pt = pt,
...@@ -48,7 +40,7 @@ pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.for...@@ -48,7 +40,7 @@ pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.for
48 } };40 } };
49}41}
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) {
52 return .{ .data = .{44 return .{ .data = .{
53 .val = val,45 .val = val,
54 .pt = pt,46 .pt = pt,
...@@ -57,7 +49,7 @@ pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatte...@@ -57,7 +49,7 @@ pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatte
57 } };49 } };
58}50}
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) {
61 return .{ .data = ctx };53 return .{ .data = ctx };
62}54}
6355
src/Zcu.zig+18-51
...@@ -793,10 +793,6 @@ pub const Namespace = struct {...@@ -793,10 +793,6 @@ pub const Namespace = struct {
793 pub_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty,793 pub_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty,
794 /// Members of the namespace which are *not* marked `pub`.794 /// Members of the namespace which are *not* marked `pub`.
795 priv_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty,795 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,
800 /// All `comptime` declarations in this namespace. We store these purely so that incremental796 /// All `comptime` declarations in this namespace. We store these purely so that incremental
801 /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.797 /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.
802 comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty,798 comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty,
...@@ -1116,7 +1112,7 @@ pub const File = struct {...@@ -1116,7 +1112,7 @@ pub const File = struct {
1116 eb: *std.zig.ErrorBundle.Wip,1112 eb: *std.zig.ErrorBundle.Wip,
1117 ) !std.zig.ErrorBundle.SourceLocationIndex {1113 ) !std.zig.ErrorBundle.SourceLocationIndex {
1118 return eb.addSourceLocation(.{1114 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)}),
1120 .span_start = 0,1116 .span_start = 0,
1121 .span_main = 0,1117 .span_main = 0,
1122 .span_end = 0,1118 .span_end = 0,
...@@ -1137,7 +1133,7 @@ pub const File = struct {...@@ -1137,7 +1133,7 @@ pub const File = struct {
1137 const end = start + tree.tokenSlice(tok).len;1133 const end = start + tree.tokenSlice(tok).len;
1138 const loc = std.zig.findLineColumn(source.bytes, start);1134 const loc = std.zig.findLineColumn(source.bytes, start);
1139 return eb.addSourceLocation(.{1135 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)}),
1141 .span_start = start,1137 .span_start = start,
1142 .span_main = start,1138 .span_main = start,
1143 .span_end = @intCast(end),1139 .span_end = @intCast(end),
...@@ -1298,9 +1294,6 @@ pub const SrcLoc = struct {...@@ -1298,9 +1294,6 @@ pub const SrcLoc = struct {
1298 .simple_var_decl,1294 .simple_var_decl,
1299 .aligned_var_decl,1295 .aligned_var_decl,
1300 => tree.fullVarDecl(node).?,1296 => tree.fullVarDecl(node).?,
1301 .@"usingnamespace" => {
1302 return tree.nodeToSpan(tree.nodeData(node).node);
1303 },
1304 else => unreachable,1297 else => unreachable,
1305 };1298 };
1306 if (full.ast.type_node.unwrap()) |type_node| {1299 if (full.ast.type_node.unwrap()) |type_node| {
...@@ -1438,12 +1431,8 @@ pub const SrcLoc = struct {...@@ -1438,12 +1431,8 @@ pub const SrcLoc = struct {
1438 .field_access => tree.nodeData(node).node_and_token[1],1431 .field_access => tree.nodeData(node).node_and_token[1],
1439 .call_one,1432 .call_one,
1440 .call_one_comma,1433 .call_one_comma,
1441 .async_call_one,
1442 .async_call_one_comma,
1443 .call,1434 .call,
1444 .call_comma,1435 .call_comma,
1445 .async_call,
1446 .async_call_comma,
1447 => blk: {1436 => blk: {
1448 const full = tree.fullCall(&buf, node).?;1437 const full = tree.fullCall(&buf, node).?;
1449 break :blk tree.lastToken(full.ast.fn_expr);1438 break :blk tree.lastToken(full.ast.fn_expr);
...@@ -3306,9 +3295,6 @@ pub fn mapOldZirToNew(...@@ -3306,9 +3295,6 @@ pub fn mapOldZirToNew(
3306 // All comptime declarations, in order, for a best-effort match.3295 // All comptime declarations, in order, for a best-effort match.
3307 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;3296 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
3308 defer comptime_decls.deinit(gpa);3297 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
3313 {3299 {
3314 var old_decl_it = old_zir.declIterator(match_item.old_inst);3300 var old_decl_it = old_zir.declIterator(match_item.old_inst);
...@@ -3316,7 +3302,6 @@ pub fn mapOldZirToNew(...@@ -3316,7 +3302,6 @@ pub fn mapOldZirToNew(
3316 const old_decl = old_zir.getDeclaration(old_decl_inst);3302 const old_decl = old_zir.getDeclaration(old_decl_inst);
3317 switch (old_decl.kind) {3303 switch (old_decl.kind) {
3318 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),3304 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
3319 .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
3320 .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),3305 .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
3321 .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),3306 .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
3322 .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),3307 .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
...@@ -3327,7 +3312,6 @@ pub fn mapOldZirToNew(...@@ -3327,7 +3312,6 @@ pub fn mapOldZirToNew(
33273312
3328 var unnamed_test_idx: u32 = 0;3313 var unnamed_test_idx: u32 = 0;
3329 var comptime_decl_idx: u32 = 0;3314 var comptime_decl_idx: u32 = 0;
3330 var usingnamespace_decl_idx: u32 = 0;
33313315
3332 var new_decl_it = new_zir.declIterator(match_item.new_inst);3316 var new_decl_it = new_zir.declIterator(match_item.new_inst);
3333 while (new_decl_it.next()) |new_decl_inst| {3317 while (new_decl_it.next()) |new_decl_inst| {
...@@ -3337,7 +3321,6 @@ pub fn mapOldZirToNew(...@@ -3337,7 +3321,6 @@ pub fn mapOldZirToNew(
3337 // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name.3321 // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name.
3338 // * For unnamed tests, we match based on order.3322 // * For unnamed tests, we match based on order.
3339 // * For comptime blocks, we match based on order.3323 // * For comptime blocks, we match based on order.
3340 // * For usingnamespace decls, we match based on order.
3341 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.3324 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
3342 const old_decl_inst = switch (new_decl.kind) {3325 const old_decl_inst = switch (new_decl.kind) {
3343 .@"comptime" => inst: {3326 .@"comptime" => inst: {
...@@ -3345,11 +3328,6 @@ pub fn mapOldZirToNew(...@@ -3345,11 +3328,6 @@ pub fn mapOldZirToNew(
3345 defer comptime_decl_idx += 1;3328 defer comptime_decl_idx += 1;
3346 break :inst comptime_decls.items[comptime_decl_idx];3329 break :inst comptime_decls.items[comptime_decl_idx];
3347 },3330 },
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 },
3353 .unnamed_test => inst: {3331 .unnamed_test => inst: {
3354 if (unnamed_test_idx == unnamed_tests.items.len) continue;3332 if (unnamed_test_idx == unnamed_tests.items.len) continue;
3355 defer unnamed_test_idx += 1;3333 defer unnamed_test_idx += 1;
...@@ -4058,7 +4036,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv...@@ -4058,7 +4036,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
4058 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;4036 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
40594037
4060 const want_analysis = switch (decl.kind) {4038 const want_analysis = switch (decl.kind) {
4061 .@"usingnamespace" => unreachable,
4062 .@"const", .@"var" => unreachable,4039 .@"const", .@"var" => unreachable,
4063 .@"comptime" => unreachable,4040 .@"comptime" => unreachable,
4064 .unnamed_test => true,4041 .unnamed_test => true,
...@@ -4116,16 +4093,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv...@@ -4116,16 +4093,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
4116 }4093 }
4117 }4094 }
4118 }4095 }
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 }
4129 continue;4096 continue;
4130 }4097 }
4131 if (unit_queue.pop()) |kv| {4098 if (unit_queue.pop()) |kv| {
...@@ -4271,17 +4238,17 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *std.io.Writer) std.io.Writer.Er...@@ -4271,17 +4238,17 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *std.io.Writer) std.io.Writer.Er
4271 const cu = ip.getComptimeUnit(cu_id);4238 const cu = ip.getComptimeUnit(cu_id);
4272 if (cu.zir_index.resolveFull(ip)) |resolved| {4239 if (cu.zir_index.resolveFull(ip)) |resolved| {
4273 const file_path = zcu.fileByIndex(resolved.file).path;4240 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) });
4275 } else {4242 } else {
4276 return writer.print("comptime(inst=<lost> [{}])", .{@intFromEnum(cu_id)});4243 return writer.print("comptime(inst=<lost> [{}])", .{@intFromEnum(cu_id)});
4277 }4244 }
4278 },4245 },
4279 .nav_val => |nav| return writer.print("nav_val('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),4246 .nav_val => |nav| return writer.print("nav_val('{f}' [{}])", .{ 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) }),4247 .nav_ty => |nav| return writer.print("nav_ty('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }),
4281 .type => |ty| return writer.print("ty('{}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),4248 .type => |ty| return writer.print("ty('{f}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }),
4282 .func => |func| {4249 .func => |func| {
4283 const nav = zcu.funcInfo(func).owner_nav;4250 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) });
4285 },4252 },
4286 .memoized_state => return writer.writeAll("memoized_state"),4253 .memoized_state => return writer.writeAll("memoized_state"),
4287 }4254 }
...@@ -4298,42 +4265,42 @@ fn formatDependee(data: FormatDependee, writer: *std.io.Writer) std.io.Writer.Er...@@ -4298,42 +4265,42 @@ fn formatDependee(data: FormatDependee, writer: *std.io.Writer) std.io.Writer.Er
4298 return writer.writeAll("inst(<lost>)");4265 return writer.writeAll("inst(<lost>)");
4299 };4266 };
4300 const file_path = zcu.fileByIndex(info.file).path;4267 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) });
4302 },4269 },
4303 .nav_val => |nav| {4270 .nav_val => |nav| {
4304 const fqn = ip.getNav(nav).fqn;4271 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)});
4306 },4273 },
4307 .nav_ty => |nav| {4274 .nav_ty => |nav| {
4308 const fqn = ip.getNav(nav).fqn;4275 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)});
4310 },4277 },
4311 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {4278 .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)}),4279 .struct_type, .union_type, .enum_type => return writer.print("type('{f}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
4313 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),4280 .func => |f| return writer.print("ies('{f}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
4314 else => unreachable,4281 else => unreachable,
4315 },4282 },
4316 .zon_file => |file| {4283 .zon_file => |file| {
4317 const file_path = zcu.fileByIndex(file).path;4284 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)});
4319 },4286 },
4320 .embed_file => |ef_idx| {4287 .embed_file => |ef_idx| {
4321 const ef = ef_idx.get(zcu);4288 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)});
4323 },4290 },
4324 .namespace => |ti| {4291 .namespace => |ti| {
4325 const info = ti.resolveFull(ip) orelse {4292 const info = ti.resolveFull(ip) orelse {
4326 return writer.writeAll("namespace(<lost>)");4293 return writer.writeAll("namespace(<lost>)");
4327 };4294 };
4328 const file_path = zcu.fileByIndex(info.file).path;4295 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) });
4330 },4297 },
4331 .namespace_name => |k| {4298 .namespace_name => |k| {
4332 const info = k.namespace.resolveFull(ip) orelse {4299 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)});
4334 };4301 };
4335 const file_path = zcu.fileByIndex(info.file).path;4302 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) });
4337 },4304 },
4338 .memoized_state => return writer.writeAll("memoized_state"),4305 .memoized_state => return writer.writeAll("memoized_state"),
4339 }4306 }
...@@ -4374,7 +4341,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu...@@ -4374,7 +4341,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
4374 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);4341 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
4375 switch (cc) {4342 switch (cc) {
4376 .auto, .@"inline" => return .ok,4343 .auto, .@"inline" => return .ok,
4377 .@"async" => return .{ .bad_backend = backend }, // nothing supports async currently4344 .async => return .{ .bad_backend = backend }, // nothing supports async currently
4378 .naked => {}, // depends only on backend4345 .naked => {}, // depends only on backend
4379 else => for (cc.archs()) |allowed_arch| {4346 else => for (cc.archs()) |allowed_arch| {
4380 if (allowed_arch == target.cpu.arch) break;4347 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 {...@@ -53,7 +53,7 @@ fn deinitFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
53 const zcu = pt.zcu;53 const zcu = pt.zcu;
54 const gpa = zcu.gpa;54 const gpa = zcu.gpa;
55 const file = zcu.fileByIndex(file_index);55 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)});
57 file.path.deinit(gpa);57 file.path.deinit(gpa);
58 file.unload(gpa);58 file.unload(gpa);
59 if (file.prev_zir) |prev_zir| {59 if (file.prev_zir) |prev_zir| {
...@@ -117,7 +117,7 @@ pub fn updateFile(...@@ -117,7 +117,7 @@ pub fn updateFile(
117 var lock: std.fs.File.Lock = switch (file.status) {117 var lock: std.fs.File.Lock = switch (file.status) {
118 .never_loaded, .retryable_failure => lock: {118 .never_loaded, .retryable_failure => lock: {
119 // First, load the cached ZIR code, if any.119 // 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})", .{
121 file.path.fmt(comp), want_local_cache, &hex_digest,121 file.path.fmt(comp), want_local_cache, &hex_digest,
122 });122 });
123123
...@@ -130,11 +130,11 @@ pub fn updateFile(...@@ -130,11 +130,11 @@ pub fn updateFile(
130 stat.inode == file.stat.inode;130 stat.inode == file.stat.inode;
131131
132 if (unchanged_metadata) {132 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)});
134 return;134 return;
135 }135 }
136136
137 log.debug("metadata changed: {}", .{file.path.fmt(comp)});137 log.debug("metadata changed: {f}", .{file.path.fmt(comp)});
138138
139 break :lock .exclusive;139 break :lock .exclusive;
140 },140 },
...@@ -221,12 +221,12 @@ pub fn updateFile(...@@ -221,12 +221,12 @@ pub fn updateFile(
221 };221 };
222 switch (result) {222 switch (result) {
223 .success => {223 .success => {
224 log.debug("AstGen cached success: {}", .{file.path.fmt(comp)});224 log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)});
225 break false;225 break false;
226 },226 },
227 .invalid => {},227 .invalid => {},
228 .truncated => log.warn("unexpected EOF reading cached ZIR for {}", .{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: {}", .{file.path.fmt(comp)}),229 .stale => log.debug("AstGen cache stale: {f}", .{file.path.fmt(comp)}),
230 }230 }
231231
232 // If we already have the exclusive lock then it is our job to update.232 // If we already have the exclusive lock then it is our job to update.
...@@ -283,7 +283,7 @@ pub fn updateFile(...@@ -283,7 +283,7 @@ pub fn updateFile(
283 },283 },
284 }284 }
285285
286 log.debug("AstGen fresh success: {}", .{file.path.fmt(comp)});286 log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)});
287 }287 }
288288
289 file.stat = .{289 file.stat = .{
...@@ -343,8 +343,9 @@ fn loadZirZoirCache(...@@ -343,8 +343,9 @@ fn loadZirZoirCache(
343 .zon => Zoir.Header,343 .zon => Zoir.Header,
344 };344 };
345345
346 var buffer: [@sizeOf(Header)]u8 = undefined;346 var buffer: [2000]u8 = undefined;
347 var cache_fr = cache_file.reader(&buffer);347 var cache_fr = cache_file.reader(&buffer);
348 cache_fr.size = stat.size;
348 const cache_br = &cache_fr.interface;349 const cache_br = &cache_fr.interface;
349350
350 // First we read the header to determine the lengths of arrays.351 // 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...@@ -1114,7 +1115,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
1114 defer block.instructions.deinit(gpa);1115 defer block.instructions.deinit(gpa);
11151116
1116 const zir_decl = zir.getDeclaration(inst_resolved.inst);1117 const zir_decl = zir.getDeclaration(inst_resolved.inst);
1117 assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
11181118
1119 const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero });1119 const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero });
1120 const init_src = block.src(.{ .node_offset_var_decl_init = .zero });1120 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...@@ -1163,7 +1163,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
1163 assert(nav_ty.zigTypeTag(zcu) == .@"fn");1163 assert(nav_ty.zigTypeTag(zcu) == .@"fn");
1164 break :is_const true;1164 break :is_const true;
1165 },1165 },
1166 .@"usingnamespace", .@"const" => true,1166 .@"const" => true,
1167 .@"var" => {1167 .@"var" => {
1168 try sema.validateVarType(1168 try sema.validateVarType(
1169 &block,1169 &block,
...@@ -1243,26 +1243,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr...@@ -1243,26 +1243,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
1243 // this resolves the type `type` (which needs no resolution), not the struct itself.1243 // this resolves the type `type` (which needs no resolution), not the struct itself.
1244 try nav_ty.resolveLayout(pt);1244 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
1266 const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {1246 const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
1267 .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen1247 .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
1268 .variable => |v| .{ v.owner_nav == nav_id, false },1248 .variable => |v| .{ v.owner_nav == nav_id, false },
...@@ -1467,7 +1447,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr...@@ -1467,7 +1447,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
1467 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);1447 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
14681448
1469 const zir_decl = zir.getDeclaration(inst_resolved.inst);1449 const zir_decl = zir.getDeclaration(inst_resolved.inst);
1470 assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
1471 const type_body = zir_decl.type_body.?;1450 const type_body = zir_decl.type_body.?;
14721451
1473 var analysis_arena: std.heap.ArenaAllocator = .init(gpa);1452 var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
...@@ -1530,7 +1509,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr...@@ -1530,7 +1509,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
15301509
1531 const is_const = switch (zir_decl.kind) {1510 const is_const = switch (zir_decl.kind) {
1532 .@"comptime" => unreachable,1511 .@"comptime" => unreachable,
1533 .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true,1512 .unnamed_test, .@"test", .decltest, .@"const" => true,
1534 .@"var" => false,1513 .@"var" => false,
1535 };1514 };
15361515
...@@ -2324,7 +2303,7 @@ pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!voi...@@ -2324,7 +2303,7 @@ pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!voi
23242303
2325 Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure(2304 Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure(
2326 .write_builtin_zig,2305 .write_builtin_zig,
2327 "unable to write '{}': {s}",2306 "unable to write '{f}': {s}",
2328 .{ file.path.fmt(comp), @errorName(err) },2307 .{ file.path.fmt(comp), @errorName(err) },
2329 );2308 );
2330}2309}
...@@ -2548,7 +2527,6 @@ pub fn scanNamespace(...@@ -2548,7 +2527,6 @@ pub fn scanNamespace(
25482527
2549 try existing_by_inst.ensureTotalCapacity(gpa, @intCast(2528 try existing_by_inst.ensureTotalCapacity(gpa, @intCast(
2550 namespace.pub_decls.count() + namespace.priv_decls.count() +2529 namespace.pub_decls.count() + namespace.priv_decls.count() +
2551 namespace.pub_usingnamespace.items.len + namespace.priv_usingnamespace.items.len +
2552 namespace.comptime_decls.items.len +2530 namespace.comptime_decls.items.len +
2553 namespace.test_decls.items.len,2531 namespace.test_decls.items.len,
2554 ));2532 ));
...@@ -2561,14 +2539,6 @@ pub fn scanNamespace(...@@ -2561,14 +2539,6 @@ pub fn scanNamespace(
2561 const zir_index = ip.getNav(nav).analysis.?.zir_index;2539 const zir_index = ip.getNav(nav).analysis.?.zir_index;
2562 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));2540 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
2563 }2541 }
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 }
2572 for (namespace.comptime_decls.items) |cu| {2542 for (namespace.comptime_decls.items) |cu| {
2573 const zir_index = ip.getComptimeUnit(cu).zir_index;2543 const zir_index = ip.getComptimeUnit(cu).zir_index;
2574 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu }));2544 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu }));
...@@ -2585,8 +2555,6 @@ pub fn scanNamespace(...@@ -2585,8 +2555,6 @@ pub fn scanNamespace(
25852555
2586 namespace.pub_decls.clearRetainingCapacity();2556 namespace.pub_decls.clearRetainingCapacity();
2587 namespace.priv_decls.clearRetainingCapacity();2557 namespace.priv_decls.clearRetainingCapacity();
2588 namespace.pub_usingnamespace.clearRetainingCapacity();
2589 namespace.priv_usingnamespace.clearRetainingCapacity();
2590 namespace.comptime_decls.clearRetainingCapacity();2558 namespace.comptime_decls.clearRetainingCapacity();
2591 namespace.test_decls.clearRetainingCapacity();2559 namespace.test_decls.clearRetainingCapacity();
25922560
...@@ -2614,7 +2582,6 @@ const ScanDeclIter = struct {...@@ -2614,7 +2582,6 @@ const ScanDeclIter = struct {
2614 /// Decl scanning is run in two passes, so that we can detect when a generated2582 /// Decl scanning is run in two passes, so that we can detect when a generated
2615 /// name would clash with an explicit name and use a different one.2583 /// name would clash with an explicit name and use a different one.
2616 pass: enum { named, unnamed },2584 pass: enum { named, unnamed },
2617 usingnamespace_index: usize = 0,
2618 unnamed_test_index: usize = 0,2585 unnamed_test_index: usize = 0,
26192586
2620 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {2587 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {
...@@ -2653,12 +2620,6 @@ const ScanDeclIter = struct {...@@ -2653,12 +2620,6 @@ const ScanDeclIter = struct {
2653 if (iter.pass != .unnamed) return;2620 if (iter.pass != .unnamed) return;
2654 break :name .none;2621 break :name .none;
2655 },2622 },
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 },
2662 .unnamed_test => name: {2623 .unnamed_test => name: {
2663 if (iter.pass != .unnamed) return;2624 if (iter.pass != .unnamed) return;
2664 const i = iter.unnamed_test_index;2625 const i = iter.unnamed_test_index;
...@@ -2717,7 +2678,7 @@ const ScanDeclIter = struct {...@@ -2717,7 +2678,7 @@ const ScanDeclIter = struct {
2717 const name = maybe_name.unwrap().?;2678 const name = maybe_name.unwrap().?;
2718 const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name);2679 const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name);
2719 const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: {2680 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);
2721 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);2682 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
2722 break :nav nav;2683 break :nav nav;
2723 };2684 };
...@@ -2729,17 +2690,6 @@ const ScanDeclIter = struct {...@@ -2729,17 +2690,6 @@ const ScanDeclIter = struct {
27292690
2730 const want_analysis = switch (decl.kind) {2691 const want_analysis = switch (decl.kind) {
2731 .@"comptime" => unreachable,2692 .@"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 },
2743 .unnamed_test, .@"test", .decltest => a: {2693 .unnamed_test, .@"test", .decltest => a: {
2744 const is_named = decl.kind != .unnamed_test;2694 const is_named = decl.kind != .unnamed_test;
2745 try namespace.test_decls.append(gpa, nav);2695 try namespace.test_decls.append(gpa, nav);
...@@ -4434,12 +4384,11 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e...@@ -4434,12 +4384,11 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e
4434 defer liveness.deinit(gpa);4384 defer liveness.deinit(gpa);
44354385
4436 if (build_options.enable_debug_extensions and comp.verbose_air) {4386 if (build_options.enable_debug_extensions and comp.verbose_air) {
4437 std.debug.lockStdErr();4387 const stderr = std.debug.lockStderrWriter(&.{});
4438 defer std.debug.unlockStdErr();4388 defer std.debug.unlockStderrWriter();
4439 const stderr = std.io.getStdErr().writer();4389 stderr.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}) catch {};
4440 stderr.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}) catch {};
4441 air.write(stderr, pt, liveness);4390 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 {};
4443 }4392 }
44444393
4445 if (std.debug.runtime_safety) {4394 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 {...@@ -436,7 +436,7 @@ const InstTracking = struct {
436 fn trackSpill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void {436 fn trackSpill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void {
437 try function.freeValue(inst_tracking.short);437 try function.freeValue(inst_tracking.short);
438 inst_tracking.reuseFrame();438 inst_tracking.reuseFrame();
439 tracking_log.debug("%{f} => {f} (spilled)", .{ inst, inst_tracking.* });439 tracking_log.debug("%{d} => {f} (spilled)", .{ inst, inst_tracking.* });
440 }440 }
441441
442 fn verifyMaterialize(inst_tracking: InstTracking, target: InstTracking) void {442 fn verifyMaterialize(inst_tracking: InstTracking, target: InstTracking) void {
...@@ -500,14 +500,14 @@ const InstTracking = struct {...@@ -500,14 +500,14 @@ const InstTracking = struct {
500 else => target.long,500 else => target.long,
501 } else target.long;501 } else target.long;
502 inst_tracking.short = target.short;502 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.* });
504 }504 }
505505
506 fn resurrect(inst_tracking: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void {506 fn resurrect(inst_tracking: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void {
507 switch (inst_tracking.short) {507 switch (inst_tracking.short) {
508 .dead => |die_generation| if (die_generation >= scope_generation) {508 .dead => |die_generation| if (die_generation >= scope_generation) {
509 inst_tracking.reuseFrame();509 inst_tracking.reuseFrame();
510 tracking_log.debug("%{f} => {f} (resurrect)", .{ inst, inst_tracking.* });510 tracking_log.debug("%{d} => {f} (resurrect)", .{ inst, inst_tracking.* });
511 },511 },
512 else => {},512 else => {},
513 }513 }
...@@ -517,7 +517,7 @@ const InstTracking = struct {...@@ -517,7 +517,7 @@ const InstTracking = struct {
517 if (inst_tracking.short == .dead) return;517 if (inst_tracking.short == .dead) return;
518 try function.freeValue(inst_tracking.short);518 try function.freeValue(inst_tracking.short);
519 inst_tracking.short = .{ .dead = function.scope_generation };519 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.* });
521 }521 }
522522
523 fn reuse(523 fn reuse(
...@@ -528,15 +528,15 @@ const InstTracking = struct {...@@ -528,15 +528,15 @@ const InstTracking = struct {
528 ) void {528 ) void {
529 inst_tracking.short = .{ .dead = function.scope_generation };529 inst_tracking.short = .{ .dead = function.scope_generation };
530 if (new_inst) |inst|530 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 })
532 else532 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 });
534 }534 }
535535
536 fn liveOut(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) void {536 fn liveOut(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) void {
537 for (inst_tracking.getRegs()) |reg| {537 for (inst_tracking.getRegs()) |reg| {
538 if (function.register_manager.isRegFree(reg)) {538 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.* });
540 continue;540 continue;
541 }541 }
542542
...@@ -563,14 +563,13 @@ const InstTracking = struct {...@@ -563,14 +563,13 @@ const InstTracking = struct {
563 // Perform side-effects of freeValue manually.563 // Perform side-effects of freeValue manually.
564 function.register_manager.freeReg(reg);564 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 });
567 }567 }
568 }568 }
569569
570 pub fn format(inst_tracking: InstTracking, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {570 pub fn format(inst_tracking: InstTracking, writer: *std.io.Writer) std.io.Writer.Error!void {
571 comptime assert(fmt.len == 0);571 if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try writer.print("|{}| ", .{inst_tracking.long});
572 if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try bw.print("|{}| ", .{inst_tracking.long});572 try writer.print("{}", .{inst_tracking.short});
573 try bw.print("{}", .{inst_tracking.short});
574 }573 }
575};574};
576575
...@@ -934,7 +933,7 @@ const FormatWipMirData = struct {...@@ -934,7 +933,7 @@ const FormatWipMirData = struct {
934 func: *Func,933 func: *Func,
935 inst: Mir.Inst.Index,934 inst: Mir.Inst.Index,
936};935};
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 {
938 const pt = data.func.pt;937 const pt = data.func.pt;
939 const comp = pt.zcu.comp;938 const comp = pt.zcu.comp;
940 var lower: Lower = .{939 var lower: Lower = .{
...@@ -957,11 +956,11 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri...@@ -957,11 +956,11 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
957 lower.err_msg.?.deinit(data.func.gpa);956 lower.err_msg.?.deinit(data.func.gpa);
958 lower.err_msg = null;957 lower.err_msg = null;
959 }958 }
960 try bw.writeAll(lower.err_msg.?.msg);959 try writer.writeAll(lower.err_msg.?.msg);
961 return;960 return;
962 },961 },
963 error.OutOfMemory, error.InvalidInstruction => |e| {962 error.OutOfMemory, error.InvalidInstruction => |e| {
964 try bw.writeAll(switch (e) {963 try writer.writeAll(switch (e) {
965 error.OutOfMemory => "Out of memory",964 error.OutOfMemory => "Out of memory",
966 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",965 error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
967 });966 });
...@@ -969,12 +968,12 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri...@@ -969,12 +968,12 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
969 },968 },
970 else => |e| return e,969 else => |e| return e,
971 }).insts) |lowered_inst| {970 }).insts) |lowered_inst| {
972 if (!first) try bw.writeAll("\ndebug(wip_mir): ");971 if (!first) try writer.writeAll("\ndebug(wip_mir): ");
973 try bw.print(" | {}", .{lowered_inst});972 try writer.print(" | {}", .{lowered_inst});
974 first = false;973 first = false;
975 }974 }
976}975}
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) {
978 return .{ .data = .{ .func = func, .inst = inst } };977 return .{ .data = .{ .func = func, .inst = inst } };
979}978}
980979
...@@ -982,10 +981,10 @@ const FormatNavData = struct {...@@ -982,10 +981,10 @@ const FormatNavData = struct {
982 ip: *const InternPool,981 ip: *const InternPool,
983 nav_index: InternPool.Nav.Index,982 nav_index: InternPool.Nav.Index,
984};983};
985fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void {984fn formatNav(data: FormatNavData, writer: *std.io.Writer) std.io.Writer.Error!void {
986 try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});985 try writer.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});
987}986}
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) {
989 return .{ .data = .{988 return .{ .data = .{
990 .ip = ip,989 .ip = ip,
991 .nav_index = nav_index,990 .nav_index = nav_index,
...@@ -996,27 +995,25 @@ const FormatAirData = struct {...@@ -996,27 +995,25 @@ const FormatAirData = struct {
996 func: *Func,995 func: *Func,
997 inst: Air.Inst.Index,996 inst: Air.Inst.Index,
998};997};
999fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {998fn formatAir(data: FormatAirData, writer: *std.io.Writer) std.io.Writer.Error!void {
1000 comptime assert(fmt.len == 0);999 // Not acceptable implementation because it ignores `writer`:
1001 // not acceptable implementation:1000 //data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness);
1002 // data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness);
1003 _ = data;1001 _ = data;
1004 _ = w;1002 _ = writer;
1005 @panic("TODO: unimplemented");1003 @panic("unimplemented");
1006}1004}
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) {
1008 return .{ .data = .{ .func = func, .inst = inst } };1006 return .{ .data = .{ .func = func, .inst = inst } };
1009}1007}
10101008
1011const FormatTrackingData = struct {1009const FormatTrackingData = struct {
1012 func: *Func,1010 func: *Func,
1013};1011};
1014fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {1012fn formatTracking(data: FormatTrackingData, writer: *std.io.Writer) std.io.Writer.Error!void {
1015 comptime assert(fmt.len == 0);
1016 var it = data.func.inst_tracking.iterator();1013 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.* });
1018}1015}
1019fn fmtTracking(func: *Func) std.fmt.Formatter(formatTracking) {1016fn fmtTracking(func: *Func) std.fmt.Formatter(FormatTrackingData, formatTracking) {
1020 return .{ .data = .{ .func = func } };1017 return .{ .data = .{ .func = func } };
1021}1018}
10221019
...@@ -1826,7 +1823,7 @@ fn computeFrameLayout(func: *Func) !FrameLayout {...@@ -1826,7 +1823,7 @@ fn computeFrameLayout(func: *Func) !FrameLayout {
1826 total_alloc_size + 64 + args_frame_size + spill_frame_size + call_frame_size,1823 total_alloc_size + 64 + args_frame_size + spill_frame_size + call_frame_size,
1827 @intCast(frame_align[@intFromEnum(FrameIndex.base_ptr)].toByteUnits().?),1824 @intCast(frame_align[@intFromEnum(FrameIndex.base_ptr)].toByteUnits().?),
1828 );1825 );
1829 log.debug("frame size: {}", .{acc_frame_size});1826 log.debug("frame size: {d}", .{acc_frame_size});
18301827
1831 // store the ra at total_size - 8, so it's the very first thing in the stack1828 // store the ra at total_size - 8, so it's the very first thing in the stack
1832 // relative to the fp1829 // relative to the fp
src/arch/riscv64/Mir.zig+2-3
...@@ -92,9 +92,8 @@ pub const Inst = struct {...@@ -92,9 +92,8 @@ pub const Inst = struct {
92 },92 },
93 };93 };
9494
95 pub fn format(inst: Inst, bw: *std.io.Writer, comptime fmt: []const u8) !void {95 pub fn format(inst: Inst, writer: *std.io.Writer) std.io.Writer.Error!void {
96 assert(fmt.len == 0);96 try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) });
97 try bw.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) });
98 }97 }
99};98};
10099
src/arch/riscv64/bits.zig-9
...@@ -256,15 +256,6 @@ pub const FrameIndex = enum(u32) {...@@ -256,15 +256,6 @@ pub const FrameIndex = enum(u32) {
256 pub fn isNamed(fi: FrameIndex) bool {256 pub fn isNamed(fi: FrameIndex) bool {
257 return @intFromEnum(fi) < named_count;257 return @intFromEnum(fi) < named_count;
258 }258 }
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 }
268};259};
269260
270/// A linker symbol not yet allocated in VM.261/// 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 {...@@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
723723
724 if (std.debug.runtime_safety) {724 if (std.debug.runtime_safety) {
725 if (self.air_bookkeeping < old_air_bookkeeping + 1) {725 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)] });
727 }727 }
728 }728 }
729 }729 }
src/arch/wasm/CodeGen.zig+6-13
...@@ -18,7 +18,7 @@ const Compilation = @import("../../Compilation.zig");...@@ -18,7 +18,7 @@ const Compilation = @import("../../Compilation.zig");
18const link = @import("../../link.zig");18const link = @import("../../link.zig");
19const Air = @import("../../Air.zig");19const Air = @import("../../Air.zig");
20const Mir = @import("Mir.zig");20const Mir = @import("Mir.zig");
21const abi = @import("abi.zig");21const abi = @import("../../codegen/wasm/abi.zig");
22const Alignment = InternPool.Alignment;22const Alignment = InternPool.Alignment;
23const errUnionPayloadOffset = codegen.errUnionPayloadOffset;23const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
24const errUnionErrorOffset = codegen.errUnionErrorOffset;24const errUnionErrorOffset = codegen.errUnionErrorOffset;
...@@ -1960,7 +1960,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1960,7 +1960,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1960 .wasm_memory_size => cg.airWasmMemorySize(inst),1960 .wasm_memory_size => cg.airWasmMemorySize(inst),
1961 .wasm_memory_grow => cg.airWasmMemoryGrow(inst),1961 .wasm_memory_grow => cg.airWasmMemoryGrow(inst),
19621962
1963 .memcpy => cg.airMemcpy(inst),1963 .memcpy, .memmove => cg.airMemcpy(inst),
19641964
1965 .ret_addr => cg.airRetAddr(inst),1965 .ret_addr => cg.airRetAddr(inst),
1966 .tag_name => cg.airTagName(inst),1966 .tag_name => cg.airTagName(inst),
...@@ -1984,7 +1984,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1984,7 +1984,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1984 .c_va_copy,1984 .c_va_copy,
1985 .c_va_end,1985 .c_va_end,
1986 .c_va_start,1986 .c_va_start,
1987 .memmove,
1988 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),1987 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
19891988
1990 .atomic_load => cg.airAtomicLoad(inst),1989 .atomic_load => cg.airAtomicLoad(inst),
...@@ -2047,7 +2046,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -2047,7 +2046,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
2047 try cg.genInst(inst);2046 try cg.genInst(inst);
20482047
2049 if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) {2048 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}')", .{
2051 inst,2050 inst,
2052 cg.air.instructions.items(.tag)[@intFromEnum(inst)],2051 cg.air.instructions.items(.tag)[@intFromEnum(inst)],
2053 });2052 });
...@@ -2405,10 +2404,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr...@@ -2405,10 +2404,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
2405 try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });2404 try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });
2406 },2405 },
2407 else => if (abi_size > 8) {2406 else => if (abi_size > 8) {
2408 return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{2407 return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{ ty.fmt(pt), abi_size });
2409 ty.fmt(pt),
2410 abi_size,
2411 });
2412 },2408 },
2413 }2409 }
2414 try cg.emitWValue(lhs);2410 try cg.emitWValue(lhs);
...@@ -2597,10 +2593,7 @@ fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WV...@@ -2597,10 +2593,7 @@ fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WV
2597 if (ty.zigTypeTag(zcu) == .int) {2593 if (ty.zigTypeTag(zcu) == .int) {
2598 return cg.binOpBigInt(lhs, rhs, ty, op);2594 return cg.binOpBigInt(lhs, rhs, ty, op);
2599 } else {2595 } else {
2600 return cg.fail(2596 return cg.fail("TODO: Implement binary operation for type: {f}", .{ty.fmt(pt)});
2601 "TODO: Implement binary operation for type: {f}",
2602 .{ty.fmt(pt)},
2603 );
2604 }2597 }
2605 }2598 }
26062599
...@@ -3333,7 +3326,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {...@@ -3333,7 +3326,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
3333 },3326 },
3334 else => unreachable,3327 else => unreachable,
3335 },3328 },
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)}),
3337 }3330 }
3338}3331}
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) {...@@ -525,47 +525,47 @@ pub const MCValue = union(enum) {
525 };525 };
526 }526 }
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 {
529 switch (mcv) {529 switch (mcv) {
530 .none, .unreach, .dead, .undef => try bw.print("({s})", .{@tagName(mcv)}),530 .none, .unreach, .dead, .undef => try w.print("({s})", .{@tagName(mcv)}),
531 .immediate => |pl| try bw.print("0x{x}", .{pl}),531 .immediate => |pl| try w.print("0x{x}", .{pl}),
532 .memory => |pl| try bw.print("[ds:0x{x}]", .{pl}),532 .memory => |pl| try w.print("[ds:0x{x}]", .{pl}),
533 inline .eflags, .register => |pl| try bw.print("{s}", .{@tagName(pl)}),533 inline .eflags, .register => |pl| try w.print("{s}", .{@tagName(pl)}),
534 .register_pair => |pl| try bw.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),534 .register_pair => |pl| try w.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),
535 .register_triple => |pl| try bw.print("{s}:{s}:{s}", .{535 .register_triple => |pl| try w.print("{s}:{s}:{s}", .{
536 @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]),536 @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]),
537 }),537 }),
538 .register_quadruple => |pl| try bw.print("{s}:{s}:{s}:{s}", .{538 .register_quadruple => |pl| try w.print("{s}:{s}:{s}:{s}", .{
539 @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]),539 @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]),
540 }),540 }),
541 .register_offset => |pl| try bw.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),541 .register_offset => |pl| try w.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),
542 .register_overflow => |pl| try bw.print("{s}:{s}", .{542 .register_overflow => |pl| try w.print("{s}:{s}", .{
543 @tagName(pl.eflags),543 @tagName(pl.eflags),
544 @tagName(pl.reg),544 @tagName(pl.reg),
545 }),545 }),
546 .register_mask => |pl| try bw.print("mask({s},{f}):{c}{s}", .{546 .register_mask => |pl| try w.print("mask({s},{f}):{c}{s}", .{
547 @tagName(pl.info.kind),547 @tagName(pl.info.kind),
548 pl.info.scalar,548 pl.info.scalar,
549 @as(u8, if (pl.info.inverted) '!' else ' '),549 @as(u8, if (pl.info.inverted) '!' else ' '),
550 @tagName(pl.reg),550 @tagName(pl.reg),
551 }),551 }),
552 .indirect => |pl| try bw.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),552 .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
553 .indirect_load_frame => |pl| try bw.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }),553 .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }),
554 .load_frame => |pl| try bw.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 bw.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 bw.print("[nav:{d}]", .{@intFromEnum(pl)}),556 .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}),
557 .lea_nav => |pl| try bw.print("nav:{d}", .{@intFromEnum(pl)}),557 .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}),
558 .load_uav => |pl| try bw.print("[uav:{d}]", .{@intFromEnum(pl.val)}),558 .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}),
559 .lea_uav => |pl| try bw.print("uav:{d}", .{@intFromEnum(pl.val)}),559 .lea_uav => |pl| try w.print("uav:{d}", .{@intFromEnum(pl.val)}),
560 .load_lazy_sym => |pl| try bw.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),560 .load_lazy_sym => |pl| try w.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) }),561 .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
562 .load_extern_func => |pl| try bw.print("[extern:{d}]", .{@intFromEnum(pl)}),562 .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}),
563 .lea_extern_func => |pl| try bw.print("extern:{d}", .{@intFromEnum(pl)}),563 .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}),
564 .elementwise_args => |pl| try bw.print("elementwise:{d}:[{} + 0x{x}]", .{564 .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{
565 pl.regs, pl.frame_index, pl.frame_off,565 pl.regs, pl.frame_index, pl.frame_off,
566 }),566 }),
567 .reserved_frame => |pl| try bw.print("(dead:{})", .{pl}),567 .reserved_frame => |pl| try w.print("(dead:{})", .{pl}),
568 .air_ref => |pl| try bw.print("(air:0x{x})", .{@intFromEnum(pl)}),568 .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}),
569 }569 }
570 }570 }
571};571};
...@@ -812,7 +812,7 @@ const InstTracking = struct {...@@ -812,7 +812,7 @@ const InstTracking = struct {
812 }812 }
813 }813 }
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 {
816 if (!std.meta.eql(tracking.long, tracking.short)) try bw.print("|{f}| ", .{tracking.long});816 if (!std.meta.eql(tracking.long, tracking.short)) try bw.print("|{f}| ", .{tracking.long});
817 try bw.print("{f}", .{tracking.short});817 try bw.print("{f}", .{tracking.short});
818 }818 }
...@@ -1088,10 +1088,10 @@ const FormatNavData = struct {...@@ -1088,10 +1088,10 @@ const FormatNavData = struct {
1088 ip: *const InternPool,1088 ip: *const InternPool,
1089 nav_index: InternPool.Nav.Index,1089 nav_index: InternPool.Nav.Index,
1090};1090};
1091fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void {1091fn formatNav(data: FormatNavData, w: *Writer) Writer.Error!void {
1092 try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});1092 try w.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)});
1093}1093}
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) {
1095 return .{ .data = .{1095 return .{ .data = .{
1096 .ip = ip,1096 .ip = ip,
1097 .nav_index = nav_index,1097 .nav_index = nav_index,
...@@ -1102,15 +1102,14 @@ const FormatAirData = struct {...@@ -1102,15 +1102,14 @@ const FormatAirData = struct {
1102 self: *CodeGen,1102 self: *CodeGen,
1103 inst: Air.Inst.Index,1103 inst: Air.Inst.Index,
1104};1104};
1105fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) Writer.Error!void {1105fn formatAir(data: FormatAirData, w: *std.io.Writer) Writer.Error!void {
1106 comptime assert(fmt.len == 0);1106 // not acceptable implementation because it ignores `w`:
1107 // not acceptable implementation:
1108 //data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness);1107 //data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness);
1109 _ = data;1108 _ = data;
1110 _ = w;1109 _ = w;
1111 @panic("TODO: unimplemented");1110 @panic("TODO: unimplemented");
1112}1111}
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) {
1114 return .{ .data = .{ .self = self, .inst = inst } };1113 return .{ .data = .{ .self = self, .inst = inst } };
1115}1114}
11161115
...@@ -1118,7 +1117,7 @@ const FormatWipMirData = struct {...@@ -1118,7 +1117,7 @@ const FormatWipMirData = struct {
1118 self: *CodeGen,1117 self: *CodeGen,
1119 inst: Mir.Inst.Index,1118 inst: Mir.Inst.Index,
1120};1119};
1121fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void {1120fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void {
1122 var lower: Lower = .{1121 var lower: Lower = .{
1123 .target = data.self.target,1122 .target = data.self.target,
1124 .allocator = data.self.gpa,1123 .allocator = data.self.gpa,
...@@ -1133,27 +1132,22 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri...@@ -1133,27 +1132,22 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
1133 lower.err_msg.?.deinit(data.self.gpa);1132 lower.err_msg.?.deinit(data.self.gpa);
1134 lower.err_msg = null;1133 lower.err_msg = null;
1135 }1134 }
1136 try bw.writeAll(lower.err_msg.?.msg);1135 try w.writeAll(lower.err_msg.?.msg);
1137 return;1136 return;
1138 },1137 },
1139 error.OutOfMemory, error.InvalidInstruction, error.CannotEncode => |e| {1138 else => |e| {
1140 try bw.writeAll(switch (e) {1139 try w.writeAll(@errorName(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 });
1145 return;1140 return;
1146 },1141 },
1147 else => |e| return e,
1148 }).insts) |lowered_inst| {1142 }).insts) |lowered_inst| {
1149 if (!first) try bw.writeAll("\ndebug(wip_mir): ");1143 if (!first) try w.writeAll("\ndebug(wip_mir): ");
1150 try bw.print(" | {f}", .{lowered_inst});1144 try w.print(" | {f}", .{lowered_inst});
1151 first = false;1145 first = false;
1152 }1146 }
1153 if (first) {1147 if (first) {
1154 const ip = &data.self.pt.zcu.intern_pool;1148 const ip = &data.self.pt.zcu.intern_pool;
1155 const mir_inst = lower.mir.instructions.get(data.inst);1149 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)});
1157 switch (mir_inst.ops) {1151 switch (mir_inst.ops) {
1158 else => unreachable,1152 else => unreachable,
1159 .pseudo_dbg_prologue_end_none,1153 .pseudo_dbg_prologue_end_none,
...@@ -1165,20 +1159,20 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri...@@ -1165,20 +1159,20 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
1165 .pseudo_dbg_var_none,1159 .pseudo_dbg_var_none,
1166 .pseudo_dead_none,1160 .pseudo_dead_none,
1167 => {},1161 => {},
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(
1169 " {[line]d}, {[column]d}",1163 " {[line]d}, {[column]d}",
1170 mir_inst.data.line_column,1164 mir_inst.data.line_column,
1171 ),1165 ),
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}", .{
1173 ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip),1167 ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip),
1174 }),1168 }),
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}", .{
1176 @as(i32, @bitCast(mir_inst.data.i.i)),1170 @as(i32, @bitCast(mir_inst.data.i.i)),
1177 }),1171 }),
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}", .{
1179 mir_inst.data.i.i,1173 mir_inst.data.i.i,
1180 }),1174 }),
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}", .{
1182 mir_inst.data.i64,1176 mir_inst.data.i64,
1183 }),1177 }),
1184 .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => {1178 .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => {
...@@ -1186,40 +1180,39 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri...@@ -1186,40 +1180,39 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri
1186 .base = .{ .reg = mir_inst.data.ro.reg },1180 .base = .{ .reg = mir_inst.data.ro.reg },
1187 .disp = mir_inst.data.ro.off,1181 .disp = mir_inst.data.ro.off,
1188 }) };1182 }) };
1189 try bw.print(" {f}", .{mem_op.fmt(.m)});1183 try w.print(" {f}", .{mem_op.fmt(.m)});
1190 },1184 },
1191 .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {1185 .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {
1192 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{1186 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{
1193 .base = .{ .frame = mir_inst.data.fa.index },1187 .base = .{ .frame = mir_inst.data.fa.index },
1194 .disp = mir_inst.data.fa.off,1188 .disp = mir_inst.data.fa.off,
1195 }) };1189 }) };
1196 try bw.print(" {f}", .{mem_op.fmt(.m)});1190 try w.print(" {f}", .{mem_op.fmt(.m)});
1197 },1191 },
1198 .pseudo_dbg_arg_m, .pseudo_dbg_var_m => {1192 .pseudo_dbg_arg_m, .pseudo_dbg_var_m => {
1199 const mem_op: encoder.Instruction.Operand = .{1193 const mem_op: encoder.Instruction.Operand = .{
1200 .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.x.payload).data.decode(),1194 .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.x.payload).data.decode(),
1201 };1195 };
1202 try bw.print(" {f}", .{mem_op.fmt(.m)});1196 try w.print(" {f}", .{mem_op.fmt(.m)});
1203 },1197 },
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}", .{
1205 Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt),1199 Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt),
1206 }),1200 }),
1207 }1201 }
1208 }1202 }
1209}1203}
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) {
1211 return .{ .data = .{ .self = self, .inst = inst } };1205 return .{ .data = .{ .self = self, .inst = inst } };
1212}1206}
12131207
1214const FormatTrackingData = struct {1208const FormatTrackingData = struct {
1215 self: *CodeGen,1209 self: *CodeGen,
1216};1210};
1217fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {1211fn formatTracking(data: FormatTrackingData, w: *Writer) Writer.Error!void {
1218 comptime assert(fmt.len == 0);
1219 var it = data.self.inst_tracking.iterator();1212 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.* });
1221}1214}
1222fn fmtTracking(self: *CodeGen) std.fmt.Formatter(formatTracking) {1215fn fmtTracking(self: *CodeGen) std.fmt.Formatter(FormatTrackingData, formatTracking) {
1223 return .{ .data = .{ .self = self } };1216 return .{ .data = .{ .self = self } };
1224}1217}
12251218
...@@ -2033,7 +2026,7 @@ fn gen(...@@ -2033,7 +2026,7 @@ fn gen(
2033 .{},2026 .{},
2034 );2027 );
2035 self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } };2028 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 });
2037 },2030 },
2038 else => unreachable,2031 else => unreachable,
2039 }2032 }
...@@ -12894,7 +12887,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -12894,7 +12887,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
12894 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ },12887 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ },
12895 } },12888 } },
12896 } }) catch |err| switch (err) {12889 } }) 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}", .{
12898 @tagName(air_tag),12891 @tagName(air_tag),
12899 cg.typeOf(bin_op.lhs).fmt(pt),12892 cg.typeOf(bin_op.lhs).fmt(pt),
12900 ops[0].tracking(cg),12893 ops[0].tracking(cg),
...@@ -21771,7 +21764,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -21771,7 +21764,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
21771 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ },21764 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ },
21772 } },21765 } },
21773 } }) catch |err| switch (err) {21766 } }) 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}", .{
21775 @tagName(air_tag),21768 @tagName(air_tag),
21776 cg.typeOf(bin_op.lhs).fmt(pt),21769 cg.typeOf(bin_op.lhs).fmt(pt),
21777 ops[0].tracking(cg),21770 ops[0].tracking(cg),
...@@ -32489,7 +32482,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -32489,7 +32482,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
32489 .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp3q, ._, ._ },32482 .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp3q, ._, ._ },
32490 } },32483 } },
32491 } }) catch |err| switch (err) {32484 } }) 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}", .{
32493 @tagName(air_tag),32486 @tagName(air_tag),
32494 cg.typeOf(bin_op.lhs).fmt(pt),32487 cg.typeOf(bin_op.lhs).fmt(pt),
32495 ops[0].tracking(cg),32488 ops[0].tracking(cg),
...@@ -59317,7 +59310,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -59317,7 +59310,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
59317 .{ ._, ._, .@"or", .tmp4q, .tmp5q, ._, ._ },59310 .{ ._, ._, .@"or", .tmp4q, .tmp5q, ._, ._ },
59318 } },59311 } },
59319 } }) catch |err| switch (err) {59312 } }) 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}", .{
59321 @tagName(air_tag),59314 @tagName(air_tag),
59322 ty_pl.ty.toType().fmt(pt),59315 ty_pl.ty.toType().fmt(pt),
59323 ops[0].tracking(cg),59316 ops[0].tracking(cg),
...@@ -60816,7 +60809,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -60816,7 +60809,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
60816 .{ ._, ._, .@"or", .dst0d, .tmp0d, ._, ._ },60809 .{ ._, ._, .@"or", .dst0d, .tmp0d, ._, ._ },
60817 } },60810 } },
60818 } }) catch |err| switch (err) {60811 } }) 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}", .{
60820 @tagName(air_tag),60813 @tagName(air_tag),
60821 cg.typeOf(bin_op.rhs).fmt(pt),60814 cg.typeOf(bin_op.rhs).fmt(pt),
60822 ops[1].tracking(cg),60815 ops[1].tracking(cg),
...@@ -64073,7 +64066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -64073,7 +64066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
64073 .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp1q, ._, ._ },64066 .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp1q, ._, ._ },
64074 } },64067 } },
64075 } }) catch |err| switch (err) {64068 } }) 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}", .{
64077 @tagName(air_tag),64070 @tagName(air_tag),
64078 lhs_ty.fmt(pt),64071 lhs_ty.fmt(pt),
64079 ops[0].tracking(cg),64072 ops[0].tracking(cg),
...@@ -79435,7 +79428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -79435,7 +79428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
79435 .@"struct", .@"union" => {79428 .@"struct", .@"union" => {
79436 assert(ty.containerLayout(zcu) == .@"packed");79429 assert(ty.containerLayout(zcu) == .@"packed");
79437 for (&ops) |*op| op.wrapInt(cg) catch |err| switch (err) {79430 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}", .{
79439 @tagName(air_tag),79432 @tagName(air_tag),
79440 ty.fmt(pt),79433 ty.fmt(pt),
79441 op.tracking(cg),79434 op.tracking(cg),
...@@ -86528,7 +86521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -86528,7 +86521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
86528 } },86521 } },
86529 }),86522 }),
86530 }) catch |err| switch (err) {86523 }) 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}", .{
86532 @tagName(air_tag),86525 @tagName(air_tag),
86533 @tagName(vector_cmp.compareOperator()),86526 @tagName(vector_cmp.compareOperator()),
86534 cg.typeOf(vector_cmp.lhs).fmt(pt),86527 cg.typeOf(vector_cmp.lhs).fmt(pt),
...@@ -157193,7 +157186,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -157193,7 +157186,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
157193 } },157186 } },
157194 } },157187 } },
157195 }) catch |err| switch (err) {157188 }) 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}", .{
157197 @tagName(air_tag),157190 @tagName(air_tag),
157198 @tagName(reduce.operation),157191 @tagName(reduce.operation),
157199 cg.typeOf(reduce.operand).fmt(pt),157192 cg.typeOf(reduce.operand).fmt(pt),
...@@ -157204,7 +157197,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -157204,7 +157197,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
157204 switch (reduce.operation) {157197 switch (reduce.operation) {
157205 .And, .Or, .Xor, .Min, .Max => {},157198 .And, .Or, .Xor, .Min, .Max => {},
157206 .Add, .Mul => if (cg.intInfo(res_ty)) |_| res[0].wrapInt(cg) catch |err| switch (err) {157199 .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}", .{
157208 @tagName(air_tag),157201 @tagName(air_tag),
157209 @tagName(reduce.operation),157202 @tagName(reduce.operation),
157210 res_ty.fmt(pt),157203 res_ty.fmt(pt),
...@@ -164487,7 +164480,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -164487,7 +164480,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
164487 } },164480 } },
164488 } },164481 } },
164489 }) catch |err| switch (err) {164482 }) 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}", .{
164491 @tagName(air_tag),164484 @tagName(air_tag),
164492 @tagName(reduce.operation),164485 @tagName(reduce.operation),
164493 cg.typeOf(reduce.operand).fmt(pt),164486 cg.typeOf(reduce.operand).fmt(pt),
...@@ -166284,7 +166277,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -166284,7 +166277,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
166284 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ },166277 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ },
166285 } },166278 } },
166286 } }) catch |err| switch (err) {166279 } }) 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}", .{
166288 @tagName(air_tag),166281 @tagName(air_tag),
166289 ty_op.ty.toType().fmt(pt),166282 ty_op.ty.toType().fmt(pt),
166290 ops[0].tracking(cg),166283 ops[0].tracking(cg),
...@@ -166300,7 +166293,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -166300,7 +166293,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
166300 const bin_op = air_datas[@intFromEnum(inst)].bin_op;166293 const bin_op = air_datas[@intFromEnum(inst)].bin_op;
166301 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }) ++ .{undefined};166294 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }) ++ .{undefined};
166302 ops[2] = ops[0].getByteLen(cg) catch |err| switch (err) {166295 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}", .{
166304 @tagName(air_tag),166297 @tagName(air_tag),
166305 cg.typeOf(bin_op.lhs).fmt(pt),166298 cg.typeOf(bin_op.lhs).fmt(pt),
166306 cg.typeOf(bin_op.rhs).fmt(pt),166299 cg.typeOf(bin_op.rhs).fmt(pt),
...@@ -166340,7 +166333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -166340,7 +166333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
166340 } },166333 } },
166341 }},166334 }},
166342 }) catch |err| switch (err) {166335 }) 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}", .{
166344 @tagName(air_tag),166337 @tagName(air_tag),
166345 cg.typeOf(bin_op.lhs).fmt(pt),166338 cg.typeOf(bin_op.lhs).fmt(pt),
166346 cg.typeOf(bin_op.rhs).fmt(pt),166339 cg.typeOf(bin_op.rhs).fmt(pt),
...@@ -181509,7 +181502,7 @@ fn genSetReg(...@@ -181509,7 +181502,7 @@ fn genSetReg(
181509 assert(!ty.optionalReprIsPayload(zcu));181502 assert(!ty.optionalReprIsPayload(zcu));
181510 break :first_ty opt_child;181503 break :first_ty opt_child;
181511 },181504 },
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) }),
181513 });181506 });
181514 const first_size: u31 = @intCast(first_ty.abiSize(zcu));181507 const first_size: u31 = @intCast(first_ty.abiSize(zcu));
181515 const frame_size = std.math.ceilPowerOfTwoAssert(u32, abi_size);181508 const frame_size = std.math.ceilPowerOfTwoAssert(u32, abi_size);
...@@ -186937,7 +186930,7 @@ const Temp = struct {...@@ -186937,7 +186930,7 @@ const Temp = struct {
186937 assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable);186930 assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable);
186938 break :part_ty .u64;186931 break :part_ty .u64;
186939 } else part_ty: switch (ip.indexToKey(src_ty.toIntern())) {186932 } 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) }),
186941 .ptr_type => |ptr_info| {186934 .ptr_type => |ptr_info| {
186942 assert(ptr_info.flags.size == .slice);186935 assert(ptr_info.flags.size == .slice);
186943 assert(src_regs.len == 2);186936 assert(src_regs.len == 2);
...@@ -186948,7 +186941,7 @@ const Temp = struct {...@@ -186948,7 +186941,7 @@ const Temp = struct {
186948 break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_abi_size, 8));186941 break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_abi_size, 8));
186949 },186942 },
186950 .opt_type => |opt_child| switch (ip.indexToKey(opt_child)) {186943 .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) }),
186952 .ptr_type => |ptr_info| {186945 .ptr_type => |ptr_info| {
186953 assert(ptr_info.flags.size == .slice);186946 assert(ptr_info.flags.size == .slice);
186954 assert(src_regs.len == 2);186947 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...@@ -707,7 +707,14 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
707 const comp = emit.bin_file.comp;707 const comp = emit.bin_file.comp;
708 const gpa = comp.gpa;708 const gpa = comp.gpa;
709 const start_offset: u32 = @intCast(emit.code.items.len);709 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 }
711 const end_offset: u32 = @intCast(emit.code.items.len);718 const end_offset: u32 = @intCast(emit.code.items.len);
712 for (reloc_info) |reloc| switch (reloc.target.type) {719 for (reloc_info) |reloc| switch (reloc.target.type) {
713 .inst => {720 .inst => {
src/arch/x86_64/Encoding.zig+31-25
...@@ -159,14 +159,12 @@ pub fn modRmExt(encoding: Encoding) u3 {...@@ -159,14 +159,12 @@ pub fn modRmExt(encoding: Encoding) u3 {
159 };159 };
160}160}
161161
162pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {162pub fn format(encoding: Encoding, writer: *std.io.Writer) std.io.Writer.Error!void {
163 comptime assert(fmt.len == 0);
164
165 var opc = encoding.opcode();163 var opc = encoding.opcode();
166 if (encoding.data.mode.isVex()) {164 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) {
170 .vex_128_w0, .vex_128_w1, .vex_128_wig => "128",168 .vex_128_w0, .vex_128_w1, .vex_128_wig => "128",
171 .vex_256_w0, .vex_256_w1, .vex_256_wig => "256",169 .vex_256_w0, .vex_256_w1, .vex_256_wig => "256",
172 .vex_lig_w0, .vex_lig_w1, .vex_lig_wig => "LIG",170 .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 {...@@ -177,25 +175,25 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
177 switch (opc[0]) {175 switch (opc[0]) {
178 else => {},176 else => {},
179 0x66, 0xf3, 0xf2 => {177 0x66, 0xf3, 0xf2 => {
180 try bw.print(".{X:0>2}", .{opc[0]});178 try writer.print(".{X:0>2}", .{opc[0]});
181 opc = opc[1..];179 opc = opc[1..];
182 },180 },
183 }181 }
184182
185 try bw.print(".{X}", .{opc[0 .. opc.len - 1]});183 try writer.print(".{X}", .{opc[0 .. opc.len - 1]});
186 opc = opc[opc.len - 1 ..];184 opc = opc[opc.len - 1 ..];
187185
188 try bw.writeAll(".W");186 try writer.writeAll(".W");
189 try bw.writeAll(switch (encoding.data.mode) {187 try writer.writeAll(switch (encoding.data.mode) {
190 .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0",188 .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0",
191 .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1",189 .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1",
192 .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG",190 .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG",
193 else => unreachable,191 else => unreachable,
194 });192 });
195193
196 try bw.writeByte(' ');194 try writer.writeByte(' ');
197 } else if (encoding.data.mode.isLong()) try bw.writeAll("REX.W + ");195 } else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + ");
198 for (opc) |byte| try bw.print("{x:0>2} ", .{byte});196 for (opc) |byte| try writer.print("{x:0>2} ", .{byte});
199197
200 switch (encoding.data.op_en) {198 switch (encoding.data.op_en) {
201 .z, .fd, .td, .i, .zi, .ii, .d => {},199 .z, .fd, .td, .i, .zi, .ii, .d => {},
...@@ -212,10 +210,10 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {...@@ -212,10 +210,10 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
212 .r64 => "rd",210 .r64 => "rd",
213 else => unreachable,211 else => unreachable,
214 };212 };
215 try bw.print("+{s} ", .{tag});213 try writer.print("+{s} ", .{tag});
216 },214 },
217 .ia, .m, .mi, .m1, .mc, .vm, .vmi => try bw.print("/{d} ", .{encoding.modRmExt()}),215 .ia, .m, .mi, .m1, .mc, .vm, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
218 .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try bw.writeAll("/r "),216 .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "),
219 }217 }
220218
221 switch (encoding.data.op_en) {219 switch (encoding.data.op_en) {
...@@ -244,24 +242,24 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {...@@ -244,24 +242,24 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void {
244 .rel32 => "cd",242 .rel32 => "cd",
245 else => unreachable,243 else => unreachable,
246 };244 };
247 try bw.print("{s} ", .{tag});245 try writer.print("{s} ", .{tag});
248 },246 },
249 .rvmr => try bw.writeAll("/is4 "),247 .rvmr => try writer.writeAll("/is4 "),
250 .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {},248 .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {},
251 }249 }
252250
253 try bw.print("{s} ", .{@tagName(encoding.mnemonic)});251 try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
254252
255 for (encoding.data.ops) |op| switch (op) {253 for (encoding.data.ops) |op| switch (op) {
256 .none => break,254 .none => break,
257 else => try bw.print("{s} ", .{@tagName(op)}),255 else => try writer.print("{s} ", .{@tagName(op)}),
258 };256 };
259257
260 const op_en = switch (encoding.data.op_en) {258 const op_en = switch (encoding.data.op_en) {
261 .zi => .i,259 .zi => .i,
262 else => |op_en| op_en,260 else => |op_en| op_en,
263 };261 };
264 try bw.print("{s}", .{@tagName(op_en)});262 try writer.print("{s}", .{@tagName(op_en)});
265}263}
266264
267pub const Mnemonic = enum {265pub const Mnemonic = enum {
...@@ -1016,13 +1014,21 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op...@@ -1016,13 +1014,21 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
1016 };1014 };
1017 @memcpy(inst.ops[0..ops.len], ops);1015 @memcpy(inst.ops[0..ops.len], ops);
10181016
1019 var buf: [15]u8 = undefined;1017 // By using a buffer with maximum length of encoded instruction, we can use
1020 var bw: Writer = .fixed(&buf);1018 // the `end` field of the Writer for the count.
1021 inst.encode(&bw, .{1019 var buf: [16]u8 = undefined;
1020 var trash: std.io.Writer.Discarding = .init(&buf);
1021 inst.encode(&trash.writer, .{
1022 .allow_frame_locs = true,1022 .allow_frame_locs = true,
1023 .allow_symbols = true,1023 .allow_symbols = true,
1024 }) catch unreachable;1024 }) catch {
1025 return @intCast(bw.end);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;
1026}1032}
10271033
1028const mnemonic_to_encodings_map = init: {1034const mnemonic_to_encodings_map = init: {
src/arch/x86_64/bits.zig+4-19
...@@ -729,15 +729,6 @@ pub const FrameIndex = enum(u32) {...@@ -729,15 +729,6 @@ pub const FrameIndex = enum(u32) {
729 pub fn isNamed(fi: FrameIndex) bool {729 pub fn isNamed(fi: FrameIndex) bool {
730 return @intFromEnum(fi) < named_count;730 return @intFromEnum(fi) < named_count;
731 }731 }
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 }
741};732};
742733
743pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };734pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
...@@ -838,14 +829,13 @@ pub const Memory = struct {...@@ -838,14 +829,13 @@ pub const Memory = struct {
838 };829 };
839 }830 }
840831
841 pub fn format(s: Size, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {832 pub fn format(s: Size, writer: *std.io.Writer) std.io.Writer.Error!void {
842 comptime assert(fmt.len == 0);
843 if (s == .none) return;833 if (s == .none) return;
844 try bw.writeAll(@tagName(s));834 try writer.writeAll(@tagName(s));
845 switch (s) {835 switch (s) {
846 .none => unreachable,836 .none => unreachable,
847 .ptr, .gpr => {},837 .ptr, .gpr => {},
848 else => try bw.writeAll(" ptr"),838 else => try writer.writeAll(" ptr"),
849 }839 }
850 }840 }
851 };841 };
...@@ -901,12 +891,7 @@ pub const Immediate = union(enum) {...@@ -901,12 +891,7 @@ pub const Immediate = union(enum) {
901 return .{ .signed = x };891 return .{ .signed = x };
902 }892 }
903893
904 pub fn format(894 pub fn format(imm: Immediate, writer: *std.io.Writer) std.io.Writer.Error!void {
905 imm: Immediate,
906 comptime _: []const u8,
907 _: std.fmt.FormatOptions,
908 writer: anytype,
909 ) @TypeOf(writer).Error!void {
910 switch (imm) {895 switch (imm) {
911 inline else => |int| try writer.print("{d}", .{int}),896 inline else => |int| try writer.print("{d}", .{int}),
912 .nav => |nav_off| try writer.print("Nav({d}) + {d}", .{ @intFromEnum(nav_off.nav), nav_off.off }),897 .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 {...@@ -353,8 +353,7 @@ pub const Instruction = struct {
353 return inst;353 return inst;
354 }354 }
355355
356 pub fn format(inst: Instruction, w: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {356 pub fn format(inst: Instruction, w: *Writer) Writer.Error!void {
357 comptime assert(unused_format_string.len == 0);
358 switch (inst.prefix) {357 switch (inst.prefix) {
359 .none, .directive => {},358 .none, .directive => {},
360 else => try w.print("{s} ", .{@tagName(inst.prefix)}),359 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 {...@@ -35,7 +35,7 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature {
35 .stage2_arm => .arm_backend,35 .stage2_arm => .arm_backend,
36 .stage2_c => .c_backend,36 .stage2_c => .c_backend,
37 .stage2_llvm => .llvm_backend,37 .stage2_llvm => .llvm_backend,
38 .stage2_powerpc => .powerpc_backend,38 .stage2_powerpc => unreachable,
39 .stage2_riscv64 => .riscv64_backend,39 .stage2_riscv64 => .riscv64_backend,
40 .stage2_sparc64 => .sparc64_backend,40 .stage2_sparc64 => .sparc64_backend,
41 .stage2_spirv => .spirv_backend,41 .stage2_spirv => .spirv_backend,
...@@ -49,11 +49,11 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature {...@@ -49,11 +49,11 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature {
49fn importBackend(comptime backend: std.builtin.CompilerBackend) type {49fn importBackend(comptime backend: std.builtin.CompilerBackend) type {
50 return switch (backend) {50 return switch (backend) {
51 .other, .stage1 => unreachable,51 .other, .stage1 => unreachable,
52 .stage2_aarch64 => @import("arch/aarch64/CodeGen.zig"),52 .stage2_aarch64 => unreachable,
53 .stage2_arm => @import("arch/arm/CodeGen.zig"),53 .stage2_arm => unreachable,
54 .stage2_c => @import("codegen/c.zig"),54 .stage2_c => @import("codegen/c.zig"),
55 .stage2_llvm => @import("codegen/llvm.zig"),55 .stage2_llvm => @import("codegen/llvm.zig"),
56 .stage2_powerpc => @import("arch/powerpc/CodeGen.zig"),56 .stage2_powerpc => unreachable,
57 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),57 .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"),
58 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),58 .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"),
59 .stage2_spirv => @import("codegen/spirv.zig"),59 .stage2_spirv => @import("codegen/spirv.zig"),
...@@ -71,14 +71,11 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co...@@ -71,14 +71,11 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co
71 inline .stage2_llvm,71 inline .stage2_llvm,
72 .stage2_c,72 .stage2_c,
73 .stage2_wasm,73 .stage2_wasm,
74 .stage2_arm,
75 .stage2_x86_64,74 .stage2_x86_64,
76 .stage2_aarch64,
77 .stage2_x86,75 .stage2_x86,
78 .stage2_riscv64,76 .stage2_riscv64,
79 .stage2_sparc64,77 .stage2_sparc64,
80 .stage2_spirv,78 .stage2_spirv,
81 .stage2_powerpc,
82 => |backend| {79 => |backend| {
83 dev.check(devFeatureForBackend(backend));80 dev.check(devFeatureForBackend(backend));
84 return importBackend(backend).legalizeFeatures(target);81 return importBackend(backend).legalizeFeatures(target);
...@@ -90,9 +87,6 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co...@@ -90,9 +87,6 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co
90/// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a87/// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a
91/// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`.88/// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`.
92pub const AnyMir = union {89pub 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"),
96 riscv64: @import("arch/riscv64/Mir.zig"),90 riscv64: @import("arch/riscv64/Mir.zig"),
97 sparc64: @import("arch/sparc64/Mir.zig"),91 sparc64: @import("arch/sparc64/Mir.zig"),
98 x86_64: @import("arch/x86_64/Mir.zig"),92 x86_64: @import("arch/x86_64/Mir.zig"),
...@@ -103,7 +97,6 @@ pub const AnyMir = union {...@@ -103,7 +97,6 @@ pub const AnyMir = union {
103 return switch (backend) {97 return switch (backend) {
104 .stage2_aarch64 => "aarch64",98 .stage2_aarch64 => "aarch64",
105 .stage2_arm => "arm",99 .stage2_arm => "arm",
106 .stage2_powerpc => "powerpc",
107 .stage2_riscv64 => "riscv64",100 .stage2_riscv64 => "riscv64",
108 .stage2_sparc64 => "sparc64",101 .stage2_sparc64 => "sparc64",
109 .stage2_x86_64 => "x86_64",102 .stage2_x86_64 => "x86_64",
...@@ -118,10 +111,7 @@ pub const AnyMir = union {...@@ -118,10 +111,7 @@ pub const AnyMir = union {
118 const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm);111 const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm);
119 switch (backend) {112 switch (backend) {
120 else => unreachable,113 else => unreachable,
121 inline .stage2_aarch64,114 inline .stage2_riscv64,
122 .stage2_arm,
123 .stage2_powerpc,
124 .stage2_riscv64,
125 .stage2_sparc64,115 .stage2_sparc64,
126 .stage2_x86_64,116 .stage2_x86_64,
127 .stage2_wasm,117 .stage2_wasm,
...@@ -149,10 +139,7 @@ pub fn generateFunction(...@@ -149,10 +139,7 @@ pub fn generateFunction(
149 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;139 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;
150 switch (target_util.zigBackend(target, false)) {140 switch (target_util.zigBackend(target, false)) {
151 else => unreachable,141 else => unreachable,
152 inline .stage2_aarch64,142 inline .stage2_riscv64,
153 .stage2_arm,
154 .stage2_powerpc,
155 .stage2_riscv64,
156 .stage2_sparc64,143 .stage2_sparc64,
157 .stage2_x86_64,144 .stage2_x86_64,
158 .stage2_wasm,145 .stage2_wasm,
...@@ -187,10 +174,7 @@ pub fn emitFunction(...@@ -187,10 +174,7 @@ pub fn emitFunction(
187 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;174 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;
188 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) {175 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) {
189 else => unreachable,176 else => unreachable,
190 inline .stage2_aarch64,177 inline .stage2_riscv64,
191 .stage2_arm,
192 .stage2_powerpc,
193 .stage2_riscv64,
194 .stage2_sparc64,178 .stage2_sparc64,
195 .stage2_x86_64,179 .stage2_x86_64,
196 => |backend| {180 => |backend| {
...@@ -216,10 +200,7 @@ pub fn generateLazyFunction(...@@ -216,10 +200,7 @@ pub fn generateLazyFunction(
216 zcu.getTarget();200 zcu.getTarget();
217 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) {201 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) {
218 else => unreachable,202 else => unreachable,
219 inline .stage2_powerpc,203 inline .stage2_riscv64, .stage2_x86_64 => |backend| {
220 .stage2_riscv64,
221 .stage2_x86_64,
222 => |backend| {
223 dev.check(devFeatureForBackend(backend));204 dev.check(devFeatureForBackend(backend));
224 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);205 return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
225 },206 },
...@@ -910,7 +891,7 @@ pub fn genNavRef(...@@ -910,7 +891,7 @@ pub fn genNavRef(
910 const zcu = pt.zcu;891 const zcu = pt.zcu;
911 const ip = &zcu.intern_pool;892 const ip = &zcu.intern_pool;
912 const nav = ip.getNav(nav_index);893 const nav = ip.getNav(nav_index);
913 log.debug("genNavRef({})", .{nav.fqn.fmt(ip)});894 log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)});
914895
915 const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e|896 const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e|
916 .{ e.lib_name, e.linkage, e.is_threadlocal and zcu.comp.config.any_non_single_threaded }897 .{ 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 {...@@ -345,12 +345,15 @@ fn isReservedIdent(ident: []const u8) bool {
345 } else return reserved_idents.has(ident);345 } else return reserved_idents.has(ident);
346}346}
347347
348fn formatIdent(348fn formatIdentSolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
349 ident: []const u8,349 return formatIdentOptions(ident, w, true);
350 w: *Writer,350}
351 comptime fmt_str: []const u8,351
352) Writer.Error!void {352fn formatIdentUnsolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
353 const solo = fmt_str.len != 0 and fmt_str[0] == ' '; // space means solo; not part of a bigger ident.353 return formatIdentOptions(ident, w, false);
354}
355
356fn formatIdentOptions(ident: []const u8, w: *std.io.Writer, solo: bool) std.io.Writer.Error!void {
354 if (solo and isReservedIdent(ident)) {357 if (solo and isReservedIdent(ident)) {
355 try w.writeAll("zig_e_");358 try w.writeAll("zig_e_");
356 }359 }
...@@ -367,29 +370,36 @@ fn formatIdent(...@@ -367,29 +370,36 @@ fn formatIdent(
367 }370 }
368 }371 }
369}372}
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) {
371 return .{ .data = ident };379 return .{ .data = ident };
372}380}
373381
374const CTypePoolStringFormatData = struct {382const CTypePoolStringFormatData = struct {
375 ctype_pool_string: CType.Pool.String,383 ctype_pool_string: CType.Pool.String,
376 ctype_pool: *const CType.Pool,384 ctype_pool: *const CType.Pool,
385 solo: bool,
377};386};
378fn formatCTypePoolString(387fn formatCTypePoolString(data: CTypePoolStringFormatData, w: *std.io.Writer) std.io.Writer.Error!void {
379 data: CTypePoolStringFormatData,
380 w: *Writer,
381 comptime fmt_str: []const u8,
382) Writer.Error!void {
383 if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice|388 if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice|
384 try formatIdent(slice, w, fmt_str)389 try formatIdentOptions(slice, w, data.solo)
385 else390 else
386 try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)});391 try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)});
387}392}
388pub fn fmtCTypePoolString(393pub fn fmtCTypePoolString(
389 ctype_pool_string: CType.Pool.String,394 ctype_pool_string: CType.Pool.String,
390 ctype_pool: *const CType.Pool,395 ctype_pool: *const CType.Pool,
391) std.fmt.Formatter(formatCTypePoolString) {396 solo: bool,
392 return .{ .data = .{ .ctype_pool_string = ctype_pool_string, .ctype_pool = ctype_pool } };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 } };
393}403}
394404
395// Returns true if `formatIdent` would make any edits to ident.405// Returns true if `formatIdent` would make any edits to ident.
...@@ -443,7 +453,7 @@ pub const Function = struct {...@@ -443,7 +453,7 @@ pub const Function = struct {
443 const ty = f.typeOf(ref);453 const ty = f.typeOf(ref);
444454
445 const result: CValue = if (lowersToArray(ty, pt)) result: {455 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;
447 const decl_c_value = try f.allocLocalValue(.{457 const decl_c_value = try f.allocLocalValue(.{
448 .ctype = try f.ctypeFromType(ty, .complete),458 .ctype = try f.ctypeFromType(ty, .complete),
449 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)),459 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)),
...@@ -599,8 +609,12 @@ pub const Function = struct {...@@ -599,8 +609,12 @@ pub const Function = struct {
599 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);609 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);
600 }610 }
601611
602 fn fmtIntLiteral(f: *Function, val: Value) !std.fmt.Formatter(formatIntLiteral) {612 fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
603 return f.object.dg.fmtIntLiteral(val, .Other);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);
604 }618 }
605619
606 fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 {620 fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 {
...@@ -619,14 +633,14 @@ pub const Function = struct {...@@ -619,14 +633,14 @@ pub const Function = struct {
619 .tag_name,633 .tag_name,
620 => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{634 => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
621 @tagName(key),635 @tagName(key),
622 fmtIdent(ip.loadEnumType(enum_ty).name.toSlice(ip)),636 fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)),
623 @intFromEnum(enum_ty),637 @intFromEnum(enum_ty),
624 }),638 }),
625 .never_tail,639 .never_tail,
626 .never_inline,640 .never_inline,
627 => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{641 => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
628 @tagName(key),642 @tagName(key),
629 fmtIdent(ip.getNav(owner_nav).name.toSlice(ip)),643 fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)),
630 @intFromEnum(owner_nav),644 @intFromEnum(owner_nav),
631 }),645 }),
632 },646 },
...@@ -662,7 +676,7 @@ pub const Function = struct {...@@ -662,7 +676,7 @@ pub const Function = struct {
662 },676 },
663 else => {},677 else => {},
664 }678 }
665 const w = &f.object.code.buffered_writer;679 const w = &f.object.code.writer;
666 const a = try Assignment.start(f, w, ctype);680 const a = try Assignment.start(f, w, ctype);
667 try f.writeCValue(w, dst, .Other);681 try f.writeCValue(w, dst, .Other);
668 try a.assign(f, w);682 try a.assign(f, w);
...@@ -704,7 +718,7 @@ pub const Object = struct {...@@ -704,7 +718,7 @@ pub const Object = struct {
704 const indent_char = ' ';718 const indent_char = ' ';
705719
706 fn newline(o: *Object) !void {720 fn newline(o: *Object) !void {
707 const w = &o.code.buffered_writer;721 const w = &o.code.writer;
708 try w.writeByte('\n');722 try w.writeByte('\n');
709 try w.splatByteAll(indent_char, o.indent_counter);723 try w.splatByteAll(indent_char, o.indent_counter);
710 }724 }
...@@ -716,9 +730,9 @@ pub const Object = struct {...@@ -716,9 +730,9 @@ pub const Object = struct {
716 const written = o.code.getWritten();730 const written = o.code.getWritten();
717 switch (written[written.len - 1]) {731 switch (written[written.len - 1]) {
718 indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width),732 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),
720 else => {734 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..])});
722 unreachable;736 unreachable;
723 },737 },
724 }738 }
...@@ -884,7 +898,7 @@ pub const DeclGen = struct {...@@ -884,7 +898,7 @@ pub const DeclGen = struct {
884 const addr_val = try pt.intValue(.usize, int.addr);898 const addr_val = try pt.intValue(.usize, int.addr);
885 try w.writeByte('(');899 try w.writeByte('(');
886 try dg.renderCType(w, ptr_ctype);900 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)});
888 },902 },
889903
890 .nav_ptr => |nav| try dg.renderNav(w, nav, location),904 .nav_ptr => |nav| try dg.renderNav(w, nav, location),
...@@ -924,7 +938,7 @@ pub const DeclGen = struct {...@@ -924,7 +938,7 @@ pub const DeclGen = struct {
924 const offset_val = try pt.intValue(.usize, byte_offset);938 const offset_val = try pt.intValue(.usize, byte_offset);
925 try w.writeAll("((char *)");939 try w.writeAll("((char *)");
926 try dg.renderPointer(w, field.parent.*, location);940 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)});
928 },942 },
929 }943 }
930 },944 },
...@@ -946,7 +960,7 @@ pub const DeclGen = struct {...@@ -946,7 +960,7 @@ pub const DeclGen = struct {
946 // The pointer already has an appropriate type - just do the arithmetic.960 // The pointer already has an appropriate type - just do the arithmetic.
947 try w.writeByte('(');961 try w.writeByte('(');
948 try dg.renderPointer(w, elem.parent.*, location);962 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)});
950 } else {964 } else {
951 // We probably have an array pointer `T (*)[n]`. Cast to an element pointer,965 // We probably have an array pointer `T (*)[n]`. Cast to an element pointer,
952 // and *then* apply the index.966 // and *then* apply the index.
...@@ -954,7 +968,7 @@ pub const DeclGen = struct {...@@ -954,7 +968,7 @@ pub const DeclGen = struct {
954 try dg.renderCType(w, result_ctype);968 try dg.renderCType(w, result_ctype);
955 try w.writeByte(')');969 try w.writeByte(')');
956 try dg.renderPointer(w, elem.parent.*, location);970 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)});
958 }972 }
959 },973 },
960974
...@@ -969,14 +983,14 @@ pub const DeclGen = struct {...@@ -969,14 +983,14 @@ pub const DeclGen = struct {
969 const offset_val = try pt.intValue(.usize, oac.byte_offset);983 const offset_val = try pt.intValue(.usize, oac.byte_offset);
970 try w.writeAll("((char *)");984 try w.writeAll("((char *)");
971 try dg.renderPointer(w, oac.parent.*, location);985 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)});
973 }987 }
974 },988 },
975 }989 }
976 }990 }
977991
978 fn renderErrorName(dg: *DeclGen, w: *Writer, err_name: InternPool.NullTerminatedString) !void {992 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))});
980 }994 }
981995
982 fn renderValue(996 fn renderValue(
...@@ -1040,11 +1054,11 @@ pub const DeclGen = struct {...@@ -1040,11 +1054,11 @@ pub const DeclGen = struct {
1040 .empty_enum_value,1054 .empty_enum_value,
1041 => unreachable, // non-runtime values1055 => unreachable, // non-runtime values
1042 .int => |int| switch (int.storage) {1056 .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)}),
1044 .lazy_align, .lazy_size => {1058 .lazy_align, .lazy_size => {
1045 try w.writeAll("((");1059 try w.writeAll("((");
1046 try dg.renderCType(w, ctype);1060 try dg.renderCType(w, ctype);
1047 try w.print("){fx})", .{try dg.fmtIntLiteral(1061 try w.print("){f})", .{try dg.fmtIntLiteralHex(
1048 try pt.intValue(.usize, val.toUnsignedInt(zcu)),1062 try pt.intValue(.usize, val.toUnsignedInt(zcu)),
1049 .Other,1063 .Other,
1050 )});1064 )});
...@@ -1173,7 +1187,7 @@ pub const DeclGen = struct {...@@ -1173,7 +1187,7 @@ pub const DeclGen = struct {
1173 try w.writeAll(", ");1187 try w.writeAll(", ");
1174 empty = false;1188 empty = false;
1175 }1189 }
1176 try w.print("{fx}", .{try dg.fmtIntLiteral(1190 try w.print("{f}", .{try dg.fmtIntLiteralHex(
1177 try pt.intValue_big(repr_ty, repr_val_big.toConst()),1191 try pt.intValue_big(repr_ty, repr_val_big.toConst()),
1178 location,1192 location,
1179 )});1193 )});
...@@ -1281,7 +1295,7 @@ pub const DeclGen = struct {...@@ -1281,7 +1295,7 @@ pub const DeclGen = struct {
1281 }1295 }
1282 const ai = ty.arrayInfo(zcu);1296 const ai = ty.arrayInfo(zcu);
1283 if (ai.elem_type.eql(.u8, zcu)) {1297 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)));
1285 try literal.start();1299 try literal.start();
1286 var index: usize = 0;1300 var index: usize = 0;
1287 while (index < ai.len) : (index += 1) {1301 while (index < ai.len) : (index += 1) {
...@@ -1562,7 +1576,7 @@ pub const DeclGen = struct {...@@ -1562,7 +1576,7 @@ pub const DeclGen = struct {
1562 .payload => {1576 .payload => {
1563 try w.writeByte('{');1577 try w.writeByte('{');
1564 if (field_ty.hasRuntimeBits(zcu)) {1578 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))});
1566 try dg.renderValue(1580 try dg.renderValue(
1567 w,1581 w,
1568 Value.fromInterned(un.val),1582 Value.fromInterned(un.val),
...@@ -1645,15 +1659,15 @@ pub const DeclGen = struct {...@@ -1645,15 +1659,15 @@ pub const DeclGen = struct {
1645 .enum_type,1659 .enum_type,
1646 .error_set_type,1660 .error_set_type,
1647 .inferred_error_set_type,1661 .inferred_error_set_type,
1648 => return w.print("{fx}", .{1662 => return w.print("{f}", .{
1649 try dg.fmtIntLiteral(try pt.undefValue(ty), location),1663 try dg.fmtIntLiteralHex(try pt.undefValue(ty), location),
1650 }),1664 }),
1651 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {1665 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1652 .one, .many, .c => {1666 .one, .many, .c => {
1653 try w.writeAll("((");1667 try w.writeAll("((");
1654 try dg.renderCType(w, ctype);1668 try dg.renderCType(w, ctype);
1655 return w.print("){fx})", .{1669 return w.print("){f})", .{
1656 try dg.fmtIntLiteral(.undef_usize, .Other),1670 try dg.fmtIntLiteralHex(.undef_usize, .Other),
1657 });1671 });
1658 },1672 },
1659 .slice => {1673 .slice => {
...@@ -1666,8 +1680,8 @@ pub const DeclGen = struct {...@@ -1666,8 +1680,8 @@ pub const DeclGen = struct {
1666 try w.writeAll("{(");1680 try w.writeAll("{(");
1667 const ptr_ty = ty.slicePtrFieldType(zcu);1681 const ptr_ty = ty.slicePtrFieldType(zcu);
1668 try dg.renderType(w, ptr_ty);1682 try dg.renderType(w, ptr_ty);
1669 return w.print("){fx}, {0fx}}}", .{1683 return w.print("){f}, {0f}}}", .{
1670 try dg.fmtIntLiteral(.undef_usize, .Other),1684 try dg.fmtIntLiteralHex(.undef_usize, .Other),
1671 });1685 });
1672 },1686 },
1673 },1687 },
...@@ -1730,8 +1744,8 @@ pub const DeclGen = struct {...@@ -1730,8 +1744,8 @@ pub const DeclGen = struct {
1730 }1744 }
1731 return w.writeByte('}');1745 return w.writeByte('}');
1732 },1746 },
1733 .@"packed" => return w.print("{fx}", .{1747 .@"packed" => return w.print("{f}", .{
1734 try dg.fmtIntLiteral(try pt.undefValue(ty), .Other),1748 try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other),
1735 }),1749 }),
1736 }1750 }
1737 },1751 },
...@@ -1800,8 +1814,8 @@ pub const DeclGen = struct {...@@ -1800,8 +1814,8 @@ pub const DeclGen = struct {
1800 }1814 }
1801 if (has_tag) try w.writeByte('}');1815 if (has_tag) try w.writeByte('}');
1802 },1816 },
1803 .@"packed" => return w.print("{fx}", .{1817 .@"packed" => return w.print("{f}", .{
1804 try dg.fmtIntLiteral(try pt.undefValue(ty), .Other),1818 try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other),
1805 }),1819 }),
1806 }1820 }
1807 },1821 },
...@@ -1840,7 +1854,7 @@ pub const DeclGen = struct {...@@ -1840,7 +1854,7 @@ pub const DeclGen = struct {
1840 const ai = ty.arrayInfo(zcu);1854 const ai = ty.arrayInfo(zcu);
1841 if (ai.elem_type.eql(.u8, zcu)) {1855 if (ai.elem_type.eql(.u8, zcu)) {
1842 const c_len = ty.arrayLenIncludingSentinel(zcu);1856 const c_len = ty.arrayLenIncludingSentinel(zcu);
1843 var literal: StringLiteral = .init(w, c_len);1857 var literal: StringLiteral = .init(w, @intCast(c_len));
1844 try literal.start();1858 try literal.start();
1845 var index: u64 = 0;1859 var index: u64 = 0;
1846 while (index < c_len) : (index += 1)1860 while (index < c_len) : (index += 1)
...@@ -1899,7 +1913,7 @@ pub const DeclGen = struct {...@@ -1899,7 +1913,7 @@ pub const DeclGen = struct {
1899 kind: CType.Kind,1913 kind: CType.Kind,
1900 name: union(enum) {1914 name: union(enum) {
1901 nav: InternPool.Nav.Index,1915 nav: InternPool.Nav.Index,
1902 fmt_ctype_pool_string: std.fmt.Formatter(formatCTypePoolString),1916 fmt_ctype_pool_string: std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString),
1903 @"export": struct {1917 @"export": struct {
1904 main_name: InternPool.NullTerminatedString,1918 main_name: InternPool.NullTerminatedString,
1905 extern_name: InternPool.NullTerminatedString,1919 extern_name: InternPool.NullTerminatedString,
...@@ -1943,8 +1957,8 @@ pub const DeclGen = struct {...@@ -1943,8 +1957,8 @@ pub const DeclGen = struct {
1943 try w.print("{f}", .{trailing});1957 try w.print("{f}", .{trailing});
1944 switch (name) {1958 switch (name) {
1945 .nav => |nav| try dg.renderNavName(w, nav),1959 .nav => |nav| try dg.renderNavName(w, nav),
1946 .fmt_ctype_pool_string => |fmt| try w.print("{f }", .{fmt}),1960 .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}),
1947 .@"export" => |@"export"| try w.print("{f }", .{fmtIdent(@"export".extern_name.toSlice(ip))}),1961 .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}),
1948 }1962 }
19491963
1950 try renderTypeSuffix(1964 try renderTypeSuffix(
...@@ -1971,17 +1985,17 @@ pub const DeclGen = struct {...@@ -1971,17 +1985,17 @@ pub const DeclGen = struct {
1971 const is_mangled = isMangledIdent(extern_name, true);1985 const is_mangled = isMangledIdent(extern_name, true);
1972 const is_export = @"export".extern_name != @"export".main_name;1986 const is_export = @"export".extern_name != @"export".main_name;
1973 if (is_mangled and is_export) {1987 if (is_mangled and is_export) {
1974 try w.print(" zig_mangled_export({f }, {fs}, {fs})", .{1988 try w.print(" zig_mangled_export({f}, {f}, {f})", .{
1975 fmtIdent(extern_name),1989 fmtIdentSolo(extern_name),
1976 fmtStringLiteral(extern_name, null),1990 fmtStringLiteral(extern_name, null),
1977 fmtStringLiteral(@"export".main_name.toSlice(ip), null),1991 fmtStringLiteral(@"export".main_name.toSlice(ip), null),
1978 });1992 });
1979 } else if (is_mangled) {1993 } else if (is_mangled) {
1980 try w.print(" zig_mangled({f }, {fs})", .{1994 try w.print(" zig_mangled({f}, {f})", .{
1981 fmtIdent(extern_name), fmtStringLiteral(extern_name, null),1995 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
1982 });1996 });
1983 } else if (is_export) {1997 } else if (is_export) {
1984 try w.print(" zig_export({fs}, {fs})", .{1998 try w.print(" zig_export({f}, {f})", .{
1985 fmtStringLiteral(@"export".main_name.toSlice(ip), null),1999 fmtStringLiteral(@"export".main_name.toSlice(ip), null),
1986 fmtStringLiteral(extern_name, null),2000 fmtStringLiteral(extern_name, null),
1987 });2001 });
...@@ -2129,7 +2143,7 @@ pub const DeclGen = struct {...@@ -2129,7 +2143,7 @@ pub const DeclGen = struct {
2129 } else if (dest_bits > 64 and src_bits <= 64) {2143 } else if (dest_bits > 64 and src_bits <= 64) {
2130 try w.writeAll("zig_make_");2144 try w.writeAll("zig_make_");
2131 try dg.renderTypeForBuiltinFnName(w, dest_ty);2145 try dg.renderTypeForBuiltinFnName(w, dest_ty);
2132 try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral?2146 try w.writeAll("(0, ");
2133 if (src_is_ptr) {2147 if (src_is_ptr) {
2134 try w.writeByte('(');2148 try w.writeByte('(');
2135 try dg.renderType(w, src_eff_ty);2149 try dg.renderType(w, src_eff_ty);
...@@ -2209,7 +2223,7 @@ pub const DeclGen = struct {...@@ -2209,7 +2223,7 @@ pub const DeclGen = struct {
2209 .new_local, .local => |i| try w.print("t{d}", .{i}),2223 .new_local, .local => |i| try w.print("t{d}", .{i}),
2210 .constant => |uav| try renderUavName(w, uav),2224 .constant => |uav| try renderUavName(w, uav),
2211 .nav => |nav| try dg.renderNavName(w, nav),2225 .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)}),
2213 else => unreachable,2227 else => unreachable,
2214 }2228 }
2215 }2229 }
...@@ -2226,13 +2240,13 @@ pub const DeclGen = struct {...@@ -2226,13 +2240,13 @@ pub const DeclGen = struct {
2226 try dg.renderNavName(w, nav);2240 try dg.renderNavName(w, nav);
2227 },2241 },
2228 .undef => |ty| try dg.renderUndefValue(w, ty, .Other),2242 .undef => |ty| try dg.renderUndefValue(w, ty, .Other),
2229 .identifier => |ident| try w.print("{f }", .{fmtIdent(ident)}),2243 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
2230 .payload_identifier => |ident| try w.print("{f }.{f }", .{2244 .payload_identifier => |ident| try w.print("{f}.{f}", .{
2231 fmtIdent("payload"),2245 fmtIdentSolo("payload"),
2232 fmtIdent(ident),2246 fmtIdentSolo(ident),
2233 }),2247 }),
2234 .ctype_pool_string => |string| try w.print("{f }", .{2248 .ctype_pool_string => |string| try w.print("{f}", .{
2235 fmtCTypePoolString(string, &dg.ctype_pool),2249 fmtCTypePoolString(string, &dg.ctype_pool, true),
2236 }),2250 }),
2237 }2251 }
2238 }2252 }
...@@ -2256,10 +2270,10 @@ pub const DeclGen = struct {...@@ -2256,10 +2270,10 @@ pub const DeclGen = struct {
2256 },2270 },
2257 .nav_ref => |nav| try dg.renderNavName(w, nav),2271 .nav_ref => |nav| try dg.renderNavName(w, nav),
2258 .undef => unreachable,2272 .undef => unreachable,
2259 .identifier => |ident| try w.print("(*{f })", .{fmtIdent(ident)}),2273 .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}),
2260 .payload_identifier => |ident| try w.print("(*{f }.{f })", .{2274 .payload_identifier => |ident| try w.print("(*{f}.{f})", .{
2261 fmtIdent("payload"),2275 fmtIdentSolo("payload"),
2262 fmtIdent(ident),2276 fmtIdentSolo(ident),
2263 }),2277 }),
2264 }2278 }
2265 }2279 }
...@@ -2318,7 +2332,7 @@ pub const DeclGen = struct {...@@ -2318,7 +2332,7 @@ pub const DeclGen = struct {
2318 const zcu = dg.pt.zcu;2332 const zcu = dg.pt.zcu;
2319 const ip = &zcu.intern_pool;2333 const ip = &zcu.intern_pool;
2320 const nav = ip.getNav(nav_index);2334 const nav = ip.getNav(nav_index);
2321 const fwd = &dg.fwd_decl.buffered_writer;2335 const fwd = &dg.fwd_decl.writer;
2322 try fwd.writeAll(switch (flags.linkage) {2336 try fwd.writeAll(switch (flags.linkage) {
2323 .internal => "static ",2337 .internal => "static ",
2324 .strong, .weak, .link_once => "zig_extern ",2338 .strong, .weak, .link_once => "zig_extern ",
...@@ -2349,15 +2363,15 @@ pub const DeclGen = struct {...@@ -2349,15 +2363,15 @@ pub const DeclGen = struct {
2349 const ip = &zcu.intern_pool;2363 const ip = &zcu.intern_pool;
2350 const nav = ip.getNav(nav_index);2364 const nav = ip.getNav(nav_index);
2351 if (nav.getExtern(ip)) |@"extern"| {2365 if (nav.getExtern(ip)) |@"extern"| {
2352 try w.print("{f }", .{2366 try w.print("{f}", .{
2353 fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),2367 fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
2354 });2368 });
2355 } else {2369 } else {
2356 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),2370 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
2357 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.2371 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
2358 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);2372 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
2359 try w.print("{f}__{d}", .{2373 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)]),
2361 @intFromEnum(nav_index),2375 @intFromEnum(nav_index),
2362 });2376 });
2363 }2377 }
...@@ -2406,7 +2420,7 @@ pub const DeclGen = struct {...@@ -2406,7 +2420,7 @@ pub const DeclGen = struct {
2406 };2420 };
24072421
2408 if (is_big) try w.print(", {}", .{int_info.signedness == .signed});2422 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(
2410 try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),2424 try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),
2411 .FunctionArgument,2425 .FunctionArgument,
2412 )});2426 )});
...@@ -2416,18 +2430,38 @@ pub const DeclGen = struct {...@@ -2416,18 +2430,38 @@ pub const DeclGen = struct {
2416 dg: *DeclGen,2430 dg: *DeclGen,
2417 val: Value,2431 val: Value,
2418 loc: ValueRenderLocation,2432 loc: ValueRenderLocation,
2419 ) !std.fmt.Formatter(formatIntLiteral) {2433 base: u8,
2434 case: std.fmt.Case,
2435 ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
2420 const zcu = dg.pt.zcu;2436 const zcu = dg.pt.zcu;
2421 const kind = loc.toCTypeKind();2437 const kind = loc.toCTypeKind();
2422 const ty = val.typeOf(zcu);2438 const ty = val.typeOf(zcu);
2423 return std.fmt.Formatter(formatIntLiteral){ .data = .{2439 return .{ .data = .{
2424 .dg = dg,2440 .dg = dg,
2425 .int_info = ty.intInfo(zcu),2441 .int_info = ty.intInfo(zcu),
2426 .kind = kind,2442 .kind = kind,
2427 .ctype = try dg.ctypeFromType(ty, kind),2443 .ctype = try dg.ctypeFromType(ty, kind),
2428 .val = val,2444 .val = val,
2445 .base = base,
2446 .case = case,
2429 } };2447 } };
2430 }2448 }
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 }
2431};2465};
24322466
2433const CTypeFix = enum { prefix, suffix };2467const CTypeFix = enum { prefix, suffix };
...@@ -2437,13 +2471,7 @@ const RenderCTypeTrailing = enum {...@@ -2437,13 +2471,7 @@ const RenderCTypeTrailing = enum {
2437 no_space,2471 no_space,
2438 maybe_space,2472 maybe_space,
24392473
2440 pub fn format(2474 pub fn format(self: @This(), w: *Writer) Writer.Error!void {
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()) ++ "'");
2447 switch (self) {2475 switch (self) {
2448 .no_space => {},2476 .no_space => {},
2449 .maybe_space => try w.writeByte(' '),2477 .maybe_space => try w.writeByte(' '),
...@@ -2465,7 +2493,7 @@ fn renderFwdDeclTypeName(...@@ -2465,7 +2493,7 @@ fn renderFwdDeclTypeName(
2465 switch (fwd_decl.name) {2493 switch (fwd_decl.name) {
2466 .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}),2494 .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}),
2467 .index => |index| try w.print("{f}__{d}", .{2495 .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)),
2469 @intFromEnum(index),2497 @intFromEnum(index),
2470 }),2498 }),
2471 }2499 }
...@@ -2679,7 +2707,7 @@ fn renderFields(...@@ -2679,7 +2707,7 @@ fn renderFields(
2679 .suffix,2707 .suffix,
2680 .{},2708 .{},
2681 );2709 );
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) });
2683 try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{});2711 try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{});
2684 try w.writeAll(";\n");2712 try w.writeAll(";\n");
2685 }2713 }
...@@ -2771,7 +2799,7 @@ pub fn genTypeDecl(...@@ -2771,7 +2799,7 @@ pub fn genTypeDecl(
27712799
2772pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {2800pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {
2773 for (zcu.global_assembly.values()) |asm_source| {2801 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)});
2775 }2803 }
2776}2804}
27772805
...@@ -2779,7 +2807,7 @@ pub fn genErrDecls(o: *Object) Error!void {...@@ -2779,7 +2807,7 @@ pub fn genErrDecls(o: *Object) Error!void {
2779 const pt = o.dg.pt;2807 const pt = o.dg.pt;
2780 const zcu = pt.zcu;2808 const zcu = pt.zcu;
2781 const ip = &zcu.intern_pool;2809 const ip = &zcu.intern_pool;
2782 const w = &o.code.buffered_writer;2810 const w = &o.code.writer;
27832811
2784 var max_name_len: usize = 0;2812 var max_name_len: usize = 0;
2785 // do not generate an invalid empty enum when the global error set is empty2813 // 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 {...@@ -2858,8 +2886,8 @@ pub fn genErrDecls(o: *Object) Error!void {
2858 const name = name_nts.toSlice(ip);2886 const name = name_nts.toSlice(ip);
2859 if (val > 1) try w.writeAll(", ");2887 if (val > 1) try w.writeAll(", ");
2860 try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{2888 try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{
2861 fmtIdent(name),2889 fmtIdentUnsolo(name),
2862 try o.dg.fmtIntLiteral(try pt.intValue(.usize, name.len), .StaticInitializer),2890 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer),
2863 });2891 });
2864 }2892 }
2865 try w.writeAll("};");2893 try w.writeAll("};");
...@@ -2871,7 +2899,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn...@@ -2871,7 +2899,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
2871 const zcu = pt.zcu;2899 const zcu = pt.zcu;
2872 const ip = &zcu.intern_pool;2900 const ip = &zcu.intern_pool;
2873 const ctype_pool = &o.dg.ctype_pool;2901 const ctype_pool = &o.dg.ctype_pool;
2874 const w = &o.code.buffered_writer;2902 const w = &o.code.writer;
2875 const key = lazy_fn.key_ptr.*;2903 const key = lazy_fn.key_ptr.*;
2876 const val = lazy_fn.value_ptr;2904 const val = lazy_fn.value_ptr;
2877 switch (key) {2905 switch (key) {
...@@ -2906,7 +2934,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn...@@ -2906,7 +2934,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
2906 } });2934 } });
29072935
2908 try w.print("case {f}: {{", .{2936 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),
2910 });2938 });
2911 o.indent();2939 o.indent();
2912 try o.newline();2940 try o.newline();
...@@ -2919,8 +2947,8 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn...@@ -2919,8 +2947,8 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
2919 try w.writeAll("return (");2947 try w.writeAll("return (");
2920 try o.dg.renderType(w, name_slice_ty);2948 try o.dg.renderType(w, name_slice_ty);
2921 try w.print("){{{f}, {f}}};", .{2949 try w.print("){{{f}, {f}}};", .{
2922 fmtIdent("name"),2950 fmtIdentUnsolo("name"),
2923 try o.dg.fmtIntLiteral(try pt.intValue(.usize, tag_name_len), .Other),2951 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other),
2924 });2952 });
2925 try o.newline();2953 try o.newline();
2926 try o.outdent();2954 try o.outdent();
...@@ -2939,9 +2967,9 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn...@@ -2939,9 +2967,9 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
2939 const fn_val = zcu.navValue(fn_nav_index);2967 const fn_val = zcu.navValue(fn_nav_index);
2940 const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete);2968 const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete);
2941 const fn_info = fn_ctype.info(ctype_pool).function;2969 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;
2945 try fwd.print("static zig_{s} ", .{@tagName(key)});2973 try fwd.print("static zig_{s} ", .{@tagName(key)});
2946 try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{2974 try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
2947 .fmt_ctype_pool_string = fn_name,2975 .fmt_ctype_pool_string = fn_name,
...@@ -3001,20 +3029,20 @@ pub fn generate(...@@ -3001,20 +3029,20 @@ pub fn generate(
3001 .pass = .{ .nav = func.owner_nav },3029 .pass = .{ .nav = func.owner_nav },
3002 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,3030 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
3003 .expected_block = null,3031 .expected_block = null,
3004 .fwd_decl = undefined,3032 .fwd_decl = .init(gpa),
3005 .ctype_pool = .empty,3033 .ctype_pool = .empty,
3006 .scratch = .empty,3034 .scratch = .empty,
3007 .uavs = .empty,3035 .uavs = .empty,
3008 },3036 },
3009 .code_header = undefined,3037 .code_header = .init(gpa),
3010 .code = undefined,3038 .code = .init(gpa),
3011 .indent_counter = 0,3039 .indent_counter = 0,
3012 },3040 },
3013 .lazy_fns = .empty,3041 .lazy_fns = .empty,
3014 };3042 };
3015 defer {3043 defer {
3016 function.object.code_header.init(gpa);3044 function.object.code_header.deinit();
3017 function.object.code.init(gpa);3045 function.object.code.deinit();
3018 function.object.dg.fwd_decl.deinit();3046 function.object.dg.fwd_decl.deinit();
3019 function.object.dg.ctype_pool.deinit(gpa);3047 function.object.dg.ctype_pool.deinit(gpa);
3020 function.object.dg.scratch.deinit(gpa);3048 function.object.dg.scratch.deinit(gpa);
...@@ -3022,18 +3050,17 @@ pub fn generate(...@@ -3022,18 +3050,17 @@ pub fn generate(
3022 function.deinit();3050 function.deinit();
3023 }3051 }
3024 try function.object.dg.ctype_pool.init(gpa);3052 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
3029 genFunc(&function) catch |err| switch (err) {3054 genFunc(&function) catch |err| switch (err) {
3030 error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?),3055 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,
3032 };3058 };
30333059
3034 var mir: Mir = .{3060 var mir: Mir = .{
3035 .uavs = .empty,3061 .uavs = .empty,
3036 .code = &.{},3062 .code = &.{},
3063 .code_header = &.{},
3037 .fwd_decl = &.{},3064 .fwd_decl = &.{},
3038 .ctype_pool = .empty,3065 .ctype_pool = .empty,
3039 .lazy_fns = .empty,3066 .lazy_fns = .empty,
...@@ -3060,7 +3087,7 @@ pub fn genFunc(f: *Function) Error!void {...@@ -3060,7 +3087,7 @@ pub fn genFunc(f: *Function) Error!void {
3060 const nav_val = zcu.navValue(nav_index);3087 const nav_val = zcu.navValue(nav_index);
3061 const nav = ip.getNav(nav_index);3088 const nav = ip.getNav(nav_index);
30623089
3063 const fwd = &o.dg.fwd_decl.buffered_writer;3090 const fwd = &o.dg.fwd_decl.writer;
3064 try fwd.writeAll("static ");3091 try fwd.writeAll("static ");
3065 try o.dg.renderFunctionSignature(3092 try o.dg.renderFunctionSignature(
3066 fwd,3093 fwd,
...@@ -3071,9 +3098,9 @@ pub fn genFunc(f: *Function) Error!void {...@@ -3071,9 +3098,9 @@ pub fn genFunc(f: *Function) Error!void {
3071 );3098 );
3072 try fwd.writeAll(";\n");3099 try fwd.writeAll(";\n");
30733100
3074 const ch = &o.code_header.buffered_writer;3101 const ch = &o.code_header.writer;
3075 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|3102 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)});
3077 try o.dg.renderFunctionSignature(3104 try o.dg.renderFunctionSignature(
3078 ch,3105 ch,
3079 nav_val,3106 nav_val,
...@@ -3089,7 +3116,7 @@ pub fn genFunc(f: *Function) Error!void {...@@ -3089,7 +3116,7 @@ pub fn genFunc(f: *Function) Error!void {
3089 o.indent();3116 o.indent();
3090 try genBodyResolveState(f, undefined, &.{}, main_body, true);3117 try genBodyResolveState(f, undefined, &.{}, main_body, true);
3091 try o.outdent();3118 try o.outdent();
3092 try o.code.buffered_writer.writeByte('}');3119 try o.code.writer.writeByte('}');
3093 try o.newline();3120 try o.newline();
3094 if (o.dg.expected_block) |_|3121 if (o.dg.expected_block) |_|
3095 return f.fail("runtime code not allowed in naked function", .{});3122 return f.fail("runtime code not allowed in naked function", .{});
...@@ -3150,7 +3177,7 @@ pub fn genDecl(o: *Object) Error!void {...@@ -3150,7 +3177,7 @@ pub fn genDecl(o: *Object) Error!void {
3150 .visibility = @"extern".visibility,3177 .visibility = @"extern".visibility,
3151 });3178 });
31523179
3153 const fwd = &o.dg.fwd_decl.buffered_writer;3180 const fwd = &o.dg.fwd_decl.writer;
3154 try fwd.writeAll("zig_extern ");3181 try fwd.writeAll("zig_extern ");
3155 try o.dg.renderFunctionSignature(3182 try o.dg.renderFunctionSignature(
3156 fwd,3183 fwd,
...@@ -3171,10 +3198,10 @@ pub fn genDecl(o: *Object) Error!void {...@@ -3171,10 +3198,10 @@ pub fn genDecl(o: *Object) Error!void {
3171 .linkage = .internal,3198 .linkage = .internal,
3172 .visibility = .default,3199 .visibility = .default,
3173 });3200 });
3174 const w = &o.code.buffered_writer;3201 const w = &o.code.writer;
3175 if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");3202 if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
3176 if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|3203 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)});
3178 try o.dg.renderTypeAndName(3205 try o.dg.renderTypeAndName(
3179 w,3206 w,
3180 nav_ty,3207 nav_ty,
...@@ -3208,14 +3235,14 @@ pub fn genDeclValue(...@@ -3208,14 +3235,14 @@ pub fn genDeclValue(
3208 const zcu = o.dg.pt.zcu;3235 const zcu = o.dg.pt.zcu;
3209 const ty = val.typeOf(zcu);3236 const ty = val.typeOf(zcu);
32103237
3211 const fwd = &o.dg.fwd_decl.buffered_writer;3238 const fwd = &o.dg.fwd_decl.writer;
3212 try fwd.writeAll("static ");3239 try fwd.writeAll("static ");
3213 try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete);3240 try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete);
3214 try fwd.writeAll(";\n");3241 try fwd.writeAll(";\n");
32153242
3216 const w = &o.code.buffered_writer;3243 const w = &o.code.writer;
3217 if (@"linksection".toSlice(&zcu.intern_pool)) |s|3244 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)});
3219 try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete);3246 try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete);
3220 try w.writeAll(" = ");3247 try w.writeAll(" = ");
3221 try o.dg.renderValue(w, val, .StaticInitializer);3248 try o.dg.renderValue(w, val, .StaticInitializer);
...@@ -3226,7 +3253,7 @@ pub fn genDeclValue(...@@ -3226,7 +3253,7 @@ pub fn genDeclValue(
3226pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {3253pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {
3227 const zcu = dg.pt.zcu;3254 const zcu = dg.pt.zcu;
3228 const ip = &zcu.intern_pool;3255 const ip = &zcu.intern_pool;
3229 const fwd = &dg.fwd_decl.buffered_writer;3256 const fwd = &dg.fwd_decl.writer;
32303257
3231 const main_name = export_indices[0].ptr(zcu).opts.name;3258 const main_name = export_indices[0].ptr(zcu).opts.name;
3232 try fwd.writeAll("#define ");3259 try fwd.writeAll("#define ");
...@@ -3235,7 +3262,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3235,7 +3262,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3235 .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)),3262 .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)),
3236 }3263 }
3237 try fwd.writeByte(' ');3264 try fwd.writeByte(' ');
3238 try fwd.print("{f }", .{fmtIdent(main_name.toSlice(ip))});3265 try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});
3239 try fwd.writeByte('\n');3266 try fwd.writeByte('\n');
32403267
3241 const exported_val = exported.getValue(zcu);3268 const exported_val = exported.getValue(zcu);
...@@ -3265,7 +3292,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3265,7 +3292,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3265 const @"export" = export_index.ptr(zcu);3292 const @"export" = export_index.ptr(zcu);
3266 try fwd.writeAll("zig_extern ");3293 try fwd.writeAll("zig_extern ");
3267 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");3294 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}) ", .{
3269 fmtStringLiteral(s, null),3296 fmtStringLiteral(s, null),
3270 });3297 });
3271 const extern_name = @"export".opts.name.toSlice(ip);3298 const extern_name = @"export".opts.name.toSlice(ip);
...@@ -3280,17 +3307,17 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3280,17 +3307,17 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3280 .complete,3307 .complete,
3281 );3308 );
3282 if (is_mangled and is_export) {3309 if (is_mangled and is_export) {
3283 try fwd.print(" zig_mangled_export({f }, {fs}, {fs})", .{3310 try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{
3284 fmtIdent(extern_name),3311 fmtIdentSolo(extern_name),
3285 fmtStringLiteral(extern_name, null),3312 fmtStringLiteral(extern_name, null),
3286 fmtStringLiteral(main_name.toSlice(ip), null),3313 fmtStringLiteral(main_name.toSlice(ip), null),
3287 });3314 });
3288 } else if (is_mangled) {3315 } else if (is_mangled) {
3289 try fwd.print(" zig_mangled({f }, {fs})", .{3316 try fwd.print(" zig_mangled({f}, {f})", .{
3290 fmtIdent(extern_name), fmtStringLiteral(extern_name, null),3317 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
3291 });3318 });
3292 } else if (is_export) {3319 } else if (is_export) {
3293 try fwd.print(" zig_export({fs}, {fs})", .{3320 try fwd.print(" zig_export({f}, {f})", .{
3294 fmtStringLiteral(main_name.toSlice(ip), null),3321 fmtStringLiteral(main_name.toSlice(ip), null),
3295 fmtStringLiteral(extern_name, null),3322 fmtStringLiteral(extern_name, null),
3296 });3323 });
...@@ -3304,7 +3331,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3304,7 +3331,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3304/// have been added to `free_locals_map`. For a version of this function that restores this state,3331/// have been added to `free_locals_map`. For a version of this function that restores this state,
3305/// see `genBodyResolveState`.3332/// see `genBodyResolveState`.
3306fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {3333fn 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;
3308 if (body.len == 0) {3335 if (body.len == 0) {
3309 try w.writeAll("{}");3336 try w.writeAll("{}");
3310 } else {3337 } else {
...@@ -3326,7 +3353,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -3326,7 +3353,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
3326fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void {3353fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void {
3327 if (body.len == 0) {3354 if (body.len == 0) {
3328 // Don't go to the expense of cloning everything!3355 // 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("{}");
3330 return;3357 return;
3331 }3358 }
33323359
...@@ -3643,7 +3670,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -3643,7 +3670,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
3643 .ret => return airRet(f, inst, false),3670 .ret => return airRet(f, inst, false),
3644 .ret_safe => return airRet(f, inst, false), // TODO3671 .ret_safe => return airRet(f, inst, false), // TODO
3645 .ret_load => return airRet(f, inst, true),3672 .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),
3647 .unreach => return airUnreach(&f.object),3674 .unreach => return airUnreach(&f.object),
36483675
3649 // Instructions which may be `noreturn`.3676 // Instructions which may be `noreturn`.
...@@ -3687,7 +3714,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [...@@ -3687,7 +3714,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
3687 const operand = try f.resolveInst(ty_op.operand);3714 const operand = try f.resolveInst(ty_op.operand);
3688 try reap(f, inst, &.{ty_op.operand});3715 try reap(f, inst, &.{ty_op.operand});
36893716
3690 const w = &f.object.code.buffered_writer;3717 const w = &f.object.code.writer;
3691 const local = try f.allocLocal(inst, inst_ty);3718 const local = try f.allocLocal(inst, inst_ty);
3692 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3719 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3693 try f.writeCValue(w, local, .Other);3720 try f.writeCValue(w, local, .Other);
...@@ -3713,7 +3740,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3713,7 +3740,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3713 const index = try f.resolveInst(bin_op.rhs);3740 const index = try f.resolveInst(bin_op.rhs);
3714 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3741 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;
3717 const local = try f.allocLocal(inst, inst_ty);3744 const local = try f.allocLocal(inst, inst_ty);
3718 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3745 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3719 try f.writeCValue(w, local, .Other);3746 try f.writeCValue(w, local, .Other);
...@@ -3740,7 +3767,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3740,7 +3767,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3740 const index = try f.resolveInst(bin_op.rhs);3767 const index = try f.resolveInst(bin_op.rhs);
3741 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3768 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;
3744 const local = try f.allocLocal(inst, inst_ty);3771 const local = try f.allocLocal(inst, inst_ty);
3745 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3772 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3746 try f.writeCValue(w, local, .Other);3773 try f.writeCValue(w, local, .Other);
...@@ -3775,7 +3802,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3775,7 +3802,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3775 const index = try f.resolveInst(bin_op.rhs);3802 const index = try f.resolveInst(bin_op.rhs);
3776 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3803 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;
3779 const local = try f.allocLocal(inst, inst_ty);3806 const local = try f.allocLocal(inst, inst_ty);
3780 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3807 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3781 try f.writeCValue(w, local, .Other);3808 try f.writeCValue(w, local, .Other);
...@@ -3803,7 +3830,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3803,7 +3830,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3803 const index = try f.resolveInst(bin_op.rhs);3830 const index = try f.resolveInst(bin_op.rhs);
3804 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3831 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;
3807 const local = try f.allocLocal(inst, inst_ty);3834 const local = try f.allocLocal(inst, inst_ty);
3808 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3835 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3809 try f.writeCValue(w, local, .Other);3836 try f.writeCValue(w, local, .Other);
...@@ -3832,7 +3859,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3832,7 +3859,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3832 const index = try f.resolveInst(bin_op.rhs);3859 const index = try f.resolveInst(bin_op.rhs);
3833 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3860 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;
3836 const local = try f.allocLocal(inst, inst_ty);3863 const local = try f.allocLocal(inst, inst_ty);
3837 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3864 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3838 try f.writeCValue(w, local, .Other);3865 try f.writeCValue(w, local, .Other);
...@@ -3895,7 +3922,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3895,7 +3922,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
3895 .{ .arg_array = i };3922 .{ .arg_array = i };
38963923
3897 if (f.liveness.isUnused(inst)) {3924 if (f.liveness.isUnused(inst)) {
3898 const w = &f.object.code.buffered_writer;3925 const w = &f.object.code.writer;
3899 try w.writeByte('(');3926 try w.writeByte('(');
3900 try f.renderType(w, .void);3927 try f.renderType(w, .void);
3901 try w.writeByte(')');3928 try w.writeByte(')');
...@@ -3934,7 +3961,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3934,7 +3961,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3934 const is_array = lowersToArray(src_ty, pt);3961 const is_array = lowersToArray(src_ty, pt);
3935 const need_memcpy = !is_aligned or is_array;3962 const need_memcpy = !is_aligned or is_array;
39363963
3937 const w = &f.object.code.buffered_writer;3964 const w = &f.object.code.writer;
3938 const local = try f.allocLocal(inst, src_ty);3965 const local = try f.allocLocal(inst, src_ty);
3939 const v = try Vectorize.start(f, inst, w, ptr_ty);3966 const v = try Vectorize.start(f, inst, w, ptr_ty);
39403967
...@@ -3979,7 +4006,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3979,7 +4006,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3979 try w.writeByte('(');4006 try w.writeByte('(');
3980 try f.writeCValueDeref(w, operand);4007 try f.writeCValueDeref(w, operand);
3981 try v.elem(f, w);4008 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)});
3983 if (cant_cast) try w.writeByte(')');4010 if (cant_cast) try w.writeByte(')');
3984 try f.object.dg.renderBuiltinInfo(w, field_ty, .bits);4011 try f.object.dg.renderBuiltinInfo(w, field_ty, .bits);
3985 try w.writeByte(')');4012 try w.writeByte(')');
...@@ -4001,7 +4028,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {...@@ -4001,7 +4028,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
4001 const pt = f.object.dg.pt;4028 const pt = f.object.dg.pt;
4002 const zcu = pt.zcu;4029 const zcu = pt.zcu;
4003 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4030 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;
4005 const op_inst = un_op.toIndex();4032 const op_inst = un_op.toIndex();
4006 const op_ty = f.typeOf(un_op);4033 const op_ty = f.typeOf(un_op);
4007 const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty;4034 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 {...@@ -4040,7 +4067,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
4040 try f.writeCValueDeref(w, ret_val)4067 try f.writeCValueDeref(w, ret_val)
4041 else4068 else
4042 try f.writeCValue(w, ret_val, .Other);4069 try f.writeCValue(w, ret_val, .Other);
4043 try w.write(";\n");4070 try w.writeAll(";\n");
4044 if (is_array) {4071 if (is_array) {
4045 try freeLocal(f, inst, ret_val.new_local, null);4072 try freeLocal(f, inst, ret_val.new_local, null);
4046 }4073 }
...@@ -4066,7 +4093,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4066,7 +4093,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
40664093
4067 if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand);4094 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;
4070 const local = try f.allocLocal(inst, inst_ty);4097 const local = try f.allocLocal(inst, inst_ty);
4071 const v = try Vectorize.start(f, inst, w, operand_ty);4098 const v = try Vectorize.start(f, inst, w, operand_ty);
4072 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));4099 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 {...@@ -4102,7 +4129,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
4102 const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);4129 const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);
4103 if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand);4130 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;
4106 const local = try f.allocLocal(inst, inst_ty);4133 const local = try f.allocLocal(inst, inst_ty);
4107 const v = try Vectorize.start(f, inst, w, operand_ty);4134 const v = try Vectorize.start(f, inst, w, operand_ty);
4108 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete));4135 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 {...@@ -4129,8 +4156,8 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
4129 try w.writeByte('(');4156 try w.writeByte('(');
4130 try f.writeCValue(w, operand, .FunctionArgument);4157 try f.writeCValue(w, operand, .FunctionArgument);
4131 try v.elem(f, w);4158 try v.elem(f, w);
4132 try w.print(", {fx})", .{4159 try w.print(", {f})", .{
4133 try f.fmtIntLiteral(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),4160 try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),
4134 });4161 });
4135 },4162 },
4136 .signed => {4163 .signed => {
...@@ -4154,9 +4181,9 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4154,9 +4181,9 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
4154 try f.writeCValue(w, operand, .FunctionArgument);4181 try f.writeCValue(w, operand, .FunctionArgument);
4155 try v.elem(f, w);4182 try v.elem(f, w);
4156 if (c_bits == 128) try w.writeByte(')');4183 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)});
4158 if (c_bits == 128) try w.writeByte(')');4185 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)});
4160 },4187 },
4161 }4188 }
4162 if (need_lo) try w.writeByte(')');4189 if (need_lo) try w.writeByte(')');
...@@ -4180,7 +4207,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -4180,7 +4207,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
41804207
4181 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndefDeep(zcu) else false;4208 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;
4184 if (val_is_undef) {4211 if (val_is_undef) {
4185 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });4212 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
4186 if (safety and ptr_info.packed_offset.host_size == 0) {4213 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 {...@@ -4273,7 +4300,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4273 try w.writeByte('(');4300 try w.writeByte('(');
4274 try f.writeCValueDeref(w, ptr_val);4301 try f.writeCValueDeref(w, ptr_val);
4275 try v.elem(f, w);4302 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)});
4277 try f.object.dg.renderTypeForBuiltinFnName(w, host_ty);4304 try f.object.dg.renderTypeForBuiltinFnName(w, host_ty);
4278 try w.writeByte('(');4305 try w.writeByte('(');
4279 const cant_cast = host_ty.isInt(zcu) and host_ty.bitSize(zcu) > 64;4306 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 {...@@ -4296,7 +4323,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4296 try f.writeCValue(w, src_val, .Other);4323 try f.writeCValue(w, src_val, .Other);
4297 try v.elem(f, w);4324 try v.elem(f, w);
4298 if (cant_cast) try w.writeByte(')');4325 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)});
4300 try a.end(f, w);4327 try a.end(f, w);
4301 try v.end(f, inst, w);4328 try v.end(f, inst, w);
4302 } else {4329 } else {
...@@ -4335,7 +4362,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:...@@ -4335,7 +4362,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
4335 const operand_ty = f.typeOf(bin_op.lhs);4362 const operand_ty = f.typeOf(bin_op.lhs);
4336 const scalar_ty = operand_ty.scalarType(zcu);4363 const scalar_ty = operand_ty.scalarType(zcu);
43374364
4338 const w = &f.object.code.buffered_writer;4365 const w = &f.object.code.writer;
4339 const local = try f.allocLocal(inst, inst_ty);4366 const local = try f.allocLocal(inst, inst_ty);
4340 const v = try Vectorize.start(f, inst, w, operand_ty);4367 const v = try Vectorize.start(f, inst, w, operand_ty);
4341 try f.writeCValueMember(w, local, .{ .field = 1 });4368 try f.writeCValueMember(w, local, .{ .field = 1 });
...@@ -4374,7 +4401,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4374,7 +4401,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
43744401
4375 const inst_ty = f.typeOfIndex(inst);4402 const inst_ty = f.typeOfIndex(inst);
43764403
4377 const w = &f.object.code.buffered_writer;4404 const w = &f.object.code.writer;
4378 const local = try f.allocLocal(inst, inst_ty);4405 const local = try f.allocLocal(inst, inst_ty);
4379 const v = try Vectorize.start(f, inst, w, operand_ty);4406 const v = try Vectorize.start(f, inst, w, operand_ty);
4380 try f.writeCValue(w, local, .Other);4407 try f.writeCValue(w, local, .Other);
...@@ -4411,7 +4438,7 @@ fn airBinOp(...@@ -4411,7 +4438,7 @@ fn airBinOp(
44114438
4412 const inst_ty = f.typeOfIndex(inst);4439 const inst_ty = f.typeOfIndex(inst);
44134440
4414 const w = &f.object.code.buffered_writer;4441 const w = &f.object.code.writer;
4415 const local = try f.allocLocal(inst, inst_ty);4442 const local = try f.allocLocal(inst, inst_ty);
4416 const v = try Vectorize.start(f, inst, w, operand_ty);4443 const v = try Vectorize.start(f, inst, w, operand_ty);
4417 try f.writeCValue(w, local, .Other);4444 try f.writeCValue(w, local, .Other);
...@@ -4462,7 +4489,7 @@ fn airCmpOp(...@@ -4462,7 +4489,7 @@ fn airCmpOp(
44624489
4463 const rhs_ty = f.typeOf(data.rhs);4490 const rhs_ty = f.typeOf(data.rhs);
4464 const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu);4491 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;
4466 const local = try f.allocLocal(inst, inst_ty);4493 const local = try f.allocLocal(inst, inst_ty);
4467 const v = try Vectorize.start(f, inst, w, lhs_ty);4494 const v = try Vectorize.start(f, inst, w, lhs_ty);
4468 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));4495 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
...@@ -4515,7 +4542,7 @@ fn airEquality(...@@ -4515,7 +4542,7 @@ fn airEquality(
4515 const rhs = try f.resolveInst(bin_op.rhs);4542 const rhs = try f.resolveInst(bin_op.rhs);
4516 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });4543 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;
4519 const local = try f.allocLocal(inst, .bool);4546 const local = try f.allocLocal(inst, .bool);
4520 const a = try Assignment.start(f, w, .bool);4547 const a = try Assignment.start(f, w, .bool);
4521 try f.writeCValue(w, local, .Other);4548 try f.writeCValue(w, local, .Other);
...@@ -4573,12 +4600,12 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4573,12 +4600,12 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
4573 const operand = try f.resolveInst(un_op);4600 const operand = try f.resolveInst(un_op);
4574 try reap(f, inst, &.{un_op});4601 try reap(f, inst, &.{un_op});
45754602
4576 const w = &f.object.code.buffered_writer;4603 const w = &f.object.code.writer;
4577 const local = try f.allocLocal(inst, .bool);4604 const local = try f.allocLocal(inst, .bool);
4578 try f.writeCValue(w, local, .Other);4605 try f.writeCValue(w, local, .Other);
4579 try w.writeAll(" = ");4606 try w.writeAll(" = ");
4580 try f.writeCValue(w, operand, .Other);4607 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")});
4582 try f.object.newline();4609 try f.object.newline();
4583 return local;4610 return local;
4584}4611}
...@@ -4600,7 +4627,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {...@@ -4600,7 +4627,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
4600 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);4627 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
46014628
4602 const local = try f.allocLocal(inst, inst_ty);4629 const local = try f.allocLocal(inst, inst_ty);
4603 const w = &f.object.code.buffered_writer;4630 const w = &f.object.code.writer;
4604 const v = try Vectorize.start(f, inst, w, inst_ty);4631 const v = try Vectorize.start(f, inst, w, inst_ty);
4605 const a = try Assignment.start(f, w, inst_scalar_ctype);4632 const a = try Assignment.start(f, w, inst_scalar_ctype);
4606 try f.writeCValue(w, local, .Other);4633 try f.writeCValue(w, local, .Other);
...@@ -4642,7 +4669,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons...@@ -4642,7 +4669,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
4642 const rhs = try f.resolveInst(bin_op.rhs);4669 const rhs = try f.resolveInst(bin_op.rhs);
4643 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });4670 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;
4646 const local = try f.allocLocal(inst, inst_ty);4673 const local = try f.allocLocal(inst, inst_ty);
4647 const v = try Vectorize.start(f, inst, w, inst_ty);4674 const v = try Vectorize.start(f, inst, w, inst_ty);
4648 try f.writeCValue(w, local, .Other);4675 try f.writeCValue(w, local, .Other);
...@@ -4682,7 +4709,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4682,7 +4709,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4682 const inst_ty = f.typeOfIndex(inst);4709 const inst_ty = f.typeOfIndex(inst);
4683 const ptr_ty = inst_ty.slicePtrFieldType(zcu);4710 const ptr_ty = inst_ty.slicePtrFieldType(zcu);
46844711
4685 const w = &f.object.code.buffered_writer;4712 const w = &f.object.code.writer;
4686 const local = try f.allocLocal(inst, inst_ty);4713 const local = try f.allocLocal(inst, inst_ty);
4687 {4714 {
4688 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));4715 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));
...@@ -4713,7 +4740,7 @@ fn airCall(...@@ -4713,7 +4740,7 @@ fn airCall(
4713 if (f.object.dg.is_naked_fn) return .none;4740 if (f.object.dg.is_naked_fn) return .none;
47144741
4715 const gpa = f.object.dg.gpa;4742 const gpa = f.object.dg.gpa;
4716 const w = &f.object.code.buffered_writer;4743 const w = &f.object.code.writer;
47174744
4718 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4745 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4719 const extra = f.air.extraData(Air.Call, pl_op.payload);4746 const extra = f.air.extraData(Air.Call, pl_op.payload);
...@@ -4864,7 +4891,7 @@ fn airCall(...@@ -4864,7 +4891,7 @@ fn airCall(
48644891
4865fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {4892fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
4866 const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;4893 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;
4868 // TODO re-evaluate whether to emit these or not. If we naively emit4895 // TODO re-evaluate whether to emit these or not. If we naively emit
4869 // these directives, the output file will report bogus line numbers because4896 // these directives, the output file will report bogus line numbers because
4870 // every newline after the #line directive adds one to the line.4897 // every newline after the #line directive adds one to the line.
...@@ -4880,7 +4907,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4880,7 +4907,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
4880}4907}
48814908
4882fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {4909fn 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;");
4884 try f.object.newline();4911 try f.object.newline();
4885 return .none;4912 return .none;
4886}4913}
...@@ -4892,7 +4919,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4892,7 +4919,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4892 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4919 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4893 const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);4920 const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
4894 const owner_nav = ip.getNav(zcu.funcInfo(extra.data.func).owner_nav);4921 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;
4896 try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)});4923 try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)});
4897 try f.object.newline();4924 try f.object.newline();
4898 return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]));4925 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 {...@@ -4908,7 +4935,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
4908 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);4935 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
49094936
4910 try reap(f, inst, &.{pl_op.operand});4937 try reap(f, inst, &.{pl_op.operand});
4911 const w = &f.object.code.buffered_writer;4938 const w = &f.object.code.writer;
4912 try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) });4939 try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) });
4913 try f.object.newline();4940 try f.object.newline();
4914 return .none;4941 return .none;
...@@ -4927,7 +4954,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)...@@ -4927,7 +4954,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
49274954
4928 const block_id = f.next_block_index;4955 const block_id = f.next_block_index;
4929 f.next_block_index += 1;4956 f.next_block_index += 1;
4930 const w = &f.object.code.buffered_writer;4957 const w = &f.object.code.writer;
49314958
4932 const inst_ty = f.typeOfIndex(inst);4959 const inst_ty = f.typeOfIndex(inst);
4933 const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst))4960 const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst))
...@@ -4996,7 +5023,7 @@ fn lowerTry(...@@ -4996,7 +5023,7 @@ fn lowerTry(
4996 const err_union = try f.resolveInst(operand);5023 const err_union = try f.resolveInst(operand);
4997 const inst_ty = f.typeOfIndex(inst);5024 const inst_ty = f.typeOfIndex(inst);
4998 const liveness_condbr = f.liveness.getCondBr(inst);5025 const liveness_condbr = f.liveness.getCondBr(inst);
4999 const w = &f.object.code.buffered_writer;5026 const w = &f.object.code.writer;
5000 const payload_ty = err_union_ty.errorUnionPayload(zcu);5027 const payload_ty = err_union_ty.errorUnionPayload(zcu);
5001 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu);5028 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu);
50025029
...@@ -5058,7 +5085,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -5058,7 +5085,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
5058 const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;5085 const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
5059 const block = f.blocks.get(branch.block_inst).?;5086 const block = f.blocks.get(branch.block_inst).?;
5060 const result = block.result;5087 const result = block.result;
5061 const w = &f.object.code.buffered_writer;5088 const w = &f.object.code.writer;
50625089
5063 if (f.object.dg.is_naked_fn) {5090 if (f.object.dg.is_naked_fn) {
5064 if (result != .none) return f.fail("runtime code not allowed in naked function", .{});5091 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 {...@@ -5084,14 +5111,14 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
50845111
5085fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {5112fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
5086 const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;5113 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)});
5088}5115}
50895116
5090fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {5117fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
5091 const pt = f.object.dg.pt;5118 const pt = f.object.dg.pt;
5092 const zcu = pt.zcu;5119 const zcu = pt.zcu;
5093 const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br;5120 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
5096 if (try f.air.value(br.operand, pt)) |cond_val| {5123 if (try f.air.value(br.operand, pt)) |cond_val| {
5097 // Comptime-known dispatch. Iterate the cases to find the correct5124 // 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...@@ -5145,7 +5172,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
5145 const zcu = pt.zcu;5172 const zcu = pt.zcu;
5146 const target = &f.object.dg.mod.resolved_target.result;5173 const target = &f.object.dg.mod.resolved_target.result;
5147 const ctype_pool = &f.object.dg.ctype_pool;5174 const ctype_pool = &f.object.dg.ctype_pool;
5148 const w = &f.object.code.buffered_writer;5175 const w = &f.object.code.writer;
51495176
5150 if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {5177 if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {
5151 const src_info = dest_ty.intInfo(zcu);5178 const src_info = dest_ty.intInfo(zcu);
...@@ -5169,13 +5196,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal...@@ -5169,13 +5196,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
5169 const operand_lval = if (operand == .constant) blk: {5196 const operand_lval = if (operand == .constant) blk: {
5170 const operand_local = try f.allocLocal(null, operand_ty);5197 const operand_local = try f.allocLocal(null, operand_ty);
5171 try f.writeCValue(w, operand_local, .Other);5198 try f.writeCValue(w, operand_local, .Other);
5172 if (operand_ty.isAbiInt(zcu)) {5199 try w.writeAll(" = ");
5173 try w.writeAll(" = ");
5174 } else {
5175 try w.writeAll(" = (");
5176 try f.renderType(w, operand_ty);
5177 try w.writeByte(')');
5178 }
5179 try f.writeCValue(w, operand, .Other);5200 try f.writeCValue(w, operand, .Other);
5180 try w.writeByte(';');5201 try w.writeByte(';');
5181 try f.object.newline();5202 try f.object.newline();
...@@ -5264,14 +5285,14 @@ fn airTrap(f: *Function, w: *Writer) !void {...@@ -5264,14 +5285,14 @@ fn airTrap(f: *Function, w: *Writer) !void {
5264}5285}
52655286
5266fn airBreakpoint(f: *Function) !CValue {5287fn airBreakpoint(f: *Function) !CValue {
5267 const w = &f.object.code.buffered_writer;5288 const w = &f.object.code.writer;
5268 try w.writeAll("zig_breakpoint();");5289 try w.writeAll("zig_breakpoint();");
5269 try f.object.newline();5290 try f.object.newline();
5270 return .none;5291 return .none;
5271}5292}
52725293
5273fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {5294fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
5274 const w = &f.object.code.buffered_writer;5295 const w = &f.object.code.writer;
5275 const local = try f.allocLocal(inst, .usize);5296 const local = try f.allocLocal(inst, .usize);
5276 try f.writeCValue(w, local, .Other);5297 try f.writeCValue(w, local, .Other);
5277 try w.writeAll(" = (");5298 try w.writeAll(" = (");
...@@ -5282,7 +5303,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5282,7 +5303,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
5282}5303}
52835304
5284fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {5305fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
5285 const w = &f.object.code.buffered_writer;5306 const w = &f.object.code.writer;
5286 const local = try f.allocLocal(inst, .usize);5307 const local = try f.allocLocal(inst, .usize);
5287 try f.writeCValue(w, local, .Other);5308 try f.writeCValue(w, local, .Other);
5288 try w.writeAll(" = (");5309 try w.writeAll(" = (");
...@@ -5295,14 +5316,14 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5295,14 +5316,14 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
5295fn airUnreach(o: *Object) !void {5316fn airUnreach(o: *Object) !void {
5296 // Not even allowed to call unreachable in a naked function.5317 // Not even allowed to call unreachable in a naked function.
5297 if (o.dg.is_naked_fn) return;5318 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");
5299}5320}
53005321
5301fn airLoop(f: *Function, inst: Air.Inst.Index) !void {5322fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
5302 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5323 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5303 const loop = f.air.extraData(Air.Block, ty_pl.payload);5324 const loop = f.air.extraData(Air.Block, ty_pl.payload);
5304 const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[loop.end..][0..loop.data.body_len]);5325 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
5307 // `repeat` instructions matching this loop will branch to5328 // `repeat` instructions matching this loop will branch to
5308 // this label. Since we need a label for arbitrary `repeat`5329 // this label. Since we need a label for arbitrary `repeat`
...@@ -5321,7 +5342,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -5321,7 +5342,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
5321 const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]);5342 const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]);
5322 const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);5343 const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
5323 const liveness_condbr = f.liveness.getCondBr(inst);5344 const liveness_condbr = f.liveness.getCondBr(inst);
5324 const w = &f.object.code.buffered_writer;5345 const w = &f.object.code.writer;
53255346
5326 try w.writeAll("if (");5347 try w.writeAll("if (");
5327 try f.writeCValue(w, cond, .Other);5348 try f.writeCValue(w, cond, .Other);
...@@ -5354,7 +5375,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5354,7 +5375,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5354 const init_condition = try f.resolveInst(switch_br.operand);5375 const init_condition = try f.resolveInst(switch_br.operand);
5355 try reap(f, inst, &.{switch_br.operand});5376 try reap(f, inst, &.{switch_br.operand});
5356 const condition_ty = f.typeOf(switch_br.operand);5377 const condition_ty = f.typeOf(switch_br.operand);
5357 const w = &f.object.code.buffered_writer;5378 const w = &f.object.code.writer;
53585379
5359 // For dispatches, we will create a local alloc to contain the condition value.5380 // For dispatches, we will create a local alloc to contain the condition value.
5360 // This may not result in optimal codegen for switch loops, but it minimizes the5381 // 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...@@ -5408,7 +5429,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5408 write_val: {5429 write_val: {
5409 if (condition_ty.isPtrAtRuntime(zcu)) {5430 if (condition_ty.isPtrAtRuntime(zcu)) {
5410 if (item_value.?.getUnsignedInt(zcu)) |item_int| {5431 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))});
5412 break :write_val;5433 break :write_val;
5413 }5434 }
5414 }5435 }
...@@ -5534,7 +5555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5534,7 +5555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5534 extra_i += inputs.len;5555 extra_i += inputs.len;
55355556
5536 const result = result: {5557 const result = result: {
5537 const w = &f.object.code.buffered_writer;5558 const w = &f.object.code.writer;
5538 const inst_ty = f.typeOfIndex(inst);5559 const inst_ty = f.typeOfIndex(inst);
5539 const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: {5560 const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: {
5540 const inst_local = try f.allocLocalValue(.{5561 const inst_local = try f.allocLocalValue(.{
...@@ -5683,7 +5704,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5683,7 +5704,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
56835704
5684 try w.writeAll("__asm");5705 try w.writeAll("__asm");
5685 if (is_volatile) try w.writeAll(" volatile");5706 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)});
5687 }5708 }
56885709
5689 extra_i = constraints_extra_begin;5710 extra_i = constraints_extra_begin;
...@@ -5701,7 +5722,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5701,7 +5722,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5701 try w.writeByte(' ');5722 try w.writeByte(' ');
5702 if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name});5723 if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name});
5703 const is_reg = constraint[1] == '{';5724 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)});
5705 if (is_reg) {5726 if (is_reg) {
5706 try f.writeCValue(w, .{ .local = locals_index }, .Other);5727 try f.writeCValue(w, .{ .local = locals_index }, .Other);
5707 locals_index += 1;5728 locals_index += 1;
...@@ -5727,7 +5748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5727,7 +5748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
57275748
5728 const is_reg = constraint[0] == '{';5749 const is_reg = constraint[0] == '{';
5729 const input_val = try f.resolveInst(input);5750 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)});
5731 try f.writeCValue(w, if (asmInputNeedsLocal(f, constraint, input_val)) local: {5752 try f.writeCValue(w, if (asmInputNeedsLocal(f, constraint, input_val)) local: {
5732 const input_local_idx = locals_index;5753 const input_local_idx = locals_index;
5733 locals_index += 1;5754 locals_index += 1;
...@@ -5745,7 +5766,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5745,7 +5766,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5745 if (clobber.len == 0) continue;5766 if (clobber.len == 0) continue;
57465767
5747 if (clobber_i > 0) try w.writeByte(',');5768 if (clobber_i > 0) try w.writeByte(',');
5748 try w.print(" {fs}", .{fmtStringLiteral(clobber, null)});5769 try w.print(" {f}", .{fmtStringLiteral(clobber, null)});
5749 }5770 }
5750 try w.writeAll(");");5771 try w.writeAll(");");
5751 try f.object.newline();5772 try f.object.newline();
...@@ -5800,7 +5821,7 @@ fn airIsNull(...@@ -5800,7 +5821,7 @@ fn airIsNull(
5800 const ctype_pool = &f.object.dg.ctype_pool;5821 const ctype_pool = &f.object.dg.ctype_pool;
5801 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5822 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;
5804 const operand = try f.resolveInst(un_op);5825 const operand = try f.resolveInst(un_op);
5805 try reap(f, inst, &.{un_op});5826 try reap(f, inst, &.{un_op});
58065827
...@@ -5868,7 +5889,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue...@@ -5868,7 +5889,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue
5868 .aligned, .array, .vector, .fwd_decl, .function => unreachable,5889 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
5869 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {5890 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
5870 .is_null, .payload => {5891 .is_null, .payload => {
5871 const w = &f.object.code.buffered_writer;5892 const w = &f.object.code.writer;
5872 const local = try f.allocLocal(inst, inst_ty);5893 const local = try f.allocLocal(inst, inst_ty);
5873 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));5894 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
5874 try f.writeCValue(w, local, .Other);5895 try f.writeCValue(w, local, .Other);
...@@ -5890,7 +5911,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5890,7 +5911,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5890 const pt = f.object.dg.pt;5911 const pt = f.object.dg.pt;
5891 const zcu = pt.zcu;5912 const zcu = pt.zcu;
5892 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5913 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;
5894 const operand = try f.resolveInst(ty_op.operand);5915 const operand = try f.resolveInst(ty_op.operand);
5895 try reap(f, inst, &.{ty_op.operand});5916 try reap(f, inst, &.{ty_op.operand});
5896 const operand_ty = f.typeOf(ty_op.operand);5917 const operand_ty = f.typeOf(ty_op.operand);
...@@ -6040,7 +6061,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6040,7 +6061,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
6040 const field_ptr_val = try f.resolveInst(extra.field_ptr);6061 const field_ptr_val = try f.resolveInst(extra.field_ptr);
6041 try reap(f, inst, &.{extra.field_ptr});6062 try reap(f, inst, &.{extra.field_ptr});
60426063
6043 const w = &f.object.code.buffered_writer;6064 const w = &f.object.code.writer;
6044 const local = try f.allocLocal(inst, container_ptr_ty);6065 const local = try f.allocLocal(inst, container_ptr_ty);
6045 try f.writeCValue(w, local, .Other);6066 try f.writeCValue(w, local, .Other);
6046 try w.writeAll(" = (");6067 try w.writeAll(" = (");
...@@ -6070,7 +6091,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6070,7 +6091,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
6070 try w.writeByte(')');6091 try w.writeByte(')');
6071 try f.writeCValue(w, field_ptr_val, .Other);6092 try f.writeCValue(w, field_ptr_val, .Other);
6072 try w.print(" - {f})", .{6093 try w.print(" - {f})", .{
6073 try f.fmtIntLiteral(try pt.intValue(.usize, byte_offset)),6094 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
6074 });6095 });
6075 },6096 },
6076 }6097 }
...@@ -6095,7 +6116,7 @@ fn fieldPtr(...@@ -6095,7 +6116,7 @@ fn fieldPtr(
6095 // Ensure complete type definition is visible before accessing fields.6116 // Ensure complete type definition is visible before accessing fields.
6096 _ = try f.ctypeFromType(container_ty, .complete);6117 _ = try f.ctypeFromType(container_ty, .complete);
60976118
6098 const w = &f.object.code.buffered_writer;6119 const w = &f.object.code.writer;
6099 const local = try f.allocLocal(inst, field_ptr_ty);6120 const local = try f.allocLocal(inst, field_ptr_ty);
6100 try f.writeCValue(w, local, .Other);6121 try f.writeCValue(w, local, .Other);
6101 try w.writeAll(" = (");6122 try w.writeAll(" = (");
...@@ -6116,7 +6137,7 @@ fn fieldPtr(...@@ -6116,7 +6137,7 @@ fn fieldPtr(
6116 try w.writeByte(')');6137 try w.writeByte(')');
6117 try f.writeCValue(w, container_ptr_val, .Other);6138 try f.writeCValue(w, container_ptr_val, .Other);
6118 try w.print(" + {f})", .{6139 try w.print(" + {f})", .{
6119 try f.fmtIntLiteral(try pt.intValue(.usize, byte_offset)),6140 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
6120 });6141 });
6121 },6142 },
6122 }6143 }
...@@ -6142,7 +6163,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6142,7 +6163,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
6142 const struct_byval = try f.resolveInst(extra.struct_operand);6163 const struct_byval = try f.resolveInst(extra.struct_operand);
6143 try reap(f, inst, &.{extra.struct_operand});6164 try reap(f, inst, &.{extra.struct_operand});
6144 const struct_ty = f.typeOf(extra.struct_operand);6165 const struct_ty = f.typeOf(extra.struct_operand);
6145 const w = &f.object.code.buffered_writer;6166 const w = &f.object.code.writer;
61466167
6147 // Ensure complete type definition is visible before accessing fields.6168 // Ensure complete type definition is visible before accessing fields.
6148 _ = try f.ctypeFromType(struct_ty, .complete);6169 _ = try f.ctypeFromType(struct_ty, .complete);
...@@ -6189,7 +6210,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6189,7 +6210,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
6189 }6210 }
6190 try f.writeCValue(w, struct_byval, .Other);6211 try f.writeCValue(w, struct_byval, .Other);
6191 if (bit_offset > 0) try w.print(", {f})", .{6212 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)),
6193 });6214 });
6194 if (cant_cast) try w.writeByte(')');6215 if (cant_cast) try w.writeByte(')');
6195 try f.object.dg.renderBuiltinInfo(w, field_int_ty, .bits);6216 try f.object.dg.renderBuiltinInfo(w, field_int_ty, .bits);
...@@ -6291,7 +6312,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6291,7 +6312,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6291 return local;6312 return local;
6292 }6313 }
62936314
6294 const w = &f.object.code.buffered_writer;6315 const w = &f.object.code.writer;
6295 try f.writeCValue(w, local, .Other);6316 try f.writeCValue(w, local, .Other);
6296 try w.writeAll(" = ");6317 try w.writeAll(" = ");
62976318
...@@ -6299,7 +6320,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6299,7 +6320,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6299 try f.writeCValue(w, operand, .Other)6320 try f.writeCValue(w, operand, .Other)
6300 else if (error_ty.errorSetIsEmpty(zcu))6321 else if (error_ty.errorSetIsEmpty(zcu))
6301 try w.print("{f}", .{6322 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)),
6303 })6324 })
6304 else if (operand_is_ptr)6325 else if (operand_is_ptr)
6305 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })6326 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
...@@ -6321,7 +6342,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu...@@ -6321,7 +6342,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
6321 const operand_ty = f.typeOf(ty_op.operand);6342 const operand_ty = f.typeOf(ty_op.operand);
6322 const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;6343 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;
6325 if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {6346 if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
6326 if (!is_ptr) return .none;6347 if (!is_ptr) return .none;
63276348
...@@ -6363,7 +6384,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6363,7 +6384,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
6363 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {6384 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
6364 .is_null, .payload => {6385 .is_null, .payload => {
6365 const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete);6386 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;
6367 const local = try f.allocLocal(inst, inst_ty);6388 const local = try f.allocLocal(inst, inst_ty);
6368 {6389 {
6369 const a = try Assignment.start(f, w, .bool);6390 const a = try Assignment.start(f, w, .bool);
...@@ -6399,7 +6420,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6399,7 +6420,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6399 const err = try f.resolveInst(ty_op.operand);6420 const err = try f.resolveInst(ty_op.operand);
6400 try reap(f, inst, &.{ty_op.operand});6421 try reap(f, inst, &.{ty_op.operand});
64016422
6402 const w = &f.object.code.buffered_writer;6423 const w = &f.object.code.writer;
6403 const local = try f.allocLocal(inst, inst_ty);6424 const local = try f.allocLocal(inst, inst_ty);
64046425
6405 if (repr_is_err and err == .local and err.local == local.new_local) {6426 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 {...@@ -6430,7 +6451,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6430fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {6451fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
6431 const pt = f.object.dg.pt;6452 const pt = f.object.dg.pt;
6432 const zcu = pt.zcu;6453 const zcu = pt.zcu;
6433 const w = &f.object.code.buffered_writer;6454 const w = &f.object.code.writer;
6434 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;6455 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6435 const inst_ty = f.typeOfIndex(inst);6456 const inst_ty = f.typeOfIndex(inst);
6436 const operand = try f.resolveInst(ty_op.operand);6457 const operand = try f.resolveInst(ty_op.operand);
...@@ -6447,7 +6468,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6447,7 +6468,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
6447 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));6468 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));
6448 try f.writeCValueDeref(w, operand);6469 try f.writeCValueDeref(w, operand);
6449 try a.assign(f, w);6470 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)});
6451 try a.end(f, w);6472 try a.end(f, w);
6452 return .none;6473 return .none;
6453 }6474 }
...@@ -6455,7 +6476,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6455,7 +6476,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
6455 const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete));6476 const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete));
6456 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" });6477 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" });
6457 try a.assign(f, w);6478 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)});
6459 try a.end(f, w);6480 try a.end(f, w);
6460 }6481 }
64616482
...@@ -6499,7 +6520,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6499,7 +6520,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
6499 const err_ty = inst_ty.errorUnionSet(zcu);6520 const err_ty = inst_ty.errorUnionSet(zcu);
6500 try reap(f, inst, &.{ty_op.operand});6521 try reap(f, inst, &.{ty_op.operand});
65016522
6502 const w = &f.object.code.buffered_writer;6523 const w = &f.object.code.writer;
6503 const local = try f.allocLocal(inst, inst_ty);6524 const local = try f.allocLocal(inst, inst_ty);
6504 if (!repr_is_err) {6525 if (!repr_is_err) {
6505 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));6526 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...@@ -6526,7 +6547,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
6526 const zcu = pt.zcu;6547 const zcu = pt.zcu;
6527 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6548 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;
6530 const operand = try f.resolveInst(un_op);6551 const operand = try f.resolveInst(un_op);
6531 try reap(f, inst, &.{un_op});6552 try reap(f, inst, &.{un_op});
6532 const operand_ty = f.typeOf(un_op);6553 const operand_ty = f.typeOf(un_op);
...@@ -6567,7 +6588,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6567,7 +6588,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6567 try reap(f, inst, &.{ty_op.operand});6588 try reap(f, inst, &.{ty_op.operand});
6568 const inst_ty = f.typeOfIndex(inst);6589 const inst_ty = f.typeOfIndex(inst);
6569 const ptr_ty = inst_ty.slicePtrFieldType(zcu);6590 const ptr_ty = inst_ty.slicePtrFieldType(zcu);
6570 const w = &f.object.code.buffered_writer;6591 const w = &f.object.code.writer;
6571 const local = try f.allocLocal(inst, inst_ty);6592 const local = try f.allocLocal(inst, inst_ty);
6572 const operand_ty = f.typeOf(ty_op.operand);6593 const operand_ty = f.typeOf(ty_op.operand);
6573 const array_ty = operand_ty.childType(zcu);6594 const array_ty = operand_ty.childType(zcu);
...@@ -6593,7 +6614,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6593,7 +6614,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6593 if (operand_child_ctype.info(ctype_pool) == .array) {6614 if (operand_child_ctype.info(ctype_pool) == .array) {
6594 try w.writeByte('&');6615 try w.writeByte('&');
6595 try f.writeCValueDeref(w, operand);6616 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)});
6597 } else try f.writeCValue(w, operand, .Other);6618 } else try f.writeCValue(w, operand, .Other);
6598 }6619 }
6599 try a.end(f, w);6620 try a.end(f, w);
...@@ -6603,7 +6624,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6603,7 +6624,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6603 try f.writeCValueMember(w, local, .{ .identifier = "len" });6624 try f.writeCValueMember(w, local, .{ .identifier = "len" });
6604 try a.assign(f, w);6625 try a.assign(f, w);
6605 try w.print("{f}", .{6626 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))),
6607 });6628 });
6608 try a.end(f, w);6629 try a.end(f, w);
6609 }6630 }
...@@ -6632,7 +6653,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6632,7 +6653,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6632 else6653 else
6633 unreachable;6654 unreachable;
66346655
6635 const w = &f.object.code.buffered_writer;6656 const w = &f.object.code.writer;
6636 const local = try f.allocLocal(inst, inst_ty);6657 const local = try f.allocLocal(inst, inst_ty);
6637 const v = try Vectorize.start(f, inst, w, operand_ty);6658 const v = try Vectorize.start(f, inst, w, operand_ty);
6638 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));6659 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
...@@ -6682,7 +6703,7 @@ fn airUnBuiltinCall(...@@ -6682,7 +6703,7 @@ fn airUnBuiltinCall(
6682 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);6703 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
6683 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;6704 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;
6686 const local = try f.allocLocal(inst, inst_ty);6707 const local = try f.allocLocal(inst, inst_ty);
6687 const v = try Vectorize.start(f, inst, w, operand_ty);6708 const v = try Vectorize.start(f, inst, w, operand_ty);
6688 if (!ref_ret) {6709 if (!ref_ret) {
...@@ -6733,7 +6754,7 @@ fn airBinBuiltinCall(...@@ -6733,7 +6754,7 @@ fn airBinBuiltinCall(
6733 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);6754 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
6734 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;6755 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;
6737 const local = try f.allocLocal(inst, inst_ty);6758 const local = try f.allocLocal(inst, inst_ty);
6738 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });6759 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6739 const v = try Vectorize.start(f, inst, w, operand_ty);6760 const v = try Vectorize.start(f, inst, w, operand_ty);
...@@ -6784,7 +6805,7 @@ fn airCmpBuiltinCall(...@@ -6784,7 +6805,7 @@ fn airCmpBuiltinCall(
6784 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);6805 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
6785 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;6806 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;
6788 const local = try f.allocLocal(inst, inst_ty);6809 const local = try f.allocLocal(inst, inst_ty);
6789 const v = try Vectorize.start(f, inst, w, operand_ty);6810 const v = try Vectorize.start(f, inst, w, operand_ty);
6790 if (!ref_ret) {6811 if (!ref_ret) {
...@@ -6812,7 +6833,7 @@ fn airCmpBuiltinCall(...@@ -6812,7 +6833,7 @@ fn airCmpBuiltinCall(
6812 try w.writeByte(')');6833 try w.writeByte(')');
6813 if (!ref_ret) try w.print("{s}{f}", .{6834 if (!ref_ret) try w.print("{s}{f}", .{
6814 compareOperatorC(operator),6835 compareOperatorC(operator),
6815 try f.fmtIntLiteral(try pt.intValue(.i32, 0)),6836 try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)),
6816 });6837 });
6817 try w.writeByte(';');6838 try w.writeByte(';');
6818 try f.object.newline();6839 try f.object.newline();
...@@ -6834,7 +6855,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue...@@ -6834,7 +6855,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
6834 const ty = ptr_ty.childType(zcu);6855 const ty = ptr_ty.childType(zcu);
6835 const ctype = try f.ctypeFromType(ty, .complete);6856 const ctype = try f.ctypeFromType(ty, .complete);
68366857
6837 const w = &f.object.code.buffered_writer;6858 const w = &f.object.code.writer;
6838 const new_value_mat = try Materialize.start(f, inst, ty, new_value);6859 const new_value_mat = try Materialize.start(f, inst, ty, new_value);
6839 try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value });6860 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 {...@@ -6941,7 +6962,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6941 const ptr = try f.resolveInst(pl_op.operand);6962 const ptr = try f.resolveInst(pl_op.operand);
6942 const operand = try f.resolveInst(extra.operand);6963 const operand = try f.resolveInst(extra.operand);
69436964
6944 const w = &f.object.code.buffered_writer;6965 const w = &f.object.code.writer;
6945 const operand_mat = try Materialize.start(f, inst, ty, operand);6966 const operand_mat = try Materialize.start(f, inst, ty, operand);
6946 try reap(f, inst, &.{ pl_op.operand, extra.operand });6967 try reap(f, inst, &.{ pl_op.operand, extra.operand });
69476968
...@@ -7002,7 +7023,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7002,7 +7023,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
7002 ty;7023 ty;
70037024
7004 const inst_ty = f.typeOfIndex(inst);7025 const inst_ty = f.typeOfIndex(inst);
7005 const w = &f.object.code.buffered_writer;7026 const w = &f.object.code.writer;
7006 const local = try f.allocLocal(inst, inst_ty);7027 const local = try f.allocLocal(inst, inst_ty);
70077028
7008 try w.writeAll("zig_atomic_load(");7029 try w.writeAll("zig_atomic_load(");
...@@ -7034,7 +7055,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa...@@ -7034,7 +7055,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
7034 const ptr = try f.resolveInst(bin_op.lhs);7055 const ptr = try f.resolveInst(bin_op.lhs);
7035 const element = try f.resolveInst(bin_op.rhs);7056 const element = try f.resolveInst(bin_op.rhs);
70367057
7037 const w = &f.object.code.buffered_writer;7058 const w = &f.object.code.writer;
7038 const element_mat = try Materialize.start(f, inst, ty, element);7059 const element_mat = try Materialize.start(f, inst, ty, element);
7039 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });7060 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 {...@@ -7082,7 +7103,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
7082 const elem_ty = f.typeOf(bin_op.rhs);7103 const elem_ty = f.typeOf(bin_op.rhs);
7083 const elem_abi_size = elem_ty.abiSize(zcu);7104 const elem_abi_size = elem_ty.abiSize(zcu);
7084 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(zcu) else false;7105 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
7087 if (val_is_undef) {7108 if (val_is_undef) {
7088 if (!safety) {7109 if (!safety) {
...@@ -7206,7 +7227,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV...@@ -7206,7 +7227,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV
7206 const src_ptr = try f.resolveInst(bin_op.rhs);7227 const src_ptr = try f.resolveInst(bin_op.rhs);
7207 const dest_ty = f.typeOf(bin_op.lhs);7228 const dest_ty = f.typeOf(bin_op.lhs);
7208 const src_ty = f.typeOf(bin_op.rhs);7229 const src_ty = f.typeOf(bin_op.rhs);
7209 const w = &f.object.code.buffered_writer;7230 const w = &f.object.code.writer;
72107231
7211 if (dest_ty.ptrSize(zcu) != .one) {7232 if (dest_ty.ptrSize(zcu) != .one) {
7212 try w.writeAll("if (");7233 try w.writeAll("if (");
...@@ -7231,10 +7252,10 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV...@@ -7231,10 +7252,10 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV
7231fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void {7252fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void {
7232 const pt = f.object.dg.pt;7253 const pt = f.object.dg.pt;
7233 const zcu = pt.zcu;7254 const zcu = pt.zcu;
7234 const w = &f.object.code.buffered_writer;7255 const w = &f.object.code.writer;
7235 switch (dest_ty.ptrSize(zcu)) {7256 switch (dest_ty.ptrSize(zcu)) {
7236 .one => try w.print("{f}", .{7257 .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))),
7238 }),7259 }),
7239 .many, .c => unreachable,7260 .many, .c => unreachable,
7240 .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }),7261 .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }),
...@@ -7254,7 +7275,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7254,7 +7275,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
7254 if (layout.tag_size == 0) return .none;7275 if (layout.tag_size == 0) return .none;
7255 const tag_ty = union_ty.unionTagTypeSafety(zcu).?;7276 const tag_ty = union_ty.unionTagTypeSafety(zcu).?;
72567277
7257 const w = &f.object.code.buffered_writer;7278 const w = &f.object.code.writer;
7258 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));7279 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
7259 try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" });7280 try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" });
7260 try a.assign(f, w);7281 try a.assign(f, w);
...@@ -7276,7 +7297,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7276,7 +7297,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
7276 if (layout.tag_size == 0) return .none;7297 if (layout.tag_size == 0) return .none;
72777298
7278 const inst_ty = f.typeOfIndex(inst);7299 const inst_ty = f.typeOfIndex(inst);
7279 const w = &f.object.code.buffered_writer;7300 const w = &f.object.code.writer;
7280 const local = try f.allocLocal(inst, inst_ty);7301 const local = try f.allocLocal(inst, inst_ty);
7281 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));7302 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
7282 try f.writeCValue(w, local, .Other);7303 try f.writeCValue(w, local, .Other);
...@@ -7294,7 +7315,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7294,7 +7315,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
7294 const operand = try f.resolveInst(un_op);7315 const operand = try f.resolveInst(un_op);
7295 try reap(f, inst, &.{un_op});7316 try reap(f, inst, &.{un_op});
72967317
7297 const w = &f.object.code.buffered_writer;7318 const w = &f.object.code.writer;
7298 const local = try f.allocLocal(inst, inst_ty);7319 const local = try f.allocLocal(inst, inst_ty);
7299 try f.writeCValue(w, local, .Other);7320 try f.writeCValue(w, local, .Other);
7300 try w.print(" = {s}(", .{7321 try w.print(" = {s}(", .{
...@@ -7310,7 +7331,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7310,7 +7331,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
7310fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {7331fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
7311 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;7332 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;
7314 const inst_ty = f.typeOfIndex(inst);7335 const inst_ty = f.typeOfIndex(inst);
7315 const operand = try f.resolveInst(un_op);7336 const operand = try f.resolveInst(un_op);
7316 try reap(f, inst, &.{un_op});7337 try reap(f, inst, &.{un_op});
...@@ -7335,7 +7356,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7335,7 +7356,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
7335 const inst_ty = f.typeOfIndex(inst);7356 const inst_ty = f.typeOfIndex(inst);
7336 const inst_scalar_ty = inst_ty.scalarType(zcu);7357 const inst_scalar_ty = inst_ty.scalarType(zcu);
73377358
7338 const w = &f.object.code.buffered_writer;7359 const w = &f.object.code.writer;
7339 const local = try f.allocLocal(inst, inst_ty);7360 const local = try f.allocLocal(inst, inst_ty);
7340 const v = try Vectorize.start(f, inst, w, inst_ty);7361 const v = try Vectorize.start(f, inst, w, inst_ty);
7341 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete));7362 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 {...@@ -7360,7 +7381,7 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
73607381
7361 const inst_ty = f.typeOfIndex(inst);7382 const inst_ty = f.typeOfIndex(inst);
73627383
7363 const w = &f.object.code.buffered_writer;7384 const w = &f.object.code.writer;
7364 const local = try f.allocLocal(inst, inst_ty);7385 const local = try f.allocLocal(inst, inst_ty);
7365 const v = try Vectorize.start(f, inst, w, inst_ty);7386 const v = try Vectorize.start(f, inst, w, inst_ty);
7366 try f.writeCValue(w, local, .Other);7387 try f.writeCValue(w, local, .Other);
...@@ -7390,7 +7411,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7390,7 +7411,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
7390 const operand = try f.resolveInst(unwrapped.operand);7411 const operand = try f.resolveInst(unwrapped.operand);
7391 const inst_ty = unwrapped.result_ty;7412 const inst_ty = unwrapped.result_ty;
73927413
7393 const w = &f.object.code.buffered_writer;7414 const w = &f.object.code.writer;
7394 const local = try f.allocLocal(inst, inst_ty);7415 const local = try f.allocLocal(inst, inst_ty);
7395 try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand7416 try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand
7396 for (mask, 0..) |mask_elem, out_idx| {7417 for (mask, 0..) |mask_elem, out_idx| {
...@@ -7424,7 +7445,7 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7424,7 +7445,7 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
7424 const inst_ty = unwrapped.result_ty;7445 const inst_ty = unwrapped.result_ty;
7425 const elem_ty = inst_ty.childType(zcu);7446 const elem_ty = inst_ty.childType(zcu);
74267447
7427 const w = &f.object.code.buffered_writer;7448 const w = &f.object.code.writer;
7428 const local = try f.allocLocal(inst, inst_ty);7449 const local = try f.allocLocal(inst, inst_ty);
7429 try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands7450 try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands
7430 for (mask, 0..) |mask_elem, out_idx| {7451 for (mask, 0..) |mask_elem, out_idx| {
...@@ -7463,7 +7484,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7463,7 +7484,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7463 const operand = try f.resolveInst(reduce.operand);7484 const operand = try f.resolveInst(reduce.operand);
7464 try reap(f, inst, &.{reduce.operand});7485 try reap(f, inst, &.{reduce.operand});
7465 const operand_ty = f.typeOf(reduce.operand);7486 const operand_ty = f.typeOf(reduce.operand);
7466 const w = &f.object.code.buffered_writer;7487 const w = &f.object.code.writer;
74677488
7468 const use_operator = scalar_ty.bitSize(zcu) <= 64;7489 const use_operator = scalar_ty.bitSize(zcu) <= 64;
7469 const op: union(enum) {7490 const op: union(enum) {
...@@ -7613,7 +7634,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7613,7 +7634,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7613 }7634 }
7614 }7635 }
76157636
7616 const w = &f.object.code.buffered_writer;7637 const w = &f.object.code.writer;
7617 const local = try f.allocLocal(inst, inst_ty);7638 const local = try f.allocLocal(inst, inst_ty);
7618 switch (ip.indexToKey(inst_ty.toIntern())) {7639 switch (ip.indexToKey(inst_ty.toIntern())) {
7619 inline .array_type, .vector_type => |info, tag| {7640 inline .array_type, .vector_type => |info, tag| {
...@@ -7727,7 +7748,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7727,7 +7748,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7727 }7748 }
77287749
7729 try w.print(", {f}", .{7750 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)),
7731 });7752 });
7732 try f.object.dg.renderBuiltinInfo(w, inst_ty, .bits);7753 try f.object.dg.renderBuiltinInfo(w, inst_ty, .bits);
7733 try w.writeByte(')');7754 try w.writeByte(')');
...@@ -7772,7 +7793,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7772,7 +7793,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
7772 const payload = try f.resolveInst(extra.init);7793 const payload = try f.resolveInst(extra.init);
7773 try reap(f, inst, &.{extra.init});7794 try reap(f, inst, &.{extra.init});
77747795
7775 const w = &f.object.code.buffered_writer;7796 const w = &f.object.code.writer;
7776 const local = try f.allocLocal(inst, union_ty);7797 const local = try f.allocLocal(inst, union_ty);
7777 if (loaded_union.flagsUnordered(ip).layout == .@"packed") return f.moveCValue(inst, union_ty, payload);7798 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 {...@@ -7785,7 +7806,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
7785 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));7806 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
7786 try f.writeCValueMember(w, local, .{ .identifier = "tag" });7807 try f.writeCValueMember(w, local, .{ .identifier = "tag" });
7787 try a.assign(f, w);7808 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))});
7789 try a.end(f, w);7810 try a.end(f, w);
7790 }7811 }
7791 break :field .{ .payload_identifier = field_name.toSlice(ip) };7812 break :field .{ .payload_identifier = field_name.toSlice(ip) };
...@@ -7808,7 +7829,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7808,7 +7829,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7808 const ptr = try f.resolveInst(prefetch.ptr);7829 const ptr = try f.resolveInst(prefetch.ptr);
7809 try reap(f, inst, &.{prefetch.ptr});7830 try reap(f, inst, &.{prefetch.ptr});
78107831
7811 const w = &f.object.code.buffered_writer;7832 const w = &f.object.code.writer;
7812 switch (prefetch.cache) {7833 switch (prefetch.cache) {
7813 .data => {7834 .data => {
7814 try w.writeAll("zig_prefetch(");7835 try w.writeAll("zig_prefetch(");
...@@ -7830,7 +7851,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7830,7 +7851,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7830fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {7851fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
7831 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;7852 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;
7834 const inst_ty = f.typeOfIndex(inst);7855 const inst_ty = f.typeOfIndex(inst);
7835 const local = try f.allocLocal(inst, inst_ty);7856 const local = try f.allocLocal(inst, inst_ty);
7836 try f.writeCValue(w, local, .Other);7857 try f.writeCValue(w, local, .Other);
...@@ -7845,7 +7866,7 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7845,7 +7866,7 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
7845fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {7866fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
7846 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;7867 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;
7849 const inst_ty = f.typeOfIndex(inst);7870 const inst_ty = f.typeOfIndex(inst);
7850 const operand = try f.resolveInst(pl_op.operand);7871 const operand = try f.resolveInst(pl_op.operand);
7851 try reap(f, inst, &.{pl_op.operand});7872 try reap(f, inst, &.{pl_op.operand});
...@@ -7874,7 +7895,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7874,7 +7895,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
7874 const inst_ty = f.typeOfIndex(inst);7895 const inst_ty = f.typeOfIndex(inst);
7875 const inst_scalar_ty = inst_ty.scalarType(zcu);7896 const inst_scalar_ty = inst_ty.scalarType(zcu);
78767897
7877 const w = &f.object.code.buffered_writer;7898 const w = &f.object.code.writer;
7878 const local = try f.allocLocal(inst, inst_ty);7899 const local = try f.allocLocal(inst, inst_ty);
7879 const v = try Vectorize.start(f, inst, w, inst_ty);7900 const v = try Vectorize.start(f, inst, w, inst_ty);
7880 try f.writeCValue(w, local, .Other);7901 try f.writeCValue(w, local, .Other);
...@@ -7899,7 +7920,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7899,7 +7920,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
78997920
7900fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {7921fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {
7901 const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;7922 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;
7903 const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));7924 const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));
7904 try f.writeCValue(w, local, .Other);7925 try f.writeCValue(w, local, .Other);
7905 try w.writeAll(" = ");7926 try w.writeAll(" = ");
...@@ -7917,7 +7938,7 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7917,7 +7938,7 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
7917 const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function;7938 const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function;
7918 assert(function_info.varargs);7939 assert(function_info.varargs);
79197940
7920 const w = &f.object.code.buffered_writer;7941 const w = &f.object.code.writer;
7921 const local = try f.allocLocal(inst, inst_ty);7942 const local = try f.allocLocal(inst, inst_ty);
7922 try w.writeAll("va_start(*(va_list *)&");7943 try w.writeAll("va_start(*(va_list *)&");
7923 try f.writeCValue(w, local, .Other);7944 try f.writeCValue(w, local, .Other);
...@@ -7937,7 +7958,7 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7937,7 +7958,7 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
7937 const va_list = try f.resolveInst(ty_op.operand);7958 const va_list = try f.resolveInst(ty_op.operand);
7938 try reap(f, inst, &.{ty_op.operand});7959 try reap(f, inst, &.{ty_op.operand});
79397960
7940 const w = &f.object.code.buffered_writer;7961 const w = &f.object.code.writer;
7941 const local = try f.allocLocal(inst, inst_ty);7962 const local = try f.allocLocal(inst, inst_ty);
7942 try f.writeCValue(w, local, .Other);7963 try f.writeCValue(w, local, .Other);
7943 try w.writeAll(" = va_arg(*(va_list *)");7964 try w.writeAll(" = va_arg(*(va_list *)");
...@@ -7955,7 +7976,7 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7955,7 +7976,7 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
7955 const va_list = try f.resolveInst(un_op);7976 const va_list = try f.resolveInst(un_op);
7956 try reap(f, inst, &.{un_op});7977 try reap(f, inst, &.{un_op});
79577978
7958 const w = &f.object.code.buffered_writer;7979 const w = &f.object.code.writer;
7959 try w.writeAll("va_end(*(va_list *)");7980 try w.writeAll("va_end(*(va_list *)");
7960 try f.writeCValue(w, va_list, .Other);7981 try f.writeCValue(w, va_list, .Other);
7961 try w.writeAll(");");7982 try w.writeAll(");");
...@@ -7970,7 +7991,7 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7970,7 +7991,7 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
7970 const va_list = try f.resolveInst(ty_op.operand);7991 const va_list = try f.resolveInst(ty_op.operand);
7971 try reap(f, inst, &.{ty_op.operand});7992 try reap(f, inst, &.{ty_op.operand});
79727993
7973 const w = &f.object.code.buffered_writer;7994 const w = &f.object.code.writer;
7974 const local = try f.allocLocal(inst, inst_ty);7995 const local = try f.allocLocal(inst, inst_ty);
7975 try w.writeAll("va_copy(*(va_list *)&");7996 try w.writeAll("va_copy(*(va_list *)&");
7976 try f.writeCValue(w, local, .Other);7997 try f.writeCValue(w, local, .Other);
...@@ -8136,8 +8157,8 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {...@@ -8136,8 +8157,8 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
8136const StringLiteral = struct {8157const StringLiteral = struct {
8137 len: usize,8158 len: usize,
8138 cur_len: usize,8159 cur_len: usize,
8139 start_count: usize,
8140 w: *Writer,8160 w: *Writer,
8161 first: bool,
81418162
8142 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,8163 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
8143 // regardless of the length of the string literal initializing it. Array initializer syntax is8164 // regardless of the length of the string literal initializing it. Array initializer syntax is
...@@ -8154,8 +8175,8 @@ const StringLiteral = struct {...@@ -8154,8 +8175,8 @@ const StringLiteral = struct {
8154 return .{8175 return .{
8155 .cur_len = 0,8176 .cur_len = 0,
8156 .len = len,8177 .len = len,
8157 .start_count = w.count,
8158 .w = w,8178 .w = w,
8179 .first = true,
8159 };8180 };
8160 }8181 }
81618182
...@@ -8175,50 +8196,83 @@ const StringLiteral = struct {...@@ -8175,50 +8196,83 @@ const StringLiteral = struct {
8175 }8196 }
8176 }8197 }
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;
8179 switch (c) {8201 switch (c) {
8180 7 => try sl.w.writeAll("\\a"),8202 7 => {
8181 8 => try sl.w.writeAll("\\b"),8203 try w.writeAll("\\a");
8182 '\t' => try sl.w.writeAll("\\t"),8204 return 2;
8183 '\n' => try sl.w.writeAll("\\n"),8205 },
8184 11 => try sl.w.writeAll("\\v"),8206 8 => {
8185 12 => try sl.w.writeAll("\\f"),8207 try w.writeAll("\\b");
8186 '\r' => try sl.w.writeAll("\\r"),8208 return 2;
8187 '"', '\'', '?', '\\' => try sl.w.print("\\{c}", .{c}),8209 },
8188 else => switch (c) {8210 '\t' => {
8189 ' '...'~' => try sl.w.writeByte(c),8211 try w.writeAll("\\t");
8190 else => try sl.w.print("\\{o:0>3}", .{c}),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;
8191 },8243 },
8192 }8244 }
8193 }8245 }
81948246
8195 pub fn writeChar(sl: *StringLiteral, c: u8) Writer.Error!void {8247 pub fn writeChar(sl: *StringLiteral, c: u8) Writer.Error!void {
8196 if (sl.len <= max_string_initializer_len) {8248 if (sl.len <= max_string_initializer_len) {
8197 if (sl.cur_len == 0 and sl.w.count - sl.start_count > 1)8249 if (sl.cur_len == 0 and !sl.first) try sl.w.writeAll("\"\"");
8198 try sl.w.writeAll("\"\"");
81998250
8200 const count = sl.w.count;8251 const char_len = try sl.writeStringLiteralChar(c);
8201 try sl.writeStringLiteralChar(c);
8202 const char_len = sl.w.count - count;
8203 assert(char_len <= max_char_len);8252 assert(char_len <= max_char_len);
8204 sl.cur_len += char_len;8253 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 }
8207 } else {8259 } else {
8208 if (sl.w.count - sl.start_count > 1) try sl.w.writeByte(',');8260 if (!sl.first) try sl.w.writeByte(',');
8209 try sl.w.print("'\\x{x}'", .{c});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;
8210 }8266 }
8211 }8267 }
8212};8268};
82138269
8214const FormatStringContext = struct { str: []const u8, sentinel: ?u8 };8270const FormatStringContext = struct {
8215fn formatStringLiteral(8271 str: []const u8,
8216 data: FormatStringContext,8272 sentinel: ?u8,
8217 w: *Writer,8273};
8218 comptime fmt: []const u8,
8219) Writer.Error!void {
8220 if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt);
82218274
8275fn formatStringLiteral(data: FormatStringContext, w: *std.io.Writer) std.io.Writer.Error!void {
8222 var literal: StringLiteral = .init(w, data.str.len + @intFromBool(data.sentinel != null));8276 var literal: StringLiteral = .init(w, data.str.len + @intFromBool(data.sentinel != null));
8223 try literal.start();8277 try literal.start();
8224 for (data.str) |c| try literal.writeChar(c);8278 for (data.str) |c| try literal.writeChar(c);
...@@ -8226,7 +8280,7 @@ fn formatStringLiteral(...@@ -8226,7 +8280,7 @@ fn formatStringLiteral(
8226 try literal.end();8280 try literal.end();
8227}8281}
82288282
8229fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) {8283fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(FormatStringContext, formatStringLiteral) {
8230 return .{ .data = .{ .str = str, .sentinel = sentinel } };8284 return .{ .data = .{ .str = str, .sentinel = sentinel } };
8231}8285}
82328286
...@@ -8242,12 +8296,10 @@ const FormatIntLiteralContext = struct {...@@ -8242,12 +8296,10 @@ const FormatIntLiteralContext = struct {
8242 kind: CType.Kind,8296 kind: CType.Kind,
8243 ctype: CType,8297 ctype: CType,
8244 val: Value,8298 val: Value,
8299 base: u8,
8300 case: std.fmt.Case,
8245};8301};
8246fn formatIntLiteral(8302fn formatIntLiteral(data: FormatIntLiteralContext, w: *std.io.Writer) std.io.Writer.Error!void {
8247 data: FormatIntLiteralContext,
8248 w: *Writer,
8249 comptime fmt: []const u8,
8250) Writer.Error!void {
8251 const pt = data.dg.pt;8303 const pt = data.dg.pt;
8252 const zcu = pt.zcu;8304 const zcu = pt.zcu;
8253 const target = &data.dg.mod.resolved_target.result;8305 const target = &data.dg.mod.resolved_target.result;
...@@ -8337,32 +8389,14 @@ fn formatIntLiteral(...@@ -8337,32 +8389,14 @@ fn formatIntLiteral(
8337 if (!int.positive) try w.writeByte('-');8389 if (!int.positive) try w.writeByte('-');
8338 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);8390 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);
83398391
8340 const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) {8392 switch (data.base) {
8341 0 => .{ .base = 10 },8393 2 => try w.writeAll("0b"),
8342 1 => switch (fmt[0]) {8394 8 => try w.writeByte('0'),
8343 'b' => style: {8395 10 => {},
8344 try w.writeAll("0b");8396 16 => try w.writeAll("0x"),
8345 break :style .{ .base = 2 };8397 else => unreachable,
8346 },8398 }
8347 'o' => style: {8399 const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch
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
8366 return error.WriteFailed;8400 return error.WriteFailed;
8367 defer allocator.free(string);8401 defer allocator.free(string);
8368 try w.writeAll(string);8402 try w.writeAll(string);
...@@ -8418,7 +8452,9 @@ fn formatIntLiteral(...@@ -8418,7 +8452,9 @@ fn formatIntLiteral(
8418 .ctype = c_limb_ctype,8452 .ctype = c_limb_ctype,
8419 .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch8453 .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch
8420 return error.WriteFailed,8454 return error.WriteFailed,
8421 }, w, fmt);8455 .base = data.base,
8456 .case = data.case,
8457 }, w);
8422 }8458 }
8423 }8459 }
8424 try data.ctype.renderLiteralSuffix(w, ctype_pool);8460 try data.ctype.renderLiteralSuffix(w, ctype_pool);
...@@ -8499,11 +8535,11 @@ const Vectorize = struct {...@@ -8499,11 +8535,11 @@ const Vectorize = struct {
84998535
8500 try w.writeAll("for (");8536 try w.writeAll("for (");
8501 try f.writeCValue(w, local, .Other);8537 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)});
8503 try f.writeCValue(w, local, .Other);8539 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)))});
8505 try f.writeCValue(w, local, .Other);8541 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)});
8507 f.object.indent();8543 f.object.indent();
8508 try f.object.newline();8544 try f.object.newline();
85098545
src/codegen/llvm.zig+40-47
...@@ -21,11 +21,11 @@ const Air = @import("../Air.zig");...@@ -21,11 +21,11 @@ const Air = @import("../Air.zig");
21const Value = @import("../Value.zig");21const Value = @import("../Value.zig");
22const Type = @import("../Type.zig");22const Type = @import("../Type.zig");
23const x86_64_abi = @import("../arch/x86_64/abi.zig");23const x86_64_abi = @import("../arch/x86_64/abi.zig");
24const wasm_c_abi = @import("../arch/wasm/abi.zig");24const wasm_c_abi = @import("wasm/abi.zig");
25const aarch64_c_abi = @import("../arch/aarch64/abi.zig");25const aarch64_c_abi = @import("aarch64/abi.zig");
26const arm_c_abi = @import("../arch/arm/abi.zig");26const arm_c_abi = @import("arm/abi.zig");
27const riscv_c_abi = @import("../arch/riscv64/abi.zig");27const 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");
29const dev = @import("../dev.zig");29const dev = @import("../dev.zig");
3030
31const target_util = @import("../target.zig");31const target_util = @import("../target.zig");
...@@ -945,7 +945,9 @@ pub const Object = struct {...@@ -945,7 +945,9 @@ pub const Object = struct {
945 if (std.mem.eql(u8, path, "-")) {945 if (std.mem.eql(u8, path, "-")) {
946 o.builder.dump();946 o.builder.dump();
947 } else {947 } 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 };
949 }951 }
950 }952 }
951953
...@@ -1053,6 +1055,7 @@ pub const Object = struct {...@@ -1053,6 +1055,7 @@ pub const Object = struct {
1053 comp.data_sections,1055 comp.data_sections,
1054 float_abi,1056 float_abi,
1055 if (target_util.llvmMachineAbi(&comp.root_mod.resolved_target.result)) |s| s.ptr else null,1057 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),
1056 );1059 );
1057 errdefer target_machine.dispose();1060 errdefer target_machine.dispose();
10581061
...@@ -2765,7 +2768,7 @@ pub const Object = struct {...@@ -2765,7 +2768,7 @@ pub const Object = struct {
2765 llvm_arg_i += 1;2768 llvm_arg_i += 1;
2766 }2769 }
27672770
2768 if (fn_info.cc == .@"async") {2771 if (fn_info.cc == .async) {
2769 @panic("TODO: LLVM backend lower async function");2772 @panic("TODO: LLVM backend lower async function");
2770 }2773 }
27712774
...@@ -2917,7 +2920,7 @@ pub const Object = struct {...@@ -2917,7 +2920,7 @@ pub const Object = struct {
2917 try attributes.addFnAttr(.nounwind, &o.builder);2920 try attributes.addFnAttr(.nounwind, &o.builder);
2918 if (owner_mod.unwind_tables != .none) {2921 if (owner_mod.unwind_tables != .none) {
2919 try attributes.addFnAttr(2922 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 },
2921 &o.builder,2924 &o.builder,
2922 );2925 );
2923 }2926 }
...@@ -5280,7 +5283,7 @@ pub const FuncGen = struct {...@@ -5280,7 +5283,7 @@ pub const FuncGen = struct {
5280 switch (modifier) {5283 switch (modifier) {
5281 .auto, .always_tail => {},5284 .auto, .always_tail => {},
5282 .never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder),5285 .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,
5284 }5287 }
52855288
5286 const ret_ptr = if (!sret) null else blk: {5289 const ret_ptr = if (!sret) null else blk: {
...@@ -5288,7 +5291,7 @@ pub const FuncGen = struct {...@@ -5288,7 +5291,7 @@ pub const FuncGen = struct {
5288 try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);5291 try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);
52895292
5290 const alignment = return_type.abiAlignment(zcu).toLlvm();5293 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);
5292 try llvm_args.append(ret_ptr);5295 try llvm_args.append(ret_ptr);
5293 break :blk ret_ptr;5296 break :blk ret_ptr;
5294 };5297 };
...@@ -5336,7 +5339,7 @@ pub const FuncGen = struct {...@@ -5336,7 +5339,7 @@ pub const FuncGen = struct {
53365339
5337 const alignment = param_ty.abiAlignment(zcu).toLlvm();5340 const alignment = param_ty.abiAlignment(zcu).toLlvm();
5338 const param_llvm_ty = try o.lowerType(param_ty);5341 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);
5340 if (isByRef(param_ty, zcu)) {5343 if (isByRef(param_ty, zcu)) {
5341 const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, "");5344 const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, "");
5342 _ = try self.wip.store(.normal, loaded, arg_ptr, alignment);5345 _ = try self.wip.store(.normal, loaded, arg_ptr, alignment);
...@@ -5359,7 +5362,7 @@ pub const FuncGen = struct {...@@ -5359,7 +5362,7 @@ pub const FuncGen = struct {
5359 // LLVM does not allow bitcasting structs so we must allocate5362 // LLVM does not allow bitcasting structs so we must allocate
5360 // a local, store as one type, and then load as another type.5363 // a local, store as one type, and then load as another type.
5361 const alignment = param_ty.abiAlignment(zcu).toLlvm();5364 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);
5363 _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment);5366 _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment);
5364 const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, "");5367 const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, "");
5365 try llvm_args.append(loaded);5368 try llvm_args.append(loaded);
...@@ -5495,7 +5498,7 @@ pub const FuncGen = struct {...@@ -5495,7 +5498,7 @@ pub const FuncGen = struct {
5495 .auto, .never_inline => .normal,5498 .auto, .never_inline => .normal,
5496 .never_tail => .notail,5499 .never_tail => .notail,
5497 .always_tail => .musttail,5500 .always_tail => .musttail,
5498 .async_kw, .no_async, .always_inline, .compile_time => unreachable,5501 .no_suspend, .always_inline, .compile_time => unreachable,
5499 },5502 },
5500 toLlvmCallConvTag(fn_info.cc, target).?,5503 toLlvmCallConvTag(fn_info.cc, target).?,
5501 try attributes.finish(&o.builder),5504 try attributes.finish(&o.builder),
...@@ -5734,7 +5737,7 @@ pub const FuncGen = struct {...@@ -5734,7 +5737,7 @@ pub const FuncGen = struct {
5734 const llvm_va_list_ty = try o.lowerType(va_list_ty);5737 const llvm_va_list_ty = try o.lowerType(va_list_ty);
57355738
5736 const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm();5739 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
5739 _ = try self.wip.callIntrinsic(.normal, .none, .va_copy, &.{dest_list.typeOfWip(&self.wip)}, &.{ dest_list, src_list }, "");5742 _ = try self.wip.callIntrinsic(.normal, .none, .va_copy, &.{dest_list.typeOfWip(&self.wip)}, &.{ dest_list, src_list }, "");
5740 return if (isByRef(va_list_ty, zcu))5743 return if (isByRef(va_list_ty, zcu))
...@@ -5759,7 +5762,7 @@ pub const FuncGen = struct {...@@ -5759,7 +5762,7 @@ pub const FuncGen = struct {
5759 const llvm_va_list_ty = try o.lowerType(va_list_ty);5762 const llvm_va_list_ty = try o.lowerType(va_list_ty);
57605763
5761 const result_alignment = va_list_ty.abiAlignment(pt.zcu).toLlvm();5764 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
5764 _ = try self.wip.callIntrinsic(.normal, .none, .va_start, &.{dest_list.typeOfWip(&self.wip)}, &.{dest_list}, "");5767 _ = try self.wip.callIntrinsic(.normal, .none, .va_start, &.{dest_list.typeOfWip(&self.wip)}, &.{dest_list}, "");
5765 return if (isByRef(va_list_ty, zcu))5768 return if (isByRef(va_list_ty, zcu))
...@@ -8037,7 +8040,7 @@ pub const FuncGen = struct {...@@ -8037,7 +8040,7 @@ pub const FuncGen = struct {
8037 self.ret_ptr8040 self.ret_ptr
8038 else brk: {8041 else brk: {
8039 const alignment = optional_ty.abiAlignment(zcu).toLlvm();8042 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);
8041 break :brk optional_ptr;8044 break :brk optional_ptr;
8042 };8045 };
80438046
...@@ -8074,7 +8077,7 @@ pub const FuncGen = struct {...@@ -8074,7 +8077,7 @@ pub const FuncGen = struct {
8074 self.ret_ptr8077 self.ret_ptr
8075 else brk: {8078 else brk: {
8076 const alignment = err_un_ty.abiAlignment(pt.zcu).toLlvm();8079 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);
8078 break :brk result_ptr;8081 break :brk result_ptr;
8079 };8082 };
80808083
...@@ -8113,7 +8116,7 @@ pub const FuncGen = struct {...@@ -8113,7 +8116,7 @@ pub const FuncGen = struct {
8113 self.ret_ptr8116 self.ret_ptr
8114 else brk: {8117 else brk: {
8115 const alignment = err_un_ty.abiAlignment(zcu).toLlvm();8118 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);
8117 break :brk result_ptr;8120 break :brk result_ptr;
8118 };8121 };
81198122
...@@ -8647,7 +8650,7 @@ pub const FuncGen = struct {...@@ -8647,7 +8650,7 @@ pub const FuncGen = struct {
86478650
8648 if (isByRef(inst_ty, zcu)) {8651 if (isByRef(inst_ty, zcu)) {
8649 const result_alignment = inst_ty.abiAlignment(zcu).toLlvm();8652 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);
8651 {8654 {
8652 const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, result_index, "");8655 const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, result_index, "");
8653 _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment);8656 _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment);
...@@ -9007,7 +9010,7 @@ pub const FuncGen = struct {...@@ -9007,7 +9010,7 @@ pub const FuncGen = struct {
90079010
9008 if (isByRef(dest_ty, zcu)) {9011 if (isByRef(dest_ty, zcu)) {
9009 const result_alignment = dest_ty.abiAlignment(zcu).toLlvm();9012 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);
9011 {9014 {
9012 const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, "");9015 const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, "");
9013 _ = try self.wip.store(.normal, result, field_ptr, result_alignment);9016 _ = try self.wip.store(.normal, result, field_ptr, result_alignment);
...@@ -9432,7 +9435,7 @@ pub const FuncGen = struct {...@@ -9432,7 +9435,7 @@ pub const FuncGen = struct {
9432 return self.ng.todo("implement bitcast vector to non-ref array", .{});9435 return self.ng.todo("implement bitcast vector to non-ref array", .{});
9433 }9436 }
9434 const alignment = inst_ty.abiAlignment(zcu).toLlvm();9437 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);
9436 const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8;9439 const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8;
9437 if (bitcast_ok) {9440 if (bitcast_ok) {
9438 _ = try self.wip.store(.normal, operand, array_ptr, alignment);9441 _ = try self.wip.store(.normal, operand, array_ptr, alignment);
...@@ -9493,7 +9496,7 @@ pub const FuncGen = struct {...@@ -9493,7 +9496,7 @@ pub const FuncGen = struct {
94939496
9494 if (result_is_ref) {9497 if (result_is_ref) {
9495 const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm();9498 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);
9497 _ = try self.wip.store(.normal, operand, result_ptr, alignment);9500 _ = try self.wip.store(.normal, operand, result_ptr, alignment);
9498 return result_ptr;9501 return result_ptr;
9499 }9502 }
...@@ -9506,7 +9509,7 @@ pub const FuncGen = struct {...@@ -9506,7 +9509,7 @@ pub const FuncGen = struct {
9506 // but LLVM won't let us bitcast struct values or vectors with padding bits.9509 // but LLVM won't let us bitcast struct values or vectors with padding bits.
9507 // Therefore, we store operand to alloca, then load for result.9510 // Therefore, we store operand to alloca, then load for result.
9508 const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm();9511 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);
9510 _ = try self.wip.store(.normal, operand, result_ptr, alignment);9513 _ = try self.wip.store(.normal, operand, result_ptr, alignment);
9511 return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, "");9514 return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, "");
9512 }9515 }
...@@ -9615,9 +9618,9 @@ pub const FuncGen = struct {...@@ -9615,9 +9618,9 @@ pub const FuncGen = struct {
9615 if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu))9618 if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu))
9616 return (try o.lowerPtrToVoid(ptr_ty)).toValue();9619 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);
9619 const alignment = ptr_ty.ptrAlignment(zcu).toLlvm();9622 const alignment = ptr_ty.ptrAlignment(zcu).toLlvm();
9620 return self.buildAllocaWorkaround(pointee_type, alignment);9623 return self.buildAlloca(pointee_llvm_ty, alignment);
9621 }9624 }
96229625
9623 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {9626 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
...@@ -9629,9 +9632,9 @@ pub const FuncGen = struct {...@@ -9629,9 +9632,9 @@ pub const FuncGen = struct {
9629 if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu))9632 if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu))
9630 return (try o.lowerPtrToVoid(ptr_ty)).toValue();9633 return (try o.lowerPtrToVoid(ptr_ty)).toValue();
9631 if (self.ret_ptr != .none) return self.ret_ptr;9634 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);
9633 const alignment = ptr_ty.ptrAlignment(zcu).toLlvm();9636 const alignment = ptr_ty.ptrAlignment(zcu).toLlvm();
9634 return self.buildAllocaWorkaround(ret_ty, alignment);9637 return self.buildAlloca(ret_llvm_ty, alignment);
9635 }9638 }
96369639
9637 /// Use this instead of builder.buildAlloca, because this function makes sure to9640 /// Use this instead of builder.buildAlloca, because this function makes sure to
...@@ -9645,16 +9648,6 @@ pub const FuncGen = struct {...@@ -9645,16 +9648,6 @@ pub const FuncGen = struct {
9645 return buildAllocaInner(&self.wip, llvm_ty, alignment, target);9648 return buildAllocaInner(&self.wip, llvm_ty, alignment, target);
9646 }9649 }
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
9658 fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value {9651 fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value {
9659 const o = self.ng.object;9652 const o = self.ng.object;
9660 const pt = o.pt;9653 const pt = o.pt;
...@@ -10693,7 +10686,7 @@ pub const FuncGen = struct {...@@ -10693,7 +10686,7 @@ pub const FuncGen = struct {
10693 const llvm_result_ty = accum_init.typeOfWip(&self.wip);10686 const llvm_result_ty = accum_init.typeOfWip(&self.wip);
1069410687
10695 // Allocate and initialize our mutable variables10688 // 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);
10697 _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default);10690 _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default);
10698 const accum_ptr = try self.buildAlloca(llvm_result_ty, .default);10691 const accum_ptr = try self.buildAlloca(llvm_result_ty, .default);
10699 _ = try self.wip.store(.normal, accum_init, accum_ptr, .default);10692 _ = try self.wip.store(.normal, accum_init, accum_ptr, .default);
...@@ -10906,7 +10899,7 @@ pub const FuncGen = struct {...@@ -10906,7 +10899,7 @@ pub const FuncGen = struct {
10906 // TODO in debug builds init to undef so that the padding will be 0xaa10899 // TODO in debug builds init to undef so that the padding will be 0xaa
10907 // even if we fully populate the fields.10900 // even if we fully populate the fields.
10908 const alignment = result_ty.abiAlignment(zcu).toLlvm();10901 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
10911 for (elements, 0..) |elem, i| {10904 for (elements, 0..) |elem, i| {
10912 if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue;10905 if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue;
...@@ -10943,7 +10936,7 @@ pub const FuncGen = struct {...@@ -10943,7 +10936,7 @@ pub const FuncGen = struct {
10943 const llvm_usize = try o.lowerType(Type.usize);10936 const llvm_usize = try o.lowerType(Type.usize);
10944 const usize_zero = try o.builder.intValue(llvm_usize, 0);10937 const usize_zero = try o.builder.intValue(llvm_usize, 0);
10945 const alignment = result_ty.abiAlignment(zcu).toLlvm();10938 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
10948 const array_info = result_ty.arrayInfo(zcu);10941 const array_info = result_ty.arrayInfo(zcu);
10949 const elem_ptr_ty = try pt.ptrType(.{10942 const elem_ptr_ty = try pt.ptrType(.{
...@@ -11018,7 +11011,7 @@ pub const FuncGen = struct {...@@ -11018,7 +11011,7 @@ pub const FuncGen = struct {
11018 // We must construct the correct unnamed struct type here, in order to then set11011 // We must construct the correct unnamed struct type here, in order to then set
11019 // the fields appropriately.11012 // the fields appropriately.
11020 const alignment = layout.abi_align.toLlvm();11013 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);
11022 const llvm_payload = try self.resolveInst(extra.init);11015 const llvm_payload = try self.resolveInst(extra.init);
11023 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]);11016 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]);
11024 const field_llvm_ty = try o.lowerType(field_ty);11017 const field_llvm_ty = try o.lowerType(field_ty);
...@@ -11315,7 +11308,7 @@ pub const FuncGen = struct {...@@ -11315,7 +11308,7 @@ pub const FuncGen = struct {
1131511308
11316 if (isByRef(optional_ty, zcu)) {11309 if (isByRef(optional_ty, zcu)) {
11317 const payload_alignment = optional_ty.abiAlignment(pt.zcu).toLlvm();11310 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
11320 {11313 {
11321 const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, "");11314 const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, "");
...@@ -11458,10 +11451,10 @@ pub const FuncGen = struct {...@@ -11458,10 +11451,10 @@ pub const FuncGen = struct {
11458 ) !Builder.Value {11451 ) !Builder.Value {
11459 const o = fg.ng.object;11452 const o = fg.ng.object;
11460 const pt = o.pt;11453 const pt = o.pt;
11461 //const pointee_llvm_ty = try o.lowerType(pointee_type);11454 const pointee_llvm_ty = try o.lowerType(pointee_type);
11462 const result_align = InternPool.Alignment.fromLlvm(ptr_alignment)11455 const result_align = InternPool.Alignment.fromLlvm(ptr_alignment)
11463 .max(pointee_type.abiAlignment(pt.zcu)).toLlvm();11456 .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);
11465 const size_bytes = pointee_type.abiSize(pt.zcu);11458 const size_bytes = pointee_type.abiSize(pt.zcu);
11466 _ = try fg.wip.callMemCpy(11459 _ = try fg.wip.callMemCpy(
11467 result_ptr,11460 result_ptr,
...@@ -11522,7 +11515,7 @@ pub const FuncGen = struct {...@@ -11522,7 +11515,7 @@ pub const FuncGen = struct {
1152211515
11523 if (isByRef(elem_ty, zcu)) {11516 if (isByRef(elem_ty, zcu)) {
11524 const result_align = elem_ty.abiAlignment(zcu).toLlvm();11517 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
11527 const same_size_int = try o.builder.intType(@intCast(elem_bits));11520 const same_size_int = try o.builder.intType(@intCast(elem_bits));
11528 const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, "");11521 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...@@ -11878,7 +11871,7 @@ fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const s
11878 }11871 }
11879 return switch (cc_tag) {11872 return switch (cc_tag) {
11880 .@"inline" => unreachable,11873 .@"inline" => unreachable,
11881 .auto, .@"async" => .fastcc,11874 .auto, .async => .fastcc,
11882 .naked => .ccc,11875 .naked => .ccc,
11883 .x86_64_sysv => .x86_64_sysvcc,11876 .x86_64_sysv => .x86_64_sysvcc,
11884 .x86_64_win => .win64cc,11877 .x86_64_win => .win64cc,
...@@ -12386,7 +12379,7 @@ const ParamTypeIterator = struct {...@@ -12386,7 +12379,7 @@ const ParamTypeIterator = struct {
12386 return .byval;12379 return .byval;
12387 }12380 }
12388 },12381 },
12389 .@"async" => {12382 .async => {
12390 @panic("TODO implement async function lowering in the LLVM backend");12383 @panic("TODO implement async function lowering in the LLVM backend");
12391 },12384 },
12392 .x86_64_sysv => return it.nextSystemV(ty),12385 .x86_64_sysv => return it.nextSystemV(ty),
...@@ -12641,7 +12634,7 @@ fn ccAbiPromoteInt(...@@ -12641,7 +12634,7 @@ fn ccAbiPromoteInt(
12641) ?std.builtin.Signedness {12634) ?std.builtin.Signedness {
12642 const target = zcu.getTarget();12635 const target = zcu.getTarget();
12643 switch (cc) {12636 switch (cc) {
12644 .auto, .@"inline", .@"async" => return null,12637 .auto, .@"inline", .async => return null,
12645 else => {},12638 else => {},
12646 }12639 }
12647 const int_info = switch (ty.zigTypeTag(zcu)) {12640 const int_info = switch (ty.zigTypeTag(zcu)) {
src/codegen/llvm/bindings.zig+1
...@@ -79,6 +79,7 @@ pub const TargetMachine = opaque {...@@ -79,6 +79,7 @@ pub const TargetMachine = opaque {
79 data_sections: bool,79 data_sections: bool,
80 float_abi: FloatABI,80 float_abi: FloatABI,
81 abi_name: ?[*:0]const u8,81 abi_name: ?[*:0]const u8,
82 emulated_tls: bool,
82 ) *TargetMachine;83 ) *TargetMachine;
8384
84 pub const dispose = LLVMDisposeTargetMachine;85 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 {...@@ -1259,11 +1259,13 @@ const NavGen = struct {
1259 }1259 }
12601260
1261 // Turn a Zig type's name into a cache reference.1261 // 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 {
1263 var aw: std.io.Writer.Allocating = .init(self.gpa);1263 var aw: std.io.Writer.Allocating = .init(self.gpa);
1264 defer aw.deinit();1264 defer aw.deinit();
1265 ty.print(&aw.interface, self.pt) catch return error.OutOfMemory;1265 ty.print(&aw.writer, self.pt) catch |err| switch (err) {
1266 return aw.toOwnedSlice();1266 error.WriteFailed => return error.OutOfMemory,
1267 };
1268 return try aw.toOwnedSlice();
1267 }1269 }
12681270
1269 /// Create an integer type suitable for storing at least 'bits' bits.1271 /// Create an integer type suitable for storing at least 'bits' bits.
src/codegen/spirv/spec.zig+4-4
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1//! This file is auto-generated by tools/gen_spirv_spec.zig.1//! This file is auto-generated by tools/gen_spirv_spec.zig.
22
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;
45
5pub const Version = packed struct(Word) {6pub const Version = packed struct(Word) {
6 padding: u8 = 0,7 padding: u8 = 0,
...@@ -18,11 +19,10 @@ pub const IdResult = enum(Word) {...@@ -18,11 +19,10 @@ pub const IdResult = enum(Word) {
18 none,19 none,
19 _,20 _,
2021
21 pub fn format(self: IdResult, bw: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void {22 pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void {
22 comptime std.debug.assert(fmt.len == 0);
23 switch (self) {23 switch (self) {
24 .none => try bw.writeAll("(none)"),24 .none => try writer.writeAll("(none)"),
25 else => try bw.print("%{}", .{@intFromEnum(self)}),25 else => try writer.print("%{d}", .{@intFromEnum(self)}),
26 }26 }
27 }27 }
28};28};
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 {...@@ -15,6 +15,14 @@ pub fn LinearFifo(comptime T: type) type {
15 count: usize,15 count: usize,
1616
17 const Self = @This();17 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
19 pub fn init(allocator: Allocator) Self {27 pub fn init(allocator: Allocator) Self {
20 return .{28 return .{
...@@ -160,7 +168,7 @@ pub fn LinearFifo(comptime T: type) type {...@@ -160,7 +168,7 @@ pub fn LinearFifo(comptime T: type) type {
160 }168 }
161169
162 /// Same as `read` except it returns an error union170 /// 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.
164 fn readFn(self: *Self, dest: []u8) error{}!usize {172 fn readFn(self: *Self, dest: []u8) error{}!usize {
165 return self.read(dest);173 return self.read(dest);
166 }174 }
...@@ -241,7 +249,7 @@ pub fn LinearFifo(comptime T: type) type {...@@ -241,7 +249,7 @@ pub fn LinearFifo(comptime T: type) type {
241 }249 }
242250
243 /// Same as `write` except it returns the number of bytes written, which is always the same251 /// 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.
245 fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {253 fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {
246 try self.write(bytes);254 try self.write(bytes);
247 return bytes.len;255 return bytes.len;
src/dev.zig+1
...@@ -154,6 +154,7 @@ pub const Env = enum {...@@ -154,6 +154,7 @@ pub const Env = enum {
154 else => Env.ast_gen.supports(feature),154 else => Env.ast_gen.supports(feature),
155 },155 },
156 .cbe => switch (feature) {156 .cbe => switch (feature) {
157 .legalize,
157 .c_backend,158 .c_backend,
158 .c_linker,159 .c_linker,
159 => true,160 => true,
src/libs/freebsd.zig+2-2
...@@ -497,13 +497,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye...@@ -497,13 +497,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
497 .lt => continue,497 .lt => continue,
498 .gt => {498 .gt => {
499 // TODO Expose via compile error mechanism instead of log.499 // 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});
501 return error.InvalidTargetLibCVersion;501 return error.InvalidTargetLibCVersion;
502 },502 },
503 }503 }
504 } else blk: {504 } else blk: {
505 const latest_index = metadata.all_versions.len - 1;505 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}", .{
507 target_version, metadata.all_versions[latest_index],507 target_version, metadata.all_versions[latest_index],
508 });508 });
509 break :blk latest_index;509 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...@@ -325,7 +325,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
325 // See the `-fno-exceptions` logic for WASI.325 // See the `-fno-exceptions` logic for WASI.
326 // The old 32-bit x86 variant of SEH doesn't use tables.326 // The old 32-bit x86 variant of SEH doesn't use tables.
327 const unwind_tables: std.builtin.UnwindTables =327 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
330 const config = Compilation.Config.resolve(.{330 const config = Compilation.Config.resolve(.{
331 .output_mode = output_mode,331 .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...@@ -48,7 +48,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
48 const optimize_mode = comp.compilerRtOptMode();48 const optimize_mode = comp.compilerRtOptMode();
49 const strip = comp.compilerRtStrip();49 const strip = comp.compilerRtStrip();
50 const unwind_tables: std.builtin.UnwindTables =50 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;
52 const link_libcpp = target.os.tag.isDarwin();52 const link_libcpp = target.os.tag.isDarwin();
5353
54 const config = Compilation.Config.resolve(.{54 const config = Compilation.Config.resolve(.{
...@@ -268,7 +268,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo...@@ -268,7 +268,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
268 const skip_linker_dependencies = !target.os.tag.isDarwin();268 const skip_linker_dependencies = !target.os.tag.isDarwin();
269 const linker_allow_shlib_undefined = target.os.tag.isDarwin();269 const linker_allow_shlib_undefined = target.os.tag.isDarwin();
270 const install_name = if (target.os.tag.isDarwin())270 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)
272 else272 else
273 null;273 null;
274 // Workaround for https://github.com/llvm/llvm-project/issues/97627274 // 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...@@ -29,7 +29,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
29 const output_mode = .Lib;29 const output_mode = .Lib;
30 const target = &comp.root_mod.resolved_target.result;30 const target = &comp.root_mod.resolved_target.result;
31 const unwind_tables: std.builtin.UnwindTables =31 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;
33 const config = Compilation.Config.resolve(.{33 const config = Compilation.Config.resolve(.{
34 .output_mode = output_mode,34 .output_mode = output_mode,
35 .resolved_target = comp.root_mod.resolved_target,35 .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...@@ -29,7 +29,7 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
29 const target = comp.getTarget();29 const target = comp.getTarget();
3030
31 // The old 32-bit x86 variant of SEH doesn't use tables.31 // 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
34 switch (crt_file) {34 switch (crt_file) {
35 .crt2_o => {35 .crt2_o => {
...@@ -325,7 +325,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -325,7 +325,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
325325
326 for (aro_comp.diagnostics.list.items) |diagnostic| {326 for (aro_comp.diagnostics.list.items) |diagnostic| {
327 if (diagnostic.kind == .@"fatal error" or diagnostic.kind == .@"error") {327 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()));
329 return error.AroPreprocessorFailed;329 return error.AroPreprocessorFailed;
330 }330 }
331 }331 }
...@@ -334,7 +334,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -334,7 +334,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
334 // new scope to ensure definition file is written before passing the path to WriteImportLibrary334 // new scope to ensure definition file is written before passing the path to WriteImportLibrary
335 const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true });335 const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true });
336 defer def_final_file.close();336 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);
338 }338 }
339339
340 const lib_final_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename });340 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{...@@ -930,7 +930,6 @@ const mingw32_x86_src = [_][]const u8{
930 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S",930 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S",
931 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c",931 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c",
932 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c",932 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c",
933 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S",
934 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c",933 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c",
935 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c",934 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c",
936 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S",935 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S",
...@@ -974,7 +973,6 @@ const mingw32_x86_32_src = [_][]const u8{...@@ -974,7 +973,6 @@ const mingw32_x86_32_src = [_][]const u8{
974 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c",973 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c",
975 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c",974 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c",
976 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S",975 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S",
977 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S",
978 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c",976 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c",
979};977};
980978
...@@ -1013,6 +1011,7 @@ const mingw32_winpthreads_src = [_][]const u8{...@@ -1013,6 +1011,7 @@ const mingw32_winpthreads_src = [_][]const u8{
1013 "winpthreads" ++ path.sep_str ++ "thread.c",1011 "winpthreads" ++ path.sep_str ++ "thread.c",
1014};1012};
10151013
1014// Note: kernel32 and ntdll are always linked even without targeting MinGW-w64.
1016pub const always_link_libs = [_][]const u8{1015pub const always_link_libs = [_][]const u8{
1017 "api-ms-win-crt-conio-l1-1-0",1016 "api-ms-win-crt-conio-l1-1-0",
1018 "api-ms-win-crt-convert-l1-1-0",1017 "api-ms-win-crt-convert-l1-1-0",
...@@ -1030,8 +1029,6 @@ pub const always_link_libs = [_][]const u8{...@@ -1030,8 +1029,6 @@ pub const always_link_libs = [_][]const u8{
1030 "api-ms-win-crt-time-l1-1-0",1029 "api-ms-win-crt-time-l1-1-0",
1031 "api-ms-win-crt-utility-l1-1-0",1030 "api-ms-win-crt-utility-l1-1-0",
1032 "advapi32",1031 "advapi32",
1033 "kernel32",
1034 "ntdll",
1035 "shell32",1032 "shell32",
1036 "user32",1033 "user32",
1037};1034};
src/libs/musl.zig-12
...@@ -821,8 +821,6 @@ const src_files = [_][]const u8{...@@ -821,8 +821,6 @@ const src_files = [_][]const u8{
821 "musl/src/malloc/replaced.c",821 "musl/src/malloc/replaced.c",
822 "musl/src/math/aarch64/ceil.c",822 "musl/src/math/aarch64/ceil.c",
823 "musl/src/math/aarch64/ceilf.c",823 "musl/src/math/aarch64/ceilf.c",
824 "musl/src/math/aarch64/floor.c",
825 "musl/src/math/aarch64/floorf.c",
826 "musl/src/math/aarch64/fma.c",824 "musl/src/math/aarch64/fma.c",
827 "musl/src/math/aarch64/fmaf.c",825 "musl/src/math/aarch64/fmaf.c",
828 "musl/src/math/aarch64/fmax.c",826 "musl/src/math/aarch64/fmax.c",
...@@ -912,9 +910,6 @@ const src_files = [_][]const u8{...@@ -912,9 +910,6 @@ const src_files = [_][]const u8{
912 "musl/src/math/fdiml.c",910 "musl/src/math/fdiml.c",
913 "musl/src/math/finite.c",911 "musl/src/math/finite.c",
914 "musl/src/math/finitef.c",912 "musl/src/math/finitef.c",
915 "musl/src/math/floor.c",
916 "musl/src/math/floorf.c",
917 "musl/src/math/floorl.c",
918 "musl/src/math/fma.c",913 "musl/src/math/fma.c",
919 "musl/src/math/fmaf.c",914 "musl/src/math/fmaf.c",
920 "musl/src/math/fmal.c",915 "musl/src/math/fmal.c",
...@@ -955,8 +950,6 @@ const src_files = [_][]const u8{...@@ -955,8 +950,6 @@ const src_files = [_][]const u8{
955 "musl/src/math/i386/exp_ld.s",950 "musl/src/math/i386/exp_ld.s",
956 "musl/src/math/i386/expl.s",951 "musl/src/math/i386/expl.s",
957 "musl/src/math/i386/expm1l.s",952 "musl/src/math/i386/expm1l.s",
958 "musl/src/math/i386/floorf.s",
959 "musl/src/math/i386/floorl.s",
960 "musl/src/math/i386/floor.s",953 "musl/src/math/i386/floor.s",
961 "musl/src/math/i386/fmod.c",954 "musl/src/math/i386/fmod.c",
962 "musl/src/math/i386/fmodf.c",955 "musl/src/math/i386/fmodf.c",
...@@ -1089,8 +1082,6 @@ const src_files = [_][]const u8{...@@ -1089,8 +1082,6 @@ const src_files = [_][]const u8{
1089 "musl/src/math/pow_data.c",1082 "musl/src/math/pow_data.c",
1090 "musl/src/math/powerpc64/ceil.c",1083 "musl/src/math/powerpc64/ceil.c",
1091 "musl/src/math/powerpc64/ceilf.c",1084 "musl/src/math/powerpc64/ceilf.c",
1092 "musl/src/math/powerpc64/floor.c",
1093 "musl/src/math/powerpc64/floorf.c",
1094 "musl/src/math/powerpc64/fma.c",1085 "musl/src/math/powerpc64/fma.c",
1095 "musl/src/math/powerpc64/fmaf.c",1086 "musl/src/math/powerpc64/fmaf.c",
1096 "musl/src/math/powerpc64/fmax.c",1087 "musl/src/math/powerpc64/fmax.c",
...@@ -1153,9 +1144,6 @@ const src_files = [_][]const u8{...@@ -1153,9 +1144,6 @@ const src_files = [_][]const u8{
1153 "musl/src/math/s390x/ceil.c",1144 "musl/src/math/s390x/ceil.c",
1154 "musl/src/math/s390x/ceilf.c",1145 "musl/src/math/s390x/ceilf.c",
1155 "musl/src/math/s390x/ceill.c",1146 "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",
1159 "musl/src/math/s390x/fma.c",1147 "musl/src/math/s390x/fma.c",
1160 "musl/src/math/s390x/fmaf.c",1148 "musl/src/math/s390x/fmaf.c",
1161 "musl/src/math/s390x/nearbyint.c",1149 "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...@@ -442,13 +442,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
442 .lt => continue,442 .lt => continue,
443 .gt => {443 .gt => {
444 // TODO Expose via compile error mechanism instead of log.444 // 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});
446 return error.InvalidTargetLibCVersion;446 return error.InvalidTargetLibCVersion;
447 },447 },
448 }448 }
449 } else blk: {449 } else blk: {
450 const latest_index = metadata.all_versions.len - 1;450 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}", .{
452 target_version, metadata.all_versions[latest_index],452 target_version, metadata.all_versions[latest_index],
453 });453 });
454 break :blk latest_index;454 break :blk latest_index;
src/libs/wasi_libc.zig+75-110
...@@ -10,43 +10,8 @@ pub const CrtFile = enum {...@@ -10,43 +10,8 @@ pub const CrtFile = enum {
10 crt1_reactor_o,10 crt1_reactor_o,
11 crt1_command_o,11 crt1_command_o,
12 libc_a,12 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,
18};13};
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
50pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile {15pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile {
51 return switch (wasi_exec_model) {16 return switch (wasi_exec_model) {
52 .reactor => CrtFile.crt1_reactor_o,17 .reactor => CrtFile.crt1_reactor_o,
...@@ -157,87 +122,57 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre...@@ -157,87 +122,57 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
157 }122 }
158 }123 }
159124
160 try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items, .{});125 {
161 },126 // Compile libdl.
162127 var args = std.ArrayList([]const u8).init(arena);
163 .libdl_a => {128 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
164 var args = std.ArrayList([]const u8).init(arena);129 try addLibcBottomHalfIncludes(comp, arena, &args);
165 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
166 try addLibcBottomHalfIncludes(comp, arena, &args);
167130
168 var emu_dl_sources = std.ArrayList(Compilation.CSourceFile).init(arena);131 for (emulated_dl_src_files) |file_path| {
169 for (emulated_dl_src_files) |file_path| {132 try libc_sources.append(.{
170 try emu_dl_sources.append(.{133 .src_path = try comp.dirs.zig_lib.join(arena, &.{
171 .src_path = try comp.dirs.zig_lib.join(arena, &.{134 "libc", try sanitize(arena, file_path),
172 "libc", try sanitize(arena, file_path),135 }),
173 }),136 .extra_flags = args.items,
174 .extra_flags = args.items,137 .owner = undefined,
175 .owner = undefined,138 });
176 });139 }
177 }140 }
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);142 {
187 for (emulated_process_clocks_src_files) |file_path| {143 // Compile libwasi-emulated-process-clocks.
188 try emu_clocks_sources.append(.{144 var args = std.ArrayList([]const u8).init(arena);
189 .src_path = try comp.dirs.zig_lib.join(arena, &.{145 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
190 "libc", try sanitize(arena, file_path),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",
191 }),154 }),
192 .extra_flags = args.items,
193 .owner = undefined,
194 });155 });
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);157 for (emulated_process_clocks_src_files) |file_path| {
204 for (emulated_getpid_src_files) |file_path| {158 try libc_sources.append(.{
205 try emu_getpid_sources.append(.{159 .src_path = try comp.dirs.zig_lib.join(arena, &.{
206 .src_path = try comp.dirs.zig_lib.join(arena, &.{160 "libc", try sanitize(arena, file_path),
207 "libc", try sanitize(arena, file_path),161 }),
208 }),162 .extra_flags = args.items,
209 .extra_flags = args.items,163 .owner = undefined,
210 .owner = undefined,164 });
211 });165 }
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 });
229 }166 }
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
235 {168 {
169 // Compile libwasi-emulated-getpid.
236 var args = std.ArrayList([]const u8).init(arena);170 var args = std.ArrayList([]const u8).init(arena);
237 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });171 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| {174 for (emulated_getpid_src_files) |file_path| {
240 try emu_signal_sources.append(.{175 try libc_sources.append(.{
241 .src_path = try comp.dirs.zig_lib.join(arena, &.{176 .src_path = try comp.dirs.zig_lib.join(arena, &.{
242 "libc", try sanitize(arena, file_path),177 "libc", try sanitize(arena, file_path),
243 }),178 }),
...@@ -248,13 +183,13 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre...@@ -248,13 +183,13 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
248 }183 }
249184
250 {185 {
186 // Compile libwasi-emulated-mman.
251 var args = std.ArrayList([]const u8).init(arena);187 var args = std.ArrayList([]const u8).init(arena);
252 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });188 try addCCArgs(comp, arena, &args, .{ .want_O3 = true });
253 try addLibcTopHalfIncludes(comp, arena, &args);189 try addLibcBottomHalfIncludes(comp, arena, &args);
254 try args.append("-D_WASI_EMULATED_SIGNAL");
255190
256 for (emulated_signal_top_half_src_files) |file_path| {191 for (emulated_mman_src_files) |file_path| {
257 try emu_signal_sources.append(.{192 try libc_sources.append(.{
258 .src_path = try comp.dirs.zig_lib.join(arena, &.{193 .src_path = try comp.dirs.zig_lib.join(arena, &.{
259 "libc", try sanitize(arena, file_path),194 "libc", try sanitize(arena, file_path),
260 }),195 }),
...@@ -264,7 +199,38 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre...@@ -264,7 +199,38 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre
264 }199 }
265 }200 }
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, .{});
268 },234 },
269 }235 }
270}236}
...@@ -754,7 +720,6 @@ const libc_top_half_src_files = [_][]const u8{...@@ -754,7 +720,6 @@ const libc_top_half_src_files = [_][]const u8{
754 "musl/src/math/fdiml.c",720 "musl/src/math/fdiml.c",
755 "musl/src/math/finite.c",721 "musl/src/math/finite.c",
756 "musl/src/math/finitef.c",722 "musl/src/math/finitef.c",
757 "musl/src/math/floorl.c",
758 "musl/src/math/fma.c",723 "musl/src/math/fma.c",
759 "musl/src/math/fmaf.c",724 "musl/src/math/fmaf.c",
760 "musl/src/math/fmaxl.c",725 "musl/src/math/fmaxl.c",
src/link.zig+8-7
...@@ -838,8 +838,10 @@ pub const File = struct {...@@ -838,8 +838,10 @@ pub const File = struct {
838 const cached_pp_file_path = the_key.status.success.object_path;838 const cached_pp_file_path = the_key.status.success.object_path;
839 cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| {839 cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| {
840 const diags = &base.comp.link_diags;840 const diags = &base.comp.link_diags;
841 return diags.fail("failed to copy '{f'}' to '{f'}': {s}", .{841 return diags.fail("failed to copy '{f}' to '{f}': {s}", .{
842 @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err),842 std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar),
843 std.fmt.alt(@as(Path, emit), .formatEscapeChar),
844 @errorName(err),
843 });845 });
844 };846 };
845 return;847 return;
...@@ -2095,8 +2097,8 @@ fn resolvePathInputLib(...@@ -2095,8 +2097,8 @@ fn resolvePathInputLib(
2095 }) {2097 }) {
2096 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {2098 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2097 error.FileNotFound => return .no_match,2099 error.FileNotFound => return .no_match,
2098 else => |e| fatal("unable to search for {s} library '{f'}': {s}", .{2100 else => |e| fatal("unable to search for {s} library '{f}': {s}", .{
2099 @tagName(link_mode), test_path, @errorName(e),2101 @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e),
2100 }),2102 }),
2101 };2103 };
2102 errdefer file.close();2104 errdefer file.close();
...@@ -2105,9 +2107,8 @@ fn resolvePathInputLib(...@@ -2105,9 +2107,8 @@ fn resolvePathInputLib(
2105 var br = fr.interface().unbuffered();2107 var br = fr.interface().unbuffered();
2106 ok: {2108 ok: {
2107 br.readSlice(ld_script_bytes.items) catch |err| switch (err) {2109 br.readSlice(ld_script_bytes.items) catch |err| switch (err) {
2108 error.ReadFailed => fatal("failed to read '{f'}': {s}", .{2110 error.ReadFailed => fatal("failed to read '{f}': {s}", .{
2109 test_path,2111 test_path, @errorName(fr.err.?),
2110 @errorName(fr.err.?),
2111 }),2112 }),
2112 error.EndOfStream => break :ok,2113 error.EndOfStream => break :ok,
2113 };2114 };
src/link/C.zig+40-32
...@@ -63,6 +63,14 @@ const String = extern struct {...@@ -63,6 +63,14 @@ const String = extern struct {
63 .start = 0,63 .start = 0,
64 .len = 0,64 .len = 0,
65 };65 };
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 }
66};74};
6775
68/// Per-declaration data.76/// Per-declaration data.
...@@ -205,8 +213,10 @@ pub fn updateFunc(...@@ -205,8 +213,10 @@ pub fn updateFunc(
205 .ctype_pool = mir.c.ctype_pool.move(),213 .ctype_pool = mir.c.ctype_pool.move(),
206 .lazy_fns = mir.c.lazy_fns.move(),214 .lazy_fns = mir.c.lazy_fns.move(),
207 };215 };
208 gop.value_ptr.fwd_decl = try self.addString(&.{&function.object.dg.fwd_decl});216 gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl);
209 gop.value_ptr.code = try self.addString(&.{ &function.object.code_header, &function.object.code });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);
210 try self.addUavsFromCodegen(&mir.c.uavs);220 try self.addUavsFromCodegen(&mir.c.uavs);
211}221}
212222
...@@ -232,8 +242,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {...@@ -232,8 +242,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {
232 .code = undefined,242 .code = undefined,
233 .indent_counter = 0,243 .indent_counter = 0,
234 };244 };
235 object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf);245 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
236 object.code.initOwnedSlice(gpa, self.code_buf);246 object.code = .initOwnedSlice(gpa, self.code_buf);
237 defer {247 defer {
238 object.dg.uavs.deinit(gpa);248 object.dg.uavs.deinit(gpa);
239 object.dg.ctype_pool.deinit(object.dg.gpa);249 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 {...@@ -259,8 +269,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {
259269
260 object.dg.ctype_pool.freeUnusedCapacity(gpa);270 object.dg.ctype_pool.freeUnusedCapacity(gpa);
261 self.uavs.values()[i] = .{271 self.uavs.values()[i] = .{
262 .fwd_decl = try self.addString(&.{&object.dg.fwd_decl}),272 .fwd_decl = try self.addString(object.dg.fwd_decl.getWritten()),
263 .code = try self.addString(&.{&object.code}),273 .code = try self.addString(object.code.getWritten()),
264 .ctype_pool = object.dg.ctype_pool.move(),274 .ctype_pool = object.dg.ctype_pool.move(),
265 };275 };
266}276}
...@@ -307,8 +317,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l...@@ -307,8 +317,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l
307 .code = undefined,317 .code = undefined,
308 .indent_counter = 0,318 .indent_counter = 0,
309 };319 };
310 object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf);320 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
311 object.code.initOwnedSlice(gpa, self.code_buf);321 object.code = .initOwnedSlice(gpa, self.code_buf);
312 defer {322 defer {
313 object.dg.uavs.deinit(gpa);323 object.dg.uavs.deinit(gpa);
314 ctype_pool.* = object.dg.ctype_pool.move();324 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...@@ -326,8 +336,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l
326 },336 },
327 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,337 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
328 };338 };
329 gop.value_ptr.fwd_decl = try self.addString(&.{&object.dg.fwd_decl});339 gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.getWritten());
330 gop.value_ptr.code = try self.addString(&.{&object.code});340 gop.value_ptr.code = try self.addString(object.code.getWritten());
331 try self.addUavsFromCodegen(&object.dg.uavs);341 try self.addUavsFromCodegen(&object.dg.uavs);
332}342}
333343
...@@ -339,12 +349,12 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn...@@ -339,12 +349,12 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn
339 _ = ti_id;349 _ = ti_id;
340}350}
341351
342fn abiDefines(bw: *std.io.BufferedWriter, target: std.Target) !void {352fn abiDefines(w: *std.io.Writer, target: *const std.Target) !void {
343 switch (target.abi) {353 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"),
345 else => {},355 else => {},
346 }356 }
347 try bw.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{357 try w.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{
348 target.cMaxIntAlignment(),358 target.cMaxIntAlignment(),
349 });359 });
350}360}
...@@ -391,10 +401,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P...@@ -391,10 +401,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
391 };401 };
392 defer f.deinit(gpa);402 defer f.deinit(gpa);
393403
394 var abi_defines_aw: std.io.AllocatingWriter = undefined;404 var abi_defines_aw: std.io.Writer.Allocating = .init(gpa);
395 abi_defines_aw.init(gpa);
396 defer abi_defines_aw.deinit();405 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) {
398 error.WriteFailed => return error.OutOfMemory,407 error.WriteFailed => return error.OutOfMemory,
399 };408 };
400409
...@@ -407,10 +416,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P...@@ -407,10 +416,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
407 const ctypes_index = f.all_buffers.items.len;416 const ctypes_index = f.all_buffers.items.len;
408 f.all_buffers.items.len += 1;417 f.all_buffers.items.len += 1;
409418
410 var asm_aw: std.io.AllocatingWriter = undefined;419 var asm_aw: std.io.Writer.Allocating = .init(gpa);
411 asm_aw.init(gpa);
412 defer asm_aw.deinit();420 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) {
414 error.WriteFailed => return error.OutOfMemory,422 error.WriteFailed => return error.OutOfMemory,
415 };423 };
416 f.appendBufAssumeCapacity(asm_aw.getWritten());424 f.appendBufAssumeCapacity(asm_aw.getWritten());
...@@ -501,11 +509,11 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P...@@ -501,11 +509,11 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
501509
502 const file = self.base.file.?;510 const file = self.base.file.?;
503 file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)});511 file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)});
504 var fw = file.writer();512 var fw = file.writer(&.{});
505 var bw = fw.interface().unbuffered();513 var w = &fw.interface;
506 bw.writeVecAll(f.all_buffers.items) catch |err| switch (err) {514 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
507 error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{515 error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{
508 self.base.emit, @errorName(fw.err.?),516 std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?),
509 }),517 }),
510 };518 };
511}519}
...@@ -575,8 +583,8 @@ fn flushCTypes(...@@ -575,8 +583,8 @@ fn flushCTypes(
575 try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len);583 try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len);
576 defer global_from_decl_map.clearRetainingCapacity();584 defer global_from_decl_map.clearRetainingCapacity();
577585
578 var ctypes_aw: std.io.AllocatingWriter = undefined;586 var ctypes_aw: std.io.Writer.Allocating = .fromArrayList(gpa, &f.ctypes);
579 const ctypes_bw = ctypes_aw.fromArrayList(gpa, &f.ctypes);587 const ctypes_bw = &ctypes_aw.writer;
580 defer f.ctypes = ctypes_aw.toArrayList();588 defer f.ctypes = ctypes_aw.toArrayList();
581589
582 for (0..decl_ctype_pool.items.len) |decl_ctype_pool_index| {590 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 {...@@ -640,8 +648,8 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, f: *Flush) FlushDeclError!void {
640 .code = undefined,648 .code = undefined,
641 .indent_counter = 0,649 .indent_counter = 0,
642 };650 };
643 _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl);651 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
644 _ = object.code.fromArrayList(gpa, &f.lazy_code);652 object.code = .fromArrayList(gpa, &f.lazy_code);
645 defer {653 defer {
646 object.dg.uavs.deinit(gpa);654 object.dg.uavs.deinit(gpa);
647 f.lazy_ctype_pool = object.dg.ctype_pool.move();655 f.lazy_ctype_pool = object.dg.ctype_pool.move();
...@@ -688,8 +696,8 @@ fn flushLazyFn(...@@ -688,8 +696,8 @@ fn flushLazyFn(
688 .code = undefined,696 .code = undefined,
689 .indent_counter = 0,697 .indent_counter = 0,
690 };698 };
691 _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl);699 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
692 _ = object.code.fromArrayList(gpa, &f.lazy_code);700 object.code = .fromArrayList(gpa, &f.lazy_code);
693 defer {701 defer {
694 // If this assert trips just handle the anon_decl_deps the same as702 // If this assert trips just handle the anon_decl_deps the same as
695 // `updateFunc()` does.703 // `updateFunc()` does.
...@@ -830,7 +838,7 @@ pub fn updateExports(...@@ -830,7 +838,7 @@ pub fn updateExports(
830 .scratch = .initBuffer(self.scratch_buf),838 .scratch = .initBuffer(self.scratch_buf),
831 .uavs = .empty,839 .uavs = .empty,
832 };840 };
833 dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf);841 dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
834 defer {842 defer {
835 assert(dg.uavs.count() == 0);843 assert(dg.uavs.count() == 0);
836 ctype_pool.* = dg.ctype_pool.move();844 ctype_pool.* = dg.ctype_pool.move();
...@@ -842,7 +850,7 @@ pub fn updateExports(...@@ -842,7 +850,7 @@ pub fn updateExports(
842 codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) {850 codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) {
843 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,851 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
844 };852 };
845 exported_block.* = .{ .fwd_decl = try self.addString(&.{&dg.fwd_decl}) };853 exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.getWritten()) };
846}854}
847855
848pub fn deleteExport(856pub fn deleteExport(
src/link/Coff.zig+15-23
...@@ -2623,7 +2623,7 @@ fn logSymtab(coff: *Coff) void {...@@ -2623,7 +2623,7 @@ fn logSymtab(coff: *Coff) void {
2623 .DEBUG => unreachable, // TODO2623 .DEBUG => unreachable, // TODO
2624 else => @intFromEnum(sym.section_number),2624 else => @intFromEnum(sym.section_number),
2625 };2625 };
2626 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{2626 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
2627 sym_id,2627 sym_id,
2628 coff.getSymbolName(.{ .sym_index = @as(u32, @intCast(sym_id)), .file = null }),2628 coff.getSymbolName(.{ .sym_index = @as(u32, @intCast(sym_id)), .file = null }),
2629 sym.value,2629 sym.value,
...@@ -3096,33 +3096,25 @@ const ImportTable = struct {...@@ -3096,33 +3096,25 @@ const ImportTable = struct {
3096 return base_vaddr + index * @sizeOf(u64);3096 return base_vaddr + index * @sizeOf(u64);
3097 }3097 }
30983098
3099 const FormatContext = struct {3099 const Format = struct {
3100 itab: ImportTable,3100 itab: ImportTable,
3101 ctx: Context,3101 ctx: Context,
3102 };
31033102
3104 fn format(itab: ImportTable, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {3103 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
3105 _ = itab;3104 const lib_name = f.ctx.coff.temp_strtab.getAssumeExists(f.ctx.name_off);
3106 _ = bw;3105 const base_vaddr = getBaseAddress(f.ctx);
3107 _ = unused_format_string;3106 try writer.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr });
3108 @compileError("do not format ImportTable directly; use itab.fmtDebug()");3107 for (f.itab.entries.items, 0..) |entry, i| {
3109 }3108 try writer.print("\n {d}@{?x} => {s}", .{
31103109 i,
3111 fn format2(fmt_ctx: FormatContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {3110 f.itab.getImportAddress(entry, f.ctx),
3112 comptime assert(unused_format_string.len == 0);3111 f.ctx.coff.getSymbolName(entry),
3113 const lib_name = fmt_ctx.ctx.coff.temp_strtab.getAssumeExists(fmt_ctx.ctx.name_off);3112 });
3114 const base_vaddr = getBaseAddress(fmt_ctx.ctx);3113 }
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 });
3122 }3114 }
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) {
3126 return .{ .data = .{ .itab = itab, .ctx = ctx } };3118 return .{ .data = .{ .itab = itab, .ctx = ctx } };
3127 }3119 }
31283120
src/link/Dwarf.zig+4-4
...@@ -2557,7 +2557,7 @@ fn initWipNavInner(...@@ -2557,7 +2557,7 @@ fn initWipNavInner(
2557 const addr: Loc = .{ .addr_reloc = sym_index };2557 const addr: Loc = .{ .addr_reloc = sym_index };
2558 const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr;2558 const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr;
2559 switch (decl.kind) {2559 switch (decl.kind) {
2560 .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable,2560 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
2561 .@"const" => {2561 .@"const" => {
2562 const const_ty_reloc_index = try wip_nav.refForward();2562 const const_ty_reloc_index = try wip_nav.refForward();
2563 try wip_nav.infoExprLoc(loc);2563 try wip_nav.infoExprLoc(loc);
...@@ -2834,7 +2834,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo...@@ -2834,7 +2834,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
28342834
2835 const is_test = switch (decl.kind) {2835 const is_test = switch (decl.kind) {
2836 .unnamed_test, .@"test", .decltest => true,2836 .unnamed_test, .@"test", .decltest => true,
2837 .@"comptime", .@"usingnamespace", .@"const", .@"var" => false,2837 .@"comptime", .@"const", .@"var" => false,
2838 };2838 };
2839 if (is_test) {2839 if (is_test) {
2840 // This isn't actually a comptime Nav! It's a test, so it'll definitely never be referenced at comptime.2840 // 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(...@@ -3657,7 +3657,7 @@ fn updateLazyType(
3657 // For better or worse, we try to match what Clang emits.3657 // For better or worse, we try to match what Clang emits.
3658 break :cc switch (func_type.cc) {3658 break :cc switch (func_type.cc) {
3659 .@"inline" => .nocall,3659 .@"inline" => .nocall,
3660 .@"async", .auto, .naked => .normal,3660 .async, .auto, .naked => .normal,
3661 .x86_64_sysv => .LLVM_X86_64SysV,3661 .x86_64_sysv => .LLVM_X86_64SysV,
3662 .x86_64_win => .LLVM_Win64,3662 .x86_64_win => .LLVM_Win64,
3663 .x86_64_regcall_v3_sysv => .LLVM_X86RegCall,3663 .x86_64_regcall_v3_sysv => .LLVM_X86RegCall,
...@@ -4301,7 +4301,7 @@ fn updateContainerTypeInner(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: Intern...@@ -4301,7 +4301,7 @@ fn updateContainerTypeInner(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: Intern
4301 };4301 };
4302 defer wip_nav.deinit();4302 defer wip_nav.deinit();
4303 const diw = wip_nav.debug_info.writer(dwarf.gpa);4303 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)});
4305 defer dwarf.gpa.free(name);4305 defer dwarf.gpa.free(name);
43064306
4307 switch (ip.indexToKey(type_index)) {4307 switch (ip.indexToKey(type_index)) {
src/link/Elf.zig+55-69
...@@ -3870,22 +3870,21 @@ pub fn failFile(...@@ -3870,22 +3870,21 @@ pub fn failFile(
3870 return error.LinkFailure;3870 return error.LinkFailure;
3871}3871}
38723872
3873const FormatShdrCtx = struct {3873const FormatShdr = struct {
3874 elf_file: *Elf,3874 elf_file: *Elf,
3875 shdr: elf.Elf64_Shdr,3875 shdr: elf.Elf64_Shdr,
3876};3876};
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) {
3879 return .{ .data = .{3879 return .{ .data = .{
3880 .shdr = shdr,3880 .shdr = shdr,
3881 .elf_file = self,3881 .elf_file = self,
3882 } };3882 } };
3883}3883}
38843884
3885fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {3885fn formatShdr(ctx: FormatShdr, writer: *std.io.Writer) std.io.Writer.Error!void {
3886 _ = unused_fmt_string;
3887 const shdr = ctx.shdr;3886 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})", .{
3889 ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,3888 ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,
3890 shdr.sh_addr, shdr.sh_addralign,3889 shdr.sh_addr, shdr.sh_addralign,
3891 shdr.sh_size, shdr.sh_entsize,3890 shdr.sh_size, shdr.sh_entsize,
...@@ -3893,74 +3892,68 @@ fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []con...@@ -3893,74 +3892,68 @@ fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []con
3893 });3892 });
3894}3893}
38953894
3896pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) {3895pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(u64, formatShdrFlags) {
3897 return .{ .data = sh_flags };3896 return .{ .data = sh_flags };
3898}3897}
38993898
3900fn formatShdrFlags(sh_flags: u64, bw: *Writer, comptime unused_fmt_string: []const u8) !void {3899fn formatShdrFlags(sh_flags: u64, writer: *std.io.Writer) std.io.Writer.Error!void {
3901 _ = unused_fmt_string;
3902 if (elf.SHF_WRITE & sh_flags != 0) {3900 if (elf.SHF_WRITE & sh_flags != 0) {
3903 try bw.writeByte('W');3901 try writer.writeByte('W');
3904 }3902 }
3905 if (elf.SHF_ALLOC & sh_flags != 0) {3903 if (elf.SHF_ALLOC & sh_flags != 0) {
3906 try bw.writeByte('A');3904 try writer.writeByte('A');
3907 }3905 }
3908 if (elf.SHF_EXECINSTR & sh_flags != 0) {3906 if (elf.SHF_EXECINSTR & sh_flags != 0) {
3909 try bw.writeByte('X');3907 try writer.writeByte('X');
3910 }3908 }
3911 if (elf.SHF_MERGE & sh_flags != 0) {3909 if (elf.SHF_MERGE & sh_flags != 0) {
3912 try bw.writeByte('M');3910 try writer.writeByte('M');
3913 }3911 }
3914 if (elf.SHF_STRINGS & sh_flags != 0) {3912 if (elf.SHF_STRINGS & sh_flags != 0) {
3915 try bw.writeByte('S');3913 try writer.writeByte('S');
3916 }3914 }
3917 if (elf.SHF_INFO_LINK & sh_flags != 0) {3915 if (elf.SHF_INFO_LINK & sh_flags != 0) {
3918 try bw.writeByte('I');3916 try writer.writeByte('I');
3919 }3917 }
3920 if (elf.SHF_LINK_ORDER & sh_flags != 0) {3918 if (elf.SHF_LINK_ORDER & sh_flags != 0) {
3921 try bw.writeByte('L');3919 try writer.writeByte('L');
3922 }3920 }
3923 if (elf.SHF_EXCLUDE & sh_flags != 0) {3921 if (elf.SHF_EXCLUDE & sh_flags != 0) {
3924 try bw.writeByte('E');3922 try writer.writeByte('E');
3925 }3923 }
3926 if (elf.SHF_COMPRESSED & sh_flags != 0) {3924 if (elf.SHF_COMPRESSED & sh_flags != 0) {
3927 try bw.writeByte('C');3925 try writer.writeByte('C');
3928 }3926 }
3929 if (elf.SHF_GROUP & sh_flags != 0) {3927 if (elf.SHF_GROUP & sh_flags != 0) {
3930 try bw.writeByte('G');3928 try writer.writeByte('G');
3931 }3929 }
3932 if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) {3930 if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) {
3933 try bw.writeByte('O');3931 try writer.writeByte('O');
3934 }3932 }
3935 if (elf.SHF_TLS & sh_flags != 0) {3933 if (elf.SHF_TLS & sh_flags != 0) {
3936 try bw.writeByte('T');3934 try writer.writeByte('T');
3937 }3935 }
3938 if (elf.SHF_X86_64_LARGE & sh_flags != 0) {3936 if (elf.SHF_X86_64_LARGE & sh_flags != 0) {
3939 try bw.writeByte('l');3937 try writer.writeByte('l');
3940 }3938 }
3941 if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) {3939 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');
3943 }3941 }
3944}3942}
39453943
3946const FormatPhdrCtx = struct {3944const FormatPhdr = struct {
3947 elf_file: *Elf,3945 elf_file: *Elf,
3948 phdr: elf.Elf64_Phdr,3946 phdr: elf.Elf64_Phdr,
3949};3947};
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) {
3952 return .{ .data = .{3950 return .{ .data = .{
3953 .phdr = phdr,3951 .phdr = phdr,
3954 .elf_file = self,3952 .elf_file = self,
3955 } };3953 } };
3956}3954}
39573955
3958fn formatPhdr(3956fn formatPhdr(ctx: FormatPhdr, writer: *std.io.Writer) std.io.Writer.Error!void {
3959 ctx: FormatPhdrCtx,
3960 bw: *Writer,
3961 comptime unused_fmt_string: []const u8,
3962) !void {
3963 _ = unused_fmt_string;
3964 const phdr = ctx.phdr;3957 const phdr = ctx.phdr;
3965 const write = phdr.p_flags & elf.PF_W != 0;3958 const write = phdr.p_flags & elf.PF_W != 0;
3966 const read = phdr.p_flags & elf.PF_R != 0;3959 const read = phdr.p_flags & elf.PF_R != 0;
...@@ -3981,40 +3974,34 @@ fn formatPhdr(...@@ -3981,40 +3974,34 @@ fn formatPhdr(
3981 elf.PT_NOTE => "NOTE",3974 elf.PT_NOTE => "NOTE",
3982 else => "UNKNOWN",3975 else => "UNKNOWN",
3983 };3976 };
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})", .{
3985 p_type, flags, phdr.p_offset, phdr.p_vaddr,3978 p_type, flags, phdr.p_offset, phdr.p_vaddr,
3986 phdr.p_align, phdr.p_filesz, phdr.p_memsz,3979 phdr.p_align, phdr.p_filesz, phdr.p_memsz,
3987 });3980 });
3988}3981}
39893982
3990pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {3983pub fn dumpState(self: *Elf) std.fmt.Formatter(*Elf, fmtDumpState) {
3991 return .{ .data = self };3984 return .{ .data = self };
3992}3985}
39933986
3994fn fmtDumpState(3987fn fmtDumpState(self: *Elf, writer: *std.io.Writer) std.io.Writer.Error!void {
3995 self: *Elf,
3996 bw: *Writer,
3997 comptime unused_fmt_string: []const u8,
3998) !void {
3999 _ = unused_fmt_string;
4000
4001 const shared_objects = self.shared_objects.values();3988 const shared_objects = self.shared_objects.values();
40023989
4003 if (self.zigObjectPtr()) |zig_object| {3990 if (self.zigObjectPtr()) |zig_object| {
4004 try bw.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename });3991 try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename });
4005 try bw.print("{f}{f}", .{3992 try writer.print("{f}{f}", .{
4006 zig_object.fmtAtoms(self),3993 zig_object.fmtAtoms(self),
4007 zig_object.fmtSymtab(self),3994 zig_object.fmtSymtab(self),
4008 });3995 });
4009 try bw.writeByte('\n');3996 try writer.writeByte('\n');
4010 }3997 }
40113998
4012 for (self.objects.items) |index| {3999 for (self.objects.items) |index| {
4013 const object = self.file(index).?.object;4000 const object = self.file(index).?.object;
4014 try bw.print("object({d}) : {f}", .{ index, object.fmtPath() });4001 try writer.print("object({d}) : {f}", .{ index, object.fmtPath() });
4015 if (!object.alive) try bw.writeAll(" : [*]");4002 if (!object.alive) try writer.writeAll(" : [*]");
4016 try bw.writeByte('\n');4003 try writer.writeByte('\n');
4017 try bw.print("{f}{f}{f}{f}{f}\n", .{4004 try writer.print("{f}{f}{f}{f}{f}\n", .{
4018 object.fmtAtoms(self),4005 object.fmtAtoms(self),
4019 object.fmtCies(self),4006 object.fmtCies(self),
4020 object.fmtFdes(self),4007 object.fmtFdes(self),
...@@ -4025,59 +4012,59 @@ fn fmtDumpState(...@@ -4025,59 +4012,59 @@ fn fmtDumpState(
40254012
4026 for (shared_objects) |index| {4013 for (shared_objects) |index| {
4027 const shared_object = self.file(index).?.shared_object;4014 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({})", .{
4029 index, shared_object.path, shared_object.needed,4016 index, shared_object.path, shared_object.needed,
4030 });4017 });
4031 if (!shared_object.alive) try bw.writeAll(" : [*]");4018 if (!shared_object.alive) try writer.writeAll(" : [*]");
4032 try bw.writeByte('\n');4019 try writer.writeByte('\n');
4033 try bw.print("{f}\n", .{shared_object.fmtSymtab(self)});4020 try writer.print("{f}\n", .{shared_object.fmtSymtab(self)});
4034 }4021 }
40354022
4036 if (self.linker_defined_index) |index| {4023 if (self.linker_defined_index) |index| {
4037 const linker_defined = self.file(index).?.linker_defined;4024 const linker_defined = self.file(index).?.linker_defined;
4038 try bw.print("linker_defined({d}) : (linker defined)\n", .{index});4025 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
4039 try bw.print("{f}\n", .{linker_defined.fmtSymtab(self)});4026 try writer.print("{f}\n", .{linker_defined.fmtSymtab(self)});
4040 }4027 }
40414028
4042 const slice = self.sections.slice();4029 const slice = self.sections.slice();
4043 {4030 {
4044 try bw.writeAll("atom lists\n");4031 try writer.writeAll("atom lists\n");
4045 for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| {4032 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) });
4047 }4034 }
4048 }4035 }
40494036
4050 if (self.requiresThunks()) {4037 if (self.requiresThunks()) {
4051 try bw.writeAll("thunks\n");4038 try writer.writeAll("thunks\n");
4052 for (self.thunks.items, 0..) |th, index| {4039 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) });
4054 }4041 }
4055 }4042 }
40564043
4057 try bw.print("{f}\n", .{self.got.fmt(self)});4044 try writer.print("{f}\n", .{self.got.fmt(self)});
4058 try bw.print("{f}\n", .{self.plt.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");
4061 for (self.group_sections.items) |cg| {4048 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 });
4063 }4050 }
40644051
4065 try bw.writeAll("\nOutput merge sections\n");4052 try writer.writeAll("\nOutput merge sections\n");
4066 for (self.merge_sections.items) |msec| {4053 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) });
4068 }4055 }
40694056
4070 try bw.writeAll("\nOutput shdrs\n");4057 try writer.writeAll("\nOutput shdrs\n");
4071 for (slice.items(.shdr), slice.items(.phndx), 0..) |shdr, phndx, shndx| {4058 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", .{
4073 shndx,4060 shndx,
4074 phndx,4061 phndx,
4075 self.fmtShdr(shdr),4062 self.fmtShdr(shdr),
4076 });4063 });
4077 }4064 }
4078 try bw.writeAll("\nOutput phdrs\n");4065 try writer.writeAll("\nOutput phdrs\n");
4079 for (self.phdrs.items, 0..) |phdr, phndx| {4066 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) });
4081 }4068 }
4082}4069}
40834070
...@@ -4215,9 +4202,8 @@ pub const Ref = struct {...@@ -4215,9 +4202,8 @@ pub const Ref = struct {
4215 return ref.index == other.index and ref.file == other.file;4202 return ref.index == other.index and ref.file == other.file;
4216 }4203 }
42174204
4218 pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {4205 pub fn format(ref: Ref, writer: *std.io.Writer) std.io.Writer.Error!void {
4219 _ = unused_fmt_string;4206 try writer.print("ref({d},{d})", .{ ref.index, ref.file });
4220 try bw.print("ref({},{})", .{ ref.index, ref.file });
4221 }4207 }
4222};4208};
42234209
src/link/Elf/Archive.zig+16-25
...@@ -45,7 +45,7 @@ pub fn parse(...@@ -45,7 +45,7 @@ pub fn parse(
4545
46 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {46 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
47 return diags.failParse(path, "invalid archive header delimiter: {f}", .{47 return diags.failParse(path, "invalid archive header delimiter: {f}", .{
48 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),48 std.ascii.hexEscape(&hdr.ar_fmag, .lower),
49 });49 });
50 }50 }
5151
...@@ -84,7 +84,7 @@ pub fn parse(...@@ -84,7 +84,7 @@ pub fn parse(
84 };84 };
8585
86 log.debug("extracting object '{f}' from archive '{f}'", .{86 log.debug("extracting object '{f}' from archive '{f}'", .{
87 object.path, path,87 @as(Path, object.path), @as(Path, path),
88 });88 });
8989
90 try objects.append(gpa, object);90 try objects.append(gpa, object);
...@@ -184,36 +184,28 @@ pub const ArSymtab = struct {...@@ -184,36 +184,28 @@ pub const ArSymtab = struct {
184 }184 }
185 }185 }
186186
187 pub fn format(ar: ArSymtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {187 const Format = struct {
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 {
195 ar: ArSymtab,188 ar: ArSymtab,
196 elf_file: *Elf,189 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 }
197 };200 };
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) {
200 return .{ .data = .{203 return .{ .data = .{
201 .ar = ar,204 .ar = ar,
202 .elf_file = elf_file,205 .elf_file = elf_file,
203 } };206 } };
204 }207 }
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
217 const Entry = struct {209 const Entry = struct {
218 /// Offset into the string table.210 /// Offset into the string table.
219 off: u32,211 off: u32,
...@@ -251,9 +243,8 @@ pub const ArStrtab = struct {...@@ -251,9 +243,8 @@ pub const ArStrtab = struct {
251 try writer.writeAll(ar.buffer.items);243 try writer.writeAll(ar.buffer.items);
252 }244 }
253245
254 pub fn format(ar: ArStrtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {246 pub fn format(ar: ArStrtab, writer: *std.io.Writer) std.io.Writer.Error!void {
255 comptime assert(unused_fmt_string.len == 0);247 try writer.print("{f}", .{std.ascii.hexEscape(ar.buffer.items, .lower)});
256 try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)});
257 }248 }
258};249};
259250
src/link/Elf/Atom.zig+35-44
...@@ -906,53 +906,45 @@ pub fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void {...@@ -906,53 +906,45 @@ pub fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void {
906 atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras);906 atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras);
907}907}
908908
909pub fn format(atom: Atom, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {909pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
917 return .{ .data = .{910 return .{ .data = .{
918 .atom = atom,911 .atom = atom,
919 .elf_file = elf_file,912 .elf_file = elf_file,
920 } };913 } };
921}914}
922915
923const FormatContext = struct {916const Format = struct {
924 atom: Atom,917 atom: Atom,
925 elf_file: *Elf,918 elf_file: *Elf,
926};
927919
928fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {920 fn default(f: Format, w: *std.io.Writer) std.io.Writer.Error!void {
929 _ = unused_fmt_string;921 const atom = f.atom;
930 const atom = ctx.atom;922 const elf_file = f.elf_file;
931 const elf_file = ctx.elf_file;923 try w.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{
932 try bw.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),
933 atom.atom_index, atom.name(elf_file), atom.address(elf_file),925 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,
934 atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,926 atom.prev_atom_ref, atom.next_atom_ref,
935 atom.prev_atom_ref, atom.next_atom_ref,927 });
936 });928 if (atom.file(elf_file)) |atom_file| switch (atom_file) {
937 if (atom.file(elf_file)) |atom_file| switch (atom_file) {929 .object => |object| {
938 .object => |object| {930 if (atom.fdes(object).len > 0) {
939 if (atom.fdes(object).len > 0) {931 try w.writeAll(" : fdes{ ");
940 try bw.writeAll(" : fdes{ ");932 const extras = atom.extra(elf_file);
941 const extras = atom.extra(elf_file);933 for (atom.fdes(object), extras.fde_start..) |fde, i| {
942 for (atom.fdes(object), extras.fde_start..) |fde, i| {934 try w.print("{d}", .{i});
943 try bw.print("{d}", .{i});935 if (!fde.alive) try w.writeAll("([*])");
944 if (!fde.alive) try bw.writeAll("([*])");936 if (i - extras.fde_start < extras.fde_count - 1) try w.writeAll(", ");
945 if (i - extras.fde_start < extras.fde_count - 1) try bw.writeAll(", ");937 }
938 try w.writeAll(" }");
946 }939 }
947 try bw.writeAll(" }");940 },
948 }941 else => {},
949 },942 };
950 else => {},943 if (!atom.alive) {
951 };944 try w.writeAll(" : [*]");
952 if (!atom.alive) {945 }
953 try bw.writeAll(" : [*]");
954 }946 }
955}947};
956948
957pub const Index = u32;949pub const Index = u32;
958950
...@@ -1385,9 +1377,8 @@ const x86_64 = struct {...@@ -1385,9 +1377,8 @@ const x86_64 = struct {
1385 // TODO: hack to force imm32s in the assembler1377 // TODO: hack to force imm32s in the assembler
1386 .{ .imm = .s(-129) },1378 .{ .imm = .s(-129) },
1387 }, t) catch return false;1379 }, t) catch return false;
1388 var buf: [std.atomic.cache_line]u8 = undefined;1380 var trash: std.io.Writer.Discarding = .init(&.{});
1389 var bw = Writer.null.buffered(&buf);1381 inst.encode(&trash.writer, .{}) catch return false;
1390 inst.encode(&bw, .{}) catch return false;
1391 return true;1382 return true;
1392 },1383 },
1393 else => return false,1384 else => return false,
...@@ -1433,7 +1424,7 @@ const x86_64 = struct {...@@ -1433,7 +1424,7 @@ const x86_64 = struct {
1433 rels: []const elf.Elf64_Rela,1424 rels: []const elf.Elf64_Rela,
1434 value: i32,1425 value: i32,
1435 elf_file: *Elf,1426 elf_file: *Elf,
1436 bw: *Writer,1427 writer: *Writer,
1437 ) !void {1428 ) !void {
1438 dev.check(.x86_64_backend);1429 dev.check(.x86_64_backend);
1439 assert(rels.len == 2);1430 assert(rels.len == 2);
...@@ -1450,8 +1441,8 @@ const x86_64 = struct {...@@ -1450,8 +1441,8 @@ const x86_64 = struct {
1450 0x48, 0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %rax1441 0x48, 0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %rax
1451 };1442 };
1452 std.mem.writeInt(i32, insts[12..][0..4], value, .little);1443 std.mem.writeInt(i32, insts[12..][0..4], value, .little);
1453 bw.end -= 4;1444 try writer.seekBy(-4);
1454 try bw.writeAll(&insts);1445 try writer.writeAll(&insts);
1455 relocs_log.debug(" relaxing {f} and {f}", .{1446 relocs_log.debug(" relaxing {f} and {f}", .{
1456 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1447 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1457 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1448 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
...@@ -1481,8 +1472,8 @@ const x86_64 = struct {...@@ -1481,8 +1472,8 @@ const x86_64 = struct {
1481 }1472 }
14821473
1483 fn encode(insts: []const Instruction, code: []u8) !void {1474 fn encode(insts: []const Instruction, code: []u8) !void {
1484 var bw: Writer = .fixed(code);1475 var stream: std.io.Writer = .fixed(code);
1485 for (insts) |inst| try inst.encode(&bw, .{});1476 for (insts) |inst| try inst.encode(&stream, .{});
1486 }1477 }
14871478
1488 const bits = @import("../../arch/x86_64/bits.zig");1479 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 {...@@ -167,32 +167,29 @@ pub fn lastAtom(list: AtomList, elf_file: *Elf) *Atom {
167 return elf_file.atom(list.atoms.keys()[list.atoms.keys().len - 1]).?;167 return elf_file.atom(list.atoms.keys()[list.atoms.keys().len - 1]).?;
168}168}
169169
170pub fn format(list: AtomList, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {170const Format = struct {
171 _ = list;171 atom_list: AtomList,
172 _ = bw;172 elf_file: *Elf,
173 _ = unused_fmt_string;173
174 @compileError("do not format AtomList directly");174 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
175}175 const list = f.atom_list;
176176 try writer.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{
177const FormatCtx = struct { AtomList, *Elf };177 list.address(f.elf_file),
178178 list.output_section_index,
179pub fn fmt(list: AtomList, elf_file: *Elf) std.fmt.Formatter(format2) {179 list.alignment.toByteUnits() orelse 0,
180 return .{ .data = .{ list, elf_file } };180 list.size,
181}181 });
182182 try writer.writeAll(" : atoms{ ");
183fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {183 for (list.atoms.keys(), 0..) |ref, i| {
184 comptime assert(unused_fmt_string.len == 0);184 try writer.print("{f}", .{ref});
185 const list, const elf_file = ctx;185 if (i < list.atoms.keys().len - 1) try writer.writeAll(", ");
186 try bw.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{186 }
187 list.address(elf_file), list.output_section_index,187 try writer.writeAll(" }");
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(", ");
194 }188 }
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 } };
196}193}
197194
198const std = @import("std");195const std = @import("std");
src/link/Elf/LinkerDefined.zig+16-17
...@@ -147,9 +147,9 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {...@@ -147,9 +147,9 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
147 for (slice.items(.shdr)) |shdr| {147 for (slice.items(.shdr)) |shdr| {
148 // TODO use getOrPut for incremental so that we don't create duplicates148 // TODO use getOrPut for incremental so that we don't create duplicates
149 if (elf_file.getStartStopBasename(shdr)) |name| {149 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);
151 defer gpa.free(start_name);151 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);
153 defer gpa.free(stop_name);153 defer gpa.free(stop_name);
154154
155 for (&[_][]const u8{ start_name, stop_name }) |nn| {155 for (&[_][]const u8{ start_name, stop_name }) |nn| {
...@@ -437,32 +437,31 @@ pub fn setSymbolExtra(self: *LinkerDefined, index: u32, extra: Symbol.Extra) voi...@@ -437,32 +437,31 @@ pub fn setSymbolExtra(self: *LinkerDefined, index: u32, extra: Symbol.Extra) voi
437 }437 }
438}438}
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) {
441 return .{ .data = .{441 return .{ .data = .{
442 .self = self,442 .self = self,
443 .elf_file = elf_file,443 .elf_file = elf_file,
444 } };444 } };
445}445}
446446
447const FormatContext = struct {447const Format = struct {
448 self: *LinkerDefined,448 self: *LinkerDefined,
449 elf_file: *Elf,449 elf_file: *Elf,
450};
451450
452fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {451 fn symtab(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
453 comptime assert(unused_fmt_string.len == 0);452 const self = ctx.self;
454 const self = ctx.self;453 const elf_file = ctx.elf_file;
455 const elf_file = ctx.elf_file;454 try writer.writeAll(" globals\n");
456 try bw.writeAll(" globals\n");455 for (self.symbols.items, 0..) |sym, i| {
457 for (self.symbols.items, 0..) |sym, i| {456 const ref = self.resolveSymbol(@intCast(i), elf_file);
458 const ref = self.resolveSymbol(@intCast(i), elf_file);457 if (elf_file.symbol(ref)) |ref_sym| {
459 if (elf_file.symbol(ref)) |ref_sym| {458 try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)});
460 try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)});459 } else {
461 } else {460 try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
462 try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)});461 }
463 }462 }
464 }463 }
465}464};
466465
467const std = @import("std");466const std = @import("std");
468const Allocator = mem.Allocator;467const Allocator = mem.Allocator;
src/link/Elf/Merge.zig+31-47
...@@ -157,42 +157,34 @@ pub const Section = struct {...@@ -157,42 +157,34 @@ pub const Section = struct {
157 }157 }
158 };158 };
159159
160 pub fn format(msec: Section, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {160 pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
168 return .{ .data = .{161 return .{ .data = .{
169 .msec = msec,162 .msec = msec,
170 .elf_file = elf_file,163 .elf_file = elf_file,
171 } };164 } };
172 }165 }
173166
174 const FormatContext = struct {167 const Format = struct {
175 msec: Section,168 msec: Section,
176 elf_file: *Elf,169 elf_file: *Elf,
177 };
178170
179 pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {171 pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
180 _ = unused_fmt_string;172 const msec = f.msec;
181 const msec = ctx.msec;173 const elf_file = f.elf_file;
182 const elf_file = ctx.elf_file;174 try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{
183 try bw.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{175 msec.name(elf_file),
184 msec.name(elf_file),176 msec.address(elf_file),
185 msec.address(elf_file),177 msec.size,
186 msec.size,178 msec.alignment.toByteUnits() orelse 0,
187 msec.alignment.toByteUnits() orelse 0,179 msec.entsize,
188 msec.entsize,180 msec.type,
189 msec.type,181 msec.flags,
190 msec.flags,182 });
191 });183 for (msec.subsections.items) |msub| {
192 for (msec.subsections.items) |msub| {184 try writer.print(" {f}\n", .{msub.fmt(elf_file)});
193 try bw.print(" {f}\n", .{msub.fmt(elf_file)});185 }
194 }186 }
195 }187 };
196188
197 pub const Index = u32;189 pub const Index = u32;
198};190};
...@@ -219,36 +211,28 @@ pub const Subsection = struct {...@@ -219,36 +211,28 @@ pub const Subsection = struct {
219 return msec.bytes.items[msub.string_index..][0..msub.size];211 return msec.bytes.items[msub.string_index..][0..msub.size];
220 }212 }
221213
222 pub fn format(msub: Subsection, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {214 pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
230 return .{ .data = .{215 return .{ .data = .{
231 .msub = msub,216 .msub = msub,
232 .elf_file = elf_file,217 .elf_file = elf_file,
233 } };218 } };
234 }219 }
235220
236 const FormatContext = struct {221 const Format = struct {
237 msub: Subsection,222 msub: Subsection,
238 elf_file: *Elf,223 elf_file: *Elf,
239 };
240224
241 pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {225 pub fn default(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
242 _ = unused_fmt_string;226 const msub = ctx.msub;
243 const msub = ctx.msub;227 const elf_file = ctx.elf_file;
244 const elf_file = ctx.elf_file;228 try writer.print("@{x} : align({x}) : size({x})", .{
245 try bw.print("@{x} : align({x}) : size({x})", .{229 msub.address(elf_file),
246 msub.address(elf_file),230 msub.alignment,
247 msub.alignment,231 msub.size,
248 msub.size,232 });
249 });233 if (!msub.alive) try writer.writeAll(" : [*]");
250 if (!msub.alive) try bw.writeAll(" : [*]");234 }
251 }235 };
252236
253 pub const Index = u32;237 pub const Index = u32;
254};238};
src/link/Elf/Object.zig+70-86
...@@ -488,10 +488,7 @@ fn parseEhFrame(...@@ -488,10 +488,7 @@ fn parseEhFrame(
488 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));488 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
489 } else {489 } else {
490 // TODO convert into an error490 // TODO convert into an error
491 log.debug("{f}: no matching CIE found for FDE at offset {x}", .{491 log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ self.fmtPath(), fde.offset });
492 self.fmtPath(),
493 fde.offset,
494 });
495 continue;492 continue;
496 };493 };
497 fde.cie_index = cie_index;494 fde.cie_index = cie_index;
...@@ -582,7 +579,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {...@@ -582,7 +579,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
582 if (sym.flags.import) {579 if (sym.flags.import) {
583 if (sym.type(elf_file) != elf.STT_FUNC)580 if (sym.type(elf_file) != elf.STT_FUNC)
584 // TODO convert into an error581 // 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", .{
586 self.fmtPath(), sym.name(elf_file),583 self.fmtPath(), sym.name(elf_file),
587 });584 });
588 sym.flags.needs_plt = true;585 sym.flags.needs_plt = true;
...@@ -1428,129 +1425,116 @@ pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group {...@@ -1428,129 +1425,116 @@ pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group {
1428 return &self.groups.items[index];1425 return &self.groups.items[index];
1429}1426}
14301427
1431pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1428pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) {
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) {
1439 return .{ .data = .{1429 return .{ .data = .{
1440 .object = self,1430 .object = self,
1441 .elf_file = elf_file,1431 .elf_file = elf_file,
1442 } };1432 } };
1443}1433}
14441434
1445const FormatContext = struct {1435const Format = struct {
1446 object: *Object,1436 object: *Object,
1447 elf_file: *Elf,1437 elf_file: *Elf,
1448};
14491438
1450fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1439 fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1451 _ = unused_fmt_string;1440 const object = f.object;
1452 const object = ctx.object;1441 const elf_file = f.elf_file;
1453 const elf_file = ctx.elf_file;1442 try writer.writeAll(" locals\n");
1454 try bw.writeAll(" locals\n");1443 for (object.locals()) |sym| {
1455 for (object.locals()) |sym| {1444 try writer.print(" {f}\n", .{sym.fmt(elf_file)});
1456 try bw.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 }
1457 }1456 }
1458 try bw.writeAll(" globals\n");1457
1459 for (object.globals(), 0..) |sym, i| {1458 fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
1460 const first_global = object.first_global.?;1459 const object = f.object;
1461 const ref = object.resolveSymbol(@intCast(i + first_global), elf_file);1460 try writer.writeAll(" atoms\n");
1462 if (elf_file.symbol(ref)) |ref_sym| {1461 for (object.atoms_indexes.items) |atom_index| {
1463 try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)});1462 const atom_ptr = object.atom(atom_index) orelse continue;
1464 } else {1463 try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)});
1465 try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
1466 }1464 }
1467 }1465 }
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) {
1471 return .{ .data = .{1502 return .{ .data = .{
1472 .object = self,1503 .object = self,
1473 .elf_file = elf_file,1504 .elf_file = elf_file,
1474 } };1505 } };
1475}1506}
14761507
1477fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1508pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.cies) {
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) {
1488 return .{ .data = .{1509 return .{ .data = .{
1489 .object = self,1510 .object = self,
1490 .elf_file = elf_file,1511 .elf_file = elf_file,
1491 } };1512 } };
1492}1513}
14931514
1494fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1515pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.fdes) {
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) {
1504 return .{ .data = .{1516 return .{ .data = .{
1505 .object = self,1517 .object = self,
1506 .elf_file = elf_file,1518 .elf_file = elf_file,
1507 } };1519 } };
1508}1520}
15091521
1510fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1522pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.groups) {
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) {
1520 return .{ .data = .{1523 return .{ .data = .{
1521 .object = self,1524 .object = self,
1522 .elf_file = elf_file,1525 .elf_file = elf_file,
1523 } };1526 } };
1524}1527}
15251528
1526fn formatGroups(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1529pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) {
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) {
1545 return .{ .data = self };1530 return .{ .data = self };
1546}1531}
15471532
1548fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1533fn formatPath(object: Object, writer: *std.io.Writer) std.io.Writer.Error!void {
1549 comptime assert(unused_fmt_string.len == 0);
1550 if (object.archive) |ar| {1534 if (object.archive) |ar| {
1551 try bw.print("{f}({f})", .{ ar.path, object.path });1535 try writer.print("{f}({f})", .{ ar.path, object.path });
1552 } else {1536 } else {
1553 try bw.print("{f}", .{object.path});1537 try writer.print("{f}", .{object.path});
1554 }1538 }
1555}1539}
15561540
src/link/Elf/SharedObject.zig+14-22
...@@ -509,39 +509,31 @@ pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void...@@ -509,39 +509,31 @@ pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void
509 }509 }
510}510}
511511
512pub fn format(self: SharedObject, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {512pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) {
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) {
520 return .{ .data = .{513 return .{ .data = .{
521 .shared = self,514 .shared = self,
522 .elf_file = elf_file,515 .elf_file = elf_file,
523 } };516 } };
524}517}
525518
526const FormatContext = struct {519const Format = struct {
527 shared: SharedObject,520 shared: SharedObject,
528 elf_file: *Elf,521 elf_file: *Elf,
529};
530522
531fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {523 fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
532 comptime assert(unused_fmt_string.len == 0);524 const shared = f.shared;
533 const shared = ctx.shared;525 const elf_file = f.elf_file;
534 const elf_file = ctx.elf_file;526 try writer.writeAll(" globals\n");
535 try bw.writeAll(" globals\n");527 for (shared.symbols.items, 0..) |sym, i| {
536 for (shared.symbols.items, 0..) |sym, i| {528 const ref = shared.resolveSymbol(@intCast(i), elf_file);
537 const ref = shared.resolveSymbol(@intCast(i), elf_file);529 if (elf_file.symbol(ref)) |ref_sym| {
538 if (elf_file.symbol(ref)) |ref_sym| {530 try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)});
539 try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)});531 } else {
540 } else {532 try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)});
541 try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)});533 }
542 }534 }
543 }535 }
544}536};
545537
546const SharedObject = @This();538const 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 {...@@ -316,81 +316,72 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
316 out.st_size = esym.st_size;316 out.st_size = esym.st_size;
317}317}
318318
319pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {319const Format = struct {
320 _ = symbol;
321 _ = bw;
322 _ = unused_fmt_string;
323 @compileError("do not format Symbol directly");
324}
325
326const FormatContext = struct {
327 symbol: Symbol,320 symbol: Symbol,
328 elf_file: *Elf,321 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 }
329};369};
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) {
332 return .{ .data = .{372 return .{ .data = .{
333 .symbol = symbol,373 .symbol = symbol,
334 .elf_file = elf_file,374 .elf_file = elf_file,
335 } };375 } };
336}376}
337377
338fn formatName(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {378pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
355 return .{ .data = .{379 return .{ .data = .{
356 .symbol = symbol,380 .symbol = symbol,
357 .elf_file = elf_file,381 .elf_file = elf_file,
358 } };382 } };
359}383}
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
394pub const Flags = packed struct {385pub const Flags = packed struct {
395 /// Whether the symbol is imported at runtime.386 /// Whether the symbol is imported at runtime.
396 import: bool = false,387 import: bool = false,
src/link/Elf/Thunk.zig+11-19
...@@ -65,35 +65,27 @@ fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {...@@ -65,35 +65,27 @@ fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
65 };65 };
66}66}
6767
68pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {68pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
76 return .{ .data = .{69 return .{ .data = .{
77 .thunk = thunk,70 .thunk = thunk,
78 .elf_file = elf_file,71 .elf_file = elf_file,
79 } };72 } };
80}73}
8174
82const FormatContext = struct {75const Format = struct {
83 thunk: Thunk,76 thunk: Thunk,
84 elf_file: *Elf,77 elf_file: *Elf,
85};
8678
87fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {79 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
88 comptime assert(unused_fmt_string.len == 0);80 const thunk = f.thunk;
89 const thunk = ctx.thunk;81 const elf_file = f.elf_file;
90 const elf_file = ctx.elf_file;82 try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
91 try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });83 for (thunk.symbols.keys()) |ref| {
92 for (thunk.symbols.keys()) |ref| {84 const sym = elf_file.symbol(ref).?;
93 const sym = elf_file.symbol(ref).?;85 try writer.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
94 try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });86 }
95 }87 }
96}88};
9789
98pub const Index = u32;90pub const Index = u32;
9991
src/link/Elf/ZigObject.zig+34-36
...@@ -799,9 +799,9 @@ pub fn initRelaSections(self: *ZigObject, elf_file: *Elf) !void {...@@ -799,9 +799,9 @@ pub fn initRelaSections(self: *ZigObject, elf_file: *Elf) !void {
799 const out_shndx = atom_ptr.output_section_index;799 const out_shndx = atom_ptr.output_section_index;
800 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];800 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];
801 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;801 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}", .{
803 elf_file.getShString(out_shdr.sh_name),803 elf_file.getShString(out_shdr.sh_name),
804 });804 }, 0);
805 defer gpa.free(rela_sect_name);805 defer gpa.free(rela_sect_name);
806 _ = elf_file.sectionByName(rela_sect_name) orelse806 _ = elf_file.sectionByName(rela_sect_name) orelse
807 try elf_file.addRelaShdr(try elf_file.insertShString(rela_sect_name), out_shndx);807 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 {...@@ -820,9 +820,9 @@ pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void {
820 const out_shndx = atom_ptr.output_section_index;820 const out_shndx = atom_ptr.output_section_index;
821 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];821 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];
822 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;822 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}", .{
824 elf_file.getShString(out_shdr.sh_name),824 elf_file.getShString(out_shdr.sh_name),
825 });825 }, 0);
826 defer gpa.free(rela_sect_name);826 defer gpa.free(rela_sect_name);
827 const out_rela_shndx = elf_file.sectionByName(rela_sect_name).?;827 const out_rela_shndx = elf_file.sectionByName(rela_sect_name).?;
828 const out_rela_shdr = &elf_file.sections.items(.shdr)[out_rela_shndx];828 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...@@ -1932,7 +1932,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e
1932 .requires_padding = requires_padding,1932 .requires_padding = requires_padding,
1933 });1933 });
1934 atom_ptr.value = @intCast(alloc_res.value);1934 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}", .{
1936 atom_ptr.name(elf_file),1936 atom_ptr.name(elf_file),
1937 atom_ptr.offset(elf_file),1937 atom_ptr.offset(elf_file),
1938 alloc_res.placement,1938 alloc_res.placement,
...@@ -1977,7 +1977,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e...@@ -1977,7 +1977,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e
1977 atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 };1977 atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 };
1978 }1978 }
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 });
1981}1981}
19821982
1983pub fn resetShdrIndexes(self: *ZigObject, backlinks: []const u32) void {1983pub fn resetShdrIndexes(self: *ZigObject, backlinks: []const u32) void {
...@@ -2186,48 +2186,46 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {...@@ -2186,48 +2186,46 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
2186 }2186 }
2187}2187}
21882188
2189pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {2189const Format = struct {
2190 return .{ .data = .{
2191 .self = self,
2192 .elf_file = elf_file,
2193 } };
2194}
2195
2196const FormatContext = struct {
2197 self: *ZigObject,2190 self: *ZigObject,
2198 elf_file: *Elf,2191 elf_file: *Elf,
2199};
22002192
2201fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2193 fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
2202 _ = unused_fmt_string;2194 const self = f.self;
2203 const self = ctx.self;2195 const elf_file = f.elf_file;
2204 const elf_file = ctx.elf_file;2196 try writer.writeAll(" locals\n");
2205 try bw.writeAll(" locals\n");2197 for (self.local_symbols.items) |index| {
2206 for (self.local_symbols.items) |index| {2198 const local = self.symbols.items[index];
2207 const local = self.symbols.items[index];2199 try writer.print(" {f}\n", .{local.fmt(elf_file)});
2208 try bw.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 }
2209 }2206 }
2210 try bw.writeAll(" globals\n");2207
2211 for (ctx.self.global_symbols.items) |index| {2208 fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
2212 const global = self.symbols.items[index];2209 try writer.writeAll(" atoms\n");
2213 try bw.print(" {f}\n", .{global.fmt(elf_file)});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 }
2214 }2214 }
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) {
2218 return .{ .data = .{2218 return .{ .data = .{
2219 .self = self,2219 .self = self,
2220 .elf_file = elf_file,2220 .elf_file = elf_file,
2221 } };2221 } };
2222}2222}
22232223
2224fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2224pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) {
2225 comptime assert(unused_fmt_string.len == 0);2225 return .{ .data = .{
2226 try bw.writeAll(" atoms\n");2226 .self = self,
2227 for (ctx.self.atoms_indexes.items) |atom_index| {2227 .elf_file = elf_file,
2228 const atom_ptr = ctx.self.atom(atom_index) orelse continue;2228 } };
2229 try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)});
2230 }
2231}2229}
22322230
2233const ElfSym = struct {2231const ElfSym = struct {
src/link/Elf/eh_frame.zig+30-62
...@@ -47,48 +47,32 @@ pub const Fde = struct {...@@ -47,48 +47,32 @@ pub const Fde = struct {
47 return object.relocs.items[fde.rel_index..][0..fde.rel_num];47 return object.relocs.items[fde.rel_index..][0..fde.rel_num];
48 }48 }
4949
50 pub fn format(50 pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
62 return .{ .data = .{51 return .{ .data = .{
63 .fde = fde,52 .fde = fde,
64 .elf_file = elf_file,53 .elf_file = elf_file,
65 } };54 } };
66 }55 }
6756
68 const FdeFormatContext = struct {57 const Format = struct {
69 fde: Fde,58 fde: Fde,
70 elf_file: *Elf,59 elf_file: *Elf,
71 };
7260
73 fn format2(61 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
74 ctx: FdeFormatContext,62 const fde = f.fde;
75 bw: *Writer,63 const elf_file = f.elf_file;
76 comptime unused_fmt_string: []const u8,64 const base_addr = fde.address(elf_file);
77 ) !void {65 const object = elf_file.file(fde.file_index).?.object;
78 _ = unused_fmt_string;66 const atom_name = fde.atom(object).name(elf_file);
79 const fde = ctx.fde;67 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
80 const elf_file = ctx.elf_file;68 base_addr + fde.out_offset,
81 const base_addr = fde.address(elf_file);69 fde.calcSize(),
82 const object = elf_file.file(fde.file_index).?.object;70 fde.cie_index,
83 const atom_name = fde.atom(object).name(elf_file);71 atom_name,
84 try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{72 });
85 base_addr + fde.out_offset,73 if (!fde.alive) try writer.writeAll(" : [*]");
86 fde.calcSize(),74 }
87 fde.cie_index,75 };
88 atom_name,
89 });
90 if (!fde.alive) try bw.writeAll(" : [*]");
91 }
92};76};
9377
94pub const Cie = struct {78pub const Cie = struct {
...@@ -146,44 +130,28 @@ pub const Cie = struct {...@@ -146,44 +130,28 @@ pub const Cie = struct {
146 return true;130 return true;
147 }131 }
148132
149 pub fn format(133 pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) {
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) {
161 return .{ .data = .{134 return .{ .data = .{
162 .cie = cie,135 .cie = cie,
163 .elf_file = elf_file,136 .elf_file = elf_file,
164 } };137 } };
165 }138 }
166139
167 const CieFormatContext = struct {140 const Format = struct {
168 cie: Cie,141 cie: Cie,
169 elf_file: *Elf,142 elf_file: *Elf,
170 };
171143
172 fn format2(144 fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void {
173 ctx: CieFormatContext,145 const cie = f.cie;
174 bw: *Writer,146 const elf_file = f.elf_file;
175 comptime unused_fmt_string: []const u8,147 const base_addr = cie.address(elf_file);
176 ) !void {148 try writer.print("@{x} : size({x})", .{
177 _ = unused_fmt_string;149 base_addr + cie.out_offset,
178 const cie = ctx.cie;150 cie.calcSize(),
179 const elf_file = ctx.elf_file;151 });
180 const base_addr = cie.address(elf_file);152 if (!cie.alive) try writer.writeAll(" : [*]");
181 try bw.print("@{x} : size({x})", .{153 }
182 base_addr + cie.out_offset,154 };
183 cie.calcSize(),
184 });
185 if (!cie.alive) try bw.writeAll(" : [*]");
186 }
187};155};
188156
189pub const Iterator = struct {157pub const Iterator = struct {
src/link/Elf/file.zig+6-7
...@@ -10,17 +10,16 @@ pub const File = union(enum) {...@@ -10,17 +10,16 @@ pub const File = union(enum) {
10 };10 };
11 }11 }
1212
13 pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {13 pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) {
14 return .{ .data = file };14 return .{ .data = file };
15 }15 }
1616
17 fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {17 fn formatPath(file: File, writer: *std.io.Writer) std.io.Writer.Error!void {
18 comptime assert(unused_fmt_string.len == 0);
19 switch (file) {18 switch (file) {
20 .zig_object => |zo| try bw.writeAll(zo.basename),19 .zig_object => |zo| try writer.writeAll(zo.basename),
21 .linker_defined => try bw.writeAll("(linker defined)"),20 .linker_defined => try writer.writeAll("(linker defined)"),
22 .object => |x| try bw.print("{f}", .{x.fmtPath()}),21 .object => |x| try writer.print("{f}", .{x.fmtPath()}),
23 .shared_object => |x| try bw.print("{f}", .{x.path}),22 .shared_object => |x| try writer.print("{f}", .{@as(Path, x.path)}),
24 }23 }
25 }24 }
2625
src/link/Elf/gc.zig+2-3
...@@ -185,9 +185,8 @@ const Level = struct {...@@ -185,9 +185,8 @@ const Level = struct {
185 self.value += 1;185 self.value += 1;
186 }186 }
187187
188 pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {188 pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void {
189 comptime assert(unused_fmt_string.len == 0);189 try w.splatByteAll(' ', self.value);
190 try bw.splatByteAll(' ', self.value);
191 }190 }
192};191};
193192
src/link/Elf/relocation.zig+5-6
...@@ -141,20 +141,19 @@ const FormatRelocTypeCtx = struct {...@@ -141,20 +141,19 @@ const FormatRelocTypeCtx = struct {
141 cpu_arch: std.Target.Cpu.Arch,141 cpu_arch: std.Target.Cpu.Arch,
142};142};
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) {
145 return .{ .data = .{145 return .{ .data = .{
146 .r_type = r_type,146 .r_type = r_type,
147 .cpu_arch = cpu_arch,147 .cpu_arch = cpu_arch,
148 } };148 } };
149}149}
150150
151fn formatRelocType(ctx: FormatRelocTypeCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {151fn formatRelocType(ctx: FormatRelocTypeCtx, writer: *std.io.Writer) std.io.Writer.Error!void {
152 comptime assert(unused_fmt_string.len == 0);
153 const r_type = ctx.r_type;152 const r_type = ctx.r_type;
154 switch (ctx.cpu_arch) {153 switch (ctx.cpu_arch) {
155 .x86_64 => try bw.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}),154 .x86_64 => try writer.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)))}),155 .aarch64 => try writer.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)))}),156 .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}),
158 else => unreachable,157 else => unreachable,
159 }158 }
160}159}
src/link/Elf/synthetic_sections.zig+36-38
...@@ -606,31 +606,30 @@ pub const GotSection = struct {...@@ -606,31 +606,30 @@ pub const GotSection = struct {
606 }606 }
607 }607 }
608608
609 const FormatCtx = struct {609 const Format = struct {
610 got: GotSection,610 got: GotSection,
611 elf_file: *Elf,611 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 }
612 };628 };
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) {
615 return .{ .data = .{ .got = got, .elf_file = elf_file } };631 return .{ .data = .{ .got = got, .elf_file = elf_file } };
616 }632 }
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 }
634};633};
635634
636pub const PltSection = struct {635pub const PltSection = struct {
...@@ -743,32 +742,31 @@ pub const PltSection = struct {...@@ -743,32 +742,31 @@ pub const PltSection = struct {
743 }742 }
744 }743 }
745744
746 const FormatCtx = struct {745 const Format = struct {
747 plt: PltSection,746 plt: PltSection,
748 elf_file: *Elf,747 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 }
749 };764 };
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) {
752 return .{ .data = .{ .plt = plt, .elf_file = elf_file } };767 return .{ .data = .{ .plt = plt, .elf_file = elf_file } };
753 }768 }
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
772 const x86_64 = struct {770 const x86_64 = struct {
773 fn write(plt: PltSection, elf_file: *Elf, bw: *Writer) Writer.Error!void {771 fn write(plt: PltSection, elf_file: *Elf, bw: *Writer) Writer.Error!void {
774 const shdrs = elf_file.sections.items(.shdr);772 const shdrs = elf_file.sections.items(.shdr);
src/link/LdScript.zig+1-1
...@@ -42,7 +42,7 @@ pub fn parse(...@@ -42,7 +42,7 @@ pub fn parse(
42 switch (tok.id) {42 switch (tok.id) {
43 .invalid => {43 .invalid => {
44 return diags.failParse(path, "invalid token in LD script: '{f}' ({d}:{d})", .{44 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,
46 });46 });
47 },47 },
48 .new_line => {48 .new_line => {
src/link/Lld.zig+12-23
...@@ -294,7 +294,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {...@@ -294,7 +294,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
294 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);294 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
295 } else null;295 } else null;
296296
297 log.debug("zcu_obj_path={?}", .{zcu_obj_path});297 log.debug("zcu_obj_path={?f}", .{zcu_obj_path});
298298
299 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)299 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
300 comp.compiler_rt_obj.?.full_object_path300 comp.compiler_rt_obj.?.full_object_path
...@@ -437,7 +437,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -437,7 +437,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
437 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename}));437 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename}));
438 }438 }
439 if (comp.version) |version| {439 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 }));
441 }441 }
442442
443 if (target_util.llvmMachineAbi(target)) |mabi| {443 if (target_util.llvmMachineAbi(target)) |mabi| {
...@@ -507,7 +507,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -507,7 +507,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
507507
508 if (comp.emit_implib) |raw_emit_path| {508 if (comp.emit_implib) |raw_emit_path| {
509 const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path);509 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}));
511 }511 }
512512
513 if (comp.config.link_libc) {513 if (comp.config.link_libc) {
...@@ -533,7 +533,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -533,7 +533,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
533 },533 },
534 .object, .archive => |obj| {534 .object, .archive => |obj| {
535 if (obj.must_link) {535 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)}));
537 } else {537 } else {
538 argv.appendAssumeCapacity(try obj.path.toString(arena));538 argv.appendAssumeCapacity(try obj.path.toString(arena));
539 }539 }
...@@ -933,9 +933,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -933,9 +933,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
933 .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{933 .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
934 @tagName(base.build_id),934 @tagName(base.build_id),
935 })),935 })),
936 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{936 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})),
937 std.fmt.fmtSliceHexLower(hs.toSlice()),
938 })),
939 }937 }
940938
941 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{elf.image_base}));939 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 {...@@ -1218,7 +1216,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1218 if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;1216 if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
1219 }1217 }
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}", .{
1222 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,1220 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1223 });1221 });
1224 try argv.append(lib_path);1222 try argv.append(lib_path);
...@@ -1231,14 +1229,14 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1231,14 +1229,14 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1231 }));1229 }));
1232 } else if (target.isFreeBSDLibC()) {1230 } else if (target.isFreeBSDLibC()) {
1233 for (freebsd.libs) |lib| {1231 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}", .{
1235 comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,1233 comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1236 });1234 });
1237 try argv.append(lib_path);1235 try argv.append(lib_path);
1238 }1236 }
1239 } else if (target.isNetBSDLibC()) {1237 } else if (target.isNetBSDLibC()) {
1240 for (netbsd.libs) |lib| {1238 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}", .{
1242 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,1240 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1243 });1241 });
1244 try argv.append(lib_path);1242 try argv.append(lib_path);
...@@ -1511,9 +1509,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1511,9 +1509,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1511 .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{1509 .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1512 @tagName(base.build_id),1510 @tagName(base.build_id),
1513 })),1511 })),
1514 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{1512 .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})),
1515 std.fmt.fmtSliceHexLower(hs.toSlice()),
1516 })),
1517 .md5 => {},1513 .md5 => {},
1518 }1514 }
15191515
...@@ -1539,13 +1535,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1539,13 +1535,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
15391535
1540 if (comp.config.link_libc and is_exe_or_dyn_lib) {1536 if (comp.config.link_libc and is_exe_or_dyn_lib) {
1541 if (target.os.tag == .wasi) {1537 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
1549 try argv.append(try comp.crtFileAsString(1538 try argv.append(try comp.crtFileAsString(
1550 arena,1539 arena,
1551 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),1540 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
...@@ -1660,7 +1649,7 @@ fn spawnLld(...@@ -1660,7 +1649,7 @@ fn spawnLld(
1660 child.stderr_behavior = .Pipe;1649 child.stderr_behavior = .Pipe;
16611650
1662 child.spawn() catch |err| break :term err;1651 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));
1664 break :term child.wait();1653 break :term child.wait();
1665 }) catch |first_err| term: {1654 }) catch |first_err| term: {
1666 const err = switch (first_err) {1655 const err = switch (first_err) {
...@@ -1674,7 +1663,7 @@ fn spawnLld(...@@ -1674,7 +1663,7 @@ fn spawnLld(
1674 log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });1663 log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });
1675 {1664 {
1676 defer rsp_file.close();1665 defer rsp_file.close();
1677 var rsp_buf = std.io.bufferedWriter(rsp_file.writer());1666 var rsp_buf = std.io.bufferedWriter(rsp_file.deprecatedWriter());
1678 const rsp_writer = rsp_buf.writer();1667 const rsp_writer = rsp_buf.writer();
1679 for (argv[2..]) |arg| {1668 for (argv[2..]) |arg| {
1680 try rsp_writer.writeByte('"');1669 try rsp_writer.writeByte('"');
...@@ -1708,7 +1697,7 @@ fn spawnLld(...@@ -1708,7 +1697,7 @@ fn spawnLld(
1708 rsp_child.stderr_behavior = .Pipe;1697 rsp_child.stderr_behavior = .Pipe;
17091698
1710 rsp_child.spawn() catch |err| break :err err;1699 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));
1712 break :term rsp_child.wait() catch |err| break :err err;1701 break :term rsp_child.wait() catch |err| break :err err;
1713 }1702 }
1714 },1703 },
src/link/MachO.zig+43-49
...@@ -3898,29 +3898,28 @@ pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void {...@@ -3898,29 +3898,28 @@ pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void {
3898 self.hot_state.mach_task = null;3898 self.hot_state.mach_task = null;
3899}3899}
39003900
3901pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) {3901pub fn dumpState(self: *MachO) std.fmt.Formatter(*MachO, fmtDumpState) {
3902 return .{ .data = self };3902 return .{ .data = self };
3903}3903}
39043904
3905fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {3905fn fmtDumpState(self: *MachO, w: *Writer) Writer.Error!void {
3906 _ = unused_fmt_string;
3907 if (self.getZigObject()) |zo| {3906 if (self.getZigObject()) |zo| {
3908 try bw.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename });3907 try w.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename });
3909 try bw.print("{f}{f}\n", .{3908 try w.print("{f}{f}\n", .{
3910 zo.fmtAtoms(self),3909 zo.fmtAtoms(self),
3911 zo.fmtSymtab(self),3910 zo.fmtSymtab(self),
3912 });3911 });
3913 }3912 }
3914 for (self.objects.items) |index| {3913 for (self.objects.items) |index| {
3915 const object = self.getFile(index).?.object;3914 const object = self.getFile(index).?.object;
3916 try bw.print("object({d}) : {f} : has_debug({})", .{3915 try w.print("object({d}) : {f} : has_debug({})", .{
3917 index,3916 index,
3918 object.fmtPath(),3917 object.fmtPath(),
3919 object.hasDebugInfo(),3918 object.hasDebugInfo(),
3920 });3919 });
3921 if (!object.alive) try bw.writeAll(" : ([*])");3920 if (!object.alive) try w.writeAll(" : ([*])");
3922 try bw.writeByte('\n');3921 try w.writeByte('\n');
3923 try bw.print("{f}{f}{f}{f}{f}\n", .{3922 try w.print("{f}{f}{f}{f}{f}\n", .{
3924 object.fmtAtoms(self),3923 object.fmtAtoms(self),
3925 object.fmtCies(self),3924 object.fmtCies(self),
3926 object.fmtFdes(self),3925 object.fmtFdes(self),
...@@ -3930,42 +3929,41 @@ fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u...@@ -3930,42 +3929,41 @@ fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u
3930 }3929 }
3931 for (self.dylibs.items) |index| {3930 for (self.dylibs.items) |index| {
3932 const dylib = self.getFile(index).?.dylib;3931 const dylib = self.getFile(index).?.dylib;
3933 try bw.print("dylib({d}) : {f} : needed({}) : weak({})", .{3932 try w.print("dylib({d}) : {f} : needed({}) : weak({})", .{
3934 index,3933 index,
3935 @as(Path, dylib.path),3934 @as(Path, dylib.path),
3936 dylib.needed,3935 dylib.needed,
3937 dylib.weak,3936 dylib.weak,
3938 });3937 });
3939 if (!dylib.isAlive(self)) try bw.writeAll(" : ([*])");3938 if (!dylib.isAlive(self)) try w.writeAll(" : ([*])");
3940 try bw.writeByte('\n');3939 try w.writeByte('\n');
3941 try bw.print("{f}\n", .{dylib.fmtSymtab(self)});3940 try w.print("{f}\n", .{dylib.fmtSymtab(self)});
3942 }3941 }
3943 if (self.getInternalObject()) |internal| {3942 if (self.getInternalObject()) |internal| {
3944 try bw.print("internal({d}) : internal\n", .{internal.index});3943 try w.print("internal({d}) : internal\n", .{internal.index});
3945 try bw.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });3944 try w.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) });
3946 }3945 }
3947 try bw.writeAll("thunks\n");3946 try w.writeAll("thunks\n");
3948 for (self.thunks.items, 0..) |thunk, index| {3947 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) });
3950 }3949 }
3951 try bw.print("stubs\n{f}\n", .{self.stubs.fmt(self)});3950 try w.print("stubs\n{f}\n", .{self.stubs.fmt(self)});
3952 try bw.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)});3951 try w.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)});
3953 try bw.print("got\n{f}\n", .{self.got.fmt(self)});3952 try w.print("got\n{f}\n", .{self.got.fmt(self)});
3954 try bw.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)});3953 try w.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)});
3955 try bw.writeByte('\n');3954 try w.writeByte('\n');
3956 try bw.print("sections\n{f}\n", .{self.fmtSections()});3955 try w.print("sections\n{f}\n", .{self.fmtSections()});
3957 try bw.print("segments\n{f}\n", .{self.fmtSegments()});3956 try w.print("segments\n{f}\n", .{self.fmtSegments()});
3958}3957}
39593958
3960fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) {3959fn fmtSections(self: *MachO) std.fmt.Formatter(*MachO, formatSections) {
3961 return .{ .data = self };3960 return .{ .data = self };
3962}3961}
39633962
3964fn formatSections(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {3963fn formatSections(self: *MachO, w: *Writer) Writer.Error!void {
3965 _ = unused_fmt_string;
3966 const slice = self.sections.slice();3964 const slice = self.sections.slice();
3967 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {3965 for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| {
3968 try bw.print(3966 try w.print(
3969 "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n",3967 "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n",
3970 .{3968 .{
3971 i, seg_id, header.segName(), header.sectName(), header.addr, header.offset,3969 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...@@ -3975,26 +3973,24 @@ fn formatSections(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const
3975 }3973 }
3976}3974}
39773975
3978fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) {3976fn fmtSegments(self: *MachO) std.fmt.Formatter(*MachO, formatSegments) {
3979 return .{ .data = self };3977 return .{ .data = self };
3980}3978}
39813979
3982fn formatSegments(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {3980fn formatSegments(self: *MachO, w: *Writer) Writer.Error!void {
3983 _ = unused_fmt_string;
3984 for (self.segments.items, 0..) |seg, i| {3981 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", .{
3986 i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize,3983 i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize,
3987 seg.fileoff, seg.fileoff + seg.filesize,3984 seg.fileoff, seg.fileoff + seg.filesize,
3988 });3985 });
3989 }3986 }
3990}3987}
39913988
3992pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) {3989pub fn fmtSectType(tt: u8) std.fmt.Formatter(u8, formatSectType) {
3993 return .{ .data = tt };3990 return .{ .data = tt };
3994}3991}
39953992
3996fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {3993fn formatSectType(tt: u8, w: *Writer) Writer.Error!void {
3997 _ = unused_fmt_string;
3998 const name = switch (tt) {3994 const name = switch (tt) {
3999 macho.S_REGULAR => "REGULAR",3995 macho.S_REGULAR => "REGULAR",
4000 macho.S_ZEROFILL => "ZEROFILL",3996 macho.S_ZEROFILL => "ZEROFILL",
...@@ -4018,9 +4014,9 @@ fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) W...@@ -4018,9 +4014,9 @@ fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) W
4018 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS",4014 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS",
4019 macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS",4015 macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS",
4020 macho.S_INIT_FUNC_OFFSETS => "INIT_FUNC_OFFSETS",4016 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}),
4022 };4018 };
4023 try bw.print("{s}", .{name});4019 try w.print("{s}", .{name});
4024}4020}
40254021
4026const is_hot_update_compatible = switch (builtin.target.os.tag) {4022const is_hot_update_compatible = switch (builtin.target.os.tag) {
...@@ -4253,28 +4249,27 @@ pub const Platform = struct {...@@ -4253,28 +4249,27 @@ pub const Platform = struct {
4253 return false;4249 return false;
4254 }4250 }
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) {
4257 return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };4253 return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
4258 }4254 }
42594255
4260 const FmtCtx = struct {4256 const Format = struct {
4261 platform: Platform,4257 platform: Platform,
4262 cpu_arch: std.Target.Cpu.Arch,4258 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 {4260 pub fn target(f: Format, w: *Writer) Writer.Error!void {
4266 _ = unused_fmt_string;4261 try w.print("{s}-{s}", .{ @tagName(f.cpu_arch), @tagName(f.platform.os_tag) });
4267 try bw.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });4262 if (f.platform.abi != .none) {
4268 if (ctx.platform.abi != .none) {4263 try w.print("-{s}", .{@tagName(f.platform.abi)});
4269 try bw.print("-{s}", .{@tagName(ctx.platform.abi)});4264 }
4270 }4265 }
4271 }4266 };
42724267
4273 /// Caller owns the memory.4268 /// Caller owns the memory.
4274 pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {4269 pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
4275 var buffer = std.ArrayList(u8).init(gpa);4270 var buffer = std.ArrayList(u8).init(gpa);
4276 defer buffer.deinit();4271 defer buffer.deinit();
4277 try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)});4272 try buffer.writer().print("{f}", .{plat.fmtTarget(cpu_arch)});
4278 return buffer.toOwnedSlice();4273 return buffer.toOwnedSlice();
4279 }4274 }
42804275
...@@ -4475,8 +4470,7 @@ pub const Ref = struct {...@@ -4475,8 +4470,7 @@ pub const Ref = struct {
4475 };4470 };
4476 }4471 }
44774472
4478 pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {4473 pub fn format(ref: Ref, bw: *Writer) Writer.Error!void {
4479 comptime assert(unused_fmt_string.len == 0);
4480 try bw.print("%{d} in file({d})", .{ ref.index, ref.file });4474 try bw.print("%{d} in file({d})", .{ ref.index, ref.file });
4481 }4475 }
4482};4476};
src/link/MachO/Archive.zig+13-14
...@@ -30,7 +30,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File...@@ -30,7 +30,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
3030
31 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {31 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
32 return diags.failParse(path, "invalid header delimiter: expected '{f}', found '{f}'", .{32 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),
34 });34 });
35 }35 }
3636
...@@ -203,26 +203,25 @@ pub const ArSymtab = struct {...@@ -203,26 +203,25 @@ pub const ArSymtab = struct {
203 try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len);203 try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len);
204 }204 }
205205
206 const FormatContext = struct {206 const PrintFormat = struct {
207 ar: ArSymtab,207 ar: ArSymtab,
208 macho_file: *MachO,208 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 }
209 };219 };
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) {
212 return .{ .data = .{ .ar = ar, .macho_file = macho_file } };222 return .{ .data = .{ .ar = ar, .macho_file = macho_file } };
213 }223 }
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
226 const Entry = struct {225 const Entry = struct {
227 /// Symbol name offset226 /// Symbol name offset
228 off: u32,227 off: u32,
src/link/MachO/Atom.zig+25-39
...@@ -937,8 +937,8 @@ const x86_64 = struct {...@@ -937,8 +937,8 @@ const x86_64 = struct {
937 }937 }
938938
939 fn encode(insts: []const Instruction, code: []u8) !void {939 fn encode(insts: []const Instruction, code: []u8) !void {
940 var bw: Writer = .fixed(code);940 var stream: Writer = .fixed(code);
941 for (insts) |inst| try inst.encode(&bw, .{});941 for (insts) |inst| try inst.encode(&stream, .{});
942 }942 }
943943
944 const bits = @import("../../arch/x86_64/bits.zig");944 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...@@ -1113,54 +1113,40 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
1113 assert(i == buffer.len);1113 assert(i == buffer.len);
1114}1114}
11151115
1116pub fn format(1116pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
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) {
1130 return .{ .data = .{1117 return .{ .data = .{
1131 .atom = atom,1118 .atom = atom,
1132 .macho_file = macho_file,1119 .macho_file = macho_file,
1133 } };1120 } };
1134}1121}
11351122
1136const FormatContext = struct {1123const Format = struct {
1137 atom: Atom,1124 atom: Atom,
1138 macho_file: *MachO,1125 macho_file: *MachO,
1139};
11401126
1141fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1127 fn print(f: Format, w: *Writer) Writer.Error!void {
1142 comptime assert(unused_fmt_string.len == 0);1128 const atom = f.atom;
1143 const atom = ctx.atom;1129 const macho_file = f.macho_file;
1144 const macho_file = ctx.macho_file;1130 const file = atom.getFile(macho_file);
1145 const file = atom.getFile(macho_file);1131 try w.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
1146 try bw.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),
1147 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),1133 atom.out_n_sect, atom.alignment, atom.size,
1148 atom.out_n_sect, atom.alignment, atom.size,1134 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
1149 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,1135 });
1150 });1136 if (!atom.isAlive()) try w.writeAll(" : [*]");
1151 if (!atom.isAlive()) try bw.writeAll(" : [*]");1137 if (atom.getUnwindRecords(macho_file).len > 0) {
1152 if (atom.getUnwindRecords(macho_file).len > 0) {1138 try w.writeAll(" : unwind{ ");
1153 try bw.writeAll(" : unwind{ ");1139 const extra = atom.getExtra(macho_file);
1154 const extra = atom.getExtra(macho_file);1140 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
1155 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {1141 const rec = file.object.getUnwindRecord(index);
1156 const rec = file.object.getUnwindRecord(index);1142 try w.print("{d}", .{index});
1157 try bw.print("{d}", .{index});1143 if (!rec.alive) try w.writeAll("([*])");
1158 if (!rec.alive) try bw.writeAll("([*])");1144 if (i < extra.unwind_index + extra.unwind_count - 1) try w.writeAll(", ");
1159 if (i < extra.unwind_index + extra.unwind_count - 1) try bw.writeAll(", ");1145 }
1146 try w.writeAll(" }");
1160 }1147 }
1161 try bw.writeAll(" }");
1162 }1148 }
1163}1149};
11641150
1165pub const Index = u32;1151pub 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 {...@@ -650,46 +650,32 @@ pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void {
650 }650 }
651}651}
652652
653pub fn format(653pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
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) {
667 return .{ .data = .{654 return .{ .data = .{
668 .dylib = self,655 .dylib = self,
669 .macho_file = macho_file,656 .macho_file = macho_file,
670 } };657 } };
671}658}
672659
673const FormatContext = struct {660const Format = struct {
674 dylib: *Dylib,661 dylib: *Dylib,
675 macho_file: *MachO,662 macho_file: *MachO,
676};
677663
678fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {664 fn symtab(f: Format, w: *Writer) Writer.Error!void {
679 _ = unused_fmt_string;665 const dylib = f.dylib;
680 const dylib = ctx.dylib;666 const macho_file = f.macho_file;
681 const macho_file = ctx.macho_file;667 try w.writeAll(" globals\n");
682 try bw.writeAll(" globals\n");668 for (dylib.symbols.items, 0..) |sym, i| {
683 for (dylib.symbols.items, 0..) |sym, i| {669 const ref = dylib.getSymbolRef(@intCast(i), macho_file);
684 const ref = dylib.getSymbolRef(@intCast(i), macho_file);670 if (ref.getFile(macho_file) == null) {
685 if (ref.getFile(macho_file) == null) {671 // TODO any better way of handling this?
686 // TODO any better way of handling this?672 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
687 try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});673 } else {
688 } else {674 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
689 try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});675 }
690 }676 }
691 }677 }
692}678};
693679
694pub const TargetMatcher = struct {680pub const TargetMatcher = struct {
695 allocator: Allocator,681 allocator: Allocator,
src/link/MachO/InternalObject.zig+26-28
...@@ -836,50 +836,48 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool {...@@ -836,50 +836,48 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool {
836 return false;836 return false;
837}837}
838838
839const FormatContext = struct {839const Format = struct {
840 self: *InternalObject,840 self: *InternalObject,
841 macho_file: *MachO,841 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 }
842};865};
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) {
845 return .{ .data = .{868 return .{ .data = .{
846 .self = self,869 .self = self,
847 .macho_file = macho_file,870 .macho_file = macho_file,
848 } };871 } };
849}872}
850873
851fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {874pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
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) {
861 return .{ .data = .{875 return .{ .data = .{
862 .self = self,876 .self = self,
863 .macho_file = macho_file,877 .macho_file = macho_file,
864 } };878 } };
865}879}
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
883const Section = struct {881const Section = struct {
884 header: macho.section_64,882 header: macho.section_64,
885 relocs: std.ArrayListUnmanaged(Relocation) = .empty,883 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 {...@@ -308,7 +308,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
308 } else nlists.len;308 } else nlists.len;
309309
310 if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {310 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);
312 defer allocator.free(name);314 defer allocator.free(name);
313 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;315 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
314 const atom_index = try self.addAtom(allocator, .{316 const atom_index = try self.addAtom(allocator, .{
...@@ -364,7 +366,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {...@@ -364,7 +366,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
364 // which cannot be contained in any non-zero atom (since then this atom366 // which cannot be contained in any non-zero atom (since then this atom
365 // would exceed section boundaries). In order to facilitate this behaviour,367 // would exceed section boundaries). In order to facilitate this behaviour,
366 // we create a dummy zero-sized atom at section end (addr + size).368 // 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);
368 defer allocator.free(name);372 defer allocator.free(name);
369 const atom_index = try self.addAtom(allocator, .{373 const atom_index = try self.addAtom(allocator, .{
370 .name = try self.addString(allocator, name),374 .name = try self.addString(allocator, name),
...@@ -394,7 +398,7 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {...@@ -394,7 +398,7 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
394 if (isFixedSizeLiteral(sect)) continue;398 if (isFixedSizeLiteral(sect)) continue;
395 if (isPtrLiteral(sect)) continue;399 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);
398 defer allocator.free(name);402 defer allocator.free(name);
399403
400 const atom_index = try self.addAtom(allocator, .{404 const atom_index = try self.addAtom(allocator, .{
...@@ -462,7 +466,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m...@@ -462,7 +466,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m
462 }466 }
463 end += 1;467 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);
466 defer allocator.free(name);470 defer allocator.free(name);
467 const name_str = try self.addString(allocator, name);471 const name_str = try self.addString(allocator, name);
468472
...@@ -529,7 +533,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO...@@ -529,7 +533,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO
529 pos += rec_size;533 pos += rec_size;
530 count += 1;534 count += 1;
531 }) {535 }) {
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);
533 defer allocator.free(name);537 defer allocator.free(name);
534 const name_str = try self.addString(allocator, name);538 const name_str = try self.addString(allocator, name);
535539
...@@ -587,7 +591,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)...@@ -587,7 +591,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)
587 for (0..num_ptrs) |i| {591 for (0..num_ptrs) |i| {
588 const pos: u32 = @as(u32, @intCast(i)) * rec_size;592 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);
591 defer allocator.free(name);595 defer allocator.free(name);
592 const name_str = try self.addString(allocator, name);596 const name_str = try self.addString(allocator, name);
593597
...@@ -1558,7 +1562,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {...@@ -1558,7 +1562,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
1558 const nlist = &self.symtab.items(.nlist)[nlist_idx];1562 const nlist = &self.symtab.items(.nlist)[nlist_idx];
1559 const nlist_atom = &self.symtab.items(.atom)[nlist_idx];1563 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);
1562 defer gpa.free(name);1566 defer gpa.free(name);
15631567
1564 const alignment = (nlist.n_desc >> 8) & 0x0f;1568 const alignment = (nlist.n_desc >> 8) & 0x0f;
...@@ -2512,130 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_...@@ -2512,130 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_
2512 return data;2516 return data;
2513}2517}
25142518
2515pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2519const Format = struct {
2516 _ = self;
2517 _ = bw;
2518 _ = unused_fmt_string;
2519 @compileError("do not format objects directly");
2520}
2521
2522const FormatContext = struct {
2523 object: *Object,2520 object: *Object,
2524 macho_file: *MachO,2521 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 }
2525};2579};
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) {
2528 return .{ .data = .{2582 return .{ .data = .{
2529 .object = self,2583 .object = self,
2530 .macho_file = macho_file,2584 .macho_file = macho_file,
2531 } };2585 } };
2532}2586}
25332587
2534fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2588pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.cies) {
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) {
2546 return .{ .data = .{2589 return .{ .data = .{
2547 .object = self,2590 .object = self,
2548 .macho_file = macho_file,2591 .macho_file = macho_file,
2549 } };2592 } };
2550}2593}
25512594
2552fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2595pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.fdes) {
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) {
2562 return .{ .data = .{2596 return .{ .data = .{
2563 .object = self,2597 .object = self,
2564 .macho_file = macho_file,2598 .macho_file = macho_file,
2565 } };2599 } };
2566}2600}
25672601
2568fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2602pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.unwindRecords) {
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) {
2578 return .{ .data = .{2603 return .{ .data = .{
2579 .object = self,2604 .object = self,
2580 .macho_file = macho_file,2605 .macho_file = macho_file,
2581 } };2606 } };
2582}2607}
25832608
2584fn formatUnwindRecords(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2609pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) {
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) {
2595 return .{ .data = .{2610 return .{ .data = .{
2596 .object = self,2611 .object = self,
2597 .macho_file = macho_file,2612 .macho_file = macho_file,
2598 } };2613 } };
2599}2614}
26002615
2601fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2616pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) {
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) {
2628 return .{ .data = self };2617 return .{ .data = self };
2629}2618}
26302619
2631fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2620fn formatPath(object: Object, w: *Writer) Writer.Error!void {
2632 _ = unused_fmt_string;
2633 if (object.in_archive) |ar| {2621 if (object.in_archive) |ar| {
2634 try bw.print("{f}({s})", .{2622 try w.print("{f}({s})", .{
2635 ar.path, object.path.basename(),2623 ar.path, object.path.basename(),
2636 });2624 });
2637 } else {2625 } else {
2638 try bw.print("{f}", .{object.path});2626 try w.print("{f}", .{object.path});
2639 }2627 }
2640}2628}
26412629
...@@ -2689,30 +2677,25 @@ const StabFile = struct {...@@ -2689,30 +2677,25 @@ const StabFile = struct {
2689 return object.symbols.items[index];2677 return object.symbols.items[index];
2690 }2678 }
26912679
2692 pub fn format(stab: Stab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2680 const Format = struct {
2693 _ = stab;2681 stab: Stab,
2694 _ = bw;2682 object: Object,
2695 _ = unused_fmt_string;
2696 @compileError("do not format stabs directly");
2697 }
2698
2699 const StabFormatContext = struct { Stab, Object };
27002683
2701 pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) {2684 fn default(f: Stab.Format, w: *Writer) Writer.Error!void {
2702 return .{ .data = .{ stab, object } };2685 const stab = f.stab;
2703 }2686 const sym = stab.getSymbol(f.object).?;
27042687 if (stab.is_func) {
2705 fn format2(ctx: StabFormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {2688 try w.print("func({d})", .{stab.index.?});
2706 _ = unused_fmt_string;2689 } else if (sym.visibility == .global) {
2707 const stab, const object = ctx;2690 try w.print("gsym({d})", .{stab.index.?});
2708 const sym = stab.getSymbol(object).?;2691 } else {
2709 if (stab.is_func) {2692 try w.print("stsym({d})", .{stab.index.?});
2710 try bw.print("func({d})", .{stab.index.?});2693 }
2711 } else if (sym.visibility == .global) {
2712 try bw.print("gsym({d})", .{stab.index.?});
2713 } else {
2714 try bw.print("stsym({d})", .{stab.index.?});
2715 }2694 }
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 } };
2716 }2699 }
2717 };2700 };
2718};2701};
src/link/MachO/Relocation.zig+43-42
...@@ -70,50 +70,51 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {...@@ -70,50 +70,51 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {
70 return lhs.offset < rhs.offset;70 return lhs.offset < rhs.offset;
71}71}
7272
73const FormatCtx = struct { Relocation, std.Target.Cpu.Arch };73pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) {
7474 return .{ .data = .{ .relocation = rel, .arch = cpu_arch } };
75pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatPretty) {
76 return .{ .data = .{ rel, cpu_arch } };
77}75}
7876
79fn formatPretty(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {77const Format = struct {
80 _ = unused_fmt_string;78 relocation: Relocation,
81 const rel, const cpu_arch = ctx;79 arch: std.Target.Cpu.Arch,
82 try bw.writeAll(switch (rel.type) {80
83 .signed => "X86_64_RELOC_SIGNED",81 fn pretty(f: Format, w: *Writer) Writer.Error!void {
84 .signed1 => "X86_64_RELOC_SIGNED_1",82 try w.writeAll(switch (f.relocation.type) {
85 .signed2 => "X86_64_RELOC_SIGNED_2",83 .signed => "X86_64_RELOC_SIGNED",
86 .signed4 => "X86_64_RELOC_SIGNED_4",84 .signed1 => "X86_64_RELOC_SIGNED_1",
87 .got_load => "X86_64_RELOC_GOT_LOAD",85 .signed2 => "X86_64_RELOC_SIGNED_2",
88 .tlv => "X86_64_RELOC_TLV",86 .signed4 => "X86_64_RELOC_SIGNED_4",
89 .page => "ARM64_RELOC_PAGE21",87 .got_load => "X86_64_RELOC_GOT_LOAD",
90 .pageoff => "ARM64_RELOC_PAGEOFF12",88 .tlv => "X86_64_RELOC_TLV",
91 .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21",89 .page => "ARM64_RELOC_PAGE21",
92 .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",90 .pageoff => "ARM64_RELOC_PAGEOFF12",
93 .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21",91 .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21",
94 .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",92 .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
95 .branch => switch (cpu_arch) {93 .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21",
96 .x86_64 => "X86_64_RELOC_BRANCH",94 .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
97 .aarch64 => "ARM64_RELOC_BRANCH26",95 .branch => switch (f.arch) {
98 else => unreachable,96 .x86_64 => "X86_64_RELOC_BRANCH",
99 },97 .aarch64 => "ARM64_RELOC_BRANCH26",
100 .got => switch (cpu_arch) {98 else => unreachable,
101 .x86_64 => "X86_64_RELOC_GOT",99 },
102 .aarch64 => "ARM64_RELOC_POINTER_TO_GOT",100 .got => switch (f.arch) {
103 else => unreachable,101 .x86_64 => "X86_64_RELOC_GOT",
104 },102 .aarch64 => "ARM64_RELOC_POINTER_TO_GOT",
105 .subtractor => switch (cpu_arch) {103 else => unreachable,
106 .x86_64 => "X86_64_RELOC_SUBTRACTOR",104 },
107 .aarch64 => "ARM64_RELOC_SUBTRACTOR",105 .subtractor => switch (f.arch) {
108 else => unreachable,106 .x86_64 => "X86_64_RELOC_SUBTRACTOR",
109 },107 .aarch64 => "ARM64_RELOC_SUBTRACTOR",
110 .unsigned => switch (cpu_arch) {108 else => unreachable,
111 .x86_64 => "X86_64_RELOC_UNSIGNED",109 },
112 .aarch64 => "ARM64_RELOC_UNSIGNED",110 .unsigned => switch (f.arch) {
113 else => unreachable,111 .x86_64 => "X86_64_RELOC_UNSIGNED",
114 },112 .aarch64 => "ARM64_RELOC_UNSIGNED",
115 });113 else => unreachable,
116}114 },
115 });
116 }
117};
117118
118pub const Type = enum {119pub const Type = enum {
119 // x86_64120 // 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...@@ -286,59 +286,51 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
286 }286 }
287}287}
288288
289pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {289pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
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) {
302 return .{ .data = .{290 return .{ .data = .{
303 .symbol = symbol,291 .symbol = symbol,
304 .macho_file = macho_file,292 .macho_file = macho_file,
305 } };293 } };
306}294}
307295
308fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {296const Format = struct {
309 comptime assert(unused_fmt_string.len == 0);297 symbol: Symbol,
310 const symbol = ctx.symbol;298 macho_file: *MachO,
311 try bw.print("%{d} : {s} : @{x}", .{299
312 symbol.nlist_idx,300 fn default(f: Format, w: *Writer) Writer.Error!void {
313 symbol.getName(ctx.macho_file),301 const symbol = f.symbol;
314 symbol.getAddress(.{}, ctx.macho_file),302 try w.print("%{d} : {s} : @{x}", .{
315 });303 symbol.nlist_idx,
316 if (symbol.getFile(ctx.macho_file)) |file| {304 symbol.getName(f.macho_file),
317 if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {305 symbol.getAddress(.{}, f.macho_file),
318 try bw.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});306 });
319 }307 if (symbol.getFile(f.macho_file)) |file| {
320 if (symbol.getAtom(ctx.macho_file)) |atom| {308 if (symbol.getOutputSectionIndex(f.macho_file) != 0) {
321 try bw.print(" : atom({d})", .{atom.atom_index});309 try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)});
322 }310 }
323 var buf: [3]u8 = .{'_'} ** 3;311 if (symbol.getAtom(f.macho_file)) |atom| {
324 if (symbol.flags.@"export") buf[0] = 'E';312 try w.print(" : atom({d})", .{atom.atom_index});
325 if (symbol.flags.import) buf[1] = 'I';313 }
326 switch (symbol.visibility) {314 var buf: [3]u8 = .{'_'} ** 3;
327 .local => buf[2] = 'L',315 if (symbol.flags.@"export") buf[0] = 'E';
328 .hidden => buf[2] = 'H',316 if (symbol.flags.import) buf[1] = 'I';
329 .global => buf[2] = 'G',317 switch (symbol.visibility) {
330 }318 .local => buf[2] = 'L',
331 try bw.print(" : {s}", .{&buf});319 .hidden => buf[2] = 'H',
332 if (symbol.flags.weak) try bw.writeAll(" : weak");320 .global => buf[2] = 'G',
333 if (symbol.isSymbolStab(ctx.macho_file)) try bw.writeAll(" : stab");321 }
334 switch (file) {322 try w.print(" : {s}", .{&buf});
335 .zig_object => |x| try bw.print(" : zig_object({d})", .{x.index}),323 if (symbol.flags.weak) try w.writeAll(" : weak");
336 .internal => |x| try bw.print(" : internal({d})", .{x.index}),324 if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab");
337 .object => |x| try bw.print(" : object({d})", .{x.index}),325 switch (file) {
338 .dylib => |x| try bw.print(" : dylib({d})", .{x.index}),326 .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}),
339 }327 .internal => |x| try w.print(" : internal({d})", .{x.index}),
340 } else try bw.writeAll(" : unresolved");328 .object => |x| try w.print(" : object({d})", .{x.index}),
341}329 .dylib => |x| try w.print(" : dylib({d})", .{x.index}),
330 }
331 } else try w.writeAll(" : unresolved");
332 }
333};
342334
343pub const Flags = packed struct {335pub const Flags = packed struct {
344 /// Whether the symbol is imported at runtime.336 /// 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 {...@@ -61,35 +61,27 @@ pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
61 }61 }
62}62}
6363
64pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {64pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
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) {
72 return .{ .data = .{65 return .{ .data = .{
73 .thunk = thunk,66 .thunk = thunk,
74 .macho_file = macho_file,67 .macho_file = macho_file,
75 } };68 } };
76}69}
7770
78const FormatContext = struct {71const Format = struct {
79 thunk: Thunk,72 thunk: Thunk,
80 macho_file: *MachO,73 macho_file: *MachO,
81};
8274
83fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {75 fn default(f: Format, w: *Writer) Writer.Error!void {
84 _ = unused_fmt_string;76 const thunk = f.thunk;
85 const thunk = ctx.thunk;77 const macho_file = f.macho_file;
86 const macho_file = ctx.macho_file;78 try w.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
87 try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });79 for (thunk.symbols.keys()) |ref| {
88 for (thunk.symbols.keys()) |ref| {80 const sym = ref.getSymbol(macho_file).?;
89 const sym = ref.getSymbol(macho_file).?;81 try w.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
90 try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });82 }
91 }83 }
92}84};
9385
94const trampoline_size = 3 * @sizeOf(u32);86const trampoline_size = 3 * @sizeOf(u32);
9587
src/link/MachO/UnwindInfo.zig+29-46
...@@ -449,9 +449,8 @@ pub const Encoding = extern struct {...@@ -449,9 +449,8 @@ pub const Encoding = extern struct {
449 return enc.enc == other.enc;449 return enc.enc == other.enc;
450 }450 }
451451
452 pub fn format(enc: Encoding, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {452 pub fn format(enc: Encoding, w: *Writer) Writer.Error!void {
453 _ = unused_fmt_string;453 try w.print("0x{x:0>8}", .{enc.enc});
454 try bw.print("0x{x:0>8}", .{enc.enc});
455 }454 }
456};455};
457456
...@@ -505,36 +504,28 @@ pub const Record = struct {...@@ -505,36 +504,28 @@ pub const Record = struct {
505 return lsda.getAddress(macho_file) + rec.lsda_offset;504 return lsda.getAddress(macho_file) + rec.lsda_offset;
506 }505 }
507506
508 pub fn format(rec: Record, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {507 pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
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) {
516 return .{ .data = .{508 return .{ .data = .{
517 .rec = rec,509 .rec = rec,
518 .macho_file = macho_file,510 .macho_file = macho_file,
519 } };511 } };
520 }512 }
521513
522 const FormatContext = struct {514 const Format = struct {
523 rec: Record,515 rec: Record,
524 macho_file: *MachO,516 macho_file: *MachO,
525 };
526517
527 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {518 fn default(f: Format, w: *Writer) Writer.Error!void {
528 _ = unused_fmt_string;519 const rec = f.rec;
529 const rec = ctx.rec;520 const macho_file = f.macho_file;
530 const macho_file = ctx.macho_file;521 try w.print("{x} : len({x})", .{
531 try bw.print("{x} : len({x})", .{522 rec.enc.enc, rec.length,
532 rec.enc.enc, rec.length,523 });
533 });524 if (rec.enc.isDwarf(macho_file)) try w.print(" : fde({d})", .{rec.fde});
534 if (rec.enc.isDwarf(macho_file)) try bw.print(" : fde({d})", .{rec.fde});525 try w.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)});
535 try bw.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)});526 if (!rec.alive) try w.writeAll(" : [*]");
536 if (!rec.alive) try bw.writeAll(" : [*]");527 }
537 }528 };
538529
539 pub const Index = u32;530 pub const Index = u32;
540531
...@@ -589,33 +580,25 @@ const Page = struct {...@@ -589,33 +580,25 @@ const Page = struct {
589 return null;580 return null;
590 }581 }
591582
592 fn format(page: *const Page, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {583 const Format = struct {
593 _ = page;
594 _ = bw;
595 _ = unused_format_string;
596 @compileError("do not format Page directly; use page.fmt()");
597 }
598
599 const FormatPageContext = struct {
600 page: Page,584 page: Page,
601 info: UnwindInfo,585 info: UnwindInfo,
602 };
603586
604 fn format2(ctx: FormatPageContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {587 fn default(f: Format, w: *Writer) Writer.Error!void {
605 _ = unused_format_string;588 try w.writeAll("Page:\n");
606 try bw.writeAll("Page:\n");589 try w.print(" kind: {s}\n", .{@tagName(f.page.kind)});
607 try bw.print(" kind: {s}\n", .{@tagName(ctx.page.kind)});590 try w.print(" entries: {d} - {d}\n", .{
608 try bw.print(" entries: {d} - {d}\n", .{591 f.page.start,
609 ctx.page.start,592 f.page.start + f.page.count,
610 ctx.page.start + ctx.page.count,593 });
611 });594 try w.print(" encodings (count = {d})\n", .{f.page.page_encodings_count});
612 try bw.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count});595 for (f.page.page_encodings[0..f.page.page_encodings_count], 0..) |enc, i| {
613 for (ctx.page.page_encodings[0..ctx.page.page_encodings_count], 0..) |enc, i| {596 try w.print(" {d}: {f}\n", .{ f.info.common_encodings_count + i, enc });
614 try bw.print(" {d}: {f}\n", .{ ctx.info.common_encodings_count + i, enc });597 }
615 }598 }
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) {
619 return .{ .data = .{602 return .{ .data = .{
620 .page = page,603 .page = page,
621 .info = info,604 .info = info,
src/link/MachO/ZigObject.zig+27-29
...@@ -957,7 +957,7 @@ fn updateNavCode(...@@ -957,7 +957,7 @@ fn updateNavCode(
957 sym.out_n_sect = sect_index;957 sym.out_n_sect = sect_index;
958 atom.out_n_sect = sect_index;958 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);
961 defer gpa.free(sym_name);961 defer gpa.free(sym_name);
962 sym.name = try self.addString(gpa, sym_name);962 sym.name = try self.addString(gpa, sym_name);
963 atom.setAlive(true);963 atom.setAlive(true);
...@@ -1676,52 +1676,50 @@ pub fn asFile(self: *ZigObject) File {...@@ -1676,52 +1676,50 @@ pub fn asFile(self: *ZigObject) File {
1676 return .{ .zig_object = self };1676 return .{ .zig_object = self };
1677}1677}
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) {
1680 return .{ .data = .{1680 return .{ .data = .{
1681 .self = self,1681 .self = self,
1682 .macho_file = macho_file,1682 .macho_file = macho_file,
1683 } };1683 } };
1684}1684}
16851685
1686const FormatContext = struct {1686const Format = struct {
1687 self: *ZigObject,1687 self: *ZigObject,
1688 macho_file: *MachO,1688 macho_file: *MachO,
1689};
16901689
1691fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {1690 fn symtab(f: Format, w: *Writer) Writer.Error!void {
1692 _ = unused_fmt_string;1691 try w.writeAll(" symbols\n");
1693 try bw.writeAll(" symbols\n");1692 const self = f.self;
1694 const self = ctx.self;1693 const macho_file = f.macho_file;
1695 const macho_file = ctx.macho_file;1694 for (self.symbols.items, 0..) |sym, i| {
1696 for (self.symbols.items, 0..) |sym, i| {1695 const ref = self.getSymbolRef(@intCast(i), macho_file);
1697 const ref = self.getSymbolRef(@intCast(i), macho_file);1696 if (ref.getFile(macho_file) == null) {
1698 if (ref.getFile(macho_file) == null) {1697 // TODO any better way of handling this?
1699 // TODO any better way of handling this?1698 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1700 try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});1699 } else {
1701 } else {1700 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1702 try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});1701 }
1703 }1702 }
1704 }1703 }
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) {
1708 return .{ .data = .{1717 return .{ .data = .{
1709 .self = self,1718 .self = self,
1710 .macho_file = macho_file,1719 .macho_file = macho_file,
1711 } };1720 } };
1712}1721}
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
1725const AvMetadata = struct {1723const AvMetadata = struct {
1726 symbol_index: Symbol.Index,1724 symbol_index: Symbol.Index,
1727 /// A list of all exports aliases of this Av.1725 /// A list of all exports aliases of this Av.
src/link/MachO/dead_strip.zig+2-3
...@@ -196,9 +196,8 @@ const Level = struct {...@@ -196,9 +196,8 @@ const Level = struct {
196 self.value += 1;196 self.value += 1;
197 }197 }
198198
199 pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {199 pub fn format(self: *const @This(), w: *Writer) Writer.Error!void {
200 _ = unused_fmt_string;200 try w.splatByteAll(' ', self.value);
201 try bw.splatByteAll(' ', self.value);
202 }201 }
203};202};
204203
src/link/MachO/dyld_info/bind.zig+2-2
...@@ -193,7 +193,7 @@ pub const Bind = struct {...@@ -193,7 +193,7 @@ pub const Bind = struct {
193 }193 }
194 }194 }
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) });
197 log.debug(" => {x}", .{current.offset});197 log.debug(" => {x}", .{current.offset});
198 switch (state) {198 switch (state) {
199 .start => {199 .start => {
...@@ -423,7 +423,7 @@ pub const WeakBind = struct {...@@ -423,7 +423,7 @@ pub const WeakBind = struct {
423 }423 }
424 }424 }
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) });
427 log.debug(" => {x}", .{current.offset});427 log.debug(" => {x}", .{current.offset});
428 switch (state) {428 switch (state) {
429 .start => {429 .start => {
src/link/MachO/eh_frame.zig+25-53
...@@ -78,40 +78,26 @@ pub const Cie = struct {...@@ -78,40 +78,26 @@ pub const Cie = struct {
78 return true;78 return true;
79 }79 }
8080
81 pub fn format(81 pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
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) {
95 return .{ .data = .{82 return .{ .data = .{
96 .cie = cie,83 .cie = cie,
97 .macho_file = macho_file,84 .macho_file = macho_file,
98 } };85 } };
99 }86 }
10087
101 const FormatContext = struct {88 const Format = struct {
102 cie: Cie,89 cie: Cie,
103 macho_file: *MachO,90 macho_file: *MachO,
104 };
10591
106 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {92 fn default(f: Format, w: *Writer) Writer.Error!void {
107 _ = unused_fmt_string;93 const cie = f.cie;
108 const cie = ctx.cie;94 try w.print("@{x} : size({x})", .{
109 try bw.print("@{x} : size({x})", .{95 cie.offset,
110 cie.offset,96 cie.getSize(),
111 cie.getSize(),97 });
112 });98 if (!cie.alive) try w.writeAll(" : [*]");
113 if (!cie.alive) try bw.writeAll(" : [*]");99 }
114 }100 };
115101
116 pub const Index = u32;102 pub const Index = u32;
117103
...@@ -223,43 +209,29 @@ pub const Fde = struct {...@@ -223,43 +209,29 @@ pub const Fde = struct {
223 return fde.getObject(macho_file).getAtom(fde.lsda);209 return fde.getObject(macho_file).getAtom(fde.lsda);
224 }210 }
225211
226 pub fn format(212 pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
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) {
240 return .{ .data = .{213 return .{ .data = .{
241 .fde = fde,214 .fde = fde,
242 .macho_file = macho_file,215 .macho_file = macho_file,
243 } };216 } };
244 }217 }
245218
246 const FormatContext = struct {219 const Format = struct {
247 fde: Fde,220 fde: Fde,
248 macho_file: *MachO,221 macho_file: *MachO,
249 };
250222
251 fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {223 fn default(f: Format, writer: *Writer) Writer.Error!void {
252 _ = unused_fmt_string;224 const fde = f.fde;
253 const fde = ctx.fde;225 const macho_file = f.macho_file;
254 const macho_file = ctx.macho_file;226 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
255 try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{227 fde.offset,
256 fde.offset,228 fde.getSize(),
257 fde.getSize(),229 fde.cie,
258 fde.cie,230 fde.getAtom(macho_file).getName(macho_file),
259 fde.getAtom(macho_file).getName(macho_file),231 });
260 });232 if (!fde.alive) try writer.writeAll(" : [*]");
261 if (!fde.alive) try bw.writeAll(" : [*]");233 }
262 }234 };
263235
264 pub const Index = u32;236 pub const Index = u32;
265};237};
src/link/MachO/file.zig+6-7
...@@ -10,17 +10,16 @@ pub const File = union(enum) {...@@ -10,17 +10,16 @@ pub const File = union(enum) {
10 };10 };
11 }11 }
1212
13 pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) {13 pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) {
14 return .{ .data = file };14 return .{ .data = file };
15 }15 }
1616
17 fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void {17 fn formatPath(file: File, w: *Writer) Writer.Error!void {
18 _ = unused_fmt_string;
19 switch (file) {18 switch (file) {
20 .zig_object => |zo| try bw.writeAll(zo.basename),19 .zig_object => |zo| try w.writeAll(zo.basename),
21 .internal => try bw.writeAll("internal"),20 .internal => try w.writeAll("internal"),
22 .object => |x| try bw.print("{f}", .{x.fmtPath()}),21 .object => |x| try w.print("{f}", .{x.fmtPath()}),
23 .dylib => |dl| try bw.print("{f}", .{@as(Path, dl.path)}),22 .dylib => |dl| try w.print("{f}", .{@as(Path, dl.path)}),
24 }23 }
25 }24 }
2625
src/link/MachO/synthetic.zig+66-86
...@@ -37,32 +37,27 @@ pub const GotSection = struct {...@@ -37,32 +37,27 @@ pub const GotSection = struct {
37 }37 }
38 }38 }
3939
40 const FormatCtx = struct {40 const Format = struct {
41 got: GotSection,41 got: GotSection,
42 macho_file: *MachO,42 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 }
43 };56 };
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) {
46 return .{ .data = .{ .got = got, .macho_file = macho_file } };59 return .{ .data = .{ .got = got, .macho_file = macho_file } };
47 }60 }
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 }
66};61};
6762
68pub const StubsSection = struct {63pub const StubsSection = struct {
...@@ -126,32 +121,27 @@ pub const StubsSection = struct {...@@ -126,32 +121,27 @@ pub const StubsSection = struct {
126 }121 }
127 }122 }
128123
129 const FormatCtx = struct {124 pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
130 stubs: StubsSection,
131 macho_file: *MachO,
132 };
133
134 pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(format2) {
135 return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } };125 return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } };
136 }126 }
137127
138 pub fn format2(128 const Format = struct {
139 ctx: FormatCtx,129 stubs: StubsSection,
140 bw: *Writer,130 macho_file: *MachO,
141 comptime unused_fmt_string: []const u8,131
142 ) !void {132 pub fn print(f: Format, w: *Writer) Writer.Error!void {
143 _ = unused_fmt_string;133 for (f.stubs.symbols.items, 0..) |ref, i| {
144 for (ctx.stubs.symbols.items, 0..) |ref, i| {134 const symbol = ref.getSymbol(f.macho_file).?;
145 const symbol = ref.getSymbol(ctx.macho_file).?;135 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
146 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{136 i,
147 i,137 symbol.getStubsAddress(f.macho_file),
148 symbol.getStubsAddress(ctx.macho_file),138 ref,
149 ref,139 symbol.getAddress(.{}, f.macho_file),
150 symbol.getAddress(.{}, ctx.macho_file),140 symbol.getName(f.macho_file),
151 symbol.getName(ctx.macho_file),141 });
152 });142 }
153 }143 }
154 }144 };
155};145};
156146
157pub const StubsHelperSection = struct {147pub const StubsHelperSection = struct {
...@@ -353,32 +343,27 @@ pub const TlvPtrSection = struct {...@@ -353,32 +343,27 @@ pub const TlvPtrSection = struct {
353 }343 }
354 }344 }
355345
356 const FormatCtx = struct {346 pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
357 tlv: TlvPtrSection,
358 macho_file: *MachO,
359 };
360
361 pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(format2) {
362 return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } };347 return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } };
363 }348 }
364349
365 pub fn format2(350 const Format = struct {
366 ctx: FormatCtx,351 tlv: TlvPtrSection,
367 bw: *Writer,352 macho_file: *MachO,
368 comptime unused_fmt_string: []const u8,353
369 ) !void {354 pub fn print(f: Format, w: *Writer) Writer.Error!void {
370 _ = unused_fmt_string;355 for (f.tlv.symbols.items, 0..) |ref, i| {
371 for (ctx.tlv.symbols.items, 0..) |ref, i| {356 const symbol = ref.getSymbol(f.macho_file).?;
372 const symbol = ref.getSymbol(ctx.macho_file).?;357 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
373 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{358 i,
374 i,359 symbol.getTlvPtrAddress(f.macho_file),
375 symbol.getTlvPtrAddress(ctx.macho_file),360 ref,
376 ref,361 symbol.getAddress(.{}, f.macho_file),
377 symbol.getAddress(.{}, ctx.macho_file),362 symbol.getName(f.macho_file),
378 symbol.getName(ctx.macho_file),363 });
379 });364 }
380 }365 }
381 }366 };
382};367};
383368
384pub const ObjcStubsSection = struct {369pub const ObjcStubsSection = struct {
...@@ -476,32 +461,27 @@ pub const ObjcStubsSection = struct {...@@ -476,32 +461,27 @@ pub const ObjcStubsSection = struct {
476 }461 }
477 }462 }
478463
479 const FormatCtx = struct {464 pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) {
480 objc: ObjcStubsSection,
481 macho_file: *MachO,
482 };
483
484 pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(format2) {
485 return .{ .data = .{ .objc = objc, .macho_file = macho_file } };465 return .{ .data = .{ .objc = objc, .macho_file = macho_file } };
486 }466 }
487467
488 pub fn format2(468 const Format = struct {
489 ctx: FormatCtx,469 objc: ObjcStubsSection,
490 bw: *Writer,470 macho_file: *MachO,
491 comptime unused_fmt_string: []const u8,471
492 ) !void {472 pub fn print(f: Format, w: *Writer) Writer.Error!void {
493 _ = unused_fmt_string;473 for (f.objc.symbols.items, 0..) |ref, i| {
494 for (ctx.objc.symbols.items, 0..) |ref, i| {474 const symbol = ref.getSymbol(f.macho_file).?;
495 const symbol = ref.getSymbol(ctx.macho_file).?;475 try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{
496 try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{476 i,
497 i,477 symbol.getObjcStubsAddress(f.macho_file),
498 symbol.getObjcStubsAddress(ctx.macho_file),478 ref,
499 ref,479 symbol.getAddress(.{}, f.macho_file),
500 symbol.getAddress(.{}, ctx.macho_file),480 symbol.getName(f.macho_file),
501 symbol.getName(ctx.macho_file),481 });
502 });482 }
503 }483 }
504 }484 };
505485
506 pub const Index = u32;486 pub const Index = u32;
507};487};
src/link/SpirV.zig+4-4
...@@ -206,7 +206,7 @@ pub fn flush(...@@ -206,7 +206,7 @@ pub fn flush(
206 var error_info: std.io.Writer.Allocating = .init(self.object.gpa);206 var error_info: std.io.Writer.Allocating = .init(self.object.gpa);
207 defer error_info.deinit();207 defer error_info.deinit();
208208
209 try error_info.writer.writeAll("zig_errors:");209 error_info.writer.writeAll("zig_errors:") catch return error.OutOfMemory;
210 const ip = &self.base.comp.zcu.?.intern_pool;210 const ip = &self.base.comp.zcu.?.intern_pool;
211 for (ip.global_error_set.getNamesFromMainThread()) |name| {211 for (ip.global_error_set.getNamesFromMainThread()) |name| {
212 // Errors can contain pretty much any character - to encode them in a string we must escape212 // Errors can contain pretty much any character - to encode them in a string we must escape
...@@ -214,8 +214,8 @@ pub fn flush(...@@ -214,8 +214,8 @@ pub fn flush(
214 // name if it contains no strange characters is nice for debugging. URI encoding fits the bill.214 // name if it contains no strange characters is nice for debugging. URI encoding fits the bill.
215 // We're using : as separator, which is a reserved character.215 // We're using : as separator, which is a reserved character.
216216
217 try error_info.writer.writeByte(':');217 error_info.writer.writeByte(':') catch return error.OutOfMemory;
218 try std.Uri.Component.percentEncode(218 std.Uri.Component.percentEncode(
219 &error_info.writer,219 &error_info.writer,
220 name.toSlice(ip),220 name.toSlice(ip),
221 struct {221 struct {
...@@ -226,7 +226,7 @@ pub fn flush(...@@ -226,7 +226,7 @@ pub fn flush(
226 };226 };
227 }227 }
228 }.isValidChar,228 }.isValidChar,
229 );229 ) catch return error.OutOfMemory;
230 }230 }
231 try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{231 try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{
232 .extension = error_info.getWritten(),232 .extension = error_info.getWritten(),
src/link/Wasm.zig+12-14
...@@ -32,7 +32,7 @@ const Writer = std.io.Writer;...@@ -32,7 +32,7 @@ const Writer = std.io.Writer;
3232
33const Mir = @import("../arch/wasm/Mir.zig");33const Mir = @import("../arch/wasm/Mir.zig");
34const CodeGen = @import("../arch/wasm/CodeGen.zig");34const CodeGen = @import("../arch/wasm/CodeGen.zig");
35const abi = @import("../arch/wasm/abi.zig");35const abi = @import("../codegen/wasm/abi.zig");
36const Compilation = @import("../Compilation.zig");36const Compilation = @import("../Compilation.zig");
37const Dwarf = @import("Dwarf.zig");37const Dwarf = @import("Dwarf.zig");
38const InternPool = @import("../InternPool.zig");38const InternPool = @import("../InternPool.zig");
...@@ -2125,26 +2125,25 @@ pub const FunctionType = extern struct {...@@ -2125,26 +2125,25 @@ pub const FunctionType = extern struct {
2125 wasm: *const Wasm,2125 wasm: *const Wasm,
2126 ft: FunctionType,2126 ft: FunctionType,
21272127
2128 pub fn format(self: Formatter, bw: *Writer, comptime format_string: []const u8) Writer.Error!void {2128 pub fn format(self: Formatter, writer: *std.io.Writer) std.io.Writer.Error!void {
2129 comptime assert(format_string.len == 0);
2130 const params = self.ft.params.slice(self.wasm);2129 const params = self.ft.params.slice(self.wasm);
2131 const returns = self.ft.returns.slice(self.wasm);2130 const returns = self.ft.returns.slice(self.wasm);
21322131
2133 try bw.writeByte('(');2132 try writer.writeByte('(');
2134 for (params, 0..) |param, i| {2133 for (params, 0..) |param, i| {
2135 try bw.print("{s}", .{@tagName(param)});2134 try writer.print("{s}", .{@tagName(param)});
2136 if (i + 1 != params.len) {2135 if (i + 1 != params.len) {
2137 try bw.writeAll(", ");2136 try writer.writeAll(", ");
2138 }2137 }
2139 }2138 }
2140 try bw.writeAll(") -> ");2139 try writer.writeAll(") -> ");
2141 if (returns.len == 0) {2140 if (returns.len == 0) {
2142 try bw.writeAll("nil");2141 try writer.writeAll("nil");
2143 } else {2142 } else {
2144 for (returns, 0..) |return_ty, i| {2143 for (returns, 0..) |return_ty, i| {
2145 try bw.print("{s}", .{@tagName(return_ty)});2144 try writer.print("{s}", .{@tagName(return_ty)});
2146 if (i + 1 != returns.len) {2145 if (i + 1 != returns.len) {
2147 try bw.writeAll(", ");2146 try writer.writeAll(", ");
2148 }2147 }
2149 }2148 }
2150 }2149 }
...@@ -2905,9 +2904,8 @@ pub const Feature = packed struct(u8) {...@@ -2905,9 +2904,8 @@ pub const Feature = packed struct(u8) {
2905 @"=",2904 @"=",
2906 };2905 };
29072906
2908 pub fn format(feature: Feature, bw: *Writer, comptime fmt: []const u8) Writer.Error!void {2907 pub fn format(feature: Feature, writer: *std.io.Writer) std.io.Writer.Error!void {
2909 _ = fmt;2908 try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
2910 try bw.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
2911 }2909 }
29122910
2913 pub fn lessThan(_: void, a: Feature, b: Feature) bool {2911 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...@@ -3299,7 +3297,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
3299 .variable => |variable| .{ variable.init, variable.owner_nav },3297 .variable => |variable| .{ variable.init, variable.owner_nav },
3300 else => .{ nav.status.fully_resolved.val, nav_index },3298 else => .{ nav.status.fully_resolved.val, nav_index },
3301 };3299 };
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 });
3303 assert(!wasm.imports.contains(chased_nav_index));3301 assert(!wasm.imports.contains(chased_nav_index));
33043302
3305 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {3303 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 {...@@ -534,7 +534,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
534 wasm.memories.limits.max = @intCast(max_memory / page_size);534 wasm.memories.limits.max = @intCast(max_memory / page_size);
535 wasm.memories.limits.flags.has_max = true;535 wasm.memories.limits.flags.has_max = true;
536 if (shared_memory) wasm.memories.limits.flags.is_shared = true;536 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});
538 }538 }
539 f.memory_layout_finished = true;539 f.memory_layout_finished = true;
540540
src/link/table_section.zig+3-4
...@@ -39,11 +39,10 @@ pub fn TableSection(comptime Entry: type) type {...@@ -39,11 +39,10 @@ pub fn TableSection(comptime Entry: type) type {
39 return self.entries.items.len;39 return self.entries.items.len;
40 }40 }
4141
42 pub fn format(self: Self, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void {42 pub fn format(self: Self, writer: *std.io.Writer) std.io.Writer.Error!void {
43 comptime assert(unused_format_string.len == 0);43 try writer.writeAll("TableSection:\n");
44 try bw.writeAll("TableSection:\n");
45 for (self.entries.items, 0..) |entry, i| {44 for (self.entries.items, 0..) |entry, i| {
46 try bw.print(" {d} => {}\n", .{ i, entry });45 try writer.print(" {d} => {}\n", .{ i, entry });
47 }46 }
48 }47 }
4948
src/link/tapi/parse.zig+10-43
...@@ -57,14 +57,9 @@ pub const Node = struct {...@@ -57,14 +57,9 @@ pub const Node = struct {
57 }57 }
58 }58 }
5959
60 pub fn format(60 pub fn format(self: *const Node, writer: *std.io.Writer) std.io.Writer.Error!void {
61 self: *const Node,
62 comptime fmt: []const u8,
63 options: std.fmt.FormatOptions,
64 writer: anytype,
65 ) !void {
66 switch (self.tag) {61 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),
68 }63 }
69 }64 }
7065
...@@ -86,24 +81,17 @@ pub const Node = struct {...@@ -86,24 +81,17 @@ pub const Node = struct {
86 }81 }
87 }82 }
8883
89 pub fn format(84 pub fn format(self: *const Doc, writer: *std.io.Writer) std.io.Writer.Error!void {
90 self: *const Doc,
91 comptime fmt: []const u8,
92 options: std.fmt.FormatOptions,
93 writer: anytype,
94 ) !void {
95 _ = options;
96 _ = fmt;
97 if (self.directive) |id| {85 if (self.directive) |id| {
98 try std.fmt.format(writer, "{{ ", .{});86 try writer.print("{{ ", .{});
99 const directive = self.base.tree.getRaw(id, id);87 const directive = self.base.tree.getRaw(id, id);
100 try std.fmt.format(writer, ".directive = {s}, ", .{directive});88 try writer.print(".directive = {s}, ", .{directive});
101 }89 }
102 if (self.value) |node| {90 if (self.value) |node| {
103 try std.fmt.format(writer, "{}", .{node});91 try writer.print("{}", .{node});
104 }92 }
105 if (self.directive != null) {93 if (self.directive != null) {
106 try std.fmt.format(writer, " }}", .{});94 try writer.print(" }}", .{});
107 }95 }
108 }96 }
109 };97 };
...@@ -133,14 +121,7 @@ pub const Node = struct {...@@ -133,14 +121,7 @@ pub const Node = struct {
133 self.values.deinit(allocator);121 self.values.deinit(allocator);
134 }122 }
135123
136 pub fn format(124 pub fn format(self: *const Map, writer: *std.io.Writer) std.io.Writer.Error!void {
137 self: *const Map,
138 comptime fmt: []const u8,
139 options: std.fmt.FormatOptions,
140 writer: anytype,
141 ) !void {
142 _ = options;
143 _ = fmt;
144 try std.fmt.format(writer, "{{ ", .{});125 try std.fmt.format(writer, "{{ ", .{});
145 for (self.values.items) |entry| {126 for (self.values.items) |entry| {
146 const key = self.base.tree.getRaw(entry.key, entry.key);127 const key = self.base.tree.getRaw(entry.key, entry.key);
...@@ -172,14 +153,7 @@ pub const Node = struct {...@@ -172,14 +153,7 @@ pub const Node = struct {
172 self.values.deinit(allocator);153 self.values.deinit(allocator);
173 }154 }
174155
175 pub fn format(156 pub fn format(self: *const List, writer: *std.io.Writer) std.io.Writer.Error!void {
176 self: *const List,
177 comptime fmt: []const u8,
178 options: std.fmt.FormatOptions,
179 writer: anytype,
180 ) !void {
181 _ = options;
182 _ = fmt;
183 try std.fmt.format(writer, "[ ", .{});157 try std.fmt.format(writer, "[ ", .{});
184 for (self.values.items) |node| {158 for (self.values.items) |node| {
185 try std.fmt.format(writer, "{}, ", .{node});159 try std.fmt.format(writer, "{}, ", .{node});
...@@ -203,14 +177,7 @@ pub const Node = struct {...@@ -203,14 +177,7 @@ pub const Node = struct {
203 self.string_value.deinit(allocator);177 self.string_value.deinit(allocator);
204 }178 }
205179
206 pub fn format(180 pub fn format(self: *const Value, writer: *std.io.Writer) std.io.Writer.Error!void {
207 self: *const Value,
208 comptime fmt: []const u8,
209 options: std.fmt.FormatOptions,
210 writer: anytype,
211 ) !void {
212 _ = options;
213 _ = fmt;
214 const raw = self.base.tree.getRaw(self.base.start, self.base.end);181 const raw = self.base.tree.getRaw(self.base.start, self.base.end);
215 return std.fmt.format(writer, "{s}", .{raw});182 return std.fmt.format(writer, "{s}", .{raw});
216 }183 }
src/main.zig+62-53
...@@ -309,6 +309,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -309,6 +309,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
309 return jitCmd(gpa, arena, cmd_args, .{309 return jitCmd(gpa, arena, cmd_args, .{
310 .cmd_name = "resinator",310 .cmd_name = "resinator",
311 .root_src_path = "resinator/main.zig",311 .root_src_path = "resinator/main.zig",
312 .windows_libs = &.{"advapi32"},
312 .depend_on_aro = true,313 .depend_on_aro = true,
313 .prepend_zig_lib_dir_path = true,314 .prepend_zig_lib_dir_path = true,
314 .server = use_server,315 .server = use_server,
...@@ -972,8 +973,6 @@ fn buildOutputType(...@@ -972,8 +973,6 @@ fn buildOutputType(
972 .windows_libs = .empty,973 .windows_libs = .empty,
973 .link_inputs = .empty,974 .link_inputs = .empty,
974975
975 .wasi_emulated_libs = .{},
976
977 .c_source_files = .{},976 .c_source_files = .{},
978 .rc_source_files = .{},977 .rc_source_files = .{},
979978
...@@ -1418,7 +1417,7 @@ fn buildOutputType(...@@ -1418,7 +1417,7 @@ fn buildOutputType(
1418 } else if (mem.eql(u8, arg, "-funwind-tables")) {1417 } else if (mem.eql(u8, arg, "-funwind-tables")) {
1419 mod_opts.unwind_tables = .sync;1418 mod_opts.unwind_tables = .sync;
1420 } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) {1419 } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) {
1421 mod_opts.unwind_tables = .@"async";1420 mod_opts.unwind_tables = .async;
1422 } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {1421 } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
1423 mod_opts.unwind_tables = .none;1422 mod_opts.unwind_tables = .none;
1424 } else if (mem.eql(u8, arg, "-fstack-check")) {1423 } else if (mem.eql(u8, arg, "-fstack-check")) {
...@@ -2039,15 +2038,15 @@ fn buildOutputType(...@@ -2039,15 +2038,15 @@ fn buildOutputType(
2039 .none => {2038 .none => {
2040 mod_opts.unwind_tables = .sync;2039 mod_opts.unwind_tables = .sync;
2041 },2040 },
2042 .sync, .@"async" => {},2041 .sync, .async => {},
2043 } else {2042 } else {
2044 mod_opts.unwind_tables = .sync;2043 mod_opts.unwind_tables = .sync;
2045 },2044 },
2046 .no_unwind_tables => mod_opts.unwind_tables = .none,2045 .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,
2048 .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {2047 .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2049 .none, .sync => {},2048 .none, .sync => {},
2050 .@"async" => {2049 .async => {
2051 mod_opts.unwind_tables = .sync;2050 mod_opts.unwind_tables = .sync;
2052 },2051 },
2053 } else {2052 } else {
...@@ -2955,7 +2954,7 @@ fn buildOutputType(...@@ -2955,7 +2954,7 @@ fn buildOutputType(
2955 create_module.opts.any_fuzz = true;2954 create_module.opts.any_fuzz = true;
2956 if (mod_opts.unwind_tables) |uwt| switch (uwt) {2955 if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2957 .none => {},2956 .none => {},
2958 .sync, .@"async" => create_module.opts.any_unwind_tables = true,2957 .sync, .async => create_module.opts.any_unwind_tables = true,
2959 };2958 };
2960 if (mod_opts.strip == false)2959 if (mod_opts.strip == false)
2961 create_module.opts.any_non_stripped = true;2960 create_module.opts.any_non_stripped = true;
...@@ -3331,16 +3330,16 @@ fn buildOutputType(...@@ -3331,16 +3330,16 @@ fn buildOutputType(
3331 // We are providing our own cache key, because this file has nothing3330 // We are providing our own cache key, because this file has nothing
3332 // to do with the cache manifest.3331 // to do with the cache manifest.
3333 var file_writer = f.writer(&.{});3332 var file_writer = f.writer(&.{});
3334 var hasher_writer = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"));
3335 var buffer: [1000]u8 = undefined;3333 var buffer: [1000]u8 = undefined;
3336 var bw = hasher_writer.writer(&buffer);3334 var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer);
3337 bw.writeFileAll(.stdin(), .{}) catch |err| switch (err) {3335 var stdin_reader = fs.File.stdin().readerStreaming(&.{});
3338 error.WriteFailed => fatal("failed to write {s}: {s}", .{ dump_path, file_writer.err.? }),3336 _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) {
3339 else => fatal("failed to pipe stdin to {s}: {s}", .{ dump_path, 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 }),
3340 };3339 };
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
3345 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{3344 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{
3346 &bin_digest, ext.canonicalName(target),3345 &bin_digest, ext.canonicalName(target),
...@@ -3412,7 +3411,6 @@ fn buildOutputType(...@@ -3412,7 +3411,6 @@ fn buildOutputType(
3412 .framework_dirs = create_module.framework_dirs.items,3411 .framework_dirs = create_module.framework_dirs.items,
3413 .frameworks = resolved_frameworks.items,3412 .frameworks = resolved_frameworks.items,
3414 .windows_lib_names = create_module.windows_libs.keys(),3413 .windows_lib_names = create_module.windows_libs.keys(),
3415 .wasi_emulated_libs = create_module.wasi_emulated_libs.items,
3416 .want_compiler_rt = want_compiler_rt,3414 .want_compiler_rt = want_compiler_rt,
3417 .want_ubsan_rt = want_ubsan_rt,3415 .want_ubsan_rt = want_ubsan_rt,
3418 .hash_style = hash_style,3416 .hash_style = hash_style,
...@@ -3562,8 +3560,8 @@ fn buildOutputType(...@@ -3562,8 +3560,8 @@ fn buildOutputType(
3562 .stdio => {3560 .stdio => {
3563 try serve(3561 try serve(
3564 comp,3562 comp,
3565 fs.File.stdin(),3563 .stdin(),
3566 fs.File.stdout(),3564 .stdout(),
3567 test_exec_args.items,3565 test_exec_args.items,
3568 self_exe_path,3566 self_exe_path,
3569 arg_mode,3567 arg_mode,
...@@ -3638,7 +3636,6 @@ fn buildOutputType(...@@ -3638,7 +3636,6 @@ fn buildOutputType(
3638 } else if (target.os.tag == .windows) {3636 } else if (target.os.tag == .windows) {
3639 try test_exec_args.appendSlice(arena, &.{3637 try test_exec_args.appendSlice(arena, &.{
3640 "--subsystem", "console",3638 "--subsystem", "console",
3641 "-lkernel32", "-lntdll",
3642 });3639 });
3643 }3640 }
36443641
...@@ -3694,8 +3691,6 @@ const CreateModule = struct {...@@ -3694,8 +3691,6 @@ const CreateModule = struct {
3694 /// output. Allocated with gpa.3691 /// output. Allocated with gpa.
3695 link_inputs: std.ArrayListUnmanaged(link.Input),3692 link_inputs: std.ArrayListUnmanaged(link.Input),
36963693
3697 wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile),
3698
3699 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),3694 c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
3700 rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile),3695 rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile),
37013696
...@@ -3826,14 +3821,6 @@ fn createModule(...@@ -3826,14 +3821,6 @@ fn createModule(
3826 .name_query => |nq| {3821 .name_query => |nq| {
3827 const lib_name = nq.name;3822 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
3837 if (std.zig.target.isLibCLibName(target, lib_name)) {3824 if (std.zig.target.isLibCLibName(target, lib_name)) {
3838 create_module.opts.link_libc = true;3825 create_module.opts.link_libc = true;
3839 continue;3826 continue;
...@@ -3852,7 +3839,8 @@ fn createModule(...@@ -3852,7 +3839,8 @@ fn createModule(
3852 .only_compiler_rt => continue,3839 .only_compiler_rt => continue,
3853 }3840 }
38543841
3855 if (target.isMinGW()) {3842 // We currently prefer import libraries provided by MinGW-w64 even for MSVC.
3843 if (target.os.tag == .windows) {
3856 const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| {3844 const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| {
3857 fatal("failed to check zig installation for DLL import libs: {s}", .{3845 fatal("failed to check zig installation for DLL import libs: {s}", .{
3858 @errorName(err),3846 @errorName(err),
...@@ -5228,6 +5216,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5228,6 +5216,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
52285216
5229 try root_mod.deps.put(arena, "@build", build_mod);5217 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
5231 const comp = Compilation.create(gpa, arena, .{5225 const comp = Compilation.create(gpa, arena, .{
5232 .dirs = dirs,5226 .dirs = dirs,
5233 .root_name = "build",5227 .root_name = "build",
...@@ -5249,6 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5249,6 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5249 .cache_mode = .whole,5243 .cache_mode = .whole,
5250 .reference_trace = reference_trace,5244 .reference_trace = reference_trace,
5251 .debug_compile_errors = debug_compile_errors,5245 .debug_compile_errors = debug_compile_errors,
5246 .windows_lib_names = windows_libs.keys(),
5252 }) catch |err| {5247 }) catch |err| {
5253 fatal("unable to create compilation: {s}", .{@errorName(err)});5248 fatal("unable to create compilation: {s}", .{@errorName(err)});
5254 };5249 };
...@@ -5299,7 +5294,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5299,7 +5294,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5299 const s = fs.path.sep_str;5294 const s = fs.path.sep_str;
5300 const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;5295 const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
5301 const stdout = dirs.local_cache.handle.readFileAlloc(arena, tmp_sub_path, 50 * 1024 * 1024) catch |err| {5296 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}", .{
5303 dirs.local_cache, tmp_sub_path, @errorName(err),5298 dirs.local_cache, tmp_sub_path, @errorName(err),
5304 });5299 });
5305 };5300 };
...@@ -5352,6 +5347,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5352,6 +5347,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5352const JitCmdOptions = struct {5347const JitCmdOptions = struct {
5353 cmd_name: []const u8,5348 cmd_name: []const u8,
5354 root_src_path: []const u8,5349 root_src_path: []const u8,
5350 windows_libs: []const []const u8 = &.{},
5355 prepend_zig_lib_dir_path: bool = false,5351 prepend_zig_lib_dir_path: bool = false,
5356 prepend_global_cache_path: bool = false,5352 prepend_global_cache_path: bool = false,
5357 prepend_zig_exe_path: bool = false,5353 prepend_zig_exe_path: bool = false,
...@@ -5468,6 +5464,13 @@ fn jitCmd(...@@ -5468,6 +5464,13 @@ fn jitCmd(
5468 try root_mod.deps.put(arena, "aro", aro_mod);5464 try root_mod.deps.put(arena, "aro", aro_mod);
5469 }5465 }
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
5471 const comp = Compilation.create(gpa, arena, .{5474 const comp = Compilation.create(gpa, arena, .{
5472 .dirs = dirs,5475 .dirs = dirs,
5473 .root_name = options.cmd_name,5476 .root_name = options.cmd_name,
...@@ -5478,6 +5481,7 @@ fn jitCmd(...@@ -5478,6 +5481,7 @@ fn jitCmd(
5478 .self_exe_path = self_exe_path,5481 .self_exe_path = self_exe_path,
5479 .thread_pool = &thread_pool,5482 .thread_pool = &thread_pool,
5480 .cache_mode = .whole,5483 .cache_mode = .whole,
5484 .windows_lib_names = windows_libs.keys(),
5481 }) catch |err| {5485 }) catch |err| {
5482 fatal("unable to create compilation: {s}", .{@errorName(err)});5486 fatal("unable to create compilation: {s}", .{@errorName(err)});
5483 };5487 };
...@@ -6049,7 +6053,7 @@ fn cmdAstCheck(...@@ -6049,7 +6053,7 @@ fn cmdAstCheck(
6049 break :file fs.cwd().openFile(p, .{}) catch |err| {6053 break :file fs.cwd().openFile(p, .{}) catch |err| {
6050 fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });6054 fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
6051 };6055 };
6052 } else io.getStdIn();6056 } else fs.File.stdin();
6053 defer if (zig_source_path != null) f.close();6057 defer if (zig_source_path != null) f.close();
6054 break :s std.zig.readSourceFileToEndAlloc(arena, f, null) catch |err| {6058 break :s std.zig.readSourceFileToEndAlloc(arena, f, null) catch |err| {
6055 fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });6059 fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
...@@ -6068,7 +6072,8 @@ fn cmdAstCheck(...@@ -6068,7 +6072,8 @@ fn cmdAstCheck(
60686072
6069 const tree = try Ast.parse(arena, source, mode);6073 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;
6072 switch (mode) {6077 switch (mode) {
6073 .zig => {6078 .zig => {
6074 const zir = try AstGen.generate(arena, tree);6079 const zir = try AstGen.generate(arena, tree);
...@@ -6133,7 +6138,7 @@ fn cmdAstCheck(...@@ -6133,7 +6138,7 @@ fn cmdAstCheck(
6133 // zig fmt: on6138 // zig fmt: on
6134 }6139 }
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);
6137 try stdout_bw.flush();6142 try stdout_bw.flush();
61386143
6139 if (zir.hasCompileErrors()) {6144 if (zir.hasCompileErrors()) {
...@@ -6161,7 +6166,7 @@ fn cmdAstCheck(...@@ -6161,7 +6166,7 @@ fn cmdAstCheck(
6161 fatal("-t option only available in builds of zig with debug extensions", .{});6166 fatal("-t option only available in builds of zig with debug extensions", .{});
6162 }6167 }
61636168
6164 try @import("print_zoir.zig").renderToWriter(zoir, arena, &stdout_bw);6169 try @import("print_zoir.zig").renderToWriter(zoir, arena, stdout_bw);
6165 try stdout_bw.flush();6170 try stdout_bw.flush();
6166 return cleanExit();6171 return cleanExit();
6167 },6172 },
...@@ -6282,7 +6287,8 @@ fn detectNativeCpuWithLLVM(...@@ -6282,7 +6287,8 @@ fn detectNativeCpuWithLLVM(
6282}6287}
62836288
6284fn printCpu(cpu: std.Target.Cpu) !void {6289fn 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
6287 if (cpu.model.llvm_name) |llvm_name| {6293 if (cpu.model.llvm_name) |llvm_name| {
6288 try stdout_bw.print("{s}\n", .{llvm_name});6294 try stdout_bw.print("{s}\n", .{llvm_name});
...@@ -6326,11 +6332,12 @@ fn cmdDumpLlvmInts(...@@ -6326,11 +6332,12 @@ fn cmdDumpLlvmInts(
6326 if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad");6332 if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad");
6327 break :t target;6333 break :t target;
6328 };6334 };
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);
6330 const dl = tm.createTargetDataLayout();6336 const dl = tm.createTargetDataLayout();
6331 const context = llvm.Context.create();6337 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;
6334 for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| {6341 for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| {
6335 const int_type = context.intType(bits);6342 const int_type = context.intType(bits);
6336 const alignment = dl.abiAlignmentOfType(int_type);6343 const alignment = dl.abiAlignmentOfType(int_type);
...@@ -6358,9 +6365,8 @@ fn cmdDumpZir(...@@ -6358,9 +6365,8 @@ fn cmdDumpZir(
6358 defer f.close();6365 defer f.close();
63596366
6360 const zir = try Zcu.loadZirCache(arena, f);6367 const zir = try Zcu.loadZirCache(arena, f);
63616368 var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer);
6362 var stdout_fw = fs.File.stdout().writer();6369 const stdout_bw = &stdout_writer.interface;
6363 var stdout_bw = stdout_fw.interface().buffered(&stdio_buffer);
6364 {6370 {
6365 const instruction_bytes = zir.instructions.len *6371 const instruction_bytes = zir.instructions.len *
6366 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include6372 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
...@@ -6385,7 +6391,7 @@ fn cmdDumpZir(...@@ -6385,7 +6391,7 @@ fn cmdDumpZir(
6385 // zig fmt: on6391 // zig fmt: on
6386 }6392 }
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);
6389 try stdout_bw.flush();6395 try stdout_bw.flush();
6390}6396}
63916397
...@@ -6444,7 +6450,8 @@ fn cmdChangelist(...@@ -6444,7 +6450,8 @@ fn cmdChangelist(
6444 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;6450 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;
6445 try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map);6451 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;
6448 {6455 {
6449 try stdout_bw.print("Instruction mappings:\n", .{});6456 try stdout_bw.print("Instruction mappings:\n", .{});
6450 var it = inst_map.iterator();6457 var it = inst_map.iterator();
...@@ -6903,9 +6910,9 @@ fn cmdFetch(...@@ -6903,9 +6910,9 @@ fn cmdFetch(
69036910
6904 const name = switch (save) {6911 const name = switch (save) {
6905 .no => {6912 .no => {
6906 var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer);6913 var stdout = fs.File.stdout().writerStreaming(&stdio_buffer);
6907 try stdout_bw.print("{s}\n", .{package_hash_slice});6914 try stdout.interface.print("{s}\n", .{package_hash_slice});
6908 try stdout_bw.flush();6915 try stdout.interface.flush();
6909 return cleanExit();6916 return cleanExit();
6910 },6917 },
6911 .yes, .exact => |name| name: {6918 .yes, .exact => |name| name: {
...@@ -6954,7 +6961,9 @@ fn cmdFetch(...@@ -6954,7 +6961,9 @@ fn cmdFetch(
6954 std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex });6961 std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex });
69556962
6956 // include the original refspec in a query parameter, could be used to check for updates6963 // 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 }) };
6958 } else {6967 } else {
6959 std.log.info("resolved to commit {s}", .{latest_commit_hex});6968 std.log.info("resolved to commit {s}", .{latest_commit_hex});
6960 }6969 }
...@@ -6974,12 +6983,12 @@ fn cmdFetch(...@@ -6974,12 +6983,12 @@ fn cmdFetch(
6974 \\ .hash = "{f}",6983 \\ .hash = "{f}",
6975 \\ }}6984 \\ }}
6976 , .{6985 , .{
6977 std.zig.fmtEscapes(saved_path_or_url),6986 std.zig.fmtString(saved_path_or_url),
6978 std.zig.fmtEscapes(package_hash_slice),6987 std.zig.fmtString(package_hash_slice),
6979 });6988 });
69806989
6981 const new_node_text = try std.fmt.allocPrint(arena, ".{fp_} = {s},\n", .{6990 const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{
6982 std.zig.fmtId(name), new_node_init,6991 std.zig.fmtIdPU(name), new_node_init,
6983 });6992 });
69846993
6985 const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{6994 const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{
...@@ -7006,12 +7015,12 @@ fn cmdFetch(...@@ -7006,12 +7015,12 @@ fn cmdFetch(
7006 const location_replace = try std.fmt.allocPrint(7015 const location_replace = try std.fmt.allocPrint(
7007 arena,7016 arena,
7008 "\"{f}\"",7017 "\"{f}\"",
7009 .{std.zig.fmtEscapes(saved_path_or_url)},7018 .{std.zig.fmtString(saved_path_or_url)},
7010 );7019 );
7011 const hash_replace = try std.fmt.allocPrint(7020 const hash_replace = try std.fmt.allocPrint(
7012 arena,7021 arena,
7013 "\"{f}\"",7022 "\"{f}\"",
7014 .{std.zig.fmtEscapes(package_hash_slice)},7023 .{std.zig.fmtString(package_hash_slice)},
7015 );7024 );
70167025
7017 warn("overwriting existing dependency named '{s}'", .{name});7026 warn("overwriting existing dependency named '{s}'", .{name});
...@@ -7411,7 +7420,7 @@ fn handleModArg(...@@ -7411,7 +7420,7 @@ fn handleModArg(
7411 create_module.opts.any_fuzz = true;7420 create_module.opts.any_fuzz = true;
7412 if (mod_opts.unwind_tables) |uwt| switch (uwt) {7421 if (mod_opts.unwind_tables) |uwt| switch (uwt) {
7413 .none => {},7422 .none => {},
7414 .sync, .@"async" => create_module.opts.any_unwind_tables = true,7423 .sync, .async => create_module.opts.any_unwind_tables = true,
7415 };7424 };
7416 if (mod_opts.strip == false)7425 if (mod_opts.strip == false)
7417 create_module.opts.any_non_stripped = true;7426 create_module.opts.any_non_stripped = true;
src/print_value.zig+114-117
...@@ -9,7 +9,6 @@ const Sema = @import("Sema.zig");...@@ -9,7 +9,6 @@ const Sema = @import("Sema.zig");
9const InternPool = @import("InternPool.zig");9const InternPool = @import("InternPool.zig");
10const Allocator = std.mem.Allocator;10const Allocator = std.mem.Allocator;
11const Target = std.Target;11const Target = std.Target;
12const Writer = std.io.Writer;
1312
14const max_aggregate_items = 100;13const max_aggregate_items = 100;
15const max_string_len = 256;14const max_string_len = 256;
...@@ -21,10 +20,9 @@ pub const FormatContext = struct {...@@ -21,10 +20,9 @@ pub const FormatContext = struct {
21 depth: u8,20 depth: u8,
22};21};
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 {
25 const sema = ctx.opt_sema.?;24 const sema = ctx.opt_sema.?;
26 comptime std.debug.assert(fmt.len == 0);25 return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
27 return print(ctx.val, bw, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
28 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function26 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
29 error.ComptimeBreak, error.ComptimeReturn => unreachable,27 error.ComptimeBreak, error.ComptimeReturn => unreachable,
30 error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully28 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...@@ -32,10 +30,9 @@ pub fn formatSema(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std
32 };30 };
33}31}
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 {
36 std.debug.assert(ctx.opt_sema == null);34 std.debug.assert(ctx.opt_sema == null);
37 comptime std.debug.assert(fmt.len == 0);35 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
38 return print(ctx.val, bw, ctx.depth, ctx.pt, null) catch |err| switch (err) {
39 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function36 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
40 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,37 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,
41 else => |e| return e,38 else => |e| return e,
...@@ -44,7 +41,7 @@ pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io....@@ -44,7 +41,7 @@ pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.
4441
45pub fn print(42pub fn print(
46 val: Value,43 val: Value,
47 bw: *Writer,44 writer: *std.io.Writer,
48 level: u8,45 level: u8,
49 pt: Zcu.PerThread,46 pt: Zcu.PerThread,
50 opt_sema: ?*Sema,47 opt_sema: ?*Sema,
...@@ -68,62 +65,62 @@ pub fn print(...@@ -68,62 +65,62 @@ pub fn print(
68 .func_type,65 .func_type,
69 .error_set_type,66 .error_set_type,
70 .inferred_error_set_type,67 .inferred_error_set_type,
71 => try Type.print(val.toType(), bw, pt),68 => try Type.print(val.toType(), writer, pt),
72 .undef => try bw.writeAll("undefined"),69 .undef => try writer.writeAll("undefined"),
73 .simple_value => |simple_value| switch (simple_value) {70 .simple_value => |simple_value| switch (simple_value) {
74 .void => try bw.writeAll("{}"),71 .void => try writer.writeAll("{}"),
75 .empty_tuple => try bw.writeAll(".{}"),72 .empty_tuple => try writer.writeAll(".{}"),
76 else => try bw.writeAll(@tagName(simple_value)),73 else => try writer.writeAll(@tagName(simple_value)),
77 },74 },
78 .variable => try bw.writeAll("(variable)"),75 .variable => try writer.writeAll("(variable)"),
79 .@"extern" => |e| try bw.print("(extern '{f}')", .{e.name.fmt(ip)}),76 .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}),
80 .func => |func| try bw.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}),77 .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}),
81 .int => |int| switch (int.storage) {78 .int => |int| switch (int.storage) {
82 inline .u64, .i64 => |x| try bw.print("{d}", .{x}),79 inline .u64, .i64 => |x| try writer.print("{d}", .{x}),
83 .big_int => |x| try bw.print("{f}", .{x}),80 .big_int => |x| try writer.print("{d}", .{x}),
84 .lazy_align => |ty| if (opt_sema != null) {81 .lazy_align => |ty| if (opt_sema != null) {
85 const a = try Type.fromInterned(ty).abiAlignmentSema(pt);82 const a = try Type.fromInterned(ty).abiAlignmentSema(pt);
86 try bw.print("{}", .{a.toByteUnits() orelse 0});83 try writer.print("{d}", .{a.toByteUnits() orelse 0});
87 } else try bw.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}),84 } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
88 .lazy_size => |ty| if (opt_sema != null) {85 .lazy_size => |ty| if (opt_sema != null) {
89 const s = try Type.fromInterned(ty).abiSizeSema(pt);86 const s = try Type.fromInterned(ty).abiSizeSema(pt);
90 try bw.print("{}", .{s});87 try writer.print("{d}", .{s});
91 } else try bw.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}),88 } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
92 },89 },
93 .err => |err| try bw.print("error.{f}", .{90 .err => |err| try writer.print("error.{f}", .{
94 err.name.fmt(ip),91 err.name.fmt(ip),
95 }),92 }),
96 .error_union => |error_union| switch (error_union.val) {93 .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}", .{
98 err_name.fmt(ip),95 err_name.fmt(ip),
99 }),96 }),
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),
101 },98 },
102 .enum_literal => |enum_literal| try bw.print(".{f}", .{99 .enum_literal => |enum_literal| try writer.print(".{f}", .{
103 enum_literal.fmt(ip),100 enum_literal.fmt(ip),
104 }),101 }),
105 .enum_tag => |enum_tag| {102 .enum_tag => |enum_tag| {
106 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());103 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());
107 if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| {104 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)});
109 }106 }
110 if (level == 0) {107 if (level == 0) {
111 return bw.writeAll("@enumFromInt(...)");108 return writer.writeAll("@enumFromInt(...)");
112 }109 }
113 try bw.writeAll("@enumFromInt(");110 try writer.writeAll("@enumFromInt(");
114 try print(Value.fromInterned(enum_tag.int), bw, level - 1, pt, opt_sema);111 try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
115 try bw.writeAll(")");112 try writer.writeAll(")");
116 },113 },
117 .empty_enum_value => try bw.writeAll("(empty enum value)"),114 .empty_enum_value => try writer.writeAll("(empty enum value)"),
118 .float => |float| switch (float.storage) {115 .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))}),
120 },117 },
121 .slice => |slice| {118 .slice => |slice| {
122 if (ip.isUndef(slice.ptr)) {119 if (ip.isUndef(slice.ptr)) {
123 if (slice.len == .zero_usize) {120 if (slice.len == .zero_usize) {
124 return bw.writeAll("&.{}");121 return writer.writeAll("&.{}");
125 }122 }
126 try print(.fromInterned(slice.ptr), bw, level - 1, pt, opt_sema);123 try print(.fromInterned(slice.ptr), writer, level - 1, pt, opt_sema);
127 } else {124 } else {
128 const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) {125 const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) {
129 .field, .arr_elem, .eu_payload, .opt_payload => unreachable,126 .field, .arr_elem, .eu_payload, .opt_payload => unreachable,
...@@ -134,15 +131,15 @@ pub fn print(...@@ -134,15 +131,15 @@ pub fn print(
134 // TODO: eventually we want to load the slice as an array with `sema`, but that's131 // TODO: eventually we want to load the slice as an array with `sema`, but that's
135 // currently not possible without e.g. triggering compile errors.132 // currently not possible without e.g. triggering compile errors.
136 }133 }
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);
138 }135 }
139 try bw.writeAll("[0..");136 try writer.writeAll("[0..");
140 if (level == 0) {137 if (level == 0) {
141 try bw.writeAll("(...)");138 try writer.writeAll("(...)");
142 } else {139 } 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);
144 }141 }
145 try bw.writeAll("]");142 try writer.writeAll("]");
146 },143 },
147 .ptr => {144 .ptr => {
148 const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) {145 const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) {
...@@ -154,29 +151,29 @@ pub fn print(...@@ -154,29 +151,29 @@ pub fn print(
154 // TODO: eventually we want to load the pointer with `sema`, but that's151 // TODO: eventually we want to load the pointer with `sema`, but that's
155 // currently not possible without e.g. triggering compile errors.152 // currently not possible without e.g. triggering compile errors.
156 }153 }
157 try printPtr(val, .rvalue, bw, level, pt, opt_sema);154 try printPtr(val, .rvalue, writer, level, pt, opt_sema);
158 },155 },
159 .opt => |opt| switch (opt.val) {156 .opt => |opt| switch (opt.val) {
160 .none => try bw.writeAll("null"),157 .none => try writer.writeAll("null"),
161 else => |payload| try print(Value.fromInterned(payload), bw, level, pt, opt_sema),158 else => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema),
162 },159 },
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),
164 .un => |un| {161 .un => |un| {
165 if (level == 0) {162 if (level == 0) {
166 try bw.writeAll(".{ ... }");163 try writer.writeAll(".{ ... }");
167 return;164 return;
168 }165 }
169 if (un.tag == .none) {166 if (un.tag == .none) {
170 const backing_ty = try val.typeOf(zcu).unionBackingType(pt);167 const backing_ty = try val.typeOf(zcu).unionBackingType(pt);
171 try bw.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});168 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
172 try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema);169 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
173 try bw.writeAll("))");170 try writer.writeAll("))");
174 } else {171 } else {
175 try bw.writeAll(".{ ");172 try writer.writeAll(".{ ");
176 try print(Value.fromInterned(un.tag), bw, level - 1, pt, opt_sema);173 try print(Value.fromInterned(un.tag), writer, level - 1, pt, opt_sema);
177 try bw.writeAll(" = ");174 try writer.writeAll(" = ");
178 try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema);175 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
179 try bw.writeAll(" }");176 try writer.writeAll(" }");
180 }177 }
181 },178 },
182 .memoized_call => unreachable,179 .memoized_call => unreachable,
...@@ -187,33 +184,33 @@ fn printAggregate(...@@ -187,33 +184,33 @@ fn printAggregate(
187 val: Value,184 val: Value,
188 aggregate: InternPool.Key.Aggregate,185 aggregate: InternPool.Key.Aggregate,
189 is_ref: bool,186 is_ref: bool,
190 bw: *Writer,187 writer: *std.io.Writer,
191 level: u8,188 level: u8,
192 pt: Zcu.PerThread,189 pt: Zcu.PerThread,
193 opt_sema: ?*Sema,190 opt_sema: ?*Sema,
194) (std.io.Writer.Error || Zcu.CompileError)!void {191) (std.io.Writer.Error || Zcu.CompileError)!void {
195 if (level == 0) {192 if (level == 0) {
196 if (is_ref) try bw.writeByte('&');193 if (is_ref) try writer.writeByte('&');
197 return bw.writeAll(".{ ... }");194 return writer.writeAll(".{ ... }");
198 }195 }
199 const zcu = pt.zcu;196 const zcu = pt.zcu;
200 const ip = &zcu.intern_pool;197 const ip = &zcu.intern_pool;
201 const ty = Type.fromInterned(aggregate.ty);198 const ty = Type.fromInterned(aggregate.ty);
202 switch (ty.zigTypeTag(zcu)) {199 switch (ty.zigTypeTag(zcu)) {
203 .@"struct" => if (!ty.isTuple(zcu)) {200 .@"struct" => if (!ty.isTuple(zcu)) {
204 if (is_ref) try bw.writeByte('&');201 if (is_ref) try writer.writeByte('&');
205 if (ty.structFieldCount(zcu) == 0) {202 if (ty.structFieldCount(zcu) == 0) {
206 return bw.writeAll(".{}");203 return writer.writeAll(".{}");
207 }204 }
208 try bw.writeAll(".{ ");205 try writer.writeAll(".{ ");
209 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items);206 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items);
210 for (0..max_len) |i| {207 for (0..max_len) |i| {
211 if (i != 0) try bw.writeAll(", ");208 if (i != 0) try writer.writeAll(", ");
212 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?;209 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?;
213 try bw.print(".{fi} = ", .{field_name.fmt(ip)});210 try writer.print(".{f} = ", .{field_name.fmt(ip)});
214 try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema);211 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
215 }212 }
216 try bw.writeAll(" }");213 try writer.writeAll(" }");
217 return;214 return;
218 },215 },
219 .array => {216 .array => {
...@@ -222,16 +219,16 @@ fn printAggregate(...@@ -222,16 +219,16 @@ fn printAggregate(
222 const len = ty.arrayLenIncludingSentinel(zcu);219 const len = ty.arrayLenIncludingSentinel(zcu);
223 if (len == 0) break :string;220 if (len == 0) break :string;
224 const slice = bytes.toSlice(if (bytes.at(len - 1, ip) == 0) len - 1 else len, ip);221 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)});222 try writer.print("\"{f}\"", .{std.zig.fmtString(slice)});
226 if (!is_ref) try bw.writeAll(".*");223 if (!is_ref) try writer.writeAll(".*");
227 return;224 return;
228 },225 },
229 .elems, .repeated_elem => {},226 .elems, .repeated_elem => {},
230 }227 }
231 switch (ty.arrayLen(zcu)) {228 switch (ty.arrayLen(zcu)) {
232 0 => {229 0 => {
233 if (is_ref) try bw.writeByte('&');230 if (is_ref) try writer.writeByte('&');
234 return bw.writeAll(".{}");231 return writer.writeAll(".{}");
235 },232 },
236 1 => one_byte_str: {233 1 => one_byte_str: {
237 // The repr isn't `bytes`, but we might still be able to print this as a string234 // The repr isn't `bytes`, but we might still be able to print this as a string
...@@ -239,47 +236,47 @@ fn printAggregate(...@@ -239,47 +236,47 @@ fn printAggregate(
239 const elem_val = Value.fromInterned(aggregate.storage.values()[0]);236 const elem_val = Value.fromInterned(aggregate.storage.values()[0]);
240 if (elem_val.isUndef(zcu)) break :one_byte_str;237 if (elem_val.isUndef(zcu)) break :one_byte_str;
241 const byte = elem_val.toUnsignedInt(zcu);238 const byte = elem_val.toUnsignedInt(zcu);
242 try bw.print("\"{f}\"", .{std.zig.fmtEscapes(&.{@intCast(byte)})});239 try writer.print("\"{f}\"", .{std.zig.fmtString(&.{@intCast(byte)})});
243 if (!is_ref) try bw.writeAll(".*");240 if (!is_ref) try writer.writeAll(".*");
244 return;241 return;
245 },242 },
246 else => {},243 else => {},
247 }244 }
248 },245 },
249 .vector => if (ty.arrayLen(zcu) == 0) {246 .vector => if (ty.arrayLen(zcu) == 0) {
250 if (is_ref) try bw.writeByte('&');247 if (is_ref) try writer.writeByte('&');
251 return bw.writeAll(".{}");248 return writer.writeAll(".{}");
252 },249 },
253 else => unreachable,250 else => unreachable,
254 }251 }
255252
256 const len = ty.arrayLen(zcu);253 const len = ty.arrayLen(zcu);
257254
258 if (is_ref) try bw.writeByte('&');255 if (is_ref) try writer.writeByte('&');
259 try bw.writeAll(".{ ");256 try writer.writeAll(".{ ");
260257
261 const max_len = @min(len, max_aggregate_items);258 const max_len = @min(len, max_aggregate_items);
262 for (0..max_len) |i| {259 for (0..max_len) |i| {
263 if (i != 0) try bw.writeAll(", ");260 if (i != 0) try writer.writeAll(", ");
264 try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema);261 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
265 }262 }
266 if (len > max_aggregate_items) {263 if (len > max_aggregate_items) {
267 try bw.writeAll(", ...");264 try writer.writeAll(", ...");
268 }265 }
269 return bw.writeAll(" }");266 return writer.writeAll(" }");
270}267}
271268
272fn printPtr(269fn printPtr(
273 ptr_val: Value,270 ptr_val: Value,
274 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.271 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.
275 want_kind: ?PrintPtrKind,272 want_kind: ?PrintPtrKind,
276 bw: *Writer,273 writer: *std.io.Writer,
277 level: u8,274 level: u8,
278 pt: Zcu.PerThread,275 pt: Zcu.PerThread,
279 opt_sema: ?*Sema,276 opt_sema: ?*Sema,
280) (std.io.Writer.Error || Zcu.CompileError)!void {277) (std.io.Writer.Error || Zcu.CompileError)!void {
281 const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) {278 const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
282 .undef => return bw.writeAll("undefined"),279 .undef => return writer.writeAll("undefined"),
283 .ptr => |ptr| ptr,280 .ptr => |ptr| ptr,
284 else => unreachable,281 else => unreachable,
285 };282 };
...@@ -291,7 +288,7 @@ fn printPtr(...@@ -291,7 +288,7 @@ fn printPtr(
291 Value.fromInterned(ptr.base_addr.uav.val),288 Value.fromInterned(ptr.base_addr.uav.val),
292 agg,289 agg,
293 true,290 true,
294 bw,291 writer,
295 level,292 level,
296 pt,293 pt,
297 opt_sema,294 opt_sema,
...@@ -307,7 +304,7 @@ fn printPtr(...@@ -307,7 +304,7 @@ fn printPtr(
307 else304 else
308 try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null);305 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 = .{
311 .level = level,308 .level = level,
312 .opt_sema = opt_sema,309 .opt_sema = opt_sema,
313 } }, 20);310 } }, 20);
...@@ -319,7 +316,7 @@ const PrintPtrKind = enum { lvalue, rvalue };...@@ -319,7 +316,7 @@ const PrintPtrKind = enum { lvalue, rvalue };
319/// Returns the root derivation, which may be ignored.316/// Returns the root derivation, which may be ignored.
320pub fn printPtrDerivation(317pub fn printPtrDerivation(
321 derivation: Value.PointerDeriveStep,318 derivation: Value.PointerDeriveStep,
322 bw: *Writer,319 writer: *std.io.Writer,
323 pt: Zcu.PerThread,320 pt: Zcu.PerThread,
324 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.321 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.
325 /// If this is `.rvalue`, the result may look like `&foo`, so it's not necessarily valid to treat it as322 /// 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(...@@ -337,7 +334,7 @@ pub fn printPtrDerivation(
337 /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary,334 /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary,
338 /// so at this depth we just write "..." to prevent stack overflow.335 /// so at this depth we just write "..." to prevent stack overflow.
339 ptr_depth: u8,336 ptr_depth: u8,
340) (std.io.Writer.Error || Zcu.CompileError)!Value.PointerDeriveStep {337) !Value.PointerDeriveStep {
341 const zcu = pt.zcu;338 const zcu = pt.zcu;
342 const ip = &zcu.intern_pool;339 const ip = &zcu.intern_pool;
343340
...@@ -351,7 +348,7 @@ pub fn printPtrDerivation(...@@ -351,7 +348,7 @@ pub fn printPtrDerivation(
351 => |step| continue :root step.parent.*,348 => |step| continue :root step.parent.*,
352 else => |step| break :root step,349 else => |step| break :root step,
353 };350 };
354 try bw.writeAll("...");351 try writer.writeAll("...");
355 return root_step;352 return root_step;
356 }353 }
357354
...@@ -374,39 +371,39 @@ pub fn printPtrDerivation(...@@ -374,39 +371,39 @@ pub fn printPtrDerivation(
374 const need_kind = want_kind orelse result_kind;371 const need_kind = want_kind orelse result_kind;
375372
376 if (need_kind == .rvalue and result_kind == .lvalue) {373 if (need_kind == .rvalue and result_kind == .lvalue) {
377 try bw.writeByte('&');374 try writer.writeByte('&');
378 }375 }
379376
380 // null if `derivation` is the root.377 // null if `derivation` is the root.
381 const root_or_null: ?Value.PointerDeriveStep = switch (derivation) {378 const root_or_null: ?Value.PointerDeriveStep = switch (derivation) {
382 .eu_payload_ptr => |info| root: {379 .eu_payload_ptr => |info| root: {
383 try bw.writeByte('(');380 try writer.writeByte('(');
384 const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1);381 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1);
385 try bw.writeAll(" catch unreachable)");382 try writer.writeAll(" catch unreachable)");
386 break :root root;383 break :root root;
387 },384 },
388 .opt_payload_ptr => |info| root: {385 .opt_payload_ptr => |info| root: {
389 const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1);386 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1);
390 try bw.writeAll(".?");387 try writer.writeAll(".?");
391 break :root root;388 break :root root;
392 },389 },
393 .field_ptr => |field| root: {390 .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);
395 const agg_ty = (try field.parent.ptrType(pt)).childType(zcu);392 const agg_ty = (try field.parent.ptrType(pt)).childType(zcu);
396 switch (agg_ty.zigTypeTag(zcu)) {393 switch (agg_ty.zigTypeTag(zcu)) {
397 .@"struct" => if (agg_ty.structFieldName(field.field_idx, zcu).unwrap()) |field_name| {394 .@"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)});
399 } else {396 } else {
400 try bw.print("[{d}]", .{field.field_idx});397 try writer.print("[{d}]", .{field.field_idx});
401 },398 },
402 .@"union" => {399 .@"union" => {
403 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu);400 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu);
404 const field_name = tag_ty.enumFieldName(field.field_idx, zcu);401 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)});
406 },403 },
407 .pointer => switch (field.field_idx) {404 .pointer => switch (field.field_idx) {
408 Value.slice_ptr_index => try bw.writeAll(".ptr"),405 Value.slice_ptr_index => try writer.writeAll(".ptr"),
409 Value.slice_len_index => try bw.writeAll(".len"),406 Value.slice_len_index => try writer.writeAll(".len"),
410 else => unreachable,407 else => unreachable,
411 },408 },
412 else => unreachable,409 else => unreachable,
...@@ -414,20 +411,20 @@ pub fn printPtrDerivation(...@@ -414,20 +411,20 @@ pub fn printPtrDerivation(
414 break :root root;411 break :root root;
415 },412 },
416 .elem_ptr => |elem| root: {413 .elem_ptr => |elem| root: {
417 const root = try printPtrDerivation(elem.parent.*, bw, pt, null, root_strat, ptr_depth - 1);414 const root = try printPtrDerivation(elem.parent.*, writer, pt, null, root_strat, ptr_depth - 1);
418 try bw.print("[{d}]", .{elem.elem_idx});415 try writer.print("[{d}]", .{elem.elem_idx});
419 break :root root;416 break :root root;
420 },417 },
421418
422 .offset_and_cast => |oac| if (oac.byte_offset == 0) root: {419 .offset_and_cast => |oac| if (oac.byte_offset == 0) root: {
423 try bw.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)});420 try writer.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);421 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1);
425 try bw.writeAll("))");422 try writer.writeAll("))");
426 break :root root;423 break :root root;
427 } else root: {424 } else root: {
428 try bw.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)});425 try writer.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);426 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1);
430 try bw.print(") + {d}))", .{oac.byte_offset});427 try writer.print(") + {d}))", .{oac.byte_offset});
431 break :root root;428 break :root root;
432 },429 },
433430
...@@ -435,33 +432,33 @@ pub fn printPtrDerivation(...@@ -435,33 +432,33 @@ pub fn printPtrDerivation(
435 };432 };
436433
437 if (root_or_null == null) switch (root_strat) {434 if (root_or_null == null) switch (root_strat) {
438 .str => |x| try bw.writeAll(x),435 .str => |x| try writer.writeAll(x),
439 .print_val => |x| switch (derivation) {436 .print_val => |x| switch (derivation) {
440 .int => |int| try bw.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }),437 .int => |int| try writer.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)}),438 .nav_ptr => |nav| try writer.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}),
442 .uav_ptr => |uav| {439 .uav_ptr => |uav| {
443 const ty = Value.fromInterned(uav.val).typeOf(zcu);440 const ty = Value.fromInterned(uav.val).typeOf(zcu);
444 try bw.print("@as({f}, ", .{ty.fmt(pt)});441 try writer.print("@as({f}, ", .{ty.fmt(pt)});
445 try print(Value.fromInterned(uav.val), bw, x.level - 1, pt, x.opt_sema);442 try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema);
446 try bw.writeByte(')');443 try writer.writeByte(')');
447 },444 },
448 .comptime_alloc_ptr => |info| {445 .comptime_alloc_ptr => |info| {
449 try bw.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)});446 try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)});
450 try print(info.val, bw, x.level - 1, pt, x.opt_sema);447 try print(info.val, writer, x.level - 1, pt, x.opt_sema);
451 try bw.writeByte(')');448 try writer.writeByte(')');
452 },449 },
453 .comptime_field_ptr => |val| {450 .comptime_field_ptr => |val| {
454 const ty = val.typeOf(zcu);451 const ty = val.typeOf(zcu);
455 try bw.print("@as({f}, ", .{ty.fmt(pt)});452 try writer.print("@as({f}, ", .{ty.fmt(pt)});
456 try print(val, bw, x.level - 1, pt, x.opt_sema);453 try print(val, writer, x.level - 1, pt, x.opt_sema);
457 try bw.writeByte(')');454 try writer.writeByte(')');
458 },455 },
459 else => unreachable,456 else => unreachable,
460 },457 },
461 };458 };
462459
463 if (need_kind == .lvalue and result_kind == .rvalue) {460 if (need_kind == .lvalue and result_kind == .rvalue) {
464 try bw.writeAll(".*");461 try writer.writeAll(".*");
465 }462 }
466463
467 return root_or_null orelse derivation;464 return root_or_null orelse derivation;
src/print_zir.zig+9-27
...@@ -253,14 +253,12 @@ const Writer = struct {...@@ -253,14 +253,12 @@ const Writer = struct {
253 .tag_name,253 .tag_name,
254 .type_name,254 .type_name,
255 .frame_type,255 .frame_type,
256 .frame_size,
257 .clz,256 .clz,
258 .ctz,257 .ctz,
259 .pop_count,258 .pop_count,
260 .byte_swap,259 .byte_swap,
261 .bit_reverse,260 .bit_reverse,
262 .@"resume",261 .@"resume",
263 .@"await",
264 .make_ptr_const,262 .make_ptr_const,
265 .validate_deref,263 .validate_deref,
266 .validate_const,264 .validate_const,
...@@ -557,7 +555,6 @@ const Writer = struct {...@@ -557,7 +555,6 @@ const Writer = struct {
557555
558 .tuple_decl => try self.writeTupleDecl(stream, extended),556 .tuple_decl => try self.writeTupleDecl(stream, extended),
559557
560 .await_nosuspend,
561 .c_undef,558 .c_undef,
562 .c_include,559 .c_include,
563 .set_float_mode,560 .set_float_mode,
...@@ -603,7 +600,6 @@ const Writer = struct {...@@ -603,7 +600,6 @@ const Writer = struct {
603 try self.writeSrcNode(stream, inst_data.node);600 try self.writeSrcNode(stream, inst_data.node);
604 },601 },
605602
606 .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended),
607 .cmpxchg => try self.writeCmpxchg(stream, extended),603 .cmpxchg => try self.writeCmpxchg(stream, extended),
608 .ptr_cast_full => try self.writePtrCastFull(stream, extended),604 .ptr_cast_full => try self.writePtrCastFull(stream, extended),
609 .ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended),605 .ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended),
...@@ -924,19 +920,6 @@ const Writer = struct {...@@ -924,19 +920,6 @@ const Writer = struct {
924 try self.writeSrcNode(stream, extra.src_node);920 try self.writeSrcNode(stream, extra.src_node);
925 }921 }
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
940 fn writeParam(self: *Writer, stream: *std.io.Writer, inst: Zir.Inst.Index) !void {923 fn writeParam(self: *Writer, stream: *std.io.Writer, inst: Zir.Inst.Index) !void {
941 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;924 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
942 const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index);925 const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index);
...@@ -1229,8 +1212,8 @@ const Writer = struct {...@@ -1229,8 +1212,8 @@ const Writer = struct {
12291212
1230 const name = self.code.nullTerminatedString(output.data.name);1213 const name = self.code.nullTerminatedString(output.data.name);
1231 const constraint = self.code.nullTerminatedString(output.data.constraint);1214 const constraint = self.code.nullTerminatedString(output.data.constraint);
1232 try stream.print("output({fp}, \"{f}\", ", .{1215 try stream.print("output({f}, \"{f}\", ", .{
1233 std.zig.fmtId(name), std.zig.fmtString(constraint),1216 std.zig.fmtIdP(name), std.zig.fmtString(constraint),
1234 });1217 });
1235 try self.writeFlag(stream, "->", is_type);1218 try self.writeFlag(stream, "->", is_type);
1236 try self.writeInstRef(stream, output.data.operand);1219 try self.writeInstRef(stream, output.data.operand);
...@@ -1248,8 +1231,8 @@ const Writer = struct {...@@ -1248,8 +1231,8 @@ const Writer = struct {
12481231
1249 const name = self.code.nullTerminatedString(input.data.name);1232 const name = self.code.nullTerminatedString(input.data.name);
1250 const constraint = self.code.nullTerminatedString(input.data.constraint);1233 const constraint = self.code.nullTerminatedString(input.data.constraint);
1251 try stream.print("input({fp}, \"{f}\", ", .{1234 try stream.print("input({f}, \"{f}\", ", .{
1252 std.zig.fmtId(name), std.zig.fmtString(constraint),1235 std.zig.fmtIdP(name), std.zig.fmtString(constraint),
1253 });1236 });
1254 try self.writeInstRef(stream, input.data.operand);1237 try self.writeInstRef(stream, input.data.operand);
1255 try stream.writeAll(")");1238 try stream.writeAll(")");
...@@ -1264,7 +1247,7 @@ const Writer = struct {...@@ -1264,7 +1247,7 @@ const Writer = struct {
1264 const str_index = self.code.extra[extra_i];1247 const str_index = self.code.extra[extra_i];
1265 extra_i += 1;1248 extra_i += 1;
1266 const clobber = self.code.nullTerminatedString(@enumFromInt(str_index));1249 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)});
1268 if (i + 1 < clobbers_len) {1251 if (i + 1 < clobbers_len) {
1269 try stream.writeAll(", ");1252 try stream.writeAll(", ");
1270 }1253 }
...@@ -1528,7 +1511,7 @@ const Writer = struct {...@@ -1528,7 +1511,7 @@ const Writer = struct {
1528 try self.writeFlag(stream, "comptime ", field.is_comptime);1511 try self.writeFlag(stream, "comptime ", field.is_comptime);
1529 if (field.name != .empty) {1512 if (field.name != .empty) {
1530 const field_name = self.code.nullTerminatedString(field.name);1513 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)});
1532 } else {1515 } else {
1533 try stream.print("@\"{d}\": ", .{i});1516 try stream.print("@\"{d}\": ", .{i});
1534 }1517 }
...@@ -1691,7 +1674,7 @@ const Writer = struct {...@@ -1691,7 +1674,7 @@ const Writer = struct {
1691 extra_index += 1;1674 extra_index += 1;
16921675
1693 try stream.splatByteAll(' ', self.indent);1676 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
1696 if (has_type) {1679 if (has_type) {
1697 const field_type = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));1680 const field_type = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
...@@ -1825,7 +1808,7 @@ const Writer = struct {...@@ -1825,7 +1808,7 @@ const Writer = struct {
1825 extra_index += 1;1808 extra_index += 1;
18261809
1827 try stream.splatByteAll(' ', self.indent);1810 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
1830 if (has_tag_value) {1813 if (has_tag_value) {
1831 const tag_value_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));1814 const tag_value_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
...@@ -1930,7 +1913,7 @@ const Writer = struct {...@@ -1930,7 +1913,7 @@ const Writer = struct {
1930 const name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);1913 const name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
1931 const name = self.code.nullTerminatedString(name_index);1914 const name = self.code.nullTerminatedString(name_index);
1932 try stream.splatByteAll(' ', self.indent);1915 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)});
1934 }1917 }
19351918
1936 self.indent -= 2;1919 self.indent -= 2;
...@@ -2597,7 +2580,6 @@ const Writer = struct {...@@ -2597,7 +2580,6 @@ const Writer = struct {
2597 }2580 }
2598 switch (decl.kind) {2581 switch (decl.kind) {
2599 .@"comptime" => try stream.writeAll("comptime"),2582 .@"comptime" => try stream.writeAll("comptime"),
2600 .@"usingnamespace" => try stream.writeAll("usingnamespace"),
2601 .unnamed_test => try stream.writeAll("test"),2583 .unnamed_test => try stream.writeAll("test"),
2602 .@"test", .decltest, .@"const", .@"var" => {2584 .@"test", .decltest, .@"const", .@"var" => {
2603 try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) });2585 try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) });
src/print_zoir.zig+3-3
...@@ -1,4 +1,6 @@...@@ -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 {
2 assert(!zoir.hasCompileErrors());4 assert(!zoir.hasCompileErrors());
35
4 const bytes_per_node = comptime n: {6 const bytes_per_node = comptime n: {
...@@ -46,8 +48,6 @@ const PrintZon = struct {...@@ -46,8 +48,6 @@ const PrintZon = struct {
46 zoir: Zoir,48 zoir: Zoir,
47 indent: u32,49 indent: u32,
4850
49 const Error = Writer.Error;
50
51 fn renderRoot(pz: *PrintZon) Error!void {51 fn renderRoot(pz: *PrintZon) Error!void {
52 try pz.renderNode(.root);52 try pz.renderNode(.root);
53 try pz.w.writeByte('\n');53 try pz.w.writeByte('\n');
src/target.zig+29-10
...@@ -85,6 +85,19 @@ pub fn defaultSingleThreaded(target: *const std.Target) bool {...@@ -85,6 +85,19 @@ pub fn defaultSingleThreaded(target: *const std.Target) bool {
85 return false;85 return false;
86}86}
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
88pub fn hasValgrindSupport(target: *const std.Target, backend: std.builtin.CompilerBackend) bool {101pub fn hasValgrindSupport(target: *const std.Target, backend: std.builtin.CompilerBackend) bool {
89 // We can't currently output the necessary Valgrind client request assembly when using the C102 // We can't currently output the necessary Valgrind client request assembly when using the C
90 // backend and compiling with an MSVC-like compiler.103 // backend and compiling with an MSVC-like compiler.
...@@ -222,10 +235,16 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {...@@ -222,10 +235,16 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
222/// than or equal to the number of behavior tests as the respective LLVM backend.235/// than or equal to the number of behavior tests as the respective LLVM backend.
223pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool {236pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool {
224 if (target.cpu.arch.isSpirV()) return true;237 if (target.cpu.arch.isSpirV()) return true;
225 if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) return switch (target.ofmt) {238 if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) {
226 .elf, .macho => true,239 if (target.os.tag == .netbsd) {
227 else => false,240 // Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341
228 };241 return false;
242 }
243 return switch (target.ofmt) {
244 .elf, .macho => true,
245 else => false,
246 };
247 }
229 return false;248 return false;
230}249}
231250
...@@ -464,12 +483,12 @@ pub fn clangSupportsNoImplicitFloatArg(target: *const std.Target) bool {...@@ -464,12 +483,12 @@ pub fn clangSupportsNoImplicitFloatArg(target: *const std.Target) bool {
464pub fn defaultUnwindTables(target: *const std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables {483pub fn defaultUnwindTables(target: *const std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables {
465 if (target.os.tag == .windows) {484 if (target.os.tag == .windows) {
466 // The old 32-bit x86 variant of SEH doesn't use tables.485 // 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;
468 }487 }
469 if (target.os.tag.isDarwin()) return .@"async";488 if (target.os.tag.isDarwin()) return .async;
470 if (libunwind) return .@"async";489 if (libunwind) return .async;
471 if (libtsan) return .@"async";490 if (libtsan) return .async;
472 if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .@"async";491 if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .async;
473 return .none;492 return .none;
474}493}
475494
...@@ -796,7 +815,7 @@ pub fn compilerRtIntAbbrev(bits: u16) []const u8 {...@@ -796,7 +815,7 @@ pub fn compilerRtIntAbbrev(bits: u16) []const u8 {
796815
797pub fn fnCallConvAllowsZigTypes(cc: std.builtin.CallingConvention) bool {816pub fn fnCallConvAllowsZigTypes(cc: std.builtin.CallingConvention) bool {
798 return switch (cc) {817 return switch (cc) {
799 .auto, .@"async", .@"inline" => true,818 .auto, .async, .@"inline" => true,
800 // For now we want to authorize PTX kernel to use zig objects, even if819 // For now we want to authorize PTX kernel to use zig objects, even if
801 // we end up exposing the ABI. The goal is to experiment with more820 // we end up exposing the ABI. The goal is to experiment with more
802 // integrated CPU/GPU code.821 // integrated CPU/GPU code.
src/tracy.zig+34-16
...@@ -120,20 +120,21 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {...@@ -120,20 +120,21 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
120 .vtable = &.{120 .vtable = &.{
121 .alloc = allocFn,121 .alloc = allocFn,
122 .resize = resizeFn,122 .resize = resizeFn,
123 .remap = remapFn,
123 .free = freeFn,124 .free = freeFn,
124 },125 },
125 };126 };
126 }127 }
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 {
129 const self: *Self = @ptrCast(@alignCast(ptr));130 const self: *Self = @ptrCast(@alignCast(ptr));
130 const result = self.parent_allocator.rawAlloc(len, ptr_align, ret_addr);131 const result = self.parent_allocator.rawAlloc(len, alignment, ret_addr);
131 if (result) |data| {132 if (result) |memory| {
132 if (len != 0) {133 if (len != 0) {
133 if (name) |n| {134 if (name) |n| {
134 allocNamed(data, len, n);135 allocNamed(memory, len, n);
135 } else {136 } else {
136 alloc(data, len);137 alloc(memory, len);
137 }138 }
138 }139 }
139 } else {140 } else {
...@@ -142,15 +143,15 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {...@@ -142,15 +143,15 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
142 return result;143 return result;
143 }144 }
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 {
146 const self: *Self = @ptrCast(@alignCast(ptr));147 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)) {
148 if (name) |n| {149 if (name) |n| {
149 freeNamed(buf.ptr, n);150 freeNamed(memory.ptr, n);
150 allocNamed(buf.ptr, new_len, n);151 allocNamed(memory.ptr, new_len, n);
151 } else {152 } else {
152 free(buf.ptr);153 free(memory.ptr);
153 alloc(buf.ptr, new_len);154 alloc(memory.ptr, new_len);
154 }155 }
155156
156 return true;157 return true;
...@@ -161,16 +162,33 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {...@@ -161,16 +162,33 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type {
161 return false;162 return false;
162 }163 }
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 {
165 const self: *Self = @ptrCast(@alignCast(ptr));166 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);
167 // this condition is to handle free being called on an empty slice that was never even allocated185 // this condition is to handle free being called on an empty slice that was never even allocated
168 // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}`186 // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}`
169 if (buf.len != 0) {187 if (memory.len != 0) {
170 if (name) |n| {188 if (name) |n| {
171 freeNamed(buf.ptr, n);189 freeNamed(memory.ptr, n);
172 } else {190 } else {
173 free(buf.ptr);191 free(memory.ptr);
174 }192 }
175 }193 }
176 }194 }
src/translate_c.zig+9-9
...@@ -357,7 +357,7 @@ fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.Fi...@@ -357,7 +357,7 @@ fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.Fi
357 var len: usize = undefined;357 var len: usize = undefined;
358 const bytes_ptr = asm_string.getString_bytes_begin_size(&len);358 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])});
361 const str_node = try Tag.string_literal.create(c.arena, str);361 const str_node = try Tag.string_literal.create(c.arena, str);
362362
363 const asm_node = try Tag.asm_simple.create(c.arena, str_node);363 const asm_node = try Tag.asm_simple.create(c.arena, str_node);
...@@ -2276,7 +2276,7 @@ fn transNarrowStringLiteral(...@@ -2276,7 +2276,7 @@ fn transNarrowStringLiteral(
2276 var len: usize = undefined;2276 var len: usize = undefined;
2277 const bytes_ptr = stmt.getString_bytes_begin_size(&len);2277 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])});
2280 const node = try Tag.string_literal.create(c.arena, str);2280 const node = try Tag.string_literal.create(c.arena, str);
2281 return maybeSuppressResult(c, result_used, node);2281 return maybeSuppressResult(c, result_used, node);
2282}2282}
...@@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined...@@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined
33383338
3339fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {3339fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
3340 return Tag.char_literal.create(c.arena, if (narrow)3340 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))})})
3342 else3342 else
3343 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));3343 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
3344}3344}
...@@ -5832,7 +5832,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {...@@ -5832,7 +5832,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
5832 num += c - 'A' + 10;5832 num += c - 'A' + 10;
5833 },5833 },
5834 else => {5834 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 });
5836 num = 0;5836 num = 0;
5837 if (c == '\\')5837 if (c == '\\')
5838 state = .escape5838 state = .escape
...@@ -5858,7 +5858,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {...@@ -5858,7 +5858,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
5858 };5858 };
5859 num += c - '0';5859 num += c - '0';
5860 } else {5860 } 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 });
5862 num = 0;5862 num = 0;
5863 count = 0;5863 count = 0;
5864 if (c == '\\')5864 if (c == '\\')
...@@ -5872,19 +5872,19 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {...@@ -5872,19 +5872,19 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
5872 }5872 }
5873 }5873 }
5874 if (state == .hex or state == .octal)5874 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 });
5876 return bytes[0..i];5876 return bytes[0..i];
5877}5877}
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.
5880/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape5880/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
5881/// non-ASCII characters so that the Zig source we output will itself be UTF-8.5881/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
5882fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 {5882fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 {
5883 const zigified = try zigifyEscapeSequences(ctx, m);5883 const zigified = try zigifyEscapeSequences(ctx, m);
5884 if (std.unicode.utf8ValidateSlice(zigified)) return zigified;5884 if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
58855885
5886 const formatter = std.fmt.fmtSliceEscapeLower(zigified);5886 const formatter = std.ascii.hexEscape(zigified, .lower);
5887 const encoded_size = std.fmt.count("{f}", .{formatter});5887 const encoded_size: usize = @intCast(std.fmt.count("{f}", .{formatter}));
5888 const output = try ctx.arena.alloc(u8, encoded_size);5888 const output = try ctx.arena.alloc(u8, encoded_size);
5889 return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {5889 return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {
5890 error.NoSpaceLeft => unreachable,5890 error.NoSpaceLeft => unreachable,
src/zig_llvm.cpp+5-1
...@@ -83,7 +83,7 @@ static const bool assertions_on = false;...@@ -83,7 +83,7 @@ static const bool assertions_on = false;
83LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,83LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,
84 const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,84 const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,
85 LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi,85 LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi,
86 const char *abi_name)86 const char *abi_name, bool emulated_tls)
87{87{
88 std::optional<Reloc::Model> RM;88 std::optional<Reloc::Model> RM;
89 switch (Reloc){89 switch (Reloc){
...@@ -149,6 +149,10 @@ LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Tri...@@ -149,6 +149,10 @@ LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Tri
149 opt.MCOptions.ABIName = abi_name;149 opt.MCOptions.ABIName = abi_name;
150 }150 }
151151
152 if (emulated_tls) {
153 opt.EmulatedTLS = true;
154 }
155
152 TargetMachine *TM = reinterpret_cast<Target*>(T)->createTargetMachine(Triple, CPU, Features, opt, RM, CM,156 TargetMachine *TM = reinterpret_cast<Target*>(T)->createTargetMachine(Triple, CPU, Features, opt, RM, CM,
153 OL, JIT);157 OL, JIT);
154 return reinterpret_cast<LLVMTargetMachineRef>(TM);158 return reinterpret_cast<LLVMTargetMachineRef>(TM);
src/zig_llvm.h+1-1
...@@ -105,7 +105,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi...@@ -105,7 +105,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi
105ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,105ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,
106 const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,106 const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,
107 LLVMCodeModel CodeModel, bool function_sections, bool data_sections, ZigLLVMFloatABI float_abi,107 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
110ZIG_EXTERN_C void ZigLLVMSetOptBisectLimit(LLVMContextRef context_ref, int limit);110ZIG_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...@@ -520,12 +520,15 @@ uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iov
520 default: panic("unimplemented: fd_read special file");520 default: panic("unimplemented: fd_read special file");
521 }521 }
522522
523 if (fds[fd].stream == NULL) {
524 store32_align2(res_size_ptr, 0);
525 return wasi_errno_success;
526 }
527
523 size_t size = 0;528 size_t size = 0;
524 for (uint32_t i = 0; i < iovs_len; i += 1) {529 for (uint32_t i = 0; i < iovs_len; i += 1) {
525 uint32_t len = load32_align2(&iovs_ptr[i].len);530 uint32_t len = load32_align2(&iovs_ptr[i].len);
526 size_t read_size = 0;531 size_t read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
527 if (fds[fd].stream != NULL)
528 read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
529 size += read_size;532 size += read_size;
530 if (read_size < len) break;533 if (read_size < len) break;
531 }534 }
...@@ -633,8 +636,10 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i...@@ -633,8 +636,10 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i
633 }636 }
634637
635 fpos_t pos;638 fpos_t pos;
636 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;639 if (fds[fd].stream != NULL) {
637 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;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
639 size_t size = 0;644 size_t size = 0;
640 for (uint32_t i = 0; i < iovs_len; i += 1) {645 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...@@ -648,7 +653,9 @@ uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t i
648 if (written_size < len) break;653 if (written_size < len) break;
649 }654 }
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
653 if (size > 0) {660 if (size > 0) {
654 time_t now = time(NULL);661 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...@@ -964,6 +971,11 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io
964 default: panic("unimplemented: fd_pread special file");971 default: panic("unimplemented: fd_pread special file");
965 }972 }
966973
974 if (fds[fd].stream == NULL) {
975 store32_align2(res_size_ptr, 0);
976 return wasi_errno_success;
977 }
978
967 fpos_t pos;979 fpos_t pos;
968 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;980 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
969 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;981 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 {...@@ -5,9 +5,7 @@ test {
5 _ = @import("behavior/align.zig");5 _ = @import("behavior/align.zig");
6 _ = @import("behavior/alignof.zig");6 _ = @import("behavior/alignof.zig");
7 _ = @import("behavior/array.zig");7 _ = @import("behavior/array.zig");
8 _ = @import("behavior/async_fn.zig");
9 _ = @import("behavior/atomics.zig");8 _ = @import("behavior/atomics.zig");
10 _ = @import("behavior/await_struct.zig");
11 _ = @import("behavior/basic.zig");9 _ = @import("behavior/basic.zig");
12 _ = @import("behavior/bit_shifting.zig");10 _ = @import("behavior/bit_shifting.zig");
13 _ = @import("behavior/bitcast.zig");11 _ = @import("behavior/bitcast.zig");
...@@ -103,7 +101,6 @@ test {...@@ -103,7 +101,6 @@ test {
103 _ = @import("behavior/underscore.zig");101 _ = @import("behavior/underscore.zig");
104 _ = @import("behavior/union.zig");102 _ = @import("behavior/union.zig");
105 _ = @import("behavior/union_with_members.zig");103 _ = @import("behavior/union_with_members.zig");
106 _ = @import("behavior/usingnamespace.zig");
107 _ = @import("behavior/var_args.zig");104 _ = @import("behavior/var_args.zig");
108 // https://github.com/llvm/llvm-project/issues/118879105 // https://github.com/llvm/llvm-project/issues/118879
109 // https://github.com/llvm/llvm-project/issues/134659106 // https://github.com/llvm/llvm-project/issues/134659
test/behavior/align.zig-24
...@@ -425,30 +425,6 @@ test "struct field explicit alignment" {...@@ -425,30 +425,6 @@ test "struct field explicit alignment" {
425 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);425 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);
426}426}
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
452test "align(N) on functions" {428test "align(N) on functions" {
453 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO429 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
454 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO430 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" {...@@ -1107,27 +1107,6 @@ test "inline call of function with a switch inside the return statement" {
1107 try expect(S.foo(1) == 1);1107 try expect(S.foo(1) == 1);
1108}1108}
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
1131test "pointer to zero sized global is mutable" {1110test "pointer to zero sized global is mutable" {
1132 const S = struct {1111 const S = struct {
1133 const Thing = struct {};1112 const Thing = struct {};
test/behavior/call.zig+3-26
...@@ -37,7 +37,7 @@ test "basic invocations" {...@@ -37,7 +37,7 @@ test "basic invocations" {
37 comptime {37 comptime {
38 // comptime calls with supported modifiers38 // comptime calls with supported modifiers
39 try expect(@call(.auto, foo, .{2}) == 1234);39 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);
41 try expect(@call(.always_tail, foo, .{4}) == 1234);41 try expect(@call(.always_tail, foo, .{4}) == 1234);
42 try expect(@call(.always_inline, foo, .{5}) == 1234);42 try expect(@call(.always_inline, foo, .{5}) == 1234);
43 }43 }
...@@ -45,7 +45,7 @@ test "basic invocations" {...@@ -45,7 +45,7 @@ test "basic invocations" {
45 const result = @call(.compile_time, foo, .{6}) == 1234;45 const result = @call(.compile_time, foo, .{6}) == 1234;
46 comptime assert(result);46 comptime assert(result);
47 // runtime calls of comptime-known function47 // runtime calls of comptime-known function
48 try expect(@call(.no_async, foo, .{7}) == 1234);48 try expect(@call(.no_suspend, foo, .{7}) == 1234);
49 try expect(@call(.never_tail, foo, .{8}) == 1234);49 try expect(@call(.never_tail, foo, .{8}) == 1234);
50 try expect(@call(.never_inline, foo, .{9}) == 1234);50 try expect(@call(.never_inline, foo, .{9}) == 1234);
51 // CBE does not support attributes on runtime functions51 // CBE does not support attributes on runtime functions
...@@ -53,7 +53,7 @@ test "basic invocations" {...@@ -53,7 +53,7 @@ test "basic invocations" {
53 // runtime calls of non comptime-known function53 // runtime calls of non comptime-known function
54 var alias_foo = &foo;54 var alias_foo = &foo;
55 _ = &alias_foo;55 _ = &alias_foo;
56 try expect(@call(.no_async, alias_foo, .{10}) == 1234);56 try expect(@call(.no_suspend, alias_foo, .{10}) == 1234);
57 try expect(@call(.never_tail, alias_foo, .{11}) == 1234);57 try expect(@call(.never_tail, alias_foo, .{11}) == 1234);
58 try expect(@call(.never_inline, alias_foo, .{12}) == 1234);58 try expect(@call(.never_inline, alias_foo, .{12}) == 1234);
59 }59 }
...@@ -507,29 +507,6 @@ test "call inline fn through pointer" {...@@ -507,29 +507,6 @@ test "call inline fn through pointer" {
507 try f(123);507 try f(123);
508}508}
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
533test "call function in comptime field" {510test "call function in comptime field" {
534 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO511 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" {...@@ -1032,44 +1032,6 @@ test "function called at runtime is properly analyzed for inferred error set" {
1032 };1032 };
1033}1033}
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
1073test "errorCast to adhoc inferred error set" {1035test "errorCast to adhoc inferred error set" {
1074 const S = struct {1036 const S = struct {
1075 inline fn baz() !i32 {1037 inline fn baz() !i32 {
test/behavior/import.zig-10
...@@ -18,16 +18,6 @@ test "importing the same thing gives the same import" {...@@ -18,16 +18,6 @@ test "importing the same thing gives the same import" {
18 try expect(@import("std") == @import("std"));18 try expect(@import("std") == @import("std"));
19}19}
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
31test "import empty file" {21test "import empty file" {
32 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;22 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
33 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;23 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" {...@@ -236,17 +236,6 @@ test "call method with mutable reference to struct with no fields" {
236 try expect(s.do());236 try expect(s.do());
237}237}
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
250test "struct field init with catch" {239test "struct field init with catch" {
251 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;240 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
252 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO241 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/type_info.zig-83
...@@ -592,24 +592,6 @@ test "StructField.is_comptime" {...@@ -592,24 +592,6 @@ test "StructField.is_comptime" {
592 try expect(info.fields[1].is_comptime);592 try expect(info.fields[1].is_comptime);
593}593}
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
613test "value from struct @typeInfo default_value_ptr can be loaded at comptime" {595test "value from struct @typeInfo default_value_ptr can be loaded at comptime" {
614 comptime {596 comptime {
615 const a = @typeInfo(@TypeOf(.{ .foo = @as(u8, 1) })).@"struct".fields[0].default_value_ptr;597 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" {...@@ -617,77 +599,12 @@ test "value from struct @typeInfo default_value_ptr can be loaded at comptime" {
617 }599 }
618}600}
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
657test "type info of tuple of string literal default value" {602test "type info of tuple of string literal default value" {
658 const struct_field = @typeInfo(@TypeOf(.{"hi"})).@"struct".fields[0];603 const struct_field = @typeInfo(@TypeOf(.{"hi"})).@"struct".fields[0];
659 const value = struct_field.defaultValue().?;604 const value = struct_field.defaultValue().?;
660 comptime std.debug.assert(value[0] == 'h');605 comptime std.debug.assert(value[0] == 'h');
661}606}
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
691test "@typeInfo function with generic return type and inferred error set" {608test "@typeInfo function with generic return type and inferred error set" {
692 const S = struct {609 const S = struct {
693 fn testFn(comptime T: type) !T {}610 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");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;4 _ = 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")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
test/compare_output.zig+3-286
...@@ -17,15 +17,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -17,15 +17,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
17 \\}17 \\}
18 , "Hello, world!" ++ if (@import("builtin").os.tag == .windows) "\r\n" else "\n");18 , "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
29 cases.addC("number literals",20 cases.addC("number literals",
30 \\const std = @import("std");21 \\const std = @import("std");
31 \\const builtin = @import("builtin");22 \\const builtin = @import("builtin");
...@@ -158,24 +149,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -158,24 +149,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
158 \\149 \\
159 );150 );
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
179 cases.addC("expose function pointer to C land",152 cases.addC("expose function pointer to C land",
180 \\const c = @cImport(@cInclude("stdlib.h"));153 \\const c = @cImport(@cInclude("stdlib.h"));
181 \\154 \\
...@@ -236,267 +209,11 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -236,267 +209,11 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
236 \\}209 \\}
237 , "3.25\n3\n3.00\n-0.40\n");210 , "3.25\n3\n3.00\n-0.40\n");
238211
239 cases.add("same named methods in incomplete struct",212 cases.add("valid carriage return example", "const std = @import(\"std\");\r\n" ++ // Testing CRLF line endings are valid
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
497 "\r\n" ++213 "\r\n" ++
498 "pub \r fn main() void {\r\n" ++ // Testing isolated carriage return as whitespace is valid214 "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" ++
500 " stdout.print(\\\\A Multiline\r\n" ++ // testing CRLF at end of multiline string line is valid and normalises to \n in the output217 " stdout.print(\\\\A Multiline\r\n" ++ // testing CRLF at end of multiline string line is valid and normalises to \n in the output
501 " \\\\String\r\n" ++218 " \\\\String\r\n" ++
502 " , .{}) catch unreachable;\r\n" ++219 " , .{}) catch unreachable;\r\n" ++
test/incremental/add_decl+7-7
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6#file=main.zig6#file=main.zig
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub fn main() !void {
9 try std.io.getStdOut().writeAll(foo);9 try std.fs.File.stdout().writeAll(foo);
10}10}
11const foo = "good morning\n";11const foo = "good morning\n";
12#expect_stdout="good morning\n"12#expect_stdout="good morning\n"
...@@ -15,7 +15,7 @@ const foo = "good morning\n";...@@ -15,7 +15,7 @@ const foo = "good morning\n";
15#file=main.zig15#file=main.zig
16const std = @import("std");16const std = @import("std");
17pub fn main() !void {17pub fn main() !void {
18 try std.io.getStdOut().writeAll(foo);18 try std.fs.File.stdout().writeAll(foo);
19}19}
20const foo = "good morning\n";20const foo = "good morning\n";
21const bar = "good evening\n";21const bar = "good evening\n";
...@@ -25,7 +25,7 @@ const bar = "good evening\n";...@@ -25,7 +25,7 @@ const bar = "good evening\n";
25#file=main.zig25#file=main.zig
26const std = @import("std");26const std = @import("std");
27pub fn main() !void {27pub fn main() !void {
28 try std.io.getStdOut().writeAll(bar);28 try std.fs.File.stdout().writeAll(bar);
29}29}
30const foo = "good morning\n";30const foo = "good morning\n";
31const bar = "good evening\n";31const bar = "good evening\n";
...@@ -35,17 +35,17 @@ const bar = "good evening\n";...@@ -35,17 +35,17 @@ const bar = "good evening\n";
35#file=main.zig35#file=main.zig
36const std = @import("std");36const std = @import("std");
37pub fn main() !void {37pub fn main() !void {
38 try std.io.getStdOut().writeAll(qux);38 try std.fs.File.stdout().writeAll(qux);
39}39}
40const foo = "good morning\n";40const foo = "good morning\n";
41const bar = "good evening\n";41const 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
44#update=add missing declaration44#update=add missing declaration
45#file=main.zig45#file=main.zig
46const std = @import("std");46const std = @import("std");
47pub fn main() !void {47pub fn main() !void {
48 try std.io.getStdOut().writeAll(qux);48 try std.fs.File.stdout().writeAll(qux);
49}49}
50const foo = "good morning\n";50const foo = "good morning\n";
51const bar = "good evening\n";51const bar = "good evening\n";
...@@ -56,7 +56,7 @@ const qux = "good night\n";...@@ -56,7 +56,7 @@ const qux = "good night\n";
56#file=main.zig56#file=main.zig
57const std = @import("std");57const std = @import("std");
58pub fn main() !void {58pub fn main() !void {
59 try std.io.getStdOut().writeAll(qux);59 try std.fs.File.stdout().writeAll(qux);
60}60}
61const qux = "good night\n";61const qux = "good night\n";
62#expect_stdout="good night\n"62#expect_stdout="good night\n"
test/incremental/add_decl_namespaced+7-7
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6#file=main.zig6#file=main.zig
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub fn main() !void {
9 try std.io.getStdOut().writeAll(@This().foo);9 try std.fs.File.stdout().writeAll(@This().foo);
10}10}
11const foo = "good morning\n";11const foo = "good morning\n";
12#expect_stdout="good morning\n"12#expect_stdout="good morning\n"
...@@ -15,7 +15,7 @@ const foo = "good morning\n";...@@ -15,7 +15,7 @@ const foo = "good morning\n";
15#file=main.zig15#file=main.zig
16const std = @import("std");16const std = @import("std");
17pub fn main() !void {17pub fn main() !void {
18 try std.io.getStdOut().writeAll(@This().foo);18 try std.fs.File.stdout().writeAll(@This().foo);
19}19}
20const foo = "good morning\n";20const foo = "good morning\n";
21const bar = "good evening\n";21const bar = "good evening\n";
...@@ -25,7 +25,7 @@ const bar = "good evening\n";...@@ -25,7 +25,7 @@ const bar = "good evening\n";
25#file=main.zig25#file=main.zig
26const std = @import("std");26const std = @import("std");
27pub fn main() !void {27pub fn main() !void {
28 try std.io.getStdOut().writeAll(@This().bar);28 try std.fs.File.stdout().writeAll(@This().bar);
29}29}
30const foo = "good morning\n";30const foo = "good morning\n";
31const bar = "good evening\n";31const bar = "good evening\n";
...@@ -35,18 +35,18 @@ const bar = "good evening\n";...@@ -35,18 +35,18 @@ const bar = "good evening\n";
35#file=main.zig35#file=main.zig
36const std = @import("std");36const std = @import("std");
37pub fn main() !void {37pub fn main() !void {
38 try std.io.getStdOut().writeAll(@This().qux);38 try std.fs.File.stdout().writeAll(@This().qux);
39}39}
40const foo = "good morning\n";40const foo = "good morning\n";
41const bar = "good evening\n";41const 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'
43#expect_error=main.zig:1:1: note: struct declared here43#expect_error=main.zig:1:1: note: struct declared here
4444
45#update=add missing declaration45#update=add missing declaration
46#file=main.zig46#file=main.zig
47const std = @import("std");47const std = @import("std");
48pub fn main() !void {48pub fn main() !void {
49 try std.io.getStdOut().writeAll(@This().qux);49 try std.fs.File.stdout().writeAll(@This().qux);
50}50}
51const foo = "good morning\n";51const foo = "good morning\n";
52const bar = "good evening\n";52const bar = "good evening\n";
...@@ -57,7 +57,7 @@ const qux = "good night\n";...@@ -57,7 +57,7 @@ const qux = "good night\n";
57#file=main.zig57#file=main.zig
58const std = @import("std");58const std = @import("std");
59pub fn main() !void {59pub fn main() !void {
60 try std.io.getStdOut().writeAll(@This().qux);60 try std.fs.File.stdout().writeAll(@This().qux);
61}61}
62const qux = "good night\n";62const qux = "good night\n";
63#expect_stdout="good night\n"63#expect_stdout="good night\n"
test/incremental/bad_import+2-2
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7#file=main.zig7#file=main.zig
8pub fn main() !void {8pub fn main() !void {
9 _ = @import("foo.zig");9 _ = @import("foo.zig");
10 try std.io.getStdOut().writeAll("success\n");10 try std.fs.File.stdout().writeAll("success\n");
11}11}
12const std = @import("std");12const std = @import("std");
13#file=foo.zig13#file=foo.zig
...@@ -29,7 +29,7 @@ comptime {...@@ -29,7 +29,7 @@ comptime {
29#file=main.zig29#file=main.zig
30pub fn main() !void {30pub fn main() !void {
31 //_ = @import("foo.zig");31 //_ = @import("foo.zig");
32 try std.io.getStdOut().writeAll("success\n");32 try std.fs.File.stdout().writeAll("success\n");
33}33}
34const std = @import("std");34const std = @import("std");
35#expect_stdout="success\n"35#expect_stdout="success\n"
test/incremental/change_embed_file+3-3
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7const std = @import("std");7const std = @import("std");
8const string = @embedFile("string.txt");8const string = @embedFile("string.txt");
9pub fn main() !void {9pub fn main() !void {
10 try std.io.getStdOut().writeAll(string);10 try std.fs.File.stdout().writeAll(string);
11}11}
12#file=string.txt12#file=string.txt
13Hello, World!13Hello, World!
...@@ -27,7 +27,7 @@ Hello again, World!...@@ -27,7 +27,7 @@ Hello again, World!
27const std = @import("std");27const std = @import("std");
28const string = @embedFile("string.txt");28const string = @embedFile("string.txt");
29pub fn main() !void {29pub fn main() !void {
30 try std.io.getStdOut().writeAll("a hardcoded string\n");30 try std.fs.File.stdout().writeAll("a hardcoded string\n");
31}31}
32#expect_stdout="a hardcoded string\n"32#expect_stdout="a hardcoded string\n"
3333
...@@ -36,7 +36,7 @@ pub fn main() !void {...@@ -36,7 +36,7 @@ pub fn main() !void {
36const std = @import("std");36const std = @import("std");
37const string = @embedFile("string.txt");37const string = @embedFile("string.txt");
38pub fn main() !void {38pub fn main() !void {
39 try std.io.getStdOut().writeAll(string);39 try std.fs.File.stdout().writeAll(string);
40}40}
41#expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound41#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) {...@@ -14,7 +14,8 @@ const Foo = enum(Tag) {
14pub fn main() !void {14pub fn main() !void {
15 var val: Foo = undefined;15 var val: Foo = undefined;
16 val = .a;16 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)});
18}19}
19const std = @import("std");20const std = @import("std");
20#expect_stdout="a\n"21#expect_stdout="a\n"
...@@ -31,7 +32,8 @@ const Foo = enum(Tag) {...@@ -31,7 +32,8 @@ const Foo = enum(Tag) {
31pub fn main() !void {32pub fn main() !void {
32 var val: Foo = undefined;33 var val: Foo = undefined;
33 val = .a;34 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)});
35}37}
36comptime {38comptime {
37 // These can't be true at the same time; analysis should stop as soon as it sees `Foo`39 // 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) {...@@ -53,7 +55,8 @@ const Foo = enum(Tag) {
53pub fn main() !void {55pub fn main() !void {
54 var val: Foo = undefined;56 var val: Foo = undefined;
55 val = .a;57 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)});
57}60}
58const std = @import("std");61const std = @import("std");
59#expect_stdout="a\n"62#expect_stdout="a\n"
test/incremental/change_exports+12-6
...@@ -16,7 +16,8 @@ pub fn main() !void {...@@ -16,7 +16,8 @@ pub fn main() !void {
16 extern const bar: u32;16 extern const bar: u32;
17 };17 };
18 S.foo();18 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});
20}21}
21const std = @import("std");22const std = @import("std");
22#expect_stdout="123\n"23#expect_stdout="123\n"
...@@ -37,7 +38,8 @@ pub fn main() !void {...@@ -37,7 +38,8 @@ pub fn main() !void {
37 extern const other: u32;38 extern const other: u32;
38 };39 };
39 S.foo();40 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 });
41}43}
42const std = @import("std");44const std = @import("std");
43#expect_error=main.zig:6:5: error: exported symbol collision: foo45#expect_error=main.zig:6:5: error: exported symbol collision: foo
...@@ -59,7 +61,8 @@ pub fn main() !void {...@@ -59,7 +61,8 @@ pub fn main() !void {
59 extern const other: u32;61 extern const other: u32;
60 };62 };
61 S.foo();63 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 });
63}66}
64const std = @import("std");67const std = @import("std");
65#expect_stdout="123 456\n"68#expect_stdout="123 456\n"
...@@ -83,7 +86,8 @@ pub fn main() !void {...@@ -83,7 +86,8 @@ pub fn main() !void {
83 extern const other: u32;86 extern const other: u32;
84 };87 };
85 S.foo();88 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 });
87}91}
88const std = @import("std");92const std = @import("std");
89#expect_stdout="123 456\n"93#expect_stdout="123 456\n"
...@@ -128,7 +132,8 @@ pub fn main() !void {...@@ -128,7 +132,8 @@ pub fn main() !void {
128 extern const other: u32;132 extern const other: u32;
129 };133 };
130 S.foo();134 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 });
132}137}
133const std = @import("std");138const std = @import("std");
134#expect_stdout="123 456\n"139#expect_stdout="123 456\n"
...@@ -152,7 +157,8 @@ pub fn main() !void {...@@ -152,7 +157,8 @@ pub fn main() !void {
152 extern const other: u32;157 extern const other: u32;
153 };158 };
154 S.foo();159 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 });
156}162}
157const std = @import("std");163const std = @import("std");
158#expect_error=main.zig:5:5: error: exported symbol collision: bar164#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 {...@@ -7,7 +7,8 @@ pub fn main() !void {
7 try foo(123);7 try foo(123);
8}8}
9fn foo(x: u8) !void {9fn 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});
11}12}
12const std = @import("std");13const std = @import("std");
13#expect_stdout="123\n"14#expect_stdout="123\n"
...@@ -18,7 +19,8 @@ pub fn main() !void {...@@ -18,7 +19,8 @@ pub fn main() !void {
18 try foo(123);19 try foo(123);
19}20}
20fn foo(x: i64) !void {21fn 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});
22}24}
23const std = @import("std");25const std = @import("std");
24#expect_stdout="123\n"26#expect_stdout="123\n"
...@@ -29,7 +31,8 @@ pub fn main() !void {...@@ -29,7 +31,8 @@ pub fn main() !void {
29 try foo(-42);31 try foo(-42);
30}32}
31fn foo(x: i64) !void {33fn 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});
33}36}
34const std = @import("std");37const std = @import("std");
35#expect_stdout="-42\n"38#expect_stdout="-42\n"
test/incremental/change_generic_line_number+2-2
...@@ -6,7 +6,7 @@ const std = @import("std");...@@ -6,7 +6,7 @@ const std = @import("std");
6fn Printer(message: []const u8) type {6fn Printer(message: []const u8) type {
7 return struct {7 return struct {
8 fn print() !void {8 fn print() !void {
9 try std.io.getStdOut().writeAll(message);9 try std.fs.File.stdout().writeAll(message);
10 }10 }
11 };11 };
12}12}
...@@ -22,7 +22,7 @@ const std = @import("std");...@@ -22,7 +22,7 @@ const std = @import("std");
22fn Printer(message: []const u8) type {22fn Printer(message: []const u8) type {
23 return struct {23 return struct {
24 fn print() !void {24 fn print() !void {
25 try std.io.getStdOut().writeAll(message);25 try std.fs.File.stdout().writeAll(message);
26 }26 }
27 };27 };
28}28}
test/incremental/change_line_number+2-2
...@@ -4,7 +4,7 @@...@@ -4,7 +4,7 @@
4#file=main.zig4#file=main.zig
5const std = @import("std");5const std = @import("std");
6pub fn main() !void {6pub fn main() !void {
7 try std.io.getStdOut().writeAll("foo\n");7 try std.fs.File.stdout().writeAll("foo\n");
8}8}
9#expect_stdout="foo\n"9#expect_stdout="foo\n"
10#update=change line number10#update=change line number
...@@ -12,6 +12,6 @@ pub fn main() !void {...@@ -12,6 +12,6 @@ pub fn main() !void {
12const std = @import("std");12const std = @import("std");
1313
14pub fn main() !void {14pub fn main() !void {
15 try std.io.getStdOut().writeAll("foo\n");15 try std.fs.File.stdout().writeAll("foo\n");
16}16}
17#expect_stdout="foo\n"17#expect_stdout="foo\n"
test/incremental/change_panic_handler+6-3
...@@ -11,7 +11,8 @@ pub fn main() !u8 {...@@ -11,7 +11,8 @@ pub fn main() !u8 {
11}11}
12pub const panic = std.debug.FullPanic(myPanic);12pub const panic = std.debug.FullPanic(myPanic);
13fn myPanic(msg: []const u8, _: ?usize) noreturn {13fn 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 {};
15 std.process.exit(0);16 std.process.exit(0);
16}17}
17const std = @import("std");18const std = @import("std");
...@@ -27,7 +28,8 @@ pub fn main() !u8 {...@@ -27,7 +28,8 @@ pub fn main() !u8 {
27}28}
28pub const panic = std.debug.FullPanic(myPanic);29pub const panic = std.debug.FullPanic(myPanic);
29fn myPanic(msg: []const u8, _: ?usize) noreturn {30fn 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 {};
31 std.process.exit(0);33 std.process.exit(0);
32}34}
33const std = @import("std");35const std = @import("std");
...@@ -43,7 +45,8 @@ pub fn main() !u8 {...@@ -43,7 +45,8 @@ pub fn main() !u8 {
43}45}
44pub const panic = std.debug.FullPanic(myPanicNew);46pub const panic = std.debug.FullPanic(myPanicNew);
45fn myPanicNew(msg: []const u8, _: ?usize) noreturn {47fn 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 {};
47 std.process.exit(0);50 std.process.exit(0);
48}51}
49const std = @import("std");52const std = @import("std");
test/incremental/change_panic_handler_explicit+6-3
...@@ -41,7 +41,8 @@ pub const panic = struct {...@@ -41,7 +41,8 @@ pub const panic = struct {
41 pub const noreturnReturned = no_panic.noreturnReturned;41 pub const noreturnReturned = no_panic.noreturnReturned;
42};42};
43fn myPanic(msg: []const u8, _: ?usize) noreturn {43fn 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 {};
45 std.process.exit(0);46 std.process.exit(0);
46}47}
47const std = @import("std");48const std = @import("std");
...@@ -87,7 +88,8 @@ pub const panic = struct {...@@ -87,7 +88,8 @@ pub const panic = struct {
87 pub const noreturnReturned = no_panic.noreturnReturned;88 pub const noreturnReturned = no_panic.noreturnReturned;
88};89};
89fn myPanic(msg: []const u8, _: ?usize) noreturn {90fn 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 {};
91 std.process.exit(0);93 std.process.exit(0);
92}94}
93const std = @import("std");95const std = @import("std");
...@@ -133,7 +135,8 @@ pub const panic = struct {...@@ -133,7 +135,8 @@ pub const panic = struct {
133 pub const noreturnReturned = no_panic.noreturnReturned;135 pub const noreturnReturned = no_panic.noreturnReturned;
134};136};
135fn myPanicNew(msg: []const u8, _: ?usize) noreturn {137fn 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 {};
137 std.process.exit(0);140 std.process.exit(0);
138}141}
139const std = @import("std");142const std = @import("std");
test/incremental/change_shift_op+4-2
...@@ -8,7 +8,8 @@ pub fn main() !void {...@@ -8,7 +8,8 @@ pub fn main() !void {
8 try foo(0x1300);8 try foo(0x1300);
9}9}
10fn foo(x: u16) !void {10fn 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});
12}13}
13const std = @import("std");14const std = @import("std");
14#expect_stdout="0x3000\n"15#expect_stdout="0x3000\n"
...@@ -18,7 +19,8 @@ pub fn main() !void {...@@ -18,7 +19,8 @@ pub fn main() !void {
18 try foo(0x1300);19 try foo(0x1300);
19}20}
20fn foo(x: u16) !void {21fn 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});
22}24}
23const std = @import("std");25const std = @import("std");
24#expect_stdout="0x130\n"26#expect_stdout="0x130\n"
test/incremental/change_struct_same_fields+6-3
...@@ -10,7 +10,8 @@ pub fn main() !void {...@@ -10,7 +10,8 @@ pub fn main() !void {
10 try foo(&val);10 try foo(&val);
11}11}
12fn foo(val: *const S) !void {12fn 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(
14 "{d} {d}\n",15 "{d} {d}\n",
15 .{ val.x, val.y },16 .{ val.x, val.y },
16 );17 );
...@@ -26,7 +27,8 @@ pub fn main() !void {...@@ -26,7 +27,8 @@ pub fn main() !void {
26 try foo(&val);27 try foo(&val);
27}28}
28fn foo(val: *const S) !void {29fn 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(
30 "{d} {d}\n",32 "{d} {d}\n",
31 .{ val.x, val.y },33 .{ val.x, val.y },
32 );34 );
...@@ -42,7 +44,8 @@ pub fn main() !void {...@@ -42,7 +44,8 @@ pub fn main() !void {
42 try foo(&val);44 try foo(&val);
43}45}
44fn foo(val: *const S) !void {46fn 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(
46 "{d} {d}\n",49 "{d} {d}\n",
47 .{ val.x, val.y },50 .{ val.x, val.y },
48 );51 );
test/incremental/change_zon_file+3-3
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7const std = @import("std");7const std = @import("std");
8const message: []const u8 = @import("message.zon");8const message: []const u8 = @import("message.zon");
9pub fn main() !void {9pub fn main() !void {
10 try std.io.getStdOut().writeAll(message);10 try std.fs.File.stdout().writeAll(message);
11}11}
12#file=message.zon12#file=message.zon
13"Hello, World!\n"13"Hello, World!\n"
...@@ -28,7 +28,7 @@ pub fn main() !void {...@@ -28,7 +28,7 @@ pub fn main() !void {
28const std = @import("std");28const std = @import("std");
29const message: []const u8 = @import("message.zon");29const message: []const u8 = @import("message.zon");
30pub fn main() !void {30pub fn main() !void {
31 try std.io.getStdOut().writeAll("a hardcoded string\n");31 try std.fs.File.stdout().writeAll("a hardcoded string\n");
32}32}
33#expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound33#expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound
34#expect_error=main.zig:2:37: note: file imported here34#expect_error=main.zig:2:37: note: file imported here
...@@ -43,6 +43,6 @@ pub fn main() !void {...@@ -43,6 +43,6 @@ pub fn main() !void {
43const std = @import("std");43const std = @import("std");
44const message: []const u8 = @import("message.zon");44const message: []const u8 = @import("message.zon");
45pub fn main() !void {45pub fn main() !void {
46 try std.io.getStdOut().writeAll(message);46 try std.fs.File.stdout().writeAll(message);
47}47}
48#expect_stdout="We're back, World!\n"48#expect_stdout="We're back, World!\n"
test/incremental/change_zon_file_no_result_type+1-1
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6#file=main.zig6#file=main.zig
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub fn main() !void {
9 try std.io.getStdOut().writeAll(@import("foo.zon").message);9 try std.fs.File.stdout().writeAll(@import("foo.zon").message);
10}10}
11#file=foo.zon11#file=foo.zon
12.{12.{
test/incremental/compile_log+3-3
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7#file=main.zig7#file=main.zig
8const std = @import("std");8const std = @import("std");
9pub fn main() !void {9pub fn main() !void {
10 try std.io.getStdOut().writeAll("Hello, World!\n");10 try std.fs.File.stdout().writeAll("Hello, World!\n");
11}11}
12#expect_stdout="Hello, World!\n"12#expect_stdout="Hello, World!\n"
1313
...@@ -15,7 +15,7 @@ pub fn main() !void {...@@ -15,7 +15,7 @@ pub fn main() !void {
15#file=main.zig15#file=main.zig
16const std = @import("std");16const std = @import("std");
17pub fn main() !void {17pub fn main() !void {
18 try std.io.getStdOut().writeAll("Hello, World!\n");18 try std.fs.File.stdout().writeAll("Hello, World!\n");
19 @compileLog("this is a log");19 @compileLog("this is a log");
20}20}
21#expect_error=main.zig:4:5: error: found compile log statement21#expect_error=main.zig:4:5: error: found compile log statement
...@@ -25,6 +25,6 @@ pub fn main() !void {...@@ -25,6 +25,6 @@ pub fn main() !void {
25#file=main.zig25#file=main.zig
26const std = @import("std");26const std = @import("std");
27pub fn main() !void {27pub fn main() !void {
28 try std.io.getStdOut().writeAll("Hello, World!\n");28 try std.fs.File.stdout().writeAll("Hello, World!\n");
29}29}
30#expect_stdout="Hello, World!\n"30#expect_stdout="Hello, World!\n"
test/incremental/fix_astgen_failure+5-5
...@@ -9,28 +9,28 @@ pub fn main() !void {...@@ -9,28 +9,28 @@ pub fn main() !void {
9}9}
10#file=foo.zig10#file=foo.zig
11pub fn hello() !void {11pub fn hello() !void {
12 try std.io.getStdOut().writeAll("Hello, World!\n");12 try std.fs.File.stdout().writeAll("Hello, World!\n");
13}13}
14#expect_error=foo.zig:2:9: error: use of undeclared identifier 'std'14#expect_error=foo.zig:2:9: error: use of undeclared identifier 'std'
15#update=fix the error15#update=fix the error
16#file=foo.zig16#file=foo.zig
17const std = @import("std");17const std = @import("std");
18pub fn hello() !void {18pub fn hello() !void {
19 try std.io.getStdOut().writeAll("Hello, World!\n");19 try std.fs.File.stdout().writeAll("Hello, World!\n");
20}20}
21#expect_stdout="Hello, World!\n"21#expect_stdout="Hello, World!\n"
22#update=add new error22#update=add new error
23#file=foo.zig23#file=foo.zig
24const std = @import("std");24const std = @import("std");
25pub fn hello() !void {25pub fn hello() !void {
26 try std.io.getStdOut().writeAll(hello_str);26 try std.fs.File.stdout().writeAll(hello_str);
27}27}
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'
29#update=fix the new error29#update=fix the new error
30#file=foo.zig30#file=foo.zig
31const std = @import("std");31const std = @import("std");
32const hello_str = "Hello, World! Again!\n";32const hello_str = "Hello, World! Again!\n";
33pub fn hello() !void {33pub fn hello() !void {
34 try std.io.getStdOut().writeAll(hello_str);34 try std.fs.File.stdout().writeAll(hello_str);
35}35}
36#expect_stdout="Hello, World! Again!\n"36#expect_stdout="Hello, World! Again!\n"
test/incremental/function_becomes_inline+3-3
...@@ -7,7 +7,7 @@ pub fn main() !void {...@@ -7,7 +7,7 @@ pub fn main() !void {
7 try foo();7 try foo();
8}8}
9fn foo() !void {9fn foo() !void {
10 try std.io.getStdOut().writer().writeAll("Hello, World!\n");10 try std.fs.File.stdout().writeAll("Hello, World!\n");
11}11}
12const std = @import("std");12const std = @import("std");
13#expect_stdout="Hello, World!\n"13#expect_stdout="Hello, World!\n"
...@@ -18,7 +18,7 @@ pub fn main() !void {...@@ -18,7 +18,7 @@ pub fn main() !void {
18 try foo();18 try foo();
19}19}
20inline fn foo() !void {20inline fn foo() !void {
21 try std.io.getStdOut().writer().writeAll("Hello, World!\n");21 try std.fs.File.stdout().writeAll("Hello, World!\n");
22}22}
23const std = @import("std");23const std = @import("std");
24#expect_stdout="Hello, World!\n"24#expect_stdout="Hello, World!\n"
...@@ -29,7 +29,7 @@ pub fn main() !void {...@@ -29,7 +29,7 @@ pub fn main() !void {
29 try foo();29 try foo();
30}30}
31inline fn foo() !void {31inline 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");
33}33}
34const std = @import("std");34const std = @import("std");
35#expect_stdout="Hello, `inline` World!\n"35#expect_stdout="Hello, `inline` World!\n"
test/incremental/hello+2-2
...@@ -6,13 +6,13 @@...@@ -6,13 +6,13 @@
6#file=main.zig6#file=main.zig
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub fn main() !void {
9 try std.io.getStdOut().writeAll("good morning\n");9 try std.fs.File.stdout().writeAll("good morning\n");
10}10}
11#expect_stdout="good morning\n"11#expect_stdout="good morning\n"
12#update=change the string12#update=change the string
13#file=main.zig13#file=main.zig
14const std = @import("std");14const std = @import("std");
15pub fn main() !void {15pub fn main() !void {
16 try std.io.getStdOut().writeAll("おはようございます\n");16 try std.fs.File.stdout().writeAll("おはようございます\n");
17}17}
18#expect_stdout="おはようございます\n"18#expect_stdout="おはようございます\n"
test/incremental/make_decl_pub+2-2
...@@ -11,7 +11,7 @@ pub fn main() !void {...@@ -11,7 +11,7 @@ pub fn main() !void {
11#file=foo.zig11#file=foo.zig
12const std = @import("std");12const std = @import("std");
13fn hello() !void {13fn hello() !void {
14 try std.io.getStdOut().writeAll("Hello, World!\n");14 try std.fs.File.stdout().writeAll("Hello, World!\n");
15}15}
16#expect_error=main.zig:3:12: error: 'hello' is not marked 'pub'16#expect_error=main.zig:3:12: error: 'hello' is not marked 'pub'
17#expect_error=foo.zig:2:1: note: declared here17#expect_error=foo.zig:2:1: note: declared here
...@@ -20,6 +20,6 @@ fn hello() !void {...@@ -20,6 +20,6 @@ fn hello() !void {
20#file=foo.zig20#file=foo.zig
21const std = @import("std");21const std = @import("std");
22pub fn hello() !void {22pub fn hello() !void {
23 try std.io.getStdOut().writeAll("Hello, World!\n");23 try std.fs.File.stdout().writeAll("Hello, World!\n");
24}24}
25#expect_stdout="Hello, World!\n"25#expect_stdout="Hello, World!\n"
test/incremental/modify_inline_fn+2-2
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub fn main() !void {
9 const str = getStr();9 const str = getStr();
10 try std.io.getStdOut().writeAll(str);10 try std.fs.File.stdout().writeAll(str);
11}11}
12inline fn getStr() []const u8 {12inline fn getStr() []const u8 {
13 return "foo\n";13 return "foo\n";
...@@ -18,7 +18,7 @@ inline fn getStr() []const u8 {...@@ -18,7 +18,7 @@ inline fn getStr() []const u8 {
18const std = @import("std");18const std = @import("std");
19pub fn main() !void {19pub fn main() !void {
20 const str = getStr();20 const str = getStr();
21 try std.io.getStdOut().writeAll(str);21 try std.fs.File.stdout().writeAll(str);
22}22}
23inline fn getStr() []const u8 {23inline fn getStr() []const u8 {
24 return "bar\n";24 return "bar\n";
test/incremental/move_src+6-4
...@@ -6,7 +6,8 @@...@@ -6,7 +6,8 @@
6#file=main.zig6#file=main.zig
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub 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() });
10}11}
11fn foo() u32 {12fn foo() u32 {
12 return @src().line;13 return @src().line;
...@@ -14,13 +15,14 @@ fn foo() u32 {...@@ -14,13 +15,14 @@ fn foo() u32 {
14fn bar() u32 {15fn bar() u32 {
15 return 123;16 return 123;
16}17}
17#expect_stdout="6 123\n"18#expect_stdout="7 123\n"
1819
19#update=add newline20#update=add newline
20#file=main.zig21#file=main.zig
21const std = @import("std");22const std = @import("std");
22pub fn main() !void {23pub 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() });
24}26}
2527
26fn foo() u32 {28fn foo() u32 {
...@@ -29,4 +31,4 @@ fn foo() u32 {...@@ -29,4 +31,4 @@ fn foo() u32 {
29fn bar() u32 {31fn bar() u32 {
30 return 123;32 return 123;
31}33}
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 @@...@@ -7,7 +7,7 @@
7const std = @import("std");7const std = @import("std");
8var some_enum: enum { first, second } = .first;8var some_enum: enum { first, second } = .first;
9pub fn main() !void {9pub fn main() !void {
10 try std.io.getStdOut().writeAll(@tagName(some_enum));10 try std.fs.File.stdout().writeAll(@tagName(some_enum));
11}11}
12#expect_stdout="first"12#expect_stdout="first"
13#update=no change13#update=no change
...@@ -15,6 +15,6 @@ pub fn main() !void {...@@ -15,6 +15,6 @@ pub fn main() !void {
15const std = @import("std");15const std = @import("std");
16var some_enum: enum { first, second } = .first;16var some_enum: enum { first, second } = .first;
17pub fn main() !void {17pub fn main() !void {
18 try std.io.getStdOut().writeAll(@tagName(some_enum));18 try std.fs.File.stdout().writeAll(@tagName(some_enum));
19}19}
20#expect_stdout="first"20#expect_stdout="first"
test/incremental/recursive_function_becomes_non_recursive+2-2
...@@ -8,7 +8,7 @@ pub fn main() !void {...@@ -8,7 +8,7 @@ pub fn main() !void {
8 try foo(false);8 try foo(false);
9}9}
10fn foo(recurse: bool) !void {10fn foo(recurse: bool) !void {
11 const stdout = std.io.getStdOut().writer();11 const stdout = std.fs.File.stdout();
12 if (recurse) return foo(true);12 if (recurse) return foo(true);
13 try stdout.writeAll("non-recursive path\n");13 try stdout.writeAll("non-recursive path\n");
14}14}
...@@ -21,7 +21,7 @@ pub fn main() !void {...@@ -21,7 +21,7 @@ pub fn main() !void {
21 try foo(true);21 try foo(true);
22}22}
23fn foo(recurse: bool) !void {23fn foo(recurse: bool) !void {
24 const stdout = std.io.getStdOut().writer();24 const stdout = std.fs.File.stdout();
25 if (recurse) return stdout.writeAll("x==1\n");25 if (recurse) return stdout.writeAll("x==1\n");
26 try stdout.writeAll("non-recursive path\n");26 try stdout.writeAll("non-recursive path\n");
27}27}
test/incremental/remove_enum_field+5-3
...@@ -9,7 +9,8 @@ const MyEnum = enum(u8) {...@@ -9,7 +9,8 @@ const MyEnum = enum(u8) {
9 bar = 2,9 bar = 2,
10};10};
11pub fn main() !void {11pub 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)});
13}14}
14const std = @import("std");15const std = @import("std");
15#expect_stdout="1\n"16#expect_stdout="1\n"
...@@ -20,8 +21,9 @@ const MyEnum = enum(u8) {...@@ -20,8 +21,9 @@ const MyEnum = enum(u8) {
20 bar = 2,21 bar = 2,
21};22};
22pub fn main() !void {23pub 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)});
24}26}
25const std = @import("std");27const 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'
27#expect_error=main.zig:1:16: note: enum declared here29#expect_error=main.zig:1:16: note: enum declared here
test/incremental/unreferenced_error+4-4
...@@ -6,7 +6,7 @@...@@ -6,7 +6,7 @@
6#file=main.zig6#file=main.zig
7const std = @import("std");7const std = @import("std");
8pub fn main() !void {8pub fn main() !void {
9 try std.io.getStdOut().writeAll(a);9 try std.fs.File.stdout().writeAll(a);
10}10}
11const a = "Hello, World!\n";11const a = "Hello, World!\n";
12#expect_stdout="Hello, World!\n"12#expect_stdout="Hello, World!\n"
...@@ -15,7 +15,7 @@ const a = "Hello, World!\n";...@@ -15,7 +15,7 @@ const a = "Hello, World!\n";
15#file=main.zig15#file=main.zig
16const std = @import("std");16const std = @import("std");
17pub fn main() !void {17pub fn main() !void {
18 try std.io.getStdOut().writeAll(a);18 try std.fs.File.stdout().writeAll(a);
19}19}
20const a = @compileError("bad a");20const a = @compileError("bad a");
21#expect_error=main.zig:5:11: error: bad a21#expect_error=main.zig:5:11: error: bad a
...@@ -24,7 +24,7 @@ const a = @compileError("bad a");...@@ -24,7 +24,7 @@ const a = @compileError("bad a");
24#file=main.zig24#file=main.zig
25const std = @import("std");25const std = @import("std");
26pub fn main() !void {26pub fn main() !void {
27 try std.io.getStdOut().writeAll(b);27 try std.fs.File.stdout().writeAll(b);
28}28}
29const a = @compileError("bad a");29const a = @compileError("bad a");
30const b = "Hi there!\n";30const b = "Hi there!\n";
...@@ -34,7 +34,7 @@ const b = "Hi there!\n";...@@ -34,7 +34,7 @@ const b = "Hi there!\n";
34#file=main.zig34#file=main.zig
35const std = @import("std");35const std = @import("std");
36pub fn main() !void {36pub fn main() !void {
37 try std.io.getStdOut().writeAll(a);37 try std.fs.File.stdout().writeAll(a);
38}38}
39const a = "Back to a\n";39const a = "Back to a\n";
40const b = @compileError("bad b");40const b = @compileError("bad b");
test/link/bss/main.zig+4-1
...@@ -4,8 +4,11 @@ const std = @import("std");...@@ -4,8 +4,11 @@ const std = @import("std");
4var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000;4var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000;
55
6pub fn main() anyerror!void {6pub fn main() anyerror!void {
7 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
8
7 buffer[0x10] = 1;9 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", .{
9 // workaround the dreaded decl_val12 // workaround the dreaded decl_val
10 (&buffer)[0],13 (&buffer)[0],
11 (&buffer)[0x10],14 (&buffer)[0x10],
test/link/elf.zig+4-4
...@@ -1315,8 +1315,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {...@@ -1315,8 +1315,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
1315 \\extern var live_var2: i32;1315 \\extern var live_var2: i32;
1316 \\extern fn live_fn2() void;1316 \\extern fn live_fn2() void;
1317 \\pub fn main() void {1317 \\pub fn main() void {
1318 \\ const stdout = std.io.getStdOut();1318 \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
1319 \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;1319 \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
1320 \\ live_fn2();1320 \\ live_fn2();
1321 \\}1321 \\}
1322 ,1322 ,
...@@ -1357,8 +1357,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {...@@ -1357,8 +1357,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step {
1357 \\extern var live_var2: i32;1357 \\extern var live_var2: i32;
1358 \\extern fn live_fn2() void;1358 \\extern fn live_fn2() void;
1359 \\pub fn main() void {1359 \\pub fn main() void {
1360 \\ const stdout = std.io.getStdOut();1360 \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
1361 \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable;1361 \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
1362 \\ live_fn2();1362 \\ live_fn2();
1363 \\}1363 \\}
1364 ,1364 ,
test/link/macho.zig+4-3
...@@ -710,7 +710,7 @@ fn testHelloZig(b: *Build, opts: Options) *Step {...@@ -710,7 +710,7 @@ fn testHelloZig(b: *Build, opts: Options) *Step {
710 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = 710 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
711 \\const std = @import("std");711 \\const std = @import("std");
712 \\pub fn main() void {712 \\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");
714 \\}714 \\}
715 });715 });
716716
...@@ -2365,10 +2365,11 @@ fn testTlsZig(b: *Build, opts: Options) *Step {...@@ -2365,10 +2365,11 @@ fn testTlsZig(b: *Build, opts: Options) *Step {
2365 \\threadlocal var x: i32 = 0;2365 \\threadlocal var x: i32 = 0;
2366 \\threadlocal var y: i32 = -1;2366 \\threadlocal var y: i32 = -1;
2367 \\pub fn main() void {2367 \\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;
2369 \\ x -= 1;2370 \\ x -= 1;
2370 \\ y += 1;2371 \\ 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;
2372 \\}2373 \\}
2373 });2374 });
23742375
test/link/wasm/extern/main.zig+2-2
...@@ -3,6 +3,6 @@ const std = @import("std");...@@ -3,6 +3,6 @@ const std = @import("std");
3extern const foo: u32;3extern const foo: u32;
44
5pub fn main() void {5pub fn main() void {
6 const std_out = std.io.getStdOut();6 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
7 std_out.writer().print("Result: {d}", .{foo}) catch {};7 stdout_writer.interface.print("Result: {d}", .{foo}) catch {};
8}8}
test/src/Cases.zig+33-6
...@@ -594,30 +594,57 @@ pub fn lowerToTranslateCSteps(...@@ -594,30 +594,57 @@ pub fn lowerToTranslateCSteps(
594 };594 };
595}595}
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
597pub fn lowerToBuildSteps(610pub fn lowerToBuildSteps(
598 self: *Cases,611 self: *Cases,
599 b: *std.Build,612 b: *std.Build,
600 parent_step: *std.Build.Step,613 parent_step: *std.Build.Step,
601 test_filters: []const []const u8,614 options: CaseTestOptions,
602 test_target_filters: []const []const u8,
603) void {615) void {
604 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|616 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|
605 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});617 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
606 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");618 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");
607619
608 for (self.cases.items) |case| {620 for (self.cases.items) |case| {
609 for (test_filters) |test_filter| {621 for (options.test_filters) |test_filter| {
610 if (std.mem.indexOf(u8, case.name, test_filter)) |_| break;622 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
613 const triple_txt = case.target.query.zigTriple(b.allocator) catch @panic("OOM");637 const triple_txt = case.target.query.zigTriple(b.allocator) catch @panic("OOM");
614638
615 if (test_target_filters.len > 0) {639 if (options.test_target_filters.len > 0) {
616 for (test_target_filters) |filter| {640 for (options.test_target_filters) |filter| {
617 if (std.mem.indexOf(u8, triple_txt, filter) != null) break;641 if (std.mem.indexOf(u8, triple_txt, filter) != null) break;
618 } else continue;642 } else continue;
619 }643 }
620644
645 if (options.skip_libc and case.link_libc)
646 continue;
647
621 const writefiles = b.addWriteFiles();648 const writefiles = b.addWriteFiles();
622 var file_sources = std.StringHashMap(std.Build.LazyPath).init(b.allocator);649 var file_sources = std.StringHashMap(std.Build.LazyPath).init(b.allocator);
623 defer file_sources.deinit();650 defer file_sources.deinit();
test/src/check-stack-trace.zig+1-1
...@@ -84,5 +84,5 @@ pub fn main() !void {...@@ -84,5 +84,5 @@ pub fn main() !void {
84 break :got_result try buf.toOwnedSlice();84 break :got_result try buf.toOwnedSlice();
85 };85 };
8686
87 try std.io.getStdOut().writeAll(got);87 try std.fs.File.stdout().writeAll(got);
88}88}
test/standalone/build.zig.zon-3
...@@ -48,9 +48,6 @@...@@ -48,9 +48,6 @@
48 .pkg_import = .{48 .pkg_import = .{
49 .path = "pkg_import",49 .path = "pkg_import",
50 },50 },
51 .use_alias = .{
52 .path = "use_alias",
53 },
54 .install_raw_hex = .{51 .install_raw_hex = .{
55 .path = "install_raw_hex",52 .path = "install_raw_hex",
56 },53 },
test/standalone/child_process/child.zig+4-3
...@@ -27,12 +27,12 @@ fn run(allocator: std.mem.Allocator) !void {...@@ -27,12 +27,12 @@ fn run(allocator: std.mem.Allocator) !void {
27 }27 }
2828
29 // test stdout pipe; parent verifies29 // 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
32 // test stdin pipe from parent32 // test stdin pipe from parent
33 const hello_stdin = "hello from stdin";33 const hello_stdin = "hello from stdin";
34 var buf: [hello_stdin.len]u8 = undefined;34 var buf: [hello_stdin.len]u8 = undefined;
35 const stdin = std.io.getStdIn().reader();35 const stdin: std.fs.File = .stdin();
36 const n = try stdin.readAll(&buf);36 const n = try stdin.readAll(&buf);
37 if (!std.mem.eql(u8, buf[0..n], hello_stdin)) {37 if (!std.mem.eql(u8, buf[0..n], hello_stdin)) {
38 testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin });38 testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin });
...@@ -40,7 +40,8 @@ fn run(allocator: std.mem.Allocator) !void {...@@ -40,7 +40,8 @@ fn run(allocator: std.mem.Allocator) !void {
40}40}
4141
42fn testError(comptime fmt: []const u8, args: anytype) void {42fn 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;
44 stderr.print("CHILD TEST ERROR: ", .{}) catch {};45 stderr.print("CHILD TEST ERROR: ", .{}) catch {};
45 stderr.print(fmt, args) catch {};46 stderr.print(fmt, args) catch {};
46 if (fmt[fmt.len - 1] != '\n') {47 if (fmt[fmt.len - 1] != '\n') {
test/standalone/child_process/main.zig+4-3
...@@ -19,13 +19,13 @@ pub fn main() !void {...@@ -19,13 +19,13 @@ pub fn main() !void {
19 child.stderr_behavior = .Inherit;19 child.stderr_behavior = .Inherit;
20 try child.spawn();20 try child.spawn();
21 const child_stdin = child.stdin.?;21 const child_stdin = child.stdin.?;
22 try child_stdin.writer().writeAll("hello from stdin"); // verified in child22 try child_stdin.writeAll("hello from stdin"); // verified in child
23 child_stdin.close();23 child_stdin.close();
24 child.stdin = null;24 child.stdin = null;
2525
26 const hello_stdout = "hello from stdout";26 const hello_stdout = "hello from stdout";
27 var buf: [hello_stdout.len]u8 = undefined;27 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);
29 if (!std.mem.eql(u8, buf[0..n], hello_stdout)) {29 if (!std.mem.eql(u8, buf[0..n], hello_stdout)) {
30 testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout });30 testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout });
31 }31 }
...@@ -45,7 +45,8 @@ pub fn main() !void {...@@ -45,7 +45,8 @@ pub fn main() !void {
45var parent_test_error = false;45var parent_test_error = false;
4646
47fn testError(comptime fmt: []const u8, args: anytype) void {47fn 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;
49 stderr.print("PARENT TEST ERROR: ", .{}) catch {};50 stderr.print("PARENT TEST ERROR: ", .{}) catch {};
50 stderr.print(fmt, args) catch {};51 stderr.print(fmt, args) catch {};
51 if (fmt[fmt.len - 1] != '\n') {52 if (fmt[fmt.len - 1] != '\n') {
test/standalone/run_output_paths/create_file.zig+1-1
...@@ -10,7 +10,7 @@ pub fn main() !void {...@@ -10,7 +10,7 @@ pub fn main() !void {
10 dir_name, .{});10 dir_name, .{});
11 const file_name = args.next().?;11 const file_name = args.next().?;
12 const file = try dir.createFile(file_name, .{});12 const file = try dir.createFile(file_name, .{});
13 try file.writer().print(13 try file.deprecatedWriter().print(
14 \\{s}14 \\{s}
15 \\{s}15 \\{s}
16 \\Hello, world!16 \\Hello, world!
test/standalone/sigpipe/breakpipe.zig+1-1
...@@ -10,7 +10,7 @@ pub fn main() !void {...@@ -10,7 +10,7 @@ pub fn main() !void {
10 std.posix.close(pipe[0]);10 std.posix.close(pipe[0]);
11 _ = std.posix.write(pipe[1], "a") catch |err| switch (err) {11 _ = std.posix.write(pipe[1], "a") catch |err| switch (err) {
12 error.BrokenPipe => {12 error.BrokenPipe => {
13 try std.io.getStdOut().writer().writeAll("BrokenPipe\n");13 try std.fs.File.stdout().writeAll("BrokenPipe\n");
14 std.posix.exit(123);14 std.posix.exit(123);
15 },15 },
16 else => |e| return e,16 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 {...@@ -50,6 +50,10 @@ pub fn build(b: *std.Build) void {
50 });50 });
51 if (case.link_libc) exe.root_module.link_libc = true;51 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
53 _ = exe.getEmittedBin();57 _ = exe.getEmittedBin();
5458
55 step.dependOn(&exe.step);59 step.dependOn(&exe.step);
...@@ -66,6 +70,10 @@ pub fn build(b: *std.Build) void {...@@ -66,6 +70,10 @@ pub fn build(b: *std.Build) void {
66 });70 });
67 if (case.link_libc) exe.root_module.link_libc = true;71 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
69 const run = b.addRunArtifact(exe);77 const run = b.addRunArtifact(exe);
70 step.dependOn(&run.step);78 step.dependOn(&run.step);
71 }79 }
...@@ -101,10 +109,6 @@ const cases = [_]Case{...@@ -101,10 +109,6 @@ const cases = [_]Case{
101 //.{109 //.{
102 // .src_path = "issue_9693/main.zig",110 // .src_path = "issue_9693/main.zig",
103 //},111 //},
104 .{
105 .src_path = "brace_expansion.zig",
106 .is_test = true,
107 },
108 .{112 .{
109 .src_path = "issue_7030.zig",113 .src_path = "issue_7030.zig",
110 .target = .{114 .target = .{
test/standalone/simple/cat/main.zig+10-10
...@@ -1,42 +1,42 @@...@@ -1,42 +1,42 @@
1const std = @import("std");1const std = @import("std");
2const io = std.io;2const io = std.io;
3const process = std.process;
4const fs = std.fs;3const fs = std.fs;
5const mem = std.mem;4const mem = std.mem;
6const warn = std.log.warn;5const warn = std.log.warn;
6const fatal = std.process.fatal;
77
8pub fn main() !void {8pub fn main() !void {
9 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);9 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
10 defer arena_instance.deinit();10 defer arena_instance.deinit();
11 const arena = arena_instance.allocator();11 const arena = arena_instance.allocator();
1212
13 const args = try process.argsAlloc(arena);13 const args = try std.process.argsAlloc(arena);
1414
15 const exe = args[0];15 const exe = args[0];
16 var catted_anything = false;16 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
19 const cwd = fs.cwd();21 const cwd = fs.cwd();
2022
21 for (args[1..]) |arg| {23 for (args[1..]) |arg| {
22 if (mem.eql(u8, arg, "-")) {24 if (mem.eql(u8, arg, "-")) {
23 catted_anything = true;25 catted_anything = true;
24 try stdout_file.writeFileAll(io.getStdIn(), .{});26 _ = try stdout.sendFileAll(&stdin_reader, .unlimited);
25 } else if (mem.startsWith(u8, arg, "-")) {27 } else if (mem.startsWith(u8, arg, "-")) {
26 return usage(exe);28 return usage(exe);
27 } else {29 } else {
28 const file = cwd.openFile(arg, .{}) catch |err| {30 const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err});
29 warn("Unable to open file: {s}\n", .{@errorName(err)});
30 return err;
31 };
32 defer file.close();31 defer file.close();
3332
34 catted_anything = true;33 catted_anything = true;
35 try stdout_file.writeFileAll(file, .{});34 var file_reader = file.reader(&.{});
35 _ = try stdout.sendFileAll(&file_reader, .unlimited);
36 }36 }
37 }37 }
38 if (!catted_anything) {38 if (!catted_anything) {
39 try stdout_file.writeFileAll(io.getStdIn(), .{});39 _ = try stdout.sendFileAll(&stdin_reader, .unlimited);
40 }40 }
41}41}
4242
test/standalone/simple/guess_number/main.zig+11-13
...@@ -1,37 +1,35 @@...@@ -1,37 +1,35 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("std");2const std = @import("std");
3const io = std.io;
4const fmt = std.fmt;
53
6pub fn main() !void {4pub fn main() !void {
7 const stdout = io.getStdOut().writer();5 var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
8 const stdin = io.getStdIn();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
12 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;11 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;
1312
14 while (true) {13 while (true) {
15 try stdout.print("\nGuess a number between 1 and 100: ", .{});14 try out.writeAll("\nGuess a number between 1 and 100: ");
16 var line_buf: [20]u8 = undefined;15 var line_buf: [20]u8 = undefined;
17
18 const amt = try stdin.read(&line_buf);16 const amt = try stdin.read(&line_buf);
19 if (amt == line_buf.len) {17 if (amt == line_buf.len) {
20 try stdout.print("Input too long.\n", .{});18 try out.writeAll("Input too long.\n");
21 continue;19 continue;
22 }20 }
23 const line = std.mem.trimEnd(u8, line_buf[0..amt], "\r\n");21 const line = std.mem.trimEnd(u8, line_buf[0..amt], "\r\n");
2422
25 const guess = fmt.parseUnsigned(u8, line, 10) catch {23 const guess = std.fmt.parseUnsigned(u8, line, 10) catch {
26 try stdout.print("Invalid number.\n", .{});24 try out.writeAll("Invalid number.\n");
27 continue;25 continue;
28 };26 };
29 if (guess > answer) {27 if (guess > answer) {
30 try stdout.print("Guess lower.\n", .{});28 try out.writeAll("Guess lower.\n");
31 } else if (guess < answer) {29 } else if (guess < answer) {
32 try stdout.print("Guess higher.\n", .{});30 try out.writeAll("Guess higher.\n");
33 } else {31 } else {
34 try stdout.print("You win!\n", .{});32 try out.writeAll("You win!\n");
35 return;33 return;
36 }34 }
37 }35 }
test/standalone/simple/std_enums_big_enums.zig+1
...@@ -6,6 +6,7 @@ pub fn main() void {...@@ -6,6 +6,7 @@ pub fn main() void {
6 const Big = @Type(.{ .@"enum" = .{6 const Big = @Type(.{ .@"enum" = .{
7 .tag_type = u16,7 .tag_type = u16,
8 .fields = make_fields: {8 .fields = make_fields: {
9 @setEvalBranchQuota(500000);
9 var fields: [1001]std.builtin.Type.EnumField = undefined;10 var fields: [1001]std.builtin.Type.EnumField = undefined;
10 for (&fields, 0..) |*field, i| {11 for (&fields, 0..) |*field, i| {
11 field.* = .{ .name = std.fmt.comptimePrint("field_{d}", .{i}), .value = i };12 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 {...@@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void {
29 .root_source_file = b.path("unwind.zig"),29 .root_source_file = b.path("unwind.zig"),
30 .target = target,30 .target = target,
31 .optimize = optimize,31 .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,
33 .omit_frame_pointer = false,33 .omit_frame_pointer = false,
34 }),34 }),
35 });35 });
...@@ -54,7 +54,7 @@ pub fn build(b: *std.Build) void {...@@ -54,7 +54,7 @@ pub fn build(b: *std.Build) void {
54 .root_source_file = b.path("unwind.zig"),54 .root_source_file = b.path("unwind.zig"),
55 .target = target,55 .target = target,
56 .optimize = optimize,56 .optimize = optimize,
57 .unwind_tables = .@"async",57 .unwind_tables = .async,
58 .omit_frame_pointer = true,58 .omit_frame_pointer = true,
59 }),59 }),
60 // self-hosted lacks omit_frame_pointer support60 // self-hosted lacks omit_frame_pointer support
...@@ -101,7 +101,7 @@ pub fn build(b: *std.Build) void {...@@ -101,7 +101,7 @@ pub fn build(b: *std.Build) void {
101 .root_source_file = b.path("shared_lib_unwind.zig"),101 .root_source_file = b.path("shared_lib_unwind.zig"),
102 .target = target,102 .target = target,
103 .optimize = optimize,103 .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,
105 .omit_frame_pointer = true,105 .omit_frame_pointer = true,
106 }),106 }),
107 // zig objcopy doesn't support incremental binaries107 // 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 {...@@ -47,6 +47,8 @@ pub fn build(b: *std.Build) !void {
47 }),47 }),
48 });48 });
4949
50 fuzz.root_module.linkSystemLibrary("advapi32", .{});
51
50 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;52 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;
51 const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom");53 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 {...@@ -58,7 +58,7 @@ pub fn main() !void {
58 std.debug.print(">>> found discrepancy <<<\n", .{});58 std.debug.print(">>> found discrepancy <<<\n", .{});
59 const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w);59 const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w);
60 defer allocator.free(cmd_line_wtf8);60 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
63 errors += 1;63 errors += 1;
64 }64 }
test/standalone/windows_argv/lib.zig+6-6
...@@ -27,8 +27,8 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {...@@ -27,8 +27,8 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
27 wtf8_buf.clearRetainingCapacity();27 wtf8_buf.clearRetainingCapacity();
28 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(expected_arg));28 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(expected_arg));
29 if (!std.mem.eql(u8, wtf8_buf.items, arg_wtf8)) {29 if (!std.mem.eql(u8, wtf8_buf.items, arg_wtf8)) {
30 std.debug.print("{}: expected: \"{}\"\n", .{ i, std.zig.fmtEscapes(wtf8_buf.items) });30 std.debug.print("{}: expected: \"{f}\"\n", .{ i, std.zig.fmtString(wtf8_buf.items) });
31 std.debug.print("{}: actual: \"{}\"\n", .{ i, std.zig.fmtEscapes(arg_wtf8) });31 std.debug.print("{}: actual: \"{f}\"\n", .{ i, std.zig.fmtString(arg_wtf8) });
32 eql = false;32 eql = false;
33 }33 }
34 }34 }
...@@ -36,22 +36,22 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {...@@ -36,22 +36,22 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
36 for (expected_args[min_len..], min_len..) |arg, i| {36 for (expected_args[min_len..], min_len..) |arg, i| {
37 wtf8_buf.clearRetainingCapacity();37 wtf8_buf.clearRetainingCapacity();
38 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(arg));38 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) });
40 }40 }
41 for (args[min_len..], min_len..) |arg, i| {41 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) });
43 }43 }
44 const peb = std.os.windows.peb();44 const peb = std.os.windows.peb();
45 const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer);45 const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer);
46 wtf8_buf.clearRetainingCapacity();46 wtf8_buf.clearRetainingCapacity();
47 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(lpCmdLine));47 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)});
49 std.debug.print("expected argv:\n", .{});49 std.debug.print("expected argv:\n", .{});
50 std.debug.print("&.{{\n", .{});50 std.debug.print("&.{{\n", .{});
51 for (expected_args) |arg| {51 for (expected_args) |arg| {
52 wtf8_buf.clearRetainingCapacity();52 wtf8_buf.clearRetainingCapacity();
53 try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(arg));53 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)});
55 }55 }
56 std.debug.print("}}\n", .{});56 std.debug.print("}}\n", .{});
57 return error.ArgvMismatch;57 return error.ArgvMismatch;
test/standalone/windows_bat_args/build.zig+4
...@@ -28,6 +28,8 @@ pub fn build(b: *std.Build) !void {...@@ -28,6 +28,8 @@ pub fn build(b: *std.Build) !void {
28 }),28 }),
29 });29 });
3030
31 test_exe.root_module.linkSystemLibrary("advapi32", .{});
32
31 const run = b.addRunArtifact(test_exe);33 const run = b.addRunArtifact(test_exe);
32 run.addArtifactArg(echo_args);34 run.addArtifactArg(echo_args);
33 run.expectExitCode(0);35 run.expectExitCode(0);
...@@ -44,6 +46,8 @@ pub fn build(b: *std.Build) !void {...@@ -44,6 +46,8 @@ pub fn build(b: *std.Build) !void {
44 }),46 }),
45 });47 });
4648
49 fuzz.root_module.linkSystemLibrary("advapi32", .{});
50
47 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;51 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;
48 const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom");52 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 {...@@ -5,7 +5,8 @@ pub fn main() !void {
5 defer arena_state.deinit();5 defer arena_state.deinit();
6 const arena = arena_state.allocator();6 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;
9 var args = try std.process.argsAlloc(arena);10 var args = try std.process.argsAlloc(arena);
10 for (args[1..], 1..) |arg, i| {11 for (args[1..], 1..) |arg, i| {
11 try stdout.writeAll(arg);12 try stdout.writeAll(arg);
test/standalone/windows_spawn/build.zig+2
...@@ -28,6 +28,8 @@ pub fn build(b: *std.Build) void {...@@ -28,6 +28,8 @@ pub fn build(b: *std.Build) void {
28 }),28 }),
29 });29 });
3030
31 main.root_module.linkSystemLibrary("advapi32", .{});
32
31 const run = b.addRunArtifact(main);33 const run = b.addRunArtifact(main);
32 run.addArtifactArg(hello);34 run.addArtifactArg(hello);
33 run.expectExitCode(0);35 run.expectExitCode(0);
test/standalone/windows_spawn/hello.zig+2-1
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn main() !void {3pub 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;
5 try stdout.writeAll("hello from exe\n");6 try stdout.writeAll("hello from exe\n");
6}7}
test/tests.zig+83-39
...@@ -918,14 +918,16 @@ const test_targets = blk: {...@@ -918,14 +918,16 @@ const test_targets = blk: {
918 .link_libc = true,918 .link_libc = true,
919 },919 },
920920
921 .{921 // TODO implement codegen airFieldParentPtr
922 .target = std.Target.Query.parse(.{922 // TODO implement airMemmove for riscv64
923 .arch_os_abi = "riscv64-linux-none",923 //.{
924 .cpu_features = "baseline+v+zbb",924 // .target = std.Target.Query.parse(.{
925 }) catch unreachable,925 // .arch_os_abi = "riscv64-linux-none",
926 .use_llvm = false,926 // .cpu_features = "baseline+v+zbb",
927 .use_lld = false,927 // }) catch unreachable,
928 },928 // .use_llvm = false,
929 // .use_lld = false,
930 //},
929 .{931 .{
930 .target = .{932 .target = .{
931 .cpu_arch = .riscv64,933 .cpu_arch = .riscv64,
...@@ -1480,16 +1482,8 @@ const test_targets = blk: {...@@ -1480,16 +1482,8 @@ const test_targets = blk: {
1480 .target = .{1482 .target = .{
1481 .cpu_arch = .aarch64,1483 .cpu_arch = .aarch64,
1482 .os_tag = .windows,1484 .os_tag = .windows,
1483 .abi = .none,1485 .abi = .msvc,
1484 },
1485 },
1486 .{
1487 .target = .{
1488 .cpu_arch = .aarch64,
1489 .os_tag = .windows,
1490 .abi = .gnu,
1491 },1486 },
1492 .link_libc = true,
1493 },1487 },
1494 .{1488 .{
1495 .target = .{1489 .target = .{
...@@ -1499,37 +1493,36 @@ const test_targets = blk: {...@@ -1499,37 +1493,36 @@ const test_targets = blk: {
1499 },1493 },
1500 .link_libc = true,1494 .link_libc = true,
1501 },1495 },
1502
1503 .{1496 .{
1504 .target = .{1497 .target = .{
1505 .cpu_arch = .x86,1498 .cpu_arch = .aarch64,
1506 .os_tag = .windows,1499 .os_tag = .windows,
1507 .abi = .none,1500 .abi = .gnu,
1508 },1501 },
1509 },1502 },
1510 .{1503 .{
1511 .target = .{1504 .target = .{
1512 .cpu_arch = .x86,1505 .cpu_arch = .aarch64,
1513 .os_tag = .windows,1506 .os_tag = .windows,
1514 .abi = .gnu,1507 .abi = .gnu,
1515 },1508 },
1516 .link_libc = true,1509 .link_libc = true,
1517 },1510 },
1511
1518 .{1512 .{
1519 .target = .{1513 .target = .{
1520 .cpu_arch = .x86,1514 .cpu_arch = .thumb,
1521 .os_tag = .windows,1515 .os_tag = .windows,
1522 .abi = .msvc,1516 .abi = .msvc,
1523 },1517 },
1524 .link_libc = true,
1525 },1518 },
1526
1527 .{1519 .{
1528 .target = .{1520 .target = .{
1529 .cpu_arch = .thumb,1521 .cpu_arch = .thumb,
1530 .os_tag = .windows,1522 .os_tag = .windows,
1531 .abi = .none,1523 .abi = .msvc,
1532 },1524 },
1525 .link_libc = true,
1533 },1526 },
1534 // https://github.com/ziglang/zig/issues/240161527 // https://github.com/ziglang/zig/issues/24016
1535 // .{1528 // .{
...@@ -1538,22 +1531,52 @@ const test_targets = blk: {...@@ -1538,22 +1531,52 @@ const test_targets = blk: {
1538 // .os_tag = .windows,1531 // .os_tag = .windows,
1539 // .abi = .gnu,1532 // .abi = .gnu,
1540 // },1533 // },
1534 // },
1535 // .{
1536 // .target = .{
1537 // .cpu_arch = .thumb,
1538 // .os_tag = .windows,
1539 // .abi = .gnu,
1540 // },
1541 // .link_libc = true,1541 // .link_libc = true,
1542 // },1542 // },
1543
1543 .{1544 .{
1544 .target = .{1545 .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,
1546 .os_tag = .windows,1554 .os_tag = .windows,
1547 .abi = .msvc,1555 .abi = .msvc,
1548 },1556 },
1549 .link_libc = true,1557 .link_libc = true,
1550 },1558 },
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
1552 .{1575 .{
1553 .target = .{1576 .target = .{
1554 .cpu_arch = .x86_64,1577 .cpu_arch = .x86_64,
1555 .os_tag = .windows,1578 .os_tag = .windows,
1556 .abi = .none,1579 .abi = .msvc,
1557 },1580 },
1558 .use_llvm = false,1581 .use_llvm = false,
1559 .use_lld = false,1582 .use_lld = false,
...@@ -1562,17 +1585,16 @@ const test_targets = blk: {...@@ -1562,17 +1585,16 @@ const test_targets = blk: {
1562 .target = .{1585 .target = .{
1563 .cpu_arch = .x86_64,1586 .cpu_arch = .x86_64,
1564 .os_tag = .windows,1587 .os_tag = .windows,
1565 .abi = .gnu,1588 .abi = .msvc,
1566 },1589 },
1567 .use_llvm = false,
1568 .use_lld = false,
1569 },1590 },
1570 .{1591 .{
1571 .target = .{1592 .target = .{
1572 .cpu_arch = .x86_64,1593 .cpu_arch = .x86_64,
1573 .os_tag = .windows,1594 .os_tag = .windows,
1574 .abi = .none,1595 .abi = .msvc,
1575 },1596 },
1597 .link_libc = true,
1576 },1598 },
1577 .{1599 .{
1578 .target = .{1600 .target = .{
...@@ -1580,13 +1602,21 @@ const test_targets = blk: {...@@ -1580,13 +1602,21 @@ const test_targets = blk: {
1580 .os_tag = .windows,1602 .os_tag = .windows,
1581 .abi = .gnu,1603 .abi = .gnu,
1582 },1604 },
1583 .link_libc = true,1605 .use_llvm = false,
1606 .use_lld = false,
1584 },1607 },
1585 .{1608 .{
1586 .target = .{1609 .target = .{
1587 .cpu_arch = .x86_64,1610 .cpu_arch = .x86_64,
1588 .os_tag = .windows,1611 .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,
1590 },1620 },
1591 .link_libc = true,1621 .link_libc = true,
1592 },1622 },
...@@ -2280,6 +2310,7 @@ const ModuleTestOptions = struct {...@@ -2280,6 +2310,7 @@ const ModuleTestOptions = struct {
2280 desc: []const u8,2310 desc: []const u8,
2281 optimize_modes: []const OptimizeMode,2311 optimize_modes: []const OptimizeMode,
2282 include_paths: []const []const u8,2312 include_paths: []const []const u8,
2313 windows_libs: []const []const u8,
2283 skip_single_threaded: bool,2314 skip_single_threaded: bool,
2284 skip_non_native: bool,2315 skip_non_native: bool,
2285 skip_freebsd: bool,2316 skip_freebsd: bool,
...@@ -2409,6 +2440,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {...@@ -2409,6 +2440,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
24092440
2410 for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path));2441 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
2412 const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{2447 const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{
2413 options.name,2448 options.name,
2414 triple_txt,2449 triple_txt,
...@@ -2517,7 +2552,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {...@@ -2517,7 +2552,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
2517 return step;2552 return step;
2518}2553}
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 {
2521 if (use_llvm) |x| return x;2556 if (use_llvm) |x| return x;
2522 if (query.ofmt == .c) return false;2557 if (query.ofmt == .c) return false;
2523 switch (optimize_mode) {2558 switch (optimize_mode) {
...@@ -2629,9 +2664,8 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step {...@@ -2629,9 +2664,8 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step {
2629pub fn addCases(2664pub fn addCases(
2630 b: *std.Build,2665 b: *std.Build,
2631 parent_step: *Step,2666 parent_step: *Step,
2632 test_filters: []const []const u8,
2633 test_target_filters: []const []const u8,
2634 target: std.Build.ResolvedTarget,2667 target: std.Build.ResolvedTarget,
2668 case_test_options: @import("src/Cases.zig").CaseTestOptions,
2635 translate_c_options: @import("src/Cases.zig").TranslateCOptions,2669 translate_c_options: @import("src/Cases.zig").TranslateCOptions,
2636 build_options: @import("cases.zig").BuildOptions,2670 build_options: @import("cases.zig").BuildOptions,
2637) !void {2671) !void {
...@@ -2646,13 +2680,19 @@ pub fn addCases(...@@ -2646,13 +2680,19 @@ pub fn addCases(
2646 cases.addFromDir(dir, b);2680 cases.addFromDir(dir, b);
2647 try @import("cases.zig").addCases(&cases, build_options, b);2681 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
2651 cases.lowerToBuildSteps(2692 cases.lowerToBuildSteps(
2652 b,2693 b,
2653 parent_step,2694 parent_step,
2654 test_filters,2695 case_test_options,
2655 test_target_filters,
2656 );2696 );
2657}2697}
26582698
...@@ -2699,6 +2739,10 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void {...@@ -2699,6 +2739,10 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void {
2699 }),2739 }),
2700 });2740 });
27012741
2742 if (b.graph.host.result.os.tag == .windows) {
2743 incr_check.root_module.linkSystemLibrary("advapi32", .{});
2744 }
2745
2702 var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true });2746 var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true });
2703 defer dir.close();2747 defer dir.close();
27042748
tools/docgen.zig+4-8
...@@ -43,8 +43,7 @@ pub fn main() !void {...@@ -43,8 +43,7 @@ pub fn main() !void {
43 while (args_it.next()) |arg| {43 while (args_it.next()) |arg| {
44 if (mem.startsWith(u8, arg, "-")) {44 if (mem.startsWith(u8, arg, "-")) {
45 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {45 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
46 const stdout = io.getStdOut().writer();46 try fs.File.stdout().writeAll(usage);
47 try stdout.writeAll(usage);
48 process.exit(0);47 process.exit(0);
49 } else if (mem.eql(u8, arg, "--code-dir")) {48 } else if (mem.eql(u8, arg, "--code-dir")) {
50 if (args_it.next()) |param| {49 if (args_it.next()) |param| {
...@@ -76,9 +75,9 @@ pub fn main() !void {...@@ -76,9 +75,9 @@ pub fn main() !void {
76 var code_dir = try fs.cwd().openDir(code_dir_path, .{});75 var code_dir = try fs.cwd().openDir(code_dir_path, .{});
77 defer code_dir.close();76 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
83 var tokenizer = Tokenizer.init(input_path, input_file_bytes);82 var tokenizer = Tokenizer.init(input_path, input_file_bytes);
84 var toc = try genToc(arena, &tokenizer);83 var toc = try genToc(arena, &tokenizer);
...@@ -426,7 +425,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {...@@ -426,7 +425,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
426 try toc.writeByte('\n');425 try toc.writeByte('\n');
427 try toc.writeByteNTimes(' ', header_stack_size * 4);426 try toc.writeByteNTimes(' ', header_stack_size * 4);
428 if (last_columns) |n| {427 if (last_columns) |n| {
429 try toc.print("<ul style=\"columns: {}\">\n", .{n});428 try toc.print("<ul style=\"columns: {d}\">\n", .{n});
430 } else {429 } else {
431 try toc.writeAll("<ul>\n");430 try toc.writeAll("<ul>\n");
432 }431 }
...@@ -710,8 +709,6 @@ fn tokenizeAndPrintRaw(...@@ -710,8 +709,6 @@ fn tokenizeAndPrintRaw(
710 .keyword_align,709 .keyword_align,
711 .keyword_and,710 .keyword_and,
712 .keyword_asm,711 .keyword_asm,
713 .keyword_async,
714 .keyword_await,
715 .keyword_break,712 .keyword_break,
716 .keyword_catch,713 .keyword_catch,
717 .keyword_comptime,714 .keyword_comptime,
...@@ -748,7 +745,6 @@ fn tokenizeAndPrintRaw(...@@ -748,7 +745,6 @@ fn tokenizeAndPrintRaw(
748 .keyword_try,745 .keyword_try,
749 .keyword_union,746 .keyword_union,
750 .keyword_unreachable,747 .keyword_unreachable,
751 .keyword_usingnamespace,
752 .keyword_var,748 .keyword_var,
753 .keyword_volatile,749 .keyword_volatile,
754 .keyword_allowzero,750 .keyword_allowzero,
tools/doctest.zig+2-5
...@@ -44,7 +44,7 @@ pub fn main() !void {...@@ -44,7 +44,7 @@ pub fn main() !void {
44 while (args_it.next()) |arg| {44 while (args_it.next()) |arg| {
45 if (mem.startsWith(u8, arg, "-")) {45 if (mem.startsWith(u8, arg, "-")) {
46 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {46 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);
48 process.exit(0);48 process.exit(0);
49 } else if (mem.eql(u8, arg, "-i")) {49 } else if (mem.eql(u8, arg, "-i")) {
50 opt_input = args_it.next() orelse fatal("expected parameter after -i", .{});50 opt_input = args_it.next() orelse fatal("expected parameter after -i", .{});
...@@ -85,7 +85,7 @@ pub fn main() !void {...@@ -85,7 +85,7 @@ pub fn main() !void {
85 var out_file = try fs.cwd().createFile(output_path, .{});85 var out_file = try fs.cwd().createFile(output_path, .{});
86 defer out_file.close();86 defer out_file.close();
8787
88 var bw = std.io.bufferedWriter(out_file.writer());88 var bw = std.io.bufferedWriter(out_file.deprecatedWriter());
89 const out = bw.writer();89 const out = bw.writer();
9090
91 try printSourceBlock(arena, out, source, fs.path.basename(input_path));91 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 {...@@ -653,8 +653,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {
653 .keyword_align,653 .keyword_align,
654 .keyword_and,654 .keyword_and,
655 .keyword_asm,655 .keyword_asm,
656 .keyword_async,
657 .keyword_await,
658 .keyword_break,656 .keyword_break,
659 .keyword_catch,657 .keyword_catch,
660 .keyword_comptime,658 .keyword_comptime,
...@@ -691,7 +689,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {...@@ -691,7 +689,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {
691 .keyword_try,689 .keyword_try,
692 .keyword_union,690 .keyword_union,
693 .keyword_unreachable,691 .keyword_unreachable,
694 .keyword_usingnamespace,
695 .keyword_var,692 .keyword_var,
696 .keyword_volatile,693 .keyword_volatile,
697 .keyword_allowzero,694 .keyword_allowzero,
tools/dump-cov.zig+4-3
...@@ -48,8 +48,9 @@ pub fn main() !void {...@@ -48,8 +48,9 @@ pub fn main() !void {
48 fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });48 fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) });
49 };49 };
5050
51 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());51 var stdout_buffer: [4000]u8 = undefined;
52 const stdout = bw.writer();52 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
53 const stdout = &stdout_writer.interface;
5354
54 const header: *SeenPcsHeader = @ptrCast(cov_bytes);55 const header: *SeenPcsHeader = @ptrCast(cov_bytes);
55 try stdout.print("{any}\n", .{header.*});56 try stdout.print("{any}\n", .{header.*});
...@@ -83,5 +84,5 @@ pub fn main() !void {...@@ -83,5 +84,5 @@ pub fn main() !void {
83 });84 });
84 }85 }
8586
86 try bw.flush();87 try stdout.flush();
87}88}
tools/fetch_them_macos_headers.zig+2-13
...@@ -5,6 +5,8 @@ const mem = std.mem;...@@ -5,6 +5,8 @@ const mem = std.mem;
5const process = std.process;5const process = std.process;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const tmpDir = std.testing.tmpDir;7const tmpDir = std.testing.tmpDir;
8const fatal = std.process.fatal;
9const info = std.log.info;
810
9const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
10const OsTag = std.Target.Os.Tag;12const OsTag = std.Target.Os.Tag;
...@@ -245,19 +247,6 @@ const ArgsIterator = struct {...@@ -245,19 +247,6 @@ const ArgsIterator = struct {
245 }247 }
246};248};
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
261const Version = struct {250const Version = struct {
262 major: u16,251 major: u16,
263 minor: u8,252 minor: u8,
tools/gen_macos_headers_c.zig+9-17
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const info = std.log.info;
4const fatal = std.process.fatal;
35
4const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
57
...@@ -13,19 +15,6 @@ const usage =...@@ -13,19 +15,6 @@ const usage =
13 \\-h, --help Print this help and exit15 \\-h, --help Print this help and exit
14;16;
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
29pub fn main() anyerror!void {18pub fn main() anyerror!void {
30 var arena_allocator = std.heap.ArenaAllocator.init(gpa);19 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
31 defer arena_allocator.deinit();20 defer arena_allocator.deinit();
...@@ -58,16 +47,19 @@ pub fn main() anyerror!void {...@@ -58,16 +47,19 @@ pub fn main() anyerror!void {
5847
59 std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan);48 std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan);
6049
61 const stdout = std.io.getStdOut().writer();50 var buffer: [2000]u8 = undefined;
62 try stdout.writeAll("#define _XOPEN_SOURCE\n");51 var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer);
52 const w = &stdout_writer.interface;
53 try w.writeAll("#define _XOPEN_SOURCE\n");
63 for (paths.items) |path| {54 for (paths.items) |path| {
64 try stdout.print("#include <{s}>\n", .{path});55 try w.print("#include <{s}>\n", .{path});
65 }56 }
66 try stdout.writeAll(57 try w.writeAll(
67 \\int main(int argc, char **argv) {58 \\int main(int argc, char **argv) {
68 \\ return 0;59 \\ return 0;
69 \\}60 \\}
70 );61 );
62 try w.flush();
71}63}
7264
73fn findHeaders(65fn findHeaders(
tools/gen_outline_atomics.zig+4-3
...@@ -17,8 +17,9 @@ pub fn main() !void {...@@ -17,8 +17,9 @@ pub fn main() !void {
1717
18 //const args = try std.process.argsAlloc(arena);18 //const args = try std.process.argsAlloc(arena);
1919
20 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());20 var stdout_buffer: [2000]u8 = undefined;
21 const w = bw.writer();21 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
22 const w = &stdout_writer.interface;
2223
23 try w.writeAll(24 try w.writeAll(
24 \\//! This file is generated by tools/gen_outline_atomics.zig.25 \\//! This file is generated by tools/gen_outline_atomics.zig.
...@@ -57,7 +58,7 @@ pub fn main() !void {...@@ -57,7 +58,7 @@ pub fn main() !void {
5758
58 try w.writeAll(footer.items);59 try w.writeAll(footer.items);
59 try w.writeAll("}\n");60 try w.writeAll("}\n");
60 try bw.flush();61 try w.flush();
61}62}
6263
63fn writeFunction(64fn writeFunction(
tools/gen_spirv_spec.zig+9-12
...@@ -91,9 +91,10 @@ pub fn main() !void {...@@ -91,9 +91,10 @@ pub fn main() !void {
9191
92 try readExtRegistry(&exts, a, std.fs.cwd(), args[2]);92 try readExtRegistry(&exts, a, std.fs.cwd(), args[2]);
9393
94 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());94 var buffer: [4000]u8 = undefined;
95 try render(bw.writer(), a, core_spec, exts.items);95 var w = std.fs.File.stdout().writerStreaming(&buffer);
96 try bw.flush();96 try render(&w, a, core_spec, exts.items);
97 try w.flush();
97}98}
9899
99fn readExtRegistry(exts: *std.ArrayList(Extension), a: Allocator, dir: std.fs.Dir, sub_path: []const u8) !void {100fn 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 {...@@ -166,7 +167,7 @@ fn tagPriorityScore(tag: []const u8) usize {
166 }167 }
167}168}
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 {
170 try writer.writeAll(171 try writer.writeAll(
171 \\//! This file is auto-generated by tools/gen_spirv_spec.zig.172 \\//! This file is auto-generated by tools/gen_spirv_spec.zig.
172 \\173 \\
...@@ -188,15 +189,10 @@ fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []c...@@ -188,15 +189,10 @@ fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []c
188 \\ none,189 \\ none,
189 \\ _,190 \\ _,
190 \\191 \\
191 \\ pub fn format(192 \\ pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void {
192 \\ self: IdResult,
193 \\ comptime _: []const u8,
194 \\ _: std.fmt.FormatOptions,
195 \\ writer: anytype,
196 \\ ) @TypeOf(writer).Error!void {
197 \\ switch (self) {193 \\ switch (self) {
198 \\ .none => try writer.writeAll("(none)"),194 \\ .none => try writer.writeAll("(none)"),
199 \\ else => try writer.print("%{}", .{@intFromEnum(self)}),195 \\ else => try writer.print("%{d}", .{@intFromEnum(self)}),
200 \\ }196 \\ }
201 \\ }197 \\ }
202 \\};198 \\};
...@@ -899,7 +895,8 @@ fn parseHexInt(text: []const u8) !u31 {...@@ -899,7 +895,8 @@ fn parseHexInt(text: []const u8) !u31 {
899}895}
900896
901fn usageAndExit(arg0: []const u8, code: u8) noreturn {897fn usageAndExit(arg0: []const u8, code: u8) noreturn {
902 std.io.getStdErr().writer().print(898 const stderr = std.debug.lockStderrWriter(&.{});
899 stderr.print(
903 \\Usage: {s} <SPIRV-Headers repository path> <path/to/zig/src/codegen/spirv/extinst.zig.grammar.json>900 \\Usage: {s} <SPIRV-Headers repository path> <path/to/zig/src/codegen/spirv/extinst.zig.grammar.json>
904 \\901 \\
905 \\Generates Zig bindings for SPIR-V specifications found in the SPIRV-Headers902 \\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 {...@@ -333,7 +333,9 @@ pub fn main() !void {
333 }333 }
334 }334 }
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;
337 try stdout.writeAll(339 try stdout.writeAll(
338 \\#ifdef PTR64340 \\#ifdef PTR64
339 \\#define WEAK64 .weak341 \\#define WEAK64 .weak
...@@ -533,6 +535,8 @@ pub fn main() !void {...@@ -533,6 +535,8 @@ pub fn main() !void {
533 .all => {},535 .all => {},
534 .single, .multi, .family, .time32 => try stdout.writeAll("#endif\n"),536 .single, .multi, .family, .time32 => try stdout.writeAll("#endif\n"),
535 }537 }
538
539 try stdout.flush();
536}540}
537541
538fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: builtin.Endian) !void {542fn 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 {...@@ -6,7 +6,9 @@ pub fn main() !void {
6 var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;6 var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
7 var allocator = gpa.allocator();7 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;
10 try output.writeAll(12 try output.writeAll(
11 \\// This file was generated by _generate_JSONTestSuite.zig13 \\// This file was generated by _generate_JSONTestSuite.zig
12 \\// These test cases are sourced from: https://github.com/nst/JSONTestSuite14 \\// These test cases are sourced from: https://github.com/nst/JSONTestSuite
...@@ -44,6 +46,8 @@ pub fn main() !void {...@@ -44,6 +46,8 @@ pub fn main() !void {
44 try writeString(output, contents);46 try writeString(output, contents);
45 try output.writeAll(");\n}\n");47 try output.writeAll(");\n}\n");
46 }48 }
49
50 try output.flush();
47}51}
4852
49const i_structure_500_nested_arrays = "[" ** 500 ++ "]" ** 500;53const 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 {...@@ -42,20 +42,23 @@ pub fn main() !void {
42 const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] });42 const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] });
43 const target = try std.zig.system.resolveTargetQuery(query);43 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;
46 inline for (@typeInfo(std.Target.CType).@"enum".fields) |field| {48 inline for (@typeInfo(std.Target.CType).@"enum".fields) |field| {
47 const c_type: std.Target.CType = @enumFromInt(field.value);49 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", .{
49 cName(c_type),51 cName(c_type),
50 target.cTypeByteSize(c_type),52 target.cTypeByteSize(c_type),
51 });53 });
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", .{
53 cName(c_type),55 cName(c_type),
54 target.cTypeAlignment(c_type),56 target.cTypeAlignment(c_type),
55 });57 });
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", .{
57 cName(c_type),59 cName(c_type),
58 target.cTypePreferredAlignment(c_type),60 target.cTypePreferredAlignment(c_type),
59 });61 });
60 }62 }
63 try w.flush();
61}64}
tools/generate_linux_syscalls.zig+11-9
...@@ -666,13 +666,16 @@ pub fn main() !void {...@@ -666,13 +666,16 @@ pub fn main() !void {
666 const allocator = arena.allocator();666 const allocator = arena.allocator();
667667
668 const args = try std.process.argsAlloc(allocator);668 const args = try std.process.argsAlloc(allocator);
669 if (args.len < 3 or mem.eql(u8, args[1], "--help"))669 if (args.len < 3 or mem.eql(u8, args[1], "--help")) {
670 usageAndExit(std.io.getStdErr(), args[0], 1);670 usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2);
671 std.process.exit(1);
672 }
671 const zig_exe = args[1];673 const zig_exe = args[1];
672 const linux_path = args[2];674 const linux_path = args[2];
673675
674 var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer());676 var stdout_buffer: [2000]u8 = undefined;
675 const writer = buf_out.writer();677 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
678 const writer = &stdout_writer.interface;
676679
677 var linux_dir = try std.fs.cwd().openDir(linux_path, .{});680 var linux_dir = try std.fs.cwd().openDir(linux_path, .{});
678 defer linux_dir.close();681 defer linux_dir.close();
...@@ -714,17 +717,16 @@ pub fn main() !void {...@@ -714,17 +717,16 @@ pub fn main() !void {
714 }717 }
715 }718 }
716719
717 try buf_out.flush();720 try writer.flush();
718}721}
719722
720fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {723fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {
721 file.writer().print(724 try w.print(
722 \\Usage: {s} /path/to/zig /path/to/linux725 \\Usage: {s} /path/to/zig /path/to/linux
723 \\Alternative Usage: zig run /path/to/git/zig/tools/generate_linux_syscalls.zig -- /path/to/zig /path/to/linux726 \\Alternative Usage: zig run /path/to/git/zig/tools/generate_linux_syscalls.zig -- /path/to/zig /path/to/linux
724 \\727 \\
725 \\Generates the list of Linux syscalls for each supported cpu arch, using the Linux development tree.728 \\Generates the list of Linux syscalls for each supported cpu arch, using the Linux development tree.
726 \\Prints to stdout Zig code which you can use to replace the file lib/std/os/linux/syscalls.zig.729 \\Prints to stdout Zig code which you can use to replace the file lib/std/os/linux/syscalls.zig.
727 \\730 \\
728 , .{arg0}) catch std.process.exit(1);731 , .{arg0});
729 std.process.exit(code);
730}732}
tools/lldb_pretty_printers.py-3
...@@ -50,8 +50,6 @@ zig_keywords = {...@@ -50,8 +50,6 @@ zig_keywords = {
50 'anyframe',50 'anyframe',
51 'anytype',51 'anytype',
52 'asm',52 'asm',
53 'async',
54 'await',
55 'break',53 'break',
56 'callconv',54 'callconv',
57 'catch',55 'catch',
...@@ -88,7 +86,6 @@ zig_keywords = {...@@ -88,7 +86,6 @@ zig_keywords = {
88 'try',86 'try',
89 'union',87 'union',
90 'unreachable',88 'unreachable',
91 'usingnamespace',
92 'var',89 'var',
93 'volatile',90 'volatile',
94 'while',91 'while',
tools/update_clang_options.zig+22-20
...@@ -634,25 +634,25 @@ pub fn main() anyerror!void {...@@ -634,25 +634,25 @@ pub fn main() anyerror!void {
634 const allocator = arena.allocator();634 const allocator = arena.allocator();
635 const args = try std.process.argsAlloc(allocator);635 const args = try std.process.argsAlloc(allocator);
636636
637 if (args.len <= 1) {637 var stdout_buffer: [4000]u8 = undefined;
638 usageAndExit(std.io.getStdErr(), args[0], 1);638 var stdout_writer = fs.stdout().writerStreaming(&stdout_buffer);
639 }639 const stdout = &stdout_writer.interface;
640
641 if (args.len <= 1) printUsageAndExit(args[0]);
642
640 if (std.mem.eql(u8, args[1], "--help")) {643 if (std.mem.eql(u8, args[1], "--help")) {
641 usageAndExit(std.io.getStdOut(), args[0], 0);644 printUsage(stdout, args[0]) catch std.process.exit(2);
642 }645 stdout.flush() catch std.process.exit(2);
643 if (args.len < 3) {646 std.process.exit(0);
644 usageAndExit(std.io.getStdErr(), args[0], 1);
645 }647 }
646648
649 if (args.len < 3) printUsageAndExit(args[0]);
650
647 const llvm_tblgen_exe = args[1];651 const llvm_tblgen_exe = args[1];
648 if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) {652 if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) printUsageAndExit(args[0]);
649 usageAndExit(std.io.getStdErr(), args[0], 1);
650 }
651653
652 const llvm_src_root = args[2];654 const llvm_src_root = args[2];
653 if (std.mem.startsWith(u8, llvm_src_root, "-")) {655 if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]);
654 usageAndExit(std.io.getStdErr(), args[0], 1);
655 }
656656
657 var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator);657 var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator);
658658
...@@ -719,8 +719,6 @@ pub fn main() anyerror!void {...@@ -719,8 +719,6 @@ pub fn main() anyerror!void {
719 // "W" and "Wl,". So we sort this list in order of descending priority.719 // "W" and "Wl,". So we sort this list in order of descending priority.
720 std.mem.sort(*json.ObjectMap, all_objects.items, {}, objectLessThan);720 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();
724 try stdout.writeAll(722 try stdout.writeAll(
725 \\// This file is generated by tools/update_clang_options.zig.723 \\// This file is generated by tools/update_clang_options.zig.
726 \\// zig fmt: off724 \\// zig fmt: off
...@@ -815,7 +813,7 @@ pub fn main() anyerror!void {...@@ -815,7 +813,7 @@ pub fn main() anyerror!void {
815 \\813 \\
816 );814 );
817815
818 try buffered_stdout.flush();816 try stdout.flush();
819}817}
820818
821// TODO we should be able to import clang_options.zig but currently this is problematic because it will819// 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 {...@@ -966,13 +964,17 @@ fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool {
966 return std.mem.lessThan(u8, a_key, b_key);964 return std.mem.lessThan(u8, a_key, b_key);
967}965}
968966
969fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {967fn printUsageAndExit(arg0: []const u8) noreturn {
970 file.writer().print(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(
971 \\Usage: {s} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project974 \\Usage: {s} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
972 \\Alternative Usage: zig run /path/to/git/zig/tools/update_clang_options.zig -- /path/to/llvm-tblgen /path/to/git/llvm/llvm-project975 \\Alternative Usage: zig run /path/to/git/zig/tools/update_clang_options.zig -- /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
973 \\976 \\
974 \\Prints to stdout Zig code which you can use to replace the file src/clang_options_data.zig.977 \\Prints to stdout Zig code which you can use to replace the file src/clang_options_data.zig.
975 \\978 \\
976 , .{arg0}) catch std.process.exit(1);979 , .{arg0});
977 std.process.exit(code);
978}980}
tools/update_cpu_features.zig+2-2
...@@ -2082,8 +2082,8 @@ fn processOneTarget(job: Job) void {...@@ -2082,8 +2082,8 @@ fn processOneTarget(job: Job) void {
2082}2082}
20832083
2084fn usageAndExit(arg0: []const u8, code: u8) noreturn {2084fn usageAndExit(arg0: []const u8, code: u8) noreturn {
2085 const stderr = std.io.getStdErr();2085 const stderr = std.debug.lockStderrWriter(&.{});
2086 stderr.writer().print(2086 stderr.print(
2087 \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig [zig_name filter]2087 \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig [zig_name filter]
2088 \\2088 \\
2089 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .2089 \\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 {...@@ -11,14 +11,10 @@ pub fn main() anyerror!void {
11 const arena = arena_state.allocator();11 const arena = arena_state.allocator();
1212
13 const args = try std.process.argsAlloc(arena);13 const args = try std.process.argsAlloc(arena);
14 if (args.len <= 1) {14 if (args.len <= 1) printUsageAndExit(args[0]);
15 usageAndExit(std.io.getStdErr(), args[0], 1);
16 }
1715
18 const zig_src_root = args[1];16 const zig_src_root = args[1];
19 if (mem.startsWith(u8, zig_src_root, "-")) {17 if (mem.startsWith(u8, zig_src_root, "-")) printUsageAndExit(args[0]);
20 usageAndExit(std.io.getStdErr(), args[0], 1);
21 }
2218
23 var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{});19 var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{});
24 defer zig_src_dir.close();20 defer zig_src_dir.close();
...@@ -193,10 +189,14 @@ pub fn main() anyerror!void {...@@ -193,10 +189,14 @@ pub fn main() anyerror!void {
193 }189 }
194}190}
195191
196fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {192fn printUsageAndExit(arg0: []const u8) noreturn {
197 file.writer().print(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(
198 \\Usage: {s} /path/git/zig199 \\Usage: {s} /path/git/zig
199 \\200 \\
200 , .{arg0}) catch std.process.exit(1);201 , .{arg0});
201 std.process.exit(code);
202}202}