| author | |
| committer | |
| log | 6e6c68d88909f008afc706949effe38c470a055d |
| tree | ed4175263a31adf21dffe28af15717f5b54c2950 |
| parent | 725dbf295e82624477aa4d95ba71ec02d9c94978 |
| parent | 43fba5ea83849ec901bb2cd4f98bd0222f51f7f6 |
368 files changed, 7860 insertions(+), 29114 deletions(-)
CMakeLists.txt-39| ... | ... | @@ -537,45 +537,6 @@ set(ZIG_STAGE2_SOURCES |
| 537 | 537 | src/Value.zig |
| 538 | 538 | src/Zcu.zig |
| 539 | 539 | 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 | 540 | src/clang.zig |
| 580 | 541 | src/clang_options.zig |
| 581 | 542 | src/clang_options_data.zig |
build.zig+34-3| ... | ... | @@ -415,7 +415,18 @@ pub fn build(b: *std.Build) !void { |
| 415 | 415 | test_step.dependOn(check_fmt); |
| 416 | 416 | |
| 417 | 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 | 430 | .skip_translate_c = skip_translate_c, |
| 420 | 431 | .skip_run_translated_c = skip_run_translated_c, |
| 421 | 432 | }, .{ |
| ... | ... | @@ -439,6 +450,7 @@ pub fn build(b: *std.Build) !void { |
| 439 | 450 | .desc = "Run the behavior tests", |
| 440 | 451 | .optimize_modes = optimization_modes, |
| 441 | 452 | .include_paths = &.{}, |
| 453 | .windows_libs = &.{}, | |
| 442 | 454 | .skip_single_threaded = skip_single_threaded, |
| 443 | 455 | .skip_non_native = skip_non_native, |
| 444 | 456 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -448,8 +460,8 @@ pub fn build(b: *std.Build) !void { |
| 448 | 460 | .skip_linux = skip_linux, |
| 449 | 461 | .skip_llvm = skip_llvm, |
| 450 | 462 | .skip_libc = skip_libc, |
| 451 | // 2923515904 was observed on an x86_64-linux-gnu host. | |
| 452 | .max_rss = 3100000000, | |
| 463 | // 3888779264 was observed on an x86_64-linux-gnu host. | |
| 464 | .max_rss = 4000000000, | |
| 453 | 465 | })); |
| 454 | 466 | |
| 455 | 467 | test_modules_step.dependOn(tests.addModuleTests(b, .{ |
| ... | ... | @@ -461,6 +473,7 @@ pub fn build(b: *std.Build) !void { |
| 461 | 473 | .desc = "Run the @cImport tests", |
| 462 | 474 | .optimize_modes = optimization_modes, |
| 463 | 475 | .include_paths = &.{"test/c_import"}, |
| 476 | .windows_libs = &.{}, | |
| 464 | 477 | .skip_single_threaded = true, |
| 465 | 478 | .skip_non_native = skip_non_native, |
| 466 | 479 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -481,6 +494,7 @@ pub fn build(b: *std.Build) !void { |
| 481 | 494 | .desc = "Run the compiler_rt tests", |
| 482 | 495 | .optimize_modes = optimization_modes, |
| 483 | 496 | .include_paths = &.{}, |
| 497 | .windows_libs = &.{}, | |
| 484 | 498 | .skip_single_threaded = true, |
| 485 | 499 | .skip_non_native = skip_non_native, |
| 486 | 500 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -502,6 +516,7 @@ pub fn build(b: *std.Build) !void { |
| 502 | 516 | .desc = "Run the zigc tests", |
| 503 | 517 | .optimize_modes = optimization_modes, |
| 504 | 518 | .include_paths = &.{}, |
| 519 | .windows_libs = &.{}, | |
| 505 | 520 | .skip_single_threaded = true, |
| 506 | 521 | .skip_non_native = skip_non_native, |
| 507 | 522 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -523,6 +538,12 @@ pub fn build(b: *std.Build) !void { |
| 523 | 538 | .desc = "Run the standard library tests", |
| 524 | 539 | .optimize_modes = optimization_modes, |
| 525 | 540 | .include_paths = &.{}, |
| 541 | .windows_libs = &.{ | |
| 542 | "advapi32", | |
| 543 | "crypt32", | |
| 544 | "iphlpapi", | |
| 545 | "ws2_32", | |
| 546 | }, | |
| 526 | 547 | .skip_single_threaded = skip_single_threaded, |
| 527 | 548 | .skip_non_native = skip_non_native, |
| 528 | 549 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -720,6 +741,12 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu |
| 720 | 741 | compiler_mod.addImport("aro", aro_mod); |
| 721 | 742 | compiler_mod.addImport("aro_translate_c", aro_translate_c_mod); |
| 722 | 743 | |
| 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 | 750 | return compiler_mod; |
| 724 | 751 | } |
| 725 | 752 | |
| ... | ... | @@ -1417,6 +1444,10 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { |
| 1417 | 1444 | }), |
| 1418 | 1445 | }); |
| 1419 | 1446 | |
| 1447 | if (b.graph.host.result.os.tag == .windows) { | |
| 1448 | doctest_exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 1449 | } | |
| 1450 | ||
| 1420 | 1451 | var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| { |
| 1421 | 1452 | std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{ |
| 1422 | 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 | 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" |
| 13 | 13 | ZIG="$PREFIX/bin/zig" |
| 14 | 14 | |
| 15 | export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH" | |
| 15 | export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH" | |
| 16 | 16 | |
| 17 | 17 | # Make the `zig version` number consistent. |
| 18 | 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 | 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" |
| 13 | 13 | ZIG="$PREFIX/bin/zig" |
| 14 | 14 | |
| 15 | export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH" | |
| 15 | export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH" | |
| 16 | 16 | |
| 17 | 17 | # Make the `zig version` number consistent. |
| 18 | 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 | 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" |
| 13 | 13 | ZIG="$PREFIX/bin/zig" |
| 14 | 14 | |
| 15 | export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH" | |
| 15 | export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-10.0.2/bin:$HOME/local/bin:$PATH" | |
| 16 | 16 | |
| 17 | 17 | # Make the `zig version` number consistent. |
| 18 | 18 | # This will affect the cmake command below. |
doc/langref.html.in+10-88| ... | ... | @@ -374,7 +374,8 @@ |
| 374 | 374 | <p> |
| 375 | 375 | Most of the time, it is more appropriate to write to stderr rather than stdout, and |
| 376 | 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 | 379 | </p> |
| 379 | 380 | {#code|hello_again.zig#} |
| 380 | 381 | |
| ... | ... | @@ -3842,37 +3843,6 @@ void do_a_thing(struct Foo *foo) { |
| 3842 | 3843 | {#header_close#} |
| 3843 | 3844 | {#header_close#} |
| 3844 | 3845 | |
| 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#} | |
| 3859 | pub 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 | 3846 | {#header_open|comptime#} |
| 3877 | 3847 | <p> |
| 3878 | 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 | 4249 | {#header_close#} |
| 4280 | 4250 | |
| 4281 | 4251 | {#header_open|Async Functions#} |
| 4282 | <p>Async functions regressed with the release of 0.11.0. Their future in | |
| 4283 | the Zig language is unclear due to multiple unsolved problems:</p> | |
| 4284 | <ul> | |
| 4285 | <li>LLVM's lack of ability to optimize them.</li> | |
| 4286 | <li>Third-party debuggers' lack of ability to debug them.</li> | |
| 4287 | <li><a href="https://github.com/ziglang/zig/issues/5913">The cancellation problem</a>.</li> | |
| 4288 | <li>Async function pointers preventing the stack size from being known.</li> | |
| 4289 | </ul> | |
| 4290 | <p>These problems are surmountable, but it will take time. The Zig team | |
| 4291 | is currently focused on other priorities.</p> | |
| 4252 | <p>Async functions regressed with the release of 0.11.0. The current plan is to | |
| 4253 | reintroduce them as a lower level primitive that powers I/O implementations.</p> | |
| 4254 | <p>Tracking issue: <a href="https://github.com/ziglang/zig/issues/23446">Proposal: stackless coroutines as low-level primitives</a></p> | |
| 4292 | 4255 | {#header_close#} |
| 4293 | 4256 | |
| 4294 | 4257 | {#header_open|Builtin Functions|2col#} |
| ... | ... | @@ -6552,7 +6515,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 6552 | 6515 | </p> |
| 6553 | 6516 | <ul> |
| 6554 | 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 | 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 | 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 | 6521 | </ul> |
| ... | ... | @@ -7372,29 +7335,6 @@ fn readU32Be() u32 {} |
| 7372 | 7335 | </ul> |
| 7373 | 7336 | </td> |
| 7374 | 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 | 7338 | <tr> |
| 7399 | 7339 | <th scope="row"> |
| 7400 | 7340 | <pre>{#syntax#}break{#endsyntax#}</pre> |
| ... | ... | @@ -7812,18 +7752,6 @@ fn readU32Be() u32 {} |
| 7812 | 7752 | </ul> |
| 7813 | 7753 | </td> |
| 7814 | 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 | 7755 | <tr> |
| 7828 | 7756 | <th scope="row"> |
| 7829 | 7757 | <pre>{#syntax#}var{#endsyntax#}</pre> |
| ... | ... | @@ -7893,7 +7821,6 @@ ComptimeDecl <- KEYWORD_comptime Block |
| 7893 | 7821 | Decl |
| 7894 | 7822 | <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block) |
| 7895 | 7823 | / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl |
| 7896 | / KEYWORD_usingnamespace Expr SEMICOLON | |
| 7897 | 7824 | |
| 7898 | 7825 | FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr |
| 7899 | 7826 | |
| ... | ... | @@ -8006,8 +7933,7 @@ TypeExpr <- PrefixTypeOp* ErrorUnionExpr |
| 8006 | 7933 | ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? |
| 8007 | 7934 | |
| 8008 | 7935 | SuffixExpr |
| 8009 | <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments | |
| 8010 | / PrimaryTypeExpr (SuffixOp / FnCallArguments)* | |
| 7936 | <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* | |
| 8011 | 7937 | |
| 8012 | 7938 | PrimaryTypeExpr |
| 8013 | 7939 | <- BUILTINIDENTIFIER FnCallArguments |
| ... | ... | @@ -8183,7 +8109,6 @@ PrefixOp |
| 8183 | 8109 | / MINUSPERCENT |
| 8184 | 8110 | / AMPERSAND |
| 8185 | 8111 | / KEYWORD_try |
| 8186 | / KEYWORD_await | |
| 8187 | 8112 | |
| 8188 | 8113 | PrefixTypeOp |
| 8189 | 8114 | <- QUESTIONMARK |
| ... | ... | @@ -8404,8 +8329,6 @@ KEYWORD_and <- 'and' end_of_word |
| 8404 | 8329 | KEYWORD_anyframe <- 'anyframe' end_of_word |
| 8405 | 8330 | KEYWORD_anytype <- 'anytype' end_of_word |
| 8406 | 8331 | KEYWORD_asm <- 'asm' end_of_word |
| 8407 | KEYWORD_async <- 'async' end_of_word | |
| 8408 | KEYWORD_await <- 'await' end_of_word | |
| 8409 | 8332 | KEYWORD_break <- 'break' end_of_word |
| 8410 | 8333 | KEYWORD_callconv <- 'callconv' end_of_word |
| 8411 | 8334 | KEYWORD_catch <- 'catch' end_of_word |
| ... | ... | @@ -8442,14 +8365,13 @@ KEYWORD_threadlocal <- 'threadlocal' end_of_word |
| 8442 | 8365 | KEYWORD_try <- 'try' end_of_word |
| 8443 | 8366 | KEYWORD_union <- 'union' end_of_word |
| 8444 | 8367 | KEYWORD_unreachable <- 'unreachable' end_of_word |
| 8445 | KEYWORD_usingnamespace <- 'usingnamespace' end_of_word | |
| 8446 | 8368 | KEYWORD_var <- 'var' end_of_word |
| 8447 | 8369 | KEYWORD_volatile <- 'volatile' end_of_word |
| 8448 | 8370 | KEYWORD_while <- 'while' end_of_word |
| 8449 | 8371 | |
| 8450 | 8372 | keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and |
| 8451 | / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async | |
| 8452 | / KEYWORD_await / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch | |
| 8373 | / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm | |
| 8374 | / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch | |
| 8453 | 8375 | / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer |
| 8454 | 8376 | / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export |
| 8455 | 8377 | / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if |
| ... | ... | @@ -8458,7 +8380,7 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and |
| 8458 | 8380 | / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection |
| 8459 | 8381 | / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test |
| 8460 | 8382 | / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable |
| 8461 | / KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while | |
| 8383 | / KEYWORD_var / KEYWORD_volatile / KEYWORD_while | |
| 8462 | 8384 | {#end_syntax_block#} |
| 8463 | 8385 | {#header_close#} |
| 8464 | 8386 | {#header_open|Zen#} |
doc/langref/bad_default_value.zig+1-1| ... | ... | @@ -17,7 +17,7 @@ pub fn main() !void { |
| 17 | 17 | .maximum = 0.20, |
| 18 | 18 | }; |
| 19 | 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 | } |
| 22 | 22 | |
| 23 | 23 | const std = @import("std"); |
doc/langref/hello.zig+1-2| ... | ... | @@ -1,8 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn main() !void { |
| 4 | const stdout = std.io.getStdOut().writer(); | |
| 5 | try stdout.print("Hello, {s}!\n", .{"world"}); | |
| 4 | try std.fs.File.stdout().writeAll("Hello, World!\n"); | |
| 6 | 5 | } |
| 7 | 6 | |
| 8 | 7 | // exe=succeed |
doc/langref/hello_again.zig+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn main() void { |
| 4 | std.debug.print("Hello, world!\n", .{}); | |
| 4 | std.debug.print("Hello, {s}!\n", .{"World"}); | |
| 5 | 5 | } |
| 6 | 6 | |
| 7 | 7 | // exe=succeed |
doc/langref/test_usingnamespace.zig deleted-8| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | test "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 | 1432 | defer buf.deinit(); |
| 1433 | 1433 | |
| 1434 | 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 | 1436 | error.StreamTooLong => if (limit == null) return e, |
| 1437 | 1437 | else => return e, |
| 1438 | 1438 | }; |
lib/compiler/aro/aro/Diagnostics.zig+17-27| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | |
| 2 | 3 | const Allocator = mem.Allocator; |
| 3 | 4 | const mem = std.mem; |
| 4 | 5 | const Source = @import("Source.zig"); |
| ... | ... | @@ -323,12 +324,13 @@ pub fn addExtra( |
| 323 | 324 | |
| 324 | 325 | pub fn render(comp: *Compilation, config: std.io.tty.Config) void { |
| 325 | 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 | 329 | defer m.deinit(); |
| 328 | 330 | renderMessages(comp, &m); |
| 329 | 331 | } |
| 330 | pub fn defaultMsgWriter(config: std.io.tty.Config) MsgWriter { | |
| 331 | return MsgWriter.init(config); | |
| 332 | pub fn defaultMsgWriter(config: std.io.tty.Config, buffer: []u8) MsgWriter { | |
| 333 | return MsgWriter.init(config, buffer); | |
| 332 | 334 | } |
| 333 | 335 | |
| 334 | 336 | pub fn renderMessages(comp: *Compilation, m: anytype) void { |
| ... | ... | @@ -449,12 +451,7 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { |
| 449 | 451 | }, |
| 450 | 452 | .normalized => { |
| 451 | 453 | const f = struct { |
| 452 | pub fn f( | |
| 453 | bytes: []const u8, | |
| 454 | comptime _: []const u8, | |
| 455 | _: std.fmt.FormatOptions, | |
| 456 | writer: anytype, | |
| 457 | ) !void { | |
| 454 | pub fn f(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 458 | 455 | var it: std.unicode.Utf8Iterator = .{ |
| 459 | 456 | .bytes = bytes, |
| 460 | 457 | .i = 0, |
| ... | ... | @@ -464,22 +461,16 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { |
| 464 | 461 | try writer.writeByte(@intCast(codepoint)); |
| 465 | 462 | } else if (codepoint < 0xFFFF) { |
| 466 | 463 | try writer.writeAll("\\u"); |
| 467 | try std.fmt.formatInt(codepoint, 16, .upper, .{ | |
| 468 | .fill = '0', | |
| 469 | .width = 4, | |
| 470 | }, writer); | |
| 464 | try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 4 }); | |
| 471 | 465 | } else { |
| 472 | 466 | try writer.writeAll("\\U"); |
| 473 | try std.fmt.formatInt(codepoint, 16, .upper, .{ | |
| 474 | .fill = '0', | |
| 475 | .width = 8, | |
| 476 | }, writer); | |
| 467 | try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 8 }); | |
| 477 | 468 | } |
| 478 | 469 | } |
| 479 | 470 | } |
| 480 | 471 | }.f; |
| 481 | printRt(m, prop.msg, .{"{s}"}, .{ | |
| 482 | std.fmt.Formatter(f){ .data = msg.extra.normalized }, | |
| 472 | printRt(m, prop.msg, .{"{f}"}, .{ | |
| 473 | std.fmt.Formatter([]const u8, f){ .data = msg.extra.normalized }, | |
| 483 | 474 | }); |
| 484 | 475 | }, |
| 485 | 476 | .none, .offset => m.write(prop.msg), |
| ... | ... | @@ -535,32 +526,31 @@ fn tagKind(d: *Diagnostics, tag: Tag, langopts: LangOpts) Kind { |
| 535 | 526 | } |
| 536 | 527 | |
| 537 | 528 | const MsgWriter = struct { |
| 538 | w: *std.fs.File.Writer, | |
| 529 | writer: *std.io.Writer, | |
| 539 | 530 | config: std.io.tty.Config, |
| 540 | 531 | |
| 541 | 532 | fn init(config: std.io.tty.Config, buffer: []u8) MsgWriter { |
| 542 | std.debug.lockStdErr(); | |
| 543 | 533 | return .{ |
| 544 | .w = std.fs.stderr().writer(buffer), | |
| 534 | .writer = std.debug.lockStderrWriter(buffer), | |
| 545 | 535 | .config = config, |
| 546 | 536 | }; |
| 547 | 537 | } |
| 548 | 538 | |
| 549 | 539 | pub fn deinit(m: *MsgWriter) void { |
| 550 | m.w.flush() catch {}; | |
| 551 | std.debug.unlockStdErr(); | |
| 540 | std.debug.unlockStderrWriter(); | |
| 541 | m.* = undefined; | |
| 552 | 542 | } |
| 553 | 543 | |
| 554 | 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 | } |
| 557 | 547 | |
| 558 | 548 | fn write(m: *MsgWriter, msg: []const u8) void { |
| 559 | m.w.writer().writeAll(msg) catch {}; | |
| 549 | m.writer.writeAll(msg) catch {}; | |
| 560 | 550 | } |
| 561 | 551 | |
| 562 | 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 | } |
| 565 | 555 | |
| 566 | 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 | 519 | |
| 520 | 520 | fn addSource(d: *Driver, path: []const u8) !Source { |
| 521 | 521 | if (mem.eql(u8, "-", path)) { |
| 522 | const stdin = std.io.getStdIn().reader(); | |
| 522 | const stdin = std.fs.File.stdin().deprecatedReader(); | |
| 523 | 523 | const input = try stdin.readAllAlloc(d.comp.gpa, std.math.maxInt(u32)); |
| 524 | 524 | defer d.comp.gpa.free(input); |
| 525 | 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 | 541 | } |
| 542 | 542 | |
| 543 | 543 | pub 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 | } |
| 546 | 546 | |
| 547 | 547 | pub 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 | 591 | var macro_buf = std.ArrayList(u8).init(d.comp.gpa); |
| 592 | 592 | defer macro_buf.deinit(); |
| 593 | 593 | |
| 594 | const std_out = std.io.getStdOut().writer(); | |
| 594 | const std_out = std.fs.File.stdout().deprecatedWriter(); | |
| 595 | 595 | if (try parseArgs(d, std_out, macro_buf.writer(), args)) return; |
| 596 | 596 | |
| 597 | 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 | 686 | std.fs.cwd().createFile(some, .{}) catch |er| |
| 687 | 687 | return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) }) |
| 688 | 688 | else |
| 689 | std.io.getStdOut(); | |
| 689 | std.fs.File.stdout(); | |
| 690 | 690 | defer if (d.output_name != null) file.close(); |
| 691 | 691 | |
| 692 | var buf_w = std.io.bufferedWriter(file.writer()); | |
| 692 | var buf_w = std.io.bufferedWriter(file.deprecatedWriter()); | |
| 693 | 693 | |
| 694 | 694 | pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er| |
| 695 | 695 | return d.fatal("unable to write result: {s}", .{errorDescription(er)}); |
| ... | ... | @@ -704,8 +704,8 @@ fn processSource( |
| 704 | 704 | defer tree.deinit(); |
| 705 | 705 | |
| 706 | 706 | if (d.verbose_ast) { |
| 707 | const stdout = std.io.getStdOut(); | |
| 708 | var buf_writer = std.io.bufferedWriter(stdout.writer()); | |
| 707 | const stdout = std.fs.File.stdout(); | |
| 708 | var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter()); | |
| 709 | 709 | tree.dump(d.detectConfig(stdout), buf_writer.writer()) catch {}; |
| 710 | 710 | buf_writer.flush() catch {}; |
| 711 | 711 | } |
| ... | ... | @@ -734,8 +734,8 @@ fn processSource( |
| 734 | 734 | defer ir.deinit(d.comp.gpa); |
| 735 | 735 | |
| 736 | 736 | if (d.verbose_ir) { |
| 737 | const stdout = std.io.getStdOut(); | |
| 738 | var buf_writer = std.io.bufferedWriter(stdout.writer()); | |
| 737 | const stdout = std.fs.File.stdout(); | |
| 738 | var buf_writer = std.io.bufferedWriter(stdout.deprecatedWriter()); | |
| 739 | 739 | ir.dump(d.comp.gpa, d.detectConfig(stdout), buf_writer.writer()) catch {}; |
| 740 | 740 | buf_writer.flush() catch {}; |
| 741 | 741 | } |
| ... | ... | @@ -806,10 +806,10 @@ fn processSource( |
| 806 | 806 | } |
| 807 | 807 | |
| 808 | 808 | fn dumpLinkerArgs(items: []const []const u8) !void { |
| 809 | const stdout = std.io.getStdOut().writer(); | |
| 809 | const stdout = std.fs.File.stdout().deprecatedWriter(); | |
| 810 | 810 | for (items, 0..) |item, i| { |
| 811 | 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 | 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 | 500 | |
| 501 | 501 | const w = p.strings.writer(); |
| 502 | 502 | const msg_str = p.comp.interner.get(@"error".msg.ref()).bytes; |
| 503 | try w.print("call to '{s}' declared with attribute error: {}", .{ | |
| 504 | p.tokSlice(@"error".__name_tok), std.zig.fmtEscapes(msg_str), | |
| 503 | try w.print("call to '{s}' declared with attribute error: {f}", .{ | |
| 504 | p.tokSlice(@"error".__name_tok), std.zig.fmtString(msg_str), | |
| 505 | 505 | }); |
| 506 | 506 | const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); |
| 507 | 507 | try p.errStr(.error_attribute, usage_tok, str); |
| ... | ... | @@ -512,8 +512,8 @@ fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_ |
| 512 | 512 | |
| 513 | 513 | const w = p.strings.writer(); |
| 514 | 514 | const msg_str = p.comp.interner.get(warning.msg.ref()).bytes; |
| 515 | try w.print("call to '{s}' declared with attribute warning: {}", .{ | |
| 516 | p.tokSlice(warning.__name_tok), std.zig.fmtEscapes(msg_str), | |
| 515 | try w.print("call to '{s}' declared with attribute warning: {f}", .{ | |
| 516 | p.tokSlice(warning.__name_tok), std.zig.fmtString(msg_str), | |
| 517 | 517 | }); |
| 518 | 518 | const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); |
| 519 | 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 | 542 | try w.writeAll(reason); |
| 543 | 543 | if (msg) |m| { |
| 544 | 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 | 547 | const str = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); |
| 548 | 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 | 811 | const source = pp.comp.getSource(raw.source); |
| 812 | 812 | const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start }); |
| 813 | 813 | |
| 814 | const stderr = std.io.getStdErr().writer(); | |
| 814 | const stderr = std.fs.File.stderr().deprecatedWriter(); | |
| 815 | 815 | var buf_writer = std.io.bufferedWriter(stderr); |
| 816 | 816 | const writer = buf_writer.writer(); |
| 817 | 817 | defer buf_writer.flush() catch {}; |
| ... | ... | @@ -3262,7 +3262,8 @@ fn printLinemarker( |
| 3262 | 3262 | // containing the same bytes as the input regardless of encoding. |
| 3263 | 3263 | else => { |
| 3264 | 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 | 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 | 961 | switch (key) { |
| 962 | 962 | .null => return w.writeAll("nullptr_t"), |
| 963 | 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 | 966 | .float => |repr| switch (repr) { |
| 967 | 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 | 982 | const without_null = bytes[0 .. bytes.len - @intFromEnum(size)]; |
| 983 | 983 | try w.writeByte('"'); |
| 984 | 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 | 986 | .@"2" => { |
| 987 | 987 | var items: [2]u16 = undefined; |
| 988 | 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 | 171 | /// strtab |
| 172 | 172 | /// section headers |
| 173 | 173 | pub 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 | 175 | const w = buf_writer.writer(); |
| 176 | 176 | |
| 177 | 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 | 1781 | fn renderErrorsAndExit(comp: *aro.Compilation) noreturn { |
| 1782 | 1782 | defer std.process.exit(1); |
| 1783 | 1783 | |
| 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 | 1786 | defer writer.deinit(); // writer deinit must run *before* exit so that stderr is flushed |
| 1786 | 1787 | |
| 1787 | 1788 | var saw_error = false; |
| ... | ... | @@ -1824,6 +1825,6 @@ pub fn main() !void { |
| 1824 | 1825 | defer tree.deinit(gpa); |
| 1825 | 1826 | |
| 1826 | 1827 | const formatted = try tree.render(arena); |
| 1827 | try std.io.getStdOut().writeAll(formatted); | |
| 1828 | try std.fs.File.stdout().writeAll(formatted); | |
| 1828 | 1829 | return std.process.cleanExit(); |
| 1829 | 1830 | } |
lib/compiler/aro_translate_c/ast.zig+6-6| ... | ... | @@ -849,7 +849,7 @@ const Context = struct { |
| 849 | 849 | fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { |
| 850 | 850 | if (std.zig.primitives.isPrimitive(bytes)) |
| 851 | 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 | } |
| 854 | 854 | |
| 855 | 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 | 1201 | |
| 1202 | 1202 | const compile_error_tok = try c.addToken(.builtin, "@compileError"); |
| 1203 | 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 | 1205 | const err_msg = try c.addNode(.{ |
| 1206 | 1206 | .tag = .string_literal, |
| 1207 | 1207 | .main_token = err_msg_tok, |
| ... | ... | @@ -2116,7 +2116,7 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex { |
| 2116 | 2116 | defer c.gpa.free(members); |
| 2117 | 2117 | |
| 2118 | 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 | 2120 | _ = try c.addToken(.colon, ":"); |
| 2121 | 2121 | const type_expr = try renderNode(c, field.type); |
| 2122 | 2122 | |
| ... | ... | @@ -2205,7 +2205,7 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI |
| 2205 | 2205 | .main_token = try c.addToken(.period, "."), |
| 2206 | 2206 | .data = .{ .node_and_token = .{ |
| 2207 | 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 | 2681 | _ = try c.addToken(.l_paren, "("); |
| 2682 | 2682 | const res = try c.addNode(.{ |
| 2683 | 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 | 2685 | .data = undefined, |
| 2686 | 2686 | }); |
| 2687 | 2687 | _ = try c.addToken(.r_paren, ")"); |
| ... | ... | @@ -2765,7 +2765,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { |
| 2765 | 2765 | _ = try c.addToken(.l_paren, "("); |
| 2766 | 2766 | const res = try c.addNode(.{ |
| 2767 | 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 | 2769 | .data = undefined, |
| 2770 | 2770 | }); |
| 2771 | 2771 | _ = try c.addToken(.r_paren, ")"); |
lib/compiler/build_runner.zig+3-1| ... | ... | @@ -255,7 +255,7 @@ pub fn main() !void { |
| 255 | 255 | builder.verbose_llvm_ir = "-"; |
| 256 | 256 | } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) { |
| 257 | 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 | 259 | builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..]; |
| 260 | 260 | } else if (mem.eql(u8, arg, "--verbose-cimport")) { |
| 261 | 261 | builder.verbose_cimport = true; |
| ... | ... | @@ -719,6 +719,8 @@ fn runStepNames( |
| 719 | 719 | if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {}; |
| 720 | 720 | if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {}; |
| 721 | 721 | |
| 722 | w.writeAll("\n") catch {}; | |
| 723 | ||
| 722 | 724 | // Print a fancy tree with build results. |
| 723 | 725 | var step_stack_copy = try step_stack.clone(gpa); |
| 724 | 726 | defer step_stack_copy.deinit(gpa); |
lib/compiler/libc.zig+3-3| ... | ... | @@ -40,7 +40,7 @@ pub fn main() !void { |
| 40 | 40 | const arg = args[i]; |
| 41 | 41 | if (mem.startsWith(u8, arg, "-")) { |
| 42 | 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 | 44 | try stdout.writeAll(usage_libc); |
| 45 | 45 | return std.process.cleanExit(); |
| 46 | 46 | } else if (mem.eql(u8, arg, "-target")) { |
| ... | ... | @@ -97,7 +97,7 @@ pub fn main() !void { |
| 97 | 97 | fatal("no include dirs detected for target {s}", .{zig_target}); |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 100 | var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter()); | |
| 101 | 101 | var writer = bw.writer(); |
| 102 | 102 | for (libc_dirs.libc_include_dir_list) |include_dir| { |
| 103 | 103 | try writer.writeAll(include_dir); |
| ... | ... | @@ -125,7 +125,7 @@ pub fn main() !void { |
| 125 | 125 | }; |
| 126 | 126 | defer libc.deinit(gpa); |
| 127 | 127 | |
| 128 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 128 | var bw = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter()); | |
| 129 | 129 | try libc.render(bw.writer()); |
| 130 | 130 | try bw.flush(); |
| 131 | 131 | } |
lib/compiler/objcopy.zig+6-6| ... | ... | @@ -54,7 +54,7 @@ fn cmdObjCopy( |
| 54 | 54 | fatal("unexpected positional argument: '{s}'", .{arg}); |
| 55 | 55 | } |
| 56 | 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 | 58 | } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) { |
| 59 | 59 | i += 1; |
| 60 | 60 | if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); |
| ... | ... | @@ -227,8 +227,8 @@ fn cmdObjCopy( |
| 227 | 227 | if (listen) { |
| 228 | 228 | var server = try Server.init(.{ |
| 229 | 229 | .gpa = gpa, |
| 230 | .in = std.io.getStdIn(), | |
| 231 | .out = std.io.getStdOut(), | |
| 230 | .in = .stdin(), | |
| 231 | .out = .stdout(), | |
| 232 | 232 | .zig_version = builtin.zig_version_string, |
| 233 | 233 | }); |
| 234 | 234 | defer server.deinit(); |
| ... | ... | @@ -635,11 +635,11 @@ const HexWriter = struct { |
| 635 | 635 | const payload_bytes = self.getPayloadBytes(); |
| 636 | 636 | assert(payload_bytes.len <= MAX_PAYLOAD_LEN); |
| 637 | 637 | |
| 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 | 639 | @as(u8, @intCast(payload_bytes.len)), |
| 640 | 640 | self.address, |
| 641 | 641 | @intFromEnum(self.payload), |
| 642 | std.fmt.fmtSliceHexUpper(payload_bytes), | |
| 642 | payload_bytes, | |
| 643 | 643 | self.checksum(), |
| 644 | 644 | }); |
| 645 | 645 | try file.writeAll(line); |
| ... | ... | @@ -1495,7 +1495,7 @@ const ElfFileHelper = struct { |
| 1495 | 1495 | if (size < prefix.len) return null; |
| 1496 | 1496 | |
| 1497 | 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); | |
| 1499 | 1499 | |
| 1500 | 1500 | // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed. |
| 1501 | 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 | 68 | const arg = args[i]; |
| 69 | 69 | if (mem.startsWith(u8, arg, "-")) { |
| 70 | 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 | 72 | try stdout.writeAll(usage); |
| 73 | 73 | return std.process.cleanExit(); |
| 74 | 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 | 160 | try walkExpression(w, decl); |
| 161 | 161 | }, |
| 162 | 162 | |
| 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 | 163 | .global_var_decl, |
| 170 | 164 | .local_var_decl, |
| 171 | 165 | .simple_var_decl, |
| ... | ... | @@ -335,7 +329,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 335 | 329 | .address_of, |
| 336 | 330 | .@"try", |
| 337 | 331 | .@"resume", |
| 338 | .@"await", | |
| 339 | 332 | .deref, |
| 340 | 333 | => { |
| 341 | 334 | return walkExpression(w, ast.nodeData(node).node); |
| ... | ... | @@ -379,12 +372,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 379 | 372 | |
| 380 | 373 | .call_one, |
| 381 | 374 | .call_one_comma, |
| 382 | .async_call_one, | |
| 383 | .async_call_one_comma, | |
| 384 | 375 | .call, |
| 385 | 376 | .call_comma, |
| 386 | .async_call, | |
| 387 | .async_call_comma, | |
| 388 | 377 | => { |
| 389 | 378 | var buf: [1]Ast.Node.Index = undefined; |
| 390 | 379 | return walkCall(w, ast.fullCall(&buf, node).?); |
| ... | ... | @@ -525,7 +514,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 525 | 514 | .local_var_decl => unreachable, |
| 526 | 515 | .simple_var_decl => unreachable, |
| 527 | 516 | .aligned_var_decl => unreachable, |
| 528 | .@"usingnamespace" => unreachable, | |
| 529 | 517 | .test_decl => unreachable, |
| 530 | 518 | .asm_output => unreachable, |
| 531 | 519 | .asm_input => unreachable, |
lib/compiler/resinator/cli.zig+13-14| ... | ... | @@ -125,13 +125,12 @@ pub const Diagnostics = struct { |
| 125 | 125 | } |
| 126 | 126 | |
| 127 | 127 | pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void { |
| 128 | std.debug.lockStdErr(); | |
| 129 | defer std.debug.unlockStdErr(); | |
| 130 | const stderr = std.io.getStdErr().writer(); | |
| 128 | const stderr = std.debug.lockStderrWriter(&.{}); | |
| 129 | defer std.debug.unlockStderrWriter(); | |
| 131 | 130 | self.renderToWriter(args, stderr, config) catch return; |
| 132 | 131 | } |
| 133 | 132 | |
| 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 | 134 | for (self.errors.items) |err_details| { |
| 136 | 135 | try renderErrorMessage(writer, config, err_details, args); |
| 137 | 136 | } |
| ... | ... | @@ -1403,7 +1402,7 @@ test parsePercent { |
| 1403 | 1402 | try std.testing.expectError(error.InvalidFormat, parsePercent("~1")); |
| 1404 | 1403 | } |
| 1405 | 1404 | |
| 1406 | pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void { | |
| 1405 | pub fn renderErrorMessage(writer: *std.io.Writer, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void { | |
| 1407 | 1406 | try config.setColor(writer, .dim); |
| 1408 | 1407 | try writer.writeAll("<cli>"); |
| 1409 | 1408 | try config.setColor(writer, .reset); |
| ... | ... | @@ -1481,27 +1480,27 @@ pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_detail |
| 1481 | 1480 | try writer.writeByte('\n'); |
| 1482 | 1481 | |
| 1483 | 1482 | try config.setColor(writer, .green); |
| 1484 | try writer.writeByteNTimes(' ', prefix.len); | |
| 1483 | try writer.splatByteAll(' ', prefix.len); | |
| 1485 | 1484 | // Special case for when the option is *only* a prefix (e.g. invalid option: -) |
| 1486 | 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 | 1487 | } else { |
| 1489 | try writer.writeByteNTimes('~', err_details.arg_span.prefix_len); | |
| 1490 | try writer.writeByteNTimes(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len); | |
| 1488 | try writer.splatByteAll('~', err_details.arg_span.prefix_len); | |
| 1489 | try writer.splatByteAll(' ', err_details.arg_span.name_offset - err_details.arg_span.prefix_len); | |
| 1491 | 1490 | if (!err_details.arg_span.point_at_next_arg and err_details.arg_span.value_offset == 0) { |
| 1492 | 1491 | try writer.writeByte('^'); |
| 1493 | try writer.writeByteNTimes('~', name_slice.len - 1); | |
| 1492 | try writer.splatByteAll('~', name_slice.len - 1); | |
| 1494 | 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 | 1495 | try writer.writeByte('^'); |
| 1497 | 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 | 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 | 1501 | try writer.writeByte('^'); |
| 1503 | 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 | 570 | switch (predefined_type) { |
| 571 | 571 | .GROUP_ICON, .GROUP_CURSOR => { |
| 572 | 572 | // Check for animated icon first |
| 573 | if (ani.isAnimatedIcon(file.reader())) { | |
| 573 | if (ani.isAnimatedIcon(file.deprecatedReader())) { | |
| 574 | 574 | // Animated icons are just put into the resource unmodified, |
| 575 | 575 | // and the resource type changes to ANIICON/ANICURSOR |
| 576 | 576 | |
| ... | ... | @@ -586,14 +586,14 @@ pub const Compiler = struct { |
| 586 | 586 | |
| 587 | 587 | try header.write(writer, self.errContext(node.id)); |
| 588 | 588 | try file.seekTo(0); |
| 589 | try writeResourceData(writer, file.reader(), header.data_size); | |
| 589 | try writeResourceData(writer, file.deprecatedReader(), header.data_size); | |
| 590 | 590 | return; |
| 591 | 591 | } |
| 592 | 592 | |
| 593 | 593 | // isAnimatedIcon moved the file cursor so reset to the start |
| 594 | 594 | try file.seekTo(0); |
| 595 | 595 | |
| 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 | 597 | error.OutOfMemory => |e| return e, |
| 598 | 598 | else => |e| { |
| 599 | 599 | return self.iconReadError( |
| ... | ... | @@ -672,7 +672,7 @@ pub const Compiler = struct { |
| 672 | 672 | } |
| 673 | 673 | |
| 674 | 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 | 676 | return self.iconReadError( |
| 677 | 677 | error.UnexpectedEOF, |
| 678 | 678 | filename_utf8, |
| ... | ... | @@ -803,7 +803,7 @@ pub const Compiler = struct { |
| 803 | 803 | } |
| 804 | 804 | |
| 805 | 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 | 807 | try writeDataPadding(writer, full_data_size); |
| 808 | 808 | |
| 809 | 809 | if (self.state.icon_id == std.math.maxInt(u16)) { |
| ... | ... | @@ -859,7 +859,7 @@ pub const Compiler = struct { |
| 859 | 859 | header.applyMemoryFlags(node.common_resource_attributes, self.source); |
| 860 | 860 | const file_size = try file.getEndPos(); |
| 861 | 861 | |
| 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 | 863 | const filename_string_index = try self.diagnostics.putString(filename_utf8); |
| 864 | 864 | return self.addErrorDetailsAndFail(.{ |
| 865 | 865 | .err = .bmp_read_error, |
| ... | ... | @@ -922,7 +922,7 @@ pub const Compiler = struct { |
| 922 | 922 | header.data_size = bmp_bytes_to_write; |
| 923 | 923 | try header.write(writer, self.errContext(node.id)); |
| 924 | 924 | try file.seekTo(bmp.file_header_len); |
| 925 | const file_reader = file.reader(); | |
| 925 | const file_reader = file.deprecatedReader(); | |
| 926 | 926 | try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size); |
| 927 | 927 | if (bitmap_info.getBitmasksByteLen() > 0) { |
| 928 | 928 | try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen()); |
| ... | ... | @@ -968,7 +968,7 @@ pub const Compiler = struct { |
| 968 | 968 | header.data_size = @intCast(file_size); |
| 969 | 969 | try header.write(writer, self.errContext(node.id)); |
| 970 | 970 | |
| 971 | var header_slurping_reader = headerSlurpingReader(148, file.reader()); | |
| 971 | var header_slurping_reader = headerSlurpingReader(148, file.deprecatedReader()); | |
| 972 | 972 | try writeResourceData(writer, header_slurping_reader.reader(), header.data_size); |
| 973 | 973 | |
| 974 | 974 | try self.state.font_dir.add(self.arena, FontDir.Font{ |
| ... | ... | @@ -1002,7 +1002,7 @@ pub const Compiler = struct { |
| 1002 | 1002 | // We now know that the data size will fit in a u32 |
| 1003 | 1003 | header.data_size = @intCast(data_size); |
| 1004 | 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 | } |
| 1007 | 1007 | |
| 1008 | 1008 | fn iconReadError( |
| ... | ... | @@ -2947,7 +2947,7 @@ pub fn HeaderSlurpingReader(comptime size: usize, comptime ReaderType: anytype) |
| 2947 | 2947 | slurped_header: [size]u8 = [_]u8{0x00} ** size, |
| 2948 | 2948 | |
| 2949 | 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); | |
| 2951 | 2951 | |
| 2952 | 2952 | pub fn read(self: *@This(), buf: []u8) Error!usize { |
| 2953 | 2953 | const amt = try self.child_reader.read(buf); |
| ... | ... | @@ -2981,7 +2981,7 @@ pub fn LimitedWriter(comptime WriterType: type) type { |
| 2981 | 2981 | bytes_left: u64, |
| 2982 | 2982 | |
| 2983 | 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); | |
| 2985 | 2985 | |
| 2986 | 2986 | const Self = @This(); |
| 2987 | 2987 |
lib/compiler/resinator/errors.zig+27-31| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | |
| 2 | 3 | const Token = @import("lex.zig").Token; |
| 3 | 4 | const SourceMappings = @import("source_mapping.zig").SourceMappings; |
| 4 | 5 | const utils = @import("utils.zig"); |
| ... | ... | @@ -61,16 +62,15 @@ pub const Diagnostics = struct { |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | 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 | defer std.debug.unlockStdErr(); | |
| 66 | const stderr = std.io.getStdErr().writer(); | |
| 65 | const stderr = std.debug.lockStderrWriter(&.{}); | |
| 66 | defer std.debug.unlockStderrWriter(); | |
| 67 | 67 | for (self.errors.items) |err_details| { |
| 68 | 68 | renderErrorMessage(stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return; |
| 69 | 69 | } |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 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 | 74 | return self.renderToStdErr(cwd, source, tty_config, source_mappings); |
| 75 | 75 | } |
| 76 | 76 | |
| ... | ... | @@ -409,15 +409,7 @@ pub const ErrorDetails = struct { |
| 409 | 409 | failed_to_open_cwd, |
| 410 | 410 | }; |
| 411 | 411 | |
| 412 | fn formatToken( | |
| 413 | ctx: TokenFormatContext, | |
| 414 | comptime fmt: []const u8, | |
| 415 | options: std.fmt.FormatOptions, | |
| 416 | writer: anytype, | |
| 417 | ) !void { | |
| 418 | _ = fmt; | |
| 419 | _ = options; | |
| 420 | ||
| 412 | fn formatToken(ctx: TokenFormatContext, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 421 | 413 | switch (ctx.token.id) { |
| 422 | 414 | .eof => return writer.writeAll(ctx.token.id.nameForErrorDisplay()), |
| 423 | 415 | else => {}, |
| ... | ... | @@ -441,7 +433,7 @@ pub const ErrorDetails = struct { |
| 441 | 433 | code_page: SupportedCodePage, |
| 442 | 434 | }; |
| 443 | 435 | |
| 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 | 437 | return .{ .data = .{ |
| 446 | 438 | .token = self.token, |
| 447 | 439 | .code_page = self.code_page, |
| ... | ... | @@ -452,7 +444,7 @@ pub const ErrorDetails = struct { |
| 452 | 444 | pub fn render(self: ErrorDetails, writer: anytype, source: []const u8, strings: []const []const u8) !void { |
| 453 | 445 | switch (self.err) { |
| 454 | 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 | 449 | .string_literal_too_long => { |
| 458 | 450 | return writer.print("string literal too long (max is currently {} characters)", .{self.extra.number}); |
| ... | ... | @@ -466,10 +458,14 @@ pub const ErrorDetails = struct { |
| 466 | 458 | .hint => return, |
| 467 | 459 | }, |
| 468 | 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 | 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 | 470 | .illegal_codepoint_outside_string_literals => { |
| 475 | 471 | // This is somewhat hacky, but we know that: |
| ... | ... | @@ -527,26 +523,26 @@ pub const ErrorDetails = struct { |
| 527 | 523 | return writer.print("unsupported code page '{s} (id={})' in #pragma code_page", .{ @tagName(code_page), number }); |
| 528 | 524 | }, |
| 529 | 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 | 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 | 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 | 534 | .expected_something_else => { |
| 539 | 535 | try writer.writeAll("expected "); |
| 540 | 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 | 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() }), | |
| 545 | .note => try writer.print("if '{s}' is intended to be a filename, it must be specified as a quoted string literal", .{self.fmtToken(source)}), | |
| 540 | .err, .warning => try writer.print("expected '<filename>', found '{f}' (resource type '{s}' can't use raw data)", .{ self.fmtToken(source), self.extra.resource.nameForErrorDisplay() }), | |
| 541 | .note => try writer.print("if '{f}' is intended to be a filename, it must be specified as a quoted string literal", .{self.fmtToken(source)}), | |
| 546 | 542 | .hint => return, |
| 547 | 543 | }, |
| 548 | 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 | 547 | .name_or_id_not_allowed => { |
| 552 | 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 | 558 | try writer.writeAll("ASCII character not equivalent to virtual key code"); |
| 563 | 559 | }, |
| 564 | 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 | 563 | .rc_would_miscompile_version_value_padding => switch (self.type) { |
| 568 | 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 | 623 | .string_already_defined => switch (self.type) { |
| 628 | 624 | .err, .warning => { |
| 629 | 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 | 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 | 629 | .hint => return, |
| ... | ... | @@ -642,7 +638,7 @@ pub const ErrorDetails = struct { |
| 642 | 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 | 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 | 643 | .accelerator_type_required => { |
| 648 | 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 | 894 | |
| 899 | 895 | const truncated_str = "<...truncated...>"; |
| 900 | 896 | |
| 901 | pub 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 { | |
| 897 | pub 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 | 898 | if (err_details.type == .hint) return; |
| 903 | 899 | |
| 904 | 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 | 977 | |
| 982 | 978 | try tty_config.setColor(writer, .green); |
| 983 | 979 | const num_spaces = truncated_visual_info.point_offset - truncated_visual_info.before_len; |
| 984 | try writer.writeByteNTimes(' ', num_spaces); | |
| 985 | try writer.writeByteNTimes('~', truncated_visual_info.before_len); | |
| 980 | try writer.splatByteAll(' ', num_spaces); | |
| 981 | try writer.splatByteAll('~', truncated_visual_info.before_len); | |
| 986 | 982 | try writer.writeByte('^'); |
| 987 | try writer.writeByteNTimes('~', truncated_visual_info.after_len); | |
| 983 | try writer.splatByteAll('~', truncated_visual_info.after_len); | |
| 988 | 984 | try writer.writeByte('\n'); |
| 989 | 985 | try tty_config.setColor(writer, .reset); |
| 990 | 986 |
lib/compiler/resinator/lex.zig+3-1| ... | ... | @@ -237,7 +237,9 @@ pub const Lexer = struct { |
| 237 | 237 | } |
| 238 | 238 | |
| 239 | 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 | } |
| 242 | 244 | |
| 243 | 245 | pub const LexMethod = enum { |
lib/compiler/resinator/main.zig+17-13| ... | ... | @@ -22,14 +22,14 @@ pub fn main() !void { |
| 22 | 22 | defer arena_state.deinit(); |
| 23 | 23 | const arena = arena_state.allocator(); |
| 24 | 24 | |
| 25 | const stderr = std.io.getStdErr(); | |
| 25 | const stderr = std.fs.File.stderr(); | |
| 26 | 26 | const stderr_config = std.io.tty.detectConfig(stderr); |
| 27 | 27 | |
| 28 | 28 | const args = try std.process.argsAlloc(allocator); |
| 29 | 29 | defer std.process.argsFree(allocator, args); |
| 30 | 30 | |
| 31 | 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 | 33 | std.process.exit(1); |
| 34 | 34 | } |
| 35 | 35 | const zig_lib_dir = args[1]; |
| ... | ... | @@ -44,7 +44,7 @@ pub fn main() !void { |
| 44 | 44 | var error_handler: ErrorHandler = switch (zig_integration) { |
| 45 | 45 | true => .{ |
| 46 | 46 | .server = .{ |
| 47 | .out = std.io.getStdOut(), | |
| 47 | .out = std.fs.File.stdout(), | |
| 48 | 48 | .in = undefined, // won't be receiving messages |
| 49 | 49 | .receive_fifo = undefined, // won't be receiving messages |
| 50 | 50 | }, |
| ... | ... | @@ -81,15 +81,15 @@ pub fn main() !void { |
| 81 | 81 | defer options.deinit(); |
| 82 | 82 | |
| 83 | 83 | if (options.print_help_and_exit) { |
| 84 | const stdout = std.io.getStdOut(); | |
| 85 | try cli.writeUsage(stdout.writer(), "zig rc"); | |
| 84 | const stdout = std.fs.File.stdout(); | |
| 85 | try cli.writeUsage(stdout.deprecatedWriter(), "zig rc"); | |
| 86 | 86 | return; |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | // Don't allow verbose when integrating with Zig via stdout |
| 90 | 90 | options.verbose = false; |
| 91 | 91 | |
| 92 | const stdout_writer = std.io.getStdOut().writer(); | |
| 92 | const stdout_writer = std.fs.File.stdout().deprecatedWriter(); | |
| 93 | 93 | if (options.verbose) { |
| 94 | 94 | try options.dumpVerbose(stdout_writer); |
| 95 | 95 | try stdout_writer.writeByte('\n'); |
| ... | ... | @@ -290,7 +290,7 @@ pub fn main() !void { |
| 290 | 290 | }; |
| 291 | 291 | defer depfile.close(); |
| 292 | 292 | |
| 293 | const depfile_writer = depfile.writer(); | |
| 293 | const depfile_writer = depfile.deprecatedWriter(); | |
| 294 | 294 | var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer); |
| 295 | 295 | switch (options.depfile_fmt) { |
| 296 | 296 | .json => { |
| ... | ... | @@ -343,7 +343,7 @@ pub fn main() !void { |
| 343 | 343 | switch (err) { |
| 344 | 344 | error.DuplicateResource => { |
| 345 | 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 | 347 | duplicate_resource.name_value, |
| 348 | 348 | fmtResourceType(duplicate_resource.type_value), |
| 349 | 349 | duplicate_resource.language, |
| ... | ... | @@ -352,7 +352,7 @@ pub fn main() !void { |
| 352 | 352 | error.ResourceDataTooLong => { |
| 353 | 353 | const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource]; |
| 354 | 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 | 356 | overflow_resource.name_value, |
| 357 | 357 | fmtResourceType(overflow_resource.type_value), |
| 358 | 358 | overflow_resource.language, |
| ... | ... | @@ -361,7 +361,7 @@ pub fn main() !void { |
| 361 | 361 | error.TotalResourceDataTooLong => { |
| 362 | 362 | const overflow_resource = resources.list.items[cvtres_diagnostics.duplicate_resource]; |
| 363 | 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 | 365 | overflow_resource.name_value, |
| 366 | 366 | fmtResourceType(overflow_resource.type_value), |
| 367 | 367 | overflow_resource.language, |
| ... | ... | @@ -471,7 +471,7 @@ const IoStream = struct { |
| 471 | 471 | allocator: std.mem.Allocator, |
| 472 | 472 | }; |
| 473 | 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); | |
| 475 | 475 | |
| 476 | 476 | pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize { |
| 477 | 477 | switch (ctx.self.*) { |
| ... | ... | @@ -645,7 +645,9 @@ const ErrorHandler = union(enum) { |
| 645 | 645 | }, |
| 646 | 646 | .tty => { |
| 647 | 647 | // 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 | 651 | aro.Diagnostics.render(comp, self.tty); |
| 650 | 652 | }, |
| 651 | 653 | } |
| ... | ... | @@ -690,7 +692,9 @@ const ErrorHandler = union(enum) { |
| 690 | 692 | try server.serveErrorBundle(error_bundle); |
| 691 | 693 | }, |
| 692 | 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 | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | |
| 2 | 3 | const rc = @import("rc.zig"); |
| 3 | 4 | const ResourceType = rc.ResourceType; |
| 4 | 5 | const CommonResourceAttributes = rc.CommonResourceAttributes; |
| ... | ... | @@ -163,14 +164,7 @@ pub const Language = packed struct(u16) { |
| 163 | 164 | return @bitCast(self); |
| 164 | 165 | } |
| 165 | 166 | |
| 166 | pub fn format( | |
| 167 | language: Language, | |
| 168 | comptime fmt: []const u8, | |
| 169 | options: std.fmt.FormatOptions, | |
| 170 | out_stream: anytype, | |
| 171 | ) !void { | |
| 172 | _ = fmt; | |
| 173 | _ = options; | |
| 167 | pub fn format(language: Language, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 174 | 168 | const language_id = language.asInt(); |
| 175 | 169 | const language_name = language_name: { |
| 176 | 170 | if (std.enums.fromInt(lang.LanguageId, language_id)) |lang_enum_val| { |
| ... | ... | @@ -181,7 +175,7 @@ pub const Language = packed struct(u16) { |
| 181 | 175 | } |
| 182 | 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 | }; |
| 187 | 181 | |
| ... | ... | @@ -445,47 +439,33 @@ pub const NameOrOrdinal = union(enum) { |
| 445 | 439 | } |
| 446 | 440 | } |
| 447 | 441 | |
| 448 | pub fn format( | |
| 449 | self: NameOrOrdinal, | |
| 450 | comptime fmt: []const u8, | |
| 451 | options: std.fmt.FormatOptions, | |
| 452 | out_stream: anytype, | |
| 453 | ) !void { | |
| 454 | _ = fmt; | |
| 455 | _ = options; | |
| 442 | pub fn format(self: NameOrOrdinal, w: *std.io.Writer) !void { | |
| 456 | 443 | switch (self) { |
| 457 | 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 | 447 | .ordinal => |ordinal| { |
| 461 | try out_stream.print("{d}", .{ordinal}); | |
| 448 | try w.print("{d}", .{ordinal}); | |
| 462 | 449 | }, |
| 463 | 450 | } |
| 464 | 451 | } |
| 465 | 452 | |
| 466 | fn formatResourceType( | |
| 467 | self: NameOrOrdinal, | |
| 468 | comptime fmt: []const u8, | |
| 469 | options: std.fmt.FormatOptions, | |
| 470 | out_stream: anytype, | |
| 471 | ) !void { | |
| 472 | _ = fmt; | |
| 473 | _ = options; | |
| 453 | fn formatResourceType(self: NameOrOrdinal, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 474 | 454 | switch (self) { |
| 475 | 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 | 458 | .ordinal => |ordinal| { |
| 479 | 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 | 461 | } else { |
| 482 | try out_stream.print("{d}", .{ordinal}); | |
| 462 | try w.print("{d}", .{ordinal}); | |
| 483 | 463 | } |
| 484 | 464 | }, |
| 485 | 465 | } |
| 486 | 466 | } |
| 487 | 467 | |
| 488 | pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(formatResourceType) { | |
| 468 | pub fn fmtResourceType(type_value: NameOrOrdinal) std.fmt.Formatter(NameOrOrdinal, formatResourceType) { | |
| 489 | 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 | 86 | |
| 87 | 87 | /// Used for generic colored errors/warnings/notes, more context-specific error messages |
| 88 | 88 | /// are handled elsewhere. |
| 89 | pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void { | |
| 89 | pub fn renderErrorMessage(writer: *std.io.Writer, config: std.io.tty.Config, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void { | |
| 90 | 90 | switch (msg_type) { |
| 91 | 91 | .err => { |
| 92 | 92 | try config.setColor(writer, .bold); |
lib/compiler/test_runner.zig+2-2| ... | ... | @@ -303,7 +303,7 @@ pub fn mainSimple() anyerror!void { |
| 303 | 303 | var failed: u64 = 0; |
| 304 | 304 | |
| 305 | 305 | // 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 {}; | |
| 307 | 307 | |
| 308 | 308 | for (builtin.test_functions) |test_fn| { |
| 309 | 309 | if (test_fn.func()) |_| { |
| ... | ... | @@ -330,7 +330,7 @@ pub fn mainSimple() anyerror!void { |
| 330 | 330 | passed += 1; |
| 331 | 331 | } |
| 332 | 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 | 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 | 86 | const result = std.os.linux.syscall3(.cacheflush, start, end - start, flags); |
| 87 | 87 | std.debug.assert(result == 0); |
| 88 | 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 | 109 | } else if (mips and os == .openbsd) { |
| 90 | 110 | // TODO |
| 91 | 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 | 18 | pub const panic = common.panic; |
| 19 | 19 | |
| 20 | 20 | comptime { |
| 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 | 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 | 238 | |
| 239 | 239 | .call_one, |
| 240 | 240 | .call_one_comma, |
| 241 | .async_call_one, | |
| 242 | .async_call_one_comma, | |
| 243 | 241 | .call, |
| 244 | 242 | .call_comma, |
| 245 | .async_call, | |
| 246 | .async_call_comma, | |
| 247 | 243 | => { |
| 248 | 244 | var buf: [1]Ast.Node.Index = undefined; |
| 249 | 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 | 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 | 451 | return Ast.parse(gpa, "", .zig); |
| 456 | 452 | } |
| ... | ... | @@ -577,7 +573,6 @@ fn struct_decl( |
| 577 | 573 | }, |
| 578 | 574 | |
| 579 | 575 | .@"comptime", |
| 580 | .@"usingnamespace", | |
| 581 | 576 | => try w.expr(&namespace.base, parent_decl, ast.nodeData(member).node), |
| 582 | 577 | |
| 583 | 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 | 644 | const ast = w.file.get_ast(); |
| 650 | 645 | switch (ast.nodeTag(node)) { |
| 651 | 646 | .root => unreachable, // Top-level declaration. |
| 652 | .@"usingnamespace" => unreachable, // Top-level declaration. | |
| 653 | 647 | .test_decl => unreachable, // Top-level declaration. |
| 654 | 648 | .container_field_init => unreachable, // Top-level declaration. |
| 655 | 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 | 743 | .@"comptime", |
| 750 | 744 | .@"nosuspend", |
| 751 | 745 | .@"suspend", |
| 752 | .@"await", | |
| 753 | 746 | .@"resume", |
| 754 | 747 | .@"try", |
| 755 | 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 | 805 | |
| 813 | 806 | .call_one, |
| 814 | 807 | .call_one_comma, |
| 815 | .async_call_one, | |
| 816 | .async_call_one_comma, | |
| 817 | 808 | .call, |
| 818 | 809 | .call_comma, |
| 819 | .async_call, | |
| 820 | .async_call_comma, | |
| 821 | 810 | => { |
| 822 | 811 | var buf: [1]Ast.Node.Index = undefined; |
| 823 | 812 | const full = ast.fullCall(&buf, node).?; |
lib/docs/wasm/html_render.zig-3| ... | ... | @@ -101,8 +101,6 @@ pub fn fileSourceHtml( |
| 101 | 101 | .keyword_align, |
| 102 | 102 | .keyword_and, |
| 103 | 103 | .keyword_asm, |
| 104 | .keyword_async, | |
| 105 | .keyword_await, | |
| 106 | 104 | .keyword_break, |
| 107 | 105 | .keyword_catch, |
| 108 | 106 | .keyword_comptime, |
| ... | ... | @@ -139,7 +137,6 @@ pub fn fileSourceHtml( |
| 139 | 137 | .keyword_try, |
| 140 | 138 | .keyword_union, |
| 141 | 139 | .keyword_unreachable, |
| 142 | .keyword_usingnamespace, | |
| 143 | 140 | .keyword_var, |
| 144 | 141 | .keyword_volatile, |
| 145 | 142 | .keyword_allowzero, |
lib/docs/wasm/markdown.zig+2-2| ... | ... | @@ -143,7 +143,7 @@ fn mainImpl() !void { |
| 143 | 143 | var parser = try Parser.init(gpa); |
| 144 | 144 | defer parser.deinit(); |
| 145 | 145 | |
| 146 | var stdin_buf = std.io.bufferedReader(std.io.getStdIn().reader()); | |
| 146 | var stdin_buf = std.io.bufferedReader(std.fs.File.stdin().deprecatedReader()); | |
| 147 | 147 | var line_buf = std.ArrayList(u8).init(gpa); |
| 148 | 148 | defer line_buf.deinit(); |
| 149 | 149 | while (stdin_buf.reader().streamUntilDelimiter(line_buf.writer(), '\n', null)) { |
| ... | ... | @@ -158,7 +158,7 @@ fn mainImpl() !void { |
| 158 | 158 | var doc = try parser.endInput(); |
| 159 | 159 | defer doc.deinit(gpa); |
| 160 | 160 | |
| 161 | var stdout_buf = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 161 | var stdout_buf = std.io.bufferedWriter(std.fs.File.stdout().deprecatedWriter()); | |
| 162 | 162 | try doc.render(stdout_buf.writer()); |
| 163 | 163 | try stdout_buf.flush(); |
| 164 | 164 | } |
lib/fuzzer.zig+12-9| ... | ... | @@ -9,7 +9,8 @@ pub const std_options = std.Options{ |
| 9 | 9 | .logFn = logOverride, |
| 10 | 10 | }; |
| 11 | 11 | |
| 12 | var log_file: ?std.fs.File = null; | |
| 12 | var log_file_buffer: [256]u8 = undefined; | |
| 13 | var log_file_writer: ?std.fs.File.Writer = null; | |
| 13 | 14 | |
| 14 | 15 | fn logOverride( |
| 15 | 16 | comptime level: std.log.Level, |
| ... | ... | @@ -17,15 +18,17 @@ fn logOverride( |
| 17 | 18 | comptime format: []const u8, |
| 18 | 19 | args: anytype, |
| 19 | 20 | ) void { |
| 20 | const f = if (log_file) |f| f else f: { | |
| 21 | const fw = if (log_file_writer) |*f| f else f: { | |
| 21 | 22 | const f = fuzzer.cache_dir.createFile("tmp/libfuzzer.log", .{}) catch |
| 22 | 23 | @panic("failed to open fuzzer log file"); |
| 23 | log_file = f; | |
| 24 | break :f f; | |
| 24 | log_file_writer = f.writer(&log_file_buffer); | |
| 25 | break :f &log_file_writer.?; | |
| 25 | 26 | }; |
| 26 | 27 | const prefix1 = comptime level.asText(); |
| 27 | 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 | } |
| 30 | 33 | |
| 31 | 34 | /// Helps determine run uniqueness in the face of recursion. |
| ... | ... | @@ -226,18 +229,18 @@ const Fuzzer = struct { |
| 226 | 229 | .read = true, |
| 227 | 230 | }) catch |e| switch (e) { |
| 228 | 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 | 234 | errdefer input_file.close(); |
| 232 | 235 | // Initialize the mmap for the current input. |
| 233 | 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 | 238 | f.corpus_directory, i, @errorName(e), |
| 236 | 239 | }); |
| 237 | 240 | }; |
| 238 | 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 | 245 | errdefer gpa.free(input); |
| 243 | 246 | f.corpus.append(gpa, .{ |
| ... | ... | @@ -263,7 +266,7 @@ const Fuzzer = struct { |
| 263 | 266 | const sub_path = try std.fmt.allocPrint(gpa, "f/{s}", .{f.unit_test_name}); |
| 264 | 267 | f.corpus_directory = .{ |
| 265 | 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 | 270 | .path = sub_path, |
| 268 | 271 | }; |
| 269 | 272 | initNextInput(f); |
lib/init/src/root.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ pub fn bufferedPrint() !void { |
| 5 | 5 | // Stdout is for the actual output of your application, for example if you |
| 6 | 6 | // are implementing gzip, then only the compressed bytes should be sent to |
| 7 | 7 | // stdout, not any debugging messages. |
| 8 | const stdout_file = std.io.getStdOut().writer(); | |
| 8 | const stdout_file = std.fs.File.stdout().deprecatedWriter(); | |
| 9 | 9 | // Buffering can improve performance significantly in print-heavy programs. |
| 10 | 10 | var bw = std.io.bufferedWriter(stdout_file); |
| 11 | 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 | ||
| 3 | double 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 | ||
| 3 | float 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 | |
| 8 | static const double_t toint = 1/EPS; | |
| 9 | ||
| 10 | double 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 | ||
| 3 | float 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 | |
| 4 | long 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 | ||
| 10 | static const long double toint = 1/LDBL_EPSILON; | |
| 11 | ||
| 12 | long double floorl(long double x) | |
| 13 | { | |
| 14 | 	union ldshape u = {x}; | |
| 15 | 	int e = u.i.se & 0x7fff; | |
| 16 | 	long double y; | |
| 17 | ||
| 18 | 	if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0) | |
| 19 | 		return x; | |
| 20 | 	/* y = int(x) - x, where int(x) is an integer neighbor of x */ | |
| 21 | 	if (u.i.se >> 15) | |
| 22 | 		y = x - toint + toint - x; | |
| 23 | 	else | |
| 24 | 		y = x + toint - toint - x; | |
| 25 | 	/* special case because of non-nearest rounding modes */ | |
| 26 | 	if (e <= 0x3fff-1) { | |
| 27 | 		FORCE_EVAL(y); | |
| 28 | 		return u.i.se >> 15 ? -1 : 0; | |
| 29 | 	} | |
| 30 | 	if (y > 0) | |
| 31 | 		return x + y - 1; | |
| 32 | 	return x + y; | |
| 33 | } | |
| 34 | #endif |
lib/libc/musl/src/math/i386/floor.s+1-16| ... | ... | @@ -1,20 +1,5 @@ |
| 1 | .global floorf | |
| 2 | .type floorf,@function | |
| 3 | floorf: | |
| 4 | 	flds 4(%esp) | |
| 5 | 	jmp 1f | |
| 6 | ||
| 7 | .global floorl | |
| 8 | .type floorl,@function | |
| 9 | floorl: | |
| 10 | 	fldt 4(%esp) | |
| 11 | 	jmp 1f | |
| 1 | /* zig patch: removed `floorl` and `floorf` in favor of using zig compiler_rt's implementations */ | |
| 12 | 2 | |
| 13 | .global floor | |
| 14 | .type floor,@function | |
| 15 | floor: | |
| 16 | 	fldl 4(%esp) | |
| 17 | 1:	mov $0x7,%al | |
| 18 | 3 | 1:	fstcw 4(%esp) |
| 19 | 4 | 	mov 5(%esp),%ah |
| 20 | 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 | ||
| 5 | double 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 | ||
| 5 | float 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 | ||
| 5 | double 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 | ||
| 5 | float 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 | ||
| 5 | long 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 | 2466 | |
| 2467 | 2467 | pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 { |
| 2468 | 2468 | return gen.path orelse { |
| 2469 | std.debug.lockStdErr(); | |
| 2470 | const stderr = std.io.getStdErr(); | |
| 2471 | dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; | |
| 2472 | std.debug.unlockStdErr(); | |
| 2469 | const w = debug.lockStderrWriter(&.{}); | |
| 2470 | dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {}; | |
| 2471 | debug.unlockStderrWriter(); | |
| 2473 | 2472 | @panic("misconfigured build script"); |
| 2474 | 2473 | }; |
| 2475 | 2474 | } |
| ... | ... | @@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) { |
| 2676 | 2675 | var file_path: Cache.Path = .{ |
| 2677 | 2676 | .root_dir = Cache.Directory.cwd(), |
| 2678 | 2677 | .sub_path = gen.file.path orelse { |
| 2679 | std.debug.lockStdErr(); | |
| 2680 | const stderr: fs.File = .stderr(); | |
| 2681 | dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {}; | |
| 2682 | std.debug.unlockStdErr(); | |
| 2678 | const w = debug.lockStderrWriter(&.{}); | |
| 2679 | dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {}; | |
| 2680 | debug.unlockStderrWriter(); | |
| 2683 | 2681 | @panic("misconfigured build script"); |
| 2684 | 2682 | }, |
| 2685 | 2683 | }; |
| ... | ... | @@ -2769,17 +2767,16 @@ fn dumpBadDirnameHelp( |
| 2769 | 2767 | const w = debug.lockStderrWriter(&.{}); |
| 2770 | 2768 | defer debug.unlockStderrWriter(); |
| 2771 | 2769 | |
| 2772 | const stderr: fs.File = .stderr(); | |
| 2773 | 2770 | try w.print(msg, args); |
| 2774 | 2771 | |
| 2775 | const tty_config = std.io.tty.detectConfig(stderr); | |
| 2772 | const tty_config = std.io.tty.detectConfig(.stderr()); | |
| 2776 | 2773 | |
| 2777 | 2774 | if (fail_step) |s| { |
| 2778 | 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 | 2777 | tty_config.setColor(w, .reset) catch {}; |
| 2781 | 2778 | |
| 2782 | s.dump(stderr); | |
| 2779 | s.dump(w, tty_config); | |
| 2783 | 2780 | } |
| 2784 | 2781 | |
| 2785 | 2782 | if (asking_step) |as| { |
| ... | ... | @@ -2787,24 +2784,23 @@ fn dumpBadDirnameHelp( |
| 2787 | 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 | 2785 | tty_config.setColor(w, .reset) catch {}; |
| 2789 | 2786 | |
| 2790 | as.dump(stderr); | |
| 2787 | as.dump(w, tty_config); | |
| 2791 | 2788 | } |
| 2792 | 2789 | |
| 2793 | 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 | 2792 | tty_config.setColor(w, .reset) catch {}; |
| 2796 | 2793 | } |
| 2797 | 2794 | |
| 2798 | 2795 | /// In this function the stderr mutex has already been locked. |
| 2799 | 2796 | pub fn dumpBadGetPathHelp( |
| 2800 | 2797 | s: *Step, |
| 2801 | stderr: fs.File, | |
| 2798 | w: *std.io.Writer, | |
| 2799 | tty_config: std.io.tty.Config, | |
| 2802 | 2800 | src_builder: *Build, |
| 2803 | 2801 | asking_step: ?*Step, |
| 2804 | 2802 | ) anyerror!void { |
| 2805 | var fw = stderr.writer(&.{}); | |
| 2806 | const bw = &fw.interface; | |
| 2807 | try bw.print( | |
| 2803 | try w.print( | |
| 2808 | 2804 | \\getPath() was called on a GeneratedFile that wasn't built yet. |
| 2809 | 2805 | \\ source package path: {s} |
| 2810 | 2806 | \\ Is there a missing Step dependency on step '{s}'? |
| ... | ... | @@ -2814,22 +2810,21 @@ pub fn dumpBadGetPathHelp( |
| 2814 | 2810 | s.name, |
| 2815 | 2811 | }); |
| 2816 | 2812 | |
| 2817 | const tty_config = std.io.tty.detectConfig(stderr); | |
| 2818 | tty_config.setColor(&bw, .red) catch {}; | |
| 2819 | try stderr.writeAll(" The step was created by this stack trace:\n"); | |
| 2820 | tty_config.setColor(&bw, .reset) catch {}; | |
| 2813 | tty_config.setColor(w, .red) catch {}; | |
| 2814 | try w.writeAll(" The step was created by this stack trace:\n"); | |
| 2815 | tty_config.setColor(w, .reset) catch {}; | |
| 2821 | 2816 | |
| 2822 | s.dump(stderr); | |
| 2817 | s.dump(w, tty_config); | |
| 2823 | 2818 | if (asking_step) |as| { |
| 2824 | tty_config.setColor(&bw, .red) catch {}; | |
| 2825 | try bw.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); | |
| 2826 | tty_config.setColor(&bw, .reset) catch {}; | |
| 2819 | tty_config.setColor(w, .red) catch {}; | |
| 2820 | try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); | |
| 2821 | tty_config.setColor(w, .reset) catch {}; | |
| 2827 | 2822 | |
| 2828 | as.dump(stderr); | |
| 2823 | as.dump(w, tty_config); | |
| 2829 | 2824 | } |
| 2830 | tty_config.setColor(&bw, .red) catch {}; | |
| 2831 | try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); | |
| 2832 | tty_config.setColor(&bw, .reset) catch {}; | |
| 2825 | tty_config.setColor(w, .red) catch {}; | |
| 2826 | try w.writeAll(" Hope that helps. Proceeding to panic.\n"); | |
| 2827 | tty_config.setColor(w, .reset) catch {}; | |
| 2833 | 2828 | } |
| 2834 | 2829 | |
| 2835 | 2830 | pub const InstallDir = union(enum) { |
| ... | ... | @@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 { |
| 2866 | 2861 | return result_path; |
| 2867 | 2862 | } |
| 2868 | 2863 | |
| 2869 | /// Deprecated; use `std.fmt.hex` instead. | |
| 2870 | pub fn hex64(x: u64) [16]u8 { | |
| 2871 | return std.fmt.hex(x); | |
| 2872 | } | |
| 2873 | ||
| 2874 | 2864 | /// A pair of target query and fully resolved target. |
| 2875 | 2865 | /// This type is generally required by build system API that need to be given a |
| 2876 | 2866 | /// 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 | 2 | //! This is not a general-purpose cache. It is designed to be fast and simple, |
| 3 | 3 | //! not to withstand attacks using specially-crafted input. |
| 4 | 4 | |
| 5 | const Cache = @This(); | |
| 6 | const std = @import("std"); | |
| 7 | const builtin = @import("builtin"); | |
| 8 | const crypto = std.crypto; | |
| 9 | const fs = std.fs; | |
| 10 | const assert = std.debug.assert; | |
| 11 | const testing = std.testing; | |
| 12 | const mem = std.mem; | |
| 13 | const fmt = std.fmt; | |
| 14 | const Allocator = std.mem.Allocator; | |
| 15 | const log = std.log.scoped(.cache); | |
| 16 | ||
| 5 | 17 | gpa: Allocator, |
| 6 | 18 | manifest_dir: fs.Dir, |
| 7 | 19 | hash: HashHelper = .{}, |
| ... | ... | @@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig"); |
| 21 | 33 | pub const Directory = @import("Cache/Directory.zig"); |
| 22 | 34 | pub const DepTokenizer = @import("Cache/DepTokenizer.zig"); |
| 23 | 35 | |
| 24 | const Cache = @This(); | |
| 25 | const std = @import("std"); | |
| 26 | const builtin = @import("builtin"); | |
| 27 | const crypto = std.crypto; | |
| 28 | const fs = std.fs; | |
| 29 | const assert = std.debug.assert; | |
| 30 | const testing = std.testing; | |
| 31 | const mem = std.mem; | |
| 32 | const fmt = std.fmt; | |
| 33 | const Allocator = std.mem.Allocator; | |
| 34 | const log = std.log.scoped(.cache); | |
| 35 | ||
| 36 | 36 | pub fn addPrefix(cache: *Cache, directory: Directory) void { |
| 37 | 37 | cache.prefixes_buffer[cache.prefixes_len] = directory; |
| 38 | 38 | cache.prefixes_len += 1; |
| ... | ... | @@ -1118,25 +1118,12 @@ pub const Manifest = struct { |
| 1118 | 1118 | if (self.manifest_dirty) { |
| 1119 | 1119 | self.manifest_dirty = false; |
| 1120 | 1120 | |
| 1121 | const gpa = self.cache.gpa; | |
| 1122 | var contents: std.ArrayListUnmanaged(u8) = .empty; | |
| 1123 | defer contents.deinit(gpa); | |
| 1124 | ||
| 1125 | try contents.appendSlice(gpa, manifest_header ++ "\n"); | |
| 1126 | for (self.files.keys()) |file| { | |
| 1127 | try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{ | |
| 1128 | file.stat.size, | |
| 1129 | file.stat.inode, | |
| 1130 | file.stat.mtime, | |
| 1131 | &file.bin_digest, | |
| 1132 | file.prefixed_path.prefix, | |
| 1133 | file.prefixed_path.sub_path, | |
| 1134 | }); | |
| 1135 | } | |
| 1136 | ||
| 1137 | try manifest_file.setEndPos(contents.items.len); | |
| 1138 | var pos: usize = 0; | |
| 1139 | while (pos < contents.items.len) pos += try manifest_file.pwrite(contents.items[pos..], pos); | |
| 1121 | var buffer: [4000]u8 = undefined; | |
| 1122 | var fw = manifest_file.writer(&buffer); | |
| 1123 | writeDirtyManifestToStream(self, &fw) catch |err| switch (err) { | |
| 1124 | error.WriteFailed => return fw.err.?, | |
| 1125 | else => |e| return e, | |
| 1126 | }; | |
| 1140 | 1127 | } |
| 1141 | 1128 | |
| 1142 | 1129 | if (self.want_shared_lock) { |
| ... | ... | @@ -1144,6 +1131,21 @@ pub const Manifest = struct { |
| 1144 | 1131 | } |
| 1145 | 1132 | } |
| 1146 | 1133 | |
| 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 | 1149 | fn downgradeToSharedLock(self: *Manifest) !void { |
| 1148 | 1150 | if (!self.have_exclusive_lock) return; |
| 1149 | 1151 |
lib/std/Build/Cache/Directory.zig+4-4| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const Directory = @This(); |
| 2 | 2 | const std = @import("../../std.zig"); |
| 3 | const assert = std.debug.assert; | |
| 3 | 4 | const fs = std.fs; |
| 4 | 5 | const fmt = std.fmt; |
| 5 | 6 | const Allocator = std.mem.Allocator; |
| ... | ... | @@ -55,11 +56,10 @@ pub fn closeAndFree(self: *Directory, gpa: Allocator) void { |
| 55 | 56 | self.* = undefined; |
| 56 | 57 | } |
| 57 | 58 | |
| 58 | pub fn format(self: Directory, w: *std.io.Writer, comptime fmt_string: []const u8) !void { | |
| 59 | if (fmt_string.len != 0) fmt.invalidFmtError(fmt_string, self); | |
| 59 | pub fn format(self: Directory, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 60 | 60 | if (self.path) |p| { |
| 61 | try w.writeAll(p); | |
| 62 | try w.writeAll(fs.path.sep_str); | |
| 61 | try writer.writeAll(p); | |
| 62 | try writer.writeAll(fs.path.sep_str); | |
| 63 | 63 | } |
| 64 | 64 | } |
| 65 | 65 |
lib/std/Build/Cache/Path.zig+42-32| ... | ... | @@ -1,3 +1,10 @@ |
| 1 | const Path = @This(); | |
| 2 | const std = @import("../../std.zig"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const fs = std.fs; | |
| 5 | const Allocator = std.mem.Allocator; | |
| 6 | const Cache = std.Build.Cache; | |
| 7 | ||
| 1 | 8 | root_dir: Cache.Directory, |
| 2 | 9 | /// The path, relative to the root dir, that this `Path` represents. |
| 3 | 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 | 144 | } |
| 138 | 145 | |
| 139 | 146 | pub 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 | } |
| 142 | 149 | |
| 143 | pub fn format(self: Path, w: *std.io.Writer, comptime fmt_string: []const u8) !void { | |
| 144 | if (fmt_string.len == 1) { | |
| 145 | // Quote-escape the string. | |
| 146 | const stringEscape = std.zig.stringEscape; | |
| 147 | const f = switch (fmt_string[0]) { | |
| 148 | 'q' => "", | |
| 149 | '\'' => "\'", | |
| 150 | else => @compileError("unsupported format string: " ++ fmt_string), | |
| 151 | }; | |
| 152 | if (self.root_dir.path) |p| { | |
| 153 | try stringEscape(p, w, f); | |
| 154 | if (self.sub_path.len > 0) try stringEscape(fs.path.sep_str, w, f); | |
| 155 | } | |
| 156 | if (self.sub_path.len > 0) { | |
| 157 | try stringEscape(self.sub_path, w, f); | |
| 158 | } | |
| 159 | return; | |
| 150 | pub fn fmtEscapeString(path: Path) std.fmt.Formatter(Path, formatEscapeString) { | |
| 151 | return .{ .data = path }; | |
| 152 | } | |
| 153 | ||
| 154 | pub fn formatEscapeString(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 155 | if (path.root_dir.path) |p| { | |
| 156 | try std.zig.stringEscape(p, writer); | |
| 157 | if (path.sub_path.len > 0) try std.zig.stringEscape(fs.path.sep_str, writer); | |
| 158 | } | |
| 159 | if (path.sub_path.len > 0) { | |
| 160 | try std.zig.stringEscape(path.sub_path, writer); | |
| 160 | 161 | } |
| 161 | if (fmt_string.len > 0) | |
| 162 | std.fmt.invalidFmtError(fmt_string, self); | |
| 162 | } | |
| 163 | ||
| 164 | pub fn fmtEscapeChar(path: Path) std.fmt.Formatter(Path, formatEscapeChar) { | |
| 165 | return .{ .data = path }; | |
| 166 | } | |
| 167 | ||
| 168 | pub 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 | ||
| 178 | pub fn format(self: Path, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 163 | 179 | if (std.fs.path.isAbsolute(self.sub_path)) { |
| 164 | try w.writeAll(self.sub_path); | |
| 180 | try writer.writeAll(self.sub_path); | |
| 165 | 181 | return; |
| 166 | 182 | } |
| 167 | 183 | if (self.root_dir.path) |p| { |
| 168 | try w.writeAll(p); | |
| 184 | try writer.writeAll(p); | |
| 169 | 185 | if (self.sub_path.len > 0) { |
| 170 | try w.writeAll(fs.path.sep_str); | |
| 171 | try w.writeAll(self.sub_path); | |
| 186 | try writer.writeAll(fs.path.sep_str); | |
| 187 | try writer.writeAll(self.sub_path); | |
| 172 | 188 | } |
| 173 | 189 | return; |
| 174 | 190 | } |
| 175 | 191 | if (self.sub_path.len > 0) { |
| 176 | try w.writeAll(self.sub_path); | |
| 192 | try writer.writeAll(self.sub_path); | |
| 177 | 193 | return; |
| 178 | 194 | } |
| 179 | try w.writeByte('.'); | |
| 195 | try writer.writeByte('.'); | |
| 180 | 196 | } |
| 181 | 197 | |
| 182 | 198 | pub fn eql(self: Path, other: Path) bool { |
| ... | ... | @@ -218,9 +234,3 @@ pub const TableAdapter = struct { |
| 218 | 234 | return a.eql(b); |
| 219 | 235 | } |
| 220 | 236 | }; |
| 221 | ||
| 222 | const Path = @This(); | |
| 223 | const std = @import("../../std.zig"); | |
| 224 | const fs = std.fs; | |
| 225 | const Allocator = std.mem.Allocator; | |
| 226 | const 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 | 124 | const show_stderr = compile.step.result_stderr.len > 0; |
| 125 | 125 | |
| 126 | 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 | 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 | } |
| 131 | 132 | |
| 132 | 133 | const rebuilt_bin_path = result catch |err| switch (err) { |
| ... | ... | @@ -151,9 +152,10 @@ fn fuzzWorkerRun( |
| 151 | 152 | |
| 152 | 153 | run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) { |
| 153 | 154 | error.MakeFailed => { |
| 154 | const bw = std.debug.lockStderrWriter(&.{}); | |
| 155 | var buf: [256]u8 = undefined; | |
| 156 | const w = std.debug.lockStderrWriter(&buf); | |
| 155 | 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 | 159 | return; |
| 158 | 160 | }, |
| 159 | 161 | else => { |
lib/std/Build/Fuzz/WebServer.zig+1-1| ... | ... | @@ -176,7 +176,7 @@ fn serveFile( |
| 176 | 176 | // We load the file with every request so that the user can make changes to the file |
| 177 | 177 | // and refresh the HTML page without restarting this server. |
| 178 | 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 | 180 | return error.AlreadyReported; |
| 181 | 181 | }; |
| 182 | 182 | defer gpa.free(file_contents); |
lib/std/Build/Module.zig+2-2| ... | ... | @@ -186,7 +186,7 @@ pub const IncludeDir = union(enum) { |
| 186 | 186 | .embed_path => |lazy_path| { |
| 187 | 187 | // Special case: this is a single arg. |
| 188 | 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 | 190 | return zig_args.append(arg); |
| 191 | 191 | }, |
| 192 | 192 | }; |
| ... | ... | @@ -572,7 +572,7 @@ pub fn appendZigProcessFlags( |
| 572 | 572 | try zig_args.append(switch (unwind_tables) { |
| 573 | 573 | .none => "-fno-unwind-tables", |
| 574 | 574 | .sync => "-funwind-tables", |
| 575 | .@"async" => "-fasync-unwind-tables", | |
| 575 | .async => "-fasync-unwind-tables", | |
| 576 | 576 | }); |
| 577 | 577 | } |
| 578 | 578 |
lib/std/Build/Step.zig+10-13| ... | ... | @@ -286,28 +286,25 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 286 | 286 | } |
| 287 | 287 | |
| 288 | 288 | /// For debugging purposes, prints identifying information about this Step. |
| 289 | pub fn dump(step: *Step, file: std.fs.File) void { | |
| 290 | var fw = file.writer(&.{}); | |
| 291 | const bw = &fw.interface; | |
| 292 | const tty_config = std.io.tty.detectConfig(file); | |
| 289 | pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void { | |
| 293 | 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 | 292 | @errorName(err), |
| 296 | 293 | }) catch {}; |
| 297 | 294 | return; |
| 298 | 295 | }; |
| 299 | 296 | if (step.getStackTrace()) |stack_trace| { |
| 300 | bw.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; | |
| 301 | std.debug.writeStackTrace(stack_trace, &bw, debug_info, tty_config) catch |err| { | |
| 302 | bw.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {}; | |
| 297 | w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; | |
| 298 | std.debug.writeStackTrace(stack_trace, w, debug_info, tty_config) catch |err| { | |
| 299 | w.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch {}; | |
| 303 | 300 | return; |
| 304 | 301 | }; |
| 305 | 302 | } else { |
| 306 | 303 | const field = "debug_stack_frames_count"; |
| 307 | 304 | comptime assert(@hasField(Build, field)); |
| 308 | tty_config.setColor(&bw, .yellow) catch {}; | |
| 309 | bw.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; | |
| 310 | tty_config.setColor(&bw, .reset) catch {}; | |
| 305 | tty_config.setColor(w, .yellow) catch {}; | |
| 306 | w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; | |
| 307 | tty_config.setColor(w, .reset) catch {}; | |
| 311 | 308 | } |
| 312 | 309 | } |
| 313 | 310 | |
| ... | ... | @@ -483,9 +480,9 @@ pub fn evalZigProcess( |
| 483 | 480 | pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus { |
| 484 | 481 | const b = s.owner; |
| 485 | 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 | 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 | 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 | 230 | literal: u64, |
| 231 | 231 | }, |
| 232 | 232 | |
| 233 | pub fn format( | |
| 234 | value: ComputeCompareExpected, | |
| 235 | bw: *Writer, | |
| 236 | comptime fmt: []const u8, | |
| 237 | ) !void { | |
| 238 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); | |
| 239 | try bw.print("{s} ", .{@tagName(value.op)}); | |
| 233 | pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void { | |
| 234 | try w.print("{t} ", .{value.op}); | |
| 240 | 235 | switch (value.value) { |
| 241 | .variable => |name| try bw.writeAll(name), | |
| 242 | .literal => |x| try bw.print("{x}", .{x}), | |
| 236 | .variable => |name| try w.writeAll(name), | |
| 237 | .literal => |x| try w.print("{x}", .{x}), | |
| 243 | 238 | } |
| 244 | 239 | } |
| 245 | 240 | }; |
| ... | ... | @@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { |
| 571 | 566 | null, |
| 572 | 567 | .of(u64), |
| 573 | 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 | }); | |
| 575 | 572 | |
| 576 | 573 | var vars: std.StringHashMap(u64) = .init(gpa); |
| 577 | 574 | for (check_object.checks.items) |chk| { |
| ... | ... | @@ -606,7 +603,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { |
| 606 | 603 | // we either format message string with escaped codes, or not to aid debugging |
| 607 | 604 | // the failed test. |
| 608 | 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 | 607 | return .{ .data = .{ |
| 611 | 608 | .kind = kind, |
| 612 | 609 | .msg = msg, |
| ... | ... | @@ -618,15 +615,10 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { |
| 618 | 615 | msg: []const u8, |
| 619 | 616 | }; |
| 620 | 617 | |
| 621 | fn formatMessageString( | |
| 622 | ctx: Ctx, | |
| 623 | bw: *Writer, | |
| 624 | comptime unused_fmt_string: []const u8, | |
| 625 | ) !void { | |
| 626 | _ = unused_fmt_string; | |
| 618 | fn formatMessageString(ctx: Ctx, w: *Writer) !void { | |
| 627 | 619 | switch (ctx.kind) { |
| 628 | .dump_section => try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), | |
| 629 | else => try bw.writeAll(ctx.msg), | |
| 620 | .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}), | |
| 621 | else => try w.writeAll(ctx.msg), | |
| 630 | 622 | } |
| 631 | 623 | } |
| 632 | 624 | }.fmtMessageString; |
| ... | ... | @@ -882,9 +874,9 @@ const MachODumper = struct { |
| 882 | 874 | try bw.writeByte('\n'); |
| 883 | 875 | } |
| 884 | 876 | |
| 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 | 878 | // print header first |
| 887 | try bw.print( | |
| 879 | try writer.print( | |
| 888 | 880 | \\LC {d} |
| 889 | 881 | \\cmd {s} |
| 890 | 882 | \\cmdsize {d} |
| ... | ... | @@ -893,8 +885,8 @@ const MachODumper = struct { |
| 893 | 885 | switch (lc.cmd()) { |
| 894 | 886 | .SEGMENT_64 => { |
| 895 | 887 | const seg = lc.cast(macho.segment_command_64).?; |
| 896 | try bw.writeByte('\n'); | |
| 897 | try bw.print( | |
| 888 | try writer.writeByte('\n'); | |
| 889 | try writer.print( | |
| 898 | 890 | \\segname {s} |
| 899 | 891 | \\vmaddr {x} |
| 900 | 892 | \\vmsize {x} |
| ... | ... | @@ -909,8 +901,8 @@ const MachODumper = struct { |
| 909 | 901 | }); |
| 910 | 902 | |
| 911 | 903 | for (lc.getSections()) |sect| { |
| 912 | try bw.writeByte('\n'); | |
| 913 | try bw.print( | |
| 904 | try writer.writeByte('\n'); | |
| 905 | try writer.print( | |
| 914 | 906 | \\sectname {s} |
| 915 | 907 | \\addr {x} |
| 916 | 908 | \\size {x} |
| ... | ... | @@ -932,8 +924,8 @@ const MachODumper = struct { |
| 932 | 924 | .REEXPORT_DYLIB, |
| 933 | 925 | => { |
| 934 | 926 | const dylib = lc.cast(macho.dylib_command).?; |
| 935 | try bw.writeByte('\n'); | |
| 936 | try bw.print( | |
| 927 | try writer.writeByte('\n'); | |
| 928 | try writer.print( | |
| 937 | 929 | \\name {s} |
| 938 | 930 | \\timestamp {d} |
| 939 | 931 | \\current version {x} |
| ... | ... | @@ -948,16 +940,16 @@ const MachODumper = struct { |
| 948 | 940 | |
| 949 | 941 | .MAIN => { |
| 950 | 942 | const main = lc.cast(macho.entry_point_command).?; |
| 951 | try bw.writeByte('\n'); | |
| 952 | try bw.print( | |
| 943 | try writer.writeByte('\n'); | |
| 944 | try writer.print( | |
| 953 | 945 | \\entryoff {x} |
| 954 | 946 | \\stacksize {x} |
| 955 | 947 | , .{ main.entryoff, main.stacksize }); |
| 956 | 948 | }, |
| 957 | 949 | |
| 958 | 950 | .RPATH => { |
| 959 | try bw.writeByte('\n'); | |
| 960 | try bw.print( | |
| 951 | try writer.writeByte('\n'); | |
| 952 | try writer.print( | |
| 961 | 953 | \\path {s} |
| 962 | 954 | , .{ |
| 963 | 955 | lc.getRpathPathName(), |
| ... | ... | @@ -966,8 +958,8 @@ const MachODumper = struct { |
| 966 | 958 | |
| 967 | 959 | .UUID => { |
| 968 | 960 | const uuid = lc.cast(macho.uuid_command).?; |
| 969 | try bw.writeByte('\n'); | |
| 970 | try bw.print("uuid {x}", .{&uuid.uuid}); | |
| 961 | try writer.writeByte('\n'); | |
| 962 | try writer.print("uuid {x}", .{&uuid.uuid}); | |
| 971 | 963 | }, |
| 972 | 964 | |
| 973 | 965 | .DATA_IN_CODE, |
| ... | ... | @@ -975,8 +967,8 @@ const MachODumper = struct { |
| 975 | 967 | .CODE_SIGNATURE, |
| 976 | 968 | => { |
| 977 | 969 | const llc = lc.cast(macho.linkedit_data_command).?; |
| 978 | try bw.writeByte('\n'); | |
| 979 | try bw.print( | |
| 970 | try writer.writeByte('\n'); | |
| 971 | try writer.print( | |
| 980 | 972 | \\dataoff {x} |
| 981 | 973 | \\datasize {x} |
| 982 | 974 | , .{ llc.dataoff, llc.datasize }); |
| ... | ... | @@ -984,8 +976,8 @@ const MachODumper = struct { |
| 984 | 976 | |
| 985 | 977 | .DYLD_INFO_ONLY => { |
| 986 | 978 | const dlc = lc.cast(macho.dyld_info_command).?; |
| 987 | try bw.writeByte('\n'); | |
| 988 | try bw.print( | |
| 979 | try writer.writeByte('\n'); | |
| 980 | try writer.print( | |
| 989 | 981 | \\rebaseoff {x} |
| 990 | 982 | \\rebasesize {x} |
| 991 | 983 | \\bindoff {x} |
| ... | ... | @@ -1012,8 +1004,8 @@ const MachODumper = struct { |
| 1012 | 1004 | |
| 1013 | 1005 | .SYMTAB => { |
| 1014 | 1006 | const slc = lc.cast(macho.symtab_command).?; |
| 1015 | try bw.writeByte('\n'); | |
| 1016 | try bw.print( | |
| 1007 | try writer.writeByte('\n'); | |
| 1008 | try writer.print( | |
| 1017 | 1009 | \\symoff {x} |
| 1018 | 1010 | \\nsyms {x} |
| 1019 | 1011 | \\stroff {x} |
| ... | ... | @@ -1028,8 +1020,8 @@ const MachODumper = struct { |
| 1028 | 1020 | |
| 1029 | 1021 | .DYSYMTAB => { |
| 1030 | 1022 | const dlc = lc.cast(macho.dysymtab_command).?; |
| 1031 | try bw.writeByte('\n'); | |
| 1032 | try bw.print( | |
| 1023 | try writer.writeByte('\n'); | |
| 1024 | try writer.print( | |
| 1033 | 1025 | \\ilocalsym {x} |
| 1034 | 1026 | \\nlocalsym {x} |
| 1035 | 1027 | \\iextdefsym {x} |
| ... | ... | @@ -1052,8 +1044,8 @@ const MachODumper = struct { |
| 1052 | 1044 | |
| 1053 | 1045 | .BUILD_VERSION => { |
| 1054 | 1046 | const blc = lc.cast(macho.build_version_command).?; |
| 1055 | try bw.writeByte('\n'); | |
| 1056 | try bw.print( | |
| 1047 | try writer.writeByte('\n'); | |
| 1048 | try writer.print( | |
| 1057 | 1049 | \\platform {s} |
| 1058 | 1050 | \\minos {d}.{d}.{d} |
| 1059 | 1051 | \\sdk {d}.{d}.{d} |
| ... | ... | @@ -1069,12 +1061,12 @@ const MachODumper = struct { |
| 1069 | 1061 | blc.ntools, |
| 1070 | 1062 | }); |
| 1071 | 1063 | for (lc.getBuildVersionTools()) |tool| { |
| 1072 | try bw.writeByte('\n'); | |
| 1064 | try writer.writeByte('\n'); | |
| 1073 | 1065 | switch (tool.tool) { |
| 1074 | .CLANG, .SWIFT, .LD, .LLD, .ZIG => try bw.print("tool {s}\n", .{@tagName(tool.tool)}), | |
| 1075 | else => |x| try bw.print("tool {d}\n", .{@intFromEnum(x)}), | |
| 1066 | .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}), | |
| 1067 | else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}), | |
| 1076 | 1068 | } |
| 1077 | try bw.print( | |
| 1069 | try writer.print( | |
| 1078 | 1070 | \\version {d}.{d}.{d} |
| 1079 | 1071 | , .{ |
| 1080 | 1072 | tool.version >> 16, |
| ... | ... | @@ -1090,8 +1082,8 @@ const MachODumper = struct { |
| 1090 | 1082 | .VERSION_MIN_TVOS, |
| 1091 | 1083 | => { |
| 1092 | 1084 | const vlc = lc.cast(macho.version_min_command).?; |
| 1093 | try bw.writeByte('\n'); | |
| 1094 | try bw.print( | |
| 1085 | try writer.writeByte('\n'); | |
| 1086 | try writer.print( | |
| 1095 | 1087 | \\version {d}.{d}.{d} |
| 1096 | 1088 | \\sdk {d}.{d}.{d} |
| 1097 | 1089 | , .{ |
| ... | ... | @@ -1943,58 +1935,58 @@ const ElfDumper = struct { |
| 1943 | 1935 | try bw.print("entry {x}\n", .{ctx.hdr.e_entry}); |
| 1944 | 1936 | } |
| 1945 | 1937 | |
| 1946 | fn dumpPhdrs(ctx: ObjectContext, bw: *Writer) !void { | |
| 1938 | fn dumpPhdrs(ctx: ObjectContext, writer: *Writer) !void { | |
| 1947 | 1939 | if (ctx.phdrs.len == 0) return; |
| 1948 | 1940 | |
| 1949 | try bw.writeAll("program headers\n"); | |
| 1941 | try writer.writeAll("program headers\n"); | |
| 1950 | 1942 | |
| 1951 | 1943 | for (ctx.phdrs, 0..) |phdr, phndx| { |
| 1952 | try bw.print("phdr {d}\n", .{phndx}); | |
| 1953 | try bw.print("type {f}\n", .{fmtPhType(phdr.p_type)}); | |
| 1954 | try bw.print("vaddr {x}\n", .{phdr.p_vaddr}); | |
| 1955 | try bw.print("paddr {x}\n", .{phdr.p_paddr}); | |
| 1956 | try bw.print("offset {x}\n", .{phdr.p_offset}); | |
| 1957 | try bw.print("memsz {x}\n", .{phdr.p_memsz}); | |
| 1958 | try bw.print("filesz {x}\n", .{phdr.p_filesz}); | |
| 1959 | try bw.print("align {x}\n", .{phdr.p_align}); | |
| 1944 | try writer.print("phdr {d}\n", .{phndx}); | |
| 1945 | try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)}); | |
| 1946 | try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); | |
| 1947 | try writer.print("paddr {x}\n", .{phdr.p_paddr}); | |
| 1948 | try writer.print("offset {x}\n", .{phdr.p_offset}); | |
| 1949 | try writer.print("memsz {x}\n", .{phdr.p_memsz}); | |
| 1950 | try writer.print("filesz {x}\n", .{phdr.p_filesz}); | |
| 1951 | try writer.print("align {x}\n", .{phdr.p_align}); | |
| 1960 | 1952 | |
| 1961 | 1953 | { |
| 1962 | 1954 | const flags = phdr.p_flags; |
| 1963 | try bw.writeAll("flags"); | |
| 1964 | if (flags > 0) try bw.writeByte(' '); | |
| 1955 | try writer.writeAll("flags"); | |
| 1956 | if (flags > 0) try writer.writeByte(' '); | |
| 1965 | 1957 | if (flags & elf.PF_R != 0) { |
| 1966 | try bw.writeByte('R'); | |
| 1958 | try writer.writeByte('R'); | |
| 1967 | 1959 | } |
| 1968 | 1960 | if (flags & elf.PF_W != 0) { |
| 1969 | try bw.writeByte('W'); | |
| 1961 | try writer.writeByte('W'); | |
| 1970 | 1962 | } |
| 1971 | 1963 | if (flags & elf.PF_X != 0) { |
| 1972 | try bw.writeByte('E'); | |
| 1964 | try writer.writeByte('E'); | |
| 1973 | 1965 | } |
| 1974 | 1966 | if (flags & elf.PF_MASKOS != 0) { |
| 1975 | try bw.writeAll("OS"); | |
| 1967 | try writer.writeAll("OS"); | |
| 1976 | 1968 | } |
| 1977 | 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 | } |
| 1984 | 1976 | |
| 1985 | fn dumpShdrs(ctx: ObjectContext, bw: *Writer) !void { | |
| 1977 | fn dumpShdrs(ctx: ObjectContext, writer: *Writer) !void { | |
| 1986 | 1978 | if (ctx.shdrs.len == 0) return; |
| 1987 | 1979 | |
| 1988 | try bw.writeAll("section headers\n"); | |
| 1980 | try writer.writeAll("section headers\n"); | |
| 1989 | 1981 | |
| 1990 | 1982 | for (ctx.shdrs, 0..) |shdr, shndx| { |
| 1991 | try bw.print("shdr {d}\n", .{shndx}); | |
| 1992 | try bw.print("name {s}\n", .{ctx.getSectionName(shndx)}); | |
| 1993 | try bw.print("type {f}\n", .{fmtShType(shdr.sh_type)}); | |
| 1994 | try bw.print("addr {x}\n", .{shdr.sh_addr}); | |
| 1995 | try bw.print("offset {x}\n", .{shdr.sh_offset}); | |
| 1996 | try bw.print("size {x}\n", .{shdr.sh_size}); | |
| 1997 | try bw.print("addralign {x}\n", .{shdr.sh_addralign}); | |
| 1983 | try writer.print("shdr {d}\n", .{shndx}); | |
| 1984 | try writer.print("name {s}\n", .{ctx.getSectionName(shndx)}); | |
| 1985 | try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)}); | |
| 1986 | try writer.print("addr {x}\n", .{shdr.sh_addr}); | |
| 1987 | try writer.print("offset {x}\n", .{shdr.sh_offset}); | |
| 1988 | try writer.print("size {x}\n", .{shdr.sh_size}); | |
| 1989 | try writer.print("addralign {x}\n", .{shdr.sh_addralign}); | |
| 1998 | 1990 | // TODO dump formatted sh_flags |
| 1999 | 1991 | } |
| 2000 | 1992 | } |
| ... | ... | @@ -2263,16 +2255,11 @@ const ElfDumper = struct { |
| 2263 | 2255 | return str[0..std.mem.indexOfScalar(u8, str, 0).?]; |
| 2264 | 2256 | } |
| 2265 | 2257 | |
| 2266 | fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) { | |
| 2258 | fn fmtShType(sh_type: u32) std.fmt.Formatter(u32, formatShType) { | |
| 2267 | 2259 | return .{ .data = sh_type }; |
| 2268 | 2260 | } |
| 2269 | 2261 | |
| 2270 | fn formatShType( | |
| 2271 | sh_type: u32, | |
| 2272 | bw: *Writer, | |
| 2273 | comptime unused_fmt_string: []const u8, | |
| 2274 | ) !void { | |
| 2275 | _ = unused_fmt_string; | |
| 2262 | fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void { | |
| 2276 | 2263 | const name = switch (sh_type) { |
| 2277 | 2264 | elf.SHT_NULL => "NULL", |
| 2278 | 2265 | elf.SHT_PROGBITS => "PROGBITS", |
| ... | ... | @@ -2298,26 +2285,21 @@ const ElfDumper = struct { |
| 2298 | 2285 | elf.SHT_GNU_VERNEED => "VERNEED", |
| 2299 | 2286 | elf.SHT_GNU_VERSYM => "VERSYM", |
| 2300 | 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 | 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 | 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 | 2293 | } else "UNKNOWN", |
| 2307 | 2294 | }; |
| 2308 | try bw.writeAll(name); | |
| 2295 | try writer.writeAll(name); | |
| 2309 | 2296 | } |
| 2310 | 2297 | |
| 2311 | fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) { | |
| 2298 | fn fmtPhType(ph_type: u32) std.fmt.Formatter(u32, formatPhType) { | |
| 2312 | 2299 | return .{ .data = ph_type }; |
| 2313 | 2300 | } |
| 2314 | 2301 | |
| 2315 | fn formatPhType( | |
| 2316 | ph_type: u32, | |
| 2317 | bw: *Writer, | |
| 2318 | comptime unused_fmt_string: []const u8, | |
| 2319 | ) !void { | |
| 2320 | _ = unused_fmt_string; | |
| 2302 | fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void { | |
| 2321 | 2303 | const p_type = switch (ph_type) { |
| 2322 | 2304 | elf.PT_NULL => "NULL", |
| 2323 | 2305 | elf.PT_LOAD => "LOAD", |
| ... | ... | @@ -2332,12 +2314,12 @@ const ElfDumper = struct { |
| 2332 | 2314 | elf.PT_GNU_STACK => "GNU_STACK", |
| 2333 | 2315 | elf.PT_GNU_RELRO => "GNU_RELRO", |
| 2334 | 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 | 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 | 2320 | } else "UNKNOWN", |
| 2339 | 2321 | }; |
| 2340 | try bw.writeAll(p_type); | |
| 2322 | try writer.writeAll(p_type); | |
| 2341 | 2323 | } |
| 2342 | 2324 | }; |
| 2343 | 2325 |
lib/std/Build/Step/Compile.zig+6-10| ... | ... | @@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking |
| 1017 | 1017 | const maybe_path: ?*GeneratedFile = @field(compile, tag_name); |
| 1018 | 1018 | |
| 1019 | 1019 | const generated_file = maybe_path orelse { |
| 1020 | std.debug.lockStdErr(); | |
| 1021 | const stderr: fs.File = .stderr(); | |
| 1022 | ||
| 1023 | std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {}; | |
| 1024 | ||
| 1020 | const w = std.debug.lockStderrWriter(&.{}); | |
| 1021 | std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {}; | |
| 1022 | std.debug.unlockStderrWriter(); | |
| 1025 | 1023 | @panic("missing emit option for " ++ tag_name); |
| 1026 | 1024 | }; |
| 1027 | 1025 | |
| 1028 | 1026 | const path = generated_file.path orelse { |
| 1029 | std.debug.lockStdErr(); | |
| 1030 | const stderr: fs.File = .stderr(); | |
| 1031 | ||
| 1032 | std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {}; | |
| 1033 | ||
| 1027 | const w = std.debug.lockStderrWriter(&.{}); | |
| 1028 | std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {}; | |
| 1029 | std.debug.unlockStderrWriter(); | |
| 1034 | 1030 | @panic(tag_name ++ " is null. Is there a missing step dependency?"); |
| 1035 | 1031 | }; |
| 1036 | 1032 |
lib/std/Build/Step/ConfigHeader.zig+8-8| ... | ... | @@ -198,7 +198,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 198 | 198 | |
| 199 | 199 | var aw: std.io.Writer.Allocating = .init(gpa); |
| 200 | 200 | defer aw.deinit(); |
| 201 | const bw = &aw.interface; | |
| 201 | const bw = &aw.writer; | |
| 202 | 202 | |
| 203 | 203 | const header_text = "This file was generated by ConfigHeader using the Zig Build System."; |
| 204 | 204 | const c_generated_line = "/* " ++ header_text ++ " */\n"; |
| ... | ... | @@ -335,7 +335,7 @@ fn render_autoconf_at( |
| 335 | 335 | ) !void { |
| 336 | 336 | const build = step.owner; |
| 337 | 337 | const allocator = build.allocator; |
| 338 | const bw = &aw.interface; | |
| 338 | const bw = &aw.writer; | |
| 339 | 339 | |
| 340 | 340 | const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); |
| 341 | 341 | for (used) |*u| u.* = false; |
| ... | ... | @@ -553,7 +553,7 @@ fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void { |
| 553 | 553 | .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }), |
| 554 | 554 | .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }), |
| 555 | 555 | // 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 | } |
| 559 | 559 | |
| ... | ... | @@ -565,7 +565,7 @@ fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void { |
| 565 | 565 | .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }), |
| 566 | 566 | .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }), |
| 567 | 567 | // 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 | } |
| 571 | 571 | |
| ... | ... | @@ -753,17 +753,17 @@ fn testReplaceVariablesAutoconfAt( |
| 753 | 753 | expected: []const u8, |
| 754 | 754 | values: std.StringArrayHashMap(Value), |
| 755 | 755 | ) !void { |
| 756 | var output: std.io.Writer.Allocating = .init(allocator); | |
| 757 | defer output.deinit(); | |
| 756 | var aw: std.io.Writer.Allocating = .init(allocator); | |
| 757 | defer aw.deinit(); | |
| 758 | 758 | |
| 759 | 759 | const used = try allocator.alloc(bool, values.count()); |
| 760 | 760 | for (used) |*u| u.* = false; |
| 761 | 761 | defer allocator.free(used); |
| 762 | 762 | |
| 763 | try expand_variables_autoconf_at(&output.interface, contents, values, used); | |
| 763 | try expand_variables_autoconf_at(&aw.writer, contents, values, used); | |
| 764 | 764 | |
| 765 | 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 | } |
| 768 | 768 | |
| 769 | 769 | fn testReplaceVariablesCMake( |
lib/std/Build/Step/Options.zig+33-22| ... | ... | @@ -62,7 +62,7 @@ fn printType( |
| 62 | 62 | |
| 63 | 63 | for (value) |slice| { |
| 64 | 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 | } |
| 67 | 67 | |
| 68 | 68 | if (name != null) { |
| ... | ... | @@ -76,28 +76,28 @@ fn printType( |
| 76 | 76 | []const u8 => { |
| 77 | 77 | if (name) |some| { |
| 78 | 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 | 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 | 84 | return out.appendSlice(gpa, "\n"); |
| 85 | 85 | }, |
| 86 | 86 | [:0]const u8 => { |
| 87 | 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 | 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 | 92 | return out.appendSlice(gpa, "\n"); |
| 93 | 93 | }, |
| 94 | 94 | ?[]const u8 => { |
| 95 | 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 | } |
| 98 | 98 | |
| 99 | 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 | 101 | } else { |
| 102 | 102 | try out.appendSlice(gpa, "null"); |
| 103 | 103 | } |
| ... | ... | @@ -111,11 +111,11 @@ fn printType( |
| 111 | 111 | }, |
| 112 | 112 | ?[:0]const u8 => { |
| 113 | 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 | } |
| 116 | 116 | |
| 117 | 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 | 119 | } else { |
| 120 | 120 | try out.appendSlice(gpa, "null"); |
| 121 | 121 | } |
| ... | ... | @@ -142,11 +142,11 @@ fn printType( |
| 142 | 142 | |
| 143 | 143 | if (value.pre) |some| { |
| 144 | 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 | 147 | if (value.build) |some| { |
| 148 | 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 | } |
| 151 | 151 | |
| 152 | 152 | if (name != null) { |
| ... | ... | @@ -162,7 +162,7 @@ fn printType( |
| 162 | 162 | switch (@typeInfo(T)) { |
| 163 | 163 | .array => { |
| 164 | 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 | } |
| 167 | 167 | |
| 168 | 168 | try out.print(gpa, "{s} {{\n", .{@typeName(T)}); |
| ... | ... | @@ -186,7 +186,7 @@ fn printType( |
| 186 | 186 | } |
| 187 | 187 | |
| 188 | 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 | } |
| 191 | 191 | |
| 192 | 192 | try out.print(gpa, "&[_]{s} {{\n", .{@typeName(p.child)}); |
| ... | ... | @@ -206,7 +206,7 @@ fn printType( |
| 206 | 206 | }, |
| 207 | 207 | .optional => { |
| 208 | 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 | } |
| 211 | 211 | |
| 212 | 212 | if (value) |inner| { |
| ... | ... | @@ -243,10 +243,10 @@ fn printType( |
| 243 | 243 | try printEnum(options, out, T, info, indent); |
| 244 | 244 | |
| 245 | 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 | 247 | std.zig.fmtId(some), |
| 248 | 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 | 252 | return; |
| ... | ... | @@ -295,7 +295,9 @@ fn printEnum( |
| 295 | 295 | |
| 296 | 296 | inline for (val.fields) |field| { |
| 297 | 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 | } |
| 300 | 302 | |
| 301 | 303 | if (!val.is_exhaustive) { |
| ... | ... | @@ -313,7 +315,7 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T: |
| 313 | 315 | if (gop.found_existing) return; |
| 314 | 316 | |
| 315 | 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))}); | |
| 317 | 319 | |
| 318 | 320 | switch (val.layout) { |
| 319 | 321 | .@"extern" => try out.appendSlice(gpa, "extern struct"), |
| ... | ... | @@ -330,9 +332,15 @@ fn printStruct(options: *Options, out: *std.ArrayListUnmanaged(u8), comptime T: |
| 330 | 332 | |
| 331 | 333 | // If the type name doesn't contains a '.' the type is from zig builtins. |
| 332 | 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 | 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 | } |
| 337 | 345 | |
| 338 | 346 | if (field.defaultValue()) |default_value| { |
| ... | ... | @@ -377,7 +385,9 @@ fn printStructValue( |
| 377 | 385 | } else { |
| 378 | 386 | inline for (struct_val.fields) |field| { |
| 379 | 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 | }); | |
| 381 | 391 | |
| 382 | 392 | const field_name = @field(val, field.name); |
| 383 | 393 | switch (@typeInfo(@TypeOf(field_name))) { |
| ... | ... | @@ -405,7 +415,8 @@ pub fn addOptionPath( |
| 405 | 415 | name: []const u8, |
| 406 | 416 | path: LazyPath, |
| 407 | 417 | ) void { |
| 408 | options.args.append(.{ | |
| 418 | const arena = options.step.owner.allocator; | |
| 419 | options.args.append(arena, .{ | |
| 409 | 420 | .name = options.step.owner.dupe(name), |
| 410 | 421 | .path = path.dupe(options.step.owner), |
| 411 | 422 | }) catch @panic("OOM"); |
lib/std/Build/Step/Run.zig+9-8| ... | ... | @@ -1015,16 +1015,17 @@ fn populateGeneratedPaths( |
| 1015 | 1015 | } |
| 1016 | 1016 | } |
| 1017 | 1017 | |
| 1018 | fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 1019 | comptime assert(fmt.len == 0); | |
| 1018 | fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 1020 | 1019 | if (term) |t| switch (t) { |
| 1021 | .Exited => |code| try w.print("exited with code {}", .{code}), | |
| 1022 | .Signal => |sig| try w.print("terminated with signal {}", .{sig}), | |
| 1023 | .Stopped => |sig| try w.print("stopped with signal {}", .{sig}), | |
| 1024 | .Unknown => |code| try w.print("terminated for unknown reason with code {}", .{code}), | |
| 1025 | } else try w.writeAll("exited with any code"); | |
| 1020 | .Exited => |code| try w.print("exited with code {d}", .{code}), | |
| 1021 | .Signal => |sig| try w.print("terminated with signal {d}", .{sig}), | |
| 1022 | .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}), | |
| 1023 | .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}), | |
| 1024 | } else { | |
| 1025 | try w.writeAll("exited with any code"); | |
| 1026 | } | |
| 1026 | 1027 | } |
| 1027 | fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { | |
| 1028 | fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(?std.process.Child.Term, formatTerm) { | |
| 1028 | 1029 | return .{ .data = term }; |
| 1029 | 1030 | } |
| 1030 | 1031 |
lib/std/Build/Watch.zig+1-1| ... | ... | @@ -659,7 +659,7 @@ const Os = switch (builtin.os.tag) { |
| 659 | 659 | path.root_dir.handle.fd |
| 660 | 660 | else |
| 661 | 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 | 664 | // Empirically the dir has to stay open or else no events are triggered. |
| 665 | 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 | 150 | }; |
| 151 | 151 | } |
| 152 | 152 | |
| 153 | pub fn format( | |
| 154 | self: Version, | |
| 155 | bw: *std.io.Writer, | |
| 156 | comptime fmt: []const u8, | |
| 157 | ) !void { | |
| 158 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); | |
| 159 | try bw.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); | |
| 160 | if (self.pre) |pre| try bw.print("-{s}", .{pre}); | |
| 161 | if (self.build) |build| try bw.print("+{s}", .{build}); | |
| 153 | pub fn format(self: Version, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 154 | try w.print("{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); | |
| 155 | if (self.pre) |pre| try w.print("-{s}", .{pre}); | |
| 156 | if (self.build) |build| try w.print("+{s}", .{build}); | |
| 162 | 157 | } |
| 163 | 158 | |
| 164 | 159 | const expect = std.testing.expect; |
| ... | ... | @@ -200,7 +195,7 @@ test format { |
| 200 | 195 | "1.0.0+0.build.1-rc.10000aaa-kk-0.1", |
| 201 | 196 | "5.4.0-1018-raspi", |
| 202 | 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)}); | |
| 204 | 199 | |
| 205 | 200 | // Invalid version strings should be rejected. |
| 206 | 201 | for ([_][]const u8{ |
| ... | ... | @@ -267,12 +262,12 @@ test format { |
| 267 | 262 | // Valid version string that may overflow. |
| 268 | 263 | const big_valid = "99999999999999999999999.999999999999999999.99999999999999999"; |
| 269 | 264 | if (parse(big_valid)) |ver| { |
| 270 | try std.testing.expectFmt(big_valid, "{}", .{ver}); | |
| 265 | try std.testing.expectFmt(big_valid, "{f}", .{ver}); | |
| 271 | 266 | } else |err| try expect(err == error.Overflow); |
| 272 | 267 | |
| 273 | 268 | // Invalid version string that may overflow. |
| 274 | 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 | } |
| 277 | 272 | |
| 278 | 273 | test "precedence" { |
lib/std/Target.zig+8-19| ... | ... | @@ -301,24 +301,13 @@ pub const Os = struct { |
| 301 | 301 | |
| 302 | 302 | /// This function is defined to serialize a Zig source code representation of this |
| 303 | 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 { | |
| 305 | const maybe_name = std.enums.tagName(WindowsVersion, ver); | |
| 306 | if (comptime std.mem.eql(u8, fmt_str, "s")) { | |
| 307 | if (maybe_name) |name| | |
| 308 | try bw.print(".{s}", .{name}) | |
| 309 | else | |
| 310 | try bw.print(".{d}", .{@intFromEnum(ver)}); | |
| 311 | } else if (comptime std.mem.eql(u8, fmt_str, "c")) { | |
| 312 | if (maybe_name) |name| | |
| 313 | try bw.print(".{s}", .{name}) | |
| 314 | else | |
| 315 | try bw.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)}); | |
| 316 | } else if (fmt_str.len == 0) { | |
| 317 | if (maybe_name) |name| | |
| 318 | try bw.print("WindowsVersion.{s}", .{name}) | |
| 319 | else | |
| 320 | try bw.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)}); | |
| 321 | } else std.fmt.invalidFmtError(fmt_str, ver); | |
| 304 | pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 305 | if (std.enums.tagName(WindowsVersion, wv)) |name| { | |
| 306 | var vecs: [2][]const u8 = .{ ".", name }; | |
| 307 | return w.writeVecAll(&vecs); | |
| 308 | } else { | |
| 309 | return w.print("@enumFromInt(0x{X:0>8})", .{wv}); | |
| 310 | } | |
| 322 | 311 | } |
| 323 | 312 | }; |
| 324 | 313 | |
| ... | ... | @@ -1686,7 +1675,7 @@ pub const Cpu = struct { |
| 1686 | 1675 | pub fn fromCallingConvention(cc: std.builtin.CallingConvention.Tag) []const Arch { |
| 1687 | 1676 | return switch (cc) { |
| 1688 | 1677 | .auto, |
| 1689 | .@"async", | |
| 1678 | .async, | |
| 1690 | 1679 | .naked, |
| 1691 | 1680 | .@"inline", |
| 1692 | 1681 | => unreachable, |
lib/std/Thread.zig+21-1| ... | ... | @@ -165,10 +165,18 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 165 | 165 | const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); |
| 166 | 166 | const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); |
| 167 | 167 | defer file.close(); |
| 168 | <<<<<<< HEAD | |
| 168 | 169 | var fw = file.writer(&.{}); |
| 169 | 170 | fw.interface.writeAll(name) catch |err| switch (err) { |
| 170 | 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 | 180 | return; |
| 173 | 181 | }, |
| 174 | 182 | .windows => { |
| ... | ... | @@ -280,11 +288,23 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co |
| 280 | 288 | const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); |
| 281 | 289 | const file = try std.fs.cwd().openFile(path, .{}); |
| 282 | 290 | defer file.close(); |
| 291 | <<<<<<< HEAD | |
| 283 | 292 | var fr = file.reader(&.{}); |
| 284 | 293 | const n = fr.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) { |
| 285 | 294 | error.ReadFailed => return fr.err.?, |
| 286 | 295 | }; |
| 287 | 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 | 309 | .windows => { |
| 290 | 310 | const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len); |
| ... | ... | @@ -1164,7 +1184,7 @@ const LinuxThreadImpl = struct { |
| 1164 | 1184 | |
| 1165 | 1185 | fn getCurrentId() Id { |
| 1166 | 1186 | return tls_thread_id orelse { |
| 1167 | const tid = @as(u32, @bitCast(linux.gettid())); | |
| 1187 | const tid: u32 = @bitCast(linux.gettid()); | |
| 1168 | 1188 | tls_thread_id = tid; |
| 1169 | 1189 | return tid; |
| 1170 | 1190 | }; |
lib/std/Uri.zig+148-106| ... | ... | @@ -3,12 +3,10 @@ |
| 3 | 3 | |
| 4 | 4 | const std = @import("std.zig"); |
| 5 | 5 | const testing = std.testing; |
| 6 | const Uri = @This(); | |
| 6 | 7 | const Allocator = std.mem.Allocator; |
| 7 | const assert = std.debug.assert; | |
| 8 | 8 | const Writer = std.io.Writer; |
| 9 | 9 | |
| 10 | const Uri = @This(); | |
| 11 | ||
| 12 | 10 | scheme: []const u8, |
| 13 | 11 | user: ?Component = null, |
| 14 | 12 | password: ?Component = null, |
| ... | ... | @@ -65,7 +63,7 @@ pub const Component = union(enum) { |
| 65 | 63 | return switch (component) { |
| 66 | 64 | .raw => |raw| raw, |
| 67 | 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 | 67 | else |
| 70 | 68 | percent_encoded, |
| 71 | 69 | }; |
| ... | ... | @@ -85,16 +83,9 @@ pub const Component = union(enum) { |
| 85 | 83 | }; |
| 86 | 84 | } |
| 87 | 85 | |
| 88 | pub fn format(component: Component, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 89 | if (fmt.len == 0) { | |
| 90 | try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{ | |
| 91 | @tagName(component), | |
| 92 | std.zig.fmtEscapes(switch (component) { | |
| 93 | .raw, .percent_encoded => |string| string, | |
| 94 | }), | |
| 95 | }); | |
| 96 | } else if (comptime std.mem.eql(u8, fmt, "raw")) switch (component) { | |
| 97 | .raw => |raw| try bw.writeAll(raw), | |
| 86 | pub fn formatRaw(component: Component, w: *Writer) Writer.Error!void { | |
| 87 | switch (component) { | |
| 88 | .raw => |raw| try w.writeAll(raw), | |
| 98 | 89 | .percent_encoded => |percent_encoded| { |
| 99 | 90 | var start: usize = 0; |
| 100 | 91 | var index: usize = 0; |
| ... | ... | @@ -103,51 +94,75 @@ pub const Component = union(enum) { |
| 103 | 94 | if (percent_encoded.len - index < 2) continue; |
| 104 | 95 | const percent_encoded_char = |
| 105 | 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 | 98 | percent_encoded[start..percent], |
| 108 | 99 | percent_encoded_char, |
| 109 | 100 | }); |
| 110 | 101 | start = percent + 3; |
| 111 | 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) { | |
| 116 | .raw => |raw| try percentEncode(bw, raw, isUnreserved), | |
| 117 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 118 | } else if (comptime std.mem.eql(u8, fmt, "user")) switch (component) { | |
| 119 | .raw => |raw| try percentEncode(bw, raw, isUserChar), | |
| 120 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 121 | } else if (comptime std.mem.eql(u8, fmt, "password")) switch (component) { | |
| 122 | .raw => |raw| try percentEncode(bw, raw, isPasswordChar), | |
| 123 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 124 | } else if (comptime std.mem.eql(u8, fmt, "host")) switch (component) { | |
| 125 | .raw => |raw| try percentEncode(bw, raw, isHostChar), | |
| 126 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 127 | } else if (comptime std.mem.eql(u8, fmt, "path")) switch (component) { | |
| 128 | .raw => |raw| try percentEncode(bw, raw, isPathChar), | |
| 129 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 130 | } else if (comptime std.mem.eql(u8, fmt, "query")) switch (component) { | |
| 131 | .raw => |raw| try percentEncode(bw, raw, isQueryChar), | |
| 132 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 133 | } else if (comptime std.mem.eql(u8, fmt, "fragment")) switch (component) { | |
| 134 | .raw => |raw| try percentEncode(bw, raw, isFragmentChar), | |
| 135 | .percent_encoded => |percent_encoded| try bw.writeAll(percent_encoded), | |
| 136 | } else @compileError("invalid format string '" ++ fmt ++ "'"); | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | pub fn formatEscaped(component: Component, w: *Writer) Writer.Error!void { | |
| 110 | switch (component) { | |
| 111 | .raw => |raw| try percentEncode(w, raw, isUnreserved), | |
| 112 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 113 | } | |
| 114 | } | |
| 115 | ||
| 116 | pub fn formatUser(component: Component, w: *Writer) Writer.Error!void { | |
| 117 | switch (component) { | |
| 118 | .raw => |raw| try percentEncode(w, raw, isUserChar), | |
| 119 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | pub fn formatPassword(component: Component, w: *Writer) Writer.Error!void { | |
| 124 | switch (component) { | |
| 125 | .raw => |raw| try percentEncode(w, raw, isPasswordChar), | |
| 126 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 127 | } | |
| 128 | } | |
| 129 | ||
| 130 | pub fn formatHost(component: Component, w: *Writer) Writer.Error!void { | |
| 131 | switch (component) { | |
| 132 | .raw => |raw| try percentEncode(w, raw, isHostChar), | |
| 133 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | pub fn formatPath(component: Component, w: *Writer) Writer.Error!void { | |
| 138 | switch (component) { | |
| 139 | .raw => |raw| try percentEncode(w, raw, isPathChar), | |
| 140 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 141 | } | |
| 137 | 142 | } |
| 138 | 143 | |
| 139 | pub fn percentEncode( | |
| 140 | bw: *Writer, | |
| 141 | raw: []const u8, | |
| 142 | comptime isValidChar: fn (u8) bool, | |
| 143 | ) Writer.Error!void { | |
| 144 | pub fn formatQuery(component: Component, w: *Writer) Writer.Error!void { | |
| 145 | switch (component) { | |
| 146 | .raw => |raw| try percentEncode(w, raw, isQueryChar), | |
| 147 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 148 | } | |
| 149 | } | |
| 150 | ||
| 151 | pub fn formatFragment(component: Component, w: *Writer) Writer.Error!void { | |
| 152 | switch (component) { | |
| 153 | .raw => |raw| try percentEncode(w, raw, isFragmentChar), | |
| 154 | .percent_encoded => |percent_encoded| try w.writeAll(percent_encoded), | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | pub fn percentEncode(w: *Writer, raw: []const u8, comptime isValidChar: fn (u8) bool) Writer.Error!void { | |
| 144 | 159 | var start: usize = 0; |
| 145 | 160 | for (raw, 0..) |char, index| { |
| 146 | 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 | 163 | start = index + 1; |
| 149 | 164 | } |
| 150 | try bw.writeAll(raw[start..]); | |
| 165 | try w.writeAll(raw[start..]); | |
| 151 | 166 | } |
| 152 | 167 | }; |
| 153 | 168 | |
| ... | ... | @@ -264,76 +279,91 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri { |
| 264 | 279 | return uri; |
| 265 | 280 | } |
| 266 | 281 | |
| 267 | pub const WriteToStreamOptions = struct { | |
| 268 | /// When true, include the scheme part of the URI. | |
| 269 | scheme: bool = false, | |
| 270 | /// When true, include the user and password part of the URI. Ignored if `authority` is false. | |
| 271 | authentication: bool = false, | |
| 272 | /// When true, include the authority part of the URI. | |
| 273 | authority: bool = false, | |
| 274 | /// When true, include the path part of the URI. | |
| 275 | path: bool = false, | |
| 276 | /// When true, include the query part of the URI. Ignored when `path` is false. | |
| 277 | query: bool = false, | |
| 278 | /// When true, include the fragment part of the URI. Ignored when `path` is false. | |
| 279 | fragment: bool = false, | |
| 280 | /// When true, include the port part of the URI. Ignored when `port` is null. | |
| 281 | port: bool = true, | |
| 282 | }; | |
| 282 | pub fn format(uri: *const Uri, writer: *Writer) Writer.Error!void { | |
| 283 | return writeToStream(uri, writer, .all); | |
| 284 | } | |
| 283 | 285 | |
| 284 | pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *Writer) Writer.Error!void { | |
| 285 | if (options.scheme) { | |
| 286 | try bw.print("{s}:", .{uri.scheme}); | |
| 287 | if (options.authority and uri.host != null) { | |
| 288 | try bw.writeAll("//"); | |
| 286 | pub fn writeToStream(uri: *const Uri, writer: *Writer, flags: Format.Flags) Writer.Error!void { | |
| 287 | if (flags.scheme) { | |
| 288 | try writer.print("{s}:", .{uri.scheme}); | |
| 289 | if (flags.authority and uri.host != null) { | |
| 290 | try writer.writeAll("//"); | |
| 289 | 291 | } |
| 290 | 292 | } |
| 291 | if (options.authority) { | |
| 292 | if (options.authentication and uri.host != null) { | |
| 293 | if (flags.authority) { | |
| 294 | if (flags.authentication and uri.host != null) { | |
| 293 | 295 | if (uri.user) |user| { |
| 294 | try bw.print("{fuser}", .{user}); | |
| 296 | try user.formatUser(writer); | |
| 295 | 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 | 304 | if (uri.host) |host| { |
| 302 | try bw.print("{fhost}", .{host}); | |
| 303 | if (options.port) { | |
| 304 | if (uri.port) |port| try bw.print(":{d}", .{port}); | |
| 305 | try host.formatHost(writer); | |
| 306 | if (flags.port) { | |
| 307 | if (uri.port) |port| try writer.print(":{d}", .{port}); | |
| 305 | 308 | } |
| 306 | 309 | } |
| 307 | 310 | } |
| 308 | if (options.path) { | |
| 309 | try bw.print("{fpath}", .{ | |
| 310 | if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path, | |
| 311 | }); | |
| 312 | if (options.query) { | |
| 313 | if (uri.query) |query| try bw.print("?{fquery}", .{query}); | |
| 311 | if (flags.path) { | |
| 312 | const uri_path: Component = if (uri.path.isEmpty()) .{ .percent_encoded = "/" } else uri.path; | |
| 313 | try uri_path.formatPath(writer); | |
| 314 | if (flags.query) { | |
| 315 | if (uri.query) |query| { | |
| 316 | try writer.writeByte('?'); | |
| 317 | try query.formatQuery(writer); | |
| 318 | } | |
| 314 | 319 | } |
| 315 | if (options.fragment) { | |
| 316 | if (uri.fragment) |fragment| try bw.print("#{ffragment}", .{fragment}); | |
| 320 | if (flags.fragment) { | |
| 321 | if (uri.fragment) |fragment| { | |
| 322 | try writer.writeByte('#'); | |
| 323 | try fragment.formatFragment(writer); | |
| 324 | } | |
| 317 | 325 | } |
| 318 | 326 | } |
| 319 | 327 | } |
| 320 | 328 | |
| 321 | pub fn format(uri: Uri, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 322 | const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0; | |
| 323 | const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0; | |
| 324 | const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0; | |
| 325 | const path = comptime std.mem.indexOfScalar(u8, fmt, '/') != null or fmt.len == 0; | |
| 326 | const query = comptime std.mem.indexOfScalar(u8, fmt, '?') != null or fmt.len == 0; | |
| 327 | const fragment = comptime std.mem.indexOfScalar(u8, fmt, '#') != null or fmt.len == 0; | |
| 329 | pub const Format = struct { | |
| 330 | uri: *const Uri, | |
| 331 | flags: Flags = .{}, | |
| 332 | ||
| 333 | pub const Flags = struct { | |
| 334 | /// When true, include the scheme part of the URI. | |
| 335 | scheme: bool = false, | |
| 336 | /// When true, include the user and password part of the URI. Ignored if `authority` is false. | |
| 337 | authentication: bool = false, | |
| 338 | /// When true, include the authority part of the URI. | |
| 339 | authority: bool = false, | |
| 340 | /// When true, include the path part of the URI. | |
| 341 | path: bool = false, | |
| 342 | /// When true, include the query part of the URI. Ignored when `path` is false. | |
| 343 | query: bool = false, | |
| 344 | /// When true, include the fragment part of the URI. Ignored when `path` is false. | |
| 345 | fragment: bool = false, | |
| 346 | /// When true, include the port part of the URI. Ignored when `port` is null. | |
| 347 | port: bool = true, | |
| 348 | ||
| 349 | pub const all: Flags = .{ | |
| 350 | .scheme = true, | |
| 351 | .authentication = true, | |
| 352 | .authority = true, | |
| 353 | .path = true, | |
| 354 | .query = true, | |
| 355 | .fragment = true, | |
| 356 | .port = true, | |
| 357 | }; | |
| 358 | }; | |
| 328 | 359 | |
| 329 | return writeToStream(uri, .{ | |
| 330 | .scheme = scheme, | |
| 331 | .authentication = authentication, | |
| 332 | .authority = authority, | |
| 333 | .path = path, | |
| 334 | .query = query, | |
| 335 | .fragment = fragment, | |
| 336 | }, bw); | |
| 360 | pub fn default(f: Format, writer: *Writer) Writer.Error!void { | |
| 361 | return writeToStream(f.uri, writer, f.flags); | |
| 362 | } | |
| 363 | }; | |
| 364 | ||
| 365 | pub fn fmt(uri: *const Uri, flags: Format.Flags) std.fmt.Formatter(Format, Format.default) { | |
| 366 | return .{ .data = .{ .uri = uri, .flags = flags } }; | |
| 337 | 367 | } |
| 338 | 368 | |
| 339 | 369 | /// The return value will contain strings pointing into the original `text`. |
| ... | ... | @@ -464,9 +494,8 @@ test remove_dot_segments { |
| 464 | 494 | fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component { |
| 465 | 495 | var aux: Writer = .fixed(aux_buf.*); |
| 466 | 496 | if (!base.isEmpty()) { |
| 467 | aux.print("{fpath}", .{base}) catch return error.NoSpaceLeft; | |
| 468 | aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse | |
| 469 | return remove_dot_segments(new); | |
| 497 | base.formatPath(&aux) catch return error.NoSpaceLeft; | |
| 498 | aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse return remove_dot_segments(new); | |
| 470 | 499 | } |
| 471 | 500 | aux.print("/{s}", .{new}) catch return error.NoSpaceLeft; |
| 472 | 501 | const merged_path = remove_dot_segments(aux.buffered()); |
| ... | ... | @@ -745,8 +774,11 @@ test "Special test" { |
| 745 | 774 | test "URI percent encoding" { |
| 746 | 775 | try std.testing.expectFmt( |
| 747 | 776 | "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad", |
| 748 | "{%}", | |
| 749 | .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }}, | |
| 777 | "{f}", | |
| 778 | .{std.fmt.alt( | |
| 779 | @as(Component, .{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }), | |
| 780 | .formatEscaped, | |
| 781 | )}, | |
| 750 | 782 | ); |
| 751 | 783 | } |
| 752 | 784 | |
| ... | ... | @@ -755,7 +787,10 @@ test "URI percent decoding" { |
| 755 | 787 | const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; |
| 756 | 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".*; |
| 757 | 789 | |
| 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 | )}); | |
| 759 | 794 | |
| 760 | 795 | var output: [expected.len]u8 = undefined; |
| 761 | 796 | try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); |
| ... | ... | @@ -767,7 +802,10 @@ test "URI percent decoding" { |
| 767 | 802 | const expected = "/abc%"; |
| 768 | 803 | var input = expected.*; |
| 769 | 804 | |
| 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 | )}); | |
| 771 | 809 | |
| 772 | 810 | var output: [expected.len]u8 = undefined; |
| 773 | 811 | try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); |
| ... | ... | @@ -781,7 +819,9 @@ test "URI query encoding" { |
| 781 | 819 | const parsed = try Uri.parse(address); |
| 782 | 820 | |
| 783 | 821 | // 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 | } |
| 786 | 826 | |
| 787 | 827 | test "format" { |
| ... | ... | @@ -795,7 +835,9 @@ test "format" { |
| 795 | 835 | .query = null, |
| 796 | 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 | } |
| 800 | 842 | |
| 801 | 843 | test "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 | 339 | } |
| 340 | 340 | |
| 341 | 341 | pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { |
| 342 | const gpa = self.allocator; | |
| 342 | 343 | var unmanaged = self.moveToUnmanaged(); |
| 343 | try unmanaged.print(self.allocator, fmt, args); | |
| 344 | self.* = unmanaged.toManaged(self.allocator); | |
| 344 | defer self.* = unmanaged.toManaged(gpa); | |
| 345 | try unmanaged.print(gpa, fmt, args); | |
| 345 | 346 | } |
| 346 | 347 | |
| 347 | 348 | /// Append a value to the list `n` times. |
| ... | ... | @@ -907,7 +908,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 907 | 908 | try self.ensureUnusedCapacity(gpa, fmt.len); |
| 908 | 909 | var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self); |
| 909 | 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 | 912 | error.WriteFailed => return error.OutOfMemory, |
| 912 | 913 | }; |
| 913 | 914 | } |
lib/std/ascii.zig+45| ... | ... | @@ -10,6 +10,10 @@ |
| 10 | 10 | |
| 11 | 11 | const std = @import("std"); |
| 12 | 12 | |
| 13 | pub const lowercase = "abcdefghijklmnopqrstuvwxyz"; | |
| 14 | pub const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
| 15 | pub const letters = lowercase ++ uppercase; | |
| 16 | ||
| 13 | 17 | /// The C0 control codes of the ASCII encoding. |
| 14 | 18 | /// |
| 15 | 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 | 439 | pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool { |
| 436 | 440 | return orderIgnoreCase(lhs, rhs) == .lt; |
| 437 | 441 | } |
| 442 | ||
| 443 | pub 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. | |
| 470 | pub 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 | ||
| 477 | test 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 | 108 | } |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | // dest must be compatible with std.io.Writer's writeAll interface | |
| 111 | // dest must be compatible with std.io.GenericWriter's writeAll interface | |
| 112 | 112 | pub fn encodeWriter(encoder: *const Base64Encoder, dest: anytype, source: []const u8) !void { |
| 113 | 113 | var chunker = window(u8, source, 3, 3); |
| 114 | 114 | while (chunker.next()) |chunk| { |
| ... | ... | @@ -118,8 +118,8 @@ pub const Base64Encoder = struct { |
| 118 | 118 | } |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | // destWriter must be compatible with std.io.Writer's writeAll interface | |
| 122 | // sourceReader must be compatible with std.io.Reader's read interface | |
| 121 | // destWriter must be compatible with std.io.GenericWriter's writeAll interface | |
| 122 | // sourceReader must be compatible with `std.io.GenericReader` read interface | |
| 123 | 123 | pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void { |
| 124 | 124 | while (true) { |
| 125 | 125 | var tempSource: [3]u8 = undefined; |
lib/std/bounded_array.zig+2-2| ... | ... | @@ -277,7 +277,7 @@ pub fn BoundedArrayAligned( |
| 277 | 277 | @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ |
| 278 | 278 | "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") |
| 279 | 279 | else |
| 280 | std.io.Writer(*Self, error{Overflow}, appendWrite); | |
| 280 | std.io.GenericWriter(*Self, error{Overflow}, appendWrite); | |
| 281 | 281 | |
| 282 | 282 | /// Initializes a writer which will write into the array. |
| 283 | 283 | pub fn writer(self: *Self) Writer { |
| ... | ... | @@ -285,7 +285,7 @@ pub fn BoundedArrayAligned( |
| 285 | 285 | } |
| 286 | 286 | |
| 287 | 287 | /// 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 | 289 | fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { |
| 290 | 290 | try self.appendSlice(m); |
| 291 | 291 | return m.len; |
lib/std/builtin.zig+9-22| ... | ... | @@ -34,23 +34,21 @@ pub const StackTrace = struct { |
| 34 | 34 | index: usize, |
| 35 | 35 | instruction_addresses: []usize, |
| 36 | 36 | |
| 37 | pub fn format(st: StackTrace, bw: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 38 | comptime if (fmt.len != 0) unreachable; | |
| 39 | ||
| 37 | pub fn format(self: StackTrace, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 40 | 38 | // TODO: re-evaluate whether to use format() methods at all. |
| 41 | 39 | // Until then, avoid an error when using DebugAllocator with WebAssembly |
| 42 | 40 | // where it tries to call detectTTYConfig here. |
| 43 | 41 | if (builtin.os.tag == .freestanding) return 0; |
| 44 | 42 | |
| 45 | 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 | 45 | @errorName(err), |
| 48 | 46 | }); |
| 49 | 47 | }; |
| 50 | const tty_config = std.io.tty.detectConfig(.stderr()); | |
| 51 | try bw.writeAll("\n"); | |
| 52 | std.debug.writeStackTrace(st, bw, debug_info, tty_config) catch |err| { | |
| 53 | try bw.print("Unable to print stack trace: {s}\n", .{@errorName(err)}); | |
| 48 | const tty_config = std.io.tty.detectConfig(std.fs.File.stderr()); | |
| 49 | try writer.writeAll("\n"); | |
| 50 | std.debug.writeStackTrace(self, writer, debug_info, tty_config) catch |err| { | |
| 51 | try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)}); | |
| 54 | 52 | }; |
| 55 | 53 | } |
| 56 | 54 | }; |
| ... | ... | @@ -195,8 +193,6 @@ pub const CallingConvention = union(enum(u8)) { |
| 195 | 193 | pub const C: CallingConvention = .c; |
| 196 | 194 | /// Deprecated; use `.naked`. |
| 197 | 195 | pub const Naked: CallingConvention = .naked; |
| 198 | /// Deprecated; use `.@"async"`. | |
| 199 | pub const Async: CallingConvention = .@"async"; | |
| 200 | 196 | /// Deprecated; use `.@"inline"`. |
| 201 | 197 | pub const Inline: CallingConvention = .@"inline"; |
| 202 | 198 | /// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`. |
| ... | ... | @@ -244,7 +240,7 @@ pub const CallingConvention = union(enum(u8)) { |
| 244 | 240 | /// The calling convention of a function that can be called with `async` syntax. An `async` call |
| 245 | 241 | /// of a runtime-known function must target a function with this calling convention. |
| 246 | 242 | /// Comptime-known functions with other calling conventions may be coerced to this one. |
| 247 | @"async", | |
| 243 | async, | |
| 248 | 244 | |
| 249 | 245 | /// Functions with this calling convention have no prologue or epilogue, making the function |
| 250 | 246 | /// uncallable in regular Zig code. This can be useful when integrating with assembly. |
| ... | ... | @@ -847,7 +843,7 @@ pub const LinkMode = enum { |
| 847 | 843 | pub const UnwindTables = enum { |
| 848 | 844 | none, |
| 849 | 845 | sync, |
| 850 | @"async", | |
| 846 | async, | |
| 851 | 847 | }; |
| 852 | 848 | |
| 853 | 849 | /// This data structure is used by the Zig language code generation and |
| ... | ... | @@ -862,32 +858,23 @@ pub const WasiExecModel = enum { |
| 862 | 858 | pub const CallModifier = enum { |
| 863 | 859 | /// Equivalent to function call syntax. |
| 864 | 860 | auto, |
| 865 | ||
| 866 | /// Equivalent to async keyword used with function call syntax. | |
| 867 | async_kw, | |
| 868 | ||
| 869 | 861 | /// Prevents tail call optimization. This guarantees that the return |
| 870 | 862 | /// address will point to the callsite, as opposed to the callsite's |
| 871 | 863 | /// callsite. If the call is otherwise required to be tail-called |
| 872 | 864 | /// or inlined, a compile error is emitted instead. |
| 873 | 865 | never_tail, |
| 874 | ||
| 875 | 866 | /// Guarantees that the call will not be inlined. If the call is |
| 876 | 867 | /// otherwise required to be inlined, a compile error is emitted instead. |
| 877 | 868 | never_inline, |
| 878 | ||
| 879 | 869 | /// Asserts that the function call will not suspend. This allows a |
| 880 | 870 | /// non-async function to call an async function. |
| 881 | no_async, | |
| 882 | ||
| 871 | no_suspend, | |
| 883 | 872 | /// Guarantees that the call will be generated with tail call optimization. |
| 884 | 873 | /// If this is not possible, a compile error is emitted instead. |
| 885 | 874 | always_tail, |
| 886 | ||
| 887 | 875 | /// Guarantees that the call will be inlined at the callsite. |
| 888 | 876 | /// If this is not possible, a compile error is emitted instead. |
| 889 | 877 | always_inline, |
| 890 | ||
| 891 | 878 | /// Evaluates the call at compile-time. If the call cannot be completed at |
| 892 | 879 | /// compile-time, a compile error is emitted instead. |
| 893 | 880 | compile_time, |
lib/std/c.zig+5-1| ... | ... | @@ -10412,7 +10412,10 @@ pub const sigfillset = switch (native_os) { |
| 10412 | 10412 | }; |
| 10413 | 10413 | |
| 10414 | 10414 | pub const sigaddset = private.sigaddset; |
| 10415 | pub const sigemptyset = private.sigemptyset; | |
| 10415 | pub const sigemptyset = switch (native_os) { | |
| 10416 | .netbsd => private.__sigemptyset14, | |
| 10417 | else => private.sigemptyset, | |
| 10418 | }; | |
| 10416 | 10419 | pub const sigdelset = private.sigdelset; |
| 10417 | 10420 | pub const sigismember = private.sigismember; |
| 10418 | 10421 | |
| ... | ... | @@ -11268,6 +11271,7 @@ const private = struct { |
| 11268 | 11271 | extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; |
| 11269 | 11272 | extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int; |
| 11270 | 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 | 11275 | extern "c" fn __sigfillset14(set: ?*sigset_t) c_int; |
| 11272 | 11276 | extern "c" fn __sigprocmask14(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; |
| 11273 | 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 | 34 | const Self = @This(); |
| 35 | 35 | |
| 36 | 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); | |
| 38 | 38 | |
| 39 | 39 | allocator: Allocator, |
| 40 | 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 | 27 | ReaderType.Error || |
| 28 | 28 | DecodeError || |
| 29 | 29 | Allocator.Error; |
| 30 | pub const Reader = std.io.Reader(*Self, Error, read); | |
| 30 | pub const Reader = std.io.GenericReader(*Self, Error, read); | |
| 31 | 31 | |
| 32 | 32 | allocator: Allocator, |
| 33 | 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 | 45 | self.data.ptr = begin; |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | pub const Writer = std.io.Writer(*ArrayListReverse, Error, prependSliceSize); | |
| 48 | pub const Writer = std.io.GenericWriter(*ArrayListReverse, Error, prependSliceSize); | |
| 49 | 49 | /// Warning: This writer writes backwards. `fn print` will NOT work as expected. |
| 50 | 50 | pub fn writer(self: *ArrayListReverse) Writer { |
| 51 | 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 | 383 | for (&d.s, v) |*dv, vv| dv.* +%= vv; |
| 384 | 384 | } |
| 385 | 385 | |
| 386 | <<<<<<< HEAD | |
| 386 | 387 | pub fn writer(this: *@This(), buffer: []u8) Writer { |
| 387 | 388 | return .{ |
| 388 | 389 | .context = this, |
| 389 | 390 | .vtable = &.{ .drain = drain }, |
| 390 | 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 | } |
| 393 | 409 | |
| 394 | 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 | 222 | /// Print to stderr, unbuffered, and silently returning on failure. Intended |
| 223 | 223 | /// for use in "printf debugging". Use `std.log` functions for proper logging. |
| 224 | 224 | pub 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 | 227 | defer unlockStderrWriter(); |
| 227 | 228 | nosuspend bw.print(fmt, args) catch return; |
| 228 | 229 | } |
| ... | ... | @@ -307,7 +308,7 @@ test dumpHexFallible { |
| 307 | 308 | var aw: std.io.Writer.Allocating = .init(std.testing.allocator); |
| 308 | 309 | defer aw.deinit(); |
| 309 | 310 | |
| 310 | try dumpHexFallible(&aw.interface, .no_color, bytes); | |
| 311 | try dumpHexFallible(&aw.writer, .no_color, bytes); | |
| 311 | 312 | const expected = try std.fmt.allocPrint(std.testing.allocator, |
| 312 | 313 | \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........ |
| 313 | 314 | \\{x:0>[2]} 01 12 13 ... |
| ... | ... | @@ -1228,9 +1229,9 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi |
| 1228 | 1229 | } |
| 1229 | 1230 | |
| 1230 | 1231 | test printLineFromFileAnyOs { |
| 1231 | var output = std.ArrayList(u8).init(std.testing.allocator); | |
| 1232 | defer output.deinit(); | |
| 1233 | const output_stream = output.writer(); | |
| 1232 | var aw: Writer.Allocating = .init(std.testing.allocator); | |
| 1233 | defer aw.deinit(); | |
| 1234 | const output_stream = &aw.writer; | |
| 1234 | 1235 | |
| 1235 | 1236 | const allocator = std.testing.allocator; |
| 1236 | 1237 | const join = std.fs.path.join; |
| ... | ... | @@ -1252,8 +1253,8 @@ test printLineFromFileAnyOs { |
| 1252 | 1253 | try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); |
| 1253 | 1254 | |
| 1254 | 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 | output.clearRetainingCapacity(); | |
| 1256 | try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.getWritten()); | |
| 1257 | aw.clearRetainingCapacity(); | |
| 1257 | 1258 | } |
| 1258 | 1259 | { |
| 1259 | 1260 | const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" }); |
| ... | ... | @@ -1268,12 +1269,12 @@ test printLineFromFileAnyOs { |
| 1268 | 1269 | }); |
| 1269 | 1270 | |
| 1270 | 1271 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); |
| 1271 | try expectEqualStrings("1\n", output.items); | |
| 1272 | output.clearRetainingCapacity(); | |
| 1272 | try expectEqualStrings("1\n", aw.getWritten()); | |
| 1273 | aw.clearRetainingCapacity(); | |
| 1273 | 1274 | |
| 1274 | 1275 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 }); |
| 1275 | try expectEqualStrings("3\n", output.items); | |
| 1276 | output.clearRetainingCapacity(); | |
| 1276 | try expectEqualStrings("3\n", aw.getWritten()); | |
| 1277 | aw.clearRetainingCapacity(); | |
| 1277 | 1278 | } |
| 1278 | 1279 | { |
| 1279 | 1280 | const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); |
| ... | ... | @@ -1282,14 +1283,17 @@ test printLineFromFileAnyOs { |
| 1282 | 1283 | defer allocator.free(path); |
| 1283 | 1284 | |
| 1284 | 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 | 1289 | try writer.splatByteAll('a', std.heap.page_size_min - overlap); |
| 1287 | 1290 | try writer.writeByte('\n'); |
| 1288 | 1291 | try writer.splatByteAll('a', overlap); |
| 1292 | try writer.flush(); | |
| 1289 | 1293 | |
| 1290 | 1294 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); |
| 1291 | try expectEqualStrings(("a" ** overlap) ++ "\n", output.items); | |
| 1292 | output.clearRetainingCapacity(); | |
| 1295 | try expectEqualStrings(("a" ** overlap) ++ "\n", aw.getWritten()); | |
| 1296 | aw.clearRetainingCapacity(); | |
| 1293 | 1297 | } |
| 1294 | 1298 | { |
| 1295 | 1299 | const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); |
| ... | ... | @@ -1297,12 +1301,13 @@ test printLineFromFileAnyOs { |
| 1297 | 1301 | const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); |
| 1298 | 1302 | defer allocator.free(path); |
| 1299 | 1303 | |
| 1300 | var writer = file.writer(); | |
| 1304 | var file_writer = file.writer(&.{}); | |
| 1305 | const writer = &file_writer.interface; | |
| 1301 | 1306 | try writer.splatByteAll('a', std.heap.page_size_max); |
| 1302 | 1307 | |
| 1303 | 1308 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); |
| 1304 | try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items); | |
| 1305 | output.clearRetainingCapacity(); | |
| 1309 | try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.getWritten()); | |
| 1310 | aw.clearRetainingCapacity(); | |
| 1306 | 1311 | } |
| 1307 | 1312 | { |
| 1308 | 1313 | const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); |
| ... | ... | @@ -1310,24 +1315,25 @@ test printLineFromFileAnyOs { |
| 1310 | 1315 | const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); |
| 1311 | 1316 | defer allocator.free(path); |
| 1312 | 1317 | |
| 1313 | var writer = file.writer(); | |
| 1318 | var file_writer = file.writer(&.{}); | |
| 1319 | const writer = &file_writer.interface; | |
| 1314 | 1320 | try writer.splatByteAll('a', 3 * std.heap.page_size_max); |
| 1315 | 1321 | |
| 1316 | 1322 | try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); |
| 1317 | 1323 | |
| 1318 | 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); | |
| 1320 | output.clearRetainingCapacity(); | |
| 1325 | try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.getWritten()); | |
| 1326 | aw.clearRetainingCapacity(); | |
| 1321 | 1327 | |
| 1322 | 1328 | try writer.writeAll("a\na"); |
| 1323 | 1329 | |
| 1324 | 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); | |
| 1326 | output.clearRetainingCapacity(); | |
| 1331 | try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.getWritten()); | |
| 1332 | aw.clearRetainingCapacity(); | |
| 1327 | 1333 | |
| 1328 | 1334 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); |
| 1329 | try expectEqualStrings("a\n", output.items); | |
| 1330 | output.clearRetainingCapacity(); | |
| 1335 | try expectEqualStrings("a\n", aw.getWritten()); | |
| 1336 | aw.clearRetainingCapacity(); | |
| 1331 | 1337 | } |
| 1332 | 1338 | { |
| 1333 | 1339 | const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); |
| ... | ... | @@ -1335,18 +1341,19 @@ test printLineFromFileAnyOs { |
| 1335 | 1341 | const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" }); |
| 1336 | 1342 | defer allocator.free(path); |
| 1337 | 1343 | |
| 1338 | var writer = file.writer(); | |
| 1344 | var file_writer = file.writer(&.{}); | |
| 1345 | const writer = &file_writer.interface; | |
| 1339 | 1346 | const real_file_start = 3 * std.heap.page_size_min; |
| 1340 | 1347 | try writer.splatByteAll('\n', real_file_start); |
| 1341 | 1348 | try writer.writeAll("abc\ndef"); |
| 1342 | 1349 | |
| 1343 | 1350 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 }); |
| 1344 | try expectEqualStrings("abc\n", output.items); | |
| 1345 | output.clearRetainingCapacity(); | |
| 1351 | try expectEqualStrings("abc\n", aw.getWritten()); | |
| 1352 | aw.clearRetainingCapacity(); | |
| 1346 | 1353 | |
| 1347 | 1354 | try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 }); |
| 1348 | try expectEqualStrings("def\n", output.items); | |
| 1349 | output.clearRetainingCapacity(); | |
| 1355 | try expectEqualStrings("def\n", aw.getWritten()); | |
| 1356 | aw.clearRetainingCapacity(); | |
| 1350 | 1357 | } |
| 1351 | 1358 | } |
| 1352 | 1359 | |
| ... | ... | @@ -1597,10 +1604,10 @@ test "manage resources correctly" { |
| 1597 | 1604 | // self-hosted debug info is still too buggy |
| 1598 | 1605 | if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; |
| 1599 | 1606 | |
| 1600 | const writer = std.io.null_writer; | |
| 1607 | var discarding: std.io.Writer.Discarding = .init(&.{}); | |
| 1601 | 1608 | var di = try SelfInfo.open(testing.allocator); |
| 1602 | 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 | } |
| 1605 | 1612 | |
| 1606 | 1613 | noinline fn showMyTrace() usize { |
lib/std/debug/Pdb.zig+3-3| ... | ... | @@ -395,7 +395,7 @@ const Msf = struct { |
| 395 | 395 | streams: []MsfStream, |
| 396 | 396 | |
| 397 | 397 | fn init(allocator: Allocator, file: File) !Msf { |
| 398 | const in = file.reader(); | |
| 398 | const in = file.deprecatedReader(); | |
| 399 | 399 | |
| 400 | 400 | const superblock = try in.takeStruct(pdb.SuperBlock); |
| 401 | 401 | |
| ... | ... | @@ -514,7 +514,7 @@ const MsfStream = struct { |
| 514 | 514 | var offset = self.pos % self.block_size; |
| 515 | 515 | |
| 516 | 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(); | |
| 518 | 518 | |
| 519 | 519 | var size: usize = 0; |
| 520 | 520 | var rem_buffer = buffer; |
| ... | ... | @@ -562,7 +562,7 @@ const MsfStream = struct { |
| 562 | 562 | return block * self.block_size + offset; |
| 563 | 563 | } |
| 564 | 564 | |
| 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 | 566 | return .{ .context = self }; |
| 567 | 567 | } |
| 568 | 568 | }; |
lib/std/elf.zig+164| ... | ... | @@ -508,6 +508,7 @@ pub const Header = struct { |
| 508 | 508 | }; |
| 509 | 509 | } |
| 510 | 510 | |
| 511 | <<<<<<< HEAD | |
| 511 | 512 | pub const ReadError = std.io.Reader.Error || ParseError; |
| 512 | 513 | |
| 513 | 514 | pub fn read(r: *std.io.Reader) ReadError!Header { |
| ... | ... | @@ -515,6 +516,19 @@ pub const Header = struct { |
| 515 | 516 | const result = try parse(@ptrCast(buf)); |
| 516 | 517 | r.toss(if (result.is_64) @sizeOf(Elf64_Ehdr) else @sizeOf(Elf32_Ehdr)); |
| 517 | 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 | } |
| 519 | 533 | |
| 520 | 534 | pub const ParseError = error{ |
| ... | ... | @@ -590,14 +604,92 @@ pub const ProgramHeaderIterator = struct { |
| 590 | 604 | if (it.index >= it.elf_header.phnum) return null; |
| 591 | 605 | defer it.index += 1; |
| 592 | 606 | |
| 607 | <<<<<<< HEAD | |
| 593 | 608 | if (it.elf_header.is_64) { |
| 594 | 609 | var phdr: Elf64_Phdr = undefined; |
| 595 | 610 | const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index; |
| 596 | 611 | try it.file_reader.seekTo(offset); |
| 597 | 612 | try it.file_reader.interface.readSlice(@ptrCast(&phdr)); |
| 598 | 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 | 637 | mem.byteSwapAllFields(Elf64_Phdr, &phdr); |
| 638 | <<<<<<< HEAD | |
| 600 | 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 | } |
| 602 | 694 | |
| 603 | 695 | var phdr: Elf32_Phdr = undefined; |
| ... | ... | @@ -624,9 +716,23 @@ pub const SectionHeaderIterator = struct { |
| 624 | 716 | file_reader: *std.fs.File.Reader, |
| 625 | 717 | index: usize = 0, |
| 626 | 718 | |
| 719 | <<<<<<< HEAD | |
| 627 | 720 | pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr { |
| 628 | 721 | if (it.index >= it.elf_header.shnum) return null; |
| 629 | 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 | |
| 630 | 736 | |
| 631 | 737 | if (it.elf_header.is_64) { |
| 632 | 738 | var shdr: Elf64_Shdr = undefined; |
| ... | ... | @@ -635,7 +741,65 @@ pub const SectionHeaderIterator = struct { |
| 635 | 741 | try it.file_reader.interface.readSlice(@ptrCast(&shdr)); |
| 636 | 742 | if (it.elf_header.endian != native_endian) |
| 637 | 743 | mem.byteSwapAllFields(Elf64_Shdr, &shdr); |
| 744 | <<<<<<< HEAD | |
| 638 | 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 | } |
| 640 | 804 | |
| 641 | 805 | var shdr: Elf32_Shdr = undefined; |
lib/std/fmt.zig+245-567| ... | ... | @@ -7,7 +7,6 @@ const io = std.io; |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const assert = std.debug.assert; |
| 9 | 9 | const mem = std.mem; |
| 10 | const unicode = std.unicode; | |
| 11 | 10 | const meta = std.meta; |
| 12 | 11 | const lossyCast = math.lossyCast; |
| 13 | 12 | const expectFmt = std.testing.expectFmt; |
| ... | ... | @@ -25,10 +24,12 @@ pub const Alignment = enum { |
| 25 | 24 | right, |
| 26 | 25 | }; |
| 27 | 26 | |
| 27 | pub const Case = enum { lower, upper }; | |
| 28 | ||
| 28 | 29 | const default_alignment = .right; |
| 29 | 30 | const default_fill_char = ' '; |
| 30 | 31 | |
| 31 | /// Deprecated; to be removed after 0.14.0 is tagged. | |
| 32 | /// Deprecated in favor of `Options`. | |
| 32 | 33 | pub const FormatOptions = Options; |
| 33 | 34 | |
| 34 | 35 | pub const Options = struct { |
| ... | ... | @@ -36,229 +37,78 @@ pub const Options = struct { |
| 36 | 37 | width: ?usize = null, |
| 37 | 38 | alignment: Alignment = default_alignment, |
| 38 | 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 `}}`. | |
| 96 | pub 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 | } | |
| 121 | 40 | |
| 122 | comptime var end_index = i; | |
| 123 | comptime var unescape_brace = false; | |
| 124 | ||
| 125 | // Handle {{ and }}, those are un-escaped as single braces | |
| 126 | if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) { | |
| 127 | unescape_brace = true; | |
| 128 | // Make the first brace part of the literal... | |
| 129 | end_index += 1; | |
| 130 | // ...and skip both | |
| 131 | i += 2; | |
| 132 | } | |
| 133 | ||
| 134 | literal = literal ++ fmt[start_index..end_index]; | |
| 135 | ||
| 136 | // We've already skipped the other brace, restart the loop | |
| 137 | if (unescape_brace) continue; | |
| 138 | ||
| 139 | // Write out the literal | |
| 140 | if (literal.len != 0) { | |
| 141 | try bw.writeAll(literal); | |
| 142 | literal = ""; | |
| 143 | } | |
| 144 | ||
| 145 | if (i >= fmt.len) break; | |
| 146 | ||
| 147 | if (fmt[i] == '}') { | |
| 148 | @compileError("missing opening {"); | |
| 149 | } | |
| 150 | ||
| 151 | // Get past the { | |
| 152 | comptime assert(fmt[i] == '{'); | |
| 153 | i += 1; | |
| 154 | ||
| 155 | const fmt_begin = i; | |
| 156 | // Find the closing brace | |
| 157 | inline while (i < fmt.len and fmt[i] != '}') : (i += 1) {} | |
| 158 | const fmt_end = i; | |
| 159 | ||
| 160 | if (i >= fmt.len) { | |
| 161 | @compileError("missing closing }"); | |
| 162 | } | |
| 163 | ||
| 164 | // Get past the } | |
| 165 | comptime assert(fmt[i] == '}'); | |
| 166 | i += 1; | |
| 167 | ||
| 168 | const placeholder = comptime Placeholder.parse(fmt[fmt_begin..fmt_end].*); | |
| 169 | const arg_pos = comptime switch (placeholder.arg) { | |
| 170 | .none => null, | |
| 171 | .number => |pos| pos, | |
| 172 | .named => |arg_name| meta.fieldIndex(ArgsType, arg_name) orelse | |
| 173 | @compileError("no argument with name '" ++ arg_name ++ "'"), | |
| 174 | }; | |
| 175 | ||
| 176 | const width = switch (placeholder.width) { | |
| 177 | .none => null, | |
| 178 | .number => |v| v, | |
| 179 | .named => |arg_name| blk: { | |
| 180 | const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse | |
| 181 | @compileError("no argument with name '" ++ arg_name ++ "'"); | |
| 182 | _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); | |
| 183 | break :blk @field(args, arg_name); | |
| 184 | }, | |
| 41 | pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number { | |
| 42 | return .{ | |
| 43 | .mode = mode, | |
| 44 | .case = case, | |
| 45 | .precision = o.precision, | |
| 46 | .width = o.width, | |
| 47 | .alignment = o.alignment, | |
| 48 | .fill = o.fill, | |
| 185 | 49 | }; |
| 50 | } | |
| 51 | }; | |
| 186 | 52 | |
| 187 | const precision = switch (placeholder.precision) { | |
| 188 | .none => null, | |
| 189 | .number => |v| v, | |
| 190 | .named => |arg_name| blk: { | |
| 191 | const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse | |
| 192 | @compileError("no argument with name '" ++ arg_name ++ "'"); | |
| 193 | _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); | |
| 194 | break :blk @field(args, arg_name); | |
| 195 | }, | |
| 196 | }; | |
| 53 | pub const Number = struct { | |
| 54 | mode: Mode = .decimal, | |
| 55 | /// Affects hex digits as well as floating point "inf"/"INF". | |
| 56 | case: Case = .lower, | |
| 57 | precision: ?usize = null, | |
| 58 | width: ?usize = null, | |
| 59 | alignment: Alignment = default_alignment, | |
| 60 | fill: u8 = default_fill_char, | |
| 197 | 61 | |
| 198 | const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse | |
| 199 | @compileError("too few arguments"); | |
| 200 | ||
| 201 | try bw.printValue( | |
| 202 | placeholder.specifier_arg, | |
| 203 | .{ | |
| 204 | .fill = placeholder.fill, | |
| 205 | .alignment = placeholder.alignment, | |
| 206 | .width = width, | |
| 207 | .precision = precision, | |
| 208 | }, | |
| 209 | @field(args, fields_info[arg_to_print].name), | |
| 210 | std.options.fmt_max_depth, | |
| 211 | ); | |
| 212 | } | |
| 213 | ||
| 214 | if (comptime arg_state.hasUnusedArgs()) { | |
| 215 | const missing_count = arg_state.args_len - @popCount(arg_state.used_args); | |
| 216 | switch (missing_count) { | |
| 217 | 0 => unreachable, | |
| 218 | 1 => @compileError("unused argument in '" ++ fmt ++ "'"), | |
| 219 | else => @compileError(comptimePrint("{d}", .{missing_count}) ++ " unused arguments in '" ++ fmt ++ "'"), | |
| 62 | pub const Mode = enum { | |
| 63 | decimal, | |
| 64 | binary, | |
| 65 | octal, | |
| 66 | hex, | |
| 67 | scientific, | |
| 68 | ||
| 69 | pub fn base(mode: Mode) ?u8 { | |
| 70 | return switch (mode) { | |
| 71 | .decimal => 10, | |
| 72 | .binary => 2, | |
| 73 | .octal => 8, | |
| 74 | .hex => 16, | |
| 75 | .scientific => null, | |
| 76 | }; | |
| 220 | 77 | } |
| 221 | } | |
| 222 | } | |
| 78 | }; | |
| 79 | }; | |
| 223 | 80 | |
| 224 | fn cacheString(str: anytype) []const u8 { | |
| 225 | return &str; | |
| 81 | /// Deprecated in favor of `Writer.print`. | |
| 82 | pub 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 | } |
| 227 | 88 | |
| 228 | 89 | pub const Placeholder = struct { |
| 229 | 90 | specifier_arg: []const u8, |
| 230 | fill: u21, | |
| 91 | fill: u8, | |
| 231 | 92 | alignment: Alignment, |
| 232 | 93 | arg: Specifier, |
| 233 | 94 | width: Specifier, |
| 234 | 95 | precision: Specifier, |
| 235 | 96 | |
| 236 | pub fn parse(comptime str: anytype) Placeholder { | |
| 237 | const view = std.unicode.Utf8View.initComptime(&str); | |
| 238 | comptime var parser = Parser{ | |
| 239 | .iter = view.iterator(), | |
| 240 | }; | |
| 241 | ||
| 242 | // Parse the positional argument number | |
| 243 | const arg = comptime parser.specifier() catch |err| | |
| 244 | @compileError(@errorName(err)); | |
| 245 | ||
| 246 | // Parse the format specifier | |
| 247 | const specifier_arg = comptime parser.until(':'); | |
| 248 | ||
| 249 | // Skip the colon, if present | |
| 250 | if (comptime parser.char()) |ch| { | |
| 251 | if (ch != ':') { | |
| 252 | @compileError("expected : or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'"); | |
| 253 | } | |
| 97 | pub fn parse(comptime bytes: []const u8) Placeholder { | |
| 98 | var parser: Parser = .{ .bytes = bytes, .i = 0 }; | |
| 99 | const arg = parser.specifier() catch |err| @compileError(@errorName(err)); | |
| 100 | const specifier_arg = parser.until(':'); | |
| 101 | if (parser.char()) |b| { | |
| 102 | if (b != ':') @compileError("expected : or }, found '" ++ &[1]u8{b} ++ "'"); | |
| 254 | 103 | } |
| 255 | 104 | |
| 256 | // Parse the fill character, if present. | |
| 257 | // When the width field is also specified, the fill character must | |
| 105 | // Parse the fill byte, if present. | |
| 106 | // | |
| 107 | // When the width field is also specified, the fill byte must | |
| 258 | 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) | |
| 260 | var fill: ?u21 = comptime if (parser.peek(1)) |ch| | |
| 261 | switch (ch) { | |
| 109 | // (in which case it's handled as part of the width specifier). | |
| 110 | var fill: ?u8 = if (parser.peek(1)) |b| | |
| 111 | switch (b) { | |
| 262 | 112 | '<', '^', '>' => parser.char(), |
| 263 | 113 | else => null, |
| 264 | 114 | } |
| ... | ... | @@ -266,8 +116,8 @@ pub const Placeholder = struct { |
| 266 | 116 | null; |
| 267 | 117 | |
| 268 | 118 | // Parse the alignment parameter |
| 269 | const alignment: ?Alignment = comptime if (parser.peek(0)) |ch| init: { | |
| 270 | switch (ch) { | |
| 119 | const alignment: ?Alignment = if (parser.peek(0)) |b| init: { | |
| 120 | switch (b) { | |
| 271 | 121 | '<', '^', '>' => { |
| 272 | 122 | // consume the character |
| 273 | 123 | break :init switch (parser.char().?) { |
| ... | ... | @@ -283,30 +133,26 @@ pub const Placeholder = struct { |
| 283 | 133 | // When none of the fill character and the alignment specifier have |
| 284 | 134 | // been provided, check whether the width starts with a zero. |
| 285 | 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 | } |
| 288 | 138 | |
| 289 | 139 | // Parse the width parameter |
| 290 | const width = comptime parser.specifier() catch |err| | |
| 291 | @compileError(@errorName(err)); | |
| 140 | const width = parser.specifier() catch |err| @compileError(@errorName(err)); | |
| 292 | 141 | |
| 293 | 142 | // Skip the dot, if present |
| 294 | if (comptime parser.char()) |ch| { | |
| 295 | if (ch != '.') { | |
| 296 | @compileError("expected . or }, found '" ++ unicode.utf8EncodeComptime(ch) ++ "'"); | |
| 297 | } | |
| 143 | if (parser.char()) |b| { | |
| 144 | if (b != '.') @compileError("expected . or }, found '" ++ &[1]u8{b} ++ "'"); | |
| 298 | 145 | } |
| 299 | 146 | |
| 300 | 147 | // Parse the precision parameter |
| 301 | const precision = comptime parser.specifier() catch |err| | |
| 302 | @compileError(@errorName(err)); | |
| 148 | const precision = parser.specifier() catch |err| @compileError(@errorName(err)); | |
| 303 | 149 | |
| 304 | if (comptime parser.char()) |ch| { | |
| 305 | @compileError("extraneous trailing character '" ++ unicode.utf8EncodeComptime(ch) ++ "'"); | |
| 306 | } | |
| 150 | if (parser.char()) |b| @compileError("extraneous trailing character '" ++ &[1]u8{b} ++ "'"); | |
| 151 | ||
| 152 | const specifier_array = specifier_arg[0..specifier_arg.len].*; | |
| 307 | 153 | |
| 308 | 154 | return .{ |
| 309 | .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), | |
| 155 | .specifier_arg = &specifier_array, | |
| 310 | 156 | .fill = fill orelse default_fill_char, |
| 311 | 157 | .alignment = alignment orelse default_alignment, |
| 312 | 158 | .arg = arg, |
| ... | ... | @@ -327,93 +173,64 @@ pub const Specifier = union(enum) { |
| 327 | 173 | /// Allows to implement formatters compatible with std.fmt without replicating |
| 328 | 174 | /// the standard library behavior. |
| 329 | 175 | pub const Parser = struct { |
| 330 | iter: std.unicode.Utf8Iterator, | |
| 176 | bytes: []const u8, | |
| 177 | i: usize, | |
| 331 | 178 | |
| 332 | // Returns a decimal number or null if the current character is not a | |
| 333 | // digit | |
| 334 | 179 | pub fn number(self: *@This()) ?usize { |
| 335 | 180 | var r: ?usize = null; |
| 336 | ||
| 337 | while (self.peek(0)) |code_point| { | |
| 338 | switch (code_point) { | |
| 181 | while (self.peek(0)) |byte| { | |
| 182 | switch (byte) { | |
| 339 | 183 | '0'...'9' => { |
| 340 | 184 | if (r == null) r = 0; |
| 341 | 185 | r.? *= 10; |
| 342 | r.? += code_point - '0'; | |
| 186 | r.? += byte - '0'; | |
| 343 | 187 | }, |
| 344 | 188 | else => break, |
| 345 | 189 | } |
| 346 | _ = self.iter.nextCodepoint(); | |
| 190 | self.i += 1; | |
| 347 | 191 | } |
| 348 | ||
| 349 | 192 | return r; |
| 350 | 193 | } |
| 351 | 194 | |
| 352 | // Returns a substring of the input starting from the current position | |
| 353 | // and ending where `ch` is found or until the end if not found | |
| 354 | pub fn until(self: *@This(), ch: u21) []const u8 { | |
| 355 | const start = self.iter.i; | |
| 356 | while (self.peek(0)) |code_point| { | |
| 357 | if (code_point == ch) | |
| 358 | break; | |
| 359 | _ = self.iter.nextCodepoint(); | |
| 360 | } | |
| 361 | return self.iter.bytes[start..self.iter.i]; | |
| 195 | pub fn until(self: *@This(), delimiter: u8) []const u8 { | |
| 196 | const start = self.i; | |
| 197 | self.i = std.mem.indexOfScalarPos(u8, self.bytes, self.i, delimiter) orelse self.bytes.len; | |
| 198 | return self.bytes[start..self.i]; | |
| 362 | 199 | } |
| 363 | 200 | |
| 364 | // Returns the character pointed to by the iterator if available, or | |
| 365 | // null otherwise | |
| 366 | pub fn char(self: *@This()) ?u21 { | |
| 367 | if (self.iter.nextCodepoint()) |code_point| { | |
| 368 | return code_point; | |
| 369 | } | |
| 370 | return null; | |
| 201 | pub fn char(self: *@This()) ?u8 { | |
| 202 | const i = self.i; | |
| 203 | if (self.bytes.len - i == 0) return null; | |
| 204 | self.i = i + 1; | |
| 205 | return self.bytes[i]; | |
| 371 | 206 | } |
| 372 | 207 | |
| 373 | // Returns true if the iterator points to an existing character and | |
| 374 | // false otherwise | |
| 375 | pub fn maybe(self: *@This(), val: u21) bool { | |
| 376 | if (self.peek(0) == val) { | |
| 377 | _ = self.iter.nextCodepoint(); | |
| 208 | pub fn maybe(self: *@This(), byte: u8) bool { | |
| 209 | if (self.peek(0) == byte) { | |
| 210 | self.i += 1; | |
| 378 | 211 | return true; |
| 379 | 212 | } |
| 380 | 213 | return false; |
| 381 | 214 | } |
| 382 | 215 | |
| 383 | // Returns a decimal number or null if the current character is not a | |
| 384 | // digit | |
| 385 | 216 | pub fn specifier(self: *@This()) !Specifier { |
| 386 | 217 | if (self.maybe('[')) { |
| 387 | 218 | const arg_name = self.until(']'); |
| 388 | ||
| 389 | if (!self.maybe(']')) | |
| 390 | return @field(anyerror, "Expected closing ]"); | |
| 391 | ||
| 392 | return Specifier{ .named = arg_name }; | |
| 219 | if (!self.maybe(']')) return error.@"Expected closing ]"; | |
| 220 | return .{ .named = arg_name }; | |
| 393 | 221 | } |
| 394 | if (self.number()) |i| | |
| 395 | return Specifier{ .number = i }; | |
| 396 | ||
| 397 | return Specifier{ .none = {} }; | |
| 222 | if (self.number()) |i| return .{ .number = i }; | |
| 223 | return .{ .none = {} }; | |
| 398 | 224 | } |
| 399 | 225 | |
| 400 | // Returns the n-th next character or null if that's past the end | |
| 401 | pub fn peek(self: *@This(), n: usize) ?u21 { | |
| 402 | const original_i = self.iter.i; | |
| 403 | defer self.iter.i = original_i; | |
| 404 | ||
| 405 | var i: usize = 0; | |
| 406 | var code_point: ?u21 = null; | |
| 407 | while (i <= n) : (i += 1) { | |
| 408 | code_point = self.iter.nextCodepoint(); | |
| 409 | if (code_point == null) return null; | |
| 410 | } | |
| 411 | return code_point; | |
| 226 | pub fn peek(self: *@This(), i: usize) ?u8 { | |
| 227 | const peek_index = self.i + i; | |
| 228 | if (peek_index >= self.bytes.len) return null; | |
| 229 | return self.bytes[peek_index]; | |
| 412 | 230 | } |
| 413 | 231 | }; |
| 414 | 232 | |
| 415 | 233 | pub const ArgSetType = u32; |
| 416 | const max_format_args = @typeInfo(ArgSetType).int.bits; | |
| 417 | 234 | |
| 418 | 235 | pub const ArgState = struct { |
| 419 | 236 | next_arg: usize = 0, |
| ... | ... | @@ -441,63 +258,12 @@ pub const ArgState = struct { |
| 441 | 258 | } |
| 442 | 259 | }; |
| 443 | 260 | |
| 444 | test { | |
| 445 | _ = float; | |
| 446 | } | |
| 447 | ||
| 448 | pub const Case = enum { lower, upper }; | |
| 449 | ||
| 450 | fn 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 | ||
| 478 | const formatSliceEscapeLower = SliceEscape(.lower).format; | |
| 479 | const 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. | |
| 484 | pub 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. | |
| 491 | pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscapeUpper) { | |
| 492 | return .{ .data = bytes }; | |
| 493 | } | |
| 494 | ||
| 495 | 261 | /// Asserts the rendered integer value fits in `buffer`. |
| 496 | 262 | /// Returns the end index within `buffer`. |
| 497 | 263 | pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize { |
| 498 | var bw: Writer = .fixed(buffer); | |
| 499 | bw.printIntOptions(value, base, case, options) catch unreachable; | |
| 500 | return bw.end; | |
| 264 | var w: Writer = .fixed(buffer); | |
| 265 | w.printInt(value, base, case, options) catch unreachable; | |
| 266 | return w.end; | |
| 501 | 267 | } |
| 502 | 268 | |
| 503 | 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 | 275 | } |
| 510 | 276 | } |
| 511 | 277 | |
| 512 | pub const ParseIntError = error{ | |
| 513 | /// The result cannot fit in the type specified | |
| 514 | Overflow, | |
| 515 | ||
| 516 | /// The input was empty or contained an invalid character | |
| 517 | InvalidCharacter, | |
| 518 | }; | |
| 278 | /// Deprecated in favor of `Alt`. | |
| 279 | pub const Formatter = Alt; | |
| 519 | 280 | |
| 520 | /// Creates a Formatter type from a format function. Wrapping data in Formatter(func) causes | |
| 521 | /// the data to be formatted using the given function `func`. `func` must be of the following | |
| 522 | /// form: | |
| 523 | /// | |
| 524 | /// fn formatExample( | |
| 525 | /// data: T, | |
| 526 | /// comptime fmt: []const u8, | |
| 527 | /// options: std.fmt.Options, | |
| 528 | /// writer: anytype, | |
| 529 | /// ) !void; | |
| 530 | /// | |
| 531 | pub fn Formatter(comptime formatFn: anytype) type { | |
| 532 | const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?; | |
| 281 | /// Creates a type suitable for instantiating and passing to a "{f}" placeholder. | |
| 282 | pub fn Alt( | |
| 283 | comptime Data: type, | |
| 284 | comptime formatFn: fn (data: Data, writer: *Writer) Writer.Error!void, | |
| 285 | ) type { | |
| 533 | 286 | return struct { |
| 534 | 287 | data: Data, |
| 535 | pub fn format(self: @This(), writer: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 536 | try formatFn(self.data, writer, fmt); | |
| 288 | pub inline fn format(self: @This(), writer: *Writer) Writer.Error!void { | |
| 289 | try formatFn(self.data, writer); | |
| 537 | 290 | } |
| 538 | 291 | }; |
| 539 | 292 | } |
| 540 | 293 | |
| 294 | /// Helper for calling alternate format methods besides one named "format". | |
| 295 | pub 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 | ||
| 302 | test 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 | ||
| 314 | pub 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 | 321 | /// Parses the string `buf` as signed or unsigned representation in the |
| 542 | 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 | 625 | /// Count the characters needed for format. |
| 846 | 626 | pub fn count(comptime fmt: []const u8, args: anytype) usize { |
| 847 | 627 | var trash_buffer: [64]u8 = undefined; |
| 848 | var w: Writer = .discarding(&trash_buffer); | |
| 849 | w.print(fmt, args) catch |err| switch (err) { | |
| 628 | var dw: Writer.Discarding = .init(&trash_buffer); | |
| 629 | dw.writer.print(fmt, args) catch |err| switch (err) { | |
| 850 | 630 | error.WriteFailed => unreachable, |
| 851 | 631 | }; |
| 852 | return w.count; | |
| 632 | return @intCast(dw.count + dw.writer.end); | |
| 853 | 633 | } |
| 854 | 634 | |
| 855 | 635 | pub 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 | 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 | 639 | error.WriteFailed => return error.OutOfMemory, |
| 860 | 640 | }; |
| 861 | 641 | return aw.toOwnedSlice(); |
| ... | ... | @@ -867,9 +647,9 @@ pub fn allocPrintSentinel( |
| 867 | 647 | args: anytype, |
| 868 | 648 | comptime sentinel: u8, |
| 869 | 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 | 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 | 653 | error.WriteFailed => return error.OutOfMemory, |
| 874 | 654 | }; |
| 875 | 655 | return aw.toOwnedSliceSentinel(sentinel); |
| ... | ... | @@ -1003,10 +783,6 @@ test "int.padded" { |
| 1003 | 783 | try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)}); |
| 1004 | 784 | try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)}); |
| 1005 | 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 | } |
| 1011 | 787 | |
| 1012 | 788 | test "buffer" { |
| ... | ... | @@ -1036,36 +812,24 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime |
| 1036 | 812 | } |
| 1037 | 813 | |
| 1038 | 814 | test "array" { |
| 1039 | { | |
| 1040 | const value: [3]u8 = "abc".*; | |
| 1041 | try expectArrayFmt("array: abc\n", "array: {s}\n", value); | |
| 1042 | try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {d}\n", value); | |
| 1043 | try expectArrayFmt("array: { 61, 62, 63 }\n", "array: {x}\n", value); | |
| 1044 | try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); | |
| 815 | const value: [3]u8 = "abc".*; | |
| 816 | try expectArrayFmt("array: abc\n", "array: {s}\n", value); | |
| 817 | try expectArrayFmt("array: 616263\n", "array: {x}\n", value); | |
| 818 | try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); | |
| 1045 | 819 | |
| 1046 | var buf: [100]u8 = undefined; | |
| 1047 | try expectFmt( | |
| 1048 | try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), | |
| 1049 | "array: {*}\n", | |
| 1050 | .{&value}, | |
| 1051 | ); | |
| 1052 | } | |
| 1053 | ||
| 1054 | { | |
| 1055 | const value = [2][3]u8{ "abc".*, "def".* }; | |
| 1056 | ||
| 1057 | try expectArrayFmt("array: { abc, def }\n", "array: {s}\n", value); | |
| 1058 | try expectArrayFmt("array: { { 97, 98, 99 }, { 100, 101, 102 } }\n", "array: {d}\n", value); | |
| 1059 | try expectArrayFmt("array: { { 61, 62, 63 }, { 64, 65, 66 } }\n", "array: {x}\n", value); | |
| 1060 | } | |
| 820 | var buf: [100]u8 = undefined; | |
| 821 | try expectFmt( | |
| 822 | try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), | |
| 823 | "array: {*}\n", | |
| 824 | .{&value}, | |
| 825 | ); | |
| 1061 | 826 | } |
| 1062 | 827 | |
| 1063 | 828 | test "slice" { |
| 1064 | 829 | { |
| 1065 | 830 | const value: []const u8 = "abc"; |
| 1066 | 831 | try expectFmt("slice: abc\n", "slice: {s}\n", .{value}); |
| 1067 | try expectFmt("slice: { 97, 98, 99 }\n", "slice: {d}\n", .{value}); | |
| 1068 | try expectFmt("slice: { 61, 62, 63 }\n", "slice: {x}\n", .{value}); | |
| 832 | try expectFmt("slice: 616263\n", "slice: {x}\n", .{value}); | |
| 1069 | 833 | try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value}); |
| 1070 | 834 | } |
| 1071 | 835 | { |
| ... | ... | @@ -1079,45 +843,33 @@ test "slice" { |
| 1079 | 843 | try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice}); |
| 1080 | 844 | } |
| 1081 | 845 | |
| 1082 | try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); | |
| 1083 | 846 | try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); |
| 1084 | 847 | |
| 1085 | 848 | { |
| 1086 | 849 | var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; |
| 1087 | var runtime_zero: usize = 0; | |
| 1088 | _ = &runtime_zero; | |
| 1089 | try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); | |
| 1090 | try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); | |
| 1091 | try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); | |
| 1092 | try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); | |
| 850 | const input: []const u32 = &int_slice; | |
| 851 | try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input}); | |
| 1093 | 852 | } |
| 1094 | 853 | { |
| 1095 | 854 | const S1 = struct { |
| 1096 | 855 | x: u8, |
| 1097 | 856 | }; |
| 1098 | 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 | 861 | const S2 = struct { |
| 1103 | 862 | x: u8, |
| 1104 | 863 | |
| 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 | 865 | try writer.print("S2({})", .{s.x}); |
| 1107 | 866 | } |
| 1108 | 867 | }; |
| 1109 | 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 | } |
| 1113 | 872 | |
| 1114 | test "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 | ||
| 1121 | 873 | test "pointer" { |
| 1122 | 874 | { |
| 1123 | 875 | const value = @as(*align(1) i32, @ptrFromInt(0xdeadbeef)); |
| ... | ... | @@ -1141,11 +893,6 @@ test "cstr" { |
| 1141 | 893 | "cstr: {s}\n", |
| 1142 | 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 | } |
| 1150 | 897 | |
| 1151 | 898 | test "struct" { |
| ... | ... | @@ -1154,8 +901,8 @@ test "struct" { |
| 1154 | 901 | field: u8, |
| 1155 | 902 | }; |
| 1156 | 903 | const value = Struct{ .field = 42 }; |
| 1157 | try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{value}); | |
| 1158 | try expectFmt("struct: fmt.test.struct.Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); | |
| 904 | try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{value}); | |
| 905 | try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{&value}); | |
| 1159 | 906 | } |
| 1160 | 907 | { |
| 1161 | 908 | const Struct = struct { |
| ... | ... | @@ -1163,7 +910,7 @@ test "struct" { |
| 1163 | 910 | b: u1, |
| 1164 | 911 | }; |
| 1165 | 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 | } |
| 1168 | 915 | |
| 1169 | 916 | const S = struct { |
| ... | ... | @@ -1176,11 +923,11 @@ test "struct" { |
| 1176 | 923 | .b = error.Unused, |
| 1177 | 924 | }; |
| 1178 | 925 | |
| 1179 | try expectFmt("fmt.test.struct.S{ .a = 456, .b = error.Unused }", "{}", .{inst}); | |
| 926 | try expectFmt(".{ .a = 456, .b = error.Unused }", "{}", .{inst}); | |
| 1180 | 927 | // Tuples |
| 1181 | try expectFmt("{ }", "{}", .{.{}}); | |
| 1182 | try expectFmt("{ -1 }", "{}", .{.{-1}}); | |
| 1183 | try expectFmt("{ -1, 42, 2.5e4 }", "{}", .{.{ -1, 42, 0.25e5 }}); | |
| 928 | try expectFmt(".{ }", "{}", .{.{}}); | |
| 929 | try expectFmt(".{ -1 }", "{}", .{.{-1}}); | |
| 930 | try expectFmt(".{ -1, 42, 25000 }", "{}", .{.{ -1, 42, 0.25e5 }}); | |
| 1184 | 931 | } |
| 1185 | 932 | |
| 1186 | 933 | test "enum" { |
| ... | ... | @@ -1189,15 +936,15 @@ test "enum" { |
| 1189 | 936 | Two, |
| 1190 | 937 | }; |
| 1191 | 938 | const value = Enum.Two; |
| 1192 | try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{value}); | |
| 1193 | try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{&value}); | |
| 1194 | try expectFmt("enum: fmt.test.enum.Enum.One\n", "enum: {}\n", .{Enum.One}); | |
| 1195 | try expectFmt("enum: fmt.test.enum.Enum.Two\n", "enum: {}\n", .{Enum.Two}); | |
| 939 | try expectFmt("enum: .Two\n", "enum: {}\n", .{value}); | |
| 940 | try expectFmt("enum: .Two\n", "enum: {}\n", .{&value}); | |
| 941 | try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One}); | |
| 942 | try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two}); | |
| 1196 | 943 | |
| 1197 | 944 | // test very large enum to verify ct branch quota is large enough |
| 1198 | 945 | // TODO: https://github.com/ziglang/zig/issues/15609 |
| 1199 | 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 | } |
| 1202 | 949 | |
| 1203 | 950 | const E = enum { |
| ... | ... | @@ -1208,7 +955,7 @@ test "enum" { |
| 1208 | 955 | |
| 1209 | 956 | const inst = E.Two; |
| 1210 | 957 | |
| 1211 | try expectFmt("fmt.test.enum.E.Two", "{}", .{inst}); | |
| 958 | try expectFmt(".Two", "{}", .{inst}); | |
| 1212 | 959 | } |
| 1213 | 960 | |
| 1214 | 961 | test "non-exhaustive enum" { |
| ... | ... | @@ -1217,13 +964,17 @@ test "non-exhaustive enum" { |
| 1217 | 964 | Two = 0xbeef, |
| 1218 | 965 | _, |
| 1219 | 966 | }; |
| 1220 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {}\n", .{Enum.One}); | |
| 1221 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {}\n", .{Enum.Two}); | |
| 1222 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))}); | |
| 1223 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {x}\n", .{Enum.One}); | |
| 1224 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {x}\n", .{Enum.Two}); | |
| 1225 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {X}\n", .{Enum.Two}); | |
| 1226 | try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(1234)\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))}); | |
| 967 | try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One}); | |
| 968 | try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two}); | |
| 969 | try expectFmt("enum: @enumFromInt(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))}); | |
| 970 | try expectFmt("enum: f\n", "enum: {x}\n", .{Enum.One}); | |
| 971 | try expectFmt("enum: beef\n", "enum: {x}\n", .{Enum.Two}); | |
| 972 | try expectFmt("enum: BEEF\n", "enum: {X}\n", .{Enum.Two}); | |
| 973 | try expectFmt("enum: 1234\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))}); | |
| 974 | ||
| 975 | try expectFmt("enum: 15\n", "enum: {d}\n", .{Enum.One}); | |
| 976 | try expectFmt("enum: 48879\n", "enum: {d}\n", .{Enum.Two}); | |
| 977 | try expectFmt("enum: 4660\n", "enum: {d}\n", .{@as(Enum, @enumFromInt(0x1234))}); | |
| 1227 | 978 | } |
| 1228 | 979 | |
| 1229 | 980 | test "float.scientific" { |
| ... | ... | @@ -1349,41 +1100,6 @@ test "float.libc.sanity" { |
| 1349 | 1100 | try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1518338049))))}); |
| 1350 | 1101 | } |
| 1351 | 1102 | |
| 1352 | test "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 | ||
| 1387 | 1103 | test "union" { |
| 1388 | 1104 | const TU = union(enum) { |
| 1389 | 1105 | float: f32, |
| ... | ... | @@ -1400,18 +1116,13 @@ test "union" { |
| 1400 | 1116 | int: u32, |
| 1401 | 1117 | }; |
| 1402 | 1118 | |
| 1403 | const tu_inst = TU{ .int = 123 }; | |
| 1404 | const uu_inst = UU{ .int = 456 }; | |
| 1405 | const eu_inst = EU{ .float = 321.123 }; | |
| 1406 | ||
| 1407 | try expectFmt("fmt.test.union.TU{ .int = 123 }", "{}", .{tu_inst}); | |
| 1119 | const tu_inst: TU = .{ .int = 123 }; | |
| 1120 | const uu_inst: UU = .{ .int = 456 }; | |
| 1121 | const eu_inst: EU = .{ .float = 321.123 }; | |
| 1408 | 1122 | |
| 1409 | var buf: [100]u8 = undefined; | |
| 1410 | const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); | |
| 1411 | try std.testing.expectEqualStrings("fmt.test.union.UU@", uu_result[0..18]); | |
| 1412 | ||
| 1413 | const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); | |
| 1414 | try std.testing.expectEqualStrings("fmt.test.union.EU@", eu_result[0..18]); | |
| 1123 | try expectFmt(".{ .int = 123 }", "{}", .{tu_inst}); | |
| 1124 | try expectFmt(".{ ... }", "{}", .{uu_inst}); | |
| 1125 | try expectFmt(".{ .float = 321.123, .int = 1134596030 }", "{}", .{eu_inst}); | |
| 1415 | 1126 | } |
| 1416 | 1127 | |
| 1417 | 1128 | test "struct.self-referential" { |
| ... | ... | @@ -1425,7 +1136,7 @@ test "struct.self-referential" { |
| 1425 | 1136 | }; |
| 1426 | 1137 | inst.a = &inst; |
| 1427 | 1138 | |
| 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 | } |
| 1430 | 1141 | |
| 1431 | 1142 | test "struct.zero-size" { |
| ... | ... | @@ -1440,7 +1151,7 @@ test "struct.zero-size" { |
| 1440 | 1151 | const a = A{}; |
| 1441 | 1152 | const b = B{ .a = a, .c = 0 }; |
| 1442 | 1153 | |
| 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 | } |
| 1445 | 1156 | |
| 1446 | 1157 | /// Encodes a sequence of bytes as hexadecimal digits. |
| ... | ... | @@ -1551,33 +1262,17 @@ test "enum-literal" { |
| 1551 | 1262 | |
| 1552 | 1263 | test "padding" { |
| 1553 | 1264 | try expectFmt("Simple", "{s}", .{"Simple"}); |
| 1554 | try expectFmt(" true", "{:10}", .{true}); | |
| 1555 | try expectFmt(" true", "{:>10}", .{true}); | |
| 1556 | try expectFmt("======true", "{:=>10}", .{true}); | |
| 1557 | try expectFmt("true======", "{:=<10}", .{true}); | |
| 1558 | try expectFmt(" true ", "{:^10}", .{true}); | |
| 1559 | try expectFmt("===true===", "{:=^10}", .{true}); | |
| 1560 | try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"}); | |
| 1561 | try expectFmt("==================Filled", "{s:=>24}", .{"Filled"}); | |
| 1562 | try expectFmt(" Centered ", "{s:^24}", .{"Centered"}); | |
| 1563 | try expectFmt("-", "{s:-^1}", .{""}); | |
| 1564 | try expectFmt("==crêpe===", "{s:=^10}", .{"crêpe"}); | |
| 1565 | try expectFmt("=====crêpe", "{s:=>10}", .{"crêpe"}); | |
| 1566 | try expectFmt("crêpe=====", "{s:=<10}", .{"crêpe"}); | |
| 1265 | try expectFmt(" 1234", "{:10}", .{1234}); | |
| 1266 | try expectFmt(" 1234", "{:>10}", .{1234}); | |
| 1267 | try expectFmt("======1234", "{:=>10}", .{1234}); | |
| 1268 | try expectFmt("1234======", "{:=<10}", .{1234}); | |
| 1269 | try expectFmt(" 1234 ", "{:^10}", .{1234}); | |
| 1270 | try expectFmt("===1234===", "{:=^10}", .{1234}); | |
| 1567 | 1271 | try expectFmt("====a", "{c:=>5}", .{'a'}); |
| 1568 | 1272 | try expectFmt("==a==", "{c:=^5}", .{'a'}); |
| 1569 | 1273 | try expectFmt("a====", "{c:=<5}", .{'a'}); |
| 1570 | 1274 | } |
| 1571 | 1275 | |
| 1572 | test "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 | ||
| 1581 | 1276 | test "decimal float padding" { |
| 1582 | 1277 | const number: f32 = 3.1415; |
| 1583 | 1278 | try expectFmt("left-pad: **3.142\n", "left-pad: {d:*>7.3}\n", .{number}); |
| ... | ... | @@ -1620,17 +1315,17 @@ test "named arguments" { |
| 1620 | 1315 | |
| 1621 | 1316 | test "runtime width specifier" { |
| 1622 | 1317 | const width: usize = 9; |
| 1623 | try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); | |
| 1624 | try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); | |
| 1625 | try expectFmt(" hello", "{s:[1]}", .{ "hello", width }); | |
| 1626 | try expectFmt("42 hello", "{d} {s:[2]}", .{ 42, "hello", width }); | |
| 1318 | try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width }); | |
| 1319 | try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width }); | |
| 1320 | try expectFmt(" 12345", "{d:[1]}", .{ 12345, width }); | |
| 1321 | try expectFmt("42 12345", "{d} {d:[2]}", .{ 42, 12345, width }); | |
| 1627 | 1322 | } |
| 1628 | 1323 | |
| 1629 | 1324 | test "runtime precision specifier" { |
| 1630 | 1325 | const number: f32 = 3.1415; |
| 1631 | 1326 | const precision: usize = 2; |
| 1632 | try expectFmt("3.14e0", "{:1.[1]}", .{ number, precision }); | |
| 1633 | try expectFmt("3.14e0", "{:1.[precision]}", .{ .number = number, .precision = precision }); | |
| 1327 | try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision }); | |
| 1328 | try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision }); | |
| 1634 | 1329 | } |
| 1635 | 1330 | |
| 1636 | 1331 | test "recursive format function" { |
| ... | ... | @@ -1639,16 +1334,16 @@ test "recursive format function" { |
| 1639 | 1334 | Leaf: i32, |
| 1640 | 1335 | Branch: struct { left: *const R, right: *const R }, |
| 1641 | 1336 | |
| 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 | 1338 | return switch (self) { |
| 1644 | .Leaf => |n| std.fmt.format(writer, "Leaf({})", .{n}), | |
| 1645 | .Branch => |b| std.fmt.format(writer, "Branch({}, {})", .{ b.left, b.right }), | |
| 1339 | .Leaf => |n| writer.print("Leaf({})", .{n}), | |
| 1340 | .Branch => |b| writer.print("Branch({f}, {f})", .{ b.left, b.right }), | |
| 1646 | 1341 | }; |
| 1647 | 1342 | } |
| 1648 | 1343 | }; |
| 1649 | 1344 | |
| 1650 | var r = R{ .Leaf = 1 }; | |
| 1651 | try expectFmt("Leaf(1)\n", "{}\n", .{&r}); | |
| 1345 | var r: R = .{ .Leaf = 1 }; | |
| 1346 | try expectFmt("Leaf(1)\n", "{f}\n", .{&r}); | |
| 1652 | 1347 | } |
| 1653 | 1348 | |
| 1654 | 1349 | pub const hex_charset = "0123456789abcdef"; |
| ... | ... | @@ -1682,54 +1377,39 @@ test hex { |
| 1682 | 1377 | |
| 1683 | 1378 | test "parser until" { |
| 1684 | 1379 | { // return substring till ':' |
| 1685 | var parser: Parser = .{ | |
| 1686 | .iter = .{ .bytes = "abc:1234", .i = 0 }, | |
| 1687 | }; | |
| 1380 | var parser: Parser = .{ .bytes = "abc:1234", .i = 0 }; | |
| 1688 | 1381 | try testing.expectEqualStrings("abc", parser.until(':')); |
| 1689 | 1382 | } |
| 1690 | 1383 | |
| 1691 | 1384 | { // return the entire string - `ch` not found |
| 1692 | var parser: Parser = .{ | |
| 1693 | .iter = .{ .bytes = "abc1234", .i = 0 }, | |
| 1694 | }; | |
| 1385 | var parser: Parser = .{ .bytes = "abc1234", .i = 0 }; | |
| 1695 | 1386 | try testing.expectEqualStrings("abc1234", parser.until(':')); |
| 1696 | 1387 | } |
| 1697 | 1388 | |
| 1698 | 1389 | { // substring is empty - `ch` is the only character |
| 1699 | var parser: Parser = .{ | |
| 1700 | .iter = .{ .bytes = ":", .i = 0 }, | |
| 1701 | }; | |
| 1390 | var parser: Parser = .{ .bytes = ":", .i = 0 }; | |
| 1702 | 1391 | try testing.expectEqualStrings("", parser.until(':')); |
| 1703 | 1392 | } |
| 1704 | 1393 | |
| 1705 | 1394 | { // empty string and `ch` not found |
| 1706 | var parser: Parser = .{ | |
| 1707 | .iter = .{ .bytes = "", .i = 0 }, | |
| 1708 | }; | |
| 1395 | var parser: Parser = .{ .bytes = "", .i = 0 }; | |
| 1709 | 1396 | try testing.expectEqualStrings("", parser.until(':')); |
| 1710 | 1397 | } |
| 1711 | 1398 | |
| 1712 | 1399 | { // substring starts at index 2 and goes upto `ch` |
| 1713 | var parser: Parser = .{ | |
| 1714 | .iter = .{ .bytes = "abc:1234", .i = 2 }, | |
| 1715 | }; | |
| 1400 | var parser: Parser = .{ .bytes = "abc:1234", .i = 2 }; | |
| 1716 | 1401 | try testing.expectEqualStrings("c", parser.until(':')); |
| 1717 | 1402 | } |
| 1718 | 1403 | |
| 1719 | 1404 | { // substring starts at index 4 and goes upto the end - `ch` not found |
| 1720 | var parser: Parser = .{ | |
| 1721 | .iter = .{ .bytes = "abc1234", .i = 4 }, | |
| 1722 | }; | |
| 1405 | var parser: Parser = .{ .bytes = "abc1234", .i = 4 }; | |
| 1723 | 1406 | try testing.expectEqualStrings("234", parser.until(':')); |
| 1724 | 1407 | } |
| 1725 | 1408 | } |
| 1726 | 1409 | |
| 1727 | 1410 | test "parser peek" { |
| 1728 | 1411 | { // start iteration from the first index |
| 1729 | var parser: Parser = .{ | |
| 1730 | .iter = .{ .bytes = "hello world", .i = 0 }, | |
| 1731 | }; | |
| 1732 | ||
| 1412 | var parser: Parser = .{ .bytes = "hello world", .i = 0 }; | |
| 1733 | 1413 | try testing.expectEqual('h', parser.peek(0)); |
| 1734 | 1414 | try testing.expectEqual('e', parser.peek(1)); |
| 1735 | 1415 | try testing.expectEqual(' ', parser.peek(5)); |
| ... | ... | @@ -1738,9 +1418,7 @@ test "parser peek" { |
| 1738 | 1418 | } |
| 1739 | 1419 | |
| 1740 | 1420 | { // start iteration from the second last index |
| 1741 | var parser: Parser = .{ | |
| 1742 | .iter = .{ .bytes = "hello world!", .i = 10 }, | |
| 1743 | }; | |
| 1421 | var parser: Parser = .{ .bytes = "hello world!", .i = 10 }; | |
| 1744 | 1422 | |
| 1745 | 1423 | try testing.expectEqual('d', parser.peek(0)); |
| 1746 | 1424 | try testing.expectEqual('!', parser.peek(1)); |
| ... | ... | @@ -1748,18 +1426,14 @@ test "parser peek" { |
| 1748 | 1426 | } |
| 1749 | 1427 | |
| 1750 | 1428 | { // start iteration beyond the length of the string |
| 1751 | var parser: Parser = .{ | |
| 1752 | .iter = .{ .bytes = "hello", .i = 5 }, | |
| 1753 | }; | |
| 1429 | var parser: Parser = .{ .bytes = "hello", .i = 5 }; | |
| 1754 | 1430 | |
| 1755 | 1431 | try testing.expectEqual(null, parser.peek(0)); |
| 1756 | 1432 | try testing.expectEqual(null, parser.peek(1)); |
| 1757 | 1433 | } |
| 1758 | 1434 | |
| 1759 | 1435 | { // empty string |
| 1760 | var parser: Parser = .{ | |
| 1761 | .iter = .{ .bytes = "", .i = 0 }, | |
| 1762 | }; | |
| 1436 | var parser: Parser = .{ .bytes = "", .i = 0 }; | |
| 1763 | 1437 | |
| 1764 | 1438 | try testing.expectEqual(null, parser.peek(0)); |
| 1765 | 1439 | try testing.expectEqual(null, parser.peek(2)); |
| ... | ... | @@ -1768,78 +1442,78 @@ test "parser peek" { |
| 1768 | 1442 | |
| 1769 | 1443 | test "parser char" { |
| 1770 | 1444 | // character exists - iterator at 0 |
| 1771 | var parser: Parser = .{ .iter = .{ .bytes = "~~hello", .i = 0 } }; | |
| 1445 | var parser: Parser = .{ .bytes = "~~hello", .i = 0 }; | |
| 1772 | 1446 | try testing.expectEqual('~', parser.char()); |
| 1773 | 1447 | |
| 1774 | 1448 | // character exists - iterator in the middle |
| 1775 | parser = .{ .iter = .{ .bytes = "~~hello", .i = 3 } }; | |
| 1449 | parser = .{ .bytes = "~~hello", .i = 3 }; | |
| 1776 | 1450 | try testing.expectEqual('e', parser.char()); |
| 1777 | 1451 | |
| 1778 | 1452 | // character exists - iterator at the end |
| 1779 | parser = .{ .iter = .{ .bytes = "~~hello", .i = 6 } }; | |
| 1453 | parser = .{ .bytes = "~~hello", .i = 6 }; | |
| 1780 | 1454 | try testing.expectEqual('o', parser.char()); |
| 1781 | 1455 | |
| 1782 | 1456 | // 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 | 1458 | try testing.expectEqual(null, parser.char()); |
| 1785 | 1459 | } |
| 1786 | 1460 | |
| 1787 | 1461 | test "parser maybe" { |
| 1788 | 1462 | // 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 | 1464 | try testing.expect(parser.maybe('h')); |
| 1791 | 1465 | |
| 1792 | 1466 | // character exists - iterator at space |
| 1793 | parser = .{ .iter = .{ .bytes = "hello world", .i = 5 } }; | |
| 1467 | parser = .{ .bytes = "hello world", .i = 5 }; | |
| 1794 | 1468 | try testing.expect(parser.maybe(' ')); |
| 1795 | 1469 | |
| 1796 | 1470 | // character exists - iterator at the end |
| 1797 | parser = .{ .iter = .{ .bytes = "hello world", .i = 10 } }; | |
| 1471 | parser = .{ .bytes = "hello world", .i = 10 }; | |
| 1798 | 1472 | try testing.expect(parser.maybe('d')); |
| 1799 | 1473 | |
| 1800 | 1474 | // 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 | 1476 | try testing.expect(!parser.maybe('e')); |
| 1803 | 1477 | } |
| 1804 | 1478 | |
| 1805 | 1479 | test "parser number" { |
| 1806 | 1480 | // 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 | 1482 | try testing.expect(7 == parser.number()); |
| 1809 | 1483 | |
| 1810 | 1484 | // input is a two digit natural number - iterator at 1 |
| 1811 | parser = .{ .iter = .{ .bytes = "29", .i = 1 } }; | |
| 1485 | parser = .{ .bytes = "29", .i = 1 }; | |
| 1812 | 1486 | try testing.expect(9 == parser.number()); |
| 1813 | 1487 | |
| 1814 | 1488 | // 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 | 1490 | try testing.expectEqual(null, parser.number()); |
| 1817 | 1491 | |
| 1818 | 1492 | // input is an integer |
| 1819 | parser = .{ .iter = .{ .bytes = "0", .i = 0 } }; | |
| 1493 | parser = .{ .bytes = "0", .i = 0 }; | |
| 1820 | 1494 | try testing.expect(0 == parser.number()); |
| 1821 | 1495 | |
| 1822 | 1496 | // input is a negative integer |
| 1823 | parser = .{ .iter = .{ .bytes = "-2", .i = 0 } }; | |
| 1497 | parser = .{ .bytes = "-2", .i = 0 }; | |
| 1824 | 1498 | try testing.expectEqual(null, parser.number()); |
| 1825 | 1499 | |
| 1826 | 1500 | // input is a string |
| 1827 | parser = .{ .iter = .{ .bytes = "no_number", .i = 2 } }; | |
| 1501 | parser = .{ .bytes = "no_number", .i = 2 }; | |
| 1828 | 1502 | try testing.expectEqual(null, parser.number()); |
| 1829 | 1503 | |
| 1830 | 1504 | // input is a single character string |
| 1831 | parser = .{ .iter = .{ .bytes = "n", .i = 0 } }; | |
| 1505 | parser = .{ .bytes = "n", .i = 0 }; | |
| 1832 | 1506 | try testing.expectEqual(null, parser.number()); |
| 1833 | 1507 | |
| 1834 | 1508 | // input is an empty string |
| 1835 | parser = .{ .iter = .{ .bytes = "", .i = 0 } }; | |
| 1509 | parser = .{ .bytes = "", .i = 0 }; | |
| 1836 | 1510 | try testing.expectEqual(null, parser.number()); |
| 1837 | 1511 | } |
| 1838 | 1512 | |
| 1839 | 1513 | test "parser specifier" { |
| 1840 | 1514 | { // input string is a digit; iterator at 0 |
| 1841 | 1515 | const expected: Specifier = Specifier{ .number = 1 }; |
| 1842 | var parser: Parser = .{ .iter = .{ .bytes = "1", .i = 0 } }; | |
| 1516 | var parser: Parser = .{ .bytes = "1", .i = 0 }; | |
| 1843 | 1517 | |
| 1844 | 1518 | const result = try parser.specifier(); |
| 1845 | 1519 | try testing.expect(expected.number == result.number); |
| ... | ... | @@ -1847,7 +1521,7 @@ test "parser specifier" { |
| 1847 | 1521 | |
| 1848 | 1522 | { // input string is a two digit number; iterator at 0 |
| 1849 | 1523 | const digit: Specifier = Specifier{ .number = 42 }; |
| 1850 | var parser: Parser = .{ .iter = .{ .bytes = "42", .i = 0 } }; | |
| 1524 | var parser: Parser = .{ .bytes = "42", .i = 0 }; | |
| 1851 | 1525 | |
| 1852 | 1526 | const result = try parser.specifier(); |
| 1853 | 1527 | try testing.expect(digit.number == result.number); |
| ... | ... | @@ -1855,7 +1529,7 @@ test "parser specifier" { |
| 1855 | 1529 | |
| 1856 | 1530 | { // input string is a two digit number digit; iterator at 1 |
| 1857 | 1531 | const digit: Specifier = Specifier{ .number = 8 }; |
| 1858 | var parser: Parser = .{ .iter = .{ .bytes = "28", .i = 1 } }; | |
| 1532 | var parser: Parser = .{ .bytes = "28", .i = 1 }; | |
| 1859 | 1533 | |
| 1860 | 1534 | const result = try parser.specifier(); |
| 1861 | 1535 | try testing.expect(digit.number == result.number); |
| ... | ... | @@ -1863,7 +1537,7 @@ test "parser specifier" { |
| 1863 | 1537 | |
| 1864 | 1538 | { // input string is a two digit number with square brackets; iterator at 0 |
| 1865 | 1539 | const digit: Specifier = Specifier{ .named = "15" }; |
| 1866 | var parser: Parser = .{ .iter = .{ .bytes = "[15]", .i = 0 } }; | |
| 1540 | var parser: Parser = .{ .bytes = "[15]", .i = 0 }; | |
| 1867 | 1541 | |
| 1868 | 1542 | const result = try parser.specifier(); |
| 1869 | 1543 | try testing.expectEqualStrings(digit.named, result.named); |
| ... | ... | @@ -1871,21 +1545,21 @@ test "parser specifier" { |
| 1871 | 1545 | |
| 1872 | 1546 | { // input string is not a number and contains square brackets; iterator at 0 |
| 1873 | 1547 | const digit: Specifier = Specifier{ .named = "hello" }; |
| 1874 | var parser: Parser = .{ .iter = .{ .bytes = "[hello]", .i = 0 } }; | |
| 1548 | var parser: Parser = .{ .bytes = "[hello]", .i = 0 }; | |
| 1875 | 1549 | |
| 1876 | 1550 | const result = try parser.specifier(); |
| 1877 | 1551 | try testing.expectEqualStrings(digit.named, result.named); |
| 1878 | 1552 | } |
| 1879 | 1553 | |
| 1880 | 1554 | { // 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 }; | |
| 1882 | 1556 | |
| 1883 | 1557 | const result = parser.specifier(); |
| 1884 | 1558 | try testing.expectError(@field(anyerror, "Expected closing ]"), result); |
| 1885 | 1559 | } |
| 1886 | 1560 | |
| 1887 | 1561 | { // 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 }; | |
| 1889 | 1563 | |
| 1890 | 1564 | const result = parser.specifier(); |
| 1891 | 1565 | try testing.expectError(@field(anyerror, "Expected closing ]"), result); |
| ... | ... | @@ -1893,7 +1567,7 @@ test "parser specifier" { |
| 1893 | 1567 | |
| 1894 | 1568 | { // input string is not a number and contains unbalanced square brackets; iterator at 0 |
| 1895 | 1569 | const digit: Specifier = Specifier{ .named = "[[hello" }; |
| 1896 | var parser: Parser = .{ .iter = .{ .bytes = "[[[hello]", .i = 0 } }; | |
| 1570 | var parser: Parser = .{ .bytes = "[[[hello]", .i = 0 }; | |
| 1897 | 1571 | |
| 1898 | 1572 | const result = try parser.specifier(); |
| 1899 | 1573 | try testing.expectEqualStrings(digit.named, result.named); |
| ... | ... | @@ -1901,7 +1575,7 @@ test "parser specifier" { |
| 1901 | 1575 | |
| 1902 | 1576 | { // input string is not a number and contains unbalanced square brackets; iterator at 1 |
| 1903 | 1577 | const digit: Specifier = Specifier{ .named = "[[hello" }; |
| 1904 | var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]", .i = 1 } }; | |
| 1578 | var parser: Parser = .{ .bytes = "[[[[hello]]]]]", .i = 1 }; | |
| 1905 | 1579 | |
| 1906 | 1580 | const result = try parser.specifier(); |
| 1907 | 1581 | try testing.expectEqualStrings(digit.named, result.named); |
| ... | ... | @@ -1909,9 +1583,13 @@ test "parser specifier" { |
| 1909 | 1583 | |
| 1910 | 1584 | { // input string is neither a digit nor a named argument |
| 1911 | 1585 | const char: Specifier = Specifier{ .none = {} }; |
| 1912 | var parser: Parser = .{ .iter = .{ .bytes = "hello", .i = 0 } }; | |
| 1586 | var parser: Parser = .{ .bytes = "hello", .i = 0 }; | |
| 1913 | 1587 | |
| 1914 | 1588 | const result = try parser.specifier(); |
| 1915 | 1589 | try testing.expectEqual(char.none, result.none); |
| 1916 | 1590 | } |
| 1917 | 1591 | } |
| 1592 | ||
| 1593 | test { | |
| 1594 | _ = float; | |
| 1595 | } |
lib/std/fs/File.zig+193-108| ... | ... | @@ -1,3 +1,20 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const Os = std.builtin.Os; | |
| 3 | const native_os = builtin.os.tag; | |
| 4 | const is_windows = native_os == .windows; | |
| 5 | ||
| 6 | const File = @This(); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | const Allocator = std.mem.Allocator; | |
| 9 | const posix = std.posix; | |
| 10 | const io = std.io; | |
| 11 | const math = std.math; | |
| 12 | const assert = std.debug.assert; | |
| 13 | const linux = std.os.linux; | |
| 14 | const windows = std.os.windows; | |
| 15 | const maxInt = std.math.maxInt; | |
| 16 | const Alignment = std.mem.Alignment; | |
| 17 | ||
| 1 | 18 | /// The OS-specific file descriptor or file handle. |
| 2 | 19 | handle: Handle, |
| 3 | 20 | |
| ... | ... | @@ -844,7 +861,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize { |
| 844 | 861 | return posix.write(self.handle, bytes); |
| 845 | 862 | } |
| 846 | 863 | |
| 847 | /// One-shot alternative to `std.io.Writer.writeAll` via `writer`. | |
| 864 | /// Deprecated in favor of `Writer`. | |
| 848 | 865 | pub fn writeAll(self: File, bytes: []const u8) WriteError!void { |
| 849 | 866 | var index: usize = 0; |
| 850 | 867 | while (index < bytes.len) { |
| ... | ... | @@ -900,6 +917,8 @@ pub const Reader = struct { |
| 900 | 917 | file: File, |
| 901 | 918 | err: ?ReadError = null, |
| 902 | 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 | 922 | pos: u64 = 0, |
| 904 | 923 | size: ?u64 = null, |
| 905 | 924 | size_err: ?GetEndPosError = null, |
| ... | ... | @@ -1008,7 +1027,7 @@ pub const Reader = struct { |
| 1008 | 1027 | }; |
| 1009 | 1028 | var remaining = std.math.cast(u64, offset) orelse return seek_err; |
| 1010 | 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 | 1031 | r.seek_err = err; |
| 1013 | 1032 | return err; |
| 1014 | 1033 | }; |
| ... | ... | @@ -1043,7 +1062,7 @@ pub const Reader = struct { |
| 1043 | 1062 | const max_buffers_len = 16; |
| 1044 | 1063 | |
| 1045 | 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 | 1066 | switch (r.mode) { |
| 1048 | 1067 | .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { |
| 1049 | 1068 | error.Unimplemented => { |
| ... | ... | @@ -1067,10 +1086,14 @@ pub const Reader = struct { |
| 1067 | 1086 | const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) { |
| 1068 | 1087 | error.Unseekable => { |
| 1069 | 1088 | r.mode = r.mode.toStreaming(); |
| 1070 | if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch { | |
| 1071 | r.mode = .failure; | |
| 1072 | return error.ReadFailed; | |
| 1073 | }; | |
| 1089 | const pos = r.pos; | |
| 1090 | if (pos != 0) { | |
| 1091 | r.pos = 0; | |
| 1092 | r.seekBy(@intCast(pos)) catch { | |
| 1093 | r.mode = .failure; | |
| 1094 | return error.ReadFailed; | |
| 1095 | }; | |
| 1096 | } | |
| 1074 | 1097 | return 0; |
| 1075 | 1098 | }, |
| 1076 | 1099 | else => |e| { |
| ... | ... | @@ -1113,7 +1136,7 @@ pub const Reader = struct { |
| 1113 | 1136 | } |
| 1114 | 1137 | |
| 1115 | 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 | 1140 | const file = r.file; |
| 1118 | 1141 | const pos = r.pos; |
| 1119 | 1142 | switch (r.mode) { |
| ... | ... | @@ -1195,10 +1218,14 @@ pub const Reader = struct { |
| 1195 | 1218 | const n = r.file.pread(dest, r.pos) catch |err| switch (err) { |
| 1196 | 1219 | error.Unseekable => { |
| 1197 | 1220 | r.mode = r.mode.toStreaming(); |
| 1198 | if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch { | |
| 1199 | r.mode = .failure; | |
| 1200 | return error.ReadFailed; | |
| 1201 | }; | |
| 1221 | const pos = r.pos; | |
| 1222 | if (pos != 0) { | |
| 1223 | r.pos = 0; | |
| 1224 | r.seekBy(@intCast(pos)) catch { | |
| 1225 | r.mode = .failure; | |
| 1226 | return error.ReadFailed; | |
| 1227 | }; | |
| 1228 | } | |
| 1202 | 1229 | return 0; |
| 1203 | 1230 | }, |
| 1204 | 1231 | else => |e| { |
| ... | ... | @@ -1246,6 +1273,8 @@ pub const Writer = struct { |
| 1246 | 1273 | file: File, |
| 1247 | 1274 | err: ?WriteError = null, |
| 1248 | 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 | 1278 | pos: u64 = 0, |
| 1250 | 1279 | sendfile_err: ?SendfileError = null, |
| 1251 | 1280 | copy_file_range_err: ?CopyFileRangeError = null, |
| ... | ... | @@ -1308,110 +1337,162 @@ pub const Writer = struct { |
| 1308 | 1337 | }; |
| 1309 | 1338 | } |
| 1310 | 1339 | |
| 1311 | pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 1312 | const w: *Writer = @fieldParentPtr("interface", io_writer); | |
| 1340 | pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 1341 | const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); | |
| 1313 | 1342 | const handle = w.file.handle; |
| 1314 | const buffered = io_writer.buffered(); | |
| 1315 | var splat_buffer: [256]u8 = undefined; | |
| 1316 | if (is_windows) { | |
| 1317 | var i: usize = 0; | |
| 1318 | while (i < buffered.len) { | |
| 1319 | const n = windows.WriteFile(handle, buffered[i..], null) catch |err| { | |
| 1343 | const buffered = io_w.buffered(); | |
| 1344 | if (is_windows) switch (w.mode) { | |
| 1345 | .positional, .positional_reading => { | |
| 1346 | if (buffered.len != 0) { | |
| 1347 | const n = windows.WriteFile(handle, buffered, w.pos) catch |err| { | |
| 1348 | w.err = err; | |
| 1349 | return error.WriteFailed; | |
| 1350 | }; | |
| 1351 | w.pos += n; | |
| 1352 | return io_w.consume(n); | |
| 1353 | } | |
| 1354 | for (data[0 .. data.len - 1]) |buf| { | |
| 1355 | if (buf.len == 0) continue; | |
| 1356 | const n = windows.WriteFile(handle, buf, w.pos) catch |err| { | |
| 1357 | w.err = err; | |
| 1358 | return error.WriteFailed; | |
| 1359 | }; | |
| 1360 | w.pos += n; | |
| 1361 | return io_w.consume(n); | |
| 1362 | } | |
| 1363 | const pattern = data[data.len - 1]; | |
| 1364 | if (pattern.len == 0 or splat == 0) return 0; | |
| 1365 | const n = windows.WriteFile(handle, pattern, w.pos) catch |err| { | |
| 1320 | 1366 | w.err = err; |
| 1321 | w.pos += i; | |
| 1322 | _ = io_writer.consume(i); | |
| 1323 | 1367 | return error.WriteFailed; |
| 1324 | 1368 | }; |
| 1325 | i += n; | |
| 1326 | if (data.len > 0 and buffered.len - i < n) { | |
| 1327 | w.pos += i; | |
| 1328 | return io_writer.consume(i); | |
| 1369 | w.pos += n; | |
| 1370 | return io_w.consume(n); | |
| 1371 | }, | |
| 1372 | .streaming, .streaming_reading => { | |
| 1373 | if (buffered.len != 0) { | |
| 1374 | const n = windows.WriteFile(handle, buffered, null) catch |err| { | |
| 1375 | w.err = err; | |
| 1376 | return error.WriteFailed; | |
| 1377 | }; | |
| 1378 | w.pos += n; | |
| 1379 | return io_w.consume(n); | |
| 1329 | 1380 | } |
| 1330 | } | |
| 1331 | if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) { | |
| 1332 | w.pos += i; | |
| 1333 | return io_writer.consume(i); | |
| 1334 | } | |
| 1335 | const n = windows.WriteFile(handle, data[0], null) catch |err| { | |
| 1336 | w.err = err; | |
| 1337 | return 0; | |
| 1338 | }; | |
| 1339 | w.pos += n; | |
| 1340 | return n; | |
| 1341 | } | |
| 1342 | if (data.len == 0) { | |
| 1343 | var i: usize = 0; | |
| 1344 | while (i < buffered.len) { | |
| 1345 | i += std.posix.write(handle, buffered) catch |err| { | |
| 1381 | for (data[0 .. data.len - 1]) |buf| { | |
| 1382 | if (buf.len == 0) continue; | |
| 1383 | const n = windows.WriteFile(handle, buf, null) catch |err| { | |
| 1384 | w.err = err; | |
| 1385 | return error.WriteFailed; | |
| 1386 | }; | |
| 1387 | w.pos += n; | |
| 1388 | return io_w.consume(n); | |
| 1389 | } | |
| 1390 | const pattern = data[data.len - 1]; | |
| 1391 | if (pattern.len == 0 or splat == 0) return 0; | |
| 1392 | const n = windows.WriteFile(handle, pattern, null) catch |err| { | |
| 1393 | std.debug.print("windows write file failed3: {t}\n", .{err}); | |
| 1346 | 1394 | w.err = err; |
| 1347 | w.pos += i; | |
| 1348 | _ = io_writer.consume(i); | |
| 1349 | 1395 | return error.WriteFailed; |
| 1350 | 1396 | }; |
| 1351 | } | |
| 1352 | w.pos += i; | |
| 1353 | return io_writer.consumeAll(); | |
| 1354 | } | |
| 1397 | w.pos += n; | |
| 1398 | return io_w.consume(n); | |
| 1399 | }, | |
| 1400 | .failure => return error.WriteFailed, | |
| 1401 | }; | |
| 1355 | 1402 | var iovecs: [max_buffers_len]std.posix.iovec_const = undefined; |
| 1356 | 1403 | var len: usize = 0; |
| 1357 | 1404 | if (buffered.len > 0) { |
| 1358 | 1405 | iovecs[len] = .{ .base = buffered.ptr, .len = buffered.len }; |
| 1359 | 1406 | len += 1; |
| 1360 | 1407 | } |
| 1361 | for (data) |d| { | |
| 1408 | for (data[0 .. data.len - 1]) |d| { | |
| 1362 | 1409 | if (d.len == 0) continue; |
| 1363 | if (iovecs.len - len == 0) break; | |
| 1364 | 1410 | iovecs[len] = .{ .base = d.ptr, .len = d.len }; |
| 1365 | 1411 | len += 1; |
| 1412 | if (iovecs.len - len == 0) break; | |
| 1366 | 1413 | } |
| 1367 | switch (splat) { | |
| 1368 | 0 => if (data[data.len - 1].len != 0) { | |
| 1369 | len -= 1; | |
| 1414 | const pattern = data[data.len - 1]; | |
| 1415 | if (iovecs.len - len != 0) switch (splat) { | |
| 1416 | 0 => {}, | |
| 1417 | 1 => if (pattern.len != 0) { | |
| 1418 | iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len }; | |
| 1419 | len += 1; | |
| 1370 | 1420 | }, |
| 1371 | 1 => {}, | |
| 1372 | else => switch (data[data.len - 1].len) { | |
| 1421 | else => switch (pattern.len) { | |
| 1373 | 1422 | 0 => {}, |
| 1374 | 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 | 1430 | const memset_len = @min(splat_buffer.len, splat); |
| 1376 | 1431 | const buf = splat_buffer[0..memset_len]; |
| 1377 | @memset(buf, data[data.len - 1][0]); | |
| 1378 | iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len }; | |
| 1432 | @memset(buf, pattern[0]); | |
| 1433 | iovecs[len] = .{ .base = buf.ptr, .len = buf.len }; | |
| 1434 | len += 1; | |
| 1379 | 1435 | var remaining_splat = splat - buf.len; |
| 1380 | while (remaining_splat > splat_buffer.len and len < iovecs.len) { | |
| 1381 | iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len }; | |
| 1382 | remaining_splat -= splat_buffer.len; | |
| 1436 | while (remaining_splat > splat_buffer.len and iovecs.len - len != 0) { | |
| 1437 | assert(buf.len == splat_buffer.len); | |
| 1438 | iovecs[len] = .{ .base = splat_buffer.ptr, .len = splat_buffer.len }; | |
| 1383 | 1439 | len += 1; |
| 1440 | remaining_splat -= splat_buffer.len; | |
| 1384 | 1441 | } |
| 1385 | if (remaining_splat > 0 and len < iovecs.len) { | |
| 1386 | iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat }; | |
| 1442 | if (remaining_splat > 0 and iovecs.len - len != 0) { | |
| 1443 | iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat }; | |
| 1387 | 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) |_| { | |
| 1395 | if (iovecs.len - len == 0) break; | |
| 1396 | iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].len }; | |
| 1447 | else => for (0..splat) |_| { | |
| 1448 | iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len }; | |
| 1397 | 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; | |
| 1406 | return io_writer.consume(n); | |
| 1454 | if (len == 0) return 0; | |
| 1455 | switch (w.mode) { | |
| 1456 | .positional, .positional_reading => { | |
| 1457 | const n = std.posix.pwritev(handle, iovecs[0..len], w.pos) catch |err| switch (err) { | |
| 1458 | error.Unseekable => { | |
| 1459 | w.mode = w.mode.toStreaming(); | |
| 1460 | const pos = w.pos; | |
| 1461 | if (pos != 0) { | |
| 1462 | w.pos = 0; | |
| 1463 | w.seekTo(@intCast(pos)) catch { | |
| 1464 | w.mode = .failure; | |
| 1465 | return error.WriteFailed; | |
| 1466 | }; | |
| 1467 | } | |
| 1468 | return 0; | |
| 1469 | }, | |
| 1470 | else => |e| { | |
| 1471 | w.err = e; | |
| 1472 | return error.WriteFailed; | |
| 1473 | }, | |
| 1474 | }; | |
| 1475 | w.pos += n; | |
| 1476 | return io_w.consume(n); | |
| 1477 | }, | |
| 1478 | .streaming, .streaming_reading => { | |
| 1479 | const n = std.posix.writev(handle, iovecs[0..len]) catch |err| { | |
| 1480 | w.err = err; | |
| 1481 | return error.WriteFailed; | |
| 1482 | }; | |
| 1483 | w.pos += n; | |
| 1484 | return io_w.consume(n); | |
| 1485 | }, | |
| 1486 | .failure => return error.WriteFailed, | |
| 1487 | } | |
| 1407 | 1488 | } |
| 1408 | 1489 | |
| 1409 | 1490 | pub fn sendFile( |
| 1410 | io_writer: *std.io.Writer, | |
| 1491 | io_w: *std.io.Writer, | |
| 1411 | 1492 | file_reader: *Reader, |
| 1412 | 1493 | limit: std.io.Limit, |
| 1413 | 1494 | ) std.io.Writer.FileError!usize { |
| 1414 | const w: *Writer = @fieldParentPtr("interface", io_writer); | |
| 1495 | const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); | |
| 1415 | 1496 | const out_fd = w.file.handle; |
| 1416 | 1497 | const in_fd = file_reader.file.handle; |
| 1417 | 1498 | // TODO try using copy_file_range on FreeBSD |
| ... | ... | @@ -1422,7 +1503,7 @@ pub const Writer = struct { |
| 1422 | 1503 | if (w.sendfile_err != null) break :sf; |
| 1423 | 1504 | // Linux sendfile does not support headers. |
| 1424 | 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 | 1507 | const max_count = 0x7ffff000; // Avoid EINVAL. |
| 1427 | 1508 | var off: std.os.linux.off_t = undefined; |
| 1428 | 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 | 1527 | const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) { |
| 1447 | 1528 | error.Unseekable => { |
| 1448 | 1529 | file_reader.mode = file_reader.mode.toStreaming(); |
| 1449 | if (file_reader.pos != 0) file_reader.seekBy(@intCast(file_reader.pos)) catch { | |
| 1450 | file_reader.mode = .failure; | |
| 1451 | return error.ReadFailed; | |
| 1452 | }; | |
| 1530 | const pos = file_reader.pos; | |
| 1531 | if (pos != 0) { | |
| 1532 | file_reader.pos = 0; | |
| 1533 | file_reader.seekBy(@intCast(pos)) catch { | |
| 1534 | file_reader.mode = .failure; | |
| 1535 | return error.ReadFailed; | |
| 1536 | }; | |
| 1537 | } | |
| 1453 | 1538 | return 0; |
| 1454 | 1539 | }, |
| 1455 | 1540 | else => |e| { |
| ... | ... | @@ -1465,21 +1550,21 @@ pub const Writer = struct { |
| 1465 | 1550 | w.pos += n; |
| 1466 | 1551 | return n; |
| 1467 | 1552 | } |
| 1468 | const copy_file_range_fn = switch (native_os) { | |
| 1553 | const copy_file_range = switch (native_os) { | |
| 1469 | 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, | |
| 1471 | else => null, | |
| 1555 | .linux => if (std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) std.os.linux.wrapped.copy_file_range else {}, | |
| 1556 | else => {}, | |
| 1472 | 1557 | }; |
| 1473 | if (copy_file_range_fn) |copy_file_range| cfr: { | |
| 1558 | if (@TypeOf(copy_file_range) != void) cfr: { | |
| 1474 | 1559 | if (w.copy_file_range_err != null) break :cfr; |
| 1475 | 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 | 1562 | var off_in: i64 = undefined; |
| 1478 | 1563 | var off_out: i64 = undefined; |
| 1479 | 1564 | const off_in_ptr: ?*i64 = switch (file_reader.mode) { |
| 1480 | 1565 | .positional_reading, .streaming_reading => return error.Unimplemented, |
| 1481 | 1566 | .positional => p: { |
| 1482 | off_in = file_reader.pos; | |
| 1567 | off_in = @intCast(file_reader.pos); | |
| 1483 | 1568 | break :p &off_in; |
| 1484 | 1569 | }, |
| 1485 | 1570 | .streaming => null, |
| ... | ... | @@ -1488,7 +1573,7 @@ pub const Writer = struct { |
| 1488 | 1573 | const off_out_ptr: ?*i64 = switch (w.mode) { |
| 1489 | 1574 | .positional_reading, .streaming_reading => return error.Unimplemented, |
| 1490 | 1575 | .positional => p: { |
| 1491 | off_out = w.pos; | |
| 1576 | off_out = @intCast(w.pos); | |
| 1492 | 1577 | break :p &off_out; |
| 1493 | 1578 | }, |
| 1494 | 1579 | .streaming => null, |
| ... | ... | @@ -1542,19 +1627,35 @@ pub const Writer = struct { |
| 1542 | 1627 | } |
| 1543 | 1628 | |
| 1544 | 1629 | pub fn seekTo(w: *Writer, offset: u64) SeekError!void { |
| 1545 | if (w.seek_err) |err| return err; | |
| 1546 | 1630 | switch (w.mode) { |
| 1547 | 1631 | .positional, .positional_reading => { |
| 1548 | 1632 | w.pos = offset; |
| 1549 | 1633 | }, |
| 1550 | 1634 | .streaming, .streaming_reading => { |
| 1635 | if (w.seek_err) |err| return err; | |
| 1551 | 1636 | posix.lseek_SET(w.file.handle, offset) catch |err| { |
| 1552 | 1637 | w.seek_err = err; |
| 1553 | 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 | }; |
| 1559 | 1660 | |
| 1560 | 1661 | /// Defaults to positional reading; falls back to streaming. |
| ... | ... | @@ -1568,9 +1669,10 @@ pub fn reader(file: File, buffer: []u8) Reader { |
| 1568 | 1669 | /// Positional is more threadsafe, since the global seek position is not |
| 1569 | 1670 | /// affected, but when such syscalls are not available, preemptively choosing |
| 1570 | 1671 | /// `Reader.Mode.streaming` will skip a failed syscall. |
| 1571 | pub fn readerStreaming(file: File) Reader { | |
| 1672 | pub fn readerStreaming(file: File, buffer: []u8) Reader { | |
| 1572 | 1673 | return .{ |
| 1573 | 1674 | .file = file, |
| 1675 | .interface = Reader.initInterface(buffer), | |
| 1574 | 1676 | .mode = .streaming, |
| 1575 | 1677 | .seek_err = error.Unseekable, |
| 1576 | 1678 | }; |
| ... | ... | @@ -1753,20 +1855,3 @@ pub fn downgradeLock(file: File) LockError!void { |
| 1753 | 1855 | }; |
| 1754 | 1856 | } |
| 1755 | 1857 | } |
| 1756 | ||
| 1757 | const builtin = @import("builtin"); | |
| 1758 | const Os = std.builtin.Os; | |
| 1759 | const native_os = builtin.os.tag; | |
| 1760 | const is_windows = native_os == .windows; | |
| 1761 | ||
| 1762 | const File = @This(); | |
| 1763 | const std = @import("../std.zig"); | |
| 1764 | const Allocator = std.mem.Allocator; | |
| 1765 | const posix = std.posix; | |
| 1766 | const io = std.io; | |
| 1767 | const math = std.math; | |
| 1768 | const assert = std.debug.assert; | |
| 1769 | const linux = std.os.linux; | |
| 1770 | const windows = std.os.windows; | |
| 1771 | const maxInt = std.math.maxInt; | |
| 1772 | const 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 | 146 | return out[0 .. out.len - 1 :0]; |
| 147 | 147 | } |
| 148 | 148 | |
| 149 | pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter(formatJoin) { | |
| 149 | pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter([]const []const u8, formatJoin) { | |
| 150 | 150 | return .{ .data = paths }; |
| 151 | 151 | } |
| 152 | 152 | |
| 153 | fn formatJoin(paths: []const []const u8, bw: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 154 | comptime assert(fmt.len == 0); | |
| 155 | ||
| 153 | fn formatJoin(paths: []const []const u8, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 156 | 154 | const first_path_idx = for (paths, 0..) |p, idx| { |
| 157 | 155 | if (p.len != 0) break idx; |
| 158 | 156 | } else return; |
| 159 | 157 | |
| 160 | try bw.writeAll(paths[first_path_idx]); // first component | |
| 158 | try w.writeAll(paths[first_path_idx]); // first component | |
| 161 | 159 | var prev_path = paths[first_path_idx]; |
| 162 | 160 | for (paths[first_path_idx + 1 ..]) |this_path| { |
| 163 | 161 | if (this_path.len == 0) continue; // skip empty components |
| 164 | 162 | const prev_sep = isSep(prev_path[prev_path.len - 1]); |
| 165 | 163 | const this_sep = isSep(this_path[0]); |
| 166 | 164 | if (!prev_sep and !this_sep) { |
| 167 | try bw.writeByte(sep); | |
| 165 | try w.writeByte(sep); | |
| 168 | 166 | } |
| 169 | 167 | if (prev_sep and this_sep) { |
| 170 | try bw.writeAll(this_path[1..]); // skip redundant separator | |
| 168 | try w.writeAll(this_path[1..]); // skip redundant separator | |
| 171 | 169 | } else { |
| 172 | try bw.writeAll(this_path); | |
| 170 | try w.writeAll(this_path); | |
| 173 | 171 | } |
| 174 | 172 | prev_path = this_path; |
| 175 | 173 | } |
lib/std/fs/test.zig+2-2| ... | ... | @@ -1798,11 +1798,11 @@ test "walker" { |
| 1798 | 1798 | var num_walked: usize = 0; |
| 1799 | 1799 | while (try walker.next()) |entry| { |
| 1800 | 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 | 1802 | return err; |
| 1803 | 1803 | }; |
| 1804 | 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 | 1806 | return err; |
| 1807 | 1807 | }; |
| 1808 | 1808 | // make sure that the entry.dir is the containing dir |
lib/std/heap.zig+1-1| ... | ... | @@ -287,7 +287,7 @@ fn rawCAlloc( |
| 287 | 287 | ) ?[*]u8 { |
| 288 | 288 | _ = context; |
| 289 | 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 | 291 | // Note that this pointer cannot be aligncasted to max_align_t because if |
| 292 | 292 | // len is < max_align_t then the alignment can be smaller. For example, if |
| 293 | 293 | // 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 | 42 | data: usize, |
| 43 | 43 | node: std.SinglyLinkedList.Node = .{}, |
| 44 | 44 | }; |
| 45 | const BufNode_alignment: Alignment = .fromByteUnits(@alignOf(BufNode)); | |
| 45 | const BufNode_alignment: Alignment = .of(BufNode); | |
| 46 | 46 | |
| 47 | 47 | pub fn init(child_allocator: Allocator) ArenaAllocator { |
| 48 | 48 | return (State{}).promote(child_allocator); |
lib/std/heap/debug_allocator.zig+1-1| ... | ... | @@ -1054,7 +1054,7 @@ const TraceKind = enum { |
| 1054 | 1054 | free, |
| 1055 | 1055 | }; |
| 1056 | 1056 | |
| 1057 | const test_config = Config{}; | |
| 1057 | const test_config: Config = .{}; | |
| 1058 | 1058 | |
| 1059 | 1059 | test "small allocations - free in same order" { |
| 1060 | 1060 | var gpa = DebugAllocator(test_config){}; |
lib/std/http.zig+2-2| ... | ... | @@ -42,8 +42,8 @@ pub const Method = enum(u64) { |
| 42 | 42 | return x; |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | pub fn write(self: Method, w: anytype) !void { | |
| 46 | const bytes = std.mem.asBytes(&@intFromEnum(self)); | |
| 45 | pub fn format(self: Method, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 46 | const bytes: []const u8 = @ptrCast(&@intFromEnum(self)); | |
| 47 | 47 | const str = std.mem.sliceTo(bytes, 0); |
| 48 | 48 | try w.writeAll(str); |
| 49 | 49 | } |
lib/std/http/Client.zig+13-4| ... | ... | @@ -920,7 +920,7 @@ pub const Request = struct { |
| 920 | 920 | .authority = connection.proxied, |
| 921 | 921 | .path = true, |
| 922 | 922 | .query = true, |
| 923 | }, w); | |
| 923 | }); | |
| 924 | 924 | } |
| 925 | 925 | try w.writeByte(' '); |
| 926 | 926 | try w.writeAll(@tagName(r.version)); |
| ... | ... | @@ -1280,9 +1280,18 @@ pub const basic_authorization = struct { |
| 1280 | 1280 | } |
| 1281 | 1281 | |
| 1282 | 1282 | pub fn valueLengthFromUri(uri: Uri) usize { |
| 1283 | // TODO don't abuse formatted printing to count percent encoded characters | |
| 1284 | const user_len = std.fmt.count("{fuser}", .{uri.user orelse Uri.Component.empty}); | |
| 1285 | const password_len = std.fmt.count("{fpassword}", .{uri.password orelse Uri.Component.empty}); | |
| 1283 | const user: Uri.Component = uri.user orelse .empty; | |
| 1284 | const password: Uri.Component = uri.password orelse .empty; | |
| 1285 | ||
| 1286 | var dw: std.io.Writer.Discarding = .init(&.{}); | |
| 1287 | user.formatUser(&dw.writer) catch unreachable; // discarding | |
| 1288 | const user_len = dw.count + dw.writer.end; | |
| 1289 | ||
| 1290 | dw.count = 0; | |
| 1291 | dw.writer.end = 0; | |
| 1292 | password.formatPassword(&dw.writer) catch unreachable; // discarding | |
| 1293 | const password_len = dw.count + dw.writer.end; | |
| 1294 | ||
| 1286 | 1295 | return valueLength(@intCast(user_len), @intCast(password_len)); |
| 1287 | 1296 | } |
| 1288 | 1297 |
lib/std/http/test.zig+2-4| ... | ... | @@ -405,10 +405,8 @@ test "general client/server API coverage" { |
| 405 | 405 | fn handleRequest(request: *http.Server.Request, listen_port: u16) !void { |
| 406 | 406 | const log = std.log.scoped(.server); |
| 407 | 407 | |
| 408 | log.info("{} {s} {s}", .{ | |
| 409 | request.head.method, | |
| 410 | @tagName(request.head.version), | |
| 411 | request.head.target, | |
| 408 | log.info("{f} {s} {s}", .{ | |
| 409 | request.head.method, @tagName(request.head.version), request.head.target, | |
| 412 | 410 | }); |
| 413 | 411 | |
| 414 | 412 | const gpa = std.testing.allocator; |
lib/std/io.zig+10| ... | ... | @@ -19,6 +19,12 @@ pub const Limit = enum(usize) { |
| 19 | 19 | return @enumFromInt(n); |
| 20 | 20 | } |
| 21 | 21 | |
| 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 | 28 | pub fn countVec(data: []const []const u8) Limit { |
| 23 | 29 | var total: usize = 0; |
| 24 | 30 | for (data) |d| total += d.len; |
| ... | ... | @@ -33,6 +39,10 @@ pub const Limit = enum(usize) { |
| 33 | 39 | return @min(n, @intFromEnum(l)); |
| 34 | 40 | } |
| 35 | 41 | |
| 42 | pub fn minInt64(l: Limit, n: u64) usize { | |
| 43 | return @min(n, @intFromEnum(l)); | |
| 44 | } | |
| 45 | ||
| 36 | 46 | pub fn slice(l: Limit, s: []u8) []u8 { |
| 37 | 47 | return s[0..l.minInt(s.len)]; |
| 38 | 48 | } |
lib/std/io/DeprecatedReader.zig created+386| ... | ... | @@ -0,0 +1,386 @@ |
| 1 | context: *const anyopaque, | |
| 2 | readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize, | |
| 3 | ||
| 4 | pub 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. | |
| 9 | pub 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. | |
| 16 | pub 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. | |
| 25 | pub 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. | |
| 37 | pub 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. | |
| 47 | pub 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 | ||
| 55 | pub 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. | |
| 89 | pub 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. | |
| 101 | pub 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. | |
| 116 | pub 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. | |
| 135 | pub 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. | |
| 151 | pub 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. | |
| 176 | pub 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. | |
| 195 | pub 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. | |
| 221 | pub 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`. | |
| 232 | pub 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. | |
| 240 | pub 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 | |
| 246 | pub 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()` | |
| 256 | pub 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. | |
| 273 | pub 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 | ||
| 279 | pub 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 | ||
| 284 | pub 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` | |
| 298 | pub 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 | |
| 304 | pub 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 | |
| 316 | pub 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 | ||
| 327 | pub 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 | ||
| 335 | pub 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 | |
| 346 | pub 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. | |
| 365 | pub 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 | ||
| 375 | const std = @import("../std.zig"); | |
| 376 | const Self = @This(); | |
| 377 | const math = std.math; | |
| 378 | const assert = std.debug.assert; | |
| 379 | const mem = std.mem; | |
| 380 | const testing = std.testing; | |
| 381 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 382 | const Alignment = std.mem.Alignment; | |
| 383 | ||
| 384 | test { | |
| 385 | _ = @import("Reader/test.zig"); | |
| 386 | } |
lib/std/io/DeprecatedWriter.zig created+109| ... | ... | @@ -0,0 +1,109 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const mem = std.mem; | |
| 4 | const native_endian = @import("builtin").target.cpu.arch.endian(); | |
| 5 | ||
| 6 | context: *const anyopaque, | |
| 7 | writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize, | |
| 8 | ||
| 9 | const Self = @This(); | |
| 10 | pub const Error = anyerror; | |
| 11 | ||
| 12 | pub fn write(self: Self, bytes: []const u8) anyerror!usize { | |
| 13 | return self.writeFn(self.context, bytes); | |
| 14 | } | |
| 15 | ||
| 16 | pub 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 | ||
| 23 | pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void { | |
| 24 | return std.fmt.format(self, format, args); | |
| 25 | } | |
| 26 | ||
| 27 | pub fn writeByte(self: Self, byte: u8) anyerror!void { | |
| 28 | const array = [1]u8{byte}; | |
| 29 | return self.writeAll(&array); | |
| 30 | } | |
| 31 | ||
| 32 | pub 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 | ||
| 44 | pub 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 | ||
| 51 | pub 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 | ||
| 57 | pub 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 | ||
| 63 | pub 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 | ||
| 74 | pub 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. | |
| 86 | pub 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 | ||
| 96 | pub 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 | 26 | /// Returns the number of bytes written, which will be at minimum `0` and |
| 27 | 27 | /// at most `limit`. The number returned, including zero, does not indicate |
| 28 | 28 | /// 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 | 32 | /// The reader's internal logical seek position moves forward in accordance |
| 32 | 33 | /// with the number of bytes returned from this function. |
| ... | ... | @@ -35,7 +36,15 @@ pub const VTable = struct { |
| 35 | 36 | /// sizes combined with short reads (returning a value less than `limit`) |
| 36 | 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 | 48 | stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize, |
| 40 | 49 | |
| 41 | 50 | /// Consumes bytes from the internally tracked stream position without |
| ... | ... | @@ -55,6 +64,8 @@ pub const VTable = struct { |
| 55 | 64 | /// The default implementation is is based on calling `stream`, borrowing |
| 56 | 65 | /// `buffer` to construct a temporary `Writer` and ignoring the written |
| 57 | 66 | /// data. |
| 67 | /// | |
| 68 | /// This function is only called when `buffer` is empty. | |
| 58 | 69 | discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard, |
| 59 | 70 | }; |
| 60 | 71 | |
| ... | ... | @@ -102,7 +113,7 @@ const ending_state: Reader = .fixed(&.{}); |
| 102 | 113 | pub const ending: *Reader = @constCast(&ending_state); |
| 103 | 114 | |
| 104 | 115 | pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited { |
| 105 | return Limited.init(r, limit, buffer); | |
| 116 | return .init(r, limit, buffer); | |
| 106 | 117 | } |
| 107 | 118 | |
| 108 | 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 | 139 | r.seek += n; |
| 129 | 140 | return n; |
| 130 | 141 | } |
| 131 | const before = w.count; | |
| 132 | 142 | const n = try r.vtable.stream(r, w, limit); |
| 133 | 143 | assert(n <= @intFromEnum(limit)); |
| 134 | assert(w.count == before + n); | |
| 135 | 144 | return n; |
| 136 | 145 | } |
| 137 | 146 | |
| ... | ... | @@ -154,19 +163,13 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize { |
| 154 | 163 | pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize { |
| 155 | 164 | assert(r.seek == 0); |
| 156 | 165 | assert(r.end == 0); |
| 157 | var w: Writer = .discarding(r.buffer); | |
| 158 | const n = r.stream(&w, limit) catch |err| switch (err) { | |
| 166 | var dw: Writer.Discarding = .init(r.buffer); | |
| 167 | const n = r.stream(&dw.writer, limit) catch |err| switch (err) { | |
| 159 | 168 | error.WriteFailed => unreachable, |
| 160 | 169 | error.ReadFailed => return error.ReadFailed, |
| 161 | 170 | error.EndOfStream => return error.EndOfStream, |
| 162 | 171 | }; |
| 163 | if (n > @intFromEnum(limit)) { | |
| 164 | const over_amt = n - @intFromEnum(limit); | |
| 165 | r.seek = w.end - over_amt; | |
| 166 | r.end = w.end; | |
| 167 | assert(r.end <= w.buffer.len); // limit may be exceeded only by an amount within buffer capacity. | |
| 168 | return @intFromEnum(limit); | |
| 169 | } | |
| 172 | assert(n <= @intFromEnum(limit)); | |
| 170 | 173 | return n; |
| 171 | 174 | } |
| 172 | 175 | |
| ... | ... | @@ -193,7 +196,7 @@ pub fn streamRemaining(r: *Reader, w: *Writer) StreamRemainingError!usize { |
| 193 | 196 | /// Consumes the stream until the end, ignoring all the data, returning the |
| 194 | 197 | /// number of bytes discarded. |
| 195 | 198 | pub fn discardRemaining(r: *Reader) ShortError!usize { |
| 196 | var offset: usize = r.end; | |
| 199 | var offset: usize = r.end - r.seek; | |
| 197 | 200 | r.seek = 0; |
| 198 | 201 | r.end = 0; |
| 199 | 202 | while (true) { |
| ... | ... | @@ -262,10 +265,9 @@ pub fn appendRemaining( |
| 262 | 265 | error.EndOfStream => break, |
| 263 | 266 | error.ReadFailed => return error.ReadFailed, |
| 264 | 267 | }; |
| 265 | if (n >= dest.len) { | |
| 268 | if (n > dest.len) { | |
| 266 | 269 | r.end = n - dest.len; |
| 267 | 270 | list.items.len += dest.len; |
| 268 | if (n == dest.len) return; | |
| 269 | 271 | return error.StreamTooLong; |
| 270 | 272 | } |
| 271 | 273 | list.items.len += n; |
| ... | ... | @@ -320,22 +322,29 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize { |
| 320 | 322 | }, |
| 321 | 323 | .writer = .{ |
| 322 | 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 | 328 | var n = r.vtable.stream(r, &wrapper.writer, .limited(remaining)) catch |err| switch (err) { |
| 327 | 329 | error.WriteFailed => { |
| 330 | assert(!wrapper.used); | |
| 328 | 331 | if (wrapper.writer.buffer.ptr == first.ptr) { |
| 329 | 332 | remaining -= wrapper.writer.end; |
| 330 | 333 | } else { |
| 334 | assert(wrapper.writer.end <= r.buffer.len); | |
| 331 | 335 | r.end = wrapper.writer.end; |
| 332 | 336 | } |
| 333 | 337 | break; |
| 334 | 338 | }, |
| 335 | 339 | else => |e| return e, |
| 336 | 340 | }; |
| 337 | if (wrapper.writer.buffer.ptr != first.ptr) { | |
| 338 | r.end = n; | |
| 341 | if (!wrapper.used) { | |
| 342 | if (wrapper.writer.buffer.ptr == first.ptr) { | |
| 343 | remaining -= n; | |
| 344 | } else { | |
| 345 | assert(n <= r.buffer.len); | |
| 346 | r.end = n; | |
| 347 | } | |
| 339 | 348 | break; |
| 340 | 349 | } |
| 341 | 350 | if (n < first.len) { |
| ... | ... | @@ -352,6 +361,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize { |
| 352 | 361 | remaining -= mid.len; |
| 353 | 362 | n -= mid.len; |
| 354 | 363 | } |
| 364 | assert(n <= r.buffer.len); | |
| 355 | 365 | r.end = n; |
| 356 | 366 | break; |
| 357 | 367 | } |
| ... | ... | @@ -441,7 +451,7 @@ pub fn toss(r: *Reader, n: usize) void { |
| 441 | 451 | } |
| 442 | 452 | |
| 443 | 453 | /// Equivalent to `toss(r.bufferedLen())`. |
| 444 | pub fn tossAll(r: *Reader) void { | |
| 454 | pub fn tossBuffered(r: *Reader) void { | |
| 445 | 455 | r.seek = 0; |
| 446 | 456 | r.end = 0; |
| 447 | 457 | } |
| ... | ... | @@ -553,7 +563,7 @@ pub fn discardShort(r: *Reader, n: usize) ShortError!usize { |
| 553 | 563 | /// See also: |
| 554 | 564 | /// * `peek` |
| 555 | 565 | /// * `readSliceShort` |
| 556 | pub fn readSlice(r: *Reader, buffer: []u8) Error!void { | |
| 566 | pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void { | |
| 557 | 567 | const n = try readSliceShort(r, buffer); |
| 558 | 568 | if (n != buffer.len) return error.EndOfStream; |
| 559 | 569 | } |
| ... | ... | @@ -567,7 +577,7 @@ pub fn readSlice(r: *Reader, buffer: []u8) Error!void { |
| 567 | 577 | /// only if the stream reached the end. |
| 568 | 578 | /// |
| 569 | 579 | /// See also: |
| 570 | /// * `readSlice` | |
| 580 | /// * `readSliceAll` | |
| 571 | 581 | pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { |
| 572 | 582 | const in_buffer = r.buffer[r.seek..r.end]; |
| 573 | 583 | const copy_len = @min(buffer.len, in_buffer.len); |
| ... | ... | @@ -588,17 +598,16 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { |
| 588 | 598 | }, |
| 589 | 599 | .writer = .{ |
| 590 | 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 | 604 | const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) { |
| 595 | 605 | error.WriteFailed => { |
| 596 | if (wrapper.writer.buffer.ptr != remaining.ptr) { | |
| 606 | if (!wrapper.used) { | |
| 597 | 607 | assert(r.seek == 0); |
| 598 | 608 | r.seek = remaining.len; |
| 599 | 609 | r.end = wrapper.writer.end; |
| 600 | 610 | @memcpy(remaining, r.buffer[0..remaining.len]); |
| 601 | return buffer.len; | |
| 602 | 611 | } |
| 603 | 612 | return buffer.len; |
| 604 | 613 | }, |
| ... | ... | @@ -626,7 +635,7 @@ pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { |
| 626 | 635 | /// comptime-known and matches host endianness. |
| 627 | 636 | /// |
| 628 | 637 | /// See also: |
| 629 | /// * `readSlice` | |
| 638 | /// * `readSliceAll` | |
| 630 | 639 | /// * `readSliceEndianAlloc` |
| 631 | 640 | pub inline fn readSliceEndian( |
| 632 | 641 | r: *Reader, |
| ... | ... | @@ -634,7 +643,7 @@ pub inline fn readSliceEndian( |
| 634 | 643 | buffer: []Elem, |
| 635 | 644 | endian: std.builtin.Endian, |
| 636 | 645 | ) Error!void { |
| 637 | try readSlice(r, @ptrCast(buffer)); | |
| 646 | try readSliceAll(r, @ptrCast(buffer)); | |
| 638 | 647 | if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem); |
| 639 | 648 | } |
| 640 | 649 | |
| ... | ... | @@ -651,15 +660,16 @@ pub inline fn readSliceEndianAlloc( |
| 651 | 660 | ) ReadAllocError![]Elem { |
| 652 | 661 | const dest = try allocator.alloc(Elem, len); |
| 653 | 662 | errdefer allocator.free(dest); |
| 654 | try readSlice(r, @ptrCast(dest)); | |
| 663 | try readSliceAll(r, @ptrCast(dest)); | |
| 655 | 664 | if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem); |
| 656 | 665 | return dest; |
| 657 | 666 | } |
| 658 | 667 | |
| 659 | pub fn readSliceAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 { | |
| 668 | /// Shortcut for calling `readSliceAll` with a buffer provided by `allocator`. | |
| 669 | pub fn readAlloc(r: *Reader, allocator: Allocator, len: usize) ReadAllocError![]u8 { | |
| 660 | 670 | const dest = try allocator.alloc(u8, len); |
| 661 | 671 | errdefer allocator.free(dest); |
| 662 | try readSlice(r, dest); | |
| 672 | try readSliceAll(r, dest); | |
| 663 | 673 | return dest; |
| 664 | 674 | } |
| 665 | 675 | |
| ... | ... | @@ -692,6 +702,17 @@ pub fn takeSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel |
| 692 | 702 | return result; |
| 693 | 703 | } |
| 694 | 704 | |
| 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` | |
| 695 | 716 | pub fn peekSentinel(r: *Reader, comptime sentinel: u8) DelimiterError![:sentinel]u8 { |
| 696 | 717 | const result = try r.peekDelimiterInclusive(sentinel); |
| 697 | 718 | return result[0 .. result.len - 1 :sentinel]; |
| ... | ... | @@ -732,26 +753,21 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 732 | 753 | @branchHint(.likely); |
| 733 | 754 | return buffer[seek .. end + 1]; |
| 734 | 755 | } |
| 735 | if (seek > 0) { | |
| 736 | const remainder = buffer[seek..]; | |
| 737 | @memmove(buffer[0..remainder.len], remainder); | |
| 738 | r.end = remainder.len; | |
| 739 | r.seek = 0; | |
| 756 | if (r.vtable.stream == &endingStream) { | |
| 757 | // Protect the `@constCast` of `fixed`. | |
| 758 | return error.EndOfStream; | |
| 740 | 759 | } |
| 741 | var writer: Writer = .{ | |
| 742 | .buffer = r.buffer, | |
| 743 | .vtable = &.{ .drain = Writer.fixedDrain }, | |
| 744 | }; | |
| 745 | while (r.end < r.buffer.len) { | |
| 746 | writer.end = r.end; | |
| 747 | const n = r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { | |
| 760 | r.rebase(); | |
| 761 | while (r.buffer.len - r.end != 0) { | |
| 762 | const end_cap = r.buffer[r.end..]; | |
| 763 | var writer: Writer = .fixed(end_cap); | |
| 764 | const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) { | |
| 748 | 765 | error.WriteFailed => unreachable, |
| 749 | 766 | else => |e| return e, |
| 750 | 767 | }; |
| 751 | const prev_end = r.end; | |
| 752 | r.end = prev_end + n; | |
| 753 | if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], prev_end, delimiter)) |end| { | |
| 754 | return r.buffer[0 .. end + 1]; | |
| 768 | r.end += n; | |
| 769 | if (std.mem.indexOfScalarPos(u8, end_cap[0..n], 0, delimiter)) |end| { | |
| 770 | return r.buffer[0 .. r.end - n + end + 1]; | |
| 755 | 771 | } |
| 756 | 772 | } |
| 757 | 773 | return error.StreamTooLong; |
| ... | ... | @@ -777,9 +793,10 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 777 | 793 | pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 778 | 794 | const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) { |
| 779 | 795 | error.EndOfStream => { |
| 780 | if (r.end == 0) return error.EndOfStream; | |
| 781 | r.toss(r.end); | |
| 782 | return r.buffer[0..r.end]; | |
| 796 | const remaining = r.buffer[r.seek..r.end]; | |
| 797 | if (remaining.len == 0) return error.EndOfStream; | |
| 798 | r.toss(remaining.len); | |
| 799 | return remaining; | |
| 783 | 800 | }, |
| 784 | 801 | else => |e| return e, |
| 785 | 802 | }; |
| ... | ... | @@ -807,8 +824,10 @@ pub fn takeDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 807 | 824 | pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 808 | 825 | const result = r.peekDelimiterInclusive(delimiter) catch |err| switch (err) { |
| 809 | 826 | error.EndOfStream => { |
| 810 | if (r.end == 0) return error.EndOfStream; | |
| 811 | return r.buffer[0..r.end]; | |
| 827 | const remaining = r.buffer[r.seek..r.end]; | |
| 828 | if (remaining.len == 0) return error.EndOfStream; | |
| 829 | r.toss(remaining.len); | |
| 830 | return remaining; | |
| 812 | 831 | }, |
| 813 | 832 | else => |e| return e, |
| 814 | 833 | }; |
| ... | ... | @@ -818,37 +837,50 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 818 | 837 | /// Appends to `w` contents by reading from the stream until `delimiter` is |
| 819 | 838 | /// found. Does not write the delimiter itself. |
| 820 | 839 | /// |
| 821 | /// Returns number of bytes streamed. | |
| 822 | pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize { | |
| 823 | const amount, const to = try r.readAny(w, delimiter, .unlimited); | |
| 824 | return switch (to) { | |
| 825 | .delimiter => amount, | |
| 826 | .limit => unreachable, | |
| 827 | .end => error.EndOfStream, | |
| 840 | /// Returns number of bytes streamed, which may be zero, or error.EndOfStream | |
| 841 | /// if the delimiter was not found. | |
| 842 | /// | |
| 843 | /// Asserts buffer capacity of at least one. This function performs better with | |
| 844 | /// larger buffers. | |
| 845 | /// | |
| 846 | /// See also: | |
| 847 | /// * `streamDelimiterEnding` | |
| 848 | /// * `streamDelimiterLimit` | |
| 849 | pub 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 | } |
| 830 | 857 | |
| 831 | 858 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. |
| 832 | 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. | |
| 837 | pub fn readDelimiterEnding( | |
| 867 | /// See also: | |
| 868 | /// * `streamDelimiter` | |
| 869 | /// * `streamDelimiterLimit` | |
| 870 | pub fn streamDelimiterEnding( | |
| 838 | 871 | r: *Reader, |
| 839 | 872 | w: *Writer, |
| 840 | 873 | delimiter: u8, |
| 841 | 874 | ) StreamRemainingError!usize { |
| 842 | const amount, const to = try r.readAny(w, delimiter, .unlimited); | |
| 843 | return switch (to) { | |
| 844 | .delimiter, .end => amount, | |
| 845 | .limit => unreachable, | |
| 875 | return streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) { | |
| 876 | error.StreamTooLong => unreachable, // unlimited is passed | |
| 877 | else => |e| return e, | |
| 846 | 878 | }; |
| 847 | 879 | } |
| 848 | 880 | |
| 849 | pub const StreamDelimiterLimitedError = StreamRemainingError || error{ | |
| 850 | /// Stream ended before the delimiter was found. | |
| 851 | EndOfStream, | |
| 881 | pub const StreamDelimiterLimitError = error{ | |
| 882 | ReadFailed, | |
| 883 | WriteFailed, | |
| 852 | 884 | /// The delimiter was not found within the limit. |
| 853 | 885 | StreamTooLong, |
| 854 | 886 | }; |
| ... | ... | @@ -856,65 +888,103 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{ |
| 856 | 888 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. |
| 857 | 889 | /// Does not write the delimiter itself. |
| 858 | 890 | /// |
| 859 | /// Returns number of bytes streamed. | |
| 860 | pub fn readDelimiterLimit( | |
| 891 | /// Returns number of bytes streamed, which may be zero. End of stream can be | |
| 892 | /// detected by checking if the next byte in the stream is the delimiter. | |
| 893 | /// | |
| 894 | /// Asserts buffer capacity of at least one. This function performs better with | |
| 895 | /// larger buffers. | |
| 896 | pub fn streamDelimiterLimit( | |
| 861 | 897 | r: *Reader, |
| 862 | 898 | w: *Writer, |
| 863 | 899 | delimiter: u8, |
| 864 | 900 | limit: Limit, |
| 865 | ) StreamDelimiterLimitedError!usize { | |
| 866 | const amount, const to = try r.readAny(w, delimiter, limit); | |
| 867 | return switch (to) { | |
| 868 | .delimiter => amount, | |
| 869 | .limit => error.StreamTooLong, | |
| 870 | .end => error.EndOfStream, | |
| 871 | }; | |
| 872 | } | |
| 873 | ||
| 874 | fn readAny( | |
| 875 | r: *Reader, | |
| 876 | w: *Writer, | |
| 877 | delimiter: ?u8, | |
| 878 | limit: Limit, | |
| 879 | ) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } { | |
| 880 | var amount: usize = 0; | |
| 881 | var remaining = limit; | |
| 882 | while (remaining.nonzero()) { | |
| 883 | const available = remaining.slice(r.peekGreedy(1) catch |err| switch (err) { | |
| 884 | error.ReadFailed => |e| return e, | |
| 885 | error.EndOfStream => return .{ amount, .end }, | |
| 901 | ) StreamDelimiterLimitError!usize { | |
| 902 | var remaining = @intFromEnum(limit); | |
| 903 | while (remaining != 0) { | |
| 904 | const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) { | |
| 905 | error.ReadFailed => return error.ReadFailed, | |
| 906 | error.EndOfStream => return @intFromEnum(limit) - remaining, | |
| 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 | 909 | try w.writeAll(available[0..delimiter_index]); |
| 889 | r.toss(delimiter_index + 1); | |
| 890 | return .{ amount + delimiter_index, .delimiter }; | |
| 891 | }; | |
| 910 | r.toss(delimiter_index); | |
| 911 | remaining -= delimiter_index; | |
| 912 | return @intFromEnum(limit) - remaining; | |
| 913 | } | |
| 892 | 914 | try w.writeAll(available); |
| 893 | 915 | r.toss(available.len); |
| 894 | amount += available.len; | |
| 895 | remaining = remaining.subtract(available.len).?; | |
| 916 | remaining -= available.len; | |
| 896 | 917 | } |
| 897 | return .{ amount, .limit }; | |
| 918 | return error.StreamTooLong; | |
| 898 | 919 | } |
| 899 | 920 | |
| 900 | 921 | /// Reads from the stream until specified byte is found, discarding all data, |
| 901 | 922 | /// including the delimiter. |
| 902 | 923 | /// |
| 903 | /// If end of stream is found, this function succeeds. | |
| 904 | pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void { | |
| 905 | _ = r; | |
| 906 | _ = delimiter; | |
| 907 | @panic("TODO"); | |
| 924 | /// Returns number of bytes discarded, or `error.EndOfStream` if the delimiter | |
| 925 | /// is not found. | |
| 926 | /// | |
| 927 | /// See also: | |
| 928 | /// * `discardDelimiterExclusive` | |
| 929 | /// * `discardDelimiterLimit` | |
| 930 | pub 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 | } |
| 909 | 940 | |
| 910 | 941 | /// Reads from the stream until specified byte is found, discarding all data, |
| 911 | 942 | /// excluding the delimiter. |
| 912 | 943 | /// |
| 913 | /// Succeeds if stream ends before delimiter found. | |
| 914 | pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!void { | |
| 915 | _ = r; | |
| 916 | _ = delimiter; | |
| 917 | @panic("TODO"); | |
| 944 | /// Returns the number of bytes discarded. | |
| 945 | /// | |
| 946 | /// Succeeds if stream ends before delimiter found. End of stream can be | |
| 947 | /// detected by checking if the delimiter is buffered. | |
| 948 | /// | |
| 949 | /// See also: | |
| 950 | /// * `discardDelimiterInclusive` | |
| 951 | /// * `discardDelimiterLimit` | |
| 952 | pub 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 | ||
| 959 | pub 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. | |
| 972 | pub 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 | } |
| 919 | 989 | |
| 920 | 990 | /// 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 | 1000 | @branchHint(.likely); |
| 931 | 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 | 1016 | rebaseCapacity(r, n); |
| 934 | 1017 | var writer: Writer = .{ |
| 935 | 1018 | .buffer = r.buffer, |
| ... | ... | @@ -970,11 +1053,12 @@ pub fn fillMore(r: *Reader) Error!void { |
| 970 | 1053 | pub fn peekByte(r: *Reader) Error!u8 { |
| 971 | 1054 | const buffer = r.buffer[0..r.end]; |
| 972 | 1055 | const seek = r.seek; |
| 973 | if (seek >= buffer.len) { | |
| 974 | @branchHint(.unlikely); | |
| 975 | try fill(r, 1); | |
| 1056 | if (seek < buffer.len) { | |
| 1057 | @branchHint(.likely); | |
| 1058 | return buffer[seek]; | |
| 976 | 1059 | } |
| 977 | return buffer[seek]; | |
| 1060 | try fill(r, 1); | |
| 1061 | return r.buffer[r.seek]; | |
| 978 | 1062 | } |
| 979 | 1063 | |
| 980 | 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 | 1093 | /// |
| 1010 | 1094 | /// See also: |
| 1011 | 1095 | /// * `peekStruct` |
| 1096 | /// * `takeStructEndian` | |
| 1012 | 1097 | pub fn takeStruct(r: *Reader, comptime T: type) Error!*align(1) T { |
| 1013 | 1098 | // Only extern and packed structs have defined in-memory layout. |
| 1014 | 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 | 1106 | /// |
| 1022 | 1107 | /// See also: |
| 1023 | 1108 | /// * `takeStruct` |
| 1109 | /// * `peekStructEndian` | |
| 1024 | 1110 | pub fn peekStruct(r: *Reader, comptime T: type) Error!*align(1) T { |
| 1025 | 1111 | // Only extern and packed structs have defined in-memory layout. |
| 1026 | 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 | 1117 | /// |
| 1032 | 1118 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` |
| 1033 | 1119 | /// when `endian` is comptime-known and matches the host endianness. |
| 1120 | /// | |
| 1121 | /// See also: | |
| 1122 | /// * `takeStruct` | |
| 1123 | /// * `peekStructEndian` | |
| 1034 | 1124 | pub inline fn takeStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { |
| 1035 | 1125 | var res = (try r.takeStruct(T)).*; |
| 1036 | 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 | 1131 | /// |
| 1042 | 1132 | /// This function is inline to avoid referencing `std.mem.byteSwapAllFields` |
| 1043 | 1133 | /// when `endian` is comptime-known and matches the host endianness. |
| 1134 | /// | |
| 1135 | /// See also: | |
| 1136 | /// * `takeStructEndian` | |
| 1137 | /// * `peekStruct` | |
| 1044 | 1138 | pub inline fn peekStructEndian(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T { |
| 1045 | 1139 | var res = (try r.peekStruct(T)).*; |
| 1046 | 1140 | if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); |
| ... | ... | @@ -1218,146 +1312,295 @@ test fixed { |
| 1218 | 1312 | } |
| 1219 | 1313 | |
| 1220 | 1314 | test 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 | } |
| 1223 | 1319 | |
| 1224 | 1320 | test peekGreedy { |
| 1225 | return error.Unimplemented; | |
| 1321 | var r: Reader = .fixed("abc"); | |
| 1322 | try testing.expectEqualStrings("abc", try r.peekGreedy(1)); | |
| 1226 | 1323 | } |
| 1227 | 1324 | |
| 1228 | 1325 | test toss { |
| 1229 | return error.Unimplemented; | |
| 1326 | var r: Reader = .fixed("abc"); | |
| 1327 | r.toss(1); | |
| 1328 | try testing.expectEqualStrings("bc", r.buffered()); | |
| 1230 | 1329 | } |
| 1231 | 1330 | |
| 1232 | 1331 | test 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 | } |
| 1235 | 1336 | |
| 1236 | 1337 | test 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 | } |
| 1239 | 1342 | |
| 1240 | 1343 | test 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 | } |
| 1243 | 1348 | |
| 1244 | 1349 | test discardAll { |
| 1245 | 1350 | var r: Reader = .fixed("foobar"); |
| 1246 | try r.discard(3); | |
| 1351 | try r.discardAll(3); | |
| 1247 | 1352 | try testing.expectEqualStrings("bar", try r.take(3)); |
| 1248 | try r.discard(0); | |
| 1249 | try testing.expectError(error.EndOfStream, r.discard(1)); | |
| 1353 | try r.discardAll(0); | |
| 1354 | try testing.expectError(error.EndOfStream, r.discardAll(1)); | |
| 1250 | 1355 | } |
| 1251 | 1356 | |
| 1252 | 1357 | test 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 | } |
| 1255 | 1363 | |
| 1256 | 1364 | test 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 | } |
| 1259 | 1374 | |
| 1260 | 1375 | test 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 | } |
| 1263 | 1381 | |
| 1264 | 1382 | test 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 | } |
| 1267 | 1387 | |
| 1268 | 1388 | test 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 | } |
| 1271 | 1393 | |
| 1272 | 1394 | test 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 | } |
| 1275 | 1401 | |
| 1276 | 1402 | test 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 | } |
| 1279 | 1408 | |
| 1280 | 1409 | test peekDelimiterExclusive { |
| 1281 | return error.Unimplemented; | |
| 1282 | } | |
| 1283 | ||
| 1284 | test readDelimiter { | |
| 1285 | return error.Unimplemented; | |
| 1286 | } | |
| 1287 | ||
| 1288 | test readDelimiterEnding { | |
| 1289 | return error.Unimplemented; | |
| 1290 | } | |
| 1291 | ||
| 1292 | test readDelimiterLimit { | |
| 1293 | return error.Unimplemented; | |
| 1410 | var r: Reader = .fixed("ab\nc"); | |
| 1411 | try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n')); | |
| 1412 | try testing.expectEqualStrings("ab", try r.peekDelimiterExclusive('\n')); | |
| 1413 | r.toss(3); | |
| 1414 | try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n')); | |
| 1415 | } | |
| 1416 | ||
| 1417 | test streamDelimiter { | |
| 1418 | var out_buffer: [10]u8 = undefined; | |
| 1419 | var r: Reader = .fixed("foo\nbars"); | |
| 1420 | var w: Writer = .fixed(&out_buffer); | |
| 1421 | try testing.expectEqual(3, try r.streamDelimiter(&w, '\n')); | |
| 1422 | try testing.expectEqualStrings("foo", w.buffered()); | |
| 1423 | try testing.expectEqual(0, try r.streamDelimiter(&w, '\n')); | |
| 1424 | r.toss(1); | |
| 1425 | try testing.expectError(error.EndOfStream, r.streamDelimiter(&w, '\n')); | |
| 1426 | } | |
| 1427 | ||
| 1428 | test 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 | ||
| 1441 | test 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 | } |
| 1295 | 1451 | |
| 1296 | 1452 | test 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 | } |
| 1299 | 1459 | |
| 1300 | 1460 | test 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 | ||
| 1466 | test 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 | } |
| 1303 | 1474 | |
| 1304 | 1475 | test fill { |
| 1305 | return error.Unimplemented; | |
| 1476 | var r: Reader = .fixed("abc"); | |
| 1477 | try r.fill(1); | |
| 1478 | try r.fill(3); | |
| 1306 | 1479 | } |
| 1307 | 1480 | |
| 1308 | 1481 | test 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 | } |
| 1311 | 1487 | |
| 1312 | 1488 | test 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 | } |
| 1315 | 1494 | |
| 1316 | 1495 | test 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 | } |
| 1319 | 1500 | |
| 1320 | 1501 | test 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 | } |
| 1323 | 1506 | |
| 1324 | 1507 | test 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 | } |
| 1327 | 1516 | |
| 1328 | 1517 | test 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 | } |
| 1331 | 1531 | |
| 1332 | 1532 | test 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 | } |
| 1335 | 1538 | |
| 1336 | 1539 | test 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 | } |
| 1339 | 1545 | |
| 1340 | 1546 | test 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 | } |
| 1343 | 1553 | |
| 1344 | 1554 | test 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 | } |
| 1347 | 1560 | |
| 1348 | 1561 | test 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 | } |
| 1351 | 1570 | |
| 1352 | 1571 | test 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 | ||
| 1584 | test 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 | } |
| 1355 | 1596 | |
| 1356 | 1597 | test "expected error.EndOfStream" { |
| 1357 | 1598 | // Unit test inspired by https://github.com/ziglang/zig/issues/17733 |
| 1358 | var r: std.io.Reader = .fixed(""); | |
| 1359 | try std.testing.expectError(error.EndOfStream, r.readEnum(enum(u8) { a, b }, .little)); | |
| 1360 | try std.testing.expectError(error.EndOfStream, r.isBytes("foo")); | |
| 1599 | var buffer: [3]u8 = undefined; | |
| 1600 | var r: std.io.Reader = .fixed(&buffer); | |
| 1601 | r.end = 0; // capacity 3, but empty | |
| 1602 | try std.testing.expectError(error.EndOfStream, r.takeEnum(enum(u8) { a, b }, .little)); | |
| 1603 | try std.testing.expectError(error.EndOfStream, r.take(3)); | |
| 1361 | 1604 | } |
| 1362 | 1605 | |
| 1363 | 1606 | fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { |
| ... | ... | @@ -1389,25 +1632,51 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize { |
| 1389 | 1632 | test "readAlloc when the backing reader provides one byte at a time" { |
| 1390 | 1633 | const OneByteReader = struct { |
| 1391 | 1634 | str: []const u8, |
| 1392 | curr: usize, | |
| 1393 | ||
| 1394 | fn read(self: *@This(), dest: []u8) usize { | |
| 1395 | if (self.str.len <= self.curr or dest.len == 0) | |
| 1396 | return 0; | |
| 1397 | ||
| 1398 | dest[0] = self.str[self.curr]; | |
| 1399 | self.curr += 1; | |
| 1635 | i: usize, | |
| 1636 | reader: Reader, | |
| 1637 | ||
| 1638 | fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { | |
| 1639 | assert(@intFromEnum(limit) >= 1); | |
| 1640 | const self: *@This() = @fieldParentPtr("reader", r); | |
| 1641 | if (self.str.len - self.i == 0) return error.EndOfStream; | |
| 1642 | try w.writeByte(self.str[self.i]); | |
| 1643 | self.i += 1; | |
| 1400 | 1644 | return 1; |
| 1401 | 1645 | } |
| 1402 | 1646 | }; |
| 1403 | ||
| 1404 | 1647 | const str = "This is a test"; |
| 1405 | var one_byte_stream: OneByteReader = .init(str); | |
| 1406 | const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1); | |
| 1648 | var one_byte_stream: OneByteReader = .{ | |
| 1649 | .str = str, | |
| 1650 | .i = 0, | |
| 1651 | .reader = .{ | |
| 1652 | .buffer = &.{}, | |
| 1653 | .vtable = &.{ .stream = OneByteReader.stream }, | |
| 1654 | .seek = 0, | |
| 1655 | .end = 0, | |
| 1656 | }, | |
| 1657 | }; | |
| 1658 | const res = try one_byte_stream.reader.allocRemaining(std.testing.allocator, .unlimited); | |
| 1407 | 1659 | defer std.testing.allocator.free(res); |
| 1408 | 1660 | try std.testing.expectEqualStrings(str, res); |
| 1409 | 1661 | } |
| 1410 | 1662 | |
| 1663 | test "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 | 1680 | /// Provides a `Reader` implementation by passing data from an underlying |
| 1412 | 1681 | /// reader through `Hasher.update`. |
| 1413 | 1682 | /// |
lib/std/io/Writer.zig+819-534| ... | ... | @@ -14,12 +14,6 @@ vtable: *const VTable, |
| 14 | 14 | buffer: []u8, |
| 15 | 15 | /// In `buffer` before this are buffered bytes, after this is `undefined`. |
| 16 | 16 | end: 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. | |
| 22 | count: usize = 0, | |
| 23 | 17 | |
| 24 | 18 | pub const VTable = struct { |
| 25 | 19 | /// Sends bytes to the logical sink. A write will only be sent here if it |
| ... | ... | @@ -37,6 +31,10 @@ pub const VTable = struct { |
| 37 | 31 | /// The last element of `data` is repeated as necessary so that it is |
| 38 | 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 | 38 | /// Number of bytes consumed from `data` is returned, excluding bytes from |
| 41 | 39 | /// `buffer`. |
| 42 | 40 | /// |
| ... | ... | @@ -113,8 +111,7 @@ pub const FileError = error{ |
| 113 | 111 | Unimplemented, |
| 114 | 112 | }; |
| 115 | 113 | |
| 116 | /// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless | |
| 117 | /// modified externally, `count` will always equal `end`. | |
| 114 | /// Writes to `buffer` and returns `error.WriteFailed` when it is full. | |
| 118 | 115 | pub fn fixed(buffer: []u8) Writer { |
| 119 | 116 | return .{ |
| 120 | 117 | .vtable = &.{ .drain = fixedDrain }, |
| ... | ... | @@ -122,8 +119,8 @@ pub fn fixed(buffer: []u8) Writer { |
| 122 | 119 | }; |
| 123 | 120 | } |
| 124 | 121 | |
| 125 | pub fn hashed(w: *Writer, hasher: anytype) Hashed(@TypeOf(hasher)) { | |
| 126 | return .{ .out = w, .hasher = hasher }; | |
| 122 | pub fn hashed(w: *Writer, hasher: anytype, buffer: []u8) Hashed(@TypeOf(hasher)) { | |
| 123 | return .initHasher(w, hasher, buffer); | |
| 127 | 124 | } |
| 128 | 125 | |
| 129 | 126 | pub const failing: Writer = .{ |
| ... | ... | @@ -133,16 +130,6 @@ pub const failing: Writer = .{ |
| 133 | 130 | }, |
| 134 | 131 | }; |
| 135 | 132 | |
| 136 | pub fn discarding(buffer: []u8) Writer { | |
| 137 | return .{ | |
| 138 | .vtable = &.{ | |
| 139 | .drain = discardingDrain, | |
| 140 | .sendFile = discardingSendFile, | |
| 141 | }, | |
| 142 | .buffer = buffer, | |
| 143 | }; | |
| 144 | } | |
| 145 | ||
| 146 | 133 | /// Returns the contents not yet drained. |
| 147 | 134 | pub fn buffered(w: *const Writer) []u8 { |
| 148 | 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 | 161 | assert(data.len > 0); |
| 175 | 162 | const buffer = w.buffer; |
| 176 | 163 | const count = countSplat(data, splat); |
| 177 | if (w.end + count > buffer.len) { | |
| 178 | const n = try w.vtable.drain(w, data, splat); | |
| 179 | w.count += n; | |
| 180 | return n; | |
| 181 | } | |
| 182 | w.count += count; | |
| 183 | for (data) |bytes| { | |
| 164 | if (w.end + count > buffer.len) return w.vtable.drain(w, data, splat); | |
| 165 | for (data[0 .. data.len - 1]) |bytes| { | |
| 184 | 166 | @memcpy(buffer[w.end..][0..bytes.len], bytes); |
| 185 | 167 | w.end += bytes.len; |
| 186 | 168 | } |
| 187 | 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 | 170 | switch (pattern.len) { |
| 195 | 171 | 0 => {}, |
| 196 | 172 | 1 => { |
| 197 | @memset(buffer[w.end..][0..remaining_splat], pattern[0]); | |
| 198 | w.end += remaining_splat; | |
| 173 | @memset(buffer[w.end..][0..splat], pattern[0]); | |
| 174 | w.end += splat; | |
| 199 | 175 | }, |
| 200 | else => { | |
| 201 | const new_end = w.end + pattern.len * remaining_splat; | |
| 202 | while (w.end < new_end) : (w.end += pattern.len) { | |
| 203 | @memcpy(buffer[w.end..][0..pattern.len], pattern); | |
| 204 | } | |
| 176 | else => for (0..splat) |_| { | |
| 177 | @memcpy(buffer[w.end..][0..pattern.len], pattern); | |
| 178 | w.end += pattern.len; | |
| 205 | 179 | }, |
| 206 | 180 | } |
| 207 | 181 | return count; |
| 208 | 182 | } |
| 209 | 183 | |
| 210 | /// Equivalent to `writeSplat` but writes at most `limit` bytes. | |
| 211 | pub 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 | 184 | /// Returns how many bytes were consumed from `header` and `data`. |
| 225 | 185 | pub fn writeSplatHeader( |
| 226 | 186 | w: *Writer, |
| ... | ... | @@ -232,38 +192,40 @@ pub fn writeSplatHeader( |
| 232 | 192 | if (new_end <= w.buffer.len) { |
| 233 | 193 | @memcpy(w.buffer[w.end..][0..header.len], header); |
| 234 | 194 | w.end = new_end; |
| 235 | w.count += header.len; | |
| 236 | 195 | return header.len + try writeSplat(w, data, splat); |
| 237 | 196 | } |
| 238 | 197 | var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size. |
| 239 | 198 | var i: usize = 1; |
| 240 | 199 | vecs[0] = header; |
| 241 | for (data) |buf| { | |
| 200 | for (data[0 .. data.len - 1]) |buf| { | |
| 242 | 201 | if (buf.len == 0) continue; |
| 243 | 202 | vecs[i] = buf; |
| 244 | 203 | i += 1; |
| 245 | 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; | |
| 248 | const n = try w.vtable.drain(w, vecs[0..i], new_splat); | |
| 249 | w.count += n; | |
| 250 | return n; | |
| 206 | const pattern = data[data.len - 1]; | |
| 207 | const new_splat = s: { | |
| 208 | if (pattern.len == 0 or vecs.len - i == 0) break :s 1; | |
| 209 | vecs[i] = pattern; | |
| 210 | i += 1; | |
| 211 | break :s splat; | |
| 212 | }; | |
| 213 | return w.vtable.drain(w, vecs[0..i], new_splat); | |
| 251 | 214 | } |
| 252 | 215 | |
| 253 | /// Equivalent to `writeSplatHeader` but writes at most `limit` bytes. | |
| 254 | pub fn writeSplatHeaderLimit( | |
| 255 | w: *Writer, | |
| 256 | header: []const u8, | |
| 257 | data: []const []const u8, | |
| 258 | splat: usize, | |
| 259 | limit: Limit, | |
| 260 | ) Error!usize { | |
| 261 | _ = w; | |
| 262 | _ = header; | |
| 263 | _ = data; | |
| 264 | _ = splat; | |
| 265 | _ = limit; | |
| 266 | @panic("TODO"); | |
| 216 | test "writeSplatHeader splatting avoids buffer aliasing temptation" { | |
| 217 | const initial_buf = try testing.allocator.alloc(u8, 8); | |
| 218 | var aw: std.io.Writer.Allocating = .initOwnedSlice(testing.allocator, initial_buf); | |
| 219 | defer aw.deinit(); | |
| 220 | // This test assumes 8 vector buffer in this function. | |
| 221 | const n = try aw.writer.writeSplatHeader("header which is longer than buf ", &.{ | |
| 222 | "1", "2", "3", "4", "5", "6", "foo", "bar", "foo", | |
| 223 | }, 3); | |
| 224 | try testing.expectEqual(41, n); | |
| 225 | try testing.expectEqualStrings( | |
| 226 | "header which is longer than buf 123456foo", | |
| 227 | aw.writer.buffered(), | |
| 228 | ); | |
| 267 | 229 | } |
| 268 | 230 | |
| 269 | 231 | /// Drains all remaining buffered data. |
| ... | ... | @@ -386,12 +348,18 @@ pub const WritableVectorIterator = struct { |
| 386 | 348 | pub const VectorWrapper = struct { |
| 387 | 349 | writer: Writer, |
| 388 | 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 | }; |
| 391 | 358 | |
| 392 | 359 | pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator { |
| 393 | if (w.vtable == &VectorWrapper.vtable) { | |
| 360 | if (w.vtable == VectorWrapper.vtable) { | |
| 394 | 361 | const wrapper: *VectorWrapper = @fieldParentPtr("writer", w); |
| 362 | wrapper.used = true; | |
| 395 | 363 | return wrapper.it; |
| 396 | 364 | } |
| 397 | 365 | return .{ .first = try writableSliceGreedy(w, 1) }; |
| ... | ... | @@ -419,7 +387,6 @@ pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void { |
| 419 | 387 | |
| 420 | 388 | pub fn undo(w: *Writer, n: usize) void { |
| 421 | 389 | w.end -= n; |
| 422 | w.count -= n; | |
| 423 | 390 | } |
| 424 | 391 | |
| 425 | 392 | /// After calling `writableSliceGreedy`, this function tracks how many bytes |
| ... | ... | @@ -430,13 +397,11 @@ pub fn advance(w: *Writer, n: usize) void { |
| 430 | 397 | const new_end = w.end + n; |
| 431 | 398 | assert(new_end <= w.buffer.len); |
| 432 | 399 | w.end = new_end; |
| 433 | w.count += n; | |
| 434 | 400 | } |
| 435 | 401 | |
| 436 | 402 | /// After calling `writableVector`, this function tracks how many bytes were |
| 437 | 403 | /// written to it. |
| 438 | 404 | pub fn advanceVector(w: *Writer, n: usize) usize { |
| 439 | w.count += n; | |
| 440 | 405 | return consume(w, n); |
| 441 | 406 | } |
| 442 | 407 | |
| ... | ... | @@ -494,12 +459,9 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize { |
| 494 | 459 | @branchHint(.likely); |
| 495 | 460 | @memcpy(w.buffer[w.end..][0..bytes.len], bytes); |
| 496 | 461 | w.end += bytes.len; |
| 497 | w.count += bytes.len; | |
| 498 | 462 | return bytes.len; |
| 499 | 463 | } |
| 500 | const n = try w.vtable.drain(w, &.{bytes}, 1); | |
| 501 | w.count += n; | |
| 502 | return n; | |
| 464 | return w.vtable.drain(w, &.{bytes}, 1); | |
| 503 | 465 | } |
| 504 | 466 | |
| 505 | 467 | /// Asserts `buffer` capacity exceeds `preserve_length`. |
| ... | ... | @@ -509,7 +471,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro |
| 509 | 471 | @branchHint(.likely); |
| 510 | 472 | @memcpy(w.buffer[w.end..][0..bytes.len], bytes); |
| 511 | 473 | w.end += bytes.len; |
| 512 | w.count += bytes.len; | |
| 513 | 474 | return bytes.len; |
| 514 | 475 | } |
| 515 | 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 | 478 | w.end = temp_end; |
| 518 | 479 | defer w.end += preserved.len; |
| 519 | 480 | const n = try w.vtable.drain(w, &.{bytes}, 1); |
| 520 | w.count += n; | |
| 521 | 481 | assert(w.end <= temp_end + preserved.len); |
| 522 | 482 | @memmove(w.buffer[w.end..][0..preserved.len], preserved); |
| 523 | 483 | return n; |
| ... | ... | @@ -542,23 +502,207 @@ pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) E |
| 542 | 502 | while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]); |
| 543 | 503 | } |
| 544 | 504 | |
| 545 | pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void { | |
| 546 | try std.fmt.format(w, format, args); | |
| 505 | /// Renders fmt string with args, calling `writer` with slices of bytes. | |
| 506 | /// If `writer` returns an error, the error is returned from `format` and | |
| 507 | /// `writer` is not called again. | |
| 508 | /// | |
| 509 | /// The format string must be comptime-known and may contain placeholders following | |
| 510 | /// this format: | |
| 511 | /// `{[argument][specifier]:[fill][alignment][width].[precision]}` | |
| 512 | /// | |
| 513 | /// Above, each word including its surrounding [ and ] is a parameter which you have to replace with something: | |
| 514 | /// | |
| 515 | /// - *argument* is either the numeric index or the field name of the argument that should be inserted | |
| 516 | /// - when using a field name, you are required to enclose the field name (an identifier) in square | |
| 517 | /// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...} | |
| 518 | /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) | |
| 519 | /// - *fill* is a single byte which is used to pad formatted numbers. | |
| 520 | /// - *alignment* is one of the three bytes '<', '^', or '>' to make numbers | |
| 521 | /// left, center, or right-aligned, respectively. | |
| 522 | /// - Not all specifiers support alignment. | |
| 523 | /// - Alignment is not Unicode-aware; appropriate only when used with raw bytes or ASCII. | |
| 524 | /// - *width* is the total width of the field in bytes. This only applies to number formatting. | |
| 525 | /// - *precision* specifies how many decimals a formatted number should have. | |
| 526 | /// | |
| 527 | /// Note that most of the parameters are optional and may be omitted. Also you | |
| 528 | /// can leave out separators like `:` and `.` when all parameters after the | |
| 529 | /// separator are omitted. | |
| 530 | /// | |
| 531 | /// Only exception is the *fill* parameter. If a non-zero *fill* character is | |
| 532 | /// required at the same time as *width* is specified, one has to specify | |
| 533 | /// *alignment* as well, as otherwise the digit following `:` is interpreted as | |
| 534 | /// *width*, not *fill*. | |
| 535 | /// | |
| 536 | /// The *specifier* has several options for types: | |
| 537 | /// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes | |
| 538 | /// - `s`: | |
| 539 | /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination | |
| 540 | /// - for slices of u8, print the entire slice as a string without zero-termination | |
| 541 | /// - `t`: | |
| 542 | /// - for enums and tagged unions: prints the tag name | |
| 543 | /// - for error sets: prints the error name | |
| 544 | /// - `b64`: output string as standard base64 | |
| 545 | /// - `e`: output floating point value in scientific notation | |
| 546 | /// - `d`: output numeric value in decimal notation | |
| 547 | /// - `b`: output integer value in binary notation | |
| 548 | /// - `o`: output integer value in octal notation | |
| 549 | /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. | |
| 550 | /// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max. | |
| 551 | /// - `D`: output nanoseconds as duration | |
| 552 | /// - `B`: output bytes in SI units (decimal) | |
| 553 | /// - `Bi`: output bytes in IEC units (binary) | |
| 554 | /// - `?`: output optional value as either the unwrapped value, or `null`; may be followed by a format specifier for the underlying value. | |
| 555 | /// - `!`: output error union value as either the unwrapped value, or the formatted error value; may be followed by a format specifier for the underlying value. | |
| 556 | /// - `*`: output the address of the value instead of the value itself. | |
| 557 | /// - `any`: output a value of any type using its default format. | |
| 558 | /// - `f`: delegates to a method on the type named "format" with the signature `fn (*Writer, args: anytype) Writer.Error!void`. | |
| 559 | /// | |
| 560 | /// A user type may be a `struct`, `vector`, `union` or `enum` type. | |
| 561 | /// | |
| 562 | /// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`. | |
| 563 | /// | |
| 564 | /// Asserts `buffer` capacity of at least 2 if a union is printed. This | |
| 565 | /// requirement could be lifted by adjusting the code, but if you trigger that | |
| 566 | /// assertion it is a clue that you should probably be using a buffer. | |
| 567 | pub 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 | } |
| 548 | 696 | |
| 549 | 697 | /// Calls `drain` as many times as necessary such that `byte` is transferred. |
| 550 | 698 | pub fn writeByte(w: *Writer, byte: u8) Error!void { |
| 551 | 699 | while (w.buffer.len - w.end == 0) { |
| 552 | 700 | const n = try w.vtable.drain(w, &.{&.{byte}}, 1); |
| 553 | if (n > 0) { | |
| 554 | w.count += 1; | |
| 555 | return; | |
| 556 | } | |
| 701 | if (n > 0) return; | |
| 557 | 702 | } else { |
| 558 | 703 | @branchHint(.likely); |
| 559 | 704 | w.buffer[w.end] = byte; |
| 560 | 705 | w.end += 1; |
| 561 | w.count += 1; | |
| 562 | 706 | } |
| 563 | 707 | } |
| 564 | 708 | |
| ... | ... | @@ -571,7 +715,6 @@ pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!voi |
| 571 | 715 | @branchHint(.likely); |
| 572 | 716 | w.buffer[w.end] = byte; |
| 573 | 717 | w.end += 1; |
| 574 | w.count += 1; | |
| 575 | 718 | } |
| 576 | 719 | } |
| 577 | 720 | |
| ... | ... | @@ -625,12 +768,23 @@ pub fn writeStruct(w: *Writer, value: anytype) Error!void { |
| 625 | 768 | /// comptime-known and matches host endianness. |
| 626 | 769 | /// TODO: make sure this value is not a reference type |
| 627 | 770 | pub inline fn writeStructEndian(w: *Writer, value: anytype, endian: std.builtin.Endian) Error!void { |
| 628 | if (native_endian == endian) { | |
| 629 | return w.writeStruct(value); | |
| 630 | } else { | |
| 631 | var copy = value; | |
| 632 | std.mem.byteSwapAllFields(@TypeOf(value), &copy); | |
| 633 | return w.writeStruct(copy); | |
| 771 | switch (@typeInfo(@TypeOf(value))) { | |
| 772 | .@"struct" => |info| switch (info.layout) { | |
| 773 | .auto => @compileError("ill-defined memory layout"), | |
| 774 | .@"extern" => { | |
| 775 | if (native_endian == endian) { | |
| 776 | return w.writeStruct(value); | |
| 777 | } else { | |
| 778 | var copy = value; | |
| 779 | std.mem.byteSwapAllFields(@TypeOf(value), &copy); | |
| 780 | return w.writeStruct(copy); | |
| 781 | } | |
| 782 | }, | |
| 783 | .@"packed" => { | |
| 784 | return writeInt(w, info.backing_integer.?, @bitCast(value), endian); | |
| 785 | }, | |
| 786 | }, | |
| 787 | else => @compileError("not a struct"), | |
| 634 | 788 | } |
| 635 | 789 | } |
| 636 | 790 | |
| ... | ... | @@ -647,14 +801,6 @@ pub inline fn writeSliceEndian( |
| 647 | 801 | } |
| 648 | 802 | } |
| 649 | 803 | |
| 650 | /// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)` | |
| 651 | pub 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 | 804 | /// Unlike `writeSplat` and `writeVec`, this function will call into `VTable` |
| 659 | 805 | /// even if there is enough buffer capacity for the file contents. |
| 660 | 806 | /// |
| ... | ... | @@ -680,12 +826,10 @@ pub fn sendFileHeader( |
| 680 | 826 | if (new_end <= w.buffer.len) { |
| 681 | 827 | @memcpy(w.buffer[w.end..][0..header.len], header); |
| 682 | 828 | w.end = new_end; |
| 683 | w.count += header.len; | |
| 684 | 829 | return header.len + try w.vtable.sendFile(w, file_reader, limit); |
| 685 | 830 | } |
| 686 | 831 | const buffered_contents = limit.slice(file_reader.interface.buffered()); |
| 687 | 832 | const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1); |
| 688 | w.count += n; | |
| 689 | 833 | file_reader.interface.toss(n - header.len); |
| 690 | 834 | return n; |
| 691 | 835 | } |
| ... | ... | @@ -698,6 +842,8 @@ pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) File |
| 698 | 842 | return n; |
| 699 | 843 | } |
| 700 | 844 | |
| 845 | /// Number of bytes logically written is returned. This excludes bytes from | |
| 846 | /// `buffer` because they have already been logically written. | |
| 701 | 847 | pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllError!usize { |
| 702 | 848 | var remaining = @intFromEnum(limit); |
| 703 | 849 | while (remaining > 0) { |
| ... | ... | @@ -772,16 +918,13 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void { |
| 772 | 918 | switch (@typeInfo(T)) { |
| 773 | 919 | .pointer => |info| { |
| 774 | 920 | try w.writeAll(@typeName(info.child) ++ "@"); |
| 775 | if (info.size == .slice) | |
| 776 | try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{}) | |
| 777 | else | |
| 778 | try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); | |
| 779 | return; | |
| 921 | const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value); | |
| 922 | return w.printInt(int, 16, .lower, .{}); | |
| 780 | 923 | }, |
| 781 | 924 | .optional => |info| { |
| 782 | 925 | if (@typeInfo(info.child) == .pointer) { |
| 783 | 926 | try w.writeAll(@typeName(info.child) ++ "@"); |
| 784 | try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); | |
| 927 | try w.printInt(@intFromPtr(value), 16, .lower, .{}); | |
| 785 | 928 | return; |
| 786 | 929 | } |
| 787 | 930 | }, |
| ... | ... | @@ -791,6 +934,7 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void { |
| 791 | 934 | @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); |
| 792 | 935 | } |
| 793 | 936 | |
| 937 | /// Asserts `buffer` capacity of at least 2 if `value` is a union. | |
| 794 | 938 | pub fn printValue( |
| 795 | 939 | w: *Writer, |
| 796 | 940 | comptime fmt: []const u8, |
| ... | ... | @@ -800,26 +944,181 @@ pub fn printValue( |
| 800 | 944 | ) Error!void { |
| 801 | 945 | const T = @TypeOf(value); |
| 802 | 946 | |
| 803 | if (comptime std.mem.eql(u8, fmt, "*")) { | |
| 804 | return w.printAddress(value); | |
| 947 | switch (fmt.len) { | |
| 948 | 1 => switch (fmt[0]) { | |
| 949 | '*' => return w.printAddress(value), | |
| 950 | 'f' => return value.format(w), | |
| 951 | 'd' => switch (@typeInfo(T)) { | |
| 952 | .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)), | |
| 953 | .int, .comptime_int => return printInt(w, value, 10, .lower, options), | |
| 954 | .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)), | |
| 955 | .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options), | |
| 956 | .vector => return printVector(w, fmt, options, value, max_depth), | |
| 957 | else => invalidFmtError(fmt, value), | |
| 958 | }, | |
| 959 | 'c' => return w.printAsciiChar(value, options), | |
| 960 | 'u' => return w.printUnicodeCodepoint(value), | |
| 961 | 'b' => switch (@typeInfo(T)) { | |
| 962 | .int, .comptime_int => return printInt(w, value, 2, .lower, options), | |
| 963 | .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options), | |
| 964 | .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)), | |
| 965 | .vector => return printVector(w, fmt, options, value, max_depth), | |
| 966 | else => invalidFmtError(fmt, value), | |
| 967 | }, | |
| 968 | 'o' => switch (@typeInfo(T)) { | |
| 969 | .int, .comptime_int => return printInt(w, value, 8, .lower, options), | |
| 970 | .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options), | |
| 971 | .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)), | |
| 972 | .vector => return printVector(w, fmt, options, value, max_depth), | |
| 973 | else => invalidFmtError(fmt, value), | |
| 974 | }, | |
| 975 | 'x' => switch (@typeInfo(T)) { | |
| 976 | .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)), | |
| 977 | .int, .comptime_int => return printInt(w, value, 16, .lower, options), | |
| 978 | .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options), | |
| 979 | .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)), | |
| 980 | .pointer => |info| switch (info.size) { | |
| 981 | .one, .slice => { | |
| 982 | const slice: []const u8 = value; | |
| 983 | optionsForbidden(options); | |
| 984 | return printHex(w, slice, .lower); | |
| 985 | }, | |
| 986 | .many, .c => { | |
| 987 | const slice: [:0]const u8 = std.mem.span(value); | |
| 988 | optionsForbidden(options); | |
| 989 | return printHex(w, slice, .lower); | |
| 990 | }, | |
| 991 | }, | |
| 992 | .array => { | |
| 993 | const slice: []const u8 = &value; | |
| 994 | optionsForbidden(options); | |
| 995 | return printHex(w, slice, .lower); | |
| 996 | }, | |
| 997 | .vector => return printVector(w, fmt, options, value, max_depth), | |
| 998 | else => invalidFmtError(fmt, value), | |
| 999 | }, | |
| 1000 | 'X' => switch (@typeInfo(T)) { | |
| 1001 | .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)), | |
| 1002 | .int, .comptime_int => return printInt(w, value, 16, .upper, options), | |
| 1003 | .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options), | |
| 1004 | .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)), | |
| 1005 | .pointer => |info| switch (info.size) { | |
| 1006 | .one, .slice => { | |
| 1007 | const slice: []const u8 = value; | |
| 1008 | optionsForbidden(options); | |
| 1009 | return printHex(w, slice, .upper); | |
| 1010 | }, | |
| 1011 | .many, .c => { | |
| 1012 | const slice: [:0]const u8 = std.mem.span(value); | |
| 1013 | optionsForbidden(options); | |
| 1014 | return printHex(w, slice, .upper); | |
| 1015 | }, | |
| 1016 | }, | |
| 1017 | .array => { | |
| 1018 | const slice: []const u8 = &value; | |
| 1019 | optionsForbidden(options); | |
| 1020 | return printHex(w, slice, .upper); | |
| 1021 | }, | |
| 1022 | .vector => return printVector(w, fmt, options, value, max_depth), | |
| 1023 | else => invalidFmtError(fmt, value), | |
| 1024 | }, | |
| 1025 | 's' => switch (@typeInfo(T)) { | |
| 1026 | .pointer => |info| switch (info.size) { | |
| 1027 | .one, .slice => { | |
| 1028 | const slice: []const u8 = value; | |
| 1029 | return w.alignBufferOptions(slice, options); | |
| 1030 | }, | |
| 1031 | .many, .c => { | |
| 1032 | const slice: [:0]const u8 = std.mem.span(value); | |
| 1033 | return w.alignBufferOptions(slice, options); | |
| 1034 | }, | |
| 1035 | }, | |
| 1036 | .array => { | |
| 1037 | const slice: []const u8 = &value; | |
| 1038 | return w.alignBufferOptions(slice, options); | |
| 1039 | }, | |
| 1040 | else => invalidFmtError(fmt, value), | |
| 1041 | }, | |
| 1042 | 'B' => switch (@typeInfo(T)) { | |
| 1043 | .int, .comptime_int => return w.printByteSize(value, .decimal, options), | |
| 1044 | .@"struct" => return value.formatByteSize(w, .decimal), | |
| 1045 | else => invalidFmtError(fmt, value), | |
| 1046 | }, | |
| 1047 | 'D' => switch (@typeInfo(T)) { | |
| 1048 | .int, .comptime_int => return w.printDuration(value, options), | |
| 1049 | .@"struct" => return value.formatDuration(w), | |
| 1050 | else => invalidFmtError(fmt, value), | |
| 1051 | }, | |
| 1052 | 'e' => switch (@typeInfo(T)) { | |
| 1053 | .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)), | |
| 1054 | .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)), | |
| 1055 | else => invalidFmtError(fmt, value), | |
| 1056 | }, | |
| 1057 | 'E' => switch (@typeInfo(T)) { | |
| 1058 | .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)), | |
| 1059 | .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)), | |
| 1060 | else => invalidFmtError(fmt, value), | |
| 1061 | }, | |
| 1062 | 't' => switch (@typeInfo(T)) { | |
| 1063 | .error_set => return w.writeAll(@errorName(value)), | |
| 1064 | .@"enum", .@"union" => return w.writeAll(@tagName(value)), | |
| 1065 | else => invalidFmtError(fmt, value), | |
| 1066 | }, | |
| 1067 | else => {}, | |
| 1068 | }, | |
| 1069 | 2 => switch (fmt[0]) { | |
| 1070 | 'B' => switch (fmt[1]) { | |
| 1071 | 'i' => switch (@typeInfo(T)) { | |
| 1072 | .int, .comptime_int => return w.printByteSize(value, .binary, options), | |
| 1073 | .@"struct" => return value.formatByteSize(w, .binary), | |
| 1074 | else => invalidFmtError(fmt, value), | |
| 1075 | }, | |
| 1076 | else => {}, | |
| 1077 | }, | |
| 1078 | else => {}, | |
| 1079 | }, | |
| 1080 | 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) { | |
| 1081 | .pointer => |info| switch (info.size) { | |
| 1082 | .one, .slice => { | |
| 1083 | const slice: []const u8 = value; | |
| 1084 | optionsForbidden(options); | |
| 1085 | return w.printBase64(slice); | |
| 1086 | }, | |
| 1087 | .many, .c => { | |
| 1088 | const slice: [:0]const u8 = std.mem.span(value); | |
| 1089 | optionsForbidden(options); | |
| 1090 | return w.printBase64(slice); | |
| 1091 | }, | |
| 1092 | }, | |
| 1093 | .array => { | |
| 1094 | const slice: []const u8 = &value; | |
| 1095 | optionsForbidden(options); | |
| 1096 | return w.printBase64(slice); | |
| 1097 | }, | |
| 1098 | else => invalidFmtError(fmt, value), | |
| 1099 | }, | |
| 1100 | else => {}, | |
| 805 | 1101 | } |
| 806 | 1102 | |
| 807 | 1103 | const is_any = comptime std.mem.eql(u8, fmt, ANY); |
| 808 | if (!is_any and std.meta.hasMethod(T, "format")) { | |
| 809 | if (fmt.len > 0 and fmt[0] == 'f') { | |
| 810 | return value.format(w, fmt[1..]); | |
| 811 | } else if (fmt.len == 0) { | |
| 812 | // after 0.15.0 is tagged, delete the hasMethod condition and this compile error | |
| 813 | @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it"); | |
| 814 | } | |
| 1104 | if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) { | |
| 1105 | // after 0.15.0 is tagged, delete this compile error and its condition | |
| 1106 | @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it"); | |
| 815 | 1107 | } |
| 816 | 1108 | |
| 817 | 1109 | switch (@typeInfo(T)) { |
| 818 | .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value), | |
| 819 | .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value), | |
| 1110 | .float, .comptime_float => { | |
| 1111 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); | |
| 1112 | return printFloat(w, value, options.toNumber(.decimal, .lower)); | |
| 1113 | }, | |
| 1114 | .int, .comptime_int => { | |
| 1115 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); | |
| 1116 | return printInt(w, value, 10, .lower, options); | |
| 1117 | }, | |
| 820 | 1118 | .bool => { |
| 821 | 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 | 1123 | .void => { |
| 825 | 1124 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| ... | ... | @@ -852,50 +1151,30 @@ pub fn printValue( |
| 852 | 1151 | } |
| 853 | 1152 | }, |
| 854 | 1153 | .error_set => { |
| 855 | if (fmt.len == 1 and fmt[0] == 's') return w.writeAll(@errorName(value)); | |
| 856 | 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" => { | |
| 860 | if (fmt.len == 1 and fmt[0] == 's') { | |
| 861 | try w.writeAll(@tagName(value)); | |
| 862 | return; | |
| 863 | } | |
| 864 | if (!is_any) { | |
| 865 | if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth); | |
| 866 | return printValue(w, ANY, options, value, max_depth); | |
| 867 | } | |
| 868 | const enum_info = @typeInfo(T).@"enum"; | |
| 869 | if (enum_info.is_exhaustive) { | |
| 870 | var vecs: [3][]const u8 = .{ @typeName(T), ".", @tagName(value) }; | |
| 871 | try w.writeVecAll(&vecs); | |
| 872 | return; | |
| 873 | } | |
| 874 | try w.writeAll(@typeName(T)); | |
| 875 | @setEvalBranchQuota(3 * enum_info.fields.len); | |
| 876 | inline for (enum_info.fields) |field| { | |
| 877 | if (@intFromEnum(value) == field.value) { | |
| 878 | try w.writeAll("."); | |
| 879 | try w.writeAll(@tagName(value)); | |
| 880 | return; | |
| 881 | } | |
| 1158 | .@"enum" => |info| { | |
| 1159 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); | |
| 1160 | optionsForbidden(options); | |
| 1161 | if (info.is_exhaustive) { | |
| 1162 | return printEnumExhaustive(w, value); | |
| 1163 | } else { | |
| 1164 | return printEnumNonexhaustive(w, value); | |
| 882 | 1165 | } |
| 883 | try w.writeByte('('); | |
| 884 | try w.printValue(ANY, options, @intFromEnum(value), max_depth); | |
| 885 | try w.writeByte(')'); | |
| 886 | 1166 | }, |
| 887 | 1167 | .@"union" => |info| { |
| 888 | 1168 | if (!is_any) { |
| 889 | 1169 | if (fmt.len != 0) invalidFmtError(fmt, value); |
| 890 | 1170 | return printValue(w, ANY, options, value, max_depth); |
| 891 | 1171 | } |
| 892 | try w.writeAll(@typeName(T)); | |
| 893 | 1172 | if (max_depth == 0) { |
| 894 | try w.writeAll("{ ... }"); | |
| 1173 | try w.writeAll(".{ ... }"); | |
| 895 | 1174 | return; |
| 896 | 1175 | } |
| 897 | 1176 | if (info.tag_type) |UnionTagType| { |
| 898 | try w.writeAll("{ ."); | |
| 1177 | try w.writeAll(".{ ."); | |
| 899 | 1178 | try w.writeAll(@tagName(@as(UnionTagType, value))); |
| 900 | 1179 | try w.writeAll(" = "); |
| 901 | 1180 | inline for (info.fields) |u_field| { |
| ... | ... | @@ -904,9 +1183,22 @@ pub fn printValue( |
| 904 | 1183 | } |
| 905 | 1184 | } |
| 906 | 1185 | try w.writeAll(" }"); |
| 907 | } else { | |
| 908 | try w.writeByte('@'); | |
| 909 | try w.printIntOptions(@intFromPtr(&value), 16, .lower, options); | |
| 1186 | } else switch (info.layout) { | |
| 1187 | .auto => { | |
| 1188 | return w.writeAll(".{ ... }"); | |
| 1189 | }, | |
| 1190 | .@"extern", .@"packed" => { | |
| 1191 | if (info.fields.len == 0) return w.writeAll(".{}"); | |
| 1192 | try w.writeAll(".{ "); | |
| 1193 | inline for (info.fields) |field| { | |
| 1194 | try w.writeByte('.'); | |
| 1195 | try w.writeAll(field.name); | |
| 1196 | try w.writeAll(" = "); | |
| 1197 | try w.printValue(ANY, options, @field(value, field.name), max_depth - 1); | |
| 1198 | (try w.writableArray(2)).* = ", ".*; | |
| 1199 | } | |
| 1200 | w.buffer[w.end - 2 ..][0..2].* = " }".*; | |
| 1201 | }, | |
| 910 | 1202 | } |
| 911 | 1203 | }, |
| 912 | 1204 | .@"struct" => |info| { |
| ... | ... | @@ -917,10 +1209,10 @@ pub fn printValue( |
| 917 | 1209 | if (info.is_tuple) { |
| 918 | 1210 | // Skip the type and field names when formatting tuples. |
| 919 | 1211 | if (max_depth == 0) { |
| 920 | try w.writeAll("{ ... }"); | |
| 1212 | try w.writeAll(".{ ... }"); | |
| 921 | 1213 | return; |
| 922 | 1214 | } |
| 923 | try w.writeAll("{"); | |
| 1215 | try w.writeAll(".{"); | |
| 924 | 1216 | inline for (info.fields, 0..) |f, i| { |
| 925 | 1217 | if (i == 0) { |
| 926 | 1218 | try w.writeAll(" "); |
| ... | ... | @@ -932,12 +1224,11 @@ pub fn printValue( |
| 932 | 1224 | try w.writeAll(" }"); |
| 933 | 1225 | return; |
| 934 | 1226 | } |
| 935 | try w.writeAll(@typeName(T)); | |
| 936 | 1227 | if (max_depth == 0) { |
| 937 | try w.writeAll("{ ... }"); | |
| 1228 | try w.writeAll(".{ ... }"); | |
| 938 | 1229 | return; |
| 939 | 1230 | } |
| 940 | try w.writeAll("{"); | |
| 1231 | try w.writeAll(".{"); | |
| 941 | 1232 | inline for (info.fields, 0..) |f, i| { |
| 942 | 1233 | if (i == 0) { |
| 943 | 1234 | try w.writeAll(" ."); |
| ... | ... | @@ -952,44 +1243,24 @@ pub fn printValue( |
| 952 | 1243 | }, |
| 953 | 1244 | .pointer => |ptr_info| switch (ptr_info.size) { |
| 954 | 1245 | .one => switch (@typeInfo(ptr_info.child)) { |
| 955 | .array, .@"enum", .@"union", .@"struct" => { | |
| 956 | return w.printValue(fmt, options, value.*, max_depth); | |
| 957 | }, | |
| 1246 | .array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth), | |
| 1247 | .@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth), | |
| 958 | 1248 | else => { |
| 959 | 1249 | var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; |
| 960 | 1250 | try w.writeVecAll(&buffers); |
| 961 | try w.printIntOptions(@intFromPtr(value), 16, .lower, options); | |
| 1251 | try w.printInt(@intFromPtr(value), 16, .lower, options); | |
| 962 | 1252 | return; |
| 963 | 1253 | }, |
| 964 | 1254 | }, |
| 965 | 1255 | .many, .c => { |
| 966 | if (ptr_info.sentinel() != null) | |
| 967 | return w.printValue(fmt, options, std.mem.span(value), max_depth); | |
| 968 | if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8) | |
| 969 | return w.alignBufferOptions(std.mem.span(value), options); | |
| 970 | if (!is_any and fmt.len == 0) | |
| 971 | @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); | |
| 972 | if (!is_any and fmt.len != 0) | |
| 973 | invalidFmtError(fmt, value); | |
| 1256 | if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); | |
| 1257 | optionsForbidden(options); | |
| 974 | 1258 | try w.printAddress(value); |
| 975 | 1259 | }, |
| 976 | 1260 | .slice => { |
| 977 | if (!is_any and fmt.len == 0) | |
| 1261 | if (!is_any) | |
| 978 | 1262 | @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})"); |
| 979 | if (max_depth == 0) | |
| 980 | return w.writeAll("{ ... }"); | |
| 981 | if (ptr_info.child == u8) switch (fmt.len) { | |
| 982 | 1 => switch (fmt[0]) { | |
| 983 | 's' => return w.alignBufferOptions(value, options), | |
| 984 | 'x' => return w.printHex(value, .lower), | |
| 985 | 'X' => return w.printHex(value, .upper), | |
| 986 | else => {}, | |
| 987 | }, | |
| 988 | 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') { | |
| 989 | return w.printBase64(value); | |
| 990 | }, | |
| 991 | else => {}, | |
| 992 | }; | |
| 1263 | if (max_depth == 0) return w.writeAll("{ ... }"); | |
| 993 | 1264 | try w.writeAll("{ "); |
| 994 | 1265 | for (value, 0..) |elem, i| { |
| 995 | 1266 | try w.printValue(fmt, options, elem, max_depth - 1); |
| ... | ... | @@ -1000,21 +1271,9 @@ pub fn printValue( |
| 1000 | 1271 | try w.writeAll(" }"); |
| 1001 | 1272 | }, |
| 1002 | 1273 | }, |
| 1003 | .array => |info| { | |
| 1004 | if (fmt.len == 0) | |
| 1005 | @compileError("cannot format array without a specifier (i.e. {s} or {any})"); | |
| 1006 | if (max_depth == 0) { | |
| 1007 | return w.writeAll("{ ... }"); | |
| 1008 | } | |
| 1009 | if (info.child == u8) { | |
| 1010 | if (fmt[0] == 's') { | |
| 1011 | return w.alignBufferOptions(&value, options); | |
| 1012 | } else if (fmt[0] == 'x') { | |
| 1013 | return w.printHex(&value, .lower); | |
| 1014 | } else if (fmt[0] == 'X') { | |
| 1015 | return w.printHex(&value, .upper); | |
| 1016 | } | |
| 1017 | } | |
| 1274 | .array => { | |
| 1275 | if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})"); | |
| 1276 | if (max_depth == 0) return w.writeAll("{ ... }"); | |
| 1018 | 1277 | try w.writeAll("{ "); |
| 1019 | 1278 | for (value, 0..) |elem, i| { |
| 1020 | 1279 | try w.printValue(fmt, options, elem, max_depth - 1); |
| ... | ... | @@ -1024,19 +1283,9 @@ pub fn printValue( |
| 1024 | 1283 | } |
| 1025 | 1284 | try w.writeAll(" }"); |
| 1026 | 1285 | }, |
| 1027 | .vector => |info| { | |
| 1028 | if (max_depth == 0) { | |
| 1029 | return w.writeAll("{ ... }"); | |
| 1030 | } | |
| 1031 | try w.writeAll("{ "); | |
| 1032 | var i: usize = 0; | |
| 1033 | while (i < info.len) : (i += 1) { | |
| 1034 | try w.printValue(fmt, options, value[i], max_depth - 1); | |
| 1035 | if (i < info.len - 1) { | |
| 1036 | try w.writeAll(", "); | |
| 1037 | } | |
| 1038 | } | |
| 1039 | try w.writeAll(" }"); | |
| 1286 | .vector => { | |
| 1287 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); | |
| 1288 | return printVector(w, fmt, options, value, max_depth); | |
| 1040 | 1289 | }, |
| 1041 | 1290 | .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), |
| 1042 | 1291 | .type => { |
| ... | ... | @@ -1045,8 +1294,9 @@ pub fn printValue( |
| 1045 | 1294 | }, |
| 1046 | 1295 | .enum_literal => { |
| 1047 | 1296 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 1048 | const buffer = [_]u8{'.'} ++ @tagName(value); | |
| 1049 | return w.alignBufferOptions(buffer, options); | |
| 1297 | optionsForbidden(options); | |
| 1298 | var vecs: [2][]const u8 = .{ ".", @tagName(value) }; | |
| 1299 | return w.writeVecAll(&vecs); | |
| 1050 | 1300 | }, |
| 1051 | 1301 | .null => { |
| 1052 | 1302 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| ... | ... | @@ -1056,75 +1306,78 @@ pub fn printValue( |
| 1056 | 1306 | } |
| 1057 | 1307 | } |
| 1058 | 1308 | |
| 1309 | fn optionsForbidden(options: std.fmt.Options) void { | |
| 1310 | assert(options.precision == null); | |
| 1311 | assert(options.width == null); | |
| 1312 | } | |
| 1313 | ||
| 1059 | 1314 | fn printErrorSet(w: *Writer, error_set: anyerror) Error!void { |
| 1060 | 1315 | var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) }; |
| 1061 | 1316 | try w.writeVecAll(&vecs); |
| 1062 | 1317 | } |
| 1063 | 1318 | |
| 1064 | pub fn printInt( | |
| 1319 | fn printEnumExhaustive(w: *Writer, value: anytype) Error!void { | |
| 1320 | var vecs: [2][]const u8 = .{ ".", @tagName(value) }; | |
| 1321 | try w.writeVecAll(&vecs); | |
| 1322 | } | |
| 1323 | ||
| 1324 | fn 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 | ||
| 1335 | pub fn printVector( | |
| 1065 | 1336 | w: *Writer, |
| 1066 | 1337 | comptime fmt: []const u8, |
| 1067 | 1338 | options: std.fmt.Options, |
| 1068 | 1339 | value: anytype, |
| 1340 | max_depth: usize, | |
| 1069 | 1341 | ) Error!void { |
| 1070 | const int_value = if (@TypeOf(value) == comptime_int) blk: { | |
| 1071 | const Int = std.math.IntFittingRange(value, value); | |
| 1072 | break :blk @as(Int, value); | |
| 1073 | } else value; | |
| 1342 | const len = @typeInfo(@TypeOf(value)).vector.len; | |
| 1343 | if (max_depth == 0) return w.writeAll("{ ... }"); | |
| 1344 | try w.writeAll("{ "); | |
| 1345 | inline for (0..len) |i| { | |
| 1346 | try w.printValue(fmt, options, value[i], max_depth - 1); | |
| 1347 | if (i < len - 1) try w.writeAll(", "); | |
| 1348 | } | |
| 1349 | try w.writeAll(" }"); | |
| 1350 | } | |
| 1074 | 1351 | |
| 1075 | switch (fmt.len) { | |
| 1076 | 0 => return w.printIntOptions(int_value, 10, .lower, options), | |
| 1077 | 1 => switch (fmt[0]) { | |
| 1078 | 'd' => return w.printIntOptions(int_value, 10, .lower, options), | |
| 1079 | 'c' => { | |
| 1080 | if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) { | |
| 1081 | return w.printAsciiChar(@as(u8, int_value), options); | |
| 1082 | } else { | |
| 1083 | @compileError("cannot print integer that is larger than 8 bits as an ASCII character"); | |
| 1084 | } | |
| 1085 | }, | |
| 1086 | 'u' => { | |
| 1087 | if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) { | |
| 1088 | return w.printUnicodeCodepoint(@as(u21, int_value), options); | |
| 1089 | } else { | |
| 1090 | @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence"); | |
| 1091 | } | |
| 1092 | }, | |
| 1093 | 'b' => return w.printIntOptions(int_value, 2, .lower, options), | |
| 1094 | 'x' => return w.printIntOptions(int_value, 16, .lower, options), | |
| 1095 | 'X' => return w.printIntOptions(int_value, 16, .upper, options), | |
| 1096 | 'o' => return w.printIntOptions(int_value, 8, .lower, options), | |
| 1097 | 'B' => return w.printByteSize(int_value, .decimal, options), | |
| 1098 | 'D' => return w.printDuration(int_value, options), | |
| 1099 | else => invalidFmtError(fmt, value), | |
| 1352 | // A wrapper around `printIntAny` to avoid the generic explosion of this | |
| 1353 | // function by funneling smaller integer types through `isize` and `usize`. | |
| 1354 | pub inline fn printInt( | |
| 1355 | w: *Writer, | |
| 1356 | value: anytype, | |
| 1357 | base: u8, | |
| 1358 | case: std.fmt.Case, | |
| 1359 | options: std.fmt.Options, | |
| 1360 | ) Error!void { | |
| 1361 | switch (@TypeOf(value)) { | |
| 1362 | isize, usize => {}, | |
| 1363 | comptime_int => { | |
| 1364 | if (comptime std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options); | |
| 1365 | if (comptime std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options); | |
| 1366 | const Int = std.math.IntFittingRange(value, value); | |
| 1367 | return printIntAny(w, @as(Int, value), base, case, options); | |
| 1100 | 1368 | }, |
| 1101 | 2 => { | |
| 1102 | if (fmt[0] == 'B' and fmt[1] == 'i') { | |
| 1103 | return w.printByteSize(int_value, .binary, options); | |
| 1104 | } else { | |
| 1105 | invalidFmtError(fmt, value); | |
| 1106 | } | |
| 1369 | else => switch (@typeInfo(@TypeOf(value)).int.signedness) { | |
| 1370 | .signed => if (std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options), | |
| 1371 | .unsigned => if (std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options), | |
| 1107 | 1372 | }, |
| 1108 | else => invalidFmtError(fmt, value), | |
| 1109 | 1373 | } |
| 1110 | comptime unreachable; | |
| 1111 | } | |
| 1112 | ||
| 1113 | pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { | |
| 1114 | return w.alignBufferOptions(@as(*const [1]u8, &c), options); | |
| 1374 | return printIntAny(w, value, base, case, options); | |
| 1115 | 1375 | } |
| 1116 | 1376 | |
| 1117 | pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { | |
| 1118 | return w.alignBufferOptions(bytes, options); | |
| 1119 | } | |
| 1120 | ||
| 1121 | pub 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 | ||
| 1127 | pub fn printIntOptions( | |
| 1377 | /// In general, prefer `printInt` to avoid generic explosion. However this | |
| 1378 | /// function may be used when optimal codegen for a particular integer type is | |
| 1379 | /// desired. | |
| 1380 | pub fn printIntAny( | |
| 1128 | 1381 | w: *Writer, |
| 1129 | 1382 | value: anytype, |
| 1130 | 1383 | base: u8, |
| ... | ... | @@ -1132,20 +1385,14 @@ pub fn printIntOptions( |
| 1132 | 1385 | options: std.fmt.Options, |
| 1133 | 1386 | ) Error!void { |
| 1134 | 1387 | assert(base >= 2); |
| 1135 | ||
| 1136 | const int_value = if (@TypeOf(value) == comptime_int) blk: { | |
| 1137 | const Int = std.math.IntFittingRange(value, value); | |
| 1138 | break :blk @as(Int, value); | |
| 1139 | } else value; | |
| 1140 | ||
| 1141 | const value_info = @typeInfo(@TypeOf(int_value)).int; | |
| 1388 | const value_info = @typeInfo(@TypeOf(value)).int; | |
| 1142 | 1389 | |
| 1143 | 1390 | // The type must have the same size as `base` or be wider in order for the |
| 1144 | 1391 | // division to work |
| 1145 | 1392 | const min_int_bits = comptime @max(value_info.bits, 8); |
| 1146 | 1393 | const MinInt = std.meta.Int(.unsigned, min_int_bits); |
| 1147 | 1394 | |
| 1148 | const abs_value = @abs(int_value); | |
| 1395 | const abs_value = @abs(value); | |
| 1149 | 1396 | // The worst case in terms of space needed is base 2, plus 1 for the sign |
| 1150 | 1397 | var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined; |
| 1151 | 1398 | |
| ... | ... | @@ -1192,41 +1439,69 @@ pub fn printIntOptions( |
| 1192 | 1439 | return w.alignBufferOptions(buf[index..], options); |
| 1193 | 1440 | } |
| 1194 | 1441 | |
| 1195 | pub fn printFloat( | |
| 1196 | w: *Writer, | |
| 1197 | comptime fmt: []const u8, | |
| 1198 | options: std.fmt.Options, | |
| 1199 | value: anytype, | |
| 1200 | ) Error!void { | |
| 1201 | var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined; | |
| 1442 | pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { | |
| 1443 | return w.alignBufferOptions(@as(*const [1]u8, &c), options); | |
| 1444 | } | |
| 1202 | 1445 | |
| 1203 | if (fmt.len > 1) invalidFmtError(fmt, value); | |
| 1204 | switch (if (fmt.len == 0) 'e' else fmt[0]) { | |
| 1205 | 'e' => { | |
| 1206 | const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { | |
| 1207 | error.BufferTooSmall => "(float)", | |
| 1208 | }; | |
| 1209 | return w.alignBufferOptions(s, options); | |
| 1210 | }, | |
| 1211 | 'd' => { | |
| 1212 | const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { | |
| 1213 | error.BufferTooSmall => "(float)", | |
| 1214 | }; | |
| 1215 | return w.alignBufferOptions(s, options); | |
| 1216 | }, | |
| 1217 | 'x' => { | |
| 1218 | var sub_bw: Writer = .fixed(&buf); | |
| 1219 | sub_bw.printFloatHexadecimal(value, options.precision) catch unreachable; | |
| 1220 | return w.alignBufferOptions(sub_bw.buffered(), options); | |
| 1446 | pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { | |
| 1447 | return w.alignBufferOptions(bytes, options); | |
| 1448 | } | |
| 1449 | ||
| 1450 | pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void { | |
| 1451 | var buf: [4]u8 = undefined; | |
| 1452 | const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { | |
| 1453 | error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: { | |
| 1454 | buf[0..3].* = std.unicode.replacement_character_utf8; | |
| 1455 | break :l 3; | |
| 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. | |
| 1462 | pub 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. | |
| 1479 | pub 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 | } |
| 1225 | 1494 | |
| 1226 | pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void { | |
| 1495 | pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void { | |
| 1227 | 1496 | if (std.math.signbit(value)) try w.writeByte('-'); |
| 1228 | if (std.math.isNan(value)) return w.writeAll("nan"); | |
| 1229 | if (std.math.isInf(value)) return w.writeAll("inf"); | |
| 1497 | if (std.math.isNan(value)) return w.writeAll(switch (case) { | |
| 1498 | .lower => "nan", | |
| 1499 | .upper => "NAN", | |
| 1500 | }); | |
| 1501 | if (std.math.isInf(value)) return w.writeAll(switch (case) { | |
| 1502 | .lower => "inf", | |
| 1503 | .upper => "INF", | |
| 1504 | }); | |
| 1230 | 1505 | |
| 1231 | 1506 | const T = @TypeOf(value); |
| 1232 | 1507 | const TU = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| ... | ... | @@ -1302,7 +1577,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) |
| 1302 | 1577 | |
| 1303 | 1578 | // +1 for the decimal part. |
| 1304 | 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); | |
| 1306 | 1581 | |
| 1307 | 1582 | try w.writeAll("0x"); |
| 1308 | 1583 | try w.writeByte(buf[0]); |
| ... | ... | @@ -1319,7 +1594,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) |
| 1319 | 1594 | try w.splatByteAll('0', precision - trimmed.len); |
| 1320 | 1595 | }; |
| 1321 | 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 | } |
| 1324 | 1599 | |
| 1325 | 1600 | pub const ByteSizeUnits = enum { |
| ... | ... | @@ -1415,7 +1690,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { |
| 1415 | 1690 | }) |unit| { |
| 1416 | 1691 | if (ns_remaining >= unit.ns) { |
| 1417 | 1692 | const units = ns_remaining / unit.ns; |
| 1418 | try w.printIntOptions(units, 10, .lower, .{}); | |
| 1693 | try w.printInt(units, 10, .lower, .{}); | |
| 1419 | 1694 | try w.writeByte(unit.sep); |
| 1420 | 1695 | ns_remaining -= units * unit.ns; |
| 1421 | 1696 | if (ns_remaining == 0) return; |
| ... | ... | @@ -1429,13 +1704,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { |
| 1429 | 1704 | }) |unit| { |
| 1430 | 1705 | const kunits = ns_remaining * 1000 / unit.ns; |
| 1431 | 1706 | if (kunits >= 1000) { |
| 1432 | try w.printIntOptions(kunits / 1000, 10, .lower, .{}); | |
| 1707 | try w.printInt(kunits / 1000, 10, .lower, .{}); | |
| 1433 | 1708 | const frac = kunits % 1000; |
| 1434 | 1709 | if (frac > 0) { |
| 1435 | 1710 | // Write up to 3 decimal places |
| 1436 | 1711 | var decimal_buf = [_]u8{ '.', 0, 0, 0 }; |
| 1437 | 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 | 1714 | var end: usize = 4; |
| 1440 | 1715 | while (end > 1) : (end -= 1) { |
| 1441 | 1716 | if (decimal_buf[end - 1] != '0') break; |
| ... | ... | @@ -1446,7 +1721,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { |
| 1446 | 1721 | } |
| 1447 | 1722 | } |
| 1448 | 1723 | |
| 1449 | try w.printIntOptions(ns_remaining, 10, .lower, .{}); | |
| 1724 | try w.printInt(ns_remaining, 10, .lower, .{}); | |
| 1450 | 1725 | try w.writeAll("ns"); |
| 1451 | 1726 | } |
| 1452 | 1727 | |
| ... | ... | @@ -1456,12 +1731,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { |
| 1456 | 1731 | pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void { |
| 1457 | 1732 | // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24 |
| 1458 | 1733 | var buf: [24]u8 = undefined; |
| 1459 | var sub_bw: Writer = .fixed(&buf); | |
| 1460 | switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { | |
| 1461 | .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable, | |
| 1462 | .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable, | |
| 1734 | var sub_writer: Writer = .fixed(&buf); | |
| 1735 | if (@TypeOf(nanoseconds) == comptime_int) { | |
| 1736 | if (nanoseconds >= 0) { | |
| 1737 | sub_writer.printDurationUnsigned(nanoseconds) catch unreachable; | |
| 1738 | } else { | |
| 1739 | sub_writer.printDurationSigned(nanoseconds) catch unreachable; | |
| 1740 | } | |
| 1741 | } else switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { | |
| 1742 | .signed => sub_writer.printDurationSigned(nanoseconds) catch unreachable, | |
| 1743 | .unsigned => sub_writer.printDurationUnsigned(nanoseconds) catch unreachable, | |
| 1463 | 1744 | } |
| 1464 | return w.alignBufferOptions(sub_bw.buffered(), options); | |
| 1745 | return w.alignBufferOptions(sub_writer.buffered(), options); | |
| 1465 | 1746 | } |
| 1466 | 1747 | |
| 1467 | 1748 | pub 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 | 1828 | } |
| 1548 | 1829 | } |
| 1549 | 1830 | |
| 1550 | test "formatValue max_depth" { | |
| 1831 | test "printValue max_depth" { | |
| 1551 | 1832 | const Vec2 = struct { |
| 1552 | 1833 | const SelfType = @This(); |
| 1553 | 1834 | x: f32, |
| 1554 | 1835 | y: f32, |
| 1555 | 1836 | |
| 1556 | pub fn format( | |
| 1557 | self: SelfType, | |
| 1558 | comptime fmt: []const u8, | |
| 1559 | options: std.fmt.Options, | |
| 1560 | w: *Writer, | |
| 1561 | ) Error!void { | |
| 1562 | _ = options; | |
| 1563 | if (fmt.len == 0) { | |
| 1564 | return w.print("({d:.3},{d:.3})", .{ self.x, self.y }); | |
| 1565 | } else { | |
| 1566 | @compileError("unknown format string: '" ++ fmt ++ "'"); | |
| 1567 | } | |
| 1837 | pub fn format(self: SelfType, w: *Writer) Error!void { | |
| 1838 | return w.print("({d:.3},{d:.3})", .{ self.x, self.y }); | |
| 1568 | 1839 | } |
| 1569 | 1840 | }; |
| 1570 | 1841 | const E = enum { |
| ... | ... | @@ -1598,133 +1869,133 @@ test "formatValue max_depth" { |
| 1598 | 1869 | var buf: [1000]u8 = undefined; |
| 1599 | 1870 | var w: Writer = .fixed(&buf); |
| 1600 | 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()); | |
| 1602 | 1873 | |
| 1603 | w.reset(); | |
| 1874 | w = .fixed(&buf); | |
| 1604 | 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()); | |
| 1606 | 1877 | |
| 1607 | w.reset(); | |
| 1878 | w = .fixed(&buf); | |
| 1608 | 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()); | |
| 1610 | 1881 | |
| 1611 | w.reset(); | |
| 1882 | w = .fixed(&buf); | |
| 1612 | 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()); | |
| 1614 | 1885 | |
| 1615 | 1886 | const vec: @Vector(4, i32) = .{ 1, 2, 3, 4 }; |
| 1616 | w.reset(); | |
| 1887 | w = .fixed(&buf); | |
| 1617 | 1888 | try w.printValue("", .{}, vec, 0); |
| 1618 | 1889 | try testing.expectEqualStrings("{ ... }", w.buffered()); |
| 1619 | 1890 | |
| 1620 | w.reset(); | |
| 1891 | w = .fixed(&buf); | |
| 1621 | 1892 | try w.printValue("", .{}, vec, 1); |
| 1622 | 1893 | try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered()); |
| 1623 | 1894 | } |
| 1624 | 1895 | |
| 1625 | 1896 | test printDuration { |
| 1626 | testDurationCase("0ns", 0); | |
| 1627 | testDurationCase("1ns", 1); | |
| 1628 | testDurationCase("999ns", std.time.ns_per_us - 1); | |
| 1629 | testDurationCase("1us", std.time.ns_per_us); | |
| 1630 | testDurationCase("1.45us", 1450); | |
| 1631 | testDurationCase("1.5us", 3 * std.time.ns_per_us / 2); | |
| 1632 | testDurationCase("14.5us", 14500); | |
| 1633 | testDurationCase("145us", 145000); | |
| 1634 | testDurationCase("999.999us", std.time.ns_per_ms - 1); | |
| 1635 | testDurationCase("1ms", std.time.ns_per_ms + 1); | |
| 1636 | testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2); | |
| 1637 | testDurationCase("1.11ms", 1110000); | |
| 1638 | testDurationCase("1.111ms", 1111000); | |
| 1639 | testDurationCase("1.111ms", 1111100); | |
| 1640 | testDurationCase("999.999ms", std.time.ns_per_s - 1); | |
| 1641 | testDurationCase("1s", std.time.ns_per_s); | |
| 1642 | testDurationCase("59.999s", std.time.ns_per_min - 1); | |
| 1643 | testDurationCase("1m", std.time.ns_per_min); | |
| 1644 | testDurationCase("1h", std.time.ns_per_hour); | |
| 1645 | testDurationCase("1d", std.time.ns_per_day); | |
| 1646 | testDurationCase("1w", std.time.ns_per_week); | |
| 1647 | testDurationCase("1y", 365 * std.time.ns_per_day); | |
| 1648 | testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1 | |
| 1649 | testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); | |
| 1650 | testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); | |
| 1651 | testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); | |
| 1652 | testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); | |
| 1653 | testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); | |
| 1654 | testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); | |
| 1655 | testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64)); | |
| 1656 | ||
| 1657 | testing.expectFmt("=======0ns", "{D:=>10}", .{0}); | |
| 1658 | testing.expectFmt("1ns=======", "{D:=<10}", .{1}); | |
| 1659 | testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1}); | |
| 1897 | try testDurationCase("0ns", 0); | |
| 1898 | try testDurationCase("1ns", 1); | |
| 1899 | try testDurationCase("999ns", std.time.ns_per_us - 1); | |
| 1900 | try testDurationCase("1us", std.time.ns_per_us); | |
| 1901 | try testDurationCase("1.45us", 1450); | |
| 1902 | try testDurationCase("1.5us", 3 * std.time.ns_per_us / 2); | |
| 1903 | try testDurationCase("14.5us", 14500); | |
| 1904 | try testDurationCase("145us", 145000); | |
| 1905 | try testDurationCase("999.999us", std.time.ns_per_ms - 1); | |
| 1906 | try testDurationCase("1ms", std.time.ns_per_ms + 1); | |
| 1907 | try testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2); | |
| 1908 | try testDurationCase("1.11ms", 1110000); | |
| 1909 | try testDurationCase("1.111ms", 1111000); | |
| 1910 | try testDurationCase("1.111ms", 1111100); | |
| 1911 | try testDurationCase("999.999ms", std.time.ns_per_s - 1); | |
| 1912 | try testDurationCase("1s", std.time.ns_per_s); | |
| 1913 | try testDurationCase("59.999s", std.time.ns_per_min - 1); | |
| 1914 | try testDurationCase("1m", std.time.ns_per_min); | |
| 1915 | try testDurationCase("1h", std.time.ns_per_hour); | |
| 1916 | try testDurationCase("1d", std.time.ns_per_day); | |
| 1917 | try testDurationCase("1w", std.time.ns_per_week); | |
| 1918 | try testDurationCase("1y", 365 * std.time.ns_per_day); | |
| 1919 | try testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1 | |
| 1920 | try testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); | |
| 1921 | try testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); | |
| 1922 | try testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); | |
| 1923 | try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); | |
| 1924 | try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); | |
| 1925 | try testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); | |
| 1926 | try testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64)); | |
| 1927 | ||
| 1928 | try testing.expectFmt("=======0ns", "{D:=>10}", .{0}); | |
| 1929 | try testing.expectFmt("1ns=======", "{D:=<10}", .{1}); | |
| 1930 | try testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1}); | |
| 1660 | 1931 | } |
| 1661 | 1932 | |
| 1662 | 1933 | test printDurationSigned { |
| 1663 | testDurationCaseSigned("0ns", 0); | |
| 1664 | testDurationCaseSigned("1ns", 1); | |
| 1665 | testDurationCaseSigned("-1ns", -(1)); | |
| 1666 | testDurationCaseSigned("999ns", std.time.ns_per_us - 1); | |
| 1667 | testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1)); | |
| 1668 | testDurationCaseSigned("1us", std.time.ns_per_us); | |
| 1669 | testDurationCaseSigned("-1us", -(std.time.ns_per_us)); | |
| 1670 | testDurationCaseSigned("1.45us", 1450); | |
| 1671 | testDurationCaseSigned("-1.45us", -(1450)); | |
| 1672 | testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2); | |
| 1673 | testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2)); | |
| 1674 | testDurationCaseSigned("14.5us", 14500); | |
| 1675 | testDurationCaseSigned("-14.5us", -(14500)); | |
| 1676 | testDurationCaseSigned("145us", 145000); | |
| 1677 | testDurationCaseSigned("-145us", -(145000)); | |
| 1678 | testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1); | |
| 1679 | testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1)); | |
| 1680 | testDurationCaseSigned("1ms", std.time.ns_per_ms + 1); | |
| 1681 | testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1)); | |
| 1682 | testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2); | |
| 1683 | testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2)); | |
| 1684 | testDurationCaseSigned("1.11ms", 1110000); | |
| 1685 | testDurationCaseSigned("-1.11ms", -(1110000)); | |
| 1686 | testDurationCaseSigned("1.111ms", 1111000); | |
| 1687 | testDurationCaseSigned("-1.111ms", -(1111000)); | |
| 1688 | testDurationCaseSigned("1.111ms", 1111100); | |
| 1689 | testDurationCaseSigned("-1.111ms", -(1111100)); | |
| 1690 | testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1); | |
| 1691 | testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1)); | |
| 1692 | testDurationCaseSigned("1s", std.time.ns_per_s); | |
| 1693 | testDurationCaseSigned("-1s", -(std.time.ns_per_s)); | |
| 1694 | testDurationCaseSigned("59.999s", std.time.ns_per_min - 1); | |
| 1695 | testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1)); | |
| 1696 | testDurationCaseSigned("1m", std.time.ns_per_min); | |
| 1697 | testDurationCaseSigned("-1m", -(std.time.ns_per_min)); | |
| 1698 | testDurationCaseSigned("1h", std.time.ns_per_hour); | |
| 1699 | testDurationCaseSigned("-1h", -(std.time.ns_per_hour)); | |
| 1700 | testDurationCaseSigned("1d", std.time.ns_per_day); | |
| 1701 | testDurationCaseSigned("-1d", -(std.time.ns_per_day)); | |
| 1702 | testDurationCaseSigned("1w", std.time.ns_per_week); | |
| 1703 | testDurationCaseSigned("-1w", -(std.time.ns_per_week)); | |
| 1704 | testDurationCaseSigned("1y", 365 * std.time.ns_per_day); | |
| 1705 | testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day)); | |
| 1706 | testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d | |
| 1707 | testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d | |
| 1708 | testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); | |
| 1709 | testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms)); | |
| 1710 | testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); | |
| 1711 | testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us)); | |
| 1712 | testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); | |
| 1713 | testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1)); | |
| 1714 | testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); | |
| 1715 | testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms)); | |
| 1716 | testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); | |
| 1717 | testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1)); | |
| 1718 | testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); | |
| 1719 | testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999)); | |
| 1720 | testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64)); | |
| 1721 | testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1); | |
| 1722 | testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64)); | |
| 1723 | ||
| 1724 | testing.expectFmt("=======0ns", "{s:=>10}", .{0}); | |
| 1725 | testing.expectFmt("1ns=======", "{s:=<10}", .{1}); | |
| 1726 | testing.expectFmt("-1ns======", "{s:=<10}", .{-(1)}); | |
| 1727 | testing.expectFmt(" -999ns ", "{s:^10}", .{-(std.time.ns_per_us - 1)}); | |
| 1934 | try testDurationCaseSigned("0ns", 0); | |
| 1935 | try testDurationCaseSigned("1ns", 1); | |
| 1936 | try testDurationCaseSigned("-1ns", -(1)); | |
| 1937 | try testDurationCaseSigned("999ns", std.time.ns_per_us - 1); | |
| 1938 | try testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1)); | |
| 1939 | try testDurationCaseSigned("1us", std.time.ns_per_us); | |
| 1940 | try testDurationCaseSigned("-1us", -(std.time.ns_per_us)); | |
| 1941 | try testDurationCaseSigned("1.45us", 1450); | |
| 1942 | try testDurationCaseSigned("-1.45us", -(1450)); | |
| 1943 | try testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2); | |
| 1944 | try testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2)); | |
| 1945 | try testDurationCaseSigned("14.5us", 14500); | |
| 1946 | try testDurationCaseSigned("-14.5us", -(14500)); | |
| 1947 | try testDurationCaseSigned("145us", 145000); | |
| 1948 | try testDurationCaseSigned("-145us", -(145000)); | |
| 1949 | try testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1); | |
| 1950 | try testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1)); | |
| 1951 | try testDurationCaseSigned("1ms", std.time.ns_per_ms + 1); | |
| 1952 | try testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1)); | |
| 1953 | try testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2); | |
| 1954 | try testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2)); | |
| 1955 | try testDurationCaseSigned("1.11ms", 1110000); | |
| 1956 | try testDurationCaseSigned("-1.11ms", -(1110000)); | |
| 1957 | try testDurationCaseSigned("1.111ms", 1111000); | |
| 1958 | try testDurationCaseSigned("-1.111ms", -(1111000)); | |
| 1959 | try testDurationCaseSigned("1.111ms", 1111100); | |
| 1960 | try testDurationCaseSigned("-1.111ms", -(1111100)); | |
| 1961 | try testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1); | |
| 1962 | try testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1)); | |
| 1963 | try testDurationCaseSigned("1s", std.time.ns_per_s); | |
| 1964 | try testDurationCaseSigned("-1s", -(std.time.ns_per_s)); | |
| 1965 | try testDurationCaseSigned("59.999s", std.time.ns_per_min - 1); | |
| 1966 | try testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1)); | |
| 1967 | try testDurationCaseSigned("1m", std.time.ns_per_min); | |
| 1968 | try testDurationCaseSigned("-1m", -(std.time.ns_per_min)); | |
| 1969 | try testDurationCaseSigned("1h", std.time.ns_per_hour); | |
| 1970 | try testDurationCaseSigned("-1h", -(std.time.ns_per_hour)); | |
| 1971 | try testDurationCaseSigned("1d", std.time.ns_per_day); | |
| 1972 | try testDurationCaseSigned("-1d", -(std.time.ns_per_day)); | |
| 1973 | try testDurationCaseSigned("1w", std.time.ns_per_week); | |
| 1974 | try testDurationCaseSigned("-1w", -(std.time.ns_per_week)); | |
| 1975 | try testDurationCaseSigned("1y", 365 * std.time.ns_per_day); | |
| 1976 | try testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day)); | |
| 1977 | try testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d | |
| 1978 | try testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d | |
| 1979 | try testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms); | |
| 1980 | try testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms)); | |
| 1981 | try testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us); | |
| 1982 | try testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us)); | |
| 1983 | try testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1); | |
| 1984 | try testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1)); | |
| 1985 | try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms); | |
| 1986 | try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms)); | |
| 1987 | try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1); | |
| 1988 | try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1)); | |
| 1989 | try testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999); | |
| 1990 | try testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999)); | |
| 1991 | try testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64)); | |
| 1992 | try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1); | |
| 1993 | try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64)); | |
| 1994 | ||
| 1995 | try testing.expectFmt("=======0ns", "{D:=>10}", .{0}); | |
| 1996 | try testing.expectFmt("1ns=======", "{D:=<10}", .{1}); | |
| 1997 | try testing.expectFmt("-1ns======", "{D:=<10}", .{-(1)}); | |
| 1998 | try testing.expectFmt(" -999ns ", "{D:^10}", .{-(std.time.ns_per_us - 1)}); | |
| 1728 | 1999 | } |
| 1729 | 2000 | |
| 1730 | 2001 | fn testDurationCase(expected: []const u8, input: u64) !void { |
| ... | ... | @@ -1741,7 +2012,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void { |
| 1741 | 2012 | try testing.expectEqualStrings(expected, w.buffered()); |
| 1742 | 2013 | } |
| 1743 | 2014 | |
| 1744 | test printIntOptions { | |
| 2015 | test printInt { | |
| 1745 | 2016 | try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{}); |
| 1746 | 2017 | |
| 1747 | 2018 | try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{}); |
| ... | ... | @@ -1757,27 +2028,22 @@ test printIntOptions { |
| 1757 | 2028 | |
| 1758 | 2029 | try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 }); |
| 1759 | 2030 | try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 }); |
| 1760 | } | |
| 1761 | 2031 | |
| 1762 | test "printInt with comptime_int" { | |
| 1763 | var buf: [20]u8 = undefined; | |
| 1764 | var w: Writer = .fixed(&buf); | |
| 1765 | try w.printInt(@as(comptime_int, 123456789123456789), "", .{}); | |
| 1766 | try std.testing.expectEqualStrings("123456789123456789", w.buffered()); | |
| 2032 | try testPrintIntCase("123456789123456789", @as(comptime_int, 123456789123456789), 10, .lower, .{}); | |
| 1767 | 2033 | } |
| 1768 | 2034 | |
| 1769 | 2035 | test "printFloat with comptime_float" { |
| 1770 | 2036 | var buf: [20]u8 = undefined; |
| 1771 | 2037 | var w: Writer = .fixed(&buf); |
| 1772 | try w.printFloat("", .{}, @as(comptime_float, 1.0)); | |
| 1773 | try std.testing.expectEqualStrings(w.buffered(), "1e0"); | |
| 1774 | try std.testing.expectFmt("1e0", "{}", .{1.0}); | |
| 2038 | try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower)); | |
| 2039 | try testing.expectEqualStrings(w.buffered(), "1e0"); | |
| 2040 | try testing.expectFmt("1", "{}", .{1.0}); | |
| 1775 | 2041 | } |
| 1776 | 2042 | |
| 1777 | 2043 | fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void { |
| 1778 | 2044 | var buffer: [100]u8 = undefined; |
| 1779 | 2045 | var w: Writer = .fixed(&buffer); |
| 1780 | w.printIntOptions(value, base, case, options); | |
| 2046 | try w.printInt(value, base, case, options); | |
| 1781 | 2047 | try testing.expectEqualStrings(expected, w.buffered()); |
| 1782 | 2048 | } |
| 1783 | 2049 | |
| ... | ... | @@ -1798,12 +2064,12 @@ test printByteSize { |
| 1798 | 2064 | |
| 1799 | 2065 | test "bytes.hex" { |
| 1800 | 2066 | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1801 | try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); | |
| 1802 | try std.testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); | |
| 1803 | try std.testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); | |
| 1804 | try std.testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); | |
| 2067 | try testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); | |
| 2068 | try testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); | |
| 2069 | try testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); | |
| 2070 | try testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); | |
| 1805 | 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 | } |
| 1808 | 2074 | |
| 1809 | 2075 | test fixed { |
| ... | ... | @@ -1832,17 +2098,22 @@ test "fixed output" { |
| 1832 | 2098 | try w.writeAll("world"); |
| 1833 | 2099 | try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld")); |
| 1834 | 2100 | |
| 1835 | try testing.expectError(error.WriteStreamEnd, w.writeAll("!")); | |
| 2101 | try testing.expectError(error.WriteFailed, w.writeAll("!")); | |
| 1836 | 2102 | try testing.expect(std.mem.eql(u8, w.buffered(), "Helloworld")); |
| 1837 | 2103 | |
| 1838 | w.reset(); | |
| 2104 | w = .fixed(&buffer); | |
| 2105 | ||
| 1839 | 2106 | try testing.expect(w.buffered().len == 0); |
| 1840 | 2107 | |
| 1841 | try testing.expectError(error.WriteStreamEnd, w.writeAll("Hello world!")); | |
| 2108 | try testing.expectError(error.WriteFailed, w.writeAll("Hello world!")); | |
| 1842 | 2109 | try testing.expect(std.mem.eql(u8, w.buffered(), "Hello worl")); |
| 2110 | } | |
| 1843 | 2111 | |
| 1844 | try w.seekTo((try w.getEndPos()) + 1); | |
| 1845 | try testing.expectError(error.WriteStreamEnd, w.writeAll("H")); | |
| 2112 | test "writeSplat 0 len splat larger than capacity" { | |
| 2113 | var buf: [8]u8 = undefined; | |
| 2114 | var w: std.io.Writer = .fixed(&buf); | |
| 2115 | const n = try w.writeSplat(&.{"something that overflows buf"}, 0); | |
| 2116 | try testing.expectEqual(0, n); | |
| 1846 | 2117 | } |
| 1847 | 2118 | |
| 1848 | 2119 | pub 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 | 2130 | return error.WriteFailed; |
| 1860 | 2131 | } |
| 1861 | 2132 | |
| 1862 | pub fn discardingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { | |
| 1863 | const slice = data[0 .. data.len - 1]; | |
| 1864 | const pattern = data[slice.len..]; | |
| 1865 | var written: usize = pattern.len * splat; | |
| 1866 | for (slice) |bytes| written += bytes.len; | |
| 1867 | w.end = 0; | |
| 1868 | return written; | |
| 1869 | } | |
| 2133 | pub const Discarding = struct { | |
| 2134 | count: u64, | |
| 2135 | writer: Writer, | |
| 1870 | 2136 | |
| 1871 | pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize { | |
| 1872 | if (File.Handle == void) return error.Unimplemented; | |
| 1873 | w.end = 0; | |
| 1874 | if (file_reader.getSize()) |size| { | |
| 1875 | const n = limit.minInt(size - file_reader.pos); | |
| 1876 | file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; | |
| 2137 | pub fn init(buffer: []u8) Discarding { | |
| 2138 | return .{ | |
| 2139 | .count = 0, | |
| 2140 | .writer = .{ | |
| 2141 | .vtable = &.{ | |
| 2142 | .drain = Discarding.drain, | |
| 2143 | .sendFile = Discarding.sendFile, | |
| 2144 | }, | |
| 2145 | .buffer = buffer, | |
| 2146 | }, | |
| 2147 | }; | |
| 2148 | } | |
| 2149 | ||
| 2150 | pub fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { | |
| 2151 | const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); | |
| 2152 | const slice = data[0 .. data.len - 1]; | |
| 2153 | const pattern = data[slice.len..]; | |
| 2154 | var written: usize = pattern.len * splat; | |
| 2155 | for (slice) |bytes| written += bytes.len; | |
| 2156 | d.count += w.end + written; | |
| 1877 | 2157 | w.end = 0; |
| 1878 | return n; | |
| 1879 | } else |_| { | |
| 1880 | // Error is observable on `file_reader` instance, and it is better to | |
| 1881 | // treat the file as a pipe. | |
| 1882 | return error.Unimplemented; | |
| 2158 | return written; | |
| 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 | }; | |
| 1885 | 2179 | |
| 1886 | 2180 | /// Removes the first `n` bytes from `buffer` by shifting buffer contents, |
| 1887 | 2181 | /// returning how many bytes are left after consuming the entire buffer, or |
| ... | ... | @@ -1966,28 +2260,27 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1966 | 2260 | return struct { |
| 1967 | 2261 | out: *Writer, |
| 1968 | 2262 | hasher: Hasher, |
| 1969 | interface: Writer, | |
| 2263 | writer: Writer, | |
| 1970 | 2264 | |
| 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 | 2270 | return .{ |
| 1973 | 2271 | .out = out, |
| 1974 | .hasher = .{}, | |
| 1975 | .interface = .{ | |
| 1976 | .vtable = &.{@This().drain}, | |
| 2272 | .hasher = hasher, | |
| 2273 | .writer = .{ | |
| 2274 | .buffer = buffer, | |
| 2275 | .vtable = &.{ .drain = @This().drain }, | |
| 1977 | 2276 | }, |
| 1978 | 2277 | }; |
| 1979 | 2278 | } |
| 1980 | 2279 | |
| 1981 | 2280 | fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { |
| 1982 | const this: *@This() = @alignCast(@fieldParentPtr("interface", w)); | |
| 1983 | if (data.len == 0) { | |
| 1984 | const buf = w.buffered(); | |
| 1985 | try this.out.writeAll(buf); | |
| 1986 | this.hasher.update(buf); | |
| 1987 | w.end = 0; | |
| 1988 | return buf.len; | |
| 1989 | } | |
| 1990 | const aux_n = try this.out.writeSplatAux(w.buffered(), data, splat); | |
| 2281 | const this: *@This() = @alignCast(@fieldParentPtr("writer", w)); | |
| 2282 | const aux = w.buffered(); | |
| 2283 | const aux_n = try this.out.writeSplatHeader(aux, data, splat); | |
| 1991 | 2284 | if (aux_n < w.end) { |
| 1992 | 2285 | this.hasher.update(w.buffer[0..aux_n]); |
| 1993 | 2286 | const remaining = w.buffer[aux_n..w.end]; |
| ... | ... | @@ -1995,29 +2288,20 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1995 | 2288 | w.end = remaining.len; |
| 1996 | 2289 | return 0; |
| 1997 | 2290 | } |
| 1998 | this.hasher.update(w.buffered()); | |
| 2291 | this.hasher.update(aux); | |
| 1999 | 2292 | const n = aux_n - w.end; |
| 2000 | 2293 | w.end = 0; |
| 2001 | 2294 | var remaining: usize = n; |
| 2002 | const short_data = data[0 .. data.len - @intFromBool(splat == 0)]; | |
| 2003 | for (short_data) |slice| { | |
| 2004 | if (remaining < slice.len) { | |
| 2295 | for (data[0 .. data.len - 1]) |slice| { | |
| 2296 | if (remaining <= slice.len) { | |
| 2005 | 2297 | this.hasher.update(slice[0..remaining]); |
| 2006 | 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 | 2303 | const pattern = data[data.len - 1]; |
| 2020 | assert(remaining == remaining_splat * pattern.len); | |
| 2304 | assert(remaining == splat * pattern.len); | |
| 2021 | 2305 | switch (pattern.len) { |
| 2022 | 2306 | 0 => { |
| 2023 | 2307 | assert(remaining == 0); |
| ... | ... | @@ -2053,12 +2337,12 @@ pub fn Hashed(comptime Hasher: type) type { |
| 2053 | 2337 | /// When using this API, it is not necessary to call `flush`. |
| 2054 | 2338 | pub const Allocating = struct { |
| 2055 | 2339 | allocator: Allocator, |
| 2056 | interface: Writer, | |
| 2340 | writer: Writer, | |
| 2057 | 2341 | |
| 2058 | 2342 | pub fn init(allocator: Allocator) Allocating { |
| 2059 | 2343 | return .{ |
| 2060 | 2344 | .allocator = allocator, |
| 2061 | .interface = .{ | |
| 2345 | .writer = .{ | |
| 2062 | 2346 | .buffer = &.{}, |
| 2063 | 2347 | .vtable = &vtable, |
| 2064 | 2348 | }, |
| ... | ... | @@ -2068,7 +2352,7 @@ pub const Allocating = struct { |
| 2068 | 2352 | pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating { |
| 2069 | 2353 | return .{ |
| 2070 | 2354 | .allocator = allocator, |
| 2071 | .interface = .{ | |
| 2355 | .writer = .{ | |
| 2072 | 2356 | .buffer = try allocator.alloc(u8, capacity), |
| 2073 | 2357 | .vtable = &vtable, |
| 2074 | 2358 | }, |
| ... | ... | @@ -2078,7 +2362,7 @@ pub const Allocating = struct { |
| 2078 | 2362 | pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating { |
| 2079 | 2363 | return .{ |
| 2080 | 2364 | .allocator = allocator, |
| 2081 | .interface = .{ | |
| 2365 | .writer = .{ | |
| 2082 | 2366 | .buffer = slice, |
| 2083 | 2367 | .vtable = &vtable, |
| 2084 | 2368 | }, |
| ... | ... | @@ -2090,7 +2374,7 @@ pub const Allocating = struct { |
| 2090 | 2374 | defer array_list.* = .empty; |
| 2091 | 2375 | return .{ |
| 2092 | 2376 | .allocator = allocator, |
| 2093 | .interface = .{ | |
| 2377 | .writer = .{ | |
| 2094 | 2378 | .vtable = &vtable, |
| 2095 | 2379 | .buffer = array_list.allocatedSlice(), |
| 2096 | 2380 | .end = array_list.items.len, |
| ... | ... | @@ -2105,14 +2389,14 @@ pub const Allocating = struct { |
| 2105 | 2389 | }; |
| 2106 | 2390 | |
| 2107 | 2391 | pub fn deinit(a: *Allocating) void { |
| 2108 | a.allocator.free(a.interface.buffer); | |
| 2392 | a.allocator.free(a.writer.buffer); | |
| 2109 | 2393 | a.* = undefined; |
| 2110 | 2394 | } |
| 2111 | 2395 | |
| 2112 | 2396 | /// Returns an array list that takes ownership of the allocated memory. |
| 2113 | 2397 | /// Resets the `Allocating` to an empty state. |
| 2114 | 2398 | pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) { |
| 2115 | const w = &a.interface; | |
| 2399 | const w = &a.writer; | |
| 2116 | 2400 | const result: std.ArrayListUnmanaged(u8) = .{ |
| 2117 | 2401 | .items = w.buffer[0..w.end], |
| 2118 | 2402 | .capacity = w.buffer.len, |
| ... | ... | @@ -2134,13 +2418,11 @@ pub const Allocating = struct { |
| 2134 | 2418 | } |
| 2135 | 2419 | |
| 2136 | 2420 | pub fn getWritten(a: *Allocating) []u8 { |
| 2137 | return a.interface.buffered(); | |
| 2421 | return a.writer.buffered(); | |
| 2138 | 2422 | } |
| 2139 | 2423 | |
| 2140 | 2424 | pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void { |
| 2141 | const shrink_by = a.interface.end - new_len; | |
| 2142 | a.interface.end = new_len; | |
| 2143 | a.interface.count -= shrink_by; | |
| 2425 | a.writer.end = new_len; | |
| 2144 | 2426 | } |
| 2145 | 2427 | |
| 2146 | 2428 | pub fn clearRetainingCapacity(a: *Allocating) void { |
| ... | ... | @@ -2148,15 +2430,18 @@ pub const Allocating = struct { |
| 2148 | 2430 | } |
| 2149 | 2431 | |
| 2150 | 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 | 2434 | const gpa = a.allocator; |
| 2153 | 2435 | const pattern = data[data.len - 1]; |
| 2154 | 2436 | const splat_len = pattern.len * splat; |
| 2155 | 2437 | var list = a.toArrayList(); |
| 2156 | 2438 | defer setArrayList(a, list); |
| 2157 | 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 | 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 | 2445 | list.appendSliceAssumeCapacity(bytes); |
| 2161 | 2446 | } |
| 2162 | 2447 | if (splat == 0) { |
| ... | ... | @@ -2171,13 +2456,13 @@ pub const Allocating = struct { |
| 2171 | 2456 | |
| 2172 | 2457 | fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize { |
| 2173 | 2458 | if (File.Handle == void) return error.Unimplemented; |
| 2174 | const a: *Allocating = @fieldParentPtr("interface", w); | |
| 2459 | const a: *Allocating = @fieldParentPtr("writer", w); | |
| 2175 | 2460 | const gpa = a.allocator; |
| 2176 | 2461 | var list = a.toArrayList(); |
| 2177 | 2462 | defer setArrayList(a, list); |
| 2178 | 2463 | const pos = file_reader.pos; |
| 2179 | 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 | 2466 | const dest = limit.slice(list.unusedCapacitySlice()); |
| 2182 | 2467 | const n = file_reader.read(dest) catch |err| switch (err) { |
| 2183 | 2468 | error.ReadFailed => return error.ReadFailed, |
| ... | ... | @@ -2188,14 +2473,14 @@ pub const Allocating = struct { |
| 2188 | 2473 | } |
| 2189 | 2474 | |
| 2190 | 2475 | fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void { |
| 2191 | a.interface.buffer = list.allocatedSlice(); | |
| 2192 | a.interface.end = list.items.len; | |
| 2476 | a.writer.buffer = list.allocatedSlice(); | |
| 2477 | a.writer.end = list.items.len; | |
| 2193 | 2478 | } |
| 2194 | 2479 | |
| 2195 | 2480 | test Allocating { |
| 2196 | var a: Allocating = .init(std.testing.allocator); | |
| 2481 | var a: Allocating = .init(testing.allocator); | |
| 2197 | 2482 | defer a.deinit(); |
| 2198 | const w = &a.interface; | |
| 2483 | const w = &a.writer; | |
| 2199 | 2484 | |
| 2200 | 2485 | const x: i32 = 42; |
| 2201 | 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 | 8 | return struct { |
| 9 | 9 | const Self = @This(); |
| 10 | 10 | pub const Error = WriterType.Error; |
| 11 | pub const Writer = io.Writer(*Self, Error, write); | |
| 11 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 12 | 12 | |
| 13 | 13 | anything_changed: bool, |
| 14 | 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 | 8 | return struct { |
| 9 | 9 | const Self = @This(); |
| 10 | 10 | pub const Error = UnderlyingWriter.Error; |
| 11 | pub const Writer = io.Writer(*Self, Error, write); | |
| 11 | pub const Writer = io.GenericWriter(*Self, Error, write); | |
| 12 | 12 | |
| 13 | 13 | underlying_writer: UnderlyingWriter, |
| 14 | 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 | 24 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 25 | 25 | defer file.close(); |
| 26 | 26 | |
| 27 | var buf_stream = io.bufferedWriter(file.writer()); | |
| 27 | var buf_stream = io.bufferedWriter(file.deprecatedWriter()); | |
| 28 | 28 | const st = buf_stream.writer(); |
| 29 | 29 | try st.print("begin", .{}); |
| 30 | 30 | try st.writeAll(data[0..]); |
| ... | ... | @@ -45,7 +45,7 @@ test "write a file, read it, then delete it" { |
| 45 | 45 | const expected_file_size: u64 = "begin".len + data.len + "end".len; |
| 46 | 46 | try expectEqual(expected_file_size, file_size); |
| 47 | 47 | |
| 48 | var buf_stream = io.bufferedReader(file.reader()); | |
| 48 | var buf_stream = io.bufferedReader(file.deprecatedReader()); | |
| 49 | 49 | const st = buf_stream.reader(); |
| 50 | 50 | const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); |
| 51 | 51 | defer std.testing.allocator.free(contents); |
| ... | ... | @@ -66,7 +66,7 @@ test "BitStreams with File Stream" { |
| 66 | 66 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 67 | 67 | defer file.close(); |
| 68 | 68 | |
| 69 | var bit_stream = io.bitWriter(native_endian, file.writer()); | |
| 69 | var bit_stream = io.bitWriter(native_endian, file.deprecatedWriter()); | |
| 70 | 70 | |
| 71 | 71 | try bit_stream.writeBits(@as(u2, 1), 1); |
| 72 | 72 | try bit_stream.writeBits(@as(u5, 2), 2); |
| ... | ... | @@ -80,7 +80,7 @@ test "BitStreams with File Stream" { |
| 80 | 80 | var file = try tmp.dir.openFile(tmp_file_name, .{}); |
| 81 | 81 | defer file.close(); |
| 82 | 82 | |
| 83 | var bit_stream = io.bitReader(native_endian, file.reader()); | |
| 83 | var bit_stream = io.bitReader(native_endian, file.deprecatedReader()); | |
| 84 | 84 | |
| 85 | 85 | var out_bits: u16 = undefined; |
| 86 | 86 |
lib/std/io/tty.zig+35-29| ... | ... | @@ -5,36 +5,9 @@ const process = std.process; |
| 5 | 5 | const windows = std.os.windows; |
| 6 | 6 | const native_os = builtin.os.tag; |
| 7 | 7 | |
| 8 | /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr). | |
| 9 | /// This includes feature checks for ANSI escape codes and the Windows console API, as well as | |
| 10 | /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default. | |
| 11 | /// Will attempt to enable ANSI escape code support if necessary/possible. | |
| 8 | /// Deprecated in favor of `Config.detect`. | |
| 12 | 9 | pub fn detectConfig(file: File) Config { |
| 13 | const force_color: ?bool = if (builtin.os.tag == .wasi) | |
| 14 | null // wasi does not support environment variables | |
| 15 | else if (process.hasNonEmptyEnvVarConstant("NO_COLOR")) | |
| 16 | false | |
| 17 | else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE")) | |
| 18 | true | |
| 19 | else | |
| 20 | null; | |
| 21 | ||
| 22 | if (force_color == false) return .no_color; | |
| 23 | ||
| 24 | if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes; | |
| 25 | ||
| 26 | if (native_os == .windows and file.isTty()) { | |
| 27 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | |
| 28 | if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) { | |
| 29 | return if (force_color == true) .escape_codes else .no_color; | |
| 30 | } | |
| 31 | return .{ .windows_api = .{ | |
| 32 | .handle = file.handle, | |
| 33 | .reset_attributes = info.wAttributes, | |
| 34 | } }; | |
| 35 | } | |
| 36 | ||
| 37 | return if (force_color == true) .escape_codes else .no_color; | |
| 10 | return .detect(file); | |
| 38 | 11 | } |
| 39 | 12 | |
| 40 | 13 | pub const Color = enum { |
| ... | ... | @@ -66,6 +39,38 @@ pub const Config = union(enum) { |
| 66 | 39 | escape_codes, |
| 67 | 40 | windows_api: if (native_os == .windows) WindowsContext else void, |
| 68 | 41 | |
| 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 | 74 | pub const WindowsContext = struct { |
| 70 | 75 | handle: File.Handle, |
| 71 | 76 | reset_attributes: u16, |
| ... | ... | @@ -123,6 +128,7 @@ pub const Config = union(enum) { |
| 123 | 128 | .dim => windows.FOREGROUND_INTENSITY, |
| 124 | 129 | .reset => ctx.reset_attributes, |
| 125 | 130 | }; |
| 131 | try w.flush(); | |
| 126 | 132 | try windows.SetConsoleTextAttribute(ctx.handle, attributes); |
| 127 | 133 | } else { |
| 128 | 134 | unreachable; |
lib/std/json.zig+2-2| ... | ... | @@ -1,12 +1,12 @@ |
| 1 | 1 | //! JSON parsing and stringification conforming to RFC 8259. https://datatracker.ietf.org/doc/html/rfc8259 |
| 2 | 2 | //! |
| 3 | 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 | 6 | //! The high-level `parseFromSlice` and `parseFromTokenSource` deserialize a JSON document into a Zig type. |
| 7 | 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 | 10 | //! The high-level `stringify` serializes a Zig or `Value` type into JSON. |
| 11 | 11 | |
| 12 | 12 | const builtin = @import("builtin"); |
lib/std/json/dynamic.zig+2-2| ... | ... | @@ -51,10 +51,10 @@ pub const Value = union(enum) { |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 53 | pub fn dump(v: Value) void { |
| 54 | const bw = std.debug.lockStderrWriter(&.{}); | |
| 54 | const w = std.debug.lockStderrWriter(&.{}); | |
| 55 | 55 | defer std.debug.unlockStderrWriter(); |
| 56 | 56 | |
| 57 | json.Stringify.value(v, .{}, bw) catch return; | |
| 57 | json.Stringify.value(v, .{}, w) catch return; | |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 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 | 251 | \\ true, |
| 252 | 252 | \\ 42, |
| 253 | 253 | \\ 43, |
| 254 | \\ 4.2e1, | |
| 254 | \\ 42, | |
| 255 | 255 | \\ "weeee", |
| 256 | 256 | \\ [ |
| 257 | 257 | \\ 1, |
lib/std/json/scanner.zig+1-1| ... | ... | @@ -219,7 +219,7 @@ pub const AllocWhen = enum { alloc_if_needed, alloc_always }; |
| 219 | 219 | /// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`. |
| 220 | 220 | pub const default_max_value_len = 4 * 1024 * 1024; |
| 221 | 221 | |
| 222 | /// Connects a `std.io.Reader` to a `std.json.Scanner`. | |
| 222 | /// Connects a `std.io.GenericReader` to a `std.json.Scanner`. | |
| 223 | 223 | /// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader. |
| 224 | 224 | pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type { |
| 225 | 225 | return struct { |
lib/std/log.zig+6-8| ... | ... | @@ -45,9 +45,8 @@ |
| 45 | 45 | //! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix; |
| 46 | 46 | //! |
| 47 | 47 | //! // Print the message to stderr, silently ignoring any errors |
| 48 | //! std.debug.lockStdErr(); | |
| 49 | //! defer std.debug.unlockStdErr(); | |
| 50 | //! const stderr = std.fs.File.stderr().writer(); | |
| 48 | //! const stderr = std.debug.lockStderrWriter(&.{}); | |
| 49 | //! defer std.debug.unlockStderrWriter(); | |
| 51 | 50 | //! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return; |
| 52 | 51 | //! } |
| 53 | 52 | //! |
| ... | ... | @@ -101,8 +100,7 @@ pub const Level = enum { |
| 101 | 100 | /// The default log level is based on build mode. |
| 102 | 101 | pub const default_level: Level = switch (builtin.mode) { |
| 103 | 102 | .Debug => .debug, |
| 104 | .ReleaseSafe => .info, | |
| 105 | .ReleaseFast, .ReleaseSmall => .err, | |
| 103 | .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info, | |
| 106 | 104 | }; |
| 107 | 105 | |
| 108 | 106 | const level = std.options.log_level; |
| ... | ... | @@ -148,10 +146,10 @@ pub fn defaultLog( |
| 148 | 146 | ) void { |
| 149 | 147 | const level_txt = comptime message_level.asText(); |
| 150 | 148 | const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; |
| 151 | var buffer: [1024]u8 = undefined; | |
| 152 | const bw = std.debug.lockStderrWriter(&buffer); | |
| 149 | var buffer: [32]u8 = undefined; | |
| 150 | const stderr = std.debug.lockStderrWriter(&buffer); | |
| 153 | 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 | } |
| 156 | 154 | |
| 157 | 155 | /// 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 | 2028 | pub fn normalize(r: *Mutable, length: usize) void { |
| 2029 | 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 | }; |
| 2032 | 2040 | |
| 2033 | 2041 | /// A arbitrary-precision big integer, with a fixed set of immutable limbs. |
| ... | ... | @@ -2317,46 +2325,25 @@ pub const Const = struct { |
| 2317 | 2325 | return .{ normalized_res.reconstruct(if (self.positive) .positive else .negative), exactness }; |
| 2318 | 2326 | } |
| 2319 | 2327 | |
| 2320 | /// To allow `std.fmt.format` to work with this type. | |
| 2321 | 2328 | /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, |
| 2322 | 2329 | /// this function will fail to print the string, printing "(BigInt)" instead of a number. |
| 2323 | 2330 | /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. |
| 2324 | 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 { | |
| 2326 | comptime var base = 10; | |
| 2327 | comptime var case: std.fmt.Case = .lower; | |
| 2328 | ||
| 2329 | if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) { | |
| 2330 | base = 10; | |
| 2331 | case = .lower; | |
| 2332 | } else if (comptime mem.eql(u8, fmt, "b")) { | |
| 2333 | base = 2; | |
| 2334 | case = .lower; | |
| 2335 | } else if (comptime mem.eql(u8, fmt, "x")) { | |
| 2336 | base = 16; | |
| 2337 | case = .lower; | |
| 2338 | } else if (comptime mem.eql(u8, fmt, "X")) { | |
| 2339 | base = 16; | |
| 2340 | case = .upper; | |
| 2341 | } else { | |
| 2342 | std.fmt.invalidFmtError(fmt, self); | |
| 2343 | } | |
| 2332 | pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void { | |
| 2333 | const available_len = 64; | |
| 2334 | if (self.limbs.len > available_len) | |
| 2335 | return w.writeAll("(BigInt)"); | |
| 2344 | 2336 | |
| 2345 | const max_str_len = self.sizeInBaseUpperBound(base); | |
| 2346 | const limbs_len = calcToStringLimbsBufferLen(self.limbs.len, base); | |
| 2347 | if (bw.writableSliceGreedy(max_str_len + @alignOf(Limb) - 1 + @sizeOf(Limb) * limbs_len)) |buf| { | |
| 2348 | const limbs: [*]Limb = @alignCast(@ptrCast(std.mem.alignPointer(buf[max_str_len..].ptr, @alignOf(Limb)))); | |
| 2349 | bw.advance(self.toString(buf[0..max_str_len], base, case, limbs[0..limbs_len])); | |
| 2350 | return; | |
| 2351 | } else |_| if (bw.writableSliceGreedy(max_str_len)) |buf| { | |
| 2352 | const available_len = 64; | |
| 2353 | var limbs: [calcToStringLimbsBufferLen(available_len, base)]Limb = undefined; | |
| 2354 | if (limbs.len >= limbs_len) { | |
| 2355 | bw.advance(self.toString(buf, base, case, &limbs)); | |
| 2356 | return; | |
| 2357 | } | |
| 2358 | } else |_| {} | |
| 2359 | try bw.writeAll("(BigInt)"); | |
| 2337 | var limbs: [calcToStringLimbsBufferLen(available_len, 10)]Limb = undefined; | |
| 2338 | ||
| 2339 | const biggest: Const = .{ | |
| 2340 | .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len), | |
| 2341 | .positive = false, | |
| 2342 | }; | |
| 2343 | var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined; | |
| 2344 | const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form"); | |
| 2345 | const len = self.toString(&buf, base, number.case, &limbs); | |
| 2346 | return w.writeAll(buf[0..len]); | |
| 2360 | 2347 | } |
| 2361 | 2348 | |
| 2362 | 2349 | /// Converts self to a string in the requested base. |
| ... | ... | @@ -2926,17 +2913,16 @@ pub const Managed = struct { |
| 2926 | 2913 | } |
| 2927 | 2914 | |
| 2928 | 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 | 2920 | /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, |
| 2930 | 2921 | /// this function will fail to print the string, printing "(BigInt)" instead of a number. |
| 2931 | 2922 | /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. |
| 2932 | 2923 | /// See `toString` and `toStringAlloc` for a way to print big integers without failure. |
| 2933 | pub fn format( | |
| 2934 | self: Managed, | |
| 2935 | comptime fmt: []const u8, | |
| 2936 | options: std.fmt.FormatOptions, | |
| 2937 | out_stream: anytype, | |
| 2938 | ) !void { | |
| 2939 | return self.toConst().format(fmt, options, out_stream); | |
| 2924 | pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void { | |
| 2925 | return self.toConst().formatNumber(w, n); | |
| 2940 | 2926 | } |
| 2941 | 2927 | |
| 2942 | 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 | 3813 | try a.pow(&a, 64 * @sizeOf(Limb) * 8); |
| 3814 | 3814 | try b.sub(&a, &c); |
| 3815 | 3815 | |
| 3816 | const a_fmt = try std.fmt.allocPrintZ(testing.allocator, "{d}", .{a}); | |
| 3817 | defer testing.allocator.free(a_fmt); | |
| 3816 | try testing.expectFmt("(BigInt)", "{d}", .{a}); | |
| 3818 | 3817 | |
| 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 | 3819 | defer testing.allocator.free(b_fmt); |
| 3821 | ||
| 3822 | try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); | |
| 3823 | 3820 | try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)")); |
| 3824 | 3821 | } |
| 3825 | 3822 | |
| ... | ... | @@ -3838,10 +3835,10 @@ test "(BigInt) negative" { |
| 3838 | 3835 | a.negate(); |
| 3839 | 3836 | try b.add(&a, &c); |
| 3840 | 3837 | |
| 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 | 3839 | defer testing.allocator.free(a_fmt); |
| 3843 | 3840 | |
| 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 | 3842 | defer testing.allocator.free(b_fmt); |
| 3846 | 3843 | |
| 3847 | 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 | 1714 | } |
| 1715 | 1715 | }, |
| 1716 | 1716 | } |
| 1717 | return @as(ReturnType, @truncate(result)); | |
| 1717 | return @truncate(result); | |
| 1718 | 1718 | } |
| 1719 | 1719 | |
| 1720 | 1720 | test readVarInt { |
lib/std/mem/Allocator.zig+1-1| ... | ... | @@ -253,7 +253,7 @@ pub inline fn allocAdvancedWithRetAddr( |
| 253 | 253 | n: usize, |
| 254 | 254 | return_address: usize, |
| 255 | 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 | 257 | const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address)); |
| 258 | 258 | return ptr[0..n]; |
| 259 | 259 | } |
lib/std/multi_array_list.zig+1| ... | ... | @@ -991,6 +991,7 @@ test "0 sized struct" { |
| 991 | 991 | test "struct with many fields" { |
| 992 | 992 | const ManyFields = struct { |
| 993 | 993 | fn Type(count: comptime_int) type { |
| 994 | @setEvalBranchQuota(50000); | |
| 994 | 995 | var fields: [count]std.builtin.Type.StructField = undefined; |
| 995 | 996 | for (0..count) |i| { |
| 996 | 997 | fields[i] = .{ |
lib/std/net.zig+16-45| ... | ... | @@ -164,22 +164,13 @@ pub const Address = extern union { |
| 164 | 164 | } |
| 165 | 165 | } |
| 166 | 166 | |
| 167 | pub fn format( | |
| 168 | self: Address, | |
| 169 | comptime fmt: []const u8, | |
| 170 | options: std.fmt.FormatOptions, | |
| 171 | out_stream: anytype, | |
| 172 | ) !void { | |
| 173 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); | |
| 167 | pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 174 | 168 | switch (self.any.family) { |
| 175 | posix.AF.INET => try self.in.format(fmt, options, out_stream), | |
| 176 | posix.AF.INET6 => try self.in6.format(fmt, options, out_stream), | |
| 169 | posix.AF.INET => try self.in.format(w), | |
| 170 | posix.AF.INET6 => try self.in6.format(w), | |
| 177 | 171 | posix.AF.UNIX => { |
| 178 | if (!has_unix_sockets) { | |
| 179 | unreachable; | |
| 180 | } | |
| 181 | ||
| 182 | try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)}); | |
| 172 | if (!has_unix_sockets) unreachable; | |
| 173 | try w.writeAll(std.mem.sliceTo(&self.un.path, 0)); | |
| 183 | 174 | }, |
| 184 | 175 | else => unreachable, |
| 185 | 176 | } |
| ... | ... | @@ -352,22 +343,9 @@ pub const Ip4Address = extern struct { |
| 352 | 343 | self.sa.port = mem.nativeToBig(u16, port); |
| 353 | 344 | } |
| 354 | 345 | |
| 355 | pub fn format( | |
| 356 | self: Ip4Address, | |
| 357 | comptime fmt: []const u8, | |
| 358 | options: std.fmt.FormatOptions, | |
| 359 | out_stream: anytype, | |
| 360 | ) !void { | |
| 361 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); | |
| 362 | _ = options; | |
| 363 | const bytes = @as(*const [4]u8, @ptrCast(&self.sa.addr)); | |
| 364 | try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{ | |
| 365 | bytes[0], | |
| 366 | bytes[1], | |
| 367 | bytes[2], | |
| 368 | bytes[3], | |
| 369 | self.getPort(), | |
| 370 | }); | |
| 346 | pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 347 | const bytes: *const [4]u8 = @ptrCast(&self.sa.addr); | |
| 348 | try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() }); | |
| 371 | 349 | } |
| 372 | 350 | |
| 373 | 351 | pub fn getOsSockLen(self: Ip4Address) posix.socklen_t { |
| ... | ... | @@ -656,17 +634,10 @@ pub const Ip6Address = extern struct { |
| 656 | 634 | self.sa.port = mem.nativeToBig(u16, port); |
| 657 | 635 | } |
| 658 | 636 | |
| 659 | pub fn format( | |
| 660 | self: Ip6Address, | |
| 661 | comptime fmt: []const u8, | |
| 662 | options: std.fmt.FormatOptions, | |
| 663 | out_stream: anytype, | |
| 664 | ) !void { | |
| 665 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); | |
| 666 | _ = options; | |
| 637 | pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 667 | 638 | const port = mem.bigToNative(u16, self.sa.port); |
| 668 | 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 | 641 | self.sa.addr[12], |
| 671 | 642 | self.sa.addr[13], |
| 672 | 643 | self.sa.addr[14], |
| ... | ... | @@ -714,14 +685,14 @@ pub const Ip6Address = extern struct { |
| 714 | 685 | longest_len = 0; |
| 715 | 686 | } |
| 716 | 687 | |
| 717 | try out_stream.writeAll("["); | |
| 688 | try w.writeAll("["); | |
| 718 | 689 | var i: usize = 0; |
| 719 | 690 | var abbrv = false; |
| 720 | 691 | while (i < native_endian_parts.len) : (i += 1) { |
| 721 | 692 | if (i == longest_start) { |
| 722 | 693 | // Emit "::" for the longest zero run |
| 723 | 694 | if (!abbrv) { |
| 724 | try out_stream.writeAll(if (i == 0) "::" else ":"); | |
| 695 | try w.writeAll(if (i == 0) "::" else ":"); | |
| 725 | 696 | abbrv = true; |
| 726 | 697 | } |
| 727 | 698 | i += longest_len - 1; // Skip the compressed range |
| ... | ... | @@ -730,12 +701,12 @@ pub const Ip6Address = extern struct { |
| 730 | 701 | if (abbrv) { |
| 731 | 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 | 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 | } |
| 740 | 711 | |
| 741 | 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 | 869 | const name_c = try gpa.dupeZ(u8, name); |
| 899 | 870 | defer gpa.free(name_c); |
| 900 | 871 | |
| 901 | const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port}); | |
| 872 | const port_c = try std.fmt.allocPrintSentinel(gpa, "{}", .{port}, 0); | |
| 902 | 873 | defer gpa.free(port_c); |
| 903 | 874 | |
| 904 | 875 | const ws2_32 = windows.ws2_32; |
lib/std/net/test.zig+16-53| ... | ... | @@ -5,20 +5,13 @@ const mem = std.mem; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | |
| 7 | 7 | test "parse and render IP addresses at comptime" { |
| 8 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 9 | 8 | comptime { |
| 10 | var ipAddrBuffer: [16]u8 = undefined; | |
| 11 | // Parses IPv6 at comptime | |
| 12 | 9 | const ipv6addr = net.Address.parseIp("::1", 0) catch unreachable; |
| 13 | var ipv6 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv6addr}) catch unreachable; | |
| 14 | try std.testing.expect(std.mem.eql(u8, "::1", ipv6[1 .. ipv6.len - 3])); | |
| 10 | try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr}); | |
| 15 | 11 | |
| 16 | // Parses IPv4 at comptime | |
| 17 | 12 | const ipv4addr = net.Address.parseIp("127.0.0.1", 0) catch unreachable; |
| 18 | var ipv4 = std.fmt.bufPrint(ipAddrBuffer[0..], "{}", .{ipv4addr}) catch unreachable; | |
| 19 | try std.testing.expect(std.mem.eql(u8, "127.0.0.1", ipv4[0 .. ipv4.len - 2])); | |
| 13 | try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr}); | |
| 20 | 14 | |
| 21 | // Returns error for invalid IP addresses at comptime | |
| 22 | 15 | try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("::123.123.123.123", 0)); |
| 23 | 16 | try testing.expectError(error.InvalidIPAddressFormat, net.Address.parseIp("127.01.0.1", 0)); |
| 24 | 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 | 20 | } |
| 28 | 21 | |
| 29 | 22 | test "format IPv6 address with no zero runs" { |
| 30 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 31 | ||
| 32 | 23 | const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0); |
| 33 | ||
| 34 | var buffer: [50]u8 = undefined; | |
| 35 | const result = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; | |
| 36 | ||
| 37 | try std.testing.expectEqualStrings("[2001:db8:1:2:3:4:5:6]:0", result); | |
| 24 | try std.testing.expectFmt("[2001:db8:1:2:3:4:5:6]:0", "{f}", .{addr}); | |
| 38 | 25 | } |
| 39 | 26 | |
| 40 | 27 | test "parse IPv6 addresses and check compressed form" { |
| 41 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 42 | ||
| 43 | const alloc = testing.allocator; | |
| 44 | ||
| 45 | // 1) Parse an IPv6 address that should compress to [2001:db8::1:0:0:2]:0 | |
| 46 | const addr1 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0); | |
| 47 | ||
| 48 | // 2) Parse an IPv6 address that should compress to [2001:db8::1:2]:0 | |
| 49 | const addr2 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0); | |
| 50 | ||
| 51 | // 3) Parse an IPv6 address that should compress to [2001:db8:1:0:1::2]:0 | |
| 52 | const addr3 = try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0); | |
| 53 | ||
| 54 | // Print each address in Zig's default "[ipv6]:port" form. | |
| 55 | const printed1 = try std.fmt.allocPrint(alloc, "{any}", .{addr1}); | |
| 56 | defer testing.allocator.free(printed1); | |
| 57 | const printed2 = try std.fmt.allocPrint(alloc, "{any}", .{addr2}); | |
| 58 | defer testing.allocator.free(printed2); | |
| 59 | const printed3 = try std.fmt.allocPrint(alloc, "{any}", .{addr3}); | |
| 60 | defer testing.allocator.free(printed3); | |
| 61 | ||
| 62 | // Check the exact compressed forms we expect. | |
| 63 | try std.testing.expectEqualStrings("[2001:db8::1:0:0:2]:0", printed1); | |
| 64 | try std.testing.expectEqualStrings("[2001:db8::1:2]:0", printed2); | |
| 65 | try std.testing.expectEqualStrings("[2001:db8:1:0:1::2]:0", printed3); | |
| 28 | try std.testing.expectFmt("[2001:db8::1:0:0:2]:0", "{f}", .{ | |
| 29 | try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0), | |
| 30 | }); | |
| 31 | try std.testing.expectFmt("[2001:db8::1:2]:0", "{f}", .{ | |
| 32 | try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0), | |
| 33 | }); | |
| 34 | try std.testing.expectFmt("[2001:db8:1:0:1::2]:0", "{f}", .{ | |
| 35 | try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0), | |
| 36 | }); | |
| 66 | 37 | } |
| 67 | 38 | |
| 68 | 39 | test "parse IPv6 address, check raw bytes" { |
| 69 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 70 | ||
| 71 | 40 | const expected_raw: [16]u8 = .{ |
| 72 | 41 | 0x20, 0x01, 0x0d, 0xb8, // 2001:db8 |
| 73 | 42 | 0x00, 0x00, 0x00, 0x00, // :0000:0000 |
| ... | ... | @@ -82,8 +51,6 @@ test "parse IPv6 address, check raw bytes" { |
| 82 | 51 | } |
| 83 | 52 | |
| 84 | 53 | test "parse and render IPv6 addresses" { |
| 85 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 86 | ||
| 87 | 54 | var buffer: [100]u8 = undefined; |
| 88 | 55 | const ips = [_][]const u8{ |
| 89 | 56 | "FF01:0:0:0:0:0:0:FB", |
| ... | ... | @@ -111,12 +78,12 @@ test "parse and render IPv6 addresses" { |
| 111 | 78 | }; |
| 112 | 79 | for (ips, 0..) |ip, i| { |
| 113 | 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 | 82 | try std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); |
| 116 | 83 | |
| 117 | 84 | if (builtin.os.tag == .linux) { |
| 118 | 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 | 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 | 115 | } |
| 149 | 116 | |
| 150 | 117 | test "parse and render IPv4 addresses" { |
| 151 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 152 | ||
| 153 | 118 | var buffer: [18]u8 = undefined; |
| 154 | 119 | for ([_][]const u8{ |
| 155 | 120 | "0.0.0.0", |
| ... | ... | @@ -159,7 +124,7 @@ test "parse and render IPv4 addresses" { |
| 159 | 124 | "127.0.0.1", |
| 160 | 125 | }) |ip| { |
| 161 | 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 | 128 | try std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); |
| 164 | 129 | } |
| 165 | 130 | |
| ... | ... | @@ -175,10 +140,8 @@ test "parse and render UNIX addresses" { |
| 175 | 140 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 176 | 141 | if (!net.has_unix_sockets) return error.SkipZigTest; |
| 177 | 142 | |
| 178 | var buffer: [14]u8 = undefined; | |
| 179 | 143 | const addr = net.Address.initUnix("/tmp/testpath") catch unreachable; |
| 180 | const fmt_addr = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; | |
| 181 | try std.testing.expectEqualSlices(u8, "/tmp/testpath", fmt_addr); | |
| 144 | try std.testing.expectFmt("/tmp/testpath", "{f}", .{addr}); | |
| 182 | 145 | |
| 183 | 146 | const too_long = [_]u8{'a'} ** 200; |
| 184 | 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 | 4 | const unexpectedErrno = std.posix.unexpectedErrno; |
| 5 | 5 | const errno = std.posix.errno; |
| 6 | 6 | |
| 7 | pub const CopyFileRangeError = error{ | |
| 7 | pub const CopyFileRangeError = std.posix.UnexpectedError || error{ | |
| 8 | 8 | /// If infd is not open for reading or outfd is not open for writing, or |
| 9 | 9 | /// opened for writing with O_APPEND, or if infd and outfd refer to the |
| 10 | 10 | /// same file. |
lib/std/os/uefi.zig+14-23| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | |
| 2 | 3 | |
| 3 | 4 | /// A protocol is an interface identified by a GUID. |
| 4 | 5 | pub const protocol = @import("uefi/protocol.zig"); |
| ... | ... | @@ -59,29 +60,19 @@ pub const Guid = extern struct { |
| 59 | 60 | node: [6]u8, |
| 60 | 61 | |
| 61 | 62 | /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format |
| 62 | pub fn format( | |
| 63 | self: @This(), | |
| 64 | comptime f: []const u8, | |
| 65 | options: std.fmt.FormatOptions, | |
| 66 | writer: anytype, | |
| 67 | ) !void { | |
| 68 | _ = options; | |
| 69 | if (f.len == 0) { | |
| 70 | const time_low = @byteSwap(self.time_low); | |
| 71 | const time_mid = @byteSwap(self.time_mid); | |
| 72 | const time_high_and_version = @byteSwap(self.time_high_and_version); | |
| 73 | ||
| 74 | return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ | |
| 75 | std.mem.asBytes(&time_low), | |
| 76 | std.mem.asBytes(&time_mid), | |
| 77 | std.mem.asBytes(&time_high_and_version), | |
| 78 | std.mem.asBytes(&self.clock_seq_high_and_reserved), | |
| 79 | std.mem.asBytes(&self.clock_seq_low), | |
| 80 | std.mem.asBytes(&self.node), | |
| 81 | }); | |
| 82 | } else { | |
| 83 | std.fmt.invalidFmtError(f, self); | |
| 84 | } | |
| 63 | pub fn format(self: @This(), writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 64 | const time_low = @byteSwap(self.time_low); | |
| 65 | const time_mid = @byteSwap(self.time_mid); | |
| 66 | const time_high_and_version = @byteSwap(self.time_high_and_version); | |
| 67 | ||
| 68 | return writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ | |
| 69 | std.mem.asBytes(&time_low), | |
| 70 | std.mem.asBytes(&time_mid), | |
| 71 | std.mem.asBytes(&time_high_and_version), | |
| 72 | std.mem.asBytes(&self.clock_seq_high_and_reserved), | |
| 73 | std.mem.asBytes(&self.clock_seq_low), | |
| 74 | std.mem.asBytes(&self.node), | |
| 75 | }); | |
| 85 | 76 | } |
| 86 | 77 | |
| 87 | 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 | 2812 | buf_wstr.len, |
| 2813 | 2813 | null, |
| 2814 | 2814 | ); |
| 2815 | std.debug.print("error.Unexpected: GetLastError({}): {}\n", .{ | |
| 2816 | @intFromEnum(err), | |
| 2817 | std.unicode.fmtUtf16Le(buf_wstr[0..len]), | |
| 2815 | std.debug.print("error.Unexpected: GetLastError({d}): {f}\n", .{ | |
| 2816 | err, std.unicode.fmtUtf16Le(buf_wstr[0..len]), | |
| 2818 | 2817 | }); |
| 2819 | 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 | 30 | const expected_path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(expected_path); |
| 31 | 31 | const actual_path = try windows.wToPrefixedFileW(null, path_utf16); |
| 32 | 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 | 34 | return e; |
| 35 | 35 | }; |
| 36 | 36 | } |
| ... | ... | @@ -48,7 +48,7 @@ fn testToPrefixedFileOnlyOracle(comptime path: []const u8) !void { |
| 48 | 48 | const zig_result = try windows.wToPrefixedFileW(null, path_utf16); |
| 49 | 49 | const win32_api_result = try RtlDosPathNameToNtPathName_U(path_utf16); |
| 50 | 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 | 52 | return e; |
| 53 | 53 | }; |
| 54 | 54 | } |
lib/std/posix.zig+3-2| ... | ... | @@ -651,7 +651,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void { |
| 651 | 651 | } |
| 652 | 652 | |
| 653 | 653 | const file: fs.File = .{ .handle = fd }; |
| 654 | const stream = file.reader(); | |
| 654 | const stream = file.deprecatedReader(); | |
| 655 | 655 | stream.readNoEof(buf) catch return error.Unexpected; |
| 656 | 656 | } |
| 657 | 657 | |
| ... | ... | @@ -3936,6 +3936,7 @@ pub fn accept( |
| 3936 | 3936 | .WSANOTINITIALISED => unreachable, // not initialized WSA |
| 3937 | 3937 | .WSAECONNRESET => return error.ConnectionResetByPeer, |
| 3938 | 3938 | .WSAEFAULT => unreachable, |
| 3939 | .WSAENOTSOCK => return error.FileDescriptorNotASocket, | |
| 3939 | 3940 | .WSAEINVAL => return error.SocketNotListening, |
| 3940 | 3941 | .WSAEMFILE => return error.ProcessFdQuotaExceeded, |
| 3941 | 3942 | .WSAENETDOWN => return error.NetworkSubsystemFailed, |
| ... | ... | @@ -4335,7 +4336,7 @@ pub const GetSockOptError = error{ |
| 4335 | 4336 | } || UnexpectedError; |
| 4336 | 4337 | |
| 4337 | 4338 | pub 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 | 4340 | switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) { |
| 4340 | 4341 | .SUCCESS => { |
| 4341 | 4342 | std.debug.assert(len == opt.len); |
lib/std/posix/test.zig+1-1| ... | ... | @@ -667,7 +667,7 @@ test "mmap" { |
| 667 | 667 | const file = try tmp.dir.createFile(test_out_file, .{}); |
| 668 | 668 | defer file.close(); |
| 669 | 669 | |
| 670 | const stream = file.writer(); | |
| 670 | const stream = file.deprecatedWriter(); | |
| 671 | 671 | |
| 672 | 672 | var i: u32 = 0; |
| 673 | 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 | 1553 | const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); |
| 1554 | 1554 | defer file.close(); |
| 1555 | 1555 | |
| 1556 | const reader = file.reader(); | |
| 1556 | const reader = file.deprecatedReader(); | |
| 1557 | 1557 | |
| 1558 | 1558 | const State = enum { |
| 1559 | 1559 | Start, |
lib/std/start.zig+2-2| ... | ... | @@ -486,7 +486,7 @@ fn _start() callconv(.naked) noreturn { |
| 486 | 486 | |
| 487 | 487 | fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 488 | 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"); | |
| 490 | 490 | |
| 491 | 491 | if (!builtin.single_threaded and !builtin.link_libc) { |
| 492 | 492 | _ = @import("os/windows/tls.zig"); |
| ... | ... | @@ -499,7 +499,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 499 | 499 | |
| 500 | 500 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 501 | 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"); | |
| 503 | 503 | |
| 504 | 504 | if (!builtin.single_threaded and !builtin.link_libc) { |
| 505 | 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 | 103 | .error_set, |
| 104 | 104 | => { |
| 105 | 105 | if (actual != expected) { |
| 106 | print("expected {}, found {}\n", .{ expected, actual }); | |
| 106 | print("expected {any}, found {any}\n", .{ expected, actual }); | |
| 107 | 107 | return error.TestExpectedEqual; |
| 108 | 108 | } |
| 109 | 109 | }, |
| ... | ... | @@ -265,9 +265,13 @@ test "expectEqual null" { |
| 265 | 265 | |
| 266 | 266 | /// This function is intended to be used only in tests. When the formatted result of the template |
| 267 | 267 | /// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how |
| 268 | /// they are not equal, then returns an error. It depends on `expectEqualStrings()` for printing | |
| 268 | /// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing | |
| 269 | 269 | /// diagnostics. |
| 270 | 270 | pub 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 | 275 | const actual = try std.fmt.allocPrint(allocator, template, args); |
| 272 | 276 | defer allocator.free(actual); |
| 273 | 277 | return expectEqualStrings(expected, actual); |
| ... | ... | @@ -354,9 +358,6 @@ test expectApproxEqRel { |
| 354 | 358 | /// The colorized output is optional and controlled by the return of `std.io.tty.detectConfig()`. |
| 355 | 359 | /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. |
| 356 | 360 | pub 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 | 361 | const diff_index: usize = diff_index: { |
| 361 | 362 | const shortest = @min(expected.len, actual.len); |
| 362 | 363 | var index: usize = 0; |
| ... | ... | @@ -365,12 +366,21 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const |
| 365 | 366 | } |
| 366 | 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 | } | |
| 368 | 375 | |
| 369 | if (!backend_can_print) { | |
| 370 | return error.TestExpectedEqual; | |
| 371 | } | |
| 372 | ||
| 373 | print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); | |
| 376 | fn failEqualSlices( | |
| 377 | comptime T: type, | |
| 378 | expected: []const T, | |
| 379 | actual: []const T, | |
| 380 | diff_index: usize, | |
| 381 | w: *std.io.Writer, | |
| 382 | ) !void { | |
| 383 | try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); | |
| 374 | 384 | |
| 375 | 385 | // TODO: Should this be configurable by the caller? |
| 376 | 386 | const max_lines: usize = 16; |
| ... | ... | @@ -388,8 +398,6 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const |
| 388 | 398 | const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)]; |
| 389 | 399 | const actual_truncated = window_start + actual_window.len < actual.len; |
| 390 | 400 | |
| 391 | const bw = std.debug.lockStderrWriter(&.{}); | |
| 392 | defer std.debug.unlockStderrWriter(); | |
| 393 | 401 | const ttyconf = std.io.tty.detectConfig(.stderr()); |
| 394 | 402 | var differ = if (T == u8) BytesDiffer{ |
| 395 | 403 | .expected = expected_window, |
| ... | ... | @@ -406,47 +414,47 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const |
| 406 | 414 | // that is usually useful. |
| 407 | 415 | const index_fmt = if (T == u8) "0x{X}" else "{}"; |
| 408 | 416 | |
| 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 | 418 | if (window_start > 0) { |
| 411 | 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 | 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 | 426 | if (expected_truncated) { |
| 419 | 427 | const end_offset = window_start + expected_window.len; |
| 420 | 428 | const num_missing_items = expected.len - (window_start + expected_window.len); |
| 421 | 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 | 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 | } |
| 427 | 435 | |
| 428 | 436 | // now reverse expected/actual and print again |
| 429 | 437 | differ.expected = actual_window; |
| 430 | 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 | 440 | if (window_start > 0) { |
| 433 | 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 | 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 | 448 | if (actual_truncated) { |
| 441 | 449 | const end_offset = window_start + actual_window.len; |
| 442 | 450 | const num_missing_items = actual.len - (window_start + actual_window.len); |
| 443 | 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 | 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", .{}); | |
| 450 | 458 | |
| 451 | 459 | return error.TestExpectedEqual; |
| 452 | 460 | } |
| ... | ... | @@ -460,17 +468,17 @@ fn SliceDiffer(comptime T: type) type { |
| 460 | 468 | |
| 461 | 469 | const Self = @This(); |
| 462 | 470 | |
| 463 | pub fn write(self: Self, bw: *Writer) !void { | |
| 471 | pub fn write(self: Self, writer: *std.io.Writer) !void { | |
| 464 | 472 | for (self.expected, 0..) |value, i| { |
| 465 | 473 | const full_index = self.start_index + i; |
| 466 | 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 | 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 | 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 | 489 | actual: []const u8, |
| 482 | 490 | ttyconf: std.io.tty.Config, |
| 483 | 491 | |
| 484 | pub fn write(self: BytesDiffer, bw: *Writer) !void { | |
| 492 | pub fn write(self: BytesDiffer, writer: *std.io.Writer) !void { | |
| 485 | 493 | var expected_iterator = std.mem.window(u8, self.expected, 16, 16); |
| 486 | 494 | var row: usize = 0; |
| 487 | 495 | while (expected_iterator.next()) |chunk| { |
| ... | ... | @@ -491,23 +499,23 @@ const BytesDiffer = struct { |
| 491 | 499 | const absolute_byte_index = col + row * 16; |
| 492 | 500 | const diff = if (absolute_byte_index < self.actual.len) self.actual[absolute_byte_index] != byte else true; |
| 493 | 501 | if (diff) diffs.set(col); |
| 494 | try self.writeDiff(bw, "{X:0>2} ", .{byte}, diff); | |
| 495 | if (col == 7) try bw.writeByte(' '); | |
| 502 | try self.writeDiff(writer, "{X:0>2} ", .{byte}, diff); | |
| 503 | if (col == 7) try writer.writeByte(' '); | |
| 496 | 504 | } |
| 497 | try bw.writeByte(' '); | |
| 505 | try writer.writeByte(' '); | |
| 498 | 506 | if (chunk.len < 16) { |
| 499 | 507 | var missing_columns = (16 - chunk.len) * 3; |
| 500 | 508 | if (chunk.len < 8) missing_columns += 1; |
| 501 | try bw.splatByteAll(' ', missing_columns); | |
| 509 | try writer.splatByteAll(' ', missing_columns); | |
| 502 | 510 | } |
| 503 | 511 | for (chunk, 0..) |byte, col| { |
| 504 | 512 | const diff = diffs.isSet(col); |
| 505 | 513 | if (std.ascii.isPrint(byte)) { |
| 506 | try self.writeDiff(bw, "{c}", .{byte}, diff); | |
| 514 | try self.writeDiff(writer, "{c}", .{byte}, diff); | |
| 507 | 515 | } else { |
| 508 | 516 | // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed |
| 509 | 517 | if (self.ttyconf == .windows_api) { |
| 510 | try self.writeDiff(bw, ".", .{}, diff); | |
| 518 | try self.writeDiff(writer, ".", .{}, diff); | |
| 511 | 519 | continue; |
| 512 | 520 | } |
| 513 | 521 | |
| ... | ... | @@ -515,22 +523,22 @@ const BytesDiffer = struct { |
| 515 | 523 | // We don't want to do this for all control codes because most control codes apart from |
| 516 | 524 | // the ones that Zig has escape sequences for are likely not very useful to print as symbols. |
| 517 | 525 | switch (byte) { |
| 518 | '\n' => try self.writeDiff(bw, "␊", .{}, diff), | |
| 519 | '\r' => try self.writeDiff(bw, "␍", .{}, diff), | |
| 520 | '\t' => try self.writeDiff(bw, "␉", .{}, diff), | |
| 521 | else => try self.writeDiff(bw, ".", .{}, diff), | |
| 526 | '\n' => try self.writeDiff(writer, "␊", .{}, diff), | |
| 527 | '\r' => try self.writeDiff(writer, "␍", .{}, diff), | |
| 528 | '\t' => try self.writeDiff(writer, "␉", .{}, diff), | |
| 529 | else => try self.writeDiff(writer, ".", .{}, diff), | |
| 522 | 530 | } |
| 523 | 531 | } |
| 524 | 532 | } |
| 525 | try bw.writeByte('\n'); | |
| 533 | try writer.writeByte('\n'); | |
| 526 | 534 | row += 1; |
| 527 | 535 | } |
| 528 | 536 | } |
| 529 | 537 | |
| 530 | fn writeDiff(self: BytesDiffer, bw: *Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void { | |
| 531 | if (diff) try self.ttyconf.setColor(bw, .red); | |
| 532 | try bw.print(fmt, args); | |
| 533 | if (diff) try self.ttyconf.setColor(bw, .reset); | |
| 538 | fn writeDiff(self: BytesDiffer, writer: *std.io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void { | |
| 539 | if (diff) try self.ttyconf.setColor(writer, .red); | |
| 540 | try writer.print(fmt, args); | |
| 541 | if (diff) try self.ttyconf.setColor(writer, .reset); | |
| 534 | 542 | } |
| 535 | 543 | }; |
| 536 | 544 | |
| ... | ... | @@ -641,6 +649,11 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir { |
| 641 | 649 | |
| 642 | 650 | pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void { |
| 643 | 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 | 657 | print("\n====== expected this output: =========\n", .{}); |
| 645 | 658 | printWithVisibleNewlines(expected); |
| 646 | 659 | print("\n======== instead found this: =========\n", .{}); |
| ... | ... | @@ -1112,7 +1125,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime |
| 1112 | 1125 | const arg_i_str = comptime str: { |
| 1113 | 1126 | var str_buf: [100]u8 = undefined; |
| 1114 | 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 | 1129 | break :str str_buf[0..str_len]; |
| 1117 | 1130 | }; |
| 1118 | 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 | 1155 | error.OutOfMemory => { |
| 1143 | 1156 | if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { |
| 1144 | 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 | 1160 | fail_index, |
| 1148 | 1161 | needed_alloc_count, |
| ... | ... | @@ -1196,3 +1209,43 @@ pub inline fn fuzz( |
| 1196 | 1209 | ) anyerror!void { |
| 1197 | 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`. | |
| 1214 | pub 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 | 9 | /// |
| 10 | 10 | /// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character |
| 11 | 11 | pub const replacement_character: u21 = 0xFFFD; |
| 12 | pub const replacement_character_utf8: [3]u8 = utf8EncodeComptime(replacement_character); | |
| 12 | 13 | |
| 13 | 14 | /// Returns how many bytes the UTF-8 representation would require |
| 14 | 15 | /// for the given codepoint. |
| ... | ... | @@ -802,14 +803,7 @@ fn testDecode(bytes: []const u8) !u21 { |
| 802 | 803 | /// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD) |
| 803 | 804 | /// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of |
| 804 | 805 | /// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder |
| 805 | fn formatUtf8( | |
| 806 | utf8: []const u8, | |
| 807 | comptime fmt: []const u8, | |
| 808 | options: std.fmt.FormatOptions, | |
| 809 | writer: anytype, | |
| 810 | ) !void { | |
| 811 | _ = fmt; | |
| 812 | _ = options; | |
| 806 | fn formatUtf8(utf8: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 813 | 807 | var buf: [300]u8 = undefined; // just an arbitrary size |
| 814 | 808 | var u8len: usize = 0; |
| 815 | 809 | |
| ... | ... | @@ -898,27 +892,27 @@ fn formatUtf8( |
| 898 | 892 | /// Ill-formed UTF-8 byte sequences are replaced by the replacement character (U+FFFD) |
| 899 | 893 | /// according to "U+FFFD Substitution of Maximal Subparts" from Chapter 3 of |
| 900 | 894 | /// the Unicode standard, and as specified by https://encoding.spec.whatwg.org/#utf-8-decoder |
| 901 | pub fn fmtUtf8(utf8: []const u8) std.fmt.Formatter(formatUtf8) { | |
| 895 | pub fn fmtUtf8(utf8: []const u8) std.fmt.Formatter([]const u8, formatUtf8) { | |
| 902 | 896 | return .{ .data = utf8 }; |
| 903 | 897 | } |
| 904 | 898 | |
| 905 | 899 | test fmtUtf8 { |
| 906 | 900 | const expectFmt = testing.expectFmt; |
| 907 | try expectFmt("", "{}", .{fmtUtf8("")}); | |
| 908 | try expectFmt("foo", "{}", .{fmtUtf8("foo")}); | |
| 909 | try expectFmt("𐐷", "{}", .{fmtUtf8("𐐷")}); | |
| 901 | try expectFmt("", "{f}", .{fmtUtf8("")}); | |
| 902 | try expectFmt("foo", "{f}", .{fmtUtf8("foo")}); | |
| 903 | try expectFmt("𐐷", "{f}", .{fmtUtf8("𐐷")}); | |
| 910 | 904 | |
| 911 | 905 | // 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")}); | |
| 913 | 907 | |
| 914 | 908 | // 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")}); | |
| 916 | 910 | |
| 917 | 911 | // 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")}); | |
| 919 | 913 | |
| 920 | 914 | // 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 | } |
| 923 | 917 | |
| 924 | 918 | fn utf16LeToUtf8ArrayListImpl( |
| ... | ... | @@ -1477,14 +1471,7 @@ test calcWtf16LeLen { |
| 1477 | 1471 | |
| 1478 | 1472 | /// Print the given `utf16le` string, encoded as UTF-8 bytes. |
| 1479 | 1473 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). |
| 1480 | fn formatUtf16Le( | |
| 1481 | utf16le: []const u16, | |
| 1482 | comptime fmt: []const u8, | |
| 1483 | options: std.fmt.FormatOptions, | |
| 1484 | writer: anytype, | |
| 1485 | ) !void { | |
| 1486 | _ = fmt; | |
| 1487 | _ = options; | |
| 1474 | fn formatUtf16Le(utf16le: []const u16, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 1488 | 1475 | var buf: [300]u8 = undefined; // just an arbitrary size |
| 1489 | 1476 | var it = Utf16LeIterator.init(utf16le); |
| 1490 | 1477 | var u8len: usize = 0; |
| ... | ... | @@ -1505,23 +1492,23 @@ pub const fmtUtf16le = @compileError("deprecated; renamed to fmtUtf16Le"); |
| 1505 | 1492 | /// Return a Formatter for a (potentially ill-formed) UTF-16 LE string, |
| 1506 | 1493 | /// which will be converted to UTF-8 during formatting. |
| 1507 | 1494 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). |
| 1508 | pub fn fmtUtf16Le(utf16le: []const u16) std.fmt.Formatter(formatUtf16Le) { | |
| 1495 | pub fn fmtUtf16Le(utf16le: []const u16) std.fmt.Formatter([]const u16, formatUtf16Le) { | |
| 1509 | 1496 | return .{ .data = utf16le }; |
| 1510 | 1497 | } |
| 1511 | 1498 | |
| 1512 | 1499 | test fmtUtf16Le { |
| 1513 | 1500 | const expectFmt = testing.expectFmt; |
| 1514 | try expectFmt("", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))}); | |
| 1515 | try expectFmt("", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))}); | |
| 1516 | try expectFmt("foo", "{}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))}); | |
| 1517 | try expectFmt("foo", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))}); | |
| 1518 | try expectFmt("𐐷", "{}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))}); | |
| 1519 | try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})}); | |
| 1520 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})}); | |
| 1521 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})}); | |
| 1522 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})}); | |
| 1523 | try expectFmt("�", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})}); | |
| 1524 | try expectFmt("", "{}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})}); | |
| 1501 | try expectFmt("", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral(""))}); | |
| 1502 | try expectFmt("", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral(""))}); | |
| 1503 | try expectFmt("foo", "{f}", .{fmtUtf16Le(utf8ToUtf16LeStringLiteral("foo"))}); | |
| 1504 | try expectFmt("foo", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("foo"))}); | |
| 1505 | try expectFmt("𐐷", "{f}", .{fmtUtf16Le(wtf8ToWtf16LeStringLiteral("𐐷"))}); | |
| 1506 | try expectFmt("", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xd7", native_endian)})}); | |
| 1507 | try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xd8", native_endian)})}); | |
| 1508 | try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdb", native_endian)})}); | |
| 1509 | try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xdc", native_endian)})}); | |
| 1510 | try expectFmt("�", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\xff\xdf", native_endian)})}); | |
| 1511 | try expectFmt("", "{f}", .{fmtUtf16Le(&[_]u16{mem.readInt(u16, "\x00\xe0", native_endian)})}); | |
| 1525 | 1512 | } |
| 1526 | 1513 | |
| 1527 | 1514 | fn testUtf8ToUtf16LeStringLiteral(utf8ToUtf16LeStringLiteral_: anytype) !void { |
lib/std/zig.zig+108-110| ... | ... | @@ -54,7 +54,7 @@ pub const Color = enum { |
| 54 | 54 | |
| 55 | 55 | pub fn get_tty_conf(color: Color) std.io.tty.Config { |
| 56 | 56 | return switch (color) { |
| 57 | .auto => std.io.tty.detectConfig(.stderr()), | |
| 57 | .auto => std.io.tty.detectConfig(std.fs.File.stderr()), | |
| 58 | 58 | .on => .escape_codes, |
| 59 | 59 | .off => .no_color, |
| 60 | 60 | }; |
| ... | ... | @@ -364,138 +364,136 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![ |
| 364 | 364 | |
| 365 | 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 primitives | |
| 368 | /// and the reserved `_` identifier. | |
| 369 | /// - Add `p` to the specifier to render identifiers that shadow primitives unescaped. | |
| 370 | /// - Add `_` to the specifier to render the reserved `_` identifier unescaped. | |
| 371 | /// - `p` and `_` can be combined, e.g. `{p_}`. | |
| 367 | /// See also `fmtIdFlags`. | |
| 368 | pub fn fmtId(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { | |
| 369 | return .{ .data = .{ .bytes = bytes, .flags = .{} } }; | |
| 370 | } | |
| 371 | ||
| 372 | /// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. | |
| 372 | 373 | /// |
| 373 | pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { | |
| 374 | return .{ .data = bytes }; | |
| 374 | /// See also `fmtId`. | |
| 375 | pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) std.fmt.Formatter(FormatId, FormatId.render) { | |
| 376 | return .{ .data = .{ .bytes = bytes, .flags = flags } }; | |
| 377 | } | |
| 378 | ||
| 379 | pub 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 | ||
| 383 | pub fn fmtIdP(bytes: []const u8) std.fmt.Formatter(FormatId, FormatId.render) { | |
| 384 | return .{ .data = .{ .bytes = bytes, .flags = .{ .allow_primitive = true } } }; | |
| 375 | 385 | } |
| 376 | 386 | |
| 377 | 387 | test fmtId { |
| 378 | 388 | const expectFmt = std.testing.expectFmt; |
| 379 | try expectFmt("@\"while\"", "{}", .{fmtId("while")}); | |
| 380 | try expectFmt("@\"while\"", "{p}", .{fmtId("while")}); | |
| 381 | try expectFmt("@\"while\"", "{_}", .{fmtId("while")}); | |
| 382 | try expectFmt("@\"while\"", "{p_}", .{fmtId("while")}); | |
| 383 | try expectFmt("@\"while\"", "{_p}", .{fmtId("while")}); | |
| 384 | ||
| 385 | try expectFmt("hello", "{}", .{fmtId("hello")}); | |
| 386 | try expectFmt("hello", "{p}", .{fmtId("hello")}); | |
| 387 | try expectFmt("hello", "{_}", .{fmtId("hello")}); | |
| 388 | try expectFmt("hello", "{p_}", .{fmtId("hello")}); | |
| 389 | try expectFmt("hello", "{_p}", .{fmtId("hello")}); | |
| 390 | ||
| 391 | try expectFmt("@\"type\"", "{}", .{fmtId("type")}); | |
| 392 | try expectFmt("type", "{p}", .{fmtId("type")}); | |
| 393 | try expectFmt("@\"type\"", "{_}", .{fmtId("type")}); | |
| 394 | try expectFmt("type", "{p_}", .{fmtId("type")}); | |
| 395 | try expectFmt("type", "{_p}", .{fmtId("type")}); | |
| 396 | ||
| 397 | try expectFmt("@\"_\"", "{}", .{fmtId("_")}); | |
| 398 | try expectFmt("@\"_\"", "{p}", .{fmtId("_")}); | |
| 399 | try expectFmt("_", "{_}", .{fmtId("_")}); | |
| 400 | try expectFmt("_", "{p_}", .{fmtId("_")}); | |
| 401 | try expectFmt("_", "{_p}", .{fmtId("_")}); | |
| 402 | ||
| 403 | try expectFmt("@\"i123\"", "{}", .{fmtId("i123")}); | |
| 404 | try expectFmt("i123", "{p}", .{fmtId("i123")}); | |
| 405 | try expectFmt("@\"4four\"", "{}", .{fmtId("4four")}); | |
| 406 | try expectFmt("_underscore", "{}", .{fmtId("_underscore")}); | |
| 407 | try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); | |
| 408 | try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); | |
| 389 | try expectFmt("@\"while\"", "{f}", .{fmtId("while")}); | |
| 390 | try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true })}); | |
| 391 | try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_underscore = true })}); | |
| 392 | try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true, .allow_underscore = true })}); | |
| 393 | ||
| 394 | try expectFmt("hello", "{f}", .{fmtId("hello")}); | |
| 395 | try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true })}); | |
| 396 | try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_underscore = true })}); | |
| 397 | try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true, .allow_underscore = true })}); | |
| 398 | ||
| 399 | try expectFmt("@\"type\"", "{f}", .{fmtId("type")}); | |
| 400 | try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true })}); | |
| 401 | try expectFmt("@\"type\"", "{f}", .{fmtIdFlags("type", .{ .allow_underscore = true })}); | |
| 402 | try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true, .allow_underscore = true })}); | |
| 403 | ||
| 404 | try expectFmt("@\"_\"", "{f}", .{fmtId("_")}); | |
| 405 | try expectFmt("@\"_\"", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true })}); | |
| 406 | try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_underscore = true })}); | |
| 407 | try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true, .allow_underscore = true })}); | |
| 408 | ||
| 409 | try expectFmt("@\"i123\"", "{f}", .{fmtId("i123")}); | |
| 410 | try expectFmt("i123", "{f}", .{fmtIdFlags("i123", .{ .allow_primitive = true })}); | |
| 411 | try expectFmt("@\"4four\"", "{f}", .{fmtId("4four")}); | |
| 412 | try expectFmt("_underscore", "{f}", .{fmtId("_underscore")}); | |
| 413 | try expectFmt("@\"11\\\"23\"", "{f}", .{fmtId("11\"23")}); | |
| 414 | try expectFmt("@\"11\\x0f23\"", "{f}", .{fmtId("11\x0F23")}); | |
| 409 | 415 | |
| 410 | 416 | // These are technically not currently legal in Zig. |
| 411 | try expectFmt("@\"\"", "{}", .{fmtId("")}); | |
| 412 | try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")}); | |
| 417 | try expectFmt("@\"\"", "{f}", .{fmtId("")}); | |
| 418 | try expectFmt("@\"\\x00\"", "{f}", .{fmtId("\x00")}); | |
| 413 | 419 | } |
| 414 | 420 | |
| 415 | /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. | |
| 416 | fn formatId(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void { | |
| 417 | const allow_primitive, const allow_underscore = comptime parse_fmt: { | |
| 418 | var allow_primitive = false; | |
| 419 | var allow_underscore = false; | |
| 420 | for (fmt) |char| { | |
| 421 | switch (char) { | |
| 422 | 'p' => if (!allow_primitive) { | |
| 423 | allow_primitive = true; | |
| 424 | continue; | |
| 425 | }, | |
| 426 | '_' => if (!allow_underscore) { | |
| 427 | allow_underscore = true; | |
| 428 | continue; | |
| 429 | }, | |
| 430 | else => {}, | |
| 431 | } | |
| 432 | @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}"); | |
| 433 | } | |
| 434 | break :parse_fmt .{ allow_primitive, allow_underscore }; | |
| 421 | pub const FormatId = struct { | |
| 422 | bytes: []const u8, | |
| 423 | flags: Flags, | |
| 424 | pub const Flags = struct { | |
| 425 | allow_primitive: bool = false, | |
| 426 | allow_underscore: bool = false, | |
| 435 | 427 | }; |
| 436 | 428 | |
| 437 | if (isValidId(bytes) and | |
| 438 | (allow_primitive or !std.zig.isPrimitive(bytes)) and | |
| 439 | (allow_underscore or !isUnderscore(bytes))) | |
| 440 | { | |
| 441 | return bw.writeAll(bytes); | |
| 429 | /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. | |
| 430 | fn render(ctx: FormatId, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 431 | const bytes = ctx.bytes; | |
| 432 | if (isValidId(bytes) and | |
| 433 | (ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and | |
| 434 | (ctx.flags.allow_underscore or !isUnderscore(bytes))) | |
| 435 | { | |
| 436 | return writer.writeAll(bytes); | |
| 437 | } | |
| 438 | try writer.writeAll("@\""); | |
| 439 | try stringEscape(bytes, writer); | |
| 440 | try writer.writeByte('"'); | |
| 442 | 441 | } |
| 443 | try bw.writeAll("@\""); | |
| 444 | try stringEscape(bytes, bw, ""); | |
| 445 | try bw.writeByte('"'); | |
| 442 | }; | |
| 443 | ||
| 444 | /// Return a formatter for escaping a double quoted Zig string. | |
| 445 | pub fn fmtString(bytes: []const u8) std.fmt.Formatter([]const u8, stringEscape) { | |
| 446 | return .{ .data = bytes }; | |
| 446 | 447 | } |
| 447 | 448 | |
| 448 | /// Return a Formatter for Zig Escapes of a double quoted string. | |
| 449 | /// The format specifier must be one of: | |
| 450 | /// * `{}` treats contents as a double-quoted string. | |
| 451 | /// * `{'}` treats contents as a single-quoted string. | |
| 452 | pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) { | |
| 449 | /// Return a formatter for escaping a single quoted Zig string. | |
| 450 | pub fn fmtChar(bytes: []const u8) std.fmt.Formatter([]const u8, charEscape) { | |
| 453 | 451 | return .{ .data = bytes }; |
| 454 | 452 | } |
| 455 | 453 | |
| 456 | test fmtEscapes { | |
| 457 | const expectFmt = std.testing.expectFmt; | |
| 458 | try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); | |
| 459 | try expectFmt( | |
| 460 | \\" \\ hi \x07 \x11 " derp \'" | |
| 461 | , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); | |
| 462 | try expectFmt( | |
| 454 | test fmtString { | |
| 455 | try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")}); | |
| 456 | try std.testing.expectFmt( | |
| 463 | 457 | \\" \\ hi \x07 \x11 \" derp '" |
| 464 | , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); | |
| 458 | , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")}); | |
| 465 | 459 | } |
| 466 | 460 | |
| 467 | /// Print the string as escaped contents of a double quoted or single-quoted string. | |
| 468 | /// Format `{}` treats contents as a double-quoted string. | |
| 469 | /// Format `{'}` treats contents as a single-quoted string. | |
| 470 | pub fn stringEscape(bytes: []const u8, bw: *Writer, comptime f: []const u8) !void { | |
| 461 | test fmtChar { | |
| 462 | try std.testing.expectFmt( | |
| 463 | \\" \\ hi \x07 \x11 " derp \'" | |
| 464 | , "\"{f}\"", .{fmtChar(" \\ hi \x07 \x11 \" derp '")}); | |
| 465 | } | |
| 466 | ||
| 467 | /// Print the string as escaped contents of a double quoted string. | |
| 468 | pub fn stringEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 471 | 469 | for (bytes) |byte| switch (byte) { |
| 472 | '\n' => try bw.writeAll("\\n"), | |
| 473 | '\r' => try bw.writeAll("\\r"), | |
| 474 | '\t' => try bw.writeAll("\\t"), | |
| 475 | '\\' => try bw.writeAll("\\\\"), | |
| 476 | '"' => { | |
| 477 | if (f.len == 1 and f[0] == '\'') { | |
| 478 | try bw.writeByte('"'); | |
| 479 | } else if (f.len == 0) { | |
| 480 | try bw.writeAll("\\\""); | |
| 481 | } else { | |
| 482 | @compileError("expected {} or {'}, found {" ++ f ++ "}"); | |
| 483 | } | |
| 484 | }, | |
| 485 | '\'' => { | |
| 486 | if (f.len == 1 and f[0] == '\'') { | |
| 487 | try bw.writeAll("\\'"); | |
| 488 | } else if (f.len == 0) { | |
| 489 | try bw.writeByte('\''); | |
| 490 | } else { | |
| 491 | @compileError("expected {} or {'}, found {" ++ f ++ "}"); | |
| 492 | } | |
| 470 | '\n' => try w.writeAll("\\n"), | |
| 471 | '\r' => try w.writeAll("\\r"), | |
| 472 | '\t' => try w.writeAll("\\t"), | |
| 473 | '\\' => try w.writeAll("\\\\"), | |
| 474 | '"' => try w.writeAll("\\\""), | |
| 475 | '\'' => try w.writeByte('\''), | |
| 476 | ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), | |
| 477 | else => { | |
| 478 | try w.writeAll("\\x"); | |
| 479 | try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); | |
| 493 | 480 | }, |
| 494 | ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte), | |
| 495 | // Use hex escapes for rest any unprintable characters. | |
| 481 | }; | |
| 482 | } | |
| 483 | ||
| 484 | /// Print the string as escaped contents of a single-quoted string. | |
| 485 | pub 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 | 494 | else => { |
| 497 | try bw.writeAll("\\x"); | |
| 498 | try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); | |
| 495 | try w.writeAll("\\x"); | |
| 496 | try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); | |
| 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 | 320 | } |
| 321 | 321 | } |
| 322 | 322 | |
| 323 | pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void { | |
| 323 | pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void { | |
| 324 | 324 | switch (parse_error.tag) { |
| 325 | 325 | .asterisk_after_ptr_deref => { |
| 326 | 326 | // Note that the token will point at the `.*` but ideally the source |
| 327 | 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 | 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 | 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 | 336 | .expected_block => { |
| 337 | return bw.print("expected block, found '{s}'", .{ | |
| 337 | return w.print("expected block, found '{s}'", .{ | |
| 338 | 338 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 339 | 339 | }); |
| 340 | 340 | }, |
| 341 | 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 | 343 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 344 | 344 | }); |
| 345 | 345 | }, |
| 346 | 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 | 348 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 349 | 349 | }); |
| 350 | 350 | }, |
| 351 | 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 | 353 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 354 | 354 | }); |
| 355 | 355 | }, |
| 356 | 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 | 358 | tree.tokenTag(parse_error.token).symbol(), |
| 359 | 359 | }); |
| 360 | 360 | }, |
| 361 | 361 | .expected_expr => { |
| 362 | return bw.print("expected expression, found '{s}'", .{ | |
| 362 | return w.print("expected expression, found '{s}'", .{ | |
| 363 | 363 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 364 | 364 | }); |
| 365 | 365 | }, |
| 366 | 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 | 368 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 369 | 369 | }); |
| 370 | 370 | }, |
| 371 | 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 | 373 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 374 | 374 | }); |
| 375 | 375 | }, |
| 376 | 376 | .expected_fn => { |
| 377 | return bw.print("expected function, found '{s}'", .{ | |
| 377 | return w.print("expected function, found '{s}'", .{ | |
| 378 | 378 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 379 | 379 | }); |
| 380 | 380 | }, |
| 381 | 381 | .expected_inlinable => { |
| 382 | return bw.print("expected 'while' or 'for', found '{s}'", .{ | |
| 382 | return w.print("expected 'while' or 'for', found '{s}'", .{ | |
| 383 | 383 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 384 | 384 | }); |
| 385 | 385 | }, |
| 386 | 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 | 388 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 389 | 389 | }); |
| 390 | 390 | }, |
| 391 | 391 | .expected_param_list => { |
| 392 | return bw.print("expected parameter list, found '{s}'", .{ | |
| 392 | return w.print("expected parameter list, found '{s}'", .{ | |
| 393 | 393 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 394 | 394 | }); |
| 395 | 395 | }, |
| 396 | 396 | .expected_prefix_expr => { |
| 397 | return bw.print("expected prefix expression, found '{s}'", .{ | |
| 397 | return w.print("expected prefix expression, found '{s}'", .{ | |
| 398 | 398 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 399 | 399 | }); |
| 400 | 400 | }, |
| 401 | 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 | 403 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 404 | 404 | }); |
| 405 | 405 | }, |
| 406 | 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 | 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 | 411 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 412 | 412 | }); |
| 413 | 413 | }, |
| 414 | 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 | 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 | 420 | .expected_statement => { |
| 421 | return bw.print("expected statement, found '{s}'", .{ | |
| 421 | return w.print("expected statement, found '{s}'", .{ | |
| 422 | 422 | tree.tokenTag(parse_error.token).symbol(), |
| 423 | 423 | }); |
| 424 | 424 | }, |
| 425 | 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 | 427 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 428 | 428 | }); |
| 429 | 429 | }, |
| 430 | 430 | .expected_type_expr => { |
| 431 | return bw.print("expected type expression, found '{s}'", .{ | |
| 431 | return w.print("expected type expression, found '{s}'", .{ | |
| 432 | 432 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 433 | 433 | }); |
| 434 | 434 | }, |
| 435 | 435 | .expected_var_decl => { |
| 436 | return bw.print("expected variable declaration, found '{s}'", .{ | |
| 436 | return w.print("expected variable declaration, found '{s}'", .{ | |
| 437 | 437 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 438 | 438 | }); |
| 439 | 439 | }, |
| 440 | 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 | 442 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 443 | 443 | }); |
| 444 | 444 | }, |
| 445 | 445 | .expected_loop_payload => { |
| 446 | return bw.print("expected loop payload, found '{s}'", .{ | |
| 446 | return w.print("expected loop payload, found '{s}'", .{ | |
| 447 | 447 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 448 | 448 | }); |
| 449 | 449 | }, |
| 450 | 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 | 452 | tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(), |
| 453 | 453 | }); |
| 454 | 454 | }, |
| 455 | 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 | 458 | .extra_addrspace_qualifier => { |
| 459 | return bw.writeAll("extra addrspace qualifier"); | |
| 459 | return w.writeAll("extra addrspace qualifier"); | |
| 460 | 460 | }, |
| 461 | 461 | .extra_align_qualifier => { |
| 462 | return bw.writeAll("extra align qualifier"); | |
| 462 | return w.writeAll("extra align qualifier"); | |
| 463 | 463 | }, |
| 464 | 464 | .extra_allowzero_qualifier => { |
| 465 | return bw.writeAll("extra allowzero qualifier"); | |
| 465 | return w.writeAll("extra allowzero qualifier"); | |
| 466 | 466 | }, |
| 467 | 467 | .extra_const_qualifier => { |
| 468 | return bw.writeAll("extra const qualifier"); | |
| 468 | return w.writeAll("extra const qualifier"); | |
| 469 | 469 | }, |
| 470 | 470 | .extra_volatile_qualifier => { |
| 471 | return bw.writeAll("extra volatile qualifier"); | |
| 471 | return w.writeAll("extra volatile qualifier"); | |
| 472 | 472 | }, |
| 473 | 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 | 475 | tree.tokenTag(parse_error.token).symbol(), |
| 476 | 476 | }); |
| 477 | 477 | }, |
| 478 | 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 | 481 | .same_line_doc_comment => { |
| 482 | return bw.writeAll("same line documentation comment"); | |
| 482 | return w.writeAll("same line documentation comment"); | |
| 483 | 483 | }, |
| 484 | 484 | .unattached_doc_comment => { |
| 485 | return bw.writeAll("unattached documentation comment"); | |
| 485 | return w.writeAll("unattached documentation comment"); | |
| 486 | 486 | }, |
| 487 | 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 | 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 | 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 | 496 | .expected_continue_expr => { |
| 497 | return bw.writeAll("expected ':' before while continue expression"); | |
| 497 | return w.writeAll("expected ':' before while continue expression"); | |
| 498 | 498 | }, |
| 499 | 499 | |
| 500 | 500 | .expected_semi_after_decl => { |
| 501 | return bw.writeAll("expected ';' after declaration"); | |
| 501 | return w.writeAll("expected ';' after declaration"); | |
| 502 | 502 | }, |
| 503 | 503 | .expected_semi_after_stmt => { |
| 504 | return bw.writeAll("expected ';' after statement"); | |
| 504 | return w.writeAll("expected ';' after statement"); | |
| 505 | 505 | }, |
| 506 | 506 | .expected_comma_after_field => { |
| 507 | return bw.writeAll("expected ',' after field"); | |
| 507 | return w.writeAll("expected ',' after field"); | |
| 508 | 508 | }, |
| 509 | 509 | .expected_comma_after_arg => { |
| 510 | return bw.writeAll("expected ',' after argument"); | |
| 510 | return w.writeAll("expected ',' after argument"); | |
| 511 | 511 | }, |
| 512 | 512 | .expected_comma_after_param => { |
| 513 | return bw.writeAll("expected ',' after parameter"); | |
| 513 | return w.writeAll("expected ',' after parameter"); | |
| 514 | 514 | }, |
| 515 | 515 | .expected_comma_after_initializer => { |
| 516 | return bw.writeAll("expected ',' after initializer"); | |
| 516 | return w.writeAll("expected ',' after initializer"); | |
| 517 | 517 | }, |
| 518 | 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 | 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 | 524 | .expected_comma_after_capture => { |
| 525 | return bw.writeAll("expected ',' after for capture"); | |
| 525 | return w.writeAll("expected ',' after for capture"); | |
| 526 | 526 | }, |
| 527 | 527 | .expected_initializer => { |
| 528 | return bw.writeAll("expected field initializer"); | |
| 528 | return w.writeAll("expected field initializer"); | |
| 529 | 529 | }, |
| 530 | 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 | 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 | 536 | .c_style_container => { |
| 537 | return bw.print("'{s} {s}' is invalid", .{ | |
| 537 | return w.print("'{s} {s}' is invalid", .{ | |
| 538 | 538 | parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token), |
| 539 | 539 | }); |
| 540 | 540 | }, |
| 541 | 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 | 543 | tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(), |
| 544 | 544 | }); |
| 545 | 545 | }, |
| 546 | 546 | .previous_field => { |
| 547 | return bw.writeAll("field before declarations here"); | |
| 547 | return w.writeAll("field before declarations here"); | |
| 548 | 548 | }, |
| 549 | 549 | .next_field => { |
| 550 | return bw.writeAll("field after declarations here"); | |
| 550 | return w.writeAll("field after declarations here"); | |
| 551 | 551 | }, |
| 552 | 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 | 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 | 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 | 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 | 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 | }, |
| 567 | 567 | |
| 568 | 568 | .invalid_byte => { |
| 569 | 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 | 571 | switch (tok_slice[0]) { |
| 572 | 572 | '\'' => "character literal", |
| 573 | 573 | '"', '\\' => "string literal", |
| 574 | 574 | '/' => "comment", |
| 575 | 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 | }, |
| 580 | 580 | |
| ... | ... | @@ -582,10 +582,10 @@ pub fn renderError(tree: Ast, parse_error: Error, bw: *Writer) Writer.Error!void |
| 582 | 582 | const found_tag = tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)); |
| 583 | 583 | const expected_symbol = parse_error.extra.expected_tag.symbol(); |
| 584 | 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 | 586 | expected_symbol, |
| 587 | 587 | }), |
| 588 | else => return bw.print("expected '{s}', found '{s}'", .{ | |
| 588 | else => return w.print("expected '{s}', found '{s}'", .{ | |
| 589 | 589 | expected_symbol, found_tag.symbol(), |
| 590 | 590 | }), |
| 591 | 591 | } |
| ... | ... | @@ -608,7 +608,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { |
| 608 | 608 | .negation_wrap, |
| 609 | 609 | .address_of, |
| 610 | 610 | .@"try", |
| 611 | .@"await", | |
| 612 | 611 | .optional_type, |
| 613 | 612 | .@"switch", |
| 614 | 613 | .switch_comma, |
| ... | ... | @@ -758,27 +757,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { |
| 758 | 757 | return i - end_offset; |
| 759 | 758 | }, |
| 760 | 759 | |
| 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 | 760 | .container_field_init, |
| 783 | 761 | .container_field_align, |
| 784 | 762 | .container_field, |
| ... | ... | @@ -898,14 +876,12 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { |
| 898 | 876 | while (true) switch (tree.nodeTag(n)) { |
| 899 | 877 | .root => return @intCast(tree.tokens.len - 1), |
| 900 | 878 | |
| 901 | .@"usingnamespace", | |
| 902 | 879 | .bool_not, |
| 903 | 880 | .negation, |
| 904 | 881 | .bit_not, |
| 905 | 882 | .negation_wrap, |
| 906 | 883 | .address_of, |
| 907 | 884 | .@"try", |
| 908 | .@"await", | |
| 909 | 885 | .optional_type, |
| 910 | 886 | .@"suspend", |
| 911 | 887 | .@"resume", |
| ... | ... | @@ -1024,7 +1000,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { |
| 1024 | 1000 | }; |
| 1025 | 1001 | }, |
| 1026 | 1002 | |
| 1027 | .call, .async_call => { | |
| 1003 | .call => { | |
| 1028 | 1004 | _, const extra_index = tree.nodeData(n).node_and_extra; |
| 1029 | 1005 | const params = tree.extraData(extra_index, Node.SubRange); |
| 1030 | 1006 | assert(params.start != params.end); |
| ... | ... | @@ -1043,7 +1019,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { |
| 1043 | 1019 | } |
| 1044 | 1020 | }, |
| 1045 | 1021 | .call_comma, |
| 1046 | .async_call_comma, | |
| 1047 | 1022 | .tagged_union_enum_tag_trailing, |
| 1048 | 1023 | => { |
| 1049 | 1024 | _, const extra_index = tree.nodeData(n).node_and_extra; |
| ... | ... | @@ -1124,7 +1099,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { |
| 1124 | 1099 | n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member |
| 1125 | 1100 | }, |
| 1126 | 1101 | .call_one, |
| 1127 | .async_call_one, | |
| 1128 | 1102 | => { |
| 1129 | 1103 | _, const first_param = tree.nodeData(n).node_and_opt_node; |
| 1130 | 1104 | end_offset += 1; // for the rparen |
| ... | ... | @@ -1273,7 +1247,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { |
| 1273 | 1247 | n = first_element; |
| 1274 | 1248 | }, |
| 1275 | 1249 | .call_one_comma, |
| 1276 | .async_call_one_comma, | |
| 1277 | 1250 | .struct_init_one_comma, |
| 1278 | 1251 | => { |
| 1279 | 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 | 1963 | pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call { |
| 1991 | 1964 | const fn_expr, const first_param = tree.nodeData(node).node_and_opt_node; |
| 1992 | 1965 | const params = loadOptionalNodesIntoBuffer(1, buffer, .{first_param}); |
| 1993 | return tree.fullCallComponents(.{ | |
| 1966 | return .{ .ast = .{ | |
| 1994 | 1967 | .lparen = tree.nodeMainToken(node), |
| 1995 | 1968 | .fn_expr = fn_expr, |
| 1996 | 1969 | .params = params, |
| 1997 | }); | |
| 1970 | } }; | |
| 1998 | 1971 | } |
| 1999 | 1972 | |
| 2000 | 1973 | pub fn callFull(tree: Ast, node: Node.Index) full.Call { |
| 2001 | 1974 | const fn_expr, const extra_index = tree.nodeData(node).node_and_extra; |
| 2002 | 1975 | const params = tree.extraDataSlice(tree.extraData(extra_index, Node.SubRange), Node.Index); |
| 2003 | return tree.fullCallComponents(.{ | |
| 1976 | return .{ .ast = .{ | |
| 2004 | 1977 | .lparen = tree.nodeMainToken(node), |
| 2005 | 1978 | .fn_expr = fn_expr, |
| 2006 | 1979 | .params = params, |
| 2007 | }); | |
| 1980 | } }; | |
| 2008 | 1981 | } |
| 2009 | 1982 | |
| 2010 | 1983 | fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl { |
| ... | ... | @@ -2338,18 +2311,6 @@ fn fullForComponents(tree: Ast, info: full.For.Components) full.For { |
| 2338 | 2311 | return result; |
| 2339 | 2312 | } |
| 2340 | 2313 | |
| 2341 | fn 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 | ||
| 2353 | 2314 | pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl { |
| 2354 | 2315 | return switch (tree.nodeTag(node)) { |
| 2355 | 2316 | .global_var_decl => tree.globalVarDecl(node), |
| ... | ... | @@ -2490,8 +2451,8 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm { |
| 2490 | 2451 | |
| 2491 | 2452 | pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call { |
| 2492 | 2453 | return switch (tree.nodeTag(node)) { |
| 2493 | .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node), | |
| 2494 | .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node), | |
| 2454 | .call, .call_comma => tree.callFull(node), | |
| 2455 | .call_one, .call_one_comma => tree.callOne(buffer, node), | |
| 2495 | 2456 | else => null, |
| 2496 | 2457 | }; |
| 2497 | 2458 | } |
| ... | ... | @@ -2884,7 +2845,6 @@ pub const full = struct { |
| 2884 | 2845 | |
| 2885 | 2846 | pub const Call = struct { |
| 2886 | 2847 | ast: Components, |
| 2887 | async_token: ?TokenIndex, | |
| 2888 | 2848 | |
| 2889 | 2849 | pub const Components = struct { |
| 2890 | 2850 | lparen: TokenIndex, |
| ... | ... | @@ -3067,12 +3027,6 @@ pub const Node = struct { |
| 3067 | 3027 | /// |
| 3068 | 3028 | /// The `main_token` field is the first token for the source file. |
| 3069 | 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 | 3030 | /// `test {}`, |
| 3077 | 3031 | /// `test "name" {}`, |
| 3078 | 3032 | /// `test identifier {}`. |
| ... | ... | @@ -3303,8 +3257,6 @@ pub const Node = struct { |
| 3303 | 3257 | address_of, |
| 3304 | 3258 | /// `try expr`. The `main_token` field is the `try` token. |
| 3305 | 3259 | @"try", |
| 3306 | /// `await expr`. The `main_token` field is the `await` token. | |
| 3307 | @"await", | |
| 3308 | 3260 | /// `?expr`. The `main_token` field is the `?` token. |
| 3309 | 3261 | optional_type, |
| 3310 | 3262 | /// `[lhs]rhs`. The `main_token` field is the `[` token. |
| ... | ... | @@ -3500,17 +3452,6 @@ pub const Node = struct { |
| 3500 | 3452 | /// Same as `call_one` except there is known to be a trailing comma |
| 3501 | 3453 | /// before the final rparen. |
| 3502 | 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 | 3455 | /// `a(b, c, d)`. |
| 3515 | 3456 | /// |
| 3516 | 3457 | /// The `data` field is a `.node_and_extra`: |
| ... | ... | @@ -3523,18 +3464,6 @@ pub const Node = struct { |
| 3523 | 3464 | /// Same as `call` except there is known to be a trailing comma before |
| 3524 | 3465 | /// the final rparen. |
| 3525 | 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 | 3467 | /// `switch(a) {}`. |
| 3539 | 3468 | /// |
| 3540 | 3469 | /// The `data` field is a `.node_and_extra`: |
lib/std/zig/Ast/Render.zig+3-23| ... | ... | @@ -265,17 +265,6 @@ fn renderMember( |
| 265 | 265 | return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon |
| 266 | 266 | }, |
| 267 | 267 | |
| 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 | 268 | .global_var_decl, |
| 280 | 269 | .local_var_decl, |
| 281 | 270 | .simple_var_decl, |
| ... | ... | @@ -594,7 +583,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 594 | 583 | |
| 595 | 584 | .@"try", |
| 596 | 585 | .@"resume", |
| 597 | .@"await", | |
| 598 | 586 | => { |
| 599 | 587 | try renderToken(r, tree.nodeMainToken(node), .space); |
| 600 | 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 | 626 | |
| 639 | 627 | .call_one, |
| 640 | 628 | .call_one_comma, |
| 641 | .async_call_one, | |
| 642 | .async_call_one_comma, | |
| 643 | 629 | .call, |
| 644 | 630 | .call_comma, |
| 645 | .async_call, | |
| 646 | .async_call_comma, | |
| 647 | 631 | => { |
| 648 | 632 | var buf: [1]Ast.Node.Index = undefined; |
| 649 | 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 | 869 | .local_var_decl => unreachable, |
| 886 | 870 | .simple_var_decl => unreachable, |
| 887 | 871 | .aligned_var_decl => unreachable, |
| 888 | .@"usingnamespace" => unreachable, | |
| 889 | 872 | .test_decl => unreachable, |
| 890 | 873 | .asm_output => unreachable, |
| 891 | 874 | .asm_input => unreachable, |
| ... | ... | @@ -1584,7 +1567,7 @@ fn renderBuiltinCall( |
| 1584 | 1567 | defer r.gpa.free(new_string); |
| 1585 | 1568 | |
| 1586 | 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 | 1571 | return renderToken(r, str_lit_token + 1, space); // ) |
| 1589 | 1572 | } |
| 1590 | 1573 | } |
| ... | ... | @@ -2556,9 +2539,6 @@ fn renderCall( |
| 2556 | 2539 | call: Ast.full.Call, |
| 2557 | 2540 | space: Space, |
| 2558 | 2541 | ) Error!void { |
| 2559 | if (call.async_token) |async_token| { | |
| 2560 | try renderToken(r, async_token, .space); | |
| 2561 | } | |
| 2562 | 2542 | try renderExpression(r, call.ast.fn_expr, .none); |
| 2563 | 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 | 2877 | .success => |codepoint| { |
| 2898 | 2878 | if (codepoint <= 0x7f) { |
| 2899 | 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 | 2881 | } else { |
| 2902 | 2882 | try ais.writeAll(escape_sequence); |
| 2903 | 2883 | } |
| ... | ... | @@ -2909,7 +2889,7 @@ fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void |
| 2909 | 2889 | }, |
| 2910 | 2890 | 0x00...('\\' - 1), ('\\' + 1)...0x7f => { |
| 2911 | 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 | 2893 | pos += 1; |
| 2914 | 2894 | }, |
| 2915 | 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 | 442 | const tree = astgen.tree; |
| 443 | 443 | switch (tree.nodeTag(node)) { |
| 444 | 444 | .root => unreachable, |
| 445 | .@"usingnamespace" => unreachable, | |
| 446 | 445 | .test_decl => unreachable, |
| 447 | 446 | .global_var_decl => unreachable, |
| 448 | 447 | .local_var_decl => unreachable, |
| ... | ... | @@ -510,12 +509,8 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins |
| 510 | 509 | .number_literal, |
| 511 | 510 | .call, |
| 512 | 511 | .call_comma, |
| 513 | .async_call, | |
| 514 | .async_call_comma, | |
| 515 | 512 | .call_one, |
| 516 | 513 | .call_one_comma, |
| 517 | .async_call_one, | |
| 518 | .async_call_one_comma, | |
| 519 | 514 | .unreachable_literal, |
| 520 | 515 | .@"return", |
| 521 | 516 | .@"if", |
| ... | ... | @@ -547,7 +542,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins |
| 547 | 542 | .merge_error_sets, |
| 548 | 543 | .switch_range, |
| 549 | 544 | .for_range, |
| 550 | .@"await", | |
| 551 | 545 | .bit_not, |
| 552 | 546 | .negation, |
| 553 | 547 | .negation_wrap, |
| ... | ... | @@ -642,7 +636,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 642 | 636 | |
| 643 | 637 | switch (tree.nodeTag(node)) { |
| 644 | 638 | .root => unreachable, // Top-level declaration. |
| 645 | .@"usingnamespace" => unreachable, // Top-level declaration. | |
| 646 | 639 | .test_decl => unreachable, // Top-level declaration. |
| 647 | 640 | .container_field_init => unreachable, // Top-level declaration. |
| 648 | 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 | 829 | |
| 837 | 830 | .call_one, |
| 838 | 831 | .call_one_comma, |
| 839 | .async_call_one, | |
| 840 | .async_call_one_comma, | |
| 841 | 832 | .call, |
| 842 | 833 | .call_comma, |
| 843 | .async_call, | |
| 844 | .async_call_comma, | |
| 845 | 834 | => { |
| 846 | 835 | var buf: [1]Ast.Node.Index = undefined; |
| 847 | 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 | 1103 | |
| 1115 | 1104 | .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node), |
| 1116 | 1105 | .@"suspend" => return suspendExpr(gz, scope, node), |
| 1117 | .@"await" => return awaitExpr(gz, scope, ri, node), | |
| 1118 | 1106 | .@"resume" => return resumeExpr(gz, scope, ri, node), |
| 1119 | 1107 | |
| 1120 | 1108 | .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node), |
| ... | ... | @@ -1259,33 +1247,6 @@ fn suspendExpr( |
| 1259 | 1247 | return suspend_inst.toRef(); |
| 1260 | 1248 | } |
| 1261 | 1249 | |
| 1262 | fn 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 | ||
| 1289 | 1250 | fn resumeExpr( |
| 1290 | 1251 | gz: *GenZir, |
| 1291 | 1252 | scope: *Scope, |
| ... | ... | @@ -2853,7 +2814,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2853 | 2814 | .tag_name, |
| 2854 | 2815 | .type_name, |
| 2855 | 2816 | .frame_type, |
| 2856 | .frame_size, | |
| 2857 | 2817 | .int_from_float, |
| 2858 | 2818 | .float_from_int, |
| 2859 | 2819 | .ptr_from_int, |
| ... | ... | @@ -2887,7 +2847,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2887 | 2847 | .min, |
| 2888 | 2848 | .c_import, |
| 2889 | 2849 | .@"resume", |
| 2890 | .@"await", | |
| 2891 | 2850 | .ret_err_value_code, |
| 2892 | 2851 | .ret_ptr, |
| 2893 | 2852 | .ret_type, |
| ... | ... | @@ -4739,69 +4698,6 @@ fn comptimeDecl( |
| 4739 | 4698 | }); |
| 4740 | 4699 | } |
| 4741 | 4700 | |
| 4742 | fn 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 | ||
| 4805 | 4701 | fn testDecl( |
| 4806 | 4702 | astgen: *AstGen, |
| 4807 | 4703 | gz: *GenZir, |
| ... | ... | @@ -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 | 5870 | .test_decl => { |
| 5992 | 5871 | const prev_decl_index = wip_members.decl_index; |
| 5993 | 5872 | // We need to have *some* decl here so that the decl count matches what's expected. |
| ... | ... | @@ -9501,7 +9380,6 @@ fn builtinCall( |
| 9501 | 9380 | .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name), |
| 9502 | 9381 | .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name), |
| 9503 | 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), | |
| 9505 | 9383 | |
| 9506 | 9384 | .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), |
| 9507 | 9385 | .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), |
| ... | ... | @@ -9767,16 +9645,6 @@ fn builtinCall( |
| 9767 | 9645 | }); |
| 9768 | 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 | 9648 | .Vector => { |
| 9781 | 9649 | const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{ |
| 9782 | 9650 | .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type), |
| ... | ... | @@ -10175,11 +10043,8 @@ fn callExpr( |
| 10175 | 10043 | |
| 10176 | 10044 | const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr); |
| 10177 | 10045 | const modifier: std.builtin.CallModifier = blk: { |
| 10178 | if (call.async_token != null) { | |
| 10179 | break :blk .async_kw; | |
| 10180 | } | |
| 10181 | 10046 | if (gz.nosuspend_node != .none) { |
| 10182 | break :blk .no_async; | |
| 10047 | break :blk .no_suspend; | |
| 10183 | 10048 | } |
| 10184 | 10049 | break :blk .auto; |
| 10185 | 10050 | }; |
| ... | ... | @@ -10451,7 +10316,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev |
| 10451 | 10316 | while (true) { |
| 10452 | 10317 | switch (tree.nodeTag(node)) { |
| 10453 | 10318 | .root, |
| 10454 | .@"usingnamespace", | |
| 10455 | 10319 | .test_decl, |
| 10456 | 10320 | .switch_case, |
| 10457 | 10321 | .switch_case_inline, |
| ... | ... | @@ -10483,12 +10347,8 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev |
| 10483 | 10347 | .switch_comma, |
| 10484 | 10348 | .call_one, |
| 10485 | 10349 | .call_one_comma, |
| 10486 | .async_call_one, | |
| 10487 | .async_call_one_comma, | |
| 10488 | 10350 | .call, |
| 10489 | 10351 | .call_comma, |
| 10490 | .async_call, | |
| 10491 | .async_call_comma, | |
| 10492 | 10352 | => return .maybe, |
| 10493 | 10353 | |
| 10494 | 10354 | .@"return", |
| ... | ... | @@ -10613,7 +10473,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev |
| 10613 | 10473 | |
| 10614 | 10474 | // Forward the question to the LHS sub-expression. |
| 10615 | 10475 | .@"try", |
| 10616 | .@"await", | |
| 10617 | 10476 | .@"comptime", |
| 10618 | 10477 | .@"nosuspend", |
| 10619 | 10478 | => node = tree.nodeData(node).node, |
| ... | ... | @@ -10664,7 +10523,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In |
| 10664 | 10523 | while (true) { |
| 10665 | 10524 | switch (tree.nodeTag(node)) { |
| 10666 | 10525 | .root, |
| 10667 | .@"usingnamespace", | |
| 10668 | 10526 | .test_decl, |
| 10669 | 10527 | .switch_case, |
| 10670 | 10528 | .switch_case_inline, |
| ... | ... | @@ -10803,12 +10661,8 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In |
| 10803 | 10661 | .switch_comma, |
| 10804 | 10662 | .call_one, |
| 10805 | 10663 | .call_one_comma, |
| 10806 | .async_call_one, | |
| 10807 | .async_call_one_comma, | |
| 10808 | 10664 | .call, |
| 10809 | 10665 | .call_comma, |
| 10810 | .async_call, | |
| 10811 | .async_call_comma, | |
| 10812 | 10666 | .block_two, |
| 10813 | 10667 | .block_two_semicolon, |
| 10814 | 10668 | .block, |
| ... | ... | @@ -10826,7 +10680,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In |
| 10826 | 10680 | |
| 10827 | 10681 | // Forward the question to the LHS sub-expression. |
| 10828 | 10682 | .@"try", |
| 10829 | .@"await", | |
| 10830 | 10683 | .@"comptime", |
| 10831 | 10684 | .@"nosuspend", |
| 10832 | 10685 | => node = tree.nodeData(node).node, |
| ... | ... | @@ -10908,7 +10761,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 10908 | 10761 | while (true) { |
| 10909 | 10762 | switch (tree.nodeTag(node)) { |
| 10910 | 10763 | .root, |
| 10911 | .@"usingnamespace", | |
| 10912 | 10764 | .test_decl, |
| 10913 | 10765 | .switch_case, |
| 10914 | 10766 | .switch_case_inline, |
| ... | ... | @@ -11047,12 +10899,8 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 11047 | 10899 | .switch_comma, |
| 11048 | 10900 | .call_one, |
| 11049 | 10901 | .call_one_comma, |
| 11050 | .async_call_one, | |
| 11051 | .async_call_one_comma, | |
| 11052 | 10902 | .call, |
| 11053 | 10903 | .call_comma, |
| 11054 | .async_call, | |
| 11055 | .async_call_comma, | |
| 11056 | 10904 | .block_two, |
| 11057 | 10905 | .block_two_semicolon, |
| 11058 | 10906 | .block, |
| ... | ... | @@ -11079,7 +10927,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 11079 | 10927 | |
| 11080 | 10928 | // Forward the question to the LHS sub-expression. |
| 11081 | 10929 | .@"try", |
| 11082 | .@"await", | |
| 11083 | 10930 | .@"comptime", |
| 11084 | 10931 | .@"nosuspend", |
| 11085 | 10932 | => node = tree.nodeData(node).node, |
| ... | ... | @@ -11462,13 +11309,7 @@ fn failWithStrLitError( |
| 11462 | 11309 | offset: u32, |
| 11463 | 11310 | ) InnerError { |
| 11464 | 11311 | const raw_string = bytes[offset..]; |
| 11465 | return failOff( | |
| 11466 | astgen, | |
| 11467 | token, | |
| 11468 | @intCast(offset + err.offset()), | |
| 11469 | "{f}", | |
| 11470 | .{err.fmt(raw_string)}, | |
| 11471 | ); | |
| 11312 | return failOff(astgen, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)}); | |
| 11472 | 11313 | } |
| 11473 | 11314 | |
| 11474 | 11315 | fn failNode( |
| ... | ... | @@ -13591,7 +13432,7 @@ fn scanContainer( |
| 13591 | 13432 | break :blk .{ .decl, ident }; |
| 13592 | 13433 | }, |
| 13593 | 13434 | |
| 13594 | .@"comptime", .@"usingnamespace" => { | |
| 13435 | .@"comptime" => { | |
| 13595 | 13436 | decl_count += 1; |
| 13596 | 13437 | continue; |
| 13597 | 13438 | }, |
| ... | ... | @@ -13970,7 +13811,6 @@ const DeclarationName = union(enum) { |
| 13970 | 13811 | decltest: Ast.TokenIndex, |
| 13971 | 13812 | unnamed_test, |
| 13972 | 13813 | @"comptime", |
| 13973 | @"usingnamespace", | |
| 13974 | 13814 | }; |
| 13975 | 13815 | |
| 13976 | 13816 | fn addFailedDeclaration( |
| ... | ... | @@ -14060,7 +13900,6 @@ fn setDeclaration( |
| 14060 | 13900 | .@"test" => .@"test", |
| 14061 | 13901 | .decltest => .decltest, |
| 14062 | 13902 | .@"comptime" => .@"comptime", |
| 14063 | .@"usingnamespace" => if (args.is_pub) .pub_usingnamespace else .@"usingnamespace", | |
| 14064 | 13903 | .@"const" => switch (args.linkage) { |
| 14065 | 13904 | .normal => if (args.is_pub) id: { |
| 14066 | 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 | 165 | } |
| 166 | 166 | return false; |
| 167 | 167 | }, |
| 168 | .@"usingnamespace" => { | |
| 169 | _ = try astrl.expr(tree.nodeData(node).node, block, ResultInfo.type_only); | |
| 170 | return false; | |
| 171 | }, | |
| 172 | 168 | .test_decl => { |
| 173 | 169 | _ = try astrl.expr(tree.nodeData(node).opt_token_and_node[1], block, ResultInfo.none); |
| 174 | 170 | return false; |
| ... | ... | @@ -334,12 +330,8 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI |
| 334 | 330 | |
| 335 | 331 | .call_one, |
| 336 | 332 | .call_one_comma, |
| 337 | .async_call_one, | |
| 338 | .async_call_one_comma, | |
| 339 | 333 | .call, |
| 340 | 334 | .call_comma, |
| 341 | .async_call, | |
| 342 | .async_call_comma, | |
| 343 | 335 | => { |
| 344 | 336 | var buf: [1]Ast.Node.Index = undefined; |
| 345 | 337 | const full = tree.fullCall(&buf, node).?; |
| ... | ... | @@ -353,11 +345,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI |
| 353 | 345 | .call, |
| 354 | 346 | .call_comma, |
| 355 | 347 | => 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 | 348 | else => unreachable, |
| 362 | 349 | }; |
| 363 | 350 | }, |
| ... | ... | @@ -503,7 +490,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI |
| 503 | 490 | return false; |
| 504 | 491 | }, |
| 505 | 492 | .@"try", |
| 506 | .@"await", | |
| 507 | 493 | .@"nosuspend", |
| 508 | 494 | => return astrl.expr(tree.nodeData(node).node, block, ri), |
| 509 | 495 | .grouped_expression, |
| ... | ... | @@ -948,7 +934,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. |
| 948 | 934 | .tag_name, |
| 949 | 935 | .type_name, |
| 950 | 936 | .Frame, |
| 951 | .frame_size, | |
| 952 | 937 | .int_from_float, |
| 953 | 938 | .float_from_int, |
| 954 | 939 | .ptr_from_int, |
| ... | ... | @@ -1079,13 +1064,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. |
| 1079 | 1064 | _ = try astrl.expr(args[3], block, ResultInfo.none); |
| 1080 | 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 | 1067 | .Vector => { |
| 1090 | 1068 | _ = try astrl.expr(args[0], block, ResultInfo.type_only); |
| 1091 | 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 | 4 | align_cast, |
| 5 | 5 | align_of, |
| 6 | 6 | as, |
| 7 | async_call, | |
| 8 | 7 | atomic_load, |
| 9 | 8 | atomic_rmw, |
| 10 | 9 | atomic_store, |
| ... | ... | @@ -55,7 +54,6 @@ pub const Tag = enum { |
| 55 | 54 | frame, |
| 56 | 55 | Frame, |
| 57 | 56 | frame_address, |
| 58 | frame_size, | |
| 59 | 57 | has_decl, |
| 60 | 58 | has_field, |
| 61 | 59 | import, |
| ... | ... | @@ -184,13 +182,6 @@ pub const list = list: { |
| 184 | 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 | 186 | "@atomicLoad", |
| 196 | 187 | .{ |
| ... | ... | @@ -550,13 +541,6 @@ pub const list = list: { |
| 550 | 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 | 545 | "@hasDecl", |
| 562 | 546 | .{ |
lib/std/zig/ErrorBundle.zig+64-56| ... | ... | @@ -164,22 +164,22 @@ pub const RenderOptions = struct { |
| 164 | 164 | |
| 165 | 165 | pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { |
| 166 | 166 | var buffer: [256]u8 = undefined; |
| 167 | const bw = std.debug.lockStderrWriter(&buffer); | |
| 167 | const w = std.debug.lockStderrWriter(&buffer); | |
| 168 | 168 | defer std.debug.unlockStderrWriter(); |
| 169 | renderToWriter(eb, options, bw) catch return; | |
| 169 | renderToWriter(eb, options, w) catch return; | |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *Writer) (Writer.Error || std.posix.UnexpectedError)!void { | |
| 172 | pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) (Writer.Error || std.posix.UnexpectedError)!void { | |
| 173 | 173 | if (eb.extra.len == 0) return; |
| 174 | 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 | } |
| 177 | 177 | |
| 178 | 178 | if (options.include_log_text) { |
| 179 | 179 | const log_text = eb.getCompileLogOutput(); |
| 180 | 180 | if (log_text.len != 0) { |
| 181 | try bw.writeAll("\nCompile Log Output:\n"); | |
| 182 | try bw.writeAll(log_text); | |
| 181 | try w.writeAll("\nCompile Log Output:\n"); | |
| 182 | try w.writeAll(log_text); | |
| 183 | 183 | } |
| 184 | 184 | } |
| 185 | 185 | } |
| ... | ... | @@ -188,73 +188,81 @@ fn renderErrorMessageToWriter( |
| 188 | 188 | eb: ErrorBundle, |
| 189 | 189 | options: RenderOptions, |
| 190 | 190 | err_msg_index: MessageIndex, |
| 191 | bw: *Writer, | |
| 191 | w: *Writer, | |
| 192 | 192 | kind: []const u8, |
| 193 | 193 | color: std.io.tty.Color, |
| 194 | 194 | indent: usize, |
| 195 | 195 | ) (Writer.Error || std.posix.UnexpectedError)!void { |
| 196 | 196 | const ttyconf = options.ttyconf; |
| 197 | 197 | const err_msg = eb.getErrorMessage(err_msg_index); |
| 198 | const prefix_start = bw.count; | |
| 199 | 198 | if (err_msg.src_loc != .none) { |
| 200 | 199 | const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); |
| 201 | try bw.splatByteAll(' ', indent); | |
| 202 | try ttyconf.setColor(bw, .bold); | |
| 203 | try bw.print("{s}:{d}:{d}: ", .{ | |
| 200 | var prefix: std.io.Writer.Discarding = .init(&.{}); | |
| 201 | try w.splatByteAll(' ', indent); | |
| 202 | prefix.count += indent; | |
| 203 | try ttyconf.setColor(w, .bold); | |
| 204 | try w.print("{s}:{d}:{d}: ", .{ | |
| 204 | 205 | eb.nullTerminatedString(src.data.src_path), |
| 205 | 206 | src.data.line + 1, |
| 206 | 207 | src.data.column + 1, |
| 207 | 208 | }); |
| 208 | try ttyconf.setColor(bw, color); | |
| 209 | try bw.writeAll(kind); | |
| 210 | try bw.writeAll(": "); | |
| 209 | try prefix.writer.print("{s}:{d}:{d}: ", .{ | |
| 210 | eb.nullTerminatedString(src.data.src_path), | |
| 211 | src.data.line + 1, | |
| 212 | src.data.column + 1, | |
| 213 | }); | |
| 214 | try ttyconf.setColor(w, color); | |
| 215 | try w.writeAll(kind); | |
| 216 | prefix.count += kind.len; | |
| 217 | try w.writeAll(": "); | |
| 218 | prefix.count += 2; | |
| 211 | 219 | // This is the length of the part before the error message: |
| 212 | 220 | // e.g. "file.zig:4:5: error: " |
| 213 | const prefix_len = bw.count - prefix_start; | |
| 214 | try ttyconf.setColor(bw, .reset); | |
| 215 | try ttyconf.setColor(bw, .bold); | |
| 221 | const prefix_len: usize = @intCast(prefix.count); | |
| 222 | try ttyconf.setColor(w, .reset); | |
| 223 | try ttyconf.setColor(w, .bold); | |
| 216 | 224 | if (err_msg.count == 1) { |
| 217 | try writeMsg(eb, err_msg, bw, prefix_len); | |
| 218 | try bw.writeByte('\n'); | |
| 225 | try writeMsg(eb, err_msg, w, prefix_len); | |
| 226 | try w.writeByte('\n'); | |
| 219 | 227 | } else { |
| 220 | try writeMsg(eb, err_msg, bw, prefix_len); | |
| 221 | try ttyconf.setColor(bw, .dim); | |
| 222 | try bw.print(" ({d} times)\n", .{err_msg.count}); | |
| 228 | try writeMsg(eb, err_msg, w, prefix_len); | |
| 229 | try ttyconf.setColor(w, .dim); | |
| 230 | try w.print(" ({d} times)\n", .{err_msg.count}); | |
| 223 | 231 | } |
| 224 | try ttyconf.setColor(bw, .reset); | |
| 232 | try ttyconf.setColor(w, .reset); | |
| 225 | 233 | if (src.data.source_line != 0 and options.include_source_line) { |
| 226 | 234 | const line = eb.nullTerminatedString(src.data.source_line); |
| 227 | 235 | for (line) |b| switch (b) { |
| 228 | '\t' => try bw.writeByte(' '), | |
| 229 | else => try bw.writeByte(b), | |
| 236 | '\t' => try w.writeByte(' '), | |
| 237 | else => try w.writeByte(b), | |
| 230 | 238 | }; |
| 231 | try bw.writeByte('\n'); | |
| 239 | try w.writeByte('\n'); | |
| 232 | 240 | // TODO basic unicode code point monospace width |
| 233 | 241 | const before_caret = src.data.span_main - src.data.span_start; |
| 234 | 242 | // -1 since span.main includes the caret |
| 235 | 243 | const after_caret = src.data.span_end -| src.data.span_main -| 1; |
| 236 | try bw.splatByteAll(' ', src.data.column - before_caret); | |
| 237 | try ttyconf.setColor(bw, .green); | |
| 238 | try bw.splatByteAll('~', before_caret); | |
| 239 | try bw.writeByte('^'); | |
| 240 | try bw.splatByteAll('~', after_caret); | |
| 241 | try bw.writeByte('\n'); | |
| 242 | try ttyconf.setColor(bw, .reset); | |
| 244 | try w.splatByteAll(' ', src.data.column - before_caret); | |
| 245 | try ttyconf.setColor(w, .green); | |
| 246 | try w.splatByteAll('~', before_caret); | |
| 247 | try w.writeByte('^'); | |
| 248 | try w.splatByteAll('~', after_caret); | |
| 249 | try w.writeByte('\n'); | |
| 250 | try ttyconf.setColor(w, .reset); | |
| 243 | 251 | } |
| 244 | 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 | 255 | if (src.data.reference_trace_len > 0 and options.include_reference_trace) { |
| 248 | try ttyconf.setColor(bw, .reset); | |
| 249 | try ttyconf.setColor(bw, .dim); | |
| 250 | try bw.print("referenced by:\n", .{}); | |
| 256 | try ttyconf.setColor(w, .reset); | |
| 257 | try ttyconf.setColor(w, .dim); | |
| 258 | try w.print("referenced by:\n", .{}); | |
| 251 | 259 | var ref_index = src.end; |
| 252 | 260 | for (0..src.data.reference_trace_len) |_| { |
| 253 | 261 | const ref_trace = eb.extraData(ReferenceTrace, ref_index); |
| 254 | 262 | ref_index = ref_trace.end; |
| 255 | 263 | if (ref_trace.data.src_loc != .none) { |
| 256 | 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 | 266 | eb.nullTerminatedString(ref_trace.data.decl_name), |
| 259 | 267 | eb.nullTerminatedString(ref_src.src_path), |
| 260 | 268 | ref_src.line + 1, |
| ... | ... | @@ -262,36 +270,36 @@ fn renderErrorMessageToWriter( |
| 262 | 270 | }); |
| 263 | 271 | } else if (ref_trace.data.decl_name != 0) { |
| 264 | 272 | const count = ref_trace.data.decl_name; |
| 265 | try bw.print( | |
| 273 | try w.print( | |
| 266 | 274 | " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n", |
| 267 | 275 | .{ count, count + src.data.reference_trace_len - 1 }, |
| 268 | 276 | ); |
| 269 | 277 | } else { |
| 270 | try bw.print( | |
| 278 | try w.print( | |
| 271 | 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 | 286 | } else { |
| 279 | try ttyconf.setColor(bw, color); | |
| 280 | try bw.splatByteAll(' ', indent); | |
| 281 | try bw.writeAll(kind); | |
| 282 | try bw.writeAll(": "); | |
| 283 | try ttyconf.setColor(bw, .reset); | |
| 287 | try ttyconf.setColor(w, color); | |
| 288 | try w.splatByteAll(' ', indent); | |
| 289 | try w.writeAll(kind); | |
| 290 | try w.writeAll(": "); | |
| 291 | try ttyconf.setColor(w, .reset); | |
| 284 | 292 | const msg = eb.nullTerminatedString(err_msg.msg); |
| 285 | 293 | if (err_msg.count == 1) { |
| 286 | try bw.print("{s}\n", .{msg}); | |
| 294 | try w.print("{s}\n", .{msg}); | |
| 287 | 295 | } else { |
| 288 | try bw.print("{s}", .{msg}); | |
| 289 | try ttyconf.setColor(bw, .dim); | |
| 290 | try bw.print(" ({d} times)\n", .{err_msg.count}); | |
| 296 | try w.print("{s}", .{msg}); | |
| 297 | try ttyconf.setColor(w, .dim); | |
| 298 | try w.print(" ({d} times)\n", .{err_msg.count}); | |
| 291 | 299 | } |
| 292 | try ttyconf.setColor(bw, .reset); | |
| 300 | try ttyconf.setColor(w, .reset); | |
| 293 | 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 | 308 | /// to allow for long, good-looking error messages. |
| 301 | 309 | /// |
| 302 | 310 | /// This is used to split the message in `@compileError("hello\nworld")` for example. |
| 303 | fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, bw: *Writer, indent: usize) !void { | |
| 311 | fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, w: *Writer, indent: usize) !void { | |
| 304 | 312 | var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n'); |
| 305 | 313 | while (lines.next()) |line| { |
| 306 | try bw.writeAll(line); | |
| 314 | try w.writeAll(line); | |
| 307 | 315 | if (lines.index == null) break; |
| 308 | try bw.writeByte('\n'); | |
| 309 | try bw.splatByteAll(' ', indent); | |
| 316 | try w.writeByte('\n'); | |
| 317 | try w.splatByteAll(' ', indent); | |
| 310 | 318 | } |
| 311 | 319 | } |
| 312 | 320 |
lib/std/zig/Parse.zig+2-90| ... | ... | @@ -359,16 +359,6 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { |
| 359 | 359 | } |
| 360 | 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 | 362 | .keyword_const, |
| 373 | 363 | .keyword_var, |
| 374 | 364 | .keyword_threadlocal, |
| ... | ... | @@ -496,7 +486,6 @@ fn findNextContainerMember(p: *Parse) void { |
| 496 | 486 | .keyword_extern, |
| 497 | 487 | .keyword_inline, |
| 498 | 488 | .keyword_noinline, |
| 499 | .keyword_usingnamespace, | |
| 500 | 489 | .keyword_threadlocal, |
| 501 | 490 | .keyword_const, |
| 502 | 491 | .keyword_var, |
| ... | ... | @@ -601,7 +590,6 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { |
| 601 | 590 | /// Decl |
| 602 | 591 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block) |
| 603 | 592 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 604 | /// / KEYWORD_usingnamespace Expr SEMICOLON | |
| 605 | 593 | fn expectTopLevelDecl(p: *Parse) !?Node.Index { |
| 606 | 594 | const extern_export_inline_token = p.nextToken(); |
| 607 | 595 | var is_extern: bool = false; |
| ... | ... | @@ -664,10 +652,7 @@ fn expectTopLevelDecl(p: *Parse) !?Node.Index { |
| 664 | 652 | if (expect_var_or_fn) { |
| 665 | 653 | return p.fail(.expected_var_decl_or_fn); |
| 666 | 654 | } |
| 667 | if (p.tokenTag(p.tok_i) != .keyword_usingnamespace) { | |
| 668 | return p.fail(.expected_pub_item); | |
| 669 | } | |
| 670 | return try p.expectUsingNamespace(); | |
| 655 | return p.fail(.expected_pub_item); | |
| 671 | 656 | } |
| 672 | 657 | |
| 673 | 658 | fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { |
| ... | ... | @@ -680,27 +665,6 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { |
| 680 | 665 | }; |
| 681 | 666 | } |
| 682 | 667 | |
| 683 | fn 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 | ||
| 694 | fn 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 | 668 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr |
| 705 | 669 | fn parseFnProto(p: *Parse) !?Node.Index { |
| 706 | 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 | 1652 | /// / MINUSPERCENT |
| 1689 | 1653 | /// / AMPERSAND |
| 1690 | 1654 | /// / KEYWORD_try |
| 1691 | /// / KEYWORD_await | |
| 1692 | 1655 | fn parsePrefixExpr(p: *Parse) Error!?Node.Index { |
| 1693 | 1656 | const tag: Node.Tag = switch (p.tokenTag(p.tok_i)) { |
| 1694 | 1657 | .bang => .bool_not, |
| ... | ... | @@ -1697,7 +1660,6 @@ fn parsePrefixExpr(p: *Parse) Error!?Node.Index { |
| 1697 | 1660 | .minus_percent => .negation_wrap, |
| 1698 | 1661 | .ampersand => .address_of, |
| 1699 | 1662 | .keyword_try => .@"try", |
| 1700 | .keyword_await => .@"await", | |
| 1701 | 1663 | else => return p.parsePrimaryExpr(), |
| 1702 | 1664 | }; |
| 1703 | 1665 | return try p.addNode(.{ |
| ... | ... | @@ -2385,62 +2347,12 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index { |
| 2385 | 2347 | } |
| 2386 | 2348 | |
| 2387 | 2349 | /// SuffixExpr |
| 2388 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments | |
| 2389 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* | |
| 2350 | /// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* | |
| 2390 | 2351 | /// |
| 2391 | 2352 | /// FnCallArguments <- LPAREN ExprList RPAREN |
| 2392 | 2353 | /// |
| 2393 | 2354 | /// ExprList <- (Expr COMMA)* Expr? |
| 2394 | 2355 | fn 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 | 2356 | var res = try p.parsePrimaryTypeExpr() orelse return null; |
| 2445 | 2357 | while (true) { |
| 2446 | 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 | 899 | type_name, |
| 900 | 900 | /// Implement builtin `@Frame`. Uses `un_node`. |
| 901 | 901 | frame_type, |
| 902 | /// Implement builtin `@frameSize`. Uses `un_node`. | |
| 903 | frame_size, | |
| 904 | 902 | |
| 905 | 903 | /// Implements the `@intFromFloat` builtin. |
| 906 | 904 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| ... | ... | @@ -1044,7 +1042,6 @@ pub const Inst = struct { |
| 1044 | 1042 | |
| 1045 | 1043 | /// Implements `resume` syntax. Uses `un_node` field. |
| 1046 | 1044 | @"resume", |
| 1047 | @"await", | |
| 1048 | 1045 | |
| 1049 | 1046 | /// A defer statement. |
| 1050 | 1047 | /// Uses the `defer` union field. |
| ... | ... | @@ -1241,7 +1238,6 @@ pub const Inst = struct { |
| 1241 | 1238 | .tag_name, |
| 1242 | 1239 | .type_name, |
| 1243 | 1240 | .frame_type, |
| 1244 | .frame_size, | |
| 1245 | 1241 | .int_from_float, |
| 1246 | 1242 | .float_from_int, |
| 1247 | 1243 | .ptr_from_int, |
| ... | ... | @@ -1279,7 +1275,6 @@ pub const Inst = struct { |
| 1279 | 1275 | .min, |
| 1280 | 1276 | .c_import, |
| 1281 | 1277 | .@"resume", |
| 1282 | .@"await", | |
| 1283 | 1278 | .ret_err_value_code, |
| 1284 | 1279 | .extended, |
| 1285 | 1280 | .ret_ptr, |
| ... | ... | @@ -1526,7 +1521,6 @@ pub const Inst = struct { |
| 1526 | 1521 | .tag_name, |
| 1527 | 1522 | .type_name, |
| 1528 | 1523 | .frame_type, |
| 1529 | .frame_size, | |
| 1530 | 1524 | .int_from_float, |
| 1531 | 1525 | .float_from_int, |
| 1532 | 1526 | .ptr_from_int, |
| ... | ... | @@ -1560,7 +1554,6 @@ pub const Inst = struct { |
| 1560 | 1554 | .min, |
| 1561 | 1555 | .c_import, |
| 1562 | 1556 | .@"resume", |
| 1563 | .@"await", | |
| 1564 | 1557 | .ret_err_value_code, |
| 1565 | 1558 | .@"break", |
| 1566 | 1559 | .break_inline, |
| ... | ... | @@ -1791,7 +1784,6 @@ pub const Inst = struct { |
| 1791 | 1784 | .tag_name = .un_node, |
| 1792 | 1785 | .type_name = .un_node, |
| 1793 | 1786 | .frame_type = .un_node, |
| 1794 | .frame_size = .un_node, | |
| 1795 | 1787 | |
| 1796 | 1788 | .int_from_float = .pl_node, |
| 1797 | 1789 | .float_from_int = .pl_node, |
| ... | ... | @@ -1852,7 +1844,6 @@ pub const Inst = struct { |
| 1852 | 1844 | .make_ptr_const = .un_node, |
| 1853 | 1845 | |
| 1854 | 1846 | .@"resume" = .un_node, |
| 1855 | .@"await" = .un_node, | |
| 1856 | 1847 | |
| 1857 | 1848 | .@"defer" = .@"defer", |
| 1858 | 1849 | .defer_err_code = .defer_err_code, |
| ... | ... | @@ -2016,8 +2007,6 @@ pub const Inst = struct { |
| 2016 | 2007 | /// Implements the `@errorCast` builtin. |
| 2017 | 2008 | /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. |
| 2018 | 2009 | error_cast, |
| 2019 | /// `operand` is payload index to `UnNode`. | |
| 2020 | await_nosuspend, | |
| 2021 | 2010 | /// Implements `@breakpoint`. |
| 2022 | 2011 | /// `operand` is `src_node: Ast.Node.Offset`. |
| 2023 | 2012 | breakpoint, |
| ... | ... | @@ -2038,9 +2027,6 @@ pub const Inst = struct { |
| 2038 | 2027 | /// `operand` is payload index to `Reify`. |
| 2039 | 2028 | /// `small` contains `NameStrategy`. |
| 2040 | 2029 | reify, |
| 2041 | /// Implements the `@asyncCall` builtin. | |
| 2042 | /// `operand` is payload index to `AsyncCall`. | |
| 2043 | builtin_async_call, | |
| 2044 | 2030 | /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins. |
| 2045 | 2031 | /// `small` 0=>weak 1=>strong |
| 2046 | 2032 | /// `operand` is payload index to `Cmpxchg`. |
| ... | ... | @@ -2689,7 +2675,6 @@ pub const Inst = struct { |
| 2689 | 2675 | @"test", |
| 2690 | 2676 | decltest, |
| 2691 | 2677 | @"comptime", |
| 2692 | @"usingnamespace", | |
| 2693 | 2678 | @"const", |
| 2694 | 2679 | @"var", |
| 2695 | 2680 | }; |
| ... | ... | @@ -2706,7 +2691,7 @@ pub const Inst = struct { |
| 2706 | 2691 | src_column: u32, |
| 2707 | 2692 | |
| 2708 | 2693 | kind: Kind, |
| 2709 | /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`. | |
| 2694 | /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"` | |
| 2710 | 2695 | name: NullTerminatedString, |
| 2711 | 2696 | /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`. |
| 2712 | 2697 | is_pub: bool, |
| ... | ... | @@ -2737,9 +2722,6 @@ pub const Inst = struct { |
| 2737 | 2722 | decltest, |
| 2738 | 2723 | @"comptime", |
| 2739 | 2724 | |
| 2740 | @"usingnamespace", | |
| 2741 | pub_usingnamespace, | |
| 2742 | ||
| 2743 | 2725 | const_simple, |
| 2744 | 2726 | const_typed, |
| 2745 | 2727 | @"const", |
| ... | ... | @@ -2776,8 +2758,6 @@ pub const Inst = struct { |
| 2776 | 2758 | return switch (id) { |
| 2777 | 2759 | .unnamed_test, |
| 2778 | 2760 | .@"comptime", |
| 2779 | .@"usingnamespace", | |
| 2780 | .pub_usingnamespace, | |
| 2781 | 2761 | => false, |
| 2782 | 2762 | else => true, |
| 2783 | 2763 | }; |
| ... | ... | @@ -2802,8 +2782,6 @@ pub const Inst = struct { |
| 2802 | 2782 | .@"test", |
| 2803 | 2783 | .decltest, |
| 2804 | 2784 | .@"comptime", |
| 2805 | .@"usingnamespace", | |
| 2806 | .pub_usingnamespace, | |
| 2807 | 2785 | => false, // these constructs are untyped |
| 2808 | 2786 | .const_simple, |
| 2809 | 2787 | .pub_const_simple, |
| ... | ... | @@ -2835,8 +2813,6 @@ pub const Inst = struct { |
| 2835 | 2813 | .@"test", |
| 2836 | 2814 | .decltest, |
| 2837 | 2815 | .@"comptime", |
| 2838 | .@"usingnamespace", | |
| 2839 | .pub_usingnamespace, | |
| 2840 | 2816 | => false, // these constructs are untyped |
| 2841 | 2817 | .const_simple, |
| 2842 | 2818 | .const_typed, |
| ... | ... | @@ -2879,7 +2855,6 @@ pub const Inst = struct { |
| 2879 | 2855 | .@"test" => .@"test", |
| 2880 | 2856 | .decltest => .decltest, |
| 2881 | 2857 | .@"comptime" => .@"comptime", |
| 2882 | .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace", | |
| 2883 | 2858 | .const_simple, |
| 2884 | 2859 | .const_typed, |
| 2885 | 2860 | .@"const", |
| ... | ... | @@ -2913,7 +2888,6 @@ pub const Inst = struct { |
| 2913 | 2888 | |
| 2914 | 2889 | pub fn isPub(id: Id) bool { |
| 2915 | 2890 | return switch (id) { |
| 2916 | .pub_usingnamespace, | |
| 2917 | 2891 | .pub_const_simple, |
| 2918 | 2892 | .pub_const_typed, |
| 2919 | 2893 | .pub_const, |
| ... | ... | @@ -2949,8 +2923,7 @@ pub const Inst = struct { |
| 2949 | 2923 | |
| 2950 | 2924 | pub const Name = enum(u32) { |
| 2951 | 2925 | @"comptime" = std.math.maxInt(u32), |
| 2952 | @"usingnamespace" = std.math.maxInt(u32) - 1, | |
| 2953 | unnamed_test = std.math.maxInt(u32) - 2, | |
| 2926 | unnamed_test = std.math.maxInt(u32) - 1, | |
| 2954 | 2927 | /// Other values are `NullTerminatedString` values, i.e. index into |
| 2955 | 2928 | /// `string_bytes`. If the byte referenced is 0, the decl is a named |
| 2956 | 2929 | /// test, and the actual name begins at the following byte. |
| ... | ... | @@ -2958,13 +2931,13 @@ pub const Inst = struct { |
| 2958 | 2931 | |
| 2959 | 2932 | pub fn isNamedTest(name: Name, zir: Zir) bool { |
| 2960 | 2933 | return switch (name) { |
| 2961 | .@"comptime", .@"usingnamespace", .unnamed_test => false, | |
| 2934 | .@"comptime", .unnamed_test => false, | |
| 2962 | 2935 | _ => zir.string_bytes[@intFromEnum(name)] == 0, |
| 2963 | 2936 | }; |
| 2964 | 2937 | } |
| 2965 | 2938 | pub fn toString(name: Name, zir: Zir) ?NullTerminatedString { |
| 2966 | 2939 | switch (name) { |
| 2967 | .@"comptime", .@"usingnamespace", .unnamed_test => return null, | |
| 2940 | .@"comptime", .unnamed_test => return null, | |
| 2968 | 2941 | _ => {}, |
| 2969 | 2942 | } |
| 2970 | 2943 | const idx: u32 = @intFromEnum(name); |
| ... | ... | @@ -3771,14 +3744,6 @@ pub const Inst = struct { |
| 3771 | 3744 | b: Ref, |
| 3772 | 3745 | }; |
| 3773 | 3746 | |
| 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 | 3747 | /// Trailing: inst: Index // for every body_len |
| 3783 | 3748 | pub const Param = struct { |
| 3784 | 3749 | /// Null-terminated string index. |
| ... | ... | @@ -4297,7 +4262,6 @@ fn findTrackableInner( |
| 4297 | 4262 | .tag_name, |
| 4298 | 4263 | .type_name, |
| 4299 | 4264 | .frame_type, |
| 4300 | .frame_size, | |
| 4301 | 4265 | .int_from_float, |
| 4302 | 4266 | .float_from_int, |
| 4303 | 4267 | .ptr_from_int, |
| ... | ... | @@ -4337,7 +4301,6 @@ fn findTrackableInner( |
| 4337 | 4301 | .resolve_inferred_alloc, |
| 4338 | 4302 | .make_ptr_const, |
| 4339 | 4303 | .@"resume", |
| 4340 | .@"await", | |
| 4341 | 4304 | .save_err_ret_index, |
| 4342 | 4305 | .restore_err_ret_index_unconditional, |
| 4343 | 4306 | .restore_err_ret_index_fn_entry, |
| ... | ... | @@ -4380,14 +4343,12 @@ fn findTrackableInner( |
| 4380 | 4343 | .prefetch, |
| 4381 | 4344 | .set_float_mode, |
| 4382 | 4345 | .error_cast, |
| 4383 | .await_nosuspend, | |
| 4384 | 4346 | .breakpoint, |
| 4385 | 4347 | .disable_instrumentation, |
| 4386 | 4348 | .disable_intrinsics, |
| 4387 | 4349 | .select, |
| 4388 | 4350 | .int_from_error, |
| 4389 | 4351 | .error_from_int, |
| 4390 | .builtin_async_call, | |
| 4391 | 4352 | .cmpxchg, |
| 4392 | 4353 | .c_va_arg, |
| 4393 | 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 | 100 | |
| 101 | 101 | switch (tree.nodeTag(node)) { |
| 102 | 102 | .root => unreachable, |
| 103 | .@"usingnamespace" => unreachable, | |
| 104 | 103 | .test_decl => unreachable, |
| 105 | 104 | .container_field_init => unreachable, |
| 106 | 105 | .container_field_align => unreachable, |
| ... | ... | @@ -204,12 +203,8 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator |
| 204 | 203 | |
| 205 | 204 | .call_one, |
| 206 | 205 | .call_one_comma, |
| 207 | .async_call_one, | |
| 208 | .async_call_one_comma, | |
| 209 | 206 | .call, |
| 210 | 207 | .call_comma, |
| 211 | .async_call, | |
| 212 | .async_call_comma, | |
| 213 | 208 | .@"return", |
| 214 | 209 | .if_simple, |
| 215 | 210 | .@"if", |
| ... | ... | @@ -226,7 +221,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator |
| 226 | 221 | .switch_comma, |
| 227 | 222 | .@"nosuspend", |
| 228 | 223 | .@"suspend", |
| 229 | .@"await", | |
| 230 | 224 | .@"resume", |
| 231 | 225 | .@"try", |
| 232 | 226 | .unreachable_literal, |
| ... | ... | @@ -776,13 +770,7 @@ fn lowerStrLitError( |
| 776 | 770 | raw_string: []const u8, |
| 777 | 771 | offset: u32, |
| 778 | 772 | ) Allocator.Error!void { |
| 779 | return ZonGen.addErrorTokOff( | |
| 780 | zg, | |
| 781 | token, | |
| 782 | @intCast(offset + err.offset()), | |
| 783 | "{f}", | |
| 784 | .{err.fmt(raw_string)}, | |
| 785 | ); | |
| 773 | return ZonGen.addErrorTokOff(zg, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)}); | |
| 786 | 774 | } |
| 787 | 775 | |
| 788 | 776 | fn 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 | const std = @import("../../std.zig"); | |
| 2 | const Allocator = std.mem.Allocator; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const bitcode_writer = @import("bitcode_writer.zig"); | |
| 5 | const Builder = @This(); | |
| 6 | const builtin = @import("builtin"); | |
| 7 | const DW = std.dwarf; | |
| 8 | const ir = @import("ir.zig"); | |
| 9 | const log = std.log.scoped(.llvm); | |
| 10 | const Writer = std.io.Writer; | |
| 11 | ||
| 1 | 12 | gpa: Allocator, |
| 2 | 13 | strip: bool, |
| 3 | 14 | |
| ... | ... | @@ -90,26 +101,38 @@ pub const String = enum(u32) { |
| 90 | 101 | const FormatData = struct { |
| 91 | 102 | string: String, |
| 92 | 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 { | |
| 95 | if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| | |
| 96 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); | |
| 106 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 97 | 107 | assert(data.string != .none); |
| 98 | 108 | const string_slice = data.string.slice(data.builder) orelse |
| 99 | return bw.print("{d}", .{@intFromEnum(data.string)}); | |
| 100 | if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| | |
| 101 | return bw.writeAll(string_slice); | |
| 102 | try printEscapedString( | |
| 103 | string_slice, | |
| 104 | if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| | |
| 105 | .always_quote | |
| 106 | else | |
| 107 | .quote_unless_valid_identifier, | |
| 108 | bw, | |
| 109 | ); | |
| 109 | return w.print("{d}", .{@intFromEnum(data.string)}); | |
| 110 | const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice); | |
| 111 | return printEscapedString(string_slice, quote_behavior, w); | |
| 112 | } | |
| 113 | ||
| 114 | pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) { | |
| 115 | return .{ .data = .{ | |
| 116 | .string = self, | |
| 117 | .builder = builder, | |
| 118 | .quote_behavior = .quote_unless_valid_identifier, | |
| 119 | } }; | |
| 110 | 120 | } |
| 111 | pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) { | |
| 112 | return .{ .data = .{ .string = self, .builder = builder } }; | |
| 121 | ||
| 122 | pub fn fmtQ(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) { | |
| 123 | return .{ .data = .{ | |
| 124 | .string = self, | |
| 125 | .builder = builder, | |
| 126 | .quote_behavior = .always_quote, | |
| 127 | } }; | |
| 128 | } | |
| 129 | ||
| 130 | pub fn fmtRaw(self: String, builder: *const Builder) std.fmt.Formatter(FormatData, format) { | |
| 131 | return .{ .data = .{ | |
| 132 | .string = self, | |
| 133 | .builder = builder, | |
| 134 | .quote_behavior = null, | |
| 135 | } }; | |
| 113 | 136 | } |
| 114 | 137 | |
| 115 | 138 | fn fromIndex(index: ?usize) String { |
| ... | ... | @@ -223,7 +246,7 @@ pub const Type = enum(u32) { |
| 223 | 246 | _, |
| 224 | 247 | |
| 225 | 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(" ")})); | |
| 227 | 250 | |
| 228 | 251 | pub const Tag = enum(u4) { |
| 229 | 252 | simple, |
| ... | ... | @@ -648,13 +671,16 @@ pub const Type = enum(u32) { |
| 648 | 671 | const FormatData = struct { |
| 649 | 672 | type: Type, |
| 650 | 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 | 679 | assert(data.type != .none); |
| 654 | if (comptime std.mem.eql(u8, fmt_str, "m")) { | |
| 680 | if (data.mode == .m) { | |
| 655 | 681 | const item = data.builder.type_items.items[@intFromEnum(data.type)]; |
| 656 | 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 | 684 | .void => "isVoid", |
| 659 | 685 | .half => "f16", |
| 660 | 686 | .bfloat => "bf16", |
| ... | ... | @@ -671,36 +697,36 @@ pub const Type = enum(u32) { |
| 671 | 697 | .function, .vararg_function => |kind| { |
| 672 | 698 | var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); |
| 673 | 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)}); | |
| 675 | for (params) |param| try bw.print("{fm}", .{param.fmt(data.builder)}); | |
| 700 | try w.print("f_{f}", .{extra.data.ret.fmt(data.builder, .m)}); | |
| 701 | for (params) |param| try w.print("{f}", .{param.fmt(data.builder, .m)}); | |
| 676 | 702 | switch (kind) { |
| 677 | 703 | .function => {}, |
| 678 | .vararg_function => try bw.writeAll("vararg"), | |
| 704 | .vararg_function => try w.writeAll("vararg"), | |
| 679 | 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}), | |
| 684 | .pointer => try bw.print("p{d}", .{item.data}), | |
| 709 | .integer => try w.print("i{d}", .{item.data}), | |
| 710 | .pointer => try w.print("p{d}", .{item.data}), | |
| 685 | 711 | .target => { |
| 686 | 712 | var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); |
| 687 | 713 | const types = extra.trail.next(extra.data.types_len, Type, data.builder); |
| 688 | 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).?}); | |
| 690 | for (types) |ty| try bw.print("_{fm}", .{ty.fmt(data.builder)}); | |
| 691 | for (ints) |int| try bw.print("_{d}", .{int}); | |
| 692 | try bw.writeByte('t'); | |
| 715 | try w.print("t{s}", .{extra.data.name.slice(data.builder).?}); | |
| 716 | for (types) |ty| try w.print("_{f}", .{ty.fmt(data.builder, .m)}); | |
| 717 | for (ints) |int| try w.print("_{d}", .{int}); | |
| 718 | try w.writeByte('t'); | |
| 693 | 719 | }, |
| 694 | 720 | .vector, .scalable_vector => |kind| { |
| 695 | 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 | 723 | switch (kind) { |
| 698 | 724 | .vector => "", |
| 699 | 725 | .scalable_vector => "nx", |
| 700 | 726 | else => unreachable, |
| 701 | 727 | }, |
| 702 | 728 | extra.len, |
| 703 | extra.child.fmt(data.builder), | |
| 729 | extra.child.fmt(data.builder, .m), | |
| 704 | 730 | }); |
| 705 | 731 | }, |
| 706 | 732 | inline .small_array, .array => |kind| { |
| ... | ... | @@ -709,72 +735,72 @@ pub const Type = enum(u32) { |
| 709 | 735 | .array => Type.Array, |
| 710 | 736 | else => unreachable, |
| 711 | 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 | 740 | .structure, .packed_structure => { |
| 715 | 741 | var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); |
| 716 | 742 | const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); |
| 717 | try bw.writeAll("sl_"); | |
| 718 | for (fields) |field| try bw.print("{fm}", .{field.fmt(data.builder)}); | |
| 719 | try bw.writeByte('s'); | |
| 743 | try w.writeAll("sl_"); | |
| 744 | for (fields) |field| try w.print("{f}", .{field.fmt(data.builder, .m)}); | |
| 745 | try w.writeByte('s'); | |
| 720 | 746 | }, |
| 721 | 747 | .named_structure => { |
| 722 | 748 | const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); |
| 723 | try bw.writeAll("s_"); | |
| 724 | if (extra.id.slice(data.builder)) |id| try bw.writeAll(id); | |
| 749 | try w.writeAll("s_"); | |
| 750 | if (extra.id.slice(data.builder)) |id| try w.writeAll(id); | |
| 725 | 751 | }, |
| 726 | 752 | } |
| 727 | 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 | 756 | const item = data.builder.type_items.items[@intFromEnum(data.type)]; |
| 731 | 757 | switch (item.tag) { |
| 732 | 758 | .simple => unreachable, |
| 733 | 759 | .function, .vararg_function => |kind| { |
| 734 | 760 | var extra = data.builder.typeExtraDataTrail(Type.Function, item.data); |
| 735 | 761 | const params = extra.trail.next(extra.data.params_len, Type, data.builder); |
| 736 | if (!comptime std.mem.eql(u8, fmt_str, ">")) | |
| 737 | try bw.print("{f%} ", .{extra.data.ret.fmt(data.builder)}); | |
| 738 | if (!comptime std.mem.eql(u8, fmt_str, "<")) { | |
| 739 | try bw.writeByte('('); | |
| 762 | if (data.mode != .gt) | |
| 763 | try w.print("{f} ", .{extra.data.ret.fmt(data.builder, .percent)}); | |
| 764 | if (data.mode != .lt) { | |
| 765 | try w.writeByte('('); | |
| 740 | 766 | for (params, 0..) |param, index| { |
| 741 | if (index > 0) try bw.writeAll(", "); | |
| 742 | try bw.print("{f%}", .{param.fmt(data.builder)}); | |
| 767 | if (index > 0) try w.writeAll(", "); | |
| 768 | try w.print("{f}", .{param.fmt(data.builder, .percent)}); | |
| 743 | 769 | } |
| 744 | 770 | switch (kind) { |
| 745 | 771 | .function => {}, |
| 746 | 772 | .vararg_function => { |
| 747 | if (params.len > 0) try bw.writeAll(", "); | |
| 748 | try bw.writeAll("..."); | |
| 773 | if (params.len > 0) try w.writeAll(", "); | |
| 774 | try w.writeAll("..."); | |
| 749 | 775 | }, |
| 750 | 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}), | |
| 756 | .pointer => try bw.print("ptr{f }", .{@as(AddrSpace, @enumFromInt(item.data))}), | |
| 781 | .integer => try w.print("i{d}", .{item.data}), | |
| 782 | .pointer => try w.print("ptr{f}", .{@as(AddrSpace, @enumFromInt(item.data)).fmt(" ")}), | |
| 757 | 783 | .target => { |
| 758 | 784 | var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); |
| 759 | 785 | const types = extra.trail.next(extra.data.types_len, Type, data.builder); |
| 760 | 786 | const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); |
| 761 | try bw.print( | |
| 762 | \\target({f"} | |
| 763 | , .{extra.data.name.fmt(data.builder)}); | |
| 764 | for (types) |ty| try bw.print(", {f%}", .{ty.fmt(data.builder)}); | |
| 765 | for (ints) |int| try bw.print(", {d}", .{int}); | |
| 766 | try bw.writeByte(')'); | |
| 787 | try w.print( | |
| 788 | \\target({f} | |
| 789 | , .{extra.data.name.fmtQ(data.builder)}); | |
| 790 | for (types) |ty| try w.print(", {f}", .{ty.fmt(data.builder, .percent)}); | |
| 791 | for (ints) |int| try w.print(", {d}", .{int}); | |
| 792 | try w.writeByte(')'); | |
| 767 | 793 | }, |
| 768 | 794 | .vector, .scalable_vector => |kind| { |
| 769 | 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 | 797 | switch (kind) { |
| 772 | 798 | .vector => "", |
| 773 | 799 | .scalable_vector => "vscale x ", |
| 774 | 800 | else => unreachable, |
| 775 | 801 | }, |
| 776 | 802 | extra.len, |
| 777 | extra.child.fmt(data.builder), | |
| 803 | extra.child.fmt(data.builder, .percent), | |
| 778 | 804 | }); |
| 779 | 805 | }, |
| 780 | 806 | inline .small_array, .array => |kind| { |
| ... | ... | @@ -783,44 +809,45 @@ pub const Type = enum(u32) { |
| 783 | 809 | .array => Type.Array, |
| 784 | 810 | else => unreachable, |
| 785 | 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 | 814 | .structure, .packed_structure => |kind| { |
| 789 | 815 | var extra = data.builder.typeExtraDataTrail(Type.Structure, item.data); |
| 790 | 816 | const fields = extra.trail.next(extra.data.fields_len, Type, data.builder); |
| 791 | 817 | switch (kind) { |
| 792 | 818 | .structure => {}, |
| 793 | .packed_structure => try bw.writeByte('<'), | |
| 819 | .packed_structure => try w.writeByte('<'), | |
| 794 | 820 | else => unreachable, |
| 795 | 821 | } |
| 796 | try bw.writeAll("{ "); | |
| 822 | try w.writeAll("{ "); | |
| 797 | 823 | for (fields, 0..) |field, index| { |
| 798 | if (index > 0) try bw.writeAll(", "); | |
| 799 | try bw.print("{f%}", .{field.fmt(data.builder)}); | |
| 824 | if (index > 0) try w.writeAll(", "); | |
| 825 | try w.print("{f}", .{field.fmt(data.builder, .percent)}); | |
| 800 | 826 | } |
| 801 | try bw.writeAll(" }"); | |
| 827 | try w.writeAll(" }"); | |
| 802 | 828 | switch (kind) { |
| 803 | 829 | .structure => {}, |
| 804 | .packed_structure => try bw.writeByte('>'), | |
| 830 | .packed_structure => try w.writeByte('>'), | |
| 805 | 831 | else => unreachable, |
| 806 | 832 | } |
| 807 | 833 | }, |
| 808 | 834 | .named_structure => { |
| 809 | 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 | 837 | extra.id.fmt(data.builder), |
| 812 | 838 | }) else switch (extra.body) { |
| 813 | .none => try bw.writeAll("opaque"), | |
| 839 | .none => try w.writeAll("opaque"), | |
| 814 | 840 | else => try format(.{ |
| 815 | 841 | .type = extra.body, |
| 816 | 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) { | |
| 823 | return .{ .data = .{ .type = self, .builder = builder } }; | |
| 849 | pub fn fmt(self: Type, builder: *const Builder, mode: FormatData.Mode) std.fmt.Formatter(FormatData, format) { | |
| 850 | return .{ .data = .{ .type = self, .builder = builder, .mode = mode } }; | |
| 824 | 851 | } |
| 825 | 852 | |
| 826 | 853 | const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void); |
| ... | ... | @@ -1128,10 +1155,13 @@ pub const Attribute = union(Kind) { |
| 1128 | 1155 | const FormatData = struct { |
| 1129 | 1156 | attribute_index: Index, |
| 1130 | 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 { | |
| 1133 | if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_| | |
| 1134 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); | |
| 1164 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 1135 | 1165 | const attribute = data.attribute_index.toAttribute(data.builder); |
| 1136 | 1166 | switch (attribute) { |
| 1137 | 1167 | .zeroext, |
| ... | ... | @@ -1204,97 +1234,99 @@ pub const Attribute = union(Kind) { |
| 1204 | 1234 | .no_sanitize_address, |
| 1205 | 1235 | .no_sanitize_hwaddress, |
| 1206 | 1236 | .sanitize_address_dyninit, |
| 1207 | => try bw.print(" {s}", .{@tagName(attribute)}), | |
| 1237 | => try w.print(" {s}", .{@tagName(attribute)}), | |
| 1208 | 1238 | .byval, |
| 1209 | 1239 | .byref, |
| 1210 | 1240 | .preallocated, |
| 1211 | 1241 | .inalloca, |
| 1212 | 1242 | .sret, |
| 1213 | 1243 | .elementtype, |
| 1214 | => |ty| try bw.print(" {s}({f%})", .{ @tagName(attribute), ty.fmt(data.builder) }), | |
| 1215 | .@"align" => |alignment| try bw.print("{f }", .{alignment}), | |
| 1244 | => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }), | |
| 1245 | .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}), | |
| 1216 | 1246 | .dereferenceable, |
| 1217 | 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 | 1249 | .nofpclass => |fpclass| { |
| 1220 | 1250 | const Int = @typeInfo(FpClass).@"struct".backing_integer.?; |
| 1221 | try bw.print(" {s}(", .{@tagName(attribute)}); | |
| 1251 | try w.print(" {s}(", .{@tagName(attribute)}); | |
| 1222 | 1252 | var any = false; |
| 1223 | 1253 | var remaining: Int = @bitCast(fpclass); |
| 1224 | 1254 | inline for (@typeInfo(FpClass).@"struct".decls) |decl| { |
| 1225 | 1255 | const pattern: Int = @bitCast(@field(FpClass, decl.name)); |
| 1226 | 1256 | if (remaining & pattern == pattern) { |
| 1227 | 1257 | if (!any) { |
| 1228 | try bw.writeByte(' '); | |
| 1258 | try w.writeByte(' '); | |
| 1229 | 1259 | any = true; |
| 1230 | 1260 | } |
| 1231 | try bw.writeAll(decl.name); | |
| 1261 | try w.writeAll(decl.name); | |
| 1232 | 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 | 1276 | .allockind => |allockind| { |
| 1245 | try bw.print(" {s}(\"", .{@tagName(attribute)}); | |
| 1277 | try w.print(" {t}(\"", .{attribute}); | |
| 1246 | 1278 | var any = false; |
| 1247 | 1279 | inline for (@typeInfo(AllocKind).@"struct".fields) |field| { |
| 1248 | 1280 | if (comptime std.mem.eql(u8, field.name, "_")) continue; |
| 1249 | 1281 | if (@field(allockind, field.name)) { |
| 1250 | 1282 | if (!any) { |
| 1251 | try bw.writeByte(','); | |
| 1283 | try w.writeByte(','); | |
| 1252 | 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 | 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 | 1293 | if (allocsize.num_elems != AllocSize.none) |
| 1262 | try bw.print(",{d}", .{allocsize.num_elems}); | |
| 1263 | try bw.writeByte(')'); | |
| 1294 | try w.print(",{d}", .{allocsize.num_elems}); | |
| 1295 | try w.writeByte(')'); | |
| 1264 | 1296 | }, |
| 1265 | 1297 | .memory => |memory| { |
| 1266 | try bw.print(" {s}(", .{@tagName(attribute)}); | |
| 1298 | try w.print(" {t}(", .{attribute}); | |
| 1267 | 1299 | var any = memory.other != .none or |
| 1268 | 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 | 1302 | inline for (.{ "argmem", "inaccessiblemem" }) |kind| { |
| 1271 | 1303 | if (@field(memory, kind) != memory.other) { |
| 1272 | if (any) try bw.writeAll(", "); | |
| 1273 | try bw.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) }); | |
| 1304 | if (any) try w.writeAll(", "); | |
| 1305 | try w.print("{s}: {s}", .{ kind, @tagName(@field(memory, kind)) }); | |
| 1274 | 1306 | any = true; |
| 1275 | 1307 | } |
| 1276 | 1308 | } |
| 1277 | try bw.writeByte(')'); | |
| 1309 | try w.writeByte(')'); | |
| 1278 | 1310 | }, |
| 1279 | 1311 | .uwtable => |uwtable| if (uwtable != .none) { |
| 1280 | try bw.print(" {s}", .{@tagName(attribute)}); | |
| 1281 | if (uwtable != UwTable.default) try bw.print("({s})", .{@tagName(uwtable)}); | |
| 1312 | try w.print(" {s}", .{@tagName(attribute)}); | |
| 1313 | if (uwtable != UwTable.default) try w.print("({s})", .{@tagName(uwtable)}); | |
| 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 | 1316 | @tagName(attribute), |
| 1285 | 1317 | vscale_range.min.toByteUnits().?, |
| 1286 | 1318 | vscale_range.max.toByteUnits() orelse 0, |
| 1287 | 1319 | }), |
| 1288 | .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { | |
| 1289 | try bw.print(" {f\"}", .{string_attr.kind.fmt(data.builder)}); | |
| 1320 | .string => |string_attr| if (data.flags.quote) { | |
| 1321 | try w.print(" {f}", .{string_attr.kind.fmtQ(data.builder)}); | |
| 1290 | 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 | 1325 | .none => unreachable, |
| 1294 | 1326 | } |
| 1295 | 1327 | } |
| 1296 | pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { | |
| 1297 | return .{ .data = .{ .attribute_index = self, .builder = builder } }; | |
| 1328 | pub fn fmt(self: Index, builder: *const Builder, mode: FormatData.mode) std.fmt.Formatter(FormatData, format) { | |
| 1329 | return .{ .data = .{ .attribute_index = self, .builder = builder, .mode = mode } }; | |
| 1298 | 1330 | } |
| 1299 | 1331 | |
| 1300 | 1332 | fn toStorage(self: Index, builder: *const Builder) Storage { |
| ... | ... | @@ -1506,9 +1538,9 @@ pub const Attribute = union(Kind) { |
| 1506 | 1538 | pub const UwTable = enum(u32) { |
| 1507 | 1539 | none, |
| 1508 | 1540 | sync, |
| 1509 | @"async", | |
| 1541 | async, | |
| 1510 | 1542 | |
| 1511 | pub const default = UwTable.@"async"; | |
| 1543 | pub const default = UwTable.async; | |
| 1512 | 1544 | }; |
| 1513 | 1545 | |
| 1514 | 1546 | pub const VScaleRange = packed struct(u32) { |
| ... | ... | @@ -1567,15 +1599,18 @@ pub const Attributes = enum(u32) { |
| 1567 | 1599 | const FormatData = struct { |
| 1568 | 1600 | attributes: Attributes, |
| 1569 | 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 | 1606 | for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{ |
| 1573 | 1607 | .attribute_index = attribute_index, |
| 1574 | 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) { | |
| 1578 | return .{ .data = .{ .attributes = self, .builder = builder } }; | |
| 1612 | pub fn fmt(self: Attributes, builder: *const Builder, flags: FormatData.Flags) std.fmt.Formatter(FormatData, format) { | |
| 1613 | return .{ .data = .{ .attributes = self, .builder = builder, .flags = flags } }; | |
| 1579 | 1614 | } |
| 1580 | 1615 | }; |
| 1581 | 1616 | |
| ... | ... | @@ -1761,14 +1796,14 @@ pub const Linkage = enum(u4) { |
| 1761 | 1796 | extern_weak = 7, |
| 1762 | 1797 | external = 0, |
| 1763 | 1798 | |
| 1764 | pub fn format(self: Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1765 | if (self != .external) try bw.print(" {s}", .{@tagName(self)}); | |
| 1799 | pub fn format(self: Linkage, w: *Writer) Writer.Error!void { | |
| 1800 | if (self != .external) try w.print(" {s}", .{@tagName(self)}); | |
| 1766 | 1801 | } |
| 1767 | 1802 | |
| 1768 | fn formatOptional(data: ?Linkage, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1769 | if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)}); | |
| 1803 | fn formatOptional(data: ?Linkage, w: *Writer) Writer.Error!void { | |
| 1804 | if (data) |linkage| try w.print(" {s}", .{@tagName(linkage)}); | |
| 1770 | 1805 | } |
| 1771 | pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) { | |
| 1806 | pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(?Linkage, formatOptional) { | |
| 1772 | 1807 | return .{ .data = self }; |
| 1773 | 1808 | } |
| 1774 | 1809 | }; |
| ... | ... | @@ -1778,8 +1813,8 @@ pub const Preemption = enum { |
| 1778 | 1813 | dso_local, |
| 1779 | 1814 | implicit_dso_local, |
| 1780 | 1815 | |
| 1781 | pub fn format(self: Preemption, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1782 | if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)}); | |
| 1816 | pub fn format(self: Preemption, w: *Writer) Writer.Error!void { | |
| 1817 | if (self == .dso_local) try w.print(" {s}", .{@tagName(self)}); | |
| 1783 | 1818 | } |
| 1784 | 1819 | }; |
| 1785 | 1820 | |
| ... | ... | @@ -1796,8 +1831,7 @@ pub const Visibility = enum(u2) { |
| 1796 | 1831 | }; |
| 1797 | 1832 | } |
| 1798 | 1833 | |
| 1799 | pub fn format(self: Visibility, comptime format_string: []const u8, writer: *Writer) Writer.Error!void { | |
| 1800 | comptime assert(format_string.len == 0); | |
| 1834 | pub fn format(self: Visibility, writer: *Writer) Writer.Error!void { | |
| 1801 | 1835 | if (self != .default) try writer.print(" {s}", .{@tagName(self)}); |
| 1802 | 1836 | } |
| 1803 | 1837 | }; |
| ... | ... | @@ -1807,8 +1841,8 @@ pub const DllStorageClass = enum(u2) { |
| 1807 | 1841 | dllimport = 1, |
| 1808 | 1842 | dllexport = 2, |
| 1809 | 1843 | |
| 1810 | pub fn format(self: DllStorageClass, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1811 | if (self != .default) try bw.print(" {s}", .{@tagName(self)}); | |
| 1844 | pub fn format(self: DllStorageClass, w: *Writer) Writer.Error!void { | |
| 1845 | if (self != .default) try w.print(" {s}", .{@tagName(self)}); | |
| 1812 | 1846 | } |
| 1813 | 1847 | }; |
| 1814 | 1848 | |
| ... | ... | @@ -1819,10 +1853,31 @@ pub const ThreadLocal = enum(u3) { |
| 1819 | 1853 | initialexec = 3, |
| 1820 | 1854 | localexec = 4, |
| 1821 | 1855 | |
| 1822 | pub fn format(self: ThreadLocal, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { | |
| 1823 | if (self == .default) return; | |
| 1824 | try bw.print("{s}thread_local", .{prefix}); | |
| 1825 | if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)}); | |
| 1856 | pub fn format(tl: ThreadLocal, w: *Writer) Writer.Error!void { | |
| 1857 | return Prefixed.format(.{ .thread_local = tl, .prefix = "" }, w); | |
| 1858 | } | |
| 1859 | ||
| 1860 | pub const Prefixed = struct { | |
| 1861 | thread_local: ThreadLocal, | |
| 1862 | prefix: []const u8, | |
| 1863 | ||
| 1864 | pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { | |
| 1865 | switch (p.thread_local) { | |
| 1866 | .default => return, | |
| 1867 | .generaldynamic => { | |
| 1868 | var vecs: [2][]const u8 = .{ p.prefix, "thread_local" }; | |
| 1869 | return w.writeVecAll(&vecs); | |
| 1870 | }, | |
| 1871 | else => { | |
| 1872 | var vecs: [4][]const u8 = .{ p.prefix, "thread_local(", @tagName(p.thread_local), ")" }; | |
| 1873 | return w.writeVecAll(&vecs); | |
| 1874 | }, | |
| 1875 | } | |
| 1876 | } | |
| 1877 | }; | |
| 1878 | ||
| 1879 | pub fn fmt(tl: ThreadLocal, prefix: []const u8) Prefixed { | |
| 1880 | return .{ .thread_local = tl, .prefix = prefix }; | |
| 1826 | 1881 | } |
| 1827 | 1882 | }; |
| 1828 | 1883 | |
| ... | ... | @@ -1833,8 +1888,8 @@ pub const UnnamedAddr = enum(u2) { |
| 1833 | 1888 | unnamed_addr = 1, |
| 1834 | 1889 | local_unnamed_addr = 2, |
| 1835 | 1890 | |
| 1836 | pub fn format(self: UnnamedAddr, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1837 | if (self != .default) try bw.print(" {s}", .{@tagName(self)}); | |
| 1891 | pub fn format(self: UnnamedAddr, w: *Writer) Writer.Error!void { | |
| 1892 | if (self != .default) try w.print(" {s}", .{@tagName(self)}); | |
| 1838 | 1893 | } |
| 1839 | 1894 | }; |
| 1840 | 1895 | |
| ... | ... | @@ -1927,8 +1982,24 @@ pub const AddrSpace = enum(u24) { |
| 1927 | 1982 | pub const funcref: AddrSpace = @enumFromInt(20); |
| 1928 | 1983 | }; |
| 1929 | 1984 | |
| 1930 | pub fn format(self: AddrSpace, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { | |
| 1931 | if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); | |
| 1985 | pub fn format(addr_space: AddrSpace, w: *Writer) Writer.Error!void { | |
| 1986 | return Prefixed.format(.{ .addr_space = addr_space, .prefix = "" }, w); | |
| 1987 | } | |
| 1988 | ||
| 1989 | pub const Prefixed = struct { | |
| 1990 | addr_space: AddrSpace, | |
| 1991 | prefix: []const u8, | |
| 1992 | ||
| 1993 | pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { | |
| 1994 | switch (p.addr_space) { | |
| 1995 | .default => return, | |
| 1996 | else => return w.print("{s}addrspace({d})", .{ p.prefix, p.addr_space }), | |
| 1997 | } | |
| 1998 | } | |
| 1999 | }; | |
| 2000 | ||
| 2001 | pub fn fmt(addr_space: AddrSpace, prefix: []const u8) Prefixed { | |
| 2002 | return .{ .addr_space = addr_space, .prefix = prefix }; | |
| 1932 | 2003 | } |
| 1933 | 2004 | }; |
| 1934 | 2005 | |
| ... | ... | @@ -1936,8 +2007,8 @@ pub const ExternallyInitialized = enum { |
| 1936 | 2007 | default, |
| 1937 | 2008 | externally_initialized, |
| 1938 | 2009 | |
| 1939 | pub fn format(self: ExternallyInitialized, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1940 | if (self != .default) try bw.print(" {s}", .{@tagName(self)}); | |
| 2010 | pub fn format(self: ExternallyInitialized, w: *Writer) Writer.Error!void { | |
| 2011 | if (self != .default) try w.print(" {s}", .{@tagName(self)}); | |
| 1941 | 2012 | } |
| 1942 | 2013 | }; |
| 1943 | 2014 | |
| ... | ... | @@ -1960,8 +2031,18 @@ pub const Alignment = enum(u6) { |
| 1960 | 2031 | return if (self == .default) 0 else (@intFromEnum(self) + 1); |
| 1961 | 2032 | } |
| 1962 | 2033 | |
| 1963 | pub fn format(self: Alignment, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { | |
| 1964 | try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); | |
| 2034 | pub const Prefixed = struct { | |
| 2035 | alignment: Alignment, | |
| 2036 | prefix: []const u8, | |
| 2037 | ||
| 2038 | pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { | |
| 2039 | const byte_units = p.alignment.toByteUnits() orelse return; | |
| 2040 | return w.print("{s}align ({d})", .{ p.prefix, byte_units }); | |
| 2041 | } | |
| 2042 | }; | |
| 2043 | ||
| 2044 | pub fn fmt(alignment: Alignment, prefix: []const u8) Prefixed { | |
| 2045 | return .{ .alignment = alignment, .prefix = prefix }; | |
| 1965 | 2046 | } |
| 1966 | 2047 | }; |
| 1967 | 2048 | |
| ... | ... | @@ -2034,7 +2115,7 @@ pub const CallConv = enum(u10) { |
| 2034 | 2115 | |
| 2035 | 2116 | pub const default = CallConv.ccc; |
| 2036 | 2117 | |
| 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 | 2119 | switch (self) { |
| 2039 | 2120 | default => {}, |
| 2040 | 2121 | .fastcc, |
| ... | ... | @@ -2088,8 +2169,8 @@ pub const CallConv = enum(u10) { |
| 2088 | 2169 | .aarch64_sme_preservemost_from_x2, |
| 2089 | 2170 | .m68k_rtdcc, |
| 2090 | 2171 | .riscv_vectorcallcc, |
| 2091 | => try bw.print(" {s}", .{@tagName(self)}), | |
| 2092 | _ => try bw.print(" cc{d}", .{@intFromEnum(self)}), | |
| 2172 | => try w.print(" {s}", .{@tagName(self)}), | |
| 2173 | _ => try w.print(" cc{d}", .{@intFromEnum(self)}), | |
| 2093 | 2174 | } |
| 2094 | 2175 | } |
| 2095 | 2176 | }; |
| ... | ... | @@ -2114,26 +2195,25 @@ pub const StrtabString = enum(u32) { |
| 2114 | 2195 | const FormatData = struct { |
| 2115 | 2196 | string: StrtabString, |
| 2116 | 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 { | |
| 2119 | if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| | |
| 2120 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); | |
| 2200 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 2121 | 2201 | assert(data.string != .none); |
| 2122 | 2202 | const string_slice = data.string.slice(data.builder) orelse |
| 2123 | return bw.print("{d}", .{@intFromEnum(data.string)}); | |
| 2124 | if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| | |
| 2125 | return bw.writeAll(string_slice); | |
| 2126 | try printEscapedString( | |
| 2127 | string_slice, | |
| 2128 | if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| | |
| 2129 | .always_quote | |
| 2130 | else | |
| 2131 | .quote_unless_valid_identifier, | |
| 2132 | bw, | |
| 2133 | ); | |
| 2203 | return w.print("{d}", .{@intFromEnum(data.string)}); | |
| 2204 | const quote_behavior = data.quote_behavior orelse return w.writeAll(string_slice); | |
| 2205 | return printEscapedString(string_slice, quote_behavior, w); | |
| 2134 | 2206 | } |
| 2135 | pub fn fmt(self: StrtabString, builder: *const Builder) std.fmt.Formatter(format) { | |
| 2136 | return .{ .data = .{ .string = self, .builder = builder } }; | |
| 2207 | pub fn fmt( | |
| 2208 | self: StrtabString, | |
| 2209 | builder: *const Builder, | |
| 2210 | quote_behavior: ?QuoteBehavior, | |
| 2211 | ) std.fmt.Formatter(FormatData, format) { | |
| 2212 | return .{ .data = .{ | |
| 2213 | .string = self, | |
| 2214 | .builder = builder, | |
| 2215 | .quote_behavior = quote_behavior, | |
| 2216 | } }; | |
| 2137 | 2217 | } |
| 2138 | 2218 | |
| 2139 | 2219 | fn fromIndex(index: ?usize) StrtabString { |
| ... | ... | @@ -2302,12 +2382,12 @@ pub const Global = struct { |
| 2302 | 2382 | global: Index, |
| 2303 | 2383 | builder: *const Builder, |
| 2304 | 2384 | }; |
| 2305 | fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 2306 | try bw.print("@{f}", .{ | |
| 2307 | data.global.unwrap(data.builder).name(data.builder).fmt(data.builder), | |
| 2385 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 2386 | try w.print("@{f}", .{ | |
| 2387 | data.global.unwrap(data.builder).name(data.builder).fmt(data.builder, null), | |
| 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 | 2391 | return .{ .data = .{ .global = self, .builder = builder } }; |
| 2312 | 2392 | } |
| 2313 | 2393 | |
| ... | ... | @@ -4747,24 +4827,23 @@ pub const Function = struct { |
| 4747 | 4827 | instruction: Instruction.Index, |
| 4748 | 4828 | function: Function.Index, |
| 4749 | 4829 | builder: *Builder, |
| 4830 | flags: FormatFlags, | |
| 4750 | 4831 | }; |
| 4751 | fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { | |
| 4752 | if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| | |
| 4753 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); | |
| 4754 | if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { | |
| 4832 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 4833 | if (data.flags.comma) { | |
| 4755 | 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 | 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( | |
| 4763 | "{f%} ", | |
| 4764 | .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder)}, | |
| 4841 | if (data.flags.percent) try w.print( | |
| 4842 | "{f} ", | |
| 4843 | .{data.instruction.typeOf(data.function, data.builder).fmt(data.builder, .percent)}, | |
| 4765 | 4844 | ); |
| 4766 | 4845 | assert(data.instruction != .none); |
| 4767 | try bw.print("%{f}", .{ | |
| 4846 | try w.print("%{f}", .{ | |
| 4768 | 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 | 4851 | self: Instruction.Index, |
| 4773 | 4852 | function: Function.Index, |
| 4774 | 4853 | builder: *Builder, |
| 4775 | ) std.fmt.Formatter(format) { | |
| 4776 | return .{ .data = .{ .instruction = self, .function = function, .builder = builder } }; | |
| 4854 | flags: FormatFlags, | |
| 4855 | ) std.fmt.Formatter(FormatData, format) { | |
| 4856 | return .{ .data = .{ | |
| 4857 | .instruction = self, | |
| 4858 | .function = function, | |
| 4859 | .builder = builder, | |
| 4860 | .flags = flags, | |
| 4861 | } }; | |
| 4777 | 4862 | } |
| 4778 | 4863 | }; |
| 4779 | 4864 | |
| ... | ... | @@ -6270,10 +6355,10 @@ pub const WipFunction = struct { |
| 6270 | 6355 | |
| 6271 | 6356 | while (true) { |
| 6272 | 6357 | gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1); |
| 6273 | const unique_name = try wip_name.builder.fmt("{fr}{s}{fr}", .{ | |
| 6274 | name.fmt(wip_name.builder), | |
| 6358 | const unique_name = try wip_name.builder.fmt("{f}{s}{f}", .{ | |
| 6359 | name.fmtRaw(wip_name.builder), | |
| 6275 | 6360 | sep, |
| 6276 | gop.value_ptr.fmt(wip_name.builder), | |
| 6361 | gop.value_ptr.fmtRaw(wip_name.builder), | |
| 6277 | 6362 | }); |
| 6278 | 6363 | const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name); |
| 6279 | 6364 | if (!unique_gop.found_existing) { |
| ... | ... | @@ -6940,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) { |
| 6940 | 7025 | normal, |
| 6941 | 7026 | @"volatile", |
| 6942 | 7027 | |
| 6943 | pub fn format(self: MemoryAccessKind, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { | |
| 6944 | if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) }); | |
| 7028 | pub fn format(memory_access_kind: MemoryAccessKind, w: *Writer) Writer.Error!void { | |
| 7029 | return Prefixed.format(.{ .memory_access_kind = memory_access_kind, .prefix = "" }, w); | |
| 7030 | } | |
| 7031 | ||
| 7032 | pub const Prefixed = struct { | |
| 7033 | memory_access_kind: MemoryAccessKind, | |
| 7034 | prefix: []const u8, | |
| 7035 | ||
| 7036 | pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { | |
| 7037 | switch (p.memory_access_kind) { | |
| 7038 | .normal => return, | |
| 7039 | .@"volatile" => { | |
| 7040 | var vecs: [2][]const u8 = .{ p.prefix, "volatile" }; | |
| 7041 | return w.writeVecAll(&vecs); | |
| 7042 | }, | |
| 7043 | } | |
| 7044 | } | |
| 7045 | }; | |
| 7046 | ||
| 7047 | pub fn fmt(memory_access_kind: MemoryAccessKind, prefix: []const u8) Prefixed { | |
| 7048 | return .{ .memory_access_kind = memory_access_kind, .prefix = prefix }; | |
| 6945 | 7049 | } |
| 6946 | 7050 | }; |
| 6947 | 7051 | |
| ... | ... | @@ -6949,10 +7053,27 @@ pub const SyncScope = enum(u1) { |
| 6949 | 7053 | singlethread, |
| 6950 | 7054 | system, |
| 6951 | 7055 | |
| 6952 | pub fn format(self: SyncScope, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { | |
| 6953 | if (self != .system) try bw.print( | |
| 6954 | \\{s}syncscope("{s}") | |
| 6955 | , .{ prefix, @tagName(self) }); | |
| 7056 | pub fn format(sync_scope: SyncScope, w: *Writer) Writer.Error!void { | |
| 7057 | return Prefixed.format(.{ .sync_scope = sync_scope, .prefix = "" }, w); | |
| 7058 | } | |
| 7059 | ||
| 7060 | pub const Prefixed = struct { | |
| 7061 | sync_scope: SyncScope, | |
| 7062 | prefix: []const u8, | |
| 7063 | ||
| 7064 | pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { | |
| 7065 | switch (p.sync_scope) { | |
| 7066 | .system => return, | |
| 7067 | .singlethread => { | |
| 7068 | var vecs: [2][]const u8 = .{ p.prefix, "syncscope(\"singlethread\")" }; | |
| 7069 | return w.writeVecAll(&vecs); | |
| 7070 | }, | |
| 7071 | } | |
| 7072 | } | |
| 7073 | }; | |
| 7074 | ||
| 7075 | pub fn fmt(sync_scope: SyncScope, prefix: []const u8) Prefixed { | |
| 7076 | return .{ .sync_scope = sync_scope, .prefix = prefix }; | |
| 6956 | 7077 | } |
| 6957 | 7078 | }; |
| 6958 | 7079 | |
| ... | ... | @@ -6965,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) { |
| 6965 | 7086 | acq_rel = 5, |
| 6966 | 7087 | seq_cst = 6, |
| 6967 | 7088 | |
| 6968 | pub fn format(self: AtomicOrdering, bw: *Writer, comptime prefix: []const u8) Writer.Error!void { | |
| 6969 | if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) }); | |
| 7089 | pub fn format(atomic_ordering: AtomicOrdering, w: *Writer) Writer.Error!void { | |
| 7090 | return Prefixed.format(.{ .atomic_ordering = atomic_ordering, .prefix = "" }, w); | |
| 7091 | } | |
| 7092 | ||
| 7093 | pub const Prefixed = struct { | |
| 7094 | atomic_ordering: AtomicOrdering, | |
| 7095 | prefix: []const u8, | |
| 7096 | ||
| 7097 | pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { | |
| 7098 | switch (p.atomic_ordering) { | |
| 7099 | .none => return, | |
| 7100 | else => { | |
| 7101 | var vecs: [2][]const u8 = .{ p.prefix, @tagName(p.atomic_ordering) }; | |
| 7102 | return w.writeVecAll(&vecs); | |
| 7103 | }, | |
| 7104 | } | |
| 7105 | } | |
| 7106 | }; | |
| 7107 | ||
| 7108 | pub fn fmt(atomic_ordering: AtomicOrdering, prefix: []const u8) Prefixed { | |
| 7109 | return .{ .atomic_ordering = atomic_ordering, .prefix = prefix }; | |
| 6970 | 7110 | } |
| 6971 | 7111 | }; |
| 6972 | 7112 | |
| ... | ... | @@ -7380,22 +7520,21 @@ pub const Constant = enum(u32) { |
| 7380 | 7520 | const FormatData = struct { |
| 7381 | 7521 | constant: Constant, |
| 7382 | 7522 | builder: *Builder, |
| 7523 | flags: FormatFlags, | |
| 7383 | 7524 | }; |
| 7384 | fn format(data: FormatData, bw: *Writer, comptime fmt_str: []const u8) Writer.Error!void { | |
| 7385 | if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| | |
| 7386 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); | |
| 7387 | if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { | |
| 7525 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 7526 | if (data.flags.comma) { | |
| 7388 | 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 | 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) | |
| 7396 | try bw.print("{f%} ", .{data.constant.typeOf(data.builder).fmt(data.builder)}); | |
| 7534 | if (data.flags.percent) | |
| 7535 | try w.print("{f} ", .{data.constant.typeOf(data.builder).fmt(data.builder, .percent)}); | |
| 7397 | 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 | 7538 | switch (data.constant.unwrap()) { |
| 7400 | 7539 | .constant => |constant| { |
| 7401 | 7540 | const item = data.builder.constant_items.get(constant); |
| ... | ... | @@ -7432,13 +7571,13 @@ pub const Constant = enum(u32) { |
| 7432 | 7571 | var stack align(@alignOf(ExpectedContents)) = |
| 7433 | 7572 | std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa); |
| 7434 | 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 | 7575 | defer allocator.free(str); |
| 7437 | try bw.writeAll(str); | |
| 7576 | try w.writeAll(str); | |
| 7438 | 7577 | }, |
| 7439 | 7578 | .half, |
| 7440 | 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 | 7581 | .half => 'H', |
| 7443 | 7582 | .bfloat => 'R', |
| 7444 | 7583 | else => unreachable, |
| ... | ... | @@ -7469,7 +7608,7 @@ pub const Constant = enum(u32) { |
| 7469 | 7608 | ) + 1, |
| 7470 | 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 | 7612 | .mantissa = std.math.shl( |
| 7474 | 7613 | Mantissa64, |
| 7475 | 7614 | repr.mantissa, |
| ... | ... | @@ -7491,13 +7630,13 @@ pub const Constant = enum(u32) { |
| 7491 | 7630 | }, |
| 7492 | 7631 | .double => { |
| 7493 | 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 | 7635 | .fp128, |
| 7497 | 7636 | .ppc_fp128, |
| 7498 | 7637 | => |tag| { |
| 7499 | 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 | 7640 | @as(u8, switch (tag) { |
| 7502 | 7641 | .fp128 => 'L', |
| 7503 | 7642 | .ppc_fp128 => 'M', |
| ... | ... | @@ -7511,7 +7650,7 @@ pub const Constant = enum(u32) { |
| 7511 | 7650 | }, |
| 7512 | 7651 | .x86_fp80 => { |
| 7513 | 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 | 7654 | extra.hi, extra.lo_hi, extra.lo_lo, |
| 7516 | 7655 | }); |
| 7517 | 7656 | }, |
| ... | ... | @@ -7520,7 +7659,7 @@ pub const Constant = enum(u32) { |
| 7520 | 7659 | .zeroinitializer, |
| 7521 | 7660 | .undef, |
| 7522 | 7661 | .poison, |
| 7523 | => |tag| try bw.writeAll(@tagName(tag)), | |
| 7662 | => |tag| try w.writeAll(@tagName(tag)), | |
| 7524 | 7663 | .structure, |
| 7525 | 7664 | .packed_structure, |
| 7526 | 7665 | .array, |
| ... | ... | @@ -7529,7 +7668,7 @@ pub const Constant = enum(u32) { |
| 7529 | 7668 | var extra = data.builder.constantExtraDataTrail(Aggregate, item.data); |
| 7530 | 7669 | const len: u32 = @intCast(extra.data.type.aggregateLen(data.builder)); |
| 7531 | 7670 | const vals = extra.trail.next(len, Constant, data.builder); |
| 7532 | try bw.writeAll(switch (tag) { | |
| 7671 | try w.writeAll(switch (tag) { | |
| 7533 | 7672 | .structure => "{ ", |
| 7534 | 7673 | .packed_structure => "<{ ", |
| 7535 | 7674 | .array => "[", |
| ... | ... | @@ -7537,10 +7676,10 @@ pub const Constant = enum(u32) { |
| 7537 | 7676 | else => unreachable, |
| 7538 | 7677 | }); |
| 7539 | 7678 | for (vals, 0..) |val, index| { |
| 7540 | if (index > 0) try bw.writeAll(", "); | |
| 7541 | try bw.print("{f%}", .{val.fmt(data.builder)}); | |
| 7679 | if (index > 0) try w.writeAll(", "); | |
| 7680 | try w.print("{f}", .{val.fmt(data.builder, .{ .percent = true })}); | |
| 7542 | 7681 | } |
| 7543 | try bw.writeAll(switch (tag) { | |
| 7682 | try w.writeAll(switch (tag) { | |
| 7544 | 7683 | .structure => " }", |
| 7545 | 7684 | .packed_structure => " }>", |
| 7546 | 7685 | .array => "]", |
| ... | ... | @@ -7551,30 +7690,30 @@ pub const Constant = enum(u32) { |
| 7551 | 7690 | .splat => { |
| 7552 | 7691 | const extra = data.builder.constantExtraData(Splat, item.data); |
| 7553 | 7692 | const len = extra.type.vectorLen(data.builder); |
| 7554 | try bw.writeByte('<'); | |
| 7693 | try w.writeByte('<'); | |
| 7555 | 7694 | for (0..len) |index| { |
| 7556 | if (index > 0) try bw.writeAll(", "); | |
| 7557 | try bw.print("{f%}", .{extra.value.fmt(data.builder)}); | |
| 7695 | if (index > 0) try w.writeAll(", "); | |
| 7696 | try w.print("{f}", .{extra.value.fmt(data.builder, .{ .percent = true })}); | |
| 7558 | 7697 | } |
| 7559 | try bw.writeByte('>'); | |
| 7698 | try w.writeByte('>'); | |
| 7560 | 7699 | }, |
| 7561 | .string => try bw.print("c{f\"}", .{ | |
| 7562 | @as(String, @enumFromInt(item.data)).fmt(data.builder), | |
| 7700 | .string => try w.print("c{f}", .{ | |
| 7701 | @as(String, @enumFromInt(item.data)).fmtQ(data.builder), | |
| 7563 | 7702 | }), |
| 7564 | 7703 | .blockaddress => |tag| { |
| 7565 | 7704 | const extra = data.builder.constantExtraData(BlockAddress, item.data); |
| 7566 | 7705 | const function = extra.function.ptrConst(data.builder); |
| 7567 | try bw.print("{s}({f}, {f})", .{ | |
| 7706 | try w.print("{s}({f}, {f})", .{ | |
| 7568 | 7707 | @tagName(tag), |
| 7569 | 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 | 7712 | .dso_local_equivalent, |
| 7574 | 7713 | .no_cfi, |
| 7575 | 7714 | => |tag| { |
| 7576 | 7715 | const function: Function.Index = @enumFromInt(item.data); |
| 7577 | try bw.print("{s} {f}", .{ | |
| 7716 | try w.print("{s} {f}", .{ | |
| 7578 | 7717 | @tagName(tag), |
| 7579 | 7718 | function.ptrConst(data.builder).global.fmt(data.builder), |
| 7580 | 7719 | }); |
| ... | ... | @@ -7586,10 +7725,10 @@ pub const Constant = enum(u32) { |
| 7586 | 7725 | .addrspacecast, |
| 7587 | 7726 | => |tag| { |
| 7588 | 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 | 7729 | @tagName(tag), |
| 7591 | extra.val.fmt(data.builder), | |
| 7592 | extra.type.fmt(data.builder), | |
| 7730 | extra.val.fmt(data.builder, .{ .percent = true }), | |
| 7731 | extra.type.fmt(data.builder, .percent), | |
| 7593 | 7732 | }); |
| 7594 | 7733 | }, |
| 7595 | 7734 | .getelementptr, |
| ... | ... | @@ -7598,13 +7737,13 @@ pub const Constant = enum(u32) { |
| 7598 | 7737 | var extra = data.builder.constantExtraDataTrail(GetElementPtr, item.data); |
| 7599 | 7738 | const indices = |
| 7600 | 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 | 7741 | @tagName(tag), |
| 7603 | extra.data.type.fmt(data.builder), | |
| 7604 | extra.data.base.fmt(data.builder), | |
| 7742 | extra.data.type.fmt(data.builder, .percent), | |
| 7743 | extra.data.base.fmt(data.builder, .{ .percent = true }), | |
| 7605 | 7744 | }); |
| 7606 | for (indices) |index| try bw.print(", {f%}", .{index.fmt(data.builder)}); | |
| 7607 | try bw.writeByte(')'); | |
| 7745 | for (indices) |index| try w.print(", {f}", .{index.fmt(data.builder, .{ .percent = true })}); | |
| 7746 | try w.writeByte(')'); | |
| 7608 | 7747 | }, |
| 7609 | 7748 | .add, |
| 7610 | 7749 | .@"add nsw", |
| ... | ... | @@ -7616,10 +7755,10 @@ pub const Constant = enum(u32) { |
| 7616 | 7755 | .xor, |
| 7617 | 7756 | => |tag| { |
| 7618 | 7757 | const extra = data.builder.constantExtraData(Binary, item.data); |
| 7619 | try bw.print("{s} ({f%}, {f%})", .{ | |
| 7758 | try w.print("{s} ({f}, {f})", .{ | |
| 7620 | 7759 | @tagName(tag), |
| 7621 | extra.lhs.fmt(data.builder), | |
| 7622 | extra.rhs.fmt(data.builder), | |
| 7760 | extra.lhs.fmt(data.builder, .{ .percent = true }), | |
| 7761 | extra.rhs.fmt(data.builder, .{ .percent = true }), | |
| 7623 | 7762 | }); |
| 7624 | 7763 | }, |
| 7625 | 7764 | .@"asm", |
| ... | ... | @@ -7640,19 +7779,23 @@ pub const Constant = enum(u32) { |
| 7640 | 7779 | .@"asm sideeffect alignstack inteldialect unwind", |
| 7641 | 7780 | => |tag| { |
| 7642 | 7781 | const extra = data.builder.constantExtraData(Assembly, item.data); |
| 7643 | try bw.print("{s} {f\"}, {f\"}", .{ | |
| 7782 | try w.print("{s} {f}, {f}", .{ | |
| 7644 | 7783 | @tagName(tag), |
| 7645 | extra.assembly.fmt(data.builder), | |
| 7646 | extra.constraints.fmt(data.builder), | |
| 7784 | extra.assembly.fmtQ(data.builder), | |
| 7785 | extra.constraints.fmtQ(data.builder), | |
| 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) { | |
| 7655 | return .{ .data = .{ .constant = self, .builder = builder } }; | |
| 7793 | pub fn fmt(self: Constant, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) { | |
| 7794 | return .{ .data = .{ | |
| 7795 | .constant = self, | |
| 7796 | .builder = builder, | |
| 7797 | .flags = flags, | |
| 7798 | } }; | |
| 7656 | 7799 | } |
| 7657 | 7800 | }; |
| 7658 | 7801 | |
| ... | ... | @@ -7707,23 +7850,26 @@ pub const Value = enum(u32) { |
| 7707 | 7850 | value: Value, |
| 7708 | 7851 | function: Function.Index, |
| 7709 | 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 | 7856 | switch (data.value.unwrap()) { |
| 7713 | 7857 | .instruction => |instruction| try Function.Instruction.Index.format(.{ |
| 7714 | 7858 | .instruction = instruction, |
| 7715 | 7859 | .function = data.function, |
| 7716 | 7860 | .builder = data.builder, |
| 7717 | }, bw, fmt_str), | |
| 7861 | .flags = data.flags, | |
| 7862 | }, w), | |
| 7718 | 7863 | .constant => |constant| try Constant.format(.{ |
| 7719 | 7864 | .constant = constant, |
| 7720 | 7865 | .builder = data.builder, |
| 7721 | }, bw, fmt_str), | |
| 7866 | .flags = data.flags, | |
| 7867 | }, w), | |
| 7722 | 7868 | .metadata => unreachable, |
| 7723 | 7869 | } |
| 7724 | 7870 | } |
| 7725 | pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) { | |
| 7726 | return .{ .data = .{ .value = self, .function = function, .builder = builder } }; | |
| 7871 | pub fn fmt(self: Value, function: Function.Index, builder: *Builder, flags: FormatFlags) std.fmt.Formatter(FormatData, format) { | |
| 7872 | return .{ .data = .{ .value = self, .function = function, .builder = builder, .flags = flags } }; | |
| 7727 | 7873 | } |
| 7728 | 7874 | }; |
| 7729 | 7875 | |
| ... | ... | @@ -7753,10 +7899,10 @@ pub const MetadataString = enum(u32) { |
| 7753 | 7899 | metadata_string: MetadataString, |
| 7754 | 7900 | builder: *const Builder, |
| 7755 | 7901 | }; |
| 7756 | fn format(data: FormatData, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 7757 | try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw); | |
| 7902 | fn format(data: FormatData, w: *Writer) Writer.Error!void { | |
| 7903 | try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, w); | |
| 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 | 7906 | return .{ .data = .{ .metadata_string = self, .builder = builder } }; |
| 7761 | 7907 | } |
| 7762 | 7908 | }; |
| ... | ... | @@ -7918,24 +8064,24 @@ pub const Metadata = enum(u32) { |
| 7918 | 8064 | AllCallsDescribed: bool = false, |
| 7919 | 8065 | Unused: u2 = 0, |
| 7920 | 8066 | |
| 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 | 8068 | var need_pipe = false; |
| 7923 | 8069 | inline for (@typeInfo(DIFlags).@"struct".fields) |field| { |
| 7924 | 8070 | switch (@typeInfo(field.type)) { |
| 7925 | 8071 | .bool => if (@field(self, field.name)) { |
| 7926 | if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; | |
| 7927 | try bw.print("DIFlag{s}", .{field.name}); | |
| 8072 | if (need_pipe) try w.writeAll(" | ") else need_pipe = true; | |
| 8073 | try w.print("DIFlag{s}", .{field.name}); | |
| 7928 | 8074 | }, |
| 7929 | 8075 | .@"enum" => if (@field(self, field.name) != .Zero) { |
| 7930 | if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; | |
| 7931 | try bw.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); | |
| 8076 | if (need_pipe) try w.writeAll(" | ") else need_pipe = true; | |
| 8077 | try w.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); | |
| 7932 | 8078 | }, |
| 7933 | 8079 | .int => assert(@field(self, field.name) == 0), |
| 7934 | 8080 | else => @compileError("bad field type: " ++ field.name ++ ": " ++ |
| 7935 | 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 | }; |
| 7941 | 8087 | |
| ... | ... | @@ -7975,24 +8121,24 @@ pub const Metadata = enum(u32) { |
| 7975 | 8121 | ObjCDirect: bool = false, |
| 7976 | 8122 | Unused: u20 = 0, |
| 7977 | 8123 | |
| 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 | 8125 | var need_pipe = false; |
| 7980 | 8126 | inline for (@typeInfo(DISPFlags).@"struct".fields) |field| { |
| 7981 | 8127 | switch (@typeInfo(field.type)) { |
| 7982 | 8128 | .bool => if (@field(self, field.name)) { |
| 7983 | if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; | |
| 7984 | try bw.print("DISPFlag{s}", .{field.name}); | |
| 8129 | if (need_pipe) try w.writeAll(" | ") else need_pipe = true; | |
| 8130 | try w.print("DISPFlag{s}", .{field.name}); | |
| 7985 | 8131 | }, |
| 7986 | 8132 | .@"enum" => if (@field(self, field.name) != .Zero) { |
| 7987 | if (need_pipe) try bw.writeAll(" | ") else need_pipe = true; | |
| 7988 | try bw.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); | |
| 8133 | if (need_pipe) try w.writeAll(" | ") else need_pipe = true; | |
| 8134 | try w.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); | |
| 7989 | 8135 | }, |
| 7990 | 8136 | .int => assert(@field(self, field.name) == 0), |
| 7991 | 8137 | else => @compileError("bad field type: " ++ field.name ++ ": " ++ |
| 7992 | 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 | }; |
| 7998 | 8144 | |
| ... | ... | @@ -8167,6 +8313,7 @@ pub const Metadata = enum(u32) { |
| 8167 | 8313 | formatter: *Formatter, |
| 8168 | 8314 | prefix: []const u8 = "", |
| 8169 | 8315 | node: Node, |
| 8316 | specialized: ?FormatFlags, | |
| 8170 | 8317 | |
| 8171 | 8318 | const Node = union(enum) { |
| 8172 | 8319 | none, |
| ... | ... | @@ -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 | 8343 | if (data.node == .none) return; |
| 8197 | 8344 | |
| 8198 | const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S'; | |
| 8199 | const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str; | |
| 8345 | const is_specialized = data.specialized != null; | |
| 8200 | 8346 | |
| 8201 | if (data.formatter.need_comma) try bw.writeAll(", "); | |
| 8347 | if (data.formatter.need_comma) try w.writeAll(", "); | |
| 8202 | 8348 | defer data.formatter.need_comma = true; |
| 8203 | try bw.writeAll(data.prefix); | |
| 8349 | try w.writeAll(data.prefix); | |
| 8204 | 8350 | |
| 8205 | 8351 | const builder = data.formatter.builder; |
| 8206 | 8352 | switch (data.node) { |
| ... | ... | @@ -8215,50 +8361,57 @@ pub const Metadata = enum(u32) { |
| 8215 | 8361 | .expression => { |
| 8216 | 8362 | var extra = builder.metadataExtraDataTrail(Expression, item.data); |
| 8217 | 8363 | const elements = extra.trail.next(extra.data.elements_len, u32, builder); |
| 8218 | try bw.writeAll("!DIExpression("); | |
| 8364 | try w.writeAll("!DIExpression("); | |
| 8219 | 8365 | for (elements) |element| try format(.{ |
| 8220 | 8366 | .formatter = data.formatter, |
| 8221 | 8367 | .node = .{ .u64 = element }, |
| 8222 | }, bw, "%"); | |
| 8223 | try bw.writeByte(')'); | |
| 8368 | .specialized = .{ .percent = true }, | |
| 8369 | }, w); | |
| 8370 | try w.writeByte(')'); | |
| 8224 | 8371 | }, |
| 8225 | 8372 | .constant => try Constant.format(.{ |
| 8226 | 8373 | .constant = @enumFromInt(item.data), |
| 8227 | 8374 | .builder = builder, |
| 8228 | }, bw, recurse_fmt_str), | |
| 8375 | .flags = data.specialized orelse .{}, | |
| 8376 | }, w), | |
| 8229 | 8377 | else => unreachable, |
| 8230 | 8378 | } |
| 8231 | 8379 | }, |
| 8232 | .index => |node| try bw.print("!{d}", .{node}), | |
| 8380 | .index => |node| try w.print("!{d}", .{node}), | |
| 8233 | 8381 | inline .local_value, .local_metadata => |node, tag| try Value.format(.{ |
| 8234 | 8382 | .value = node.value, |
| 8235 | 8383 | .function = node.function, |
| 8236 | 8384 | .builder = builder, |
| 8237 | }, bw, switch (tag) { | |
| 8238 | .local_value => recurse_fmt_str, | |
| 8239 | .local_metadata => "%", | |
| 8240 | else => unreachable, | |
| 8241 | }), | |
| 8385 | .flags = switch (tag) { | |
| 8386 | .local_value => data.specialized orelse .{}, | |
| 8387 | .local_metadata => .{ .percent = true }, | |
| 8388 | else => unreachable, | |
| 8389 | }, | |
| 8390 | }, w), | |
| 8242 | 8391 | inline .local_inline, .local_index => |node, tag| { |
| 8243 | if (comptime std.mem.eql(u8, recurse_fmt_str, "%")) | |
| 8244 | try bw.print("{f%} ", .{Type.metadata.fmt(builder)}); | |
| 8392 | if (data.specialized) |flags| { | |
| 8393 | if (flags.onlyPercent()) { | |
| 8394 | try w.print("{f} ", .{Type.metadata.fmt(builder, .percent)}); | |
| 8395 | } | |
| 8396 | } | |
| 8245 | 8397 | try format(.{ |
| 8246 | 8398 | .formatter = data.formatter, |
| 8247 | 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}", .{ | |
| 8251 | node.fmt(builder), | |
| 8403 | .string => |node| try w.print("{s}{f}", .{ | |
| 8404 | @as([]const u8, if (is_specialized) "" else "!"), node.fmt(builder), | |
| 8252 | 8405 | }), |
| 8253 | inline .bool, .u32, .u64 => |node| try bw.print("{}", .{node}), | |
| 8254 | inline .di_flags, .sp_flags => |node| try bw.print("{f}", .{node}), | |
| 8255 | .raw => |node| try bw.writeAll(node), | |
| 8406 | inline .bool, .u32, .u64 => |node| try w.print("{}", .{node}), | |
| 8407 | inline .di_flags, .sp_flags => |node| try w.print("{f}", .{node}), | |
| 8408 | .raw => |node| try w.writeAll(node), | |
| 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 | 8412 | Metadata => Allocator.Error, |
| 8260 | 8413 | else => error{}, |
| 8261 | }!std.fmt.Formatter(format) { | |
| 8414 | }!std.fmt.Formatter(FormatData, format) { | |
| 8262 | 8415 | const Node = @TypeOf(node); |
| 8263 | 8416 | const MaybeNode = switch (@typeInfo(Node)) { |
| 8264 | 8417 | .optional => Node, |
| ... | ... | @@ -8295,6 +8448,7 @@ pub const Metadata = enum(u32) { |
| 8295 | 8448 | .optional, .null => .none, |
| 8296 | 8449 | else => unreachable, |
| 8297 | 8450 | }, |
| 8451 | .specialized = special, | |
| 8298 | 8452 | } }; |
| 8299 | 8453 | } |
| 8300 | 8454 | inline fn fmtLocal( |
| ... | ... | @@ -8302,7 +8456,7 @@ pub const Metadata = enum(u32) { |
| 8302 | 8456 | prefix: []const u8, |
| 8303 | 8457 | value: Value, |
| 8304 | 8458 | function: Function.Index, |
| 8305 | ) Allocator.Error!std.fmt.Formatter(format) { | |
| 8459 | ) Allocator.Error!std.fmt.Formatter(FormatData, format) { | |
| 8306 | 8460 | return .{ .data = .{ |
| 8307 | 8461 | .formatter = formatter, |
| 8308 | 8462 | .prefix = prefix, |
| ... | ... | @@ -8327,6 +8481,7 @@ pub const Metadata = enum(u32) { |
| 8327 | 8481 | }; |
| 8328 | 8482 | }, |
| 8329 | 8483 | }, |
| 8484 | .specialized = null, | |
| 8330 | 8485 | } }; |
| 8331 | 8486 | } |
| 8332 | 8487 | fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node { |
| ... | ... | @@ -8366,7 +8521,7 @@ pub const Metadata = enum(u32) { |
| 8366 | 8521 | DIGlobalVariableExpression, |
| 8367 | 8522 | }, |
| 8368 | 8523 | nodes: anytype, |
| 8369 | bw: *Writer, | |
| 8524 | w: *Writer, | |
| 8370 | 8525 | ) !void { |
| 8371 | 8526 | comptime var fmt_str: []const u8 = ""; |
| 8372 | 8527 | const names = comptime std.meta.fieldNames(@TypeOf(nodes)); |
| ... | ... | @@ -8383,10 +8538,10 @@ pub const Metadata = enum(u32) { |
| 8383 | 8538 | } |
| 8384 | 8539 | fmt_str = fmt_str ++ "("; |
| 8385 | 8540 | inline for (fields[2..], names) |*field, name| { |
| 8386 | fmt_str = fmt_str ++ "{[" ++ name ++ "]fS}"; | |
| 8541 | fmt_str = fmt_str ++ "{[" ++ name ++ "]f}"; | |
| 8387 | 8542 | field.* = .{ |
| 8388 | 8543 | .name = name, |
| 8389 | .type = std.fmt.Formatter(format), | |
| 8544 | .type = std.fmt.Formatter(FormatData, format), | |
| 8390 | 8545 | .default_value_ptr = null, |
| 8391 | 8546 | .is_comptime = false, |
| 8392 | 8547 | .alignment = 0, |
| ... | ... | @@ -8405,8 +8560,9 @@ pub const Metadata = enum(u32) { |
| 8405 | 8560 | inline for (names) |name| @field(fmt_args, name) = try formatter.fmt( |
| 8406 | 8561 | name ++ ": ", |
| 8407 | 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 | 8652 | inline for (.{ 0, 4 }) |addr_space_index| { |
| 8497 | 8653 | const addr_space: AddrSpace = @enumFromInt(addr_space_index); |
| 8498 | 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 | } |
| 8502 | 8658 | |
| ... | ... | @@ -8619,7 +8775,7 @@ pub fn deinit(self: *Builder) void { |
| 8619 | 8775 | self.* = undefined; |
| 8620 | 8776 | } |
| 8621 | 8777 | |
| 8622 | pub fn finishModuleAsm(self: *Builder, aw: *std.io.Writer.Allocating) Allocator.Error!void { | |
| 8778 | pub fn finishModuleAsm(self: *Builder, aw: *Writer.Allocating) Allocator.Error!void { | |
| 8623 | 8779 | self.module_asm = aw.toArrayList(); |
| 8624 | 8780 | if (self.module_asm.getLastOrNull()) |last| if (last != '\n') |
| 8625 | 8781 | try self.module_asm.append(self.gpa, '\n'); |
| ... | ... | @@ -8929,11 +9085,11 @@ pub fn getIntrinsic( |
| 8929 | 9085 | |
| 8930 | 9086 | const name = name: { |
| 8931 | 9087 | { |
| 8932 | var aw: std.io.Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes); | |
| 8933 | const bw = &aw.interface; | |
| 9088 | var aw: Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes); | |
| 9089 | const w = &aw.writer; | |
| 8934 | 9090 | defer self.strtab_string_bytes = aw.toArrayList(); |
| 8935 | bw.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory; | |
| 8936 | for (overload) |ty| bw.print(".{fm}", .{ty.fmt(self)}) catch return error.OutOfMemory; | |
| 9091 | w.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory; | |
| 9092 | for (overload) |ty| w.print(".{f}", .{ty.fmt(self, .m)}) catch return error.OutOfMemory; | |
| 8937 | 9093 | } |
| 8938 | 9094 | break :name try self.trailingStrtabString(); |
| 8939 | 9095 | }; |
| ... | ... | @@ -9348,110 +9504,105 @@ pub fn asmValue( |
| 9348 | 9504 | return (try self.asmConst(ty, info, assembly, constraints)).toValue(); |
| 9349 | 9505 | } |
| 9350 | 9506 | |
| 9351 | pub fn dump(self: *Builder) void { | |
| 9507 | pub fn dump(b: *Builder) void { | |
| 9508 | var buffer: [4000]u8 = undefined; | |
| 9352 | 9509 | const stderr: std.fs.File = .stderr(); |
| 9353 | self.printBuffered(stderr.writer()) catch {}; | |
| 9510 | b.printToFile(stderr, &buffer) catch {}; | |
| 9354 | 9511 | } |
| 9355 | 9512 | |
| 9356 | pub fn printToFile(self: *Builder, path: []const u8) bool { | |
| 9357 | var file = std.fs.cwd().createFile(path, .{}) catch |err| { | |
| 9358 | log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); | |
| 9359 | return false; | |
| 9360 | }; | |
| 9513 | pub fn printToFilePath(b: *Builder, dir: std.fs.Dir, path: []const u8) !void { | |
| 9514 | var buffer: [4000]u8 = undefined; | |
| 9515 | const file = try dir.createFile(path, .{}); | |
| 9361 | 9516 | defer file.close(); |
| 9362 | self.printBuffered(file.writer()) catch |err| { | |
| 9363 | log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); | |
| 9364 | return false; | |
| 9365 | }; | |
| 9366 | return true; | |
| 9517 | try b.printToFile(file, &buffer); | |
| 9367 | 9518 | } |
| 9368 | 9519 | |
| 9369 | pub fn printBuffered(self: *Builder, writer: Writer) Writer.Error!void { | |
| 9370 | var buffer: [4096]u8 = undefined; | |
| 9371 | var bw = writer.buffered(&buffer); | |
| 9372 | try self.print(&bw); | |
| 9373 | try bw.flush(); | |
| 9520 | pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void { | |
| 9521 | var fw = file.writer(buffer); | |
| 9522 | try print(b, &fw.interface); | |
| 9523 | try fw.interface.flush(); | |
| 9374 | 9524 | } |
| 9375 | 9525 | |
| 9376 | pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { | |
| 9526 | pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void { | |
| 9377 | 9527 | var need_newline = false; |
| 9378 | 9528 | var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; |
| 9379 | 9529 | defer metadata_formatter.map.deinit(self.gpa); |
| 9380 | 9530 | |
| 9381 | 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; | |
| 9383 | if (self.source_filename != .none) try bw.print( | |
| 9532 | if (need_newline) try w.writeByte('\n') else need_newline = true; | |
| 9533 | if (self.source_filename != .none) try w.print( | |
| 9384 | 9534 | \\; ModuleID = '{s}' |
| 9385 | \\source_filename = {f"} | |
| 9535 | \\source_filename = {f} | |
| 9386 | 9536 | \\ |
| 9387 | , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); | |
| 9388 | if (self.data_layout != .none) try bw.print( | |
| 9389 | \\target datalayout = {f"} | |
| 9537 | , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) }); | |
| 9538 | if (self.data_layout != .none) try w.print( | |
| 9539 | \\target datalayout = {f} | |
| 9390 | 9540 | \\ |
| 9391 | , .{self.data_layout.fmt(self)}); | |
| 9392 | if (self.target_triple != .none) try bw.print( | |
| 9393 | \\target triple = {f"} | |
| 9541 | , .{self.data_layout.fmtQ(self)}); | |
| 9542 | if (self.target_triple != .none) try w.print( | |
| 9543 | \\target triple = {f} | |
| 9394 | 9544 | \\ |
| 9395 | , .{self.target_triple.fmt(self)}); | |
| 9545 | , .{self.target_triple.fmtQ(self)}); | |
| 9396 | 9546 | } |
| 9397 | 9547 | |
| 9398 | 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 | 9550 | var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n'); |
| 9401 | 9551 | while (line_it.next()) |line| { |
| 9402 | try bw.writeAll("module asm "); | |
| 9403 | try printEscapedString(line, .always_quote, bw); | |
| 9404 | try bw.writeByte('\n'); | |
| 9552 | try w.writeAll("module asm "); | |
| 9553 | try printEscapedString(line, .always_quote, w); | |
| 9554 | try w.writeByte('\n'); | |
| 9405 | 9555 | } |
| 9406 | 9556 | } |
| 9407 | 9557 | |
| 9408 | 9558 | if (self.types.count() > 0) { |
| 9409 | if (need_newline) try bw.writeByte('\n') else need_newline = true; | |
| 9410 | for (self.types.keys(), self.types.values()) |id, ty| try bw.print( | |
| 9559 | if (need_newline) try w.writeByte('\n') else need_newline = true; | |
| 9560 | for (self.types.keys(), self.types.values()) |id, ty| try w.print( | |
| 9411 | 9561 | \\%{f} = type {f} |
| 9412 | 9562 | \\ |
| 9413 | , .{ id.fmt(self), ty.fmt(self) }); | |
| 9563 | , .{ id.fmt(self), ty.fmt(self, .default) }); | |
| 9414 | 9564 | } |
| 9415 | 9565 | |
| 9416 | 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 | 9568 | for (self.variables.items) |variable| { |
| 9419 | 9569 | if (variable.global.getReplacement(self) != .none) continue; |
| 9420 | 9570 | const global = variable.global.ptrConst(self); |
| 9421 | 9571 | metadata_formatter.need_comma = true; |
| 9422 | 9572 | defer metadata_formatter.need_comma = undefined; |
| 9423 | try bw.print( | |
| 9424 | \\{f} ={f}{f}{f}{f}{f }{f}{f }{f} {s} {f%}{f }{f, }{f} | |
| 9573 | try w.print( | |
| 9574 | \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f} | |
| 9425 | 9575 | \\ |
| 9426 | 9576 | , .{ |
| 9427 | 9577 | variable.global.fmt(self), |
| 9428 | Linkage.fmtOptional(if (global.linkage == .external and | |
| 9429 | variable.init != .no_init) null else global.linkage), | |
| 9578 | Linkage.fmtOptional( | |
| 9579 | if (global.linkage == .external and variable.init != .no_init) null else global.linkage, | |
| 9580 | ), | |
| 9430 | 9581 | global.preemption, |
| 9431 | 9582 | global.visibility, |
| 9432 | 9583 | global.dll_storage_class, |
| 9433 | variable.thread_local, | |
| 9584 | variable.thread_local.fmt(" "), | |
| 9434 | 9585 | global.unnamed_addr, |
| 9435 | global.addr_space, | |
| 9586 | global.addr_space.fmt(" "), | |
| 9436 | 9587 | global.externally_initialized, |
| 9437 | 9588 | @tagName(variable.mutability), |
| 9438 | global.type.fmt(self), | |
| 9439 | variable.init.fmt(self), | |
| 9440 | variable.alignment, | |
| 9441 | try metadata_formatter.fmt("!dbg ", global.dbg), | |
| 9589 | global.type.fmt(self, .percent), | |
| 9590 | variable.init.fmt(self, .{ .space = true }), | |
| 9591 | variable.alignment.fmt(", "), | |
| 9592 | try metadata_formatter.fmt("!dbg ", global.dbg, null), | |
| 9442 | 9593 | }); |
| 9443 | 9594 | } |
| 9444 | 9595 | } |
| 9445 | 9596 | |
| 9446 | 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 | 9599 | for (self.aliases.items) |alias| { |
| 9449 | 9600 | if (alias.global.getReplacement(self) != .none) continue; |
| 9450 | 9601 | const global = alias.global.ptrConst(self); |
| 9451 | 9602 | metadata_formatter.need_comma = true; |
| 9452 | 9603 | defer metadata_formatter.need_comma = undefined; |
| 9453 | try bw.print( | |
| 9454 | \\{f} ={f}{f}{f}{f}{f }{f} alias {f%}, {f%}{f} | |
| 9604 | try w.print( | |
| 9605 | \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f} | |
| 9455 | 9606 | \\ |
| 9456 | 9607 | , .{ |
| 9457 | 9608 | alias.global.fmt(self), |
| ... | ... | @@ -9459,11 +9610,11 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9459 | 9610 | global.preemption, |
| 9460 | 9611 | global.visibility, |
| 9461 | 9612 | global.dll_storage_class, |
| 9462 | alias.thread_local, | |
| 9613 | alias.thread_local.fmt(" "), | |
| 9463 | 9614 | global.unnamed_addr, |
| 9464 | global.type.fmt(self), | |
| 9465 | alias.aliasee.fmt(self), | |
| 9466 | try metadata_formatter.fmt("!dbg ", global.dbg), | |
| 9615 | global.type.fmt(self, .percent), | |
| 9616 | alias.aliasee.fmt(self, .{ .percent = true }), | |
| 9617 | try metadata_formatter.fmt("!dbg ", global.dbg, null), | |
| 9467 | 9618 | }); |
| 9468 | 9619 | } |
| 9469 | 9620 | } |
| ... | ... | @@ -9473,17 +9624,17 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9473 | 9624 | |
| 9474 | 9625 | for (0.., self.functions.items) |function_i, function| { |
| 9475 | 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 | 9628 | const function_index: Function.Index = @enumFromInt(function_i); |
| 9478 | 9629 | const global = function.global.ptrConst(self); |
| 9479 | 9630 | const params_len = global.type.functionParameters(self).len; |
| 9480 | 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 | 9633 | \\; Function Attrs:{f} |
| 9483 | 9634 | \\ |
| 9484 | , .{function_attributes.fmt(self)}); | |
| 9485 | try bw.print( | |
| 9486 | \\{s}{f}{f}{f}{f}{f}{f"} {f%} {f}( | |
| 9635 | , .{function_attributes.fmt(self, .{})}); | |
| 9636 | try w.print( | |
| 9637 | \\{s}{f}{f}{f}{f}{f}{f} {f} {f}( | |
| 9487 | 9638 | , .{ |
| 9488 | 9639 | if (function.instructions.len > 0) "define" else "declare", |
| 9489 | 9640 | global.linkage, |
| ... | ... | @@ -9491,45 +9642,45 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9491 | 9642 | global.visibility, |
| 9492 | 9643 | global.dll_storage_class, |
| 9493 | 9644 | function.call_conv, |
| 9494 | function.attributes.ret(self).fmt(self), | |
| 9495 | global.type.functionReturn(self).fmt(self), | |
| 9645 | function.attributes.ret(self).fmt(self, .{}), | |
| 9646 | global.type.functionReturn(self).fmt(self, .percent), | |
| 9496 | 9647 | function.global.fmt(self), |
| 9497 | 9648 | }); |
| 9498 | 9649 | for (0..params_len) |arg| { |
| 9499 | if (arg > 0) try bw.writeAll(", "); | |
| 9500 | try bw.print( | |
| 9501 | \\{f%}{f"} | |
| 9650 | if (arg > 0) try w.writeAll(", "); | |
| 9651 | try w.print( | |
| 9652 | \\{f}{f} | |
| 9502 | 9653 | , .{ |
| 9503 | global.type.functionParameters(self)[arg].fmt(self), | |
| 9504 | function.attributes.param(arg, self).fmt(self), | |
| 9654 | global.type.functionParameters(self)[arg].fmt(self, .percent), | |
| 9655 | function.attributes.param(arg, self).fmt(self, .{}), | |
| 9505 | 9656 | }); |
| 9506 | 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 | 9659 | else |
| 9509 | try bw.print(" %{d}", .{arg}); | |
| 9660 | try w.print(" %{d}", .{arg}); | |
| 9510 | 9661 | } |
| 9511 | 9662 | switch (global.type.functionKind(self)) { |
| 9512 | 9663 | .normal => {}, |
| 9513 | 9664 | .vararg => { |
| 9514 | if (params_len > 0) try bw.writeAll(", "); | |
| 9515 | try bw.writeAll("..."); | |
| 9665 | if (params_len > 0) try w.writeAll(", "); | |
| 9666 | try w.writeAll("..."); | |
| 9516 | 9667 | }, |
| 9517 | 9668 | } |
| 9518 | try bw.print("){f}{f }", .{ global.unnamed_addr, global.addr_space }); | |
| 9519 | if (function_attributes != .none) try bw.print(" #{d}", .{ | |
| 9669 | try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") }); | |
| 9670 | if (function_attributes != .none) try w.print(" #{d}", .{ | |
| 9520 | 9671 | (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, |
| 9521 | 9672 | }); |
| 9522 | 9673 | { |
| 9523 | 9674 | metadata_formatter.need_comma = false; |
| 9524 | 9675 | defer metadata_formatter.need_comma = undefined; |
| 9525 | try bw.print("{f }{f}", .{ | |
| 9526 | function.alignment, | |
| 9527 | try metadata_formatter.fmt(" !dbg ", global.dbg), | |
| 9676 | try w.print("{f}{f}", .{ | |
| 9677 | function.alignment.fmt(" "), | |
| 9678 | try metadata_formatter.fmt(" !dbg ", global.dbg, null), | |
| 9528 | 9679 | }); |
| 9529 | 9680 | } |
| 9530 | 9681 | if (function.instructions.len > 0) { |
| 9531 | 9682 | var block_incoming_len: u32 = undefined; |
| 9532 | try bw.writeAll(" {\n"); | |
| 9683 | try w.writeAll(" {\n"); | |
| 9533 | 9684 | var maybe_dbg_index: ?u32 = null; |
| 9534 | 9685 | for (params_len..function.instructions.len) |instruction_i| { |
| 9535 | 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 | 9778 | .xor, |
| 9628 | 9779 | => |tag| { |
| 9629 | 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 | 9782 | instruction_index.name(&function).fmt(self), |
| 9632 | 9783 | @tagName(tag), |
| 9633 | extra.lhs.fmt(function_index, self), | |
| 9634 | extra.rhs.fmt(function_index, self), | |
| 9784 | extra.lhs.fmt(function_index, self, .{ .percent = true }), | |
| 9785 | extra.rhs.fmt(function_index, self, .{ .percent = true }), | |
| 9635 | 9786 | }); |
| 9636 | 9787 | }, |
| 9637 | 9788 | .addrspacecast, |
| ... | ... | @@ -9649,73 +9800,76 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9649 | 9800 | .zext, |
| 9650 | 9801 | => |tag| { |
| 9651 | 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 | 9804 | instruction_index.name(&function).fmt(self), |
| 9654 | 9805 | @tagName(tag), |
| 9655 | extra.val.fmt(function_index, self), | |
| 9656 | extra.type.fmt(self), | |
| 9806 | extra.val.fmt(function_index, self, .{ .percent = true }), | |
| 9807 | extra.type.fmt(self, .percent), | |
| 9657 | 9808 | }); |
| 9658 | 9809 | }, |
| 9659 | 9810 | .alloca, |
| 9660 | 9811 | .@"alloca inalloca", |
| 9661 | 9812 | => |tag| { |
| 9662 | 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 | 9815 | instruction_index.name(&function).fmt(self), |
| 9665 | 9816 | @tagName(tag), |
| 9666 | extra.type.fmt(self), | |
| 9817 | extra.type.fmt(self, .percent), | |
| 9667 | 9818 | Value.fmt(switch (extra.len) { |
| 9668 | 9819 | .@"1" => .none, |
| 9669 | 9820 | else => extra.len, |
| 9670 | }, function_index, self), | |
| 9671 | extra.info.alignment, | |
| 9672 | extra.info.addr_space, | |
| 9821 | }, function_index, self, .{ | |
| 9822 | .comma = true, | |
| 9823 | .percent = true, | |
| 9824 | }), | |
| 9825 | extra.info.alignment.fmt(", "), | |
| 9826 | extra.info.addr_space.fmt(", "), | |
| 9673 | 9827 | }); |
| 9674 | 9828 | }, |
| 9675 | 9829 | .arg => unreachable, |
| 9676 | 9830 | .atomicrmw => |tag| { |
| 9677 | 9831 | const extra = |
| 9678 | 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 | 9834 | instruction_index.name(&function).fmt(self), |
| 9681 | @tagName(tag), | |
| 9682 | extra.info.access_kind, | |
| 9683 | @tagName(extra.info.atomic_rmw_operation), | |
| 9684 | extra.ptr.fmt(function_index, self), | |
| 9685 | extra.val.fmt(function_index, self), | |
| 9686 | extra.info.sync_scope, | |
| 9687 | extra.info.success_ordering, | |
| 9688 | extra.info.alignment, | |
| 9835 | tag, | |
| 9836 | extra.info.access_kind.fmt(" "), | |
| 9837 | extra.info.atomic_rmw_operation, | |
| 9838 | extra.ptr.fmt(function_index, self, .{ .percent = true }), | |
| 9839 | extra.val.fmt(function_index, self, .{ .percent = true }), | |
| 9840 | extra.info.sync_scope.fmt(" "), | |
| 9841 | extra.info.success_ordering.fmt(" "), | |
| 9842 | extra.info.alignment.fmt(", "), | |
| 9689 | 9843 | }); |
| 9690 | 9844 | }, |
| 9691 | 9845 | .block => { |
| 9692 | 9846 | block_incoming_len = instruction.data; |
| 9693 | 9847 | const name = instruction_index.name(&function); |
| 9694 | 9848 | if (@intFromEnum(instruction_index) > params_len) |
| 9695 | try bw.writeByte('\n'); | |
| 9696 | try bw.print("{f}:\n", .{name.fmt(self)}); | |
| 9849 | try w.writeByte('\n'); | |
| 9850 | try w.print("{f}:\n", .{name.fmt(self)}); | |
| 9697 | 9851 | continue; |
| 9698 | 9852 | }, |
| 9699 | 9853 | .br => |tag| { |
| 9700 | 9854 | const target: Function.Block.Index = @enumFromInt(instruction.data); |
| 9701 | try bw.print(" {s} {f%}", .{ | |
| 9702 | @tagName(tag), target.toInst(&function).fmt(function_index, self), | |
| 9855 | try w.print(" {s} {f}", .{ | |
| 9856 | @tagName(tag), target.toInst(&function).fmt(function_index, self, .{ .percent = true }), | |
| 9703 | 9857 | }); |
| 9704 | 9858 | }, |
| 9705 | 9859 | .br_cond => { |
| 9706 | 9860 | const extra = function.extraData(Function.Instruction.BrCond, instruction.data); |
| 9707 | try bw.print(" br {f%}, {f%}, {f%}", .{ | |
| 9708 | extra.cond.fmt(function_index, self), | |
| 9709 | extra.then.toInst(&function).fmt(function_index, self), | |
| 9710 | extra.@"else".toInst(&function).fmt(function_index, self), | |
| 9861 | try w.print(" br {f}, {f}, {f}", .{ | |
| 9862 | extra.cond.fmt(function_index, self, .{ .percent = true }), | |
| 9863 | extra.then.toInst(&function).fmt(function_index, self, .{ .percent = true }), | |
| 9864 | extra.@"else".toInst(&function).fmt(function_index, self, .{ .percent = true }), | |
| 9711 | 9865 | }); |
| 9712 | 9866 | metadata_formatter.need_comma = true; |
| 9713 | 9867 | defer metadata_formatter.need_comma = undefined; |
| 9714 | 9868 | switch (extra.weights) { |
| 9715 | 9869 | .none => {}, |
| 9716 | .unpredictable => try bw.writeAll("!unpredictable !{}"), | |
| 9717 | _ => try bw.print("{f}", .{ | |
| 9718 | try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))), | |
| 9870 | .unpredictable => try w.writeAll("!unpredictable !{}"), | |
| 9871 | _ => try w.print("{f}", .{ | |
| 9872 | try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights))), null), | |
| 9719 | 9873 | }), |
| 9720 | 9874 | } |
| 9721 | 9875 | }, |
| ... | ... | @@ -9731,42 +9885,42 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9731 | 9885 | var extra = |
| 9732 | 9886 | function.extraDataTrail(Function.Instruction.Call, instruction.data); |
| 9733 | 9887 | const args = extra.trail.next(extra.data.args_len, Value, &function); |
| 9734 | try bw.writeAll(" "); | |
| 9888 | try w.writeAll(" "); | |
| 9735 | 9889 | const ret_ty = extra.data.ty.functionReturn(self); |
| 9736 | 9890 | switch (ret_ty) { |
| 9737 | 9891 | .void => {}, |
| 9738 | else => try bw.print("%{f} = ", .{ | |
| 9892 | else => try w.print("%{f} = ", .{ | |
| 9739 | 9893 | instruction_index.name(&function).fmt(self), |
| 9740 | 9894 | }), |
| 9741 | 9895 | .none => unreachable, |
| 9742 | 9896 | } |
| 9743 | try bw.print("{s}{f}{f}{f} {f%} {f}(", .{ | |
| 9744 | @tagName(tag), | |
| 9897 | try w.print("{t}{f}{f}{f} {f} {f}(", .{ | |
| 9898 | tag, | |
| 9745 | 9899 | extra.data.info.call_conv, |
| 9746 | extra.data.attributes.ret(self).fmt(self), | |
| 9900 | extra.data.attributes.ret(self).fmt(self, .{}), | |
| 9747 | 9901 | extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), |
| 9748 | 9902 | switch (extra.data.ty.functionKind(self)) { |
| 9749 | 9903 | .normal => ret_ty, |
| 9750 | 9904 | .vararg => extra.data.ty, |
| 9751 | }.fmt(self), | |
| 9752 | extra.data.callee.fmt(function_index, self), | |
| 9905 | }.fmt(self, .percent), | |
| 9906 | extra.data.callee.fmt(function_index, self, .{}), | |
| 9753 | 9907 | }); |
| 9754 | 9908 | for (0.., args) |arg_index, arg| { |
| 9755 | if (arg_index > 0) try bw.writeAll(", "); | |
| 9909 | if (arg_index > 0) try w.writeAll(", "); | |
| 9756 | 9910 | metadata_formatter.need_comma = false; |
| 9757 | 9911 | defer metadata_formatter.need_comma = undefined; |
| 9758 | try bw.print("{f%}{f}{f}", .{ | |
| 9759 | arg.typeOf(function_index, self).fmt(self), | |
| 9760 | extra.data.attributes.param(arg_index, self).fmt(self), | |
| 9912 | try w.print("{f}{f}{f}", .{ | |
| 9913 | arg.typeOf(function_index, self).fmt(self, .percent), | |
| 9914 | extra.data.attributes.param(arg_index, self).fmt(self, .{}), | |
| 9761 | 9915 | try metadata_formatter.fmtLocal(" ", arg, function_index), |
| 9762 | 9916 | }); |
| 9763 | 9917 | } |
| 9764 | try bw.writeByte(')'); | |
| 9918 | try w.writeByte(')'); | |
| 9765 | 9919 | if (extra.data.info.has_op_bundle_cold) { |
| 9766 | try bw.writeAll(" [ \"cold\"() ]"); | |
| 9920 | try w.writeAll(" [ \"cold\"() ]"); | |
| 9767 | 9921 | } |
| 9768 | 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 | 9924 | (try attribute_groups.getOrPutValue( |
| 9771 | 9925 | self.gpa, |
| 9772 | 9926 | call_function_attributes, |
| ... | ... | @@ -9779,27 +9933,27 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9779 | 9933 | => |tag| { |
| 9780 | 9934 | const extra = |
| 9781 | 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 | 9937 | instruction_index.name(&function).fmt(self), |
| 9784 | @tagName(tag), | |
| 9785 | extra.info.access_kind, | |
| 9786 | extra.ptr.fmt(function_index, self), | |
| 9787 | extra.cmp.fmt(function_index, self), | |
| 9788 | extra.new.fmt(function_index, self), | |
| 9789 | extra.info.sync_scope, | |
| 9790 | extra.info.success_ordering, | |
| 9791 | extra.info.failure_ordering, | |
| 9792 | extra.info.alignment, | |
| 9938 | tag, | |
| 9939 | extra.info.access_kind.fmt(" "), | |
| 9940 | extra.ptr.fmt(function_index, self, .{ .percent = true }), | |
| 9941 | extra.cmp.fmt(function_index, self, .{ .percent = true }), | |
| 9942 | extra.new.fmt(function_index, self, .{ .percent = true }), | |
| 9943 | extra.info.sync_scope.fmt(" "), | |
| 9944 | extra.info.success_ordering.fmt(" "), | |
| 9945 | extra.info.failure_ordering.fmt(" "), | |
| 9946 | extra.info.alignment.fmt(", "), | |
| 9793 | 9947 | }); |
| 9794 | 9948 | }, |
| 9795 | 9949 | .extractelement => |tag| { |
| 9796 | 9950 | const extra = |
| 9797 | 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 | 9953 | instruction_index.name(&function).fmt(self), |
| 9800 | 9954 | @tagName(tag), |
| 9801 | extra.val.fmt(function_index, self), | |
| 9802 | extra.index.fmt(function_index, self), | |
| 9955 | extra.val.fmt(function_index, self, .{ .percent = true }), | |
| 9956 | extra.index.fmt(function_index, self, .{ .percent = true }), | |
| 9803 | 9957 | }); |
| 9804 | 9958 | }, |
| 9805 | 9959 | .extractvalue => |tag| { |
| ... | ... | @@ -9808,29 +9962,29 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9808 | 9962 | instruction.data, |
| 9809 | 9963 | ); |
| 9810 | 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 | 9966 | instruction_index.name(&function).fmt(self), |
| 9813 | 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 | 9972 | .fence => |tag| { |
| 9819 | 9973 | const info: MemoryAccessInfo = @bitCast(instruction.data); |
| 9820 | try bw.print(" {s}{f }{f }", .{ | |
| 9821 | @tagName(tag), | |
| 9822 | info.sync_scope, | |
| 9823 | info.success_ordering, | |
| 9974 | try w.print(" {t}{f}{f}", .{ | |
| 9975 | tag, | |
| 9976 | info.sync_scope.fmt(" "), | |
| 9977 | info.success_ordering.fmt(" "), | |
| 9824 | 9978 | }); |
| 9825 | 9979 | }, |
| 9826 | 9980 | .fneg, |
| 9827 | 9981 | .@"fneg fast", |
| 9828 | 9982 | => |tag| { |
| 9829 | 9983 | const val: Value = @enumFromInt(instruction.data); |
| 9830 | try bw.print(" %{f} = {s} {f%}", .{ | |
| 9984 | try w.print(" %{f} = {s} {f}", .{ | |
| 9831 | 9985 | instruction_index.name(&function).fmt(self), |
| 9832 | 9986 | @tagName(tag), |
| 9833 | val.fmt(function_index, self), | |
| 9987 | val.fmt(function_index, self, .{ .percent = true }), | |
| 9834 | 9988 | }); |
| 9835 | 9989 | }, |
| 9836 | 9990 | .getelementptr, |
| ... | ... | @@ -9841,14 +9995,14 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9841 | 9995 | instruction.data, |
| 9842 | 9996 | ); |
| 9843 | 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 | 9999 | instruction_index.name(&function).fmt(self), |
| 9846 | 10000 | @tagName(tag), |
| 9847 | extra.data.type.fmt(self), | |
| 9848 | extra.data.base.fmt(function_index, self), | |
| 10001 | extra.data.type.fmt(self, .percent), | |
| 10002 | extra.data.base.fmt(function_index, self, .{ .percent = true }), | |
| 9849 | 10003 | }); |
| 9850 | for (indices) |index| try bw.print(", {f%}", .{ | |
| 9851 | index.fmt(function_index, self), | |
| 10004 | for (indices) |index| try w.print(", {f}", .{ | |
| 10005 | index.fmt(function_index, self, .{ .percent = true }), | |
| 9852 | 10006 | }); |
| 9853 | 10007 | }, |
| 9854 | 10008 | .indirectbr => |tag| { |
| ... | ... | @@ -9856,54 +10010,54 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9856 | 10010 | function.extraDataTrail(Function.Instruction.IndirectBr, instruction.data); |
| 9857 | 10011 | const targets = |
| 9858 | 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 | 10014 | @tagName(tag), |
| 9861 | extra.data.addr.fmt(function_index, self), | |
| 10015 | extra.data.addr.fmt(function_index, self, .{ .percent = true }), | |
| 9862 | 10016 | }); |
| 9863 | 10017 | for (0.., targets) |target_index, target| { |
| 9864 | if (target_index > 0) try bw.writeAll(", "); | |
| 9865 | try bw.print("{f%}", .{ | |
| 9866 | target.toInst(&function).fmt(function_index, self), | |
| 10018 | if (target_index > 0) try w.writeAll(", "); | |
| 10019 | try w.print("{f}", .{ | |
| 10020 | target.toInst(&function).fmt(function_index, self, .{ .percent = true }), | |
| 9867 | 10021 | }); |
| 9868 | 10022 | } |
| 9869 | try bw.writeByte(']'); | |
| 10023 | try w.writeByte(']'); | |
| 9870 | 10024 | }, |
| 9871 | 10025 | .insertelement => |tag| { |
| 9872 | 10026 | const extra = |
| 9873 | 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 | 10029 | instruction_index.name(&function).fmt(self), |
| 9876 | 10030 | @tagName(tag), |
| 9877 | extra.val.fmt(function_index, self), | |
| 9878 | extra.elem.fmt(function_index, self), | |
| 9879 | extra.index.fmt(function_index, self), | |
| 10031 | extra.val.fmt(function_index, self, .{ .percent = true }), | |
| 10032 | extra.elem.fmt(function_index, self, .{ .percent = true }), | |
| 10033 | extra.index.fmt(function_index, self, .{ .percent = true }), | |
| 9880 | 10034 | }); |
| 9881 | 10035 | }, |
| 9882 | 10036 | .insertvalue => |tag| { |
| 9883 | 10037 | var extra = |
| 9884 | 10038 | function.extraDataTrail(Function.Instruction.InsertValue, instruction.data); |
| 9885 | 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 | 10041 | instruction_index.name(&function).fmt(self), |
| 9888 | 10042 | @tagName(tag), |
| 9889 | extra.data.val.fmt(function_index, self), | |
| 9890 | extra.data.elem.fmt(function_index, self), | |
| 10043 | extra.data.val.fmt(function_index, self, .{ .percent = true }), | |
| 10044 | extra.data.elem.fmt(function_index, self, .{ .percent = true }), | |
| 9891 | 10045 | }); |
| 9892 | for (indices) |index| try bw.print(", {d}", .{index}); | |
| 10046 | for (indices) |index| try w.print(", {d}", .{index}); | |
| 9893 | 10047 | }, |
| 9894 | 10048 | .load, |
| 9895 | 10049 | .@"load atomic", |
| 9896 | 10050 | => |tag| { |
| 9897 | 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 | 10053 | instruction_index.name(&function).fmt(self), |
| 9900 | @tagName(tag), | |
| 9901 | extra.info.access_kind, | |
| 9902 | extra.type.fmt(self), | |
| 9903 | extra.ptr.fmt(function_index, self), | |
| 9904 | extra.info.sync_scope, | |
| 9905 | extra.info.success_ordering, | |
| 9906 | extra.info.alignment, | |
| 10054 | tag, | |
| 10055 | extra.info.access_kind.fmt(" "), | |
| 10056 | extra.type.fmt(self, .percent), | |
| 10057 | extra.ptr.fmt(function_index, self, .{ .percent = true }), | |
| 10058 | extra.info.sync_scope.fmt(" "), | |
| 10059 | extra.info.success_ordering.fmt(" "), | |
| 10060 | extra.info.alignment.fmt(", "), | |
| 9907 | 10061 | }); |
| 9908 | 10062 | }, |
| 9909 | 10063 | .phi, |
| ... | ... | @@ -9913,64 +10067,64 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9913 | 10067 | const vals = extra.trail.next(block_incoming_len, Value, &function); |
| 9914 | 10068 | const blocks = |
| 9915 | 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 | 10071 | instruction_index.name(&function).fmt(self), |
| 9918 | 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 | 10075 | for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| { |
| 9922 | if (incoming_index > 0) try bw.writeAll(", "); | |
| 9923 | try bw.print("[ {f}, {f} ]", .{ | |
| 9924 | incoming_val.fmt(function_index, self), | |
| 9925 | incoming_block.toInst(&function).fmt(function_index, self), | |
| 10076 | if (incoming_index > 0) try w.writeAll(", "); | |
| 10077 | try w.print("[ {f}, {f} ]", .{ | |
| 10078 | incoming_val.fmt(function_index, self, .{}), | |
| 10079 | incoming_block.toInst(&function).fmt(function_index, self, .{}), | |
| 9926 | 10080 | }); |
| 9927 | 10081 | } |
| 9928 | 10082 | }, |
| 9929 | 10083 | .ret => |tag| { |
| 9930 | 10084 | const val: Value = @enumFromInt(instruction.data); |
| 9931 | try bw.print(" {s} {f%}", .{ | |
| 10085 | try w.print(" {s} {f}", .{ | |
| 9932 | 10086 | @tagName(tag), |
| 9933 | val.fmt(function_index, self), | |
| 10087 | val.fmt(function_index, self, .{ .percent = true }), | |
| 9934 | 10088 | }); |
| 9935 | 10089 | }, |
| 9936 | 10090 | .@"ret void", |
| 9937 | 10091 | .@"unreachable", |
| 9938 | => |tag| try bw.print(" {s}", .{@tagName(tag)}), | |
| 10092 | => |tag| try w.print(" {s}", .{@tagName(tag)}), | |
| 9939 | 10093 | .select, |
| 9940 | 10094 | .@"select fast", |
| 9941 | 10095 | => |tag| { |
| 9942 | 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 | 10098 | instruction_index.name(&function).fmt(self), |
| 9945 | 10099 | @tagName(tag), |
| 9946 | extra.cond.fmt(function_index, self), | |
| 9947 | extra.lhs.fmt(function_index, self), | |
| 9948 | extra.rhs.fmt(function_index, self), | |
| 10100 | extra.cond.fmt(function_index, self, .{ .percent = true }), | |
| 10101 | extra.lhs.fmt(function_index, self, .{ .percent = true }), | |
| 10102 | extra.rhs.fmt(function_index, self, .{ .percent = true }), | |
| 9949 | 10103 | }); |
| 9950 | 10104 | }, |
| 9951 | 10105 | .shufflevector => |tag| { |
| 9952 | 10106 | const extra = |
| 9953 | 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 | 10109 | instruction_index.name(&function).fmt(self), |
| 9956 | 10110 | @tagName(tag), |
| 9957 | extra.lhs.fmt(function_index, self), | |
| 9958 | extra.rhs.fmt(function_index, self), | |
| 9959 | extra.mask.fmt(function_index, self), | |
| 10111 | extra.lhs.fmt(function_index, self, .{ .percent = true }), | |
| 10112 | extra.rhs.fmt(function_index, self, .{ .percent = true }), | |
| 10113 | extra.mask.fmt(function_index, self, .{ .percent = true }), | |
| 9960 | 10114 | }); |
| 9961 | 10115 | }, |
| 9962 | 10116 | .store, |
| 9963 | 10117 | .@"store atomic", |
| 9964 | 10118 | => |tag| { |
| 9965 | 10119 | const extra = function.extraData(Function.Instruction.Store, instruction.data); |
| 9966 | try bw.print(" {s}{f } {f%}, {f%}{f }{f }{f, }", .{ | |
| 9967 | @tagName(tag), | |
| 9968 | extra.info.access_kind, | |
| 9969 | extra.val.fmt(function_index, self), | |
| 9970 | extra.ptr.fmt(function_index, self), | |
| 9971 | extra.info.sync_scope, | |
| 9972 | extra.info.success_ordering, | |
| 9973 | extra.info.alignment, | |
| 10120 | try w.print(" {t}{f} {f}, {f}{f}{f}{f}", .{ | |
| 10121 | tag, | |
| 10122 | extra.info.access_kind.fmt(" "), | |
| 10123 | extra.val.fmt(function_index, self, .{ .percent = true }), | |
| 10124 | extra.ptr.fmt(function_index, self, .{ .percent = true }), | |
| 10125 | extra.info.sync_scope.fmt(" "), | |
| 10126 | extra.info.success_ordering.fmt(" "), | |
| 10127 | extra.info.alignment.fmt(", "), | |
| 9974 | 10128 | }); |
| 9975 | 10129 | }, |
| 9976 | 10130 | .@"switch" => |tag| { |
| ... | ... | @@ -9979,80 +10133,80 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 9979 | 10133 | const vals = extra.trail.next(extra.data.cases_len, Constant, &function); |
| 9980 | 10134 | const blocks = |
| 9981 | 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 | 10137 | @tagName(tag), |
| 9984 | extra.data.val.fmt(function_index, self), | |
| 9985 | extra.data.default.toInst(&function).fmt(function_index, self), | |
| 10138 | extra.data.val.fmt(function_index, self, .{ .percent = true }), | |
| 10139 | extra.data.default.toInst(&function).fmt(function_index, self, .{ .percent = true }), | |
| 9986 | 10140 | }); |
| 9987 | for (vals, blocks) |case_val, case_block| try bw.print( | |
| 9988 | " {f%}, {f%}\n", | |
| 10141 | for (vals, blocks) |case_val, case_block| try w.print( | |
| 10142 | " {f}, {f}\n", | |
| 9989 | 10143 | .{ |
| 9990 | case_val.fmt(self), | |
| 9991 | case_block.toInst(&function).fmt(function_index, self), | |
| 10144 | case_val.fmt(self, .{ .percent = true }), | |
| 10145 | case_block.toInst(&function).fmt(function_index, self, .{ .percent = true }), | |
| 9992 | 10146 | }, |
| 9993 | 10147 | ); |
| 9994 | try bw.writeAll(" ]"); | |
| 10148 | try w.writeAll(" ]"); | |
| 9995 | 10149 | metadata_formatter.need_comma = true; |
| 9996 | 10150 | defer metadata_formatter.need_comma = undefined; |
| 9997 | 10151 | switch (extra.data.weights) { |
| 9998 | 10152 | .none => {}, |
| 9999 | .unpredictable => try bw.writeAll("!unpredictable !{}"), | |
| 10000 | _ => try bw.print("{f}", .{ | |
| 10001 | try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))), | |
| 10153 | .unpredictable => try w.writeAll("!unpredictable !{}"), | |
| 10154 | _ => try w.print("{f}", .{ | |
| 10155 | try metadata_formatter.fmt("!prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights))), null), | |
| 10002 | 10156 | }), |
| 10003 | 10157 | } |
| 10004 | 10158 | }, |
| 10005 | 10159 | .va_arg => |tag| { |
| 10006 | 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 | 10162 | instruction_index.name(&function).fmt(self), |
| 10009 | 10163 | @tagName(tag), |
| 10010 | extra.list.fmt(function_index, self), | |
| 10011 | extra.type.fmt(self), | |
| 10164 | extra.list.fmt(function_index, self, .{ .percent = true }), | |
| 10165 | extra.type.fmt(self, .percent), | |
| 10012 | 10166 | }); |
| 10013 | 10167 | }, |
| 10014 | 10168 | } |
| 10015 | 10169 | |
| 10016 | 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 | } |
| 10025 | 10179 | |
| 10026 | 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 | 10182 | for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| |
| 10029 | try bw.print( | |
| 10030 | \\attributes #{d} = {{{f#"} }} | |
| 10183 | try w.print( | |
| 10184 | \\attributes #{d} = {{{f} }} | |
| 10031 | 10185 | \\ |
| 10032 | , .{ attribute_group_index, attribute_group.fmt(self) }); | |
| 10186 | , .{ attribute_group_index, attribute_group.fmt(self, .{ .pound = true, .quote = true }) }); | |
| 10033 | 10187 | } |
| 10034 | 10188 | |
| 10035 | 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 | 10191 | for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| { |
| 10038 | 10192 | const elements: []const Metadata = |
| 10039 | 10193 | @ptrCast(self.metadata_extra.items[data.index..][0..data.len]); |
| 10040 | try bw.writeByte('!'); | |
| 10041 | try printEscapedString(name.slice(self), .quote_unless_valid_identifier, bw); | |
| 10042 | try bw.writeAll(" = !{"); | |
| 10194 | try w.writeByte('!'); | |
| 10195 | try printEscapedString(name.slice(self), .quote_unless_valid_identifier, w); | |
| 10196 | try w.writeAll(" = !{"); | |
| 10043 | 10197 | metadata_formatter.need_comma = false; |
| 10044 | 10198 | defer metadata_formatter.need_comma = undefined; |
| 10045 | for (elements) |element| try bw.print("{f}", .{try metadata_formatter.fmt("", element)}); | |
| 10046 | try bw.writeAll("}\n"); | |
| 10199 | for (elements) |element| try w.print("{f}", .{try metadata_formatter.fmt("", element, null)}); | |
| 10200 | try w.writeAll("}\n"); | |
| 10047 | 10201 | } |
| 10048 | 10202 | } |
| 10049 | 10203 | |
| 10050 | 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 | 10206 | var metadata_index: usize = 0; |
| 10053 | 10207 | while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) { |
| 10054 | 10208 | @setEvalBranchQuota(10_000); |
| 10055 | try bw.print("!{d} = ", .{metadata_index}); | |
| 10209 | try w.print("!{d} = ", .{metadata_index}); | |
| 10056 | 10210 | metadata_formatter.need_comma = false; |
| 10057 | 10211 | defer metadata_formatter.need_comma = undefined; |
| 10058 | 10212 | |
| ... | ... | @@ -10065,7 +10219,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10065 | 10219 | .scope = location.scope, |
| 10066 | 10220 | .inlinedAt = location.inlined_at, |
| 10067 | 10221 | .isImplicitCode = false, |
| 10068 | }, bw); | |
| 10222 | }, w); | |
| 10069 | 10223 | continue; |
| 10070 | 10224 | }, |
| 10071 | 10225 | .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)), |
| ... | ... | @@ -10081,7 +10235,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10081 | 10235 | .checksumkind = null, |
| 10082 | 10236 | .checksum = null, |
| 10083 | 10237 | .source = null, |
| 10084 | }, bw); | |
| 10238 | }, w); | |
| 10085 | 10239 | }, |
| 10086 | 10240 | .compile_unit, |
| 10087 | 10241 | .@"compile_unit optimized", |
| ... | ... | @@ -10112,7 +10266,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10112 | 10266 | .rangesBaseAddress = null, |
| 10113 | 10267 | .sysroot = null, |
| 10114 | 10268 | .sdk = null, |
| 10115 | }, bw); | |
| 10269 | }, w); | |
| 10116 | 10270 | }, |
| 10117 | 10271 | .subprogram, |
| 10118 | 10272 | .@"subprogram local", |
| ... | ... | @@ -10146,7 +10300,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10146 | 10300 | .thrownTypes = null, |
| 10147 | 10301 | .annotations = null, |
| 10148 | 10302 | .targetFuncName = null, |
| 10149 | }, bw); | |
| 10303 | }, w); | |
| 10150 | 10304 | }, |
| 10151 | 10305 | .lexical_block => { |
| 10152 | 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 | 10309 | .file = extra.file, |
| 10156 | 10310 | .line = extra.line, |
| 10157 | 10311 | .column = extra.column, |
| 10158 | }, bw); | |
| 10312 | }, w); | |
| 10159 | 10313 | }, |
| 10160 | 10314 | .location => { |
| 10161 | 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 | 10319 | .scope = extra.scope, |
| 10166 | 10320 | .inlinedAt = extra.inlined_at, |
| 10167 | 10321 | .isImplicitCode = false, |
| 10168 | }, bw); | |
| 10322 | }, w); | |
| 10169 | 10323 | }, |
| 10170 | 10324 | .basic_bool_type, |
| 10171 | 10325 | .basic_unsigned_type, |
| ... | ... | @@ -10194,7 +10348,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10194 | 10348 | else => unreachable, |
| 10195 | 10349 | }), |
| 10196 | 10350 | .flags = null, |
| 10197 | }, bw); | |
| 10351 | }, w); | |
| 10198 | 10352 | }, |
| 10199 | 10353 | .composite_struct_type, |
| 10200 | 10354 | .composite_union_type, |
| ... | ... | @@ -10239,7 +10393,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10239 | 10393 | .allocated = null, |
| 10240 | 10394 | .rank = null, |
| 10241 | 10395 | .annotations = null, |
| 10242 | }, bw); | |
| 10396 | }, w); | |
| 10243 | 10397 | }, |
| 10244 | 10398 | .derived_pointer_type, |
| 10245 | 10399 | .derived_member_type, |
| ... | ... | @@ -10272,7 +10426,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10272 | 10426 | .extraData = null, |
| 10273 | 10427 | .dwarfAddressSpace = null, |
| 10274 | 10428 | .annotations = null, |
| 10275 | }, bw); | |
| 10429 | }, w); | |
| 10276 | 10430 | }, |
| 10277 | 10431 | .subroutine_type => { |
| 10278 | 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 | 10434 | .flags = null, |
| 10281 | 10435 | .cc = null, |
| 10282 | 10436 | .types = extra.types_tuple, |
| 10283 | }, bw); | |
| 10437 | }, w); | |
| 10284 | 10438 | }, |
| 10285 | 10439 | .enumerator_unsigned, |
| 10286 | 10440 | .enumerator_signed_positive, |
| ... | ... | @@ -10330,7 +10484,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10330 | 10484 | => false, |
| 10331 | 10485 | else => unreachable, |
| 10332 | 10486 | }, |
| 10333 | }, bw); | |
| 10487 | }, w); | |
| 10334 | 10488 | }, |
| 10335 | 10489 | .subrange => { |
| 10336 | 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 | 10493 | .lowerBound = extra.lower_bound, |
| 10340 | 10494 | .upperBound = null, |
| 10341 | 10495 | .stride = null, |
| 10342 | }, bw); | |
| 10496 | }, w); | |
| 10343 | 10497 | }, |
| 10344 | 10498 | .tuple => { |
| 10345 | 10499 | var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data); |
| 10346 | 10500 | const elements = extra.trail.next(extra.data.elements_len, Metadata, self); |
| 10347 | try bw.writeAll("!{"); | |
| 10348 | for (elements) |element| try bw.print("{[element]f%}", .{ | |
| 10349 | .element = try metadata_formatter.fmt("", element), | |
| 10501 | try w.writeAll("!{"); | |
| 10502 | for (elements) |element| try w.print("{[element]f}", .{ | |
| 10503 | .element = try metadata_formatter.fmt("", element, .{ .percent = true }), | |
| 10350 | 10504 | }); |
| 10351 | try bw.writeAll("}\n"); | |
| 10505 | try w.writeAll("}\n"); | |
| 10352 | 10506 | }, |
| 10353 | 10507 | .str_tuple => { |
| 10354 | 10508 | var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data); |
| 10355 | 10509 | const elements = extra.trail.next(extra.data.elements_len, Metadata, self); |
| 10356 | try bw.print("!{{{[str]f%}", .{ | |
| 10357 | .str = try metadata_formatter.fmt("", extra.data.str), | |
| 10510 | try w.print("!{{{[str]f}", .{ | |
| 10511 | .str = try metadata_formatter.fmt("", extra.data.str, .{ .percent = true }), | |
| 10358 | 10512 | }); |
| 10359 | for (elements) |element| try bw.print("{[element]f%}", .{ | |
| 10360 | .element = try metadata_formatter.fmt("", element), | |
| 10513 | for (elements) |element| try w.print("{[element]f}", .{ | |
| 10514 | .element = try metadata_formatter.fmt("", element, .{ .percent = true }), | |
| 10361 | 10515 | }); |
| 10362 | try bw.writeAll("}\n"); | |
| 10516 | try w.writeAll("}\n"); | |
| 10363 | 10517 | }, |
| 10364 | 10518 | .module_flag => { |
| 10365 | 10519 | const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data); |
| 10366 | try bw.print("!{{{[behavior]f%}{[name]f%}{[constant]f%}}}\n", .{ | |
| 10367 | .behavior = try metadata_formatter.fmt("", extra.behavior), | |
| 10368 | .name = try metadata_formatter.fmt("", extra.name), | |
| 10369 | .constant = try metadata_formatter.fmt("", extra.constant), | |
| 10520 | try w.print("!{{{[behavior]f}{[name]f}{[constant]f}}}\n", .{ | |
| 10521 | .behavior = try metadata_formatter.fmt("", extra.behavior, .{ .percent = true }), | |
| 10522 | .name = try metadata_formatter.fmt("", extra.name, .{ .percent = true }), | |
| 10523 | .constant = try metadata_formatter.fmt("", extra.constant, .{ .percent = true }), | |
| 10370 | 10524 | }); |
| 10371 | 10525 | }, |
| 10372 | 10526 | .local_var => { |
| ... | ... | @@ -10381,7 +10535,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10381 | 10535 | .flags = null, |
| 10382 | 10536 | .@"align" = null, |
| 10383 | 10537 | .annotations = null, |
| 10384 | }, bw); | |
| 10538 | }, w); | |
| 10385 | 10539 | }, |
| 10386 | 10540 | .parameter => { |
| 10387 | 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 | 10549 | .flags = null, |
| 10396 | 10550 | .@"align" = null, |
| 10397 | 10551 | .annotations = null, |
| 10398 | }, bw); | |
| 10552 | }, w); | |
| 10399 | 10553 | }, |
| 10400 | 10554 | .global_var, |
| 10401 | 10555 | .@"global_var local", |
| ... | ... | @@ -10418,7 +10572,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10418 | 10572 | .templateParams = null, |
| 10419 | 10573 | .@"align" = null, |
| 10420 | 10574 | .annotations = null, |
| 10421 | }, bw); | |
| 10575 | }, w); | |
| 10422 | 10576 | }, |
| 10423 | 10577 | .global_var_expression => { |
| 10424 | 10578 | const extra = |
| ... | ... | @@ -10426,7 +10580,7 @@ pub fn print(self: *Builder, bw: *Writer) Writer.Error!void { |
| 10426 | 10580 | try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{ |
| 10427 | 10581 | .@"var" = extra.variable, |
| 10428 | 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 | 10599 | } |
| 10446 | 10600 | |
| 10447 | 10601 | const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier }; |
| 10448 | fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *Writer) Writer.Error!void { | |
| 10602 | fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, w: *Writer) Writer.Error!void { | |
| 10449 | 10603 | const need_quotes = switch (quotes) { |
| 10450 | 10604 | .always_quote => true, |
| 10451 | 10605 | .quote_unless_valid_identifier => !isValidIdentifier(slice), |
| 10452 | 10606 | }; |
| 10453 | if (need_quotes) try bw.writeByte('"'); | |
| 10607 | if (need_quotes) try w.writeByte('"'); | |
| 10454 | 10608 | for (slice) |byte| switch (byte) { |
| 10455 | '\\' => try bw.writeAll("\\\\"), | |
| 10456 | ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try bw.writeByte(byte), | |
| 10457 | else => try bw.print("\\{X:0>2}", .{byte}), | |
| 10609 | '\\' => try w.writeAll("\\\\"), | |
| 10610 | ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try w.writeByte(byte), | |
| 10611 | else => try w.print("\\{X:0>2}", .{byte}), | |
| 10458 | 10612 | }; |
| 10459 | if (need_quotes) try bw.writeByte('"'); | |
| 10613 | if (need_quotes) try w.writeByte('"'); | |
| 10460 | 10614 | } |
| 10461 | 10615 | |
| 10462 | 10616 | fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void { |
| ... | ... | @@ -15084,13 +15238,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco |
| 15084 | 15238 | return bitcode.toOwnedSlice(); |
| 15085 | 15239 | } |
| 15086 | 15240 | |
| 15087 | const std = @import("../../std.zig"); | |
| 15088 | const Allocator = std.mem.Allocator; | |
| 15089 | const assert = std.debug.assert; | |
| 15090 | const bitcode_writer = @import("bitcode_writer.zig"); | |
| 15091 | const Builder = @This(); | |
| 15092 | const builtin = @import("builtin"); | |
| 15093 | const DW = std.dwarf; | |
| 15094 | const ir = @import("ir.zig"); | |
| 15095 | const log = std.log.scoped(.llvm); | |
| 15096 | const Writer = std.io.Writer; | |
| 15241 | const FormatFlags = struct { | |
| 15242 | comma: bool = false, | |
| 15243 | space: bool = false, | |
| 15244 | percent: bool = false, | |
| 15245 | ||
| 15246 | fn onlyPercent(f: FormatFlags) bool { | |
| 15247 | return !f.comma and !f.space and f.percent; | |
| 15248 | } | |
| 15249 | }; |
lib/std/zig/parser_test.zig+10-149| ... | ... | @@ -1,3 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const print = std.debug.print; | |
| 4 | const io = std.io; | |
| 5 | const maxInt = std.math.maxInt; | |
| 6 | ||
| 1 | 7 | test "zig fmt: remove extra whitespace at start and end of file with comment between" { |
| 2 | 8 | try testTransform( |
| 3 | 9 | \\ |
| ... | ... | @@ -341,15 +347,6 @@ test "zig fmt: nosuspend block" { |
| 341 | 347 | ); |
| 342 | 348 | } |
| 343 | 349 | |
| 344 | test "zig fmt: nosuspend await" { | |
| 345 | try testCanonical( | |
| 346 | \\fn foo() void { | |
| 347 | \\ x = nosuspend await y; | |
| 348 | \\} | |
| 349 | \\ | |
| 350 | ); | |
| 351 | } | |
| 352 | ||
| 353 | 350 | test "zig fmt: container declaration, single line" { |
| 354 | 351 | try testCanonical( |
| 355 | 352 | \\const X = struct { foo: i32 }; |
| ... | ... | @@ -1093,18 +1090,6 @@ test "zig fmt: block in slice expression" { |
| 1093 | 1090 | ); |
| 1094 | 1091 | } |
| 1095 | 1092 | |
| 1096 | test "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 | ||
| 1108 | 1093 | test "zig fmt: whitespace fixes" { |
| 1109 | 1094 | try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n", |
| 1110 | 1095 | \\test "" { |
| ... | ... | @@ -1549,17 +1534,6 @@ test "zig fmt: spaces around slice operator" { |
| 1549 | 1534 | ); |
| 1550 | 1535 | } |
| 1551 | 1536 | |
| 1552 | test "zig fmt: async call in if condition" { | |
| 1553 | try testCanonical( | |
| 1554 | \\comptime { | |
| 1555 | \\ if (async b()) { | |
| 1556 | \\ a(); | |
| 1557 | \\ } | |
| 1558 | \\} | |
| 1559 | \\ | |
| 1560 | ); | |
| 1561 | } | |
| 1562 | ||
| 1563 | 1537 | test "zig fmt: 2nd arg multiline string" { |
| 1564 | 1538 | try testCanonical( |
| 1565 | 1539 | \\comptime { |
| ... | ... | @@ -2770,11 +2744,11 @@ test "zig fmt: preserve spacing" { |
| 2770 | 2744 | \\const std = @import("std"); |
| 2771 | 2745 | \\ |
| 2772 | 2746 | \\pub fn main() !void { |
| 2773 | \\ var stdout_file = std.io.getStdOut; | |
| 2774 | \\ var stdout_file = std.io.getStdOut; | |
| 2747 | \\ var stdout_file = std.lol.abcd; | |
| 2748 | \\ var stdout_file = std.lol.abcd; | |
| 2775 | 2749 | \\ |
| 2776 | \\ var stdout_file = std.io.getStdOut; | |
| 2777 | \\ var stdout_file = std.io.getStdOut; | |
| 2750 | \\ var stdout_file = std.lol.abcd; | |
| 2751 | \\ var stdout_file = std.lol.abcd; | |
| 2778 | 2752 | \\} |
| 2779 | 2753 | \\ |
| 2780 | 2754 | ); |
| ... | ... | @@ -3946,27 +3920,6 @@ test "zig fmt: inline asm" { |
| 3946 | 3920 | ); |
| 3947 | 3921 | } |
| 3948 | 3922 | |
| 3949 | test "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 | ||
| 3970 | 3923 | test "zig fmt: nosuspend" { |
| 3971 | 3924 | try testCanonical( |
| 3972 | 3925 | \\const a = nosuspend foo(); |
| ... | ... | @@ -3989,14 +3942,6 @@ test "zig fmt: Block after if" { |
| 3989 | 3942 | ); |
| 3990 | 3943 | } |
| 3991 | 3944 | |
| 3992 | test "zig fmt: usingnamespace" { | |
| 3993 | try testCanonical( | |
| 3994 | \\usingnamespace @import("std"); | |
| 3995 | \\pub usingnamespace @import("std"); | |
| 3996 | \\ | |
| 3997 | ); | |
| 3998 | } | |
| 3999 | ||
| 4000 | 3945 | test "zig fmt: string identifier" { |
| 4001 | 3946 | try testCanonical( |
| 4002 | 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 | 5085 | ); |
| 5141 | 5086 | } |
| 5142 | 5087 | |
| 5143 | test "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 | ||
| 5154 | 5088 | test "zig fmt: respect extra newline between switch items" { |
| 5155 | 5089 | try testCanonical( |
| 5156 | 5090 | \\const a = switch (b) { |
| ... | ... | @@ -5719,34 +5653,6 @@ test "zig fmt: canonicalize symbols (primitive types)" { |
| 5719 | 5653 | ); |
| 5720 | 5654 | } |
| 5721 | 5655 | |
| 5722 | // Never unescape names spelled like keywords. | |
| 5723 | test "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 | ||
| 5750 | 5656 | test "zig fmt: no space before newline before multiline string" { |
| 5751 | 5657 | try testCanonical( |
| 5752 | 5658 | \\const S = struct { |
| ... | ... | @@ -6181,29 +6087,6 @@ test "recovery: missing return type" { |
| 6181 | 6087 | }); |
| 6182 | 6088 | } |
| 6183 | 6089 | |
| 6184 | test "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 | ||
| 6207 | 6090 | test "recovery: invalid extern/inline" { |
| 6208 | 6091 | try testError( |
| 6209 | 6092 | \\inline test "" { a & b; } |
| ... | ... | @@ -6232,22 +6115,6 @@ test "recovery: missing semicolon" { |
| 6232 | 6115 | }); |
| 6233 | 6116 | } |
| 6234 | 6117 | |
| 6235 | test "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 | 6118 | // TODO after https://github.com/ziglang/zig/issues/35 is implemented, |
| 6252 | 6119 | // we should be able to recover from this *at any indentation level*, |
| 6253 | 6120 | // reporting a parse error and yet also parsing all the decls even |
| ... | ... | @@ -6454,12 +6321,6 @@ test "ampersand" { |
| 6454 | 6321 | , &.{}); |
| 6455 | 6322 | } |
| 6456 | 6323 | |
| 6457 | const std = @import("std"); | |
| 6458 | const mem = std.mem; | |
| 6459 | const print = std.debug.print; | |
| 6460 | const io = std.io; | |
| 6461 | const maxInt = std.math.maxInt; | |
| 6462 | ||
| 6463 | 6324 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 6464 | 6325 | |
| 6465 | 6326 | fn 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 | 45 | raw_string: []const u8, |
| 46 | 46 | }; |
| 47 | 47 | |
| 48 | fn formatMessage(self: FormatMessage, bw: *Writer, comptime f: []const u8) !void { | |
| 49 | _ = f; | |
| 48 | fn formatMessage(self: FormatMessage, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 50 | 49 | switch (self.err) { |
| 51 | .invalid_escape_character => |bad_index| try bw.print( | |
| 50 | .invalid_escape_character => |bad_index| try writer.print( | |
| 52 | 51 | "invalid escape character: '{c}'", |
| 53 | 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 | 55 | "expected hex digit, found '{c}'", |
| 57 | 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 | 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 | 62 | "expected hex digit or '}}', found '{c}'", |
| 64 | 63 | .{self.raw_string[bad_index]}, |
| 65 | 64 | ), |
| 66 | .invalid_unicode_codepoint => try bw.writeAll( | |
| 65 | .invalid_unicode_codepoint => try writer.writeAll( | |
| 67 | 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 | 69 | "expected '{{', found '{c}'", |
| 71 | 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 | 73 | "expected '}}', found '{c}'", |
| 75 | 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 | 77 | "expected single quote ('), found '{c}'", |
| 79 | 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 | 81 | "invalid byte in string or character literal: '{c}'", |
| 83 | 82 | .{self.raw_string[bad_index]}, |
| 84 | 83 | ), |
| 85 | .empty_char_literal => try bw.writeAll( | |
| 84 | .empty_char_literal => try writer.writeAll( | |
| 86 | 85 | "empty character literal", |
| 87 | 86 | ), |
| 88 | 87 | } |
| 89 | 88 | } |
| 90 | 89 | |
| 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 | 91 | return .{ .data = .{ |
| 93 | 92 | .err = self, |
| 94 | 93 | .raw_string = raw_string, |
| ... | ... | @@ -318,6 +317,7 @@ test parseCharLiteral { |
| 318 | 317 | } |
| 319 | 318 | |
| 320 | 319 | /// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type. |
| 320 | /// | |
| 321 | 321 | /// Asserts `bytes` has '"' at beginning and end. |
| 322 | 322 | pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result { |
| 323 | 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 | 388 | const current_arch = builtin.cpu.arch; |
| 389 | 389 | switch (current_arch) { |
| 390 | 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 | 393 | .aarch64, .aarch64_be => { |
| 394 | 394 | const registers = [12]u64{ |
| ... | ... | @@ -410,13 +410,13 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { |
| 410 | 410 | return core; |
| 411 | 411 | }, |
| 412 | 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 | 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 | 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 | 421 | else => {}, |
| 422 | 422 | } |
lib/std/zig/tokenizer.zig-9| ... | ... | @@ -17,8 +17,6 @@ pub const Token = struct { |
| 17 | 17 | .{ "anyframe", .keyword_anyframe }, |
| 18 | 18 | .{ "anytype", .keyword_anytype }, |
| 19 | 19 | .{ "asm", .keyword_asm }, |
| 20 | .{ "async", .keyword_async }, | |
| 21 | .{ "await", .keyword_await }, | |
| 22 | 20 | .{ "break", .keyword_break }, |
| 23 | 21 | .{ "callconv", .keyword_callconv }, |
| 24 | 22 | .{ "catch", .keyword_catch }, |
| ... | ... | @@ -55,7 +53,6 @@ pub const Token = struct { |
| 55 | 53 | .{ "try", .keyword_try }, |
| 56 | 54 | .{ "union", .keyword_union }, |
| 57 | 55 | .{ "unreachable", .keyword_unreachable }, |
| 58 | .{ "usingnamespace", .keyword_usingnamespace }, | |
| 59 | 56 | .{ "var", .keyword_var }, |
| 60 | 57 | .{ "volatile", .keyword_volatile }, |
| 61 | 58 | .{ "while", .keyword_while }, |
| ... | ... | @@ -146,8 +143,6 @@ pub const Token = struct { |
| 146 | 143 | keyword_anyframe, |
| 147 | 144 | keyword_anytype, |
| 148 | 145 | keyword_asm, |
| 149 | keyword_async, | |
| 150 | keyword_await, | |
| 151 | 146 | keyword_break, |
| 152 | 147 | keyword_callconv, |
| 153 | 148 | keyword_catch, |
| ... | ... | @@ -184,7 +179,6 @@ pub const Token = struct { |
| 184 | 179 | keyword_try, |
| 185 | 180 | keyword_union, |
| 186 | 181 | keyword_unreachable, |
| 187 | keyword_usingnamespace, | |
| 188 | 182 | keyword_var, |
| 189 | 183 | keyword_volatile, |
| 190 | 184 | keyword_while, |
| ... | ... | @@ -273,8 +267,6 @@ pub const Token = struct { |
| 273 | 267 | .keyword_anyframe => "anyframe", |
| 274 | 268 | .keyword_anytype => "anytype", |
| 275 | 269 | .keyword_asm => "asm", |
| 276 | .keyword_async => "async", | |
| 277 | .keyword_await => "await", | |
| 278 | 270 | .keyword_break => "break", |
| 279 | 271 | .keyword_callconv => "callconv", |
| 280 | 272 | .keyword_catch => "catch", |
| ... | ... | @@ -311,7 +303,6 @@ pub const Token = struct { |
| 311 | 303 | .keyword_try => "try", |
| 312 | 304 | .keyword_union => "union", |
| 313 | 305 | .keyword_unreachable => "unreachable", |
| 314 | .keyword_usingnamespace => "usingnamespace", | |
| 315 | 306 | .keyword_var => "var", |
| 316 | 307 | .keyword_volatile => "volatile", |
| 317 | 308 | .keyword_while => "while", |
lib/std/zon/parse.zig+113-130| ... | ... | @@ -64,22 +64,14 @@ pub const Error = union(enum) { |
| 64 | 64 | } |
| 65 | 65 | }; |
| 66 | 66 | |
| 67 | fn formatMessage( | |
| 68 | self: []const u8, | |
| 69 | comptime f: []const u8, | |
| 70 | options: std.fmt.FormatOptions, | |
| 71 | writer: anytype, | |
| 72 | ) !void { | |
| 73 | _ = f; | |
| 74 | _ = options; | |
| 75 | ||
| 67 | fn formatMessage(self: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 76 | 68 | // Just writes the string for now, but we're keeping this behind a formatter so we have |
| 77 | 69 | // the option to extend it in the future to print more advanced messages (like `Error` |
| 78 | 70 | // does) without breaking the API. |
| 79 | try writer.writeAll(self); | |
| 71 | try w.writeAll(self); | |
| 80 | 72 | } |
| 81 | 73 | |
| 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 | 75 | return .{ .data = switch (self) { |
| 84 | 76 | .zoir => |note| note.msg.get(diag.zoir), |
| 85 | 77 | .type_check => |note| note.msg, |
| ... | ... | @@ -155,21 +147,14 @@ pub const Error = union(enum) { |
| 155 | 147 | diag: *const Diagnostics, |
| 156 | 148 | }; |
| 157 | 149 | |
| 158 | fn formatMessage( | |
| 159 | self: FormatMessage, | |
| 160 | comptime f: []const u8, | |
| 161 | options: std.fmt.FormatOptions, | |
| 162 | writer: anytype, | |
| 163 | ) !void { | |
| 164 | _ = f; | |
| 165 | _ = options; | |
| 150 | fn formatMessage(self: FormatMessage, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 166 | 151 | switch (self.err) { |
| 167 | .zoir => |err| try writer.writeAll(err.msg.get(self.diag.zoir)), | |
| 168 | .type_check => |tc| try writer.writeAll(tc.message), | |
| 152 | .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)), | |
| 153 | .type_check => |tc| try w.writeAll(tc.message), | |
| 169 | 154 | } |
| 170 | 155 | } |
| 171 | 156 | |
| 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 | 158 | return .{ .data = .{ |
| 174 | 159 | .err = self, |
| 175 | 160 | .diag = diag, |
| ... | ... | @@ -241,25 +226,18 @@ pub const Diagnostics = struct { |
| 241 | 226 | return .{ .diag = self }; |
| 242 | 227 | } |
| 243 | 228 | |
| 244 | pub fn format( | |
| 245 | self: *const @This(), | |
| 246 | comptime fmt: []const u8, | |
| 247 | options: std.fmt.FormatOptions, | |
| 248 | writer: anytype, | |
| 249 | ) !void { | |
| 250 | _ = fmt; | |
| 251 | _ = options; | |
| 229 | pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void { | |
| 252 | 230 | var errors = self.iterateErrors(); |
| 253 | 231 | while (errors.next()) |err| { |
| 254 | 232 | const loc = err.getLocation(self); |
| 255 | 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 }); | |
| 257 | 235 | |
| 258 | 236 | var notes = err.iterateNotes(self); |
| 259 | 237 | while (notes.next()) |note| { |
| 260 | 238 | const note_loc = note.getLocation(self); |
| 261 | 239 | const note_msg = note.fmtMessage(self); |
| 262 | try writer.print("{}:{}: note: {s}\n", .{ | |
| 240 | try w.print("{d}:{d}: note: {f}\n", .{ | |
| 263 | 241 | note_loc.line + 1, |
| 264 | 242 | note_loc.column + 1, |
| 265 | 243 | note_msg, |
| ... | ... | @@ -648,7 +626,7 @@ const Parser = struct { |
| 648 | 626 | .failure => |err| { |
| 649 | 627 | const token = self.ast.nodeMainToken(ast_node); |
| 650 | 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 | } |
| 654 | 632 | |
| ... | ... | @@ -1089,7 +1067,10 @@ const Parser = struct { |
| 1089 | 1067 | try buf.appendSlice(gpa, msg); |
| 1090 | 1068 | inline for (info.fields, 0..) |field_info, i| { |
| 1091 | 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 | 1075 | break :b .{ |
| 1095 | 1076 | .token = token, |
| ... | ... | @@ -1300,7 +1281,7 @@ test "std.zon ast errors" { |
| 1300 | 1281 | error.ParseZon, |
| 1301 | 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 | } |
| 1305 | 1286 | |
| 1306 | 1287 | test "std.zon comments" { |
| ... | ... | @@ -1322,7 +1303,7 @@ test "std.zon comments" { |
| 1322 | 1303 | , &diag, .{})); |
| 1323 | 1304 | try std.testing.expectFmt( |
| 1324 | 1305 | "1:1: error: expected expression, found 'a document comment'\n", |
| 1325 | "{}", | |
| 1306 | "{f}", | |
| 1326 | 1307 | .{diag}, |
| 1327 | 1308 | ); |
| 1328 | 1309 | } |
| ... | ... | @@ -1343,7 +1324,7 @@ test "std.zon failure/oom formatting" { |
| 1343 | 1324 | &diag, |
| 1344 | 1325 | .{}, |
| 1345 | 1326 | )); |
| 1346 | try std.testing.expectFmt("", "{}", .{diag}); | |
| 1327 | try std.testing.expectFmt("", "{f}", .{diag}); | |
| 1347 | 1328 | } |
| 1348 | 1329 | |
| 1349 | 1330 | test "std.zon fromSlice syntax error" { |
| ... | ... | @@ -1423,7 +1404,7 @@ test "std.zon unions" { |
| 1423 | 1404 | \\1:4: note: supported: 'x', 'y' |
| 1424 | 1405 | \\ |
| 1425 | 1406 | , |
| 1426 | "{}", | |
| 1407 | "{f}", | |
| 1427 | 1408 | .{diag}, |
| 1428 | 1409 | ); |
| 1429 | 1410 | } |
| ... | ... | @@ -1437,7 +1418,7 @@ test "std.zon unions" { |
| 1437 | 1418 | error.ParseZon, |
| 1438 | 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 | } |
| 1442 | 1423 | |
| 1443 | 1424 | // Extra field |
| ... | ... | @@ -1449,7 +1430,7 @@ test "std.zon unions" { |
| 1449 | 1430 | error.ParseZon, |
| 1450 | 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 | } |
| 1454 | 1435 | |
| 1455 | 1436 | // No fields |
| ... | ... | @@ -1461,7 +1442,7 @@ test "std.zon unions" { |
| 1461 | 1442 | error.ParseZon, |
| 1462 | 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 | } |
| 1466 | 1447 | |
| 1467 | 1448 | // Enum literals cannot coerce into untagged unions |
| ... | ... | @@ -1470,7 +1451,7 @@ test "std.zon unions" { |
| 1470 | 1451 | var diag: Diagnostics = .{}; |
| 1471 | 1452 | defer diag.deinit(gpa); |
| 1472 | 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 | } |
| 1475 | 1456 | |
| 1476 | 1457 | // Unknown field for enum literal coercion |
| ... | ... | @@ -1484,7 +1465,7 @@ test "std.zon unions" { |
| 1484 | 1465 | \\1:2: note: supported: 'x' |
| 1485 | 1466 | \\ |
| 1486 | 1467 | , |
| 1487 | "{}", | |
| 1468 | "{f}", | |
| 1488 | 1469 | .{diag}, |
| 1489 | 1470 | ); |
| 1490 | 1471 | } |
| ... | ... | @@ -1495,7 +1476,7 @@ test "std.zon unions" { |
| 1495 | 1476 | var diag: Diagnostics = .{}; |
| 1496 | 1477 | defer diag.deinit(gpa); |
| 1497 | 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 | } |
| 1501 | 1482 | |
| ... | ... | @@ -1551,7 +1532,7 @@ test "std.zon structs" { |
| 1551 | 1532 | \\1:12: note: supported: 'x', 'y' |
| 1552 | 1533 | \\ |
| 1553 | 1534 | , |
| 1554 | "{}", | |
| 1535 | "{f}", | |
| 1555 | 1536 | .{diag}, |
| 1556 | 1537 | ); |
| 1557 | 1538 | } |
| ... | ... | @@ -1569,7 +1550,7 @@ test "std.zon structs" { |
| 1569 | 1550 | \\1:4: error: duplicate struct field name |
| 1570 | 1551 | \\1:12: note: duplicate name here |
| 1571 | 1552 | \\ |
| 1572 | , "{}", .{diag}); | |
| 1553 | , "{f}", .{diag}); | |
| 1573 | 1554 | } |
| 1574 | 1555 | |
| 1575 | 1556 | // Ignore unknown fields |
| ... | ... | @@ -1594,7 +1575,7 @@ test "std.zon structs" { |
| 1594 | 1575 | \\1:4: error: unexpected field 'x' |
| 1595 | 1576 | \\1:4: note: none expected |
| 1596 | 1577 | \\ |
| 1597 | , "{}", .{diag}); | |
| 1578 | , "{f}", .{diag}); | |
| 1598 | 1579 | } |
| 1599 | 1580 | |
| 1600 | 1581 | // Missing field |
| ... | ... | @@ -1606,7 +1587,7 @@ test "std.zon structs" { |
| 1606 | 1587 | error.ParseZon, |
| 1607 | 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 | } |
| 1611 | 1592 | |
| 1612 | 1593 | // Default field |
| ... | ... | @@ -1633,7 +1614,7 @@ test "std.zon structs" { |
| 1633 | 1614 | try std.testing.expectFmt( |
| 1634 | 1615 | \\1:18: error: cannot initialize comptime field |
| 1635 | 1616 | \\ |
| 1636 | , "{}", .{diag}); | |
| 1617 | , "{f}", .{diag}); | |
| 1637 | 1618 | } |
| 1638 | 1619 | |
| 1639 | 1620 | // Enum field (regression test, we were previously getting the field name in an |
| ... | ... | @@ -1663,7 +1644,7 @@ test "std.zon structs" { |
| 1663 | 1644 | \\1:1: error: types are not available in ZON |
| 1664 | 1645 | \\1:1: note: replace the type with '.' |
| 1665 | 1646 | \\ |
| 1666 | , "{}", .{diag}); | |
| 1647 | , "{f}", .{diag}); | |
| 1667 | 1648 | } |
| 1668 | 1649 | |
| 1669 | 1650 | // Arrays |
| ... | ... | @@ -1676,7 +1657,7 @@ test "std.zon structs" { |
| 1676 | 1657 | \\1:1: error: types are not available in ZON |
| 1677 | 1658 | \\1:1: note: replace the type with '.' |
| 1678 | 1659 | \\ |
| 1679 | , "{}", .{diag}); | |
| 1660 | , "{f}", .{diag}); | |
| 1680 | 1661 | } |
| 1681 | 1662 | |
| 1682 | 1663 | // Slices |
| ... | ... | @@ -1689,7 +1670,7 @@ test "std.zon structs" { |
| 1689 | 1670 | \\1:1: error: types are not available in ZON |
| 1690 | 1671 | \\1:1: note: replace the type with '.' |
| 1691 | 1672 | \\ |
| 1692 | , "{}", .{diag}); | |
| 1673 | , "{f}", .{diag}); | |
| 1693 | 1674 | } |
| 1694 | 1675 | |
| 1695 | 1676 | // Tuples |
| ... | ... | @@ -1708,7 +1689,7 @@ test "std.zon structs" { |
| 1708 | 1689 | \\1:1: error: types are not available in ZON |
| 1709 | 1690 | \\1:1: note: replace the type with '.' |
| 1710 | 1691 | \\ |
| 1711 | , "{}", .{diag}); | |
| 1692 | , "{f}", .{diag}); | |
| 1712 | 1693 | } |
| 1713 | 1694 | |
| 1714 | 1695 | // Nested |
| ... | ... | @@ -1721,7 +1702,7 @@ test "std.zon structs" { |
| 1721 | 1702 | \\1:9: error: types are not available in ZON |
| 1722 | 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 | 1747 | error.ParseZon, |
| 1767 | 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 | } |
| 1771 | 1752 | |
| 1772 | 1753 | // Extra field |
| ... | ... | @@ -1780,7 +1761,7 @@ test "std.zon tuples" { |
| 1780 | 1761 | ); |
| 1781 | 1762 | try std.testing.expectFmt( |
| 1782 | 1763 | "1:2: error: missing tuple field with index 1\n", |
| 1783 | "{}", | |
| 1764 | "{f}", | |
| 1784 | 1765 | .{diag}, |
| 1785 | 1766 | ); |
| 1786 | 1767 | } |
| ... | ... | @@ -1794,7 +1775,7 @@ test "std.zon tuples" { |
| 1794 | 1775 | error.ParseZon, |
| 1795 | 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 | } |
| 1799 | 1780 | |
| 1800 | 1781 | // Struct with missing field names |
| ... | ... | @@ -1806,7 +1787,7 @@ test "std.zon tuples" { |
| 1806 | 1787 | error.ParseZon, |
| 1807 | 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 | } |
| 1811 | 1792 | |
| 1812 | 1793 | // Comptime field |
| ... | ... | @@ -1826,7 +1807,7 @@ test "std.zon tuples" { |
| 1826 | 1807 | try std.testing.expectFmt( |
| 1827 | 1808 | \\1:9: error: cannot initialize comptime field |
| 1828 | 1809 | \\ |
| 1829 | , "{}", .{diag}); | |
| 1810 | , "{f}", .{diag}); | |
| 1830 | 1811 | } |
| 1831 | 1812 | } |
| 1832 | 1813 | |
| ... | ... | @@ -1938,7 +1919,7 @@ test "std.zon arrays and slices" { |
| 1938 | 1919 | ); |
| 1939 | 1920 | try std.testing.expectFmt( |
| 1940 | 1921 | "1:3: error: index 0 outside of array of length 0\n", |
| 1941 | "{}", | |
| 1922 | "{f}", | |
| 1942 | 1923 | .{diag}, |
| 1943 | 1924 | ); |
| 1944 | 1925 | } |
| ... | ... | @@ -1953,7 +1934,7 @@ test "std.zon arrays and slices" { |
| 1953 | 1934 | ); |
| 1954 | 1935 | try std.testing.expectFmt( |
| 1955 | 1936 | "1:8: error: index 1 outside of array of length 1\n", |
| 1956 | "{}", | |
| 1937 | "{f}", | |
| 1957 | 1938 | .{diag}, |
| 1958 | 1939 | ); |
| 1959 | 1940 | } |
| ... | ... | @@ -1968,7 +1949,7 @@ test "std.zon arrays and slices" { |
| 1968 | 1949 | ); |
| 1969 | 1950 | try std.testing.expectFmt( |
| 1970 | 1951 | "1:2: error: expected 2 array elements; found 1\n", |
| 1971 | "{}", | |
| 1952 | "{f}", | |
| 1972 | 1953 | .{diag}, |
| 1973 | 1954 | ); |
| 1974 | 1955 | } |
| ... | ... | @@ -1983,7 +1964,7 @@ test "std.zon arrays and slices" { |
| 1983 | 1964 | ); |
| 1984 | 1965 | try std.testing.expectFmt( |
| 1985 | 1966 | "1:2: error: expected 3 array elements; found 0\n", |
| 1986 | "{}", | |
| 1967 | "{f}", | |
| 1987 | 1968 | .{diag}, |
| 1988 | 1969 | ); |
| 1989 | 1970 | } |
| ... | ... | @@ -1998,7 +1979,7 @@ test "std.zon arrays and slices" { |
| 1998 | 1979 | error.ParseZon, |
| 1999 | 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 | } |
| 2003 | 1984 | |
| 2004 | 1985 | // Slice |
| ... | ... | @@ -2009,7 +1990,7 @@ test "std.zon arrays and slices" { |
| 2009 | 1990 | error.ParseZon, |
| 2010 | 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 | } |
| 2015 | 1996 | |
| ... | ... | @@ -2023,7 +2004,7 @@ test "std.zon arrays and slices" { |
| 2023 | 2004 | error.ParseZon, |
| 2024 | 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 | } |
| 2028 | 2009 | |
| 2029 | 2010 | // Slice |
| ... | ... | @@ -2034,7 +2015,7 @@ test "std.zon arrays and slices" { |
| 2034 | 2015 | error.ParseZon, |
| 2035 | 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 | } |
| 2040 | 2021 | |
| ... | ... | @@ -2048,7 +2029,7 @@ test "std.zon arrays and slices" { |
| 2048 | 2029 | ); |
| 2049 | 2030 | try std.testing.expectFmt( |
| 2050 | 2031 | "1:3: error: pointers are not available in ZON\n", |
| 2051 | "{}", | |
| 2032 | "{f}", | |
| 2052 | 2033 | .{diag}, |
| 2053 | 2034 | ); |
| 2054 | 2035 | } |
| ... | ... | @@ -2087,7 +2068,7 @@ test "std.zon string literal" { |
| 2087 | 2068 | error.ParseZon, |
| 2088 | 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 | } |
| 2092 | 2073 | |
| 2093 | 2074 | { |
| ... | ... | @@ -2097,7 +2078,7 @@ test "std.zon string literal" { |
| 2097 | 2078 | error.ParseZon, |
| 2098 | 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 | } |
| 2103 | 2084 | |
| ... | ... | @@ -2114,7 +2095,7 @@ test "std.zon string literal" { |
| 2114 | 2095 | error.ParseZon, |
| 2115 | 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 | } |
| 2119 | 2100 | |
| 2120 | 2101 | { |
| ... | ... | @@ -2124,7 +2105,7 @@ test "std.zon string literal" { |
| 2124 | 2105 | error.ParseZon, |
| 2125 | 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 | } |
| 2130 | 2111 | |
| ... | ... | @@ -2166,7 +2147,7 @@ test "std.zon string literal" { |
| 2166 | 2147 | error.ParseZon, |
| 2167 | 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 | } |
| 2171 | 2152 | |
| 2172 | 2153 | { |
| ... | ... | @@ -2176,7 +2157,7 @@ test "std.zon string literal" { |
| 2176 | 2157 | error.ParseZon, |
| 2177 | 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 | } |
| 2182 | 2163 | |
| ... | ... | @@ -2188,7 +2169,7 @@ test "std.zon string literal" { |
| 2188 | 2169 | error.ParseZon, |
| 2189 | 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 | } |
| 2193 | 2174 | |
| 2194 | 2175 | // Expecting string literal, getting an incompatible tuple |
| ... | ... | @@ -2199,7 +2180,7 @@ test "std.zon string literal" { |
| 2199 | 2180 | error.ParseZon, |
| 2200 | 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 | } |
| 2204 | 2185 | |
| 2205 | 2186 | // Invalid string literal |
| ... | ... | @@ -2210,7 +2191,7 @@ test "std.zon string literal" { |
| 2210 | 2191 | error.ParseZon, |
| 2211 | 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 | } |
| 2215 | 2196 | |
| 2216 | 2197 | // Slice wrong child type |
| ... | ... | @@ -2222,7 +2203,7 @@ test "std.zon string literal" { |
| 2222 | 2203 | error.ParseZon, |
| 2223 | 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 | } |
| 2227 | 2208 | |
| 2228 | 2209 | { |
| ... | ... | @@ -2232,7 +2213,7 @@ test "std.zon string literal" { |
| 2232 | 2213 | error.ParseZon, |
| 2233 | 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 | } |
| 2238 | 2219 | |
| ... | ... | @@ -2245,7 +2226,7 @@ test "std.zon string literal" { |
| 2245 | 2226 | error.ParseZon, |
| 2246 | 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 | } |
| 2250 | 2231 | |
| 2251 | 2232 | { |
| ... | ... | @@ -2255,7 +2236,7 @@ test "std.zon string literal" { |
| 2255 | 2236 | error.ParseZon, |
| 2256 | 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 | } |
| 2261 | 2242 | |
| ... | ... | @@ -2329,7 +2310,7 @@ test "std.zon enum literals" { |
| 2329 | 2310 | \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' |
| 2330 | 2311 | \\ |
| 2331 | 2312 | , |
| 2332 | "{}", | |
| 2313 | "{f}", | |
| 2333 | 2314 | .{diag}, |
| 2334 | 2315 | ); |
| 2335 | 2316 | } |
| ... | ... | @@ -2347,7 +2328,7 @@ test "std.zon enum literals" { |
| 2347 | 2328 | \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' |
| 2348 | 2329 | \\ |
| 2349 | 2330 | , |
| 2350 | "{}", | |
| 2331 | "{f}", | |
| 2351 | 2332 | .{diag}, |
| 2352 | 2333 | ); |
| 2353 | 2334 | } |
| ... | ... | @@ -2360,7 +2341,7 @@ test "std.zon enum literals" { |
| 2360 | 2341 | error.ParseZon, |
| 2361 | 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 | } |
| 2365 | 2346 | |
| 2366 | 2347 | // Test embedded nulls in an identifier |
| ... | ... | @@ -2373,7 +2354,7 @@ test "std.zon enum literals" { |
| 2373 | 2354 | ); |
| 2374 | 2355 | try std.testing.expectFmt( |
| 2375 | 2356 | "1:2: error: identifier cannot contain null bytes\n", |
| 2376 | "{}", | |
| 2357 | "{f}", | |
| 2377 | 2358 | .{diag}, |
| 2378 | 2359 | ); |
| 2379 | 2360 | } |
| ... | ... | @@ -2399,13 +2380,13 @@ test "std.zon parse bool" { |
| 2399 | 2380 | \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' |
| 2400 | 2381 | \\1:2: note: precede identifier with '.' for an enum literal |
| 2401 | 2382 | \\ |
| 2402 | , "{}", .{diag}); | |
| 2383 | , "{f}", .{diag}); | |
| 2403 | 2384 | } |
| 2404 | 2385 | { |
| 2405 | 2386 | var diag: Diagnostics = .{}; |
| 2406 | 2387 | defer diag.deinit(gpa); |
| 2407 | 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 | } |
| 2411 | 2392 | |
| ... | ... | @@ -2478,7 +2459,7 @@ test "std.zon parse int" { |
| 2478 | 2459 | )); |
| 2479 | 2460 | try std.testing.expectFmt( |
| 2480 | 2461 | "1:1: error: type 'i66' cannot represent value\n", |
| 2481 | "{}", | |
| 2462 | "{f}", | |
| 2482 | 2463 | .{diag}, |
| 2483 | 2464 | ); |
| 2484 | 2465 | } |
| ... | ... | @@ -2494,7 +2475,7 @@ test "std.zon parse int" { |
| 2494 | 2475 | )); |
| 2495 | 2476 | try std.testing.expectFmt( |
| 2496 | 2477 | "1:1: error: type 'i66' cannot represent value\n", |
| 2497 | "{}", | |
| 2478 | "{f}", | |
| 2498 | 2479 | .{diag}, |
| 2499 | 2480 | ); |
| 2500 | 2481 | } |
| ... | ... | @@ -2583,7 +2564,7 @@ test "std.zon parse int" { |
| 2583 | 2564 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{})); |
| 2584 | 2565 | try std.testing.expectFmt( |
| 2585 | 2566 | "1:3: error: invalid digit 'a' for decimal base\n", |
| 2586 | "{}", | |
| 2567 | "{f}", | |
| 2587 | 2568 | .{diag}, |
| 2588 | 2569 | ); |
| 2589 | 2570 | } |
| ... | ... | @@ -2593,7 +2574,7 @@ test "std.zon parse int" { |
| 2593 | 2574 | var diag: Diagnostics = .{}; |
| 2594 | 2575 | defer diag.deinit(gpa); |
| 2595 | 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 | } |
| 2598 | 2579 | |
| 2599 | 2580 | // Failing because an int is out of range |
| ... | ... | @@ -2603,7 +2584,7 @@ test "std.zon parse int" { |
| 2603 | 2584 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{})); |
| 2604 | 2585 | try std.testing.expectFmt( |
| 2605 | 2586 | "1:1: error: type 'u8' cannot represent value\n", |
| 2606 | "{}", | |
| 2587 | "{f}", | |
| 2607 | 2588 | .{diag}, |
| 2608 | 2589 | ); |
| 2609 | 2590 | } |
| ... | ... | @@ -2615,7 +2596,7 @@ test "std.zon parse int" { |
| 2615 | 2596 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{})); |
| 2616 | 2597 | try std.testing.expectFmt( |
| 2617 | 2598 | "1:1: error: type 'i8' cannot represent value\n", |
| 2618 | "{}", | |
| 2599 | "{f}", | |
| 2619 | 2600 | .{diag}, |
| 2620 | 2601 | ); |
| 2621 | 2602 | } |
| ... | ... | @@ -2627,7 +2608,7 @@ test "std.zon parse int" { |
| 2627 | 2608 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{})); |
| 2628 | 2609 | try std.testing.expectFmt( |
| 2629 | 2610 | "1:1: error: type 'u8' cannot represent value\n", |
| 2630 | "{}", | |
| 2611 | "{f}", | |
| 2631 | 2612 | .{diag}, |
| 2632 | 2613 | ); |
| 2633 | 2614 | } |
| ... | ... | @@ -2639,7 +2620,7 @@ test "std.zon parse int" { |
| 2639 | 2620 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{})); |
| 2640 | 2621 | try std.testing.expectFmt( |
| 2641 | 2622 | "1:1: error: type 'u8' cannot represent value\n", |
| 2642 | "{}", | |
| 2623 | "{f}", | |
| 2643 | 2624 | .{diag}, |
| 2644 | 2625 | ); |
| 2645 | 2626 | } |
| ... | ... | @@ -2651,7 +2632,7 @@ test "std.zon parse int" { |
| 2651 | 2632 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{})); |
| 2652 | 2633 | try std.testing.expectFmt( |
| 2653 | 2634 | "1:1: error: type 'u8' cannot represent value\n", |
| 2654 | "{}", | |
| 2635 | "{f}", | |
| 2655 | 2636 | .{diag}, |
| 2656 | 2637 | ); |
| 2657 | 2638 | } |
| ... | ... | @@ -2666,7 +2647,7 @@ test "std.zon parse int" { |
| 2666 | 2647 | \\1:2: note: use '0' for an integer zero |
| 2667 | 2648 | \\1:2: note: use '-0.0' for a floating-point signed zero |
| 2668 | 2649 | \\ |
| 2669 | , "{}", .{diag}); | |
| 2650 | , "{f}", .{diag}); | |
| 2670 | 2651 | } |
| 2671 | 2652 | |
| 2672 | 2653 | // Negative integer zero casted to float |
| ... | ... | @@ -2679,7 +2660,7 @@ test "std.zon parse int" { |
| 2679 | 2660 | \\1:2: note: use '0' for an integer zero |
| 2680 | 2661 | \\1:2: note: use '-0.0' for a floating-point signed zero |
| 2681 | 2662 | \\ |
| 2682 | , "{}", .{diag}); | |
| 2663 | , "{f}", .{diag}); | |
| 2683 | 2664 | } |
| 2684 | 2665 | |
| 2685 | 2666 | // Negative float 0 is allowed |
| ... | ... | @@ -2695,7 +2676,7 @@ test "std.zon parse int" { |
| 2695 | 2676 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{})); |
| 2696 | 2677 | try std.testing.expectFmt( |
| 2697 | 2678 | "1:1: error: expected number or 'inf' after '-'\n", |
| 2698 | "{}", | |
| 2679 | "{f}", | |
| 2699 | 2680 | .{diag}, |
| 2700 | 2681 | ); |
| 2701 | 2682 | } |
| ... | ... | @@ -2709,7 +2690,7 @@ test "std.zon parse int" { |
| 2709 | 2690 | ); |
| 2710 | 2691 | try std.testing.expectFmt( |
| 2711 | 2692 | "1:1: error: expected number or 'inf' after '-'\n", |
| 2712 | "{}", | |
| 2693 | "{f}", | |
| 2713 | 2694 | .{diag}, |
| 2714 | 2695 | ); |
| 2715 | 2696 | } |
| ... | ... | @@ -2719,7 +2700,7 @@ test "std.zon parse int" { |
| 2719 | 2700 | var diag: Diagnostics = .{}; |
| 2720 | 2701 | defer diag.deinit(gpa); |
| 2721 | 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 | } |
| 2724 | 2705 | |
| 2725 | 2706 | // Notes on invalid int literal |
| ... | ... | @@ -2731,7 +2712,7 @@ test "std.zon parse int" { |
| 2731 | 2712 | \\1:1: error: number '0123' has leading zero |
| 2732 | 2713 | \\1:1: note: use '0o' prefix for octal literals |
| 2733 | 2714 | \\ |
| 2734 | , "{}", .{diag}); | |
| 2715 | , "{f}", .{diag}); | |
| 2735 | 2716 | } |
| 2736 | 2717 | } |
| 2737 | 2718 | |
| ... | ... | @@ -2744,7 +2725,7 @@ test "std.zon negative char" { |
| 2744 | 2725 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{})); |
| 2745 | 2726 | try std.testing.expectFmt( |
| 2746 | 2727 | "1:1: error: expected number or 'inf' after '-'\n", |
| 2747 | "{}", | |
| 2728 | "{f}", | |
| 2748 | 2729 | .{diag}, |
| 2749 | 2730 | ); |
| 2750 | 2731 | } |
| ... | ... | @@ -2754,13 +2735,15 @@ test "std.zon negative char" { |
| 2754 | 2735 | try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{})); |
| 2755 | 2736 | try std.testing.expectFmt( |
| 2756 | 2737 | "1:1: error: expected number or 'inf' after '-'\n", |
| 2757 | "{}", | |
| 2738 | "{f}", | |
| 2758 | 2739 | .{diag}, |
| 2759 | 2740 | ); |
| 2760 | 2741 | } |
| 2761 | 2742 | } |
| 2762 | 2743 | |
| 2763 | 2744 | test "std.zon parse float" { |
| 2745 | if (builtin.cpu.arch == .x86 and builtin.abi == .musl and builtin.link_mode == .dynamic) return error.SkipZigTest; | |
| 2746 | ||
| 2764 | 2747 | const gpa = std.testing.allocator; |
| 2765 | 2748 | |
| 2766 | 2749 | // Test decimals |
| ... | ... | @@ -2841,7 +2824,7 @@ test "std.zon parse float" { |
| 2841 | 2824 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{})); |
| 2842 | 2825 | try std.testing.expectFmt( |
| 2843 | 2826 | "1:1: error: expected number or 'inf' after '-'\n", |
| 2844 | "{}", | |
| 2827 | "{f}", | |
| 2845 | 2828 | .{diag}, |
| 2846 | 2829 | ); |
| 2847 | 2830 | } |
| ... | ... | @@ -2851,7 +2834,7 @@ test "std.zon parse float" { |
| 2851 | 2834 | var diag: Diagnostics = .{}; |
| 2852 | 2835 | defer diag.deinit(gpa); |
| 2853 | 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 | } |
| 2856 | 2839 | |
| 2857 | 2840 | // nan as int not allowed |
| ... | ... | @@ -2859,7 +2842,7 @@ test "std.zon parse float" { |
| 2859 | 2842 | var diag: Diagnostics = .{}; |
| 2860 | 2843 | defer diag.deinit(gpa); |
| 2861 | 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 | } |
| 2864 | 2847 | |
| 2865 | 2848 | // inf as int not allowed |
| ... | ... | @@ -2867,7 +2850,7 @@ test "std.zon parse float" { |
| 2867 | 2850 | var diag: Diagnostics = .{}; |
| 2868 | 2851 | defer diag.deinit(gpa); |
| 2869 | 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 | } |
| 2872 | 2855 | |
| 2873 | 2856 | // -inf as int not allowed |
| ... | ... | @@ -2875,7 +2858,7 @@ test "std.zon parse float" { |
| 2875 | 2858 | var diag: Diagnostics = .{}; |
| 2876 | 2859 | defer diag.deinit(gpa); |
| 2877 | 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 | } |
| 2880 | 2863 | |
| 2881 | 2864 | // Bad identifier as float |
| ... | ... | @@ -2888,7 +2871,7 @@ test "std.zon parse float" { |
| 2888 | 2871 | \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' |
| 2889 | 2872 | \\1:1: note: precede identifier with '.' for an enum literal |
| 2890 | 2873 | \\ |
| 2891 | , "{}", .{diag}); | |
| 2874 | , "{f}", .{diag}); | |
| 2892 | 2875 | } |
| 2893 | 2876 | |
| 2894 | 2877 | { |
| ... | ... | @@ -2897,7 +2880,7 @@ test "std.zon parse float" { |
| 2897 | 2880 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{})); |
| 2898 | 2881 | try std.testing.expectFmt( |
| 2899 | 2882 | "1:1: error: expected number or 'inf' after '-'\n", |
| 2900 | "{}", | |
| 2883 | "{f}", | |
| 2901 | 2884 | .{diag}, |
| 2902 | 2885 | ); |
| 2903 | 2886 | } |
| ... | ... | @@ -2910,7 +2893,7 @@ test "std.zon parse float" { |
| 2910 | 2893 | error.ParseZon, |
| 2911 | 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 | } |
| 2916 | 2899 | |
| ... | ... | @@ -3154,7 +3137,7 @@ test "std.zon vector" { |
| 3154 | 3137 | ); |
| 3155 | 3138 | try std.testing.expectFmt( |
| 3156 | 3139 | "1:2: error: expected 2 vector elements; found 1\n", |
| 3157 | "{}", | |
| 3140 | "{f}", | |
| 3158 | 3141 | .{diag}, |
| 3159 | 3142 | ); |
| 3160 | 3143 | } |
| ... | ... | @@ -3169,7 +3152,7 @@ test "std.zon vector" { |
| 3169 | 3152 | ); |
| 3170 | 3153 | try std.testing.expectFmt( |
| 3171 | 3154 | "1:2: error: expected 2 vector elements; found 3\n", |
| 3172 | "{}", | |
| 3155 | "{f}", | |
| 3173 | 3156 | .{diag}, |
| 3174 | 3157 | ); |
| 3175 | 3158 | } |
| ... | ... | @@ -3184,7 +3167,7 @@ test "std.zon vector" { |
| 3184 | 3167 | ); |
| 3185 | 3168 | try std.testing.expectFmt( |
| 3186 | 3169 | "1:8: error: expected type 'f32'\n", |
| 3187 | "{}", | |
| 3170 | "{f}", | |
| 3188 | 3171 | .{diag}, |
| 3189 | 3172 | ); |
| 3190 | 3173 | } |
| ... | ... | @@ -3197,7 +3180,7 @@ test "std.zon vector" { |
| 3197 | 3180 | error.ParseZon, |
| 3198 | 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 | } |
| 3202 | 3185 | |
| 3203 | 3186 | // Elements should get freed on error |
| ... | ... | @@ -3208,7 +3191,7 @@ test "std.zon vector" { |
| 3208 | 3191 | error.ParseZon, |
| 3209 | 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 | } |
| 3214 | 3197 | |
| ... | ... | @@ -3332,7 +3315,7 @@ test "std.zon add pointers" { |
| 3332 | 3315 | error.ParseZon, |
| 3333 | 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 | } |
| 3337 | 3320 | |
| 3338 | 3321 | { |
| ... | ... | @@ -3342,7 +3325,7 @@ test "std.zon add pointers" { |
| 3342 | 3325 | error.ParseZon, |
| 3343 | 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 | } |
| 3347 | 3330 | |
| 3348 | 3331 | { |
| ... | ... | @@ -3352,7 +3335,7 @@ test "std.zon add pointers" { |
| 3352 | 3335 | error.ParseZon, |
| 3353 | 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 | } |
| 3357 | 3340 | |
| 3358 | 3341 | { |
| ... | ... | @@ -3362,7 +3345,7 @@ test "std.zon add pointers" { |
| 3362 | 3345 | error.ParseZon, |
| 3363 | 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 | } |
| 3367 | 3350 | |
| 3368 | 3351 | { |
| ... | ... | @@ -3372,7 +3355,7 @@ test "std.zon add pointers" { |
| 3372 | 3355 | error.ParseZon, |
| 3373 | 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 | } |
| 3377 | 3360 | |
| 3378 | 3361 | { |
| ... | ... | @@ -3382,7 +3365,7 @@ test "std.zon add pointers" { |
| 3382 | 3365 | error.ParseZon, |
| 3383 | 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 | } |
| 3387 | 3370 | |
| 3388 | 3371 | { |
| ... | ... | @@ -3392,7 +3375,7 @@ test "std.zon add pointers" { |
| 3392 | 3375 | error.ParseZon, |
| 3393 | 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 | } |
| 3397 | 3380 | |
| 3398 | 3381 | { |
| ... | ... | @@ -3402,7 +3385,7 @@ test "std.zon add pointers" { |
| 3402 | 3385 | error.ParseZon, |
| 3403 | 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 | } |
| 3407 | 3390 | |
| 3408 | 3391 | { |
| ... | ... | @@ -3412,7 +3395,7 @@ test "std.zon add pointers" { |
| 3412 | 3395 | error.ParseZon, |
| 3413 | 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 | } |
| 3417 | 3400 | |
| 3418 | 3401 | { |
| ... | ... | @@ -3422,7 +3405,7 @@ test "std.zon add pointers" { |
| 3422 | 3405 | error.ParseZon, |
| 3423 | 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 | } |
| 3427 | 3410 | |
| 3428 | 3411 | { |
| ... | ... | @@ -3432,7 +3415,7 @@ test "std.zon add pointers" { |
| 3432 | 3415 | error.ParseZon, |
| 3433 | 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 | } |
| 3437 | 3420 | |
| 3438 | 3421 | { |
| ... | ... | @@ -3442,7 +3425,7 @@ test "std.zon add pointers" { |
| 3442 | 3425 | error.ParseZon, |
| 3443 | 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 | } |
| 3447 | 3430 | |
| 3448 | 3431 | { |
| ... | ... | @@ -3452,7 +3435,7 @@ test "std.zon add pointers" { |
| 3452 | 3435 | error.ParseZon, |
| 3453 | 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 | } |
| 3458 | 3441 |
lib/ubsan_rt.zig+26-44| ... | ... | @@ -119,24 +119,22 @@ const Value = extern struct { |
| 119 | 119 | } |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | pub fn format(value: Value, bw: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 123 | comptime assert(fmt.len == 0); | |
| 124 | ||
| 122 | pub fn format(value: Value, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 125 | 123 | // Work around x86_64 backend limitation. |
| 126 | 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 | } |
| 129 | 127 | |
| 130 | 128 | switch (value.td.kind) { |
| 131 | 129 | .integer => { |
| 132 | 130 | if (value.td.isSigned()) { |
| 133 | return bw.print("{d}", .{value.getSignedInteger()}); | |
| 131 | try writer.print("{d}", .{value.getSignedInteger()}); | |
| 134 | 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()}), | |
| 139 | .unknown => return bw.writeAll("(unknown)"), | |
| 136 | .float => try writer.print("{d}", .{value.getFloat()}), | |
| 137 | .unknown => try writer.writeAll("(unknown)"), | |
| 140 | 138 | } |
| 141 | 139 | } |
| 142 | 140 | }; |
| ... | ... | @@ -166,17 +164,12 @@ fn overflowHandler( |
| 166 | 164 | ) callconv(.c) noreturn { |
| 167 | 165 | const lhs: Value = .{ .handle = lhs_handle, .td = data.td }; |
| 168 | 166 | const rhs: Value = .{ .handle = rhs_handle, .td = data.td }; |
| 169 | ||
| 170 | const is_signed = data.td.isSigned(); | |
| 171 | const fmt = "{s} integer overflow: " ++ "{f} " ++ | |
| 172 | operator ++ " {f} cannot be represented in type {s}"; | |
| 173 | ||
| 174 | panic(@returnAddress(), fmt, .{ | |
| 175 | if (is_signed) "signed" else "unsigned", | |
| 176 | lhs, | |
| 177 | rhs, | |
| 178 | data.td.getName(), | |
| 179 | }); | |
| 167 | const signed_str = if (data.td.isSigned()) "signed" else "unsigned"; | |
| 168 | panic( | |
| 169 | @returnAddress(), | |
| 170 | "{s} integer overflow: {f} " ++ operator ++ " {f} cannot be represented in type {s}", | |
| 171 | .{ signed_str, lhs, rhs, data.td.getName() }, | |
| 172 | ); | |
| 180 | 173 | } |
| 181 | 174 | }; |
| 182 | 175 | |
| ... | ... | @@ -195,11 +188,9 @@ fn negationHandler( |
| 195 | 188 | value_handle: ValueHandle, |
| 196 | 189 | ) callconv(.c) noreturn { |
| 197 | 190 | const value: Value = .{ .handle = value_handle, .td = data.td }; |
| 198 | panic( | |
| 199 | @returnAddress(), | |
| 200 | "negation of {f} cannot be represented in type {s}", | |
| 201 | .{ value, data.td.getName() }, | |
| 202 | ); | |
| 191 | panic(@returnAddress(), "negation of {f} cannot be represented in type {s}", .{ | |
| 192 | value, data.td.getName(), | |
| 193 | }); | |
| 203 | 194 | } |
| 204 | 195 | |
| 205 | 196 | fn divRemHandlerAbort( |
| ... | ... | @@ -219,11 +210,9 @@ fn divRemHandler( |
| 219 | 210 | const rhs: Value = .{ .handle = rhs_handle, .td = data.td }; |
| 220 | 211 | |
| 221 | 212 | if (rhs.isMinusOne()) { |
| 222 | panic( | |
| 223 | @returnAddress(), | |
| 224 | "division of {f} by -1 cannot be represented in type {s}", | |
| 225 | .{ lhs, data.td.getName() }, | |
| 226 | ); | |
| 213 | panic(@returnAddress(), "division of {f} by -1 cannot be represented in type {s}", .{ | |
| 214 | lhs, data.td.getName(), | |
| 215 | }); | |
| 227 | 216 | } else panic(@returnAddress(), "division by zero", .{}); |
| 228 | 217 | } |
| 229 | 218 | |
| ... | ... | @@ -353,11 +342,10 @@ fn outOfBounds( |
| 353 | 342 | index_handle: ValueHandle, |
| 354 | 343 | ) callconv(.c) noreturn { |
| 355 | 344 | const index: Value = .{ .handle = index_handle, .td = data.index_type }; |
| 356 | panic( | |
| 357 | @returnAddress(), | |
| 358 | "index {f} out of bounds for type {s}", | |
| 359 | .{ index, data.array_type.getName() }, | |
| 360 | ); | |
| 345 | panic(@returnAddress(), "index {f} out of bounds for type {s}", .{ | |
| 346 | index, | |
| 347 | data.array_type.getName(), | |
| 348 | }); | |
| 361 | 349 | } |
| 362 | 350 | |
| 363 | 351 | const PointerOverflowData = extern struct { |
| ... | ... | @@ -547,11 +535,9 @@ fn loadInvalidValue( |
| 547 | 535 | value_handle: ValueHandle, |
| 548 | 536 | ) callconv(.c) noreturn { |
| 549 | 537 | const value: Value = .{ .handle = value_handle, .td = data.td }; |
| 550 | panic( | |
| 551 | @returnAddress(), | |
| 552 | "load of value {f}, which is not valid for type {s}", | |
| 553 | .{ value, data.td.getName() }, | |
| 554 | ); | |
| 538 | panic(@returnAddress(), "load of value {f}, which is not valid for type {s}", .{ | |
| 539 | value, data.td.getName(), | |
| 540 | }); | |
| 555 | 541 | } |
| 556 | 542 | |
| 557 | 543 | const InvalidBuiltinData = extern struct { |
| ... | ... | @@ -590,11 +576,7 @@ fn vlaBoundNotPositive( |
| 590 | 576 | bound_handle: ValueHandle, |
| 591 | 577 | ) callconv(.c) noreturn { |
| 592 | 578 | const bound: Value = .{ .handle = bound_handle, .td = data.td }; |
| 593 | panic( | |
| 594 | @returnAddress(), | |
| 595 | "variable length array bound evaluates to non-positive value {f}", | |
| 596 | .{bound}, | |
| 597 | ); | |
| 579 | panic(@returnAddress(), "variable length array bound evaluates to non-positive value {f}", .{bound}); | |
| 598 | 580 | } |
| 599 | 581 | |
| 600 | 582 | const FloatCastOverflowData = extern struct { |
src/Air.zig+8-5| ... | ... | @@ -747,7 +747,9 @@ pub const Inst = struct { |
| 747 | 747 | /// Dest slice may have any alignment; source pointer may have any alignment. |
| 748 | 748 | /// The two memory regions must not overlap. |
| 749 | 749 | /// Result type is always void. |
| 750 | /// | |
| 750 | 751 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer. |
| 752 | /// | |
| 751 | 753 | /// If the length is compile-time known (due to the destination or |
| 752 | 754 | /// source being a pointer-to-array), then it is guaranteed to be |
| 753 | 755 | /// greater than zero. |
| ... | ... | @@ -759,7 +761,9 @@ pub const Inst = struct { |
| 759 | 761 | /// Dest slice may have any alignment; source pointer may have any alignment. |
| 760 | 762 | /// The two memory regions may overlap. |
| 761 | 763 | /// Result type is always void. |
| 764 | /// | |
| 762 | 765 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer. |
| 766 | /// | |
| 763 | 767 | /// If the length is compile-time known (due to the destination or |
| 764 | 768 | /// source being a pointer-to-array), then it is guaranteed to be |
| 765 | 769 | /// greater than zero. |
| ... | ... | @@ -958,14 +962,13 @@ pub const Inst = struct { |
| 958 | 962 | return index.unwrap().target; |
| 959 | 963 | } |
| 960 | 964 | |
| 961 | pub fn format(index: Index, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 962 | comptime assert(fmt.len == 0); | |
| 963 | try bw.writeByte('%'); | |
| 965 | pub fn format(index: Index, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 966 | try w.writeByte('%'); | |
| 964 | 967 | switch (index.unwrap()) { |
| 965 | 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 | }; |
| 971 | 974 |
src/Air/Liveness.zig+11-12| ... | ... | @@ -1300,10 +1300,10 @@ fn analyzeOperands( |
| 1300 | 1300 | |
| 1301 | 1301 | // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`. |
| 1302 | 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 | 1304 | break :blk false; |
| 1305 | 1305 | } else blk: { |
| 1306 | log.debug("[{}] %{}: immediate death", .{ pass, @intFromEnum(inst) }); | |
| 1306 | log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) }); | |
| 1307 | 1307 | break :blk true; |
| 1308 | 1308 | }; |
| 1309 | 1309 | |
| ... | ... | @@ -1324,7 +1324,7 @@ fn analyzeOperands( |
| 1324 | 1324 | const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i)); |
| 1325 | 1325 | |
| 1326 | 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 | 1328 | tomb_bits |= mask; |
| 1329 | 1329 | } |
| 1330 | 1330 | } |
| ... | ... | @@ -2037,15 +2037,15 @@ fn fmtInstSet(set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void)) FmtIns |
| 2037 | 2037 | const FmtInstSet = struct { |
| 2038 | 2038 | set: *const std.AutoHashMapUnmanaged(Air.Inst.Index, void), |
| 2039 | 2039 | |
| 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 | 2041 | if (val.set.count() == 0) { |
| 2042 | try bw.writeAll("[no instructions]"); | |
| 2042 | try w.writeAll("[no instructions]"); | |
| 2043 | 2043 | return; |
| 2044 | 2044 | } |
| 2045 | 2045 | var it = val.set.keyIterator(); |
| 2046 | try bw.print("%{f}", .{it.next().?.*}); | |
| 2046 | try w.print("%{f}", .{it.next().?.*}); | |
| 2047 | 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 | 2057 | const FmtInstList = struct { |
| 2058 | 2058 | list: []const Air.Inst.Index, |
| 2059 | 2059 | |
| 2060 | pub fn format(val: FmtInstList, bw: *Writer, comptime fmt: []const u8) !void { | |
| 2061 | comptime assert(fmt.len == 0); | |
| 2060 | pub fn format(val: FmtInstList, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 2062 | 2061 | if (val.list.len == 0) { |
| 2063 | try bw.writeAll("[no instructions]"); | |
| 2062 | try w.writeAll("[no instructions]"); | |
| 2064 | 2063 | return; |
| 2065 | 2064 | } |
| 2066 | try bw.print("%{f}", .{val.list[0]}); | |
| 2065 | try w.print("%{f}", .{val.list[0]}); | |
| 2067 | 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 | 511 | |
| 512 | 512 | // The same stuff should be alive after the loop as before it. |
| 513 | 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 | 515 | defer { |
| 516 | 516 | var live = self.loops.fetchRemove(inst).?; |
| 517 | 517 | live.value.deinit(self.gpa); |
| ... | ... | @@ -560,7 +560,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 560 | 560 | // after the loop as before it. |
| 561 | 561 | { |
| 562 | 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 | 564 | gop.value_ptr.* = self.live.move(); |
| 565 | 565 | } |
| 566 | 566 | defer { |
| ... | ... | @@ -601,7 +601,9 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies |
| 601 | 601 | return; |
| 602 | 602 | }; |
| 603 | 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 | 607 | } else { |
| 606 | 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 | 518 | if (mask_idx > 0) try s.writeAll(", "); |
| 519 | 519 | switch (mask_elem.unwrap()) { |
| 520 | 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 | 524 | try s.writeByte(']'); |
| 525 | 525 | } |
| 526 | 526 | |
| 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 | 528 | const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst); |
| 529 | 529 | try w.writeType(s, unwrapped.result_ty); |
| 530 | 530 | try s.writeAll(", "); |
| ... | ... | @@ -590,7 +590,7 @@ const Writer = struct { |
| 590 | 590 | const ip = &w.pt.zcu.intern_pool; |
| 591 | 591 | const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; |
| 592 | 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 | } |
| 595 | 595 | |
| 596 | 596 | fn writeAtomicLoad(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { |
| ... | ... | @@ -710,7 +710,7 @@ const Writer = struct { |
| 710 | 710 | } |
| 711 | 711 | } |
| 712 | 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 | } |
| 715 | 715 | |
| 716 | 716 | fn writeDbgStmt(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { |
| ... | ... | @@ -722,7 +722,7 @@ const Writer = struct { |
| 722 | 722 | const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 723 | 723 | try w.writeOperand(s, inst, 0, pl_op.operand); |
| 724 | 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 | } |
| 727 | 727 | |
| 728 | 728 | fn writeCall(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { |
| ... | ... | @@ -1010,7 +1010,7 @@ const Writer = struct { |
| 1010 | 1010 | |
| 1011 | 1011 | fn writeInstRef( |
| 1012 | 1012 | w: *Writer, |
| 1013 | s: anytype, | |
| 1013 | s: *std.io.Writer, | |
| 1014 | 1014 | operand: Air.Inst.Ref, |
| 1015 | 1015 | dies: bool, |
| 1016 | 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 | 57 | \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. |
| 58 | 58 | \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; |
| 59 | 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_}; | |
| 63 | \\pub const link_mode: std.builtin.LinkMode = .{fp_}; | |
| 64 | \\pub const unwind_tables: std.builtin.UnwindTables = .{fp_}; | |
| 62 | \\pub const output_mode: std.builtin.OutputMode = .{f}; | |
| 63 | \\pub const link_mode: std.builtin.LinkMode = .{f}; | |
| 64 | \\pub const unwind_tables: std.builtin.UnwindTables = .{f}; | |
| 65 | 65 | \\pub const is_test = {}; |
| 66 | 66 | \\pub const single_threaded = {}; |
| 67 | \\pub const abi: std.Target.Abi = .{fp_}; | |
| 67 | \\pub const abi: std.Target.Abi = .{f}; | |
| 68 | 68 | \\pub const cpu: std.Target.Cpu = .{{ |
| 69 | \\ .arch = .{fp_}, | |
| 70 | \\ .model = &std.Target.{fp_}.cpu.{fp_}, | |
| 71 | \\ .features = std.Target.{fp_}.featureSet(&.{{ | |
| 69 | \\ .arch = .{f}, | |
| 70 | \\ .model = &std.Target.{f}.cpu.{f}, | |
| 71 | \\ .features = std.Target.{f}.featureSet(&.{{ | |
| 72 | 72 | \\ |
| 73 | 73 | , .{ |
| 74 | 74 | build_options.version, |
| 75 | std.zig.fmtId(@tagName(zig_backend)), | |
| 76 | std.zig.fmtId(@tagName(opts.output_mode)), | |
| 77 | std.zig.fmtId(@tagName(opts.link_mode)), | |
| 78 | std.zig.fmtId(@tagName(opts.unwind_tables)), | |
| 75 | std.zig.fmtIdPU(@tagName(zig_backend)), | |
| 76 | std.zig.fmtIdPU(@tagName(opts.output_mode)), | |
| 77 | std.zig.fmtIdPU(@tagName(opts.link_mode)), | |
| 78 | std.zig.fmtIdPU(@tagName(opts.unwind_tables)), | |
| 79 | 79 | opts.is_test, |
| 80 | 80 | opts.single_threaded, |
| 81 | std.zig.fmtId(@tagName(target.abi)), | |
| 82 | std.zig.fmtId(@tagName(target.cpu.arch)), | |
| 83 | std.zig.fmtId(arch_family_name), | |
| 84 | std.zig.fmtId(target.cpu.model.name), | |
| 85 | std.zig.fmtId(arch_family_name), | |
| 81 | std.zig.fmtIdPU(@tagName(target.abi)), | |
| 82 | std.zig.fmtIdPU(@tagName(target.cpu.arch)), | |
| 83 | std.zig.fmtIdPU(arch_family_name), | |
| 84 | std.zig.fmtIdPU(target.cpu.model.name), | |
| 85 | std.zig.fmtIdPU(arch_family_name), | |
| 86 | 86 | }); |
| 87 | 87 | |
| 88 | 88 | for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| { |
| 89 | 89 | const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); |
| 90 | 90 | const is_enabled = target.cpu.features.isEnabled(index); |
| 91 | 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 | 95 | try buffer.print( |
| 96 | 96 | \\ }}), |
| 97 | 97 | \\}}; |
| 98 | 98 | \\pub const os: std.Target.Os = .{{ |
| 99 | \\ .tag = .{fp_}, | |
| 99 | \\ .tag = .{f}, | |
| 100 | 100 | \\ .version_range = .{{ |
| 101 | 101 | , |
| 102 | .{std.zig.fmtId(@tagName(target.os.tag))}, | |
| 102 | .{std.zig.fmtIdPU(@tagName(target.os.tag))}, | |
| 103 | 103 | ); |
| 104 | 104 | |
| 105 | 105 | switch (target.os.versionRange()) { |
| ... | ... | @@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { |
| 200 | 200 | }), |
| 201 | 201 | .windows => |windows| try buffer.print( |
| 202 | 202 | \\ .windows = .{{ |
| 203 | \\ .min = {fc}, | |
| 204 | \\ .max = {fc}, | |
| 203 | \\ .min = {f}, | |
| 204 | \\ .max = {f}, | |
| 205 | 205 | \\ }}}}, |
| 206 | 206 | \\ |
| 207 | 207 | , .{ windows.min, windows.max }), |
| ... | ... | @@ -238,8 +238,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { |
| 238 | 238 | const link_libc = opts.link_libc; |
| 239 | 239 | |
| 240 | 240 | try buffer.print( |
| 241 | \\pub const object_format: std.Target.ObjectFormat = .{fp_}; | |
| 242 | \\pub const mode: std.builtin.OptimizeMode = .{fp_}; | |
| 241 | \\pub const object_format: std.Target.ObjectFormat = .{f}; | |
| 242 | \\pub const mode: std.builtin.OptimizeMode = .{f}; | |
| 243 | 243 | \\pub const link_libc = {}; |
| 244 | 244 | \\pub const link_libcpp = {}; |
| 245 | 245 | \\pub const have_error_return_tracing = {}; |
| ... | ... | @@ -249,12 +249,12 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { |
| 249 | 249 | \\pub const position_independent_code = {}; |
| 250 | 250 | \\pub const position_independent_executable = {}; |
| 251 | 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 | 253 | \\pub const omit_frame_pointer = {}; |
| 254 | 254 | \\ |
| 255 | 255 | , .{ |
| 256 | std.zig.fmtId(@tagName(target.ofmt)), | |
| 257 | std.zig.fmtId(@tagName(opts.optimize_mode)), | |
| 256 | std.zig.fmtIdPU(@tagName(target.ofmt)), | |
| 257 | std.zig.fmtIdPU(@tagName(opts.optimize_mode)), | |
| 258 | 258 | link_libc, |
| 259 | 259 | opts.link_libcpp, |
| 260 | 260 | opts.error_tracing, |
| ... | ... | @@ -264,15 +264,15 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { |
| 264 | 264 | opts.pic, |
| 265 | 265 | opts.pie, |
| 266 | 266 | opts.strip, |
| 267 | std.zig.fmtId(@tagName(opts.code_model)), | |
| 267 | std.zig.fmtIdPU(@tagName(opts.code_model)), | |
| 268 | 268 | opts.omit_frame_pointer, |
| 269 | 269 | }); |
| 270 | 270 | |
| 271 | 271 | if (target.os.tag == .wasi) { |
| 272 | 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 | } |
| 277 | 277 | |
| 278 | 278 | if (opts.is_test) { |
| ... | ... | @@ -317,7 +317,7 @@ pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void { |
| 317 | 317 | if (root_dir.statFile(sub_path)) |stat| { |
| 318 | 318 | if (stat.size != file.source.?.len) { |
| 319 | 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 | 321 | "Overwriting with correct file contents now", |
| 322 | 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 | 237 | glibc_so_files: ?glibc.BuiltSharedObjects = null, |
| 238 | 238 | freebsd_so_files: ?freebsd.BuiltSharedObjects = null, |
| 239 | 239 | netbsd_so_files: ?netbsd.BuiltSharedObjects = null, |
| 240 | wasi_emulated_libs: []const wasi_libc.CrtFile, | |
| 241 | 240 | |
| 242 | 241 | /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, |
| 243 | 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 | 402 | const Formatter = struct { |
| 404 | 403 | p: Path, |
| 405 | 404 | comp: *Compilation, |
| 406 | pub fn format(f: Formatter, comptime unused_fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void { | |
| 407 | comptime assert(unused_fmt.len == 0); | |
| 408 | _ = options; | |
| 405 | pub fn format(f: Formatter, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 409 | 406 | const root_path: []const u8 = switch (f.p.root) { |
| 410 | 407 | .zig_lib => f.comp.dirs.zig_lib.path orelse ".", |
| 411 | 408 | .global_cache => f.comp.dirs.global_cache.path orelse ".", |
| ... | ... | @@ -734,10 +731,10 @@ pub const Directories = struct { |
| 734 | 731 | }; |
| 735 | 732 | |
| 736 | 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 | 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 | } |
| 742 | 739 | |
| 743 | 740 | return .{ |
| ... | ... | @@ -1570,12 +1567,6 @@ pub const CreateOptions = struct { |
| 1570 | 1567 | framework_dirs: []const []const u8 = &[0][]const u8{}, |
| 1571 | 1568 | frameworks: []const Framework = &.{}, |
| 1572 | 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 | 1570 | /// This means that if the output mode is an executable it will be a |
| 1580 | 1571 | /// Position Independent Executable. If the output mode is not an |
| 1581 | 1572 | /// executable this field is ignored. |
| ... | ... | @@ -2055,7 +2046,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2055 | 2046 | .function_sections = options.function_sections, |
| 2056 | 2047 | .data_sections = options.data_sections, |
| 2057 | 2048 | .native_system_include_paths = options.native_system_include_paths, |
| 2058 | .wasi_emulated_libs = options.wasi_emulated_libs, | |
| 2059 | 2049 | .force_undefined_symbols = options.force_undefined_symbols, |
| 2060 | 2050 | .link_eh_frame_hdr = link_eh_frame_hdr, |
| 2061 | 2051 | .global_cc_argv = options.global_cc_argv, |
| ... | ... | @@ -2070,12 +2060,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2070 | 2060 | .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), |
| 2071 | 2061 | }; |
| 2072 | 2062 | |
| 2073 | errdefer { | |
| 2074 | for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); | |
| 2075 | comp.windows_libs.deinit(gpa); | |
| 2076 | } | |
| 2077 | try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len); | |
| 2078 | for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {}); | |
| 2063 | comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{}); | |
| 2064 | errdefer comp.windows_libs.deinit(gpa); | |
| 2079 | 2065 | |
| 2080 | 2066 | // Prevent some footguns by making the "any" fields of config reflect |
| 2081 | 2067 | // the default Module settings. |
| ... | ... | @@ -2306,6 +2292,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2306 | 2292 | |
| 2307 | 2293 | if (comp.emit_bin != null and target.ofmt != .c) { |
| 2308 | 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 | 2302 | // If we need to build libc for the target, add work items for it. |
| 2310 | 2303 | // We go through the work queue so that building can be done in parallel. |
| 2311 | 2304 | // 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 | 2374 | } else if (target.isWasiLibC()) { |
| 2382 | 2375 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 2383 | 2376 | |
| 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 | 2377 | comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true; |
| 2390 | 2378 | comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true; |
| 2391 | 2379 | comp.link_task_queue.pending_prelink_tasks += 2; |
| ... | ... | @@ -2399,7 +2387,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2399 | 2387 | |
| 2400 | 2388 | // When linking mingw-w64 there are some import libs we always need. |
| 2401 | 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 | 2391 | } else { |
| 2404 | 2392 | return error.LibCUnavailable; |
| 2405 | 2393 | } |
| ... | ... | @@ -2497,7 +2485,6 @@ pub fn destroy(comp: *Compilation) void { |
| 2497 | 2485 | comp.c_object_work_queue.deinit(); |
| 2498 | 2486 | comp.win32_resource_work_queue.deinit(); |
| 2499 | 2487 | |
| 2500 | for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); | |
| 2501 | 2488 | comp.windows_libs.deinit(gpa); |
| 2502 | 2489 | |
| 2503 | 2490 | { |
| ... | ... | @@ -2994,7 +2981,7 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat |
| 2994 | 2981 | break @intCast(i); |
| 2995 | 2982 | } |
| 2996 | 2983 | } else std.debug.panic( |
| 2997 | "missing prefix directory '{s}' ('{}') for '{s}'", | |
| 2984 | "missing prefix directory '{s}' ('{f}') for '{s}'", | |
| 2998 | 2985 | .{ @tagName(path.root), want_prefix_dir, path.sub_path }, |
| 2999 | 2986 | ); |
| 3000 | 2987 | |
| ... | ... | @@ -3333,7 +3320,7 @@ fn emitFromCObject( |
| 3333 | 3320 | emit_path.root_dir.handle, |
| 3334 | 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 | 3324 | src_path, |
| 3338 | 3325 | emit_path, |
| 3339 | 3326 | @errorName(err), |
| ... | ... | @@ -3681,7 +3668,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3681 | 3668 | .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"), |
| 3682 | 3669 | }, |
| 3683 | 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 | 3672 | .span_start = start, |
| 3686 | 3673 | .span_main = start, |
| 3687 | 3674 | .span_end = @intCast(end), |
| ... | ... | @@ -3728,7 +3715,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3728 | 3715 | assert(!is_retryable); |
| 3729 | 3716 | // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors. |
| 3730 | 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 | 3719 | defer gpa.free(path); |
| 3733 | 3720 | if (file.zir != null) { |
| 3734 | 3721 | try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path); |
| ... | ... | @@ -3784,8 +3771,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3784 | 3771 | } |
| 3785 | 3772 | |
| 3786 | 3773 | std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{ |
| 3787 | error_msg.msg, | |
| 3788 | zcu.fmtAnalUnit(anal_unit), | |
| 3774 | error_msg.msg, zcu.fmtAnalUnit(anal_unit), | |
| 3789 | 3775 | }); |
| 3790 | 3776 | |
| 3791 | 3777 | try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error); |
| ... | ... | @@ -4047,7 +4033,7 @@ pub fn addModuleErrorMsg( |
| 4047 | 4033 | const err_src_loc = module_err_msg.src_loc.upgrade(zcu); |
| 4048 | 4034 | const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| { |
| 4049 | 4035 | try eb.addRootErrorMessage(.{ |
| 4050 | .msg = try eb.printString("unable to load '{}': {s}", .{ | |
| 4036 | .msg = try eb.printString("unable to load '{f}': {s}", .{ | |
| 4051 | 4037 | err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err), |
| 4052 | 4038 | }), |
| 4053 | 4039 | }); |
| ... | ... | @@ -4110,7 +4096,7 @@ pub fn addModuleErrorMsg( |
| 4110 | 4096 | } |
| 4111 | 4097 | |
| 4112 | 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 | 4100 | .span_start = err_span.start, |
| 4115 | 4101 | .span_main = err_span.main, |
| 4116 | 4102 | .span_end = err_span.end, |
| ... | ... | @@ -4142,7 +4128,7 @@ pub fn addModuleErrorMsg( |
| 4142 | 4128 | const gop = try notes.getOrPutContext(gpa, .{ |
| 4143 | 4129 | .msg = try eb.addString(module_note.msg), |
| 4144 | 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 | 4132 | .span_start = span.start, |
| 4147 | 4133 | .span_main = span.main, |
| 4148 | 4134 | .span_end = span.end, |
| ... | ... | @@ -4187,7 +4173,7 @@ fn addReferenceTraceFrame( |
| 4187 | 4173 | try ref_traces.append(gpa, .{ |
| 4188 | 4174 | .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), |
| 4189 | 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 | 4177 | .span_start = span.start, |
| 4192 | 4178 | .span_main = span.main, |
| 4193 | 4179 | .span_end = span.end, |
| ... | ... | @@ -4906,7 +4892,7 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, |
| 4906 | 4892 | var walker = try mod_dir.walk(comp.gpa); |
| 4907 | 4893 | defer walker.deinit(); |
| 4908 | 4894 | |
| 4909 | var archiver = std.tar.writer(tar_file.writer().any()); | |
| 4895 | var archiver = std.tar.writer(tar_file.deprecatedWriter().any()); | |
| 4910 | 4896 | archiver.prefix = name; |
| 4911 | 4897 | |
| 4912 | 4898 | while (try walker.next()) |entry| { |
| ... | ... | @@ -4919,13 +4905,13 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, |
| 4919 | 4905 | else => continue, |
| 4920 | 4906 | } |
| 4921 | 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 | 4909 | root.fmt(comp), entry.path, @errorName(err), |
| 4924 | 4910 | }); |
| 4925 | 4911 | }; |
| 4926 | 4912 | defer file.close(); |
| 4927 | 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 | 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 | 5041 | var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { |
| 5056 | 5042 | return comp.lockAndSetMiscFailure( |
| 5057 | 5043 | .docs_copy, |
| 5058 | "unable to create output directory '{}': {s}", | |
| 5044 | "unable to create output directory '{f}': {s}", | |
| 5059 | 5045 | .{ docs_path, @errorName(err) }, |
| 5060 | 5046 | ); |
| 5061 | 5047 | }; |
| ... | ... | @@ -5067,10 +5053,8 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 5067 | 5053 | "main.wasm", |
| 5068 | 5054 | .{}, |
| 5069 | 5055 | ) catch |err| { |
| 5070 | return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{ | |
| 5071 | crt_file.full_object_path, | |
| 5072 | docs_path, | |
| 5073 | @errorName(err), | |
| 5056 | return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {s}", .{ | |
| 5057 | crt_file.full_object_path, docs_path, @errorName(err), | |
| 5074 | 5058 | }); |
| 5075 | 5059 | }; |
| 5076 | 5060 | } |
| ... | ... | @@ -6024,16 +6008,15 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 |
| 6024 | 6008 | |
| 6025 | 6009 | // In .rc files, a " within a quoted string is escaped as "" |
| 6026 | 6010 | const fmtRcEscape = struct { |
| 6027 | fn formatRcEscape(bytes: []const u8, bw: *Writer, comptime fmt: []const u8) !void { | |
| 6028 | comptime assert(fmt.len == 0); | |
| 6011 | fn formatRcEscape(bytes: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 6029 | 6012 | for (bytes) |byte| switch (byte) { |
| 6030 | '"' => try bw.writeAll("\"\""), | |
| 6031 | '\\' => try bw.writeAll("\\\\"), | |
| 6032 | else => try bw.writeByte(byte), | |
| 6013 | '"' => try writer.writeAll("\"\""), | |
| 6014 | '\\' => try writer.writeAll("\\\\"), | |
| 6015 | else => try writer.writeByte(byte), | |
| 6033 | 6016 | }; |
| 6034 | 6017 | } |
| 6035 | 6018 | |
| 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 | 6020 | return .{ .data = bytes }; |
| 6038 | 6021 | } |
| 6039 | 6022 | }.fmtRcEscape; |
| ... | ... | @@ -6047,7 +6030,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 |
| 6047 | 6030 | // 24 is RT_MANIFEST |
| 6048 | 6031 | const resource_type = 24; |
| 6049 | 6032 | |
| 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 | }); | |
| 6051 | 6036 | |
| 6052 | 6037 | try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input }); |
| 6053 | 6038 | |
| ... | ... | @@ -6259,7 +6244,7 @@ fn spawnZigRc( |
| 6259 | 6244 | } |
| 6260 | 6245 | |
| 6261 | 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 | 6248 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); |
| 6264 | 6249 | |
| 6265 | 6250 | const term = child.wait() catch |err| { |
| ... | ... | @@ -6474,7 +6459,7 @@ pub fn addCCArgs( |
| 6474 | 6459 | try argv.append("-fno-asynchronous-unwind-tables"); |
| 6475 | 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 | } |
| 6479 | 6464 | |
| 6480 | 6465 | try argv.append("-nostdinc"); |
| ... | ... | @@ -7597,27 +7582,6 @@ fn getCrtPathsInner( |
| 7597 | 7582 | }; |
| 7598 | 7583 | } |
| 7599 | 7584 | |
| 7600 | pub 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 | 7585 | /// This decides the optimization mode for all zig-provided libraries, including |
| 7622 | 7586 | /// compiler-rt, libcxx, libc, libunwind, etc. |
| 7623 | 7587 | pub 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 | 142 | const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index"); |
| 143 | 143 | const nav = ip.getNav(nav_index); |
| 144 | 144 | try w.print( |
| 145 | \\name: '{}' | |
| 146 | \\fqn: '{}' | |
| 145 | \\name: '{f}' | |
| 146 | \\fqn: '{f}' | |
| 147 | 147 | \\status: {s} |
| 148 | 148 | \\created on generation: {d} |
| 149 | 149 | \\ |
| ... | ... | @@ -234,7 +234,7 @@ fn handleCommand(zcu: *Zcu, output: *std.ArrayListUnmanaged(u8), cmd_str: []cons |
| 234 | 234 | for (unit_info.deps.items, 0..) |dependee, i| { |
| 235 | 235 | try w.print("[{d}] ", .{i}); |
| 236 | 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 | 238 | .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }), |
| 239 | 239 | .interned => |ip_index| switch (ip.indexToKey(ip_index)) { |
| 240 | 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 | 260 | const ip_index: InternPool.Index = @enumFromInt(parseIndex(arg_str) orelse return w.writeAll("malformed ip index")); |
| 261 | 261 | const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type"); |
| 262 | 262 | try w.print( |
| 263 | \\name: '{}' | |
| 263 | \\name: '{f}' | |
| 264 | 264 | \\created on generation: {d} |
| 265 | 265 | \\ |
| 266 | 266 | , .{ |
| ... | ... | @@ -365,7 +365,7 @@ fn printType(ty: Type, zcu: *const Zcu, w: anytype) !void { |
| 365 | 365 | .union_type, |
| 366 | 366 | .enum_type, |
| 367 | 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()) }), | |
| 369 | 369 | |
| 370 | 370 | else => unreachable, |
| 371 | 371 | } |
src/InternPool.zig+1-13| ... | ... | @@ -518,8 +518,6 @@ pub const Nav = struct { |
| 518 | 518 | namespace: NamespaceIndex, |
| 519 | 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 | 521 | status: union(enum) { |
| 524 | 522 | /// This `Nav` is pending semantic analysis. |
| 525 | 523 | unresolved, |
| ... | ... | @@ -735,7 +733,7 @@ pub const Nav = struct { |
| 735 | 733 | @"addrspace": std.builtin.AddressSpace, |
| 736 | 734 | /// Populated only if `bits.status == .type_resolved`. |
| 737 | 735 | is_threadlocal: bool, |
| 738 | is_usingnamespace: bool, | |
| 736 | _: u1 = 0, | |
| 739 | 737 | }; |
| 740 | 738 | |
| 741 | 739 | fn unpack(repr: Repr) Nav { |
| ... | ... | @@ -749,7 +747,6 @@ pub const Nav = struct { |
| 749 | 747 | assert(repr.analysis_zir_index == .none); |
| 750 | 748 | break :a null; |
| 751 | 749 | }, |
| 752 | .is_usingnamespace = repr.bits.is_usingnamespace, | |
| 753 | 750 | .status = switch (repr.bits.status) { |
| 754 | 751 | .unresolved => .unresolved, |
| 755 | 752 | .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{ |
| ... | ... | @@ -797,7 +794,6 @@ pub const Nav = struct { |
| 797 | 794 | .is_const = false, |
| 798 | 795 | .alignment = .none, |
| 799 | 796 | .@"addrspace" = .generic, |
| 800 | .is_usingnamespace = nav.is_usingnamespace, | |
| 801 | 797 | .is_threadlocal = false, |
| 802 | 798 | }, |
| 803 | 799 | .type_resolved => |r| .{ |
| ... | ... | @@ -805,7 +801,6 @@ pub const Nav = struct { |
| 805 | 801 | .is_const = r.is_const, |
| 806 | 802 | .alignment = r.alignment, |
| 807 | 803 | .@"addrspace" = r.@"addrspace", |
| 808 | .is_usingnamespace = nav.is_usingnamespace, | |
| 809 | 804 | .is_threadlocal = r.is_threadlocal, |
| 810 | 805 | }, |
| 811 | 806 | .fully_resolved => |r| .{ |
| ... | ... | @@ -813,7 +808,6 @@ pub const Nav = struct { |
| 813 | 808 | .is_const = r.is_const, |
| 814 | 809 | .alignment = r.alignment, |
| 815 | 810 | .@"addrspace" = r.@"addrspace", |
| 816 | .is_usingnamespace = nav.is_usingnamespace, | |
| 817 | 811 | .is_threadlocal = false, |
| 818 | 812 | }, |
| 819 | 813 | }, |
| ... | ... | @@ -6865,8 +6859,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { |
| 6865 | 6859 | { |
| 6866 | 6860 | namespace.pub_decls.deinit(gpa); |
| 6867 | 6861 | namespace.priv_decls.deinit(gpa); |
| 6868 | namespace.pub_usingnamespace.deinit(gpa); | |
| 6869 | namespace.priv_usingnamespace.deinit(gpa); | |
| 6870 | 6862 | namespace.comptime_decls.deinit(gpa); |
| 6871 | 6863 | namespace.test_decls.deinit(gpa); |
| 6872 | 6864 | } |
| ... | ... | @@ -11502,7 +11494,6 @@ pub fn createNav( |
| 11502 | 11494 | .@"linksection" = opts.@"linksection", |
| 11503 | 11495 | .@"addrspace" = opts.@"addrspace", |
| 11504 | 11496 | } }, |
| 11505 | .is_usingnamespace = false, | |
| 11506 | 11497 | })); |
| 11507 | 11498 | return index_unwrapped.wrap(ip); |
| 11508 | 11499 | } |
| ... | ... | @@ -11517,8 +11508,6 @@ pub fn createDeclNav( |
| 11517 | 11508 | fqn: NullTerminatedString, |
| 11518 | 11509 | zir_index: TrackedInst.Index, |
| 11519 | 11510 | namespace: NamespaceIndex, |
| 11520 | /// TODO: this is hacky! See `Nav.is_usingnamespace`. | |
| 11521 | is_usingnamespace: bool, | |
| 11522 | 11511 | ) Allocator.Error!Nav.Index { |
| 11523 | 11512 | const navs = ip.getLocal(tid).getMutableNavs(gpa); |
| 11524 | 11513 | |
| ... | ... | @@ -11537,7 +11526,6 @@ pub fn createDeclNav( |
| 11537 | 11526 | .zir_index = zir_index, |
| 11538 | 11527 | }, |
| 11539 | 11528 | .status = .unresolved, |
| 11540 | .is_usingnamespace = is_usingnamespace, | |
| 11541 | 11529 | })); |
| 11542 | 11530 | |
| 11543 | 11531 | return nav; |
src/Package/Fetch.zig+47-44| ... | ... | @@ -27,6 +27,22 @@ |
| 27 | 27 | //! All of this must be done with only referring to the state inside this struct |
| 28 | 28 | //! because this work will be done in a dedicated thread. |
| 29 | 29 | |
| 30 | const builtin = @import("builtin"); | |
| 31 | const std = @import("std"); | |
| 32 | const fs = std.fs; | |
| 33 | const assert = std.debug.assert; | |
| 34 | const ascii = std.ascii; | |
| 35 | const Allocator = std.mem.Allocator; | |
| 36 | const Cache = std.Build.Cache; | |
| 37 | const ThreadPool = std.Thread.Pool; | |
| 38 | const WaitGroup = std.Thread.WaitGroup; | |
| 39 | const Fetch = @This(); | |
| 40 | const git = @import("Fetch/git.zig"); | |
| 41 | const Package = @import("../Package.zig"); | |
| 42 | const Manifest = Package.Manifest; | |
| 43 | const ErrorBundle = std.zig.ErrorBundle; | |
| 44 | const native_os = builtin.os.tag; | |
| 45 | ||
| 30 | 46 | arena: std.heap.ArenaAllocator, |
| 31 | 47 | location: Location, |
| 32 | 48 | location_tok: std.zig.Ast.TokenIndex, |
| ... | ... | @@ -184,7 +200,7 @@ pub const JobQueue = struct { |
| 184 | 200 | |
| 185 | 201 | const hash_slice = hash.toSlice(); |
| 186 | 202 | |
| 187 | try buf.print( | |
| 203 | try buf.writer().print( | |
| 188 | 204 | \\ pub const {f} = struct {{ |
| 189 | 205 | \\ |
| 190 | 206 | , .{std.zig.fmtId(hash_slice)}); |
| ... | ... | @@ -211,15 +227,15 @@ pub const JobQueue = struct { |
| 211 | 227 | } |
| 212 | 228 | |
| 213 | 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)}); | |
| 217 | 233 | |
| 218 | 234 | if (fetch.has_build_zig) { |
| 219 | 235 | try buf.print( |
| 220 | 236 | \\ pub const build_zig = @import("{f}"); |
| 221 | 237 | \\ |
| 222 | , .{std.zig.fmtEscapes(hash_slice)}); | |
| 238 | , .{std.zig.fmtString(hash_slice)}); | |
| 223 | 239 | } |
| 224 | 240 | |
| 225 | 241 | if (fetch.manifest) |*manifest| { |
| ... | ... | @@ -231,7 +247,7 @@ pub const JobQueue = struct { |
| 231 | 247 | const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue; |
| 232 | 248 | try buf.print( |
| 233 | 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 | } |
| 237 | 253 | |
| ... | ... | @@ -263,7 +279,7 @@ pub const JobQueue = struct { |
| 263 | 279 | const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue; |
| 264 | 280 | try buf.print( |
| 265 | 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 | 285 | try buf.appendSlice("};\n"); |
| ... | ... | @@ -420,14 +436,14 @@ pub fn run(f: *Fetch) RunError!void { |
| 420 | 436 | } |
| 421 | 437 | if (f.job_queue.read_only) return f.fail( |
| 422 | 438 | f.name_tok, |
| 423 | try eb.printString("package not found at '{}{s}'", .{ | |
| 439 | try eb.printString("package not found at '{f}{s}'", .{ | |
| 424 | 440 | cache_root, pkg_sub_path, |
| 425 | 441 | }), |
| 426 | 442 | ); |
| 427 | 443 | }, |
| 428 | 444 | else => |e| { |
| 429 | 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 | 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 | 977 | if (ascii.eqlIgnoreCase(uri.scheme, "file")) { |
| 962 | 978 | const path = try uri.path.toRawMaybeAlloc(arena); |
| 963 | 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 | 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 | 1079 | }); |
| 1064 | 1080 | const notes_start = try eb.reserveNotes(notes_len); |
| 1065 | 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 | 1087 | return error.FetchFailed; |
| 1069 | 1088 | } |
| 1070 | 1089 | |
| 1071 | 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 | 1092 | var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| { |
| 1074 | 1093 | return f.fail(f.location_tok, try eb.printString( |
| 1075 | 1094 | "unable to create fetch stream: {s}", |
| ... | ... | @@ -1305,7 +1324,7 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { |
| 1305 | 1324 | .{@errorName(err)}, |
| 1306 | 1325 | )); |
| 1307 | 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 | 1328 | "write temporary zip file failed: {s}", |
| 1310 | 1329 | .{@errorName(err)}, |
| 1311 | 1330 | )); |
| ... | ... | @@ -1813,28 +1832,6 @@ pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifes |
| 1813 | 1832 | } |
| 1814 | 1833 | } |
| 1815 | 1834 | |
| 1816 | const builtin = @import("builtin"); | |
| 1817 | const std = @import("std"); | |
| 1818 | const fs = std.fs; | |
| 1819 | const assert = std.debug.assert; | |
| 1820 | const ascii = std.ascii; | |
| 1821 | const Allocator = std.mem.Allocator; | |
| 1822 | const Cache = std.Build.Cache; | |
| 1823 | const ThreadPool = std.Thread.Pool; | |
| 1824 | const WaitGroup = std.Thread.WaitGroup; | |
| 1825 | const Fetch = @This(); | |
| 1826 | const git = @import("Fetch/git.zig"); | |
| 1827 | const Package = @import("../Package.zig"); | |
| 1828 | const Manifest = Package.Manifest; | |
| 1829 | const ErrorBundle = std.zig.ErrorBundle; | |
| 1830 | const native_os = builtin.os.tag; | |
| 1831 | ||
| 1832 | test { | |
| 1833 | _ = Filter; | |
| 1834 | _ = FileType; | |
| 1835 | _ = UnpackResult; | |
| 1836 | } | |
| 1837 | ||
| 1838 | 1835 | // Detects executable header: ELF or Macho-O magic header or shebang line. |
| 1839 | 1836 | const FileHeader = struct { |
| 1840 | 1837 | header: [4]u8 = undefined, |
| ... | ... | @@ -2052,15 +2049,15 @@ const UnpackResult = struct { |
| 2052 | 2049 | // output errors to string |
| 2053 | 2050 | var errors = try fetch.error_bundle.toOwnedBundle(""); |
| 2054 | 2051 | defer errors.deinit(gpa); |
| 2055 | var out = std.ArrayList(u8).init(gpa); | |
| 2056 | defer out.deinit(); | |
| 2057 | try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer()); | |
| 2052 | var aw: std.io.Writer.Allocating = .init(gpa); | |
| 2053 | defer aw.deinit(); | |
| 2054 | try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer); | |
| 2058 | 2055 | try std.testing.expectEqualStrings( |
| 2059 | 2056 | \\error: unable to unpack |
| 2060 | 2057 | \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError |
| 2061 | 2058 | \\ note: file 'dir2/file4' has unsupported type 'x' |
| 2062 | 2059 | \\ |
| 2063 | , out.items); | |
| 2060 | , aw.getWritten()); | |
| 2064 | 2061 | } |
| 2065 | 2062 | }; |
| 2066 | 2063 | |
| ... | ... | @@ -2076,7 +2073,7 @@ test "zip" { |
| 2076 | 2073 | { |
| 2077 | 2074 | var zip_file = try tmp.dir.createFile("test.zip", .{}); |
| 2078 | 2075 | defer zip_file.close(); |
| 2079 | var bw = std.io.bufferedWriter(zip_file.writer()); | |
| 2076 | var bw = std.io.bufferedWriter(zip_file.deprecatedWriter()); | |
| 2080 | 2077 | var store: [test_files.len]std.zip.testutil.FileStore = undefined; |
| 2081 | 2078 | try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{}); |
| 2082 | 2079 | try bw.flush(); |
| ... | ... | @@ -2109,7 +2106,7 @@ test "zip with one root folder" { |
| 2109 | 2106 | { |
| 2110 | 2107 | var zip_file = try tmp.dir.createFile("test.zip", .{}); |
| 2111 | 2108 | defer zip_file.close(); |
| 2112 | var bw = std.io.bufferedWriter(zip_file.writer()); | |
| 2109 | var bw = std.io.bufferedWriter(zip_file.deprecatedWriter()); | |
| 2113 | 2110 | var store: [test_files.len]std.zip.testutil.FileStore = undefined; |
| 2114 | 2111 | try std.zip.testutil.writeZip(bw.writer(), &test_files, &store, .{}); |
| 2115 | 2112 | try bw.flush(); |
| ... | ... | @@ -2427,9 +2424,15 @@ const TestFetchBuilder = struct { |
| 2427 | 2424 | if (notes_len > 0) { |
| 2428 | 2425 | try std.testing.expectEqual(notes_len, em.notes_len); |
| 2429 | 2426 | } |
| 2430 | var al = std.ArrayList(u8).init(std.testing.allocator); | |
| 2431 | defer al.deinit(); | |
| 2432 | try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer()); | |
| 2433 | try std.testing.expectEqualStrings(msg, al.items); | |
| 2427 | var aw: std.io.Writer.Allocating = .init(std.testing.allocator); | |
| 2428 | defer aw.deinit(); | |
| 2429 | try errors.renderToWriter(.{ .ttyconf = .no_color }, &aw.writer); | |
| 2430 | try std.testing.expectEqualStrings(msg, aw.getWritten()); | |
| 2434 | 2431 | } |
| 2435 | 2432 | }; |
| 2433 | ||
| 2434 | test { | |
| 2435 | _ = Filter; | |
| 2436 | _ = FileType; | |
| 2437 | _ = UnpackResult; | |
| 2438 | } |
src/Package/Fetch/git.zig+27-12| ... | ... | @@ -135,9 +135,8 @@ pub const Oid = union(Format) { |
| 135 | 135 | } else error.InvalidOid; |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | pub fn format(oid: Oid, w: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 139 | comptime assert(fmt.len == 0); | |
| 140 | try w.print("{x}", .{oid.slice()}); | |
| 138 | pub fn format(oid: Oid, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 139 | try writer.print("{x}", .{oid.slice()}); | |
| 141 | 140 | } |
| 142 | 141 | |
| 143 | 142 | pub fn slice(oid: *const Oid) []const u8 { |
| ... | ... | @@ -697,13 +696,21 @@ pub const Session = struct { |
| 697 | 696 | fn init(allocator: Allocator, uri: std.Uri) !Location { |
| 698 | 697 | const scheme = try allocator.dupe(u8, uri.scheme); |
| 699 | 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 | 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 | 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 | 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 | 714 | errdefer allocator.free(path); |
| 708 | 715 | // The query and fragment are not used as part of the base server URI. |
| 709 | 716 | return .{ |
| ... | ... | @@ -734,7 +741,9 @@ pub const Session = struct { |
| 734 | 741 | fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator { |
| 735 | 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 | 747 | defer session.allocator.free(session_uri_path); |
| 739 | 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 | 767 | if (request.response.status != .ok) return error.ProtocolError; |
| 759 | 768 | const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; |
| 760 | 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 | 773 | defer session.allocator.free(request_uri_path); |
| 763 | 774 | if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; |
| 764 | 775 | var new_uri = request.uri; |
| ... | ... | @@ -845,7 +856,9 @@ pub const Session = struct { |
| 845 | 856 | pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator { |
| 846 | 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 | 862 | defer session.allocator.free(session_uri_path); |
| 850 | 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 | 975 | ) !FetchStream { |
| 963 | 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 | 981 | defer session.allocator.free(session_uri_path); |
| 967 | 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 | 1073 | ProtocolError, |
| 1059 | 1074 | UnexpectedPacket, |
| 1060 | 1075 | }; |
| 1061 | pub const Reader = std.io.Reader(*FetchStream, ReadError, read); | |
| 1076 | pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read); | |
| 1062 | 1077 | |
| 1063 | 1078 | const StreamCode = enum(u8) { |
| 1064 | 1079 | pack_data = 1, |
src/Sema.zig+108-258| ... | ... | @@ -5,6 +5,39 @@ |
| 5 | 5 | //! Does type checking, comptime control flow, and safety-check generation. |
| 6 | 6 | //! This is the the heart of the Zig compiler. |
| 7 | 7 | |
| 8 | const std = @import("std"); | |
| 9 | const math = std.math; | |
| 10 | const mem = std.mem; | |
| 11 | const Allocator = mem.Allocator; | |
| 12 | const assert = std.debug.assert; | |
| 13 | const log = std.log.scoped(.sema); | |
| 14 | ||
| 15 | const Sema = @This(); | |
| 16 | const Value = @import("Value.zig"); | |
| 17 | const MutableValue = @import("mutable_value.zig").MutableValue; | |
| 18 | const Type = @import("Type.zig"); | |
| 19 | const Air = @import("Air.zig"); | |
| 20 | const Zir = std.zig.Zir; | |
| 21 | const Zcu = @import("Zcu.zig"); | |
| 22 | const trace = @import("tracy.zig").trace; | |
| 23 | const Namespace = Zcu.Namespace; | |
| 24 | const CompileError = Zcu.CompileError; | |
| 25 | const SemaError = Zcu.SemaError; | |
| 26 | const LazySrcLoc = Zcu.LazySrcLoc; | |
| 27 | const RangeSet = @import("RangeSet.zig"); | |
| 28 | const target_util = @import("target.zig"); | |
| 29 | const Package = @import("Package.zig"); | |
| 30 | const crash_report = @import("crash_report.zig"); | |
| 31 | const build_options = @import("build_options"); | |
| 32 | const Compilation = @import("Compilation.zig"); | |
| 33 | const InternPool = @import("InternPool.zig"); | |
| 34 | const Alignment = InternPool.Alignment; | |
| 35 | const AnalUnit = InternPool.AnalUnit; | |
| 36 | const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; | |
| 37 | const Cache = std.Build.Cache; | |
| 38 | const LowerZon = @import("Sema/LowerZon.zig"); | |
| 39 | const arith = @import("Sema/arith.zig"); | |
| 40 | ||
| 8 | 41 | pt: Zcu.PerThread, |
| 9 | 42 | /// Alias to `zcu.gpa`. |
| 10 | 43 | gpa: Allocator, |
| ... | ... | @@ -157,39 +190,6 @@ pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc { |
| 157 | 190 | return &sema.comptime_allocs.items[@intFromEnum(idx)]; |
| 158 | 191 | } |
| 159 | 192 | |
| 160 | const std = @import("std"); | |
| 161 | const math = std.math; | |
| 162 | const mem = std.mem; | |
| 163 | const Allocator = mem.Allocator; | |
| 164 | const assert = std.debug.assert; | |
| 165 | const log = std.log.scoped(.sema); | |
| 166 | ||
| 167 | const Sema = @This(); | |
| 168 | const Value = @import("Value.zig"); | |
| 169 | const MutableValue = @import("mutable_value.zig").MutableValue; | |
| 170 | const Type = @import("Type.zig"); | |
| 171 | const Air = @import("Air.zig"); | |
| 172 | const Zir = std.zig.Zir; | |
| 173 | const Zcu = @import("Zcu.zig"); | |
| 174 | const trace = @import("tracy.zig").trace; | |
| 175 | const Namespace = Zcu.Namespace; | |
| 176 | const CompileError = Zcu.CompileError; | |
| 177 | const SemaError = Zcu.SemaError; | |
| 178 | const LazySrcLoc = Zcu.LazySrcLoc; | |
| 179 | const RangeSet = @import("RangeSet.zig"); | |
| 180 | const target_util = @import("target.zig"); | |
| 181 | const Package = @import("Package.zig"); | |
| 182 | const crash_report = @import("crash_report.zig"); | |
| 183 | const build_options = @import("build_options"); | |
| 184 | const Compilation = @import("Compilation.zig"); | |
| 185 | const InternPool = @import("InternPool.zig"); | |
| 186 | const Alignment = InternPool.Alignment; | |
| 187 | const AnalUnit = InternPool.AnalUnit; | |
| 188 | const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; | |
| 189 | const Cache = std.Build.Cache; | |
| 190 | const LowerZon = @import("Sema/LowerZon.zig"); | |
| 191 | const arith = @import("Sema/arith.zig"); | |
| 192 | ||
| 193 | 193 | pub const default_branch_quota = 1000; |
| 194 | 194 | |
| 195 | 195 | pub const InferredErrorSet = struct { |
| ... | ... | @@ -1144,7 +1144,7 @@ fn analyzeBodyInner( |
| 1144 | 1144 | |
| 1145 | 1145 | // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. |
| 1146 | 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 | 1148 | const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); |
| 1149 | 1149 | const file = zcu.fileByIndex(file_index); |
| 1150 | 1150 | break :path file.path.fmt(zcu.comp); |
| ... | ... | @@ -1280,7 +1280,6 @@ fn analyzeBodyInner( |
| 1280 | 1280 | .tag_name => try sema.zirTagName(block, inst), |
| 1281 | 1281 | .type_name => try sema.zirTypeName(block, inst), |
| 1282 | 1282 | .frame_type => try sema.zirFrameType(block, inst), |
| 1283 | .frame_size => try sema.zirFrameSize(block, inst), | |
| 1284 | 1283 | .int_from_float => try sema.zirIntFromFloat(block, inst), |
| 1285 | 1284 | .float_from_int => try sema.zirFloatFromInt(block, inst), |
| 1286 | 1285 | .ptr_from_int => try sema.zirPtrFromInt(block, inst), |
| ... | ... | @@ -1302,7 +1301,6 @@ fn analyzeBodyInner( |
| 1302 | 1301 | .mul_add => try sema.zirMulAdd(block, inst), |
| 1303 | 1302 | .builtin_call => try sema.zirBuiltinCall(block, inst), |
| 1304 | 1303 | .@"resume" => try sema.zirResume(block, inst), |
| 1305 | .@"await" => try sema.zirAwait(block, inst), | |
| 1306 | 1304 | .for_len => try sema.zirForLen(block, inst), |
| 1307 | 1305 | .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), |
| 1308 | 1306 | .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), |
| ... | ... | @@ -1410,12 +1408,10 @@ fn analyzeBodyInner( |
| 1410 | 1408 | .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), |
| 1411 | 1409 | .prefetch => try sema.zirPrefetch( block, extended), |
| 1412 | 1410 | .error_cast => try sema.zirErrorCast( block, extended), |
| 1413 | .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), | |
| 1414 | 1411 | .select => try sema.zirSelect( block, extended), |
| 1415 | 1412 | .int_from_error => try sema.zirIntFromError( block, extended), |
| 1416 | 1413 | .error_from_int => try sema.zirErrorFromInt( block, extended), |
| 1417 | 1414 | .reify => try sema.zirReify( block, extended, inst), |
| 1418 | .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), | |
| 1419 | 1415 | .cmpxchg => try sema.zirCmpxchg( block, extended), |
| 1420 | 1416 | .c_va_arg => try sema.zirCVaArg( block, extended), |
| 1421 | 1417 | .c_va_copy => try sema.zirCVaCopy( block, extended), |
| ... | ... | @@ -2767,7 +2763,7 @@ fn zirTupleDecl( |
| 2767 | 2763 | const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); |
| 2768 | 2764 | const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value }); |
| 2769 | 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 | 2767 | return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val); |
| 2772 | 2768 | } |
| 2773 | 2769 | break :init field_init_val.toIntern(); |
| ... | ... | @@ -2864,7 +2860,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us |
| 2864 | 2860 | sema.code.nullTerminatedString(str), |
| 2865 | 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 | 2864 | break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav }); |
| 2869 | 2865 | }, |
| 2870 | 2866 | .decl_ref => |str| capture: { |
| ... | ... | @@ -2874,7 +2870,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us |
| 2874 | 2870 | sema.code.nullTerminatedString(str), |
| 2875 | 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 | 2874 | break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav }); |
| 2879 | 2875 | }, |
| 2880 | 2876 | }; |
| ... | ... | @@ -3030,8 +3026,8 @@ pub fn createTypeName( |
| 3030 | 3026 | |
| 3031 | 3027 | var aw: std.io.Writer.Allocating = .init(gpa); |
| 3032 | 3028 | defer aw.deinit(); |
| 3033 | const bw = &aw.interface; | |
| 3034 | bw.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; | |
| 3029 | const w = &aw.writer; | |
| 3030 | w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; | |
| 3035 | 3031 | |
| 3036 | 3032 | var arg_i: usize = 0; |
| 3037 | 3033 | for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { |
| ... | ... | @@ -3044,13 +3040,13 @@ pub fn createTypeName( |
| 3044 | 3040 | // result in a compile error. |
| 3045 | 3041 | const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat |
| 3046 | 3042 | |
| 3047 | if (arg_i != 0) bw.writeByte(',') catch return error.OutOfMemory; | |
| 3043 | if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; | |
| 3048 | 3044 | |
| 3049 | 3045 | // Limiting the depth here helps avoid type names getting too long, which |
| 3050 | 3046 | // in turn helps to avoid unreasonably long symbol names for namespaced |
| 3051 | 3047 | // symbols. Such names should ideally be human-readable, and additionally, |
| 3052 | 3048 | // some tooling may not support very long symbol names. |
| 3053 | bw.print("{f}", .{Value.fmtValueSemaFull(.{ | |
| 3049 | w.print("{f}", .{Value.fmtValueSemaFull(.{ | |
| 3054 | 3050 | .val = arg_val, |
| 3055 | 3051 | .pt = pt, |
| 3056 | 3052 | .opt_sema = sema, |
| ... | ... | @@ -3063,7 +3059,7 @@ pub fn createTypeName( |
| 3063 | 3059 | else => continue, |
| 3064 | 3060 | }; |
| 3065 | 3061 | |
| 3066 | try bw.writeByte(')'); | |
| 3062 | w.writeByte(')') catch return error.OutOfMemory; | |
| 3067 | 3063 | return .{ |
| 3068 | 3064 | .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls), |
| 3069 | 3065 | .nav = .none, |
| ... | ... | @@ -5578,9 +5574,8 @@ fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp |
| 5578 | 5574 | |
| 5579 | 5575 | if (operand_ty.arrayLen(zcu) != extra.expect_len) { |
| 5580 | 5576 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 5581 | const msg = try sema.errMsg(src, "expected {} elements for destructure, found {}", .{ | |
| 5582 | extra.expect_len, | |
| 5583 | operand_ty.arrayLen(zcu), | |
| 5577 | const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{ | |
| 5578 | extra.expect_len, operand_ty.arrayLen(zcu), | |
| 5584 | 5579 | }); |
| 5585 | 5580 | errdefer msg.destroy(sema.gpa); |
| 5586 | 5581 | try sema.errNote(destructure_src, msg, "result destructured here", .{}); |
| ... | ... | @@ -5912,26 +5907,25 @@ fn zirCompileLog( |
| 5912 | 5907 | |
| 5913 | 5908 | var aw: std.io.Writer.Allocating = .init(gpa); |
| 5914 | 5909 | defer aw.deinit(); |
| 5915 | const bw = &aw.interface; | |
| 5910 | const writer = &aw.writer; | |
| 5916 | 5911 | |
| 5917 | 5912 | const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); |
| 5918 | 5913 | const src_node = extra.data.src_node; |
| 5919 | 5914 | const args = sema.code.refSlice(extra.end, extended.small); |
| 5920 | 5915 | |
| 5921 | 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; | |
| 5923 | 5918 | |
| 5924 | 5919 | const arg = try sema.resolveInst(arg_ref); |
| 5925 | 5920 | const arg_ty = sema.typeOf(arg); |
| 5926 | 5921 | if (try sema.resolveValueResolveLazy(arg)) |val| { |
| 5927 | bw.print("@as({f}, {f})", .{ | |
| 5922 | writer.print("@as({f}, {f})", .{ | |
| 5928 | 5923 | arg_ty.fmt(pt), val.fmtValueSema(pt, sema), |
| 5929 | 5924 | }) catch return error.OutOfMemory; |
| 5930 | 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; | |
| 5935 | 5929 | |
| 5936 | 5930 | const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls); |
| 5937 | 5931 | |
| ... | ... | @@ -6928,7 +6922,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 6928 | 6922 | inst_data.get(sema.code), |
| 6929 | 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 | 6926 | return sema.analyzeNavRef(block, src, nav_index); |
| 6933 | 6927 | } |
| 6934 | 6928 | |
| ... | ... | @@ -6943,16 +6937,16 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 6943 | 6937 | inst_data.get(sema.code), |
| 6944 | 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 | 6941 | return sema.analyzeNavVal(block, src, nav); |
| 6948 | 6942 | } |
| 6949 | 6943 | |
| 6950 | fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { | |
| 6944 | fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { | |
| 6951 | 6945 | const pt = sema.pt; |
| 6952 | 6946 | const zcu = pt.zcu; |
| 6953 | 6947 | var namespace = block.namespace; |
| 6954 | 6948 | while (true) { |
| 6955 | if (try sema.lookupInNamespace(block, src, namespace, name, false)) |lookup| { | |
| 6949 | if (try sema.lookupInNamespace(block, namespace, name)) |lookup| { | |
| 6956 | 6950 | assert(lookup.accessible); |
| 6957 | 6951 | return lookup.nav; |
| 6958 | 6952 | } |
| ... | ... | @@ -6961,15 +6955,12 @@ fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPoo |
| 6961 | 6955 | unreachable; // AstGen detects use of undeclared identifiers. |
| 6962 | 6956 | } |
| 6963 | 6957 | |
| 6964 | /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but | |
| 6965 | /// only for ones in the specified namespace. | |
| 6958 | /// This looks up a member of a specific namespace. | |
| 6966 | 6959 | fn lookupInNamespace( |
| 6967 | 6960 | sema: *Sema, |
| 6968 | 6961 | block: *Block, |
| 6969 | src: LazySrcLoc, | |
| 6970 | 6962 | namespace_index: InternPool.NamespaceIndex, |
| 6971 | 6963 | ident_name: InternPool.NullTerminatedString, |
| 6972 | observe_usingnamespace: bool, | |
| 6973 | 6964 | ) CompileError!?struct { |
| 6974 | 6965 | nav: InternPool.Nav.Index, |
| 6975 | 6966 | /// If `false`, the declaration is in a different file and is not `pub`. |
| ... | ... | @@ -6978,7 +6969,6 @@ fn lookupInNamespace( |
| 6978 | 6969 | } { |
| 6979 | 6970 | const pt = sema.pt; |
| 6980 | 6971 | const zcu = pt.zcu; |
| 6981 | const ip = &zcu.intern_pool; | |
| 6982 | 6972 | |
| 6983 | 6973 | try pt.ensureNamespaceUpToDate(namespace_index); |
| 6984 | 6974 | |
| ... | ... | @@ -6995,75 +6985,7 @@ fn lookupInNamespace( |
| 6995 | 6985 | } }); |
| 6996 | 6986 | } |
| 6997 | 6987 | |
| 6998 | if (observe_usingnamespace and (namespace.pub_usingnamespace.items.len != 0 or namespace.priv_usingnamespace.items.len != 0)) { | |
| 6999 | const gpa = sema.gpa; | |
| 7000 | var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .empty; | |
| 7001 | defer checked_namespaces.deinit(gpa); | |
| 7002 | ||
| 7003 | // Keep track of name conflicts for error notes. | |
| 7004 | var candidates: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty; | |
| 7005 | defer candidates.deinit(gpa); | |
| 7006 | ||
| 7007 | try checked_namespaces.put(gpa, namespace, {}); | |
| 7008 | var check_i: usize = 0; | |
| 7009 | ||
| 7010 | while (check_i < checked_namespaces.count()) : (check_i += 1) { | |
| 7011 | const check_ns = checked_namespaces.keys()[check_i]; | |
| 7012 | const Pass = enum { @"pub", priv }; | |
| 7013 | for ([2]Pass{ .@"pub", .priv }) |pass| { | |
| 7014 | if (pass == .priv and src_file != check_ns.file_scope) { | |
| 7015 | continue; | |
| 7016 | } | |
| 7017 | ||
| 7018 | const decls, const usingnamespaces = switch (pass) { | |
| 7019 | .@"pub" => .{ &check_ns.pub_decls, &check_ns.pub_usingnamespace }, | |
| 7020 | .priv => .{ &check_ns.priv_decls, &check_ns.priv_usingnamespace }, | |
| 7021 | }; | |
| 7022 | ||
| 7023 | if (decls.getKeyAdapted(ident_name, adapter)) |nav_index| { | |
| 7024 | try candidates.append(gpa, nav_index); | |
| 7025 | } | |
| 7026 | ||
| 7027 | for (usingnamespaces.items) |sub_ns_nav| { | |
| 7028 | try sema.ensureNavResolved(block, src, sub_ns_nav, .fully); | |
| 7029 | const sub_ns_ty: Type = .fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val); | |
| 7030 | const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu)); | |
| 7031 | try checked_namespaces.put(gpa, sub_ns, {}); | |
| 7032 | } | |
| 7033 | } | |
| 7034 | } | |
| 7035 | ||
| 7036 | ignore_self: { | |
| 7037 | const skip_nav = switch (sema.owner.unwrap()) { | |
| 7038 | .@"comptime", .type, .func, .memoized_state => break :ignore_self, | |
| 7039 | .nav_ty, .nav_val => |nav| nav, | |
| 7040 | }; | |
| 7041 | var i: usize = 0; | |
| 7042 | while (i < candidates.items.len) { | |
| 7043 | if (candidates.items[i] == skip_nav) { | |
| 7044 | _ = candidates.orderedRemove(i); | |
| 7045 | } else { | |
| 7046 | i += 1; | |
| 7047 | } | |
| 7048 | } | |
| 7049 | } | |
| 7050 | ||
| 7051 | switch (candidates.items.len) { | |
| 7052 | 0 => {}, | |
| 7053 | 1 => return .{ | |
| 7054 | .nav = candidates.items[0], | |
| 7055 | .accessible = true, | |
| 7056 | }, | |
| 7057 | else => return sema.failWithOwnedErrorMsg(block, msg: { | |
| 7058 | const msg = try sema.errMsg(src, "ambiguous reference", .{}); | |
| 7059 | errdefer msg.destroy(gpa); | |
| 7060 | for (candidates.items) |candidate| { | |
| 7061 | try sema.errNote(zcu.navSrcLoc(candidate), msg, "declared here", .{}); | |
| 7062 | } | |
| 7063 | break :msg msg; | |
| 7064 | }), | |
| 7065 | } | |
| 7066 | } else if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { | |
| 6988 | if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { | |
| 7067 | 6989 | return .{ |
| 7068 | 6990 | .nav = nav_index, |
| 7069 | 6991 | .accessible = true, |
| ... | ... | @@ -7652,10 +7574,6 @@ fn analyzeCall( |
| 7652 | 7574 | const ip = &zcu.intern_pool; |
| 7653 | 7575 | const arena = sema.arena; |
| 7654 | 7576 | |
| 7655 | if (modifier == .async_kw) { | |
| 7656 | return sema.failWithUseOfAsync(block, call_src); | |
| 7657 | } | |
| 7658 | ||
| 7659 | 7577 | const maybe_func_inst = try sema.funcDeclSrcInst(callee); |
| 7660 | 7578 | const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ |
| 7661 | 7579 | .base_node_inst = fn_decl_inst, |
| ... | ... | @@ -8047,14 +7965,13 @@ fn analyzeCall( |
| 8047 | 7965 | } |
| 8048 | 7966 | |
| 8049 | 7967 | const call_tag: Air.Inst.Tag = switch (modifier) { |
| 8050 | .auto, .no_async => .call, | |
| 7968 | .auto, .no_suspend => .call, | |
| 8051 | 7969 | .never_tail => .call_never_tail, |
| 8052 | 7970 | .never_inline => .call_never_inline, |
| 8053 | 7971 | .always_tail => .call_always_tail, |
| 8054 | 7972 | |
| 8055 | 7973 | .always_inline, |
| 8056 | 7974 | .compile_time, |
| 8057 | .async_kw, | |
| 8058 | 7975 | => unreachable, |
| 8059 | 7976 | }; |
| 8060 | 7977 | |
| ... | ... | @@ -9417,14 +9334,6 @@ fn resolveGenericBody( |
| 9417 | 9334 | return sema.resolveConstDefinedValue(block, src, result, reason); |
| 9418 | 9335 | } |
| 9419 | 9336 | |
| 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`). | |
| 9428 | 9337 | pub fn handleExternLibName( |
| 9429 | 9338 | sema: *Sema, |
| 9430 | 9339 | block: *Block, |
| ... | ... | @@ -9474,11 +9383,6 @@ pub fn handleExternLibName( |
| 9474 | 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 | } |
| 9484 | 9388 | |
| ... | ... | @@ -9543,18 +9447,17 @@ fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool { |
| 9543 | 9447 | fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void { |
| 9544 | 9448 | const CallingConventionsSupportingVarArgsList = struct { |
| 9545 | 9449 | arch: std.Target.Cpu.Arch, |
| 9546 | pub fn format(ctx: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 9547 | comptime assert(fmt.len == 0); | |
| 9450 | pub fn format(ctx: @This(), w: *std.io.Writer) std.io.Writer.Error!void { | |
| 9548 | 9451 | var first = true; |
| 9549 | 9452 | for (calling_conventions_supporting_var_args) |cc_inner| { |
| 9550 | 9453 | for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| { |
| 9551 | 9454 | if (supported_arch == ctx.arch) break; |
| 9552 | 9455 | } else continue; // callconv not supported by this arch |
| 9553 | 9456 | if (!first) { |
| 9554 | try bw.writeAll(", "); | |
| 9457 | try w.writeAll(", "); | |
| 9555 | 9458 | } |
| 9556 | 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 | 9892 | .bad_arch => |allowed_archs| { |
| 9990 | 9893 | const ArchListFormatter = struct { |
| 9991 | 9894 | archs: []const std.Target.Cpu.Arch, |
| 9992 | pub fn format(formatter: @This(), bw: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 9993 | comptime assert(fmt.len == 0); | |
| 9895 | pub fn format(formatter: @This(), w: *std.io.Writer) std.io.Writer.Error!void { | |
| 9994 | 9896 | for (formatter.archs, 0..) |arch, i| { |
| 9995 | 9897 | if (i != 0) |
| 9996 | try bw.writeAll(", "); | |
| 9997 | try bw.print("'{s}'", .{@tagName(arch)}); | |
| 9898 | try w.writeAll(", "); | |
| 9899 | try w.print("'{s}'", .{@tagName(arch)}); | |
| 9998 | 9900 | } |
| 9999 | 9901 | } |
| 10000 | 9902 | }; |
| ... | ... | @@ -13965,7 +13867,6 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 13965 | 13867 | const zcu = pt.zcu; |
| 13966 | 13868 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 13967 | 13869 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 13968 | const src = block.nodeOffset(inst_data.src_node); | |
| 13969 | 13870 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 13970 | 13871 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 13971 | 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 | 13875 | try sema.checkNamespaceType(block, lhs_src, container_type); |
| 13975 | 13876 | |
| 13976 | 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 | 13879 | if (lookup.accessible) { |
| 13979 | 13880 | return .bool_true; |
| 13980 | 13881 | } |
| ... | ... | @@ -14173,7 +14074,7 @@ fn zirShl( |
| 14173 | 14074 | }); |
| 14174 | 14075 | } |
| 14175 | 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 | } |
| 14178 | 14079 | |
| 14179 | 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 | 14379 | const scalar_tag = scalar_ty.zigTypeTag(zcu); |
| 14479 | 14380 | |
| 14480 | 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)}); | |
| 14482 | 14383 | |
| 14483 | 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 | 16995 | const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; |
| 17095 | 16996 | const tree = file.getTree(zcu) catch |err| { |
| 17096 | 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 | 16999 | file.path.fmt(zcu.comp), @errorName(err), |
| 17099 | 17000 | }); |
| 17100 | 17001 | break :name null; |
| ... | ... | @@ -17122,7 +17023,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat |
| 17122 | 17023 | const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; |
| 17123 | 17024 | const tree = file.getTree(zcu) catch |err| { |
| 17124 | 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 | 17027 | file.path.fmt(zcu.comp), @errorName(err), |
| 17127 | 17028 | }); |
| 17128 | 17029 | break :name null; |
| ... | ... | @@ -17755,7 +17656,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 17755 | 17656 | } }); |
| 17756 | 17657 | }; |
| 17757 | 17658 | |
| 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()); | |
| 17759 | 17660 | |
| 17760 | 17661 | const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum"); |
| 17761 | 17662 | |
| ... | ... | @@ -17868,7 +17769,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 17868 | 17769 | } }); |
| 17869 | 17770 | }; |
| 17870 | 17771 | |
| 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()); | |
| 17872 | 17773 | |
| 17873 | 17774 | const enum_tag_ty_val = try pt.intern(.{ .opt = .{ |
| 17874 | 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 | 17964 | } }); |
| 18064 | 17965 | }; |
| 18065 | 17966 | |
| 18066 | const decls_val = try sema.typeInfoDecls(block, src, ty.getNamespace(zcu)); | |
| 17967 | const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); | |
| 18067 | 17968 | |
| 18068 | 17969 | const backing_integer_val = try pt.intern(.{ .opt = .{ |
| 18069 | 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 | 18003 | const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque"); |
| 18103 | 18004 | |
| 18104 | 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)); | |
| 18106 | 18007 | |
| 18107 | 18008 | const field_values = .{ |
| 18108 | 18009 | // decls: []const Declaration, |
| ... | ... | @@ -18124,7 +18025,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 18124 | 18025 | |
| 18125 | 18026 | fn typeInfoDecls( |
| 18126 | 18027 | sema: *Sema, |
| 18127 | block: *Block, | |
| 18128 | 18028 | src: LazySrcLoc, |
| 18129 | 18029 | opt_namespace: InternPool.OptionalNamespaceIndex, |
| 18130 | 18030 | ) CompileError!InternPool.Index { |
| ... | ... | @@ -18140,7 +18040,7 @@ fn typeInfoDecls( |
| 18140 | 18040 | var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); |
| 18141 | 18041 | defer seen_namespaces.deinit(); |
| 18142 | 18042 | |
| 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); | |
| 18144 | 18044 | |
| 18145 | 18045 | const array_decl_ty = try pt.arrayType(.{ |
| 18146 | 18046 | .len = decl_vals.items.len, |
| ... | ... | @@ -18174,8 +18074,6 @@ fn typeInfoDecls( |
| 18174 | 18074 | |
| 18175 | 18075 | fn typeInfoNamespaceDecls( |
| 18176 | 18076 | sema: *Sema, |
| 18177 | block: *Block, | |
| 18178 | src: LazySrcLoc, | |
| 18179 | 18077 | opt_namespace_index: InternPool.OptionalNamespaceIndex, |
| 18180 | 18078 | declaration_ty: Type, |
| 18181 | 18079 | decl_vals: *std.ArrayList(InternPool.Index), |
| ... | ... | @@ -18231,15 +18129,6 @@ fn typeInfoNamespaceDecls( |
| 18231 | 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 | } |
| 18244 | 18133 | |
| 18245 | 18134 | fn 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 | 18264 | const uncasted_ty = sema.typeOf(uncasted_operand); |
| 18376 | 18265 | if (uncasted_ty.isVector(zcu)) { |
| 18377 | 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 | 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 | 19295 | |
| 19407 | 19296 | if (host_size != 0) { |
| 19408 | 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 | 19299 | elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, |
| 19411 | 19300 | }); |
| 19412 | 19301 | } |
| 19413 | 19302 | const elem_bit_size = try elem_ty.bitSizeSema(pt); |
| 19414 | 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 | 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 | 20462 | const is_vector = operand_ty.zigTypeTag(zcu) == .vector; |
| 20574 | 20463 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 20575 | 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 | 20467 | const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; |
| 20579 | 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 | 20745 | 64 => .f64, |
| 20857 | 20746 | 80 => .f80, |
| 20858 | 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 | 20750 | return Air.internedToRef(ty.toIntern()); |
| 20862 | 20751 | }, |
| ... | ... | @@ -21747,7 +21636,7 @@ fn reifyTuple( |
| 21747 | 21636 | return sema.fail( |
| 21748 | 21637 | block, |
| 21749 | 21638 | src, |
| 21750 | "tuple field name '{}' does not match field index {}", | |
| 21639 | "tuple field name '{d}' does not match field index {d}", | |
| 21751 | 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 | 22032 | return sema.failWithUseOfAsync(block, src); |
| 22144 | 22033 | } |
| 22145 | 22034 | |
| 22146 | fn 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 | ||
| 22152 | 22035 | fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 22153 | 22036 | const pt = sema.pt; |
| 22154 | 22037 | const zcu = pt.zcu; |
| ... | ... | @@ -22771,7 +22654,7 @@ fn ptrCastFull( |
| 22771 | 22654 | |
| 22772 | 22655 | if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { |
| 22773 | 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 | 22658 | src_info.packed_offset.host_size, |
| 22776 | 22659 | dest_info.packed_offset.host_size, |
| 22777 | 22660 | }); |
| ... | ... | @@ -22783,7 +22666,7 @@ fn ptrCastFull( |
| 22783 | 22666 | |
| 22784 | 22667 | if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { |
| 22785 | 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 | 22670 | src_info.packed_offset.bit_offset, |
| 22788 | 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 | 23236 | return sema.fail( |
| 23354 | 23237 | block, |
| 23355 | 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 | 23240 | .{ scalar_ty.fmt(pt), bits }, |
| 23358 | 23241 | ); |
| 23359 | 23242 | } |
| ... | ... | @@ -23690,7 +23573,7 @@ fn checkNumericType( |
| 23690 | 23573 | .comptime_float, .float, .comptime_int, .int => {}, |
| 23691 | 23574 | .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { |
| 23692 | 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 | 23578 | else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}), |
| 23696 | 23579 | } |
| ... | ... | @@ -24367,7 +24250,7 @@ fn analyzeShuffle( |
| 24367 | 24250 | if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: { |
| 24368 | 24251 | const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); |
| 24369 | 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 | 24254 | break :msg msg; |
| 24372 | 24255 | }); |
| 24373 | 24256 | } |
| ... | ... | @@ -24795,14 +24678,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 24795 | 24678 | var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier); |
| 24796 | 24679 | switch (modifier) { |
| 24797 | 24680 | // These can be upgraded to comptime or nosuspend calls. |
| 24798 | .auto, .never_tail, .no_async => { | |
| 24681 | .auto, .never_tail, .no_suspend => { | |
| 24799 | 24682 | if (block.isComptime()) { |
| 24800 | 24683 | if (modifier == .never_tail) { |
| 24801 | 24684 | return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); |
| 24802 | 24685 | } |
| 24803 | 24686 | modifier = .compile_time; |
| 24804 | 24687 | } else if (extra.flags.is_nosuspend) { |
| 24805 | modifier = .no_async; | |
| 24688 | modifier = .no_suspend; | |
| 24806 | 24689 | } |
| 24807 | 24690 | }, |
| 24808 | 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 | 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 | 24706 | .never_inline => { |
| 24832 | 24707 | if (block.isComptime()) { |
| 24833 | 24708 | return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); |
| ... | ... | @@ -25160,7 +25035,7 @@ fn analyzeMinMax( |
| 25160 | 25035 | try sema.checkNumericType(block, operand_src, operand_ty); |
| 25161 | 25036 | if (operand_ty.zigTypeTag(zcu) != .vector) { |
| 25162 | 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 | 25039 | errdefer msg.destroy(zcu.gpa); |
| 25165 | 25040 | try sema.errNote(operand_srcs[0], msg, "vector operand here", .{}); |
| 25166 | 25041 | break :msg msg; |
| ... | ... | @@ -25168,7 +25043,7 @@ fn analyzeMinMax( |
| 25168 | 25043 | } |
| 25169 | 25044 | if (operand_ty.vectorLen(zcu) != vec_len) { |
| 25170 | 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 | 25047 | errdefer msg.destroy(zcu.gpa); |
| 25173 | 25048 | try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len}); |
| 25174 | 25049 | break :msg msg; |
| ... | ... | @@ -25181,7 +25056,7 @@ fn analyzeMinMax( |
| 25181 | 25056 | const operand_ty = sema.typeOf(operand); |
| 25182 | 25057 | if (operand_ty.zigTypeTag(zcu) == .vector) { |
| 25183 | 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 | 25060 | errdefer msg.destroy(zcu.gpa); |
| 25186 | 25061 | try sema.errNote(operand_src, msg, "vector operand here", .{}); |
| 25187 | 25062 | break :msg msg; |
| ... | ... | @@ -25816,40 +25691,12 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25816 | 25691 | }); |
| 25817 | 25692 | } |
| 25818 | 25693 | |
| 25819 | fn 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 | ||
| 25825 | 25694 | fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 25826 | 25695 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| 25827 | 25696 | const src = block.nodeOffset(inst_data.src_node); |
| 25828 | 25697 | return sema.failWithUseOfAsync(block, src); |
| 25829 | 25698 | } |
| 25830 | 25699 | |
| 25831 | fn 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 | ||
| 25842 | fn 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 | ||
| 25853 | 25700 | fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 25854 | 25701 | const tracy = trace(@src()); |
| 25855 | 25702 | defer tracy.end(); |
| ... | ... | @@ -26757,7 +26604,7 @@ fn explainWhyTypeIsNotExtern( |
| 26757 | 26604 | } |
| 26758 | 26605 | switch (ty.fnCallingConvention(zcu)) { |
| 26759 | 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 | 26608 | .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}), |
| 26762 | 26609 | else => return, |
| 26763 | 26610 | } |
| ... | ... | @@ -27779,7 +27626,7 @@ fn namespaceLookup( |
| 27779 | 27626 | const pt = sema.pt; |
| 27780 | 27627 | const zcu = pt.zcu; |
| 27781 | 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 | 27630 | if (!lookup.accessible) { |
| 27784 | 27631 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 27785 | 27632 | const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{ |
| ... | ... | @@ -29312,7 +29159,7 @@ fn coerceExtra( |
| 29312 | 29159 | // return sema.fail( |
| 29313 | 29160 | // block, |
| 29314 | 29161 | // inst_src, |
| 29315 | // "type '{f}' cannot represent integer value '{}'", | |
| 29162 | // "type '{f}' cannot represent integer value '{f}'", | |
| 29316 | 29163 | // .{ dest_ty.fmt(pt), val }, |
| 29317 | 29164 | // ); |
| 29318 | 29165 | //} |
| ... | ... | @@ -29519,7 +29366,7 @@ fn coerceExtra( |
| 29519 | 29366 | try sema.errNote(param_src, msg, "parameter type declared here", .{}); |
| 29520 | 29367 | } |
| 29521 | 29368 | |
| 29522 | // TODO maybe add "cannot store an error in type '{}'" note | |
| 29369 | // TODO maybe add "cannot store an error in type '{f}'" note | |
| 29523 | 29370 | |
| 29524 | 29371 | break :msg msg; |
| 29525 | 29372 | }; |
| ... | ... | @@ -29867,12 +29714,12 @@ const InMemoryCoercionResult = union(enum) { |
| 29867 | 29714 | }, |
| 29868 | 29715 | .ptr_bit_range => |bit_range| { |
| 29869 | 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 | 29718 | bit_range.actual_host, bit_range.wanted_host, |
| 29872 | 29719 | }); |
| 29873 | 29720 | } |
| 29874 | 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 | 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 | 34836 | return sema.fail( |
| 34990 | 34837 | block, |
| 34991 | 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 | 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 | 35179 | switch (union_type.flagsUnordered(ip).status) { |
| 35333 | 35180 | .none => {}, |
| 35334 | 35181 | .field_types_wip => { |
| 35335 | const msg = try sema.errMsg( | |
| 35336 | ty.srcLoc(zcu), | |
| 35337 | "union '{f}' depends on itself", | |
| 35338 | .{ty.fmt(pt)}, | |
| 35339 | ); | |
| 35182 | const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)}); | |
| 35340 | 35183 | return sema.failWithOwnedErrorMsg(null, msg); |
| 35341 | 35184 | }, |
| 35342 | 35185 | .have_field_types, |
| ... | ... | @@ -37330,7 +37173,14 @@ fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg |
| 37330 | 37173 | } |
| 37331 | 37174 | } |
| 37332 | 37175 | |
| 37333 | fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, val: Value, intermediate_value_count: u32, start_value_name: InternPool.NullTerminatedString) Allocator.Error!union(enum) { | |
| 37176 | fn 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 | 37184 | done, |
| 37335 | 37185 | new_val: Value, |
| 37336 | 37186 | } { |
| ... | ... | @@ -37341,9 +37191,9 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, |
| 37341 | 37191 | |
| 37342 | 37192 | var first_path: std.ArrayListUnmanaged(u8) = .empty; |
| 37343 | 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 | 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 | } |
| 37348 | 37198 | |
| 37349 | 37199 | const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path); |
| ... | ... | @@ -37373,7 +37223,7 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, |
| 37373 | 37223 | const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count}); |
| 37374 | 37224 | const deriv_start = @import("print_value.zig").printPtrDerivation( |
| 37375 | 37225 | derivation, |
| 37376 | &second_path_aw.interface, | |
| 37226 | &second_path_aw.writer, | |
| 37377 | 37227 | pt, |
| 37378 | 37228 | .lvalue, |
| 37379 | 37229 | .{ .str = inter_name }, |
| ... | ... | @@ -37437,7 +37287,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList |
| 37437 | 37287 | const backing_enum = union_ty.unionTagTypeHypothetical(zcu); |
| 37438 | 37288 | const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?; |
| 37439 | 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 | 37291 | return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path); |
| 37442 | 37292 | }, |
| 37443 | 37293 | .aggregate => |agg| { |
| ... | ... | @@ -37462,7 +37312,7 @@ fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList |
| 37462 | 37312 | try path.print(arena, "[{d}]", .{elem_idx}); |
| 37463 | 37313 | } else { |
| 37464 | 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 | 37317 | else => unreachable, |
| 37468 | 37318 | } |
src/Sema/LowerZon.zig+8-12| ... | ... | @@ -360,11 +360,7 @@ fn fail( |
| 360 | 360 | fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index { |
| 361 | 361 | const pt = self.sema.pt; |
| 362 | 362 | return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) { |
| 363 | error.WrongType => return self.fail( | |
| 364 | node, | |
| 365 | "expected type '{f}'", | |
| 366 | .{res_ty.fmt(pt)}, | |
| 367 | ), | |
| 363 | error.WrongType => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(pt)}), | |
| 368 | 364 | else => |e| return e, |
| 369 | 365 | }; |
| 370 | 366 | } |
| ... | ... | @@ -458,7 +454,7 @@ fn lowerInt( |
| 458 | 454 | // If lhs is unsigned and rhs is less than 0, we're out of bounds |
| 459 | 455 | if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail( |
| 460 | 456 | node, |
| 461 | "type '{f}' cannot represent integer value '{}'", | |
| 457 | "type '{f}' cannot represent integer value '{d}'", | |
| 462 | 458 | .{ res_ty.fmt(self.sema.pt), rhs }, |
| 463 | 459 | ); |
| 464 | 460 | |
| ... | ... | @@ -478,7 +474,7 @@ fn lowerInt( |
| 478 | 474 | if (rhs < min_int or rhs > max_int) { |
| 479 | 475 | return self.fail( |
| 480 | 476 | node, |
| 481 | "type '{f}' cannot represent integer value '{}'", | |
| 477 | "type '{f}' cannot represent integer value '{d}'", | |
| 482 | 478 | .{ res_ty.fmt(self.sema.pt), rhs }, |
| 483 | 479 | ); |
| 484 | 480 | } |
| ... | ... | @@ -496,7 +492,7 @@ fn lowerInt( |
| 496 | 492 | if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) { |
| 497 | 493 | return self.fail( |
| 498 | 494 | node, |
| 499 | "type '{f}' cannot represent integer value '{f}'", | |
| 495 | "type '{f}' cannot represent integer value '{d}'", | |
| 500 | 496 | .{ res_ty.fmt(self.sema.pt), val }, |
| 501 | 497 | ); |
| 502 | 498 | } |
| ... | ... | @@ -517,7 +513,7 @@ fn lowerInt( |
| 517 | 513 | switch (big_int.setFloat(val, .trunc)) { |
| 518 | 514 | .inexact => return self.fail( |
| 519 | 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 | 517 | .{ val, res_ty.fmt(self.sema.pt) }, |
| 522 | 518 | ), |
| 523 | 519 | .exact => {}, |
| ... | ... | @@ -528,8 +524,8 @@ fn lowerInt( |
| 528 | 524 | if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { |
| 529 | 525 | return self.fail( |
| 530 | 526 | node, |
| 531 | "type '{}' cannot represent integer value '{f}'", | |
| 532 | .{ val, res_ty.fmt(self.sema.pt) }, | |
| 527 | "type '{f}' cannot represent integer value '{d}'", | |
| 528 | .{ res_ty.fmt(self.sema.pt), val }, | |
| 533 | 529 | ); |
| 534 | 530 | } |
| 535 | 531 | |
| ... | ... | @@ -550,7 +546,7 @@ fn lowerInt( |
| 550 | 546 | if (val >= out_of_range) { |
| 551 | 547 | return self.fail( |
| 552 | 548 | node, |
| 553 | "type '{f}' cannot represent integer value '{}'", | |
| 549 | "type '{f}' cannot represent integer value '{d}'", | |
| 554 | 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 | 122 | return a.toIntern() == b.toIntern(); |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | pub fn format(ty: Type, bw: *Writer, comptime f: []const u8) !usize { | |
| 125 | pub fn format(ty: Type, writer: *std.io.Writer) !void { | |
| 126 | 126 | _ = ty; |
| 127 | _ = f; | |
| 128 | _ = bw; | |
| 127 | _ = writer; | |
| 129 | 128 | @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); |
| 130 | 129 | } |
| 131 | 130 | |
| 132 | pub const Formatter = std.fmt.Formatter(format2); | |
| 131 | pub const Formatter = std.fmt.Formatter(Format, Format.default); | |
| 133 | 132 | |
| 134 | 133 | pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter { |
| 135 | 134 | return .{ .data = .{ |
| ... | ... | @@ -138,30 +137,28 @@ pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter { |
| 138 | 137 | } }; |
| 139 | 138 | } |
| 140 | 139 | |
| 141 | const FormatContext = struct { | |
| 140 | const Format = struct { | |
| 142 | 141 | ty: Type, |
| 143 | 142 | pt: Zcu.PerThread, |
| 144 | }; | |
| 145 | 143 | |
| 146 | fn format2(ctx: FormatContext, bw: *Writer, comptime f: []const u8) !void { | |
| 147 | comptime assert(f.len == 0); | |
| 148 | try print(ctx.ty, bw, ctx.pt); | |
| 149 | } | |
| 144 | fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 145 | return print(f.ty, writer, f.pt); | |
| 146 | } | |
| 147 | }; | |
| 150 | 148 | |
| 151 | pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { | |
| 149 | pub fn fmtDebug(ty: Type) std.fmt.Formatter(Type, dump) { | |
| 152 | 150 | return .{ .data = ty }; |
| 153 | 151 | } |
| 154 | 152 | |
| 155 | 153 | /// This is a debug function. In order to print types in a meaningful way |
| 156 | 154 | /// we also need access to the module. |
| 157 | pub fn dump(start_type: Type, bw: *Writer, comptime unused_format_string: []const u8) !void { | |
| 158 | comptime assert(unused_format_string.len == 0); | |
| 159 | return bw.print("{any}", .{start_type.ip_index}); | |
| 155 | pub fn dump(start_type: Type, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 156 | return writer.print("{any}", .{start_type.ip_index}); | |
| 160 | 157 | } |
| 161 | 158 | |
| 162 | 159 | /// Prints a name suitable for `@typeName`. |
| 163 | 160 | /// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels. |
| 164 | pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { | |
| 161 | pub fn print(ty: Type, writer: *std.io.Writer, pt: Zcu.PerThread) std.io.Writer.Error!void { | |
| 165 | 162 | const zcu = pt.zcu; |
| 166 | 163 | const ip = &zcu.intern_pool; |
| 167 | 164 | switch (ip.indexToKey(ty.toIntern())) { |
| ... | ... | @@ -171,22 +168,22 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { |
| 171 | 168 | .signed => 'i', |
| 172 | 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 | 173 | .ptr_type => { |
| 177 | 174 | const info = ty.ptrInfo(zcu); |
| 178 | 175 | |
| 179 | 176 | if (info.sentinel != .none) switch (info.flags.size) { |
| 180 | 177 | .one, .c => unreachable, |
| 181 | .many => try bw.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), | |
| 182 | .slice => try bw.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), | |
| 178 | .many => try writer.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), | |
| 179 | .slice => try writer.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}), | |
| 183 | 180 | } else switch (info.flags.size) { |
| 184 | .one => try bw.writeAll("*"), | |
| 185 | .many => try bw.writeAll("[*]"), | |
| 186 | .c => try bw.writeAll("[*c]"), | |
| 187 | .slice => try bw.writeAll("[]"), | |
| 181 | .one => try writer.writeAll("*"), | |
| 182 | .many => try writer.writeAll("[*]"), | |
| 183 | .c => try writer.writeAll("[*c]"), | |
| 184 | .slice => try writer.writeAll("[]"), | |
| 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 | 187 | if (info.flags.alignment != .none or |
| 191 | 188 | info.packed_offset.host_size != 0 or |
| 192 | 189 | info.flags.vector_index != .none) |
| ... | ... | @@ -195,72 +192,72 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { |
| 195 | 192 | info.flags.alignment |
| 196 | 193 | else |
| 197 | 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}); | |
| 199 | 196 | |
| 200 | 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 | 199 | info.packed_offset.bit_offset, info.packed_offset.host_size, |
| 203 | 200 | }); |
| 204 | 201 | } |
| 205 | 202 | if (info.flags.vector_index == .runtime) { |
| 206 | try bw.writeAll(":?"); | |
| 203 | try writer.writeAll(":?"); | |
| 207 | 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 | 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 "); | |
| 216 | if (info.flags.is_volatile) try bw.writeAll("volatile "); | |
| 212 | if (info.flags.is_const) try writer.writeAll("const "); | |
| 213 | if (info.flags.is_volatile) try writer.writeAll("volatile "); | |
| 217 | 214 | |
| 218 | try print(Type.fromInterned(info.child), bw, pt); | |
| 215 | try print(Type.fromInterned(info.child), writer, pt); | |
| 219 | 216 | }, |
| 220 | 217 | .array_type => |array_type| { |
| 221 | 218 | if (array_type.sentinel == .none) { |
| 222 | try bw.print("[{d}]", .{array_type.len}); | |
| 223 | try print(Type.fromInterned(array_type.child), bw, pt); | |
| 219 | try writer.print("[{d}]", .{array_type.len}); | |
| 220 | try print(Type.fromInterned(array_type.child), writer, pt); | |
| 224 | 221 | } else { |
| 225 | try bw.print("[{d}:{f}]", .{ | |
| 222 | try writer.print("[{d}:{f}]", .{ | |
| 226 | 223 | array_type.len, |
| 227 | 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 | 229 | .vector_type => |vector_type| { |
| 233 | try bw.print("@Vector({d}, ", .{vector_type.len}); | |
| 234 | try print(Type.fromInterned(vector_type.child), bw, pt); | |
| 235 | try bw.writeAll(")"); | |
| 230 | try writer.print("@Vector({d}, ", .{vector_type.len}); | |
| 231 | try print(Type.fromInterned(vector_type.child), writer, pt); | |
| 232 | try writer.writeAll(")"); | |
| 236 | 233 | }, |
| 237 | 234 | .opt_type => |child| { |
| 238 | try bw.writeByte('?'); | |
| 239 | try print(Type.fromInterned(child), bw, pt); | |
| 235 | try writer.writeByte('?'); | |
| 236 | try print(Type.fromInterned(child), writer, pt); | |
| 240 | 237 | }, |
| 241 | 238 | .error_union_type => |error_union_type| { |
| 242 | try print(Type.fromInterned(error_union_type.error_set_type), bw, pt); | |
| 243 | try bw.writeByte('!'); | |
| 239 | try print(Type.fromInterned(error_union_type.error_set_type), writer, pt); | |
| 240 | try writer.writeByte('!'); | |
| 244 | 241 | if (error_union_type.payload_type == .generic_poison_type) { |
| 245 | try bw.writeAll("anytype"); | |
| 242 | try writer.writeAll("anytype"); | |
| 246 | 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 | 247 | .inferred_error_set_type => |func_index| { |
| 251 | 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 | 250 | func_nav.fqn.fmt(ip), |
| 254 | 251 | }); |
| 255 | 252 | }, |
| 256 | 253 | .error_set_type => |error_set_type| { |
| 257 | 254 | const names = error_set_type.names; |
| 258 | try bw.writeAll("error{"); | |
| 255 | try writer.writeAll("error{"); | |
| 259 | 256 | for (names.get(ip), 0..) |name, i| { |
| 260 | if (i != 0) try bw.writeByte(','); | |
| 261 | try bw.print("{f}", .{name.fmt(ip)}); | |
| 257 | if (i != 0) try writer.writeByte(','); | |
| 258 | try writer.print("{f}", .{name.fmt(ip)}); | |
| 262 | 259 | } |
| 263 | try bw.writeAll("}"); | |
| 260 | try writer.writeAll("}"); | |
| 264 | 261 | }, |
| 265 | 262 | .simple_type => |s| switch (s) { |
| 266 | 263 | .f16, |
| ... | ... | @@ -289,97 +286,99 @@ pub fn print(ty: Type, bw: *Writer, pt: Zcu.PerThread) Writer.Error!void { |
| 289 | 286 | .comptime_float, |
| 290 | 287 | .noreturn, |
| 291 | 288 | .adhoc_inferred_error_set, |
| 292 | => return bw.writeAll(@tagName(s)), | |
| 289 | => return writer.writeAll(@tagName(s)), | |
| 293 | 290 | |
| 294 | 291 | .null, |
| 295 | 292 | .undefined, |
| 296 | => return bw.print("@TypeOf({s})", .{@tagName(s)}), | |
| 293 | => return writer.print("@TypeOf({s})", .{@tagName(s)}), | |
| 297 | 294 | |
| 298 | .enum_literal => return bw.writeAll("@Type(.enum_literal)"), | |
| 295 | .enum_literal => return writer.writeAll("@Type(.enum_literal)"), | |
| 299 | 296 | |
| 300 | 297 | .generic_poison => unreachable, |
| 301 | 298 | }, |
| 302 | 299 | .struct_type => { |
| 303 | 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 | 303 | .tuple_type => |tuple| { |
| 307 | 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 | 308 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| { |
| 312 | try bw.writeAll(if (i == 0) " " else ", "); | |
| 313 | if (val != .none) try bw.writeAll("comptime "); | |
| 314 | try print(Type.fromInterned(field_ty), bw, pt); | |
| 315 | if (val != .none) try bw.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)}); | |
| 309 | try writer.writeAll(if (i == 0) " " else ", "); | |
| 310 | if (val != .none) try writer.writeAll("comptime "); | |
| 311 | try print(Type.fromInterned(field_ty), writer, pt); | |
| 312 | if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)}); | |
| 316 | 313 | } |
| 317 | try bw.writeAll(" }"); | |
| 314 | try writer.writeAll(" }"); | |
| 318 | 315 | }, |
| 319 | 316 | |
| 320 | 317 | .union_type => { |
| 321 | 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 | 321 | .opaque_type => { |
| 325 | 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 | 325 | .enum_type => { |
| 329 | 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 | 329 | .func_type => |fn_info| { |
| 333 | 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 | 334 | const param_types = fn_info.param_types.get(&zcu.intern_pool); |
| 338 | 335 | for (param_types, 0..) |param_ty, i| { |
| 339 | if (i != 0) try bw.writeAll(", "); | |
| 336 | if (i != 0) try writer.writeAll(", "); | |
| 340 | 337 | if (std.math.cast(u5, i)) |index| { |
| 341 | 338 | if (fn_info.paramIsComptime(index)) { |
| 342 | try bw.writeAll("comptime "); | |
| 339 | try writer.writeAll("comptime "); | |
| 343 | 340 | } |
| 344 | 341 | if (fn_info.paramIsNoalias(index)) { |
| 345 | try bw.writeAll("noalias "); | |
| 342 | try writer.writeAll("noalias "); | |
| 346 | 343 | } |
| 347 | 344 | } |
| 348 | 345 | if (param_ty == .generic_poison_type) { |
| 349 | try bw.writeAll("anytype"); | |
| 346 | try writer.writeAll("anytype"); | |
| 350 | 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 | 351 | if (fn_info.is_var_args) { |
| 355 | 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 | 358 | if (fn_info.cc != .auto) print_cc: { |
| 362 | 359 | if (zcu.getTarget().cCallingConvention()) |ccc| { |
| 363 | 360 | if (fn_info.cc.eql(ccc)) { |
| 364 | try bw.writeAll("callconv(.c) "); | |
| 361 | try writer.writeAll("callconv(.c) "); | |
| 365 | 362 | break :print_cc; |
| 366 | 363 | } |
| 367 | 364 | } |
| 368 | 365 | switch (fn_info.cc) { |
| 369 | .auto, .@"async", .naked, .@"inline" => try bw.print("callconv(.{f}) ", .{std.zig.fmtId(@tagName(fn_info.cc))}), | |
| 370 | else => try bw.print("callconv({any}) ", .{fn_info.cc}), | |
| 366 | .auto, .async, .naked, .@"inline" => try writer.print("callconv(.{f}) ", .{ | |
| 367 | std.zig.fmtId(@tagName(fn_info.cc)), | |
| 368 | }), | |
| 369 | else => try writer.print("callconv({any}) ", .{fn_info.cc}), | |
| 371 | 370 | } |
| 372 | 371 | } |
| 373 | 372 | if (fn_info.return_type == .generic_poison_type) { |
| 374 | try bw.writeAll("anytype"); | |
| 373 | try writer.writeAll("anytype"); | |
| 375 | 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 | 378 | .anyframe_type => |child| { |
| 380 | if (child == .none) return bw.writeAll("anyframe"); | |
| 381 | try bw.writeAll("anyframe->"); | |
| 382 | try print(Type.fromInterned(child), bw, pt); | |
| 379 | if (child == .none) return writer.writeAll("anyframe"); | |
| 380 | try writer.writeAll("anyframe->"); | |
| 381 | try print(Type.fromInterned(child), writer, pt); | |
| 383 | 382 | }, |
| 384 | 383 | |
| 385 | 384 | // values, not types |
src/Value.zig+7-15| ... | ... | @@ -15,31 +15,23 @@ const Value = @This(); |
| 15 | 15 | |
| 16 | 16 | ip_index: InternPool.Index, |
| 17 | 17 | |
| 18 | pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 18 | pub fn format(val: Value, writer: *std.io.Writer) !void { | |
| 19 | 19 | _ = val; |
| 20 | _ = fmt; | |
| 21 | _ = options; | |
| 22 | 20 | _ = writer; |
| 23 | 21 | @compileError("do not use format values directly; use either fmtDebug or fmtValue"); |
| 24 | 22 | } |
| 25 | 23 | |
| 26 | 24 | /// This is a debug function. In order to print values in a meaningful way |
| 27 | 25 | /// we also need access to the type. |
| 28 | pub fn dump( | |
| 29 | start_val: Value, | |
| 30 | comptime fmt: []const u8, | |
| 31 | _: std.fmt.FormatOptions, | |
| 32 | out_stream: anytype, | |
| 33 | ) !void { | |
| 34 | comptime assert(fmt.len == 0); | |
| 35 | try out_stream.print("(interned: {})", .{start_val.toIntern()}); | |
| 26 | pub fn dump(start_val: Value, w: std.io.Writer) std.io.Writer.Error!void { | |
| 27 | try w.print("(interned: {})", .{start_val.toIntern()}); | |
| 36 | 28 | } |
| 37 | 29 | |
| 38 | pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) { | |
| 30 | pub fn fmtDebug(val: Value) std.fmt.Formatter(Value, dump) { | |
| 39 | 31 | return .{ .data = val }; |
| 40 | 32 | } |
| 41 | 33 | |
| 42 | pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.format) { | |
| 34 | pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.FormatContext, print_value.format) { | |
| 43 | 35 | return .{ .data = .{ |
| 44 | 36 | .val = val, |
| 45 | 37 | .pt = pt, |
| ... | ... | @@ -48,7 +40,7 @@ pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Formatter(print_value.for |
| 48 | 40 | } }; |
| 49 | 41 | } |
| 50 | 42 | |
| 51 | pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatter(print_value.formatSema) { | |
| 43 | pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatter(print_value.FormatContext, print_value.formatSema) { | |
| 52 | 44 | return .{ .data = .{ |
| 53 | 45 | .val = val, |
| 54 | 46 | .pt = pt, |
| ... | ... | @@ -57,7 +49,7 @@ pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Formatte |
| 57 | 49 | } }; |
| 58 | 50 | } |
| 59 | 51 | |
| 60 | pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Formatter(print_value.formatSema) { | |
| 52 | pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Formatter(print_value.FormatContext, print_value.formatSema) { | |
| 61 | 53 | return .{ .data = ctx }; |
| 62 | 54 | } |
| 63 | 55 |
src/Zcu.zig+18-51| ... | ... | @@ -793,10 +793,6 @@ pub const Namespace = struct { |
| 793 | 793 | pub_decls: std.ArrayHashMapUnmanaged(InternPool.Nav.Index, void, NavNameContext, true) = .empty, |
| 794 | 794 | /// Members of the namespace which are *not* marked `pub`. |
| 795 | 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 | 796 | /// All `comptime` declarations in this namespace. We store these purely so that incremental |
| 801 | 797 | /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes. |
| 802 | 798 | comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty, |
| ... | ... | @@ -1116,7 +1112,7 @@ pub const File = struct { |
| 1116 | 1112 | eb: *std.zig.ErrorBundle.Wip, |
| 1117 | 1113 | ) !std.zig.ErrorBundle.SourceLocationIndex { |
| 1118 | 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 | 1116 | .span_start = 0, |
| 1121 | 1117 | .span_main = 0, |
| 1122 | 1118 | .span_end = 0, |
| ... | ... | @@ -1137,7 +1133,7 @@ pub const File = struct { |
| 1137 | 1133 | const end = start + tree.tokenSlice(tok).len; |
| 1138 | 1134 | const loc = std.zig.findLineColumn(source.bytes, start); |
| 1139 | 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 | 1137 | .span_start = start, |
| 1142 | 1138 | .span_main = start, |
| 1143 | 1139 | .span_end = @intCast(end), |
| ... | ... | @@ -1298,9 +1294,6 @@ pub const SrcLoc = struct { |
| 1298 | 1294 | .simple_var_decl, |
| 1299 | 1295 | .aligned_var_decl, |
| 1300 | 1296 | => tree.fullVarDecl(node).?, |
| 1301 | .@"usingnamespace" => { | |
| 1302 | return tree.nodeToSpan(tree.nodeData(node).node); | |
| 1303 | }, | |
| 1304 | 1297 | else => unreachable, |
| 1305 | 1298 | }; |
| 1306 | 1299 | if (full.ast.type_node.unwrap()) |type_node| { |
| ... | ... | @@ -1438,12 +1431,8 @@ pub const SrcLoc = struct { |
| 1438 | 1431 | .field_access => tree.nodeData(node).node_and_token[1], |
| 1439 | 1432 | .call_one, |
| 1440 | 1433 | .call_one_comma, |
| 1441 | .async_call_one, | |
| 1442 | .async_call_one_comma, | |
| 1443 | 1434 | .call, |
| 1444 | 1435 | .call_comma, |
| 1445 | .async_call, | |
| 1446 | .async_call_comma, | |
| 1447 | 1436 | => blk: { |
| 1448 | 1437 | const full = tree.fullCall(&buf, node).?; |
| 1449 | 1438 | break :blk tree.lastToken(full.ast.fn_expr); |
| ... | ... | @@ -3306,9 +3295,6 @@ pub fn mapOldZirToNew( |
| 3306 | 3295 | // All comptime declarations, in order, for a best-effort match. |
| 3307 | 3296 | var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; |
| 3308 | 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); | |
| 3312 | 3298 | |
| 3313 | 3299 | { |
| 3314 | 3300 | var old_decl_it = old_zir.declIterator(match_item.old_inst); |
| ... | ... | @@ -3316,7 +3302,6 @@ pub fn mapOldZirToNew( |
| 3316 | 3302 | const old_decl = old_zir.getDeclaration(old_decl_inst); |
| 3317 | 3303 | switch (old_decl.kind) { |
| 3318 | 3304 | .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), |
| 3319 | .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst), | |
| 3320 | 3305 | .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst), |
| 3321 | 3306 | .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), |
| 3322 | 3307 | .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), |
| ... | ... | @@ -3327,7 +3312,6 @@ pub fn mapOldZirToNew( |
| 3327 | 3312 | |
| 3328 | 3313 | var unnamed_test_idx: u32 = 0; |
| 3329 | 3314 | var comptime_decl_idx: u32 = 0; |
| 3330 | var usingnamespace_decl_idx: u32 = 0; | |
| 3331 | 3315 | |
| 3332 | 3316 | var new_decl_it = new_zir.declIterator(match_item.new_inst); |
| 3333 | 3317 | while (new_decl_it.next()) |new_decl_inst| { |
| ... | ... | @@ -3337,7 +3321,6 @@ pub fn mapOldZirToNew( |
| 3337 | 3321 | // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name. |
| 3338 | 3322 | // * For unnamed tests, we match based on order. |
| 3339 | 3323 | // * For comptime blocks, we match based on order. |
| 3340 | // * For usingnamespace decls, we match based on order. | |
| 3341 | 3324 | // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`. |
| 3342 | 3325 | const old_decl_inst = switch (new_decl.kind) { |
| 3343 | 3326 | .@"comptime" => inst: { |
| ... | ... | @@ -3345,11 +3328,6 @@ pub fn mapOldZirToNew( |
| 3345 | 3328 | defer comptime_decl_idx += 1; |
| 3346 | 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 | 3331 | .unnamed_test => inst: { |
| 3354 | 3332 | if (unnamed_test_idx == unnamed_tests.items.len) continue; |
| 3355 | 3333 | defer unnamed_test_idx += 1; |
| ... | ... | @@ -4058,7 +4036,6 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv |
| 4058 | 4036 | if (!comp.config.is_test or file.mod != zcu.main_mod) continue; |
| 4059 | 4037 | |
| 4060 | 4038 | const want_analysis = switch (decl.kind) { |
| 4061 | .@"usingnamespace" => unreachable, | |
| 4062 | 4039 | .@"const", .@"var" => unreachable, |
| 4063 | 4040 | .@"comptime" => unreachable, |
| 4064 | 4041 | .unnamed_test => true, |
| ... | ... | @@ -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 | 4096 | continue; |
| 4130 | 4097 | } |
| 4131 | 4098 | if (unit_queue.pop()) |kv| { |
| ... | ... | @@ -4271,17 +4238,17 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *std.io.Writer) std.io.Writer.Er |
| 4271 | 4238 | const cu = ip.getComptimeUnit(cu_id); |
| 4272 | 4239 | if (cu.zir_index.resolveFull(ip)) |resolved| { |
| 4273 | 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 | 4242 | } else { |
| 4276 | 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) }), | |
| 4280 | .nav_ty => |nav| return writer.print("nav_ty('{}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), | |
| 4281 | .type => |ty| return writer.print("ty('{}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }), | |
| 4246 | .nav_val => |nav| return writer.print("nav_val('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), | |
| 4247 | .nav_ty => |nav| return writer.print("nav_ty('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), | |
| 4248 | .type => |ty| return writer.print("ty('{f}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }), | |
| 4282 | 4249 | .func => |func| { |
| 4283 | 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 | 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 | 4265 | return writer.writeAll("inst(<lost>)"); |
| 4299 | 4266 | }; |
| 4300 | 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 | 4270 | .nav_val => |nav| { |
| 4304 | 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 | 4274 | .nav_ty => |nav| { |
| 4308 | 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 | 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)}), | |
| 4313 | .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}), | |
| 4279 | .struct_type, .union_type, .enum_type => return writer.print("type('{f}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}), | |
| 4280 | .func => |f| return writer.print("ies('{f}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}), | |
| 4314 | 4281 | else => unreachable, |
| 4315 | 4282 | }, |
| 4316 | 4283 | .zon_file => |file| { |
| 4317 | 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 | 4287 | .embed_file => |ef_idx| { |
| 4321 | 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 | 4291 | .namespace => |ti| { |
| 4325 | 4292 | const info = ti.resolveFull(ip) orelse { |
| 4326 | 4293 | return writer.writeAll("namespace(<lost>)"); |
| 4327 | 4294 | }; |
| 4328 | 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 | 4298 | .namespace_name => |k| { |
| 4332 | 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 | 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 | 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 | 4341 | const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); |
| 4375 | 4342 | switch (cc) { |
| 4376 | 4343 | .auto, .@"inline" => return .ok, |
| 4377 | .@"async" => return .{ .bad_backend = backend }, // nothing supports async currently | |
| 4344 | .async => return .{ .bad_backend = backend }, // nothing supports async currently | |
| 4378 | 4345 | .naked => {}, // depends only on backend |
| 4379 | 4346 | else => for (cc.archs()) |allowed_arch| { |
| 4380 | 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 | 53 | const zcu = pt.zcu; |
| 54 | 54 | const gpa = zcu.gpa; |
| 55 | 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 | 57 | file.path.deinit(gpa); |
| 58 | 58 | file.unload(gpa); |
| 59 | 59 | if (file.prev_zir) |prev_zir| { |
| ... | ... | @@ -117,7 +117,7 @@ pub fn updateFile( |
| 117 | 117 | var lock: std.fs.File.Lock = switch (file.status) { |
| 118 | 118 | .never_loaded, .retryable_failure => lock: { |
| 119 | 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 | 121 | file.path.fmt(comp), want_local_cache, &hex_digest, |
| 122 | 122 | }); |
| 123 | 123 | |
| ... | ... | @@ -130,11 +130,11 @@ pub fn updateFile( |
| 130 | 130 | stat.inode == file.stat.inode; |
| 131 | 131 | |
| 132 | 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 | 134 | return; |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | log.debug("metadata changed: {}", .{file.path.fmt(comp)}); | |
| 137 | log.debug("metadata changed: {f}", .{file.path.fmt(comp)}); | |
| 138 | 138 | |
| 139 | 139 | break :lock .exclusive; |
| 140 | 140 | }, |
| ... | ... | @@ -221,12 +221,12 @@ pub fn updateFile( |
| 221 | 221 | }; |
| 222 | 222 | switch (result) { |
| 223 | 223 | .success => { |
| 224 | log.debug("AstGen cached success: {}", .{file.path.fmt(comp)}); | |
| 224 | log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)}); | |
| 225 | 225 | break false; |
| 226 | 226 | }, |
| 227 | 227 | .invalid => {}, |
| 228 | .truncated => log.warn("unexpected EOF reading cached ZIR for {}", .{file.path.fmt(comp)}), | |
| 229 | .stale => log.debug("AstGen cache stale: {}", .{file.path.fmt(comp)}), | |
| 228 | .truncated => log.warn("unexpected EOF reading cached ZIR for {f}", .{file.path.fmt(comp)}), | |
| 229 | .stale => log.debug("AstGen cache stale: {f}", .{file.path.fmt(comp)}), | |
| 230 | 230 | } |
| 231 | 231 | |
| 232 | 232 | // If we already have the exclusive lock then it is our job to update. |
| ... | ... | @@ -283,7 +283,7 @@ pub fn updateFile( |
| 283 | 283 | }, |
| 284 | 284 | } |
| 285 | 285 | |
| 286 | log.debug("AstGen fresh success: {}", .{file.path.fmt(comp)}); | |
| 286 | log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)}); | |
| 287 | 287 | } |
| 288 | 288 | |
| 289 | 289 | file.stat = .{ |
| ... | ... | @@ -343,8 +343,9 @@ fn loadZirZoirCache( |
| 343 | 343 | .zon => Zoir.Header, |
| 344 | 344 | }; |
| 345 | 345 | |
| 346 | var buffer: [@sizeOf(Header)]u8 = undefined; | |
| 346 | var buffer: [2000]u8 = undefined; | |
| 347 | 347 | var cache_fr = cache_file.reader(&buffer); |
| 348 | cache_fr.size = stat.size; | |
| 348 | 349 | const cache_br = &cache_fr.interface; |
| 349 | 350 | |
| 350 | 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 | 1115 | defer block.instructions.deinit(gpa); |
| 1115 | 1116 | |
| 1116 | 1117 | const zir_decl = zir.getDeclaration(inst_resolved.inst); |
| 1117 | assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); | |
| 1118 | 1118 | |
| 1119 | 1119 | const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); |
| 1120 | 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 | 1163 | assert(nav_ty.zigTypeTag(zcu) == .@"fn"); |
| 1164 | 1164 | break :is_const true; |
| 1165 | 1165 | }, |
| 1166 | .@"usingnamespace", .@"const" => true, | |
| 1166 | .@"const" => true, | |
| 1167 | 1167 | .@"var" => { |
| 1168 | 1168 | try sema.validateVarType( |
| 1169 | 1169 | &block, |
| ... | ... | @@ -1243,26 +1243,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 1243 | 1243 | // this resolves the type `type` (which needs no resolution), not the struct itself. |
| 1244 | 1244 | try nav_ty.resolveLayout(pt); |
| 1245 | 1245 | |
| 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 | 1246 | const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) { |
| 1267 | 1247 | .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen |
| 1268 | 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 | 1447 | defer _ = zcu.analysis_in_progress.swapRemove(anal_unit); |
| 1468 | 1448 | |
| 1469 | 1449 | const zir_decl = zir.getDeclaration(inst_resolved.inst); |
| 1470 | assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); | |
| 1471 | 1450 | const type_body = zir_decl.type_body.?; |
| 1472 | 1451 | |
| 1473 | 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 | 1509 | |
| 1531 | 1510 | const is_const = switch (zir_decl.kind) { |
| 1532 | 1511 | .@"comptime" => unreachable, |
| 1533 | .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true, | |
| 1512 | .unnamed_test, .@"test", .decltest, .@"const" => true, | |
| 1534 | 1513 | .@"var" => false, |
| 1535 | 1514 | }; |
| 1536 | 1515 | |
| ... | ... | @@ -2324,7 +2303,7 @@ pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!voi |
| 2324 | 2303 | |
| 2325 | 2304 | Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure( |
| 2326 | 2305 | .write_builtin_zig, |
| 2327 | "unable to write '{}': {s}", | |
| 2306 | "unable to write '{f}': {s}", | |
| 2328 | 2307 | .{ file.path.fmt(comp), @errorName(err) }, |
| 2329 | 2308 | ); |
| 2330 | 2309 | } |
| ... | ... | @@ -2548,7 +2527,6 @@ pub fn scanNamespace( |
| 2548 | 2527 | |
| 2549 | 2528 | try existing_by_inst.ensureTotalCapacity(gpa, @intCast( |
| 2550 | 2529 | namespace.pub_decls.count() + namespace.priv_decls.count() + |
| 2551 | namespace.pub_usingnamespace.items.len + namespace.priv_usingnamespace.items.len + | |
| 2552 | 2530 | namespace.comptime_decls.items.len + |
| 2553 | 2531 | namespace.test_decls.items.len, |
| 2554 | 2532 | )); |
| ... | ... | @@ -2561,14 +2539,6 @@ pub fn scanNamespace( |
| 2561 | 2539 | const zir_index = ip.getNav(nav).analysis.?.zir_index; |
| 2562 | 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 | 2542 | for (namespace.comptime_decls.items) |cu| { |
| 2573 | 2543 | const zir_index = ip.getComptimeUnit(cu).zir_index; |
| 2574 | 2544 | existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu })); |
| ... | ... | @@ -2585,8 +2555,6 @@ pub fn scanNamespace( |
| 2585 | 2555 | |
| 2586 | 2556 | namespace.pub_decls.clearRetainingCapacity(); |
| 2587 | 2557 | namespace.priv_decls.clearRetainingCapacity(); |
| 2588 | namespace.pub_usingnamespace.clearRetainingCapacity(); | |
| 2589 | namespace.priv_usingnamespace.clearRetainingCapacity(); | |
| 2590 | 2558 | namespace.comptime_decls.clearRetainingCapacity(); |
| 2591 | 2559 | namespace.test_decls.clearRetainingCapacity(); |
| 2592 | 2560 | |
| ... | ... | @@ -2614,7 +2582,6 @@ const ScanDeclIter = struct { |
| 2614 | 2582 | /// Decl scanning is run in two passes, so that we can detect when a generated |
| 2615 | 2583 | /// name would clash with an explicit name and use a different one. |
| 2616 | 2584 | pass: enum { named, unnamed }, |
| 2617 | usingnamespace_index: usize = 0, | |
| 2618 | 2585 | unnamed_test_index: usize = 0, |
| 2619 | 2586 | |
| 2620 | 2587 | fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString { |
| ... | ... | @@ -2653,12 +2620,6 @@ const ScanDeclIter = struct { |
| 2653 | 2620 | if (iter.pass != .unnamed) return; |
| 2654 | 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 | 2623 | .unnamed_test => name: { |
| 2663 | 2624 | if (iter.pass != .unnamed) return; |
| 2664 | 2625 | const i = iter.unnamed_test_index; |
| ... | ... | @@ -2717,7 +2678,7 @@ const ScanDeclIter = struct { |
| 2717 | 2678 | const name = maybe_name.unwrap().?; |
| 2718 | 2679 | const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name); |
| 2719 | 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 | 2682 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); |
| 2722 | 2683 | break :nav nav; |
| 2723 | 2684 | }; |
| ... | ... | @@ -2729,17 +2690,6 @@ const ScanDeclIter = struct { |
| 2729 | 2690 | |
| 2730 | 2691 | const want_analysis = switch (decl.kind) { |
| 2731 | 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 | 2693 | .unnamed_test, .@"test", .decltest => a: { |
| 2744 | 2694 | const is_named = decl.kind != .unnamed_test; |
| 2745 | 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 | 4384 | defer liveness.deinit(gpa); |
| 4435 | 4385 | |
| 4436 | 4386 | if (build_options.enable_debug_extensions and comp.verbose_air) { |
| 4437 | std.debug.lockStdErr(); | |
| 4438 | defer std.debug.unlockStdErr(); | |
| 4439 | const stderr = std.io.getStdErr().writer(); | |
| 4440 | stderr.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}) catch {}; | |
| 4387 | const stderr = std.debug.lockStderrWriter(&.{}); | |
| 4388 | defer std.debug.unlockStderrWriter(); | |
| 4389 | stderr.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}) catch {}; | |
| 4441 | 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 | } |
| 4444 | 4393 | |
| 4445 | 4394 | if (std.debug.runtime_safety) { |
src/arch/aarch64/CodeGen.zig deleted-6401| ... | ... | @@ -1,6401 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const mem = std.mem; | |
| 4 | const math = std.math; | |
| 5 | const assert = std.debug.assert; | |
| 6 | const codegen = @import("../../codegen.zig"); | |
| 7 | const Air = @import("../../Air.zig"); | |
| 8 | const Mir = @import("Mir.zig"); | |
| 9 | const Emit = @import("Emit.zig"); | |
| 10 | const Type = @import("../../Type.zig"); | |
| 11 | const Value = @import("../../Value.zig"); | |
| 12 | const link = @import("../../link.zig"); | |
| 13 | const Zcu = @import("../../Zcu.zig"); | |
| 14 | const InternPool = @import("../../InternPool.zig"); | |
| 15 | const Compilation = @import("../../Compilation.zig"); | |
| 16 | const ErrorMsg = Zcu.ErrorMsg; | |
| 17 | const Target = std.Target; | |
| 18 | const Allocator = mem.Allocator; | |
| 19 | const trace = @import("../../tracy.zig").trace; | |
| 20 | const leb128 = std.leb; | |
| 21 | const log = std.log.scoped(.codegen); | |
| 22 | const build_options = @import("build_options"); | |
| 23 | const Alignment = InternPool.Alignment; | |
| 24 | ||
| 25 | const CodeGenError = codegen.CodeGenError; | |
| 26 | ||
| 27 | const bits = @import("bits.zig"); | |
| 28 | const abi = @import("abi.zig"); | |
| 29 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | |
| 30 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | |
| 31 | const RegisterManager = abi.RegisterManager; | |
| 32 | const RegisterLock = RegisterManager.RegisterLock; | |
| 33 | const Register = bits.Register; | |
| 34 | const Instruction = bits.Instruction; | |
| 35 | const Condition = bits.Instruction.Condition; | |
| 36 | const callee_preserved_regs = abi.callee_preserved_regs; | |
| 37 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | |
| 38 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | |
| 39 | const gp = abi.RegisterClass.gp; | |
| 40 | ||
| 41 | const InnerError = CodeGenError || error{OutOfRegisters}; | |
| 42 | ||
| 43 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { | |
| 44 | return null; | |
| 45 | } | |
| 46 | ||
| 47 | gpa: Allocator, | |
| 48 | pt: Zcu.PerThread, | |
| 49 | air: Air, | |
| 50 | liveness: Air.Liveness, | |
| 51 | bin_file: *link.File, | |
| 52 | target: *const std.Target, | |
| 53 | func_index: InternPool.Index, | |
| 54 | owner_nav: InternPool.Nav.Index, | |
| 55 | args: []MCValue, | |
| 56 | ret_mcv: MCValue, | |
| 57 | fn_type: Type, | |
| 58 | arg_index: u32, | |
| 59 | src_loc: Zcu.LazySrcLoc, | |
| 60 | stack_align: u32, | |
| 61 | ||
| 62 | /// MIR Instructions | |
| 63 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | |
| 64 | /// MIR extra data | |
| 65 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | |
| 66 | ||
| 67 | /// Byte offset within the source file of the ending curly. | |
| 68 | end_di_line: u32, | |
| 69 | end_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. | |
| 74 | exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty, | |
| 75 | ||
| 76 | reused_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. | |
| 82 | dbg_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. | |
| 91 | branch_stack: *std.ArrayList(Branch), | |
| 92 | ||
| 93 | // Key is the block instruction | |
| 94 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, | |
| 95 | ||
| 96 | register_manager: RegisterManager = .{}, | |
| 97 | /// Maps offset to what is stored there. | |
| 98 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .empty, | |
| 99 | /// Tracks the current instruction allocated to the compare flags | |
| 100 | compare_flags_inst: ?Air.Inst.Index = null, | |
| 101 | ||
| 102 | /// Offset from the stack base, representing the end of the stack frame. | |
| 103 | max_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`. | |
| 106 | next_stack_offset: u32 = 0, | |
| 107 | ||
| 108 | saved_regs_stack_space: u32 = 0, | |
| 109 | ||
| 110 | /// Debug field, used to find bugs in the compiler. | |
| 111 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | |
| 112 | ||
| 113 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | |
| 114 | ||
| 115 | const 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 | ||
| 166 | const 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 | ||
| 257 | const 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 | ||
| 266 | const 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 | ||
| 272 | const 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 | ||
| 281 | const 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 | ||
| 327 | const Self = @This(); | |
| 328 | ||
| 329 | pub 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 | ||
| 409 | fn 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 | ||
| 419 | fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { | |
| 420 | return try self.addInst(.{ | |
| 421 | .tag = .nop, | |
| 422 | .data = .{ .nop = {} }, | |
| 423 | }); | |
| 424 | } | |
| 425 | ||
| 426 | pub 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 | ||
| 432 | pub 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 | ||
| 445 | fn 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 | ||
| 626 | fn 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. | |
| 898 | fn 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. | |
| 919 | fn finishAirBookkeeping(self: *Self) void { | |
| 920 | if (std.debug.runtime_safety) { | |
| 921 | self.air_bookkeeping += 1; | |
| 922 | } | |
| 923 | } | |
| 924 | ||
| 925 | fn 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 | ||
| 963 | fn 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 | ||
| 968 | fn 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. | |
| 1001 | fn 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 | ||
| 1022 | fn 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 | ||
| 1042 | pub 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 | |
| 1060 | fn 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. | |
| 1090 | fn 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. | |
| 1100 | fn 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 | ||
| 1108 | fn 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 | ||
| 1113 | fn 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 | ||
| 1135 | fn 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 | ||
| 1141 | fn 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 | ||
| 1147 | fn 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 | ||
| 1192 | fn 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 | ||
| 1227 | fn 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 | ||
| 1276 | fn 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 | ||
| 1289 | fn 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 | ||
| 1378 | fn 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 | ||
| 1460 | fn 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 | ||
| 1478 | fn 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 | |
| 1497 | const 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 | |
| 1537 | const 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 | |
| 1551 | const 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. | |
| 1584 | fn 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 | |
| 1727 | fn 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 | |
| 1812 | fn 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 | ||
| 1869 | fn 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 | ||
| 1932 | fn 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 | ||
| 1960 | fn 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 | ||
| 1982 | fn 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 | ||
| 2017 | fn 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 | ||
| 2051 | fn 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 | ||
| 2086 | fn 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 | ||
| 2155 | fn 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 | ||
| 2178 | fn 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 | ||
| 2214 | fn 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 | ||
| 2248 | fn 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 | ||
| 2302 | fn 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 | ||
| 2346 | fn 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 | ||
| 2374 | fn 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 | ||
| 2420 | fn 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 | ||
| 2470 | fn 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 | ||
| 2485 | fn 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 | ||
| 2491 | fn 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 | ||
| 2497 | fn 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 | ||
| 2503 | fn 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 | ||
| 2632 | fn 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 | ||
| 2856 | fn 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 | ||
| 2995 | fn 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 | ||
| 3007 | fn 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 | ||
| 3017 | fn 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 | ||
| 3044 | fn 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 | ||
| 3050 | fn 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 | |
| 3057 | fn 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 | ||
| 3126 | fn 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 | |
| 3138 | fn 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 | ||
| 3207 | fn 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 | |
| 3219 | fn 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 | |
| 3226 | fn 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 | ||
| 3232 | fn 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 | ||
| 3238 | fn 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 | ||
| 3246 | fn 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 | ||
| 3251 | fn 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 | ||
| 3256 | fn 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 | |
| 3302 | fn 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 | |
| 3327 | fn 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 | ||
| 3351 | fn 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 | ||
| 3368 | fn 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 | ||
| 3377 | fn 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 | ||
| 3401 | fn 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 | ||
| 3418 | fn 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 | ||
| 3433 | fn 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 | ||
| 3452 | fn 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 | ||
| 3476 | fn 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 | ||
| 3495 | fn 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 | ||
| 3501 | fn 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 | ||
| 3515 | fn 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 | ||
| 3531 | fn 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 | ||
| 3537 | fn 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 | ||
| 3543 | fn 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 | ||
| 3549 | fn 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 | ||
| 3555 | fn 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 | ||
| 3561 | fn 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 | ||
| 3567 | fn 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 | ||
| 3573 | fn 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 | ||
| 3579 | fn 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 | ||
| 3588 | fn 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 | ||
| 3625 | fn 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 | ||
| 3699 | fn 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 | ||
| 3776 | fn 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 | ||
| 3808 | fn 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 | ||
| 3874 | fn 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 | ||
| 3906 | fn 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 | ||
| 3930 | fn 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 | ||
| 3952 | fn 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 | ||
| 4075 | fn 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 | ||
| 4092 | fn 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 | ||
| 4099 | fn 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 | ||
| 4105 | fn 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 | ||
| 4127 | fn 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 | ||
| 4183 | fn 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 | ||
| 4207 | fn 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 | ||
| 4234 | fn airTrap(self: *Self) InnerError!void { | |
| 4235 | _ = try self.addInst(.{ | |
| 4236 | .tag = .brk, | |
| 4237 | .data = .{ .imm16 = 0x0001 }, | |
| 4238 | }); | |
| 4239 | return self.finishAirBookkeeping(); | |
| 4240 | } | |
| 4241 | ||
| 4242 | fn airBreakpoint(self: *Self) InnerError!void { | |
| 4243 | _ = try self.addInst(.{ | |
| 4244 | .tag = .brk, | |
| 4245 | .data = .{ .imm16 = 0xf000 }, | |
| 4246 | }); | |
| 4247 | return self.finishAirBookkeeping(); | |
| 4248 | } | |
| 4249 | ||
| 4250 | fn 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 | ||
| 4255 | fn 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 | ||
| 4260 | fn 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 | ||
| 4418 | fn 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 | ||
| 4451 | fn 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 | ||
| 4495 | fn 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 | ||
| 4506 | fn 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 | ||
| 4593 | fn 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 | ||
| 4598 | fn 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 | ||
| 4606 | fn 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 | ||
| 4620 | fn 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 | ||
| 4631 | fn 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 | ||
| 4651 | fn 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 | ||
| 4682 | fn 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 | ||
| 4826 | fn 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 | ||
| 4873 | fn 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 | ||
| 4879 | fn 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 | ||
| 4896 | fn 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 | ||
| 4915 | fn 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 | ||
| 4926 | fn 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 | ||
| 4943 | fn 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 | ||
| 4954 | fn 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 | ||
| 4971 | fn 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 | ||
| 4982 | fn 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 | ||
| 4999 | fn 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 | ||
| 5010 | fn 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 | ||
| 5027 | fn 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`. | |
| 5041 | fn jump(self: *Self, inst: Mir.Inst.Index) !void { | |
| 5042 | _ = try self.addInst(.{ | |
| 5043 | .tag = .b, | |
| 5044 | .data = .{ .inst = inst }, | |
| 5045 | }); | |
| 5046 | } | |
| 5047 | ||
| 5048 | fn 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 | ||
| 5054 | fn 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 | ||
| 5086 | fn 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 | ||
| 5210 | fn 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 | ||
| 5220 | fn 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 | ||
| 5226 | fn 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 | ||
| 5252 | fn 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 | ||
| 5264 | fn 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 | ||
| 5382 | fn 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. | |
| 5392 | fn 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 | ||
| 5404 | fn 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 | ||
| 5576 | fn 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 | ||
| 5740 | fn 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 | ||
| 5914 | fn 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 | ||
| 5935 | fn 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 | ||
| 5953 | fn 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 | ||
| 5961 | fn 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 | ||
| 5969 | fn 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 | ||
| 5979 | fn 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 | ||
| 5984 | fn 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 | ||
| 5989 | fn 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 | ||
| 5995 | fn 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 | ||
| 6005 | fn 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 | ||
| 6010 | fn 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 | ||
| 6015 | fn 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 | ||
| 6025 | fn 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 | ||
| 6035 | fn 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 | ||
| 6041 | fn 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 | ||
| 6048 | fn 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 | ||
| 6053 | fn 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 | ||
| 6058 | fn 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 | ||
| 6064 | fn 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 | ||
| 6088 | fn 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 | ||
| 6095 | fn 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 | ||
| 6100 | fn 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 | ||
| 6109 | fn 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 | ||
| 6137 | fn 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 | ||
| 6146 | fn 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 | ||
| 6160 | fn 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 | ||
| 6172 | fn 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 | ||
| 6194 | const 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`. | |
| 6207 | fn 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`. | |
| 6341 | fn 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 | ||
| 6350 | fn 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 | ||
| 6355 | fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { | |
| 6356 | @branchHint(.cold); | |
| 6357 | return self.pt.zcu.codegenFailMsg(self.owner_nav, msg); | |
| 6358 | } | |
| 6359 | ||
| 6360 | fn 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 | ||
| 6367 | fn 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 | ||
| 6395 | fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { | |
| 6396 | return self.air.typeOf(inst, &self.pt.zcu.intern_pool); | |
| 6397 | } | |
| 6398 | ||
| 6399 | fn 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 | ||
| 4 | const Emit = @This(); | |
| 5 | const std = @import("std"); | |
| 6 | const math = std.math; | |
| 7 | const Mir = @import("Mir.zig"); | |
| 8 | const bits = @import("bits.zig"); | |
| 9 | const link = @import("../../link.zig"); | |
| 10 | const Zcu = @import("../../Zcu.zig"); | |
| 11 | const ErrorMsg = Zcu.ErrorMsg; | |
| 12 | const assert = std.debug.assert; | |
| 13 | const Instruction = bits.Instruction; | |
| 14 | const Register = bits.Register; | |
| 15 | const log = std.log.scoped(.aarch64_emit); | |
| 16 | ||
| 17 | mir: Mir, | |
| 18 | bin_file: *link.File, | |
| 19 | debug_output: link.File.DebugInfoOutput, | |
| 20 | target: *const std.Target, | |
| 21 | err_msg: ?*ErrorMsg = null, | |
| 22 | src_loc: Zcu.LazySrcLoc, | |
| 23 | code: *std.ArrayListUnmanaged(u8), | |
| 24 | ||
| 25 | prev_di_line: u32, | |
| 26 | prev_di_column: u32, | |
| 27 | ||
| 28 | /// Relative to the beginning of `code`. | |
| 29 | prev_di_pc: usize, | |
| 30 | ||
| 31 | /// The amount of stack space consumed by the saved callee-saved | |
| 32 | /// registers in bytes | |
| 33 | saved_regs_stack_space: u32, | |
| 34 | ||
| 35 | /// The branch type of every branch | |
| 36 | branch_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 | |
| 40 | branch_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 | |
| 47 | code_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. | |
| 51 | stack_size: u32, | |
| 52 | ||
| 53 | const InnerError = error{ | |
| 54 | OutOfMemory, | |
| 55 | EmitFail, | |
| 56 | }; | |
| 57 | ||
| 58 | const 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 | ||
| 73 | pub 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 | ||
| 217 | pub 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 | ||
| 231 | fn 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 | ||
| 260 | fn 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 | ||
| 293 | fn 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 | ||
| 304 | fn 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 | ||
| 315 | fn 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 | ||
| 424 | fn 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 | ||
| 431 | fn 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 | ||
| 440 | fn 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 | ||
| 484 | fn 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 | ||
| 523 | fn 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 | ||
| 540 | fn 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 | ||
| 555 | fn 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 | ||
| 572 | fn 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 | ||
| 597 | fn 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 | ||
| 614 | fn 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 | ||
| 625 | fn 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 | ||
| 636 | fn 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 | ||
| 646 | fn 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 | ||
| 660 | fn 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 | ||
| 671 | fn 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 | ||
| 705 | fn 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 | ||
| 728 | fn 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 | ||
| 769 | fn 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 | ||
| 810 | fn 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 | ||
| 834 | fn 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 | ||
| 851 | fn 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 | ||
| 979 | fn 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 | ||
| 994 | fn 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 | ||
| 1055 | fn 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 | ||
| 1119 | fn 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 | ||
| 1140 | fn 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 | ||
| 1158 | fn 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 | ||
| 1193 | fn 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 | ||
| 1204 | fn 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 | ||
| 1235 | fn mirNop(emit: *Emit) !void { | |
| 1236 | try emit.writeInstruction(Instruction.nop()); | |
| 1237 | } | |
| 1238 | ||
| 1239 | fn regListIsSet(reg_list: u32, reg: Register) bool { | |
| 1240 | return reg_list & @as(u32, 1) << @as(u5, @intCast(reg.id())) != 0; | |
| 1241 | } | |
| 1242 | ||
| 1243 | fn 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 | ||
| 1322 | fn 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 | ||
| 1337 | fn 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 | ||
| 9 | const Mir = @This(); | |
| 10 | const std = @import("std"); | |
| 11 | const builtin = @import("builtin"); | |
| 12 | const assert = std.debug.assert; | |
| 13 | ||
| 14 | const bits = @import("bits.zig"); | |
| 15 | const Register = bits.Register; | |
| 16 | const InternPool = @import("../../InternPool.zig"); | |
| 17 | const Emit = @import("Emit.zig"); | |
| 18 | const codegen = @import("../../codegen.zig"); | |
| 19 | const link = @import("../../link.zig"); | |
| 20 | const Zcu = @import("../../Zcu.zig"); | |
| 21 | ||
| 22 | max_end_stack: u32, | |
| 23 | saved_regs_stack_space: u32, | |
| 24 | ||
| 25 | instructions: std.MultiArrayList(Inst).Slice, | |
| 26 | /// The meaning of this data is determined by `Inst.Tag` value. | |
| 27 | extra: []const u32, | |
| 28 | ||
| 29 | pub 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 | ||
| 503 | pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { | |
| 504 | mir.instructions.deinit(gpa); | |
| 505 | gpa.free(mir.extra); | |
| 506 | mir.* = undefined; | |
| 507 | } | |
| 508 | ||
| 509 | pub 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. | |
| 544 | pub 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 | ||
| 562 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const bits = @import("bits.zig"); | |
| 4 | const Register = bits.Register; | |
| 5 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | |
| 6 | const Type = @import("../../Type.zig"); | |
| 7 | const Zcu = @import("../../Zcu.zig"); | |
| 8 | ||
| 9 | pub 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. | |
| 18 | pub 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 | ||
| 76 | const sret_float_count = 4; | |
| 77 | fn 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 | ||
| 119 | pub 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 | ||
| 143 | pub const callee_preserved_regs = [_]Register{ | |
| 144 | .x19, .x20, .x21, .x22, .x23, | |
| 145 | .x24, .x25, .x26, .x27, .x28, | |
| 146 | }; | |
| 147 | ||
| 148 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 149 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 150 | ||
| 151 | const allocatable_registers = callee_preserved_regs; | |
| 152 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); | |
| 153 | ||
| 154 | // Register classes | |
| 155 | const RegisterBitSet = RegisterManager.RegisterBitSet; | |
| 156 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const mem = std.mem; | |
| 4 | const math = std.math; | |
| 5 | const assert = std.debug.assert; | |
| 6 | const codegen = @import("../../codegen.zig"); | |
| 7 | const Air = @import("../../Air.zig"); | |
| 8 | const Mir = @import("Mir.zig"); | |
| 9 | const Emit = @import("Emit.zig"); | |
| 10 | const Type = @import("../../Type.zig"); | |
| 11 | const Value = @import("../../Value.zig"); | |
| 12 | const link = @import("../../link.zig"); | |
| 13 | const Zcu = @import("../../Zcu.zig"); | |
| 14 | const InternPool = @import("../../InternPool.zig"); | |
| 15 | const Compilation = @import("../../Compilation.zig"); | |
| 16 | const ErrorMsg = Zcu.ErrorMsg; | |
| 17 | const Target = std.Target; | |
| 18 | const Allocator = mem.Allocator; | |
| 19 | const trace = @import("../../tracy.zig").trace; | |
| 20 | const leb128 = std.leb; | |
| 21 | const log = std.log.scoped(.codegen); | |
| 22 | const build_options = @import("build_options"); | |
| 23 | const Alignment = InternPool.Alignment; | |
| 24 | ||
| 25 | const CodeGenError = codegen.CodeGenError; | |
| 26 | ||
| 27 | const bits = @import("bits.zig"); | |
| 28 | const abi = @import("abi.zig"); | |
| 29 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | |
| 30 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | |
| 31 | const RegisterManager = abi.RegisterManager; | |
| 32 | const RegisterLock = RegisterManager.RegisterLock; | |
| 33 | const Register = bits.Register; | |
| 34 | const Instruction = bits.Instruction; | |
| 35 | const Condition = bits.Condition; | |
| 36 | const callee_preserved_regs = abi.callee_preserved_regs; | |
| 37 | const caller_preserved_regs = abi.caller_preserved_regs; | |
| 38 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | |
| 39 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | |
| 40 | const gp = abi.RegisterClass.gp; | |
| 41 | ||
| 42 | const InnerError = CodeGenError || error{OutOfRegisters}; | |
| 43 | ||
| 44 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { | |
| 45 | return null; | |
| 46 | } | |
| 47 | ||
| 48 | gpa: Allocator, | |
| 49 | pt: Zcu.PerThread, | |
| 50 | air: Air, | |
| 51 | liveness: Air.Liveness, | |
| 52 | bin_file: *link.File, | |
| 53 | target: *const std.Target, | |
| 54 | func_index: InternPool.Index, | |
| 55 | err_msg: ?*ErrorMsg, | |
| 56 | args: []MCValue, | |
| 57 | ret_mcv: MCValue, | |
| 58 | fn_type: Type, | |
| 59 | arg_index: u32, | |
| 60 | src_loc: Zcu.LazySrcLoc, | |
| 61 | stack_align: u32, | |
| 62 | ||
| 63 | /// MIR Instructions | |
| 64 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | |
| 65 | /// MIR extra data | |
| 66 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | |
| 67 | ||
| 68 | /// Byte offset within the source file of the ending curly. | |
| 69 | end_di_line: u32, | |
| 70 | end_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. | |
| 75 | exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .empty, | |
| 76 | ||
| 77 | reused_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. | |
| 83 | dbg_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. | |
| 92 | branch_stack: *std.ArrayList(Branch), | |
| 93 | ||
| 94 | // Key is the block instruction | |
| 95 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, | |
| 96 | ||
| 97 | register_manager: RegisterManager = .{}, | |
| 98 | /// Maps offset to what is stored there. | |
| 99 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .empty, | |
| 100 | /// Tracks the current instruction allocated to the compare flags | |
| 101 | cpsr_flags_inst: ?Air.Inst.Index = null, | |
| 102 | ||
| 103 | /// Offset from the stack base, representing the end of the stack frame. | |
| 104 | max_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`. | |
| 107 | next_stack_offset: u32 = 0, | |
| 108 | ||
| 109 | saved_regs_stack_space: u32 = 0, | |
| 110 | ||
| 111 | /// Debug field, used to find bugs in the compiler. | |
| 112 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | |
| 113 | ||
| 114 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | |
| 115 | ||
| 116 | const 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 | ||
| 173 | const 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 | ||
| 182 | const 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 | ||
| 188 | const 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 | ||
| 197 | const 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 | ||
| 245 | const 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 | ||
| 336 | const Self = @This(); | |
| 337 | ||
| 338 | pub 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 | ||
| 418 | fn 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 | ||
| 428 | fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { | |
| 429 | return try self.addInst(.{ | |
| 430 | .tag = .nop, | |
| 431 | .data = .{ .nop = {} }, | |
| 432 | }); | |
| 433 | } | |
| 434 | ||
| 435 | pub 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 | ||
| 441 | pub 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 | ||
| 454 | fn 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 | ||
| 615 | fn 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. | |
| 887 | fn 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. | |
| 910 | fn finishAirBookkeeping(self: *Self) void { | |
| 911 | if (std.debug.runtime_safety) { | |
| 912 | self.air_bookkeeping += 1; | |
| 913 | } | |
| 914 | } | |
| 915 | ||
| 916 | fn 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 | ||
| 956 | fn 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 | ||
| 961 | fn 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. | |
| 986 | fn 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 | ||
| 1008 | fn 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 | ||
| 1030 | pub 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 | |
| 1050 | fn 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. | |
| 1084 | fn 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 | ||
| 1090 | fn 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 | ||
| 1095 | fn 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 | ||
| 1117 | fn 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 | ||
| 1123 | fn 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 | ||
| 1129 | fn 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 | ||
| 1167 | fn 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 | |
| 1190 | fn 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 | ||
| 1240 | fn 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 | ||
| 1253 | fn 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 | ||
| 1343 | fn 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 | ||
| 1440 | fn 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 | ||
| 1458 | fn 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 | ||
| 1475 | fn 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 | ||
| 1525 | fn 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 | ||
| 1540 | fn 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 | ||
| 1546 | fn 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 | ||
| 1552 | fn 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 | ||
| 1558 | fn 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 | ||
| 1672 | fn 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 | ||
| 1837 | fn 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 | ||
| 1976 | fn 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 | ||
| 1988 | fn 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 | ||
| 1994 | fn 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 | ||
| 2000 | fn 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 | ||
| 2006 | fn 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 | |
| 2024 | fn 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 | ||
| 2090 | fn 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 | |
| 2102 | fn 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 | ||
| 2168 | fn 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 | |
| 2180 | fn 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 | |
| 2187 | fn 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 | ||
| 2193 | fn 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 | ||
| 2199 | fn 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 | ||
| 2207 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | |
| 2208 | _ = inst; | |
| 2209 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); | |
| 2210 | } | |
| 2211 | ||
| 2212 | fn 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 | |
| 2218 | fn 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 | |
| 2243 | fn 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 | |
| 2268 | fn 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 | ||
| 2284 | fn 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 | ||
| 2293 | fn 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 | ||
| 2314 | fn 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 | ||
| 2334 | fn 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 | ||
| 2355 | fn 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 | ||
| 2421 | fn 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 | ||
| 2440 | fn 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 | ||
| 2459 | fn 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 | ||
| 2510 | fn 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 | ||
| 2522 | fn 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 | ||
| 2536 | fn 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 | ||
| 2552 | fn 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 | ||
| 2559 | fn 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 | ||
| 2566 | fn 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 | ||
| 2573 | fn 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 | ||
| 2580 | fn 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 | ||
| 2587 | fn 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 | ||
| 2594 | fn 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 | ||
| 2601 | fn 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 | ||
| 2608 | fn 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 | ||
| 2617 | fn 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 | ||
| 2657 | fn 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 | ||
| 2735 | fn 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 | ||
| 2765 | fn 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 | ||
| 2854 | fn 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 | ||
| 2871 | fn 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 | ||
| 2878 | fn 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 | ||
| 2884 | fn 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 | ||
| 2906 | fn 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 | ||
| 3001 | fn 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 | |
| 3032 | const 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 | |
| 3072 | const 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 | |
| 3086 | const 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. | |
| 3119 | fn 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 | |
| 3255 | fn 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 | |
| 3328 | fn 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 | ||
| 3388 | fn 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 | ||
| 3446 | fn 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 | ||
| 3475 | fn 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 | ||
| 3497 | fn 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 | ||
| 3541 | fn 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 | ||
| 3585 | fn 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 | ||
| 3608 | fn 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 | ||
| 3673 | fn 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 | ||
| 3696 | fn 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 | ||
| 3735 | fn 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 | ||
| 3781 | fn 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 | ||
| 3824 | fn 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 | ||
| 3871 | fn 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 | ||
| 3906 | fn 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 | ||
| 3952 | fn 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 | ||
| 3988 | fn 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 | ||
| 4023 | fn 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 | ||
| 4096 | fn 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 | ||
| 4128 | fn 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 | ||
| 4190 | fn 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 | ||
| 4217 | fn airTrap(self: *Self) !void { | |
| 4218 | _ = try self.addInst(.{ | |
| 4219 | .tag = .undefined_instruction, | |
| 4220 | .data = .{ .nop = {} }, | |
| 4221 | }); | |
| 4222 | return self.finishAirBookkeeping(); | |
| 4223 | } | |
| 4224 | ||
| 4225 | fn airBreakpoint(self: *Self) !void { | |
| 4226 | _ = try self.addInst(.{ | |
| 4227 | .tag = .bkpt, | |
| 4228 | .data = .{ .imm16 = 0 }, | |
| 4229 | }); | |
| 4230 | return self.finishAirBookkeeping(); | |
| 4231 | } | |
| 4232 | ||
| 4233 | fn 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 | ||
| 4238 | fn 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 | ||
| 4243 | fn 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 | ||
| 4391 | fn 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 | ||
| 4424 | fn 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 | ||
| 4469 | fn 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 | ||
| 4480 | fn 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 | ||
| 4565 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { | |
| 4566 | _ = inst; | |
| 4567 | return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); | |
| 4568 | } | |
| 4569 | ||
| 4570 | fn 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 | ||
| 4578 | fn 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 | ||
| 4593 | fn 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 | ||
| 4604 | fn 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. | |
| 4626 | fn 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 | ||
| 4658 | fn 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 | ||
| 4802 | fn 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 | ||
| 4819 | fn 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 | ||
| 4830 | fn 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 | ||
| 4841 | fn 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 | ||
| 4858 | fn 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 | ||
| 4869 | fn 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 | ||
| 4886 | fn 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 | ||
| 4903 | fn 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 | ||
| 4922 | fn 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 | ||
| 4933 | fn 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 | ||
| 4950 | fn 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 | ||
| 4961 | fn 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 | ||
| 4978 | fn 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`. | |
| 4992 | fn jump(self: *Self, inst: Mir.Inst.Index) !void { | |
| 4993 | _ = try self.addInst(.{ | |
| 4994 | .tag = .b, | |
| 4995 | .data = .{ .inst = inst }, | |
| 4996 | }); | |
| 4997 | } | |
| 4998 | ||
| 4999 | fn 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 | ||
| 5005 | fn 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 | ||
| 5037 | fn 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 | ||
| 5160 | fn 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 | ||
| 5168 | fn 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 | ||
| 5174 | fn 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 | ||
| 5199 | fn 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 | ||
| 5209 | fn 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 | ||
| 5323 | fn 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. | |
| 5333 | fn 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 | ||
| 5345 | fn 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 | ||
| 5508 | fn 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 | ||
| 5756 | fn 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 | ||
| 5883 | fn 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 | ||
| 5906 | fn 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 | ||
| 5924 | fn 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 | ||
| 5932 | fn 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 | ||
| 5940 | fn 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 | ||
| 5950 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { | |
| 5951 | _ = inst; | |
| 5952 | return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); | |
| 5953 | } | |
| 5954 | ||
| 5955 | fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { | |
| 5956 | _ = inst; | |
| 5957 | return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); | |
| 5958 | } | |
| 5959 | ||
| 5960 | fn 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 | ||
| 5966 | fn 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 | ||
| 5976 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | |
| 5977 | _ = inst; | |
| 5978 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); | |
| 5979 | } | |
| 5980 | ||
| 5981 | fn airMemmove(self: *Self, inst: Air.Inst.Index) !void { | |
| 5982 | _ = inst; | |
| 5983 | return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch}); | |
| 5984 | } | |
| 5985 | ||
| 5986 | fn 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 | ||
| 5996 | fn 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 | ||
| 6006 | fn 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 | ||
| 6012 | fn 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 | ||
| 6019 | fn airShuffleOne(self: *Self, inst: Air.Inst.Index) !void { | |
| 6020 | _ = inst; | |
| 6021 | return self.fail("TODO implement airShuffleOne for arm", .{}); | |
| 6022 | } | |
| 6023 | ||
| 6024 | fn airShuffleTwo(self: *Self, inst: Air.Inst.Index) !void { | |
| 6025 | _ = inst; | |
| 6026 | return self.fail("TODO implement airShuffleTwo for arm", .{}); | |
| 6027 | } | |
| 6028 | ||
| 6029 | fn 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 | ||
| 6035 | fn 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 | ||
| 6059 | fn 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 | ||
| 6067 | fn 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 | ||
| 6072 | fn 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 | ||
| 6081 | fn 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 | ||
| 6109 | fn 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 | ||
| 6118 | fn 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 | ||
| 6132 | fn 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 | ||
| 6144 | fn 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 | ||
| 6168 | const 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`. | |
| 6181 | fn 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`. | |
| 6303 | fn 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 | ||
| 6312 | fn 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 | ||
| 6320 | fn 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 | ||
| 6327 | fn 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 | ||
| 6334 | fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { | |
| 6335 | return self.air.typeOf(inst, &self.pt.zcu.intern_pool); | |
| 6336 | } | |
| 6337 | ||
| 6338 | fn 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 | ||
| 4 | const Emit = @This(); | |
| 5 | const builtin = @import("builtin"); | |
| 6 | const std = @import("std"); | |
| 7 | const math = std.math; | |
| 8 | const Mir = @import("Mir.zig"); | |
| 9 | const bits = @import("bits.zig"); | |
| 10 | const link = @import("../../link.zig"); | |
| 11 | const Zcu = @import("../../Zcu.zig"); | |
| 12 | const Type = @import("../../Type.zig"); | |
| 13 | const ErrorMsg = Zcu.ErrorMsg; | |
| 14 | const Target = std.Target; | |
| 15 | const assert = std.debug.assert; | |
| 16 | const Instruction = bits.Instruction; | |
| 17 | const Register = bits.Register; | |
| 18 | const log = std.log.scoped(.aarch32_emit); | |
| 19 | const CodeGen = @import("CodeGen.zig"); | |
| 20 | ||
| 21 | mir: Mir, | |
| 22 | bin_file: *link.File, | |
| 23 | debug_output: link.File.DebugInfoOutput, | |
| 24 | target: *const std.Target, | |
| 25 | err_msg: ?*ErrorMsg = null, | |
| 26 | src_loc: Zcu.LazySrcLoc, | |
| 27 | code: *std.ArrayListUnmanaged(u8), | |
| 28 | ||
| 29 | prev_di_line: u32, | |
| 30 | prev_di_column: u32, | |
| 31 | /// Relative to the beginning of `code`. | |
| 32 | prev_di_pc: usize, | |
| 33 | ||
| 34 | /// The amount of stack space consumed by the saved callee-saved | |
| 35 | /// registers in bytes | |
| 36 | saved_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. | |
| 40 | stack_size: u32, | |
| 41 | ||
| 42 | /// The branch type of every branch | |
| 43 | branch_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 | |
| 46 | branch_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 | |
| 52 | code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, | |
| 53 | ||
| 54 | const InnerError = error{ | |
| 55 | OutOfMemory, | |
| 56 | EmitFail, | |
| 57 | }; | |
| 58 | ||
| 59 | const 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 | ||
| 70 | pub 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 | ||
| 151 | pub 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 | ||
| 166 | fn 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 | ||
| 181 | fn 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 | ||
| 217 | fn isBranch(tag: Mir.Inst.Tag) bool { | |
| 218 | return switch (tag) { | |
| 219 | .b => true, | |
| 220 | else => false, | |
| 221 | }; | |
| 222 | } | |
| 223 | ||
| 224 | fn 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 | ||
| 233 | fn 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 | ||
| 342 | fn 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 | ||
| 349 | fn 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 | ||
| 358 | fn 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 | ||
| 400 | fn 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 | ||
| 445 | fn 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 | ||
| 476 | fn 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 | ||
| 489 | fn 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 | ||
| 505 | fn mirUndefinedInstruction(emit: *Emit) !void { | |
| 506 | try emit.writeInstruction(Instruction.undefinedInstruction()); | |
| 507 | } | |
| 508 | ||
| 509 | fn 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 | ||
| 519 | fn 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 | ||
| 531 | fn 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 | ||
| 541 | fn 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 | ||
| 552 | fn 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 | ||
| 563 | fn 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 | ||
| 577 | fn 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 | ||
| 623 | fn 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 | ||
| 637 | fn 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 | ||
| 649 | fn 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 | ||
| 661 | fn 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 | ||
| 673 | fn mirNop(emit: *Emit) !void { | |
| 674 | try emit.writeInstruction(Instruction.nop()); | |
| 675 | } | |
| 676 | ||
| 677 | fn 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 | ||
| 689 | fn 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 | ||
| 700 | fn 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 | ||
| 9 | const Mir = @This(); | |
| 10 | const std = @import("std"); | |
| 11 | const builtin = @import("builtin"); | |
| 12 | const assert = std.debug.assert; | |
| 13 | ||
| 14 | const bits = @import("bits.zig"); | |
| 15 | const Register = bits.Register; | |
| 16 | const InternPool = @import("../../InternPool.zig"); | |
| 17 | const Emit = @import("Emit.zig"); | |
| 18 | const codegen = @import("../../codegen.zig"); | |
| 19 | const link = @import("../../link.zig"); | |
| 20 | const Zcu = @import("../../Zcu.zig"); | |
| 21 | ||
| 22 | max_end_stack: u32, | |
| 23 | saved_regs_stack_space: u32, | |
| 24 | ||
| 25 | instructions: std.MultiArrayList(Inst).Slice, | |
| 26 | /// The meaning of this data is determined by `Inst.Tag` value. | |
| 27 | extra: []const u32, | |
| 28 | ||
| 29 | pub 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 | ||
| 283 | pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { | |
| 284 | mir.instructions.deinit(gpa); | |
| 285 | gpa.free(mir.extra); | |
| 286 | mir.* = undefined; | |
| 287 | } | |
| 288 | ||
| 289 | pub 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. | |
| 324 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const bits = @import("bits.zig"); | |
| 4 | const Register = bits.Register; | |
| 5 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | |
| 6 | const Type = @import("../../Type.zig"); | |
| 7 | const Zcu = @import("../../Zcu.zig"); | |
| 8 | ||
| 9 | pub 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 | ||
| 25 | pub const Context = enum { ret, arg }; | |
| 26 | ||
| 27 | pub 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 | ||
| 122 | const byval_float_count = 4; | |
| 123 | fn 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 | ||
| 167 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | |
| 168 | pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | |
| 169 | ||
| 170 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | |
| 171 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | |
| 172 | ||
| 173 | const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; | |
| 174 | pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); | |
| 175 | ||
| 176 | // Register classes | |
| 177 | const RegisterBitSet = RegisterManager.RegisterBitSet; | |
| 178 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | /// The condition field specifies the flags necessary for an | |
| 6 | /// Instruction to be executed | |
| 7 | pub 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 | ||
| 90 | test "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 | ||
| 101 | test "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 | |
| 107 | pub 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 | ||
| 169 | test "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 | |
| 176 | pub const Psr = enum { | |
| 177 | cpsr, | |
| 178 | spsr, | |
| 179 | }; | |
| 180 | ||
| 181 | /// Represents an instruction in the ARM instruction set architecture | |
| 182 | pub 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 | ||
| 1423 | test "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 | ||
| 1516 | test "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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Type = @import("../../Type.zig"); | |
| 3 | const Zcu = @import("../../Zcu.zig"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | pub const Class = union(enum) { | |
| 7 | memory, | |
| 8 | byval, | |
| 9 | i32_array: u8, | |
| 10 | }; | |
| 11 | ||
| 12 | pub const Context = enum { ret, arg }; | |
| 13 | ||
| 14 | pub 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 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("std"); | |
| 3 | ||
| 4 | const Air = @import("../../Air.zig"); | |
| 5 | const codegen = @import("../../codegen.zig"); | |
| 6 | const InternPool = @import("../../InternPool.zig"); | |
| 7 | const link = @import("../../link.zig"); | |
| 8 | const Zcu = @import("../../Zcu.zig"); | |
| 9 | ||
| 10 | const assert = std.debug.assert; | |
| 11 | const log = std.log.scoped(.codegen); | |
| 12 | ||
| 13 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { | |
| 14 | return null; | |
| 15 | } | |
| 16 | ||
| 17 | pub 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 | ||
| 35 | pub 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 | 436 | fn trackSpill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 437 | 437 | try function.freeValue(inst_tracking.short); |
| 438 | 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 | } |
| 441 | 441 | |
| 442 | 442 | fn verifyMaterialize(inst_tracking: InstTracking, target: InstTracking) void { |
| ... | ... | @@ -500,14 +500,14 @@ const InstTracking = struct { |
| 500 | 500 | else => target.long, |
| 501 | 501 | } else target.long; |
| 502 | 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 | } |
| 505 | 505 | |
| 506 | 506 | fn resurrect(inst_tracking: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void { |
| 507 | 507 | switch (inst_tracking.short) { |
| 508 | 508 | .dead => |die_generation| if (die_generation >= scope_generation) { |
| 509 | 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 | 512 | else => {}, |
| 513 | 513 | } |
| ... | ... | @@ -517,7 +517,7 @@ const InstTracking = struct { |
| 517 | 517 | if (inst_tracking.short == .dead) return; |
| 518 | 518 | try function.freeValue(inst_tracking.short); |
| 519 | 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 | } |
| 522 | 522 | |
| 523 | 523 | fn reuse( |
| ... | ... | @@ -528,15 +528,15 @@ const InstTracking = struct { |
| 528 | 528 | ) void { |
| 529 | 529 | inst_tracking.short = .{ .dead = function.scope_generation }; |
| 530 | 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 | 532 | 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 | } |
| 535 | 535 | |
| 536 | 536 | fn liveOut(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) void { |
| 537 | 537 | for (inst_tracking.getRegs()) |reg| { |
| 538 | 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 | 540 | continue; |
| 541 | 541 | } |
| 542 | 542 | |
| ... | ... | @@ -563,14 +563,13 @@ const InstTracking = struct { |
| 563 | 563 | // Perform side-effects of freeValue manually. |
| 564 | 564 | function.register_manager.freeReg(reg); |
| 565 | 565 | |
| 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 | } |
| 569 | 569 | |
| 570 | pub fn format(inst_tracking: InstTracking, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 571 | comptime assert(fmt.len == 0); | |
| 572 | if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try bw.print("|{}| ", .{inst_tracking.long}); | |
| 573 | try bw.print("{}", .{inst_tracking.short}); | |
| 570 | pub fn format(inst_tracking: InstTracking, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 571 | if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try writer.print("|{}| ", .{inst_tracking.long}); | |
| 572 | try writer.print("{}", .{inst_tracking.short}); | |
| 574 | 573 | } |
| 575 | 574 | }; |
| 576 | 575 | |
| ... | ... | @@ -934,7 +933,7 @@ const FormatWipMirData = struct { |
| 934 | 933 | func: *Func, |
| 935 | 934 | inst: Mir.Inst.Index, |
| 936 | 935 | }; |
| 937 | fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 936 | fn formatWipMir(data: FormatWipMirData, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 938 | 937 | const pt = data.func.pt; |
| 939 | 938 | const comp = pt.zcu.comp; |
| 940 | 939 | var lower: Lower = .{ |
| ... | ... | @@ -957,11 +956,11 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri |
| 957 | 956 | lower.err_msg.?.deinit(data.func.gpa); |
| 958 | 957 | lower.err_msg = null; |
| 959 | 958 | } |
| 960 | try bw.writeAll(lower.err_msg.?.msg); | |
| 959 | try writer.writeAll(lower.err_msg.?.msg); | |
| 961 | 960 | return; |
| 962 | 961 | }, |
| 963 | 962 | error.OutOfMemory, error.InvalidInstruction => |e| { |
| 964 | try bw.writeAll(switch (e) { | |
| 963 | try writer.writeAll(switch (e) { | |
| 965 | 964 | error.OutOfMemory => "Out of memory", |
| 966 | 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 | 968 | }, |
| 970 | 969 | else => |e| return e, |
| 971 | 970 | }).insts) |lowered_inst| { |
| 972 | if (!first) try bw.writeAll("\ndebug(wip_mir): "); | |
| 973 | try bw.print(" | {}", .{lowered_inst}); | |
| 971 | if (!first) try writer.writeAll("\ndebug(wip_mir): "); | |
| 972 | try writer.print(" | {}", .{lowered_inst}); | |
| 974 | 973 | first = false; |
| 975 | 974 | } |
| 976 | 975 | } |
| 977 | fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { | |
| 976 | fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(FormatWipMirData, formatWipMir) { | |
| 978 | 977 | return .{ .data = .{ .func = func, .inst = inst } }; |
| 979 | 978 | } |
| 980 | 979 | |
| ... | ... | @@ -982,10 +981,10 @@ const FormatNavData = struct { |
| 982 | 981 | ip: *const InternPool, |
| 983 | 982 | nav_index: InternPool.Nav.Index, |
| 984 | 983 | }; |
| 985 | fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 986 | try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); | |
| 984 | fn formatNav(data: FormatNavData, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 985 | try writer.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); | |
| 987 | 986 | } |
| 988 | fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) { | |
| 987 | fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(FormatNavData, formatNav) { | |
| 989 | 988 | return .{ .data = .{ |
| 990 | 989 | .ip = ip, |
| 991 | 990 | .nav_index = nav_index, |
| ... | ... | @@ -996,27 +995,25 @@ const FormatAirData = struct { |
| 996 | 995 | func: *Func, |
| 997 | 996 | inst: Air.Inst.Index, |
| 998 | 997 | }; |
| 999 | fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 1000 | comptime assert(fmt.len == 0); | |
| 1001 | // not acceptable implementation: | |
| 1002 | // data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness); | |
| 998 | fn formatAir(data: FormatAirData, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 999 | // Not acceptable implementation because it ignores `writer`: | |
| 1000 | //data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness); | |
| 1003 | 1001 | _ = data; |
| 1004 | _ = w; | |
| 1005 | @panic("TODO: unimplemented"); | |
| 1002 | _ = writer; | |
| 1003 | @panic("unimplemented"); | |
| 1006 | 1004 | } |
| 1007 | fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { | |
| 1005 | fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) { | |
| 1008 | 1006 | return .{ .data = .{ .func = func, .inst = inst } }; |
| 1009 | 1007 | } |
| 1010 | 1008 | |
| 1011 | 1009 | const FormatTrackingData = struct { |
| 1012 | 1010 | func: *Func, |
| 1013 | 1011 | }; |
| 1014 | fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 1015 | comptime assert(fmt.len == 0); | |
| 1012 | fn formatTracking(data: FormatTrackingData, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 1016 | 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 | } |
| 1019 | fn fmtTracking(func: *Func) std.fmt.Formatter(formatTracking) { | |
| 1016 | fn fmtTracking(func: *Func) std.fmt.Formatter(FormatTrackingData, formatTracking) { | |
| 1020 | 1017 | return .{ .data = .{ .func = func } }; |
| 1021 | 1018 | } |
| 1022 | 1019 | |
| ... | ... | @@ -1826,7 +1823,7 @@ fn computeFrameLayout(func: *Func) !FrameLayout { |
| 1826 | 1823 | total_alloc_size + 64 + args_frame_size + spill_frame_size + call_frame_size, |
| 1827 | 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}); | |
| 1830 | 1827 | |
| 1831 | 1828 | // store the ra at total_size - 8, so it's the very first thing in the stack |
| 1832 | 1829 | // relative to the fp |
src/arch/riscv64/Mir.zig+2-3| ... | ... | @@ -92,9 +92,8 @@ pub const Inst = struct { |
| 92 | 92 | }, |
| 93 | 93 | }; |
| 94 | 94 | |
| 95 | pub fn format(inst: Inst, bw: *std.io.Writer, comptime fmt: []const u8) !void { | |
| 96 | assert(fmt.len == 0); | |
| 97 | try bw.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) }); | |
| 95 | pub fn format(inst: Inst, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 96 | try writer.print("Tag: {s}, Data: {s}", .{ @tagName(inst.tag), @tagName(inst.data) }); | |
| 98 | 97 | } |
| 99 | 98 | }; |
| 100 | 99 |
src/arch/riscv64/bits.zig-9| ... | ... | @@ -256,15 +256,6 @@ pub const FrameIndex = enum(u32) { |
| 256 | 256 | pub fn isNamed(fi: FrameIndex) bool { |
| 257 | 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 | }; |
| 269 | 260 | |
| 270 | 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 | 723 | |
| 724 | 724 | if (std.debug.runtime_safety) { |
| 725 | 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 | 18 | const link = @import("../../link.zig"); |
| 19 | 19 | const Air = @import("../../Air.zig"); |
| 20 | 20 | const Mir = @import("Mir.zig"); |
| 21 | const abi = @import("abi.zig"); | |
| 21 | const abi = @import("../../codegen/wasm/abi.zig"); | |
| 22 | 22 | const Alignment = InternPool.Alignment; |
| 23 | 23 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 24 | 24 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| ... | ... | @@ -1960,7 +1960,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1960 | 1960 | .wasm_memory_size => cg.airWasmMemorySize(inst), |
| 1961 | 1961 | .wasm_memory_grow => cg.airWasmMemoryGrow(inst), |
| 1962 | 1962 | |
| 1963 | .memcpy => cg.airMemcpy(inst), | |
| 1963 | .memcpy, .memmove => cg.airMemcpy(inst), | |
| 1964 | 1964 | |
| 1965 | 1965 | .ret_addr => cg.airRetAddr(inst), |
| 1966 | 1966 | .tag_name => cg.airTagName(inst), |
| ... | ... | @@ -1984,7 +1984,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1984 | 1984 | .c_va_copy, |
| 1985 | 1985 | .c_va_end, |
| 1986 | 1986 | .c_va_start, |
| 1987 | .memmove, | |
| 1988 | 1987 | => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 1989 | 1988 | |
| 1990 | 1989 | .atomic_load => cg.airAtomicLoad(inst), |
| ... | ... | @@ -2047,7 +2046,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 2047 | 2046 | try cg.genInst(inst); |
| 2048 | 2047 | |
| 2049 | 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 | 2050 | inst, |
| 2052 | 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 | 2404 | try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) }); |
| 2406 | 2405 | }, |
| 2407 | 2406 | else => if (abi_size > 8) { |
| 2408 | return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{ | |
| 2409 | ty.fmt(pt), | |
| 2410 | abi_size, | |
| 2411 | }); | |
| 2407 | return cg.fail("TODO: `store` for type `{f}` with abisize `{d}`", .{ ty.fmt(pt), abi_size }); | |
| 2412 | 2408 | }, |
| 2413 | 2409 | } |
| 2414 | 2410 | try cg.emitWValue(lhs); |
| ... | ... | @@ -2597,10 +2593,7 @@ fn binOp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WV |
| 2597 | 2593 | if (ty.zigTypeTag(zcu) == .int) { |
| 2598 | 2594 | return cg.binOpBigInt(lhs, rhs, ty, op); |
| 2599 | 2595 | } else { |
| 2600 | return cg.fail( | |
| 2601 | "TODO: Implement binary operation for type: {f}", | |
| 2602 | .{ty.fmt(pt)}, | |
| 2603 | ); | |
| 2596 | return cg.fail("TODO: Implement binary operation for type: {f}", .{ty.fmt(pt)}); | |
| 2604 | 2597 | } |
| 2605 | 2598 | } |
| 2606 | 2599 | |
| ... | ... | @@ -3333,7 +3326,7 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 3333 | 3326 | }, |
| 3334 | 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 | } |
| 3339 | 3332 |
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 | ||
| 7 | const std = @import("std"); | |
| 8 | const Target = std.Target; | |
| 9 | const assert = std.debug.assert; | |
| 10 | ||
| 11 | const Type = @import("../../Type.zig"); | |
| 12 | const 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. | |
| 16 | pub 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. | |
| 23 | pub 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 | ||
| 85 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | // zig fmt: off | |
| 4 | pub 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. | |
| 54 | pub 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 | 525 | }; |
| 526 | 526 | } |
| 527 | 527 | |
| 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 | 529 | switch (mcv) { |
| 530 | .none, .unreach, .dead, .undef => try bw.print("({s})", .{@tagName(mcv)}), | |
| 531 | .immediate => |pl| try bw.print("0x{x}", .{pl}), | |
| 532 | .memory => |pl| try bw.print("[ds:0x{x}]", .{pl}), | |
| 533 | inline .eflags, .register => |pl| try bw.print("{s}", .{@tagName(pl)}), | |
| 534 | .register_pair => |pl| try bw.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), | |
| 535 | .register_triple => |pl| try bw.print("{s}:{s}:{s}", .{ | |
| 530 | .none, .unreach, .dead, .undef => try w.print("({s})", .{@tagName(mcv)}), | |
| 531 | .immediate => |pl| try w.print("0x{x}", .{pl}), | |
| 532 | .memory => |pl| try w.print("[ds:0x{x}]", .{pl}), | |
| 533 | inline .eflags, .register => |pl| try w.print("{s}", .{@tagName(pl)}), | |
| 534 | .register_pair => |pl| try w.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), | |
| 535 | .register_triple => |pl| try w.print("{s}:{s}:{s}", .{ | |
| 536 | 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 | 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 }), | |
| 542 | .register_overflow => |pl| try bw.print("{s}:{s}", .{ | |
| 541 | .register_offset => |pl| try w.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), | |
| 542 | .register_overflow => |pl| try w.print("{s}:{s}", .{ | |
| 543 | 543 | @tagName(pl.eflags), |
| 544 | 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 | 547 | @tagName(pl.info.kind), |
| 548 | 548 | pl.info.scalar, |
| 549 | 549 | @as(u8, if (pl.info.inverted) '!' else ' '), |
| 550 | 550 | @tagName(pl.reg), |
| 551 | 551 | }), |
| 552 | .indirect => |pl| try bw.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), | |
| 553 | .indirect_load_frame => |pl| try bw.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }), | |
| 554 | .load_frame => |pl| try bw.print("[{} + 0x{x}]", .{ pl.index, pl.off }), | |
| 555 | .lea_frame => |pl| try bw.print("{} + 0x{x}", .{ pl.index, pl.off }), | |
| 556 | .load_nav => |pl| try bw.print("[nav:{d}]", .{@intFromEnum(pl)}), | |
| 557 | .lea_nav => |pl| try bw.print("nav:{d}", .{@intFromEnum(pl)}), | |
| 558 | .load_uav => |pl| try bw.print("[uav:{d}]", .{@intFromEnum(pl.val)}), | |
| 559 | .lea_uav => |pl| try bw.print("uav:{d}", .{@intFromEnum(pl.val)}), | |
| 560 | .load_lazy_sym => |pl| try bw.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), | |
| 561 | .lea_lazy_sym => |pl| try bw.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), | |
| 562 | .load_extern_func => |pl| try bw.print("[extern:{d}]", .{@intFromEnum(pl)}), | |
| 563 | .lea_extern_func => |pl| try bw.print("extern:{d}", .{@intFromEnum(pl)}), | |
| 564 | .elementwise_args => |pl| try bw.print("elementwise:{d}:[{} + 0x{x}]", .{ | |
| 552 | .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), | |
| 553 | .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }), | |
| 554 | .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }), | |
| 555 | .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }), | |
| 556 | .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}), | |
| 557 | .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}), | |
| 558 | .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}), | |
| 559 | .lea_uav => |pl| try w.print("uav:{d}", .{@intFromEnum(pl.val)}), | |
| 560 | .load_lazy_sym => |pl| try w.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), | |
| 561 | .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), | |
| 562 | .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}), | |
| 563 | .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}), | |
| 564 | .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{ | |
| 565 | 565 | pl.regs, pl.frame_index, pl.frame_off, |
| 566 | 566 | }), |
| 567 | .reserved_frame => |pl| try bw.print("(dead:{})", .{pl}), | |
| 568 | .air_ref => |pl| try bw.print("(air:0x{x})", .{@intFromEnum(pl)}), | |
| 567 | .reserved_frame => |pl| try w.print("(dead:{})", .{pl}), | |
| 568 | .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}), | |
| 569 | 569 | } |
| 570 | 570 | } |
| 571 | 571 | }; |
| ... | ... | @@ -812,7 +812,7 @@ const InstTracking = struct { |
| 812 | 812 | } |
| 813 | 813 | } |
| 814 | 814 | |
| 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 | 816 | if (!std.meta.eql(tracking.long, tracking.short)) try bw.print("|{f}| ", .{tracking.long}); |
| 817 | 817 | try bw.print("{f}", .{tracking.short}); |
| 818 | 818 | } |
| ... | ... | @@ -1088,10 +1088,10 @@ const FormatNavData = struct { |
| 1088 | 1088 | ip: *const InternPool, |
| 1089 | 1089 | nav_index: InternPool.Nav.Index, |
| 1090 | 1090 | }; |
| 1091 | fn formatNav(data: FormatNavData, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1092 | try bw.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); | |
| 1091 | fn formatNav(data: FormatNavData, w: *Writer) Writer.Error!void { | |
| 1092 | try w.print("{f}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); | |
| 1093 | 1093 | } |
| 1094 | fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) { | |
| 1094 | fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(FormatNavData, formatNav) { | |
| 1095 | 1095 | return .{ .data = .{ |
| 1096 | 1096 | .ip = ip, |
| 1097 | 1097 | .nav_index = nav_index, |
| ... | ... | @@ -1102,15 +1102,14 @@ const FormatAirData = struct { |
| 1102 | 1102 | self: *CodeGen, |
| 1103 | 1103 | inst: Air.Inst.Index, |
| 1104 | 1104 | }; |
| 1105 | fn formatAir(data: FormatAirData, w: *std.io.Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 1106 | comptime assert(fmt.len == 0); | |
| 1107 | // not acceptable implementation: | |
| 1105 | fn formatAir(data: FormatAirData, w: *std.io.Writer) Writer.Error!void { | |
| 1106 | // not acceptable implementation because it ignores `w`: | |
| 1108 | 1107 | //data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness); |
| 1109 | 1108 | _ = data; |
| 1110 | 1109 | _ = w; |
| 1111 | 1110 | @panic("TODO: unimplemented"); |
| 1112 | 1111 | } |
| 1113 | fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { | |
| 1112 | fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) { | |
| 1114 | 1113 | return .{ .data = .{ .self = self, .inst = inst } }; |
| 1115 | 1114 | } |
| 1116 | 1115 | |
| ... | ... | @@ -1118,7 +1117,7 @@ const FormatWipMirData = struct { |
| 1118 | 1117 | self: *CodeGen, |
| 1119 | 1118 | inst: Mir.Inst.Index, |
| 1120 | 1119 | }; |
| 1121 | fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Writer.Error!void { | |
| 1120 | fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void { | |
| 1122 | 1121 | var lower: Lower = .{ |
| 1123 | 1122 | .target = data.self.target, |
| 1124 | 1123 | .allocator = data.self.gpa, |
| ... | ... | @@ -1133,27 +1132,22 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri |
| 1133 | 1132 | lower.err_msg.?.deinit(data.self.gpa); |
| 1134 | 1133 | lower.err_msg = null; |
| 1135 | 1134 | } |
| 1136 | try bw.writeAll(lower.err_msg.?.msg); | |
| 1135 | try w.writeAll(lower.err_msg.?.msg); | |
| 1137 | 1136 | return; |
| 1138 | 1137 | }, |
| 1139 | error.OutOfMemory, error.InvalidInstruction, error.CannotEncode => |e| { | |
| 1140 | try bw.writeAll(switch (e) { | |
| 1141 | error.OutOfMemory => "Out of memory", | |
| 1142 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", | |
| 1143 | error.CannotEncode => "CodeGen failed to encode the instruction.", | |
| 1144 | }); | |
| 1138 | else => |e| { | |
| 1139 | try w.writeAll(@errorName(e)); | |
| 1145 | 1140 | return; |
| 1146 | 1141 | }, |
| 1147 | else => |e| return e, | |
| 1148 | 1142 | }).insts) |lowered_inst| { |
| 1149 | if (!first) try bw.writeAll("\ndebug(wip_mir): "); | |
| 1150 | try bw.print(" | {f}", .{lowered_inst}); | |
| 1143 | if (!first) try w.writeAll("\ndebug(wip_mir): "); | |
| 1144 | try w.print(" | {f}", .{lowered_inst}); | |
| 1151 | 1145 | first = false; |
| 1152 | 1146 | } |
| 1153 | 1147 | if (first) { |
| 1154 | 1148 | const ip = &data.self.pt.zcu.intern_pool; |
| 1155 | 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 | 1151 | switch (mir_inst.ops) { |
| 1158 | 1152 | else => unreachable, |
| 1159 | 1153 | .pseudo_dbg_prologue_end_none, |
| ... | ... | @@ -1165,20 +1159,20 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri |
| 1165 | 1159 | .pseudo_dbg_var_none, |
| 1166 | 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 | 1163 | " {[line]d}, {[column]d}", |
| 1170 | 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 | 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 | 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 | 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 | 1176 | mir_inst.data.i64, |
| 1183 | 1177 | }), |
| 1184 | 1178 | .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => { |
| ... | ... | @@ -1186,40 +1180,39 @@ fn formatWipMir(data: FormatWipMirData, bw: *Writer, comptime _: []const u8) Wri |
| 1186 | 1180 | .base = .{ .reg = mir_inst.data.ro.reg }, |
| 1187 | 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 | 1185 | .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { |
| 1192 | 1186 | const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ |
| 1193 | 1187 | .base = .{ .frame = mir_inst.data.fa.index }, |
| 1194 | 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 | 1192 | .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { |
| 1199 | 1193 | const mem_op: encoder.Instruction.Operand = .{ |
| 1200 | 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 | 1199 | Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt), |
| 1206 | 1200 | }), |
| 1207 | 1201 | } |
| 1208 | 1202 | } |
| 1209 | 1203 | } |
| 1210 | fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { | |
| 1204 | fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(FormatWipMirData, formatWipMir) { | |
| 1211 | 1205 | return .{ .data = .{ .self = self, .inst = inst } }; |
| 1212 | 1206 | } |
| 1213 | 1207 | |
| 1214 | 1208 | const FormatTrackingData = struct { |
| 1215 | 1209 | self: *CodeGen, |
| 1216 | 1210 | }; |
| 1217 | fn formatTracking(data: FormatTrackingData, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 1218 | comptime assert(fmt.len == 0); | |
| 1211 | fn formatTracking(data: FormatTrackingData, w: *Writer) Writer.Error!void { | |
| 1219 | 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 | } |
| 1222 | fn fmtTracking(self: *CodeGen) std.fmt.Formatter(formatTracking) { | |
| 1215 | fn fmtTracking(self: *CodeGen) std.fmt.Formatter(FormatTrackingData, formatTracking) { | |
| 1223 | 1216 | return .{ .data = .{ .self = self } }; |
| 1224 | 1217 | } |
| 1225 | 1218 | |
| ... | ... | @@ -2033,7 +2026,7 @@ fn gen( |
| 2033 | 2026 | .{}, |
| 2034 | 2027 | ); |
| 2035 | 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 | 2031 | else => unreachable, |
| 2039 | 2032 | } |
| ... | ... | @@ -12894,7 +12887,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 12894 | 12887 | .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, |
| 12895 | 12888 | } }, |
| 12896 | 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 | 12891 | @tagName(air_tag), |
| 12899 | 12892 | cg.typeOf(bin_op.lhs).fmt(pt), |
| 12900 | 12893 | ops[0].tracking(cg), |
| ... | ... | @@ -21771,7 +21764,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 21771 | 21764 | .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, |
| 21772 | 21765 | } }, |
| 21773 | 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 | 21768 | @tagName(air_tag), |
| 21776 | 21769 | cg.typeOf(bin_op.lhs).fmt(pt), |
| 21777 | 21770 | ops[0].tracking(cg), |
| ... | ... | @@ -32489,7 +32482,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 32489 | 32482 | .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp3q, ._, ._ }, |
| 32490 | 32483 | } }, |
| 32491 | 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 | 32486 | @tagName(air_tag), |
| 32494 | 32487 | cg.typeOf(bin_op.lhs).fmt(pt), |
| 32495 | 32488 | ops[0].tracking(cg), |
| ... | ... | @@ -59317,7 +59310,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 59317 | 59310 | .{ ._, ._, .@"or", .tmp4q, .tmp5q, ._, ._ }, |
| 59318 | 59311 | } }, |
| 59319 | 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 | 59314 | @tagName(air_tag), |
| 59322 | 59315 | ty_pl.ty.toType().fmt(pt), |
| 59323 | 59316 | ops[0].tracking(cg), |
| ... | ... | @@ -60816,7 +60809,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 60816 | 60809 | .{ ._, ._, .@"or", .dst0d, .tmp0d, ._, ._ }, |
| 60817 | 60810 | } }, |
| 60818 | 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 | 60813 | @tagName(air_tag), |
| 60821 | 60814 | cg.typeOf(bin_op.rhs).fmt(pt), |
| 60822 | 60815 | ops[1].tracking(cg), |
| ... | ... | @@ -64073,7 +64066,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 64073 | 64066 | .{ .@"0:", ._, .mov, .memad(.dst0q, .add_size, -8), .tmp1q, ._, ._ }, |
| 64074 | 64067 | } }, |
| 64075 | 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 | 64070 | @tagName(air_tag), |
| 64078 | 64071 | lhs_ty.fmt(pt), |
| 64079 | 64072 | ops[0].tracking(cg), |
| ... | ... | @@ -79435,7 +79428,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 79435 | 79428 | .@"struct", .@"union" => { |
| 79436 | 79429 | assert(ty.containerLayout(zcu) == .@"packed"); |
| 79437 | 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 | 79432 | @tagName(air_tag), |
| 79440 | 79433 | ty.fmt(pt), |
| 79441 | 79434 | op.tracking(cg), |
| ... | ... | @@ -86528,7 +86521,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 86528 | 86521 | } }, |
| 86529 | 86522 | }), |
| 86530 | 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 | 86525 | @tagName(air_tag), |
| 86533 | 86526 | @tagName(vector_cmp.compareOperator()), |
| 86534 | 86527 | cg.typeOf(vector_cmp.lhs).fmt(pt), |
| ... | ... | @@ -157193,7 +157186,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 157193 | 157186 | } }, |
| 157194 | 157187 | } }, |
| 157195 | 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 | 157190 | @tagName(air_tag), |
| 157198 | 157191 | @tagName(reduce.operation), |
| 157199 | 157192 | cg.typeOf(reduce.operand).fmt(pt), |
| ... | ... | @@ -157204,7 +157197,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 157204 | 157197 | switch (reduce.operation) { |
| 157205 | 157198 | .And, .Or, .Xor, .Min, .Max => {}, |
| 157206 | 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 | 157201 | @tagName(air_tag), |
| 157209 | 157202 | @tagName(reduce.operation), |
| 157210 | 157203 | res_ty.fmt(pt), |
| ... | ... | @@ -164487,7 +164480,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 164487 | 164480 | } }, |
| 164488 | 164481 | } }, |
| 164489 | 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 | 164484 | @tagName(air_tag), |
| 164492 | 164485 | @tagName(reduce.operation), |
| 164493 | 164486 | cg.typeOf(reduce.operand).fmt(pt), |
| ... | ... | @@ -166284,7 +166277,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 166284 | 166277 | .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, |
| 166285 | 166278 | } }, |
| 166286 | 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 | 166281 | @tagName(air_tag), |
| 166289 | 166282 | ty_op.ty.toType().fmt(pt), |
| 166290 | 166283 | ops[0].tracking(cg), |
| ... | ... | @@ -166300,7 +166293,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 166300 | 166293 | const bin_op = air_datas[@intFromEnum(inst)].bin_op; |
| 166301 | 166294 | var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }) ++ .{undefined}; |
| 166302 | 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 | 166297 | @tagName(air_tag), |
| 166305 | 166298 | cg.typeOf(bin_op.lhs).fmt(pt), |
| 166306 | 166299 | cg.typeOf(bin_op.rhs).fmt(pt), |
| ... | ... | @@ -166340,7 +166333,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 166340 | 166333 | } }, |
| 166341 | 166334 | }}, |
| 166342 | 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 | 166337 | @tagName(air_tag), |
| 166345 | 166338 | cg.typeOf(bin_op.lhs).fmt(pt), |
| 166346 | 166339 | cg.typeOf(bin_op.rhs).fmt(pt), |
| ... | ... | @@ -181509,7 +181502,7 @@ fn genSetReg( |
| 181509 | 181502 | assert(!ty.optionalReprIsPayload(zcu)); |
| 181510 | 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 | 181507 | const first_size: u31 = @intCast(first_ty.abiSize(zcu)); |
| 181515 | 181508 | const frame_size = std.math.ceilPowerOfTwoAssert(u32, abi_size); |
| ... | ... | @@ -186937,7 +186930,7 @@ const Temp = struct { |
| 186937 | 186930 | assert(src_regs.len == std.math.divCeil(u16, int_info.bits, 64) catch unreachable); |
| 186938 | 186931 | break :part_ty .u64; |
| 186939 | 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 | 186934 | .ptr_type => |ptr_info| { |
| 186942 | 186935 | assert(ptr_info.flags.size == .slice); |
| 186943 | 186936 | assert(src_regs.len == 2); |
| ... | ... | @@ -186948,7 +186941,7 @@ const Temp = struct { |
| 186948 | 186941 | break :part_ty try cg.pt.intType(.unsigned, @as(u16, 8) * @min(src_abi_size, 8)); |
| 186949 | 186942 | }, |
| 186950 | 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 | 186945 | .ptr_type => |ptr_info| { |
| 186953 | 186946 | assert(ptr_info.flags.size == .slice); |
| 186954 | 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 | 707 | const comp = emit.bin_file.comp; |
| 708 | 708 | const gpa = comp.gpa; |
| 709 | 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 | 718 | const end_offset: u32 = @intCast(emit.code.items.len); |
| 712 | 719 | for (reloc_info) |reloc| switch (reloc.target.type) { |
| 713 | 720 | .inst => { |
src/arch/x86_64/Encoding.zig+31-25| ... | ... | @@ -159,14 +159,12 @@ pub fn modRmExt(encoding: Encoding) u3 { |
| 159 | 159 | }; |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void { | |
| 163 | comptime assert(fmt.len == 0); | |
| 164 | ||
| 162 | pub fn format(encoding: Encoding, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 165 | 163 | var opc = encoding.opcode(); |
| 166 | 164 | if (encoding.data.mode.isVex()) { |
| 167 | try bw.writeAll("VEX."); | |
| 165 | try writer.writeAll("VEX."); | |
| 168 | 166 | |
| 169 | try bw.writeAll(switch (encoding.data.mode) { | |
| 167 | try writer.writeAll(switch (encoding.data.mode) { | |
| 170 | 168 | .vex_128_w0, .vex_128_w1, .vex_128_wig => "128", |
| 171 | 169 | .vex_256_w0, .vex_256_w1, .vex_256_wig => "256", |
| 172 | 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 | 175 | switch (opc[0]) { |
| 178 | 176 | else => {}, |
| 179 | 177 | 0x66, 0xf3, 0xf2 => { |
| 180 | try bw.print(".{X:0>2}", .{opc[0]}); | |
| 178 | try writer.print(".{X:0>2}", .{opc[0]}); | |
| 181 | 179 | opc = opc[1..]; |
| 182 | 180 | }, |
| 183 | 181 | } |
| 184 | 182 | |
| 185 | try bw.print(".{X}", .{opc[0 .. opc.len - 1]}); | |
| 183 | try writer.print(".{X}", .{opc[0 .. opc.len - 1]}); | |
| 186 | 184 | opc = opc[opc.len - 1 ..]; |
| 187 | 185 | |
| 188 | try bw.writeAll(".W"); | |
| 189 | try bw.writeAll(switch (encoding.data.mode) { | |
| 186 | try writer.writeAll(".W"); | |
| 187 | try writer.writeAll(switch (encoding.data.mode) { | |
| 190 | 188 | .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0", |
| 191 | 189 | .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1", |
| 192 | 190 | .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG", |
| 193 | 191 | else => unreachable, |
| 194 | 192 | }); |
| 195 | 193 | |
| 196 | try bw.writeByte(' '); | |
| 197 | } else if (encoding.data.mode.isLong()) try bw.writeAll("REX.W + "); | |
| 198 | for (opc) |byte| try bw.print("{x:0>2} ", .{byte}); | |
| 194 | try writer.writeByte(' '); | |
| 195 | } else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + "); | |
| 196 | for (opc) |byte| try writer.print("{x:0>2} ", .{byte}); | |
| 199 | 197 | |
| 200 | 198 | switch (encoding.data.op_en) { |
| 201 | 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 | 210 | .r64 => "rd", |
| 213 | 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()}), | |
| 218 | .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try bw.writeAll("/r "), | |
| 215 | .ia, .m, .mi, .m1, .mc, .vm, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}), | |
| 216 | .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "), | |
| 219 | 217 | } |
| 220 | 218 | |
| 221 | 219 | switch (encoding.data.op_en) { |
| ... | ... | @@ -244,24 +242,24 @@ pub fn format(encoding: Encoding, bw: *Writer, comptime fmt: []const u8) !void { |
| 244 | 242 | .rel32 => "cd", |
| 245 | 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 | 248 | .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {}, |
| 251 | 249 | } |
| 252 | 250 | |
| 253 | try bw.print("{s} ", .{@tagName(encoding.mnemonic)}); | |
| 251 | try writer.print("{s} ", .{@tagName(encoding.mnemonic)}); | |
| 254 | 252 | |
| 255 | 253 | for (encoding.data.ops) |op| switch (op) { |
| 256 | 254 | .none => break, |
| 257 | else => try bw.print("{s} ", .{@tagName(op)}), | |
| 255 | else => try writer.print("{s} ", .{@tagName(op)}), | |
| 258 | 256 | }; |
| 259 | 257 | |
| 260 | 258 | const op_en = switch (encoding.data.op_en) { |
| 261 | 259 | .zi => .i, |
| 262 | 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 | } |
| 266 | 264 | |
| 267 | 265 | pub const Mnemonic = enum { |
| ... | ... | @@ -1016,13 +1014,21 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op |
| 1016 | 1014 | }; |
| 1017 | 1015 | @memcpy(inst.ops[0..ops.len], ops); |
| 1018 | 1016 | |
| 1019 | var buf: [15]u8 = undefined; | |
| 1020 | var bw: Writer = .fixed(&buf); | |
| 1021 | inst.encode(&bw, .{ | |
| 1017 | // By using a buffer with maximum length of encoded instruction, we can use | |
| 1018 | // the `end` field of the Writer for the count. | |
| 1019 | var buf: [16]u8 = undefined; | |
| 1020 | var trash: std.io.Writer.Discarding = .init(&buf); | |
| 1021 | inst.encode(&trash.writer, .{ | |
| 1022 | 1022 | .allow_frame_locs = true, |
| 1023 | 1023 | .allow_symbols = true, |
| 1024 | }) catch unreachable; | |
| 1025 | return @intCast(bw.end); | |
| 1024 | }) catch { | |
| 1025 | // Since the function signature for encode() does not mention under what | |
| 1026 | // conditions it can fail, I have changed `unreachable` to `@panic` here. | |
| 1027 | // This is a TODO item since it indicates this function | |
| 1028 | // (`estimateInstructionLength`) has the wrong function signature. | |
| 1029 | @panic("unexpected failure to encode"); | |
| 1030 | }; | |
| 1031 | return trash.writer.end; | |
| 1026 | 1032 | } |
| 1027 | 1033 | |
| 1028 | 1034 | const mnemonic_to_encodings_map = init: { |
src/arch/x86_64/bits.zig+4-19| ... | ... | @@ -729,15 +729,6 @@ pub const FrameIndex = enum(u32) { |
| 729 | 729 | pub fn isNamed(fi: FrameIndex) bool { |
| 730 | 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 | }; |
| 742 | 733 | |
| 743 | 734 | pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; |
| ... | ... | @@ -838,14 +829,13 @@ pub const Memory = struct { |
| 838 | 829 | }; |
| 839 | 830 | } |
| 840 | 831 | |
| 841 | pub fn format(s: Size, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 842 | comptime assert(fmt.len == 0); | |
| 832 | pub fn format(s: Size, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 843 | 833 | if (s == .none) return; |
| 844 | try bw.writeAll(@tagName(s)); | |
| 834 | try writer.writeAll(@tagName(s)); | |
| 845 | 835 | switch (s) { |
| 846 | 836 | .none => unreachable, |
| 847 | 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 | 891 | return .{ .signed = x }; |
| 902 | 892 | } |
| 903 | 893 | |
| 904 | pub fn format( | |
| 905 | imm: Immediate, | |
| 906 | comptime _: []const u8, | |
| 907 | _: std.fmt.FormatOptions, | |
| 908 | writer: anytype, | |
| 909 | ) @TypeOf(writer).Error!void { | |
| 894 | pub fn format(imm: Immediate, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 910 | 895 | switch (imm) { |
| 911 | 896 | inline else => |int| try writer.print("{d}", .{int}), |
| 912 | 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 | 353 | return inst; |
| 354 | 354 | } |
| 355 | 355 | |
| 356 | pub fn format(inst: Instruction, w: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { | |
| 357 | comptime assert(unused_format_string.len == 0); | |
| 356 | pub fn format(inst: Instruction, w: *Writer) Writer.Error!void { | |
| 358 | 357 | switch (inst.prefix) { |
| 359 | 358 | .none, .directive => {}, |
| 360 | 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 | 35 | .stage2_arm => .arm_backend, |
| 36 | 36 | .stage2_c => .c_backend, |
| 37 | 37 | .stage2_llvm => .llvm_backend, |
| 38 | .stage2_powerpc => .powerpc_backend, | |
| 38 | .stage2_powerpc => unreachable, | |
| 39 | 39 | .stage2_riscv64 => .riscv64_backend, |
| 40 | 40 | .stage2_sparc64 => .sparc64_backend, |
| 41 | 41 | .stage2_spirv => .spirv_backend, |
| ... | ... | @@ -49,11 +49,11 @@ fn devFeatureForBackend(backend: std.builtin.CompilerBackend) dev.Feature { |
| 49 | 49 | fn importBackend(comptime backend: std.builtin.CompilerBackend) type { |
| 50 | 50 | return switch (backend) { |
| 51 | 51 | .other, .stage1 => unreachable, |
| 52 | .stage2_aarch64 => @import("arch/aarch64/CodeGen.zig"), | |
| 53 | .stage2_arm => @import("arch/arm/CodeGen.zig"), | |
| 52 | .stage2_aarch64 => unreachable, | |
| 53 | .stage2_arm => unreachable, | |
| 54 | 54 | .stage2_c => @import("codegen/c.zig"), |
| 55 | 55 | .stage2_llvm => @import("codegen/llvm.zig"), |
| 56 | .stage2_powerpc => @import("arch/powerpc/CodeGen.zig"), | |
| 56 | .stage2_powerpc => unreachable, | |
| 57 | 57 | .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"), |
| 58 | 58 | .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"), |
| 59 | 59 | .stage2_spirv => @import("codegen/spirv.zig"), |
| ... | ... | @@ -71,14 +71,11 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co |
| 71 | 71 | inline .stage2_llvm, |
| 72 | 72 | .stage2_c, |
| 73 | 73 | .stage2_wasm, |
| 74 | .stage2_arm, | |
| 75 | 74 | .stage2_x86_64, |
| 76 | .stage2_aarch64, | |
| 77 | 75 | .stage2_x86, |
| 78 | 76 | .stage2_riscv64, |
| 79 | 77 | .stage2_sparc64, |
| 80 | 78 | .stage2_spirv, |
| 81 | .stage2_powerpc, | |
| 82 | 79 | => |backend| { |
| 83 | 80 | dev.check(devFeatureForBackend(backend)); |
| 84 | 81 | return importBackend(backend).legalizeFeatures(target); |
| ... | ... | @@ -90,9 +87,6 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co |
| 90 | 87 | /// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a |
| 91 | 88 | /// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`. |
| 92 | 89 | pub 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 | 90 | riscv64: @import("arch/riscv64/Mir.zig"), |
| 97 | 91 | sparc64: @import("arch/sparc64/Mir.zig"), |
| 98 | 92 | x86_64: @import("arch/x86_64/Mir.zig"), |
| ... | ... | @@ -103,7 +97,6 @@ pub const AnyMir = union { |
| 103 | 97 | return switch (backend) { |
| 104 | 98 | .stage2_aarch64 => "aarch64", |
| 105 | 99 | .stage2_arm => "arm", |
| 106 | .stage2_powerpc => "powerpc", | |
| 107 | 100 | .stage2_riscv64 => "riscv64", |
| 108 | 101 | .stage2_sparc64 => "sparc64", |
| 109 | 102 | .stage2_x86_64 => "x86_64", |
| ... | ... | @@ -118,10 +111,7 @@ pub const AnyMir = union { |
| 118 | 111 | const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); |
| 119 | 112 | switch (backend) { |
| 120 | 113 | else => unreachable, |
| 121 | inline .stage2_aarch64, | |
| 122 | .stage2_arm, | |
| 123 | .stage2_powerpc, | |
| 124 | .stage2_riscv64, | |
| 114 | inline .stage2_riscv64, | |
| 125 | 115 | .stage2_sparc64, |
| 126 | 116 | .stage2_x86_64, |
| 127 | 117 | .stage2_wasm, |
| ... | ... | @@ -149,10 +139,7 @@ pub fn generateFunction( |
| 149 | 139 | const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; |
| 150 | 140 | switch (target_util.zigBackend(target, false)) { |
| 151 | 141 | else => unreachable, |
| 152 | inline .stage2_aarch64, | |
| 153 | .stage2_arm, | |
| 154 | .stage2_powerpc, | |
| 155 | .stage2_riscv64, | |
| 142 | inline .stage2_riscv64, | |
| 156 | 143 | .stage2_sparc64, |
| 157 | 144 | .stage2_x86_64, |
| 158 | 145 | .stage2_wasm, |
| ... | ... | @@ -187,10 +174,7 @@ pub fn emitFunction( |
| 187 | 174 | const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; |
| 188 | 175 | switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { |
| 189 | 176 | else => unreachable, |
| 190 | inline .stage2_aarch64, | |
| 191 | .stage2_arm, | |
| 192 | .stage2_powerpc, | |
| 193 | .stage2_riscv64, | |
| 177 | inline .stage2_riscv64, | |
| 194 | 178 | .stage2_sparc64, |
| 195 | 179 | .stage2_x86_64, |
| 196 | 180 | => |backend| { |
| ... | ... | @@ -216,10 +200,7 @@ pub fn generateLazyFunction( |
| 216 | 200 | zcu.getTarget(); |
| 217 | 201 | switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { |
| 218 | 202 | else => unreachable, |
| 219 | inline .stage2_powerpc, | |
| 220 | .stage2_riscv64, | |
| 221 | .stage2_x86_64, | |
| 222 | => |backend| { | |
| 203 | inline .stage2_riscv64, .stage2_x86_64 => |backend| { | |
| 223 | 204 | dev.check(devFeatureForBackend(backend)); |
| 224 | 205 | return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); |
| 225 | 206 | }, |
| ... | ... | @@ -910,7 +891,7 @@ pub fn genNavRef( |
| 910 | 891 | const zcu = pt.zcu; |
| 911 | 892 | const ip = &zcu.intern_pool; |
| 912 | 893 | const nav = ip.getNav(nav_index); |
| 913 | log.debug("genNavRef({})", .{nav.fqn.fmt(ip)}); | |
| 894 | log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)}); | |
| 914 | 895 | |
| 915 | 896 | const lib_name, const linkage, const is_threadlocal = if (nav.getExtern(ip)) |e| |
| 916 | 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const bits = @import("../../arch/aarch64/bits.zig"); | |
| 4 | const Register = bits.Register; | |
| 5 | const Type = @import("../../Type.zig"); | |
| 6 | const Zcu = @import("../../Zcu.zig"); | |
| 7 | ||
| 8 | pub 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. | |
| 17 | pub 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 | ||
| 75 | const sret_float_count = 4; | |
| 76 | fn 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 | ||
| 118 | pub 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 | ||
| 142 | pub const callee_preserved_regs = [_]Register{ | |
| 143 | .x19, .x20, .x21, .x22, .x23, | |
| 144 | .x24, .x25, .x26, .x27, .x28, | |
| 145 | }; | |
| 146 | ||
| 147 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 148 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 149 | ||
| 150 | const allocatable_registers = callee_preserved_regs; |
src/codegen/arm/abi.zig created+163| ... | ... | @@ -0,0 +1,163 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | |
| 4 | const Type = @import("../../Type.zig"); | |
| 5 | const Zcu = @import("../../Zcu.zig"); | |
| 6 | ||
| 7 | pub 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 | ||
| 23 | pub const Context = enum { ret, arg }; | |
| 24 | ||
| 25 | pub 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 | ||
| 120 | const byval_float_count = 4; | |
| 121 | fn 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 | 345 | } else return reserved_idents.has(ident); |
| 346 | 346 | } |
| 347 | 347 | |
| 348 | fn formatIdent( | |
| 349 | ident: []const u8, | |
| 350 | w: *Writer, | |
| 351 | comptime fmt_str: []const u8, | |
| 352 | ) Writer.Error!void { | |
| 353 | const solo = fmt_str.len != 0 and fmt_str[0] == ' '; // space means solo; not part of a bigger ident. | |
| 348 | fn formatIdentSolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 349 | return formatIdentOptions(ident, w, true); | |
| 350 | } | |
| 351 | ||
| 352 | fn formatIdentUnsolo(ident: []const u8, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 353 | return formatIdentOptions(ident, w, false); | |
| 354 | } | |
| 355 | ||
| 356 | fn formatIdentOptions(ident: []const u8, w: *std.io.Writer, solo: bool) std.io.Writer.Error!void { | |
| 354 | 357 | if (solo and isReservedIdent(ident)) { |
| 355 | 358 | try w.writeAll("zig_e_"); |
| 356 | 359 | } |
| ... | ... | @@ -367,29 +370,36 @@ fn formatIdent( |
| 367 | 370 | } |
| 368 | 371 | } |
| 369 | 372 | } |
| 370 | pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { | |
| 373 | ||
| 374 | pub fn fmtIdentSolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentSolo) { | |
| 375 | return .{ .data = ident }; | |
| 376 | } | |
| 377 | ||
| 378 | pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentUnsolo) { | |
| 371 | 379 | return .{ .data = ident }; |
| 372 | 380 | } |
| 373 | 381 | |
| 374 | 382 | const CTypePoolStringFormatData = struct { |
| 375 | 383 | ctype_pool_string: CType.Pool.String, |
| 376 | 384 | ctype_pool: *const CType.Pool, |
| 385 | solo: bool, | |
| 377 | 386 | }; |
| 378 | fn formatCTypePoolString( | |
| 379 | data: CTypePoolStringFormatData, | |
| 380 | w: *Writer, | |
| 381 | comptime fmt_str: []const u8, | |
| 382 | ) Writer.Error!void { | |
| 387 | fn formatCTypePoolString(data: CTypePoolStringFormatData, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 383 | 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 | 390 | else |
| 386 | 391 | try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)}); |
| 387 | 392 | } |
| 388 | 393 | pub fn fmtCTypePoolString( |
| 389 | 394 | ctype_pool_string: CType.Pool.String, |
| 390 | 395 | ctype_pool: *const CType.Pool, |
| 391 | ) std.fmt.Formatter(formatCTypePoolString) { | |
| 392 | return .{ .data = .{ .ctype_pool_string = ctype_pool_string, .ctype_pool = ctype_pool } }; | |
| 396 | solo: bool, | |
| 397 | ) std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString) { | |
| 398 | return .{ .data = .{ | |
| 399 | .ctype_pool_string = ctype_pool_string, | |
| 400 | .ctype_pool = ctype_pool, | |
| 401 | .solo = solo, | |
| 402 | } }; | |
| 393 | 403 | } |
| 394 | 404 | |
| 395 | 405 | // Returns true if `formatIdent` would make any edits to ident. |
| ... | ... | @@ -443,7 +453,7 @@ pub const Function = struct { |
| 443 | 453 | const ty = f.typeOf(ref); |
| 444 | 454 | |
| 445 | 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 | 457 | const decl_c_value = try f.allocLocalValue(.{ |
| 448 | 458 | .ctype = try f.ctypeFromType(ty, .complete), |
| 449 | 459 | .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)), |
| ... | ... | @@ -599,8 +609,12 @@ pub const Function = struct { |
| 599 | 609 | return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); |
| 600 | 610 | } |
| 601 | 611 | |
| 602 | fn fmtIntLiteral(f: *Function, val: Value) !std.fmt.Formatter(formatIntLiteral) { | |
| 603 | return f.object.dg.fmtIntLiteral(val, .Other); | |
| 612 | fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { | |
| 613 | return f.object.dg.fmtIntLiteralDec(val, .Other); | |
| 614 | } | |
| 615 | ||
| 616 | fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { | |
| 617 | return f.object.dg.fmtIntLiteralHex(val, .Other); | |
| 604 | 618 | } |
| 605 | 619 | |
| 606 | 620 | fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 { |
| ... | ... | @@ -619,14 +633,14 @@ pub const Function = struct { |
| 619 | 633 | .tag_name, |
| 620 | 634 | => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{ |
| 621 | 635 | @tagName(key), |
| 622 | fmtIdent(ip.loadEnumType(enum_ty).name.toSlice(ip)), | |
| 636 | fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)), | |
| 623 | 637 | @intFromEnum(enum_ty), |
| 624 | 638 | }), |
| 625 | 639 | .never_tail, |
| 626 | 640 | .never_inline, |
| 627 | 641 | => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{ |
| 628 | 642 | @tagName(key), |
| 629 | fmtIdent(ip.getNav(owner_nav).name.toSlice(ip)), | |
| 643 | fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)), | |
| 630 | 644 | @intFromEnum(owner_nav), |
| 631 | 645 | }), |
| 632 | 646 | }, |
| ... | ... | @@ -662,7 +676,7 @@ pub const Function = struct { |
| 662 | 676 | }, |
| 663 | 677 | else => {}, |
| 664 | 678 | } |
| 665 | const w = &f.object.code.buffered_writer; | |
| 679 | const w = &f.object.code.writer; | |
| 666 | 680 | const a = try Assignment.start(f, w, ctype); |
| 667 | 681 | try f.writeCValue(w, dst, .Other); |
| 668 | 682 | try a.assign(f, w); |
| ... | ... | @@ -704,7 +718,7 @@ pub const Object = struct { |
| 704 | 718 | const indent_char = ' '; |
| 705 | 719 | |
| 706 | 720 | fn newline(o: *Object) !void { |
| 707 | const w = &o.code.buffered_writer; | |
| 721 | const w = &o.code.writer; | |
| 708 | 722 | try w.writeByte('\n'); |
| 709 | 723 | try w.splatByteAll(indent_char, o.indent_counter); |
| 710 | 724 | } |
| ... | ... | @@ -716,9 +730,9 @@ pub const Object = struct { |
| 716 | 730 | const written = o.code.getWritten(); |
| 717 | 731 | switch (written[written.len - 1]) { |
| 718 | 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 | 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 | 736 | unreachable; |
| 723 | 737 | }, |
| 724 | 738 | } |
| ... | ... | @@ -884,7 +898,7 @@ pub const DeclGen = struct { |
| 884 | 898 | const addr_val = try pt.intValue(.usize, int.addr); |
| 885 | 899 | try w.writeByte('('); |
| 886 | 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 | }, |
| 889 | 903 | |
| 890 | 904 | .nav_ptr => |nav| try dg.renderNav(w, nav, location), |
| ... | ... | @@ -924,7 +938,7 @@ pub const DeclGen = struct { |
| 924 | 938 | const offset_val = try pt.intValue(.usize, byte_offset); |
| 925 | 939 | try w.writeAll("((char *)"); |
| 926 | 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 | 960 | // The pointer already has an appropriate type - just do the arithmetic. |
| 947 | 961 | try w.writeByte('('); |
| 948 | 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 | 964 | } else { |
| 951 | 965 | // We probably have an array pointer `T (*)[n]`. Cast to an element pointer, |
| 952 | 966 | // and *then* apply the index. |
| ... | ... | @@ -954,7 +968,7 @@ pub const DeclGen = struct { |
| 954 | 968 | try dg.renderCType(w, result_ctype); |
| 955 | 969 | try w.writeByte(')'); |
| 956 | 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 | }, |
| 960 | 974 | |
| ... | ... | @@ -969,14 +983,14 @@ pub const DeclGen = struct { |
| 969 | 983 | const offset_val = try pt.intValue(.usize, oac.byte_offset); |
| 970 | 984 | try w.writeAll("((char *)"); |
| 971 | 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 | } |
| 977 | 991 | |
| 978 | 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 | } |
| 981 | 995 | |
| 982 | 996 | fn renderValue( |
| ... | ... | @@ -1040,11 +1054,11 @@ pub const DeclGen = struct { |
| 1040 | 1054 | .empty_enum_value, |
| 1041 | 1055 | => unreachable, // non-runtime values |
| 1042 | 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 | 1058 | .lazy_align, .lazy_size => { |
| 1045 | 1059 | try w.writeAll("(("); |
| 1046 | 1060 | try dg.renderCType(w, ctype); |
| 1047 | try w.print("){fx})", .{try dg.fmtIntLiteral( | |
| 1061 | try w.print("){f})", .{try dg.fmtIntLiteralHex( | |
| 1048 | 1062 | try pt.intValue(.usize, val.toUnsignedInt(zcu)), |
| 1049 | 1063 | .Other, |
| 1050 | 1064 | )}); |
| ... | ... | @@ -1173,7 +1187,7 @@ pub const DeclGen = struct { |
| 1173 | 1187 | try w.writeAll(", "); |
| 1174 | 1188 | empty = false; |
| 1175 | 1189 | } |
| 1176 | try w.print("{fx}", .{try dg.fmtIntLiteral( | |
| 1190 | try w.print("{f}", .{try dg.fmtIntLiteralHex( | |
| 1177 | 1191 | try pt.intValue_big(repr_ty, repr_val_big.toConst()), |
| 1178 | 1192 | location, |
| 1179 | 1193 | )}); |
| ... | ... | @@ -1281,7 +1295,7 @@ pub const DeclGen = struct { |
| 1281 | 1295 | } |
| 1282 | 1296 | const ai = ty.arrayInfo(zcu); |
| 1283 | 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 | 1299 | try literal.start(); |
| 1286 | 1300 | var index: usize = 0; |
| 1287 | 1301 | while (index < ai.len) : (index += 1) { |
| ... | ... | @@ -1562,7 +1576,7 @@ pub const DeclGen = struct { |
| 1562 | 1576 | .payload => { |
| 1563 | 1577 | try w.writeByte('{'); |
| 1564 | 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 | 1580 | try dg.renderValue( |
| 1567 | 1581 | w, |
| 1568 | 1582 | Value.fromInterned(un.val), |
| ... | ... | @@ -1645,15 +1659,15 @@ pub const DeclGen = struct { |
| 1645 | 1659 | .enum_type, |
| 1646 | 1660 | .error_set_type, |
| 1647 | 1661 | .inferred_error_set_type, |
| 1648 | => return w.print("{fx}", .{ | |
| 1649 | try dg.fmtIntLiteral(try pt.undefValue(ty), location), | |
| 1662 | => return w.print("{f}", .{ | |
| 1663 | try dg.fmtIntLiteralHex(try pt.undefValue(ty), location), | |
| 1650 | 1664 | }), |
| 1651 | 1665 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { |
| 1652 | 1666 | .one, .many, .c => { |
| 1653 | 1667 | try w.writeAll("(("); |
| 1654 | 1668 | try dg.renderCType(w, ctype); |
| 1655 | return w.print("){fx})", .{ | |
| 1656 | try dg.fmtIntLiteral(.undef_usize, .Other), | |
| 1669 | return w.print("){f})", .{ | |
| 1670 | try dg.fmtIntLiteralHex(.undef_usize, .Other), | |
| 1657 | 1671 | }); |
| 1658 | 1672 | }, |
| 1659 | 1673 | .slice => { |
| ... | ... | @@ -1666,8 +1680,8 @@ pub const DeclGen = struct { |
| 1666 | 1680 | try w.writeAll("{("); |
| 1667 | 1681 | const ptr_ty = ty.slicePtrFieldType(zcu); |
| 1668 | 1682 | try dg.renderType(w, ptr_ty); |
| 1669 | return w.print("){fx}, {0fx}}}", .{ | |
| 1670 | try dg.fmtIntLiteral(.undef_usize, .Other), | |
| 1683 | return w.print("){f}, {0f}}}", .{ | |
| 1684 | try dg.fmtIntLiteralHex(.undef_usize, .Other), | |
| 1671 | 1685 | }); |
| 1672 | 1686 | }, |
| 1673 | 1687 | }, |
| ... | ... | @@ -1730,8 +1744,8 @@ pub const DeclGen = struct { |
| 1730 | 1744 | } |
| 1731 | 1745 | return w.writeByte('}'); |
| 1732 | 1746 | }, |
| 1733 | .@"packed" => return w.print("{fx}", .{ | |
| 1734 | try dg.fmtIntLiteral(try pt.undefValue(ty), .Other), | |
| 1747 | .@"packed" => return w.print("{f}", .{ | |
| 1748 | try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other), | |
| 1735 | 1749 | }), |
| 1736 | 1750 | } |
| 1737 | 1751 | }, |
| ... | ... | @@ -1800,8 +1814,8 @@ pub const DeclGen = struct { |
| 1800 | 1814 | } |
| 1801 | 1815 | if (has_tag) try w.writeByte('}'); |
| 1802 | 1816 | }, |
| 1803 | .@"packed" => return w.print("{fx}", .{ | |
| 1804 | try dg.fmtIntLiteral(try pt.undefValue(ty), .Other), | |
| 1817 | .@"packed" => return w.print("{f}", .{ | |
| 1818 | try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other), | |
| 1805 | 1819 | }), |
| 1806 | 1820 | } |
| 1807 | 1821 | }, |
| ... | ... | @@ -1840,7 +1854,7 @@ pub const DeclGen = struct { |
| 1840 | 1854 | const ai = ty.arrayInfo(zcu); |
| 1841 | 1855 | if (ai.elem_type.eql(.u8, zcu)) { |
| 1842 | 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 | 1858 | try literal.start(); |
| 1845 | 1859 | var index: u64 = 0; |
| 1846 | 1860 | while (index < c_len) : (index += 1) |
| ... | ... | @@ -1899,7 +1913,7 @@ pub const DeclGen = struct { |
| 1899 | 1913 | kind: CType.Kind, |
| 1900 | 1914 | name: union(enum) { |
| 1901 | 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 | 1917 | @"export": struct { |
| 1904 | 1918 | main_name: InternPool.NullTerminatedString, |
| 1905 | 1919 | extern_name: InternPool.NullTerminatedString, |
| ... | ... | @@ -1943,8 +1957,8 @@ pub const DeclGen = struct { |
| 1943 | 1957 | try w.print("{f}", .{trailing}); |
| 1944 | 1958 | switch (name) { |
| 1945 | 1959 | .nav => |nav| try dg.renderNavName(w, nav), |
| 1946 | .fmt_ctype_pool_string => |fmt| try w.print("{f }", .{fmt}), | |
| 1947 | .@"export" => |@"export"| try w.print("{f }", .{fmtIdent(@"export".extern_name.toSlice(ip))}), | |
| 1960 | .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}), | |
| 1961 | .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}), | |
| 1948 | 1962 | } |
| 1949 | 1963 | |
| 1950 | 1964 | try renderTypeSuffix( |
| ... | ... | @@ -1971,17 +1985,17 @@ pub const DeclGen = struct { |
| 1971 | 1985 | const is_mangled = isMangledIdent(extern_name, true); |
| 1972 | 1986 | const is_export = @"export".extern_name != @"export".main_name; |
| 1973 | 1987 | if (is_mangled and is_export) { |
| 1974 | try w.print(" zig_mangled_export({f }, {fs}, {fs})", .{ | |
| 1975 | fmtIdent(extern_name), | |
| 1988 | try w.print(" zig_mangled_export({f}, {f}, {f})", .{ | |
| 1989 | fmtIdentSolo(extern_name), | |
| 1976 | 1990 | fmtStringLiteral(extern_name, null), |
| 1977 | 1991 | fmtStringLiteral(@"export".main_name.toSlice(ip), null), |
| 1978 | 1992 | }); |
| 1979 | 1993 | } else if (is_mangled) { |
| 1980 | try w.print(" zig_mangled({f }, {fs})", .{ | |
| 1981 | fmtIdent(extern_name), fmtStringLiteral(extern_name, null), | |
| 1994 | try w.print(" zig_mangled({f}, {f})", .{ | |
| 1995 | fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), | |
| 1982 | 1996 | }); |
| 1983 | 1997 | } else if (is_export) { |
| 1984 | try w.print(" zig_export({fs}, {fs})", .{ | |
| 1998 | try w.print(" zig_export({f}, {f})", .{ | |
| 1985 | 1999 | fmtStringLiteral(@"export".main_name.toSlice(ip), null), |
| 1986 | 2000 | fmtStringLiteral(extern_name, null), |
| 1987 | 2001 | }); |
| ... | ... | @@ -2129,7 +2143,7 @@ pub const DeclGen = struct { |
| 2129 | 2143 | } else if (dest_bits > 64 and src_bits <= 64) { |
| 2130 | 2144 | try w.writeAll("zig_make_"); |
| 2131 | 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 | 2147 | if (src_is_ptr) { |
| 2134 | 2148 | try w.writeByte('('); |
| 2135 | 2149 | try dg.renderType(w, src_eff_ty); |
| ... | ... | @@ -2209,7 +2223,7 @@ pub const DeclGen = struct { |
| 2209 | 2223 | .new_local, .local => |i| try w.print("t{d}", .{i}), |
| 2210 | 2224 | .constant => |uav| try renderUavName(w, uav), |
| 2211 | 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 | 2227 | else => unreachable, |
| 2214 | 2228 | } |
| 2215 | 2229 | } |
| ... | ... | @@ -2226,13 +2240,13 @@ pub const DeclGen = struct { |
| 2226 | 2240 | try dg.renderNavName(w, nav); |
| 2227 | 2241 | }, |
| 2228 | 2242 | .undef => |ty| try dg.renderUndefValue(w, ty, .Other), |
| 2229 | .identifier => |ident| try w.print("{f }", .{fmtIdent(ident)}), | |
| 2230 | .payload_identifier => |ident| try w.print("{f }.{f }", .{ | |
| 2231 | fmtIdent("payload"), | |
| 2232 | fmtIdent(ident), | |
| 2243 | .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}), | |
| 2244 | .payload_identifier => |ident| try w.print("{f}.{f}", .{ | |
| 2245 | fmtIdentSolo("payload"), | |
| 2246 | fmtIdentSolo(ident), | |
| 2233 | 2247 | }), |
| 2234 | .ctype_pool_string => |string| try w.print("{f }", .{ | |
| 2235 | fmtCTypePoolString(string, &dg.ctype_pool), | |
| 2248 | .ctype_pool_string => |string| try w.print("{f}", .{ | |
| 2249 | fmtCTypePoolString(string, &dg.ctype_pool, true), | |
| 2236 | 2250 | }), |
| 2237 | 2251 | } |
| 2238 | 2252 | } |
| ... | ... | @@ -2256,10 +2270,10 @@ pub const DeclGen = struct { |
| 2256 | 2270 | }, |
| 2257 | 2271 | .nav_ref => |nav| try dg.renderNavName(w, nav), |
| 2258 | 2272 | .undef => unreachable, |
| 2259 | .identifier => |ident| try w.print("(*{f })", .{fmtIdent(ident)}), | |
| 2260 | .payload_identifier => |ident| try w.print("(*{f }.{f })", .{ | |
| 2261 | fmtIdent("payload"), | |
| 2262 | fmtIdent(ident), | |
| 2273 | .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}), | |
| 2274 | .payload_identifier => |ident| try w.print("(*{f}.{f})", .{ | |
| 2275 | fmtIdentSolo("payload"), | |
| 2276 | fmtIdentSolo(ident), | |
| 2263 | 2277 | }), |
| 2264 | 2278 | } |
| 2265 | 2279 | } |
| ... | ... | @@ -2318,7 +2332,7 @@ pub const DeclGen = struct { |
| 2318 | 2332 | const zcu = dg.pt.zcu; |
| 2319 | 2333 | const ip = &zcu.intern_pool; |
| 2320 | 2334 | const nav = ip.getNav(nav_index); |
| 2321 | const fwd = &dg.fwd_decl.buffered_writer; | |
| 2335 | const fwd = &dg.fwd_decl.writer; | |
| 2322 | 2336 | try fwd.writeAll(switch (flags.linkage) { |
| 2323 | 2337 | .internal => "static ", |
| 2324 | 2338 | .strong, .weak, .link_once => "zig_extern ", |
| ... | ... | @@ -2349,15 +2363,15 @@ pub const DeclGen = struct { |
| 2349 | 2363 | const ip = &zcu.intern_pool; |
| 2350 | 2364 | const nav = ip.getNav(nav_index); |
| 2351 | 2365 | if (nav.getExtern(ip)) |@"extern"| { |
| 2352 | try w.print("{f }", .{ | |
| 2353 | fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), | |
| 2366 | try w.print("{f}", .{ | |
| 2367 | fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), | |
| 2354 | 2368 | }); |
| 2355 | 2369 | } else { |
| 2356 | 2370 | // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), |
| 2357 | 2371 | // expand to 3x the length of its input, but let's cut it off at a much shorter limit. |
| 2358 | 2372 | const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); |
| 2359 | 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 | 2375 | @intFromEnum(nav_index), |
| 2362 | 2376 | }); |
| 2363 | 2377 | } |
| ... | ... | @@ -2406,7 +2420,7 @@ pub const DeclGen = struct { |
| 2406 | 2420 | }; |
| 2407 | 2421 | |
| 2408 | 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 | 2424 | try pt.intValue(if (is_big) .u16 else .u8, int_info.bits), |
| 2411 | 2425 | .FunctionArgument, |
| 2412 | 2426 | )}); |
| ... | ... | @@ -2416,18 +2430,38 @@ pub const DeclGen = struct { |
| 2416 | 2430 | dg: *DeclGen, |
| 2417 | 2431 | val: Value, |
| 2418 | 2432 | loc: ValueRenderLocation, |
| 2419 | ) !std.fmt.Formatter(formatIntLiteral) { | |
| 2433 | base: u8, | |
| 2434 | case: std.fmt.Case, | |
| 2435 | ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) { | |
| 2420 | 2436 | const zcu = dg.pt.zcu; |
| 2421 | 2437 | const kind = loc.toCTypeKind(); |
| 2422 | 2438 | const ty = val.typeOf(zcu); |
| 2423 | return std.fmt.Formatter(formatIntLiteral){ .data = .{ | |
| 2439 | return .{ .data = .{ | |
| 2424 | 2440 | .dg = dg, |
| 2425 | 2441 | .int_info = ty.intInfo(zcu), |
| 2426 | 2442 | .kind = kind, |
| 2427 | 2443 | .ctype = try dg.ctypeFromType(ty, kind), |
| 2428 | 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 | }; |
| 2432 | 2466 | |
| 2433 | 2467 | const CTypeFix = enum { prefix, suffix }; |
| ... | ... | @@ -2437,13 +2471,7 @@ const RenderCTypeTrailing = enum { |
| 2437 | 2471 | no_space, |
| 2438 | 2472 | maybe_space, |
| 2439 | 2473 | |
| 2440 | pub fn format( | |
| 2441 | self: @This(), | |
| 2442 | w: *Writer, | |
| 2443 | comptime fmt: []const u8, | |
| 2444 | ) Writer.Error!void { | |
| 2445 | if (fmt.len != 0) @compileError("invalid format string '" ++ | |
| 2446 | fmt ++ "' for type '" ++ @typeName(@This()) ++ "'"); | |
| 2474 | pub fn format(self: @This(), w: *Writer) Writer.Error!void { | |
| 2447 | 2475 | switch (self) { |
| 2448 | 2476 | .no_space => {}, |
| 2449 | 2477 | .maybe_space => try w.writeByte(' '), |
| ... | ... | @@ -2465,7 +2493,7 @@ fn renderFwdDeclTypeName( |
| 2465 | 2493 | switch (fwd_decl.name) { |
| 2466 | 2494 | .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}), |
| 2467 | 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 | 2497 | @intFromEnum(index), |
| 2470 | 2498 | }), |
| 2471 | 2499 | } |
| ... | ... | @@ -2679,7 +2707,7 @@ fn renderFields( |
| 2679 | 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 | 2711 | try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{}); |
| 2684 | 2712 | try w.writeAll(";\n"); |
| 2685 | 2713 | } |
| ... | ... | @@ -2771,7 +2799,7 @@ pub fn genTypeDecl( |
| 2771 | 2799 | |
| 2772 | 2800 | pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void { |
| 2773 | 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 | } |
| 2777 | 2805 | |
| ... | ... | @@ -2779,7 +2807,7 @@ pub fn genErrDecls(o: *Object) Error!void { |
| 2779 | 2807 | const pt = o.dg.pt; |
| 2780 | 2808 | const zcu = pt.zcu; |
| 2781 | 2809 | const ip = &zcu.intern_pool; |
| 2782 | const w = &o.code.buffered_writer; | |
| 2810 | const w = &o.code.writer; | |
| 2783 | 2811 | |
| 2784 | 2812 | var max_name_len: usize = 0; |
| 2785 | 2813 | // 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 | 2886 | const name = name_nts.toSlice(ip); |
| 2859 | 2887 | if (val > 1) try w.writeAll(", "); |
| 2860 | 2888 | try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{ |
| 2861 | fmtIdent(name), | |
| 2862 | try o.dg.fmtIntLiteral(try pt.intValue(.usize, name.len), .StaticInitializer), | |
| 2889 | fmtIdentUnsolo(name), | |
| 2890 | try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer), | |
| 2863 | 2891 | }); |
| 2864 | 2892 | } |
| 2865 | 2893 | try w.writeAll("};"); |
| ... | ... | @@ -2871,7 +2899,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn |
| 2871 | 2899 | const zcu = pt.zcu; |
| 2872 | 2900 | const ip = &zcu.intern_pool; |
| 2873 | 2901 | const ctype_pool = &o.dg.ctype_pool; |
| 2874 | const w = &o.code.buffered_writer; | |
| 2902 | const w = &o.code.writer; | |
| 2875 | 2903 | const key = lazy_fn.key_ptr.*; |
| 2876 | 2904 | const val = lazy_fn.value_ptr; |
| 2877 | 2905 | switch (key) { |
| ... | ... | @@ -2906,7 +2934,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn |
| 2906 | 2934 | } }); |
| 2907 | 2935 | |
| 2908 | 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 | 2939 | o.indent(); |
| 2912 | 2940 | try o.newline(); |
| ... | ... | @@ -2919,8 +2947,8 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn |
| 2919 | 2947 | try w.writeAll("return ("); |
| 2920 | 2948 | try o.dg.renderType(w, name_slice_ty); |
| 2921 | 2949 | try w.print("){{{f}, {f}}};", .{ |
| 2922 | fmtIdent("name"), | |
| 2923 | try o.dg.fmtIntLiteral(try pt.intValue(.usize, tag_name_len), .Other), | |
| 2950 | fmtIdentUnsolo("name"), | |
| 2951 | try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other), | |
| 2924 | 2952 | }); |
| 2925 | 2953 | try o.newline(); |
| 2926 | 2954 | try o.outdent(); |
| ... | ... | @@ -2939,9 +2967,9 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn |
| 2939 | 2967 | const fn_val = zcu.navValue(fn_nav_index); |
| 2940 | 2968 | const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete); |
| 2941 | 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); | |
| 2943 | 2971 | |
| 2944 | const fwd = &o.dg.fwd_decl.buffered_writer; | |
| 2972 | const fwd = &o.dg.fwd_decl.writer; | |
| 2945 | 2973 | try fwd.print("static zig_{s} ", .{@tagName(key)}); |
| 2946 | 2974 | try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{ |
| 2947 | 2975 | .fmt_ctype_pool_string = fn_name, |
| ... | ... | @@ -3001,20 +3029,20 @@ pub fn generate( |
| 3001 | 3029 | .pass = .{ .nav = func.owner_nav }, |
| 3002 | 3030 | .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, |
| 3003 | 3031 | .expected_block = null, |
| 3004 | .fwd_decl = undefined, | |
| 3032 | .fwd_decl = .init(gpa), | |
| 3005 | 3033 | .ctype_pool = .empty, |
| 3006 | 3034 | .scratch = .empty, |
| 3007 | 3035 | .uavs = .empty, |
| 3008 | 3036 | }, |
| 3009 | .code_header = undefined, | |
| 3010 | .code = undefined, | |
| 3037 | .code_header = .init(gpa), | |
| 3038 | .code = .init(gpa), | |
| 3011 | 3039 | .indent_counter = 0, |
| 3012 | 3040 | }, |
| 3013 | 3041 | .lazy_fns = .empty, |
| 3014 | 3042 | }; |
| 3015 | 3043 | defer { |
| 3016 | function.object.code_header.init(gpa); | |
| 3017 | function.object.code.init(gpa); | |
| 3044 | function.object.code_header.deinit(); | |
| 3045 | function.object.code.deinit(); | |
| 3018 | 3046 | function.object.dg.fwd_decl.deinit(); |
| 3019 | 3047 | function.object.dg.ctype_pool.deinit(gpa); |
| 3020 | 3048 | function.object.dg.scratch.deinit(gpa); |
| ... | ... | @@ -3022,18 +3050,17 @@ pub fn generate( |
| 3022 | 3050 | function.deinit(); |
| 3023 | 3051 | } |
| 3024 | 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); | |
| 3028 | 3053 | |
| 3029 | 3054 | genFunc(&function) catch |err| switch (err) { |
| 3030 | 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 | }; |
| 3033 | 3059 | |
| 3034 | 3060 | var mir: Mir = .{ |
| 3035 | 3061 | .uavs = .empty, |
| 3036 | 3062 | .code = &.{}, |
| 3063 | .code_header = &.{}, | |
| 3037 | 3064 | .fwd_decl = &.{}, |
| 3038 | 3065 | .ctype_pool = .empty, |
| 3039 | 3066 | .lazy_fns = .empty, |
| ... | ... | @@ -3060,7 +3087,7 @@ pub fn genFunc(f: *Function) Error!void { |
| 3060 | 3087 | const nav_val = zcu.navValue(nav_index); |
| 3061 | 3088 | const nav = ip.getNav(nav_index); |
| 3062 | 3089 | |
| 3063 | const fwd = &o.dg.fwd_decl.buffered_writer; | |
| 3090 | const fwd = &o.dg.fwd_decl.writer; | |
| 3064 | 3091 | try fwd.writeAll("static "); |
| 3065 | 3092 | try o.dg.renderFunctionSignature( |
| 3066 | 3093 | fwd, |
| ... | ... | @@ -3071,9 +3098,9 @@ pub fn genFunc(f: *Function) Error!void { |
| 3071 | 3098 | ); |
| 3072 | 3099 | try fwd.writeAll(";\n"); |
| 3073 | 3100 | |
| 3074 | const ch = &o.code_header.buffered_writer; | |
| 3101 | const ch = &o.code_header.writer; | |
| 3075 | 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 | 3104 | try o.dg.renderFunctionSignature( |
| 3078 | 3105 | ch, |
| 3079 | 3106 | nav_val, |
| ... | ... | @@ -3089,7 +3116,7 @@ pub fn genFunc(f: *Function) Error!void { |
| 3089 | 3116 | o.indent(); |
| 3090 | 3117 | try genBodyResolveState(f, undefined, &.{}, main_body, true); |
| 3091 | 3118 | try o.outdent(); |
| 3092 | try o.code.buffered_writer.writeByte('}'); | |
| 3119 | try o.code.writer.writeByte('}'); | |
| 3093 | 3120 | try o.newline(); |
| 3094 | 3121 | if (o.dg.expected_block) |_| |
| 3095 | 3122 | return f.fail("runtime code not allowed in naked function", .{}); |
| ... | ... | @@ -3150,7 +3177,7 @@ pub fn genDecl(o: *Object) Error!void { |
| 3150 | 3177 | .visibility = @"extern".visibility, |
| 3151 | 3178 | }); |
| 3152 | 3179 | |
| 3153 | const fwd = &o.dg.fwd_decl.buffered_writer; | |
| 3180 | const fwd = &o.dg.fwd_decl.writer; | |
| 3154 | 3181 | try fwd.writeAll("zig_extern "); |
| 3155 | 3182 | try o.dg.renderFunctionSignature( |
| 3156 | 3183 | fwd, |
| ... | ... | @@ -3171,10 +3198,10 @@ pub fn genDecl(o: *Object) Error!void { |
| 3171 | 3198 | .linkage = .internal, |
| 3172 | 3199 | .visibility = .default, |
| 3173 | 3200 | }); |
| 3174 | const w = &o.code.buffered_writer; | |
| 3201 | const w = &o.code.writer; | |
| 3175 | 3202 | if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); |
| 3176 | 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 | 3205 | try o.dg.renderTypeAndName( |
| 3179 | 3206 | w, |
| 3180 | 3207 | nav_ty, |
| ... | ... | @@ -3208,14 +3235,14 @@ pub fn genDeclValue( |
| 3208 | 3235 | const zcu = o.dg.pt.zcu; |
| 3209 | 3236 | const ty = val.typeOf(zcu); |
| 3210 | 3237 | |
| 3211 | const fwd = &o.dg.fwd_decl.buffered_writer; | |
| 3238 | const fwd = &o.dg.fwd_decl.writer; | |
| 3212 | 3239 | try fwd.writeAll("static "); |
| 3213 | 3240 | try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete); |
| 3214 | 3241 | try fwd.writeAll(";\n"); |
| 3215 | 3242 | |
| 3216 | const w = &o.code.buffered_writer; | |
| 3243 | const w = &o.code.writer; | |
| 3217 | 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 | 3246 | try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete); |
| 3220 | 3247 | try w.writeAll(" = "); |
| 3221 | 3248 | try o.dg.renderValue(w, val, .StaticInitializer); |
| ... | ... | @@ -3226,7 +3253,7 @@ pub fn genDeclValue( |
| 3226 | 3253 | pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void { |
| 3227 | 3254 | const zcu = dg.pt.zcu; |
| 3228 | 3255 | const ip = &zcu.intern_pool; |
| 3229 | const fwd = &dg.fwd_decl.buffered_writer; | |
| 3256 | const fwd = &dg.fwd_decl.writer; | |
| 3230 | 3257 | |
| 3231 | 3258 | const main_name = export_indices[0].ptr(zcu).opts.name; |
| 3232 | 3259 | try fwd.writeAll("#define "); |
| ... | ... | @@ -3235,7 +3262,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3235 | 3262 | .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)), |
| 3236 | 3263 | } |
| 3237 | 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 | 3266 | try fwd.writeByte('\n'); |
| 3240 | 3267 | |
| 3241 | 3268 | const exported_val = exported.getValue(zcu); |
| ... | ... | @@ -3265,7 +3292,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3265 | 3292 | const @"export" = export_index.ptr(zcu); |
| 3266 | 3293 | try fwd.writeAll("zig_extern "); |
| 3267 | 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 | 3296 | fmtStringLiteral(s, null), |
| 3270 | 3297 | }); |
| 3271 | 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 | 3307 | .complete, |
| 3281 | 3308 | ); |
| 3282 | 3309 | if (is_mangled and is_export) { |
| 3283 | try fwd.print(" zig_mangled_export({f }, {fs}, {fs})", .{ | |
| 3284 | fmtIdent(extern_name), | |
| 3310 | try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{ | |
| 3311 | fmtIdentSolo(extern_name), | |
| 3285 | 3312 | fmtStringLiteral(extern_name, null), |
| 3286 | 3313 | fmtStringLiteral(main_name.toSlice(ip), null), |
| 3287 | 3314 | }); |
| 3288 | 3315 | } else if (is_mangled) { |
| 3289 | try fwd.print(" zig_mangled({f }, {fs})", .{ | |
| 3290 | fmtIdent(extern_name), fmtStringLiteral(extern_name, null), | |
| 3316 | try fwd.print(" zig_mangled({f}, {f})", .{ | |
| 3317 | fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), | |
| 3291 | 3318 | }); |
| 3292 | 3319 | } else if (is_export) { |
| 3293 | try fwd.print(" zig_export({fs}, {fs})", .{ | |
| 3320 | try fwd.print(" zig_export({f}, {f})", .{ | |
| 3294 | 3321 | fmtStringLiteral(main_name.toSlice(ip), null), |
| 3295 | 3322 | fmtStringLiteral(extern_name, null), |
| 3296 | 3323 | }); |
| ... | ... | @@ -3304,7 +3331,7 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const |
| 3304 | 3331 | /// have been added to `free_locals_map`. For a version of this function that restores this state, |
| 3305 | 3332 | /// see `genBodyResolveState`. |
| 3306 | 3333 | fn 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 | 3335 | if (body.len == 0) { |
| 3309 | 3336 | try w.writeAll("{}"); |
| 3310 | 3337 | } else { |
| ... | ... | @@ -3326,7 +3353,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 3326 | 3353 | fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void { |
| 3327 | 3354 | if (body.len == 0) { |
| 3328 | 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 | 3357 | return; |
| 3331 | 3358 | } |
| 3332 | 3359 | |
| ... | ... | @@ -3643,7 +3670,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 3643 | 3670 | .ret => return airRet(f, inst, false), |
| 3644 | 3671 | .ret_safe => return airRet(f, inst, false), // TODO |
| 3645 | 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 | 3674 | .unreach => return airUnreach(&f.object), |
| 3648 | 3675 | |
| 3649 | 3676 | // Instructions which may be `noreturn`. |
| ... | ... | @@ -3687,7 +3714,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ |
| 3687 | 3714 | const operand = try f.resolveInst(ty_op.operand); |
| 3688 | 3715 | try reap(f, inst, &.{ty_op.operand}); |
| 3689 | 3716 | |
| 3690 | const w = &f.object.code.buffered_writer; | |
| 3717 | const w = &f.object.code.writer; | |
| 3691 | 3718 | const local = try f.allocLocal(inst, inst_ty); |
| 3692 | 3719 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 3693 | 3720 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -3713,7 +3740,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3713 | 3740 | const index = try f.resolveInst(bin_op.rhs); |
| 3714 | 3741 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3715 | 3742 | |
| 3716 | const w = &f.object.code.buffered_writer; | |
| 3743 | const w = &f.object.code.writer; | |
| 3717 | 3744 | const local = try f.allocLocal(inst, inst_ty); |
| 3718 | 3745 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 3719 | 3746 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -3740,7 +3767,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3740 | 3767 | const index = try f.resolveInst(bin_op.rhs); |
| 3741 | 3768 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3742 | 3769 | |
| 3743 | const w = &f.object.code.buffered_writer; | |
| 3770 | const w = &f.object.code.writer; | |
| 3744 | 3771 | const local = try f.allocLocal(inst, inst_ty); |
| 3745 | 3772 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 3746 | 3773 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -3775,7 +3802,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3775 | 3802 | const index = try f.resolveInst(bin_op.rhs); |
| 3776 | 3803 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3777 | 3804 | |
| 3778 | const w = &f.object.code.buffered_writer; | |
| 3805 | const w = &f.object.code.writer; | |
| 3779 | 3806 | const local = try f.allocLocal(inst, inst_ty); |
| 3780 | 3807 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 3781 | 3808 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -3803,7 +3830,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3803 | 3830 | const index = try f.resolveInst(bin_op.rhs); |
| 3804 | 3831 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3805 | 3832 | |
| 3806 | const w = &f.object.code.buffered_writer; | |
| 3833 | const w = &f.object.code.writer; | |
| 3807 | 3834 | const local = try f.allocLocal(inst, inst_ty); |
| 3808 | 3835 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 3809 | 3836 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -3832,7 +3859,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3832 | 3859 | const index = try f.resolveInst(bin_op.rhs); |
| 3833 | 3860 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3834 | 3861 | |
| 3835 | const w = &f.object.code.buffered_writer; | |
| 3862 | const w = &f.object.code.writer; | |
| 3836 | 3863 | const local = try f.allocLocal(inst, inst_ty); |
| 3837 | 3864 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 3838 | 3865 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -3895,7 +3922,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3895 | 3922 | .{ .arg_array = i }; |
| 3896 | 3923 | |
| 3897 | 3924 | if (f.liveness.isUnused(inst)) { |
| 3898 | const w = &f.object.code.buffered_writer; | |
| 3925 | const w = &f.object.code.writer; | |
| 3899 | 3926 | try w.writeByte('('); |
| 3900 | 3927 | try f.renderType(w, .void); |
| 3901 | 3928 | try w.writeByte(')'); |
| ... | ... | @@ -3934,7 +3961,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3934 | 3961 | const is_array = lowersToArray(src_ty, pt); |
| 3935 | 3962 | const need_memcpy = !is_aligned or is_array; |
| 3936 | 3963 | |
| 3937 | const w = &f.object.code.buffered_writer; | |
| 3964 | const w = &f.object.code.writer; | |
| 3938 | 3965 | const local = try f.allocLocal(inst, src_ty); |
| 3939 | 3966 | const v = try Vectorize.start(f, inst, w, ptr_ty); |
| 3940 | 3967 | |
| ... | ... | @@ -3979,7 +4006,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3979 | 4006 | try w.writeByte('('); |
| 3980 | 4007 | try f.writeCValueDeref(w, operand); |
| 3981 | 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 | 4010 | if (cant_cast) try w.writeByte(')'); |
| 3984 | 4011 | try f.object.dg.renderBuiltinInfo(w, field_ty, .bits); |
| 3985 | 4012 | try w.writeByte(')'); |
| ... | ... | @@ -4001,7 +4028,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void { |
| 4001 | 4028 | const pt = f.object.dg.pt; |
| 4002 | 4029 | const zcu = pt.zcu; |
| 4003 | 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 | 4032 | const op_inst = un_op.toIndex(); |
| 4006 | 4033 | const op_ty = f.typeOf(un_op); |
| 4007 | 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 | 4067 | try f.writeCValueDeref(w, ret_val) |
| 4041 | 4068 | else |
| 4042 | 4069 | try f.writeCValue(w, ret_val, .Other); |
| 4043 | try w.write(";\n"); | |
| 4070 | try w.writeAll(";\n"); | |
| 4044 | 4071 | if (is_array) { |
| 4045 | 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 | 4093 | |
| 4067 | 4094 | if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand); |
| 4068 | 4095 | |
| 4069 | const w = &f.object.code.buffered_writer; | |
| 4096 | const w = &f.object.code.writer; | |
| 4070 | 4097 | const local = try f.allocLocal(inst, inst_ty); |
| 4071 | 4098 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 4072 | 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 | 4129 | const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits); |
| 4103 | 4130 | if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand); |
| 4104 | 4131 | |
| 4105 | const w = &f.object.code.buffered_writer; | |
| 4132 | const w = &f.object.code.writer; | |
| 4106 | 4133 | const local = try f.allocLocal(inst, inst_ty); |
| 4107 | 4134 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 4108 | 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 | 4156 | try w.writeByte('('); |
| 4130 | 4157 | try f.writeCValue(w, operand, .FunctionArgument); |
| 4131 | 4158 | try v.elem(f, w); |
| 4132 | try w.print(", {fx})", .{ | |
| 4133 | try f.fmtIntLiteral(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)), | |
| 4159 | try w.print(", {f})", .{ | |
| 4160 | try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)), | |
| 4134 | 4161 | }); |
| 4135 | 4162 | }, |
| 4136 | 4163 | .signed => { |
| ... | ... | @@ -4154,9 +4181,9 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4154 | 4181 | try f.writeCValue(w, operand, .FunctionArgument); |
| 4155 | 4182 | try v.elem(f, w); |
| 4156 | 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 | 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 | 4189 | if (need_lo) try w.writeByte(')'); |
| ... | ... | @@ -4180,7 +4207,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { |
| 4180 | 4207 | |
| 4181 | 4208 | const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndefDeep(zcu) else false; |
| 4182 | 4209 | |
| 4183 | const w = &f.object.code.buffered_writer; | |
| 4210 | const w = &f.object.code.writer; | |
| 4184 | 4211 | if (val_is_undef) { |
| 4185 | 4212 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4186 | 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 | 4300 | try w.writeByte('('); |
| 4274 | 4301 | try f.writeCValueDeref(w, ptr_val); |
| 4275 | 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 | 4304 | try f.object.dg.renderTypeForBuiltinFnName(w, host_ty); |
| 4278 | 4305 | try w.writeByte('('); |
| 4279 | 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 | 4323 | try f.writeCValue(w, src_val, .Other); |
| 4297 | 4324 | try v.elem(f, w); |
| 4298 | 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 | 4327 | try a.end(f, w); |
| 4301 | 4328 | try v.end(f, inst, w); |
| 4302 | 4329 | } else { |
| ... | ... | @@ -4335,7 +4362,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: |
| 4335 | 4362 | const operand_ty = f.typeOf(bin_op.lhs); |
| 4336 | 4363 | const scalar_ty = operand_ty.scalarType(zcu); |
| 4337 | 4364 | |
| 4338 | const w = &f.object.code.buffered_writer; | |
| 4365 | const w = &f.object.code.writer; | |
| 4339 | 4366 | const local = try f.allocLocal(inst, inst_ty); |
| 4340 | 4367 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 4341 | 4368 | try f.writeCValueMember(w, local, .{ .field = 1 }); |
| ... | ... | @@ -4374,7 +4401,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4374 | 4401 | |
| 4375 | 4402 | const inst_ty = f.typeOfIndex(inst); |
| 4376 | 4403 | |
| 4377 | const w = &f.object.code.buffered_writer; | |
| 4404 | const w = &f.object.code.writer; | |
| 4378 | 4405 | const local = try f.allocLocal(inst, inst_ty); |
| 4379 | 4406 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 4380 | 4407 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -4411,7 +4438,7 @@ fn airBinOp( |
| 4411 | 4438 | |
| 4412 | 4439 | const inst_ty = f.typeOfIndex(inst); |
| 4413 | 4440 | |
| 4414 | const w = &f.object.code.buffered_writer; | |
| 4441 | const w = &f.object.code.writer; | |
| 4415 | 4442 | const local = try f.allocLocal(inst, inst_ty); |
| 4416 | 4443 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 4417 | 4444 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -4462,7 +4489,7 @@ fn airCmpOp( |
| 4462 | 4489 | |
| 4463 | 4490 | const rhs_ty = f.typeOf(data.rhs); |
| 4464 | 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 | 4493 | const local = try f.allocLocal(inst, inst_ty); |
| 4467 | 4494 | const v = try Vectorize.start(f, inst, w, lhs_ty); |
| 4468 | 4495 | const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); |
| ... | ... | @@ -4515,7 +4542,7 @@ fn airEquality( |
| 4515 | 4542 | const rhs = try f.resolveInst(bin_op.rhs); |
| 4516 | 4543 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4517 | 4544 | |
| 4518 | const w = &f.object.code.buffered_writer; | |
| 4545 | const w = &f.object.code.writer; | |
| 4519 | 4546 | const local = try f.allocLocal(inst, .bool); |
| 4520 | 4547 | const a = try Assignment.start(f, w, .bool); |
| 4521 | 4548 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -4573,12 +4600,12 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4573 | 4600 | const operand = try f.resolveInst(un_op); |
| 4574 | 4601 | try reap(f, inst, &.{un_op}); |
| 4575 | 4602 | |
| 4576 | const w = &f.object.code.buffered_writer; | |
| 4603 | const w = &f.object.code.writer; | |
| 4577 | 4604 | const local = try f.allocLocal(inst, .bool); |
| 4578 | 4605 | try f.writeCValue(w, local, .Other); |
| 4579 | 4606 | try w.writeAll(" = "); |
| 4580 | 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 | 4609 | try f.object.newline(); |
| 4583 | 4610 | return local; |
| 4584 | 4611 | } |
| ... | ... | @@ -4600,7 +4627,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { |
| 4600 | 4627 | const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); |
| 4601 | 4628 | |
| 4602 | 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 | 4631 | const v = try Vectorize.start(f, inst, w, inst_ty); |
| 4605 | 4632 | const a = try Assignment.start(f, w, inst_scalar_ctype); |
| 4606 | 4633 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -4642,7 +4669,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons |
| 4642 | 4669 | const rhs = try f.resolveInst(bin_op.rhs); |
| 4643 | 4670 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4644 | 4671 | |
| 4645 | const w = &f.object.code.buffered_writer; | |
| 4672 | const w = &f.object.code.writer; | |
| 4646 | 4673 | const local = try f.allocLocal(inst, inst_ty); |
| 4647 | 4674 | const v = try Vectorize.start(f, inst, w, inst_ty); |
| 4648 | 4675 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -4682,7 +4709,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4682 | 4709 | const inst_ty = f.typeOfIndex(inst); |
| 4683 | 4710 | const ptr_ty = inst_ty.slicePtrFieldType(zcu); |
| 4684 | 4711 | |
| 4685 | const w = &f.object.code.buffered_writer; | |
| 4712 | const w = &f.object.code.writer; | |
| 4686 | 4713 | const local = try f.allocLocal(inst, inst_ty); |
| 4687 | 4714 | { |
| 4688 | 4715 | const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete)); |
| ... | ... | @@ -4713,7 +4740,7 @@ fn airCall( |
| 4713 | 4740 | if (f.object.dg.is_naked_fn) return .none; |
| 4714 | 4741 | |
| 4715 | 4742 | const gpa = f.object.dg.gpa; |
| 4716 | const w = &f.object.code.buffered_writer; | |
| 4743 | const w = &f.object.code.writer; | |
| 4717 | 4744 | |
| 4718 | 4745 | const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 4719 | 4746 | const extra = f.air.extraData(Air.Call, pl_op.payload); |
| ... | ... | @@ -4864,7 +4891,7 @@ fn airCall( |
| 4864 | 4891 | |
| 4865 | 4892 | fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4866 | 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 | 4895 | // TODO re-evaluate whether to emit these or not. If we naively emit |
| 4869 | 4896 | // these directives, the output file will report bogus line numbers because |
| 4870 | 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 | 4907 | } |
| 4881 | 4908 | |
| 4882 | 4909 | fn 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 | 4911 | try f.object.newline(); |
| 4885 | 4912 | return .none; |
| 4886 | 4913 | } |
| ... | ... | @@ -4892,7 +4919,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4892 | 4919 | const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4893 | 4920 | const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload); |
| 4894 | 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 | 4923 | try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)}); |
| 4897 | 4924 | try f.object.newline(); |
| 4898 | 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 | 4935 | if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); |
| 4909 | 4936 | |
| 4910 | 4937 | try reap(f, inst, &.{pl_op.operand}); |
| 4911 | const w = &f.object.code.buffered_writer; | |
| 4938 | const w = &f.object.code.writer; | |
| 4912 | 4939 | try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) }); |
| 4913 | 4940 | try f.object.newline(); |
| 4914 | 4941 | return .none; |
| ... | ... | @@ -4927,7 +4954,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 4927 | 4954 | |
| 4928 | 4955 | const block_id = f.next_block_index; |
| 4929 | 4956 | f.next_block_index += 1; |
| 4930 | const w = &f.object.code.buffered_writer; | |
| 4957 | const w = &f.object.code.writer; | |
| 4931 | 4958 | |
| 4932 | 4959 | const inst_ty = f.typeOfIndex(inst); |
| 4933 | 4960 | const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst)) |
| ... | ... | @@ -4996,7 +5023,7 @@ fn lowerTry( |
| 4996 | 5023 | const err_union = try f.resolveInst(operand); |
| 4997 | 5024 | const inst_ty = f.typeOfIndex(inst); |
| 4998 | 5025 | const liveness_condbr = f.liveness.getCondBr(inst); |
| 4999 | const w = &f.object.code.buffered_writer; | |
| 5026 | const w = &f.object.code.writer; | |
| 5000 | 5027 | const payload_ty = err_union_ty.errorUnionPayload(zcu); |
| 5001 | 5028 | const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu); |
| 5002 | 5029 | |
| ... | ... | @@ -5058,7 +5085,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void { |
| 5058 | 5085 | const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br; |
| 5059 | 5086 | const block = f.blocks.get(branch.block_inst).?; |
| 5060 | 5087 | const result = block.result; |
| 5061 | const w = &f.object.code.buffered_writer; | |
| 5088 | const w = &f.object.code.writer; | |
| 5062 | 5089 | |
| 5063 | 5090 | if (f.object.dg.is_naked_fn) { |
| 5064 | 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 | 5111 | |
| 5085 | 5112 | fn airRepeat(f: *Function, inst: Air.Inst.Index) !void { |
| 5086 | 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 | } |
| 5089 | 5116 | |
| 5090 | 5117 | fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void { |
| 5091 | 5118 | const pt = f.object.dg.pt; |
| 5092 | 5119 | const zcu = pt.zcu; |
| 5093 | 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; | |
| 5095 | 5122 | |
| 5096 | 5123 | if (try f.air.value(br.operand, pt)) |cond_val| { |
| 5097 | 5124 | // 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 | 5172 | const zcu = pt.zcu; |
| 5146 | 5173 | const target = &f.object.dg.mod.resolved_target.result; |
| 5147 | 5174 | const ctype_pool = &f.object.dg.ctype_pool; |
| 5148 | const w = &f.object.code.buffered_writer; | |
| 5175 | const w = &f.object.code.writer; | |
| 5149 | 5176 | |
| 5150 | 5177 | if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) { |
| 5151 | 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 | 5196 | const operand_lval = if (operand == .constant) blk: { |
| 5170 | 5197 | const operand_local = try f.allocLocal(null, operand_ty); |
| 5171 | 5198 | try f.writeCValue(w, operand_local, .Other); |
| 5172 | if (operand_ty.isAbiInt(zcu)) { | |
| 5173 | try w.writeAll(" = "); | |
| 5174 | } else { | |
| 5175 | try w.writeAll(" = ("); | |
| 5176 | try f.renderType(w, operand_ty); | |
| 5177 | try w.writeByte(')'); | |
| 5178 | } | |
| 5199 | try w.writeAll(" = "); | |
| 5179 | 5200 | try f.writeCValue(w, operand, .Other); |
| 5180 | 5201 | try w.writeByte(';'); |
| 5181 | 5202 | try f.object.newline(); |
| ... | ... | @@ -5264,14 +5285,14 @@ fn airTrap(f: *Function, w: *Writer) !void { |
| 5264 | 5285 | } |
| 5265 | 5286 | |
| 5266 | 5287 | fn airBreakpoint(f: *Function) !CValue { |
| 5267 | const w = &f.object.code.buffered_writer; | |
| 5288 | const w = &f.object.code.writer; | |
| 5268 | 5289 | try w.writeAll("zig_breakpoint();"); |
| 5269 | 5290 | try f.object.newline(); |
| 5270 | 5291 | return .none; |
| 5271 | 5292 | } |
| 5272 | 5293 | |
| 5273 | 5294 | fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5274 | const w = &f.object.code.buffered_writer; | |
| 5295 | const w = &f.object.code.writer; | |
| 5275 | 5296 | const local = try f.allocLocal(inst, .usize); |
| 5276 | 5297 | try f.writeCValue(w, local, .Other); |
| 5277 | 5298 | try w.writeAll(" = ("); |
| ... | ... | @@ -5282,7 +5303,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5282 | 5303 | } |
| 5283 | 5304 | |
| 5284 | 5305 | fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5285 | const w = &f.object.code.buffered_writer; | |
| 5306 | const w = &f.object.code.writer; | |
| 5286 | 5307 | const local = try f.allocLocal(inst, .usize); |
| 5287 | 5308 | try f.writeCValue(w, local, .Other); |
| 5288 | 5309 | try w.writeAll(" = ("); |
| ... | ... | @@ -5295,14 +5316,14 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5295 | 5316 | fn airUnreach(o: *Object) !void { |
| 5296 | 5317 | // Not even allowed to call unreachable in a naked function. |
| 5297 | 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 | } |
| 5300 | 5321 | |
| 5301 | 5322 | fn airLoop(f: *Function, inst: Air.Inst.Index) !void { |
| 5302 | 5323 | const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5303 | 5324 | const loop = f.air.extraData(Air.Block, ty_pl.payload); |
| 5304 | 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; | |
| 5306 | 5327 | |
| 5307 | 5328 | // `repeat` instructions matching this loop will branch to |
| 5308 | 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 | 5342 | const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]); |
| 5322 | 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 | 5344 | const liveness_condbr = f.liveness.getCondBr(inst); |
| 5324 | const w = &f.object.code.buffered_writer; | |
| 5345 | const w = &f.object.code.writer; | |
| 5325 | 5346 | |
| 5326 | 5347 | try w.writeAll("if ("); |
| 5327 | 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 | 5375 | const init_condition = try f.resolveInst(switch_br.operand); |
| 5355 | 5376 | try reap(f, inst, &.{switch_br.operand}); |
| 5356 | 5377 | const condition_ty = f.typeOf(switch_br.operand); |
| 5357 | const w = &f.object.code.buffered_writer; | |
| 5378 | const w = &f.object.code.writer; | |
| 5358 | 5379 | |
| 5359 | 5380 | // For dispatches, we will create a local alloc to contain the condition value. |
| 5360 | 5381 | // 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 | 5429 | write_val: { |
| 5409 | 5430 | if (condition_ty.isPtrAtRuntime(zcu)) { |
| 5410 | 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 | 5433 | break :write_val; |
| 5413 | 5434 | } |
| 5414 | 5435 | } |
| ... | ... | @@ -5534,7 +5555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5534 | 5555 | extra_i += inputs.len; |
| 5535 | 5556 | |
| 5536 | 5557 | const result = result: { |
| 5537 | const w = &f.object.code.buffered_writer; | |
| 5558 | const w = &f.object.code.writer; | |
| 5538 | 5559 | const inst_ty = f.typeOfIndex(inst); |
| 5539 | 5560 | const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: { |
| 5540 | 5561 | const inst_local = try f.allocLocalValue(.{ |
| ... | ... | @@ -5683,7 +5704,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5683 | 5704 | |
| 5684 | 5705 | try w.writeAll("__asm"); |
| 5685 | 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 | } |
| 5688 | 5709 | |
| 5689 | 5710 | extra_i = constraints_extra_begin; |
| ... | ... | @@ -5701,7 +5722,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5701 | 5722 | try w.writeByte(' '); |
| 5702 | 5723 | if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name}); |
| 5703 | 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 | 5726 | if (is_reg) { |
| 5706 | 5727 | try f.writeCValue(w, .{ .local = locals_index }, .Other); |
| 5707 | 5728 | locals_index += 1; |
| ... | ... | @@ -5727,7 +5748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5727 | 5748 | |
| 5728 | 5749 | const is_reg = constraint[0] == '{'; |
| 5729 | 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 | 5752 | try f.writeCValue(w, if (asmInputNeedsLocal(f, constraint, input_val)) local: { |
| 5732 | 5753 | const input_local_idx = locals_index; |
| 5733 | 5754 | locals_index += 1; |
| ... | ... | @@ -5745,7 +5766,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5745 | 5766 | if (clobber.len == 0) continue; |
| 5746 | 5767 | |
| 5747 | 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 | 5771 | try w.writeAll(");"); |
| 5751 | 5772 | try f.object.newline(); |
| ... | ... | @@ -5800,7 +5821,7 @@ fn airIsNull( |
| 5800 | 5821 | const ctype_pool = &f.object.dg.ctype_pool; |
| 5801 | 5822 | const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5802 | 5823 | |
| 5803 | const w = &f.object.code.buffered_writer; | |
| 5824 | const w = &f.object.code.writer; | |
| 5804 | 5825 | const operand = try f.resolveInst(un_op); |
| 5805 | 5826 | try reap(f, inst, &.{un_op}); |
| 5806 | 5827 | |
| ... | ... | @@ -5868,7 +5889,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue |
| 5868 | 5889 | .aligned, .array, .vector, .fwd_decl, .function => unreachable, |
| 5869 | 5890 | .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { |
| 5870 | 5891 | .is_null, .payload => { |
| 5871 | const w = &f.object.code.buffered_writer; | |
| 5892 | const w = &f.object.code.writer; | |
| 5872 | 5893 | const local = try f.allocLocal(inst, inst_ty); |
| 5873 | 5894 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 5874 | 5895 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -5890,7 +5911,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5890 | 5911 | const pt = f.object.dg.pt; |
| 5891 | 5912 | const zcu = pt.zcu; |
| 5892 | 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 | 5915 | const operand = try f.resolveInst(ty_op.operand); |
| 5895 | 5916 | try reap(f, inst, &.{ty_op.operand}); |
| 5896 | 5917 | const operand_ty = f.typeOf(ty_op.operand); |
| ... | ... | @@ -6040,7 +6061,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6040 | 6061 | const field_ptr_val = try f.resolveInst(extra.field_ptr); |
| 6041 | 6062 | try reap(f, inst, &.{extra.field_ptr}); |
| 6042 | 6063 | |
| 6043 | const w = &f.object.code.buffered_writer; | |
| 6064 | const w = &f.object.code.writer; | |
| 6044 | 6065 | const local = try f.allocLocal(inst, container_ptr_ty); |
| 6045 | 6066 | try f.writeCValue(w, local, .Other); |
| 6046 | 6067 | try w.writeAll(" = ("); |
| ... | ... | @@ -6070,7 +6091,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6070 | 6091 | try w.writeByte(')'); |
| 6071 | 6092 | try f.writeCValue(w, field_ptr_val, .Other); |
| 6072 | 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 | 6116 | // Ensure complete type definition is visible before accessing fields. |
| 6096 | 6117 | _ = try f.ctypeFromType(container_ty, .complete); |
| 6097 | 6118 | |
| 6098 | const w = &f.object.code.buffered_writer; | |
| 6119 | const w = &f.object.code.writer; | |
| 6099 | 6120 | const local = try f.allocLocal(inst, field_ptr_ty); |
| 6100 | 6121 | try f.writeCValue(w, local, .Other); |
| 6101 | 6122 | try w.writeAll(" = ("); |
| ... | ... | @@ -6116,7 +6137,7 @@ fn fieldPtr( |
| 6116 | 6137 | try w.writeByte(')'); |
| 6117 | 6138 | try f.writeCValue(w, container_ptr_val, .Other); |
| 6118 | 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 | 6163 | const struct_byval = try f.resolveInst(extra.struct_operand); |
| 6143 | 6164 | try reap(f, inst, &.{extra.struct_operand}); |
| 6144 | 6165 | const struct_ty = f.typeOf(extra.struct_operand); |
| 6145 | const w = &f.object.code.buffered_writer; | |
| 6166 | const w = &f.object.code.writer; | |
| 6146 | 6167 | |
| 6147 | 6168 | // Ensure complete type definition is visible before accessing fields. |
| 6148 | 6169 | _ = try f.ctypeFromType(struct_ty, .complete); |
| ... | ... | @@ -6189,7 +6210,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6189 | 6210 | } |
| 6190 | 6211 | try f.writeCValue(w, struct_byval, .Other); |
| 6191 | 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 | 6215 | if (cant_cast) try w.writeByte(')'); |
| 6195 | 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 | 6312 | return local; |
| 6292 | 6313 | } |
| 6293 | 6314 | |
| 6294 | const w = &f.object.code.buffered_writer; | |
| 6315 | const w = &f.object.code.writer; | |
| 6295 | 6316 | try f.writeCValue(w, local, .Other); |
| 6296 | 6317 | try w.writeAll(" = "); |
| 6297 | 6318 | |
| ... | ... | @@ -6299,7 +6320,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6299 | 6320 | try f.writeCValue(w, operand, .Other) |
| 6300 | 6321 | else if (error_ty.errorSetIsEmpty(zcu)) |
| 6301 | 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 | 6325 | else if (operand_is_ptr) |
| 6305 | 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 | 6342 | const operand_ty = f.typeOf(ty_op.operand); |
| 6322 | 6343 | const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; |
| 6323 | 6344 | |
| 6324 | const w = &f.object.code.buffered_writer; | |
| 6345 | const w = &f.object.code.writer; | |
| 6325 | 6346 | if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) { |
| 6326 | 6347 | if (!is_ptr) return .none; |
| 6327 | 6348 | |
| ... | ... | @@ -6363,7 +6384,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6363 | 6384 | .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { |
| 6364 | 6385 | .is_null, .payload => { |
| 6365 | 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 | 6388 | const local = try f.allocLocal(inst, inst_ty); |
| 6368 | 6389 | { |
| 6369 | 6390 | const a = try Assignment.start(f, w, .bool); |
| ... | ... | @@ -6399,7 +6420,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6399 | 6420 | const err = try f.resolveInst(ty_op.operand); |
| 6400 | 6421 | try reap(f, inst, &.{ty_op.operand}); |
| 6401 | 6422 | |
| 6402 | const w = &f.object.code.buffered_writer; | |
| 6423 | const w = &f.object.code.writer; | |
| 6403 | 6424 | const local = try f.allocLocal(inst, inst_ty); |
| 6404 | 6425 | |
| 6405 | 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 | 6451 | fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6431 | 6452 | const pt = f.object.dg.pt; |
| 6432 | 6453 | const zcu = pt.zcu; |
| 6433 | const w = &f.object.code.buffered_writer; | |
| 6454 | const w = &f.object.code.writer; | |
| 6434 | 6455 | const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6435 | 6456 | const inst_ty = f.typeOfIndex(inst); |
| 6436 | 6457 | const operand = try f.resolveInst(ty_op.operand); |
| ... | ... | @@ -6447,7 +6468,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6447 | 6468 | const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete)); |
| 6448 | 6469 | try f.writeCValueDeref(w, operand); |
| 6449 | 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 | 6472 | try a.end(f, w); |
| 6452 | 6473 | return .none; |
| 6453 | 6474 | } |
| ... | ... | @@ -6455,7 +6476,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6455 | 6476 | const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete)); |
| 6456 | 6477 | try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }); |
| 6457 | 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 | 6480 | try a.end(f, w); |
| 6460 | 6481 | } |
| 6461 | 6482 | |
| ... | ... | @@ -6499,7 +6520,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6499 | 6520 | const err_ty = inst_ty.errorUnionSet(zcu); |
| 6500 | 6521 | try reap(f, inst, &.{ty_op.operand}); |
| 6501 | 6522 | |
| 6502 | const w = &f.object.code.buffered_writer; | |
| 6523 | const w = &f.object.code.writer; | |
| 6503 | 6524 | const local = try f.allocLocal(inst, inst_ty); |
| 6504 | 6525 | if (!repr_is_err) { |
| 6505 | 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 | 6547 | const zcu = pt.zcu; |
| 6527 | 6548 | const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 6528 | 6549 | |
| 6529 | const w = &f.object.code.buffered_writer; | |
| 6550 | const w = &f.object.code.writer; | |
| 6530 | 6551 | const operand = try f.resolveInst(un_op); |
| 6531 | 6552 | try reap(f, inst, &.{un_op}); |
| 6532 | 6553 | const operand_ty = f.typeOf(un_op); |
| ... | ... | @@ -6567,7 +6588,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6567 | 6588 | try reap(f, inst, &.{ty_op.operand}); |
| 6568 | 6589 | const inst_ty = f.typeOfIndex(inst); |
| 6569 | 6590 | const ptr_ty = inst_ty.slicePtrFieldType(zcu); |
| 6570 | const w = &f.object.code.buffered_writer; | |
| 6591 | const w = &f.object.code.writer; | |
| 6571 | 6592 | const local = try f.allocLocal(inst, inst_ty); |
| 6572 | 6593 | const operand_ty = f.typeOf(ty_op.operand); |
| 6573 | 6594 | const array_ty = operand_ty.childType(zcu); |
| ... | ... | @@ -6593,7 +6614,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6593 | 6614 | if (operand_child_ctype.info(ctype_pool) == .array) { |
| 6594 | 6615 | try w.writeByte('&'); |
| 6595 | 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 | 6618 | } else try f.writeCValue(w, operand, .Other); |
| 6598 | 6619 | } |
| 6599 | 6620 | try a.end(f, w); |
| ... | ... | @@ -6603,7 +6624,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6603 | 6624 | try f.writeCValueMember(w, local, .{ .identifier = "len" }); |
| 6604 | 6625 | try a.assign(f, w); |
| 6605 | 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 | 6629 | try a.end(f, w); |
| 6609 | 6630 | } |
| ... | ... | @@ -6632,7 +6653,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6632 | 6653 | else |
| 6633 | 6654 | unreachable; |
| 6634 | 6655 | |
| 6635 | const w = &f.object.code.buffered_writer; | |
| 6656 | const w = &f.object.code.writer; | |
| 6636 | 6657 | const local = try f.allocLocal(inst, inst_ty); |
| 6637 | 6658 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 6638 | 6659 | const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); |
| ... | ... | @@ -6682,7 +6703,7 @@ fn airUnBuiltinCall( |
| 6682 | 6703 | const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); |
| 6683 | 6704 | const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; |
| 6684 | 6705 | |
| 6685 | const w = &f.object.code.buffered_writer; | |
| 6706 | const w = &f.object.code.writer; | |
| 6686 | 6707 | const local = try f.allocLocal(inst, inst_ty); |
| 6687 | 6708 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 6688 | 6709 | if (!ref_ret) { |
| ... | ... | @@ -6733,7 +6754,7 @@ fn airBinBuiltinCall( |
| 6733 | 6754 | const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); |
| 6734 | 6755 | const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; |
| 6735 | 6756 | |
| 6736 | const w = &f.object.code.buffered_writer; | |
| 6757 | const w = &f.object.code.writer; | |
| 6737 | 6758 | const local = try f.allocLocal(inst, inst_ty); |
| 6738 | 6759 | if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 6739 | 6760 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| ... | ... | @@ -6784,7 +6805,7 @@ fn airCmpBuiltinCall( |
| 6784 | 6805 | const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); |
| 6785 | 6806 | const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; |
| 6786 | 6807 | |
| 6787 | const w = &f.object.code.buffered_writer; | |
| 6808 | const w = &f.object.code.writer; | |
| 6788 | 6809 | const local = try f.allocLocal(inst, inst_ty); |
| 6789 | 6810 | const v = try Vectorize.start(f, inst, w, operand_ty); |
| 6790 | 6811 | if (!ref_ret) { |
| ... | ... | @@ -6812,7 +6833,7 @@ fn airCmpBuiltinCall( |
| 6812 | 6833 | try w.writeByte(')'); |
| 6813 | 6834 | if (!ref_ret) try w.print("{s}{f}", .{ |
| 6814 | 6835 | compareOperatorC(operator), |
| 6815 | try f.fmtIntLiteral(try pt.intValue(.i32, 0)), | |
| 6836 | try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)), | |
| 6816 | 6837 | }); |
| 6817 | 6838 | try w.writeByte(';'); |
| 6818 | 6839 | try f.object.newline(); |
| ... | ... | @@ -6834,7 +6855,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue |
| 6834 | 6855 | const ty = ptr_ty.childType(zcu); |
| 6835 | 6856 | const ctype = try f.ctypeFromType(ty, .complete); |
| 6836 | 6857 | |
| 6837 | const w = &f.object.code.buffered_writer; | |
| 6858 | const w = &f.object.code.writer; | |
| 6838 | 6859 | const new_value_mat = try Materialize.start(f, inst, ty, new_value); |
| 6839 | 6860 | try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value }); |
| 6840 | 6861 | |
| ... | ... | @@ -6941,7 +6962,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6941 | 6962 | const ptr = try f.resolveInst(pl_op.operand); |
| 6942 | 6963 | const operand = try f.resolveInst(extra.operand); |
| 6943 | 6964 | |
| 6944 | const w = &f.object.code.buffered_writer; | |
| 6965 | const w = &f.object.code.writer; | |
| 6945 | 6966 | const operand_mat = try Materialize.start(f, inst, ty, operand); |
| 6946 | 6967 | try reap(f, inst, &.{ pl_op.operand, extra.operand }); |
| 6947 | 6968 | |
| ... | ... | @@ -7002,7 +7023,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7002 | 7023 | ty; |
| 7003 | 7024 | |
| 7004 | 7025 | const inst_ty = f.typeOfIndex(inst); |
| 7005 | const w = &f.object.code.buffered_writer; | |
| 7026 | const w = &f.object.code.writer; | |
| 7006 | 7027 | const local = try f.allocLocal(inst, inst_ty); |
| 7007 | 7028 | |
| 7008 | 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 | 7055 | const ptr = try f.resolveInst(bin_op.lhs); |
| 7035 | 7056 | const element = try f.resolveInst(bin_op.rhs); |
| 7036 | 7057 | |
| 7037 | const w = &f.object.code.buffered_writer; | |
| 7058 | const w = &f.object.code.writer; | |
| 7038 | 7059 | const element_mat = try Materialize.start(f, inst, ty, element); |
| 7039 | 7060 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 7040 | 7061 | |
| ... | ... | @@ -7082,7 +7103,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { |
| 7082 | 7103 | const elem_ty = f.typeOf(bin_op.rhs); |
| 7083 | 7104 | const elem_abi_size = elem_ty.abiSize(zcu); |
| 7084 | 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; | |
| 7086 | 7107 | |
| 7087 | 7108 | if (val_is_undef) { |
| 7088 | 7109 | if (!safety) { |
| ... | ... | @@ -7206,7 +7227,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV |
| 7206 | 7227 | const src_ptr = try f.resolveInst(bin_op.rhs); |
| 7207 | 7228 | const dest_ty = f.typeOf(bin_op.lhs); |
| 7208 | 7229 | const src_ty = f.typeOf(bin_op.rhs); |
| 7209 | const w = &f.object.code.buffered_writer; | |
| 7230 | const w = &f.object.code.writer; | |
| 7210 | 7231 | |
| 7211 | 7232 | if (dest_ty.ptrSize(zcu) != .one) { |
| 7212 | 7233 | try w.writeAll("if ("); |
| ... | ... | @@ -7231,10 +7252,10 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CV |
| 7231 | 7252 | fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void { |
| 7232 | 7253 | const pt = f.object.dg.pt; |
| 7233 | 7254 | const zcu = pt.zcu; |
| 7234 | const w = &f.object.code.buffered_writer; | |
| 7255 | const w = &f.object.code.writer; | |
| 7235 | 7256 | switch (dest_ty.ptrSize(zcu)) { |
| 7236 | 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 | 7260 | .many, .c => unreachable, |
| 7240 | 7261 | .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }), |
| ... | ... | @@ -7254,7 +7275,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7254 | 7275 | if (layout.tag_size == 0) return .none; |
| 7255 | 7276 | const tag_ty = union_ty.unionTagTypeSafety(zcu).?; |
| 7256 | 7277 | |
| 7257 | const w = &f.object.code.buffered_writer; | |
| 7278 | const w = &f.object.code.writer; | |
| 7258 | 7279 | const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete)); |
| 7259 | 7280 | try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" }); |
| 7260 | 7281 | try a.assign(f, w); |
| ... | ... | @@ -7276,7 +7297,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7276 | 7297 | if (layout.tag_size == 0) return .none; |
| 7277 | 7298 | |
| 7278 | 7299 | const inst_ty = f.typeOfIndex(inst); |
| 7279 | const w = &f.object.code.buffered_writer; | |
| 7300 | const w = &f.object.code.writer; | |
| 7280 | 7301 | const local = try f.allocLocal(inst, inst_ty); |
| 7281 | 7302 | const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); |
| 7282 | 7303 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -7294,7 +7315,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7294 | 7315 | const operand = try f.resolveInst(un_op); |
| 7295 | 7316 | try reap(f, inst, &.{un_op}); |
| 7296 | 7317 | |
| 7297 | const w = &f.object.code.buffered_writer; | |
| 7318 | const w = &f.object.code.writer; | |
| 7298 | 7319 | const local = try f.allocLocal(inst, inst_ty); |
| 7299 | 7320 | try f.writeCValue(w, local, .Other); |
| 7300 | 7321 | try w.print(" = {s}(", .{ |
| ... | ... | @@ -7310,7 +7331,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7310 | 7331 | fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7311 | 7332 | const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 7312 | 7333 | |
| 7313 | const w = &f.object.code.buffered_writer; | |
| 7334 | const w = &f.object.code.writer; | |
| 7314 | 7335 | const inst_ty = f.typeOfIndex(inst); |
| 7315 | 7336 | const operand = try f.resolveInst(un_op); |
| 7316 | 7337 | try reap(f, inst, &.{un_op}); |
| ... | ... | @@ -7335,7 +7356,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7335 | 7356 | const inst_ty = f.typeOfIndex(inst); |
| 7336 | 7357 | const inst_scalar_ty = inst_ty.scalarType(zcu); |
| 7337 | 7358 | |
| 7338 | const w = &f.object.code.buffered_writer; | |
| 7359 | const w = &f.object.code.writer; | |
| 7339 | 7360 | const local = try f.allocLocal(inst, inst_ty); |
| 7340 | 7361 | const v = try Vectorize.start(f, inst, w, inst_ty); |
| 7341 | 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 | 7381 | |
| 7361 | 7382 | const inst_ty = f.typeOfIndex(inst); |
| 7362 | 7383 | |
| 7363 | const w = &f.object.code.buffered_writer; | |
| 7384 | const w = &f.object.code.writer; | |
| 7364 | 7385 | const local = try f.allocLocal(inst, inst_ty); |
| 7365 | 7386 | const v = try Vectorize.start(f, inst, w, inst_ty); |
| 7366 | 7387 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -7390,7 +7411,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7390 | 7411 | const operand = try f.resolveInst(unwrapped.operand); |
| 7391 | 7412 | const inst_ty = unwrapped.result_ty; |
| 7392 | 7413 | |
| 7393 | const w = &f.object.code.buffered_writer; | |
| 7414 | const w = &f.object.code.writer; | |
| 7394 | 7415 | const local = try f.allocLocal(inst, inst_ty); |
| 7395 | 7416 | try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand |
| 7396 | 7417 | for (mask, 0..) |mask_elem, out_idx| { |
| ... | ... | @@ -7424,7 +7445,7 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7424 | 7445 | const inst_ty = unwrapped.result_ty; |
| 7425 | 7446 | const elem_ty = inst_ty.childType(zcu); |
| 7426 | 7447 | |
| 7427 | const w = &f.object.code.buffered_writer; | |
| 7448 | const w = &f.object.code.writer; | |
| 7428 | 7449 | const local = try f.allocLocal(inst, inst_ty); |
| 7429 | 7450 | try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands |
| 7430 | 7451 | for (mask, 0..) |mask_elem, out_idx| { |
| ... | ... | @@ -7463,7 +7484,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7463 | 7484 | const operand = try f.resolveInst(reduce.operand); |
| 7464 | 7485 | try reap(f, inst, &.{reduce.operand}); |
| 7465 | 7486 | const operand_ty = f.typeOf(reduce.operand); |
| 7466 | const w = &f.object.code.buffered_writer; | |
| 7487 | const w = &f.object.code.writer; | |
| 7467 | 7488 | |
| 7468 | 7489 | const use_operator = scalar_ty.bitSize(zcu) <= 64; |
| 7469 | 7490 | const op: union(enum) { |
| ... | ... | @@ -7613,7 +7634,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7613 | 7634 | } |
| 7614 | 7635 | } |
| 7615 | 7636 | |
| 7616 | const w = &f.object.code.buffered_writer; | |
| 7637 | const w = &f.object.code.writer; | |
| 7617 | 7638 | const local = try f.allocLocal(inst, inst_ty); |
| 7618 | 7639 | switch (ip.indexToKey(inst_ty.toIntern())) { |
| 7619 | 7640 | inline .array_type, .vector_type => |info, tag| { |
| ... | ... | @@ -7727,7 +7748,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7727 | 7748 | } |
| 7728 | 7749 | |
| 7729 | 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 | 7753 | try f.object.dg.renderBuiltinInfo(w, inst_ty, .bits); |
| 7733 | 7754 | try w.writeByte(')'); |
| ... | ... | @@ -7772,7 +7793,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7772 | 7793 | const payload = try f.resolveInst(extra.init); |
| 7773 | 7794 | try reap(f, inst, &.{extra.init}); |
| 7774 | 7795 | |
| 7775 | const w = &f.object.code.buffered_writer; | |
| 7796 | const w = &f.object.code.writer; | |
| 7776 | 7797 | const local = try f.allocLocal(inst, union_ty); |
| 7777 | 7798 | if (loaded_union.flagsUnordered(ip).layout == .@"packed") return f.moveCValue(inst, union_ty, payload); |
| 7778 | 7799 | |
| ... | ... | @@ -7785,7 +7806,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7785 | 7806 | const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete)); |
| 7786 | 7807 | try f.writeCValueMember(w, local, .{ .identifier = "tag" }); |
| 7787 | 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 | 7810 | try a.end(f, w); |
| 7790 | 7811 | } |
| 7791 | 7812 | break :field .{ .payload_identifier = field_name.toSlice(ip) }; |
| ... | ... | @@ -7808,7 +7829,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7808 | 7829 | const ptr = try f.resolveInst(prefetch.ptr); |
| 7809 | 7830 | try reap(f, inst, &.{prefetch.ptr}); |
| 7810 | 7831 | |
| 7811 | const w = &f.object.code.buffered_writer; | |
| 7832 | const w = &f.object.code.writer; | |
| 7812 | 7833 | switch (prefetch.cache) { |
| 7813 | 7834 | .data => { |
| 7814 | 7835 | try w.writeAll("zig_prefetch("); |
| ... | ... | @@ -7830,7 +7851,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7830 | 7851 | fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7831 | 7852 | const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 7832 | 7853 | |
| 7833 | const w = &f.object.code.buffered_writer; | |
| 7854 | const w = &f.object.code.writer; | |
| 7834 | 7855 | const inst_ty = f.typeOfIndex(inst); |
| 7835 | 7856 | const local = try f.allocLocal(inst, inst_ty); |
| 7836 | 7857 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -7845,7 +7866,7 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7845 | 7866 | fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7846 | 7867 | const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 7847 | 7868 | |
| 7848 | const w = &f.object.code.buffered_writer; | |
| 7869 | const w = &f.object.code.writer; | |
| 7849 | 7870 | const inst_ty = f.typeOfIndex(inst); |
| 7850 | 7871 | const operand = try f.resolveInst(pl_op.operand); |
| 7851 | 7872 | try reap(f, inst, &.{pl_op.operand}); |
| ... | ... | @@ -7874,7 +7895,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7874 | 7895 | const inst_ty = f.typeOfIndex(inst); |
| 7875 | 7896 | const inst_scalar_ty = inst_ty.scalarType(zcu); |
| 7876 | 7897 | |
| 7877 | const w = &f.object.code.buffered_writer; | |
| 7898 | const w = &f.object.code.writer; | |
| 7878 | 7899 | const local = try f.allocLocal(inst, inst_ty); |
| 7879 | 7900 | const v = try Vectorize.start(f, inst, w, inst_ty); |
| 7880 | 7901 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -7899,7 +7920,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7899 | 7920 | |
| 7900 | 7921 | fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7901 | 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 | 7924 | const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); |
| 7904 | 7925 | try f.writeCValue(w, local, .Other); |
| 7905 | 7926 | try w.writeAll(" = "); |
| ... | ... | @@ -7917,7 +7938,7 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7917 | 7938 | const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function; |
| 7918 | 7939 | assert(function_info.varargs); |
| 7919 | 7940 | |
| 7920 | const w = &f.object.code.buffered_writer; | |
| 7941 | const w = &f.object.code.writer; | |
| 7921 | 7942 | const local = try f.allocLocal(inst, inst_ty); |
| 7922 | 7943 | try w.writeAll("va_start(*(va_list *)&"); |
| 7923 | 7944 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -7937,7 +7958,7 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7937 | 7958 | const va_list = try f.resolveInst(ty_op.operand); |
| 7938 | 7959 | try reap(f, inst, &.{ty_op.operand}); |
| 7939 | 7960 | |
| 7940 | const w = &f.object.code.buffered_writer; | |
| 7961 | const w = &f.object.code.writer; | |
| 7941 | 7962 | const local = try f.allocLocal(inst, inst_ty); |
| 7942 | 7963 | try f.writeCValue(w, local, .Other); |
| 7943 | 7964 | try w.writeAll(" = va_arg(*(va_list *)"); |
| ... | ... | @@ -7955,7 +7976,7 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7955 | 7976 | const va_list = try f.resolveInst(un_op); |
| 7956 | 7977 | try reap(f, inst, &.{un_op}); |
| 7957 | 7978 | |
| 7958 | const w = &f.object.code.buffered_writer; | |
| 7979 | const w = &f.object.code.writer; | |
| 7959 | 7980 | try w.writeAll("va_end(*(va_list *)"); |
| 7960 | 7981 | try f.writeCValue(w, va_list, .Other); |
| 7961 | 7982 | try w.writeAll(");"); |
| ... | ... | @@ -7970,7 +7991,7 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7970 | 7991 | const va_list = try f.resolveInst(ty_op.operand); |
| 7971 | 7992 | try reap(f, inst, &.{ty_op.operand}); |
| 7972 | 7993 | |
| 7973 | const w = &f.object.code.buffered_writer; | |
| 7994 | const w = &f.object.code.writer; | |
| 7974 | 7995 | const local = try f.allocLocal(inst, inst_ty); |
| 7975 | 7996 | try w.writeAll("va_copy(*(va_list *)&"); |
| 7976 | 7997 | try f.writeCValue(w, local, .Other); |
| ... | ... | @@ -8136,8 +8157,8 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 { |
| 8136 | 8157 | const StringLiteral = struct { |
| 8137 | 8158 | len: usize, |
| 8138 | 8159 | cur_len: usize, |
| 8139 | start_count: usize, | |
| 8140 | 8160 | w: *Writer, |
| 8161 | first: bool, | |
| 8141 | 8162 | |
| 8142 | 8163 | // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal, |
| 8143 | 8164 | // regardless of the length of the string literal initializing it. Array initializer syntax is |
| ... | ... | @@ -8154,8 +8175,8 @@ const StringLiteral = struct { |
| 8154 | 8175 | return .{ |
| 8155 | 8176 | .cur_len = 0, |
| 8156 | 8177 | .len = len, |
| 8157 | .start_count = w.count, | |
| 8158 | 8178 | .w = w, |
| 8179 | .first = true, | |
| 8159 | 8180 | }; |
| 8160 | 8181 | } |
| 8161 | 8182 | |
| ... | ... | @@ -8175,50 +8196,83 @@ const StringLiteral = struct { |
| 8175 | 8196 | } |
| 8176 | 8197 | } |
| 8177 | 8198 | |
| 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 | 8201 | switch (c) { |
| 8180 | 7 => try sl.w.writeAll("\\a"), | |
| 8181 | 8 => try sl.w.writeAll("\\b"), | |
| 8182 | '\t' => try sl.w.writeAll("\\t"), | |
| 8183 | '\n' => try sl.w.writeAll("\\n"), | |
| 8184 | 11 => try sl.w.writeAll("\\v"), | |
| 8185 | 12 => try sl.w.writeAll("\\f"), | |
| 8186 | '\r' => try sl.w.writeAll("\\r"), | |
| 8187 | '"', '\'', '?', '\\' => try sl.w.print("\\{c}", .{c}), | |
| 8188 | else => switch (c) { | |
| 8189 | ' '...'~' => try sl.w.writeByte(c), | |
| 8190 | else => try sl.w.print("\\{o:0>3}", .{c}), | |
| 8202 | 7 => { | |
| 8203 | try w.writeAll("\\a"); | |
| 8204 | return 2; | |
| 8205 | }, | |
| 8206 | 8 => { | |
| 8207 | try w.writeAll("\\b"); | |
| 8208 | return 2; | |
| 8209 | }, | |
| 8210 | '\t' => { | |
| 8211 | try w.writeAll("\\t"); | |
| 8212 | return 2; | |
| 8213 | }, | |
| 8214 | '\n' => { | |
| 8215 | try w.writeAll("\\n"); | |
| 8216 | return 2; | |
| 8217 | }, | |
| 8218 | 11 => { | |
| 8219 | try w.writeAll("\\v"); | |
| 8220 | return 2; | |
| 8221 | }, | |
| 8222 | 12 => { | |
| 8223 | try w.writeAll("\\f"); | |
| 8224 | return 2; | |
| 8225 | }, | |
| 8226 | '\r' => { | |
| 8227 | try w.writeAll("\\r"); | |
| 8228 | return 2; | |
| 8229 | }, | |
| 8230 | '"', '\'', '?', '\\' => { | |
| 8231 | try w.print("\\{c}", .{c}); | |
| 8232 | return 2; | |
| 8233 | }, | |
| 8234 | ' '...'!', '#'...'&', '('...'>', '@'...'[', ']'...'~' => { | |
| 8235 | try w.writeByte(c); | |
| 8236 | return 1; | |
| 8237 | }, | |
| 8238 | else => { | |
| 8239 | var buf: [4]u8 = undefined; | |
| 8240 | const printed = std.fmt.bufPrint(&buf, "\\{o:0>3}", .{c}) catch unreachable; | |
| 8241 | try w.writeAll(printed); | |
| 8242 | return printed.len; | |
| 8191 | 8243 | }, |
| 8192 | 8244 | } |
| 8193 | 8245 | } |
| 8194 | 8246 | |
| 8195 | 8247 | pub fn writeChar(sl: *StringLiteral, c: u8) Writer.Error!void { |
| 8196 | 8248 | if (sl.len <= max_string_initializer_len) { |
| 8197 | if (sl.cur_len == 0 and sl.w.count - sl.start_count > 1) | |
| 8198 | try sl.w.writeAll("\"\""); | |
| 8249 | if (sl.cur_len == 0 and !sl.first) try sl.w.writeAll("\"\""); | |
| 8199 | 8250 | |
| 8200 | const count = sl.w.count; | |
| 8201 | try sl.writeStringLiteralChar(c); | |
| 8202 | const char_len = sl.w.count - count; | |
| 8251 | const char_len = try sl.writeStringLiteralChar(c); | |
| 8203 | 8252 | assert(char_len <= max_char_len); |
| 8204 | 8253 | sl.cur_len += char_len; |
| 8205 | 8254 | |
| 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 | 8259 | } else { |
| 8208 | if (sl.w.count - sl.start_count > 1) try sl.w.writeByte(','); | |
| 8209 | try sl.w.print("'\\x{x}'", .{c}); | |
| 8260 | if (!sl.first) try sl.w.writeByte(','); | |
| 8261 | var buf: [6]u8 = undefined; | |
| 8262 | const printed = std.fmt.bufPrint(&buf, "'\\x{x}'", .{c}) catch unreachable; | |
| 8263 | try sl.w.writeAll(printed); | |
| 8264 | sl.cur_len += printed.len; | |
| 8265 | sl.first = false; | |
| 8210 | 8266 | } |
| 8211 | 8267 | } |
| 8212 | 8268 | }; |
| 8213 | 8269 | |
| 8214 | const FormatStringContext = struct { str: []const u8, sentinel: ?u8 }; | |
| 8215 | fn formatStringLiteral( | |
| 8216 | data: FormatStringContext, | |
| 8217 | w: *Writer, | |
| 8218 | comptime fmt: []const u8, | |
| 8219 | ) Writer.Error!void { | |
| 8220 | if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt); | |
| 8270 | const FormatStringContext = struct { | |
| 8271 | str: []const u8, | |
| 8272 | sentinel: ?u8, | |
| 8273 | }; | |
| 8221 | 8274 | |
| 8275 | fn formatStringLiteral(data: FormatStringContext, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 8222 | 8276 | var literal: StringLiteral = .init(w, data.str.len + @intFromBool(data.sentinel != null)); |
| 8223 | 8277 | try literal.start(); |
| 8224 | 8278 | for (data.str) |c| try literal.writeChar(c); |
| ... | ... | @@ -8226,7 +8280,7 @@ fn formatStringLiteral( |
| 8226 | 8280 | try literal.end(); |
| 8227 | 8281 | } |
| 8228 | 8282 | |
| 8229 | fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) { | |
| 8283 | fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(FormatStringContext, formatStringLiteral) { | |
| 8230 | 8284 | return .{ .data = .{ .str = str, .sentinel = sentinel } }; |
| 8231 | 8285 | } |
| 8232 | 8286 | |
| ... | ... | @@ -8242,12 +8296,10 @@ const FormatIntLiteralContext = struct { |
| 8242 | 8296 | kind: CType.Kind, |
| 8243 | 8297 | ctype: CType, |
| 8244 | 8298 | val: Value, |
| 8299 | base: u8, | |
| 8300 | case: std.fmt.Case, | |
| 8245 | 8301 | }; |
| 8246 | fn formatIntLiteral( | |
| 8247 | data: FormatIntLiteralContext, | |
| 8248 | w: *Writer, | |
| 8249 | comptime fmt: []const u8, | |
| 8250 | ) Writer.Error!void { | |
| 8302 | fn formatIntLiteral(data: FormatIntLiteralContext, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 8251 | 8303 | const pt = data.dg.pt; |
| 8252 | 8304 | const zcu = pt.zcu; |
| 8253 | 8305 | const target = &data.dg.mod.resolved_target.result; |
| ... | ... | @@ -8337,32 +8389,14 @@ fn formatIntLiteral( |
| 8337 | 8389 | if (!int.positive) try w.writeByte('-'); |
| 8338 | 8390 | try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool); |
| 8339 | 8391 | |
| 8340 | const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) { | |
| 8341 | 0 => .{ .base = 10 }, | |
| 8342 | 1 => switch (fmt[0]) { | |
| 8343 | 'b' => style: { | |
| 8344 | try w.writeAll("0b"); | |
| 8345 | break :style .{ .base = 2 }; | |
| 8346 | }, | |
| 8347 | 'o' => style: { | |
| 8348 | try w.writeByte('0'); | |
| 8349 | break :style .{ .base = 8 }; | |
| 8350 | }, | |
| 8351 | 'd' => .{ .base = 10 }, | |
| 8352 | 'x', 'X' => |base| style: { | |
| 8353 | try w.writeAll("0x"); | |
| 8354 | break :style .{ .base = 16, .case = switch (base) { | |
| 8355 | 'x' => .lower, | |
| 8356 | 'X' => .upper, | |
| 8357 | else => unreachable, | |
| 8358 | } }; | |
| 8359 | }, | |
| 8360 | else => @compileError("Invalid fmt: " ++ fmt), | |
| 8361 | }, | |
| 8362 | else => @compileError("Invalid fmt: " ++ fmt), | |
| 8363 | }; | |
| 8364 | ||
| 8365 | const string = int.abs().toStringAlloc(allocator, style.base, style.case) catch | |
| 8392 | switch (data.base) { | |
| 8393 | 2 => try w.writeAll("0b"), | |
| 8394 | 8 => try w.writeByte('0'), | |
| 8395 | 10 => {}, | |
| 8396 | 16 => try w.writeAll("0x"), | |
| 8397 | else => unreachable, | |
| 8398 | } | |
| 8399 | const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch | |
| 8366 | 8400 | return error.WriteFailed; |
| 8367 | 8401 | defer allocator.free(string); |
| 8368 | 8402 | try w.writeAll(string); |
| ... | ... | @@ -8418,7 +8452,9 @@ fn formatIntLiteral( |
| 8418 | 8452 | .ctype = c_limb_ctype, |
| 8419 | 8453 | .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch |
| 8420 | 8454 | return error.WriteFailed, |
| 8421 | }, w, fmt); | |
| 8455 | .base = data.base, | |
| 8456 | .case = data.case, | |
| 8457 | }, w); | |
| 8422 | 8458 | } |
| 8423 | 8459 | } |
| 8424 | 8460 | try data.ctype.renderLiteralSuffix(w, ctype_pool); |
| ... | ... | @@ -8499,11 +8535,11 @@ const Vectorize = struct { |
| 8499 | 8535 | |
| 8500 | 8536 | try w.writeAll("for ("); |
| 8501 | 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 | 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 | 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 | 8543 | f.object.indent(); |
| 8508 | 8544 | try f.object.newline(); |
| 8509 | 8545 |
src/codegen/llvm.zig+40-47| ... | ... | @@ -21,11 +21,11 @@ const Air = @import("../Air.zig"); |
| 21 | 21 | const Value = @import("../Value.zig"); |
| 22 | 22 | const Type = @import("../Type.zig"); |
| 23 | 23 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 24 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); | |
| 25 | const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); | |
| 26 | const arm_c_abi = @import("../arch/arm/abi.zig"); | |
| 24 | const wasm_c_abi = @import("wasm/abi.zig"); | |
| 25 | const aarch64_c_abi = @import("aarch64/abi.zig"); | |
| 26 | const arm_c_abi = @import("arm/abi.zig"); | |
| 27 | 27 | const riscv_c_abi = @import("../arch/riscv64/abi.zig"); |
| 28 | const mips_c_abi = @import("../arch/mips/abi.zig"); | |
| 28 | const mips_c_abi = @import("mips/abi.zig"); | |
| 29 | 29 | const dev = @import("../dev.zig"); |
| 30 | 30 | |
| 31 | 31 | const target_util = @import("../target.zig"); |
| ... | ... | @@ -945,7 +945,9 @@ pub const Object = struct { |
| 945 | 945 | if (std.mem.eql(u8, path, "-")) { |
| 946 | 946 | o.builder.dump(); |
| 947 | 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 | } |
| 951 | 953 | |
| ... | ... | @@ -1053,6 +1055,7 @@ pub const Object = struct { |
| 1053 | 1055 | comp.data_sections, |
| 1054 | 1056 | float_abi, |
| 1055 | 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 | 1060 | errdefer target_machine.dispose(); |
| 1058 | 1061 | |
| ... | ... | @@ -2765,7 +2768,7 @@ pub const Object = struct { |
| 2765 | 2768 | llvm_arg_i += 1; |
| 2766 | 2769 | } |
| 2767 | 2770 | |
| 2768 | if (fn_info.cc == .@"async") { | |
| 2771 | if (fn_info.cc == .async) { | |
| 2769 | 2772 | @panic("TODO: LLVM backend lower async function"); |
| 2770 | 2773 | } |
| 2771 | 2774 | |
| ... | ... | @@ -2917,7 +2920,7 @@ pub const Object = struct { |
| 2917 | 2920 | try attributes.addFnAttr(.nounwind, &o.builder); |
| 2918 | 2921 | if (owner_mod.unwind_tables != .none) { |
| 2919 | 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 | 2924 | &o.builder, |
| 2922 | 2925 | ); |
| 2923 | 2926 | } |
| ... | ... | @@ -5280,7 +5283,7 @@ pub const FuncGen = struct { |
| 5280 | 5283 | switch (modifier) { |
| 5281 | 5284 | .auto, .always_tail => {}, |
| 5282 | 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 | } |
| 5285 | 5288 | |
| 5286 | 5289 | const ret_ptr = if (!sret) null else blk: { |
| ... | ... | @@ -5288,7 +5291,7 @@ pub const FuncGen = struct { |
| 5288 | 5291 | try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder); |
| 5289 | 5292 | |
| 5290 | 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 | 5295 | try llvm_args.append(ret_ptr); |
| 5293 | 5296 | break :blk ret_ptr; |
| 5294 | 5297 | }; |
| ... | ... | @@ -5336,7 +5339,7 @@ pub const FuncGen = struct { |
| 5336 | 5339 | |
| 5337 | 5340 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 5338 | 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 | 5343 | if (isByRef(param_ty, zcu)) { |
| 5341 | 5344 | const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, ""); |
| 5342 | 5345 | _ = try self.wip.store(.normal, loaded, arg_ptr, alignment); |
| ... | ... | @@ -5359,7 +5362,7 @@ pub const FuncGen = struct { |
| 5359 | 5362 | // LLVM does not allow bitcasting structs so we must allocate |
| 5360 | 5363 | // a local, store as one type, and then load as another type. |
| 5361 | 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 | 5366 | _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment); |
| 5364 | 5367 | const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, ""); |
| 5365 | 5368 | try llvm_args.append(loaded); |
| ... | ... | @@ -5495,7 +5498,7 @@ pub const FuncGen = struct { |
| 5495 | 5498 | .auto, .never_inline => .normal, |
| 5496 | 5499 | .never_tail => .notail, |
| 5497 | 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 | 5503 | toLlvmCallConvTag(fn_info.cc, target).?, |
| 5501 | 5504 | try attributes.finish(&o.builder), |
| ... | ... | @@ -5734,7 +5737,7 @@ pub const FuncGen = struct { |
| 5734 | 5737 | const llvm_va_list_ty = try o.lowerType(va_list_ty); |
| 5735 | 5738 | |
| 5736 | 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); | |
| 5738 | 5741 | |
| 5739 | 5742 | _ = try self.wip.callIntrinsic(.normal, .none, .va_copy, &.{dest_list.typeOfWip(&self.wip)}, &.{ dest_list, src_list }, ""); |
| 5740 | 5743 | return if (isByRef(va_list_ty, zcu)) |
| ... | ... | @@ -5759,7 +5762,7 @@ pub const FuncGen = struct { |
| 5759 | 5762 | const llvm_va_list_ty = try o.lowerType(va_list_ty); |
| 5760 | 5763 | |
| 5761 | 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); | |
| 5763 | 5766 | |
| 5764 | 5767 | _ = try self.wip.callIntrinsic(.normal, .none, .va_start, &.{dest_list.typeOfWip(&self.wip)}, &.{dest_list}, ""); |
| 5765 | 5768 | return if (isByRef(va_list_ty, zcu)) |
| ... | ... | @@ -8037,7 +8040,7 @@ pub const FuncGen = struct { |
| 8037 | 8040 | self.ret_ptr |
| 8038 | 8041 | else brk: { |
| 8039 | 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 | 8044 | break :brk optional_ptr; |
| 8042 | 8045 | }; |
| 8043 | 8046 | |
| ... | ... | @@ -8074,7 +8077,7 @@ pub const FuncGen = struct { |
| 8074 | 8077 | self.ret_ptr |
| 8075 | 8078 | else brk: { |
| 8076 | 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 | 8081 | break :brk result_ptr; |
| 8079 | 8082 | }; |
| 8080 | 8083 | |
| ... | ... | @@ -8113,7 +8116,7 @@ pub const FuncGen = struct { |
| 8113 | 8116 | self.ret_ptr |
| 8114 | 8117 | else brk: { |
| 8115 | 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 | 8120 | break :brk result_ptr; |
| 8118 | 8121 | }; |
| 8119 | 8122 | |
| ... | ... | @@ -8647,7 +8650,7 @@ pub const FuncGen = struct { |
| 8647 | 8650 | |
| 8648 | 8651 | if (isByRef(inst_ty, zcu)) { |
| 8649 | 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 | 8655 | const field_ptr = try self.wip.gepStruct(llvm_inst_ty, alloca_inst, result_index, ""); |
| 8653 | 8656 | _ = try self.wip.store(.normal, result_val, field_ptr, result_alignment); |
| ... | ... | @@ -9007,7 +9010,7 @@ pub const FuncGen = struct { |
| 9007 | 9010 | |
| 9008 | 9011 | if (isByRef(dest_ty, zcu)) { |
| 9009 | 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 | 9015 | const field_ptr = try self.wip.gepStruct(llvm_dest_ty, alloca_inst, result_index, ""); |
| 9013 | 9016 | _ = try self.wip.store(.normal, result, field_ptr, result_alignment); |
| ... | ... | @@ -9432,7 +9435,7 @@ pub const FuncGen = struct { |
| 9432 | 9435 | return self.ng.todo("implement bitcast vector to non-ref array", .{}); |
| 9433 | 9436 | } |
| 9434 | 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 | 9439 | const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8; |
| 9437 | 9440 | if (bitcast_ok) { |
| 9438 | 9441 | _ = try self.wip.store(.normal, operand, array_ptr, alignment); |
| ... | ... | @@ -9493,7 +9496,7 @@ pub const FuncGen = struct { |
| 9493 | 9496 | |
| 9494 | 9497 | if (result_is_ref) { |
| 9495 | 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 | 9500 | _ = try self.wip.store(.normal, operand, result_ptr, alignment); |
| 9498 | 9501 | return result_ptr; |
| 9499 | 9502 | } |
| ... | ... | @@ -9506,7 +9509,7 @@ pub const FuncGen = struct { |
| 9506 | 9509 | // but LLVM won't let us bitcast struct values or vectors with padding bits. |
| 9507 | 9510 | // Therefore, we store operand to alloca, then load for result. |
| 9508 | 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 | 9513 | _ = try self.wip.store(.normal, operand, result_ptr, alignment); |
| 9511 | 9514 | return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, ""); |
| 9512 | 9515 | } |
| ... | ... | @@ -9615,9 +9618,9 @@ pub const FuncGen = struct { |
| 9615 | 9618 | if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) |
| 9616 | 9619 | return (try o.lowerPtrToVoid(ptr_ty)).toValue(); |
| 9617 | 9620 | |
| 9618 | //const pointee_llvm_ty = try o.lowerType(pointee_type); | |
| 9621 | const pointee_llvm_ty = try o.lowerType(pointee_type); | |
| 9619 | 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 | } |
| 9622 | 9625 | |
| 9623 | 9626 | fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| ... | ... | @@ -9629,9 +9632,9 @@ pub const FuncGen = struct { |
| 9629 | 9632 | if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) |
| 9630 | 9633 | return (try o.lowerPtrToVoid(ptr_ty)).toValue(); |
| 9631 | 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 | 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 | } |
| 9636 | 9639 | |
| 9637 | 9640 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| ... | ... | @@ -9645,16 +9648,6 @@ pub const FuncGen = struct { |
| 9645 | 9648 | return buildAllocaInner(&self.wip, llvm_ty, alignment, target); |
| 9646 | 9649 | } |
| 9647 | 9650 | |
| 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 | 9651 | fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { |
| 9659 | 9652 | const o = self.ng.object; |
| 9660 | 9653 | const pt = o.pt; |
| ... | ... | @@ -10693,7 +10686,7 @@ pub const FuncGen = struct { |
| 10693 | 10686 | const llvm_result_ty = accum_init.typeOfWip(&self.wip); |
| 10694 | 10687 | |
| 10695 | 10688 | // 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 | 10690 | _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default); |
| 10698 | 10691 | const accum_ptr = try self.buildAlloca(llvm_result_ty, .default); |
| 10699 | 10692 | _ = try self.wip.store(.normal, accum_init, accum_ptr, .default); |
| ... | ... | @@ -10906,7 +10899,7 @@ pub const FuncGen = struct { |
| 10906 | 10899 | // TODO in debug builds init to undef so that the padding will be 0xaa |
| 10907 | 10900 | // even if we fully populate the fields. |
| 10908 | 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); | |
| 10910 | 10903 | |
| 10911 | 10904 | for (elements, 0..) |elem, i| { |
| 10912 | 10905 | if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue; |
| ... | ... | @@ -10943,7 +10936,7 @@ pub const FuncGen = struct { |
| 10943 | 10936 | const llvm_usize = try o.lowerType(Type.usize); |
| 10944 | 10937 | const usize_zero = try o.builder.intValue(llvm_usize, 0); |
| 10945 | 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); | |
| 10947 | 10940 | |
| 10948 | 10941 | const array_info = result_ty.arrayInfo(zcu); |
| 10949 | 10942 | const elem_ptr_ty = try pt.ptrType(.{ |
| ... | ... | @@ -11018,7 +11011,7 @@ pub const FuncGen = struct { |
| 11018 | 11011 | // We must construct the correct unnamed struct type here, in order to then set |
| 11019 | 11012 | // the fields appropriately. |
| 11020 | 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 | 11015 | const llvm_payload = try self.resolveInst(extra.init); |
| 11023 | 11016 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); |
| 11024 | 11017 | const field_llvm_ty = try o.lowerType(field_ty); |
| ... | ... | @@ -11315,7 +11308,7 @@ pub const FuncGen = struct { |
| 11315 | 11308 | |
| 11316 | 11309 | if (isByRef(optional_ty, zcu)) { |
| 11317 | 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); | |
| 11319 | 11312 | |
| 11320 | 11313 | { |
| 11321 | 11314 | const field_ptr = try self.wip.gepStruct(optional_llvm_ty, alloca_inst, 0, ""); |
| ... | ... | @@ -11458,10 +11451,10 @@ pub const FuncGen = struct { |
| 11458 | 11451 | ) !Builder.Value { |
| 11459 | 11452 | const o = fg.ng.object; |
| 11460 | 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 | 11455 | const result_align = InternPool.Alignment.fromLlvm(ptr_alignment) |
| 11463 | 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 | 11458 | const size_bytes = pointee_type.abiSize(pt.zcu); |
| 11466 | 11459 | _ = try fg.wip.callMemCpy( |
| 11467 | 11460 | result_ptr, |
| ... | ... | @@ -11522,7 +11515,7 @@ pub const FuncGen = struct { |
| 11522 | 11515 | |
| 11523 | 11516 | if (isByRef(elem_ty, zcu)) { |
| 11524 | 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); | |
| 11526 | 11519 | |
| 11527 | 11520 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); |
| 11528 | 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 | 11871 | } |
| 11879 | 11872 | return switch (cc_tag) { |
| 11880 | 11873 | .@"inline" => unreachable, |
| 11881 | .auto, .@"async" => .fastcc, | |
| 11874 | .auto, .async => .fastcc, | |
| 11882 | 11875 | .naked => .ccc, |
| 11883 | 11876 | .x86_64_sysv => .x86_64_sysvcc, |
| 11884 | 11877 | .x86_64_win => .win64cc, |
| ... | ... | @@ -12386,7 +12379,7 @@ const ParamTypeIterator = struct { |
| 12386 | 12379 | return .byval; |
| 12387 | 12380 | } |
| 12388 | 12381 | }, |
| 12389 | .@"async" => { | |
| 12382 | .async => { | |
| 12390 | 12383 | @panic("TODO implement async function lowering in the LLVM backend"); |
| 12391 | 12384 | }, |
| 12392 | 12385 | .x86_64_sysv => return it.nextSystemV(ty), |
| ... | ... | @@ -12641,7 +12634,7 @@ fn ccAbiPromoteInt( |
| 12641 | 12634 | ) ?std.builtin.Signedness { |
| 12642 | 12635 | const target = zcu.getTarget(); |
| 12643 | 12636 | switch (cc) { |
| 12644 | .auto, .@"inline", .@"async" => return null, | |
| 12637 | .auto, .@"inline", .async => return null, | |
| 12645 | 12638 | else => {}, |
| 12646 | 12639 | } |
| 12647 | 12640 | const int_info = switch (ty.zigTypeTag(zcu)) { |
src/codegen/llvm/bindings.zig+1| ... | ... | @@ -79,6 +79,7 @@ pub const TargetMachine = opaque { |
| 79 | 79 | data_sections: bool, |
| 80 | 80 | float_abi: FloatABI, |
| 81 | 81 | abi_name: ?[*:0]const u8, |
| 82 | emulated_tls: bool, | |
| 82 | 83 | ) *TargetMachine; |
| 83 | 84 | |
| 84 | 85 | pub const dispose = LLVMDisposeTargetMachine; |
src/codegen/mips/abi.zig created+84| ... | ... | @@ -0,0 +1,84 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Type = @import("../../Type.zig"); | |
| 3 | const Zcu = @import("../../Zcu.zig"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | pub const Class = union(enum) { | |
| 7 | memory, | |
| 8 | byval, | |
| 9 | i32_array: u8, | |
| 10 | }; | |
| 11 | ||
| 12 | pub const Context = enum { ret, arg }; | |
| 13 | ||
| 14 | pub 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 | 1259 | } |
| 1260 | 1260 | |
| 1261 | 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 | 1263 | var aw: std.io.Writer.Allocating = .init(self.gpa); |
| 1264 | 1264 | defer aw.deinit(); |
| 1265 | ty.print(&aw.interface, self.pt) catch return error.OutOfMemory; | |
| 1266 | return aw.toOwnedSlice(); | |
| 1265 | ty.print(&aw.writer, self.pt) catch |err| switch (err) { | |
| 1266 | error.WriteFailed => return error.OutOfMemory, | |
| 1267 | }; | |
| 1268 | return try aw.toOwnedSlice(); | |
| 1267 | 1269 | } |
| 1268 | 1270 | |
| 1269 | 1271 | /// Create an integer type suitable for storing at least 'bits' bits. |
src/codegen/spirv/spec.zig+4-4| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | //! This file is auto-generated by tools/gen_spirv_spec.zig. |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const assert = std.debug.assert; | |
| 4 | 5 | |
| 5 | 6 | pub const Version = packed struct(Word) { |
| 6 | 7 | padding: u8 = 0, |
| ... | ... | @@ -18,11 +19,10 @@ pub const IdResult = enum(Word) { |
| 18 | 19 | none, |
| 19 | 20 | _, |
| 20 | 21 | |
| 21 | pub fn format(self: IdResult, bw: *std.io.Writer, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 22 | comptime std.debug.assert(fmt.len == 0); | |
| 22 | pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 23 | 23 | switch (self) { |
| 24 | .none => try bw.writeAll("(none)"), | |
| 25 | else => try bw.print("%{}", .{@intFromEnum(self)}), | |
| 24 | .none => try writer.writeAll("(none)"), | |
| 25 | else => try writer.print("%{d}", .{@intFromEnum(self)}), | |
| 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 | ||
| 7 | const std = @import("std"); | |
| 8 | const Target = std.Target; | |
| 9 | const assert = std.debug.assert; | |
| 10 | ||
| 11 | const Type = @import("../../Type.zig"); | |
| 12 | const 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. | |
| 16 | pub 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. | |
| 23 | pub 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 | ||
| 85 | pub 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 | 15 | count: usize, |
| 16 | 16 | |
| 17 | 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 | |
| 18 | 26 | |
| 19 | 27 | pub fn init(allocator: Allocator) Self { |
| 20 | 28 | return .{ |
| ... | ... | @@ -160,7 +168,7 @@ pub fn LinearFifo(comptime T: type) type { |
| 160 | 168 | } |
| 161 | 169 | |
| 162 | 170 | /// 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 | 172 | fn readFn(self: *Self, dest: []u8) error{}!usize { |
| 165 | 173 | return self.read(dest); |
| 166 | 174 | } |
| ... | ... | @@ -241,7 +249,7 @@ pub fn LinearFifo(comptime T: type) type { |
| 241 | 249 | } |
| 242 | 250 | |
| 243 | 251 | /// 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 | 253 | fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize { |
| 246 | 254 | try self.write(bytes); |
| 247 | 255 | return bytes.len; |
src/dev.zig+1| ... | ... | @@ -154,6 +154,7 @@ pub const Env = enum { |
| 154 | 154 | else => Env.ast_gen.supports(feature), |
| 155 | 155 | }, |
| 156 | 156 | .cbe => switch (feature) { |
| 157 | .legalize, | |
| 157 | 158 | .c_backend, |
| 158 | 159 | .c_linker, |
| 159 | 160 | => true, |
src/libs/freebsd.zig+2-2| ... | ... | @@ -497,13 +497,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 497 | 497 | .lt => continue, |
| 498 | 498 | .gt => { |
| 499 | 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 | 501 | return error.InvalidTargetLibCVersion; |
| 502 | 502 | }, |
| 503 | 503 | } |
| 504 | 504 | } else blk: { |
| 505 | 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 | 507 | target_version, metadata.all_versions[latest_index], |
| 508 | 508 | }); |
| 509 | 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 | 325 | // See the `-fno-exceptions` logic for WASI. |
| 326 | 326 | // The old 32-bit x86 variant of SEH doesn't use tables. |
| 327 | 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; | |
| 329 | 329 | |
| 330 | 330 | const config = Compilation.Config.resolve(.{ |
| 331 | 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 | 48 | const optimize_mode = comp.compilerRtOptMode(); |
| 49 | 49 | const strip = comp.compilerRtStrip(); |
| 50 | 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 | 52 | const link_libcpp = target.os.tag.isDarwin(); |
| 53 | 53 | |
| 54 | 54 | const config = Compilation.Config.resolve(.{ |
| ... | ... | @@ -268,7 +268,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo |
| 268 | 268 | const skip_linker_dependencies = !target.os.tag.isDarwin(); |
| 269 | 269 | const linker_allow_shlib_undefined = target.os.tag.isDarwin(); |
| 270 | 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 | 272 | else |
| 273 | 273 | null; |
| 274 | 274 | // 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 | 29 | const output_mode = .Lib; |
| 30 | 30 | const target = &comp.root_mod.resolved_target.result; |
| 31 | 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 | 33 | const config = Compilation.Config.resolve(.{ |
| 34 | 34 | .output_mode = output_mode, |
| 35 | 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 | 29 | const target = comp.getTarget(); |
| 30 | 30 | |
| 31 | 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; | |
| 33 | 33 | |
| 34 | 34 | switch (crt_file) { |
| 35 | 35 | .crt2_o => { |
| ... | ... | @@ -325,7 +325,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 325 | 325 | |
| 326 | 326 | for (aro_comp.diagnostics.list.items) |diagnostic| { |
| 327 | 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 | 329 | return error.AroPreprocessorFailed; |
| 330 | 330 | } |
| 331 | 331 | } |
| ... | ... | @@ -334,7 +334,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 334 | 334 | // new scope to ensure definition file is written before passing the path to WriteImportLibrary |
| 335 | 335 | const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true }); |
| 336 | 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 | } |
| 339 | 339 | |
| 340 | 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 | 930 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S", |
| 931 | 931 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c", |
| 932 | 932 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c", |
| 933 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S", | |
| 934 | 933 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c", |
| 935 | 934 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c", |
| 936 | 935 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S", |
| ... | ... | @@ -974,7 +973,6 @@ const mingw32_x86_32_src = [_][]const u8{ |
| 974 | 973 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c", |
| 975 | 974 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c", |
| 976 | 975 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S", |
| 977 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S", | |
| 978 | 976 | "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c", |
| 979 | 977 | }; |
| 980 | 978 | |
| ... | ... | @@ -1013,6 +1011,7 @@ const mingw32_winpthreads_src = [_][]const u8{ |
| 1013 | 1011 | "winpthreads" ++ path.sep_str ++ "thread.c", |
| 1014 | 1012 | }; |
| 1015 | 1013 | |
| 1014 | // Note: kernel32 and ntdll are always linked even without targeting MinGW-w64. | |
| 1016 | 1015 | pub const always_link_libs = [_][]const u8{ |
| 1017 | 1016 | "api-ms-win-crt-conio-l1-1-0", |
| 1018 | 1017 | "api-ms-win-crt-convert-l1-1-0", |
| ... | ... | @@ -1030,8 +1029,6 @@ pub const always_link_libs = [_][]const u8{ |
| 1030 | 1029 | "api-ms-win-crt-time-l1-1-0", |
| 1031 | 1030 | "api-ms-win-crt-utility-l1-1-0", |
| 1032 | 1031 | "advapi32", |
| 1033 | "kernel32", | |
| 1034 | "ntdll", | |
| 1035 | 1032 | "shell32", |
| 1036 | 1033 | "user32", |
| 1037 | 1034 | }; |
src/libs/musl.zig-12| ... | ... | @@ -821,8 +821,6 @@ const src_files = [_][]const u8{ |
| 821 | 821 | "musl/src/malloc/replaced.c", |
| 822 | 822 | "musl/src/math/aarch64/ceil.c", |
| 823 | 823 | "musl/src/math/aarch64/ceilf.c", |
| 824 | "musl/src/math/aarch64/floor.c", | |
| 825 | "musl/src/math/aarch64/floorf.c", | |
| 826 | 824 | "musl/src/math/aarch64/fma.c", |
| 827 | 825 | "musl/src/math/aarch64/fmaf.c", |
| 828 | 826 | "musl/src/math/aarch64/fmax.c", |
| ... | ... | @@ -912,9 +910,6 @@ const src_files = [_][]const u8{ |
| 912 | 910 | "musl/src/math/fdiml.c", |
| 913 | 911 | "musl/src/math/finite.c", |
| 914 | 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 | 913 | "musl/src/math/fma.c", |
| 919 | 914 | "musl/src/math/fmaf.c", |
| 920 | 915 | "musl/src/math/fmal.c", |
| ... | ... | @@ -955,8 +950,6 @@ const src_files = [_][]const u8{ |
| 955 | 950 | "musl/src/math/i386/exp_ld.s", |
| 956 | 951 | "musl/src/math/i386/expl.s", |
| 957 | 952 | "musl/src/math/i386/expm1l.s", |
| 958 | "musl/src/math/i386/floorf.s", | |
| 959 | "musl/src/math/i386/floorl.s", | |
| 960 | 953 | "musl/src/math/i386/floor.s", |
| 961 | 954 | "musl/src/math/i386/fmod.c", |
| 962 | 955 | "musl/src/math/i386/fmodf.c", |
| ... | ... | @@ -1089,8 +1082,6 @@ const src_files = [_][]const u8{ |
| 1089 | 1082 | "musl/src/math/pow_data.c", |
| 1090 | 1083 | "musl/src/math/powerpc64/ceil.c", |
| 1091 | 1084 | "musl/src/math/powerpc64/ceilf.c", |
| 1092 | "musl/src/math/powerpc64/floor.c", | |
| 1093 | "musl/src/math/powerpc64/floorf.c", | |
| 1094 | 1085 | "musl/src/math/powerpc64/fma.c", |
| 1095 | 1086 | "musl/src/math/powerpc64/fmaf.c", |
| 1096 | 1087 | "musl/src/math/powerpc64/fmax.c", |
| ... | ... | @@ -1153,9 +1144,6 @@ const src_files = [_][]const u8{ |
| 1153 | 1144 | "musl/src/math/s390x/ceil.c", |
| 1154 | 1145 | "musl/src/math/s390x/ceilf.c", |
| 1155 | 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 | 1147 | "musl/src/math/s390x/fma.c", |
| 1160 | 1148 | "musl/src/math/s390x/fmaf.c", |
| 1161 | 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 | 442 | .lt => continue, |
| 443 | 443 | .gt => { |
| 444 | 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 | 446 | return error.InvalidTargetLibCVersion; |
| 447 | 447 | }, |
| 448 | 448 | } |
| 449 | 449 | } else blk: { |
| 450 | 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 | 452 | target_version, metadata.all_versions[latest_index], |
| 453 | 453 | }); |
| 454 | 454 | break :blk latest_index; |
src/libs/wasi_libc.zig+75-110| ... | ... | @@ -10,43 +10,8 @@ pub const CrtFile = enum { |
| 10 | 10 | crt1_reactor_o, |
| 11 | 11 | crt1_command_o, |
| 12 | 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 | }; |
| 19 | 14 | |
| 20 | pub 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 | ||
| 39 | pub 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 | ||
| 50 | 15 | pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile { |
| 51 | 16 | return switch (wasi_exec_model) { |
| 52 | 17 | .reactor => CrtFile.crt1_reactor_o, |
| ... | ... | @@ -157,87 +122,57 @@ pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progre |
| 157 | 122 | } |
| 158 | 123 | } |
| 159 | 124 | |
| 160 | try comp.build_crt_file("c", .Lib, .@"wasi libc.a", prog_node, libc_sources.items, .{}); | |
| 161 | }, | |
| 162 | ||
| 163 | .libdl_a => { | |
| 164 | var args = std.ArrayList([]const u8).init(arena); | |
| 165 | try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); | |
| 166 | try addLibcBottomHalfIncludes(comp, arena, &args); | |
| 125 | { | |
| 126 | // Compile libdl. | |
| 127 | var args = std.ArrayList([]const u8).init(arena); | |
| 128 | try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); | |
| 129 | try addLibcBottomHalfIncludes(comp, arena, &args); | |
| 167 | 130 | |
| 168 | var emu_dl_sources = std.ArrayList(Compilation.CSourceFile).init(arena); | |
| 169 | for (emulated_dl_src_files) |file_path| { | |
| 170 | try emu_dl_sources.append(.{ | |
| 171 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ | |
| 172 | "libc", try sanitize(arena, file_path), | |
| 173 | }), | |
| 174 | .extra_flags = args.items, | |
| 175 | .owner = undefined, | |
| 176 | }); | |
| 131 | for (emulated_dl_src_files) |file_path| { | |
| 132 | try libc_sources.append(.{ | |
| 133 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ | |
| 134 | "libc", try sanitize(arena, file_path), | |
| 135 | }), | |
| 136 | .extra_flags = args.items, | |
| 137 | .owner = undefined, | |
| 138 | }); | |
| 139 | } | |
| 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); | |
| 185 | 141 | |
| 186 | var emu_clocks_sources = std.ArrayList(Compilation.CSourceFile).init(arena); | |
| 187 | for (emulated_process_clocks_src_files) |file_path| { | |
| 188 | try emu_clocks_sources.append(.{ | |
| 189 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ | |
| 190 | "libc", try sanitize(arena, file_path), | |
| 142 | { | |
| 143 | // Compile libwasi-emulated-process-clocks. | |
| 144 | var args = std.ArrayList([]const u8).init(arena); | |
| 145 | try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); | |
| 146 | try args.appendSlice(&.{ | |
| 147 | "-I", | |
| 148 | try comp.dirs.zig_lib.join(arena, &.{ | |
| 149 | "libc", | |
| 150 | "wasi", | |
| 151 | "libc-bottom-half", | |
| 152 | "cloudlibc", | |
| 153 | "src", | |
| 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); | |
| 202 | 156 | |
| 203 | var emu_getpid_sources = std.ArrayList(Compilation.CSourceFile).init(arena); | |
| 204 | for (emulated_getpid_src_files) |file_path| { | |
| 205 | try emu_getpid_sources.append(.{ | |
| 206 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ | |
| 207 | "libc", try sanitize(arena, file_path), | |
| 208 | }), | |
| 209 | .extra_flags = args.items, | |
| 210 | .owner = undefined, | |
| 211 | }); | |
| 212 | } | |
| 213 | try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items, .{}); | |
| 214 | }, | |
| 215 | .libwasi_emulated_mman_a => { | |
| 216 | var args = std.ArrayList([]const u8).init(arena); | |
| 217 | try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); | |
| 218 | try addLibcBottomHalfIncludes(comp, arena, &args); | |
| 219 | ||
| 220 | var emu_mman_sources = std.ArrayList(Compilation.CSourceFile).init(arena); | |
| 221 | for (emulated_mman_src_files) |file_path| { | |
| 222 | try emu_mman_sources.append(.{ | |
| 223 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ | |
| 224 | "libc", try sanitize(arena, file_path), | |
| 225 | }), | |
| 226 | .extra_flags = args.items, | |
| 227 | .owner = undefined, | |
| 228 | }); | |
| 157 | for (emulated_process_clocks_src_files) |file_path| { | |
| 158 | try libc_sources.append(.{ | |
| 159 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ | |
| 160 | "libc", try sanitize(arena, file_path), | |
| 161 | }), | |
| 162 | .extra_flags = args.items, | |
| 163 | .owner = undefined, | |
| 164 | }); | |
| 165 | } | |
| 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); | |
| 234 | 167 | |
| 235 | 168 | { |
| 169 | // Compile libwasi-emulated-getpid. | |
| 236 | 170 | var args = std.ArrayList([]const u8).init(arena); |
| 237 | 171 | try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); |
| 172 | try addLibcBottomHalfIncludes(comp, arena, &args); | |
| 238 | 173 | |
| 239 | for (emulated_signal_bottom_half_src_files) |file_path| { | |
| 240 | try emu_signal_sources.append(.{ | |
| 174 | for (emulated_getpid_src_files) |file_path| { | |
| 175 | try libc_sources.append(.{ | |
| 241 | 176 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ |
| 242 | 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 | 183 | } |
| 249 | 184 | |
| 250 | 185 | { |
| 186 | // Compile libwasi-emulated-mman. | |
| 251 | 187 | var args = std.ArrayList([]const u8).init(arena); |
| 252 | 188 | try addCCArgs(comp, arena, &args, .{ .want_O3 = true }); |
| 253 | try addLibcTopHalfIncludes(comp, arena, &args); | |
| 254 | try args.append("-D_WASI_EMULATED_SIGNAL"); | |
| 189 | try addLibcBottomHalfIncludes(comp, arena, &args); | |
| 255 | 190 | |
| 256 | for (emulated_signal_top_half_src_files) |file_path| { | |
| 257 | try emu_signal_sources.append(.{ | |
| 191 | for (emulated_mman_src_files) |file_path| { | |
| 192 | try libc_sources.append(.{ | |
| 258 | 193 | .src_path = try comp.dirs.zig_lib.join(arena, &.{ |
| 259 | 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 | 199 | } |
| 265 | 200 | } |
| 266 | 201 | |
| 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 | 720 | "musl/src/math/fdiml.c", |
| 755 | 721 | "musl/src/math/finite.c", |
| 756 | 722 | "musl/src/math/finitef.c", |
| 757 | "musl/src/math/floorl.c", | |
| 758 | 723 | "musl/src/math/fma.c", |
| 759 | 724 | "musl/src/math/fmaf.c", |
| 760 | 725 | "musl/src/math/fmaxl.c", |
src/link.zig+8-7| ... | ... | @@ -838,8 +838,10 @@ pub const File = struct { |
| 838 | 838 | const cached_pp_file_path = the_key.status.success.object_path; |
| 839 | 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 | 840 | const diags = &base.comp.link_diags; |
| 841 | return diags.fail("failed to copy '{f'}' to '{f'}': {s}", .{ | |
| 842 | @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err), | |
| 841 | return diags.fail("failed to copy '{f}' to '{f}': {s}", .{ | |
| 842 | std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar), | |
| 843 | std.fmt.alt(@as(Path, emit), .formatEscapeChar), | |
| 844 | @errorName(err), | |
| 843 | 845 | }); |
| 844 | 846 | }; |
| 845 | 847 | return; |
| ... | ... | @@ -2095,8 +2097,8 @@ fn resolvePathInputLib( |
| 2095 | 2097 | }) { |
| 2096 | 2098 | var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { |
| 2097 | 2099 | error.FileNotFound => return .no_match, |
| 2098 | else => |e| fatal("unable to search for {s} library '{f'}': {s}", .{ | |
| 2099 | @tagName(link_mode), test_path, @errorName(e), | |
| 2100 | else => |e| fatal("unable to search for {s} library '{f}': {s}", .{ | |
| 2101 | @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e), | |
| 2100 | 2102 | }), |
| 2101 | 2103 | }; |
| 2102 | 2104 | errdefer file.close(); |
| ... | ... | @@ -2105,9 +2107,8 @@ fn resolvePathInputLib( |
| 2105 | 2107 | var br = fr.interface().unbuffered(); |
| 2106 | 2108 | ok: { |
| 2107 | 2109 | br.readSlice(ld_script_bytes.items) catch |err| switch (err) { |
| 2108 | error.ReadFailed => fatal("failed to read '{f'}': {s}", .{ | |
| 2109 | test_path, | |
| 2110 | @errorName(fr.err.?), | |
| 2110 | error.ReadFailed => fatal("failed to read '{f}': {s}", .{ | |
| 2111 | test_path, @errorName(fr.err.?), | |
| 2111 | 2112 | }), |
| 2112 | 2113 | error.EndOfStream => break :ok, |
| 2113 | 2114 | }; |
src/link/C.zig+40-32| ... | ... | @@ -63,6 +63,14 @@ const String = extern struct { |
| 63 | 63 | .start = 0, |
| 64 | 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 | }; |
| 67 | 75 | |
| 68 | 76 | /// Per-declaration data. |
| ... | ... | @@ -205,8 +213,10 @@ pub fn updateFunc( |
| 205 | 213 | .ctype_pool = mir.c.ctype_pool.move(), |
| 206 | 214 | .lazy_fns = mir.c.lazy_fns.move(), |
| 207 | 215 | }; |
| 208 | gop.value_ptr.fwd_decl = try self.addString(&.{&function.object.dg.fwd_decl}); | |
| 209 | gop.value_ptr.code = try self.addString(&.{ &function.object.code_header, &function.object.code }); | |
| 216 | gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl); | |
| 217 | const code_header = try self.addString(mir.c.code_header); | |
| 218 | const code = try self.addString(mir.c.code); | |
| 219 | gop.value_ptr.code = code_header.concat(code); | |
| 210 | 220 | try self.addUavsFromCodegen(&mir.c.uavs); |
| 211 | 221 | } |
| 212 | 222 | |
| ... | ... | @@ -232,8 +242,8 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void { |
| 232 | 242 | .code = undefined, |
| 233 | 243 | .indent_counter = 0, |
| 234 | 244 | }; |
| 235 | object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); | |
| 236 | object.code.initOwnedSlice(gpa, self.code_buf); | |
| 245 | object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); | |
| 246 | object.code = .initOwnedSlice(gpa, self.code_buf); | |
| 237 | 247 | defer { |
| 238 | 248 | object.dg.uavs.deinit(gpa); |
| 239 | 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 | 269 | |
| 260 | 270 | object.dg.ctype_pool.freeUnusedCapacity(gpa); |
| 261 | 271 | self.uavs.values()[i] = .{ |
| 262 | .fwd_decl = try self.addString(&.{&object.dg.fwd_decl}), | |
| 263 | .code = try self.addString(&.{&object.code}), | |
| 272 | .fwd_decl = try self.addString(object.dg.fwd_decl.getWritten()), | |
| 273 | .code = try self.addString(object.code.getWritten()), | |
| 264 | 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 | 317 | .code = undefined, |
| 308 | 318 | .indent_counter = 0, |
| 309 | 319 | }; |
| 310 | object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); | |
| 311 | object.code.initOwnedSlice(gpa, self.code_buf); | |
| 320 | object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); | |
| 321 | object.code = .initOwnedSlice(gpa, self.code_buf); | |
| 312 | 322 | defer { |
| 313 | 323 | object.dg.uavs.deinit(gpa); |
| 314 | 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 | 336 | }, |
| 327 | 337 | error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, |
| 328 | 338 | }; |
| 329 | gop.value_ptr.fwd_decl = try self.addString(&.{&object.dg.fwd_decl}); | |
| 330 | gop.value_ptr.code = try self.addString(&.{&object.code}); | |
| 339 | gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.getWritten()); | |
| 340 | gop.value_ptr.code = try self.addString(object.code.getWritten()); | |
| 331 | 341 | try self.addUavsFromCodegen(&object.dg.uavs); |
| 332 | 342 | } |
| 333 | 343 | |
| ... | ... | @@ -339,12 +349,12 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn |
| 339 | 349 | _ = ti_id; |
| 340 | 350 | } |
| 341 | 351 | |
| 342 | fn abiDefines(bw: *std.io.BufferedWriter, target: std.Target) !void { | |
| 352 | fn abiDefines(w: *std.io.Writer, target: *const std.Target) !void { | |
| 343 | 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 | 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 | 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 | 401 | }; |
| 392 | 402 | defer f.deinit(gpa); |
| 393 | 403 | |
| 394 | var abi_defines_aw: std.io.AllocatingWriter = undefined; | |
| 395 | abi_defines_aw.init(gpa); | |
| 404 | var abi_defines_aw: std.io.Writer.Allocating = .init(gpa); | |
| 396 | 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 | 407 | error.WriteFailed => return error.OutOfMemory, |
| 399 | 408 | }; |
| 400 | 409 | |
| ... | ... | @@ -407,10 +416,9 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P |
| 407 | 416 | const ctypes_index = f.all_buffers.items.len; |
| 408 | 417 | f.all_buffers.items.len += 1; |
| 409 | 418 | |
| 410 | var asm_aw: std.io.AllocatingWriter = undefined; | |
| 411 | asm_aw.init(gpa); | |
| 419 | var asm_aw: std.io.Writer.Allocating = .init(gpa); | |
| 412 | 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 | 422 | error.WriteFailed => return error.OutOfMemory, |
| 415 | 423 | }; |
| 416 | 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 | 509 | |
| 502 | 510 | const file = self.base.file.?; |
| 503 | 511 | file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)}); |
| 504 | var fw = file.writer(); | |
| 505 | var bw = fw.interface().unbuffered(); | |
| 506 | bw.writeVecAll(f.all_buffers.items) catch |err| switch (err) { | |
| 507 | error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{ | |
| 508 | self.base.emit, @errorName(fw.err.?), | |
| 512 | var fw = file.writer(&.{}); | |
| 513 | var w = &fw.interface; | |
| 514 | w.writeVecAll(f.all_buffers.items) catch |err| switch (err) { | |
| 515 | error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{ | |
| 516 | std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?), | |
| 509 | 517 | }), |
| 510 | 518 | }; |
| 511 | 519 | } |
| ... | ... | @@ -575,8 +583,8 @@ fn flushCTypes( |
| 575 | 583 | try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len); |
| 576 | 584 | defer global_from_decl_map.clearRetainingCapacity(); |
| 577 | 585 | |
| 578 | var ctypes_aw: std.io.AllocatingWriter = undefined; | |
| 579 | const ctypes_bw = ctypes_aw.fromArrayList(gpa, &f.ctypes); | |
| 586 | var ctypes_aw: std.io.Writer.Allocating = .fromArrayList(gpa, &f.ctypes); | |
| 587 | const ctypes_bw = &ctypes_aw.writer; | |
| 580 | 588 | defer f.ctypes = ctypes_aw.toArrayList(); |
| 581 | 589 | |
| 582 | 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 | 648 | .code = undefined, |
| 641 | 649 | .indent_counter = 0, |
| 642 | 650 | }; |
| 643 | _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl); | |
| 644 | _ = object.code.fromArrayList(gpa, &f.lazy_code); | |
| 651 | object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl); | |
| 652 | object.code = .fromArrayList(gpa, &f.lazy_code); | |
| 645 | 653 | defer { |
| 646 | 654 | object.dg.uavs.deinit(gpa); |
| 647 | 655 | f.lazy_ctype_pool = object.dg.ctype_pool.move(); |
| ... | ... | @@ -688,8 +696,8 @@ fn flushLazyFn( |
| 688 | 696 | .code = undefined, |
| 689 | 697 | .indent_counter = 0, |
| 690 | 698 | }; |
| 691 | _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl); | |
| 692 | _ = object.code.fromArrayList(gpa, &f.lazy_code); | |
| 699 | object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl); | |
| 700 | object.code = .fromArrayList(gpa, &f.lazy_code); | |
| 693 | 701 | defer { |
| 694 | 702 | // If this assert trips just handle the anon_decl_deps the same as |
| 695 | 703 | // `updateFunc()` does. |
| ... | ... | @@ -830,7 +838,7 @@ pub fn updateExports( |
| 830 | 838 | .scratch = .initBuffer(self.scratch_buf), |
| 831 | 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 | 842 | defer { |
| 835 | 843 | assert(dg.uavs.count() == 0); |
| 836 | 844 | ctype_pool.* = dg.ctype_pool.move(); |
| ... | ... | @@ -842,7 +850,7 @@ pub fn updateExports( |
| 842 | 850 | codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) { |
| 843 | 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 | } |
| 847 | 855 | |
| 848 | 856 | pub fn deleteExport( |
src/link/Coff.zig+15-23| ... | ... | @@ -2623,7 +2623,7 @@ fn logSymtab(coff: *Coff) void { |
| 2623 | 2623 | .DEBUG => unreachable, // TODO |
| 2624 | 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 | 2627 | sym_id, |
| 2628 | 2628 | coff.getSymbolName(.{ .sym_index = @as(u32, @intCast(sym_id)), .file = null }), |
| 2629 | 2629 | sym.value, |
| ... | ... | @@ -3096,33 +3096,25 @@ const ImportTable = struct { |
| 3096 | 3096 | return base_vaddr + index * @sizeOf(u64); |
| 3097 | 3097 | } |
| 3098 | 3098 | |
| 3099 | const FormatContext = struct { | |
| 3099 | const Format = struct { | |
| 3100 | 3100 | itab: ImportTable, |
| 3101 | 3101 | ctx: Context, |
| 3102 | }; | |
| 3103 | 3102 | |
| 3104 | fn format(itab: ImportTable, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { | |
| 3105 | _ = itab; | |
| 3106 | _ = bw; | |
| 3107 | _ = unused_format_string; | |
| 3108 | @compileError("do not format ImportTable directly; use itab.fmtDebug()"); | |
| 3109 | } | |
| 3110 | ||
| 3111 | fn format2(fmt_ctx: FormatContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { | |
| 3112 | comptime assert(unused_format_string.len == 0); | |
| 3113 | const lib_name = fmt_ctx.ctx.coff.temp_strtab.getAssumeExists(fmt_ctx.ctx.name_off); | |
| 3114 | const base_vaddr = getBaseAddress(fmt_ctx.ctx); | |
| 3115 | try bw.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr }); | |
| 3116 | for (fmt_ctx.itab.entries.items, 0..) |entry, i| { | |
| 3117 | try bw.print("\n {d}@{?x} => {s}", .{ | |
| 3118 | i, | |
| 3119 | fmt_ctx.itab.getImportAddress(entry, fmt_ctx.ctx), | |
| 3120 | fmt_ctx.ctx.coff.getSymbolName(entry), | |
| 3121 | }); | |
| 3103 | fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 3104 | const lib_name = f.ctx.coff.temp_strtab.getAssumeExists(f.ctx.name_off); | |
| 3105 | const base_vaddr = getBaseAddress(f.ctx); | |
| 3106 | try writer.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr }); | |
| 3107 | for (f.itab.entries.items, 0..) |entry, i| { | |
| 3108 | try writer.print("\n {d}@{?x} => {s}", .{ | |
| 3109 | i, | |
| 3110 | f.itab.getImportAddress(entry, f.ctx), | |
| 3111 | f.ctx.coff.getSymbolName(entry), | |
| 3112 | }); | |
| 3113 | } | |
| 3122 | 3114 | } |
| 3123 | } | |
| 3115 | }; | |
| 3124 | 3116 | |
| 3125 | fn fmtDebug(itab: ImportTable, ctx: Context) fmt.Formatter(format2) { | |
| 3117 | fn fmtDebug(itab: ImportTable, ctx: Context) fmt.Formatter(Format, Format.default) { | |
| 3126 | 3118 | return .{ .data = .{ .itab = itab, .ctx = ctx } }; |
| 3127 | 3119 | } |
| 3128 | 3120 |
src/link/Dwarf.zig+4-4| ... | ... | @@ -2557,7 +2557,7 @@ fn initWipNavInner( |
| 2557 | 2557 | const addr: Loc = .{ .addr_reloc = sym_index }; |
| 2558 | 2558 | const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr; |
| 2559 | 2559 | switch (decl.kind) { |
| 2560 | .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable, | |
| 2560 | .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable, | |
| 2561 | 2561 | .@"const" => { |
| 2562 | 2562 | const const_ty_reloc_index = try wip_nav.refForward(); |
| 2563 | 2563 | try wip_nav.infoExprLoc(loc); |
| ... | ... | @@ -2834,7 +2834,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo |
| 2834 | 2834 | |
| 2835 | 2835 | const is_test = switch (decl.kind) { |
| 2836 | 2836 | .unnamed_test, .@"test", .decltest => true, |
| 2837 | .@"comptime", .@"usingnamespace", .@"const", .@"var" => false, | |
| 2837 | .@"comptime", .@"const", .@"var" => false, | |
| 2838 | 2838 | }; |
| 2839 | 2839 | if (is_test) { |
| 2840 | 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 | 3657 | // For better or worse, we try to match what Clang emits. |
| 3658 | 3658 | break :cc switch (func_type.cc) { |
| 3659 | 3659 | .@"inline" => .nocall, |
| 3660 | .@"async", .auto, .naked => .normal, | |
| 3660 | .async, .auto, .naked => .normal, | |
| 3661 | 3661 | .x86_64_sysv => .LLVM_X86_64SysV, |
| 3662 | 3662 | .x86_64_win => .LLVM_Win64, |
| 3663 | 3663 | .x86_64_regcall_v3_sysv => .LLVM_X86RegCall, |
| ... | ... | @@ -4301,7 +4301,7 @@ fn updateContainerTypeInner(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: Intern |
| 4301 | 4301 | }; |
| 4302 | 4302 | defer wip_nav.deinit(); |
| 4303 | 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 | 4305 | defer dwarf.gpa.free(name); |
| 4306 | 4306 | |
| 4307 | 4307 | switch (ip.indexToKey(type_index)) { |
src/link/Elf.zig+55-69| ... | ... | @@ -3870,22 +3870,21 @@ pub fn failFile( |
| 3870 | 3870 | return error.LinkFailure; |
| 3871 | 3871 | } |
| 3872 | 3872 | |
| 3873 | const FormatShdrCtx = struct { | |
| 3873 | const FormatShdr = struct { | |
| 3874 | 3874 | elf_file: *Elf, |
| 3875 | 3875 | shdr: elf.Elf64_Shdr, |
| 3876 | 3876 | }; |
| 3877 | 3877 | |
| 3878 | fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) { | |
| 3878 | fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(FormatShdr, formatShdr) { | |
| 3879 | 3879 | return .{ .data = .{ |
| 3880 | 3880 | .shdr = shdr, |
| 3881 | 3881 | .elf_file = self, |
| 3882 | 3882 | } }; |
| 3883 | 3883 | } |
| 3884 | 3884 | |
| 3885 | fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 3886 | _ = unused_fmt_string; | |
| 3885 | fn formatShdr(ctx: FormatShdr, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 3887 | 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 | 3888 | ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset, |
| 3890 | 3889 | shdr.sh_addr, shdr.sh_addralign, |
| 3891 | 3890 | shdr.sh_size, shdr.sh_entsize, |
| ... | ... | @@ -3893,74 +3892,68 @@ fn formatShdr(ctx: FormatShdrCtx, bw: *Writer, comptime unused_fmt_string: []con |
| 3893 | 3892 | }); |
| 3894 | 3893 | } |
| 3895 | 3894 | |
| 3896 | pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) { | |
| 3895 | pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(u64, formatShdrFlags) { | |
| 3897 | 3896 | return .{ .data = sh_flags }; |
| 3898 | 3897 | } |
| 3899 | 3898 | |
| 3900 | fn formatShdrFlags(sh_flags: u64, bw: *Writer, comptime unused_fmt_string: []const u8) !void { | |
| 3901 | _ = unused_fmt_string; | |
| 3899 | fn formatShdrFlags(sh_flags: u64, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 3902 | 3900 | if (elf.SHF_WRITE & sh_flags != 0) { |
| 3903 | try bw.writeByte('W'); | |
| 3901 | try writer.writeByte('W'); | |
| 3904 | 3902 | } |
| 3905 | 3903 | if (elf.SHF_ALLOC & sh_flags != 0) { |
| 3906 | try bw.writeByte('A'); | |
| 3904 | try writer.writeByte('A'); | |
| 3907 | 3905 | } |
| 3908 | 3906 | if (elf.SHF_EXECINSTR & sh_flags != 0) { |
| 3909 | try bw.writeByte('X'); | |
| 3907 | try writer.writeByte('X'); | |
| 3910 | 3908 | } |
| 3911 | 3909 | if (elf.SHF_MERGE & sh_flags != 0) { |
| 3912 | try bw.writeByte('M'); | |
| 3910 | try writer.writeByte('M'); | |
| 3913 | 3911 | } |
| 3914 | 3912 | if (elf.SHF_STRINGS & sh_flags != 0) { |
| 3915 | try bw.writeByte('S'); | |
| 3913 | try writer.writeByte('S'); | |
| 3916 | 3914 | } |
| 3917 | 3915 | if (elf.SHF_INFO_LINK & sh_flags != 0) { |
| 3918 | try bw.writeByte('I'); | |
| 3916 | try writer.writeByte('I'); | |
| 3919 | 3917 | } |
| 3920 | 3918 | if (elf.SHF_LINK_ORDER & sh_flags != 0) { |
| 3921 | try bw.writeByte('L'); | |
| 3919 | try writer.writeByte('L'); | |
| 3922 | 3920 | } |
| 3923 | 3921 | if (elf.SHF_EXCLUDE & sh_flags != 0) { |
| 3924 | try bw.writeByte('E'); | |
| 3922 | try writer.writeByte('E'); | |
| 3925 | 3923 | } |
| 3926 | 3924 | if (elf.SHF_COMPRESSED & sh_flags != 0) { |
| 3927 | try bw.writeByte('C'); | |
| 3925 | try writer.writeByte('C'); | |
| 3928 | 3926 | } |
| 3929 | 3927 | if (elf.SHF_GROUP & sh_flags != 0) { |
| 3930 | try bw.writeByte('G'); | |
| 3928 | try writer.writeByte('G'); | |
| 3931 | 3929 | } |
| 3932 | 3930 | if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) { |
| 3933 | try bw.writeByte('O'); | |
| 3931 | try writer.writeByte('O'); | |
| 3934 | 3932 | } |
| 3935 | 3933 | if (elf.SHF_TLS & sh_flags != 0) { |
| 3936 | try bw.writeByte('T'); | |
| 3934 | try writer.writeByte('T'); | |
| 3937 | 3935 | } |
| 3938 | 3936 | if (elf.SHF_X86_64_LARGE & sh_flags != 0) { |
| 3939 | try bw.writeByte('l'); | |
| 3937 | try writer.writeByte('l'); | |
| 3940 | 3938 | } |
| 3941 | 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 | } |
| 3945 | 3943 | |
| 3946 | const FormatPhdrCtx = struct { | |
| 3944 | const FormatPhdr = struct { | |
| 3947 | 3945 | elf_file: *Elf, |
| 3948 | 3946 | phdr: elf.Elf64_Phdr, |
| 3949 | 3947 | }; |
| 3950 | 3948 | |
| 3951 | fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) { | |
| 3949 | fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(FormatPhdr, formatPhdr) { | |
| 3952 | 3950 | return .{ .data = .{ |
| 3953 | 3951 | .phdr = phdr, |
| 3954 | 3952 | .elf_file = self, |
| 3955 | 3953 | } }; |
| 3956 | 3954 | } |
| 3957 | 3955 | |
| 3958 | fn formatPhdr( | |
| 3959 | ctx: FormatPhdrCtx, | |
| 3960 | bw: *Writer, | |
| 3961 | comptime unused_fmt_string: []const u8, | |
| 3962 | ) !void { | |
| 3963 | _ = unused_fmt_string; | |
| 3956 | fn formatPhdr(ctx: FormatPhdr, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 3964 | 3957 | const phdr = ctx.phdr; |
| 3965 | 3958 | const write = phdr.p_flags & elf.PF_W != 0; |
| 3966 | 3959 | const read = phdr.p_flags & elf.PF_R != 0; |
| ... | ... | @@ -3981,40 +3974,34 @@ fn formatPhdr( |
| 3981 | 3974 | elf.PT_NOTE => "NOTE", |
| 3982 | 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 | 3978 | p_type, flags, phdr.p_offset, phdr.p_vaddr, |
| 3986 | 3979 | phdr.p_align, phdr.p_filesz, phdr.p_memsz, |
| 3987 | 3980 | }); |
| 3988 | 3981 | } |
| 3989 | 3982 | |
| 3990 | pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { | |
| 3983 | pub fn dumpState(self: *Elf) std.fmt.Formatter(*Elf, fmtDumpState) { | |
| 3991 | 3984 | return .{ .data = self }; |
| 3992 | 3985 | } |
| 3993 | 3986 | |
| 3994 | fn fmtDumpState( | |
| 3995 | self: *Elf, | |
| 3996 | bw: *Writer, | |
| 3997 | comptime unused_fmt_string: []const u8, | |
| 3998 | ) !void { | |
| 3999 | _ = unused_fmt_string; | |
| 4000 | ||
| 3987 | fn fmtDumpState(self: *Elf, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 4001 | 3988 | const shared_objects = self.shared_objects.values(); |
| 4002 | 3989 | |
| 4003 | 3990 | if (self.zigObjectPtr()) |zig_object| { |
| 4004 | try bw.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); | |
| 4005 | try bw.print("{f}{f}", .{ | |
| 3991 | try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); | |
| 3992 | try writer.print("{f}{f}", .{ | |
| 4006 | 3993 | zig_object.fmtAtoms(self), |
| 4007 | 3994 | zig_object.fmtSymtab(self), |
| 4008 | 3995 | }); |
| 4009 | try bw.writeByte('\n'); | |
| 3996 | try writer.writeByte('\n'); | |
| 4010 | 3997 | } |
| 4011 | 3998 | |
| 4012 | 3999 | for (self.objects.items) |index| { |
| 4013 | 4000 | const object = self.file(index).?.object; |
| 4014 | try bw.print("object({d}) : {f}", .{ index, object.fmtPath() }); | |
| 4015 | if (!object.alive) try bw.writeAll(" : [*]"); | |
| 4016 | try bw.writeByte('\n'); | |
| 4017 | try bw.print("{f}{f}{f}{f}{f}\n", .{ | |
| 4001 | try writer.print("object({d}) : {f}", .{ index, object.fmtPath() }); | |
| 4002 | if (!object.alive) try writer.writeAll(" : [*]"); | |
| 4003 | try writer.writeByte('\n'); | |
| 4004 | try writer.print("{f}{f}{f}{f}{f}\n", .{ | |
| 4018 | 4005 | object.fmtAtoms(self), |
| 4019 | 4006 | object.fmtCies(self), |
| 4020 | 4007 | object.fmtFdes(self), |
| ... | ... | @@ -4025,59 +4012,59 @@ fn fmtDumpState( |
| 4025 | 4012 | |
| 4026 | 4013 | for (shared_objects) |index| { |
| 4027 | 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 | 4016 | index, shared_object.path, shared_object.needed, |
| 4030 | 4017 | }); |
| 4031 | if (!shared_object.alive) try bw.writeAll(" : [*]"); | |
| 4032 | try bw.writeByte('\n'); | |
| 4033 | try bw.print("{f}\n", .{shared_object.fmtSymtab(self)}); | |
| 4018 | if (!shared_object.alive) try writer.writeAll(" : [*]"); | |
| 4019 | try writer.writeByte('\n'); | |
| 4020 | try writer.print("{f}\n", .{shared_object.fmtSymtab(self)}); | |
| 4034 | 4021 | } |
| 4035 | 4022 | |
| 4036 | 4023 | if (self.linker_defined_index) |index| { |
| 4037 | 4024 | const linker_defined = self.file(index).?.linker_defined; |
| 4038 | try bw.print("linker_defined({d}) : (linker defined)\n", .{index}); | |
| 4039 | try bw.print("{f}\n", .{linker_defined.fmtSymtab(self)}); | |
| 4025 | try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); | |
| 4026 | try writer.print("{f}\n", .{linker_defined.fmtSymtab(self)}); | |
| 4040 | 4027 | } |
| 4041 | 4028 | |
| 4042 | 4029 | const slice = self.sections.slice(); |
| 4043 | 4030 | { |
| 4044 | try bw.writeAll("atom lists\n"); | |
| 4031 | try writer.writeAll("atom lists\n"); | |
| 4045 | 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 | } |
| 4049 | 4036 | |
| 4050 | 4037 | if (self.requiresThunks()) { |
| 4051 | try bw.writeAll("thunks\n"); | |
| 4038 | try writer.writeAll("thunks\n"); | |
| 4052 | 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 | } |
| 4056 | 4043 | |
| 4057 | try bw.print("{f}\n", .{self.got.fmt(self)}); | |
| 4058 | try bw.print("{f}\n", .{self.plt.fmt(self)}); | |
| 4044 | try writer.print("{f}\n", .{self.got.fmt(self)}); | |
| 4045 | try writer.print("{f}\n", .{self.plt.fmt(self)}); | |
| 4059 | 4046 | |
| 4060 | try bw.writeAll("Output groups\n"); | |
| 4047 | try writer.writeAll("Output groups\n"); | |
| 4061 | 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 | } |
| 4064 | 4051 | |
| 4065 | try bw.writeAll("\nOutput merge sections\n"); | |
| 4052 | try writer.writeAll("\nOutput merge sections\n"); | |
| 4066 | 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 | } |
| 4069 | 4056 | |
| 4070 | try bw.writeAll("\nOutput shdrs\n"); | |
| 4057 | try writer.writeAll("\nOutput shdrs\n"); | |
| 4071 | 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 | 4060 | shndx, |
| 4074 | 4061 | phndx, |
| 4075 | 4062 | self.fmtShdr(shdr), |
| 4076 | 4063 | }); |
| 4077 | 4064 | } |
| 4078 | try bw.writeAll("\nOutput phdrs\n"); | |
| 4065 | try writer.writeAll("\nOutput phdrs\n"); | |
| 4079 | 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 | } |
| 4083 | 4070 | |
| ... | ... | @@ -4215,9 +4202,8 @@ pub const Ref = struct { |
| 4215 | 4202 | return ref.index == other.index and ref.file == other.file; |
| 4216 | 4203 | } |
| 4217 | 4204 | |
| 4218 | pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 4219 | _ = unused_fmt_string; | |
| 4220 | try bw.print("ref({},{})", .{ ref.index, ref.file }); | |
| 4205 | pub fn format(ref: Ref, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 4206 | try writer.print("ref({d},{d})", .{ ref.index, ref.file }); | |
| 4221 | 4207 | } |
| 4222 | 4208 | }; |
| 4223 | 4209 |
src/link/Elf/Archive.zig+16-25| ... | ... | @@ -45,7 +45,7 @@ pub fn parse( |
| 45 | 45 | |
| 46 | 46 | if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { |
| 47 | 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 | } |
| 51 | 51 | |
| ... | ... | @@ -84,7 +84,7 @@ pub fn parse( |
| 84 | 84 | }; |
| 85 | 85 | |
| 86 | 86 | log.debug("extracting object '{f}' from archive '{f}'", .{ |
| 87 | object.path, path, | |
| 87 | @as(Path, object.path), @as(Path, path), | |
| 88 | 88 | }); |
| 89 | 89 | |
| 90 | 90 | try objects.append(gpa, object); |
| ... | ... | @@ -184,36 +184,28 @@ pub const ArSymtab = struct { |
| 184 | 184 | } |
| 185 | 185 | } |
| 186 | 186 | |
| 187 | pub fn format(ar: ArSymtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 188 | _ = ar; | |
| 189 | _ = bw; | |
| 190 | _ = unused_fmt_string; | |
| 191 | @compileError("do not format ar symtab directly; use fmt instead"); | |
| 192 | } | |
| 193 | ||
| 194 | const FormatContext = struct { | |
| 187 | const Format = struct { | |
| 195 | 188 | ar: ArSymtab, |
| 196 | 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 | }; |
| 198 | 201 | |
| 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 | 203 | return .{ .data = .{ |
| 201 | 204 | .ar = ar, |
| 202 | 205 | .elf_file = elf_file, |
| 203 | 206 | } }; |
| 204 | 207 | } |
| 205 | 208 | |
| 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 | 209 | const Entry = struct { |
| 218 | 210 | /// Offset into the string table. |
| 219 | 211 | off: u32, |
| ... | ... | @@ -251,9 +243,8 @@ pub const ArStrtab = struct { |
| 251 | 243 | try writer.writeAll(ar.buffer.items); |
| 252 | 244 | } |
| 253 | 245 | |
| 254 | pub fn format(ar: ArStrtab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 255 | comptime assert(unused_fmt_string.len == 0); | |
| 256 | try bw.print("{f}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)}); | |
| 246 | pub fn format(ar: ArStrtab, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 247 | try writer.print("{f}", .{std.ascii.hexEscape(ar.buffer.items, .lower)}); | |
| 257 | 248 | } |
| 258 | 249 | }; |
| 259 | 250 |
src/link/Elf/Atom.zig+35-44| ... | ... | @@ -906,53 +906,45 @@ pub fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void { |
| 906 | 906 | atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras); |
| 907 | 907 | } |
| 908 | 908 | |
| 909 | pub fn format(atom: Atom, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 910 | _ = atom; | |
| 911 | _ = bw; | |
| 912 | _ = unused_fmt_string; | |
| 913 | @compileError("do not format Atom directly"); | |
| 914 | } | |
| 915 | ||
| 916 | pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 909 | pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 917 | 910 | return .{ .data = .{ |
| 918 | 911 | .atom = atom, |
| 919 | 912 | .elf_file = elf_file, |
| 920 | 913 | } }; |
| 921 | 914 | } |
| 922 | 915 | |
| 923 | const FormatContext = struct { | |
| 916 | const Format = struct { | |
| 924 | 917 | atom: Atom, |
| 925 | 918 | elf_file: *Elf, |
| 926 | }; | |
| 927 | 919 | |
| 928 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 929 | _ = unused_fmt_string; | |
| 930 | const atom = ctx.atom; | |
| 931 | const elf_file = ctx.elf_file; | |
| 932 | try bw.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{ | |
| 933 | atom.atom_index, atom.name(elf_file), atom.address(elf_file), | |
| 934 | atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size, | |
| 935 | atom.prev_atom_ref, atom.next_atom_ref, | |
| 936 | }); | |
| 937 | if (atom.file(elf_file)) |atom_file| switch (atom_file) { | |
| 938 | .object => |object| { | |
| 939 | if (atom.fdes(object).len > 0) { | |
| 940 | try bw.writeAll(" : fdes{ "); | |
| 941 | const extras = atom.extra(elf_file); | |
| 942 | for (atom.fdes(object), extras.fde_start..) |fde, i| { | |
| 943 | try bw.print("{d}", .{i}); | |
| 944 | if (!fde.alive) try bw.writeAll("([*])"); | |
| 945 | if (i - extras.fde_start < extras.fde_count - 1) try bw.writeAll(", "); | |
| 920 | fn default(f: Format, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 921 | const atom = f.atom; | |
| 922 | const elf_file = f.elf_file; | |
| 923 | try w.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x}) : prev({f}) : next({f})", .{ | |
| 924 | atom.atom_index, atom.name(elf_file), atom.address(elf_file), | |
| 925 | atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size, | |
| 926 | atom.prev_atom_ref, atom.next_atom_ref, | |
| 927 | }); | |
| 928 | if (atom.file(elf_file)) |atom_file| switch (atom_file) { | |
| 929 | .object => |object| { | |
| 930 | if (atom.fdes(object).len > 0) { | |
| 931 | try w.writeAll(" : fdes{ "); | |
| 932 | const extras = atom.extra(elf_file); | |
| 933 | for (atom.fdes(object), extras.fde_start..) |fde, i| { | |
| 934 | try w.print("{d}", .{i}); | |
| 935 | if (!fde.alive) try w.writeAll("([*])"); | |
| 936 | if (i - extras.fde_start < extras.fde_count - 1) try w.writeAll(", "); | |
| 937 | } | |
| 938 | try w.writeAll(" }"); | |
| 946 | 939 | } |
| 947 | try bw.writeAll(" }"); | |
| 948 | } | |
| 949 | }, | |
| 950 | else => {}, | |
| 951 | }; | |
| 952 | if (!atom.alive) { | |
| 953 | try bw.writeAll(" : [*]"); | |
| 940 | }, | |
| 941 | else => {}, | |
| 942 | }; | |
| 943 | if (!atom.alive) { | |
| 944 | try w.writeAll(" : [*]"); | |
| 945 | } | |
| 954 | 946 | } |
| 955 | } | |
| 947 | }; | |
| 956 | 948 | |
| 957 | 949 | pub const Index = u32; |
| 958 | 950 | |
| ... | ... | @@ -1385,9 +1377,8 @@ const x86_64 = struct { |
| 1385 | 1377 | // TODO: hack to force imm32s in the assembler |
| 1386 | 1378 | .{ .imm = .s(-129) }, |
| 1387 | 1379 | }, t) catch return false; |
| 1388 | var buf: [std.atomic.cache_line]u8 = undefined; | |
| 1389 | var bw = Writer.null.buffered(&buf); | |
| 1390 | inst.encode(&bw, .{}) catch return false; | |
| 1380 | var trash: std.io.Writer.Discarding = .init(&.{}); | |
| 1381 | inst.encode(&trash.writer, .{}) catch return false; | |
| 1391 | 1382 | return true; |
| 1392 | 1383 | }, |
| 1393 | 1384 | else => return false, |
| ... | ... | @@ -1433,7 +1424,7 @@ const x86_64 = struct { |
| 1433 | 1424 | rels: []const elf.Elf64_Rela, |
| 1434 | 1425 | value: i32, |
| 1435 | 1426 | elf_file: *Elf, |
| 1436 | bw: *Writer, | |
| 1427 | writer: *Writer, | |
| 1437 | 1428 | ) !void { |
| 1438 | 1429 | dev.check(.x86_64_backend); |
| 1439 | 1430 | assert(rels.len == 2); |
| ... | ... | @@ -1450,8 +1441,8 @@ const x86_64 = struct { |
| 1450 | 1441 | 0x48, 0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %rax |
| 1451 | 1442 | }; |
| 1452 | 1443 | std.mem.writeInt(i32, insts[12..][0..4], value, .little); |
| 1453 | bw.end -= 4; | |
| 1454 | try bw.writeAll(&insts); | |
| 1444 | try writer.seekBy(-4); | |
| 1445 | try writer.writeAll(&insts); | |
| 1455 | 1446 | relocs_log.debug(" relaxing {f} and {f}", .{ |
| 1456 | 1447 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1457 | 1448 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| ... | ... | @@ -1481,8 +1472,8 @@ const x86_64 = struct { |
| 1481 | 1472 | } |
| 1482 | 1473 | |
| 1483 | 1474 | fn encode(insts: []const Instruction, code: []u8) !void { |
| 1484 | var bw: Writer = .fixed(code); | |
| 1485 | for (insts) |inst| try inst.encode(&bw, .{}); | |
| 1475 | var stream: std.io.Writer = .fixed(code); | |
| 1476 | for (insts) |inst| try inst.encode(&stream, .{}); | |
| 1486 | 1477 | } |
| 1487 | 1478 | |
| 1488 | 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 | 167 | return elf_file.atom(list.atoms.keys()[list.atoms.keys().len - 1]).?; |
| 168 | 168 | } |
| 169 | 169 | |
| 170 | pub fn format(list: AtomList, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 171 | _ = list; | |
| 172 | _ = bw; | |
| 173 | _ = unused_fmt_string; | |
| 174 | @compileError("do not format AtomList directly"); | |
| 175 | } | |
| 176 | ||
| 177 | const FormatCtx = struct { AtomList, *Elf }; | |
| 178 | ||
| 179 | pub fn fmt(list: AtomList, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 180 | return .{ .data = .{ list, elf_file } }; | |
| 181 | } | |
| 182 | ||
| 183 | fn format2(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 184 | comptime assert(unused_fmt_string.len == 0); | |
| 185 | const list, const elf_file = ctx; | |
| 186 | try bw.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{ | |
| 187 | list.address(elf_file), list.output_section_index, | |
| 188 | list.alignment.toByteUnits() orelse 0, list.size, | |
| 189 | }); | |
| 190 | try bw.writeAll(" : atoms{ "); | |
| 191 | for (list.atoms.keys(), 0..) |ref, i| { | |
| 192 | try bw.print("{f}", .{ref}); | |
| 193 | if (i < list.atoms.keys().len - 1) try bw.writeAll(", "); | |
| 170 | const Format = struct { | |
| 171 | atom_list: AtomList, | |
| 172 | elf_file: *Elf, | |
| 173 | ||
| 174 | fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 175 | const list = f.atom_list; | |
| 176 | try writer.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{ | |
| 177 | list.address(f.elf_file), | |
| 178 | list.output_section_index, | |
| 179 | list.alignment.toByteUnits() orelse 0, | |
| 180 | list.size, | |
| 181 | }); | |
| 182 | try writer.writeAll(" : atoms{ "); | |
| 183 | for (list.atoms.keys(), 0..) |ref, i| { | |
| 184 | try writer.print("{f}", .{ref}); | |
| 185 | if (i < list.atoms.keys().len - 1) try writer.writeAll(", "); | |
| 186 | } | |
| 187 | try writer.writeAll(" }"); | |
| 194 | 188 | } |
| 195 | try bw.writeAll(" }"); | |
| 189 | }; | |
| 190 | ||
| 191 | pub 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 | } |
| 197 | 194 | |
| 198 | 195 | const std = @import("std"); |
src/link/Elf/LinkerDefined.zig+16-17| ... | ... | @@ -147,9 +147,9 @@ pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void { |
| 147 | 147 | for (slice.items(.shdr)) |shdr| { |
| 148 | 148 | // TODO use getOrPut for incremental so that we don't create duplicates |
| 149 | 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 | 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 | 153 | defer gpa.free(stop_name); |
| 154 | 154 | |
| 155 | 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 | 437 | } |
| 438 | 438 | } |
| 439 | 439 | |
| 440 | pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { | |
| 440 | pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { | |
| 441 | 441 | return .{ .data = .{ |
| 442 | 442 | .self = self, |
| 443 | 443 | .elf_file = elf_file, |
| 444 | 444 | } }; |
| 445 | 445 | } |
| 446 | 446 | |
| 447 | const FormatContext = struct { | |
| 447 | const Format = struct { | |
| 448 | 448 | self: *LinkerDefined, |
| 449 | 449 | elf_file: *Elf, |
| 450 | }; | |
| 451 | 450 | |
| 452 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 453 | comptime assert(unused_fmt_string.len == 0); | |
| 454 | const self = ctx.self; | |
| 455 | const elf_file = ctx.elf_file; | |
| 456 | try bw.writeAll(" globals\n"); | |
| 457 | for (self.symbols.items, 0..) |sym, i| { | |
| 458 | const ref = self.resolveSymbol(@intCast(i), elf_file); | |
| 459 | if (elf_file.symbol(ref)) |ref_sym| { | |
| 460 | try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)}); | |
| 461 | } else { | |
| 462 | try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); | |
| 451 | fn symtab(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 452 | const self = ctx.self; | |
| 453 | const elf_file = ctx.elf_file; | |
| 454 | try writer.writeAll(" globals\n"); | |
| 455 | for (self.symbols.items, 0..) |sym, i| { | |
| 456 | const ref = self.resolveSymbol(@intCast(i), elf_file); | |
| 457 | if (elf_file.symbol(ref)) |ref_sym| { | |
| 458 | try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); | |
| 459 | } else { | |
| 460 | try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); | |
| 461 | } | |
| 463 | 462 | } |
| 464 | 463 | } |
| 465 | } | |
| 464 | }; | |
| 466 | 465 | |
| 467 | 466 | const std = @import("std"); |
| 468 | 467 | const Allocator = mem.Allocator; |
src/link/Elf/Merge.zig+31-47| ... | ... | @@ -157,42 +157,34 @@ pub const Section = struct { |
| 157 | 157 | } |
| 158 | 158 | }; |
| 159 | 159 | |
| 160 | pub fn format(msec: Section, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 161 | _ = msec; | |
| 162 | _ = bw; | |
| 163 | _ = unused_fmt_string; | |
| 164 | @compileError("do not format directly"); | |
| 165 | } | |
| 166 | ||
| 167 | pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 160 | pub fn fmt(msec: Section, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 168 | 161 | return .{ .data = .{ |
| 169 | 162 | .msec = msec, |
| 170 | 163 | .elf_file = elf_file, |
| 171 | 164 | } }; |
| 172 | 165 | } |
| 173 | 166 | |
| 174 | const FormatContext = struct { | |
| 167 | const Format = struct { | |
| 175 | 168 | msec: Section, |
| 176 | 169 | elf_file: *Elf, |
| 177 | }; | |
| 178 | 170 | |
| 179 | pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 180 | _ = unused_fmt_string; | |
| 181 | const msec = ctx.msec; | |
| 182 | const elf_file = ctx.elf_file; | |
| 183 | try bw.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ | |
| 184 | msec.name(elf_file), | |
| 185 | msec.address(elf_file), | |
| 186 | msec.size, | |
| 187 | msec.alignment.toByteUnits() orelse 0, | |
| 188 | msec.entsize, | |
| 189 | msec.type, | |
| 190 | msec.flags, | |
| 191 | }); | |
| 192 | for (msec.subsections.items) |msub| { | |
| 193 | try bw.print(" {f}\n", .{msub.fmt(elf_file)}); | |
| 171 | pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 172 | const msec = f.msec; | |
| 173 | const elf_file = f.elf_file; | |
| 174 | try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ | |
| 175 | msec.name(elf_file), | |
| 176 | msec.address(elf_file), | |
| 177 | msec.size, | |
| 178 | msec.alignment.toByteUnits() orelse 0, | |
| 179 | msec.entsize, | |
| 180 | msec.type, | |
| 181 | msec.flags, | |
| 182 | }); | |
| 183 | for (msec.subsections.items) |msub| { | |
| 184 | try writer.print(" {f}\n", .{msub.fmt(elf_file)}); | |
| 185 | } | |
| 194 | 186 | } |
| 195 | } | |
| 187 | }; | |
| 196 | 188 | |
| 197 | 189 | pub const Index = u32; |
| 198 | 190 | }; |
| ... | ... | @@ -219,36 +211,28 @@ pub const Subsection = struct { |
| 219 | 211 | return msec.bytes.items[msub.string_index..][0..msub.size]; |
| 220 | 212 | } |
| 221 | 213 | |
| 222 | pub fn format(msub: Subsection, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 223 | _ = msub; | |
| 224 | _ = bw; | |
| 225 | _ = unused_fmt_string; | |
| 226 | @compileError("do not format directly"); | |
| 227 | } | |
| 228 | ||
| 229 | pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 214 | pub fn fmt(msub: Subsection, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 230 | 215 | return .{ .data = .{ |
| 231 | 216 | .msub = msub, |
| 232 | 217 | .elf_file = elf_file, |
| 233 | 218 | } }; |
| 234 | 219 | } |
| 235 | 220 | |
| 236 | const FormatContext = struct { | |
| 221 | const Format = struct { | |
| 237 | 222 | msub: Subsection, |
| 238 | 223 | elf_file: *Elf, |
| 239 | }; | |
| 240 | 224 | |
| 241 | pub fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 242 | _ = unused_fmt_string; | |
| 243 | const msub = ctx.msub; | |
| 244 | const elf_file = ctx.elf_file; | |
| 245 | try bw.print("@{x} : align({x}) : size({x})", .{ | |
| 246 | msub.address(elf_file), | |
| 247 | msub.alignment, | |
| 248 | msub.size, | |
| 249 | }); | |
| 250 | if (!msub.alive) try bw.writeAll(" : [*]"); | |
| 251 | } | |
| 225 | pub fn default(ctx: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 226 | const msub = ctx.msub; | |
| 227 | const elf_file = ctx.elf_file; | |
| 228 | try writer.print("@{x} : align({x}) : size({x})", .{ | |
| 229 | msub.address(elf_file), | |
| 230 | msub.alignment, | |
| 231 | msub.size, | |
| 232 | }); | |
| 233 | if (!msub.alive) try writer.writeAll(" : [*]"); | |
| 234 | } | |
| 235 | }; | |
| 252 | 236 | |
| 253 | 237 | pub const Index = u32; |
| 254 | 238 | }; |
src/link/Elf/Object.zig+70-86| ... | ... | @@ -488,10 +488,7 @@ fn parseEhFrame( |
| 488 | 488 | if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index)); |
| 489 | 489 | } else { |
| 490 | 490 | // TODO convert into an error |
| 491 | log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ | |
| 492 | self.fmtPath(), | |
| 493 | fde.offset, | |
| 494 | }); | |
| 491 | log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ self.fmtPath(), fde.offset }); | |
| 495 | 492 | continue; |
| 496 | 493 | }; |
| 497 | 494 | fde.cie_index = cie_index; |
| ... | ... | @@ -582,7 +579,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { |
| 582 | 579 | if (sym.flags.import) { |
| 583 | 580 | if (sym.type(elf_file) != elf.STT_FUNC) |
| 584 | 581 | // 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 | 583 | self.fmtPath(), sym.name(elf_file), |
| 587 | 584 | }); |
| 588 | 585 | sym.flags.needs_plt = true; |
| ... | ... | @@ -1428,129 +1425,116 @@ pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group { |
| 1428 | 1425 | return &self.groups.items[index]; |
| 1429 | 1426 | } |
| 1430 | 1427 | |
| 1431 | pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1432 | _ = self; | |
| 1433 | _ = bw; | |
| 1434 | _ = unused_fmt_string; | |
| 1435 | @compileError("do not format objects directly"); | |
| 1436 | } | |
| 1437 | ||
| 1438 | pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { | |
| 1428 | pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { | |
| 1439 | 1429 | return .{ .data = .{ |
| 1440 | 1430 | .object = self, |
| 1441 | 1431 | .elf_file = elf_file, |
| 1442 | 1432 | } }; |
| 1443 | 1433 | } |
| 1444 | 1434 | |
| 1445 | const FormatContext = struct { | |
| 1435 | const Format = struct { | |
| 1446 | 1436 | object: *Object, |
| 1447 | 1437 | elf_file: *Elf, |
| 1448 | }; | |
| 1449 | 1438 | |
| 1450 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1451 | _ = unused_fmt_string; | |
| 1452 | const object = ctx.object; | |
| 1453 | const elf_file = ctx.elf_file; | |
| 1454 | try bw.writeAll(" locals\n"); | |
| 1455 | for (object.locals()) |sym| { | |
| 1456 | try bw.print(" {f}\n", .{sym.fmt(elf_file)}); | |
| 1439 | fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 1440 | const object = f.object; | |
| 1441 | const elf_file = f.elf_file; | |
| 1442 | try writer.writeAll(" locals\n"); | |
| 1443 | for (object.locals()) |sym| { | |
| 1444 | try writer.print(" {f}\n", .{sym.fmt(elf_file)}); | |
| 1445 | } | |
| 1446 | try writer.writeAll(" globals\n"); | |
| 1447 | for (object.globals(), 0..) |sym, i| { | |
| 1448 | const first_global = object.first_global.?; | |
| 1449 | const ref = object.resolveSymbol(@intCast(i + first_global), elf_file); | |
| 1450 | if (elf_file.symbol(ref)) |ref_sym| { | |
| 1451 | try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); | |
| 1452 | } else { | |
| 1453 | try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); | |
| 1454 | } | |
| 1455 | } | |
| 1457 | 1456 | } |
| 1458 | try bw.writeAll(" globals\n"); | |
| 1459 | for (object.globals(), 0..) |sym, i| { | |
| 1460 | const first_global = object.first_global.?; | |
| 1461 | const ref = object.resolveSymbol(@intCast(i + first_global), elf_file); | |
| 1462 | if (elf_file.symbol(ref)) |ref_sym| { | |
| 1463 | try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)}); | |
| 1464 | } else { | |
| 1465 | try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); | |
| 1457 | ||
| 1458 | fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 1459 | const object = f.object; | |
| 1460 | try writer.writeAll(" atoms\n"); | |
| 1461 | for (object.atoms_indexes.items) |atom_index| { | |
| 1462 | const atom_ptr = object.atom(atom_index) orelse continue; | |
| 1463 | try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)}); | |
| 1466 | 1464 | } |
| 1467 | 1465 | } |
| 1468 | } | |
| 1469 | 1466 | |
| 1470 | pub 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 | ||
| 1501 | pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) { | |
| 1471 | 1502 | return .{ .data = .{ |
| 1472 | 1503 | .object = self, |
| 1473 | 1504 | .elf_file = elf_file, |
| 1474 | 1505 | } }; |
| 1475 | 1506 | } |
| 1476 | 1507 | |
| 1477 | fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1478 | _ = unused_fmt_string; | |
| 1479 | const object = ctx.object; | |
| 1480 | try bw.writeAll(" atoms\n"); | |
| 1481 | for (object.atoms_indexes.items) |atom_index| { | |
| 1482 | const atom_ptr = object.atom(atom_index) orelse continue; | |
| 1483 | try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)}); | |
| 1484 | } | |
| 1485 | } | |
| 1486 | ||
| 1487 | pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) { | |
| 1508 | pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.cies) { | |
| 1488 | 1509 | return .{ .data = .{ |
| 1489 | 1510 | .object = self, |
| 1490 | 1511 | .elf_file = elf_file, |
| 1491 | 1512 | } }; |
| 1492 | 1513 | } |
| 1493 | 1514 | |
| 1494 | fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1495 | _ = unused_fmt_string; | |
| 1496 | const object = ctx.object; | |
| 1497 | try bw.writeAll(" cies\n"); | |
| 1498 | for (object.cies.items, 0..) |cie, i| { | |
| 1499 | try bw.print(" cie({d}) : {f}\n", .{ i, cie.fmt(ctx.elf_file) }); | |
| 1500 | } | |
| 1501 | } | |
| 1502 | ||
| 1503 | pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) { | |
| 1515 | pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.fdes) { | |
| 1504 | 1516 | return .{ .data = .{ |
| 1505 | 1517 | .object = self, |
| 1506 | 1518 | .elf_file = elf_file, |
| 1507 | 1519 | } }; |
| 1508 | 1520 | } |
| 1509 | 1521 | |
| 1510 | fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1511 | _ = unused_fmt_string; | |
| 1512 | const object = ctx.object; | |
| 1513 | try bw.writeAll(" fdes\n"); | |
| 1514 | for (object.fdes.items, 0..) |fde, i| { | |
| 1515 | try bw.print(" fde({d}) : {f}\n", .{ i, fde.fmt(ctx.elf_file) }); | |
| 1516 | } | |
| 1517 | } | |
| 1518 | ||
| 1519 | pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatGroups) { | |
| 1522 | pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(Format, Format.groups) { | |
| 1520 | 1523 | return .{ .data = .{ |
| 1521 | 1524 | .object = self, |
| 1522 | 1525 | .elf_file = elf_file, |
| 1523 | 1526 | } }; |
| 1524 | 1527 | } |
| 1525 | 1528 | |
| 1526 | fn formatGroups(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1527 | comptime assert(unused_fmt_string.len == 0); | |
| 1528 | const object = ctx.object; | |
| 1529 | const elf_file = ctx.elf_file; | |
| 1530 | try bw.writeAll(" groups\n"); | |
| 1531 | for (object.groups.items, 0..) |g, g_index| { | |
| 1532 | try bw.print(" {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index }); | |
| 1533 | if (!g.alive) try bw.writeAll(" : [*]"); | |
| 1534 | try bw.writeByte('\n'); | |
| 1535 | const g_members = g.members(elf_file); | |
| 1536 | for (g_members) |shndx| { | |
| 1537 | const atom_index = object.atoms_indexes.items[shndx]; | |
| 1538 | const atom_ptr = object.atom(atom_index) orelse continue; | |
| 1539 | try bw.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) }); | |
| 1540 | } | |
| 1541 | } | |
| 1542 | } | |
| 1543 | ||
| 1544 | pub fn fmtPath(self: Object) std.fmt.Formatter(formatPath) { | |
| 1529 | pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) { | |
| 1545 | 1530 | return .{ .data = self }; |
| 1546 | 1531 | } |
| 1547 | 1532 | |
| 1548 | fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1549 | comptime assert(unused_fmt_string.len == 0); | |
| 1533 | fn formatPath(object: Object, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 1550 | 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 | 1536 | } else { |
| 1553 | try bw.print("{f}", .{object.path}); | |
| 1537 | try writer.print("{f}", .{object.path}); | |
| 1554 | 1538 | } |
| 1555 | 1539 | } |
| 1556 | 1540 |
src/link/Elf/SharedObject.zig+14-22| ... | ... | @@ -509,39 +509,31 @@ pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void |
| 509 | 509 | } |
| 510 | 510 | } |
| 511 | 511 | |
| 512 | pub fn format(self: SharedObject, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 513 | _ = self; | |
| 514 | _ = bw; | |
| 515 | _ = unused_fmt_string; | |
| 516 | @compileError("unreachable"); | |
| 517 | } | |
| 518 | ||
| 519 | pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { | |
| 512 | pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { | |
| 520 | 513 | return .{ .data = .{ |
| 521 | 514 | .shared = self, |
| 522 | 515 | .elf_file = elf_file, |
| 523 | 516 | } }; |
| 524 | 517 | } |
| 525 | 518 | |
| 526 | const FormatContext = struct { | |
| 519 | const Format = struct { | |
| 527 | 520 | shared: SharedObject, |
| 528 | 521 | elf_file: *Elf, |
| 529 | }; | |
| 530 | 522 | |
| 531 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 532 | comptime assert(unused_fmt_string.len == 0); | |
| 533 | const shared = ctx.shared; | |
| 534 | const elf_file = ctx.elf_file; | |
| 535 | try bw.writeAll(" globals\n"); | |
| 536 | for (shared.symbols.items, 0..) |sym, i| { | |
| 537 | const ref = shared.resolveSymbol(@intCast(i), elf_file); | |
| 538 | if (elf_file.symbol(ref)) |ref_sym| { | |
| 539 | try bw.print(" {f}\n", .{ref_sym.fmt(elf_file)}); | |
| 540 | } else { | |
| 541 | try bw.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); | |
| 523 | fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 524 | const shared = f.shared; | |
| 525 | const elf_file = f.elf_file; | |
| 526 | try writer.writeAll(" globals\n"); | |
| 527 | for (shared.symbols.items, 0..) |sym, i| { | |
| 528 | const ref = shared.resolveSymbol(@intCast(i), elf_file); | |
| 529 | if (elf_file.symbol(ref)) |ref_sym| { | |
| 530 | try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); | |
| 531 | } else { | |
| 532 | try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); | |
| 533 | } | |
| 542 | 534 | } |
| 543 | 535 | } |
| 544 | } | |
| 536 | }; | |
| 545 | 537 | |
| 546 | 538 | const SharedObject = @This(); |
| 547 | 539 |
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 | 316 | out.st_size = esym.st_size; |
| 317 | 317 | } |
| 318 | 318 | |
| 319 | pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 320 | _ = symbol; | |
| 321 | _ = bw; | |
| 322 | _ = unused_fmt_string; | |
| 323 | @compileError("do not format Symbol directly"); | |
| 324 | } | |
| 325 | ||
| 326 | const FormatContext = struct { | |
| 319 | const Format = struct { | |
| 327 | 320 | symbol: Symbol, |
| 328 | 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 | }; |
| 330 | 370 | |
| 331 | pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(formatName) { | |
| 371 | pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.name) { | |
| 332 | 372 | return .{ .data = .{ |
| 333 | 373 | .symbol = symbol, |
| 334 | 374 | .elf_file = elf_file, |
| 335 | 375 | } }; |
| 336 | 376 | } |
| 337 | 377 | |
| 338 | fn formatName(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 339 | _ = unused_fmt_string; | |
| 340 | const elf_file = ctx.elf_file; | |
| 341 | const symbol = ctx.symbol; | |
| 342 | try bw.writeAll(symbol.name(elf_file)); | |
| 343 | switch (symbol.version_index.VERSION) { | |
| 344 | @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {}, | |
| 345 | else => { | |
| 346 | const file_ptr = symbol.file(elf_file).?; | |
| 347 | assert(file_ptr == .shared_object); | |
| 348 | const shared_object = file_ptr.shared_object; | |
| 349 | try bw.print("@{s}", .{shared_object.versionString(symbol.version_index)}); | |
| 350 | }, | |
| 351 | } | |
| 352 | } | |
| 353 | ||
| 354 | pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 378 | pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 355 | 379 | return .{ .data = .{ |
| 356 | 380 | .symbol = symbol, |
| 357 | 381 | .elf_file = elf_file, |
| 358 | 382 | } }; |
| 359 | 383 | } |
| 360 | 384 | |
| 361 | fn 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 | ||
| 394 | 385 | pub const Flags = packed struct { |
| 395 | 386 | /// Whether the symbol is imported at runtime. |
| 396 | 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 | 65 | }; |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 69 | _ = thunk; | |
| 70 | _ = bw; | |
| 71 | _ = unused_fmt_string; | |
| 72 | @compileError("do not format Thunk directly"); | |
| 73 | } | |
| 74 | ||
| 75 | pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 68 | pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 76 | 69 | return .{ .data = .{ |
| 77 | 70 | .thunk = thunk, |
| 78 | 71 | .elf_file = elf_file, |
| 79 | 72 | } }; |
| 80 | 73 | } |
| 81 | 74 | |
| 82 | const FormatContext = struct { | |
| 75 | const Format = struct { | |
| 83 | 76 | thunk: Thunk, |
| 84 | 77 | elf_file: *Elf, |
| 85 | }; | |
| 86 | 78 | |
| 87 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 88 | comptime assert(unused_fmt_string.len == 0); | |
| 89 | const thunk = ctx.thunk; | |
| 90 | const elf_file = ctx.elf_file; | |
| 91 | try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); | |
| 92 | for (thunk.symbols.keys()) |ref| { | |
| 93 | const sym = elf_file.symbol(ref).?; | |
| 94 | try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); | |
| 79 | fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 80 | const thunk = f.thunk; | |
| 81 | const elf_file = f.elf_file; | |
| 82 | try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); | |
| 83 | for (thunk.symbols.keys()) |ref| { | |
| 84 | const sym = elf_file.symbol(ref).?; | |
| 85 | try writer.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); | |
| 86 | } | |
| 95 | 87 | } |
| 96 | } | |
| 88 | }; | |
| 97 | 89 | |
| 98 | 90 | pub const Index = u32; |
| 99 | 91 |
src/link/Elf/ZigObject.zig+34-36| ... | ... | @@ -799,9 +799,9 @@ pub fn initRelaSections(self: *ZigObject, elf_file: *Elf) !void { |
| 799 | 799 | const out_shndx = atom_ptr.output_section_index; |
| 800 | 800 | const out_shdr = elf_file.sections.items(.shdr)[out_shndx]; |
| 801 | 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 | 803 | elf_file.getShString(out_shdr.sh_name), |
| 804 | }); | |
| 804 | }, 0); | |
| 805 | 805 | defer gpa.free(rela_sect_name); |
| 806 | 806 | _ = elf_file.sectionByName(rela_sect_name) orelse |
| 807 | 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 | 820 | const out_shndx = atom_ptr.output_section_index; |
| 821 | 821 | const out_shdr = elf_file.sections.items(.shdr)[out_shndx]; |
| 822 | 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 | 824 | elf_file.getShString(out_shdr.sh_name), |
| 825 | }); | |
| 825 | }, 0); | |
| 826 | 826 | defer gpa.free(rela_sect_name); |
| 827 | 827 | const out_rela_shndx = elf_file.sectionByName(rela_sect_name).?; |
| 828 | 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 | 1932 | .requires_padding = requires_padding, |
| 1933 | 1933 | }); |
| 1934 | 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 | 1936 | atom_ptr.name(elf_file), |
| 1937 | 1937 | atom_ptr.offset(elf_file), |
| 1938 | 1938 | alloc_res.placement, |
| ... | ... | @@ -1977,7 +1977,7 @@ pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, e |
| 1977 | 1977 | atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 }; |
| 1978 | 1978 | } |
| 1979 | 1979 | |
| 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 | } |
| 1982 | 1982 | |
| 1983 | 1983 | pub fn resetShdrIndexes(self: *ZigObject, backlinks: []const u32) void { |
| ... | ... | @@ -2186,48 +2186,46 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void { |
| 2186 | 2186 | } |
| 2187 | 2187 | } |
| 2188 | 2188 | |
| 2189 | pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { | |
| 2190 | return .{ .data = .{ | |
| 2191 | .self = self, | |
| 2192 | .elf_file = elf_file, | |
| 2193 | } }; | |
| 2194 | } | |
| 2195 | ||
| 2196 | const FormatContext = struct { | |
| 2189 | const Format = struct { | |
| 2197 | 2190 | self: *ZigObject, |
| 2198 | 2191 | elf_file: *Elf, |
| 2199 | }; | |
| 2200 | 2192 | |
| 2201 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2202 | _ = unused_fmt_string; | |
| 2203 | const self = ctx.self; | |
| 2204 | const elf_file = ctx.elf_file; | |
| 2205 | try bw.writeAll(" locals\n"); | |
| 2206 | for (self.local_symbols.items) |index| { | |
| 2207 | const local = self.symbols.items[index]; | |
| 2208 | try bw.print(" {f}\n", .{local.fmt(elf_file)}); | |
| 2193 | fn symtab(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 2194 | const self = f.self; | |
| 2195 | const elf_file = f.elf_file; | |
| 2196 | try writer.writeAll(" locals\n"); | |
| 2197 | for (self.local_symbols.items) |index| { | |
| 2198 | const local = self.symbols.items[index]; | |
| 2199 | try writer.print(" {f}\n", .{local.fmt(elf_file)}); | |
| 2200 | } | |
| 2201 | try writer.writeAll(" globals\n"); | |
| 2202 | for (f.self.global_symbols.items) |index| { | |
| 2203 | const global = self.symbols.items[index]; | |
| 2204 | try writer.print(" {f}\n", .{global.fmt(elf_file)}); | |
| 2205 | } | |
| 2209 | 2206 | } |
| 2210 | try bw.writeAll(" globals\n"); | |
| 2211 | for (ctx.self.global_symbols.items) |index| { | |
| 2212 | const global = self.symbols.items[index]; | |
| 2213 | try bw.print(" {f}\n", .{global.fmt(elf_file)}); | |
| 2207 | ||
| 2208 | fn atoms(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 2209 | try writer.writeAll(" atoms\n"); | |
| 2210 | for (f.self.atoms_indexes.items) |atom_index| { | |
| 2211 | const atom_ptr = f.self.atom(atom_index) orelse continue; | |
| 2212 | try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)}); | |
| 2213 | } | |
| 2214 | 2214 | } |
| 2215 | } | |
| 2215 | }; | |
| 2216 | 2216 | |
| 2217 | pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatAtoms) { | |
| 2217 | pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.symtab) { | |
| 2218 | 2218 | return .{ .data = .{ |
| 2219 | 2219 | .self = self, |
| 2220 | 2220 | .elf_file = elf_file, |
| 2221 | 2221 | } }; |
| 2222 | 2222 | } |
| 2223 | 2223 | |
| 2224 | fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2225 | comptime assert(unused_fmt_string.len == 0); | |
| 2226 | try bw.writeAll(" atoms\n"); | |
| 2227 | for (ctx.self.atoms_indexes.items) |atom_index| { | |
| 2228 | const atom_ptr = ctx.self.atom(atom_index) orelse continue; | |
| 2229 | try bw.print(" {f}\n", .{atom_ptr.fmt(ctx.elf_file)}); | |
| 2230 | } | |
| 2224 | pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(Format, Format.atoms) { | |
| 2225 | return .{ .data = .{ | |
| 2226 | .self = self, | |
| 2227 | .elf_file = elf_file, | |
| 2228 | } }; | |
| 2231 | 2229 | } |
| 2232 | 2230 | |
| 2233 | 2231 | const ElfSym = struct { |
src/link/Elf/eh_frame.zig+30-62| ... | ... | @@ -47,48 +47,32 @@ pub const Fde = struct { |
| 47 | 47 | return object.relocs.items[fde.rel_index..][0..fde.rel_num]; |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | pub fn format( | |
| 51 | fde: Fde, | |
| 52 | bw: *Writer, | |
| 53 | comptime unused_fmt_string: []const u8, | |
| 54 | ) !void { | |
| 55 | _ = fde; | |
| 56 | _ = unused_fmt_string; | |
| 57 | _ = bw; | |
| 58 | @compileError("do not format FDEs directly"); | |
| 59 | } | |
| 60 | ||
| 61 | pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 50 | pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 62 | 51 | return .{ .data = .{ |
| 63 | 52 | .fde = fde, |
| 64 | 53 | .elf_file = elf_file, |
| 65 | 54 | } }; |
| 66 | 55 | } |
| 67 | 56 | |
| 68 | const FdeFormatContext = struct { | |
| 57 | const Format = struct { | |
| 69 | 58 | fde: Fde, |
| 70 | 59 | elf_file: *Elf, |
| 71 | }; | |
| 72 | 60 | |
| 73 | fn format2( | |
| 74 | ctx: FdeFormatContext, | |
| 75 | bw: *Writer, | |
| 76 | comptime unused_fmt_string: []const u8, | |
| 77 | ) !void { | |
| 78 | _ = unused_fmt_string; | |
| 79 | const fde = ctx.fde; | |
| 80 | const elf_file = ctx.elf_file; | |
| 81 | const base_addr = fde.address(elf_file); | |
| 82 | const object = elf_file.file(fde.file_index).?.object; | |
| 83 | const atom_name = fde.atom(object).name(elf_file); | |
| 84 | try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{ | |
| 85 | base_addr + fde.out_offset, | |
| 86 | fde.calcSize(), | |
| 87 | fde.cie_index, | |
| 88 | atom_name, | |
| 89 | }); | |
| 90 | if (!fde.alive) try bw.writeAll(" : [*]"); | |
| 91 | } | |
| 61 | fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 62 | const fde = f.fde; | |
| 63 | const elf_file = f.elf_file; | |
| 64 | const base_addr = fde.address(elf_file); | |
| 65 | const object = elf_file.file(fde.file_index).?.object; | |
| 66 | const atom_name = fde.atom(object).name(elf_file); | |
| 67 | try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ | |
| 68 | base_addr + fde.out_offset, | |
| 69 | fde.calcSize(), | |
| 70 | fde.cie_index, | |
| 71 | atom_name, | |
| 72 | }); | |
| 73 | if (!fde.alive) try writer.writeAll(" : [*]"); | |
| 74 | } | |
| 75 | }; | |
| 92 | 76 | }; |
| 93 | 77 | |
| 94 | 78 | pub const Cie = struct { |
| ... | ... | @@ -146,44 +130,28 @@ pub const Cie = struct { |
| 146 | 130 | return true; |
| 147 | 131 | } |
| 148 | 132 | |
| 149 | pub fn format( | |
| 150 | cie: Cie, | |
| 151 | bw: *Writer, | |
| 152 | comptime unused_fmt_string: []const u8, | |
| 153 | ) !void { | |
| 154 | _ = cie; | |
| 155 | _ = unused_fmt_string; | |
| 156 | _ = bw; | |
| 157 | @compileError("do not format CIEs directly"); | |
| 158 | } | |
| 159 | ||
| 160 | pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 133 | pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(Format, Format.default) { | |
| 161 | 134 | return .{ .data = .{ |
| 162 | 135 | .cie = cie, |
| 163 | 136 | .elf_file = elf_file, |
| 164 | 137 | } }; |
| 165 | 138 | } |
| 166 | 139 | |
| 167 | const CieFormatContext = struct { | |
| 140 | const Format = struct { | |
| 168 | 141 | cie: Cie, |
| 169 | 142 | elf_file: *Elf, |
| 170 | }; | |
| 171 | 143 | |
| 172 | fn format2( | |
| 173 | ctx: CieFormatContext, | |
| 174 | bw: *Writer, | |
| 175 | comptime unused_fmt_string: []const u8, | |
| 176 | ) !void { | |
| 177 | _ = unused_fmt_string; | |
| 178 | const cie = ctx.cie; | |
| 179 | const elf_file = ctx.elf_file; | |
| 180 | const base_addr = cie.address(elf_file); | |
| 181 | try bw.print("@{x} : size({x})", .{ | |
| 182 | base_addr + cie.out_offset, | |
| 183 | cie.calcSize(), | |
| 184 | }); | |
| 185 | if (!cie.alive) try bw.writeAll(" : [*]"); | |
| 186 | } | |
| 144 | fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 145 | const cie = f.cie; | |
| 146 | const elf_file = f.elf_file; | |
| 147 | const base_addr = cie.address(elf_file); | |
| 148 | try writer.print("@{x} : size({x})", .{ | |
| 149 | base_addr + cie.out_offset, | |
| 150 | cie.calcSize(), | |
| 151 | }); | |
| 152 | if (!cie.alive) try writer.writeAll(" : [*]"); | |
| 153 | } | |
| 154 | }; | |
| 187 | 155 | }; |
| 188 | 156 | |
| 189 | 157 | pub const Iterator = struct { |
src/link/Elf/file.zig+6-7| ... | ... | @@ -10,17 +10,16 @@ pub const File = union(enum) { |
| 10 | 10 | }; |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { | |
| 13 | pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) { | |
| 14 | 14 | return .{ .data = file }; |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 18 | comptime assert(unused_fmt_string.len == 0); | |
| 17 | fn formatPath(file: File, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 19 | 18 | switch (file) { |
| 20 | .zig_object => |zo| try bw.writeAll(zo.basename), | |
| 21 | .linker_defined => try bw.writeAll("(linker defined)"), | |
| 22 | .object => |x| try bw.print("{f}", .{x.fmtPath()}), | |
| 23 | .shared_object => |x| try bw.print("{f}", .{x.path}), | |
| 19 | .zig_object => |zo| try writer.writeAll(zo.basename), | |
| 20 | .linker_defined => try writer.writeAll("(linker defined)"), | |
| 21 | .object => |x| try writer.print("{f}", .{x.fmtPath()}), | |
| 22 | .shared_object => |x| try writer.print("{f}", .{@as(Path, x.path)}), | |
| 24 | 23 | } |
| 25 | 24 | } |
| 26 | 25 |
src/link/Elf/gc.zig+2-3| ... | ... | @@ -185,9 +185,8 @@ const Level = struct { |
| 185 | 185 | self.value += 1; |
| 186 | 186 | } |
| 187 | 187 | |
| 188 | pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 189 | comptime assert(unused_fmt_string.len == 0); | |
| 190 | try bw.splatByteAll(' ', self.value); | |
| 188 | pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void { | |
| 189 | try w.splatByteAll(' ', self.value); | |
| 191 | 190 | } |
| 192 | 191 | }; |
| 193 | 192 |
src/link/Elf/relocation.zig+5-6| ... | ... | @@ -141,20 +141,19 @@ const FormatRelocTypeCtx = struct { |
| 141 | 141 | cpu_arch: std.Target.Cpu.Arch, |
| 142 | 142 | }; |
| 143 | 143 | |
| 144 | pub fn fmtRelocType(r_type: u32, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatRelocType) { | |
| 144 | pub fn fmtRelocType(r_type: u32, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(FormatRelocTypeCtx, formatRelocType) { | |
| 145 | 145 | return .{ .data = .{ |
| 146 | 146 | .r_type = r_type, |
| 147 | 147 | .cpu_arch = cpu_arch, |
| 148 | 148 | } }; |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | fn formatRelocType(ctx: FormatRelocTypeCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 152 | comptime assert(unused_fmt_string.len == 0); | |
| 151 | fn formatRelocType(ctx: FormatRelocTypeCtx, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 153 | 152 | const r_type = ctx.r_type; |
| 154 | 153 | switch (ctx.cpu_arch) { |
| 155 | .x86_64 => try bw.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}), | |
| 156 | .aarch64 => try bw.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}), | |
| 157 | .riscv64 => try bw.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}), | |
| 154 | .x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}), | |
| 155 | .aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}), | |
| 156 | .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}), | |
| 158 | 157 | else => unreachable, |
| 159 | 158 | } |
| 160 | 159 | } |
src/link/Elf/synthetic_sections.zig+36-38| ... | ... | @@ -606,31 +606,30 @@ pub const GotSection = struct { |
| 606 | 606 | } |
| 607 | 607 | } |
| 608 | 608 | |
| 609 | const FormatCtx = struct { | |
| 609 | const Format = struct { | |
| 610 | 610 | got: GotSection, |
| 611 | 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 | }; |
| 613 | 629 | |
| 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 | 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 | }; |
| 635 | 634 | |
| 636 | 635 | pub const PltSection = struct { |
| ... | ... | @@ -743,32 +742,31 @@ pub const PltSection = struct { |
| 743 | 742 | } |
| 744 | 743 | } |
| 745 | 744 | |
| 746 | const FormatCtx = struct { | |
| 745 | const Format = struct { | |
| 747 | 746 | plt: PltSection, |
| 748 | 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 | }; |
| 750 | 765 | |
| 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 | 767 | return .{ .data = .{ .plt = plt, .elf_file = elf_file } }; |
| 753 | 768 | } |
| 754 | 769 | |
| 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 | 770 | const x86_64 = struct { |
| 773 | 771 | fn write(plt: PltSection, elf_file: *Elf, bw: *Writer) Writer.Error!void { |
| 774 | 772 | const shdrs = elf_file.sections.items(.shdr); |
src/link/LdScript.zig+1-1| ... | ... | @@ -42,7 +42,7 @@ pub fn parse( |
| 42 | 42 | switch (tok.id) { |
| 43 | 43 | .invalid => { |
| 44 | 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 | 48 | .new_line => { |
src/link/Lld.zig+12-23| ... | ... | @@ -294,7 +294,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { |
| 294 | 294 | break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?); |
| 295 | 295 | } else null; |
| 296 | 296 | |
| 297 | log.debug("zcu_obj_path={?}", .{zcu_obj_path}); | |
| 297 | log.debug("zcu_obj_path={?f}", .{zcu_obj_path}); | |
| 298 | 298 | |
| 299 | 299 | const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj) |
| 300 | 300 | comp.compiler_rt_obj.?.full_object_path |
| ... | ... | @@ -437,7 +437,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { |
| 437 | 437 | try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename})); |
| 438 | 438 | } |
| 439 | 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 | } |
| 442 | 442 | |
| 443 | 443 | if (target_util.llvmMachineAbi(target)) |mabi| { |
| ... | ... | @@ -507,7 +507,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { |
| 507 | 507 | |
| 508 | 508 | if (comp.emit_implib) |raw_emit_path| { |
| 509 | 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 | } |
| 512 | 512 | |
| 513 | 513 | if (comp.config.link_libc) { |
| ... | ... | @@ -533,7 +533,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { |
| 533 | 533 | }, |
| 534 | 534 | .object, .archive => |obj| { |
| 535 | 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 | 537 | } else { |
| 538 | 538 | argv.appendAssumeCapacity(try obj.path.toString(arena)); |
| 539 | 539 | } |
| ... | ... | @@ -933,9 +933,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 933 | 933 | .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ |
| 934 | 934 | @tagName(base.build_id), |
| 935 | 935 | })), |
| 936 | .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ | |
| 937 | std.fmt.fmtSliceHexLower(hs.toSlice()), | |
| 938 | })), | |
| 936 | .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})), | |
| 939 | 937 | } |
| 940 | 938 | |
| 941 | 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 | 1216 | if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue; |
| 1219 | 1217 | } |
| 1220 | 1218 | |
| 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 | 1220 | comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, |
| 1223 | 1221 | }); |
| 1224 | 1222 | try argv.append(lib_path); |
| ... | ... | @@ -1231,14 +1229,14 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { |
| 1231 | 1229 | })); |
| 1232 | 1230 | } else if (target.isFreeBSDLibC()) { |
| 1233 | 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 | 1233 | comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, |
| 1236 | 1234 | }); |
| 1237 | 1235 | try argv.append(lib_path); |
| 1238 | 1236 | } |
| 1239 | 1237 | } else if (target.isNetBSDLibC()) { |
| 1240 | 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 | 1240 | comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, |
| 1243 | 1241 | }); |
| 1244 | 1242 | try argv.append(lib_path); |
| ... | ... | @@ -1511,9 +1509,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { |
| 1511 | 1509 | .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ |
| 1512 | 1510 | @tagName(base.build_id), |
| 1513 | 1511 | })), |
| 1514 | .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ | |
| 1515 | std.fmt.fmtSliceHexLower(hs.toSlice()), | |
| 1516 | })), | |
| 1512 | .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})), | |
| 1517 | 1513 | .md5 => {}, |
| 1518 | 1514 | } |
| 1519 | 1515 | |
| ... | ... | @@ -1539,13 +1535,6 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { |
| 1539 | 1535 | |
| 1540 | 1536 | if (comp.config.link_libc and is_exe_or_dyn_lib) { |
| 1541 | 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 | 1538 | try argv.append(try comp.crtFileAsString( |
| 1550 | 1539 | arena, |
| 1551 | 1540 | wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), |
| ... | ... | @@ -1660,7 +1649,7 @@ fn spawnLld( |
| 1660 | 1649 | child.stderr_behavior = .Pipe; |
| 1661 | 1650 | |
| 1662 | 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 | 1653 | break :term child.wait(); |
| 1665 | 1654 | }) catch |first_err| term: { |
| 1666 | 1655 | const err = switch (first_err) { |
| ... | ... | @@ -1674,7 +1663,7 @@ fn spawnLld( |
| 1674 | 1663 | log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); |
| 1675 | 1664 | { |
| 1676 | 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 | 1667 | const rsp_writer = rsp_buf.writer(); |
| 1679 | 1668 | for (argv[2..]) |arg| { |
| 1680 | 1669 | try rsp_writer.writeByte('"'); |
| ... | ... | @@ -1708,7 +1697,7 @@ fn spawnLld( |
| 1708 | 1697 | rsp_child.stderr_behavior = .Pipe; |
| 1709 | 1698 | |
| 1710 | 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 | 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 | 3898 | self.hot_state.mach_task = null; |
| 3899 | 3899 | } |
| 3900 | 3900 | |
| 3901 | pub fn dumpState(self: *MachO) std.fmt.Formatter(fmtDumpState) { | |
| 3901 | pub fn dumpState(self: *MachO) std.fmt.Formatter(*MachO, fmtDumpState) { | |
| 3902 | 3902 | return .{ .data = self }; |
| 3903 | 3903 | } |
| 3904 | 3904 | |
| 3905 | fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 3906 | _ = unused_fmt_string; | |
| 3905 | fn fmtDumpState(self: *MachO, w: *Writer) Writer.Error!void { | |
| 3907 | 3906 | if (self.getZigObject()) |zo| { |
| 3908 | try bw.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); | |
| 3909 | try bw.print("{f}{f}\n", .{ | |
| 3907 | try w.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); | |
| 3908 | try w.print("{f}{f}\n", .{ | |
| 3910 | 3909 | zo.fmtAtoms(self), |
| 3911 | 3910 | zo.fmtSymtab(self), |
| 3912 | 3911 | }); |
| 3913 | 3912 | } |
| 3914 | 3913 | for (self.objects.items) |index| { |
| 3915 | 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 | 3916 | index, |
| 3918 | 3917 | object.fmtPath(), |
| 3919 | 3918 | object.hasDebugInfo(), |
| 3920 | 3919 | }); |
| 3921 | if (!object.alive) try bw.writeAll(" : ([*])"); | |
| 3922 | try bw.writeByte('\n'); | |
| 3923 | try bw.print("{f}{f}{f}{f}{f}\n", .{ | |
| 3920 | if (!object.alive) try w.writeAll(" : ([*])"); | |
| 3921 | try w.writeByte('\n'); | |
| 3922 | try w.print("{f}{f}{f}{f}{f}\n", .{ | |
| 3924 | 3923 | object.fmtAtoms(self), |
| 3925 | 3924 | object.fmtCies(self), |
| 3926 | 3925 | object.fmtFdes(self), |
| ... | ... | @@ -3930,42 +3929,41 @@ fn fmtDumpState(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u |
| 3930 | 3929 | } |
| 3931 | 3930 | for (self.dylibs.items) |index| { |
| 3932 | 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 | 3933 | index, |
| 3935 | 3934 | @as(Path, dylib.path), |
| 3936 | 3935 | dylib.needed, |
| 3937 | 3936 | dylib.weak, |
| 3938 | 3937 | }); |
| 3939 | if (!dylib.isAlive(self)) try bw.writeAll(" : ([*])"); | |
| 3940 | try bw.writeByte('\n'); | |
| 3941 | try bw.print("{f}\n", .{dylib.fmtSymtab(self)}); | |
| 3938 | if (!dylib.isAlive(self)) try w.writeAll(" : ([*])"); | |
| 3939 | try w.writeByte('\n'); | |
| 3940 | try w.print("{f}\n", .{dylib.fmtSymtab(self)}); | |
| 3942 | 3941 | } |
| 3943 | 3942 | if (self.getInternalObject()) |internal| { |
| 3944 | try bw.print("internal({d}) : internal\n", .{internal.index}); | |
| 3945 | try bw.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); | |
| 3943 | try w.print("internal({d}) : internal\n", .{internal.index}); | |
| 3944 | try w.print("{f}{f}\n", .{ internal.fmtAtoms(self), internal.fmtSymtab(self) }); | |
| 3946 | 3945 | } |
| 3947 | try bw.writeAll("thunks\n"); | |
| 3946 | try w.writeAll("thunks\n"); | |
| 3948 | 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)}); | |
| 3952 | try bw.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)}); | |
| 3953 | try bw.print("got\n{f}\n", .{self.got.fmt(self)}); | |
| 3954 | try bw.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)}); | |
| 3955 | try bw.writeByte('\n'); | |
| 3956 | try bw.print("sections\n{f}\n", .{self.fmtSections()}); | |
| 3957 | try bw.print("segments\n{f}\n", .{self.fmtSegments()}); | |
| 3950 | try w.print("stubs\n{f}\n", .{self.stubs.fmt(self)}); | |
| 3951 | try w.print("objc_stubs\n{f}\n", .{self.objc_stubs.fmt(self)}); | |
| 3952 | try w.print("got\n{f}\n", .{self.got.fmt(self)}); | |
| 3953 | try w.print("tlv_ptr\n{f}\n", .{self.tlv_ptr.fmt(self)}); | |
| 3954 | try w.writeByte('\n'); | |
| 3955 | try w.print("sections\n{f}\n", .{self.fmtSections()}); | |
| 3956 | try w.print("segments\n{f}\n", .{self.fmtSegments()}); | |
| 3958 | 3957 | } |
| 3959 | 3958 | |
| 3960 | fn fmtSections(self: *MachO) std.fmt.Formatter(formatSections) { | |
| 3959 | fn fmtSections(self: *MachO) std.fmt.Formatter(*MachO, formatSections) { | |
| 3961 | 3960 | return .{ .data = self }; |
| 3962 | 3961 | } |
| 3963 | 3962 | |
| 3964 | fn formatSections(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 3965 | _ = unused_fmt_string; | |
| 3963 | fn formatSections(self: *MachO, w: *Writer) Writer.Error!void { | |
| 3966 | 3964 | const slice = self.sections.slice(); |
| 3967 | 3965 | for (slice.items(.header), slice.items(.segment_id), 0..) |header, seg_id, i| { |
| 3968 | try bw.print( | |
| 3966 | try w.print( | |
| 3969 | 3967 | "sect({d}) : seg({d}) : {s},{s} : @{x} ({x}) : align({x}) : size({x}) : relocs({x};{d})\n", |
| 3970 | 3968 | .{ |
| 3971 | 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 | 3973 | } |
| 3976 | 3974 | } |
| 3977 | 3975 | |
| 3978 | fn fmtSegments(self: *MachO) std.fmt.Formatter(formatSegments) { | |
| 3976 | fn fmtSegments(self: *MachO) std.fmt.Formatter(*MachO, formatSegments) { | |
| 3979 | 3977 | return .{ .data = self }; |
| 3980 | 3978 | } |
| 3981 | 3979 | |
| 3982 | fn formatSegments(self: *MachO, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 3983 | _ = unused_fmt_string; | |
| 3980 | fn formatSegments(self: *MachO, w: *Writer) Writer.Error!void { | |
| 3984 | 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 | 3983 | i, seg.segName(), seg.vmaddr, seg.vmaddr + seg.vmsize, |
| 3987 | 3984 | seg.fileoff, seg.fileoff + seg.filesize, |
| 3988 | 3985 | }); |
| 3989 | 3986 | } |
| 3990 | 3987 | } |
| 3991 | 3988 | |
| 3992 | pub fn fmtSectType(tt: u8) std.fmt.Formatter(formatSectType) { | |
| 3989 | pub fn fmtSectType(tt: u8) std.fmt.Formatter(u8, formatSectType) { | |
| 3993 | 3990 | return .{ .data = tt }; |
| 3994 | 3991 | } |
| 3995 | 3992 | |
| 3996 | fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 3997 | _ = unused_fmt_string; | |
| 3993 | fn formatSectType(tt: u8, w: *Writer) Writer.Error!void { | |
| 3998 | 3994 | const name = switch (tt) { |
| 3999 | 3995 | macho.S_REGULAR => "REGULAR", |
| 4000 | 3996 | macho.S_ZEROFILL => "ZEROFILL", |
| ... | ... | @@ -4018,9 +4014,9 @@ fn formatSectType(tt: u8, bw: *Writer, comptime unused_fmt_string: []const u8) W |
| 4018 | 4014 | macho.S_THREAD_LOCAL_VARIABLE_POINTERS => "THREAD_LOCAL_VARIABLE_POINTERS", |
| 4019 | 4015 | macho.S_THREAD_LOCAL_INIT_FUNCTION_POINTERS => "THREAD_LOCAL_INIT_FUNCTION_POINTERS", |
| 4020 | 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 | } |
| 4025 | 4021 | |
| 4026 | 4022 | const is_hot_update_compatible = switch (builtin.target.os.tag) { |
| ... | ... | @@ -4253,28 +4249,27 @@ pub const Platform = struct { |
| 4253 | 4249 | return false; |
| 4254 | 4250 | } |
| 4255 | 4251 | |
| 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 | 4253 | return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } }; |
| 4258 | 4254 | } |
| 4259 | 4255 | |
| 4260 | const FmtCtx = struct { | |
| 4256 | const Format = struct { | |
| 4261 | 4257 | platform: Platform, |
| 4262 | 4258 | cpu_arch: std.Target.Cpu.Arch, |
| 4263 | }; | |
| 4264 | 4259 | |
| 4265 | pub fn formatTarget(ctx: FmtCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 4266 | _ = unused_fmt_string; | |
| 4267 | try bw.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) }); | |
| 4268 | if (ctx.platform.abi != .none) { | |
| 4269 | try bw.print("-{s}", .{@tagName(ctx.platform.abi)}); | |
| 4260 | pub fn target(f: Format, w: *Writer) Writer.Error!void { | |
| 4261 | try w.print("{s}-{s}", .{ @tagName(f.cpu_arch), @tagName(f.platform.os_tag) }); | |
| 4262 | if (f.platform.abi != .none) { | |
| 4263 | try w.print("-{s}", .{@tagName(f.platform.abi)}); | |
| 4264 | } | |
| 4270 | 4265 | } |
| 4271 | } | |
| 4266 | }; | |
| 4272 | 4267 | |
| 4273 | 4268 | /// Caller owns the memory. |
| 4274 | 4269 | pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 { |
| 4275 | 4270 | var buffer = std.ArrayList(u8).init(gpa); |
| 4276 | 4271 | defer buffer.deinit(); |
| 4277 | try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)}); | |
| 4272 | try buffer.writer().print("{f}", .{plat.fmtTarget(cpu_arch)}); | |
| 4278 | 4273 | return buffer.toOwnedSlice(); |
| 4279 | 4274 | } |
| 4280 | 4275 | |
| ... | ... | @@ -4475,8 +4470,7 @@ pub const Ref = struct { |
| 4475 | 4470 | }; |
| 4476 | 4471 | } |
| 4477 | 4472 | |
| 4478 | pub fn format(ref: Ref, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 4479 | comptime assert(unused_fmt_string.len == 0); | |
| 4473 | pub fn format(ref: Ref, bw: *Writer) Writer.Error!void { | |
| 4480 | 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 | 30 | |
| 31 | 31 | if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { |
| 32 | 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 | } |
| 36 | 36 | |
| ... | ... | @@ -203,26 +203,25 @@ pub const ArSymtab = struct { |
| 203 | 203 | try bw.splatByteAll(0, strtab_size - ar.strtab.buffer.items.len); |
| 204 | 204 | } |
| 205 | 205 | |
| 206 | const FormatContext = struct { | |
| 206 | const PrintFormat = struct { | |
| 207 | 207 | ar: ArSymtab, |
| 208 | 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 | }; |
| 210 | 220 | |
| 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 | 222 | return .{ .data = .{ .ar = ar, .macho_file = macho_file } }; |
| 213 | 223 | } |
| 214 | 224 | |
| 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 | 225 | const Entry = struct { |
| 227 | 226 | /// Symbol name offset |
| 228 | 227 | off: u32, |
src/link/MachO/Atom.zig+25-39| ... | ... | @@ -937,8 +937,8 @@ const x86_64 = struct { |
| 937 | 937 | } |
| 938 | 938 | |
| 939 | 939 | fn encode(insts: []const Instruction, code: []u8) !void { |
| 940 | var bw: Writer = .fixed(code); | |
| 941 | for (insts) |inst| try inst.encode(&bw, .{}); | |
| 940 | var stream: Writer = .fixed(code); | |
| 941 | for (insts) |inst| try inst.encode(&stream, .{}); | |
| 942 | 942 | } |
| 943 | 943 | |
| 944 | 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 | 1113 | assert(i == buffer.len); |
| 1114 | 1114 | } |
| 1115 | 1115 | |
| 1116 | pub fn format( | |
| 1117 | atom: Atom, | |
| 1118 | comptime unused_fmt_string: []const u8, | |
| 1119 | options: std.fmt.FormatOptions, | |
| 1120 | writer: anytype, | |
| 1121 | ) !void { | |
| 1122 | _ = atom; | |
| 1123 | _ = unused_fmt_string; | |
| 1124 | _ = options; | |
| 1125 | _ = writer; | |
| 1126 | @compileError("do not format Atom directly"); | |
| 1127 | } | |
| 1128 | ||
| 1129 | pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 1116 | pub fn fmt(atom: Atom, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { | |
| 1130 | 1117 | return .{ .data = .{ |
| 1131 | 1118 | .atom = atom, |
| 1132 | 1119 | .macho_file = macho_file, |
| 1133 | 1120 | } }; |
| 1134 | 1121 | } |
| 1135 | 1122 | |
| 1136 | const FormatContext = struct { | |
| 1123 | const Format = struct { | |
| 1137 | 1124 | atom: Atom, |
| 1138 | 1125 | macho_file: *MachO, |
| 1139 | }; | |
| 1140 | 1126 | |
| 1141 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1142 | comptime assert(unused_fmt_string.len == 0); | |
| 1143 | const atom = ctx.atom; | |
| 1144 | const macho_file = ctx.macho_file; | |
| 1145 | const file = atom.getFile(macho_file); | |
| 1146 | try bw.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ | |
| 1147 | atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), | |
| 1148 | atom.out_n_sect, atom.alignment, atom.size, | |
| 1149 | atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, | |
| 1150 | }); | |
| 1151 | if (!atom.isAlive()) try bw.writeAll(" : [*]"); | |
| 1152 | if (atom.getUnwindRecords(macho_file).len > 0) { | |
| 1153 | try bw.writeAll(" : unwind{ "); | |
| 1154 | const extra = atom.getExtra(macho_file); | |
| 1155 | for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { | |
| 1156 | const rec = file.object.getUnwindRecord(index); | |
| 1157 | try bw.print("{d}", .{index}); | |
| 1158 | if (!rec.alive) try bw.writeAll("([*])"); | |
| 1159 | if (i < extra.unwind_index + extra.unwind_count - 1) try bw.writeAll(", "); | |
| 1127 | fn print(f: Format, w: *Writer) Writer.Error!void { | |
| 1128 | const atom = f.atom; | |
| 1129 | const macho_file = f.macho_file; | |
| 1130 | const file = atom.getFile(macho_file); | |
| 1131 | try w.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ | |
| 1132 | atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), | |
| 1133 | atom.out_n_sect, atom.alignment, atom.size, | |
| 1134 | atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, | |
| 1135 | }); | |
| 1136 | if (!atom.isAlive()) try w.writeAll(" : [*]"); | |
| 1137 | if (atom.getUnwindRecords(macho_file).len > 0) { | |
| 1138 | try w.writeAll(" : unwind{ "); | |
| 1139 | const extra = atom.getExtra(macho_file); | |
| 1140 | for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { | |
| 1141 | const rec = file.object.getUnwindRecord(index); | |
| 1142 | try w.print("{d}", .{index}); | |
| 1143 | if (!rec.alive) try w.writeAll("([*])"); | |
| 1144 | if (i < extra.unwind_index + extra.unwind_count - 1) try w.writeAll(", "); | |
| 1145 | } | |
| 1146 | try w.writeAll(" }"); | |
| 1160 | 1147 | } |
| 1161 | try bw.writeAll(" }"); | |
| 1162 | 1148 | } |
| 1163 | } | |
| 1149 | }; | |
| 1164 | 1150 | |
| 1165 | 1151 | pub const Index = u32; |
| 1166 | 1152 |
src/link/MachO/Dylib.zig+15-29| ... | ... | @@ -650,46 +650,32 @@ pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void { |
| 650 | 650 | } |
| 651 | 651 | } |
| 652 | 652 | |
| 653 | pub fn format( | |
| 654 | self: *Dylib, | |
| 655 | comptime unused_fmt_string: []const u8, | |
| 656 | options: std.fmt.FormatOptions, | |
| 657 | writer: anytype, | |
| 658 | ) !void { | |
| 659 | _ = self; | |
| 660 | _ = unused_fmt_string; | |
| 661 | _ = options; | |
| 662 | _ = writer; | |
| 663 | @compileError("do not format dylib directly"); | |
| 664 | } | |
| 665 | ||
| 666 | pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { | |
| 653 | pub fn fmtSymtab(self: *Dylib, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { | |
| 667 | 654 | return .{ .data = .{ |
| 668 | 655 | .dylib = self, |
| 669 | 656 | .macho_file = macho_file, |
| 670 | 657 | } }; |
| 671 | 658 | } |
| 672 | 659 | |
| 673 | const FormatContext = struct { | |
| 660 | const Format = struct { | |
| 674 | 661 | dylib: *Dylib, |
| 675 | 662 | macho_file: *MachO, |
| 676 | }; | |
| 677 | 663 | |
| 678 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 679 | _ = unused_fmt_string; | |
| 680 | const dylib = ctx.dylib; | |
| 681 | const macho_file = ctx.macho_file; | |
| 682 | try bw.writeAll(" globals\n"); | |
| 683 | for (dylib.symbols.items, 0..) |sym, i| { | |
| 684 | const ref = dylib.getSymbolRef(@intCast(i), macho_file); | |
| 685 | if (ref.getFile(macho_file) == null) { | |
| 686 | // TODO any better way of handling this? | |
| 687 | try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); | |
| 688 | } else { | |
| 689 | try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); | |
| 664 | fn symtab(f: Format, w: *Writer) Writer.Error!void { | |
| 665 | const dylib = f.dylib; | |
| 666 | const macho_file = f.macho_file; | |
| 667 | try w.writeAll(" globals\n"); | |
| 668 | for (dylib.symbols.items, 0..) |sym, i| { | |
| 669 | const ref = dylib.getSymbolRef(@intCast(i), macho_file); | |
| 670 | if (ref.getFile(macho_file) == null) { | |
| 671 | // TODO any better way of handling this? | |
| 672 | try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); | |
| 673 | } else { | |
| 674 | try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); | |
| 675 | } | |
| 690 | 676 | } |
| 691 | 677 | } |
| 692 | } | |
| 678 | }; | |
| 693 | 679 | |
| 694 | 680 | pub const TargetMatcher = struct { |
| 695 | 681 | allocator: Allocator, |
src/link/MachO/InternalObject.zig+26-28| ... | ... | @@ -836,50 +836,48 @@ fn needsObjcMsgsendSymbol(self: InternalObject) bool { |
| 836 | 836 | return false; |
| 837 | 837 | } |
| 838 | 838 | |
| 839 | const FormatContext = struct { | |
| 839 | const Format = struct { | |
| 840 | 840 | self: *InternalObject, |
| 841 | 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 | }; |
| 843 | 866 | |
| 844 | pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { | |
| 867 | pub fn fmtAtoms(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { | |
| 845 | 868 | return .{ .data = .{ |
| 846 | 869 | .self = self, |
| 847 | 870 | .macho_file = macho_file, |
| 848 | 871 | } }; |
| 849 | 872 | } |
| 850 | 873 | |
| 851 | fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 852 | _ = unused_fmt_string; | |
| 853 | try bw.writeAll(" atoms\n"); | |
| 854 | for (ctx.self.getAtoms()) |atom_index| { | |
| 855 | const atom = ctx.self.getAtom(atom_index) orelse continue; | |
| 856 | try bw.print(" {f}\n", .{atom.fmt(ctx.macho_file)}); | |
| 857 | } | |
| 858 | } | |
| 859 | ||
| 860 | pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { | |
| 874 | pub fn fmtSymtab(self: *InternalObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { | |
| 861 | 875 | return .{ .data = .{ |
| 862 | 876 | .self = self, |
| 863 | 877 | .macho_file = macho_file, |
| 864 | 878 | } }; |
| 865 | 879 | } |
| 866 | 880 | |
| 867 | fn 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 | ||
| 883 | 881 | const Section = struct { |
| 884 | 882 | header: macho.section_64, |
| 885 | 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 | 308 | } else nlists.len; |
| 309 | 309 | |
| 310 | 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 | 314 | defer allocator.free(name); |
| 313 | 315 | const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr; |
| 314 | 316 | const atom_index = try self.addAtom(allocator, .{ |
| ... | ... | @@ -364,7 +366,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void { |
| 364 | 366 | // which cannot be contained in any non-zero atom (since then this atom |
| 365 | 367 | // would exceed section boundaries). In order to facilitate this behaviour, |
| 366 | 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 | 372 | defer allocator.free(name); |
| 369 | 373 | const atom_index = try self.addAtom(allocator, .{ |
| 370 | 374 | .name = try self.addString(allocator, name), |
| ... | ... | @@ -394,7 +398,7 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void { |
| 394 | 398 | if (isFixedSizeLiteral(sect)) continue; |
| 395 | 399 | if (isPtrLiteral(sect)) continue; |
| 396 | 400 | |
| 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 | 402 | defer allocator.free(name); |
| 399 | 403 | |
| 400 | 404 | const atom_index = try self.addAtom(allocator, .{ |
| ... | ... | @@ -462,7 +466,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m |
| 462 | 466 | } |
| 463 | 467 | end += 1; |
| 464 | 468 | |
| 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 | 470 | defer allocator.free(name); |
| 467 | 471 | const name_str = try self.addString(allocator, name); |
| 468 | 472 | |
| ... | ... | @@ -529,7 +533,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO |
| 529 | 533 | pos += rec_size; |
| 530 | 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 | 537 | defer allocator.free(name); |
| 534 | 538 | const name_str = try self.addString(allocator, name); |
| 535 | 539 | |
| ... | ... | @@ -587,7 +591,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) |
| 587 | 591 | for (0..num_ptrs) |i| { |
| 588 | 592 | const pos: u32 = @as(u32, @intCast(i)) * rec_size; |
| 589 | 593 | |
| 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 | 595 | defer allocator.free(name); |
| 592 | 596 | const name_str = try self.addString(allocator, name); |
| 593 | 597 | |
| ... | ... | @@ -1558,7 +1562,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { |
| 1558 | 1562 | const nlist = &self.symtab.items(.nlist)[nlist_idx]; |
| 1559 | 1563 | const nlist_atom = &self.symtab.items(.atom)[nlist_idx]; |
| 1560 | 1564 | |
| 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 | 1566 | defer gpa.free(name); |
| 1563 | 1567 | |
| 1564 | 1568 | const alignment = (nlist.n_desc >> 8) & 0x0f; |
| ... | ... | @@ -2512,130 +2516,114 @@ pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_ |
| 2512 | 2516 | return data; |
| 2513 | 2517 | } |
| 2514 | 2518 | |
| 2515 | pub fn format(self: *Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2516 | _ = self; | |
| 2517 | _ = bw; | |
| 2518 | _ = unused_fmt_string; | |
| 2519 | @compileError("do not format objects directly"); | |
| 2520 | } | |
| 2521 | ||
| 2522 | const FormatContext = struct { | |
| 2519 | const Format = struct { | |
| 2523 | 2520 | object: *Object, |
| 2524 | 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 | }; |
| 2526 | 2580 | |
| 2527 | pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatAtoms) { | |
| 2581 | pub fn fmtAtoms(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { | |
| 2528 | 2582 | return .{ .data = .{ |
| 2529 | 2583 | .object = self, |
| 2530 | 2584 | .macho_file = macho_file, |
| 2531 | 2585 | } }; |
| 2532 | 2586 | } |
| 2533 | 2587 | |
| 2534 | fn formatAtoms(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2535 | _ = unused_fmt_string; | |
| 2536 | const object = ctx.object; | |
| 2537 | const macho_file = ctx.macho_file; | |
| 2538 | try bw.writeAll(" atoms\n"); | |
| 2539 | for (object.getAtoms()) |atom_index| { | |
| 2540 | const atom = object.getAtom(atom_index) orelse continue; | |
| 2541 | try bw.print(" {f}\n", .{atom.fmt(macho_file)}); | |
| 2542 | } | |
| 2543 | } | |
| 2544 | ||
| 2545 | pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatCies) { | |
| 2588 | pub fn fmtCies(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.cies) { | |
| 2546 | 2589 | return .{ .data = .{ |
| 2547 | 2590 | .object = self, |
| 2548 | 2591 | .macho_file = macho_file, |
| 2549 | 2592 | } }; |
| 2550 | 2593 | } |
| 2551 | 2594 | |
| 2552 | fn formatCies(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2553 | _ = unused_fmt_string; | |
| 2554 | const object = ctx.object; | |
| 2555 | try bw.writeAll(" cies\n"); | |
| 2556 | for (object.cies.items, 0..) |cie, i| { | |
| 2557 | try bw.print(" cie({d}) : {f}\n", .{ i, cie.fmt(ctx.macho_file) }); | |
| 2558 | } | |
| 2559 | } | |
| 2560 | ||
| 2561 | pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatFdes) { | |
| 2595 | pub fn fmtFdes(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.fdes) { | |
| 2562 | 2596 | return .{ .data = .{ |
| 2563 | 2597 | .object = self, |
| 2564 | 2598 | .macho_file = macho_file, |
| 2565 | 2599 | } }; |
| 2566 | 2600 | } |
| 2567 | 2601 | |
| 2568 | fn formatFdes(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2569 | _ = unused_fmt_string; | |
| 2570 | const object = ctx.object; | |
| 2571 | try bw.writeAll(" fdes\n"); | |
| 2572 | for (object.fdes.items, 0..) |fde, i| { | |
| 2573 | try bw.print(" fde({d}) : {f}\n", .{ i, fde.fmt(ctx.macho_file) }); | |
| 2574 | } | |
| 2575 | } | |
| 2576 | ||
| 2577 | pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatUnwindRecords) { | |
| 2602 | pub fn fmtUnwindRecords(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.unwindRecords) { | |
| 2578 | 2603 | return .{ .data = .{ |
| 2579 | 2604 | .object = self, |
| 2580 | 2605 | .macho_file = macho_file, |
| 2581 | 2606 | } }; |
| 2582 | 2607 | } |
| 2583 | 2608 | |
| 2584 | fn formatUnwindRecords(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2585 | _ = unused_fmt_string; | |
| 2586 | const object = ctx.object; | |
| 2587 | const macho_file = ctx.macho_file; | |
| 2588 | try bw.writeAll(" unwind records\n"); | |
| 2589 | for (object.unwind_records_indexes.items) |rec| { | |
| 2590 | try bw.print(" rec({d}) : {f}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); | |
| 2591 | } | |
| 2592 | } | |
| 2593 | ||
| 2594 | pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { | |
| 2609 | pub fn fmtSymtab(self: *Object, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { | |
| 2595 | 2610 | return .{ .data = .{ |
| 2596 | 2611 | .object = self, |
| 2597 | 2612 | .macho_file = macho_file, |
| 2598 | 2613 | } }; |
| 2599 | 2614 | } |
| 2600 | 2615 | |
| 2601 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2602 | _ = unused_fmt_string; | |
| 2603 | const object = ctx.object; | |
| 2604 | const macho_file = ctx.macho_file; | |
| 2605 | try bw.writeAll(" symbols\n"); | |
| 2606 | for (object.symbols.items, 0..) |sym, i| { | |
| 2607 | const ref = object.getSymbolRef(@intCast(i), macho_file); | |
| 2608 | if (ref.getFile(macho_file) == null) { | |
| 2609 | // TODO any better way of handling this? | |
| 2610 | try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); | |
| 2611 | } else { | |
| 2612 | try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); | |
| 2613 | } | |
| 2614 | } | |
| 2615 | for (object.stab_files.items) |sf| { | |
| 2616 | try bw.print(" stabs({s},{s},{s})\n", .{ | |
| 2617 | sf.getCompDir(object.*), | |
| 2618 | sf.getTuName(object.*), | |
| 2619 | sf.getOsoPath(object.*), | |
| 2620 | }); | |
| 2621 | for (sf.stabs.items) |stab| { | |
| 2622 | try bw.print(" {f}", .{stab.fmt(object.*)}); | |
| 2623 | } | |
| 2624 | } | |
| 2625 | } | |
| 2626 | ||
| 2627 | pub fn fmtPath(self: Object) std.fmt.Formatter(formatPath) { | |
| 2616 | pub fn fmtPath(self: Object) std.fmt.Formatter(Object, formatPath) { | |
| 2628 | 2617 | return .{ .data = self }; |
| 2629 | 2618 | } |
| 2630 | 2619 | |
| 2631 | fn formatPath(object: Object, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2632 | _ = unused_fmt_string; | |
| 2620 | fn formatPath(object: Object, w: *Writer) Writer.Error!void { | |
| 2633 | 2621 | if (object.in_archive) |ar| { |
| 2634 | try bw.print("{f}({s})", .{ | |
| 2622 | try w.print("{f}({s})", .{ | |
| 2635 | 2623 | ar.path, object.path.basename(), |
| 2636 | 2624 | }); |
| 2637 | 2625 | } else { |
| 2638 | try bw.print("{f}", .{object.path}); | |
| 2626 | try w.print("{f}", .{object.path}); | |
| 2639 | 2627 | } |
| 2640 | 2628 | } |
| 2641 | 2629 | |
| ... | ... | @@ -2689,30 +2677,25 @@ const StabFile = struct { |
| 2689 | 2677 | return object.symbols.items[index]; |
| 2690 | 2678 | } |
| 2691 | 2679 | |
| 2692 | pub fn format(stab: Stab, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2693 | _ = stab; | |
| 2694 | _ = bw; | |
| 2695 | _ = unused_fmt_string; | |
| 2696 | @compileError("do not format stabs directly"); | |
| 2697 | } | |
| 2698 | ||
| 2699 | const StabFormatContext = struct { Stab, Object }; | |
| 2680 | const Format = struct { | |
| 2681 | stab: Stab, | |
| 2682 | object: Object, | |
| 2700 | 2683 | |
| 2701 | pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) { | |
| 2702 | return .{ .data = .{ stab, object } }; | |
| 2703 | } | |
| 2704 | ||
| 2705 | fn format2(ctx: StabFormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 2706 | _ = unused_fmt_string; | |
| 2707 | const stab, const object = ctx; | |
| 2708 | const sym = stab.getSymbol(object).?; | |
| 2709 | if (stab.is_func) { | |
| 2710 | try bw.print("func({d})", .{stab.index.?}); | |
| 2711 | } else if (sym.visibility == .global) { | |
| 2712 | try bw.print("gsym({d})", .{stab.index.?}); | |
| 2713 | } else { | |
| 2714 | try bw.print("stsym({d})", .{stab.index.?}); | |
| 2684 | fn default(f: Stab.Format, w: *Writer) Writer.Error!void { | |
| 2685 | const stab = f.stab; | |
| 2686 | const sym = stab.getSymbol(f.object).?; | |
| 2687 | if (stab.is_func) { | |
| 2688 | try w.print("func({d})", .{stab.index.?}); | |
| 2689 | } else if (sym.visibility == .global) { | |
| 2690 | try w.print("gsym({d})", .{stab.index.?}); | |
| 2691 | } else { | |
| 2692 | try w.print("stsym({d})", .{stab.index.?}); | |
| 2693 | } | |
| 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 | 70 | return lhs.offset < rhs.offset; |
| 71 | 71 | } |
| 72 | 72 | |
| 73 | const FormatCtx = struct { Relocation, std.Target.Cpu.Arch }; | |
| 74 | ||
| 75 | pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatPretty) { | |
| 76 | return .{ .data = .{ rel, cpu_arch } }; | |
| 73 | pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) { | |
| 74 | return .{ .data = .{ .relocation = rel, .arch = cpu_arch } }; | |
| 77 | 75 | } |
| 78 | 76 | |
| 79 | fn formatPretty(ctx: FormatCtx, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 80 | _ = unused_fmt_string; | |
| 81 | const rel, const cpu_arch = ctx; | |
| 82 | try bw.writeAll(switch (rel.type) { | |
| 83 | .signed => "X86_64_RELOC_SIGNED", | |
| 84 | .signed1 => "X86_64_RELOC_SIGNED_1", | |
| 85 | .signed2 => "X86_64_RELOC_SIGNED_2", | |
| 86 | .signed4 => "X86_64_RELOC_SIGNED_4", | |
| 87 | .got_load => "X86_64_RELOC_GOT_LOAD", | |
| 88 | .tlv => "X86_64_RELOC_TLV", | |
| 89 | .page => "ARM64_RELOC_PAGE21", | |
| 90 | .pageoff => "ARM64_RELOC_PAGEOFF12", | |
| 91 | .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21", | |
| 92 | .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", | |
| 93 | .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", | |
| 94 | .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", | |
| 95 | .branch => switch (cpu_arch) { | |
| 96 | .x86_64 => "X86_64_RELOC_BRANCH", | |
| 97 | .aarch64 => "ARM64_RELOC_BRANCH26", | |
| 98 | else => unreachable, | |
| 99 | }, | |
| 100 | .got => switch (cpu_arch) { | |
| 101 | .x86_64 => "X86_64_RELOC_GOT", | |
| 102 | .aarch64 => "ARM64_RELOC_POINTER_TO_GOT", | |
| 103 | else => unreachable, | |
| 104 | }, | |
| 105 | .subtractor => switch (cpu_arch) { | |
| 106 | .x86_64 => "X86_64_RELOC_SUBTRACTOR", | |
| 107 | .aarch64 => "ARM64_RELOC_SUBTRACTOR", | |
| 108 | else => unreachable, | |
| 109 | }, | |
| 110 | .unsigned => switch (cpu_arch) { | |
| 111 | .x86_64 => "X86_64_RELOC_UNSIGNED", | |
| 112 | .aarch64 => "ARM64_RELOC_UNSIGNED", | |
| 113 | else => unreachable, | |
| 114 | }, | |
| 115 | }); | |
| 116 | } | |
| 77 | const Format = struct { | |
| 78 | relocation: Relocation, | |
| 79 | arch: std.Target.Cpu.Arch, | |
| 80 | ||
| 81 | fn pretty(f: Format, w: *Writer) Writer.Error!void { | |
| 82 | try w.writeAll(switch (f.relocation.type) { | |
| 83 | .signed => "X86_64_RELOC_SIGNED", | |
| 84 | .signed1 => "X86_64_RELOC_SIGNED_1", | |
| 85 | .signed2 => "X86_64_RELOC_SIGNED_2", | |
| 86 | .signed4 => "X86_64_RELOC_SIGNED_4", | |
| 87 | .got_load => "X86_64_RELOC_GOT_LOAD", | |
| 88 | .tlv => "X86_64_RELOC_TLV", | |
| 89 | .page => "ARM64_RELOC_PAGE21", | |
| 90 | .pageoff => "ARM64_RELOC_PAGEOFF12", | |
| 91 | .got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21", | |
| 92 | .got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12", | |
| 93 | .tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21", | |
| 94 | .tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", | |
| 95 | .branch => switch (f.arch) { | |
| 96 | .x86_64 => "X86_64_RELOC_BRANCH", | |
| 97 | .aarch64 => "ARM64_RELOC_BRANCH26", | |
| 98 | else => unreachable, | |
| 99 | }, | |
| 100 | .got => switch (f.arch) { | |
| 101 | .x86_64 => "X86_64_RELOC_GOT", | |
| 102 | .aarch64 => "ARM64_RELOC_POINTER_TO_GOT", | |
| 103 | else => unreachable, | |
| 104 | }, | |
| 105 | .subtractor => switch (f.arch) { | |
| 106 | .x86_64 => "X86_64_RELOC_SUBTRACTOR", | |
| 107 | .aarch64 => "ARM64_RELOC_SUBTRACTOR", | |
| 108 | else => unreachable, | |
| 109 | }, | |
| 110 | .unsigned => switch (f.arch) { | |
| 111 | .x86_64 => "X86_64_RELOC_UNSIGNED", | |
| 112 | .aarch64 => "ARM64_RELOC_UNSIGNED", | |
| 113 | else => unreachable, | |
| 114 | }, | |
| 115 | }); | |
| 116 | } | |
| 117 | }; | |
| 117 | 118 | |
| 118 | 119 | pub const Type = enum { |
| 119 | 120 | // 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 | 286 | } |
| 287 | 287 | } |
| 288 | 288 | |
| 289 | pub fn format(symbol: Symbol, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 290 | _ = symbol; | |
| 291 | _ = bw; | |
| 292 | _ = unused_fmt_string; | |
| 293 | @compileError("do not format symbols directly"); | |
| 294 | } | |
| 295 | ||
| 296 | const FormatContext = struct { | |
| 297 | symbol: Symbol, | |
| 298 | macho_file: *MachO, | |
| 299 | }; | |
| 300 | ||
| 301 | pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 289 | pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { | |
| 302 | 290 | return .{ .data = .{ |
| 303 | 291 | .symbol = symbol, |
| 304 | 292 | .macho_file = macho_file, |
| 305 | 293 | } }; |
| 306 | 294 | } |
| 307 | 295 | |
| 308 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 309 | comptime assert(unused_fmt_string.len == 0); | |
| 310 | const symbol = ctx.symbol; | |
| 311 | try bw.print("%{d} : {s} : @{x}", .{ | |
| 312 | symbol.nlist_idx, | |
| 313 | symbol.getName(ctx.macho_file), | |
| 314 | symbol.getAddress(.{}, ctx.macho_file), | |
| 315 | }); | |
| 316 | if (symbol.getFile(ctx.macho_file)) |file| { | |
| 317 | if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) { | |
| 318 | try bw.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)}); | |
| 319 | } | |
| 320 | if (symbol.getAtom(ctx.macho_file)) |atom| { | |
| 321 | try bw.print(" : atom({d})", .{atom.atom_index}); | |
| 322 | } | |
| 323 | var buf: [3]u8 = .{'_'} ** 3; | |
| 324 | if (symbol.flags.@"export") buf[0] = 'E'; | |
| 325 | if (symbol.flags.import) buf[1] = 'I'; | |
| 326 | switch (symbol.visibility) { | |
| 327 | .local => buf[2] = 'L', | |
| 328 | .hidden => buf[2] = 'H', | |
| 329 | .global => buf[2] = 'G', | |
| 330 | } | |
| 331 | try bw.print(" : {s}", .{&buf}); | |
| 332 | if (symbol.flags.weak) try bw.writeAll(" : weak"); | |
| 333 | if (symbol.isSymbolStab(ctx.macho_file)) try bw.writeAll(" : stab"); | |
| 334 | switch (file) { | |
| 335 | .zig_object => |x| try bw.print(" : zig_object({d})", .{x.index}), | |
| 336 | .internal => |x| try bw.print(" : internal({d})", .{x.index}), | |
| 337 | .object => |x| try bw.print(" : object({d})", .{x.index}), | |
| 338 | .dylib => |x| try bw.print(" : dylib({d})", .{x.index}), | |
| 339 | } | |
| 340 | } else try bw.writeAll(" : unresolved"); | |
| 341 | } | |
| 296 | const Format = struct { | |
| 297 | symbol: Symbol, | |
| 298 | macho_file: *MachO, | |
| 299 | ||
| 300 | fn default(f: Format, w: *Writer) Writer.Error!void { | |
| 301 | const symbol = f.symbol; | |
| 302 | try w.print("%{d} : {s} : @{x}", .{ | |
| 303 | symbol.nlist_idx, | |
| 304 | symbol.getName(f.macho_file), | |
| 305 | symbol.getAddress(.{}, f.macho_file), | |
| 306 | }); | |
| 307 | if (symbol.getFile(f.macho_file)) |file| { | |
| 308 | if (symbol.getOutputSectionIndex(f.macho_file) != 0) { | |
| 309 | try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)}); | |
| 310 | } | |
| 311 | if (symbol.getAtom(f.macho_file)) |atom| { | |
| 312 | try w.print(" : atom({d})", .{atom.atom_index}); | |
| 313 | } | |
| 314 | var buf: [3]u8 = .{'_'} ** 3; | |
| 315 | if (symbol.flags.@"export") buf[0] = 'E'; | |
| 316 | if (symbol.flags.import) buf[1] = 'I'; | |
| 317 | switch (symbol.visibility) { | |
| 318 | .local => buf[2] = 'L', | |
| 319 | .hidden => buf[2] = 'H', | |
| 320 | .global => buf[2] = 'G', | |
| 321 | } | |
| 322 | try w.print(" : {s}", .{&buf}); | |
| 323 | if (symbol.flags.weak) try w.writeAll(" : weak"); | |
| 324 | if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab"); | |
| 325 | switch (file) { | |
| 326 | .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}), | |
| 327 | .internal => |x| try w.print(" : internal({d})", .{x.index}), | |
| 328 | .object => |x| try w.print(" : object({d})", .{x.index}), | |
| 329 | .dylib => |x| try w.print(" : dylib({d})", .{x.index}), | |
| 330 | } | |
| 331 | } else try w.writeAll(" : unresolved"); | |
| 332 | } | |
| 333 | }; | |
| 342 | 334 | |
| 343 | 335 | pub const Flags = packed struct { |
| 344 | 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 | 61 | } |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | pub fn format(thunk: Thunk, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 65 | _ = thunk; | |
| 66 | _ = bw; | |
| 67 | _ = unused_fmt_string; | |
| 68 | @compileError("do not format Thunk directly"); | |
| 69 | } | |
| 70 | ||
| 71 | pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 64 | pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { | |
| 72 | 65 | return .{ .data = .{ |
| 73 | 66 | .thunk = thunk, |
| 74 | 67 | .macho_file = macho_file, |
| 75 | 68 | } }; |
| 76 | 69 | } |
| 77 | 70 | |
| 78 | const FormatContext = struct { | |
| 71 | const Format = struct { | |
| 79 | 72 | thunk: Thunk, |
| 80 | 73 | macho_file: *MachO, |
| 81 | }; | |
| 82 | 74 | |
| 83 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 84 | _ = unused_fmt_string; | |
| 85 | const thunk = ctx.thunk; | |
| 86 | const macho_file = ctx.macho_file; | |
| 87 | try bw.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); | |
| 88 | for (thunk.symbols.keys()) |ref| { | |
| 89 | const sym = ref.getSymbol(macho_file).?; | |
| 90 | try bw.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); | |
| 75 | fn default(f: Format, w: *Writer) Writer.Error!void { | |
| 76 | const thunk = f.thunk; | |
| 77 | const macho_file = f.macho_file; | |
| 78 | try w.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); | |
| 79 | for (thunk.symbols.keys()) |ref| { | |
| 80 | const sym = ref.getSymbol(macho_file).?; | |
| 81 | try w.print(" {f} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); | |
| 82 | } | |
| 91 | 83 | } |
| 92 | } | |
| 84 | }; | |
| 93 | 85 | |
| 94 | 86 | const trampoline_size = 3 * @sizeOf(u32); |
| 95 | 87 |
src/link/MachO/UnwindInfo.zig+29-46| ... | ... | @@ -449,9 +449,8 @@ pub const Encoding = extern struct { |
| 449 | 449 | return enc.enc == other.enc; |
| 450 | 450 | } |
| 451 | 451 | |
| 452 | pub fn format(enc: Encoding, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 453 | _ = unused_fmt_string; | |
| 454 | try bw.print("0x{x:0>8}", .{enc.enc}); | |
| 452 | pub fn format(enc: Encoding, w: *Writer) Writer.Error!void { | |
| 453 | try w.print("0x{x:0>8}", .{enc.enc}); | |
| 455 | 454 | } |
| 456 | 455 | }; |
| 457 | 456 | |
| ... | ... | @@ -505,36 +504,28 @@ pub const Record = struct { |
| 505 | 504 | return lsda.getAddress(macho_file) + rec.lsda_offset; |
| 506 | 505 | } |
| 507 | 506 | |
| 508 | pub fn format(rec: Record, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 509 | _ = rec; | |
| 510 | _ = bw; | |
| 511 | _ = unused_fmt_string; | |
| 512 | @compileError("do not format UnwindInfo.Records directly"); | |
| 513 | } | |
| 514 | ||
| 515 | pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 507 | pub fn fmt(rec: Record, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { | |
| 516 | 508 | return .{ .data = .{ |
| 517 | 509 | .rec = rec, |
| 518 | 510 | .macho_file = macho_file, |
| 519 | 511 | } }; |
| 520 | 512 | } |
| 521 | 513 | |
| 522 | const FormatContext = struct { | |
| 514 | const Format = struct { | |
| 523 | 515 | rec: Record, |
| 524 | 516 | macho_file: *MachO, |
| 525 | }; | |
| 526 | 517 | |
| 527 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 528 | _ = unused_fmt_string; | |
| 529 | const rec = ctx.rec; | |
| 530 | const macho_file = ctx.macho_file; | |
| 531 | try bw.print("{x} : len({x})", .{ | |
| 532 | rec.enc.enc, rec.length, | |
| 533 | }); | |
| 534 | if (rec.enc.isDwarf(macho_file)) try bw.print(" : fde({d})", .{rec.fde}); | |
| 535 | try bw.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); | |
| 536 | if (!rec.alive) try bw.writeAll(" : [*]"); | |
| 537 | } | |
| 518 | fn default(f: Format, w: *Writer) Writer.Error!void { | |
| 519 | const rec = f.rec; | |
| 520 | const macho_file = f.macho_file; | |
| 521 | try w.print("{x} : len({x})", .{ | |
| 522 | rec.enc.enc, rec.length, | |
| 523 | }); | |
| 524 | if (rec.enc.isDwarf(macho_file)) try w.print(" : fde({d})", .{rec.fde}); | |
| 525 | try w.print(" : {s}", .{rec.getAtom(macho_file).getName(macho_file)}); | |
| 526 | if (!rec.alive) try w.writeAll(" : [*]"); | |
| 527 | } | |
| 528 | }; | |
| 538 | 529 | |
| 539 | 530 | pub const Index = u32; |
| 540 | 531 | |
| ... | ... | @@ -589,33 +580,25 @@ const Page = struct { |
| 589 | 580 | return null; |
| 590 | 581 | } |
| 591 | 582 | |
| 592 | fn format(page: *const Page, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { | |
| 593 | _ = page; | |
| 594 | _ = bw; | |
| 595 | _ = unused_format_string; | |
| 596 | @compileError("do not format Page directly; use page.fmt()"); | |
| 597 | } | |
| 598 | ||
| 599 | const FormatPageContext = struct { | |
| 583 | const Format = struct { | |
| 600 | 584 | page: Page, |
| 601 | 585 | info: UnwindInfo, |
| 602 | }; | |
| 603 | 586 | |
| 604 | fn format2(ctx: FormatPageContext, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { | |
| 605 | _ = unused_format_string; | |
| 606 | try bw.writeAll("Page:\n"); | |
| 607 | try bw.print(" kind: {s}\n", .{@tagName(ctx.page.kind)}); | |
| 608 | try bw.print(" entries: {d} - {d}\n", .{ | |
| 609 | ctx.page.start, | |
| 610 | ctx.page.start + ctx.page.count, | |
| 611 | }); | |
| 612 | try bw.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count}); | |
| 613 | for (ctx.page.page_encodings[0..ctx.page.page_encodings_count], 0..) |enc, i| { | |
| 614 | try bw.print(" {d}: {f}\n", .{ ctx.info.common_encodings_count + i, enc }); | |
| 587 | fn default(f: Format, w: *Writer) Writer.Error!void { | |
| 588 | try w.writeAll("Page:\n"); | |
| 589 | try w.print(" kind: {s}\n", .{@tagName(f.page.kind)}); | |
| 590 | try w.print(" entries: {d} - {d}\n", .{ | |
| 591 | f.page.start, | |
| 592 | f.page.start + f.page.count, | |
| 593 | }); | |
| 594 | try w.print(" encodings (count = {d})\n", .{f.page.page_encodings_count}); | |
| 595 | for (f.page.page_encodings[0..f.page.page_encodings_count], 0..) |enc, i| { | |
| 596 | try w.print(" {d}: {f}\n", .{ f.info.common_encodings_count + i, enc }); | |
| 597 | } | |
| 615 | 598 | } |
| 616 | } | |
| 599 | }; | |
| 617 | 600 | |
| 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 | 602 | return .{ .data = .{ |
| 620 | 603 | .page = page, |
| 621 | 604 | .info = info, |
src/link/MachO/ZigObject.zig+27-29| ... | ... | @@ -957,7 +957,7 @@ fn updateNavCode( |
| 957 | 957 | sym.out_n_sect = sect_index; |
| 958 | 958 | atom.out_n_sect = sect_index; |
| 959 | 959 | |
| 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 | 961 | defer gpa.free(sym_name); |
| 962 | 962 | sym.name = try self.addString(gpa, sym_name); |
| 963 | 963 | atom.setAlive(true); |
| ... | ... | @@ -1676,52 +1676,50 @@ pub fn asFile(self: *ZigObject) File { |
| 1676 | 1676 | return .{ .zig_object = self }; |
| 1677 | 1677 | } |
| 1678 | 1678 | |
| 1679 | pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(formatSymtab) { | |
| 1679 | pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.symtab) { | |
| 1680 | 1680 | return .{ .data = .{ |
| 1681 | 1681 | .self = self, |
| 1682 | 1682 | .macho_file = macho_file, |
| 1683 | 1683 | } }; |
| 1684 | 1684 | } |
| 1685 | 1685 | |
| 1686 | const FormatContext = struct { | |
| 1686 | const Format = struct { | |
| 1687 | 1687 | self: *ZigObject, |
| 1688 | 1688 | macho_file: *MachO, |
| 1689 | }; | |
| 1690 | 1689 | |
| 1691 | fn formatSymtab(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 1692 | _ = unused_fmt_string; | |
| 1693 | try bw.writeAll(" symbols\n"); | |
| 1694 | const self = ctx.self; | |
| 1695 | const macho_file = ctx.macho_file; | |
| 1696 | for (self.symbols.items, 0..) |sym, i| { | |
| 1697 | const ref = self.getSymbolRef(@intCast(i), macho_file); | |
| 1698 | if (ref.getFile(macho_file) == null) { | |
| 1699 | // TODO any better way of handling this? | |
| 1700 | try bw.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); | |
| 1701 | } else { | |
| 1702 | try bw.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); | |
| 1690 | fn symtab(f: Format, w: *Writer) Writer.Error!void { | |
| 1691 | try w.writeAll(" symbols\n"); | |
| 1692 | const self = f.self; | |
| 1693 | const macho_file = f.macho_file; | |
| 1694 | for (self.symbols.items, 0..) |sym, i| { | |
| 1695 | const ref = self.getSymbolRef(@intCast(i), macho_file); | |
| 1696 | if (ref.getFile(macho_file) == null) { | |
| 1697 | // TODO any better way of handling this? | |
| 1698 | try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); | |
| 1699 | } else { | |
| 1700 | try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); | |
| 1701 | } | |
| 1703 | 1702 | } |
| 1704 | 1703 | } |
| 1705 | } | |
| 1706 | 1704 | |
| 1707 | pub 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 | ||
| 1716 | pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Formatter(Format, Format.atoms) { | |
| 1708 | 1717 | return .{ .data = .{ |
| 1709 | 1718 | .self = self, |
| 1710 | 1719 | .macho_file = macho_file, |
| 1711 | 1720 | } }; |
| 1712 | 1721 | } |
| 1713 | 1722 | |
| 1714 | fn 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 | ||
| 1725 | 1723 | const AvMetadata = struct { |
| 1726 | 1724 | symbol_index: Symbol.Index, |
| 1727 | 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 | 196 | self.value += 1; |
| 197 | 197 | } |
| 198 | 198 | |
| 199 | pub fn format(self: *const @This(), bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 200 | _ = unused_fmt_string; | |
| 201 | try bw.splatByteAll(' ', self.value); | |
| 199 | pub fn format(self: *const @This(), w: *Writer) Writer.Error!void { | |
| 200 | try w.splatByteAll(' ', self.value); | |
| 202 | 201 | } |
| 203 | 202 | }; |
| 204 | 203 |
src/link/MachO/dyld_info/bind.zig+2-2| ... | ... | @@ -193,7 +193,7 @@ pub const Bind = struct { |
| 193 | 193 | } |
| 194 | 194 | } |
| 195 | 195 | |
| 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 | 197 | log.debug(" => {x}", .{current.offset}); |
| 198 | 198 | switch (state) { |
| 199 | 199 | .start => { |
| ... | ... | @@ -423,7 +423,7 @@ pub const WeakBind = struct { |
| 423 | 423 | } |
| 424 | 424 | } |
| 425 | 425 | |
| 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 | 427 | log.debug(" => {x}", .{current.offset}); |
| 428 | 428 | switch (state) { |
| 429 | 429 | .start => { |
src/link/MachO/eh_frame.zig+25-53| ... | ... | @@ -78,40 +78,26 @@ pub const Cie = struct { |
| 78 | 78 | return true; |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | pub fn format( | |
| 82 | cie: Cie, | |
| 83 | comptime unused_fmt_string: []const u8, | |
| 84 | options: std.fmt.FormatOptions, | |
| 85 | writer: anytype, | |
| 86 | ) !void { | |
| 87 | _ = cie; | |
| 88 | _ = unused_fmt_string; | |
| 89 | _ = options; | |
| 90 | _ = writer; | |
| 91 | @compileError("do not format CIEs directly"); | |
| 92 | } | |
| 93 | ||
| 94 | pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 81 | pub fn fmt(cie: Cie, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { | |
| 95 | 82 | return .{ .data = .{ |
| 96 | 83 | .cie = cie, |
| 97 | 84 | .macho_file = macho_file, |
| 98 | 85 | } }; |
| 99 | 86 | } |
| 100 | 87 | |
| 101 | const FormatContext = struct { | |
| 88 | const Format = struct { | |
| 102 | 89 | cie: Cie, |
| 103 | 90 | macho_file: *MachO, |
| 104 | }; | |
| 105 | 91 | |
| 106 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 107 | _ = unused_fmt_string; | |
| 108 | const cie = ctx.cie; | |
| 109 | try bw.print("@{x} : size({x})", .{ | |
| 110 | cie.offset, | |
| 111 | cie.getSize(), | |
| 112 | }); | |
| 113 | if (!cie.alive) try bw.writeAll(" : [*]"); | |
| 114 | } | |
| 92 | fn default(f: Format, w: *Writer) Writer.Error!void { | |
| 93 | const cie = f.cie; | |
| 94 | try w.print("@{x} : size({x})", .{ | |
| 95 | cie.offset, | |
| 96 | cie.getSize(), | |
| 97 | }); | |
| 98 | if (!cie.alive) try w.writeAll(" : [*]"); | |
| 99 | } | |
| 100 | }; | |
| 115 | 101 | |
| 116 | 102 | pub const Index = u32; |
| 117 | 103 | |
| ... | ... | @@ -223,43 +209,29 @@ pub const Fde = struct { |
| 223 | 209 | return fde.getObject(macho_file).getAtom(fde.lsda); |
| 224 | 210 | } |
| 225 | 211 | |
| 226 | pub fn format( | |
| 227 | fde: Fde, | |
| 228 | comptime unused_fmt_string: []const u8, | |
| 229 | options: std.fmt.FormatOptions, | |
| 230 | writer: anytype, | |
| 231 | ) !void { | |
| 232 | _ = fde; | |
| 233 | _ = unused_fmt_string; | |
| 234 | _ = options; | |
| 235 | _ = writer; | |
| 236 | @compileError("do not format FDEs directly"); | |
| 237 | } | |
| 238 | ||
| 239 | pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 212 | pub fn fmt(fde: Fde, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) { | |
| 240 | 213 | return .{ .data = .{ |
| 241 | 214 | .fde = fde, |
| 242 | 215 | .macho_file = macho_file, |
| 243 | 216 | } }; |
| 244 | 217 | } |
| 245 | 218 | |
| 246 | const FormatContext = struct { | |
| 219 | const Format = struct { | |
| 247 | 220 | fde: Fde, |
| 248 | 221 | macho_file: *MachO, |
| 249 | }; | |
| 250 | 222 | |
| 251 | fn format2(ctx: FormatContext, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 252 | _ = unused_fmt_string; | |
| 253 | const fde = ctx.fde; | |
| 254 | const macho_file = ctx.macho_file; | |
| 255 | try bw.print("@{x} : size({x}) : cie({d}) : {s}", .{ | |
| 256 | fde.offset, | |
| 257 | fde.getSize(), | |
| 258 | fde.cie, | |
| 259 | fde.getAtom(macho_file).getName(macho_file), | |
| 260 | }); | |
| 261 | if (!fde.alive) try bw.writeAll(" : [*]"); | |
| 262 | } | |
| 223 | fn default(f: Format, writer: *Writer) Writer.Error!void { | |
| 224 | const fde = f.fde; | |
| 225 | const macho_file = f.macho_file; | |
| 226 | try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ | |
| 227 | fde.offset, | |
| 228 | fde.getSize(), | |
| 229 | fde.cie, | |
| 230 | fde.getAtom(macho_file).getName(macho_file), | |
| 231 | }); | |
| 232 | if (!fde.alive) try writer.writeAll(" : [*]"); | |
| 233 | } | |
| 234 | }; | |
| 263 | 235 | |
| 264 | 236 | pub const Index = u32; |
| 265 | 237 | }; |
src/link/MachO/file.zig+6-7| ... | ... | @@ -10,17 +10,16 @@ pub const File = union(enum) { |
| 10 | 10 | }; |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { | |
| 13 | pub fn fmtPath(file: File) std.fmt.Formatter(File, formatPath) { | |
| 14 | 14 | return .{ .data = file }; |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | fn formatPath(file: File, bw: *Writer, comptime unused_fmt_string: []const u8) Writer.Error!void { | |
| 18 | _ = unused_fmt_string; | |
| 17 | fn formatPath(file: File, w: *Writer) Writer.Error!void { | |
| 19 | 18 | switch (file) { |
| 20 | .zig_object => |zo| try bw.writeAll(zo.basename), | |
| 21 | .internal => try bw.writeAll("internal"), | |
| 22 | .object => |x| try bw.print("{f}", .{x.fmtPath()}), | |
| 23 | .dylib => |dl| try bw.print("{f}", .{@as(Path, dl.path)}), | |
| 19 | .zig_object => |zo| try w.writeAll(zo.basename), | |
| 20 | .internal => try w.writeAll("internal"), | |
| 21 | .object => |x| try w.print("{f}", .{x.fmtPath()}), | |
| 22 | .dylib => |dl| try w.print("{f}", .{@as(Path, dl.path)}), | |
| 24 | 23 | } |
| 25 | 24 | } |
| 26 | 25 |
src/link/MachO/synthetic.zig+66-86| ... | ... | @@ -37,32 +37,27 @@ pub const GotSection = struct { |
| 37 | 37 | } |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | const FormatCtx = struct { | |
| 40 | const Format = struct { | |
| 41 | 41 | got: GotSection, |
| 42 | 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 | }; |
| 44 | 57 | |
| 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 | 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 | }; |
| 67 | 62 | |
| 68 | 63 | pub const StubsSection = struct { |
| ... | ... | @@ -126,32 +121,27 @@ pub const StubsSection = struct { |
| 126 | 121 | } |
| 127 | 122 | } |
| 128 | 123 | |
| 129 | const FormatCtx = struct { | |
| 130 | stubs: StubsSection, | |
| 131 | macho_file: *MachO, | |
| 132 | }; | |
| 133 | ||
| 134 | pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 124 | pub fn fmt(stubs: StubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { | |
| 135 | 125 | return .{ .data = .{ .stubs = stubs, .macho_file = macho_file } }; |
| 136 | 126 | } |
| 137 | 127 | |
| 138 | pub fn format2( | |
| 139 | ctx: FormatCtx, | |
| 140 | bw: *Writer, | |
| 141 | comptime unused_fmt_string: []const u8, | |
| 142 | ) !void { | |
| 143 | _ = unused_fmt_string; | |
| 144 | for (ctx.stubs.symbols.items, 0..) |ref, i| { | |
| 145 | const symbol = ref.getSymbol(ctx.macho_file).?; | |
| 146 | try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ | |
| 147 | i, | |
| 148 | symbol.getStubsAddress(ctx.macho_file), | |
| 149 | ref, | |
| 150 | symbol.getAddress(.{}, ctx.macho_file), | |
| 151 | symbol.getName(ctx.macho_file), | |
| 152 | }); | |
| 128 | const Format = struct { | |
| 129 | stubs: StubsSection, | |
| 130 | macho_file: *MachO, | |
| 131 | ||
| 132 | pub fn print(f: Format, w: *Writer) Writer.Error!void { | |
| 133 | for (f.stubs.symbols.items, 0..) |ref, i| { | |
| 134 | const symbol = ref.getSymbol(f.macho_file).?; | |
| 135 | try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ | |
| 136 | i, | |
| 137 | symbol.getStubsAddress(f.macho_file), | |
| 138 | ref, | |
| 139 | symbol.getAddress(.{}, f.macho_file), | |
| 140 | symbol.getName(f.macho_file), | |
| 141 | }); | |
| 142 | } | |
| 153 | 143 | } |
| 154 | } | |
| 144 | }; | |
| 155 | 145 | }; |
| 156 | 146 | |
| 157 | 147 | pub const StubsHelperSection = struct { |
| ... | ... | @@ -353,32 +343,27 @@ pub const TlvPtrSection = struct { |
| 353 | 343 | } |
| 354 | 344 | } |
| 355 | 345 | |
| 356 | const FormatCtx = struct { | |
| 357 | tlv: TlvPtrSection, | |
| 358 | macho_file: *MachO, | |
| 359 | }; | |
| 360 | ||
| 361 | pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 346 | pub fn fmt(tlv: TlvPtrSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { | |
| 362 | 347 | return .{ .data = .{ .tlv = tlv, .macho_file = macho_file } }; |
| 363 | 348 | } |
| 364 | 349 | |
| 365 | pub fn format2( | |
| 366 | ctx: FormatCtx, | |
| 367 | bw: *Writer, | |
| 368 | comptime unused_fmt_string: []const u8, | |
| 369 | ) !void { | |
| 370 | _ = unused_fmt_string; | |
| 371 | for (ctx.tlv.symbols.items, 0..) |ref, i| { | |
| 372 | const symbol = ref.getSymbol(ctx.macho_file).?; | |
| 373 | try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ | |
| 374 | i, | |
| 375 | symbol.getTlvPtrAddress(ctx.macho_file), | |
| 376 | ref, | |
| 377 | symbol.getAddress(.{}, ctx.macho_file), | |
| 378 | symbol.getName(ctx.macho_file), | |
| 379 | }); | |
| 350 | const Format = struct { | |
| 351 | tlv: TlvPtrSection, | |
| 352 | macho_file: *MachO, | |
| 353 | ||
| 354 | pub fn print(f: Format, w: *Writer) Writer.Error!void { | |
| 355 | for (f.tlv.symbols.items, 0..) |ref, i| { | |
| 356 | const symbol = ref.getSymbol(f.macho_file).?; | |
| 357 | try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ | |
| 358 | i, | |
| 359 | symbol.getTlvPtrAddress(f.macho_file), | |
| 360 | ref, | |
| 361 | symbol.getAddress(.{}, f.macho_file), | |
| 362 | symbol.getName(f.macho_file), | |
| 363 | }); | |
| 364 | } | |
| 380 | 365 | } |
| 381 | } | |
| 366 | }; | |
| 382 | 367 | }; |
| 383 | 368 | |
| 384 | 369 | pub const ObjcStubsSection = struct { |
| ... | ... | @@ -476,32 +461,27 @@ pub const ObjcStubsSection = struct { |
| 476 | 461 | } |
| 477 | 462 | } |
| 478 | 463 | |
| 479 | const FormatCtx = struct { | |
| 480 | objc: ObjcStubsSection, | |
| 481 | macho_file: *MachO, | |
| 482 | }; | |
| 483 | ||
| 484 | pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 464 | pub fn fmt(objc: ObjcStubsSection, macho_file: *MachO) std.fmt.Formatter(Format, Format.print) { | |
| 485 | 465 | return .{ .data = .{ .objc = objc, .macho_file = macho_file } }; |
| 486 | 466 | } |
| 487 | 467 | |
| 488 | pub fn format2( | |
| 489 | ctx: FormatCtx, | |
| 490 | bw: *Writer, | |
| 491 | comptime unused_fmt_string: []const u8, | |
| 492 | ) !void { | |
| 493 | _ = unused_fmt_string; | |
| 494 | for (ctx.objc.symbols.items, 0..) |ref, i| { | |
| 495 | const symbol = ref.getSymbol(ctx.macho_file).?; | |
| 496 | try bw.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ | |
| 497 | i, | |
| 498 | symbol.getObjcStubsAddress(ctx.macho_file), | |
| 499 | ref, | |
| 500 | symbol.getAddress(.{}, ctx.macho_file), | |
| 501 | symbol.getName(ctx.macho_file), | |
| 502 | }); | |
| 468 | const Format = struct { | |
| 469 | objc: ObjcStubsSection, | |
| 470 | macho_file: *MachO, | |
| 471 | ||
| 472 | pub fn print(f: Format, w: *Writer) Writer.Error!void { | |
| 473 | for (f.objc.symbols.items, 0..) |ref, i| { | |
| 474 | const symbol = ref.getSymbol(f.macho_file).?; | |
| 475 | try w.print(" {d}@0x{x} => {f}@0x{x} ({s})\n", .{ | |
| 476 | i, | |
| 477 | symbol.getObjcStubsAddress(f.macho_file), | |
| 478 | ref, | |
| 479 | symbol.getAddress(.{}, f.macho_file), | |
| 480 | symbol.getName(f.macho_file), | |
| 481 | }); | |
| 482 | } | |
| 503 | 483 | } |
| 504 | } | |
| 484 | }; | |
| 505 | 485 | |
| 506 | 486 | pub const Index = u32; |
| 507 | 487 | }; |
src/link/SpirV.zig+4-4| ... | ... | @@ -206,7 +206,7 @@ pub fn flush( |
| 206 | 206 | var error_info: std.io.Writer.Allocating = .init(self.object.gpa); |
| 207 | 207 | defer error_info.deinit(); |
| 208 | 208 | |
| 209 | try error_info.writer.writeAll("zig_errors:"); | |
| 209 | error_info.writer.writeAll("zig_errors:") catch return error.OutOfMemory; | |
| 210 | 210 | const ip = &self.base.comp.zcu.?.intern_pool; |
| 211 | 211 | for (ip.global_error_set.getNamesFromMainThread()) |name| { |
| 212 | 212 | // Errors can contain pretty much any character - to encode them in a string we must escape |
| ... | ... | @@ -214,8 +214,8 @@ pub fn flush( |
| 214 | 214 | // name if it contains no strange characters is nice for debugging. URI encoding fits the bill. |
| 215 | 215 | // We're using : as separator, which is a reserved character. |
| 216 | 216 | |
| 217 | try error_info.writer.writeByte(':'); | |
| 218 | try std.Uri.Component.percentEncode( | |
| 217 | error_info.writer.writeByte(':') catch return error.OutOfMemory; | |
| 218 | std.Uri.Component.percentEncode( | |
| 219 | 219 | &error_info.writer, |
| 220 | 220 | name.toSlice(ip), |
| 221 | 221 | struct { |
| ... | ... | @@ -226,7 +226,7 @@ pub fn flush( |
| 226 | 226 | }; |
| 227 | 227 | } |
| 228 | 228 | }.isValidChar, |
| 229 | ); | |
| 229 | ) catch return error.OutOfMemory; | |
| 230 | 230 | } |
| 231 | 231 | try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{ |
| 232 | 232 | .extension = error_info.getWritten(), |
src/link/Wasm.zig+12-14| ... | ... | @@ -32,7 +32,7 @@ const Writer = std.io.Writer; |
| 32 | 32 | |
| 33 | 33 | const Mir = @import("../arch/wasm/Mir.zig"); |
| 34 | 34 | const CodeGen = @import("../arch/wasm/CodeGen.zig"); |
| 35 | const abi = @import("../arch/wasm/abi.zig"); | |
| 35 | const abi = @import("../codegen/wasm/abi.zig"); | |
| 36 | 36 | const Compilation = @import("../Compilation.zig"); |
| 37 | 37 | const Dwarf = @import("Dwarf.zig"); |
| 38 | 38 | const InternPool = @import("../InternPool.zig"); |
| ... | ... | @@ -2125,26 +2125,25 @@ pub const FunctionType = extern struct { |
| 2125 | 2125 | wasm: *const Wasm, |
| 2126 | 2126 | ft: FunctionType, |
| 2127 | 2127 | |
| 2128 | pub fn format(self: Formatter, bw: *Writer, comptime format_string: []const u8) Writer.Error!void { | |
| 2129 | comptime assert(format_string.len == 0); | |
| 2128 | pub fn format(self: Formatter, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 2130 | 2129 | const params = self.ft.params.slice(self.wasm); |
| 2131 | 2130 | const returns = self.ft.returns.slice(self.wasm); |
| 2132 | 2131 | |
| 2133 | try bw.writeByte('('); | |
| 2132 | try writer.writeByte('('); | |
| 2134 | 2133 | for (params, 0..) |param, i| { |
| 2135 | try bw.print("{s}", .{@tagName(param)}); | |
| 2134 | try writer.print("{s}", .{@tagName(param)}); | |
| 2136 | 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 | 2140 | if (returns.len == 0) { |
| 2142 | try bw.writeAll("nil"); | |
| 2141 | try writer.writeAll("nil"); | |
| 2143 | 2142 | } else { |
| 2144 | 2143 | for (returns, 0..) |return_ty, i| { |
| 2145 | try bw.print("{s}", .{@tagName(return_ty)}); | |
| 2144 | try writer.print("{s}", .{@tagName(return_ty)}); | |
| 2146 | 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 | 2904 | @"=", |
| 2906 | 2905 | }; |
| 2907 | 2906 | |
| 2908 | pub fn format(feature: Feature, bw: *Writer, comptime fmt: []const u8) Writer.Error!void { | |
| 2909 | _ = fmt; | |
| 2910 | try bw.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) }); | |
| 2907 | pub fn format(feature: Feature, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 2908 | try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) }); | |
| 2911 | 2909 | } |
| 2912 | 2910 | |
| 2913 | 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 | 3297 | .variable => |variable| .{ variable.init, variable.owner_nav }, |
| 3300 | 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 | 3301 | assert(!wasm.imports.contains(chased_nav_index)); |
| 3304 | 3302 | |
| 3305 | 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 | 534 | wasm.memories.limits.max = @intCast(max_memory / page_size); |
| 535 | 535 | wasm.memories.limits.flags.has_max = true; |
| 536 | 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 | 539 | f.memory_layout_finished = true; |
| 540 | 540 |
src/link/table_section.zig+3-4| ... | ... | @@ -39,11 +39,10 @@ pub fn TableSection(comptime Entry: type) type { |
| 39 | 39 | return self.entries.items.len; |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | pub fn format(self: Self, bw: *Writer, comptime unused_format_string: []const u8) Writer.Error!void { | |
| 43 | comptime assert(unused_format_string.len == 0); | |
| 44 | try bw.writeAll("TableSection:\n"); | |
| 42 | pub fn format(self: Self, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 43 | try writer.writeAll("TableSection:\n"); | |
| 45 | 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 | } |
| 49 | 48 |
src/link/tapi/parse.zig+10-43| ... | ... | @@ -57,14 +57,9 @@ pub const Node = struct { |
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | pub fn format( | |
| 61 | self: *const Node, | |
| 62 | comptime fmt: []const u8, | |
| 63 | options: std.fmt.FormatOptions, | |
| 64 | writer: anytype, | |
| 65 | ) !void { | |
| 60 | pub fn format(self: *const Node, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 66 | 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 | } |
| 70 | 65 | |
| ... | ... | @@ -86,24 +81,17 @@ pub const Node = struct { |
| 86 | 81 | } |
| 87 | 82 | } |
| 88 | 83 | |
| 89 | pub fn format( | |
| 90 | self: *const Doc, | |
| 91 | comptime fmt: []const u8, | |
| 92 | options: std.fmt.FormatOptions, | |
| 93 | writer: anytype, | |
| 94 | ) !void { | |
| 95 | _ = options; | |
| 96 | _ = fmt; | |
| 84 | pub fn format(self: *const Doc, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 97 | 85 | if (self.directive) |id| { |
| 98 | try std.fmt.format(writer, "{{ ", .{}); | |
| 86 | try writer.print("{{ ", .{}); | |
| 99 | 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 | 90 | if (self.value) |node| { |
| 103 | try std.fmt.format(writer, "{}", .{node}); | |
| 91 | try writer.print("{}", .{node}); | |
| 104 | 92 | } |
| 105 | 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 | 121 | self.values.deinit(allocator); |
| 134 | 122 | } |
| 135 | 123 | |
| 136 | pub fn format( | |
| 137 | self: *const Map, | |
| 138 | comptime fmt: []const u8, | |
| 139 | options: std.fmt.FormatOptions, | |
| 140 | writer: anytype, | |
| 141 | ) !void { | |
| 142 | _ = options; | |
| 143 | _ = fmt; | |
| 124 | pub fn format(self: *const Map, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 144 | 125 | try std.fmt.format(writer, "{{ ", .{}); |
| 145 | 126 | for (self.values.items) |entry| { |
| 146 | 127 | const key = self.base.tree.getRaw(entry.key, entry.key); |
| ... | ... | @@ -172,14 +153,7 @@ pub const Node = struct { |
| 172 | 153 | self.values.deinit(allocator); |
| 173 | 154 | } |
| 174 | 155 | |
| 175 | pub fn format( | |
| 176 | self: *const List, | |
| 177 | comptime fmt: []const u8, | |
| 178 | options: std.fmt.FormatOptions, | |
| 179 | writer: anytype, | |
| 180 | ) !void { | |
| 181 | _ = options; | |
| 182 | _ = fmt; | |
| 156 | pub fn format(self: *const List, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 183 | 157 | try std.fmt.format(writer, "[ ", .{}); |
| 184 | 158 | for (self.values.items) |node| { |
| 185 | 159 | try std.fmt.format(writer, "{}, ", .{node}); |
| ... | ... | @@ -203,14 +177,7 @@ pub const Node = struct { |
| 203 | 177 | self.string_value.deinit(allocator); |
| 204 | 178 | } |
| 205 | 179 | |
| 206 | pub fn format( | |
| 207 | self: *const Value, | |
| 208 | comptime fmt: []const u8, | |
| 209 | options: std.fmt.FormatOptions, | |
| 210 | writer: anytype, | |
| 211 | ) !void { | |
| 212 | _ = options; | |
| 213 | _ = fmt; | |
| 180 | pub fn format(self: *const Value, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 214 | 181 | const raw = self.base.tree.getRaw(self.base.start, self.base.end); |
| 215 | 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 | 309 | return jitCmd(gpa, arena, cmd_args, .{ |
| 310 | 310 | .cmd_name = "resinator", |
| 311 | 311 | .root_src_path = "resinator/main.zig", |
| 312 | .windows_libs = &.{"advapi32"}, | |
| 312 | 313 | .depend_on_aro = true, |
| 313 | 314 | .prepend_zig_lib_dir_path = true, |
| 314 | 315 | .server = use_server, |
| ... | ... | @@ -972,8 +973,6 @@ fn buildOutputType( |
| 972 | 973 | .windows_libs = .empty, |
| 973 | 974 | .link_inputs = .empty, |
| 974 | 975 | |
| 975 | .wasi_emulated_libs = .{}, | |
| 976 | ||
| 977 | 976 | .c_source_files = .{}, |
| 978 | 977 | .rc_source_files = .{}, |
| 979 | 978 | |
| ... | ... | @@ -1418,7 +1417,7 @@ fn buildOutputType( |
| 1418 | 1417 | } else if (mem.eql(u8, arg, "-funwind-tables")) { |
| 1419 | 1418 | mod_opts.unwind_tables = .sync; |
| 1420 | 1419 | } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) { |
| 1421 | mod_opts.unwind_tables = .@"async"; | |
| 1420 | mod_opts.unwind_tables = .async; | |
| 1422 | 1421 | } else if (mem.eql(u8, arg, "-fno-unwind-tables")) { |
| 1423 | 1422 | mod_opts.unwind_tables = .none; |
| 1424 | 1423 | } else if (mem.eql(u8, arg, "-fstack-check")) { |
| ... | ... | @@ -2039,15 +2038,15 @@ fn buildOutputType( |
| 2039 | 2038 | .none => { |
| 2040 | 2039 | mod_opts.unwind_tables = .sync; |
| 2041 | 2040 | }, |
| 2042 | .sync, .@"async" => {}, | |
| 2041 | .sync, .async => {}, | |
| 2043 | 2042 | } else { |
| 2044 | 2043 | mod_opts.unwind_tables = .sync; |
| 2045 | 2044 | }, |
| 2046 | 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 | 2047 | .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) { |
| 2049 | 2048 | .none, .sync => {}, |
| 2050 | .@"async" => { | |
| 2049 | .async => { | |
| 2051 | 2050 | mod_opts.unwind_tables = .sync; |
| 2052 | 2051 | }, |
| 2053 | 2052 | } else { |
| ... | ... | @@ -2955,7 +2954,7 @@ fn buildOutputType( |
| 2955 | 2954 | create_module.opts.any_fuzz = true; |
| 2956 | 2955 | if (mod_opts.unwind_tables) |uwt| switch (uwt) { |
| 2957 | 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 | 2959 | if (mod_opts.strip == false) |
| 2961 | 2960 | create_module.opts.any_non_stripped = true; |
| ... | ... | @@ -3331,16 +3330,16 @@ fn buildOutputType( |
| 3331 | 3330 | // We are providing our own cache key, because this file has nothing |
| 3332 | 3331 | // to do with the cache manifest. |
| 3333 | 3332 | var file_writer = f.writer(&.{}); |
| 3334 | var hasher_writer = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef")); | |
| 3335 | 3333 | var buffer: [1000]u8 = undefined; |
| 3336 | var bw = hasher_writer.writer(&buffer); | |
| 3337 | bw.writeFileAll(.stdin(), .{}) catch |err| switch (err) { | |
| 3338 | error.WriteFailed => fatal("failed to write {s}: {s}", .{ dump_path, file_writer.err.? }), | |
| 3339 | else => fatal("failed to pipe stdin to {s}: {s}", .{ dump_path, err }), | |
| 3334 | var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer); | |
| 3335 | var stdin_reader = fs.File.stdin().readerStreaming(&.{}); | |
| 3336 | _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) { | |
| 3337 | error.WriteFailed => fatal("failed to write {s}: {t}", .{ dump_path, file_writer.err.? }), | |
| 3338 | else => fatal("failed to pipe stdin to {s}: {t}", .{ dump_path, err }), | |
| 3340 | 3339 | }; |
| 3341 | try bw.flush(); | |
| 3340 | try hasher.writer.flush(); | |
| 3342 | 3341 | |
| 3343 | const bin_digest: Cache.BinDigest = hasher_writer.final(); | |
| 3342 | const bin_digest: Cache.BinDigest = hasher.hasher.finalResult(); | |
| 3344 | 3343 | |
| 3345 | 3344 | const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{ |
| 3346 | 3345 | &bin_digest, ext.canonicalName(target), |
| ... | ... | @@ -3412,7 +3411,6 @@ fn buildOutputType( |
| 3412 | 3411 | .framework_dirs = create_module.framework_dirs.items, |
| 3413 | 3412 | .frameworks = resolved_frameworks.items, |
| 3414 | 3413 | .windows_lib_names = create_module.windows_libs.keys(), |
| 3415 | .wasi_emulated_libs = create_module.wasi_emulated_libs.items, | |
| 3416 | 3414 | .want_compiler_rt = want_compiler_rt, |
| 3417 | 3415 | .want_ubsan_rt = want_ubsan_rt, |
| 3418 | 3416 | .hash_style = hash_style, |
| ... | ... | @@ -3562,8 +3560,8 @@ fn buildOutputType( |
| 3562 | 3560 | .stdio => { |
| 3563 | 3561 | try serve( |
| 3564 | 3562 | comp, |
| 3565 | fs.File.stdin(), | |
| 3566 | fs.File.stdout(), | |
| 3563 | .stdin(), | |
| 3564 | .stdout(), | |
| 3567 | 3565 | test_exec_args.items, |
| 3568 | 3566 | self_exe_path, |
| 3569 | 3567 | arg_mode, |
| ... | ... | @@ -3638,7 +3636,6 @@ fn buildOutputType( |
| 3638 | 3636 | } else if (target.os.tag == .windows) { |
| 3639 | 3637 | try test_exec_args.appendSlice(arena, &.{ |
| 3640 | 3638 | "--subsystem", "console", |
| 3641 | "-lkernel32", "-lntdll", | |
| 3642 | 3639 | }); |
| 3643 | 3640 | } |
| 3644 | 3641 | |
| ... | ... | @@ -3694,8 +3691,6 @@ const CreateModule = struct { |
| 3694 | 3691 | /// output. Allocated with gpa. |
| 3695 | 3692 | link_inputs: std.ArrayListUnmanaged(link.Input), |
| 3696 | 3693 | |
| 3697 | wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile), | |
| 3698 | ||
| 3699 | 3694 | c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile), |
| 3700 | 3695 | rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile), |
| 3701 | 3696 | |
| ... | ... | @@ -3826,14 +3821,6 @@ fn createModule( |
| 3826 | 3821 | .name_query => |nq| { |
| 3827 | 3822 | const lib_name = nq.name; |
| 3828 | 3823 | |
| 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 | 3824 | if (std.zig.target.isLibCLibName(target, lib_name)) { |
| 3838 | 3825 | create_module.opts.link_libc = true; |
| 3839 | 3826 | continue; |
| ... | ... | @@ -3852,7 +3839,8 @@ fn createModule( |
| 3852 | 3839 | .only_compiler_rt => continue, |
| 3853 | 3840 | } |
| 3854 | 3841 | |
| 3855 | if (target.isMinGW()) { | |
| 3842 | // We currently prefer import libraries provided by MinGW-w64 even for MSVC. | |
| 3843 | if (target.os.tag == .windows) { | |
| 3856 | 3844 | const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| { |
| 3857 | 3845 | fatal("failed to check zig installation for DLL import libs: {s}", .{ |
| 3858 | 3846 | @errorName(err), |
| ... | ... | @@ -5228,6 +5216,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5228 | 5216 | |
| 5229 | 5217 | try root_mod.deps.put(arena, "@build", build_mod); |
| 5230 | 5218 | |
| 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 | 5225 | const comp = Compilation.create(gpa, arena, .{ |
| 5232 | 5226 | .dirs = dirs, |
| 5233 | 5227 | .root_name = "build", |
| ... | ... | @@ -5249,6 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5249 | 5243 | .cache_mode = .whole, |
| 5250 | 5244 | .reference_trace = reference_trace, |
| 5251 | 5245 | .debug_compile_errors = debug_compile_errors, |
| 5246 | .windows_lib_names = windows_libs.keys(), | |
| 5252 | 5247 | }) catch |err| { |
| 5253 | 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 | 5294 | const s = fs.path.sep_str; |
| 5300 | 5295 | const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce; |
| 5301 | 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 | 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 | 5347 | const JitCmdOptions = struct { |
| 5353 | 5348 | cmd_name: []const u8, |
| 5354 | 5349 | root_src_path: []const u8, |
| 5350 | windows_libs: []const []const u8 = &.{}, | |
| 5355 | 5351 | prepend_zig_lib_dir_path: bool = false, |
| 5356 | 5352 | prepend_global_cache_path: bool = false, |
| 5357 | 5353 | prepend_zig_exe_path: bool = false, |
| ... | ... | @@ -5468,6 +5464,13 @@ fn jitCmd( |
| 5468 | 5464 | try root_mod.deps.put(arena, "aro", aro_mod); |
| 5469 | 5465 | } |
| 5470 | 5466 | |
| 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 | 5474 | const comp = Compilation.create(gpa, arena, .{ |
| 5472 | 5475 | .dirs = dirs, |
| 5473 | 5476 | .root_name = options.cmd_name, |
| ... | ... | @@ -5478,6 +5481,7 @@ fn jitCmd( |
| 5478 | 5481 | .self_exe_path = self_exe_path, |
| 5479 | 5482 | .thread_pool = &thread_pool, |
| 5480 | 5483 | .cache_mode = .whole, |
| 5484 | .windows_lib_names = windows_libs.keys(), | |
| 5481 | 5485 | }) catch |err| { |
| 5482 | 5486 | fatal("unable to create compilation: {s}", .{@errorName(err)}); |
| 5483 | 5487 | }; |
| ... | ... | @@ -6049,7 +6053,7 @@ fn cmdAstCheck( |
| 6049 | 6053 | break :file fs.cwd().openFile(p, .{}) catch |err| { |
| 6050 | 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 | 6057 | defer if (zig_source_path != null) f.close(); |
| 6054 | 6058 | break :s std.zig.readSourceFileToEndAlloc(arena, f, null) catch |err| { |
| 6055 | 6059 | fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) }); |
| ... | ... | @@ -6068,7 +6072,8 @@ fn cmdAstCheck( |
| 6068 | 6072 | |
| 6069 | 6073 | const tree = try Ast.parse(arena, source, mode); |
| 6070 | 6074 | |
| 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 | 6077 | switch (mode) { |
| 6073 | 6078 | .zig => { |
| 6074 | 6079 | const zir = try AstGen.generate(arena, tree); |
| ... | ... | @@ -6133,7 +6138,7 @@ fn cmdAstCheck( |
| 6133 | 6138 | // zig fmt: on |
| 6134 | 6139 | } |
| 6135 | 6140 | |
| 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 | 6142 | try stdout_bw.flush(); |
| 6138 | 6143 | |
| 6139 | 6144 | if (zir.hasCompileErrors()) { |
| ... | ... | @@ -6161,7 +6166,7 @@ fn cmdAstCheck( |
| 6161 | 6166 | fatal("-t option only available in builds of zig with debug extensions", .{}); |
| 6162 | 6167 | } |
| 6163 | 6168 | |
| 6164 | try @import("print_zoir.zig").renderToWriter(zoir, arena, &stdout_bw); | |
| 6169 | try @import("print_zoir.zig").renderToWriter(zoir, arena, stdout_bw); | |
| 6165 | 6170 | try stdout_bw.flush(); |
| 6166 | 6171 | return cleanExit(); |
| 6167 | 6172 | }, |
| ... | ... | @@ -6282,7 +6287,8 @@ fn detectNativeCpuWithLLVM( |
| 6282 | 6287 | } |
| 6283 | 6288 | |
| 6284 | 6289 | fn 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; | |
| 6286 | 6292 | |
| 6287 | 6293 | if (cpu.model.llvm_name) |llvm_name| { |
| 6288 | 6294 | try stdout_bw.print("{s}\n", .{llvm_name}); |
| ... | ... | @@ -6326,11 +6332,12 @@ fn cmdDumpLlvmInts( |
| 6326 | 6332 | if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad"); |
| 6327 | 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 | 6336 | const dl = tm.createTargetDataLayout(); |
| 6331 | 6337 | const context = llvm.Context.create(); |
| 6332 | 6338 | |
| 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 | 6341 | for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| { |
| 6335 | 6342 | const int_type = context.intType(bits); |
| 6336 | 6343 | const alignment = dl.abiAlignmentOfType(int_type); |
| ... | ... | @@ -6358,9 +6365,8 @@ fn cmdDumpZir( |
| 6358 | 6365 | defer f.close(); |
| 6359 | 6366 | |
| 6360 | 6367 | const zir = try Zcu.loadZirCache(arena, f); |
| 6361 | ||
| 6362 | var stdout_fw = fs.File.stdout().writer(); | |
| 6363 | var stdout_bw = stdout_fw.interface().buffered(&stdio_buffer); | |
| 6368 | var stdout_writer = fs.File.stdout().writerStreaming(&stdio_buffer); | |
| 6369 | const stdout_bw = &stdout_writer.interface; | |
| 6364 | 6370 | { |
| 6365 | 6371 | const instruction_bytes = zir.instructions.len * |
| 6366 | 6372 | // Here we don't use @sizeOf(Zir.Inst.Data) because it would include |
| ... | ... | @@ -6385,7 +6391,7 @@ fn cmdDumpZir( |
| 6385 | 6391 | // zig fmt: on |
| 6386 | 6392 | } |
| 6387 | 6393 | |
| 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 | 6395 | try stdout_bw.flush(); |
| 6390 | 6396 | } |
| 6391 | 6397 | |
| ... | ... | @@ -6444,7 +6450,8 @@ fn cmdChangelist( |
| 6444 | 6450 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty; |
| 6445 | 6451 | try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map); |
| 6446 | 6452 | |
| 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 | 6456 | try stdout_bw.print("Instruction mappings:\n", .{}); |
| 6450 | 6457 | var it = inst_map.iterator(); |
| ... | ... | @@ -6903,9 +6910,9 @@ fn cmdFetch( |
| 6903 | 6910 | |
| 6904 | 6911 | const name = switch (save) { |
| 6905 | 6912 | .no => { |
| 6906 | var stdout_bw = fs.File.stdout().writer().buffered(&stdio_buffer); | |
| 6907 | try stdout_bw.print("{s}\n", .{package_hash_slice}); | |
| 6908 | try stdout_bw.flush(); | |
| 6913 | var stdout = fs.File.stdout().writerStreaming(&stdio_buffer); | |
| 6914 | try stdout.interface.print("{s}\n", .{package_hash_slice}); | |
| 6915 | try stdout.interface.flush(); | |
| 6909 | 6916 | return cleanExit(); |
| 6910 | 6917 | }, |
| 6911 | 6918 | .yes, .exact => |name| name: { |
| ... | ... | @@ -6954,7 +6961,9 @@ fn cmdFetch( |
| 6954 | 6961 | std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex }); |
| 6955 | 6962 | |
| 6956 | 6963 | // 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 | 6967 | } else { |
| 6959 | 6968 | std.log.info("resolved to commit {s}", .{latest_commit_hex}); |
| 6960 | 6969 | } |
| ... | ... | @@ -6974,12 +6983,12 @@ fn cmdFetch( |
| 6974 | 6983 | \\ .hash = "{f}", |
| 6975 | 6984 | \\ }} |
| 6976 | 6985 | , .{ |
| 6977 | std.zig.fmtEscapes(saved_path_or_url), | |
| 6978 | std.zig.fmtEscapes(package_hash_slice), | |
| 6986 | std.zig.fmtString(saved_path_or_url), | |
| 6987 | std.zig.fmtString(package_hash_slice), | |
| 6979 | 6988 | }); |
| 6980 | 6989 | |
| 6981 | const new_node_text = try std.fmt.allocPrint(arena, ".{fp_} = {s},\n", .{ | |
| 6982 | std.zig.fmtId(name), new_node_init, | |
| 6990 | const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{ | |
| 6991 | std.zig.fmtIdPU(name), new_node_init, | |
| 6983 | 6992 | }); |
| 6984 | 6993 | |
| 6985 | 6994 | const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{ |
| ... | ... | @@ -7006,12 +7015,12 @@ fn cmdFetch( |
| 7006 | 7015 | const location_replace = try std.fmt.allocPrint( |
| 7007 | 7016 | arena, |
| 7008 | 7017 | "\"{f}\"", |
| 7009 | .{std.zig.fmtEscapes(saved_path_or_url)}, | |
| 7018 | .{std.zig.fmtString(saved_path_or_url)}, | |
| 7010 | 7019 | ); |
| 7011 | 7020 | const hash_replace = try std.fmt.allocPrint( |
| 7012 | 7021 | arena, |
| 7013 | 7022 | "\"{f}\"", |
| 7014 | .{std.zig.fmtEscapes(package_hash_slice)}, | |
| 7023 | .{std.zig.fmtString(package_hash_slice)}, | |
| 7015 | 7024 | ); |
| 7016 | 7025 | |
| 7017 | 7026 | warn("overwriting existing dependency named '{s}'", .{name}); |
| ... | ... | @@ -7411,7 +7420,7 @@ fn handleModArg( |
| 7411 | 7420 | create_module.opts.any_fuzz = true; |
| 7412 | 7421 | if (mod_opts.unwind_tables) |uwt| switch (uwt) { |
| 7413 | 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 | 7425 | if (mod_opts.strip == false) |
| 7417 | 7426 | create_module.opts.any_non_stripped = true; |
src/print_value.zig+114-117| ... | ... | @@ -9,7 +9,6 @@ const Sema = @import("Sema.zig"); |
| 9 | 9 | const InternPool = @import("InternPool.zig"); |
| 10 | 10 | const Allocator = std.mem.Allocator; |
| 11 | 11 | const Target = std.Target; |
| 12 | const Writer = std.io.Writer; | |
| 13 | 12 | |
| 14 | 13 | const max_aggregate_items = 100; |
| 15 | 14 | const max_string_len = 256; |
| ... | ... | @@ -21,10 +20,9 @@ pub const FormatContext = struct { |
| 21 | 20 | depth: u8, |
| 22 | 21 | }; |
| 23 | 22 | |
| 24 | pub fn formatSema(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 23 | pub fn formatSema(ctx: FormatContext, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 25 | 24 | const sema = ctx.opt_sema.?; |
| 26 | comptime std.debug.assert(fmt.len == 0); | |
| 27 | return print(ctx.val, bw, ctx.depth, ctx.pt, sema) catch |err| switch (err) { | |
| 25 | return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) { | |
| 28 | 26 | error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function |
| 29 | 27 | error.ComptimeBreak, error.ComptimeReturn => unreachable, |
| 30 | 28 | 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 | 30 | }; |
| 33 | 31 | } |
| 34 | 32 | |
| 35 | pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 33 | pub fn format(ctx: FormatContext, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 36 | 34 | std.debug.assert(ctx.opt_sema == null); |
| 37 | comptime std.debug.assert(fmt.len == 0); | |
| 38 | return print(ctx.val, bw, ctx.depth, ctx.pt, null) catch |err| switch (err) { | |
| 35 | return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) { | |
| 39 | 36 | error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function |
| 40 | 37 | error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable, |
| 41 | 38 | else => |e| return e, |
| ... | ... | @@ -44,7 +41,7 @@ pub fn format(ctx: FormatContext, bw: *Writer, comptime fmt: []const u8) std.io. |
| 44 | 41 | |
| 45 | 42 | pub fn print( |
| 46 | 43 | val: Value, |
| 47 | bw: *Writer, | |
| 44 | writer: *std.io.Writer, | |
| 48 | 45 | level: u8, |
| 49 | 46 | pt: Zcu.PerThread, |
| 50 | 47 | opt_sema: ?*Sema, |
| ... | ... | @@ -68,62 +65,62 @@ pub fn print( |
| 68 | 65 | .func_type, |
| 69 | 66 | .error_set_type, |
| 70 | 67 | .inferred_error_set_type, |
| 71 | => try Type.print(val.toType(), bw, pt), | |
| 72 | .undef => try bw.writeAll("undefined"), | |
| 68 | => try Type.print(val.toType(), writer, pt), | |
| 69 | .undef => try writer.writeAll("undefined"), | |
| 73 | 70 | .simple_value => |simple_value| switch (simple_value) { |
| 74 | .void => try bw.writeAll("{}"), | |
| 75 | .empty_tuple => try bw.writeAll(".{}"), | |
| 76 | else => try bw.writeAll(@tagName(simple_value)), | |
| 71 | .void => try writer.writeAll("{}"), | |
| 72 | .empty_tuple => try writer.writeAll(".{}"), | |
| 73 | else => try writer.writeAll(@tagName(simple_value)), | |
| 77 | 74 | }, |
| 78 | .variable => try bw.writeAll("(variable)"), | |
| 79 | .@"extern" => |e| try bw.print("(extern '{f}')", .{e.name.fmt(ip)}), | |
| 80 | .func => |func| try bw.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}), | |
| 75 | .variable => try writer.writeAll("(variable)"), | |
| 76 | .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}), | |
| 77 | .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}), | |
| 81 | 78 | .int => |int| switch (int.storage) { |
| 82 | inline .u64, .i64 => |x| try bw.print("{d}", .{x}), | |
| 83 | .big_int => |x| try bw.print("{f}", .{x}), | |
| 79 | inline .u64, .i64 => |x| try writer.print("{d}", .{x}), | |
| 80 | .big_int => |x| try writer.print("{d}", .{x}), | |
| 84 | 81 | .lazy_align => |ty| if (opt_sema != null) { |
| 85 | 82 | const a = try Type.fromInterned(ty).abiAlignmentSema(pt); |
| 86 | try bw.print("{}", .{a.toByteUnits() orelse 0}); | |
| 87 | } else try bw.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}), | |
| 83 | try writer.print("{d}", .{a.toByteUnits() orelse 0}); | |
| 84 | } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}), | |
| 88 | 85 | .lazy_size => |ty| if (opt_sema != null) { |
| 89 | 86 | const s = try Type.fromInterned(ty).abiSizeSema(pt); |
| 90 | try bw.print("{}", .{s}); | |
| 91 | } else try bw.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}), | |
| 87 | try writer.print("{d}", .{s}); | |
| 88 | } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}), | |
| 92 | 89 | }, |
| 93 | .err => |err| try bw.print("error.{f}", .{ | |
| 90 | .err => |err| try writer.print("error.{f}", .{ | |
| 94 | 91 | err.name.fmt(ip), |
| 95 | 92 | }), |
| 96 | 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 | 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 | 100 | enum_literal.fmt(ip), |
| 104 | 101 | }), |
| 105 | 102 | .enum_tag => |enum_tag| { |
| 106 | 103 | const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern()); |
| 107 | 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 | 107 | if (level == 0) { |
| 111 | return bw.writeAll("@enumFromInt(...)"); | |
| 108 | return writer.writeAll("@enumFromInt(...)"); | |
| 112 | 109 | } |
| 113 | try bw.writeAll("@enumFromInt("); | |
| 114 | try print(Value.fromInterned(enum_tag.int), bw, level - 1, pt, opt_sema); | |
| 115 | try bw.writeAll(")"); | |
| 110 | try writer.writeAll("@enumFromInt("); | |
| 111 | try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema); | |
| 112 | try writer.writeAll(")"); | |
| 116 | 113 | }, |
| 117 | .empty_enum_value => try bw.writeAll("(empty enum value)"), | |
| 114 | .empty_enum_value => try writer.writeAll("(empty enum value)"), | |
| 118 | 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 | 118 | .slice => |slice| { |
| 122 | 119 | if (ip.isUndef(slice.ptr)) { |
| 123 | 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 | 124 | } else { |
| 128 | 125 | const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) { |
| 129 | 126 | .field, .arr_elem, .eu_payload, .opt_payload => unreachable, |
| ... | ... | @@ -134,15 +131,15 @@ pub fn print( |
| 134 | 131 | // TODO: eventually we want to load the slice as an array with `sema`, but that's |
| 135 | 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 | 137 | if (level == 0) { |
| 141 | try bw.writeAll("(...)"); | |
| 138 | try writer.writeAll("(...)"); | |
| 142 | 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 | 144 | .ptr => { |
| 148 | 145 | const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) { |
| ... | ... | @@ -154,29 +151,29 @@ pub fn print( |
| 154 | 151 | // TODO: eventually we want to load the pointer with `sema`, but that's |
| 155 | 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 | 156 | .opt => |opt| switch (opt.val) { |
| 160 | .none => try bw.writeAll("null"), | |
| 161 | else => |payload| try print(Value.fromInterned(payload), bw, level, pt, opt_sema), | |
| 157 | .none => try writer.writeAll("null"), | |
| 158 | else => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema), | |
| 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 | 161 | .un => |un| { |
| 165 | 162 | if (level == 0) { |
| 166 | try bw.writeAll(".{ ... }"); | |
| 163 | try writer.writeAll(".{ ... }"); | |
| 167 | 164 | return; |
| 168 | 165 | } |
| 169 | 166 | if (un.tag == .none) { |
| 170 | 167 | const backing_ty = try val.typeOf(zcu).unionBackingType(pt); |
| 171 | try bw.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)}); | |
| 172 | try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema); | |
| 173 | try bw.writeAll("))"); | |
| 168 | try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)}); | |
| 169 | try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); | |
| 170 | try writer.writeAll("))"); | |
| 174 | 171 | } else { |
| 175 | try bw.writeAll(".{ "); | |
| 176 | try print(Value.fromInterned(un.tag), bw, level - 1, pt, opt_sema); | |
| 177 | try bw.writeAll(" = "); | |
| 178 | try print(Value.fromInterned(un.val), bw, level - 1, pt, opt_sema); | |
| 179 | try bw.writeAll(" }"); | |
| 172 | try writer.writeAll(".{ "); | |
| 173 | try print(Value.fromInterned(un.tag), writer, level - 1, pt, opt_sema); | |
| 174 | try writer.writeAll(" = "); | |
| 175 | try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); | |
| 176 | try writer.writeAll(" }"); | |
| 180 | 177 | } |
| 181 | 178 | }, |
| 182 | 179 | .memoized_call => unreachable, |
| ... | ... | @@ -187,33 +184,33 @@ fn printAggregate( |
| 187 | 184 | val: Value, |
| 188 | 185 | aggregate: InternPool.Key.Aggregate, |
| 189 | 186 | is_ref: bool, |
| 190 | bw: *Writer, | |
| 187 | writer: *std.io.Writer, | |
| 191 | 188 | level: u8, |
| 192 | 189 | pt: Zcu.PerThread, |
| 193 | 190 | opt_sema: ?*Sema, |
| 194 | 191 | ) (std.io.Writer.Error || Zcu.CompileError)!void { |
| 195 | 192 | if (level == 0) { |
| 196 | if (is_ref) try bw.writeByte('&'); | |
| 197 | return bw.writeAll(".{ ... }"); | |
| 193 | if (is_ref) try writer.writeByte('&'); | |
| 194 | return writer.writeAll(".{ ... }"); | |
| 198 | 195 | } |
| 199 | 196 | const zcu = pt.zcu; |
| 200 | 197 | const ip = &zcu.intern_pool; |
| 201 | 198 | const ty = Type.fromInterned(aggregate.ty); |
| 202 | 199 | switch (ty.zigTypeTag(zcu)) { |
| 203 | 200 | .@"struct" => if (!ty.isTuple(zcu)) { |
| 204 | if (is_ref) try bw.writeByte('&'); | |
| 201 | if (is_ref) try writer.writeByte('&'); | |
| 205 | 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 | 206 | const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items); |
| 210 | 207 | for (0..max_len) |i| { |
| 211 | if (i != 0) try bw.writeAll(", "); | |
| 208 | if (i != 0) try writer.writeAll(", "); | |
| 212 | 209 | const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?; |
| 213 | try bw.print(".{fi} = ", .{field_name.fmt(ip)}); | |
| 214 | try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema); | |
| 210 | try writer.print(".{f} = ", .{field_name.fmt(ip)}); | |
| 211 | try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); | |
| 215 | 212 | } |
| 216 | try bw.writeAll(" }"); | |
| 213 | try writer.writeAll(" }"); | |
| 217 | 214 | return; |
| 218 | 215 | }, |
| 219 | 216 | .array => { |
| ... | ... | @@ -222,16 +219,16 @@ fn printAggregate( |
| 222 | 219 | const len = ty.arrayLenIncludingSentinel(zcu); |
| 223 | 220 | if (len == 0) break :string; |
| 224 | 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)}); | |
| 226 | if (!is_ref) try bw.writeAll(".*"); | |
| 222 | try writer.print("\"{f}\"", .{std.zig.fmtString(slice)}); | |
| 223 | if (!is_ref) try writer.writeAll(".*"); | |
| 227 | 224 | return; |
| 228 | 225 | }, |
| 229 | 226 | .elems, .repeated_elem => {}, |
| 230 | 227 | } |
| 231 | 228 | switch (ty.arrayLen(zcu)) { |
| 232 | 229 | 0 => { |
| 233 | if (is_ref) try bw.writeByte('&'); | |
| 234 | return bw.writeAll(".{}"); | |
| 230 | if (is_ref) try writer.writeByte('&'); | |
| 231 | return writer.writeAll(".{}"); | |
| 235 | 232 | }, |
| 236 | 233 | 1 => one_byte_str: { |
| 237 | 234 | // The repr isn't `bytes`, but we might still be able to print this as a string |
| ... | ... | @@ -239,47 +236,47 @@ fn printAggregate( |
| 239 | 236 | const elem_val = Value.fromInterned(aggregate.storage.values()[0]); |
| 240 | 237 | if (elem_val.isUndef(zcu)) break :one_byte_str; |
| 241 | 238 | const byte = elem_val.toUnsignedInt(zcu); |
| 242 | try bw.print("\"{f}\"", .{std.zig.fmtEscapes(&.{@intCast(byte)})}); | |
| 243 | if (!is_ref) try bw.writeAll(".*"); | |
| 239 | try writer.print("\"{f}\"", .{std.zig.fmtString(&.{@intCast(byte)})}); | |
| 240 | if (!is_ref) try writer.writeAll(".*"); | |
| 244 | 241 | return; |
| 245 | 242 | }, |
| 246 | 243 | else => {}, |
| 247 | 244 | } |
| 248 | 245 | }, |
| 249 | 246 | .vector => if (ty.arrayLen(zcu) == 0) { |
| 250 | if (is_ref) try bw.writeByte('&'); | |
| 251 | return bw.writeAll(".{}"); | |
| 247 | if (is_ref) try writer.writeByte('&'); | |
| 248 | return writer.writeAll(".{}"); | |
| 252 | 249 | }, |
| 253 | 250 | else => unreachable, |
| 254 | 251 | } |
| 255 | 252 | |
| 256 | 253 | const len = ty.arrayLen(zcu); |
| 257 | 254 | |
| 258 | if (is_ref) try bw.writeByte('&'); | |
| 259 | try bw.writeAll(".{ "); | |
| 255 | if (is_ref) try writer.writeByte('&'); | |
| 256 | try writer.writeAll(".{ "); | |
| 260 | 257 | |
| 261 | 258 | const max_len = @min(len, max_aggregate_items); |
| 262 | 259 | for (0..max_len) |i| { |
| 263 | if (i != 0) try bw.writeAll(", "); | |
| 264 | try print(try val.fieldValue(pt, i), bw, level - 1, pt, opt_sema); | |
| 260 | if (i != 0) try writer.writeAll(", "); | |
| 261 | try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); | |
| 265 | 262 | } |
| 266 | 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 | } |
| 271 | 268 | |
| 272 | 269 | fn printPtr( |
| 273 | 270 | ptr_val: Value, |
| 274 | 271 | /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen. |
| 275 | 272 | want_kind: ?PrintPtrKind, |
| 276 | bw: *Writer, | |
| 273 | writer: *std.io.Writer, | |
| 277 | 274 | level: u8, |
| 278 | 275 | pt: Zcu.PerThread, |
| 279 | 276 | opt_sema: ?*Sema, |
| 280 | 277 | ) (std.io.Writer.Error || Zcu.CompileError)!void { |
| 281 | 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 | 280 | .ptr => |ptr| ptr, |
| 284 | 281 | else => unreachable, |
| 285 | 282 | }; |
| ... | ... | @@ -291,7 +288,7 @@ fn printPtr( |
| 291 | 288 | Value.fromInterned(ptr.base_addr.uav.val), |
| 292 | 289 | agg, |
| 293 | 290 | true, |
| 294 | bw, | |
| 291 | writer, | |
| 295 | 292 | level, |
| 296 | 293 | pt, |
| 297 | 294 | opt_sema, |
| ... | ... | @@ -307,7 +304,7 @@ fn printPtr( |
| 307 | 304 | else |
| 308 | 305 | try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null); |
| 309 | 306 | |
| 310 | _ = try printPtrDerivation(derivation, bw, pt, want_kind, .{ .print_val = .{ | |
| 307 | _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{ | |
| 311 | 308 | .level = level, |
| 312 | 309 | .opt_sema = opt_sema, |
| 313 | 310 | } }, 20); |
| ... | ... | @@ -319,7 +316,7 @@ const PrintPtrKind = enum { lvalue, rvalue }; |
| 319 | 316 | /// Returns the root derivation, which may be ignored. |
| 320 | 317 | pub fn printPtrDerivation( |
| 321 | 318 | derivation: Value.PointerDeriveStep, |
| 322 | bw: *Writer, | |
| 319 | writer: *std.io.Writer, | |
| 323 | 320 | pt: Zcu.PerThread, |
| 324 | 321 | /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen. |
| 325 | 322 | /// 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 | 334 | /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary, |
| 338 | 335 | /// so at this depth we just write "..." to prevent stack overflow. |
| 339 | 336 | ptr_depth: u8, |
| 340 | ) (std.io.Writer.Error || Zcu.CompileError)!Value.PointerDeriveStep { | |
| 337 | ) !Value.PointerDeriveStep { | |
| 341 | 338 | const zcu = pt.zcu; |
| 342 | 339 | const ip = &zcu.intern_pool; |
| 343 | 340 | |
| ... | ... | @@ -351,7 +348,7 @@ pub fn printPtrDerivation( |
| 351 | 348 | => |step| continue :root step.parent.*, |
| 352 | 349 | else => |step| break :root step, |
| 353 | 350 | }; |
| 354 | try bw.writeAll("..."); | |
| 351 | try writer.writeAll("..."); | |
| 355 | 352 | return root_step; |
| 356 | 353 | } |
| 357 | 354 | |
| ... | ... | @@ -374,39 +371,39 @@ pub fn printPtrDerivation( |
| 374 | 371 | const need_kind = want_kind orelse result_kind; |
| 375 | 372 | |
| 376 | 373 | if (need_kind == .rvalue and result_kind == .lvalue) { |
| 377 | try bw.writeByte('&'); | |
| 374 | try writer.writeByte('&'); | |
| 378 | 375 | } |
| 379 | 376 | |
| 380 | 377 | // null if `derivation` is the root. |
| 381 | 378 | const root_or_null: ?Value.PointerDeriveStep = switch (derivation) { |
| 382 | 379 | .eu_payload_ptr => |info| root: { |
| 383 | try bw.writeByte('('); | |
| 384 | const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1); | |
| 385 | try bw.writeAll(" catch unreachable)"); | |
| 380 | try writer.writeByte('('); | |
| 381 | const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1); | |
| 382 | try writer.writeAll(" catch unreachable)"); | |
| 386 | 383 | break :root root; |
| 387 | 384 | }, |
| 388 | 385 | .opt_payload_ptr => |info| root: { |
| 389 | const root = try printPtrDerivation(info.parent.*, bw, pt, .lvalue, root_strat, ptr_depth - 1); | |
| 390 | try bw.writeAll(".?"); | |
| 386 | const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1); | |
| 387 | try writer.writeAll(".?"); | |
| 391 | 388 | break :root root; |
| 392 | 389 | }, |
| 393 | 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 | 392 | const agg_ty = (try field.parent.ptrType(pt)).childType(zcu); |
| 396 | 393 | switch (agg_ty.zigTypeTag(zcu)) { |
| 397 | 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 | 396 | } else { |
| 400 | try bw.print("[{d}]", .{field.field_idx}); | |
| 397 | try writer.print("[{d}]", .{field.field_idx}); | |
| 401 | 398 | }, |
| 402 | 399 | .@"union" => { |
| 403 | 400 | const tag_ty = agg_ty.unionTagTypeHypothetical(zcu); |
| 404 | 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 | 404 | .pointer => switch (field.field_idx) { |
| 408 | Value.slice_ptr_index => try bw.writeAll(".ptr"), | |
| 409 | Value.slice_len_index => try bw.writeAll(".len"), | |
| 405 | Value.slice_ptr_index => try writer.writeAll(".ptr"), | |
| 406 | Value.slice_len_index => try writer.writeAll(".len"), | |
| 410 | 407 | else => unreachable, |
| 411 | 408 | }, |
| 412 | 409 | else => unreachable, |
| ... | ... | @@ -414,20 +411,20 @@ pub fn printPtrDerivation( |
| 414 | 411 | break :root root; |
| 415 | 412 | }, |
| 416 | 413 | .elem_ptr => |elem| root: { |
| 417 | const root = try printPtrDerivation(elem.parent.*, bw, pt, null, root_strat, ptr_depth - 1); | |
| 418 | try bw.print("[{d}]", .{elem.elem_idx}); | |
| 414 | const root = try printPtrDerivation(elem.parent.*, writer, pt, null, root_strat, ptr_depth - 1); | |
| 415 | try writer.print("[{d}]", .{elem.elem_idx}); | |
| 419 | 416 | break :root root; |
| 420 | 417 | }, |
| 421 | 418 | |
| 422 | 419 | .offset_and_cast => |oac| if (oac.byte_offset == 0) root: { |
| 423 | try bw.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)}); | |
| 424 | const root = try printPtrDerivation(oac.parent.*, bw, pt, .rvalue, root_strat, ptr_depth - 1); | |
| 425 | try bw.writeAll("))"); | |
| 420 | try writer.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)}); | |
| 421 | const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1); | |
| 422 | try writer.writeAll("))"); | |
| 426 | 423 | break :root root; |
| 427 | 424 | } else root: { |
| 428 | try bw.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)}); | |
| 429 | const root = try printPtrDerivation(oac.parent.*, bw, pt, .rvalue, root_strat, ptr_depth - 1); | |
| 430 | try bw.print(") + {d}))", .{oac.byte_offset}); | |
| 425 | try writer.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)}); | |
| 426 | const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1); | |
| 427 | try writer.print(") + {d}))", .{oac.byte_offset}); | |
| 431 | 428 | break :root root; |
| 432 | 429 | }, |
| 433 | 430 | |
| ... | ... | @@ -435,33 +432,33 @@ pub fn printPtrDerivation( |
| 435 | 432 | }; |
| 436 | 433 | |
| 437 | 434 | if (root_or_null == null) switch (root_strat) { |
| 438 | .str => |x| try bw.writeAll(x), | |
| 435 | .str => |x| try writer.writeAll(x), | |
| 439 | 436 | .print_val => |x| switch (derivation) { |
| 440 | .int => |int| try bw.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }), | |
| 441 | .nav_ptr => |nav| try bw.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}), | |
| 437 | .int => |int| try writer.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }), | |
| 438 | .nav_ptr => |nav| try writer.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}), | |
| 442 | 439 | .uav_ptr => |uav| { |
| 443 | 440 | const ty = Value.fromInterned(uav.val).typeOf(zcu); |
| 444 | try bw.print("@as({f}, ", .{ty.fmt(pt)}); | |
| 445 | try print(Value.fromInterned(uav.val), bw, x.level - 1, pt, x.opt_sema); | |
| 446 | try bw.writeByte(')'); | |
| 441 | try writer.print("@as({f}, ", .{ty.fmt(pt)}); | |
| 442 | try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema); | |
| 443 | try writer.writeByte(')'); | |
| 447 | 444 | }, |
| 448 | 445 | .comptime_alloc_ptr => |info| { |
| 449 | try bw.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)}); | |
| 450 | try print(info.val, bw, x.level - 1, pt, x.opt_sema); | |
| 451 | try bw.writeByte(')'); | |
| 446 | try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)}); | |
| 447 | try print(info.val, writer, x.level - 1, pt, x.opt_sema); | |
| 448 | try writer.writeByte(')'); | |
| 452 | 449 | }, |
| 453 | 450 | .comptime_field_ptr => |val| { |
| 454 | 451 | const ty = val.typeOf(zcu); |
| 455 | try bw.print("@as({f}, ", .{ty.fmt(pt)}); | |
| 456 | try print(val, bw, x.level - 1, pt, x.opt_sema); | |
| 457 | try bw.writeByte(')'); | |
| 452 | try writer.print("@as({f}, ", .{ty.fmt(pt)}); | |
| 453 | try print(val, writer, x.level - 1, pt, x.opt_sema); | |
| 454 | try writer.writeByte(')'); | |
| 458 | 455 | }, |
| 459 | 456 | else => unreachable, |
| 460 | 457 | }, |
| 461 | 458 | }; |
| 462 | 459 | |
| 463 | 460 | if (need_kind == .lvalue and result_kind == .rvalue) { |
| 464 | try bw.writeAll(".*"); | |
| 461 | try writer.writeAll(".*"); | |
| 465 | 462 | } |
| 466 | 463 | |
| 467 | 464 | return root_or_null orelse derivation; |
src/print_zir.zig+9-27| ... | ... | @@ -253,14 +253,12 @@ const Writer = struct { |
| 253 | 253 | .tag_name, |
| 254 | 254 | .type_name, |
| 255 | 255 | .frame_type, |
| 256 | .frame_size, | |
| 257 | 256 | .clz, |
| 258 | 257 | .ctz, |
| 259 | 258 | .pop_count, |
| 260 | 259 | .byte_swap, |
| 261 | 260 | .bit_reverse, |
| 262 | 261 | .@"resume", |
| 263 | .@"await", | |
| 264 | 262 | .make_ptr_const, |
| 265 | 263 | .validate_deref, |
| 266 | 264 | .validate_const, |
| ... | ... | @@ -557,7 +555,6 @@ const Writer = struct { |
| 557 | 555 | |
| 558 | 556 | .tuple_decl => try self.writeTupleDecl(stream, extended), |
| 559 | 557 | |
| 560 | .await_nosuspend, | |
| 561 | 558 | .c_undef, |
| 562 | 559 | .c_include, |
| 563 | 560 | .set_float_mode, |
| ... | ... | @@ -603,7 +600,6 @@ const Writer = struct { |
| 603 | 600 | try self.writeSrcNode(stream, inst_data.node); |
| 604 | 601 | }, |
| 605 | 602 | |
| 606 | .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended), | |
| 607 | 603 | .cmpxchg => try self.writeCmpxchg(stream, extended), |
| 608 | 604 | .ptr_cast_full => try self.writePtrCastFull(stream, extended), |
| 609 | 605 | .ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended), |
| ... | ... | @@ -924,19 +920,6 @@ const Writer = struct { |
| 924 | 920 | try self.writeSrcNode(stream, extra.src_node); |
| 925 | 921 | } |
| 926 | 922 | |
| 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 | 923 | fn writeParam(self: *Writer, stream: *std.io.Writer, inst: Zir.Inst.Index) !void { |
| 941 | 924 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; |
| 942 | 925 | const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index); |
| ... | ... | @@ -1229,8 +1212,8 @@ const Writer = struct { |
| 1229 | 1212 | |
| 1230 | 1213 | const name = self.code.nullTerminatedString(output.data.name); |
| 1231 | 1214 | const constraint = self.code.nullTerminatedString(output.data.constraint); |
| 1232 | try stream.print("output({fp}, \"{f}\", ", .{ | |
| 1233 | std.zig.fmtId(name), std.zig.fmtString(constraint), | |
| 1215 | try stream.print("output({f}, \"{f}\", ", .{ | |
| 1216 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), | |
| 1234 | 1217 | }); |
| 1235 | 1218 | try self.writeFlag(stream, "->", is_type); |
| 1236 | 1219 | try self.writeInstRef(stream, output.data.operand); |
| ... | ... | @@ -1248,8 +1231,8 @@ const Writer = struct { |
| 1248 | 1231 | |
| 1249 | 1232 | const name = self.code.nullTerminatedString(input.data.name); |
| 1250 | 1233 | const constraint = self.code.nullTerminatedString(input.data.constraint); |
| 1251 | try stream.print("input({fp}, \"{f}\", ", .{ | |
| 1252 | std.zig.fmtId(name), std.zig.fmtString(constraint), | |
| 1234 | try stream.print("input({f}, \"{f}\", ", .{ | |
| 1235 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), | |
| 1253 | 1236 | }); |
| 1254 | 1237 | try self.writeInstRef(stream, input.data.operand); |
| 1255 | 1238 | try stream.writeAll(")"); |
| ... | ... | @@ -1264,7 +1247,7 @@ const Writer = struct { |
| 1264 | 1247 | const str_index = self.code.extra[extra_i]; |
| 1265 | 1248 | extra_i += 1; |
| 1266 | 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 | 1251 | if (i + 1 < clobbers_len) { |
| 1269 | 1252 | try stream.writeAll(", "); |
| 1270 | 1253 | } |
| ... | ... | @@ -1528,7 +1511,7 @@ const Writer = struct { |
| 1528 | 1511 | try self.writeFlag(stream, "comptime ", field.is_comptime); |
| 1529 | 1512 | if (field.name != .empty) { |
| 1530 | 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 | 1515 | } else { |
| 1533 | 1516 | try stream.print("@\"{d}\": ", .{i}); |
| 1534 | 1517 | } |
| ... | ... | @@ -1691,7 +1674,7 @@ const Writer = struct { |
| 1691 | 1674 | extra_index += 1; |
| 1692 | 1675 | |
| 1693 | 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)}); | |
| 1695 | 1678 | |
| 1696 | 1679 | if (has_type) { |
| 1697 | 1680 | const field_type = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); |
| ... | ... | @@ -1825,7 +1808,7 @@ const Writer = struct { |
| 1825 | 1808 | extra_index += 1; |
| 1826 | 1809 | |
| 1827 | 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)}); | |
| 1829 | 1812 | |
| 1830 | 1813 | if (has_tag_value) { |
| 1831 | 1814 | const tag_value_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); |
| ... | ... | @@ -1930,7 +1913,7 @@ const Writer = struct { |
| 1930 | 1913 | const name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]); |
| 1931 | 1914 | const name = self.code.nullTerminatedString(name_index); |
| 1932 | 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 | } |
| 1935 | 1918 | |
| 1936 | 1919 | self.indent -= 2; |
| ... | ... | @@ -2597,7 +2580,6 @@ const Writer = struct { |
| 2597 | 2580 | } |
| 2598 | 2581 | switch (decl.kind) { |
| 2599 | 2582 | .@"comptime" => try stream.writeAll("comptime"), |
| 2600 | .@"usingnamespace" => try stream.writeAll("usingnamespace"), | |
| 2601 | 2583 | .unnamed_test => try stream.writeAll("test"), |
| 2602 | 2584 | .@"test", .decltest, .@"const", .@"var" => { |
| 2603 | 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 | pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: *Writer) error{ WriteFailed, OutOfMemory }!void { | |
| 1 | pub const Error = error{ WriteFailed, OutOfMemory }; | |
| 2 | ||
| 3 | pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: *Writer) Error!void { | |
| 2 | 4 | assert(!zoir.hasCompileErrors()); |
| 3 | 5 | |
| 4 | 6 | const bytes_per_node = comptime n: { |
| ... | ... | @@ -46,8 +48,6 @@ const PrintZon = struct { |
| 46 | 48 | zoir: Zoir, |
| 47 | 49 | indent: u32, |
| 48 | 50 | |
| 49 | const Error = Writer.Error; | |
| 50 | ||
| 51 | 51 | fn renderRoot(pz: *PrintZon) Error!void { |
| 52 | 52 | try pz.renderNode(.root); |
| 53 | 53 | try pz.w.writeByte('\n'); |
src/target.zig+29-10| ... | ... | @@ -85,6 +85,19 @@ pub fn defaultSingleThreaded(target: *const std.Target) bool { |
| 85 | 85 | return false; |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | pub 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 | ||
| 88 | 101 | pub fn hasValgrindSupport(target: *const std.Target, backend: std.builtin.CompilerBackend) bool { |
| 89 | 102 | // We can't currently output the necessary Valgrind client request assembly when using the C |
| 90 | 103 | // backend and compiling with an MSVC-like compiler. |
| ... | ... | @@ -222,10 +235,16 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { |
| 222 | 235 | /// than or equal to the number of behavior tests as the respective LLVM backend. |
| 223 | 236 | pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool { |
| 224 | 237 | if (target.cpu.arch.isSpirV()) return true; |
| 225 | if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) return switch (target.ofmt) { | |
| 226 | .elf, .macho => true, | |
| 227 | else => false, | |
| 228 | }; | |
| 238 | if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) { | |
| 239 | if (target.os.tag == .netbsd) { | |
| 240 | // Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341 | |
| 241 | return false; | |
| 242 | } | |
| 243 | return switch (target.ofmt) { | |
| 244 | .elf, .macho => true, | |
| 245 | else => false, | |
| 246 | }; | |
| 247 | } | |
| 229 | 248 | return false; |
| 230 | 249 | } |
| 231 | 250 | |
| ... | ... | @@ -464,12 +483,12 @@ pub fn clangSupportsNoImplicitFloatArg(target: *const std.Target) bool { |
| 464 | 483 | pub fn defaultUnwindTables(target: *const std.Target, libunwind: bool, libtsan: bool) std.builtin.UnwindTables { |
| 465 | 484 | if (target.os.tag == .windows) { |
| 466 | 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"; | |
| 470 | if (libunwind) return .@"async"; | |
| 471 | if (libtsan) return .@"async"; | |
| 472 | if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .@"async"; | |
| 488 | if (target.os.tag.isDarwin()) return .async; | |
| 489 | if (libunwind) return .async; | |
| 490 | if (libtsan) return .async; | |
| 491 | if (std.debug.Dwarf.abi.supportsUnwinding(target)) return .async; | |
| 473 | 492 | return .none; |
| 474 | 493 | } |
| 475 | 494 | |
| ... | ... | @@ -796,7 +815,7 @@ pub fn compilerRtIntAbbrev(bits: u16) []const u8 { |
| 796 | 815 | |
| 797 | 816 | pub fn fnCallConvAllowsZigTypes(cc: std.builtin.CallingConvention) bool { |
| 798 | 817 | return switch (cc) { |
| 799 | .auto, .@"async", .@"inline" => true, | |
| 818 | .auto, .async, .@"inline" => true, | |
| 800 | 819 | // For now we want to authorize PTX kernel to use zig objects, even if |
| 801 | 820 | // we end up exposing the ABI. The goal is to experiment with more |
| 802 | 821 | // integrated CPU/GPU code. |
src/tracy.zig+34-16| ... | ... | @@ -120,20 +120,21 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { |
| 120 | 120 | .vtable = &.{ |
| 121 | 121 | .alloc = allocFn, |
| 122 | 122 | .resize = resizeFn, |
| 123 | .remap = remapFn, | |
| 123 | 124 | .free = freeFn, |
| 124 | 125 | }, |
| 125 | 126 | }; |
| 126 | 127 | } |
| 127 | 128 | |
| 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 | 130 | const self: *Self = @ptrCast(@alignCast(ptr)); |
| 130 | const result = self.parent_allocator.rawAlloc(len, ptr_align, ret_addr); | |
| 131 | if (result) |data| { | |
| 131 | const result = self.parent_allocator.rawAlloc(len, alignment, ret_addr); | |
| 132 | if (result) |memory| { | |
| 132 | 133 | if (len != 0) { |
| 133 | 134 | if (name) |n| { |
| 134 | allocNamed(data, len, n); | |
| 135 | allocNamed(memory, len, n); | |
| 135 | 136 | } else { |
| 136 | alloc(data, len); | |
| 137 | alloc(memory, len); | |
| 137 | 138 | } |
| 138 | 139 | } |
| 139 | 140 | } else { |
| ... | ... | @@ -142,15 +143,15 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { |
| 142 | 143 | return result; |
| 143 | 144 | } |
| 144 | 145 | |
| 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 | 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 | 149 | if (name) |n| { |
| 149 | freeNamed(buf.ptr, n); | |
| 150 | allocNamed(buf.ptr, new_len, n); | |
| 150 | freeNamed(memory.ptr, n); | |
| 151 | allocNamed(memory.ptr, new_len, n); | |
| 151 | 152 | } else { |
| 152 | free(buf.ptr); | |
| 153 | alloc(buf.ptr, new_len); | |
| 153 | free(memory.ptr); | |
| 154 | alloc(memory.ptr, new_len); | |
| 154 | 155 | } |
| 155 | 156 | |
| 156 | 157 | return true; |
| ... | ... | @@ -161,16 +162,33 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { |
| 161 | 162 | return false; |
| 162 | 163 | } |
| 163 | 164 | |
| 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 | 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 | 185 | // this condition is to handle free being called on an empty slice that was never even allocated |
| 168 | 186 | // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}` |
| 169 | if (buf.len != 0) { | |
| 187 | if (memory.len != 0) { | |
| 170 | 188 | if (name) |n| { |
| 171 | freeNamed(buf.ptr, n); | |
| 189 | freeNamed(memory.ptr, n); | |
| 172 | 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 | 357 | var len: usize = undefined; |
| 358 | 358 | const bytes_ptr = asm_string.getString_bytes_begin_size(&len); |
| 359 | 359 | |
| 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 | 361 | const str_node = try Tag.string_literal.create(c.arena, str); |
| 362 | 362 | |
| 363 | 363 | const asm_node = try Tag.asm_simple.create(c.arena, str_node); |
| ... | ... | @@ -2276,7 +2276,7 @@ fn transNarrowStringLiteral( |
| 2276 | 2276 | var len: usize = undefined; |
| 2277 | 2277 | const bytes_ptr = stmt.getString_bytes_begin_size(&len); |
| 2278 | 2278 | |
| 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 | 2280 | const node = try Tag.string_literal.create(c.arena, str); |
| 2281 | 2281 | return maybeSuppressResult(c, result_used, node); |
| 2282 | 2282 | } |
| ... | ... | @@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined |
| 3338 | 3338 | |
| 3339 | 3339 | fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { |
| 3340 | 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 | 3342 | else |
| 3343 | 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 | 5832 | num += c - 'A' + 10; |
| 5833 | 5833 | }, |
| 5834 | 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 | 5836 | num = 0; |
| 5837 | 5837 | if (c == '\\') |
| 5838 | 5838 | state = .escape |
| ... | ... | @@ -5858,7 +5858,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5858 | 5858 | }; |
| 5859 | 5859 | num += c - '0'; |
| 5860 | 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 | 5862 | num = 0; |
| 5863 | 5863 | count = 0; |
| 5864 | 5864 | if (c == '\\') |
| ... | ... | @@ -5872,19 +5872,19 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5872 | 5872 | } |
| 5873 | 5873 | } |
| 5874 | 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 | 5876 | return bytes[0..i]; |
| 5877 | 5877 | } |
| 5878 | 5878 | |
| 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 | 5880 | /// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape |
| 5881 | 5881 | /// non-ASCII characters so that the Zig source we output will itself be UTF-8. |
| 5882 | 5882 | fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5883 | 5883 | const zigified = try zigifyEscapeSequences(ctx, m); |
| 5884 | 5884 | if (std.unicode.utf8ValidateSlice(zigified)) return zigified; |
| 5885 | 5885 | |
| 5886 | const formatter = std.fmt.fmtSliceEscapeLower(zigified); | |
| 5887 | const encoded_size = std.fmt.count("{f}", .{formatter}); | |
| 5886 | const formatter = std.ascii.hexEscape(zigified, .lower); | |
| 5887 | const encoded_size: usize = @intCast(std.fmt.count("{f}", .{formatter})); | |
| 5888 | 5888 | const output = try ctx.arena.alloc(u8, encoded_size); |
| 5889 | 5889 | return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) { |
| 5890 | 5890 | error.NoSpaceLeft => unreachable, |
src/zig_llvm.cpp+5-1| ... | ... | @@ -83,7 +83,7 @@ static const bool assertions_on = false; |
| 83 | 83 | LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, |
| 84 | 84 | const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, |
| 85 | 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 | 88 | std::optional<Reloc::Model> RM; |
| 89 | 89 | switch (Reloc){ |
| ... | ... | @@ -149,6 +149,10 @@ LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Tri |
| 149 | 149 | opt.MCOptions.ABIName = abi_name; |
| 150 | 150 | } |
| 151 | 151 | |
| 152 | if (emulated_tls) { | |
| 153 | opt.EmulatedTLS = true; | |
| 154 | } | |
| 155 | ||
| 152 | 156 | TargetMachine *TM = reinterpret_cast<Target*>(T)->createTargetMachine(Triple, CPU, Features, opt, RM, CM, |
| 153 | 157 | OL, JIT); |
| 154 | 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 | 105 | ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, |
| 106 | 106 | const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, |
| 107 | 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); | |
| 109 | 109 | |
| 110 | 110 | ZIG_EXTERN_C void ZigLLVMSetOptBisectLimit(LLVMContextRef context_ref, int limit); |
| 111 | 111 |
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 | 520 | default: panic("unimplemented: fd_read special file"); |
| 521 | 521 | } |
| 522 | 522 | |
| 523 | if (fds[fd].stream == NULL) { | |
| 524 | store32_align2(res_size_ptr, 0); | |
| 525 | return wasi_errno_success; | |
| 526 | } | |
| 527 | ||
| 523 | 528 | size_t size = 0; |
| 524 | 529 | for (uint32_t i = 0; i < iovs_len; i += 1) { |
| 525 | 530 | uint32_t len = load32_align2(&iovs_ptr[i].len); |
| 526 | size_t read_size = 0; | |
| 527 | if (fds[fd].stream != NULL) | |
| 528 | read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream); | |
| 531 | size_t read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream); | |
| 529 | 532 | size += read_size; |
| 530 | 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 | 636 | } |
| 634 | 637 | |
| 635 | 638 | fpos_t pos; |
| 636 | if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; | |
| 637 | if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; | |
| 639 | if (fds[fd].stream != NULL) { | |
| 640 | if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; | |
| 641 | if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; | |
| 642 | } | |
| 638 | 643 | |
| 639 | 644 | size_t size = 0; |
| 640 | 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 | 653 | if (written_size < len) break; |
| 649 | 654 | } |
| 650 | 655 | |
| 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 | } | |
| 652 | 659 | |
| 653 | 660 | if (size > 0) { |
| 654 | 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 | 971 | default: panic("unimplemented: fd_pread special file"); |
| 965 | 972 | } |
| 966 | 973 | |
| 974 | if (fds[fd].stream == NULL) { | |
| 975 | store32_align2(res_size_ptr, 0); | |
| 976 | return wasi_errno_success; | |
| 977 | } | |
| 978 | ||
| 967 | 979 | fpos_t pos; |
| 968 | 980 | if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; |
| 969 | 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 | 5 | _ = @import("behavior/align.zig"); |
| 6 | 6 | _ = @import("behavior/alignof.zig"); |
| 7 | 7 | _ = @import("behavior/array.zig"); |
| 8 | _ = @import("behavior/async_fn.zig"); | |
| 9 | 8 | _ = @import("behavior/atomics.zig"); |
| 10 | _ = @import("behavior/await_struct.zig"); | |
| 11 | 9 | _ = @import("behavior/basic.zig"); |
| 12 | 10 | _ = @import("behavior/bit_shifting.zig"); |
| 13 | 11 | _ = @import("behavior/bitcast.zig"); |
| ... | ... | @@ -103,7 +101,6 @@ test { |
| 103 | 101 | _ = @import("behavior/underscore.zig"); |
| 104 | 102 | _ = @import("behavior/union.zig"); |
| 105 | 103 | _ = @import("behavior/union_with_members.zig"); |
| 106 | _ = @import("behavior/usingnamespace.zig"); | |
| 107 | 104 | _ = @import("behavior/var_args.zig"); |
| 108 | 105 | // https://github.com/llvm/llvm-project/issues/118879 |
| 109 | 106 | // https://github.com/llvm/llvm-project/issues/134659 |
test/behavior/align.zig-24| ... | ... | @@ -425,30 +425,6 @@ test "struct field explicit alignment" { |
| 425 | 425 | try expect(@intFromPtr(&node.massive_byte) % 64 == 0); |
| 426 | 426 | } |
| 427 | 427 | |
| 428 | test "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 | ||
| 452 | 428 | test "align(N) on functions" { |
| 453 | 429 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 454 | 430 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/async_fn.zig deleted-1911| ... | ... | @@ -1,1911 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const expect = std.testing.expect; | |
| 5 | const expectEqual = std.testing.expectEqual; | |
| 6 | const expectEqualStrings = std.testing.expectEqualStrings; | |
| 7 | const expectError = std.testing.expectError; | |
| 8 | ||
| 9 | var global_x: i32 = 1; | |
| 10 | ||
| 11 | test "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 | } | |
| 24 | fn simpleAsyncFn() void { | |
| 25 | global_x += 1; | |
| 26 | suspend {} | |
| 27 | global_x += 1; | |
| 28 | suspend {} | |
| 29 | global_x += 1; | |
| 30 | } | |
| 31 | ||
| 32 | var global_y: i32 = 1; | |
| 33 | ||
| 34 | test "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 | } | |
| 43 | fn simpleAsyncFnWithArg(delta: i32) void { | |
| 44 | global_y += delta; | |
| 45 | suspend {} | |
| 46 | global_y += delta; | |
| 47 | } | |
| 48 | ||
| 49 | test "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 | ||
| 71 | test "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 | ||
| 103 | test "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 | ||
| 130 | test "@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 | ||
| 166 | test "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 | ||
| 210 | test "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 | ||
| 221 | var a_promise: anyframe = undefined; | |
| 222 | var global_result = false; | |
| 223 | fn 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 | ||
| 236 | var await_a_promise: anyframe = undefined; | |
| 237 | var await_final_result: i32 = 0; | |
| 238 | ||
| 239 | test "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 | } | |
| 252 | fn 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 | } | |
| 259 | fn 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 | ||
| 269 | var await_points = [_]u8{0} ** "abcdefghi".len; | |
| 270 | var await_seq_index: usize = 0; | |
| 271 | ||
| 272 | fn await_seq(c: u8) void { | |
| 273 | await_points[await_seq_index] = c; | |
| 274 | await_seq_index += 1; | |
| 275 | } | |
| 276 | ||
| 277 | var early_final_result: i32 = 0; | |
| 278 | ||
| 279 | test "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 | } | |
| 290 | fn 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 | } | |
| 297 | fn early_another() callconv(.@"async") i32 { | |
| 298 | early_seq('c'); | |
| 299 | return 1234; | |
| 300 | } | |
| 301 | ||
| 302 | var early_points = [_]u8{0} ** "abcdef".len; | |
| 303 | var early_seq_index: usize = 0; | |
| 304 | ||
| 305 | fn early_seq(c: u8) void { | |
| 306 | early_points[early_seq_index] = c; | |
| 307 | early_seq_index += 1; | |
| 308 | } | |
| 309 | ||
| 310 | test "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 | ||
| 326 | test "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 | ||
| 346 | fn doTheAwait(f: anyframe->void) void { | |
| 347 | await f; | |
| 348 | } | |
| 349 | fn simpleAsyncFn2(y: *i32) callconv(.@"async") void { | |
| 350 | defer y.* += 2; | |
| 351 | y.* += 1; | |
| 352 | suspend {} | |
| 353 | } | |
| 354 | ||
| 355 | test "@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 | ||
| 383 | test "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 | ||
| 417 | test "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 | ||
| 427 | test "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 | ||
| 437 | fn 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 | } | |
| 444 | fn suspendThenFail() callconv(.@"async") anyerror!void { | |
| 445 | suspend {} | |
| 446 | return error.Fail; | |
| 447 | } | |
| 448 | fn 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 | ||
| 460 | test "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 | } | |
| 469 | fn 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 | ||
| 478 | test "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 | ||
| 505 | test "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 | ||
| 549 | test "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 | ||
| 578 | test "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 | ||
| 614 | test "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 | ||
| 638 | test "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 | ||
| 664 | test "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 | ||
| 697 | test "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 | ||
| 731 | test "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 | ||
| 746 | fn 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 | ||
| 829 | test "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 | ||
| 844 | test "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 | } | |
| 850 | fn simpleNothing() void { | |
| 851 | var x: i32 = 1234; | |
| 852 | _ = &x; | |
| 853 | } | |
| 854 | ||
| 855 | test "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 | ||
| 878 | test "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 | ||
| 895 | test "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 | ||
| 943 | test "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 | ||
| 974 | test "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 | ||
| 1004 | test "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 | ||
| 1017 | test "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 | ||
| 1025 | fn 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 | ||
| 1082 | test "@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 | ||
| 1114 | test "@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 | ||
| 1131 | test "@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 | ||
| 1162 | test "@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 | ||
| 1176 | test "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 | ||
| 1204 | test "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 | ||
| 1243 | test "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 | ||
| 1262 | test "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 | ||
| 1281 | test "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 | ||
| 1302 | test "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 | ||
| 1322 | test "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 | ||
| 1351 | test "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 | ||
| 1391 | test "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 | ||
| 1417 | test "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 | ||
| 1451 | test "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 | ||
| 1490 | test "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 | ||
| 1515 | test "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 | ||
| 1539 | test "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 | ||
| 1565 | test "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 | ||
| 1590 | test "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 | ||
| 1617 | test "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 | ||
| 1645 | test "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 | ||
| 1688 | test "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 | ||
| 1720 | test "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 | ||
| 1744 | test "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 | ||
| 1763 | test "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 | ||
| 1807 | test "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 | ||
| 1832 | test "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 | ||
| 1851 | test "@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 | ||
| 1888 | test "@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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | const Foo = struct { | |
| 6 | x: i32, | |
| 7 | }; | |
| 8 | ||
| 9 | var await_a_promise: anyframe = undefined; | |
| 10 | var await_final_result = Foo{ .x = 0 }; | |
| 11 | ||
| 12 | test "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 | } | |
| 24 | fn 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 | } | |
| 31 | fn 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 | ||
| 41 | var await_points = [_]u8{0} ** "abcdefghi".len; | |
| 42 | var await_seq_index: usize = 0; | |
| 43 | ||
| 44 | fn 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 | 1107 | try expect(S.foo(1) == 1); |
| 1108 | 1108 | } |
| 1109 | 1109 | |
| 1110 | test "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 | ||
| 1131 | 1110 | test "pointer to zero sized global is mutable" { |
| 1132 | 1111 | const S = struct { |
| 1133 | 1112 | const Thing = struct {}; |
test/behavior/call.zig+3-26| ... | ... | @@ -37,7 +37,7 @@ test "basic invocations" { |
| 37 | 37 | comptime { |
| 38 | 38 | // comptime calls with supported modifiers |
| 39 | 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 | 41 | try expect(@call(.always_tail, foo, .{4}) == 1234); |
| 42 | 42 | try expect(@call(.always_inline, foo, .{5}) == 1234); |
| 43 | 43 | } |
| ... | ... | @@ -45,7 +45,7 @@ test "basic invocations" { |
| 45 | 45 | const result = @call(.compile_time, foo, .{6}) == 1234; |
| 46 | 46 | comptime assert(result); |
| 47 | 47 | // 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 | 49 | try expect(@call(.never_tail, foo, .{8}) == 1234); |
| 50 | 50 | try expect(@call(.never_inline, foo, .{9}) == 1234); |
| 51 | 51 | // CBE does not support attributes on runtime functions |
| ... | ... | @@ -53,7 +53,7 @@ test "basic invocations" { |
| 53 | 53 | // runtime calls of non comptime-known function |
| 54 | 54 | var alias_foo = &foo; |
| 55 | 55 | _ = &alias_foo; |
| 56 | try expect(@call(.no_async, alias_foo, .{10}) == 1234); | |
| 56 | try expect(@call(.no_suspend, alias_foo, .{10}) == 1234); | |
| 57 | 57 | try expect(@call(.never_tail, alias_foo, .{11}) == 1234); |
| 58 | 58 | try expect(@call(.never_inline, alias_foo, .{12}) == 1234); |
| 59 | 59 | } |
| ... | ... | @@ -507,29 +507,6 @@ test "call inline fn through pointer" { |
| 507 | 507 | try f(123); |
| 508 | 508 | } |
| 509 | 509 | |
| 510 | test "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 | ||
| 533 | 510 | test "call function in comptime field" { |
| 534 | 511 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 535 | 512 |
test/behavior/error.zig-38| ... | ... | @@ -1032,44 +1032,6 @@ test "function called at runtime is properly analyzed for inferred error set" { |
| 1032 | 1032 | }; |
| 1033 | 1033 | } |
| 1034 | 1034 | |
| 1035 | test "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 | ||
| 1073 | 1035 | test "errorCast to adhoc inferred error set" { |
| 1074 | 1036 | const S = struct { |
| 1075 | 1037 | inline fn baz() !i32 { |
test/behavior/import.zig-10| ... | ... | @@ -18,16 +18,6 @@ test "importing the same thing gives the same import" { |
| 18 | 18 | try expect(@import("std") == @import("std")); |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | test "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 | ||
| 31 | 21 | test "import empty file" { |
| 32 | 22 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 33 | 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 | 236 | try expect(s.do()); |
| 237 | 237 | } |
| 238 | 238 | |
| 239 | test "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 | ||
| 250 | 239 | test "struct field init with catch" { |
| 251 | 240 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 252 | 241 | 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 | 592 | try expect(info.fields[1].is_comptime); |
| 593 | 593 | } |
| 594 | 594 | |
| 595 | test "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 | ||
| 613 | 595 | test "value from struct @typeInfo default_value_ptr can be loaded at comptime" { |
| 614 | 596 | comptime { |
| 615 | 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 | 599 | } |
| 618 | 600 | } |
| 619 | 601 | |
| 620 | test "@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 | ||
| 644 | test "@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 | ||
| 657 | 602 | test "type info of tuple of string literal default value" { |
| 658 | 603 | const struct_field = @typeInfo(@TypeOf(.{"hi"})).@"struct".fields[0]; |
| 659 | 604 | const value = struct_field.defaultValue().?; |
| 660 | 605 | comptime std.debug.assert(value[0] == 'h'); |
| 661 | 606 | } |
| 662 | 607 | |
| 663 | test "@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 | ||
| 691 | 608 | test "@typeInfo function with generic return type and inferred error set" { |
| 692 | 609 | const S = struct { |
| 693 | 610 | fn testFn(comptime T: type) !T {} |
test/behavior/usingnamespace.zig deleted-125| ... | ... | @@ -1,125 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | const A = struct { | |
| 6 | pub const B = bool; | |
| 7 | }; | |
| 8 | ||
| 9 | const C = struct { | |
| 10 | usingnamespace A; | |
| 11 | }; | |
| 12 | ||
| 13 | test "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 | ||
| 20 | fn Foo(comptime T: type) type { | |
| 21 | return struct { | |
| 22 | usingnamespace T; | |
| 23 | }; | |
| 24 | } | |
| 25 | ||
| 26 | test "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 | ||
| 36 | usingnamespace struct { | |
| 37 | pub const foo = 42; | |
| 38 | }; | |
| 39 | ||
| 40 | test "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 | ||
| 47 | usingnamespace @import("usingnamespace/foo.zig"); | |
| 48 | test "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 | } | |
| 57 | fn privateFunction() bool { | |
| 58 | return true; | |
| 59 | } | |
| 60 | ||
| 61 | test { | |
| 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 | ||
| 68 | usingnamespace @import("usingnamespace/a.zig"); | |
| 69 | test "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 | ||
| 78 | test { | |
| 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 | ||
| 100 | const Bar = struct { | |
| 101 | usingnamespace Mixin; | |
| 102 | }; | |
| 103 | ||
| 104 | const Mixin = struct { | |
| 105 | pub fn two(self: Bar) void { | |
| 106 | _ = self; | |
| 107 | } | |
| 108 | }; | |
| 109 | ||
| 110 | test "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 | ||
| 118 | usingnamespace opaque {}; | |
| 119 | ||
| 120 | usingnamespace @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 @@ |
| 1 | usingnamespace @import("b.zig"); | |
| 2 | ||
| 3 | pub const a_text = "OK\n"; | |
| 4 | ||
| 5 | pub 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 @@ |
| 1 | usingnamespace @import("a.zig"); | |
| 2 | ||
| 3 | pub const b_text = @This().a_text; |
test/behavior/usingnamespace/bar.zig deleted-8| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | usingnamespace @import("other.zig"); | |
| 2 | ||
| 3 | pub var saw_bar_function = false; | |
| 4 | pub 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 | |
| 3 | fn privateFunction() bool { | |
| 4 | return false; | |
| 5 | } | |
| 6 | ||
| 7 | pub fn printText() bool { | |
| 8 | return privateFunction(); | |
| 9 | } | |
| 10 | ||
| 11 | pub var saw_foo_function = false; | |
| 12 | pub fn foo_function() void { | |
| 13 | saw_foo_function = true; | |
| 14 | } |
test/behavior/usingnamespace/import_segregation.zig deleted-20| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | usingnamespace @import("foo.zig"); | |
| 5 | usingnamespace @import("bar.zig"); | |
| 6 | ||
| 7 | test "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 @@ |
| 1 | pub 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 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = async amain(); | |
| 3 | } | |
| 4 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = async amain(); | |
| 3 | } | |
| 4 | fn amain() callconv(.@"async") void { | |
| 5 | other(); | |
| 6 | } | |
| 7 | fn 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 @@ |
| 1 | export fn a() void { | |
| 2 | const f = async func(); | |
| 3 | resume f; | |
| 4 | } | |
| 5 | export fn b() void { | |
| 6 | const f = async func(); | |
| 7 | var x: anyframe = &f; | |
| 8 | _ = &x; | |
| 9 | } | |
| 10 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | foo(); | |
| 3 | } | |
| 4 | fn foo() void { | |
| 5 | bar(); | |
| 6 | } | |
| 7 | fn 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 @@ |
| 1 | var frame: ?anyframe = null; | |
| 2 | ||
| 3 | export fn a() void { | |
| 4 | _ = async rangeSum(10); | |
| 5 | while (frame) |f| resume f; | |
| 6 | } | |
| 7 | ||
| 8 | fn 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 | ||
| 19 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | var frame = async func(); | |
| 3 | var result = await frame; | |
| 4 | _ = &result; | |
| 5 | } | |
| 6 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = async amain(); | |
| 3 | } | |
| 4 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = async amain(); | |
| 3 | } | |
| 4 | fn amain() void { | |
| 5 | var ptr = afunc; | |
| 6 | _ = ptr(); | |
| 7 | _ = &ptr; | |
| 8 | } | |
| 9 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | var ptr = afunc; | |
| 3 | _ = async ptr(); | |
| 4 | _ = &ptr; | |
| 5 | } | |
| 6 | ||
| 7 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | var frame: @Frame(foo) = undefined; | |
| 3 | frame = async bar(); | |
| 4 | } | |
| 5 | fn foo() void { | |
| 6 | suspend {} | |
| 7 | } | |
| 8 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = async amain(); | |
| 3 | } | |
| 4 | fn amain() i32 { | |
| 5 | var frame: @Frame(foo) = undefined; | |
| 6 | return await @asyncCall(&frame, false, foo, .{}); | |
| 7 | } | |
| 8 | fn 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 | ||
| 8 | const S = struct { | |
| 9 | ok: u32, | |
| 10 | bad: @typeInfo(T), | |
| 11 | }; | |
| 12 | ||
| 13 | const T = struct { | |
| 14 | pub usingnamespace @compileError("usingnamespace analyzed"); | |
| 15 | }; | |
| 16 | ||
| 17 | comptime { | |
| 18 | const a: S = .{ .ok = 123, .bad = undefined }; | |
| 19 | _ = a; | |
| 20 | @compileError("should not be reached"); | |
| 21 | } | |
| 22 | ||
| 23 | comptime { | |
| 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 @@ |
| 1 | export fn entry() void { | |
| 2 | nosuspend { | |
| 3 | const bar = async foo(); | |
| 4 | suspend {} | |
| 5 | resume bar; | |
| 6 | } | |
| 7 | } | |
| 8 | fn 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 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = async foo(); | |
| 3 | } | |
| 4 | fn 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 @@ |
| 1 | usingnamespace 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 5 | _ = message; | |
| 6 | _ = stack_trace; | |
| 7 | std.process.exit(0); | |
| 8 | } | |
| 9 | pub 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 | } | |
| 21 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | var frame: anyframe = undefined; | |
| 9 | ||
| 10 | pub fn main() !void { | |
| 11 | _ = async amain(); | |
| 12 | resume frame; | |
| 13 | return error.TestFailed; | |
| 14 | } | |
| 15 | ||
| 16 | fn amain() void { | |
| 17 | var f = async func(); | |
| 18 | await f; | |
| 19 | await f; | |
| 20 | } | |
| 21 | ||
| 22 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | ||
| 9 | var failing_frame: @Frame(failing) = undefined; | |
| 10 | ||
| 11 | pub fn main() !void { | |
| 12 | const p = nonFailing(); | |
| 13 | resume p; | |
| 14 | const p2 = async printTrace(p); | |
| 15 | _ = p2; | |
| 16 | return error.TestFailed; | |
| 17 | } | |
| 18 | ||
| 19 | fn nonFailing() anyframe->anyerror!void { | |
| 20 | failing_frame = async failing(); | |
| 21 | return &failing_frame; | |
| 22 | } | |
| 23 | ||
| 24 | fn failing() anyerror!void { | |
| 25 | suspend {} | |
| 26 | return second(); | |
| 27 | } | |
| 28 | ||
| 29 | fn second() callconv(.@"async") anyerror!void { | |
| 30 | return error.Fail; | |
| 31 | } | |
| 32 | ||
| 33 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | pub fn main() !void { | |
| 9 | var p = async suspendOnce(); | |
| 10 | resume p; //ok | |
| 11 | resume p; //bad | |
| 12 | return error.TestFailed; | |
| 13 | } | |
| 14 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | pub fn main() !void { | |
| 9 | var frame = async first(); | |
| 10 | resume frame; | |
| 11 | return error.TestFailed; | |
| 12 | } | |
| 13 | fn first() void { | |
| 14 | other(); | |
| 15 | } | |
| 16 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | pub fn main() !void { | |
| 9 | var frame = async first(); | |
| 10 | resume frame; | |
| 11 | return error.TestFailed; | |
| 12 | } | |
| 13 | fn first() void { | |
| 14 | var frame = async other(); | |
| 15 | await frame; | |
| 16 | } | |
| 17 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | fn foo() void { | |
| 9 | suspend { | |
| 10 | global_frame = @frame(); | |
| 11 | } | |
| 12 | var f = async bar(@frame()); | |
| 13 | _ = &f; | |
| 14 | std.process.exit(1); | |
| 15 | } | |
| 16 | ||
| 17 | fn bar(frame: anyframe) void { | |
| 18 | suspend { | |
| 19 | resume frame; | |
| 20 | } | |
| 21 | std.process.exit(1); | |
| 22 | } | |
| 23 | ||
| 24 | var global_frame: anyframe = undefined; | |
| 25 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = message; | |
| 5 | _ = stack_trace; | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | fn foo() void { | |
| 9 | var f = async bar(@frame()); | |
| 10 | _ = &f; | |
| 11 | std.process.exit(1); | |
| 12 | } | |
| 13 | ||
| 14 | fn bar(frame: anyframe) void { | |
| 15 | suspend { | |
| 16 | resume frame; | |
| 17 | } | |
| 18 | std.process.exit(1); | |
| 19 | } | |
| 20 | ||
| 21 | pub 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 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
test/compare_output.zig+3-286| ... | ... | @@ -17,15 +17,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 17 | 17 | \\} |
| 18 | 18 | , "Hello, world!" ++ if (@import("builtin").os.tag == .windows) "\r\n" else "\n"); |
| 19 | 19 | |
| 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 | 20 | cases.addC("number literals", |
| 30 | 21 | \\const std = @import("std"); |
| 31 | 22 | \\const builtin = @import("builtin"); |
| ... | ... | @@ -158,24 +149,6 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 158 | 149 | \\ |
| 159 | 150 | ); |
| 160 | 151 | |
| 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 | 152 | cases.addC("expose function pointer to C land", |
| 180 | 153 | \\const c = @cImport(@cInclude("stdlib.h")); |
| 181 | 154 | \\ |
| ... | ... | @@ -236,267 +209,11 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 236 | 209 | \\} |
| 237 | 210 | , "3.25\n3\n3.00\n-0.40\n"); |
| 238 | 211 | |
| 239 | cases.add("same named methods in incomplete struct", | |
| 240 | \\const io = @import("std").io; | |
| 241 | \\ | |
| 242 | \\const Foo = struct { | |
| 243 | \\ field1: Bar, | |
| 244 | \\ | |
| 245 | \\ fn method(a: *const Foo) bool { | |
| 246 | \\ _ = a; | |
| 247 | \\ return true; | |
| 248 | \\ } | |
| 249 | \\}; | |
| 250 | \\ | |
| 251 | \\const Bar = struct { | |
| 252 | \\ field2: i32, | |
| 253 | \\ | |
| 254 | \\ fn method(b: *const Bar) bool { | |
| 255 | \\ _ = b; | |
| 256 | \\ return true; | |
| 257 | \\ } | |
| 258 | \\}; | |
| 259 | \\ | |
| 260 | \\pub fn main() void { | |
| 261 | \\ const bar = Bar {.field2 = 13,}; | |
| 262 | \\ const foo = Foo {.field1 = bar,}; | |
| 263 | \\ const stdout = io.getStdOut().writer(); | |
| 264 | \\ if (!foo.method()) { | |
| 265 | \\ stdout.print("BAD\n", .{}) catch unreachable; | |
| 266 | \\ } | |
| 267 | \\ if (!bar.method()) { | |
| 268 | \\ stdout.print("BAD\n", .{}) catch unreachable; | |
| 269 | \\ } | |
| 270 | \\ stdout.print("OK\n", .{}) catch unreachable; | |
| 271 | \\} | |
| 272 | , "OK\n"); | |
| 273 | ||
| 274 | cases.add("defer with only fallthrough", | |
| 275 | \\const io = @import("std").io; | |
| 276 | \\pub fn main() void { | |
| 277 | \\ const stdout = io.getStdOut().writer(); | |
| 278 | \\ stdout.print("before\n", .{}) catch unreachable; | |
| 279 | \\ defer stdout.print("defer1\n", .{}) catch unreachable; | |
| 280 | \\ defer stdout.print("defer2\n", .{}) catch unreachable; | |
| 281 | \\ defer stdout.print("defer3\n", .{}) catch unreachable; | |
| 282 | \\ stdout.print("after\n", .{}) catch unreachable; | |
| 283 | \\} | |
| 284 | , "before\nafter\ndefer3\ndefer2\ndefer1\n"); | |
| 285 | ||
| 286 | cases.add("defer with return", | |
| 287 | \\const io = @import("std").io; | |
| 288 | \\const os = @import("std").os; | |
| 289 | \\pub fn main() void { | |
| 290 | \\ const stdout = io.getStdOut().writer(); | |
| 291 | \\ stdout.print("before\n", .{}) catch unreachable; | |
| 292 | \\ defer stdout.print("defer1\n", .{}) catch unreachable; | |
| 293 | \\ defer stdout.print("defer2\n", .{}) catch unreachable; | |
| 294 | \\ var gpa: @import("std").heap.GeneralPurposeAllocator(.{}) = .init; | |
| 295 | \\ defer _ = gpa.deinit(); | |
| 296 | \\ var arena = @import("std").heap.ArenaAllocator.init(gpa.allocator()); | |
| 297 | \\ defer arena.deinit(); | |
| 298 | \\ var args_it = @import("std").process.argsWithAllocator(arena.allocator()) catch unreachable; | |
| 299 | \\ if (args_it.skip() and !args_it.skip()) return; | |
| 300 | \\ defer stdout.print("defer3\n", .{}) catch unreachable; | |
| 301 | \\ stdout.print("after\n", .{}) catch unreachable; | |
| 302 | \\} | |
| 303 | , "before\ndefer2\ndefer1\n"); | |
| 304 | ||
| 305 | cases.add("errdefer and it fails", | |
| 306 | \\const io = @import("std").io; | |
| 307 | \\pub fn main() void { | |
| 308 | \\ do_test() catch return; | |
| 309 | \\} | |
| 310 | \\fn do_test() !void { | |
| 311 | \\ const stdout = io.getStdOut().writer(); | |
| 312 | \\ stdout.print("before\n", .{}) catch unreachable; | |
| 313 | \\ defer stdout.print("defer1\n", .{}) catch unreachable; | |
| 314 | \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; | |
| 315 | \\ try its_gonna_fail(); | |
| 316 | \\ defer stdout.print("defer3\n", .{}) catch unreachable; | |
| 317 | \\ stdout.print("after\n", .{}) catch unreachable; | |
| 318 | \\} | |
| 319 | \\fn its_gonna_fail() !void { | |
| 320 | \\ return error.IToldYouItWouldFail; | |
| 321 | \\} | |
| 322 | , "before\ndeferErr\ndefer1\n"); | |
| 323 | ||
| 324 | cases.add("errdefer and it passes", | |
| 325 | \\const io = @import("std").io; | |
| 326 | \\pub fn main() void { | |
| 327 | \\ do_test() catch return; | |
| 328 | \\} | |
| 329 | \\fn do_test() !void { | |
| 330 | \\ const stdout = io.getStdOut().writer(); | |
| 331 | \\ stdout.print("before\n", .{}) catch unreachable; | |
| 332 | \\ defer stdout.print("defer1\n", .{}) catch unreachable; | |
| 333 | \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; | |
| 334 | \\ try its_gonna_pass(); | |
| 335 | \\ defer stdout.print("defer3\n", .{}) catch unreachable; | |
| 336 | \\ stdout.print("after\n", .{}) catch unreachable; | |
| 337 | \\} | |
| 338 | \\fn its_gonna_pass() anyerror!void { } | |
| 339 | , "before\nafter\ndefer3\ndefer1\n"); | |
| 340 | ||
| 341 | cases.addCase(x: { | |
| 342 | var tc = cases.create("@embedFile", | |
| 343 | \\const foo_txt = @embedFile("foo.txt"); | |
| 344 | \\const io = @import("std").io; | |
| 345 | \\ | |
| 346 | \\pub fn main() void { | |
| 347 | \\ const stdout = io.getStdOut().writer(); | |
| 348 | \\ stdout.print(foo_txt, .{}) catch unreachable; | |
| 349 | \\} | |
| 350 | , "1234\nabcd\n"); | |
| 351 | ||
| 352 | tc.addSourceFile("foo.txt", "1234\nabcd\n"); | |
| 353 | ||
| 354 | break :x tc; | |
| 355 | }); | |
| 356 | ||
| 357 | cases.addCase(x: { | |
| 358 | var tc = cases.create("parsing args", | |
| 359 | \\const std = @import("std"); | |
| 360 | \\const io = std.io; | |
| 361 | \\const os = std.os; | |
| 362 | \\ | |
| 363 | \\pub fn main() !void { | |
| 364 | \\ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | |
| 365 | \\ defer _ = gpa.deinit(); | |
| 366 | \\ var arena = std.heap.ArenaAllocator.init(gpa.allocator()); | |
| 367 | \\ defer arena.deinit(); | |
| 368 | \\ var args_it = try std.process.argsWithAllocator(arena.allocator()); | |
| 369 | \\ const stdout = io.getStdOut().writer(); | |
| 370 | \\ var index: usize = 0; | |
| 371 | \\ _ = args_it.skip(); | |
| 372 | \\ while (args_it.next()) |arg| : (index += 1) { | |
| 373 | \\ try stdout.print("{}: {s}\n", .{index, arg}); | |
| 374 | \\ } | |
| 375 | \\} | |
| 376 | , | |
| 377 | \\0: first arg | |
| 378 | \\1: 'a' 'b' \ | |
| 379 | \\2: bare | |
| 380 | \\3: ba""re | |
| 381 | \\4: " | |
| 382 | \\5: last arg | |
| 383 | \\ | |
| 384 | ); | |
| 385 | ||
| 386 | tc.setCommandLineArgs(&[_][]const u8{ | |
| 387 | "first arg", | |
| 388 | "'a' 'b' \\", | |
| 389 | "bare", | |
| 390 | "ba\"\"re", | |
| 391 | "\"", | |
| 392 | "last arg", | |
| 393 | }); | |
| 394 | ||
| 395 | break :x tc; | |
| 396 | }); | |
| 397 | ||
| 398 | cases.addCase(x: { | |
| 399 | var tc = cases.create("parsing args new API", | |
| 400 | \\const std = @import("std"); | |
| 401 | \\const io = std.io; | |
| 402 | \\const os = std.os; | |
| 403 | \\ | |
| 404 | \\pub fn main() !void { | |
| 405 | \\ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | |
| 406 | \\ defer _ = gpa.deinit(); | |
| 407 | \\ var arena = std.heap.ArenaAllocator.init(gpa.allocator()); | |
| 408 | \\ defer arena.deinit(); | |
| 409 | \\ var args_it = try std.process.argsWithAllocator(arena.allocator()); | |
| 410 | \\ const stdout = io.getStdOut().writer(); | |
| 411 | \\ var index: usize = 0; | |
| 412 | \\ _ = args_it.skip(); | |
| 413 | \\ while (args_it.next()) |arg| : (index += 1) { | |
| 414 | \\ try stdout.print("{}: {s}\n", .{index, arg}); | |
| 415 | \\ } | |
| 416 | \\} | |
| 417 | , | |
| 418 | \\0: first arg | |
| 419 | \\1: 'a' 'b' \ | |
| 420 | \\2: bare | |
| 421 | \\3: ba""re | |
| 422 | \\4: " | |
| 423 | \\5: last arg | |
| 424 | \\ | |
| 425 | ); | |
| 426 | ||
| 427 | tc.setCommandLineArgs(&[_][]const u8{ | |
| 428 | "first arg", | |
| 429 | "'a' 'b' \\", | |
| 430 | "bare", | |
| 431 | "ba\"\"re", | |
| 432 | "\"", | |
| 433 | "last arg", | |
| 434 | }); | |
| 435 | ||
| 436 | break :x tc; | |
| 437 | }); | |
| 438 | ||
| 439 | // It is required to override the log function in order to print to stdout instead of stderr | |
| 440 | cases.add("std.log per scope log level override", | |
| 441 | \\const std = @import("std"); | |
| 442 | \\ | |
| 443 | \\pub const std_options: std.Options = .{ | |
| 444 | \\ .log_level = .debug, | |
| 445 | \\ | |
| 446 | \\ .log_scope_levels = &.{ | |
| 447 | \\ .{ .scope = .a, .level = .warn }, | |
| 448 | \\ .{ .scope = .c, .level = .err }, | |
| 449 | \\ }, | |
| 450 | \\ .logFn = log, | |
| 451 | \\}; | |
| 452 | \\ | |
| 453 | \\const loga = std.log.scoped(.a); | |
| 454 | \\const logb = std.log.scoped(.b); | |
| 455 | \\const logc = std.log.scoped(.c); | |
| 456 | \\ | |
| 457 | \\pub fn main() !void { | |
| 458 | \\ loga.debug("", .{}); | |
| 459 | \\ logb.debug("", .{}); | |
| 460 | \\ logc.debug("", .{}); | |
| 461 | \\ | |
| 462 | \\ loga.info("", .{}); | |
| 463 | \\ logb.info("", .{}); | |
| 464 | \\ logc.info("", .{}); | |
| 465 | \\ | |
| 466 | \\ loga.warn("", .{}); | |
| 467 | \\ logb.warn("", .{}); | |
| 468 | \\ logc.warn("", .{}); | |
| 469 | \\ | |
| 470 | \\ loga.err("", .{}); | |
| 471 | \\ logb.err("", .{}); | |
| 472 | \\ logc.err("", .{}); | |
| 473 | \\} | |
| 474 | \\pub fn log( | |
| 475 | \\ comptime level: std.log.Level, | |
| 476 | \\ comptime scope: @TypeOf(.EnumLiteral), | |
| 477 | \\ comptime format: []const u8, | |
| 478 | \\ args: anytype, | |
| 479 | \\) void { | |
| 480 | \\ const level_txt = comptime level.asText(); | |
| 481 | \\ const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "):"; | |
| 482 | \\ const stdout = std.io.getStdOut().writer(); | |
| 483 | \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; | |
| 484 | \\} | |
| 485 | , | |
| 486 | \\debug(b): | |
| 487 | \\info(b): | |
| 488 | \\warning(a): | |
| 489 | \\warning(b): | |
| 490 | \\error(a): | |
| 491 | \\error(b): | |
| 492 | \\error(c): | |
| 493 | \\ | |
| 494 | ); | |
| 495 | ||
| 496 | cases.add("valid carriage return example", "const io = @import(\"std\").io;\r\n" ++ // Testing CRLF line endings are valid | |
| 212 | cases.add("valid carriage return example", "const std = @import(\"std\");\r\n" ++ // Testing CRLF line endings are valid | |
| 497 | 213 | "\r\n" ++ |
| 498 | 214 | "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 | 217 | " stdout.print(\\\\A Multiline\r\n" ++ // testing CRLF at end of multiline string line is valid and normalises to \n in the output |
| 501 | 218 | " \\\\String\r\n" ++ |
| 502 | 219 | " , .{}) catch unreachable;\r\n" ++ |
test/incremental/add_decl+7-7| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | #file=main.zig |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub fn main() !void { |
| 9 | try std.io.getStdOut().writeAll(foo); | |
| 9 | try std.fs.File.stdout().writeAll(foo); | |
| 10 | 10 | } |
| 11 | 11 | const foo = "good morning\n"; |
| 12 | 12 | #expect_stdout="good morning\n" |
| ... | ... | @@ -15,7 +15,7 @@ const foo = "good morning\n"; |
| 15 | 15 | #file=main.zig |
| 16 | 16 | const std = @import("std"); |
| 17 | 17 | pub fn main() !void { |
| 18 | try std.io.getStdOut().writeAll(foo); | |
| 18 | try std.fs.File.stdout().writeAll(foo); | |
| 19 | 19 | } |
| 20 | 20 | const foo = "good morning\n"; |
| 21 | 21 | const bar = "good evening\n"; |
| ... | ... | @@ -25,7 +25,7 @@ const bar = "good evening\n"; |
| 25 | 25 | #file=main.zig |
| 26 | 26 | const std = @import("std"); |
| 27 | 27 | pub fn main() !void { |
| 28 | try std.io.getStdOut().writeAll(bar); | |
| 28 | try std.fs.File.stdout().writeAll(bar); | |
| 29 | 29 | } |
| 30 | 30 | const foo = "good morning\n"; |
| 31 | 31 | const bar = "good evening\n"; |
| ... | ... | @@ -35,17 +35,17 @@ const bar = "good evening\n"; |
| 35 | 35 | #file=main.zig |
| 36 | 36 | const std = @import("std"); |
| 37 | 37 | pub fn main() !void { |
| 38 | try std.io.getStdOut().writeAll(qux); | |
| 38 | try std.fs.File.stdout().writeAll(qux); | |
| 39 | 39 | } |
| 40 | 40 | const foo = "good morning\n"; |
| 41 | 41 | const 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' | |
| 43 | 43 | |
| 44 | 44 | #update=add missing declaration |
| 45 | 45 | #file=main.zig |
| 46 | 46 | const std = @import("std"); |
| 47 | 47 | pub fn main() !void { |
| 48 | try std.io.getStdOut().writeAll(qux); | |
| 48 | try std.fs.File.stdout().writeAll(qux); | |
| 49 | 49 | } |
| 50 | 50 | const foo = "good morning\n"; |
| 51 | 51 | const bar = "good evening\n"; |
| ... | ... | @@ -56,7 +56,7 @@ const qux = "good night\n"; |
| 56 | 56 | #file=main.zig |
| 57 | 57 | const std = @import("std"); |
| 58 | 58 | pub fn main() !void { |
| 59 | try std.io.getStdOut().writeAll(qux); | |
| 59 | try std.fs.File.stdout().writeAll(qux); | |
| 60 | 60 | } |
| 61 | 61 | const qux = "good night\n"; |
| 62 | 62 | #expect_stdout="good night\n" |
test/incremental/add_decl_namespaced+7-7| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | #file=main.zig |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub fn main() !void { |
| 9 | try std.io.getStdOut().writeAll(@This().foo); | |
| 9 | try std.fs.File.stdout().writeAll(@This().foo); | |
| 10 | 10 | } |
| 11 | 11 | const foo = "good morning\n"; |
| 12 | 12 | #expect_stdout="good morning\n" |
| ... | ... | @@ -15,7 +15,7 @@ const foo = "good morning\n"; |
| 15 | 15 | #file=main.zig |
| 16 | 16 | const std = @import("std"); |
| 17 | 17 | pub fn main() !void { |
| 18 | try std.io.getStdOut().writeAll(@This().foo); | |
| 18 | try std.fs.File.stdout().writeAll(@This().foo); | |
| 19 | 19 | } |
| 20 | 20 | const foo = "good morning\n"; |
| 21 | 21 | const bar = "good evening\n"; |
| ... | ... | @@ -25,7 +25,7 @@ const bar = "good evening\n"; |
| 25 | 25 | #file=main.zig |
| 26 | 26 | const std = @import("std"); |
| 27 | 27 | pub fn main() !void { |
| 28 | try std.io.getStdOut().writeAll(@This().bar); | |
| 28 | try std.fs.File.stdout().writeAll(@This().bar); | |
| 29 | 29 | } |
| 30 | 30 | const foo = "good morning\n"; |
| 31 | 31 | const bar = "good evening\n"; |
| ... | ... | @@ -35,18 +35,18 @@ const bar = "good evening\n"; |
| 35 | 35 | #file=main.zig |
| 36 | 36 | const std = @import("std"); |
| 37 | 37 | pub fn main() !void { |
| 38 | try std.io.getStdOut().writeAll(@This().qux); | |
| 38 | try std.fs.File.stdout().writeAll(@This().qux); | |
| 39 | 39 | } |
| 40 | 40 | const foo = "good morning\n"; |
| 41 | 41 | const 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 | 43 | #expect_error=main.zig:1:1: note: struct declared here |
| 44 | 44 | |
| 45 | 45 | #update=add missing declaration |
| 46 | 46 | #file=main.zig |
| 47 | 47 | const std = @import("std"); |
| 48 | 48 | pub fn main() !void { |
| 49 | try std.io.getStdOut().writeAll(@This().qux); | |
| 49 | try std.fs.File.stdout().writeAll(@This().qux); | |
| 50 | 50 | } |
| 51 | 51 | const foo = "good morning\n"; |
| 52 | 52 | const bar = "good evening\n"; |
| ... | ... | @@ -57,7 +57,7 @@ const qux = "good night\n"; |
| 57 | 57 | #file=main.zig |
| 58 | 58 | const std = @import("std"); |
| 59 | 59 | pub fn main() !void { |
| 60 | try std.io.getStdOut().writeAll(@This().qux); | |
| 60 | try std.fs.File.stdout().writeAll(@This().qux); | |
| 61 | 61 | } |
| 62 | 62 | const qux = "good night\n"; |
| 63 | 63 | #expect_stdout="good night\n" |
test/incremental/bad_import+2-2| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | #file=main.zig |
| 8 | 8 | pub fn main() !void { |
| 9 | 9 | _ = @import("foo.zig"); |
| 10 | try std.io.getStdOut().writeAll("success\n"); | |
| 10 | try std.fs.File.stdout().writeAll("success\n"); | |
| 11 | 11 | } |
| 12 | 12 | const std = @import("std"); |
| 13 | 13 | #file=foo.zig |
| ... | ... | @@ -29,7 +29,7 @@ comptime { |
| 29 | 29 | #file=main.zig |
| 30 | 30 | pub fn main() !void { |
| 31 | 31 | //_ = @import("foo.zig"); |
| 32 | try std.io.getStdOut().writeAll("success\n"); | |
| 32 | try std.fs.File.stdout().writeAll("success\n"); | |
| 33 | 33 | } |
| 34 | 34 | const std = @import("std"); |
| 35 | 35 | #expect_stdout="success\n" |
test/incremental/change_embed_file+3-3| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | const string = @embedFile("string.txt"); |
| 9 | 9 | pub fn main() !void { |
| 10 | try std.io.getStdOut().writeAll(string); | |
| 10 | try std.fs.File.stdout().writeAll(string); | |
| 11 | 11 | } |
| 12 | 12 | #file=string.txt |
| 13 | 13 | Hello, World! |
| ... | ... | @@ -27,7 +27,7 @@ Hello again, World! |
| 27 | 27 | const std = @import("std"); |
| 28 | 28 | const string = @embedFile("string.txt"); |
| 29 | 29 | pub 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 | 32 | #expect_stdout="a hardcoded string\n" |
| 33 | 33 | |
| ... | ... | @@ -36,7 +36,7 @@ pub fn main() !void { |
| 36 | 36 | const std = @import("std"); |
| 37 | 37 | const string = @embedFile("string.txt"); |
| 38 | 38 | pub fn main() !void { |
| 39 | try std.io.getStdOut().writeAll(string); | |
| 39 | try std.fs.File.stdout().writeAll(string); | |
| 40 | 40 | } |
| 41 | 41 | #expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound |
| 42 | 42 |
test/incremental/change_enum_tag_type+6-3| ... | ... | @@ -14,7 +14,8 @@ const Foo = enum(Tag) { |
| 14 | 14 | pub fn main() !void { |
| 15 | 15 | var val: Foo = undefined; |
| 16 | 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 | } |
| 19 | 20 | const std = @import("std"); |
| 20 | 21 | #expect_stdout="a\n" |
| ... | ... | @@ -31,7 +32,8 @@ const Foo = enum(Tag) { |
| 31 | 32 | pub fn main() !void { |
| 32 | 33 | var val: Foo = undefined; |
| 33 | 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 | } |
| 36 | 38 | comptime { |
| 37 | 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 | 55 | pub fn main() !void { |
| 54 | 56 | var val: Foo = undefined; |
| 55 | 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 | } |
| 58 | 61 | const std = @import("std"); |
| 59 | 62 | #expect_stdout="a\n" |
test/incremental/change_exports+12-6| ... | ... | @@ -16,7 +16,8 @@ pub fn main() !void { |
| 16 | 16 | extern const bar: u32; |
| 17 | 17 | }; |
| 18 | 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 | } |
| 21 | 22 | const std = @import("std"); |
| 22 | 23 | #expect_stdout="123\n" |
| ... | ... | @@ -37,7 +38,8 @@ pub fn main() !void { |
| 37 | 38 | extern const other: u32; |
| 38 | 39 | }; |
| 39 | 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 | } |
| 42 | 44 | const std = @import("std"); |
| 43 | 45 | #expect_error=main.zig:6:5: error: exported symbol collision: foo |
| ... | ... | @@ -59,7 +61,8 @@ pub fn main() !void { |
| 59 | 61 | extern const other: u32; |
| 60 | 62 | }; |
| 61 | 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 | } |
| 64 | 67 | const std = @import("std"); |
| 65 | 68 | #expect_stdout="123 456\n" |
| ... | ... | @@ -83,7 +86,8 @@ pub fn main() !void { |
| 83 | 86 | extern const other: u32; |
| 84 | 87 | }; |
| 85 | 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 | } |
| 88 | 92 | const std = @import("std"); |
| 89 | 93 | #expect_stdout="123 456\n" |
| ... | ... | @@ -128,7 +132,8 @@ pub fn main() !void { |
| 128 | 132 | extern const other: u32; |
| 129 | 133 | }; |
| 130 | 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 | } |
| 133 | 138 | const std = @import("std"); |
| 134 | 139 | #expect_stdout="123 456\n" |
| ... | ... | @@ -152,7 +157,8 @@ pub fn main() !void { |
| 152 | 157 | extern const other: u32; |
| 153 | 158 | }; |
| 154 | 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 | } |
| 157 | 163 | const std = @import("std"); |
| 158 | 164 | #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 | try foo(123); |
| 8 | 8 | } |
| 9 | 9 | fn 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 | } |
| 12 | 13 | const std = @import("std"); |
| 13 | 14 | #expect_stdout="123\n" |
| ... | ... | @@ -18,7 +19,8 @@ pub fn main() !void { |
| 18 | 19 | try foo(123); |
| 19 | 20 | } |
| 20 | 21 | fn 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 | } |
| 23 | 25 | const std = @import("std"); |
| 24 | 26 | #expect_stdout="123\n" |
| ... | ... | @@ -29,7 +31,8 @@ pub fn main() !void { |
| 29 | 31 | try foo(-42); |
| 30 | 32 | } |
| 31 | 33 | fn 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 | } |
| 34 | 37 | const std = @import("std"); |
| 35 | 38 | #expect_stdout="-42\n" |
test/incremental/change_generic_line_number+2-2| ... | ... | @@ -6,7 +6,7 @@ const std = @import("std"); |
| 6 | 6 | fn Printer(message: []const u8) type { |
| 7 | 7 | return struct { |
| 8 | 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 | 22 | fn Printer(message: []const u8) type { |
| 23 | 23 | return struct { |
| 24 | 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 | 4 | #file=main.zig |
| 5 | 5 | const std = @import("std"); |
| 6 | 6 | pub fn main() !void { |
| 7 | try std.io.getStdOut().writeAll("foo\n"); | |
| 7 | try std.fs.File.stdout().writeAll("foo\n"); | |
| 8 | 8 | } |
| 9 | 9 | #expect_stdout="foo\n" |
| 10 | 10 | #update=change line number |
| ... | ... | @@ -12,6 +12,6 @@ pub fn main() !void { |
| 12 | 12 | const std = @import("std"); |
| 13 | 13 | |
| 14 | 14 | pub fn main() !void { |
| 15 | try std.io.getStdOut().writeAll("foo\n"); | |
| 15 | try std.fs.File.stdout().writeAll("foo\n"); | |
| 16 | 16 | } |
| 17 | 17 | #expect_stdout="foo\n" |
test/incremental/change_panic_handler+6-3| ... | ... | @@ -11,7 +11,8 @@ pub fn main() !u8 { |
| 11 | 11 | } |
| 12 | 12 | pub const panic = std.debug.FullPanic(myPanic); |
| 13 | 13 | fn 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 | 16 | std.process.exit(0); |
| 16 | 17 | } |
| 17 | 18 | const std = @import("std"); |
| ... | ... | @@ -27,7 +28,8 @@ pub fn main() !u8 { |
| 27 | 28 | } |
| 28 | 29 | pub const panic = std.debug.FullPanic(myPanic); |
| 29 | 30 | fn 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 | 33 | std.process.exit(0); |
| 32 | 34 | } |
| 33 | 35 | const std = @import("std"); |
| ... | ... | @@ -43,7 +45,8 @@ pub fn main() !u8 { |
| 43 | 45 | } |
| 44 | 46 | pub const panic = std.debug.FullPanic(myPanicNew); |
| 45 | 47 | fn 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 | 50 | std.process.exit(0); |
| 48 | 51 | } |
| 49 | 52 | const std = @import("std"); |
test/incremental/change_panic_handler_explicit+6-3| ... | ... | @@ -41,7 +41,8 @@ pub const panic = struct { |
| 41 | 41 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 42 | 42 | }; |
| 43 | 43 | fn 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 | 46 | std.process.exit(0); |
| 46 | 47 | } |
| 47 | 48 | const std = @import("std"); |
| ... | ... | @@ -87,7 +88,8 @@ pub const panic = struct { |
| 87 | 88 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 88 | 89 | }; |
| 89 | 90 | fn 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 | 93 | std.process.exit(0); |
| 92 | 94 | } |
| 93 | 95 | const std = @import("std"); |
| ... | ... | @@ -133,7 +135,8 @@ pub const panic = struct { |
| 133 | 135 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 134 | 136 | }; |
| 135 | 137 | fn 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 | 140 | std.process.exit(0); |
| 138 | 141 | } |
| 139 | 142 | const std = @import("std"); |
test/incremental/change_shift_op+4-2| ... | ... | @@ -8,7 +8,8 @@ pub fn main() !void { |
| 8 | 8 | try foo(0x1300); |
| 9 | 9 | } |
| 10 | 10 | fn 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 | } |
| 13 | 14 | const std = @import("std"); |
| 14 | 15 | #expect_stdout="0x3000\n" |
| ... | ... | @@ -18,7 +19,8 @@ pub fn main() !void { |
| 18 | 19 | try foo(0x1300); |
| 19 | 20 | } |
| 20 | 21 | fn 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 | } |
| 23 | 25 | const std = @import("std"); |
| 24 | 26 | #expect_stdout="0x130\n" |
test/incremental/change_struct_same_fields+6-3| ... | ... | @@ -10,7 +10,8 @@ pub fn main() !void { |
| 10 | 10 | try foo(&val); |
| 11 | 11 | } |
| 12 | 12 | fn 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 | 15 | "{d} {d}\n", |
| 15 | 16 | .{ val.x, val.y }, |
| 16 | 17 | ); |
| ... | ... | @@ -26,7 +27,8 @@ pub fn main() !void { |
| 26 | 27 | try foo(&val); |
| 27 | 28 | } |
| 28 | 29 | fn 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 | 32 | "{d} {d}\n", |
| 31 | 33 | .{ val.x, val.y }, |
| 32 | 34 | ); |
| ... | ... | @@ -42,7 +44,8 @@ pub fn main() !void { |
| 42 | 44 | try foo(&val); |
| 43 | 45 | } |
| 44 | 46 | fn 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 | 49 | "{d} {d}\n", |
| 47 | 50 | .{ val.x, val.y }, |
| 48 | 51 | ); |
test/incremental/change_zon_file+3-3| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | const message: []const u8 = @import("message.zon"); |
| 9 | 9 | pub fn main() !void { |
| 10 | try std.io.getStdOut().writeAll(message); | |
| 10 | try std.fs.File.stdout().writeAll(message); | |
| 11 | 11 | } |
| 12 | 12 | #file=message.zon |
| 13 | 13 | "Hello, World!\n" |
| ... | ... | @@ -28,7 +28,7 @@ pub fn main() !void { |
| 28 | 28 | const std = @import("std"); |
| 29 | 29 | const message: []const u8 = @import("message.zon"); |
| 30 | 30 | pub 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 | 33 | #expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound |
| 34 | 34 | #expect_error=main.zig:2:37: note: file imported here |
| ... | ... | @@ -43,6 +43,6 @@ pub fn main() !void { |
| 43 | 43 | const std = @import("std"); |
| 44 | 44 | const message: []const u8 = @import("message.zon"); |
| 45 | 45 | pub fn main() !void { |
| 46 | try std.io.getStdOut().writeAll(message); | |
| 46 | try std.fs.File.stdout().writeAll(message); | |
| 47 | 47 | } |
| 48 | 48 | #expect_stdout="We're back, World!\n" |
test/incremental/change_zon_file_no_result_type+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | #file=main.zig |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub 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 | 11 | #file=foo.zon |
| 12 | 12 | .{ |
test/incremental/compile_log+3-3| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | #file=main.zig |
| 8 | 8 | const std = @import("std"); |
| 9 | 9 | pub 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 | 12 | #expect_stdout="Hello, World!\n" |
| 13 | 13 | |
| ... | ... | @@ -15,7 +15,7 @@ pub fn main() !void { |
| 15 | 15 | #file=main.zig |
| 16 | 16 | const std = @import("std"); |
| 17 | 17 | pub fn main() !void { |
| 18 | try std.io.getStdOut().writeAll("Hello, World!\n"); | |
| 18 | try std.fs.File.stdout().writeAll("Hello, World!\n"); | |
| 19 | 19 | @compileLog("this is a log"); |
| 20 | 20 | } |
| 21 | 21 | #expect_error=main.zig:4:5: error: found compile log statement |
| ... | ... | @@ -25,6 +25,6 @@ pub fn main() !void { |
| 25 | 25 | #file=main.zig |
| 26 | 26 | const std = @import("std"); |
| 27 | 27 | pub 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 | 30 | #expect_stdout="Hello, World!\n" |
test/incremental/fix_astgen_failure+5-5| ... | ... | @@ -9,28 +9,28 @@ pub fn main() !void { |
| 9 | 9 | } |
| 10 | 10 | #file=foo.zig |
| 11 | 11 | pub 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 | 14 | #expect_error=foo.zig:2:9: error: use of undeclared identifier 'std' |
| 15 | 15 | #update=fix the error |
| 16 | 16 | #file=foo.zig |
| 17 | 17 | const std = @import("std"); |
| 18 | 18 | pub 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 | 21 | #expect_stdout="Hello, World!\n" |
| 22 | 22 | #update=add new error |
| 23 | 23 | #file=foo.zig |
| 24 | 24 | const std = @import("std"); |
| 25 | 25 | pub 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 | 29 | #update=fix the new error |
| 30 | 30 | #file=foo.zig |
| 31 | 31 | const std = @import("std"); |
| 32 | 32 | const hello_str = "Hello, World! Again!\n"; |
| 33 | 33 | pub fn hello() !void { |
| 34 | try std.io.getStdOut().writeAll(hello_str); | |
| 34 | try std.fs.File.stdout().writeAll(hello_str); | |
| 35 | 35 | } |
| 36 | 36 | #expect_stdout="Hello, World! Again!\n" |
test/incremental/function_becomes_inline+3-3| ... | ... | @@ -7,7 +7,7 @@ pub fn main() !void { |
| 7 | 7 | try foo(); |
| 8 | 8 | } |
| 9 | 9 | fn foo() !void { |
| 10 | try std.io.getStdOut().writer().writeAll("Hello, World!\n"); | |
| 10 | try std.fs.File.stdout().writeAll("Hello, World!\n"); | |
| 11 | 11 | } |
| 12 | 12 | const std = @import("std"); |
| 13 | 13 | #expect_stdout="Hello, World!\n" |
| ... | ... | @@ -18,7 +18,7 @@ pub fn main() !void { |
| 18 | 18 | try foo(); |
| 19 | 19 | } |
| 20 | 20 | inline 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 | } |
| 23 | 23 | const std = @import("std"); |
| 24 | 24 | #expect_stdout="Hello, World!\n" |
| ... | ... | @@ -29,7 +29,7 @@ pub fn main() !void { |
| 29 | 29 | try foo(); |
| 30 | 30 | } |
| 31 | 31 | inline 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 | } |
| 34 | 34 | const std = @import("std"); |
| 35 | 35 | #expect_stdout="Hello, `inline` World!\n" |
test/incremental/hello+2-2| ... | ... | @@ -6,13 +6,13 @@ |
| 6 | 6 | #file=main.zig |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub 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 | 11 | #expect_stdout="good morning\n" |
| 12 | 12 | #update=change the string |
| 13 | 13 | #file=main.zig |
| 14 | 14 | const std = @import("std"); |
| 15 | 15 | pub fn main() !void { |
| 16 | try std.io.getStdOut().writeAll("おはようございます\n"); | |
| 16 | try std.fs.File.stdout().writeAll("おはようございます\n"); | |
| 17 | 17 | } |
| 18 | 18 | #expect_stdout="おはようございます\n" |
test/incremental/make_decl_pub+2-2| ... | ... | @@ -11,7 +11,7 @@ pub fn main() !void { |
| 11 | 11 | #file=foo.zig |
| 12 | 12 | const std = @import("std"); |
| 13 | 13 | fn hello() !void { |
| 14 | try std.io.getStdOut().writeAll("Hello, World!\n"); | |
| 14 | try std.fs.File.stdout().writeAll("Hello, World!\n"); | |
| 15 | 15 | } |
| 16 | 16 | #expect_error=main.zig:3:12: error: 'hello' is not marked 'pub' |
| 17 | 17 | #expect_error=foo.zig:2:1: note: declared here |
| ... | ... | @@ -20,6 +20,6 @@ fn hello() !void { |
| 20 | 20 | #file=foo.zig |
| 21 | 21 | const std = @import("std"); |
| 22 | 22 | pub 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 | 25 | #expect_stdout="Hello, World!\n" |
test/incremental/modify_inline_fn+2-2| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub fn main() !void { |
| 9 | 9 | const str = getStr(); |
| 10 | try std.io.getStdOut().writeAll(str); | |
| 10 | try std.fs.File.stdout().writeAll(str); | |
| 11 | 11 | } |
| 12 | 12 | inline fn getStr() []const u8 { |
| 13 | 13 | return "foo\n"; |
| ... | ... | @@ -18,7 +18,7 @@ inline fn getStr() []const u8 { |
| 18 | 18 | const std = @import("std"); |
| 19 | 19 | pub fn main() !void { |
| 20 | 20 | const str = getStr(); |
| 21 | try std.io.getStdOut().writeAll(str); | |
| 21 | try std.fs.File.stdout().writeAll(str); | |
| 22 | 22 | } |
| 23 | 23 | inline fn getStr() []const u8 { |
| 24 | 24 | return "bar\n"; |
test/incremental/move_src+6-4| ... | ... | @@ -6,7 +6,8 @@ |
| 6 | 6 | #file=main.zig |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub 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 | } |
| 11 | 12 | fn foo() u32 { |
| 12 | 13 | return @src().line; |
| ... | ... | @@ -14,13 +15,14 @@ fn foo() u32 { |
| 14 | 15 | fn bar() u32 { |
| 15 | 16 | return 123; |
| 16 | 17 | } |
| 17 | #expect_stdout="6 123\n" | |
| 18 | #expect_stdout="7 123\n" | |
| 18 | 19 | |
| 19 | 20 | #update=add newline |
| 20 | 21 | #file=main.zig |
| 21 | 22 | const std = @import("std"); |
| 22 | 23 | pub 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 | } |
| 25 | 27 | |
| 26 | 28 | fn foo() u32 { |
| ... | ... | @@ -29,4 +31,4 @@ fn foo() u32 { |
| 29 | 31 | fn bar() u32 { |
| 30 | 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 | const std = @import("std"); |
| 8 | 8 | var some_enum: enum { first, second } = .first; |
| 9 | 9 | pub 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 | 12 | #expect_stdout="first" |
| 13 | 13 | #update=no change |
| ... | ... | @@ -15,6 +15,6 @@ pub fn main() !void { |
| 15 | 15 | const std = @import("std"); |
| 16 | 16 | var some_enum: enum { first, second } = .first; |
| 17 | 17 | pub 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 | 20 | #expect_stdout="first" |
test/incremental/recursive_function_becomes_non_recursive+2-2| ... | ... | @@ -8,7 +8,7 @@ pub fn main() !void { |
| 8 | 8 | try foo(false); |
| 9 | 9 | } |
| 10 | 10 | fn foo(recurse: bool) !void { |
| 11 | const stdout = std.io.getStdOut().writer(); | |
| 11 | const stdout = std.fs.File.stdout(); | |
| 12 | 12 | if (recurse) return foo(true); |
| 13 | 13 | try stdout.writeAll("non-recursive path\n"); |
| 14 | 14 | } |
| ... | ... | @@ -21,7 +21,7 @@ pub fn main() !void { |
| 21 | 21 | try foo(true); |
| 22 | 22 | } |
| 23 | 23 | fn foo(recurse: bool) !void { |
| 24 | const stdout = std.io.getStdOut().writer(); | |
| 24 | const stdout = std.fs.File.stdout(); | |
| 25 | 25 | if (recurse) return stdout.writeAll("x==1\n"); |
| 26 | 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 | 9 | bar = 2, |
| 10 | 10 | }; |
| 11 | 11 | pub 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 | } |
| 14 | 15 | const std = @import("std"); |
| 15 | 16 | #expect_stdout="1\n" |
| ... | ... | @@ -20,8 +21,9 @@ const MyEnum = enum(u8) { |
| 20 | 21 | bar = 2, |
| 21 | 22 | }; |
| 22 | 23 | pub 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 | } |
| 25 | 27 | const 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 | 29 | #expect_error=main.zig:1:16: note: enum declared here |
test/incremental/unreferenced_error+4-4| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | #file=main.zig |
| 7 | 7 | const std = @import("std"); |
| 8 | 8 | pub fn main() !void { |
| 9 | try std.io.getStdOut().writeAll(a); | |
| 9 | try std.fs.File.stdout().writeAll(a); | |
| 10 | 10 | } |
| 11 | 11 | const a = "Hello, World!\n"; |
| 12 | 12 | #expect_stdout="Hello, World!\n" |
| ... | ... | @@ -15,7 +15,7 @@ const a = "Hello, World!\n"; |
| 15 | 15 | #file=main.zig |
| 16 | 16 | const std = @import("std"); |
| 17 | 17 | pub fn main() !void { |
| 18 | try std.io.getStdOut().writeAll(a); | |
| 18 | try std.fs.File.stdout().writeAll(a); | |
| 19 | 19 | } |
| 20 | 20 | const a = @compileError("bad a"); |
| 21 | 21 | #expect_error=main.zig:5:11: error: bad a |
| ... | ... | @@ -24,7 +24,7 @@ const a = @compileError("bad a"); |
| 24 | 24 | #file=main.zig |
| 25 | 25 | const std = @import("std"); |
| 26 | 26 | pub fn main() !void { |
| 27 | try std.io.getStdOut().writeAll(b); | |
| 27 | try std.fs.File.stdout().writeAll(b); | |
| 28 | 28 | } |
| 29 | 29 | const a = @compileError("bad a"); |
| 30 | 30 | const b = "Hi there!\n"; |
| ... | ... | @@ -34,7 +34,7 @@ const b = "Hi there!\n"; |
| 34 | 34 | #file=main.zig |
| 35 | 35 | const std = @import("std"); |
| 36 | 36 | pub fn main() !void { |
| 37 | try std.io.getStdOut().writeAll(a); | |
| 37 | try std.fs.File.stdout().writeAll(a); | |
| 38 | 38 | } |
| 39 | 39 | const a = "Back to a\n"; |
| 40 | 40 | const b = @compileError("bad b"); |
test/link/bss/main.zig+4-1| ... | ... | @@ -4,8 +4,11 @@ const std = @import("std"); |
| 4 | 4 | var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; |
| 5 | 5 | |
| 6 | 6 | pub fn main() anyerror!void { |
| 7 | var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); | |
| 8 | ||
| 7 | 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 | 12 | // workaround the dreaded decl_val |
| 10 | 13 | (&buffer)[0], |
| 11 | 14 | (&buffer)[0x10], |
test/link/elf.zig+4-4| ... | ... | @@ -1315,8 +1315,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { |
| 1315 | 1315 | \\extern var live_var2: i32; |
| 1316 | 1316 | \\extern fn live_fn2() void; |
| 1317 | 1317 | \\pub fn main() void { |
| 1318 | \\ const stdout = std.io.getStdOut(); | |
| 1319 | \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; | |
| 1318 | \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); | |
| 1319 | \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); | |
| 1320 | 1320 | \\ live_fn2(); |
| 1321 | 1321 | \\} |
| 1322 | 1322 | , |
| ... | ... | @@ -1357,8 +1357,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { |
| 1357 | 1357 | \\extern var live_var2: i32; |
| 1358 | 1358 | \\extern fn live_fn2() void; |
| 1359 | 1359 | \\pub fn main() void { |
| 1360 | \\ const stdout = std.io.getStdOut(); | |
| 1361 | \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; | |
| 1360 | \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); | |
| 1361 | \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); | |
| 1362 | 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 | 710 | const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = |
| 711 | 711 | \\const std = @import("std"); |
| 712 | 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 | }); |
| 716 | 716 | |
| ... | ... | @@ -2365,10 +2365,11 @@ fn testTlsZig(b: *Build, opts: Options) *Step { |
| 2365 | 2365 | \\threadlocal var x: i32 = 0; |
| 2366 | 2366 | \\threadlocal var y: i32 = -1; |
| 2367 | 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 | 2370 | \\ x -= 1; |
| 2370 | 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 | }); |
| 2374 | 2375 |
test/link/wasm/extern/main.zig+2-2| ... | ... | @@ -3,6 +3,6 @@ const std = @import("std"); |
| 3 | 3 | extern const foo: u32; |
| 4 | 4 | |
| 5 | 5 | pub fn main() void { |
| 6 | const std_out = std.io.getStdOut(); | |
| 7 | std_out.writer().print("Result: {d}", .{foo}) catch {}; | |
| 6 | var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); | |
| 7 | stdout_writer.interface.print("Result: {d}", .{foo}) catch {}; | |
| 8 | 8 | } |
test/src/Cases.zig+33-6| ... | ... | @@ -594,30 +594,57 @@ pub fn lowerToTranslateCSteps( |
| 594 | 594 | }; |
| 595 | 595 | } |
| 596 | 596 | |
| 597 | pub 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 | ||
| 597 | 610 | pub fn lowerToBuildSteps( |
| 598 | 611 | self: *Cases, |
| 599 | 612 | b: *std.Build, |
| 600 | 613 | parent_step: *std.Build.Step, |
| 601 | test_filters: []const []const u8, | |
| 602 | test_target_filters: []const []const u8, | |
| 614 | options: CaseTestOptions, | |
| 603 | 615 | ) void { |
| 604 | 616 | const host = std.zig.system.resolveTargetQuery(.{}) catch |err| |
| 605 | 617 | std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); |
| 606 | 618 | const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM"); |
| 607 | 619 | |
| 608 | 620 | for (self.cases.items) |case| { |
| 609 | for (test_filters) |test_filter| { | |
| 621 | for (options.test_filters) |test_filter| { | |
| 610 | 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; | |
| 612 | 636 | |
| 613 | 637 | const triple_txt = case.target.query.zigTriple(b.allocator) catch @panic("OOM"); |
| 614 | 638 | |
| 615 | if (test_target_filters.len > 0) { | |
| 616 | for (test_target_filters) |filter| { | |
| 639 | if (options.test_target_filters.len > 0) { | |
| 640 | for (options.test_target_filters) |filter| { | |
| 617 | 641 | if (std.mem.indexOf(u8, triple_txt, filter) != null) break; |
| 618 | 642 | } else continue; |
| 619 | 643 | } |
| 620 | 644 | |
| 645 | if (options.skip_libc and case.link_libc) | |
| 646 | continue; | |
| 647 | ||
| 621 | 648 | const writefiles = b.addWriteFiles(); |
| 622 | 649 | var file_sources = std.StringHashMap(std.Build.LazyPath).init(b.allocator); |
| 623 | 650 | defer file_sources.deinit(); |
test/src/check-stack-trace.zig+1-1| ... | ... | @@ -84,5 +84,5 @@ pub fn main() !void { |
| 84 | 84 | break :got_result try buf.toOwnedSlice(); |
| 85 | 85 | }; |
| 86 | 86 | |
| 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 | 48 | .pkg_import = .{ |
| 49 | 49 | .path = "pkg_import", |
| 50 | 50 | }, |
| 51 | .use_alias = .{ | |
| 52 | .path = "use_alias", | |
| 53 | }, | |
| 54 | 51 | .install_raw_hex = .{ |
| 55 | 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 | 27 | } |
| 28 | 28 | |
| 29 | 29 | // 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"); | |
| 31 | 31 | |
| 32 | 32 | // test stdin pipe from parent |
| 33 | 33 | const hello_stdin = "hello from stdin"; |
| 34 | 34 | var buf: [hello_stdin.len]u8 = undefined; |
| 35 | const stdin = std.io.getStdIn().reader(); | |
| 35 | const stdin: std.fs.File = .stdin(); | |
| 36 | 36 | const n = try stdin.readAll(&buf); |
| 37 | 37 | if (!std.mem.eql(u8, buf[0..n], hello_stdin)) { |
| 38 | 38 | testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin }); |
| ... | ... | @@ -40,7 +40,8 @@ fn run(allocator: std.mem.Allocator) !void { |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 | fn 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 | 45 | stderr.print("CHILD TEST ERROR: ", .{}) catch {}; |
| 45 | 46 | stderr.print(fmt, args) catch {}; |
| 46 | 47 | if (fmt[fmt.len - 1] != '\n') { |
test/standalone/child_process/main.zig+4-3| ... | ... | @@ -19,13 +19,13 @@ pub fn main() !void { |
| 19 | 19 | child.stderr_behavior = .Inherit; |
| 20 | 20 | try child.spawn(); |
| 21 | 21 | const child_stdin = child.stdin.?; |
| 22 | try child_stdin.writer().writeAll("hello from stdin"); // verified in child | |
| 22 | try child_stdin.writeAll("hello from stdin"); // verified in child | |
| 23 | 23 | child_stdin.close(); |
| 24 | 24 | child.stdin = null; |
| 25 | 25 | |
| 26 | 26 | const hello_stdout = "hello from stdout"; |
| 27 | 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 | 29 | if (!std.mem.eql(u8, buf[0..n], hello_stdout)) { |
| 30 | 30 | testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout }); |
| 31 | 31 | } |
| ... | ... | @@ -45,7 +45,8 @@ pub fn main() !void { |
| 45 | 45 | var parent_test_error = false; |
| 46 | 46 | |
| 47 | 47 | fn 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 | 50 | stderr.print("PARENT TEST ERROR: ", .{}) catch {}; |
| 50 | 51 | stderr.print(fmt, args) catch {}; |
| 51 | 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 | 10 | dir_name, .{}); |
| 11 | 11 | const file_name = args.next().?; |
| 12 | 12 | const file = try dir.createFile(file_name, .{}); |
| 13 | try file.writer().print( | |
| 13 | try file.deprecatedWriter().print( | |
| 14 | 14 | \\{s} |
| 15 | 15 | \\{s} |
| 16 | 16 | \\Hello, world! |
test/standalone/sigpipe/breakpipe.zig+1-1| ... | ... | @@ -10,7 +10,7 @@ pub fn main() !void { |
| 10 | 10 | std.posix.close(pipe[0]); |
| 11 | 11 | _ = std.posix.write(pipe[1], "a") catch |err| switch (err) { |
| 12 | 12 | error.BrokenPipe => { |
| 13 | try std.io.getStdOut().writer().writeAll("BrokenPipe\n"); | |
| 13 | try std.fs.File.stdout().writeAll("BrokenPipe\n"); | |
| 14 | 14 | std.posix.exit(123); |
| 15 | 15 | }, |
| 16 | 16 | else => |e| return e, |
test/standalone/simple/brace_expansion.zig deleted-292| ... | ... | @@ -1,292 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const io = std.io; | |
| 3 | const mem = std.mem; | |
| 4 | const debug = std.debug; | |
| 5 | const assert = debug.assert; | |
| 6 | const testing = std.testing; | |
| 7 | const ArrayList = std.ArrayList; | |
| 8 | const maxInt = std.math.maxInt; | |
| 9 | ||
| 10 | const Token = union(enum) { | |
| 11 | Word: []const u8, | |
| 12 | OpenBrace, | |
| 13 | CloseBrace, | |
| 14 | Comma, | |
| 15 | Eof, | |
| 16 | }; | |
| 17 | ||
| 18 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | |
| 19 | var global_allocator = gpa.allocator(); | |
| 20 | ||
| 21 | fn 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 | ||
| 68 | const 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 | ||
| 91 | const ParseError = error{ | |
| 92 | InvalidInput, | |
| 93 | OutOfMemory, | |
| 94 | }; | |
| 95 | ||
| 96 | fn 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 | ||
| 137 | fn 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 | ||
| 170 | const ExpandNodeError = error{OutOfMemory}; | |
| 171 | ||
| 172 | fn 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 | ||
| 226 | pub 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 | ||
| 241 | test "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 | ||
| 255 | fn 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 | ||
| 262 | test "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 | ||
| 285 | fn 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 | 50 | }); |
| 51 | 51 | if (case.link_libc) exe.root_module.link_libc = true; |
| 52 | 52 | |
| 53 | if (resolved_target.result.os.tag == .windows) { | |
| 54 | exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 55 | } | |
| 56 | ||
| 53 | 57 | _ = exe.getEmittedBin(); |
| 54 | 58 | |
| 55 | 59 | step.dependOn(&exe.step); |
| ... | ... | @@ -66,6 +70,10 @@ pub fn build(b: *std.Build) void { |
| 66 | 70 | }); |
| 67 | 71 | if (case.link_libc) exe.root_module.link_libc = true; |
| 68 | 72 | |
| 73 | if (resolved_target.result.os.tag == .windows) { | |
| 74 | exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 75 | } | |
| 76 | ||
| 69 | 77 | const run = b.addRunArtifact(exe); |
| 70 | 78 | step.dependOn(&run.step); |
| 71 | 79 | } |
| ... | ... | @@ -101,10 +109,6 @@ const cases = [_]Case{ |
| 101 | 109 | //.{ |
| 102 | 110 | // .src_path = "issue_9693/main.zig", |
| 103 | 111 | //}, |
| 104 | .{ | |
| 105 | .src_path = "brace_expansion.zig", | |
| 106 | .is_test = true, | |
| 107 | }, | |
| 108 | 112 | .{ |
| 109 | 113 | .src_path = "issue_7030.zig", |
| 110 | 114 | .target = .{ |
test/standalone/simple/cat/main.zig+10-10| ... | ... | @@ -1,42 +1,42 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const io = std.io; |
| 3 | const process = std.process; | |
| 4 | 3 | const fs = std.fs; |
| 5 | 4 | const mem = std.mem; |
| 6 | 5 | const warn = std.log.warn; |
| 6 | const fatal = std.process.fatal; | |
| 7 | 7 | |
| 8 | 8 | pub fn main() !void { |
| 9 | 9 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 10 | 10 | defer arena_instance.deinit(); |
| 11 | 11 | const arena = arena_instance.allocator(); |
| 12 | 12 | |
| 13 | const args = try process.argsAlloc(arena); | |
| 13 | const args = try std.process.argsAlloc(arena); | |
| 14 | 14 | |
| 15 | 15 | const exe = args[0]; |
| 16 | 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(&.{}); | |
| 18 | 20 | |
| 19 | 21 | const cwd = fs.cwd(); |
| 20 | 22 | |
| 21 | 23 | for (args[1..]) |arg| { |
| 22 | 24 | if (mem.eql(u8, arg, "-")) { |
| 23 | 25 | catted_anything = true; |
| 24 | try stdout_file.writeFileAll(io.getStdIn(), .{}); | |
| 26 | _ = try stdout.sendFileAll(&stdin_reader, .unlimited); | |
| 25 | 27 | } else if (mem.startsWith(u8, arg, "-")) { |
| 26 | 28 | return usage(exe); |
| 27 | 29 | } else { |
| 28 | const file = cwd.openFile(arg, .{}) catch |err| { | |
| 29 | warn("Unable to open file: {s}\n", .{@errorName(err)}); | |
| 30 | return err; | |
| 31 | }; | |
| 30 | const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err}); | |
| 32 | 31 | defer file.close(); |
| 33 | 32 | |
| 34 | 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 | 38 | if (!catted_anything) { |
| 39 | try stdout_file.writeFileAll(io.getStdIn(), .{}); | |
| 39 | _ = try stdout.sendFileAll(&stdin_reader, .unlimited); | |
| 40 | 40 | } |
| 41 | 41 | } |
| 42 | 42 |
test/standalone/simple/guess_number/main.zig+11-13| ... | ... | @@ -1,37 +1,35 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std"); |
| 3 | const io = std.io; | |
| 4 | const fmt = std.fmt; | |
| 5 | 3 | |
| 6 | 4 | pub fn main() !void { |
| 7 | const stdout = io.getStdOut().writer(); | |
| 8 | const stdin = io.getStdIn(); | |
| 5 | var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); | |
| 6 | const out = &stdout_writer.interface; | |
| 7 | const stdin: std.fs.File = .stdin(); | |
| 9 | 8 | |
| 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"); | |
| 11 | 10 | |
| 12 | 11 | const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1; |
| 13 | 12 | |
| 14 | 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 | 15 | var line_buf: [20]u8 = undefined; |
| 17 | ||
| 18 | 16 | const amt = try stdin.read(&line_buf); |
| 19 | 17 | if (amt == line_buf.len) { |
| 20 | try stdout.print("Input too long.\n", .{}); | |
| 18 | try out.writeAll("Input too long.\n"); | |
| 21 | 19 | continue; |
| 22 | 20 | } |
| 23 | 21 | const line = std.mem.trimEnd(u8, line_buf[0..amt], "\r\n"); |
| 24 | 22 | |
| 25 | const guess = fmt.parseUnsigned(u8, line, 10) catch { | |
| 26 | try stdout.print("Invalid number.\n", .{}); | |
| 23 | const guess = std.fmt.parseUnsigned(u8, line, 10) catch { | |
| 24 | try out.writeAll("Invalid number.\n"); | |
| 27 | 25 | continue; |
| 28 | 26 | }; |
| 29 | 27 | if (guess > answer) { |
| 30 | try stdout.print("Guess lower.\n", .{}); | |
| 28 | try out.writeAll("Guess lower.\n"); | |
| 31 | 29 | } else if (guess < answer) { |
| 32 | try stdout.print("Guess higher.\n", .{}); | |
| 30 | try out.writeAll("Guess higher.\n"); | |
| 33 | 31 | } else { |
| 34 | try stdout.print("You win!\n", .{}); | |
| 32 | try out.writeAll("You win!\n"); | |
| 35 | 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 | const Big = @Type(.{ .@"enum" = .{ |
| 7 | 7 | .tag_type = u16, |
| 8 | 8 | .fields = make_fields: { |
| 9 | @setEvalBranchQuota(500000); | |
| 9 | 10 | var fields: [1001]std.builtin.Type.EnumField = undefined; |
| 10 | 11 | for (&fields, 0..) |*field, i| { |
| 11 | 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 | 29 | .root_source_file = b.path("unwind.zig"), |
| 30 | 30 | .target = target, |
| 31 | 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 | 33 | .omit_frame_pointer = false, |
| 34 | 34 | }), |
| 35 | 35 | }); |
| ... | ... | @@ -54,7 +54,7 @@ pub fn build(b: *std.Build) void { |
| 54 | 54 | .root_source_file = b.path("unwind.zig"), |
| 55 | 55 | .target = target, |
| 56 | 56 | .optimize = optimize, |
| 57 | .unwind_tables = .@"async", | |
| 57 | .unwind_tables = .async, | |
| 58 | 58 | .omit_frame_pointer = true, |
| 59 | 59 | }), |
| 60 | 60 | // self-hosted lacks omit_frame_pointer support |
| ... | ... | @@ -101,7 +101,7 @@ pub fn build(b: *std.Build) void { |
| 101 | 101 | .root_source_file = b.path("shared_lib_unwind.zig"), |
| 102 | 102 | .target = target, |
| 103 | 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 | 105 | .omit_frame_pointer = true, |
| 106 | 106 | }), |
| 107 | 107 | // zig objcopy doesn't support incremental binaries |
test/standalone/use_alias/build.zig deleted-17| ... | ... | @@ -1,17 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 @@ |
| 1 | pub usingnamespace @cImport(@cInclude("foo.h")); |
test/standalone/use_alias/foo.h deleted-4| ... | ... | @@ -1,4 +0,0 @@ |
| 1 | struct Foo { | |
| 2 | int a; | |
| 3 | int b; | |
| 4 | }; |
test/standalone/use_alias/main.zig deleted-11| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | const c = @import("c.zig"); | |
| 2 | const expect = @import("std").testing.expect; | |
| 3 | ||
| 4 | test "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 | 47 | }), |
| 48 | 48 | }); |
| 49 | 49 | |
| 50 | fuzz.root_module.linkSystemLibrary("advapi32", .{}); | |
| 51 | ||
| 50 | 52 | const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; |
| 51 | 53 | const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); |
| 52 | 54 |
test/standalone/windows_argv/fuzz.zig+1-1| ... | ... | @@ -58,7 +58,7 @@ pub fn main() !void { |
| 58 | 58 | std.debug.print(">>> found discrepancy <<<\n", .{}); |
| 59 | 59 | const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w); |
| 60 | 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)}); | |
| 62 | 62 | |
| 63 | 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 | 27 | wtf8_buf.clearRetainingCapacity(); |
| 28 | 28 | try std.unicode.wtf16LeToWtf8ArrayList(&wtf8_buf, std.mem.span(expected_arg)); |
| 29 | 29 | if (!std.mem.eql(u8, wtf8_buf.items, arg_wtf8)) { |
| 30 | std.debug.print("{}: expected: \"{}\"\n", .{ i, std.zig.fmtEscapes(wtf8_buf.items) }); | |
| 31 | std.debug.print("{}: actual: \"{}\"\n", .{ i, std.zig.fmtEscapes(arg_wtf8) }); | |
| 30 | std.debug.print("{}: expected: \"{f}\"\n", .{ i, std.zig.fmtString(wtf8_buf.items) }); | |
| 31 | std.debug.print("{}: actual: \"{f}\"\n", .{ i, std.zig.fmtString(arg_wtf8) }); | |
| 32 | 32 | eql = false; |
| 33 | 33 | } |
| 34 | 34 | } |
| ... | ... | @@ -36,22 +36,22 @@ fn testArgv(expected_args: []const [*:0]const u16) !void { |
| 36 | 36 | for (expected_args[min_len..], min_len..) |arg, i| { |
| 37 | 37 | wtf8_buf.clearRetainingCapacity(); |
| 38 | 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 | 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 | 44 | const peb = std.os.windows.peb(); |
| 45 | 45 | const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer); |
| 46 | 46 | wtf8_buf.clearRetainingCapacity(); |
| 47 | 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 | 49 | std.debug.print("expected argv:\n", .{}); |
| 50 | 50 | std.debug.print("&.{{\n", .{}); |
| 51 | 51 | for (expected_args) |arg| { |
| 52 | 52 | wtf8_buf.clearRetainingCapacity(); |
| 53 | 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 | 56 | std.debug.print("}}\n", .{}); |
| 57 | 57 | return error.ArgvMismatch; |
test/standalone/windows_bat_args/build.zig+4| ... | ... | @@ -28,6 +28,8 @@ pub fn build(b: *std.Build) !void { |
| 28 | 28 | }), |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | test_exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 32 | ||
| 31 | 33 | const run = b.addRunArtifact(test_exe); |
| 32 | 34 | run.addArtifactArg(echo_args); |
| 33 | 35 | run.expectExitCode(0); |
| ... | ... | @@ -44,6 +46,8 @@ pub fn build(b: *std.Build) !void { |
| 44 | 46 | }), |
| 45 | 47 | }); |
| 46 | 48 | |
| 49 | fuzz.root_module.linkSystemLibrary("advapi32", .{}); | |
| 50 | ||
| 47 | 51 | const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; |
| 48 | 52 | const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); |
| 49 | 53 |
test/standalone/windows_bat_args/echo-args.zig+2-1| ... | ... | @@ -5,7 +5,8 @@ pub fn main() !void { |
| 5 | 5 | defer arena_state.deinit(); |
| 6 | 6 | const arena = arena_state.allocator(); |
| 7 | 7 | |
| 8 | const stdout = std.io.getStdOut().writer(); | |
| 8 | var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); | |
| 9 | const stdout = &stdout_writer.interface; | |
| 9 | 10 | var args = try std.process.argsAlloc(arena); |
| 10 | 11 | for (args[1..], 1..) |arg, i| { |
| 11 | 12 | try stdout.writeAll(arg); |
test/standalone/windows_spawn/build.zig+2| ... | ... | @@ -28,6 +28,8 @@ pub fn build(b: *std.Build) void { |
| 28 | 28 | }), |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | main.root_module.linkSystemLibrary("advapi32", .{}); | |
| 32 | ||
| 31 | 33 | const run = b.addRunArtifact(main); |
| 32 | 34 | run.addArtifactArg(hello); |
| 33 | 35 | run.expectExitCode(0); |
test/standalone/windows_spawn/hello.zig+2-1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub 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 | 6 | try stdout.writeAll("hello from exe\n"); |
| 6 | 7 | } |
test/tests.zig+83-39| ... | ... | @@ -918,14 +918,16 @@ const test_targets = blk: { |
| 918 | 918 | .link_libc = true, |
| 919 | 919 | }, |
| 920 | 920 | |
| 921 | .{ | |
| 922 | .target = std.Target.Query.parse(.{ | |
| 923 | .arch_os_abi = "riscv64-linux-none", | |
| 924 | .cpu_features = "baseline+v+zbb", | |
| 925 | }) catch unreachable, | |
| 926 | .use_llvm = false, | |
| 927 | .use_lld = false, | |
| 928 | }, | |
| 921 | // TODO implement codegen airFieldParentPtr | |
| 922 | // TODO implement airMemmove for riscv64 | |
| 923 | //.{ | |
| 924 | // .target = std.Target.Query.parse(.{ | |
| 925 | // .arch_os_abi = "riscv64-linux-none", | |
| 926 | // .cpu_features = "baseline+v+zbb", | |
| 927 | // }) catch unreachable, | |
| 928 | // .use_llvm = false, | |
| 929 | // .use_lld = false, | |
| 930 | //}, | |
| 929 | 931 | .{ |
| 930 | 932 | .target = .{ |
| 931 | 933 | .cpu_arch = .riscv64, |
| ... | ... | @@ -1480,16 +1482,8 @@ const test_targets = blk: { |
| 1480 | 1482 | .target = .{ |
| 1481 | 1483 | .cpu_arch = .aarch64, |
| 1482 | 1484 | .os_tag = .windows, |
| 1483 | .abi = .none, | |
| 1484 | }, | |
| 1485 | }, | |
| 1486 | .{ | |
| 1487 | .target = .{ | |
| 1488 | .cpu_arch = .aarch64, | |
| 1489 | .os_tag = .windows, | |
| 1490 | .abi = .gnu, | |
| 1485 | .abi = .msvc, | |
| 1491 | 1486 | }, |
| 1492 | .link_libc = true, | |
| 1493 | 1487 | }, |
| 1494 | 1488 | .{ |
| 1495 | 1489 | .target = .{ |
| ... | ... | @@ -1499,37 +1493,36 @@ const test_targets = blk: { |
| 1499 | 1493 | }, |
| 1500 | 1494 | .link_libc = true, |
| 1501 | 1495 | }, |
| 1502 | ||
| 1503 | 1496 | .{ |
| 1504 | 1497 | .target = .{ |
| 1505 | .cpu_arch = .x86, | |
| 1498 | .cpu_arch = .aarch64, | |
| 1506 | 1499 | .os_tag = .windows, |
| 1507 | .abi = .none, | |
| 1500 | .abi = .gnu, | |
| 1508 | 1501 | }, |
| 1509 | 1502 | }, |
| 1510 | 1503 | .{ |
| 1511 | 1504 | .target = .{ |
| 1512 | .cpu_arch = .x86, | |
| 1505 | .cpu_arch = .aarch64, | |
| 1513 | 1506 | .os_tag = .windows, |
| 1514 | 1507 | .abi = .gnu, |
| 1515 | 1508 | }, |
| 1516 | 1509 | .link_libc = true, |
| 1517 | 1510 | }, |
| 1511 | ||
| 1518 | 1512 | .{ |
| 1519 | 1513 | .target = .{ |
| 1520 | .cpu_arch = .x86, | |
| 1514 | .cpu_arch = .thumb, | |
| 1521 | 1515 | .os_tag = .windows, |
| 1522 | 1516 | .abi = .msvc, |
| 1523 | 1517 | }, |
| 1524 | .link_libc = true, | |
| 1525 | 1518 | }, |
| 1526 | ||
| 1527 | 1519 | .{ |
| 1528 | 1520 | .target = .{ |
| 1529 | 1521 | .cpu_arch = .thumb, |
| 1530 | 1522 | .os_tag = .windows, |
| 1531 | .abi = .none, | |
| 1523 | .abi = .msvc, | |
| 1532 | 1524 | }, |
| 1525 | .link_libc = true, | |
| 1533 | 1526 | }, |
| 1534 | 1527 | // https://github.com/ziglang/zig/issues/24016 |
| 1535 | 1528 | // .{ |
| ... | ... | @@ -1538,22 +1531,52 @@ const test_targets = blk: { |
| 1538 | 1531 | // .os_tag = .windows, |
| 1539 | 1532 | // .abi = .gnu, |
| 1540 | 1533 | // }, |
| 1534 | // }, | |
| 1535 | // .{ | |
| 1536 | // .target = .{ | |
| 1537 | // .cpu_arch = .thumb, | |
| 1538 | // .os_tag = .windows, | |
| 1539 | // .abi = .gnu, | |
| 1540 | // }, | |
| 1541 | 1541 | // .link_libc = true, |
| 1542 | 1542 | // }, |
| 1543 | ||
| 1543 | 1544 | .{ |
| 1544 | 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 | 1554 | .os_tag = .windows, |
| 1547 | 1555 | .abi = .msvc, |
| 1548 | 1556 | }, |
| 1549 | 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 | }, | |
| 1551 | 1574 | |
| 1552 | 1575 | .{ |
| 1553 | 1576 | .target = .{ |
| 1554 | 1577 | .cpu_arch = .x86_64, |
| 1555 | 1578 | .os_tag = .windows, |
| 1556 | .abi = .none, | |
| 1579 | .abi = .msvc, | |
| 1557 | 1580 | }, |
| 1558 | 1581 | .use_llvm = false, |
| 1559 | 1582 | .use_lld = false, |
| ... | ... | @@ -1562,17 +1585,16 @@ const test_targets = blk: { |
| 1562 | 1585 | .target = .{ |
| 1563 | 1586 | .cpu_arch = .x86_64, |
| 1564 | 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 | 1592 | .target = .{ |
| 1572 | 1593 | .cpu_arch = .x86_64, |
| 1573 | 1594 | .os_tag = .windows, |
| 1574 | .abi = .none, | |
| 1595 | .abi = .msvc, | |
| 1575 | 1596 | }, |
| 1597 | .link_libc = true, | |
| 1576 | 1598 | }, |
| 1577 | 1599 | .{ |
| 1578 | 1600 | .target = .{ |
| ... | ... | @@ -1580,13 +1602,21 @@ const test_targets = blk: { |
| 1580 | 1602 | .os_tag = .windows, |
| 1581 | 1603 | .abi = .gnu, |
| 1582 | 1604 | }, |
| 1583 | .link_libc = true, | |
| 1605 | .use_llvm = false, | |
| 1606 | .use_lld = false, | |
| 1584 | 1607 | }, |
| 1585 | 1608 | .{ |
| 1586 | 1609 | .target = .{ |
| 1587 | 1610 | .cpu_arch = .x86_64, |
| 1588 | 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 | 1621 | .link_libc = true, |
| 1592 | 1622 | }, |
| ... | ... | @@ -2280,6 +2310,7 @@ const ModuleTestOptions = struct { |
| 2280 | 2310 | desc: []const u8, |
| 2281 | 2311 | optimize_modes: []const OptimizeMode, |
| 2282 | 2312 | include_paths: []const []const u8, |
| 2313 | windows_libs: []const []const u8, | |
| 2283 | 2314 | skip_single_threaded: bool, |
| 2284 | 2315 | skip_non_native: bool, |
| 2285 | 2316 | skip_freebsd: bool, |
| ... | ... | @@ -2409,6 +2440,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 2409 | 2440 | |
| 2410 | 2441 | for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path)); |
| 2411 | 2442 | |
| 2443 | if (target.os.tag == .windows) { | |
| 2444 | for (options.windows_libs) |lib| these_tests.linkSystemLibrary(lib); | |
| 2445 | } | |
| 2446 | ||
| 2412 | 2447 | const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{ |
| 2413 | 2448 | options.name, |
| 2414 | 2449 | triple_txt, |
| ... | ... | @@ -2517,7 +2552,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 2517 | 2552 | return step; |
| 2518 | 2553 | } |
| 2519 | 2554 | |
| 2520 | fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool { | |
| 2555 | pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool { | |
| 2521 | 2556 | if (use_llvm) |x| return x; |
| 2522 | 2557 | if (query.ofmt == .c) return false; |
| 2523 | 2558 | switch (optimize_mode) { |
| ... | ... | @@ -2629,9 +2664,8 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step { |
| 2629 | 2664 | pub fn addCases( |
| 2630 | 2665 | b: *std.Build, |
| 2631 | 2666 | parent_step: *Step, |
| 2632 | test_filters: []const []const u8, | |
| 2633 | test_target_filters: []const []const u8, | |
| 2634 | 2667 | target: std.Build.ResolvedTarget, |
| 2668 | case_test_options: @import("src/Cases.zig").CaseTestOptions, | |
| 2635 | 2669 | translate_c_options: @import("src/Cases.zig").TranslateCOptions, |
| 2636 | 2670 | build_options: @import("cases.zig").BuildOptions, |
| 2637 | 2671 | ) !void { |
| ... | ... | @@ -2646,13 +2680,19 @@ pub fn addCases( |
| 2646 | 2680 | cases.addFromDir(dir, b); |
| 2647 | 2681 | try @import("cases.zig").addCases(&cases, build_options, b); |
| 2648 | 2682 | |
| 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 | ); | |
| 2650 | 2691 | |
| 2651 | 2692 | cases.lowerToBuildSteps( |
| 2652 | 2693 | b, |
| 2653 | 2694 | parent_step, |
| 2654 | test_filters, | |
| 2655 | test_target_filters, | |
| 2695 | case_test_options, | |
| 2656 | 2696 | ); |
| 2657 | 2697 | } |
| 2658 | 2698 | |
| ... | ... | @@ -2699,6 +2739,10 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { |
| 2699 | 2739 | }), |
| 2700 | 2740 | }); |
| 2701 | 2741 | |
| 2742 | if (b.graph.host.result.os.tag == .windows) { | |
| 2743 | incr_check.root_module.linkSystemLibrary("advapi32", .{}); | |
| 2744 | } | |
| 2745 | ||
| 2702 | 2746 | var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); |
| 2703 | 2747 | defer dir.close(); |
| 2704 | 2748 |
tools/docgen.zig+4-8| ... | ... | @@ -43,8 +43,7 @@ pub fn main() !void { |
| 43 | 43 | while (args_it.next()) |arg| { |
| 44 | 44 | if (mem.startsWith(u8, arg, "-")) { |
| 45 | 45 | if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { |
| 46 | const stdout = io.getStdOut().writer(); | |
| 47 | try stdout.writeAll(usage); | |
| 46 | try fs.File.stdout().writeAll(usage); | |
| 48 | 47 | process.exit(0); |
| 49 | 48 | } else if (mem.eql(u8, arg, "--code-dir")) { |
| 50 | 49 | if (args_it.next()) |param| { |
| ... | ... | @@ -76,9 +75,9 @@ pub fn main() !void { |
| 76 | 75 | var code_dir = try fs.cwd().openDir(code_dir_path, .{}); |
| 77 | 76 | defer code_dir.close(); |
| 78 | 77 | |
| 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); | |
| 80 | 79 | |
| 81 | var buffered_writer = io.bufferedWriter(out_file.writer()); | |
| 80 | var buffered_writer = io.bufferedWriter(out_file.deprecatedWriter()); | |
| 82 | 81 | |
| 83 | 82 | var tokenizer = Tokenizer.init(input_path, input_file_bytes); |
| 84 | 83 | var toc = try genToc(arena, &tokenizer); |
| ... | ... | @@ -426,7 +425,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc { |
| 426 | 425 | try toc.writeByte('\n'); |
| 427 | 426 | try toc.writeByteNTimes(' ', header_stack_size * 4); |
| 428 | 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 | 429 | } else { |
| 431 | 430 | try toc.writeAll("<ul>\n"); |
| 432 | 431 | } |
| ... | ... | @@ -710,8 +709,6 @@ fn tokenizeAndPrintRaw( |
| 710 | 709 | .keyword_align, |
| 711 | 710 | .keyword_and, |
| 712 | 711 | .keyword_asm, |
| 713 | .keyword_async, | |
| 714 | .keyword_await, | |
| 715 | 712 | .keyword_break, |
| 716 | 713 | .keyword_catch, |
| 717 | 714 | .keyword_comptime, |
| ... | ... | @@ -748,7 +745,6 @@ fn tokenizeAndPrintRaw( |
| 748 | 745 | .keyword_try, |
| 749 | 746 | .keyword_union, |
| 750 | 747 | .keyword_unreachable, |
| 751 | .keyword_usingnamespace, | |
| 752 | 748 | .keyword_var, |
| 753 | 749 | .keyword_volatile, |
| 754 | 750 | .keyword_allowzero, |
tools/doctest.zig+2-5| ... | ... | @@ -44,7 +44,7 @@ pub fn main() !void { |
| 44 | 44 | while (args_it.next()) |arg| { |
| 45 | 45 | if (mem.startsWith(u8, arg, "-")) { |
| 46 | 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 | 48 | process.exit(0); |
| 49 | 49 | } else if (mem.eql(u8, arg, "-i")) { |
| 50 | 50 | opt_input = args_it.next() orelse fatal("expected parameter after -i", .{}); |
| ... | ... | @@ -85,7 +85,7 @@ pub fn main() !void { |
| 85 | 85 | var out_file = try fs.cwd().createFile(output_path, .{}); |
| 86 | 86 | defer out_file.close(); |
| 87 | 87 | |
| 88 | var bw = std.io.bufferedWriter(out_file.writer()); | |
| 88 | var bw = std.io.bufferedWriter(out_file.deprecatedWriter()); | |
| 89 | 89 | const out = bw.writer(); |
| 90 | 90 | |
| 91 | 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 | 653 | .keyword_align, |
| 654 | 654 | .keyword_and, |
| 655 | 655 | .keyword_asm, |
| 656 | .keyword_async, | |
| 657 | .keyword_await, | |
| 658 | 656 | .keyword_break, |
| 659 | 657 | .keyword_catch, |
| 660 | 658 | .keyword_comptime, |
| ... | ... | @@ -691,7 +689,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void { |
| 691 | 689 | .keyword_try, |
| 692 | 690 | .keyword_union, |
| 693 | 691 | .keyword_unreachable, |
| 694 | .keyword_usingnamespace, | |
| 695 | 692 | .keyword_var, |
| 696 | 693 | .keyword_volatile, |
| 697 | 694 | .keyword_allowzero, |
tools/dump-cov.zig+4-3| ... | ... | @@ -48,8 +48,9 @@ pub fn main() !void { |
| 48 | 48 | fatal("failed to load coverage file {}: {s}", .{ cov_path, @errorName(err) }); |
| 49 | 49 | }; |
| 50 | 50 | |
| 51 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 52 | const stdout = bw.writer(); | |
| 51 | var stdout_buffer: [4000]u8 = undefined; | |
| 52 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); | |
| 53 | const stdout = &stdout_writer.interface; | |
| 53 | 54 | |
| 54 | 55 | const header: *SeenPcsHeader = @ptrCast(cov_bytes); |
| 55 | 56 | try stdout.print("{any}\n", .{header.*}); |
| ... | ... | @@ -83,5 +84,5 @@ pub fn main() !void { |
| 83 | 84 | }); |
| 84 | 85 | } |
| 85 | 86 | |
| 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 | 5 | const process = std.process; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | 7 | const tmpDir = std.testing.tmpDir; |
| 8 | const fatal = std.process.fatal; | |
| 9 | const info = std.log.info; | |
| 8 | 10 | |
| 9 | 11 | const Allocator = mem.Allocator; |
| 10 | 12 | const OsTag = std.Target.Os.Tag; |
| ... | ... | @@ -245,19 +247,6 @@ const ArgsIterator = struct { |
| 245 | 247 | } |
| 246 | 248 | }; |
| 247 | 249 | |
| 248 | fn 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 | ||
| 253 | fn 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 | ||
| 261 | 250 | const Version = struct { |
| 262 | 251 | major: u16, |
| 263 | 252 | minor: u8, |
tools/gen_macos_headers_c.zig+9-17| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | const info = std.log.info; | |
| 4 | const fatal = std.process.fatal; | |
| 3 | 5 | |
| 4 | 6 | const Allocator = std.mem.Allocator; |
| 5 | 7 | |
| ... | ... | @@ -13,19 +15,6 @@ const usage = |
| 13 | 15 | \\-h, --help Print this help and exit |
| 14 | 16 | ; |
| 15 | 17 | |
| 16 | fn 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 | ||
| 21 | fn 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 | ||
| 29 | 18 | pub fn main() anyerror!void { |
| 30 | 19 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 31 | 20 | defer arena_allocator.deinit(); |
| ... | ... | @@ -58,16 +47,19 @@ pub fn main() anyerror!void { |
| 58 | 47 | |
| 59 | 48 | std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan); |
| 60 | 49 | |
| 61 | const stdout = std.io.getStdOut().writer(); | |
| 62 | try stdout.writeAll("#define _XOPEN_SOURCE\n"); | |
| 50 | var buffer: [2000]u8 = undefined; | |
| 51 | var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer); | |
| 52 | const w = &stdout_writer.interface; | |
| 53 | try w.writeAll("#define _XOPEN_SOURCE\n"); | |
| 63 | 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 | 58 | \\int main(int argc, char **argv) { |
| 68 | 59 | \\ return 0; |
| 69 | 60 | \\} |
| 70 | 61 | ); |
| 62 | try w.flush(); | |
| 71 | 63 | } |
| 72 | 64 | |
| 73 | 65 | fn findHeaders( |
tools/gen_outline_atomics.zig+4-3| ... | ... | @@ -17,8 +17,9 @@ pub fn main() !void { |
| 17 | 17 | |
| 18 | 18 | //const args = try std.process.argsAlloc(arena); |
| 19 | 19 | |
| 20 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 21 | const w = bw.writer(); | |
| 20 | var stdout_buffer: [2000]u8 = undefined; | |
| 21 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); | |
| 22 | const w = &stdout_writer.interface; | |
| 22 | 23 | |
| 23 | 24 | try w.writeAll( |
| 24 | 25 | \\//! This file is generated by tools/gen_outline_atomics.zig. |
| ... | ... | @@ -57,7 +58,7 @@ pub fn main() !void { |
| 57 | 58 | |
| 58 | 59 | try w.writeAll(footer.items); |
| 59 | 60 | try w.writeAll("}\n"); |
| 60 | try bw.flush(); | |
| 61 | try w.flush(); | |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | 64 | fn writeFunction( |
tools/gen_spirv_spec.zig+9-12| ... | ... | @@ -91,9 +91,10 @@ pub fn main() !void { |
| 91 | 91 | |
| 92 | 92 | try readExtRegistry(&exts, a, std.fs.cwd(), args[2]); |
| 93 | 93 | |
| 94 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 95 | try render(bw.writer(), a, core_spec, exts.items); | |
| 96 | try bw.flush(); | |
| 94 | var buffer: [4000]u8 = undefined; | |
| 95 | var w = std.fs.File.stdout().writerStreaming(&buffer); | |
| 96 | try render(&w, a, core_spec, exts.items); | |
| 97 | try w.flush(); | |
| 97 | 98 | } |
| 98 | 99 | |
| 99 | 100 | fn 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 | 167 | } |
| 167 | 168 | } |
| 168 | 169 | |
| 169 | fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void { | |
| 170 | fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void { | |
| 170 | 171 | try writer.writeAll( |
| 171 | 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 | 189 | \\ none, |
| 189 | 190 | \\ _, |
| 190 | 191 | \\ |
| 191 | \\ pub fn format( | |
| 192 | \\ self: IdResult, | |
| 193 | \\ comptime _: []const u8, | |
| 194 | \\ _: std.fmt.FormatOptions, | |
| 195 | \\ writer: anytype, | |
| 196 | \\ ) @TypeOf(writer).Error!void { | |
| 192 | \\ pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void { | |
| 197 | 193 | \\ switch (self) { |
| 198 | 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 | 895 | } |
| 900 | 896 | |
| 901 | 897 | fn usageAndExit(arg0: []const u8, code: u8) noreturn { |
| 902 | std.io.getStdErr().writer().print( | |
| 898 | const stderr = std.debug.lockStderrWriter(&.{}); | |
| 899 | stderr.print( | |
| 903 | 900 | \\Usage: {s} <SPIRV-Headers repository path> <path/to/zig/src/codegen/spirv/extinst.zig.grammar.json> |
| 904 | 901 | \\ |
| 905 | 902 | \\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 | 333 | } |
| 334 | 334 | } |
| 335 | 335 | |
| 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 | 339 | try stdout.writeAll( |
| 338 | 340 | \\#ifdef PTR64 |
| 339 | 341 | \\#define WEAK64 .weak |
| ... | ... | @@ -533,6 +535,8 @@ pub fn main() !void { |
| 533 | 535 | .all => {}, |
| 534 | 536 | .single, .multi, .family, .time32 => try stdout.writeAll("#endif\n"), |
| 535 | 537 | } |
| 538 | ||
| 539 | try stdout.flush(); | |
| 536 | 540 | } |
| 537 | 541 | |
| 538 | 542 | fn 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 | 6 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; |
| 7 | 7 | var allocator = gpa.allocator(); |
| 8 | 8 | |
| 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 | 12 | try output.writeAll( |
| 11 | 13 | \\// This file was generated by _generate_JSONTestSuite.zig |
| 12 | 14 | \\// These test cases are sourced from: https://github.com/nst/JSONTestSuite |
| ... | ... | @@ -44,6 +46,8 @@ pub fn main() !void { |
| 44 | 46 | try writeString(output, contents); |
| 45 | 47 | try output.writeAll(");\n}\n"); |
| 46 | 48 | } |
| 49 | ||
| 50 | try output.flush(); | |
| 47 | 51 | } |
| 48 | 52 | |
| 49 | 53 | const 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 | 42 | const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] }); |
| 43 | 43 | const target = try std.zig.system.resolveTargetQuery(query); |
| 44 | 44 | |
| 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 | 48 | inline for (@typeInfo(std.Target.CType).@"enum".fields) |field| { |
| 47 | 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 | 51 | cName(c_type), |
| 50 | 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 | 55 | cName(c_type), |
| 54 | 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 | 59 | cName(c_type), |
| 58 | 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 | 666 | const allocator = arena.allocator(); |
| 667 | 667 | |
| 668 | 668 | const args = try std.process.argsAlloc(allocator); |
| 669 | if (args.len < 3 or mem.eql(u8, args[1], "--help")) | |
| 670 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 669 | if (args.len < 3 or mem.eql(u8, args[1], "--help")) { | |
| 670 | usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2); | |
| 671 | std.process.exit(1); | |
| 672 | } | |
| 671 | 673 | const zig_exe = args[1]; |
| 672 | 674 | const linux_path = args[2]; |
| 673 | 675 | |
| 674 | var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 675 | const writer = buf_out.writer(); | |
| 676 | var stdout_buffer: [2000]u8 = undefined; | |
| 677 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); | |
| 678 | const writer = &stdout_writer.interface; | |
| 676 | 679 | |
| 677 | 680 | var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); |
| 678 | 681 | defer linux_dir.close(); |
| ... | ... | @@ -714,17 +717,16 @@ pub fn main() !void { |
| 714 | 717 | } |
| 715 | 718 | } |
| 716 | 719 | |
| 717 | try buf_out.flush(); | |
| 720 | try writer.flush(); | |
| 718 | 721 | } |
| 719 | 722 | |
| 720 | fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { | |
| 721 | file.writer().print( | |
| 723 | fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { | |
| 724 | try w.print( | |
| 722 | 725 | \\Usage: {s} /path/to/zig /path/to/linux |
| 723 | 726 | \\Alternative Usage: zig run /path/to/git/zig/tools/generate_linux_syscalls.zig -- /path/to/zig /path/to/linux |
| 724 | 727 | \\ |
| 725 | 728 | \\Generates the list of Linux syscalls for each supported cpu arch, using the Linux development tree. |
| 726 | 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); | |
| 729 | std.process.exit(code); | |
| 731 | , .{arg0}); | |
| 730 | 732 | } |
tools/lldb_pretty_printers.py-3| ... | ... | @@ -50,8 +50,6 @@ zig_keywords = { |
| 50 | 50 | 'anyframe', |
| 51 | 51 | 'anytype', |
| 52 | 52 | 'asm', |
| 53 | 'async', | |
| 54 | 'await', | |
| 55 | 53 | 'break', |
| 56 | 54 | 'callconv', |
| 57 | 55 | 'catch', |
| ... | ... | @@ -88,7 +86,6 @@ zig_keywords = { |
| 88 | 86 | 'try', |
| 89 | 87 | 'union', |
| 90 | 88 | 'unreachable', |
| 91 | 'usingnamespace', | |
| 92 | 89 | 'var', |
| 93 | 90 | 'volatile', |
| 94 | 91 | 'while', |
tools/update_clang_options.zig+22-20| ... | ... | @@ -634,25 +634,25 @@ pub fn main() anyerror!void { |
| 634 | 634 | const allocator = arena.allocator(); |
| 635 | 635 | const args = try std.process.argsAlloc(allocator); |
| 636 | 636 | |
| 637 | if (args.len <= 1) { | |
| 638 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 639 | } | |
| 637 | var stdout_buffer: [4000]u8 = undefined; | |
| 638 | var stdout_writer = fs.stdout().writerStreaming(&stdout_buffer); | |
| 639 | const stdout = &stdout_writer.interface; | |
| 640 | ||
| 641 | if (args.len <= 1) printUsageAndExit(args[0]); | |
| 642 | ||
| 640 | 643 | if (std.mem.eql(u8, args[1], "--help")) { |
| 641 | usageAndExit(std.io.getStdOut(), args[0], 0); | |
| 642 | } | |
| 643 | if (args.len < 3) { | |
| 644 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 644 | printUsage(stdout, args[0]) catch std.process.exit(2); | |
| 645 | stdout.flush() catch std.process.exit(2); | |
| 646 | std.process.exit(0); | |
| 645 | 647 | } |
| 646 | 648 | |
| 649 | if (args.len < 3) printUsageAndExit(args[0]); | |
| 650 | ||
| 647 | 651 | const llvm_tblgen_exe = args[1]; |
| 648 | if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) { | |
| 649 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 650 | } | |
| 652 | if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) printUsageAndExit(args[0]); | |
| 651 | 653 | |
| 652 | 654 | const llvm_src_root = args[2]; |
| 653 | if (std.mem.startsWith(u8, llvm_src_root, "-")) { | |
| 654 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 655 | } | |
| 655 | if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]); | |
| 656 | 656 | |
| 657 | 657 | var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator); |
| 658 | 658 | |
| ... | ... | @@ -719,8 +719,6 @@ pub fn main() anyerror!void { |
| 719 | 719 | // "W" and "Wl,". So we sort this list in order of descending priority. |
| 720 | 720 | std.mem.sort(*json.ObjectMap, all_objects.items, {}, objectLessThan); |
| 721 | 721 | |
| 722 | var buffered_stdout = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
| 723 | const stdout = buffered_stdout.writer(); | |
| 724 | 722 | try stdout.writeAll( |
| 725 | 723 | \\// This file is generated by tools/update_clang_options.zig. |
| 726 | 724 | \\// zig fmt: off |
| ... | ... | @@ -815,7 +813,7 @@ pub fn main() anyerror!void { |
| 815 | 813 | \\ |
| 816 | 814 | ); |
| 817 | 815 | |
| 818 | try buffered_stdout.flush(); | |
| 816 | try stdout.flush(); | |
| 819 | 817 | } |
| 820 | 818 | |
| 821 | 819 | // 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 | 964 | return std.mem.lessThan(u8, a_key, b_key); |
| 967 | 965 | } |
| 968 | 966 | |
| 969 | fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { | |
| 970 | file.writer().print( | |
| 967 | fn printUsageAndExit(arg0: []const u8) noreturn { | |
| 968 | printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2); | |
| 969 | std.process.exit(1); | |
| 970 | } | |
| 971 | ||
| 972 | fn printUsage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { | |
| 973 | try w.print( | |
| 971 | 974 | \\Usage: {s} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project |
| 972 | 975 | \\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 | 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); | |
| 977 | std.process.exit(code); | |
| 979 | , .{arg0}); | |
| 978 | 980 | } |
tools/update_cpu_features.zig+2-2| ... | ... | @@ -2082,8 +2082,8 @@ fn processOneTarget(job: Job) void { |
| 2082 | 2082 | } |
| 2083 | 2083 | |
| 2084 | 2084 | fn usageAndExit(arg0: []const u8, code: u8) noreturn { |
| 2085 | const stderr = std.io.getStdErr(); | |
| 2086 | stderr.writer().print( | |
| 2085 | const stderr = std.debug.lockStderrWriter(&.{}); | |
| 2086 | stderr.print( | |
| 2087 | 2087 | \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig [zig_name filter] |
| 2088 | 2088 | \\ |
| 2089 | 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 | 11 | const arena = arena_state.allocator(); |
| 12 | 12 | |
| 13 | 13 | const args = try std.process.argsAlloc(arena); |
| 14 | if (args.len <= 1) { | |
| 15 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 16 | } | |
| 14 | if (args.len <= 1) printUsageAndExit(args[0]); | |
| 17 | 15 | |
| 18 | 16 | const zig_src_root = args[1]; |
| 19 | if (mem.startsWith(u8, zig_src_root, "-")) { | |
| 20 | usageAndExit(std.io.getStdErr(), args[0], 1); | |
| 21 | } | |
| 17 | if (mem.startsWith(u8, zig_src_root, "-")) printUsageAndExit(args[0]); | |
| 22 | 18 | |
| 23 | 19 | var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{}); |
| 24 | 20 | defer zig_src_dir.close(); |
| ... | ... | @@ -193,10 +189,14 @@ pub fn main() anyerror!void { |
| 193 | 189 | } |
| 194 | 190 | } |
| 195 | 191 | |
| 196 | fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { | |
| 197 | file.writer().print( | |
| 192 | fn printUsageAndExit(arg0: []const u8) noreturn { | |
| 193 | printUsage(std.debug.lockStderrWriter(&.{}), arg0) catch std.process.exit(2); | |
| 194 | std.process.exit(1); | |
| 195 | } | |
| 196 | ||
| 197 | fn printUsage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { | |
| 198 | return w.print( | |
| 198 | 199 | \\Usage: {s} /path/git/zig |
| 199 | 200 | \\ |
| 200 | , .{arg0}) catch std.process.exit(1); | |
| 201 | std.process.exit(code); | |
| 201 | , .{arg0}); | |
| 202 | 202 | } |